ajout de la pagination des résultats de recherche
This commit is contained in:
+7
-4
@@ -1,6 +1,9 @@
|
||||
export function buildURL(query) {
|
||||
export function buildURL(query, limit = 20, offset = 0) {
|
||||
let url =
|
||||
"https://data.enseignementsup-recherche.gouv.fr/api/explore/v2.1/catalog/datasets/fr-esr-parcoursup/records?limit=10"
|
||||
"https://data.enseignementsup-recherche.gouv.fr/api/explore/v2.1/catalog/datasets/fr-esr-parcoursup/records?"
|
||||
|
||||
url += "limit=" + limit
|
||||
url += "&offset=" + offset
|
||||
|
||||
if (query && query.trim() !== "") {
|
||||
url += "&where=search(lib_for_voe_ins, '" + query + "')"
|
||||
@@ -9,8 +12,8 @@ export function buildURL(query) {
|
||||
return url
|
||||
}
|
||||
|
||||
export async function fetchFormations(query) {
|
||||
const url = buildURL(query)
|
||||
export async function fetchFormations(query, limit = 20, offset = 0) {
|
||||
const url = buildURL(query, limit, offset)
|
||||
const response = await fetch(url)
|
||||
|
||||
if (!response.ok) {
|
||||
|
||||
+62
-4
@@ -1,6 +1,6 @@
|
||||
<app>
|
||||
<div class="page">
|
||||
<h1>Mini projet Parcoursup</h1>
|
||||
<h1>Open Data Parcoursup</h1>
|
||||
<p class="subtitle">Recherche de formations Parcoursup avec Riot</p>
|
||||
|
||||
<p><b>{ state.selectedFormations.length }</b> formation(s) sélectionnée(s)</p>
|
||||
@@ -8,7 +8,12 @@
|
||||
<div if={ !state.selected }>
|
||||
<search-bar onsearch={ launchSearch }></search-bar>
|
||||
|
||||
<p if={ state.hasSearched && !state.loading }>
|
||||
<b>{ state.query }</b> : { state.total } résultat(s)
|
||||
</p>
|
||||
|
||||
<div class="layout">
|
||||
<div>
|
||||
<result-list
|
||||
results={ state.results }
|
||||
hasSearched={ state.hasSearched }
|
||||
@@ -17,6 +22,19 @@
|
||||
onselect={ addToSelection }>
|
||||
</result-list>
|
||||
|
||||
<div class="compare-controls" if={ state.total > state.limit }>
|
||||
<button onclick={ previousPage } disabled={ state.page === 1 }>
|
||||
Précédent
|
||||
</button>
|
||||
|
||||
<p>Page { state.page } / { getTotalPages() }</p>
|
||||
|
||||
<button onclick={ nextPage } disabled={ state.page === getTotalPages() }>
|
||||
Suivant
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<map-view results={ state.results }></map-view>
|
||||
</div>
|
||||
|
||||
@@ -109,7 +127,11 @@
|
||||
selectedFormations: [],
|
||||
note: 12,
|
||||
serie: 'general',
|
||||
sortBy: 'nom'
|
||||
sortBy: 'nom',
|
||||
query: '',
|
||||
page: 1,
|
||||
limit: 20,
|
||||
total: 0
|
||||
},
|
||||
|
||||
onMounted() {
|
||||
@@ -130,11 +152,28 @@
|
||||
this.update({
|
||||
loading: true,
|
||||
hasSearched: true,
|
||||
selected: null
|
||||
selected: null,
|
||||
query: query,
|
||||
page: 1
|
||||
})
|
||||
|
||||
await this.loadPage(1)
|
||||
},
|
||||
|
||||
async loadPage(page) {
|
||||
this.update({
|
||||
loading: true
|
||||
})
|
||||
|
||||
try {
|
||||
const data = await window.fetchFormations(query)
|
||||
const offset = (page - 1) * this.state.limit
|
||||
|
||||
const data = await window.fetchFormations(
|
||||
this.state.query,
|
||||
this.state.limit,
|
||||
offset
|
||||
)
|
||||
|
||||
const formations = []
|
||||
|
||||
if (data.results) {
|
||||
@@ -146,17 +185,36 @@
|
||||
|
||||
this.update({
|
||||
results: formations,
|
||||
total: data.total_count ? data.total_count : 0,
|
||||
page: page,
|
||||
loading: false
|
||||
})
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
this.update({
|
||||
results: [],
|
||||
total: 0,
|
||||
loading: false
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
getTotalPages() {
|
||||
return Math.ceil(this.state.total / this.state.limit)
|
||||
},
|
||||
|
||||
async nextPage() {
|
||||
if (this.state.page < this.getTotalPages()) {
|
||||
await this.loadPage(this.state.page + 1)
|
||||
}
|
||||
},
|
||||
|
||||
async previousPage() {
|
||||
if (this.state.page > 1) {
|
||||
await this.loadPage(this.state.page - 1)
|
||||
}
|
||||
},
|
||||
|
||||
showDetail(index) {
|
||||
this.update({
|
||||
selected: this.state.results[index]
|
||||
|
||||
@@ -1,89 +1,153 @@
|
||||
<detail-view>
|
||||
<div if={ props.formation } class="detail-card">
|
||||
<h2>{ props.formation.nom }</h2>
|
||||
<div if={ props.formation } class="detail-page">
|
||||
<h2>Formation</h2>
|
||||
|
||||
<p><b>Établissement :</b> { props.formation.etablissement }</p>
|
||||
<h1 class="formation-title">{ props.formation.etablissement } - { props.formation.nom }</h1>
|
||||
|
||||
<div class="formation-meta">
|
||||
<p><b>Ville :</b> { props.formation.ville }</p>
|
||||
<p><b>Département :</b> { props.formation.departement } - { props.formation.departementLib }</p>
|
||||
<p><b>Département :</b> { props.formation.departement } { props.formation.departementLib }</p>
|
||||
<p><b>Académie :</b> { props.formation.academie }</p>
|
||||
<p><b>Région :</b> { props.formation.region }</p>
|
||||
<p><b>Contrat :</b> { props.formation.contrat }</p>
|
||||
|
||||
<hr>
|
||||
|
||||
<h3>Formation</h3>
|
||||
<p><b>Filière :</b> { props.formation.filiere }</p>
|
||||
<p><b>Sélectivité :</b> { props.formation.selectivite }</p>
|
||||
<p>{ props.formation.contrat }</p>
|
||||
<p><b>Capacité :</b> { props.formation.capacite }</p>
|
||||
<p><b>Candidats :</b> { props.formation.candidats }</p>
|
||||
<p><b>Admis :</b> { props.formation.admis }</p>
|
||||
<p><b>Taux d'accès :</b> { props.formation.tauxAcces }%</p>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div class="detail-grid">
|
||||
<div>
|
||||
<h2>Phase principale d'admission</h2>
|
||||
|
||||
<h3>Profil des admis</h3>
|
||||
|
||||
<div class="chart-container">
|
||||
<div class="chart-title">Répartition des bacs</div>
|
||||
|
||||
<table class="charts-css bar show-labels">
|
||||
<table class="detail-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Bac</th>
|
||||
<th>Voeux</th>
|
||||
<th>Classés</th>
|
||||
<th>Propositions</th>
|
||||
<th>Acceptés</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Général</th>
|
||||
<td style="--size: { props.formation.pctGeneral / 100 }">
|
||||
{ props.formation.pctGeneral }%
|
||||
</td>
|
||||
<td>Gén</td>
|
||||
<td>{ props.formation.voePPGeneral }</td>
|
||||
<td>{ props.formation.classesPPGeneral }</td>
|
||||
<td>{ props.formation.propositionsPPGeneral }</td>
|
||||
<td>{ props.formation.acceptesPPGeneral }</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Technologique</th>
|
||||
<td style="--size: { props.formation.pctTechno / 100 }">
|
||||
{ props.formation.pctTechno }%
|
||||
</td>
|
||||
<td>Techno</td>
|
||||
<td>{ props.formation.voePPTechno }</td>
|
||||
<td>{ props.formation.classesPPTechno }</td>
|
||||
<td>{ props.formation.propositionsPPTechno }</td>
|
||||
<td>{ props.formation.acceptesPPTechno }</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Professionnel</th>
|
||||
<td style="--size: { props.formation.pctPro / 100 }">
|
||||
{ props.formation.pctPro }%
|
||||
</td>
|
||||
<td>Pro</td>
|
||||
<td>{ props.formation.voePPPro }</td>
|
||||
<td>{ props.formation.classesPPPro }</td>
|
||||
<td>{ props.formation.propositionsPPPro }</td>
|
||||
<td>{ props.formation.acceptesPPPro }</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Autres</td>
|
||||
<td>{ props.formation.voePPAutres }</td>
|
||||
<td>{ props.formation.classesPPAutres }</td>
|
||||
<td>{ props.formation.propositionsPPAutres }</td>
|
||||
<td>{ props.formation.acceptesPPAutres }</td>
|
||||
</tr>
|
||||
<tr class="total-row">
|
||||
<td>Total</td>
|
||||
<td>{ props.formation.voePPTotal }</td>
|
||||
<td>{ props.formation.classesPPTotal }</td>
|
||||
<td>{ props.formation.propositionsPPTotal }</td>
|
||||
<td>{ props.formation.acceptesPPTotal }</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div class="timeline-box">
|
||||
<h3>Vitesse de remplissage</h3>
|
||||
|
||||
<h3>Mentions au bac</h3>
|
||||
<div class="timeline">
|
||||
<div class="timeline-item">
|
||||
<div class="timeline-dot"></div>
|
||||
<div>
|
||||
<b>Ouverture 30 mai</b><br />
|
||||
{ props.formation.pctDebutPhase }%
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="chart-container">
|
||||
<table class="charts-css bar show-labels">
|
||||
<div class="timeline-item">
|
||||
<div class="timeline-dot"></div>
|
||||
<div>
|
||||
<b>16 juin</b><br />
|
||||
{ props.formation.pctDateBac }%
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="timeline-item">
|
||||
<div class="timeline-dot"></div>
|
||||
<div>
|
||||
<b>11 juillet</b><br />
|
||||
{ props.formation.pctFinPhase }%
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>Phase complémentaire d'admission</h2>
|
||||
|
||||
<table class="detail-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Bac</th>
|
||||
<th>Voeux</th>
|
||||
<th>Classés</th>
|
||||
<th>Propositions</th>
|
||||
<th>Acceptés</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Sans mention</th>
|
||||
<td style="--size: { props.formation.pctSansMention / 100 }">
|
||||
{ props.formation.pctSansMention }%
|
||||
</td>
|
||||
<td>Gén</td>
|
||||
<td>{ props.formation.voePCGeneral }</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>AB</th>
|
||||
<td style="--size: { props.formation.pctAB / 100 }">
|
||||
{ props.formation.pctAB }%
|
||||
</td>
|
||||
<td>Techno</td>
|
||||
<td>{ props.formation.voePCTechno }</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>B</th>
|
||||
<td style="--size: { props.formation.pctB / 100 }">
|
||||
{ props.formation.pctB }%
|
||||
</td>
|
||||
<td>Pro</td>
|
||||
<td>{ props.formation.voePCPro }</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>TB</th>
|
||||
<td style="--size: { props.formation.pctTB / 100 }">
|
||||
{ props.formation.pctTB }%
|
||||
</td>
|
||||
<td>Autres</td>
|
||||
<td>{ props.formation.voePCAutres }</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr class="total-row">
|
||||
<td>Total</td>
|
||||
<td>{ props.formation.voePCTotal }</td>
|
||||
<td>{ props.formation.classesPCTotal }</td>
|
||||
<td>{ props.formation.acceptesPCTotal }</td>
|
||||
<td>{ props.formation.acceptesPCTotal }</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<button onclick={ () => props.onback() }>Retour</button>
|
||||
</div>
|
||||
</detail-view>
|
||||
+34
-7
@@ -33,7 +33,6 @@ export function createFormation(raw) {
|
||||
? raw.g_olocalisation_des_formations.lon
|
||||
: null,
|
||||
|
||||
// profils admis
|
||||
pctFemmes: raw.pct_f,
|
||||
pctBoursiers: raw.pct_bours,
|
||||
pctNeoBac: raw.pct_neobac,
|
||||
@@ -48,19 +47,47 @@ export function createFormation(raw) {
|
||||
pctTB: raw.pct_tb,
|
||||
pctTBF: raw.pct_tbf,
|
||||
|
||||
// vitesse de remplissage / phase principale
|
||||
pctDebutPhase: raw.pct_acc_debutpp,
|
||||
pctDateBac: raw.pct_acc_datebac,
|
||||
pctFinPhase: raw.pct_acc_finpp,
|
||||
|
||||
// chiffres bruts utiles
|
||||
admisDebutPhase: raw.acc_debutpp,
|
||||
admisDateBac: raw.acc_datebac,
|
||||
admisFinPhase: raw.acc_finpp,
|
||||
|
||||
admisGeneral: raw.acc_bg,
|
||||
admisTechno: raw.acc_bt,
|
||||
admisPro: raw.acc_bp,
|
||||
admisAutres: raw.acc_at
|
||||
// phase principale
|
||||
voePPGeneral: raw.nb_voe_pp_bg,
|
||||
voePPTechno: raw.nb_voe_pp_bt,
|
||||
voePPPro: raw.nb_voe_pp_bp,
|
||||
voePPAutres: raw.nb_voe_pp_at,
|
||||
voePPTotal: raw.nb_voe_pp,
|
||||
|
||||
classesPPGeneral: raw.nb_cla_pp_bg,
|
||||
classesPPTechno: raw.nb_cla_pp_bt,
|
||||
classesPPPro: raw.nb_cla_pp_bp,
|
||||
classesPPAutres: raw.nb_cla_pp_at,
|
||||
classesPPTotal: raw.nb_cla_pp,
|
||||
|
||||
propositionsPPGeneral: raw.prop_tot_bg,
|
||||
propositionsPPTechno: raw.prop_tot_bt,
|
||||
propositionsPPPro: raw.prop_tot_bp,
|
||||
propositionsPPAutres: raw.prop_tot_at,
|
||||
propositionsPPTotal: raw.prop_tot,
|
||||
|
||||
acceptesPPGeneral: raw.acc_bg,
|
||||
acceptesPPTechno: raw.acc_bt,
|
||||
acceptesPPPro: raw.acc_bp,
|
||||
acceptesPPAutres: raw.acc_at,
|
||||
acceptesPPTotal: raw.acc_pp,
|
||||
|
||||
// phase complémentaire
|
||||
voePCGeneral: raw.nb_voe_pc_bg,
|
||||
voePCTechno: raw.nb_voe_pc_bt,
|
||||
voePCPro: raw.nb_voe_pc_bp,
|
||||
voePCAutres: raw.nb_voe_pc_at,
|
||||
voePCTotal: raw.nb_voe_pc,
|
||||
|
||||
classesPCTotal: raw.nb_cla_pc,
|
||||
acceptesPCTotal: raw.acc_pc
|
||||
}
|
||||
}
|
||||
+75
-3
@@ -164,9 +164,81 @@ body {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
@media (min-width: 900px) {
|
||||
.layout {
|
||||
grid-template-columns: 1.2fr 1fr;
|
||||
.detail-page {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.formation-title {
|
||||
color: #3d4fff;
|
||||
font-size: 2rem;
|
||||
line-height: 1.15;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.formation-meta p {
|
||||
margin: 8px 0;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.detail-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 2fr 1fr;
|
||||
gap: 40px;
|
||||
align-items: start;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.detail-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
background: white;
|
||||
}
|
||||
|
||||
.detail-table th,
|
||||
.detail-table td {
|
||||
border: 1px solid #d7d7d7;
|
||||
padding: 14px 12px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.detail-table th {
|
||||
background: #fafafa;
|
||||
font-size: 1.05rem;
|
||||
}
|
||||
|
||||
.total-row td {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.timeline-box {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.timeline {
|
||||
position: relative;
|
||||
margin-top: 30px;
|
||||
padding-left: 30px;
|
||||
border-left: 2px solid #d9d9d9;
|
||||
}
|
||||
|
||||
.timeline-item {
|
||||
position: relative;
|
||||
margin-bottom: 45px;
|
||||
}
|
||||
|
||||
.timeline-dot {
|
||||
position: absolute;
|
||||
left: -39px;
|
||||
top: 6px;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
background: #21c8b4;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.detail-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user