@@ -146,12 +314,24 @@ export abstract class AbstractVmdRdvView extends LitElement {
- ${lieuxDisponibles.length ? html`
+
+ ${this.daySelectorAvailable?html`
+
+ ) => {
+ this.jourSelectionne = event.detail.date;
+ this.rafraichirDonneesAffichees();
+ }}">
+
+ `:html``}
+
+ ${countLieuxDisponibles ? html`
- ${lieuxDisponibles.length} Lieu${Strings.plural(lieuxDisponibles.length, 'x')} de vaccination avec des disponibilités
+ ${countLieuxDisponibles} Lieu${Strings.plural(countLieuxDisponibles, 'x')} de vaccination avec des disponibilités
` : html`
@@ -180,14 +360,14 @@ export abstract class AbstractVmdRdvView extends LitElement {
return html`
this.prendreRdv(event.detail.lieu)}"
@verification-rdv-cliquee="${(event: LieuCliqueCustomEvent) => this.verifierRdv(event.detail.lieu)}"
/>`;
})}
- ${SearchRequest.isStandardType(this.currentSearch)?html`
+ ${standardMode?html`
Les critères d'éligibilité sont vérifiés lors de la prise de rendez-vous
`:html``}
@@ -200,8 +380,6 @@ export abstract class AbstractVmdRdvView extends LitElement {
this.registerInfiniteScroll();
}
-
-
async connectedCallback() {
super.connectedCallback();
this.launchCheckingUpdates();
@@ -261,7 +439,7 @@ export abstract class AbstractVmdRdvView extends LitElement {
? currentSearch.departement.code_departement
: currentSearch.commune.codeDepartement
const derniereMiseAJour = this.lieuxParDepartementAffiches?.derniereMiseAJour
- const lieuxAJourPourDepartement = await State.current.lieuxPour(codeDepartement)
+ const lieuxAJourPourDepartement = await State.current.lieuxPour([codeDepartement])
this.miseAJourDisponible = (derniereMiseAJour !== lieuxAJourPourDepartement.derniereMiseAJour);
// we stop the update check if there has been one
@@ -275,8 +453,6 @@ export abstract class AbstractVmdRdvView extends LitElement {
}
}
- abstract codeDepartementAdditionnels(codeDepartementSelectionne: CodeDepartement): CodeDepartement[]
-
async refreshLieux() {
const currentSearch = this.currentSearch
if(currentSearch) {
@@ -287,30 +463,14 @@ export abstract class AbstractVmdRdvView extends LitElement {
try {
this.searchInProgress = true;
await delay(1) // give some time (one tick) to render loader before doing the heavy lifting
- const [lieuxDepartement, ...lieuxDepartementsLimitrophes] = await Promise.all([
- State.current.lieuxPour(codeDepartement),
- ...this.codeDepartementAdditionnels(codeDepartement).map(dept => State.current.lieuxPour(dept))
- ]);
-
- const lieuxParDepartement = [lieuxDepartement].concat(lieuxDepartementsLimitrophes).reduce((mergedLieuxParDepartement, lieuxParDepartement) => ({
- codeDepartements: mergedLieuxParDepartement.codeDepartements.concat(lieuxParDepartement.codeDepartements),
- derniereMiseAJour: mergedLieuxParDepartement.derniereMiseAJour,
- lieuxDisponibles: mergedLieuxParDepartement.lieuxDisponibles.concat(lieuxParDepartement.lieuxDisponibles),
- lieuxIndisponibles: mergedLieuxParDepartement.lieuxIndisponibles.concat(lieuxParDepartement.lieuxIndisponibles),
- }), {
- codeDepartements: [],
- derniereMiseAJour: lieuxDepartement.derniereMiseAJour,
- lieuxDisponibles: [],
- lieuxIndisponibles: []
- } as LieuxParDepartement);
-
- this.lieuxParDepartementAffiches = this.afficherLieuxParDepartement(lieuxParDepartement, currentSearch);
- this.cartesAffichees = this.infiniteScroll.ajouterCartesPaginees(this.lieuxParDepartementAffiches, []);
+ this.lieuxParDepartement = await State.current.lieuxPour([codeDepartement].concat(this.options.codeDepartementAdditionnels(codeDepartement)));
+
+ this.rafraichirDonneesAffichees();
const commune = SearchRequest.isByCommune(currentSearch) ? currentSearch.commune : undefined
Analytics.INSTANCE.rechercheLieuEffectuee(
codeDepartement,
- this.currentCritereTri(),
+ this.currentTri(),
currentSearch.type,
commune,
this.lieuxParDepartementAffiches);
@@ -323,22 +483,100 @@ export abstract class AbstractVmdRdvView extends LitElement {
}
}
+ private autoSelectJourSelectionne(daySelectorAvailable: boolean) {
+ if(daySelectorAvailable) {
+ // On voit quel jour selectionner:
+ // 1/ on essaie de conserver le même jour selectionné si possible
+ // 2/ si pas possible (pas de créneau) on prend le premier jour dispo avec des créneaux
+ // 3/ si pas possible (aucun jour avec des créneaux) aucun jour n'est sélectionné
+ if(this.jourSelectionne) {
+ const creneauxQuotidienSelectionnes = this.creneauxQuotidiensAffiches.find(cq => cq.date === this.jourSelectionne);
+ if(!creneauxQuotidienSelectionnes || creneauxQuotidienSelectionnes.total===0) {
+ this.jourSelectionne = undefined;
+ }
+ }
+ if(!this.jourSelectionne) {
+ this.jourSelectionne = this.creneauxQuotidiensAffiches.filter(dailyAppointments => dailyAppointments.total !== 0)[0]?.date;
+ }
+ } else {
+ this.jourSelectionne = undefined;
+ }
+ }
+
+ rafraichirDonneesAffichees() {
+ if(this.currentSearch && this.lieuxParDepartement) {
+ const searchTypeConfig = searchTypeConfigFor(this.currentSearch.type);
+ const lieuxMatchantCriteres = this.filtrerLieuxMatchantLesCriteres(this.lieuxParDepartement, this.currentSearch);
+ // On calcule les créneaux quotidiens en fonction des lieux matchant les critères
+ this.creneauxQuotidiensAffiches = this.filtrerCreneauxQuotidiensEnFonctionDesLieuxMatchantLesCriteres(this.lieuxParDepartement.statsCreneauxLieuxQuotidiens, lieuxMatchantCriteres, searchTypeConfig);
+
+ let daySelectorAvailable = this.daySelectorAvailable;
+ this.autoSelectJourSelectionne(daySelectorAvailable);
+
+ // On calcule les lieux affichés en fonction du jour sélectionné
+ const creneauxQuotidienSelectionnes = this.creneauxQuotidiensAffiches.find(cq => cq.date === this.jourSelectionne);
+ const creneauxParLieu = creneauxQuotidienSelectionnes?.creneauxParLieu || [];
+ const lieuxMatchantCriteresAvecCountRdvMAJ = lieuxMatchantCriteres.map(l => ({
+ ...l,
+ appointment_count: searchTypeConfig.cardAppointmentsExtractor(l, daySelectorAvailable, creneauxParLieu)
+ }));
+
+ let lieuxDisponiblesAffiches = lieuxMatchantCriteresAvecCountRdvMAJ
+ .filter(l => searchTypeConfig.lieuConsidereCommeDisponible(l, creneauxParLieu.find(cpl => cpl.lieu === l.internal_id)))
+ .map(l => ({
+ ...l,
+ disponible: true
+ }));
+ let lieuxIndisponiblesAffiches = lieuxMatchantCriteresAvecCountRdvMAJ
+ .filter(l => !searchTypeConfig.lieuConsidereCommeDisponible(l, creneauxParLieu.find(cpl => cpl.lieu === l.internal_id)))
+ .map(l => ({
+ ...l,
+ disponible: false
+ }));
+
+ this.lieuxParDepartementAffiches = {
+ derniereMiseAJour: this.lieuxParDepartement.derniereMiseAJour,
+ codeDepartements: this.lieuxParDepartement.codeDepartements,
+ lieuxMatchantCriteres: lieuxDisponiblesAffiches.concat(lieuxIndisponiblesAffiches),
+ lieuxDisponibles: lieuxDisponiblesAffiches
+ };
+
+ this.cartesAffichees = this.infiniteScroll.ajouterCartesPaginees(this.lieuxParDepartementAffiches, []);
+ }
+ }
+
+ private filtrerCreneauxQuotidiensEnFonctionDesLieuxMatchantLesCriteres(statsCreneauxLieuxParJours: StatsCreneauxLieuxParJour[], lieuxMatchantCriteres: LieuAffichableAvecDistance[], searchTypeConfig: SearchTypeConfig): RendezVousDuJour[] {
+ const lieuIdsMatchantCriteres = lieuxMatchantCriteres.map(l => l.internal_id);
+ return statsCreneauxLieuxParJours.map(statsCreneauxLieuxParJour => {
+ const lieuxAvecCreneauxFromDailyStats = statsCreneauxLieuxParJour.statsCreneauxParLieu
+ .map(scpl => ({ lieu: scpl.lieu, creneaux: countCreneauxFromCreneauxParTag(scpl.statsCreneauxParTag, searchTypeConfig.tagCreneau) }))
+ .filter(result => result.creneaux > 0)
+
+ const lieuxAvecCreneauxFiltres = lieuxAvecCreneauxFromDailyStats.filter(cpl => lieuIdsMatchantCriteres.includes(cpl.lieu));
+ return {
+ date: statsCreneauxLieuxParJour.date,
+ total: lieuxAvecCreneauxFiltres.reduce((total, lac) => total + lac.creneaux, 0),
+ creneauxParLieu: lieuxAvecCreneauxFiltres
+ }
+ });
+ }
+
private prendreRdv(lieu: Lieu) {
if(this.currentSearch && SearchRequest.isByCommune(this.currentSearch) && lieu.url) {
- Analytics.INSTANCE.clickSurRdv(lieu, this.currentCritereTri(), this.currentSearch.type, this.currentSearch.commune);
+ Analytics.INSTANCE.clickSurRdv(lieu, this.currentTri(), this.currentSearch.type, this.currentSearch.commune);
}
Router.navigateToUrlIfPossible(lieu.url);
}
private verifierRdv(lieu: Lieu) {
if(this.currentSearch && SearchRequest.isByCommune(this.currentSearch) && lieu.url) {
- Analytics.INSTANCE.clickSurVerifRdv(lieu, this.currentCritereTri(), this.currentSearch.type, this.currentSearch.commune);
+ Analytics.INSTANCE.clickSurVerifRdv(lieu, this.currentTri(), this.currentSearch.type, this.currentSearch.commune);
}
Router.navigateToUrlIfPossible(lieu.url);
}
- renderAdditionnalSearchCriteria(): TemplateResult {
- return html``;
+ private currentTri(): CodeTriCentre|"unknown" {
+ return this.currentSearch?this.currentSearch.tri:'unknown';
}
// FIXME move me to testable files
@@ -381,14 +619,9 @@ export abstract class AbstractVmdRdvView extends LitElement {
}
}
- protected transformLieuEnFonctionDuTypeDeRecherche(lieu: LieuAffichableAvecDistance) {
- return lieu;
- }
-
- abstract currentCritereTri(): CodeTriCentre;
abstract libelleLieuSelectionne(): TemplateResult;
// FIXME move me to a testable file
- abstract afficherLieuxParDepartement(lieuxParDepartement: LieuxParDepartement, search: SearchRequest): LieuxAvecDistanceParDepartement;
+ abstract filtrerLieuxMatchantLesCriteres(lieuxParDepartement: LieuxParDepartement, search: SearchRequest): LieuAffichableAvecDistance[];
}
@customElement('vmd-rdv-par-commune')
@@ -406,35 +639,53 @@ export class VmdRdvParCommuneView extends AbstractVmdRdvView {
this._codePostalSelectionne = code
this.updateCurrentSearch()
}
- @property({type: String}) set critèreDeTri (critèreDeTri: 'date' | 'distance') {
- this._critèreDeTri = critèreDeTri
- this.updateCurrentSearch()
- }
@internalProperty() private _searchType: SearchType | undefined = undefined;
@internalProperty() private _codeCommuneSelectionne: string | undefined = undefined;
@internalProperty() private _codePostalSelectionne: string | undefined = undefined;
- @internalProperty() private _critèreDeTri: CodeTriCentre = 'distance'
+ @internalProperty() private _distanceSelectionnee: number = 50;
+
private currentSearchMarker = {}
+ constructor() {
+ super({
+ codeDepartementAdditionnels: (codeDepartementSelectionne) => DEPARTEMENTS_LIMITROPHES[codeDepartementSelectionne],
+ criteresDeRechercheAdditionnels: () => html`
+
+ `
+ });
+ }
+
private async updateCurrentSearch() {
- if (this._codeCommuneSelectionne && this._codePostalSelectionne && this._critèreDeTri && this._searchType) {
+ if (this._codeCommuneSelectionne && this._codePostalSelectionne && this._searchType) {
const marker = {}
this.currentSearchMarker = marker
await delay(20)
if (this.currentSearchMarker !== marker) { return }
const commune = await State.current.autocomplete.findCommune(this._codePostalSelectionne, this._codeCommuneSelectionne)
if (commune) {
- this.currentSearch = SearchRequest.ByCommune(commune, this._critèreDeTri, this._searchType)
+ this.currentSearch = SearchRequest.ByCommune(commune, this._searchType, this.jourSelectionne)
this.refreshLieux()
}
}
}
- codeDepartementAdditionnels(codeDepartementSelectionne: CodeDepartement) {
- return DEPARTEMENTS_LIMITROPHES[codeDepartementSelectionne];
- }
-
libelleLieuSelectionne(): TemplateResult {
let nom = '???'
if (this.currentSearch) {
@@ -448,69 +699,24 @@ export class VmdRdvParCommuneView extends AbstractVmdRdvView {
}
// FIXME move me to testable file
- afficherLieuxParDepartement(lieuxParDepartement: LieuxParDepartement, search: SearchRequest.ByCommune): LieuxAvecDistanceParDepartement {
+ filtrerLieuxMatchantLesCriteres(lieuxParDepartement: LieuxParDepartement, search: SearchRequest.ByCommune): LieuAffichableAvecDistance[] {
const origin = search.commune
const distanceAvec = (lieu: Lieu) => (lieu.location ? distanceEntreDeuxPoints(origin, lieu.location) : Infinity)
-
const { lieuxDisponibles, lieuxIndisponibles } = lieuxParDepartement
- return {
- ...lieuxParDepartement,
- lieuxAffichables: ArrayBuilder.from([...lieuxDisponibles].map(l => ({...l, disponible: true})))
- .concat([...lieuxIndisponibles].map(l => ({...l, disponible: false})))
- .map(l => ({...l, distance: distanceAvec(l) }))
- .map(l => this.transformLieuEnFonctionDuTypeDeRecherche(l))
- .filter(l => !l.distance || l.distance < MAX_DISTANCE_CENTRE_IN_KM)
- .sortBy(l => this.extraireFormuleDeTri(l, search.tri))
- .build()
- };
- }
-
- // FIXME move me to vmd-search)
- critereTriUpdated(triCentre: CodeTriCentre) {
- Analytics.INSTANCE.critereTriCentresMisAJour(triCentre);
- if (this.currentSearch) {
- const nextSearch = {
- ...this.currentSearch,
- tri: triCentre
- }
- this.goToNewSearch(nextSearch)
- }
- }
- protected beforeNewSearchFromLocation (search: SearchRequest): SearchRequest {
- if (SearchRequest.isByCommune(search) && this.currentSearch) {
- return {
- ...search,
- tri: this.currentSearch.tri
- }
- }
- return search
- }
- // FIXME move me to vmd-search
- renderAdditionnalSearchCriteria(): TemplateResult {
- if(SearchRequest.isStandardType(this.currentSearch)) {
- return html`
-
- `;
- } else {
- return html``;
+ let lieuxAffichablesBuilder = ArrayBuilder.from([...lieuxDisponibles].map(l => ({...l, disponible: true})))
+ .concat([...lieuxIndisponibles].map(l => ({...l, disponible: false})))
+ .map(l => ({ ...l, distance: distanceAvec(l) })
+ ).filter(l => (!l.distance || l.distance < this._distanceSelectionnee))
+ if(this.searchTypeConfig.excludeAppointmentByPhoneOnly) {
+ lieuxAffichablesBuilder.filter(l => !l.appointment_by_phone_only)
}
- }
- currentCritereTri(): CodeTriCentre {
- return this.critèreDeTri;
+ lieuxAffichablesBuilder.sortBy(l => this.extraireFormuleDeTri(l, 'distance'))
+
+ const lieuxMatchantCriteres = lieuxAffichablesBuilder.build();
+ return lieuxMatchantCriteres;
}
}
@@ -530,22 +736,25 @@ export class VmdRdvParDepartementView extends AbstractVmdRdvView {
@internalProperty() private _codeDepartement: CodeDepartement | void = undefined
@internalProperty() protected currentSearch: SearchRequest.ByDepartement | void = undefined
+ constructor() {
+ super({
+ codeDepartementAdditionnels: () => [],
+ criteresDeRechercheAdditionnels: () => html``
+ });
+ }
+
private async updateCurrentSearch() {
const code = this._codeDepartement
if (code && this._searchType) {
const departements = await State.current.departementsDisponibles()
const departementSelectionne = departements.find(d => d.code_departement === code);
if (departementSelectionne) {
- this.currentSearch = SearchRequest.ByDepartement(departementSelectionne, this._searchType)
+ this.currentSearch = SearchRequest.ByDepartement(departementSelectionne, this._searchType, this.jourSelectionne)
this.refreshLieux()
}
}
}
- codeDepartementAdditionnels () {
- return []
- }
-
libelleLieuSelectionne(): TemplateResult {
let nom = '???'
if (this.currentSearch) {
@@ -559,21 +768,20 @@ export class VmdRdvParDepartementView extends AbstractVmdRdvView {
}
// FIXME move me to testable file
- afficherLieuxParDepartement(lieuxParDepartement: LieuxParDepartement): LieuxAvecDistanceParDepartement {
+ filtrerLieuxMatchantLesCriteres(lieuxParDepartement: LieuxParDepartement /*, search: SearchRequest */): LieuAffichableAvecDistance[] {
const { lieuxDisponibles, lieuxIndisponibles } = lieuxParDepartement
- return {
- ...lieuxParDepartement,
- lieuxAffichables: ArrayBuilder.from([...lieuxDisponibles].map(l => ({...l, disponible: true})))
- .concat([...lieuxIndisponibles].map(l => ({...l, disponible: false})))
- .map(l => ({...l, distance: undefined }))
- .map(l => this.transformLieuEnFonctionDuTypeDeRecherche(l))
- .sortBy(l => this.extraireFormuleDeTri(l, 'date'))
- .build()
- };
- }
+ let lieuxAffichablesBuilder = ArrayBuilder.from([...lieuxDisponibles].map(l => ({...l, disponible: true})))
+ .concat([...lieuxIndisponibles].map(l => ({...l, disponible: false})))
+ .map(l => ({ ...l, distance: undefined }))
+
+ if(this.searchTypeConfig.excludeAppointmentByPhoneOnly) {
+ lieuxAffichablesBuilder.filter(l => !l.appointment_by_phone_only)
+ }
+
+ lieuxAffichablesBuilder.sortBy(l => this.extraireFormuleDeTri(l, 'distance'))
- currentCritereTri(): CodeTriCentre {
- return 'date';
+ const lieuxMatchantCriteres = lieuxAffichablesBuilder.build();
+ return lieuxMatchantCriteres;
}
}