diff --git a/database/dataSource.ts b/database/dataSource.ts index 82120e540..8b70a81a0 100644 --- a/database/dataSource.ts +++ b/database/dataSource.ts @@ -38,7 +38,9 @@ import { AjouterBlocBudgetFinanceEJ1677495763184 } from "./migrations/1677495763 import { AjouteHAD1680014929754 } from "./migrations/1680014929754-AjouteHAD"; import { AjoutCapacitesSanitaireEJ1680076022425 } from "./migrations/1680076022425-AjoutCapacitesSanitaireEJ"; import { AjoutTableUtilisateurRoleEtablissement1686646154737 } from "./migrations/1686646154737-AjoutTableUtilisateurRoleEtablissement"; -import { ModificationTableInstitution1688376404752 } from "./migrations/1688376404752-ModificationTableInstitution" +import { ModificationTableInstitution1688376404752 } from "./migrations/1688376404752-ModificationTableInstitution"; +import { AjoutTableFavori1691393817990 } from "./migrations/1691393817990-AjoutTableFavori"; +import { AjoutTableSearchHistory1691400360927 } from "./migrations/1691400360927-AjoutTableSearchHistory"; import { ActivitéMédicoSocialModel } from "./models/ActivitéMédicoSocialModel"; import { ActivitéSanitaireEntitéJuridiqueModel } from "./models/ActivitéSanitaireEntitéJuridiqueModel"; import { ActivitéSanitaireModel } from "./models/ActivitéSanitaireModel"; @@ -52,11 +54,13 @@ import { CapacitéAutorisationSanitaireModel } from "./models/CapacitéAutorisat import { CpomModel } from "./models/CpomModel"; import { DateMiseÀJourFichierSourceModel } from "./models/DateMiseÀJourFichierSourceModel"; import { EntitéJuridiqueModel } from "./models/EntitéJuridiqueModel"; +import { FavorisModel } from "./models/FavorisModel"; import { InstitutionModel } from "./models/InstitutionModel"; import { RechercheModel } from "./models/RechercheModel"; import { ReconnaissanceContractuelleSanitaireModel } from "./models/ReconnaissanceContractuelleSanitaireModel"; import { RessourcesHumainesMédicoSocialModel } from "./models/RessourcesHumainesMédicoSocialModel"; import { RoleModel } from "./models/RoleModel"; +import { SearchHistoryModel } from "./models/SearchHistoryModel"; import { UtilisateurModel } from "./models/UtilisateurModel"; import { ÉquipementMatérielLourdSanitaireModel } from "./models/ÉquipementMatérielLourdSanitaireModel"; import { ÉtablissementTerritorialIdentitéModel } from "./models/ÉtablissementTerritorialIdentitéModel"; @@ -79,6 +83,7 @@ export default new DataSource({ CpomModel, DateMiseÀJourFichierSourceModel, EntitéJuridiqueModel, + FavorisModel, ÉquipementMatérielLourdSanitaireModel, ÉtablissementTerritorialIdentitéModel, RechercheModel, @@ -88,6 +93,7 @@ export default new DataSource({ UtilisateurModel, RoleModel, InstitutionModel, + SearchHistoryModel, ], logger: "debug", logging: [environmentVariables.ORM_DEBUG] as LoggerOptions, @@ -126,6 +132,8 @@ export default new DataSource({ AjouterBlocBudgetFinanceEJ1677495763184, AjoutCapacitesSanitaireEJ1680076022425, AjouteHAD1680014929754, + AjoutTableSearchHistory1691400360927, + AjoutTableFavori1691393817990, AjoutTableUtilisateurRoleEtablissement1686646154737, ModificationTableInstitution1688376404752, ], diff --git a/database/migrations/1691393817990-AjoutTableFavori.ts b/database/migrations/1691393817990-AjoutTableFavori.ts new file mode 100644 index 000000000..a6408a428 --- /dev/null +++ b/database/migrations/1691393817990-AjoutTableFavori.ts @@ -0,0 +1,28 @@ +import { MigrationInterface, QueryRunner } from "typeorm" + +export class AjoutTableFavori1691393817990 implements MigrationInterface { + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + CREATE TABLE public.favori( + fav_id bigserial NOT NULL, + user_id uuid NOT NULL, + type character varying(255) NOT NULL, + finess_number character varying(255), + social_reason character varying(255) NOT NULL, + commune character varying(255) NOT NULL, + departement character varying(255) NOT NULL, + + PRIMARY KEY (fav_id), + + constraint favori_user_id_fkey foreign key (user_id) references public.utilisateur(ut_code) + );`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + DROP TABLE public.favori; + `); + } + +} diff --git a/database/migrations/1691400360927-AjoutTableSearchHistory.ts b/database/migrations/1691400360927-AjoutTableSearchHistory.ts new file mode 100644 index 000000000..ab269539c --- /dev/null +++ b/database/migrations/1691400360927-AjoutTableSearchHistory.ts @@ -0,0 +1,26 @@ +import { MigrationInterface, QueryRunner } from "typeorm" + +export class AjoutTableSearchHistory1691400360927 implements MigrationInterface { + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + CREATE TABLE public.search_history( + id bigserial NOT NULL, + user_id uuid NOT NULL, + title character varying(255) NOT NULL, + finess_number character varying(255) NOT NULL, + date timestamp NOT NULL, + type character varying(255) NOT NULL, + PRIMARY KEY (id), + + constraint search_history_user_id_fkey foreign key (user_id) references public.utilisateur(ut_code) + );`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + DROP TABLE public.search_history; + `); + } + +} diff --git a/database/models/FavorisModel.ts b/database/models/FavorisModel.ts new file mode 100644 index 000000000..c49a84f19 --- /dev/null +++ b/database/models/FavorisModel.ts @@ -0,0 +1,32 @@ +import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn } from "typeorm"; + +import { UtilisateurModel } from "./UtilisateurModel"; + +@Entity({ name: "favori" }) +export class FavorisModel { + @PrimaryGeneratedColumn({ name: "fav_id" }) + public id!: number; + + @Column({ name: "finess_number" }) + public finessNumber!: string; + + @Column({ name: 'type' }) + public type!: string; + + @Column({ name: 'user_id' }) + public userId!: string; + + @Column({ name: 'social_reason' }) + public socialReason!: string; + + @Column({ name: 'commune' }) + public commune!: string; + + @Column({ name: 'departement' }) + public departement!: string; + + @ManyToOne(() => UtilisateurModel) + @JoinColumn({ name: "user_id", referencedColumnName: "id" }) + public user!: UtilisateurModel; + +} diff --git a/database/models/SearchHistoryModel.ts b/database/models/SearchHistoryModel.ts new file mode 100644 index 000000000..494f966c5 --- /dev/null +++ b/database/models/SearchHistoryModel.ts @@ -0,0 +1,29 @@ +import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn } from "typeorm"; + +import { UtilisateurModel } from "./UtilisateurModel"; + +@Entity({ name: "search_history" }) +export class SearchHistoryModel { + @PrimaryGeneratedColumn({ name: "id" }) + public id!: number; + + @Column({ name: "title" }) + public title!: string; + + @Column({ name: "finess_number" }) + public finessNumber!: string; + + @Column({ name: "type" }) + public type!: string; + + @Column({ name: "date" }) + public date!: Date; + + @Column({ name: 'user_id' }) + public userId!: string; + + @ManyToOne(() => UtilisateurModel) + @JoinColumn({ name: "user_id", referencedColumnName: "id" }) + public user!: UtilisateurModel; + +} \ No newline at end of file diff --git a/src/backend/infrastructure/controllers/addToFavorisEndpoint.ts b/src/backend/infrastructure/controllers/addToFavorisEndpoint.ts new file mode 100644 index 000000000..700d25162 --- /dev/null +++ b/src/backend/infrastructure/controllers/addToFavorisEndpoint.ts @@ -0,0 +1,15 @@ +import { AddToFavorisUseCase } from "../../métier/use-cases/FavorisUseCase"; +import { Dependencies } from "../dependencies"; + +export async function addToFavorisEndpoint(dependencies: Dependencies, finessNumber: string, type: string, idUser: number, commune: string, departement: string, socialReason + : string): Promise { + try { + const addToFavorisUseCase = new AddToFavorisUseCase(dependencies.favorisLoader); + const userId = idUser.toString(); + + return await addToFavorisUseCase.exécute(finessNumber, type, userId, commune, departement, socialReason); + } catch (error) { + dependencies.logger.error(error); + throw error; + } +} diff --git a/src/backend/infrastructure/controllers/getAllFavorisEndpoint.ts b/src/backend/infrastructure/controllers/getAllFavorisEndpoint.ts new file mode 100644 index 000000000..1b82dc2ac --- /dev/null +++ b/src/backend/infrastructure/controllers/getAllFavorisEndpoint.ts @@ -0,0 +1,13 @@ +import { FavorisModel } from "../../../../database/models/FavorisModel"; +import { GetAllFavorisUseCase } from "../../métier/use-cases/FavorisUseCase"; +import { Dependencies } from "../dependencies"; + +export async function getAllFavorisEndpoint(dependencies: Dependencies, idUser: string): Promise { + try { + const getAllFavorisUseCase = new GetAllFavorisUseCase(dependencies.favorisLoader); + return await getAllFavorisUseCase.exécute(idUser); + } catch (error) { + dependencies.logger.error(error); + throw error; + } +} diff --git a/src/backend/infrastructure/controllers/getAllUserSearchHistory.ts b/src/backend/infrastructure/controllers/getAllUserSearchHistory.ts new file mode 100644 index 000000000..d7730a43a --- /dev/null +++ b/src/backend/infrastructure/controllers/getAllUserSearchHistory.ts @@ -0,0 +1,13 @@ +import { SearchHistoryModel } from "../../../../database/models/SearchHistoryModel"; +import { GetAllUserSearchHistoryUseCase } from "../../métier/use-cases/SearchHistoryUseCase"; +import { Dependencies } from "../dependencies"; + +export async function getAllUserSearchHistoryEndpoint(dependencies: Dependencies, idUser: string): Promise { + try { + const getAllUserSearchHistoryUseCase = new GetAllUserSearchHistoryUseCase(dependencies.searchHistoryLoader); + return await getAllUserSearchHistoryUseCase.exécute(idUser); + } catch (error) { + dependencies.logger.error(error); + throw error; + } +} diff --git a/src/backend/infrastructure/controllers/removeFromFavorisEndpoint.ts b/src/backend/infrastructure/controllers/removeFromFavorisEndpoint.ts new file mode 100644 index 000000000..98515caa5 --- /dev/null +++ b/src/backend/infrastructure/controllers/removeFromFavorisEndpoint.ts @@ -0,0 +1,13 @@ +import { RemoveFromFavorisUseCase } from "../../métier/use-cases/FavorisUseCase"; +import { Dependencies } from "../dependencies"; + +export async function removeFromFavorisEndpoint(dependencies: Dependencies, idUser: number, finessNumber: string): Promise { + try { + const removeFromFavorisUseCase = new RemoveFromFavorisUseCase(dependencies.favorisLoader); + const userId = idUser.toString(); + return await removeFromFavorisUseCase.exécute(userId, finessNumber); + } catch (error) { + dependencies.logger.error(error); + throw error; + } +} diff --git a/src/backend/infrastructure/controllers/saveSearchHistoryEndpoint.ts b/src/backend/infrastructure/controllers/saveSearchHistoryEndpoint.ts new file mode 100644 index 000000000..a42bf2c7a --- /dev/null +++ b/src/backend/infrastructure/controllers/saveSearchHistoryEndpoint.ts @@ -0,0 +1,13 @@ +import { SaveSearchHistoryUseCase } from "../../métier/use-cases/SearchHistoryUseCase"; +import { Dependencies } from "../dependencies"; + +export async function saveSearchHistoryEndpoint(dependencies: Dependencies, titre: string, idUser: string, finessNumber: string, type: string): Promise { + try { + const saveSearchHistoryUseCase = new SaveSearchHistoryUseCase(dependencies.searchHistoryLoader); + + return await saveSearchHistoryUseCase.exécute(titre, idUser, finessNumber, type); + } catch (error) { + dependencies.logger.error(error); + throw error; + } +} \ No newline at end of file diff --git a/src/backend/infrastructure/dependencies.ts b/src/backend/infrastructure/dependencies.ts index 365131d82..22b29adca 100644 --- a/src/backend/infrastructure/dependencies.ts +++ b/src/backend/infrastructure/dependencies.ts @@ -3,8 +3,10 @@ import * as Sentry from "@sentry/nextjs"; import { ChangePasswordLoader } from "../métier/gateways/ChangePasswordLoader"; import { EntitéJuridiqueLoader } from "../métier/gateways/EntitéJuridiqueLoader"; import { EnvironmentVariables } from "../métier/gateways/EnvironmentVariables"; +import { FavorisLoader } from "../métier/gateways/FavorisLoader"; import { Logger } from "../métier/gateways/Logger"; import { RechercheLoader } from "../métier/gateways/RechercheLoader"; +import { SearchHistoryLoader } from "../métier/gateways/SearchHistoryLoader"; import { UtilisateurLoader } from "../métier/gateways/UtilisateurLoader"; import { ÉtablissementTerritorialMédicoSocialLoader } from "../métier/gateways/ÉtablissementTerritorialMédicoSocialLoader"; import { ÉtablissementTerritorialRattachéLoader } from "../métier/gateways/ÉtablissementTerritorialRattachéLoader"; @@ -13,10 +15,12 @@ import { TypeOrmChangePasswordLoader } from "./gateways/change-password-loader/T import { dotEnvConfig } from "./gateways/dot-env/dotEnvConfig"; import { TypeOrmEntitéJuridiqueLoader } from "./gateways/entité-juridique-loader/TypeOrmEntitéJuridiqueLoader"; import { NodeEnvironmentVariables } from "./gateways/environnement-variables/NodeEnvironmentVariables"; +import { TypeOrmFavorisLoader } from "./gateways/favoris-loader/TypeOrmFavorisLoader"; import { TypeOrmForgetPasswordLoader } from "./gateways/forget-password-loader/TypeOrmForgetPasswordLoader"; import { ConsoleLogger } from "./gateways/logger/ConsoleLogger"; import { typeOrmOrm } from "./gateways/orm/typeOrmOrm"; import { TypeOrmRechercheLoader } from "./gateways/recherche-loader/TypeOrmRechercheLoader"; +import { TypeOrmSearchHistoryLoader } from "./gateways/search-history-loader/TypeOrmSearchHistoryLoader"; import { TypeOrmUtilisateurLoader } from "./gateways/utilisateur-loader/TypeOrmUtilisateurLoader"; import { TypeOrmÉtablissementTerritorialMédicoSocialLoader } from "./gateways/établissement-territorial-loader/TypeOrmÉtablissementTerritorialMédicoSocialLoader"; import { TypeOrmÉtablissementTerritorialRattachéLoader } from "./gateways/établissement-territorial-loader/TypeOrmÉtablissementTerritorialRattachéLoader"; @@ -31,8 +35,10 @@ export type Dependencies = Readonly<{ établissementTerritorialMédicoSocialLoader: ÉtablissementTerritorialMédicoSocialLoader; établissementTerritorialRattachéLoader: ÉtablissementTerritorialRattachéLoader; établissementTerritorialSanitaireLoader: ÉtablissementTerritorialSanitaireLoader; + favorisLoader: FavorisLoader; changePasswordLoader: ChangePasswordLoader; - forgetPasswordLoader : TypeOrmForgetPasswordLoader; + forgetPasswordLoader: TypeOrmForgetPasswordLoader; + searchHistoryLoader: SearchHistoryLoader }>; const createDependencies = (): Dependencies => { @@ -56,8 +62,10 @@ const createDependencies = (): Dependencies => { établissementTerritorialMédicoSocialLoader: new TypeOrmÉtablissementTerritorialMédicoSocialLoader(orm), établissementTerritorialRattachéLoader: new TypeOrmÉtablissementTerritorialRattachéLoader(orm), établissementTerritorialSanitaireLoader: new TypeOrmÉtablissementTerritorialSanitaireLoader(orm), + favorisLoader: new TypeOrmFavorisLoader(orm), changePasswordLoader: new TypeOrmChangePasswordLoader(orm), - forgetPasswordLoader : new TypeOrmForgetPasswordLoader(orm) + forgetPasswordLoader: new TypeOrmForgetPasswordLoader(orm), + searchHistoryLoader: new TypeOrmSearchHistoryLoader(orm) }; }; diff --git a/src/backend/infrastructure/gateways/favoris-loader/TypeOrmFavorisLoader.ts b/src/backend/infrastructure/gateways/favoris-loader/TypeOrmFavorisLoader.ts new file mode 100644 index 000000000..311e7c09a --- /dev/null +++ b/src/backend/infrastructure/gateways/favoris-loader/TypeOrmFavorisLoader.ts @@ -0,0 +1,29 @@ +import { DataSource } from "typeorm"; + +import { FavorisModel } from "../../../../../database/models/FavorisModel"; +import { FavorisLoader } from "../../../métier/gateways/FavorisLoader"; + +export class TypeOrmFavorisLoader implements FavorisLoader { + constructor(private readonly orm: Promise) { } + + + async addToFavoris(finessNumber: string, type: string, idUser: string, commune: string, departement: string, socialReason + : string) { + const favori = new FavorisModel(); + favori.finessNumber = finessNumber; + favori.type = type; + favori.userId = idUser; + favori.commune = commune; + favori.departement = departement; + favori.socialReason = socialReason; + await (await this.orm).getRepository(FavorisModel).save(favori); + } + + async removeFromFavoris(idUser: string, finessNumber: string) { + await (await this.orm).getRepository(FavorisModel).delete({ userId: idUser, finessNumber: finessNumber }); + } + + async getAllFavoris(idUser: string): Promise { + return await (await this.orm).getRepository(FavorisModel).find({ where: { userId: idUser } }); + } +} \ No newline at end of file diff --git a/src/backend/infrastructure/gateways/search-history-loader/TypeOrmSearchHistoryLoader.ts b/src/backend/infrastructure/gateways/search-history-loader/TypeOrmSearchHistoryLoader.ts new file mode 100644 index 000000000..52df38889 --- /dev/null +++ b/src/backend/infrastructure/gateways/search-history-loader/TypeOrmSearchHistoryLoader.ts @@ -0,0 +1,38 @@ +import { DataSource } from "typeorm"; + +import { SearchHistoryModel } from "../../../../../database/models/SearchHistoryModel"; +import { SearchHistoryLoader } from "../../../métier/gateways/SearchHistoryLoader"; + +export class TypeOrmSearchHistoryLoader implements SearchHistoryLoader { + constructor(private readonly orm: Promise) { } + + + async saveSearchHistory(title: string, idUser: string, finessNumber: string, type: string) { + const history = await (await this.orm).getRepository(SearchHistoryModel).findOne({ where: { userId: idUser, finessNumber: finessNumber } }); + if (history) { + await (await this.orm).getRepository(SearchHistoryModel).update(history.id, { date: new Date() }); + } else { + // make sure to keep only the last 10 search + const count = await (await this.orm).getRepository(SearchHistoryModel).countBy({ userId: idUser }); + const searchHistory = new SearchHistoryModel(); + searchHistory.date = new Date(); + searchHistory.finessNumber = finessNumber; + searchHistory.title = title; + searchHistory.userId = idUser; + searchHistory.type = type; + if (count === 10) { + // delete the oldest line + const oldestElement = await (await this.orm).getRepository(SearchHistoryModel).findOne({ + where: { userId: idUser }, + order: { date: 'ASC' }, + }); + if (oldestElement) await (await this.orm).getRepository(SearchHistoryModel).remove(oldestElement); + } + await (await this.orm).getRepository(SearchHistoryModel).save(searchHistory); + } + } + + async getAllUserSearchHistory(idUser: string) { + return await (await this.orm).getRepository(SearchHistoryModel).find({ where: { userId: idUser }, order: { date: 'DESC' } }); + } +} \ No newline at end of file diff --git "a/src/backend/m\303\251tier/gateways/FavorisLoader.ts" "b/src/backend/m\303\251tier/gateways/FavorisLoader.ts" new file mode 100644 index 000000000..9997985be --- /dev/null +++ "b/src/backend/m\303\251tier/gateways/FavorisLoader.ts" @@ -0,0 +1,7 @@ +import { FavorisModel } from "../../../../database/models/FavorisModel"; + +export interface FavorisLoader { + addToFavoris(finessNumber: string, type: string, idUser: string, commune: string, departement: string, social_reason: string): Promise; + removeFromFavoris(idUser: string, finessNumber: string): Promise; + getAllFavoris(idUser: string): Promise; +} diff --git "a/src/backend/m\303\251tier/gateways/SearchHistoryLoader.ts" "b/src/backend/m\303\251tier/gateways/SearchHistoryLoader.ts" new file mode 100644 index 000000000..ab05d3355 --- /dev/null +++ "b/src/backend/m\303\251tier/gateways/SearchHistoryLoader.ts" @@ -0,0 +1,6 @@ +import { SearchHistoryModel } from "../../../../database/models/SearchHistoryModel"; + +export interface SearchHistoryLoader { + saveSearchHistory(titre: string, idUser: string, finessNumber: string, type: string): Promise; + getAllUserSearchHistory(idUser: string): Promise +} \ No newline at end of file diff --git "a/src/backend/m\303\251tier/use-cases/FavorisUseCase.ts" "b/src/backend/m\303\251tier/use-cases/FavorisUseCase.ts" new file mode 100644 index 000000000..a0535093a --- /dev/null +++ "b/src/backend/m\303\251tier/use-cases/FavorisUseCase.ts" @@ -0,0 +1,27 @@ +import { FavorisModel } from "../../../../database/models/FavorisModel"; +import { FavorisLoader } from "../gateways/FavorisLoader"; + +export class AddToFavorisUseCase { + constructor(private favorisLoader: FavorisLoader) { } + + async exécute(finessNumber: string, type: string, idUser: string, commune: string, departement: string, socialReason + : string): Promise { + await this.favorisLoader.addToFavoris(finessNumber, type, idUser, commune, departement, socialReason); + } +} + +export class RemoveFromFavorisUseCase { + constructor(private favorisLoader: FavorisLoader) { } + + async exécute(idUser: string, finessNumber: string): Promise { + await this.favorisLoader.removeFromFavoris(idUser, finessNumber); + } +} + +export class GetAllFavorisUseCase { + constructor(private favorisLoader: FavorisLoader) { } + + async exécute(idUser: string): Promise { + return await this.favorisLoader.getAllFavoris(idUser); + } +} \ No newline at end of file diff --git "a/src/backend/m\303\251tier/use-cases/SearchHistoryUseCase.ts" "b/src/backend/m\303\251tier/use-cases/SearchHistoryUseCase.ts" new file mode 100644 index 000000000..563d20359 --- /dev/null +++ "b/src/backend/m\303\251tier/use-cases/SearchHistoryUseCase.ts" @@ -0,0 +1,18 @@ +import { SearchHistoryModel } from "../../../../database/models/SearchHistoryModel"; +import { SearchHistoryLoader } from "../gateways/SearchHistoryLoader"; + +export class SaveSearchHistoryUseCase { + constructor(private searchHistoryLoader: SearchHistoryLoader) { } + + async exécute(titre: string, idUser: string, finessNumber: string, type: string): Promise { + return await this.searchHistoryLoader.saveSearchHistory(titre, idUser, finessNumber, type); + } +} + +export class GetAllUserSearchHistoryUseCase { + constructor(private searchHistoryLoader: SearchHistoryLoader) { } + + async exécute(idUser: string): Promise { + return await this.searchHistoryLoader.getAllUserSearchHistory(idUser); + } +} \ No newline at end of file diff --git a/src/frontend/configuration/Paths.ts b/src/frontend/configuration/Paths.ts index dd8e37113..f4b4df90e 100644 --- a/src/frontend/configuration/Paths.ts +++ b/src/frontend/configuration/Paths.ts @@ -10,6 +10,8 @@ export class Paths { readonly RÉGION: string = "/region"; readonly ECO_CONCEPTION: string = "/eco-conception"; readonly GESTION_DES_COOKIES: string = "/gestion-des-cookies"; + readonly FAVORIS: string = "/favoris" + readonly HISTORY: string = "/history" readonly FORGET_PASSWORD = '/mot-passe-oublie'; readonly CHANGE_PASSWORD = '/change-mot-passe'; } diff --git a/src/frontend/configuration/wording/Wording.tsx b/src/frontend/configuration/wording/Wording.tsx index b7a25933f..5d46f99a7 100644 --- a/src/frontend/configuration/wording/Wording.tsx +++ b/src/frontend/configuration/wording/Wording.tsx @@ -331,6 +331,11 @@ export interface Wording { readonly EN_CONSTRUCTION: string; + // Favoris + readonly FAVORIS_LIST: string; + readonly EJ_SECTION_TITLE: string; + readonly SANITAIRE_SECTION_TITLE: string; + readonly SOCIAL_SECTION_TITLE: string; // Mot de passe oublié readonly MOT_PASSE_OUBLIE_TITRE: string; readonly MOT_PASSE_OUBLIE_LABEL: string; @@ -344,6 +349,11 @@ export interface Wording { readonly MOT_DE_PASSE: string; readonly CONFIRMER_MOT_DE_PASSE: string; readonly CONFIRM_CHANGE_PASSWORD: string; + + // Historique de recherche + readonly HISTORIQUE_DE_RECHERECHE_TITRE: string; + readonly ETABLISSEMENT_CONSULTE: string; + readonly DATE: string; readonly CONFIRM_UPDATE_PASSWORD: string; readonly REINITIALISATION_MOT_PASSE_TITRE: string; readonly REINITIALISATION_MOT_PASSE_DESCRIPTION: string; diff --git a/src/frontend/configuration/wording/WordingFr.tsx b/src/frontend/configuration/wording/WordingFr.tsx index 61d53e902..fb73f12fa 100644 --- a/src/frontend/configuration/wording/WordingFr.tsx +++ b/src/frontend/configuration/wording/WordingFr.tsx @@ -420,6 +420,12 @@ export class WordingFr implements Wording { readonly EN_CONSTRUCTION: string = "Demander à Daisy ce que l’on doit écrire."; + // Favoris + readonly FAVORIS_LIST: string = "Liste des favoris"; + readonly EJ_SECTION_TITLE: string = "Entités juridiques"; + readonly SANITAIRE_SECTION_TITLE: string = "Etablissements sanitaires"; + readonly SOCIAL_SECTION_TITLE: string = "Etablissements sociaux et médico-sociaux"; + // Mot de passe oublié readonly MOT_PASSE_OUBLIE_TITRE: string = "Vous avez oublié votre mot de passe ?"; readonly MOT_PASSE_OUBLIE_LABEL: string = "Courriel"; @@ -433,6 +439,11 @@ export class WordingFr implements Wording { readonly MOT_DE_PASSE: string = "Nouveau mot de passe"; readonly CONFIRMER_MOT_DE_PASSE: string = "Confirmation mot de passe"; readonly CONFIRM_CHANGE_PASSWORD: string = "Confirmer la demande de réinitialisation"; + + // Historique de recherche + readonly HISTORIQUE_DE_RECHERECHE_TITRE: string = "Historique des derniers établissements consultés suite à une recherche"; + readonly ETABLISSEMENT_CONSULTE: string = "Etablissements consultés"; + readonly DATE: string = "Date"; readonly CONFIRM_UPDATE_PASSWORD: string = "Confirmer la modification du mot de passe"; readonly REINITIALISATION_MOT_PASSE_TITRE: string = "Réinitialisation de votre mot de passe"; readonly REINITIALISATION_MOT_PASSE_DESCRIPTION: string = "Merci de renseigner votre nouveau mot de passe"; diff --git a/src/frontend/ui/commun/Breadcrumb/Breadcrumb.test.tsx b/src/frontend/ui/commun/Breadcrumb/Breadcrumb.test.tsx index 6eefe4204..6d3761c4f 100644 --- a/src/frontend/ui/commun/Breadcrumb/Breadcrumb.test.tsx +++ b/src/frontend/ui/commun/Breadcrumb/Breadcrumb.test.tsx @@ -1,5 +1,7 @@ import { screen, within } from "@testing-library/react"; +import { SessionProvider } from "next-auth/react"; +import { RésultatDeRechercheTestBuilder } from "../../../../backend/test-builder/RésultatDeRechercheTestBuilder"; import PageDAccueil from "../../../../pages"; import Accessibilité from "../../../../pages/accessibilite"; import DonnéesPersonnelles from "../../../../pages/donnees-personnelles"; @@ -10,6 +12,7 @@ import { ÉtablissementTerritorialMédicoSocialViewModelTestBuilder } from "../. import { ÉtablissementTerritorialSanitaireViewModelTestBuilder } from "../../../test-helpers/test-builder/ÉtablissementTerritorialSanitaireViewModelTestBuilder"; import { fakeFrontDependencies, renderFakeComponent } from "../../../test-helpers/testHelper"; import { PageEntitéJuridique } from "../../entité-juridique/PageEntitéJuridique"; +import { RechercheViewModel } from "../../home/RechercheViewModel"; import { PageRégion } from "../../région/PageRégion"; import { régions } from "../../région/régions"; import { PageÉtablissementTerritorialMédicoSocial } from "../../établissement-territorial-médico-social/PageÉtablissementTerritorialMédicoSocial"; @@ -18,6 +21,20 @@ import { Breadcrumb } from "./Breadcrumb"; jest.mock("next/router", () => require("next-router-mock")); const { paths, wording } = fakeFrontDependencies; +const mockSession = { + name: "john", + email: "test@test.fr", + user: { + idUser: '1', + firstname: 'Doe', + role: 'admin', + institution: {}, + }, + expires: "1235" +} + +const result = RésultatDeRechercheTestBuilder.créeUnRésultatDeRechercheEntité({ numéroFiness: "000000000" }); +const rechercheViewModel = new RechercheViewModel(result, paths); describe("Le fil d’Ariane (breadcrumb)", () => { it("ne s’affiche pas sur la page d’accueil", () => { @@ -69,11 +86,14 @@ describe("Le fil d’Ariane (breadcrumb)", () => { // WHEN renderFakeComponent( <> - - + + + + ); @@ -97,8 +117,12 @@ describe("Le fil d’Ariane (breadcrumb)", () => { // WHEN renderFakeComponent( <> - - + + + + ); @@ -126,8 +150,13 @@ describe("Le fil d’Ariane (breadcrumb)", () => { // WHEN renderFakeComponent( <> - - + + + + ); @@ -167,4 +196,4 @@ describe("Le fil d’Ariane (breadcrumb)", () => { expect(lienAccueil).toHaveAttribute("href", "/"); expect(within(région).getByText(wording.régionBreadcrumb(régions["france-metropolitaine"].label))).toBeInTheDocument(); }); -}); +}); \ No newline at end of file diff --git a/src/frontend/ui/commun/Header/Header.test.tsx b/src/frontend/ui/commun/Header/Header.test.tsx index da169d5f8..ac39fa70c 100644 --- a/src/frontend/ui/commun/Header/Header.test.tsx +++ b/src/frontend/ui/commun/Header/Header.test.tsx @@ -8,7 +8,10 @@ import { Header } from "./Header"; jest.mock("next/router", () => require("next-router-mock")); const { paths, wording } = fakeFrontDependencies; const mockSession = { + name: "john", + email: "test@test.fr", user: { + idUser: '1', firstname: "Doe", name: "john", email: "test@test.fr", @@ -18,9 +21,19 @@ const mockSession = { expires: "1235" } + + describe("En-tête de page", () => { it("affiche un lien pour accéder à la page d’accueil", () => { + // @ts-ignore + jest.spyOn(global, "fetch").mockResolvedValue({ + json: jest.fn().mockResolvedValue([]), + }); // WHEN + // @ts-ignore + jest.spyOn(global, "fetch").mockResolvedValue({ + json: jest.fn().mockResolvedValue([]), + }); renderFakeComponent(
); // THEN @@ -30,6 +43,10 @@ describe("En-tête de page", () => { }); it("affiche un menu pour afficher la déconnexion et un pour le moteur de recherche en mobile", () => { + // @ts-ignore + jest.spyOn(global, "fetch").mockResolvedValue({ + json: jest.fn().mockResolvedValue([]), + }); // WHEN renderFakeComponent(
); @@ -43,6 +60,10 @@ describe("En-tête de page", () => { }); it("affiche le formulaire de recherche", () => { + // @ts-ignore + jest.spyOn(global, "fetch").mockResolvedValue({ + json: jest.fn().mockResolvedValue([]), + }); // WHEN renderFakeComponent(
); @@ -57,6 +78,10 @@ describe("En-tête de page", () => { }); it("redirection vers la recherche quand on fait une recherche", () => { + // @ts-ignore + jest.spyOn(global, "fetch").mockResolvedValue({ + json: jest.fn().mockResolvedValue([]), + }); // GIVEN const router = mockRouter; const terme = "hospitalier"; @@ -76,8 +101,16 @@ describe("En-tête de page", () => { it("n’affiche pas le formulaire de recherche quand on est sur l’accueil", () => { // GIVEN + // @ts-ignore + jest.spyOn(global, "fetch").mockResolvedValue({ + json: jest.fn().mockResolvedValue([]), + }); const router = mockRouter; router.push(paths.ACCUEIL); + // @ts-ignore + jest.spyOn(global, "fetch").mockResolvedValue({ + json: jest.fn().mockResolvedValue([]), + }); // WHEN renderFakeComponent(
); diff --git a/src/frontend/ui/commun/Header/Header.tsx b/src/frontend/ui/commun/Header/Header.tsx index cad8ed7fe..a31fe6d35 100644 --- a/src/frontend/ui/commun/Header/Header.tsx +++ b/src/frontend/ui/commun/Header/Header.tsx @@ -2,12 +2,13 @@ import { signOut, useSession } from "next-auth/react"; import Image from "next/image"; import Link from "next/link"; import { useRouter } from "next/router"; -import { ChangeEvent, useState } from "react"; +import { ChangeEvent, useEffect, useState } from "react"; import "@gouvfr/dsfr/dist/component/header/header.min.css"; import "@gouvfr/dsfr/dist/component/logo/logo.min.css"; import "@gouvfr/dsfr/dist/component/link/link.min.css"; import "@gouvfr/dsfr/dist/component/modal/modal.min.css"; +import { useFavoris } from "../../favoris/useFavoris"; import { Breadcrumb } from "../Breadcrumb/Breadcrumb"; import { useDependencies } from "../contexts/useDependencies"; import { useOutsideClick } from "../hooks/useOutsideClick"; @@ -17,7 +18,8 @@ import styles from "./Header.module.css"; export const Header = () => { const { paths, wording } = useDependencies(); const router = useRouter(); - const { data, status } = useSession() + const { data, status } = useSession(); + const { getAllFavoris } = useFavoris(); const [terme, setTerme] = useState(""); const [displayMenu, setDisplayMenu] = useState(false); @@ -27,6 +29,13 @@ export const Header = () => { setTerme(event.target.value); }; + + useEffect(() => { + if (data?.user?.idUser) { + getAllFavoris(data?.user?.idUser); + } + }, [data?.user?.idUser]); + return ( <>
@@ -75,7 +84,7 @@ export const Header = () => {
- {router.pathname !== paths.ACCUEIL && router.pathname !== paths.FORGET_PASSWORD && router.pathname !== paths.CHANGE_PASSWORD && router.pathname !== paths.CONNEXION && ( + {router.pathname !== paths.ACCUEIL && router.pathname !== paths.FORGET_PASSWORD && router.pathname !== paths.CHANGE_PASSWORD && router.pathname !== paths.CONNEXION && router.pathname !== paths.HISTORY && router.pathname !== paths.FAVORIS && (