From ff420bbfd40a18e9d6d943c0d0e73f5e9599073b Mon Sep 17 00:00:00 2001 From: schlaifa <136464784+schlaifa@users.noreply.github.com> Date: Mon, 30 Dec 2024 15:15:54 +0100 Subject: [PATCH 1/2] Feat/recherche avancee (#627) --- package.json | 3 +- .../controllers/comparaisonEndpoint.ts | 6 +- .../getAnneesComparaisonEndpoint.ts | 12 + .../controllers/getDatesMiseAjourSources.ts | 13 + .../TypeOrmComparaisonLoader.test.ts | 135 ++------- .../TypeOrmComparaisonLoader.ts | 273 ++++++++++-------- .../entities/ResultatDeComparaison.ts" | 14 +- .../gateways/ComparaisonLoader.ts" | 6 +- .../ComparaisonEtablissementsUseCase.ts" | 20 +- .../configuration/wording/Wording.tsx | 2 + .../configuration/wording/WordingFr.tsx | 2 + src/frontend/ui/commun/Table/Table.tsx | 50 ++-- .../Table/TableExtensionCalculMoyenne.tsx | 34 +-- .../ui/commun/contexts/ComparaisonContext.ts | 34 +++ .../contexts/ComparaisonContextProvider.tsx | 61 ++++ .../ui/comparaison/Comparaison.module.css | 104 +++++++ .../ui/comparaison/ComparaisonPage.tsx | 163 ++++++----- src/frontend/ui/comparaison/ExportExcel.tsx | 165 +++++++++++ .../AjoutEtablissements.tsx | 157 ++++++++++ .../ListEtablissements.tsx | 94 ++++++ .../useRechercheAvanceeComparaison.tsx | 167 +++++++++++ src/frontend/ui/comparaison/model/data.tsx | 119 +------- src/frontend/ui/comparaison/useComparaison.ts | 72 ----- .../ui/comparaison/useComparaison.tsx | 187 ++++++++++++ src/frontend/ui/home/ComparaisonViewModel.tsx | 94 +++--- .../ui/recherche-avancee/FiltreCapacite.tsx | 14 +- .../ui/recherche-avancee/FiltreStructure.tsx | 16 +- .../FiltreZoneGeographique.tsx | 28 +- .../RechecheAvanceeFormulaire.tsx | 25 +- .../ResultatRechercheAvancee.tsx | 15 +- .../TableHeaderRechercheAvancee.tsx | 4 +- .../RechercheAvanceeFooter.test.tsx | 10 +- .../RechercheAvanceeFooter.tsx | 5 +- src/pages/_app.tsx | 29 +- .../compare.ts} | 21 +- src/pages/api/favoris/add.ts | 4 +- src/pages/comparaison.tsx | 47 ++- src/pages/recherche-avancee.tsx | 2 +- yarn.lock | 8 + 39 files changed, 1535 insertions(+), 680 deletions(-) create mode 100644 src/backend/infrastructure/controllers/getAnneesComparaisonEndpoint.ts create mode 100644 src/backend/infrastructure/controllers/getDatesMiseAjourSources.ts create mode 100644 src/frontend/ui/commun/contexts/ComparaisonContext.ts create mode 100644 src/frontend/ui/commun/contexts/ComparaisonContextProvider.tsx create mode 100644 src/frontend/ui/comparaison/ExportExcel.tsx create mode 100644 src/frontend/ui/comparaison/ajout-etablissements/AjoutEtablissements.tsx create mode 100644 src/frontend/ui/comparaison/ajout-etablissements/ListEtablissements.tsx create mode 100644 src/frontend/ui/comparaison/ajout-etablissements/useRechercheAvanceeComparaison.tsx delete mode 100644 src/frontend/ui/comparaison/useComparaison.ts create mode 100644 src/frontend/ui/comparaison/useComparaison.tsx rename src/pages/api/{comparaison.ts => comparaison/compare.ts} (54%) diff --git a/package.json b/package.json index 9b2300918..378711140 100644 --- a/package.json +++ b/package.json @@ -112,6 +112,7 @@ "bcrypt": "^5.1.0", "chart.js": "^3.8.0", "chartjs-plugin-datalabels": "^2.0.0", + "cookie": "^1.0.2", "cookies-next": "^4.1.0", "crypto": "^1.0.1", "csv-parser": "^3.0.0", @@ -188,4 +189,4 @@ "node": "16.16" }, "packageManager": "yarn@3.2.4" -} \ No newline at end of file +} diff --git a/src/backend/infrastructure/controllers/comparaisonEndpoint.ts b/src/backend/infrastructure/controllers/comparaisonEndpoint.ts index 76b3fd6b6..efbc2c183 100644 --- a/src/backend/infrastructure/controllers/comparaisonEndpoint.ts +++ b/src/backend/infrastructure/controllers/comparaisonEndpoint.ts @@ -6,13 +6,15 @@ export async function comparaisonEndpoint( dependencies: Dependencies, type: string, numerosFiness: string[], + annee: string, page: number, order: string, - orderBy: string + orderBy: string, + forExport: boolean, ): Promise { try { const comparaisonEtablissementsUseCase = new ComparaisonEtablissementsUseCase(dependencies.comparaisonLoader); - return await comparaisonEtablissementsUseCase.exécute(type, numerosFiness, page, order, orderBy); + return await comparaisonEtablissementsUseCase.exécute(type, numerosFiness, annee, page, order, orderBy, forExport); } catch (error) { dependencies.logger.error(error); throw error; diff --git a/src/backend/infrastructure/controllers/getAnneesComparaisonEndpoint.ts b/src/backend/infrastructure/controllers/getAnneesComparaisonEndpoint.ts new file mode 100644 index 000000000..d15ea0651 --- /dev/null +++ b/src/backend/infrastructure/controllers/getAnneesComparaisonEndpoint.ts @@ -0,0 +1,12 @@ +import { ComparaisonEtablissementsUseCase } from "../../métier/use-cases/ComparaisonEtablissementsUseCase"; +import { Dependencies } from "../dependencies"; + +export async function getAnneesComparaisonEndpoint(dependencies: Dependencies, type: string, numerosFiness: string[]): Promise { + try { + const comparaisonEtablissementsUseCase = new ComparaisonEtablissementsUseCase(dependencies.comparaisonLoader); + return await comparaisonEtablissementsUseCase.getAnneesComparaison(type, numerosFiness); + } catch (error) { + dependencies.logger.error(error); + throw error; + } +} \ No newline at end of file diff --git a/src/backend/infrastructure/controllers/getDatesMiseAjourSources.ts b/src/backend/infrastructure/controllers/getDatesMiseAjourSources.ts new file mode 100644 index 000000000..915f7b3da --- /dev/null +++ b/src/backend/infrastructure/controllers/getDatesMiseAjourSources.ts @@ -0,0 +1,13 @@ +import { DatesMisAjourSources } from "../../métier/entities/ResultatDeComparaison"; +import { ComparaisonEtablissementsUseCase } from "../../métier/use-cases/ComparaisonEtablissementsUseCase"; +import { Dependencies } from "../dependencies"; + +export async function getDatesMiseAjourSourcesEndpoint(dependencies: Dependencies): Promise { + try { + const comparaisonEtablissementsUseCase = new ComparaisonEtablissementsUseCase(dependencies.comparaisonLoader); + return await comparaisonEtablissementsUseCase.getDatesMisAJourSourcesComparaison(); + } catch (error) { + dependencies.logger.error(error); + throw error; + } +} \ No newline at end of file diff --git a/src/backend/infrastructure/gateways/comparaison-loader/TypeOrmComparaisonLoader.test.ts b/src/backend/infrastructure/gateways/comparaison-loader/TypeOrmComparaisonLoader.test.ts index 5d22c8fcf..f5bb3e583 100644 --- a/src/backend/infrastructure/gateways/comparaison-loader/TypeOrmComparaisonLoader.test.ts +++ b/src/backend/infrastructure/gateways/comparaison-loader/TypeOrmComparaisonLoader.test.ts @@ -50,16 +50,19 @@ describe("La comparaison des établissements médico sociaux", () => { numéroFinessEntitéJuridique: "000000000", numéroFinessÉtablissementTerritorial: "100000000", raisonSociale: `établissement territorial MS 1`, + raisonSocialeCourte: `établissement territorial MS 1`, }), ÉtablissementTerritorialIdentitéModelTestBuilder.créeMédicoSocial({ numéroFinessEntitéJuridique: "000000000", numéroFinessÉtablissementTerritorial: "100000001", raisonSociale: `établissement territorial MS 2`, + raisonSocialeCourte: `établissement territorial MS 2`, }), ÉtablissementTerritorialIdentitéModelTestBuilder.créeMédicoSocial({ numéroFinessEntitéJuridique: "000000000", numéroFinessÉtablissementTerritorial: "199999999", raisonSociale: `établissement territorial MS 3`, + raisonSocialeCourte: `établissement territorial MS 3`, }), ]); @@ -155,63 +158,39 @@ describe("La comparaison des établissements médico sociaux", () => { const typeOrmComparaisonLoader = new TypeOrmComparaisonLoader(orm); // WHEN - const comparaison = await typeOrmComparaisonLoader.compare("Médico-social", ["100000000", "100000001", "199999999"], premièrePage, "", ""); + const comparaison = await typeOrmComparaisonLoader.compare("Médico-social", ["100000000", "100000001", "199999999"], '2022', premièrePage, "", "", false); - expect(comparaison.nombreDeResultats).toStrictEqual([ - { annee: 2021, total: "2" }, - { annee: 2022, total: "3" }, - { annee: 2023, total: "2" }, - ]); - expect(comparaison.moyennes).toHaveLength(3); - expect(comparaison.moyennes[0]).toStrictEqual({ - annee: 2021, + expect(comparaison.nombreDeResultats).toBe(3); + + expect(comparaison.moyennes).toStrictEqual({ capaciteMoyenne: 40, - realisationAcitiviteMoyenne: 80, - acceuilDeJourMoyenne: 80, - hebergementPermanentMoyenne: 80, - hebergementTemporaireMoyenne: 80, + realisationAcitiviteMoyenne: 8000, + acceuilDeJourMoyenne: 8000, + hebergementPermanentMoyenne: 8000, + hebergementTemporaireMoyenne: 8000, fileActivePersonnesAccompagnesMoyenne: 80, - rotationPersonnelMoyenne: 0.667, - absenteismeMoyenne: 0.0767, - prestationExterneMoyenne: 0.659, - etpVacantMoyenne: 0.652, - tauxCafMoyenne: 0.13548734436644624, - vetusteConstructionMoyenne: 0.38845089702004892, - roulementNetGlobalMoyenne: 2206969.2599999998, - resultatNetComptableMoyenne: -38330.669999999503, + rotationPersonnelMoyenne: 66.7, + absenteismeMoyenne: 7.7, + prestationExterneMoyenne: 65.9, + etpVacantMoyenne: 65.2, + tauxCafMoyenne: 16.5, + vetusteConstructionMoyenne: 53.2, + roulementNetGlobalMoyenne: null, + resultatNetComptableMoyenne: 95999, }); expect(comparaison.resultat).toStrictEqual([ { - annee: 2021, - numéroFiness: "100000000", - socialReason: "établissement territorial MS 1", - type: "Médico-social", - capacite: "40", - realisationActivite: 8000, - acceuilDeJour: 8000, - hebergementPermanent: 8000, - hebergementTemporaire: 8000, - fileActivePersonnesAccompagnes: 8000, - rotationPersonnel: 66.7, - absenteisme: 7.7, - prestationExterne: 65.9, - etpVacant: 65.2, - tauxCaf: 13.5, - vetusteConstruction: 38.8, - roulementNetGlobal: 2206969, - resultatNetComptable: -38331, - }, - { - annee: 2022, numéroFiness: "100000000", socialReason: "établissement territorial MS 1", type: "Médico-social", capacite: "40", + commune: "NANTUA", + departement: "AIN", realisationActivite: 8000, acceuilDeJour: 8000, hebergementPermanent: 8000, hebergementTemporaire: 8000, - fileActivePersonnesAccompagnes: 8000, + fileActivePersonnesAccompagnes: 80, rotationPersonnel: 66.7, absenteisme: 7.7, prestationExterne: 65.9, @@ -222,56 +201,17 @@ describe("La comparaison des établissements médico sociaux", () => { resultatNetComptable: 95999, }, { - annee: 2023, - numéroFiness: "100000000", - socialReason: "établissement territorial MS 1", - type: "Médico-social", - capacite: "40", - realisationActivite: 8000, - acceuilDeJour: 8000, - hebergementPermanent: 8000, - hebergementTemporaire: 8000, - fileActivePersonnesAccompagnes: 8000, - rotationPersonnel: 66.7, - absenteisme: 7.7, - prestationExterne: 65.9, - etpVacant: 65.2, - tauxCaf: null, - vetusteConstruction: 31.2, - roulementNetGlobal: null, - resultatNetComptable: 18887, - }, - { - annee: 2021, numéroFiness: "100000001", socialReason: "établissement territorial MS 2", type: "Médico-social", capacite: null, + commune: "NANTUA", + departement: "AIN", realisationActivite: 8000, acceuilDeJour: 8000, hebergementPermanent: 8000, hebergementTemporaire: 8000, - fileActivePersonnesAccompagnes: 8000, - rotationPersonnel: 66.7, - absenteisme: 7.7, - prestationExterne: 65.9, - etpVacant: 65.2, - tauxCaf: 13.5, - vetusteConstruction: 38.8, - roulementNetGlobal: 2206969, - resultatNetComptable: -38331, - }, - { - annee: 2022, - numéroFiness: "100000001", - socialReason: "établissement territorial MS 2", - type: "Médico-social", - capacite: null, - realisationActivite: 8000, - acceuilDeJour: 8000, - hebergementPermanent: 8000, - hebergementTemporaire: 8000, - fileActivePersonnesAccompagnes: 8000, + fileActivePersonnesAccompagnes: 80, rotationPersonnel: 66.7, absenteisme: 7.7, prestationExterne: 65.9, @@ -282,36 +222,17 @@ describe("La comparaison des établissements médico sociaux", () => { resultatNetComptable: 95999, }, { - annee: 2023, - numéroFiness: "100000001", - socialReason: "établissement territorial MS 2", - type: "Médico-social", - capacite: null, - realisationActivite: 8000, - acceuilDeJour: 8000, - hebergementPermanent: 8000, - hebergementTemporaire: 8000, - fileActivePersonnesAccompagnes: 8000, - rotationPersonnel: 66.7, - absenteisme: 7.7, - prestationExterne: 65.9, - etpVacant: 65.2, - tauxCaf: null, - vetusteConstruction: null, - roulementNetGlobal: null, - resultatNetComptable: null, - }, - { - annee: 2022, numéroFiness: "199999999", socialReason: "établissement territorial MS 3", type: "Médico-social", capacite: null, + commune: "NANTUA", + departement: "AIN", realisationActivite: 8000, acceuilDeJour: 8000, hebergementPermanent: 8000, hebergementTemporaire: 8000, - fileActivePersonnesAccompagnes: 8000, + fileActivePersonnesAccompagnes: 80, rotationPersonnel: null, absenteisme: null, prestationExterne: null, diff --git a/src/backend/infrastructure/gateways/comparaison-loader/TypeOrmComparaisonLoader.ts b/src/backend/infrastructure/gateways/comparaison-loader/TypeOrmComparaisonLoader.ts index 8fd56fae5..22803e378 100644 --- a/src/backend/infrastructure/gateways/comparaison-loader/TypeOrmComparaisonLoader.ts +++ b/src/backend/infrastructure/gateways/comparaison-loader/TypeOrmComparaisonLoader.ts @@ -1,13 +1,15 @@ import { DataSource } from "typeorm"; -import { MoyenneSMS, ResultatDeComparaison, ResultatSMS } from "../../../métier/entities/ResultatDeComparaison"; +import { DateMiseÀJourFichierSourceModel, FichierSource } from "../../../../../database/models/DateMiseÀJourFichierSourceModel"; +import { DatesMisAjourSources, MoyenneSMS, ResultatDeComparaison, ResultatSMS } from "../../../métier/entities/ResultatDeComparaison"; import { ComparaisonLoader } from "../../../métier/gateways/ComparaisonLoader"; type ComparaisonSMSTypeOrm = Readonly<{ - numero_finess: string; - annee: number; + numero_finess_etablissement_territorial: string; raison_sociale_courte: string; - structure: string; + domaine: string; + commune: string; + departement: string; taux_realisation_activite: number; file_active_personnes_accompagnees: number; taux_occupation_en_hebergement_permanent: number; @@ -25,16 +27,64 @@ type ComparaisonSMSTypeOrm = Readonly<{ }>; export class TypeOrmComparaisonLoader implements ComparaisonLoader { - constructor(private readonly orm: Promise) {} + constructor(private readonly orm: Promise) { } private readonly NOMBRE_DE_RÉSULTATS_MAX_PAR_PAGE = 20; - async compare(type: string, numerosFiness: string[], page: number, order: string, orderBy: string): Promise { + async listeAnnees(type: string, numerosFiness: string[]): Promise { + if (type === "Entité juridique") { + return []; + } else { + if (type === "Médico-social") { + const generateAnnees = `SELECT generate_series( + CASE + WHEN maxannee = extract(year FROM current_date) THEN (maxannee - 2)::int + ELSE (extract(year FROM current_date) - 3)::int + END, + CASE + WHEN maxannee = extract(year FROM current_date) THEN maxannee::int + ELSE (extract(year FROM current_date) - 1)::int + END + ) annee + FROM ( + SELECT max(annee) maxannee FROM ( + Select annee from activite_medico_social ac where ac.numero_finess_etablissement_territorial in (${numerosFiness.map((finess) => "'" + finess + "'")}) + UNION + Select annee from ressources_humaines_medico_social rh where rh.numero_finess_etablissement_territorial in (${numerosFiness.map((finess) => "'" + finess + "'")}) + UNION + Select annee from budget_et_finances_medico_social budget where budget.numero_finess_etablissement_territorial in (${numerosFiness.map((finess) => "'" + finess + "'")}) + ) anc ) ang + `; + const generateAnneesResult = await (await this.orm).query(generateAnnees); + return generateAnneesResult.map((item: any) => item.annee); + } else { + return []; + } + } + } + + async getDatesMisAJourSourcesComparaison(): Promise { + const dateMAJFiness = (await this.chargeLaDateDeMiseÀJourModel( + FichierSource.FINESS_CS1400105 + )) as DateMiseÀJourFichierSourceModel; + + const dateMAJTdbperf = (await this.chargeLaDateDeMiseÀJourModel( + FichierSource.DIAMANT_ANN_MS_TDP_ET + )) as DateMiseÀJourFichierSourceModel; + + const dateMAJCnsa = (await this.chargeLaDateDeMiseÀJourModel( + FichierSource.DIAMANT_ANN_ERRD_EJ + )) as DateMiseÀJourFichierSourceModel; + + return { date_mis_a_jour_finess: dateMAJFiness.dernièreMiseÀJour || "", date_mis_a_jour_tdbPerf: dateMAJTdbperf.dernièreMiseÀJour || "", date_mis_a_jour_cnsa: dateMAJCnsa.dernièreMiseÀJour || "" } + } + + async compare(type: string, numerosFiness: string[], annee: string, page: number, order: string, orderBy: string, forExport: boolean): Promise { try { if (type === "Entité juridique") { return await this.compareEJ(); } else { if (type === "Médico-social") { - return await this.compareSMS(numerosFiness, page, order, orderBy); + return await this.compareSMS(numerosFiness, page, order, orderBy, annee, forExport); } else { return await this.compareSAN(); } @@ -45,148 +95,114 @@ export class TypeOrmComparaisonLoader implements ComparaisonLoader { } private async compareEJ(): Promise { - return { nombreDeResultats: [{ annee: 2020, total: 0 }], resultat: [], moyennes: [] }; + return { nombreDeResultats: 0, resultat: [], moyennes: [] }; } - private async compareSMS(numerosFiness: string[], page: number, order: string, orderBy: string): Promise { - const compareSMSQueryBody = ` From - (select SUM(public.autorisation_medico_social.capacite_installee_totale) as capacite_total, - public.autorisation_medico_social.numero_finess_etablissement_territorial as numero_finess - FROM public.autorisation_medico_social - where public.autorisation_medico_social.numero_finess_etablissement_territorial - IN(${numerosFiness.map((finess) => "'" + finess + "'")}) - GROUP BY public.autorisation_medico_social.numero_finess_etablissement_territorial - ) capacites - FULL JOIN - (SELECT etablissement.numero_finess_etablissement_territorial as numero_finess, - etablissement.raison_sociale as raison_sociale_courte, - etablissement.domaine as structure, - activite.taux_realisation_activite, - activite.file_active_personnes_accompagnees, - activite.taux_occupation_en_hebergement_permanent, - activite.taux_occupation_en_hebergement_temporaire, - activite.taux_occupation_accueil_de_jour, - budget.taux_de_caf, - budget.taux_de_vetuste_construction, - budget.fonds_de_roulement, - budget.resultat_net_comptable, - rh.taux_prestation_externes, - rh.taux_rotation_personnel, - rh.taux_etp_vacants, - rh.taux_absenteisme_hors_formation, - COALESCE(activite.annee, budget.annee, rh.annee) as annee - FROM ressources_humaines_medico_social rh - FULL JOIN - budget_et_finances_medico_social budget - ON rh.numero_finess_etablissement_territorial = budget.numero_finess_etablissement_territorial - AND rh.annee = budget.annee - FULL JOIN - activite_medico_social activite - ON activite.numero_finess_etablissement_territorial = COALESCE(budget.numero_finess_etablissement_territorial, rh.numero_finess_etablissement_territorial) - AND activite.annee = COALESCE(budget.annee, rh.annee) - left JOIN - etablissement_territorial etablissement - ON etablissement.numero_finess_etablissement_territorial = COALESCE(activite.numero_finess_etablissement_territorial, budget.numero_finess_etablissement_territorial, rh.numero_finess_etablissement_territorial) - where etablissement.numero_finess_etablissement_territorial IN(${numerosFiness.map((finess) => "'" + finess + "'")})) annual - on capacites.numero_finess = annual.numero_finess `; - - const compareSMSQuery = - `select COALESCE(capacites.numero_finess, annual.numero_finess) as numero_finess, - annual.annee, - annual.raison_sociale_courte, - annual.structure, - annual.taux_realisation_activite, - annual.file_active_personnes_accompagnees, - annual.taux_occupation_en_hebergement_permanent, - annual.taux_occupation_en_hebergement_temporaire, - annual.taux_occupation_accueil_de_jour, - annual.taux_de_caf, - annual.taux_de_vetuste_construction, - annual.fonds_de_roulement, - annual.resultat_net_comptable, - annual.taux_prestation_externes, - annual.taux_rotation_personnel, - annual.taux_etp_vacants, - annual.taux_absenteisme_hors_formation, - capacites.capacite_total ` + compareSMSQueryBody; + private async chargeLaDateDeMiseÀJourModel(source: FichierSource): Promise { + return (await (await this.orm).getRepository(DateMiseÀJourFichierSourceModel).findOneBy({ fichier: source })) as DateMiseÀJourFichierSourceModel; + } + + private async compareSMS(numerosFiness: string[], page: number, order: string, orderBy: string, annee: string, forExport: boolean): Promise { + const compareSMSCapacite = `(select SUM(public.autorisation_medico_social.capacite_installee_totale) as capacite_total, + autorisation_medico_social.numero_finess_etablissement_territorial + FROM autorisation_medico_social + GROUP BY autorisation_medico_social.numero_finess_etablissement_territorial) cp`; + + const compareSMSQueryBody = ` from etablissement_territorial et + LEFT JOIN activite_medico_social ac + on et.numero_finess_etablissement_territorial = ac.numero_finess_etablissement_territorial and ac.annee = ${annee} + LEFT JOIN budget_et_finances_medico_social bg + on et.numero_finess_etablissement_territorial = bg.numero_finess_etablissement_territorial and bg.annee = ${annee} + LEFT JOIN ressources_humaines_medico_social rh + on et.numero_finess_etablissement_territorial = rh.numero_finess_etablissement_territorial and rh.annee = ${annee} + LEFT JOIN ${compareSMSCapacite} + on et.numero_finess_etablissement_territorial = cp.numero_finess_etablissement_territorial + where et.numero_finess_etablissement_territorial IN(${numerosFiness.map((finess) => "'" + finess + "'")})`; + + const compareSMSQuery = `Select et.numero_finess_etablissement_territorial, + et.raison_sociale_courte, + et.domaine, + et.commune, + et.departement, + ac.taux_realisation_activite, + ac.file_active_personnes_accompagnees, + ac.taux_occupation_en_hebergement_permanent, + ac.taux_occupation_en_hebergement_temporaire, + ac.taux_occupation_accueil_de_jour, + bg.taux_de_caf, + bg.taux_de_vetuste_construction, + bg.fonds_de_roulement, + bg.resultat_net_comptable, + rh.taux_prestation_externes, + rh.taux_rotation_personnel, + rh.taux_etp_vacants, + rh.taux_absenteisme_hors_formation, + cp.capacite_total` + compareSMSQueryBody; const paginatedCompareSMSQuery = order && orderBy ? compareSMSQuery + - `ORDER BY ${orderBy} ${order} LIMIT ${this.NOMBRE_DE_RÉSULTATS_MAX_PAR_PAGE} OFFSET ${this.NOMBRE_DE_RÉSULTATS_MAX_PAR_PAGE * (page - 1)} ` + `ORDER BY ${orderBy} ${order} ${forExport ? "" : `LIMIT ${this.NOMBRE_DE_RÉSULTATS_MAX_PAR_PAGE} OFFSET ${this.NOMBRE_DE_RÉSULTATS_MAX_PAR_PAGE * (page - 1)}`} ` : compareSMSQuery + - `ORDER BY numero_finess ASC LIMIT ${this.NOMBRE_DE_RÉSULTATS_MAX_PAR_PAGE} OFFSET ${this.NOMBRE_DE_RÉSULTATS_MAX_PAR_PAGE * (page - 1)} `; - - const averagesCompareSMSQuery = - ` - select - annual.annee, - AVG(annual.taux_realisation_activite) as realisationAcitiviteMoyenne, - AVG(annual.file_active_personnes_accompagnees) as fileActivePersonnesAccompagnesMoyenne, - AVG(annual.taux_occupation_en_hebergement_permanent) as hebergementPermanentMoyenne, - AVG(annual.taux_occupation_en_hebergement_temporaire) as hebergementTemporaireMoyenne , - AVG(annual.taux_occupation_accueil_de_jour) as acceuilDeJourMoyenne, - AVG(annual.taux_de_caf) as tauxCafMoyenne, - AVG(annual.taux_de_vetuste_construction) as vetusteConstructionMoyenne, - AVG(annual.fonds_de_roulement) as roulementNetGlobalMoyenne, - AVG(annual.resultat_net_comptable) as resultatNetComptableMoyenne, - AVG(annual.taux_prestation_externes) as prestationExterneMoyenne, - AVG(annual.taux_rotation_personnel) as rotationPersonnelMoyenne, - AVG(annual.taux_etp_vacants) as etpVacantMoyenne, - AVG(annual.taux_absenteisme_hors_formation) as absenteismeMoyenne, - AVG(capacites.capacite_total) as capaciteMoyenne + `ORDER BY numero_finess_etablissement_territorial ASC ${forExport ? "" : `LIMIT ${this.NOMBRE_DE_RÉSULTATS_MAX_PAR_PAGE} OFFSET ${this.NOMBRE_DE_RÉSULTATS_MAX_PAR_PAGE * (page - 1)}`} `; + + const averagesCompareSMSQuery = `select + AVG(ROUND(ac.taux_realisation_activite::NUMERIC , 3)) as realisationAcitiviteMoyenne, + AVG(ac.file_active_personnes_accompagnees) as fileActivePersonnesAccompagnesMoyenne, + AVG(ROUND(ac.taux_occupation_en_hebergement_permanent::NUMERIC , 3)) as hebergementPermanentMoyenne, + AVG(ROUND(ac.taux_occupation_en_hebergement_temporaire::NUMERIC , 3)) as hebergementTemporaireMoyenne , + AVG(ROUND(ac.taux_occupation_accueil_de_jour::NUMERIC , 3)) as acceuilDeJourMoyenne, + AVG(ROUND(bg.taux_de_caf::NUMERIC , 3)) as tauxCafMoyenne, + AVG(ROUND(bg.taux_de_vetuste_construction::NUMERIC , 3)) as vetusteConstructionMoyenne, + AVG(ROUND(bg.fonds_de_roulement::NUMERIC , 2)) as roulementNetGlobalMoyenne, + AVG(ROUND(bg.resultat_net_comptable::NUMERIC , 2)) as resultatNetComptableMoyenne, + AVG(ROUND(rh.taux_prestation_externes::NUMERIC , 3)) as prestationExterneMoyenne, + AVG(ROUND(rh.taux_rotation_personnel::NUMERIC , 3)) as rotationPersonnelMoyenne, + AVG(ROUND(rh.taux_etp_vacants::NUMERIC , 3)) as etpVacantMoyenne, + AVG(ROUND(rh.taux_absenteisme_hors_formation::NUMERIC , 3)) as absenteismeMoyenne, + AVG(cp.capacite_total) as capaciteMoyenne ` + - compareSMSQueryBody + - " GROUP BY annee"; + compareSMSQueryBody; - const countCompareSMSQuery = ` - SELECT COUNT(*) AS total, annee - FROM(${compareSMSQuery}) AS subquery - GROUP BY annee - `; const compareSMSQueryResult = await (await this.orm).query(paginatedCompareSMSQuery); const moyennesCompareSMSQueryResult = await (await this.orm).query(averagesCompareSMSQuery); - const nombreDeResultats = await (await this.orm).query(countCompareSMSQuery); return { - nombreDeResultats: nombreDeResultats, + nombreDeResultats: numerosFiness.length, resultat: this.contruitResultatSMS(compareSMSQueryResult), - moyennes: this.contruitMoyennesSMS(moyennesCompareSMSQueryResult), + moyennes: this.contruitMoyennesSMS(moyennesCompareSMSQueryResult[0]), }; } private async compareSAN(): Promise { - return { nombreDeResultats: [{ annee: 2020, total: 0 }], resultat: [], moyennes: [] }; + return { nombreDeResultats: 0, resultat: [], moyennes: [] }; } - private contruitMoyennesSMS(moyennes: any[]): MoyenneSMS[] { - return moyennes.map((moyenne: any): MoyenneSMS => { - return { - annee: moyenne.annee, - capaciteMoyenne: this.makeNumberArrondi(moyenne.capacitemoyenne, 1), - realisationAcitiviteMoyenne: moyenne.realisationacitivitemoyenne, - acceuilDeJourMoyenne: moyenne.realisationacitivitemoyenne, - hebergementPermanentMoyenne: moyenne.hebergementpermanentmoyenne, - hebergementTemporaireMoyenne: moyenne.hebergementtemporairemoyenne, - fileActivePersonnesAccompagnesMoyenne: moyenne.fileactivepersonnesaccompagnesmoyenne, - rotationPersonnelMoyenne: moyenne.rotationpersonnelmoyenne, - absenteismeMoyenne: moyenne.absenteismemoyenne, - prestationExterneMoyenne: moyenne.prestationexternemoyenne, - etpVacantMoyenne: moyenne.etpvacantmoyenne, - tauxCafMoyenne: moyenne.tauxcafmoyenne, - vetusteConstructionMoyenne: moyenne.vetusteconstructionmoyenne, - roulementNetGlobalMoyenne: moyenne.roulementnetglobalmoyenne, - resultatNetComptableMoyenne: moyenne.resultatnetcomptablemoyenne, - }; - }); + private contruitMoyennesSMS(moyenne: any): MoyenneSMS { + return { + capaciteMoyenne: moyenne.capacitemoyenne ? this.makeNumberArrondi(Number(moyenne.capacitemoyenne), 2) : null, + realisationAcitiviteMoyenne: moyenne.realisationacitivitemoyenne !== null ? this.transformInRate(moyenne.realisationacitivitemoyenne, 1) : null, + acceuilDeJourMoyenne: moyenne.acceuildejourmoyenne !== null ? this.transformInRate(moyenne.acceuildejourmoyenne, 1) : null, + hebergementPermanentMoyenne: moyenne.hebergementpermanentmoyenne !== null ? this.transformInRate(moyenne.hebergementpermanentmoyenne, 1) : null, + hebergementTemporaireMoyenne: moyenne.hebergementtemporairemoyenne !== null ? this.transformInRate(moyenne.hebergementtemporairemoyenne, 1) : null, + fileActivePersonnesAccompagnesMoyenne: moyenne.fileactivepersonnesaccompagnesmoyenne, + rotationPersonnelMoyenne: moyenne.rotationpersonnelmoyenne !== null ? this.transformInRate(moyenne.rotationpersonnelmoyenne, 1) : null, + absenteismeMoyenne: moyenne.absenteismemoyenne !== null ? this.transformInRate(moyenne.absenteismemoyenne, 1) : null, + prestationExterneMoyenne: moyenne.prestationexternemoyenne !== null ? this.transformInRate(moyenne.prestationexternemoyenne, 1) : null, + etpVacantMoyenne: moyenne.etpvacantmoyenne !== null ? this.transformInRate(moyenne.etpvacantmoyenne, 1) : null, + tauxCafMoyenne: moyenne.tauxcafmoyenne !== null ? this.transformInRate(moyenne.tauxcafmoyenne, 1) : null, + vetusteConstructionMoyenne: moyenne.vetusteconstructionmoyenne !== null ? this.transformInRate(moyenne.vetusteconstructionmoyenne, 1) : null, + roulementNetGlobalMoyenne: moyenne.roulementnetglobalmoyenne !== null ? this.makeNumberArrondi(moyenne.roulementnetglobalmoyenne, 0) : null, + resultatNetComptableMoyenne: moyenne.resultatnetcomptablemoyenne !== null ? this.makeNumberArrondi(moyenne.resultatnetcomptablemoyenne, 0) : null + }; } private makeNumberArrondi(value: any, num: number): number | null { // Convert value to a number and check if it's a valid number - const numericValue = value ? Number(value) : null; + const numericValue = value !== null ? Number(value) : null; - if (numericValue && !isNaN(numericValue)) { + if (numericValue !== null && !isNaN(numericValue)) { // If numericValue is a valid number, return the rounded number return Number(numericValue.toFixed(num)); } else { @@ -196,22 +212,23 @@ export class TypeOrmComparaisonLoader implements ComparaisonLoader { } private transformInRate(number: number, chiffre: number): number | null { - return number ? this.makeNumberArrondi(number * 100, chiffre) : number; + return number !== null ? this.makeNumberArrondi(number * 100, chiffre) : number; } private contruitResultatSMS(resultats: ComparaisonSMSTypeOrm[]): ResultatSMS[] { return resultats.map((resultat: ComparaisonSMSTypeOrm): ResultatSMS => { return { - annee: resultat.annee, - numéroFiness: resultat.numero_finess, + numéroFiness: resultat.numero_finess_etablissement_territorial, socialReason: resultat.raison_sociale_courte, - type: resultat.structure, + type: resultat.domaine, + commune: resultat.commune, + departement: resultat.departement, capacite: resultat.capacite_total, realisationActivite: this.transformInRate(resultat.taux_realisation_activite, 1), acceuilDeJour: this.transformInRate(resultat.taux_occupation_accueil_de_jour, 1), hebergementPermanent: this.transformInRate(resultat.taux_occupation_en_hebergement_permanent, 1), hebergementTemporaire: this.transformInRate(resultat.taux_occupation_en_hebergement_temporaire, 1), - fileActivePersonnesAccompagnes: this.transformInRate(resultat.file_active_personnes_accompagnees, 1), + fileActivePersonnesAccompagnes: resultat.file_active_personnes_accompagnees, rotationPersonnel: this.transformInRate(resultat.taux_rotation_personnel, 1), absenteisme: this.transformInRate(resultat.taux_absenteisme_hors_formation, 1), prestationExterne: this.transformInRate(resultat.taux_prestation_externes, 1), diff --git "a/src/backend/m\303\251tier/entities/ResultatDeComparaison.ts" "b/src/backend/m\303\251tier/entities/ResultatDeComparaison.ts" index 26a4317c5..eed0e51fe 100644 --- "a/src/backend/m\303\251tier/entities/ResultatDeComparaison.ts" +++ "b/src/backend/m\303\251tier/entities/ResultatDeComparaison.ts" @@ -7,9 +7,10 @@ export type ResultatEJ = Readonly<{ }>; export type ResultatSMS = Readonly<{ - annee: number; numéroFiness: string; socialReason: string; + commune: string; + departement: string; type: string; capacite: number | null; realisationActivite: number | null; @@ -37,7 +38,6 @@ export type ResultatES = Readonly<{ export type MoyenneEJ = Readonly<{}>; export type MoyenneSMS = Readonly<{ - annee: number; capaciteMoyenne: number | null; realisationAcitiviteMoyenne: number | null; acceuilDeJourMoyenne: number | null; @@ -56,7 +56,13 @@ export type MoyenneSMS = Readonly<{ export type MoyenneES = Readonly<{}>; export type ResultatDeComparaison = { - nombreDeResultats: [{ annee: number; total: number }]; + nombreDeResultats: number; resultat: ResultatEJ[] | ResultatSMS[] | ResultatES[]; - moyennes: MoyenneEJ[] | MoyenneSMS[] | MoyenneES[]; + moyennes: MoyenneEJ | MoyenneSMS | MoyenneES; +}; + +export type DatesMisAjourSources = { + date_mis_a_jour_finess: string; + date_mis_a_jour_tdbPerf: string; + date_mis_a_jour_cnsa: string; }; diff --git "a/src/backend/m\303\251tier/gateways/ComparaisonLoader.ts" "b/src/backend/m\303\251tier/gateways/ComparaisonLoader.ts" index 82a0f6ea6..94411a655 100644 --- "a/src/backend/m\303\251tier/gateways/ComparaisonLoader.ts" +++ "b/src/backend/m\303\251tier/gateways/ComparaisonLoader.ts" @@ -1,5 +1,7 @@ -import { ResultatDeComparaison } from "../entities/ResultatDeComparaison"; +import { DatesMisAjourSources, ResultatDeComparaison } from "../entities/ResultatDeComparaison"; export interface ComparaisonLoader { - compare(type: string, numerosFiness: string[], page: number, orderBy: string, order: string): Promise; + listeAnnees(type: string, numerosFiness: string[]): Promise; + getDatesMisAJourSourcesComparaison(): Promise; + compare(type: string, numerosFiness: string[], annee: string, page: number, orderBy: string, order: string, forExport: boolean): Promise; } diff --git "a/src/backend/m\303\251tier/use-cases/ComparaisonEtablissementsUseCase.ts" "b/src/backend/m\303\251tier/use-cases/ComparaisonEtablissementsUseCase.ts" index 023de41f0..b2a7342eb 100644 --- "a/src/backend/m\303\251tier/use-cases/ComparaisonEtablissementsUseCase.ts" +++ "b/src/backend/m\303\251tier/use-cases/ComparaisonEtablissementsUseCase.ts" @@ -1,4 +1,4 @@ -import { ResultatDeComparaison } from "../entities/ResultatDeComparaison"; +import { DatesMisAjourSources, ResultatDeComparaison } from "../entities/ResultatDeComparaison"; import { ComparaisonLoader } from "../gateways/ComparaisonLoader"; export class ComparaisonEtablissementsUseCase { @@ -7,10 +7,24 @@ export class ComparaisonEtablissementsUseCase { async exécute( type: string, numerosFiness: string[], + annee: string, page: number, order: string, - orderBy: string + orderBy: string, + forExport: boolean ): Promise { - return await this.comparaisonLoader.compare(type, numerosFiness, page, order, orderBy); + return await this.comparaisonLoader.compare(type, numerosFiness, annee, page, order, orderBy, forExport); } + + async getAnneesComparaison( + type: string, + numerosFiness: string[], + ): Promise { + return await this.comparaisonLoader.listeAnnees(type, numerosFiness); + } + + async getDatesMisAJourSourcesComparaison(): Promise { + return await this.comparaisonLoader.getDatesMisAJourSourcesComparaison(); + } + } diff --git a/src/frontend/configuration/wording/Wording.tsx b/src/frontend/configuration/wording/Wording.tsx index 2f5996450..a20f94a5d 100644 --- a/src/frontend/configuration/wording/Wording.tsx +++ b/src/frontend/configuration/wording/Wording.tsx @@ -423,6 +423,8 @@ export interface Wording { // Comparaison readonly COMPARAISON: string; readonly AJOUTER_DES_ETABLISSEMENTS: string; + readonly TITRE_AJOUTER_DES_ETABLISSEMENTS: string; + readonly LIBELLE_AJOUTER_DES_ETABLISSEMENTS: string; // Favoris readonly FAVORIS_LIST: string; diff --git a/src/frontend/configuration/wording/WordingFr.tsx b/src/frontend/configuration/wording/WordingFr.tsx index f92613957..15fffdabb 100644 --- a/src/frontend/configuration/wording/WordingFr.tsx +++ b/src/frontend/configuration/wording/WordingFr.tsx @@ -604,4 +604,6 @@ export class WordingFr implements Wording { readonly ALERTE_TYPE_DIFFERENT_TITRE: string = "Message d'information"; readonly ALERTE_TYPE_DIFFERENT_CORPS: string = "Pour réaliser une comparaison, vous devez sélectionner des établissements de structure identique (Entité juridique, Etablissement sanitaire ou Etablissement médico-social)"; + readonly TITRE_AJOUTER_DES_ETABLISSEMENTS: string = "Ajouter un ou plusieurs établissements"; + readonly LIBELLE_AJOUTER_DES_ETABLISSEMENTS: string = "A partir d'une recherche"; } diff --git a/src/frontend/ui/commun/Table/Table.tsx b/src/frontend/ui/commun/Table/Table.tsx index 9bb0201e9..32451e939 100644 --- a/src/frontend/ui/commun/Table/Table.tsx +++ b/src/frontend/ui/commun/Table/Table.tsx @@ -16,13 +16,15 @@ interface Header { key: string; isButton?: boolean; sort?: boolean; + info?: boolean; orderBy?: string; } interface DataTableProps { headers: Header[]; data: RechercheViewModel[] | ComparaisonViewModel[]; - forMoyenne: MoyenneResultatComparaison; + forMoyenne?: MoyenneResultatComparaison; + total?: number; onButtonClick?: (rowIndex: number, colIndex: number) => void; selectedRows: SelectedRows; setSelectedRows: Dispatch>>; @@ -31,6 +33,7 @@ interface DataTableProps { setOrder: (order: string) => void; setOrderBy: (orderBy: string) => void; isShowAvrage: boolean; + isCenter: boolean; onClickInfobull?: (name: string) => void; page: number; handleSelectAll: () => void; @@ -48,6 +51,7 @@ interface TableHeaderProps { onClickInfobull?: (name: string) => void; handleSelectAll: () => void; isAllSelected: boolean; + isCenter: boolean; page: number; } @@ -55,9 +59,11 @@ interface TableBodyProps { headers: Header[]; selectedRows: SelectedRows; data: RechercheViewModel[] | ComparaisonViewModel[]; - forMoyenne: MoyenneResultatComparaison; + forMoyenne?: MoyenneResultatComparaison; + total?: number; handleSelectRow: (valeurs: any) => void; isShowAvrage: boolean; + isCenter: boolean; page: number; onClickDelete: (finessNumber: string) => void; handleInfoBullMoyenne?: Dispatch>; @@ -124,7 +130,7 @@ const construisLeLien = (type: string, finess: string): string => { return "/entite-juridique/" + finess; }; -const TableHeader = ({ headers, order, orderBy, setOrderBy, setOrder, onClickInfobull, handleSelectAll, isAllSelected }: TableHeaderProps) => { +const TableHeader = ({ headers, order, orderBy, setOrderBy, setOrder, onClickInfobull, handleSelectAll, isAllSelected, isCenter }: TableHeaderProps) => { return ( @@ -137,30 +143,23 @@ const TableHeader = ({ headers, order, orderBy, setOrderBy, setOrder, onClickInf {headers.map((header, index) => - header.sort ? ( - - {header.label} - - - ) : ( - - {header.label} - {header.key !== "delete" && header.key !== "favori" && onClickInfobull && ( - +
+

{wording.COMPARAISON}

+ +
+
+ {!isShowAjoutEtab && ( + + )} + {isShowAjoutEtab && } +
Année @@ -157,24 +162,26 @@ export const ComparaisonPage = () => { ) : ( <> {}} - setOrderBy={() => {}} + setOrder={setOrder} + setOrderBy={setOrderBy} setSelectedRows={setSelectedRows} + total={nombreRésultats} /> - {})} /> + { })} /> )} diff --git a/src/frontend/ui/comparaison/ExportExcel.tsx b/src/frontend/ui/comparaison/ExportExcel.tsx new file mode 100644 index 000000000..5deafbefa --- /dev/null +++ b/src/frontend/ui/comparaison/ExportExcel.tsx @@ -0,0 +1,165 @@ + + +import { useContext } from "react"; +import * as XLSX from "xlsx"; + +import { MoyenneSMS, ResultatDeComparaison, ResultatSMS } from "../../../backend/métier/entities/ResultatDeComparaison"; +import { UserContext } from "../commun/contexts/userContext"; +import { RechercheViewModel } from "../home/RechercheViewModel"; + +export function getCurrentDate() { + const today = new Date(); + const year = today.getFullYear(); + const month = String(today.getMonth() + 1).padStart(2, "0"); + const day = String(today.getDate()).padStart(2, "0"); + const currentDate = `${year}${month}${day}`; + return currentDate; +} + +function getType(type: string | undefined) { + if(type === "Médico-social") return "Social et Médico-Social" + else return type; +} + +async function getComparaisonData(annee: string, order = "", orderBy = "") { + const listFiness = sessionStorage.getItem("listFinessNumbers"); + const typeStored = sessionStorage.getItem("comparaisonType"); + + let parsedFiness = null; + try { + parsedFiness = listFiness ? JSON.parse(listFiness) : null; + } catch (e) { + alert("Error :" + e); + } + + const type = typeStored || undefined; + + return fetch("/api/comparaison/compare", { + body: JSON.stringify({ type, numerosFiness: parsedFiness, annee, order, orderBy, forExport: true }), + headers: { "Content-Type": "application/json" }, + method: "POST", + }) + .then((response) => response.json()) + .then((data: ResultatDeComparaison) => { + return ({ resultat: data.resultat, moyenne: data.moyennes, type }) + } + ) +} + +function getFavoris(favoris: RechercheViewModel[] | undefined, numeroFiness: string): string { + const filtredFavoris = favoris?.filter((item) => item.numéroFiness === numeroFiness); + return filtredFavoris && filtredFavoris.length > 0 ? "Oui" : "Non" +} + +function transformData(data: any, favoris: RechercheViewModel[] | undefined) { + return data.resultat.map((etab: ResultatSMS) => [ + etab.type || "-", + getFavoris(favoris, etab.numéroFiness), + etab.socialReason || "-", + etab.numéroFiness || "-", + etab.capacite || "-", + etab.realisationActivite || "-", + etab.fileActivePersonnesAccompagnes || "-", + etab.hebergementPermanent || "-", + etab.hebergementTemporaire || "-", + etab.acceuilDeJour || "-", + etab.prestationExterne || "-", + etab.rotationPersonnel || "-", + etab.etpVacant || "-", + etab.absenteisme || "-", + etab.tauxCaf || "-", + etab.vetusteConstruction || "-", + etab.roulementNetGlobal || "-", + etab.resultatNetComptable || "-" + ]); +} + +function transformMoyenne(moyenne: MoyenneSMS ): (string | number)[] { + return [ + "Moyenne", + "-", + "-", + "-", + moyenne.capaciteMoyenne || "-", + moyenne.realisationAcitiviteMoyenne || "-", + moyenne.fileActivePersonnesAccompagnesMoyenne || "-", + moyenne.hebergementPermanentMoyenne || "-", + moyenne.hebergementTemporaireMoyenne || "-", + moyenne.acceuilDeJourMoyenne || "-", + moyenne.prestationExterneMoyenne || "-", + moyenne.rotationPersonnelMoyenne || "-", + moyenne.etpVacantMoyenne || "-", + moyenne.absenteismeMoyenne || "-", + moyenne.tauxCafMoyenne || "-", + moyenne.vetusteConstructionMoyenne || "-", + moyenne.roulementNetGlobalMoyenne || "-", + moyenne.resultatNetComptableMoyenne || "-" + ] +} + +function ExportToExcel(header: string[], headerType: (string|undefined)[], headers: string[], data: (string | Number)[][], fileName: string, moyenneResultat: (string | Number)[]) { + const ws = XLSX.utils.aoa_to_sheet([header, headerType, [""], headers, moyenneResultat, ...data]); + const wb = XLSX.utils.book_new(); + XLSX.utils.book_append_sheet(wb, ws, "Comparaison"); + XLSX.writeFile(wb, fileName); +} + +async function generateAndExportExcel( + year: string, order: string, orderBy: string, favoris: RechercheViewModel[] | undefined, datesMisAjour: string, +) { + + const fileName: string = `${getCurrentDate()}_Helios_comparaison${year}.xlsx`; + const data = await getComparaisonData(year, order, orderBy) + const dataTransormed = transformData(data, favoris); + const moyenneTransformed = transformMoyenne(data.moyenne as MoyenneSMS) + + const headerYear = ["Année", year]; + + const type = getType(data.type) + const headerType = ["Indicateurs", type]; + + const headers = [ + "Type d'établissement", + "Favoris", + "Raison Sociale", + "FINESS", + `Capacité Totale au ${datesMisAjour}`, + "Taux de réalisation de l'activité (en %)", + "File active des personnes accompagnées sur la période", + "Taux d'occupation en hébergement permanent (en %)", + "Taux d'occupation en hébergement temporaire (en %)", + "Taux d'occupation en accueil de jour (en %)", + "Taux de prestations externes sur les prestations directes (en %)", + "Taux de rotation du personnel sur effectifs réels (en %)", + "Taux d'ETP vacants au 31/12 (en %)", + "Taux d'absentéisme (en %)", + "Taux de CAF (en %)", + "Taux de vétusté des construction (en %)", + "Fond de roulement net global (en €)", + "Résultat net comptable (en €)" + ]; + ExportToExcel(headerYear, headerType, headers, dataTransormed, fileName, moyenneTransformed); +} + +const ExportExcel = ({ + year, order, orderBy, disabled, datesMisAjour +}: { + year: string, order: string, orderBy: string, disabled: boolean, datesMisAjour: string +}) => { + const userContext = useContext(UserContext); + + return ( + + ); +}; + +export default ExportExcel; diff --git a/src/frontend/ui/comparaison/ajout-etablissements/AjoutEtablissements.tsx b/src/frontend/ui/comparaison/ajout-etablissements/AjoutEtablissements.tsx new file mode 100644 index 000000000..59eddc83f --- /dev/null +++ b/src/frontend/ui/comparaison/ajout-etablissements/AjoutEtablissements.tsx @@ -0,0 +1,157 @@ +import { useContext, useEffect, useState } from "react"; + +import { WordingFr } from "../../../configuration/wording/WordingFr"; +import { ComparaisonContext } from "../../commun/contexts/ComparaisonContext"; +import { RechercheViewModel } from "../../home/RechercheViewModel"; +import { RechercheAvanceeFormulaire } from "../../recherche-avancee/RechecheAvanceeFormulaire"; +import styles from "../Comparaison.module.css"; +import { ListEtablissements } from "./ListEtablissements"; +import { useRechercheAvanceeComparaison } from "./useRechercheAvanceeComparaison"; +import type { Dispatch, SetStateAction } from "react"; + +type AjoutEtablissementsProps = { + setIsShowAjoutEtab: Dispatch>; + setReloadTable: Dispatch>; +}; + +export const AjoutEtablissements = ({ setIsShowAjoutEtab, setReloadTable }: AjoutEtablissementsProps) => { + const { lancerLaRecherche, rechercheOnChange, resultats, lastPage, nombreRésultats } = useRechercheAvanceeComparaison(); + const wording = new WordingFr(); + const [listData, setListData] = useState([]); + const [currentPageData, setCurrentPageData] = useState([]); + const [isAtBottom, setIsAtBottom] = useState(false); + const comparaisonContext = useContext(ComparaisonContext); + const [prevPage, setPrevPage] = useState(1); + const [isChangedCapacite, setIsChangedCapacite] = useState(false); + const [isChangedZG, setIsChangedZG] = useState(false); + const [reload, setReload] = useState(false); + const [newEtablissements, setNewEtablissement] = useState([]); + + useEffect(() => { + if (isAtBottom && comparaisonContext) { + if (comparaisonContext.page < lastPage) { + comparaisonContext.setPage(prevPage + 1); + setPrevPage(prevPage + 1); + } + setIsAtBottom(false); + } + if (resultats) { + setCurrentPageData(resultats); + } + }, [resultats, isAtBottom]); + + // update la list des resultats ( ajout des resultats de la nouvelle page à la list ) + useEffect(() => { + if (!arraysAreEqual(currentPageData, listData.slice(-20))) { + const collectData = comparaisonContext?.page === 1 ? currentPageData : [...listData, ...currentPageData]; + setListData(collectData); + } + }, [currentPageData]); + + // lancer la recherche quand la page change + useEffect(() => { + lancerLaRecherche(); + setReload(false); + }, [prevPage, reload]); + + // detect filtre(s) changes to update results + useEffect(() => { + if (isChangedZG || isChangedCapacite || comparaisonContext?.terme) { + comparaisonContext?.setPage(1); + setPrevPage(1); + if (isChangedZG || isChangedCapacite) { + setIsChangedCapacite(false); + setIsChangedZG(false); + setReload(true); + } + } + }, [isChangedZG, isChangedCapacite, comparaisonContext?.terme]); + + // check if lits are equals or not + const arraysAreEqual = (arr1: any[], arr2: any[]): boolean => { + const str1 = JSON.stringify(arr1); + const str2 = JSON.stringify(arr2); + if (str1 !== str2) { + return false; + } + return true; + }; + + // fonction de la fermeture du composent + const onClickFermer = () => { + comparaisonContext?.setCapaciteAgees([]); + comparaisonContext?.setCapaciteHandicap([]); + comparaisonContext?.setCapaciteMedicoSociaux([]); + comparaisonContext?.setZoneGeo(""); + comparaisonContext?.setZoneGeoD(""); + comparaisonContext?.setZoneGeoLabel(""); + comparaisonContext?.setZoneGeoType(""); + setPrevPage(1); + setIsShowAjoutEtab(false); + }; + + const onClickAjouter = () => { + const stringListOfTable = sessionStorage.getItem("listFinessNumbers"); + const arrayListOfTable = stringListOfTable ? JSON.parse(stringListOfTable) : []; + const listToCompare = [...arrayListOfTable, ...newEtablissements]; + sessionStorage.setItem("listFinessNumbers", JSON.stringify(listToCompare)); + document.cookie = `list=${encodeURIComponent(JSON.stringify(listToCompare))}; path=/`; + setReloadTable(true); + onClickFermer(); + }; + + return ( +
+
+
+

+ {wording.TITRE_AJOUTER_DES_ETABLISSEMENTS} +

+ +
+
+ + +
+
+ + {nombreRésultats > 0 && {nombreRésultats} Établissement(s)} +
+
+
+ ); +}; diff --git a/src/frontend/ui/comparaison/ajout-etablissements/ListEtablissements.tsx b/src/frontend/ui/comparaison/ajout-etablissements/ListEtablissements.tsx new file mode 100644 index 000000000..95e9a59b4 --- /dev/null +++ b/src/frontend/ui/comparaison/ajout-etablissements/ListEtablissements.tsx @@ -0,0 +1,94 @@ +import { Dispatch, SetStateAction, useEffect } from "react"; + +import { LogoEntiteJuridiqueSvg } from "../../entité-juridique/bloc-activité/LogoEntitéJuridique"; +import { RechercheViewModel } from "../../home/RechercheViewModel"; +import { LogoEtablissementTerritorialMedicoSociauxSvg } from "../../établissement-territorial-médico-social/logo-établissement-territorial-médico-social"; +import { LogoEtablissementTerritorialSanitaireSvg as LogoÉtablissementTerritorialSanitaire } from "../../établissement-territorial-sanitaire/logo-établissement-territorial-sanitaire"; +import styles from "../Comparaison.module.css"; +import { checkFillSvg } from "../model/data"; + +type listEtablissementsProps = { + resultatRechercheList?: RechercheViewModel[]; + setIsAtBottom: Dispatch>; + newEtablissements: string[]; + setNewEtablissement: Dispatch>; +}; + +export const ListEtablissements = ({ resultatRechercheList, setIsAtBottom, newEtablissements, setNewEtablissement }: listEtablissementsProps) => { + const codeColorOfDisabled = "#808080"; + const codeColorOfBlack = "#3a3a3a"; + const codeColorOfSelected = "#000091"; + + const listFinessFromStorage = sessionStorage.getItem("listFinessNumbers"); + const finessNumbersListFromTable = listFinessFromStorage ? JSON.parse(listFinessFromStorage) : null; + + useEffect(() => { + const scrollableDiv = document.getElementById("list-etablissements-container"); + + if (scrollableDiv) { + const handleScroll = () => { + if (Math.floor(scrollableDiv.scrollHeight - scrollableDiv.scrollTop) === scrollableDiv.clientHeight) { + setIsAtBottom(true); + } else { + setIsAtBottom(false); + } + }; + scrollableDiv.addEventListener("scroll", handleScroll); + + return () => { + scrollableDiv.removeEventListener("scroll", handleScroll); + }; + } + return () => {}; + }, []); + + const onHandleSelectEtablissement = (numFiness: string) => { + if (!finessNumbersListFromTable.includes(numFiness)) { + setNewEtablissement((prevSelected) => { + if (prevSelected.includes(numFiness)) { + return prevSelected.filter((finess) => finess !== numFiness); + } else { + return [...prevSelected, numFiness]; + } + }); + } + }; + + return ( +
+ {resultatRechercheList && ( +
    + {resultatRechercheList.map((res) => ( +
  • +
    onHandleSelectEtablissement(res.numéroFiness)} + onKeyDown={() => {}} + role="button" + style={{ display: "flex", marginTop: "5px", marginBottom: "5px" }} + tabIndex={0} + > + + {res.type === "Sanitaire" && {LogoÉtablissementTerritorialSanitaire(codeColorOfBlack)}} + {res.type === "Médico-social" && ( + {LogoEtablissementTerritorialMedicoSociauxSvg(codeColorOfBlack)} + )} + {res.type === "Entité juridique" && {LogoEntiteJuridiqueSvg(codeColorOfBlack)}} + + + {res.numéroFiness} - {res.socialReason} + {(newEtablissements.includes(res.numéroFiness) || (finessNumbersListFromTable && finessNumbersListFromTable.includes(res.numéroFiness))) && ( +
    + {checkFillSvg(newEtablissements.includes(res.numéroFiness) ? codeColorOfSelected : codeColorOfDisabled)} +
    + )} +
    +
    +
  • + ))} +
+ )} + {!resultatRechercheList && Aucun résultat trouvé.} +
+ ); +}; diff --git a/src/frontend/ui/comparaison/ajout-etablissements/useRechercheAvanceeComparaison.tsx b/src/frontend/ui/comparaison/ajout-etablissements/useRechercheAvanceeComparaison.tsx new file mode 100644 index 000000000..f57b17cee --- /dev/null +++ b/src/frontend/ui/comparaison/ajout-etablissements/useRechercheAvanceeComparaison.tsx @@ -0,0 +1,167 @@ +import { ChangeEvent, useContext, useState } from "react"; + +import { Résultat, RésultatDeRecherche } from "../../../../backend/métier/entities/RésultatDeRecherche"; +import { OrderDir } from "../../../../backend/métier/use-cases/RechercheAvanceeParmiLesEntitésEtÉtablissementsUseCase"; +import { ComparaisonContext } from "../../commun/contexts/ComparaisonContext"; +import { useDependencies } from "../../commun/contexts/useDependencies"; +import { RechercheViewModel } from "../../home/RechercheViewModel"; +import { AttribuesDefaults } from "../../recherche-avancee/model/Attribues"; +import { CapaciteEtablissement } from "../../recherche-avancee/model/CapaciteEtablissement"; + +type RechercheAvanceeState = Readonly<{ + estCeEnAttente: boolean; + estCeQueLeBackendNeRépondPas: boolean; + estCeQueLesRésultatsSontReçus: boolean; + estCeQueLaRechercheEstLancee: boolean; + nombreRésultats: number; + lastPage: number; + résultats: RechercheViewModel[]; +}>; + +export function useRechercheAvanceeComparaison() { + const { paths } = useDependencies(); + const comparaisonContext = useContext(ComparaisonContext); + const pageInitiale = 1; + const statutsJuridiquesDefaultValue: string[] = []; + const take = 20; + // const lastPage = data.nombreDeRésultats > 0 ? Math.ceil(data.nombreDeRésultats / take) : 1; + + const construisLesRésultatsDeLaRecherche = (data: RésultatDeRecherche): RechercheViewModel[] => { + return data.résultats.map((résultat: Résultat) => new RechercheViewModel(résultat, paths)); + }; + const [state, setState] = useState({ + estCeEnAttente: false, + estCeQueLeBackendNeRépondPas: false, + estCeQueLesRésultatsSontReçus: false, + estCeQueLaRechercheEstLancee: false, + nombreRésultats: 0, + lastPage: pageInitiale, + résultats: construisLesRésultatsDeLaRecherche({ nombreDeRésultats: 0, résultats: [] }), + }); + + const lancerLaRecherche = (): void => { + if (lancerRechercheRequisParamValidator()) { + const capacites = [ + { classification: "non_classifie", ranges: comparaisonContext?.capaciteMedicoSociaux || [] }, + { classification: "publics_en_situation_de_handicap", ranges: comparaisonContext?.capaciteHandicap || [] }, + { classification: "personnes_agees", ranges: comparaisonContext?.capaciteAgees || [] }, + ].filter((capacite) => capacite.ranges && capacite.ranges.length > 0); + setState({ + ...state, + estCeEnAttente: true, + estCeQueLesRésultatsSontReçus: false, + estCeQueLaRechercheEstLancee: true, + }); + rechercher( + comparaisonContext?.terme, + comparaisonContext?.zoneGeo, + comparaisonContext?.zoneGeoD, + comparaisonContext?.zoneGeoType, + AttribuesDefaults.etablissementMedicoSocial, + statutsJuridiquesDefaultValue, + capacites, + "", + "ASC", + comparaisonContext?.page + ); + } else { + setState({ + ...state, + nombreRésultats: 0, + résultats: [], + }); + } + }; + + const rechercheOnChange = (event: ChangeEvent) => { + comparaisonContext?.setTerme(event.target.value); + }; + const rechercher = async ( + terme: string | undefined, + zone: string | undefined, + zoneD: string | undefined, + typeZone: string | undefined, + type: string, + statutJuridique: string[], + capaciteSMS: CapaciteEtablissement[] | undefined, + orderBy: string | undefined, + order: OrderDir, + page: number | undefined + ) => { + rechercheParamValidator(terme, zone, zoneD, typeZone, capaciteSMS, orderBy, order, page); + fetch("/api/recherche-avancee", { + body: JSON.stringify({ page, terme, zone, zoneD, typeZone, type, statutJuridique, capaciteSMS }), + headers: { "Content-Type": "application/json" }, + method: "POST", + }) + .then((response) => response.json()) + .then((data) => { + setState({ + ...state, + estCeEnAttente: false, + estCeQueLesRésultatsSontReçus: true, + estCeQueLaRechercheEstLancee: true, + nombreRésultats: data.nombreDeRésultats, + lastPage: Math.ceil(data.nombreDeRésultats / take), + résultats: construisLesRésultatsDeLaRecherche(data), + }); + }) + .catch(() => { + setState({ + ...state, + estCeEnAttente: false, + estCeQueLeBackendNeRépondPas: true, + }); + }); + }; + + const rechercheParamValidator = ( + terme: string | undefined, + zone: string | undefined, + zoneD: string | undefined, + typeZone: string | undefined, + capaciteSMS: CapaciteEtablissement[] | undefined, + orderBy: string | undefined, + order: OrderDir, + page: number | undefined + ) => { + terme ?? ""; + zone ?? ""; + zoneD ?? ""; + typeZone ?? ""; + capaciteSMS ?? []; + page ?? pageInitiale; + orderBy ?? "numéroFiness"; + order ?? "ASC"; + }; + + const lancerRechercheRequisParamValidator = () => { + if ( + comparaisonContext?.terme === "" && + comparaisonContext?.capaciteAgees.length === 0 && + comparaisonContext?.capaciteHandicap.length === 0 && + comparaisonContext?.capaciteMedicoSociaux.length === 0 && + comparaisonContext?.zoneGeo === "" && + comparaisonContext?.zoneGeoD === "" + ) { + return false; + } + + return true; + }; + + return { + estCeEnAttente: state.estCeEnAttente, + estCeQueLeBackendNeRépondPas: state.estCeQueLeBackendNeRépondPas, + estCeQueLesRésultatsSontReçus: state.estCeQueLesRésultatsSontReçus, + estCeQueLaRechercheEstLancee: state.estCeQueLaRechercheEstLancee, + lancerLaRecherche, + rechercheOnChange, + lastPage: state.lastPage, + terme: comparaisonContext?.terme, + setPage: comparaisonContext?.setPage, + page: comparaisonContext?.page, + resultats: state.résultats, + nombreRésultats: state.nombreRésultats, + }; +} diff --git a/src/frontend/ui/comparaison/model/data.tsx b/src/frontend/ui/comparaison/model/data.tsx index 007c74a7a..6d65a93a0 100644 --- a/src/frontend/ui/comparaison/model/data.tsx +++ b/src/frontend/ui/comparaison/model/data.tsx @@ -1,114 +1,3 @@ -import { ReactChild } from "react"; - -import { ContenuTauxDeCaf } from "../../indicateur-métier/taux-de-caf/ContenuTauxDeCaf"; -import { ContenuDesTauxDAbsentéismes } from "../../établissement-territorial-médico-social/InfoBulle/ContenuDesTauxDAbsentéismes"; -import { ContenuDePrestationsExternes } from "../../établissement-territorial-médico-social/InfoBulle/ContenuDuTauxDePrestationsExternes"; -import { ContenuDuTauxDeRotationDuPersonnel } from "../../établissement-territorial-médico-social/InfoBulle/ContenuDuTauxDeRotationDuPersonnel"; -import { ContenuDuTauxDEtpVacants } from "../../établissement-territorial-médico-social/InfoBulle/ContenuDuTauxDEtpVacants"; -import { ContenuDuTauxOccupation } from "../../établissement-territorial-médico-social/InfoBulle/ContenuDuTauxOccupation"; -import { ContenuFileActivePersonnesAccompagnées } from "../../établissement-territorial-médico-social/InfoBulle/ContenuFileActivePersonnesAccompagnées"; -import { ContenuFondDeRoulementNetGlobal } from "../../établissement-territorial-médico-social/InfoBulle/ContenuFondDeRoulementNetGlobal"; -import { ContenuRésultatNetComptable } from "../../établissement-territorial-médico-social/InfoBulle/ContenuRésultatNetComptable"; -import { ContenuTauxDeVétustéConstruction } from "../../établissement-territorial-médico-social/InfoBulle/ContenuTauxDeVétustéConstruction"; -import { ContenuTauxRéalisationActivité } from "../../établissement-territorial-médico-social/InfoBulle/ContenuTauxRéalisationActivité"; - -const tdbPref = TdB Perf; -const cnsa = CNSA; - -export const contenuModal = (name: string): { contenu: any; titre: ReactChild } => { - switch (name) { - case "realisationActivite": - return { - contenu: , - titre: <>Taux d’occupation en accueil de jour, - }; - case "fileActivePersonnesAccompagnes": - return { - contenu: , - titre: <>File active des personnes accompagnées sur la période, - }; - case "hebergementPermanent": - return { - contenu: , - titre: <>Taux d’occupation en hébergement permanent, - }; - case "hebergementTemporaire": - return { - contenu: , - titre: <>Taux d’occupation en hébergement temporaire, - }; - case "acceuilDeJour": - return { - contenu: , - titre: <>Taux d’occupation en accueil de jour, - }; - case "prestationExterne": - return { - contenu: , - titre: <>Taux de prestations externes sur les prestations directes, - }; - case "rotationPersonnel": - return { - contenu: , - titre: <>Taux de rotation du personnel sur effectifs réels, - }; - case "etpVacant": - return { - contenu: , - titre: <>Taux d’ETP vacants au 31/12, - }; - case "absenteisme": - return { - contenu: , - titre: <>Taux d'Absentéiseme, - }; - case "tauxCaf": - return { - contenu: , - titre: <>Taux de CAF, - }; - case "vetusteConstruction": - return { - contenu: , - titre: <>Taux de vétusté construction, - }; - case "resultatNetComptable": - return { - contenu: , - titre: <>Résultat net comptable, - }; - case "roulementNetGlobal": - return { - contenu: , - titre: <>Fond de roulement net global, - }; - default: - return { contenu: "Aucun contenue à afficher pour l'instant", titre: "Bonjour" }; - } -}; - -export const tableHeaders = [ - { label: "", key: "delete" }, - { label: "", key: "etsLogo", sort: true }, - { label: "", key: "favori" }, - { label: "Raison Sociale Courte", key: "socialReason", sort: true }, - { label: "Numéro Finess", key: "numéroFiness", sort: true }, - { label: "Capacité Totale", key: "capacite", sort: true }, - { label: "Réalisation de l'activité", key: "realisationActivite" }, - { label: "Activité personnes accompagnées", key: "fileActivePersonnesAccompagnes" }, - { label: "HP", key: "hebergementPermanent" }, - { label: "HT", key: "hebergementTemporaire" }, - { label: "AJ", key: "acceuilDeJour" }, - { label: "Prestations externes vs directes", key: "prestationExterne" }, - { label: "Rotation du personnel", key: "rotationPersonnel" }, - { label: "ETP vacants", key: "etpVacant" }, - { label: "Absentéiseme", key: "absenteisme" }, - { label: "CAF", key: "tauxCaf" }, - { label: "Vétusté", key: "vetusteConstruction" }, - { label: "Fond net global", key: "roulementNetGlobal" }, - { label: "Resultat net comptable", key: "resultatNetComptable" }, -]; - export const moyenneInitialValues = { capaciteMoyenne: 0, realisationAcitiviteMoyenne: 0, @@ -125,3 +14,11 @@ export const moyenneInitialValues = { roulementNetGlobalMoyenne: 0, resultatNetComptableMoyenne: 0, }; + +export const checkFillSvg = (color: string) => { + return ( + + + + ); +}; diff --git a/src/frontend/ui/comparaison/useComparaison.ts b/src/frontend/ui/comparaison/useComparaison.ts deleted file mode 100644 index b1e144a88..000000000 --- a/src/frontend/ui/comparaison/useComparaison.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { useState } from "react"; - -import { ApiComparaisonResultat, ComparaisonMoyenneViewModel, ComparaisonViewModel, MoyenneResultatComparaison } from "../home/ComparaisonViewModel"; - -type comparaisonState = Readonly<{ - nombreRésultats: number; - lastPage: number; - résultats: ComparaisonViewModel[]; - moyenne: MoyenneResultatComparaison[]; -}>; - -export function useComparaison() { - const take = 20; - const [state, setState] = useState({ - nombreRésultats: 0, - lastPage: 1, - résultats: [], - moyenne: [], - }); - - const pageInitiale = 1; - // const lastPage = data.nombreDeRésultats > 0 ? Math.ceil(data.nombreDeRésultats / take) : 1; - - const lancerLaComparaison = (): void => { - const listFiness = sessionStorage.getItem("listFinessNumbers"); - const typeStored = sessionStorage.getItem("comparaisonType"); - - let parsedFiness = null; - try { - parsedFiness = listFiness ? JSON.parse(listFiness) : null; - } catch (e) { - alert("Error :" + e); - } - - const type = typeStored || undefined; - comparer(type, parsedFiness, pageInitiale); - }; - - const construisLesRésultatsDeLaComparaison = (data: ApiComparaisonResultat): ComparaisonViewModel[] => { - return data.resultat.map((resultat) => new ComparaisonViewModel(resultat)); - }; - - const construisLaMoyenneDesResultat = (data: ApiComparaisonResultat): MoyenneResultatComparaison[] => { - return data.moyennes.map((resultat) => new ComparaisonMoyenneViewModel(resultat)); - }; - - const comparer = async (type: string = "", numerosFiness: string[] = [], page: number = 1, order = "", orderBy = "") => { - // rechercheAvanceeContext?.setPage(page, true); - fetch("/api/comparaison", { - body: JSON.stringify({ type, numerosFiness, page, order, orderBy }), - headers: { "Content-Type": "application/json" }, - method: "POST", - }) - .then((response) => response.json()) - .then((data) => { - setState({ - ...state, - lastPage: Math.ceil(data.resultat.length / take), - résultats: construisLesRésultatsDeLaComparaison(data), - moyenne: construisLaMoyenneDesResultat(data), - }); - }) - .catch(() => {}); - }; - - return { - lancerLaComparaison, - resultats: state.résultats, - moyenne: state.moyenne, - lastPage: state.lastPage, - }; -} diff --git a/src/frontend/ui/comparaison/useComparaison.tsx b/src/frontend/ui/comparaison/useComparaison.tsx new file mode 100644 index 000000000..c00afc2b4 --- /dev/null +++ b/src/frontend/ui/comparaison/useComparaison.tsx @@ -0,0 +1,187 @@ +import { ReactChild, useState } from "react"; + +import { DatesMisAjourSources } from "../../../backend/métier/entities/ResultatDeComparaison"; +import { useDependencies } from "../commun/contexts/useDependencies"; +import { StringFormater } from "../commun/StringFormater"; +import { ApiComparaisonResultat, ComparaisonViewModel, MoyenneResultatComparaison } from "../home/ComparaisonViewModel"; +import { ContenuTauxDeCaf } from "../indicateur-métier/taux-de-caf/ContenuTauxDeCaf"; +import { ContenuCapacitéParActivité } from "../établissement-territorial-médico-social/InfoBulle/ContenuCapacitéParActivité"; +import { ContenuDesTauxDAbsentéismes } from "../établissement-territorial-médico-social/InfoBulle/ContenuDesTauxDAbsentéismes"; +import { ContenuDePrestationsExternes } from "../établissement-territorial-médico-social/InfoBulle/ContenuDuTauxDePrestationsExternes"; +import { ContenuDuTauxDeRotationDuPersonnel } from "../établissement-territorial-médico-social/InfoBulle/ContenuDuTauxDeRotationDuPersonnel"; +import { ContenuDuTauxDEtpVacants } from "../établissement-territorial-médico-social/InfoBulle/ContenuDuTauxDEtpVacants"; +import { ContenuDuTauxOccupation } from "../établissement-territorial-médico-social/InfoBulle/ContenuDuTauxOccupation"; +import { ContenuFileActivePersonnesAccompagnées } from "../établissement-territorial-médico-social/InfoBulle/ContenuFileActivePersonnesAccompagnées"; +import { ContenuFondDeRoulementNetGlobal } from "../établissement-territorial-médico-social/InfoBulle/ContenuFondDeRoulementNetGlobal"; +import { ContenuRésultatNetComptable } from "../établissement-territorial-médico-social/InfoBulle/ContenuRésultatNetComptable"; +import { ContenuTauxDeVétustéConstruction } from "../établissement-territorial-médico-social/InfoBulle/ContenuTauxDeVétustéConstruction"; +import { ContenuTauxRéalisationActivité } from "../établissement-territorial-médico-social/InfoBulle/ContenuTauxRéalisationActivité"; + + +type comparaisonState = Readonly<{ + dateDeMiseAJourCapacite: string; + nombreRésultats: number; + lastPage: number; + résultats: ComparaisonViewModel[]; + moyenne: MoyenneResultatComparaison; + loading: boolean; +}>; + +export function useComparaison() { + const { wording } = useDependencies(); + + const take = 20; + const [state, setState] = useState({ + dateDeMiseAJourCapacite: '', + nombreRésultats: 0, + lastPage: 1, + loading: false, + résultats: [], + moyenne: { + capaciteMoyenne: 0, + realisationAcitiviteMoyenne: 0, + acceuilDeJourMoyenne: 0, + hebergementPermanentMoyenne: 0, + hebergementTemporaireMoyenne: 0, + fileActivePersonnesAccompagnesMoyenne: 0, + rotationPersonnelMoyenne: 0, + absenteismeMoyenne: 0, + prestationExterneMoyenne: 0, + etpVacantMoyenne: 0, + tauxCafMoyenne: 0, + vetusteConstructionMoyenne: 0, + roulementNetGlobalMoyenne: 0, + resultatNetComptableMoyenne: 0 + }, + }); + + const lancerLaComparaison = (page: number, annee: string, order: string, orderBy: string): void => { + const listFiness = sessionStorage.getItem("listFinessNumbers"); + const typeStored = sessionStorage.getItem("comparaisonType"); + + let parsedFiness = null; + try { + parsedFiness = listFiness ? JSON.parse(listFiness) : null; + } catch (e) { + alert("Error :" + e); + } + + const type = typeStored || undefined; + comparer(type, parsedFiness, annee, page, order, orderBy); + }; + + const construisLesRésultatsDeLaComparaison = (data: ApiComparaisonResultat): ComparaisonViewModel[] => { + return data.resultat.map((resultat) => new ComparaisonViewModel(resultat)); + }; + + const comparer = async (type: string = "", numerosFiness: string[] = [], annee: string, page: number = 1, order = "", orderBy = "") => { + setState({ ...state, loading: true }); + fetch("/api/comparaison/compare", { + body: JSON.stringify({ type, numerosFiness, annee, page, order, orderBy }), + headers: { "Content-Type": "application/json" }, + method: "POST", + }) + .then((response) => response.json()) + .then((data) => { + setState({ + ...state, + dateDeMiseAJourCapacite: data.date_mis_a_jour_capacite, + nombreRésultats: data.nombreDeResultats, + lastPage: Math.ceil(data.nombreDeResultats / take), + résultats: construisLesRésultatsDeLaComparaison(data), + moyenne: data.moyennes, + loading: false + }); + }) + .catch(() => { + setState({ ...state, loading: false }); + }); + }; + + + const contenuModal = (name: string, dates: DatesMisAjourSources): { contenu: any; titre: ReactChild } => { + switch (name) { + case "realisationActivite": + return { contenu: , titre: wording.TAUX_RÉALISATION_ACTIVITÉ } + case "fileActivePersonnesAccompagnes": + return { + contenu: , + titre: wording.FILE_ACTIVE_PERSONNES_ACCOMPAGNÉES, + }; + case "hebergementPermanent": + return { + contenu: , + titre: wording.TAUX_OCCUPATION_HÉBERGEMENT_PERMANENT, + }; + case "hebergementTemporaire": + return { + contenu: , + titre: wording.TAUX_OCCUPATION_HÉBERGEMENT_TEMPORAIRE, + }; + case "acceuilDeJour": + return { + contenu: , + titre: wording.TAUX_OCCUPATION_ACCUEIL_DE_JOUR, + }; + case "prestationExterne": + return { + contenu: , + titre: wording.TAUX_DE_PRESTATIONS_EXTERNES_SUR_LES_PRESTATIONS_DIRECTES, + }; + case "rotationPersonnel": + return { + contenu: , + titre: wording.TAUX_DE_ROTATION_DU_PERSONNEL, + }; + case "etpVacant": + return { + contenu: , + titre: wording.TAUX_D_ETP_VACANTS_AU_31_12, + }; + case "absenteisme": + return { + contenu: , + titre: wording.TAUX_D_ABSENTÉISME, + }; + case "tauxCaf": + return { + contenu: , + titre: wording.TAUX_DE_CAF, + }; + case "vetusteConstruction": + return { + contenu: , + titre: wording.TAUX_DE_VÉTUSTÉ_CONSTRUCTION, + }; + case "resultatNetComptable": + return { + contenu: , + titre: wording.RÉSULTAT_NET_COMPTABLE, + }; + case "roulementNetGlobal": + return { + contenu: , + titre: wording.FONDS_DE_ROULEMENT_NET_GLOBAL, + }; + case "capacite": + return { + contenu: , + titre: wording.CAPACITÉ_INSTALLÉE_PAR_ACTIVITÉS, + }; + default: + return { contenu: "Aucun contenue à afficher pour l'instant", titre: "Bonjour" }; + } + } + + return { + lancerLaComparaison, + contenuModal, + dateDeMiseAJourCapacite: state.dateDeMiseAJourCapacite, + nombreRésultats: state.nombreRésultats, + resultats: state.résultats, + moyenne: state.moyenne, + lastPage: state.lastPage, + loading: state.loading, + NombreDeResultatsMaxParPage: take + }; +} diff --git a/src/frontend/ui/home/ComparaisonViewModel.tsx b/src/frontend/ui/home/ComparaisonViewModel.tsx index 3038468af..92f5967e9 100644 --- a/src/frontend/ui/home/ComparaisonViewModel.tsx +++ b/src/frontend/ui/home/ComparaisonViewModel.tsx @@ -1,5 +1,4 @@ export type ResultatComparaison = Readonly<{ - annee: number; numéroFiness: string; socialReason: string; type: string; @@ -17,11 +16,11 @@ export type ResultatComparaison = Readonly<{ vetusteConstruction: number; roulementNetGlobal: number; resultatNetComptable: number; + commune: string; + departement: string; }>; export type MoyenneResultatComparaison = { - nombreEtablissement?: number; - annee: number; capaciteMoyenne: number; realisationAcitiviteMoyenne: number; acceuilDeJourMoyenne: number; @@ -40,16 +39,12 @@ export type MoyenneResultatComparaison = { export type ApiComparaisonResultat = Readonly<{ moyennes: MoyenneResultatComparaison[]; - nombreDeResultats: [{ annee: number; total: string }]; + nombreDeResultats: number; resultat: ResultatComparaison[]; }>; export class ComparaisonViewModel { - constructor(private readonly comparaison: ResultatComparaison) {} - - public get annee(): number { - return this.comparaison.annee; - } + constructor(private readonly comparaison: ResultatComparaison) { } public get numéroFiness(): string { return this.comparaison.numéroFiness; @@ -68,19 +63,19 @@ export class ComparaisonViewModel { } public get realisationActivite(): string | null { - return this.comparaison.realisationActivite ? this.comparaison.realisationActivite + "%" : null; + return this.comparaison.realisationActivite !== null ? this.comparaison.realisationActivite + "%" : null; } public get acceuilDeJour(): string | null { - return this.comparaison.acceuilDeJour ? this.comparaison.acceuilDeJour + "%" : null; + return this.comparaison.acceuilDeJour !== null ? this.comparaison.acceuilDeJour + "%" : null; } public get hebergementPermanent(): string | null { - return this.comparaison.hebergementPermanent ? this.comparaison.hebergementPermanent + "%" : null; + return this.comparaison.hebergementPermanent !== null ? this.comparaison.hebergementPermanent + "%" : null; } public get hebergementTemporaire(): string | null { - return this.comparaison.hebergementTemporaire ? this.comparaison.hebergementTemporaire + "%" : null; + return this.comparaison.hebergementTemporaire !== null ? this.comparaison.hebergementTemporaire + "%" : null; } public get fileActivePersonnesAccompagnes(): number | null { @@ -88,60 +83,61 @@ export class ComparaisonViewModel { } public get rotationPersonnel(): string | null { - return this.comparaison.rotationPersonnel ? this.comparaison.rotationPersonnel + "%" : null; + return this.comparaison.rotationPersonnel !== null ? this.comparaison.rotationPersonnel + "%" : null; } public get absenteisme(): string | null { - return this.comparaison.absenteisme ? this.comparaison.absenteisme + "%" : null; + return this.comparaison.absenteisme !== null ? this.comparaison.absenteisme + "%" : null; } public get prestationExterne(): string | null { - return this.comparaison.prestationExterne ? this.comparaison.prestationExterne + "%" : null; + return this.comparaison.prestationExterne !== null ? this.comparaison.prestationExterne + "%" : null; } public get etpVacant(): string | null { - return this.comparaison.etpVacant ? this.comparaison.etpVacant + "%" : null; + return this.comparaison.etpVacant !== null ? this.comparaison.etpVacant + "%" : null; } public get tauxCaf(): string | null { - return this.comparaison.tauxCaf ? this.comparaison.tauxCaf + "%" : null; + return this.comparaison.tauxCaf !== null ? this.comparaison.tauxCaf + "%" : null; } public get vetusteConstruction(): string | null { - return this.comparaison.vetusteConstruction ? this.comparaison.vetusteConstruction + "%" : null; + return this.comparaison.vetusteConstruction !== null ? this.comparaison.vetusteConstruction + "%" : null; + } + + public get roulementNetGlobal(): string { + return this.comparaison.roulementNetGlobal ? this.comparaison.roulementNetGlobal + .toLocaleString("fr-FR", { + style: "currency", + currency: "EUR", + }) + .split(",")[0] + " €" + : "-"; + } + + public get commune(): string { + return this.comparaison.commune; } - public get roulementNetGlobal(): number { - return this.comparaison.roulementNetGlobal; + public get departement(): string { + return this.comparaison.departement; } public get resultatNetComptable(): string { return this.comparaison.resultatNetComptable ? this.comparaison.resultatNetComptable - .toLocaleString("fr-FR", { - style: "currency", - currency: "EUR", - }) - .split(",")[0] + "€" + .toLocaleString("fr-FR", { + style: "currency", + currency: "EUR", + }) + .split(",")[0] + " €" : "-"; } - - // Méthode pour formater le roulement net global en valeur absolue - public get formatRoulementNetGlobal(): string { - return this.comparaison.roulementNetGlobal.toLocaleString("fr-FR", { - style: "currency", - currency: "EUR", - }); - } } export class ComparaisonMoyenneViewModel { - constructor(private moyenne: MoyenneResultatComparaison) {} - - // Accesseur pour l'année - public get annee(): number { - return this.moyenne.annee; - } + constructor(private moyenne: MoyenneResultatComparaison) { } // Accesseurs pour les autres propriétés public get capaciteMoyenne(): number { @@ -209,21 +205,3 @@ const transformInRate = (number: number, chiffre: number): number => { return makeNumberArrondi(number * 100, chiffre); }; -export const initialData: MoyenneResultatComparaison = { - nombreEtablissement: 0, - annee: 0, - capaciteMoyenne: 0, - realisationAcitiviteMoyenne: 0, - hebergementPermanentMoyenne: 0, - hebergementTemporaireMoyenne: 0, - acceuilDeJourMoyenne: 0, - prestationExterneMoyenne: 0, - rotationPersonnelMoyenne: 0, - etpVacantMoyenne: 0, - absenteismeMoyenne: 0, - tauxCafMoyenne: 0, - vetusteConstructionMoyenne: 0, - resultatNetComptableMoyenne: 0, - fileActivePersonnesAccompagnesMoyenne: 0, - roulementNetGlobalMoyenne: 0, -}; diff --git a/src/frontend/ui/recherche-avancee/FiltreCapacite.tsx b/src/frontend/ui/recherche-avancee/FiltreCapacite.tsx index 41fbc7bdb..6690b5d18 100644 --- a/src/frontend/ui/recherche-avancee/FiltreCapacite.tsx +++ b/src/frontend/ui/recherche-avancee/FiltreCapacite.tsx @@ -1,17 +1,23 @@ import { Dispatch, SetStateAction, useContext, useEffect, useState } from "react"; import { WordingFr } from "../../configuration/wording/WordingFr"; +import { ComparaisonContext } from "../commun/contexts/ComparaisonContext"; import { RechercheAvanceeContext } from "../commun/contexts/RechercheAvanceeContext"; import { CapaciteEtablissement } from "./model/CapaciteEtablissement"; import { classificationTypes } from "./model/ClassificationTypes"; import styles from "./RechercheAvanceeFormulaire.module.css"; import "@gouvfr/dsfr/dist/component/tooltip/tooltip.css"; -export const FiltreCapacite = () => { +type FiltresForComparaisonProps = Readonly<{ + isComparaison: boolean; + setIsChanged: Dispatch> | undefined; +}>; + +export const FiltreCapacite = ({ isComparaison, setIsChanged }: FiltresForComparaisonProps) => { const wording = new WordingFr(); const [showToolip, setShowTooltip] = useState(false); const [showToolip2, setShowTooltip2] = useState(false); - const rechercheAvanceeContext = useContext(RechercheAvanceeContext); + const rechercheAvanceeContext = useContext(isComparaison ? ComparaisonContext : RechercheAvanceeContext); const [capaciteMedicoSociaux, setCapaciteMedicoSociaux] = useState( new CapaciteEtablissement("non_classifie", rechercheAvanceeContext?.capaciteMedicoSociaux || []) ); @@ -133,6 +139,7 @@ export const FiltreCapacite = () => { rechercheAvanceeContext?.setCapaciteMedicoSociaux(capaciteMedicoSociaux.ranges); rechercheAvanceeContext?.setCapaciteHandicap(capaciteHandicap.ranges); rechercheAvanceeContext?.setCapaciteAgees(capaciteAgees.ranges); + if (setIsChanged) setIsChanged(true); } }; @@ -143,6 +150,7 @@ export const FiltreCapacite = () => { rechercheAvanceeContext?.setCapaciteMedicoSociaux([]); rechercheAvanceeContext?.setCapaciteHandicap([]); rechercheAvanceeContext?.setCapaciteAgees([]); + if (setIsChanged) setIsChanged(true); // rechercheAvanceeContext?.setTypeStructure(""); }; @@ -174,7 +182,7 @@ export const FiltreCapacite = () => { {showToolip ? contenuInfoBulle : contenuInfoBulleAgee} -
+
diff --git a/src/frontend/ui/recherche-avancee/FiltreStructure.tsx b/src/frontend/ui/recherche-avancee/FiltreStructure.tsx index f69ec317b..f660a68a1 100644 --- a/src/frontend/ui/recherche-avancee/FiltreStructure.tsx +++ b/src/frontend/ui/recherche-avancee/FiltreStructure.tsx @@ -3,6 +3,7 @@ import { Dispatch, SetStateAction, useContext, useEffect, useRef, useState } fro import { WordingFr } from "../../configuration/wording/WordingFr"; import { Badge } from "../commun/Badge/Badge"; +import { ComparaisonContext } from "../commun/contexts/ComparaisonContext"; import { RechercheAvanceeContext } from "../commun/contexts/RechercheAvanceeContext"; import LogoÉtablissementTerritorialMédicoSocial from "../entité-juridique/liste-des-établissements/logo-établissement-territorial-médico-social-noir.svg"; import LogoÉtablissementTerritorialSanitaire from "../entité-juridique/liste-des-établissements/logo-établissement-territorial-sanitaire-noir.svg"; @@ -10,9 +11,14 @@ import LogoEntitéJuridiqueNoir from "../home/logo-entité-juridique-noir.svg"; import { AttribuesDefaults } from "./model/Attribues"; import styles from "./RechercheAvanceeFormulaire.module.css"; -export const FiltreStructure = () => { +type FiltresForComparaisonProps = Readonly<{ + isComparaison: boolean; + setIsChanged: Dispatch> | undefined; +}>; + +export const FiltreStructure = ({ isComparaison, setIsChanged }: FiltresForComparaisonProps) => { const wording = new WordingFr(); - const rechercheAvanceeContext = useContext(RechercheAvanceeContext); + const rechercheAvanceeContext = useContext(isComparaison ? ComparaisonContext : RechercheAvanceeContext); const [typeSelected, setTypeSelected] = useState(rechercheAvanceeContext?.typeStructure || ""); const [statutJuridiqueSelected, setStatutJuridiqueSelected] = useState(rechercheAvanceeContext?.statutJuridiqueStructure || []); const checkboxElementPublic = useRef(); @@ -24,9 +30,9 @@ export const FiltreStructure = () => { (rechercheAvanceeContext?.capaciteMedicoSociaux && rechercheAvanceeContext?.capaciteMedicoSociaux.length > 0); useEffect(() => { - if (changedCapacite) { + if (changedCapacite || isComparaison) { setTypeSelected(AttribuesDefaults.etablissementMedicoSocial); - rechercheAvanceeContext.setTypeStructure(AttribuesDefaults.etablissementMedicoSocial); + rechercheAvanceeContext?.setTypeStructure(AttribuesDefaults.etablissementMedicoSocial); } }, [rechercheAvanceeContext?.capaciteAgees, rechercheAvanceeContext?.capaciteHandicap, rechercheAvanceeContext?.capaciteMedicoSociaux]); @@ -96,6 +102,7 @@ export const FiltreStructure = () => { emptyStatutJuridiqueCheckboxs(); rechercheAvanceeContext?.setTypeStructure(""); rechercheAvanceeContext?.setStatutJuridiqueStructure([]); + if (setIsChanged) setIsChanged(true); }; function emptyStatutJuridiqueCheckboxs() { @@ -119,6 +126,7 @@ export const FiltreStructure = () => { rechercheAvanceeContext?.setCapaciteHandicap([]); rechercheAvanceeContext?.setCapaciteAgees([]); } + if (setIsChanged) setIsChanged(true); } }; diff --git a/src/frontend/ui/recherche-avancee/FiltreZoneGeographique.tsx b/src/frontend/ui/recherche-avancee/FiltreZoneGeographique.tsx index fe1da9cac..ec8e9293b 100644 --- a/src/frontend/ui/recherche-avancee/FiltreZoneGeographique.tsx +++ b/src/frontend/ui/recherche-avancee/FiltreZoneGeographique.tsx @@ -1,6 +1,7 @@ import { useSession } from "next-auth/react"; -import { ChangeEvent, useContext, useEffect, useState } from "react"; +import { ChangeEvent, Dispatch, SetStateAction, useContext, useEffect, useState } from "react"; +import { ComparaisonContext } from "../commun/contexts/ComparaisonContext"; import { RechercheAvanceeContext } from "../commun/contexts/RechercheAvanceeContext"; import styles from "./RechercheAvanceeFormulaire.module.css"; @@ -17,9 +18,14 @@ type ZoneGeo = Readonly<{ codeNum: string; }>; -export const FiltreZoneGeographique = () => { +type FiltresForComparaisonProps = Readonly<{ + isComparaison: boolean; + setIsChanged: Dispatch> | undefined; +}>; + +export const FiltreZoneGeographique = ({ isComparaison, setIsChanged }: FiltresForComparaisonProps) => { const { data } = useSession(); - const rechercheAvanceeContext = useContext(RechercheAvanceeContext); + const rechercheAvanceeContext = useContext(isComparaison ? ComparaisonContext : RechercheAvanceeContext); const [zoneGeoValue, setZoneGeoValue] = useState(rechercheAvanceeContext?.zoneGeo || ""); const [zoneGeoType, setZoneGeoType] = useState(rechercheAvanceeContext?.zoneGeoType || ""); const [suggestions, setSuggestions] = useState([]); @@ -29,7 +35,7 @@ export const FiltreZoneGeographique = () => { nom: "", departement: { code: "", - nom: "" + nom: "", }, code: "", codeRegion: "", @@ -106,11 +112,11 @@ export const FiltreZoneGeographique = () => { const sortedOptions = data?.user.role === 3 || data?.user.role === 2 ? sortedAlphabetically.sort((a: any, b: any) => { - const estMaRegionA = a.codeRegion === maRegion; - const estMaRegionB = b.codeRegion === maRegion; - if (estMaRegionA === estMaRegionB) return 0; - return estMaRegionA ? -1 : 1; - }) + const estMaRegionA = a.codeRegion === maRegion; + const estMaRegionB = b.codeRegion === maRegion; + if (estMaRegionA === estMaRegionB) return 0; + return estMaRegionA ? -1 : 1; + }) : sortedAlphabetically; setSuggestions(sortedOptions); @@ -160,13 +166,15 @@ export const FiltreZoneGeographique = () => { rechercheAvanceeContext?.setZoneGeoType(""); setSuggestions([]); rechercheAvanceeContext?.setZoneGeoLabel(""); + if (setIsChanged) setIsChanged(true); }; const applyZoneGeoValue = () => { - rechercheAvanceeContext?.setZoneGeoD(zoneGeoType === "C" ? zoneGeoSelected?.departement.nom : ''); + rechercheAvanceeContext?.setZoneGeoD(zoneGeoType === "C" ? zoneGeoSelected?.departement.nom : ""); rechercheAvanceeContext?.setZoneGeo(zoneGeoType === "R" ? zoneGeoSelected?.codeRegion : zoneGeoValue); rechercheAvanceeContext?.setZoneGeoType(zoneGeoType); rechercheAvanceeContext?.setZoneGeoLabel(zoneGeoSelected.codeNum ? `${zoneGeoSelected.nom} (${zoneGeoSelected.codeNum})` : zoneGeoSelected.nom); + if (setIsChanged) setIsChanged(true); }; return ( diff --git a/src/frontend/ui/recherche-avancee/RechecheAvanceeFormulaire.tsx b/src/frontend/ui/recherche-avancee/RechecheAvanceeFormulaire.tsx index 3693851b8..d474cc976 100644 --- a/src/frontend/ui/recherche-avancee/RechecheAvanceeFormulaire.tsx +++ b/src/frontend/ui/recherche-avancee/RechecheAvanceeFormulaire.tsx @@ -1,5 +1,6 @@ -import { ChangeEvent, MouseEvent, useContext, useEffect, useState } from "react"; +import { ChangeEvent, Dispatch, MouseEvent, SetStateAction, useContext, useEffect, useState } from "react"; +import { ComparaisonContext } from "../commun/contexts/ComparaisonContext"; import { RechercheAvanceeContext } from "../commun/contexts/RechercheAvanceeContext"; import { useDependencies } from "../commun/contexts/useDependencies"; import { FiltreCapacite } from "./FiltreCapacite"; @@ -11,11 +12,22 @@ import styles from "./RechercheAvanceeFormulaire.module.css"; type RechercheAvanceeFormulaireProps = Readonly<{ lancerLaRecherche: (event: MouseEvent) => void; rechercheOnChange: (event: ChangeEvent) => void; + isComparaison: boolean; + setIsChangedZG?: Dispatch>; + setIsChangedCapacite?: Dispatch>; + setIsChangedStructure?: Dispatch>; }>; -export const RechercheAvanceeFormulaire = ({ lancerLaRecherche, rechercheOnChange }: RechercheAvanceeFormulaireProps) => { +export const RechercheAvanceeFormulaire = ({ + lancerLaRecherche, + rechercheOnChange, + isComparaison, + setIsChangedZG, + setIsChangedStructure, + setIsChangedCapacite, +}: RechercheAvanceeFormulaireProps) => { const { wording } = useDependencies(); - const rechercheAvanceeContext = useContext(RechercheAvanceeContext); + const rechercheAvanceeContext = useContext(isComparaison ? ComparaisonContext : RechercheAvanceeContext); const [disableCapaciter, setDisableCapaciter] = useState(false); const listTypes = [AttribuesDefaults.entiteJuridque, AttribuesDefaults.etablissementSanitaire]; @@ -123,6 +135,7 @@ export const RechercheAvanceeFormulaire = ({ lancerLaRecherche, rechercheOnChang aria-controls="fr-modal-Structure-Filtre" className="fr-btn fr-btn--icon-right fr-icon-arrow-down-s-fill fr-btn--secondary" data-fr-opened="false" + disabled={isComparaison} > {getWording(wording.STRUCTURE)} @@ -137,9 +150,9 @@ export const RechercheAvanceeFormulaire = ({ lancerLaRecherche, rechercheOnChang
- - - + + +
); diff --git a/src/frontend/ui/recherche-avancee/resultat-recherche-avancee/ResultatRechercheAvancee.tsx b/src/frontend/ui/recherche-avancee/resultat-recherche-avancee/ResultatRechercheAvancee.tsx index 6d782e701..3908c4cd3 100644 --- a/src/frontend/ui/recherche-avancee/resultat-recherche-avancee/ResultatRechercheAvancee.tsx +++ b/src/frontend/ui/recherche-avancee/resultat-recherche-avancee/ResultatRechercheAvancee.tsx @@ -4,7 +4,7 @@ import "@gouvfr/dsfr/dist/component/alert/alert.min.css"; import { WordingFr } from "../../../configuration/wording/WordingFr"; import { RechercheAvanceeContext } from "../../commun/contexts/RechercheAvanceeContext"; import { Table } from "../../commun/Table/Table"; -import { ComparaisonViewModel, initialData } from "../../home/ComparaisonViewModel"; +import { ComparaisonViewModel } from "../../home/ComparaisonViewModel"; import { RechercheViewModel } from "../../home/RechercheViewModel"; import { TableFooterRechercheAvancee } from "./resultat-recherche-avancee-footer/RechercheAvanceeFooter"; import { TableHeaderRechercheAvancee } from "./TableHeaderRechercheAvancee"; @@ -65,21 +65,20 @@ export const ResultatRechercheAvancee = ({ data, nombreRésultats, page, setPage
{}} + onClickDelete={() => { }} order={rechercheAvanceeContext?.order || ""} orderBy={rechercheAvanceeContext?.orderBy || ""} page={page} selectedRows={selectedRows} - setOrder={rechercheAvanceeContext?.setOrder || (() => {})} - setOrderBy={rechercheAvanceeContext?.setOrderBy || (() => {})} - setSelectedRows={setSelectedRows} - /> - {})} /> + setOrder={rechercheAvanceeContext?.setOrder || (() => { })} + setOrderBy={rechercheAvanceeContext?.setOrderBy || (() => { })} + setSelectedRows={setSelectedRows} /> + { })} /> ); }; diff --git a/src/frontend/ui/recherche-avancee/resultat-recherche-avancee/TableHeaderRechercheAvancee.tsx b/src/frontend/ui/recherche-avancee/resultat-recherche-avancee/TableHeaderRechercheAvancee.tsx index c46484f31..fea621891 100644 --- a/src/frontend/ui/recherche-avancee/resultat-recherche-avancee/TableHeaderRechercheAvancee.tsx +++ b/src/frontend/ui/recherche-avancee/resultat-recherche-avancee/TableHeaderRechercheAvancee.tsx @@ -17,7 +17,7 @@ export const TableHeaderRechercheAvancee = ({ selectedRows, setShowAlert }: Tabl }, [selectedRows]) const onClickComparer = () => { - const formattedSelectedRows = Object.values(selectedRows).flat(); + const formattedSelectedRows = Object.values(selectedRows).flat(); const firstType = formattedSelectedRows[0].type; const hasDifferentTypes = formattedSelectedRows.some((row) => row.type !== firstType); const listFinessNumbers = formattedSelectedRows.map((row) => row.numéroFiness); @@ -29,6 +29,8 @@ export const TableHeaderRechercheAvancee = ({ selectedRows, setShowAlert }: Tabl // Navigate if types are the same sessionStorage.setItem("listFinessNumbers", JSON.stringify(listFinessNumbers)); sessionStorage.setItem("comparaisonType", firstType); + document.cookie = `list=${encodeURIComponent(JSON.stringify(listFinessNumbers))}; path=/`; + document.cookie = `type=${encodeURIComponent(firstType)}; path=/`; setShowAlert(false); router.push("/comparaison"); } diff --git a/src/frontend/ui/recherche-avancee/resultat-recherche-avancee/resultat-recherche-avancee-footer/RechercheAvanceeFooter.test.tsx b/src/frontend/ui/recherche-avancee/resultat-recherche-avancee/resultat-recherche-avancee-footer/RechercheAvanceeFooter.test.tsx index 1bccc5900..f4bad10f8 100644 --- a/src/frontend/ui/recherche-avancee/resultat-recherche-avancee/resultat-recherche-avancee-footer/RechercheAvanceeFooter.test.tsx +++ b/src/frontend/ui/recherche-avancee/resultat-recherche-avancee/resultat-recherche-avancee-footer/RechercheAvanceeFooter.test.tsx @@ -1,4 +1,4 @@ -import { render, screen, fireEvent } from '@testing-library/react'; +import { render, screen, fireEvent } from '@testing-library/react'; import PaginationBtn from '../../../parametrage-utilisateurs/UsersListPage/Pagination/PaginationBtn/PaginationBtn'; import { TableFooterRechercheAvancee } from './RechercheAvanceeFooter'; @@ -9,7 +9,7 @@ describe('PaginationBtn Component', () => { const renderPaginationBtn = (page: number, lastPage: number) => { return render( - + ); }; @@ -52,13 +52,13 @@ describe('TableFooterRechercheAvancee établissements number Component', () => { const setPage = jest.fn(); it('renders the correct number of results', () => { - render(); + render(); expect(screen.getByText(/100 établissements/i)).toBeInTheDocument(); }); it('applies the correct CSS classes', () => { - render(); - + render(); + const footerContainer = screen.getByTestId('footer-container'); expect(footerContainer).toHaveClass(styles["footer-container"]); diff --git a/src/frontend/ui/recherche-avancee/resultat-recherche-avancee/resultat-recherche-avancee-footer/RechercheAvanceeFooter.tsx b/src/frontend/ui/recherche-avancee/resultat-recherche-avancee/resultat-recherche-avancee-footer/RechercheAvanceeFooter.tsx index 5496556b4..21fa91ff4 100644 --- a/src/frontend/ui/recherche-avancee/resultat-recherche-avancee/resultat-recherche-avancee-footer/RechercheAvanceeFooter.tsx +++ b/src/frontend/ui/recherche-avancee/resultat-recherche-avancee/resultat-recherche-avancee-footer/RechercheAvanceeFooter.tsx @@ -9,15 +9,16 @@ export type PaginationEts = { type TableFooterRechercheAvanceeProps = { nombreRésultats: number; + nombreDeResultatsMaxParPage: number } & PaginationEts; -export const TableFooterRechercheAvancee = ({ nombreRésultats, lastPage, page, setPage }: TableFooterRechercheAvanceeProps) => { +export const TableFooterRechercheAvancee = ({ nombreRésultats, lastPage, page, setPage, nombreDeResultatsMaxParPage }: TableFooterRechercheAvanceeProps) => { return (
{nombreRésultats + (nombreRésultats > 1 ? " établissements" : " établissement")} - {nombreRésultats > 20 && ( + {nombreRésultats > nombreDeResultatsMaxParPage && (
diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 58652e5ef..605321ae7 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -22,6 +22,7 @@ import "@gouvfr/dsfr/dist/component/table/table.min.css"; import "../frontend/ui/commun/global.css"; import { BackToSearchContextProvider } from "../frontend/ui/commun/contexts/BackToSearchContextProvider"; +import { ComparaisonContextProvider } from "../frontend/ui/commun/contexts/ComparaisonContextProvider"; import { ProfileContextProvider } from "../frontend/ui/commun/contexts/ProfileContextProvider"; import { RechecheAvanceeContextProvider } from "../frontend/ui/commun/contexts/RechercheAvanceeContextProvider"; import { DependenciesProvider } from "../frontend/ui/commun/contexts/useDependencies"; @@ -73,20 +74,22 @@ export default function MyApp({ Component, pageProps: { session, ...pageProps } - - - - - {/* + + + + + + {/* */} - -
- -
- - - {process.env.NODE_ENV !== "development" && + + {process.env.NODE_ENV !== "development" &&