mAj
This commit is contained in:
+53
-128
@@ -1,4 +1,9 @@
|
||||
<app>
|
||||
<!--
|
||||
Composant racine — routeur SPA basé sur les ancres URL (#/)
|
||||
3 vues : 'search' | 'detail' | 'comparateur'
|
||||
-->
|
||||
|
||||
<header class="site-header">
|
||||
<div class="header-inner">
|
||||
<a class="logo" href="#/">
|
||||
@@ -6,6 +11,7 @@
|
||||
<span class="logo-text">Parcoursup <span class="logo-light">Explorer</span></span>
|
||||
</a>
|
||||
<div class="header-right">
|
||||
<!-- Badge cliquable vers le comparateur, visible si au moins 1 formation sélectionnée -->
|
||||
<a href="#/comparateur" class="header-badge badge-clickable" if={ state.selectedFormations.length > 0 }>
|
||||
{ state.selectedFormations.length } sélection(s)
|
||||
</a>
|
||||
@@ -16,7 +22,7 @@
|
||||
|
||||
<div class="page">
|
||||
|
||||
<!-- ============== VUE RECHERCHE ============== -->
|
||||
<!-- VUE RECHERCHE -->
|
||||
<div if={ state.view === 'search' }>
|
||||
<search-bar onsearch={ lancerRecherche }></search-bar>
|
||||
|
||||
@@ -36,6 +42,7 @@
|
||||
onselect={ ajouterSelection }>
|
||||
</result-list>
|
||||
|
||||
<!-- Pagination (visible si plus de résultats que la limite) -->
|
||||
<div class="pagination" if={ state.total > state.limit }>
|
||||
<button class="btn btn-outline" onclick={ pagePrecedente } disabled={ state.page === 1 }>
|
||||
← Précédent
|
||||
@@ -49,35 +56,30 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ============== VUE DÉTAIL ============== -->
|
||||
<!-- VUE DÉTAIL -->
|
||||
<div if={ state.view === 'detail' && state.selected }>
|
||||
<detail-view
|
||||
formation={ state.selected }
|
||||
onback={ retourRecherche }>
|
||||
</detail-view>
|
||||
<detail-view formation={ state.selected } onback={ retourRecherche }></detail-view>
|
||||
</div>
|
||||
|
||||
<div if={ state.view === 'detail' && !state.selected } class="message">
|
||||
Chargement de la formation...
|
||||
</div>
|
||||
|
||||
<!-- ============== VUE COMPARATEUR ============== -->
|
||||
<!-- VUE COMPARATEUR -->
|
||||
<div if={ state.view === 'comparateur' }>
|
||||
|
||||
<button class="btn btn-outline" onclick={ retourRecherche } style="margin-bottom: 16px;">← Retour à la recherche</button>
|
||||
|
||||
<comparateur-view
|
||||
formations={ state.selectedFormations }
|
||||
onretirer={ retirerSelection }
|
||||
onvider={ viderSelection }>
|
||||
</comparateur-view>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
|
||||
state: {
|
||||
view: 'search',
|
||||
loading: false,
|
||||
@@ -97,21 +99,17 @@
|
||||
var saved = localStorage.getItem('selectionFormations');
|
||||
var soi = this;
|
||||
|
||||
// Charger la sélection locale si elle existe
|
||||
// Restaurer la sélection depuis le localStorage
|
||||
if (saved) {
|
||||
try {
|
||||
this.state.selectedFormations = JSON.parse(saved);
|
||||
} catch (e) {
|
||||
console.error('Erreur lecture localStorage :', e);
|
||||
}
|
||||
try { this.state.selectedFormations = JSON.parse(saved); }
|
||||
catch (e) { console.error('Erreur localStorage :', e); }
|
||||
}
|
||||
|
||||
// Écouter les changements de connexion Firebase
|
||||
// Écouter les changements de session Firebase
|
||||
window.firebaseServices.onUserChanged(function(user) {
|
||||
|
||||
soi.update({ user: user });
|
||||
|
||||
// Si un utilisateur vient de se connecter, charger sa sélection depuis Firestore
|
||||
// À la connexion, on charge la sélection depuis Firestore
|
||||
if (user) {
|
||||
window.firebaseServices.loadUserData(user.uid)
|
||||
.then(function(donnees) {
|
||||
@@ -120,49 +118,36 @@
|
||||
localStorage.setItem('selectionFormations', JSON.stringify(donnees.selection));
|
||||
}
|
||||
})
|
||||
.catch(function(err) {
|
||||
console.error('Erreur chargement Firestore :', err);
|
||||
});
|
||||
.catch(function(err) { console.error('Erreur Firestore :', err); });
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
window.addEventListener('hashchange', function() {
|
||||
soi.gererRoute();
|
||||
});
|
||||
|
||||
// Écouter les changements d'URL pour le routage
|
||||
window.addEventListener('hashchange', function() { soi.gererRoute(); });
|
||||
this.gererRoute();
|
||||
},
|
||||
|
||||
// Appelée après une connexion ou inscription réussie
|
||||
surConnexion() {
|
||||
// L'écouteur onUserChanged dans onMounted gère automatiquement le reste
|
||||
// Géré automatiquement par onUserChanged ci-dessus
|
||||
},
|
||||
|
||||
// Appelée après une déconnexion
|
||||
surDeconnexion() {
|
||||
// On vide la sélection : l'utilisateur doit se reconnecter pour retrouver ses formations
|
||||
this.update({ selectedFormations: [] });
|
||||
localStorage.removeItem('selectionFormations');
|
||||
},
|
||||
|
||||
// Sauvegarder la sélection en local ET dans Firestore si connecté
|
||||
// Sauvegarde la sélection en local ET dans Firestore si connecté
|
||||
async sauvegarderSelection(selection) {
|
||||
|
||||
localStorage.setItem('selectionFormations', JSON.stringify(selection));
|
||||
|
||||
if (this.state.user) {
|
||||
try {
|
||||
await window.firebaseServices.saveUserData(this.state.user.uid, { selection: selection });
|
||||
} catch (err) {
|
||||
console.error('Erreur sauvegarde Firestore :', err);
|
||||
}
|
||||
try { await window.firebaseServices.saveUserData(this.state.user.uid, { selection }); }
|
||||
catch (err) { console.error('Erreur sauvegarde :', err); }
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
// Lit l'ancre URL et affiche la vue correspondante
|
||||
gererRoute() {
|
||||
var ancre = window.location.hash || '#/';
|
||||
var ancre = window.location.hash || '#/';
|
||||
var chemin = ancre.slice(1) || '/';
|
||||
|
||||
if (chemin.startsWith('/formation/')) {
|
||||
@@ -176,44 +161,32 @@
|
||||
}
|
||||
},
|
||||
|
||||
// Cherche la formation dans le cache (résultats / sélection) avant de faire un appel API
|
||||
async chargerFormationParId(id) {
|
||||
var i;
|
||||
|
||||
for (i = 0; i < this.state.results.length; i++) {
|
||||
if (this.state.results[i].id === id) {
|
||||
this.update({ selected: this.state.results[i] });
|
||||
return;
|
||||
}
|
||||
if (this.state.results[i].id === id) { this.update({ selected: this.state.results[i] }); return; }
|
||||
}
|
||||
|
||||
for (i = 0; i < this.state.selectedFormations.length; i++) {
|
||||
if (this.state.selectedFormations[i].id === id) {
|
||||
this.update({ selected: this.state.selectedFormations[i] });
|
||||
return;
|
||||
}
|
||||
if (this.state.selectedFormations[i].id === id) { this.update({ selected: this.state.selectedFormations[i] }); return; }
|
||||
}
|
||||
|
||||
// En dernier recours : appel API avec un mot-clé extrait de l'ID
|
||||
try {
|
||||
var parties = id.split('-');
|
||||
var motCle = parties.slice(1).join(' ').substring(0, 30);
|
||||
|
||||
var motCle = id.split('-').slice(1).join(' ').substring(0, 30);
|
||||
if (motCle) {
|
||||
var donnees = await window.chargerFormations(motCle, 10, 0);
|
||||
|
||||
if (donnees.results && donnees.results.length > 0) {
|
||||
for (i = 0; i < donnees.results.length; i++) {
|
||||
var formation = window.creerFormation(donnees.results[i]);
|
||||
if (formation.id === id) {
|
||||
this.update({ selected: formation });
|
||||
return;
|
||||
}
|
||||
var f = window.creerFormation(donnees.results[i]);
|
||||
if (f.id === id) { this.update({ selected: f }); return; }
|
||||
}
|
||||
this.update({ selected: window.creerFormation(donnees.results[0]) });
|
||||
}
|
||||
}
|
||||
} catch (erreur) {
|
||||
console.error('Erreur chargement formation :', erreur);
|
||||
}
|
||||
} catch (erreur) { console.error('Erreur chargement formation :', erreur); }
|
||||
},
|
||||
|
||||
afficherDetail(index) {
|
||||
@@ -227,61 +200,29 @@
|
||||
},
|
||||
|
||||
async lancerRecherche(requete, filtres) {
|
||||
this.update({
|
||||
loading: true,
|
||||
hasSearched: true,
|
||||
selected: null,
|
||||
view: 'search',
|
||||
query: requete,
|
||||
filters: filtres || {},
|
||||
page: 1
|
||||
});
|
||||
|
||||
this.update({ loading: true, hasSearched: true, selected: null, view: 'search', query: requete, filters: filtres || {}, page: 1 });
|
||||
window.location.hash = '#/';
|
||||
await this.chargerPage(1);
|
||||
},
|
||||
|
||||
// Charge la page demandée en calculant l'offset (décalage) correspondant
|
||||
async chargerPage(page) {
|
||||
this.update({ loading: true });
|
||||
|
||||
try {
|
||||
var decalage = (page - 1) * this.state.limit;
|
||||
|
||||
var donnees = await window.chargerFormations(
|
||||
this.state.query,
|
||||
this.state.limit,
|
||||
decalage,
|
||||
this.state.filters
|
||||
);
|
||||
|
||||
var donnees = await window.chargerFormations(this.state.query, this.state.limit, decalage, this.state.filters);
|
||||
var formations = [];
|
||||
|
||||
if (donnees.results) {
|
||||
for (var i = 0; i < donnees.results.length; i++) {
|
||||
var brut = donnees.results[i];
|
||||
formations.push(window.creerFormation(brut));
|
||||
formations.push(window.creerFormation(donnees.results[i]));
|
||||
}
|
||||
}
|
||||
|
||||
var total = 0;
|
||||
|
||||
if (donnees.total_count) {
|
||||
total = donnees.total_count;
|
||||
}
|
||||
|
||||
this.update({
|
||||
results: formations,
|
||||
total: total,
|
||||
page: page,
|
||||
loading: false
|
||||
});
|
||||
this.update({ results: formations, total: donnees.total_count || 0, page, loading: false });
|
||||
} catch (erreur) {
|
||||
console.error(erreur);
|
||||
this.update({
|
||||
results: [],
|
||||
total: 0,
|
||||
loading: false
|
||||
});
|
||||
this.update({ results: [], total: 0, loading: false });
|
||||
}
|
||||
},
|
||||
|
||||
@@ -290,48 +231,32 @@
|
||||
},
|
||||
|
||||
async pageSuivante() {
|
||||
if (this.state.page < this.nombreTotalPages()) {
|
||||
await this.chargerPage(this.state.page + 1);
|
||||
}
|
||||
if (this.state.page < this.nombreTotalPages()) { await this.chargerPage(this.state.page + 1); }
|
||||
},
|
||||
|
||||
async pagePrecedente() {
|
||||
if (this.state.page > 1) {
|
||||
await this.chargerPage(this.state.page - 1);
|
||||
}
|
||||
if (this.state.page > 1) { await this.chargerPage(this.state.page - 1); }
|
||||
},
|
||||
|
||||
// Ajoute la formation au comparateur si elle n'y est pas déjà
|
||||
ajouterSelection(index) {
|
||||
var formation = this.state.results[index];
|
||||
var selection = this.state.selectedFormations.slice();
|
||||
var dejaAjout = false;
|
||||
var formation = this.state.results[index];
|
||||
var selection = this.state.selectedFormations.slice();
|
||||
|
||||
for (var i = 0; i < selection.length; i++) {
|
||||
if (selection[i].id === formation.id) {
|
||||
dejaAjout = true;
|
||||
}
|
||||
if (selection[i].id === formation.id) { return; } // doublon
|
||||
}
|
||||
|
||||
if (!dejaAjout) {
|
||||
selection.push(formation);
|
||||
this.update({ selectedFormations: selection });
|
||||
this.sauvegarderSelection(selection);
|
||||
}
|
||||
selection.push(formation);
|
||||
this.update({ selectedFormations: selection });
|
||||
this.sauvegarderSelection(selection);
|
||||
},
|
||||
|
||||
// Retire la formation avec cet id de la sélection
|
||||
retirerSelection(id) {
|
||||
var nouvelleSelection = [];
|
||||
|
||||
for (var i = 0; i < this.state.selectedFormations.length; i++) {
|
||||
var f = this.state.selectedFormations[i];
|
||||
|
||||
if (f.id !== id) {
|
||||
nouvelleSelection.push(f);
|
||||
}
|
||||
}
|
||||
|
||||
this.update({ selectedFormations: nouvelleSelection });
|
||||
this.sauvegarderSelection(nouvelleSelection);
|
||||
var nouvelle = this.state.selectedFormations.filter(function(f) { return f.id !== id; });
|
||||
this.update({ selectedFormations: nouvelle });
|
||||
this.sauvegarderSelection(nouvelle);
|
||||
},
|
||||
|
||||
viderSelection() {
|
||||
|
||||
Reference in New Issue
Block a user