From 873dcf713a540c16fc60e95724f35c246f7dda8f Mon Sep 17 00:00:00 2001 From: "Yassine R." Date: Wed, 22 Jan 2025 01:36:59 +0100 Subject: [PATCH 1/5] feat(frontend): add referrer --- .../_common/model/usager/UsagerLight.type.ts | 1 + .../1736291936636-auto-migration.ts | 20 +++++ .../entities/usager/UsagerTable.typeorm.ts | 3 + .../usagers/controllers/agenda.controller.ts | 17 +--- .../src/usagers/dto/UsagerAyantDroitDto.ts | 1 + .../dto/decision-form/create-usager.dto.ts | 13 +++ .../src/users/controllers/users.controller.ts | 1 + .../src/usager/interfaces/Usager.interface.ts | 1 + .../interfaces/UsagerDecision.interface.ts | 10 ++- .../UsagerDecisionMotif.type.ts | 0 .../UsagerDecisionOrientation.type.ts | 0 .../UsagerDecisionStatut.type.ts | 0 .../common/src/usager/types/decision/index.ts | 4 + packages/common/src/usager/types/index.ts | 4 +- .../form/UsagerEtatCivilFormData.type.ts | 1 + .../UserStructureProfile.type.ts | 1 + .../frontend/src/app/app-routing.module.ts | 8 ++ .../components/login/login.component.html | 2 +- .../edit-user/edit-user.component.html | 0 .../edit-user/edit-user.component.spec.ts | 0 .../edit-user/edit-user.component.ts | 64 ++++++++------- .../register-user-admin.component.html | 0 .../register-user-admin.component.spec.ts | 0 .../register-user-admin.component.ts | 9 ++- .../user-profil/user-profil.component.html | 0 .../user-profil/user-profil.component.spec.ts | 0 .../user-profil/user-profil.component.ts | 5 +- .../manage-users-routing.module.ts | 26 ++++++ .../manage-users/manage-users.module.ts | 34 ++++++++ .../services/manage-users.service.spec.ts | 16 ++++ .../services/manage-users.service.ts | 79 +++++++++++++++++++ ...display-etat-civil-decision.component.html | 7 ++ .../step-etat-civil.component.html | 16 ++++ .../step-etat-civil.component.ts | 4 +- .../step-rdv/step-rdv.component.html | 4 +- .../components/step-rdv/step-rdv.component.ts | 27 ++++--- .../services/usager-dossier.service.ts | 17 +--- .../display-etat-civil.component.html | 7 ++ .../etat-civil-parent-form.component.ts | 14 +++- .../profil-etat-civil-form.component.html | 15 ++++ .../profil-etat-civil-form.component.ts | 4 +- .../interfaces/UsagerFormModel.ts | 2 + .../usager-shared/pipes/referrer-name.pipe.ts | 16 ++++ .../usager-shared/usager-shared.module.ts | 3 + .../reset-password.component.ts | 2 +- .../modules/users/services/users.service.ts | 57 +------------ .../app/modules/users/users-routing.module.ts | 11 +-- .../src/app/modules/users/users.module.ts | 17 +--- 48 files changed, 368 insertions(+), 175 deletions(-) create mode 100644 packages/backend/src/_migrations/1736291936636-auto-migration.ts rename packages/common/src/usager/types/{ => decision}/UsagerDecisionMotif.type.ts (100%) rename packages/common/src/usager/types/{ => decision}/UsagerDecisionOrientation.type.ts (100%) rename packages/common/src/usager/types/{ => decision}/UsagerDecisionStatut.type.ts (100%) create mode 100644 packages/common/src/usager/types/decision/index.ts rename packages/frontend/src/app/modules/{users => manage-users}/components/edit-user/edit-user.component.html (100%) rename packages/frontend/src/app/modules/{users => manage-users}/components/edit-user/edit-user.component.spec.ts (100%) rename packages/frontend/src/app/modules/{users => manage-users}/components/edit-user/edit-user.component.ts (83%) rename packages/frontend/src/app/modules/{users => manage-users}/components/register-user-admin/register-user-admin.component.html (100%) rename packages/frontend/src/app/modules/{users => manage-users}/components/register-user-admin/register-user-admin.component.spec.ts (100%) rename packages/frontend/src/app/modules/{users => manage-users}/components/register-user-admin/register-user-admin.component.ts (93%) rename packages/frontend/src/app/modules/{users => manage-users}/components/user-profil/user-profil.component.html (100%) rename packages/frontend/src/app/modules/{users => manage-users}/components/user-profil/user-profil.component.spec.ts (100%) rename packages/frontend/src/app/modules/{users => manage-users}/components/user-profil/user-profil.component.ts (96%) create mode 100644 packages/frontend/src/app/modules/manage-users/manage-users-routing.module.ts create mode 100644 packages/frontend/src/app/modules/manage-users/manage-users.module.ts create mode 100644 packages/frontend/src/app/modules/manage-users/services/manage-users.service.spec.ts create mode 100644 packages/frontend/src/app/modules/manage-users/services/manage-users.service.ts create mode 100644 packages/frontend/src/app/modules/usager-shared/pipes/referrer-name.pipe.ts diff --git a/packages/backend/src/_common/model/usager/UsagerLight.type.ts b/packages/backend/src/_common/model/usager/UsagerLight.type.ts index 1ccd901b2a..2e33f7c392 100644 --- a/packages/backend/src/_common/model/usager/UsagerLight.type.ts +++ b/packages/backend/src/_common/model/usager/UsagerLight.type.ts @@ -26,6 +26,7 @@ export type UsagerLight = AppEntity & | "options" | "historique" | "ayantsDroits" + | "referrerId" | "villeNaissance" | "telephone" | "import" diff --git a/packages/backend/src/_migrations/1736291936636-auto-migration.ts b/packages/backend/src/_migrations/1736291936636-auto-migration.ts new file mode 100644 index 0000000000..714062d277 --- /dev/null +++ b/packages/backend/src/_migrations/1736291936636-auto-migration.ts @@ -0,0 +1,20 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; +import { domifaConfig } from "../config"; + +export class AutoMigration1736291936636 implements MigrationInterface { + name = "AutoMigration1736291936636"; + + public async up(queryRunner: QueryRunner): Promise { + if ( + domifaConfig().envId === "prod" || + domifaConfig().envId === "preprod" || + domifaConfig().envId === "local" + ) { + await queryRunner.query(`ALTER TABLE "usager" ADD "referrerId" integer`); + } + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "usager" DROP COLUMN "referrerId"`); + } +} diff --git a/packages/backend/src/database/entities/usager/UsagerTable.typeorm.ts b/packages/backend/src/database/entities/usager/UsagerTable.typeorm.ts index 362f67fb8f..54231b5005 100644 --- a/packages/backend/src/database/entities/usager/UsagerTable.typeorm.ts +++ b/packages/backend/src/database/entities/usager/UsagerTable.typeorm.ts @@ -123,6 +123,9 @@ export class UsagerTable @Column({ type: "integer", default: 0 }) public etapeDemande!: number; + @Column({ type: "integer", default: null, nullable: true }) + public referrerId!: number; + @Column({ type: "jsonb", nullable: true }) public rdv!: UsagerRdv | null; diff --git a/packages/backend/src/usagers/controllers/agenda.controller.ts b/packages/backend/src/usagers/controllers/agenda.controller.ts index e041c1eabb..a0b70f5ecd 100644 --- a/packages/backend/src/usagers/controllers/agenda.controller.ts +++ b/packages/backend/src/usagers/controllers/agenda.controller.ts @@ -23,10 +23,7 @@ import { usagerRepository, } from "../../database"; import { ExpressResponse } from "../../util/express"; -import { - UserStructureAuthenticated, - UserStructureProfile, -} from "../../_common/model"; +import { UserStructureAuthenticated } from "../../_common/model"; import { RdvDto } from "../dto/decision-form/rdv.dto"; import { UsagersService } from "../services/usagers.service"; import { getUsagerNomComplet, Usager } from "@domifa/common"; @@ -39,18 +36,6 @@ import { usagerAppointmentCreatedEmailSender } from "../../modules/mails/service export class AgendaController { constructor(private readonly usagersService: UsagersService) {} - @Get("users") - @ApiOperation({ summary: "Liste des utilisateurs pour l'agenda" }) - @AllowUserStructureRoles("simple", "responsable", "admin") - public getAllUsersForAgenda( - @CurrentUser() currentUser: UserStructureAuthenticated - ): Promise { - return userStructureRepository.findVerifiedStructureUsersByRoles({ - structureId: currentUser.structureId, - roles: ["admin", "simple", "responsable"], - }); - } - @Get("") @ApiOperation({ summary: "Liste des rendez-vous à venir" }) @AllowUserStructureRoles("simple", "responsable", "admin") diff --git a/packages/backend/src/usagers/dto/UsagerAyantDroitDto.ts b/packages/backend/src/usagers/dto/UsagerAyantDroitDto.ts index 4bd8304722..5113a9f1e3 100644 --- a/packages/backend/src/usagers/dto/UsagerAyantDroitDto.ts +++ b/packages/backend/src/usagers/dto/UsagerAyantDroitDto.ts @@ -51,6 +51,7 @@ export class UsagerAyantDroitDto { example: "20/12/2002", description: "Date de naissance de l'ayant-droit", type: Date, + required: true, }) @IsNotEmpty() @IsDateString() diff --git a/packages/backend/src/usagers/dto/decision-form/create-usager.dto.ts b/packages/backend/src/usagers/dto/decision-form/create-usager.dto.ts index 9d164afbac..467b06e49a 100644 --- a/packages/backend/src/usagers/dto/decision-form/create-usager.dto.ts +++ b/packages/backend/src/usagers/dto/decision-form/create-usager.dto.ts @@ -19,6 +19,7 @@ import { IsArray, ValidateIf, ValidateNested, + IsNumber, } from "class-validator"; import { Trim, @@ -128,6 +129,8 @@ export class CreateUsagerDto { public customRef!: string; @ApiProperty({ + required: false, + type: "string", example: "test@test.fr", description: "Email du domicilié", }) @@ -137,6 +140,16 @@ export class CreateUsagerDto { @LowerCaseTransform() public email!: string; + @ApiProperty({ + required: false, + example: 10, + type: Number, + description: "Id du référent", + }) + @IsOptional() + @IsNumber() + public referrerId!: number; + @ApiProperty({ type: Object, required: false, diff --git a/packages/backend/src/users/controllers/users.controller.ts b/packages/backend/src/users/controllers/users.controller.ts index 5ed07831fe..61c9b16836 100644 --- a/packages/backend/src/users/controllers/users.controller.ts +++ b/packages/backend/src/users/controllers/users.controller.ts @@ -58,6 +58,7 @@ export class UsersController { }, select: { uuid: true, + id: true, role: true, nom: true, prenom: true, diff --git a/packages/common/src/usager/interfaces/Usager.interface.ts b/packages/common/src/usager/interfaces/Usager.interface.ts index eb280e4196..bd54cc3bd7 100644 --- a/packages/common/src/usager/interfaces/Usager.interface.ts +++ b/packages/common/src/usager/interfaces/Usager.interface.ts @@ -55,6 +55,7 @@ export interface Usager extends AppEntity { numeroDistribution: string | null; pinnedNote: UsagerPinnedNote; + referrerId?: number | null; nbNotes?: number; statusInfos?: any; diff --git a/packages/common/src/usager/interfaces/UsagerDecision.interface.ts b/packages/common/src/usager/interfaces/UsagerDecision.interface.ts index d1afd37d4a..27c36615e5 100644 --- a/packages/common/src/usager/interfaces/UsagerDecision.interface.ts +++ b/packages/common/src/usager/interfaces/UsagerDecision.interface.ts @@ -1,7 +1,9 @@ -import { type UsagerDecisionMotif } from "../types/UsagerDecisionMotif.type"; -import { type UsagerDecisionOrientation } from "../types/UsagerDecisionOrientation.type"; -import { type UsagerDecisionStatut } from "../types/UsagerDecisionStatut.type"; -import { type UsagerTypeDom } from "../types/UsagerTypeDom.type"; +import { + UsagerTypeDom, + UsagerDecisionStatut, + UsagerDecisionMotif, + UsagerDecisionOrientation, +} from "../types"; export interface UsagerDecision { uuid: string; // permet d'identifier une décision en cas de suppression de l'historique diff --git a/packages/common/src/usager/types/UsagerDecisionMotif.type.ts b/packages/common/src/usager/types/decision/UsagerDecisionMotif.type.ts similarity index 100% rename from packages/common/src/usager/types/UsagerDecisionMotif.type.ts rename to packages/common/src/usager/types/decision/UsagerDecisionMotif.type.ts diff --git a/packages/common/src/usager/types/UsagerDecisionOrientation.type.ts b/packages/common/src/usager/types/decision/UsagerDecisionOrientation.type.ts similarity index 100% rename from packages/common/src/usager/types/UsagerDecisionOrientation.type.ts rename to packages/common/src/usager/types/decision/UsagerDecisionOrientation.type.ts diff --git a/packages/common/src/usager/types/UsagerDecisionStatut.type.ts b/packages/common/src/usager/types/decision/UsagerDecisionStatut.type.ts similarity index 100% rename from packages/common/src/usager/types/UsagerDecisionStatut.type.ts rename to packages/common/src/usager/types/decision/UsagerDecisionStatut.type.ts diff --git a/packages/common/src/usager/types/decision/index.ts b/packages/common/src/usager/types/decision/index.ts new file mode 100644 index 0000000000..5faf15fd36 --- /dev/null +++ b/packages/common/src/usager/types/decision/index.ts @@ -0,0 +1,4 @@ +// @index('./*', f => `export * from '${f.path}'`) +export * from "./UsagerDecisionMotif.type"; +export * from "./UsagerDecisionOrientation.type"; +export * from "./UsagerDecisionStatut.type"; diff --git a/packages/common/src/usager/types/index.ts b/packages/common/src/usager/types/index.ts index bebb77bd97..23778fab8b 100644 --- a/packages/common/src/usager/types/index.ts +++ b/packages/common/src/usager/types/index.ts @@ -1,9 +1,7 @@ // @index('./*', f => `export * from '${f.path}'`) export * from "./AyantDroitLienParente.type"; +export * from "./decision"; export * from "./entretien"; -export * from "./UsagerDecisionMotif.type"; -export * from "./UsagerDecisionOrientation.type"; -export * from "./UsagerDecisionStatut.type"; export * from "./UsagerImport.type"; export * from "./UsagerPinnedNote.type"; export * from "./UsagerSexe.type"; diff --git a/packages/frontend/src/_common/model/usager/form/UsagerEtatCivilFormData.type.ts b/packages/frontend/src/_common/model/usager/form/UsagerEtatCivilFormData.type.ts index 3f432c2de8..0fbb4e9764 100644 --- a/packages/frontend/src/_common/model/usager/form/UsagerEtatCivilFormData.type.ts +++ b/packages/frontend/src/_common/model/usager/form/UsagerEtatCivilFormData.type.ts @@ -16,5 +16,6 @@ export type UsagerEtatCivilFormData = { nationalite: string | null; telephone: Telephone; contactByPhone: boolean; + referrerId: number | null; ayantsDroits: UsagerAyantDroit[]; }; diff --git a/packages/frontend/src/_common/model/user-structure/UserStructureProfile.type.ts b/packages/frontend/src/_common/model/user-structure/UserStructureProfile.type.ts index 68ba84e260..431deff858 100644 --- a/packages/frontend/src/_common/model/user-structure/UserStructureProfile.type.ts +++ b/packages/frontend/src/_common/model/user-structure/UserStructureProfile.type.ts @@ -6,6 +6,7 @@ export type UserStructureProfile = Pick< | "nom" | "prenom" | "role" + | "id" | "verified" | "uuid" | "createdAt" diff --git a/packages/frontend/src/app/app-routing.module.ts b/packages/frontend/src/app/app-routing.module.ts index a5cfd8739d..ae1ef82b9e 100644 --- a/packages/frontend/src/app/app-routing.module.ts +++ b/packages/frontend/src/app/app-routing.module.ts @@ -71,6 +71,14 @@ export const routes: Routes = [ ), path: "manage", }, + { + canActivate: [AuthGuard], + loadChildren: () => + import("./modules/manage-users/manage-users.module").then( + (m) => m.ManageUsersModule + ), + path: "manage-users", + }, { canActivate: [AuthGuard, FacteurGuard], loadChildren: () => diff --git a/packages/frontend/src/app/modules/general/components/login/login.component.html b/packages/frontend/src/app/modules/general/components/login/login.component.html index aefedef41d..f5291146f9 100644 --- a/packages/frontend/src/app/modules/general/components/login/login.component.html +++ b/packages/frontend/src/app/modules/general/components/login/login.component.html @@ -143,7 +143,7 @@


Mot de passe oublié diff --git a/packages/frontend/src/app/modules/users/components/edit-user/edit-user.component.html b/packages/frontend/src/app/modules/manage-users/components/edit-user/edit-user.component.html similarity index 100% rename from packages/frontend/src/app/modules/users/components/edit-user/edit-user.component.html rename to packages/frontend/src/app/modules/manage-users/components/edit-user/edit-user.component.html diff --git a/packages/frontend/src/app/modules/users/components/edit-user/edit-user.component.spec.ts b/packages/frontend/src/app/modules/manage-users/components/edit-user/edit-user.component.spec.ts similarity index 100% rename from packages/frontend/src/app/modules/users/components/edit-user/edit-user.component.spec.ts rename to packages/frontend/src/app/modules/manage-users/components/edit-user/edit-user.component.spec.ts diff --git a/packages/frontend/src/app/modules/users/components/edit-user/edit-user.component.ts b/packages/frontend/src/app/modules/manage-users/components/edit-user/edit-user.component.ts similarity index 83% rename from packages/frontend/src/app/modules/users/components/edit-user/edit-user.component.ts rename to packages/frontend/src/app/modules/manage-users/components/edit-user/edit-user.component.ts index ed4090ac53..234a15e524 100644 --- a/packages/frontend/src/app/modules/users/components/edit-user/edit-user.component.ts +++ b/packages/frontend/src/app/modules/manage-users/components/edit-user/edit-user.component.ts @@ -18,20 +18,21 @@ import { Title } from "@angular/platform-browser"; import { Observable, of, Subject, Subscription } from "rxjs"; import { map, takeUntil } from "rxjs/operators"; +import { EmailValidator, NoWhiteSpaceValidator } from "../../../../shared"; +import { AuthService, CustomToastService } from "../../../shared/services"; +import { UserStructure } from "@domifa/common"; +import { format } from "date-fns"; import { - FormEmailTakenValidator, UsagerLight, + FormEmailTakenValidator, } from "../../../../../_common/model"; +import { PASSWORD_VALIDATOR } from "../../../users/PASSWORD_VALIDATOR.const"; import { - PasswordValidator, UsersService, + PasswordValidator, userStructureBuilder, -} from "../../services"; -import { format } from "date-fns"; -import { PASSWORD_VALIDATOR } from "../../PASSWORD_VALIDATOR.const"; -import { EmailValidator, NoWhiteSpaceValidator } from "../../../../shared"; -import { AuthService, CustomToastService } from "../../../shared/services"; -import { UserStructure } from "@domifa/common"; +} from "../../../users/services"; +import { ManageUsersService } from "../../services/manage-users.service"; @Component({ selector: "app-edit-user", @@ -72,7 +73,8 @@ export class EditUserComponent implements OnInit, OnDestroy { constructor( private readonly authService: AuthService, - private readonly userService: UsersService, + private readonly manageUsersService: ManageUsersService, + private readonly usersService: UsersService, private readonly toastService: CustomToastService, private readonly formBuilder: UntypedFormBuilder, private readonly titleService: Title, @@ -99,7 +101,7 @@ export class EditUserComponent implements OnInit, OnDestroy { this.getLastPasswordUpdate(); if (this.me?.role !== "facteur") { - this.usagers$ = this.userService.agenda(); + this.usagers$ = this.manageUsersService.agenda(); } } @@ -155,7 +157,7 @@ export class EditUserComponent implements OnInit, OnDestroy { private getLastPasswordUpdate(): void { this.subscription.add( - this.userService.getLastPasswordUpdate().subscribe({ + this.manageUsersService.getLastPasswordUpdate().subscribe({ next: (lastPassword: Date | null) => { this.lastPasswordUpdate = lastPassword === null @@ -177,7 +179,7 @@ export class EditUserComponent implements OnInit, OnDestroy { } this.loading = true; this.subscription.add( - this.userService.patch(this.userForm.value).subscribe({ + this.manageUsersService.patch(this.userForm.value).subscribe({ next: (user: UserStructure) => { this.loading = false; this.me = userStructureBuilder.buildUserStructure(user); @@ -208,23 +210,25 @@ export class EditUserComponent implements OnInit, OnDestroy { this.loading = true; this.subscription.add( - this.userService.updateMyPassword(this.passwordForm.value).subscribe({ - next: () => { - this.loading = false; - this.editPassword = false; - this.submitted = false; - this.getLastPasswordUpdate(); - this.toastService.success( - "Félicitations ! : votre mot de passe a été modifié avec succès" - ); - }, - error: () => { - this.loading = false; - this.toastService.error( - "Une erreur est survenue, veuillez vérifier le formulaire" - ); - }, - }) + this.manageUsersService + .updateMyPassword(this.passwordForm.value) + .subscribe({ + next: () => { + this.loading = false; + this.editPassword = false; + this.submitted = false; + this.getLastPasswordUpdate(); + this.toastService.success( + "Félicitations ! : votre mot de passe a été modifié avec succès" + ); + }, + error: () => { + this.loading = false; + this.toastService.error( + "Une erreur est survenue, veuillez vérifier le formulaire" + ); + }, + }) ); } @@ -240,7 +244,7 @@ export class EditUserComponent implements OnInit, OnDestroy { } return isEmail(control.value) - ? this.userService.validateEmail(control.value).pipe( + ? this.usersService.validateEmail(control.value).pipe( takeUntil(this.unsubscribe), map((res: boolean) => { return res === false ? null : { emailTaken: true }; diff --git a/packages/frontend/src/app/modules/users/components/register-user-admin/register-user-admin.component.html b/packages/frontend/src/app/modules/manage-users/components/register-user-admin/register-user-admin.component.html similarity index 100% rename from packages/frontend/src/app/modules/users/components/register-user-admin/register-user-admin.component.html rename to packages/frontend/src/app/modules/manage-users/components/register-user-admin/register-user-admin.component.html diff --git a/packages/frontend/src/app/modules/users/components/register-user-admin/register-user-admin.component.spec.ts b/packages/frontend/src/app/modules/manage-users/components/register-user-admin/register-user-admin.component.spec.ts similarity index 100% rename from packages/frontend/src/app/modules/users/components/register-user-admin/register-user-admin.component.spec.ts rename to packages/frontend/src/app/modules/manage-users/components/register-user-admin/register-user-admin.component.spec.ts diff --git a/packages/frontend/src/app/modules/users/components/register-user-admin/register-user-admin.component.ts b/packages/frontend/src/app/modules/manage-users/components/register-user-admin/register-user-admin.component.ts similarity index 93% rename from packages/frontend/src/app/modules/users/components/register-user-admin/register-user-admin.component.ts rename to packages/frontend/src/app/modules/manage-users/components/register-user-admin/register-user-admin.component.ts index 76a8024627..9c5ee3ca2a 100644 --- a/packages/frontend/src/app/modules/users/components/register-user-admin/register-user-admin.component.ts +++ b/packages/frontend/src/app/modules/manage-users/components/register-user-admin/register-user-admin.component.ts @@ -24,8 +24,9 @@ import { NoWhiteSpaceValidator, } from "../../../../shared"; import { CustomToastService } from "../../../shared/services"; -import { UsersService, userStructureBuilder } from "../../services"; + import { UserStructure } from "@domifa/common"; +import { UsersService, userStructureBuilder } from "../../../users/services"; @Component({ animations: [fadeInOut], @@ -55,7 +56,7 @@ export class RegisterUserAdminComponent implements OnInit, OnDestroy { constructor( private readonly formBuilder: UntypedFormBuilder, - private readonly userService: UsersService, + private readonly usersService: UsersService, private readonly toastService: CustomToastService ) { this.user = userStructureBuilder.buildUserStructure({}); @@ -92,7 +93,7 @@ export class RegisterUserAdminComponent implements OnInit, OnDestroy { } else { this.loading = true; this.subscription.add( - this.userService.registerUser(this.userForm.value).subscribe({ + this.usersService.registerUser(this.userForm.value).subscribe({ next: () => { this.loading = false; this.submitted = false; @@ -117,7 +118,7 @@ export class RegisterUserAdminComponent implements OnInit, OnDestroy { control: AbstractControl ): FormEmailTakenValidator { return isEmail(control.value) - ? this.userService.validateEmail(control.value).pipe( + ? this.usersService.validateEmail(control.value).pipe( takeUntil(this.unsubscribe), map((res: boolean) => { return res === false ? null : { emailTaken: true }; diff --git a/packages/frontend/src/app/modules/users/components/user-profil/user-profil.component.html b/packages/frontend/src/app/modules/manage-users/components/user-profil/user-profil.component.html similarity index 100% rename from packages/frontend/src/app/modules/users/components/user-profil/user-profil.component.html rename to packages/frontend/src/app/modules/manage-users/components/user-profil/user-profil.component.html diff --git a/packages/frontend/src/app/modules/users/components/user-profil/user-profil.component.spec.ts b/packages/frontend/src/app/modules/manage-users/components/user-profil/user-profil.component.spec.ts similarity index 100% rename from packages/frontend/src/app/modules/users/components/user-profil/user-profil.component.spec.ts rename to packages/frontend/src/app/modules/manage-users/components/user-profil/user-profil.component.spec.ts diff --git a/packages/frontend/src/app/modules/users/components/user-profil/user-profil.component.ts b/packages/frontend/src/app/modules/manage-users/components/user-profil/user-profil.component.ts similarity index 96% rename from packages/frontend/src/app/modules/users/components/user-profil/user-profil.component.ts rename to packages/frontend/src/app/modules/manage-users/components/user-profil/user-profil.component.ts index c4a4ad6dcb..374b9a2b9e 100644 --- a/packages/frontend/src/app/modules/users/components/user-profil/user-profil.component.ts +++ b/packages/frontend/src/app/modules/manage-users/components/user-profil/user-profil.component.ts @@ -10,9 +10,10 @@ import { UserStructureProfile, } from "../../../../../_common/model"; import { AuthService } from "../../../shared/services/auth.service"; -import { UsersService } from "../../services/users.service"; + import { differenceInCalendarDays } from "date-fns"; import { SortValues, UserStructure, UserStructureRole } from "@domifa/common"; +import { ManageUsersService } from "../../services/manage-users.service"; @Component({ selector: "app-user-profil", @@ -37,7 +38,7 @@ export class UserProfilComponent implements OnInit, OnDestroy { }; constructor( private readonly authService: AuthService, - private readonly userService: UsersService, + private readonly userService: ManageUsersService, private readonly modalService: NgbModal, private readonly toastService: CustomToastService, private readonly titleService: Title diff --git a/packages/frontend/src/app/modules/manage-users/manage-users-routing.module.ts b/packages/frontend/src/app/modules/manage-users/manage-users-routing.module.ts new file mode 100644 index 0000000000..92b7e9915a --- /dev/null +++ b/packages/frontend/src/app/modules/manage-users/manage-users-routing.module.ts @@ -0,0 +1,26 @@ +import { NgModule } from "@angular/core"; +import { Routes, RouterModule } from "@angular/router"; + +import { AuthGuard, ResponsableGuard } from "../../guards"; +import { EditUserComponent } from "./components/edit-user/edit-user.component"; +import { UserProfilComponent } from "./components/user-profil/user-profil.component"; + +const routes: Routes = [ + { + canActivate: [AuthGuard], + component: EditUserComponent, + path: "my-account", + }, + + { + canActivate: [AuthGuard, ResponsableGuard], + component: UserProfilComponent, + path: "accounts", + }, +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], +}) +export class ManageUsersRoutingModule {} diff --git a/packages/frontend/src/app/modules/manage-users/manage-users.module.ts b/packages/frontend/src/app/modules/manage-users/manage-users.module.ts new file mode 100644 index 0000000000..2813e14c73 --- /dev/null +++ b/packages/frontend/src/app/modules/manage-users/manage-users.module.ts @@ -0,0 +1,34 @@ +import { NgModule } from "@angular/core"; +import { CommonModule } from "@angular/common"; +import { EditUserComponent } from "./components/edit-user/edit-user.component"; +import { HttpClientModule } from "@angular/common/http"; +import { FormsModule, ReactiveFormsModule } from "@angular/forms"; +import { NgbModule } from "@ng-bootstrap/ng-bootstrap"; +import { TableHeadSortComponent } from "../shared/components/table-head-sort/table-head-sort.component"; +import { SortArrayPipe } from "../shared/pipes"; +import { SharedModule } from "../shared/shared.module"; +import { UserProfilComponent } from "./components/user-profil/user-profil.component"; +import { UsersModule } from "../users/users.module"; +import { RegisterUserAdminComponent } from "./components/register-user-admin/register-user-admin.component"; +import { ManageUsersRoutingModule } from "./manage-users-routing.module"; + +@NgModule({ + declarations: [ + UserProfilComponent, + EditUserComponent, + RegisterUserAdminComponent, + ], + imports: [ + TableHeadSortComponent, + FormsModule, + HttpClientModule, + NgbModule, + UsersModule, + ReactiveFormsModule, + SharedModule, + CommonModule, + ManageUsersRoutingModule, + SortArrayPipe, + ], +}) +export class ManageUsersModule {} diff --git a/packages/frontend/src/app/modules/manage-users/services/manage-users.service.spec.ts b/packages/frontend/src/app/modules/manage-users/services/manage-users.service.spec.ts new file mode 100644 index 0000000000..276c155625 --- /dev/null +++ b/packages/frontend/src/app/modules/manage-users/services/manage-users.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from "@angular/core/testing"; + +import { ManageUsersService } from "./manage-users.service"; + +describe("ManageUsersService", () => { + let service: ManageUsersService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(ManageUsersService); + }); + + it("should be created", () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/packages/frontend/src/app/modules/manage-users/services/manage-users.service.ts b/packages/frontend/src/app/modules/manage-users/services/manage-users.service.ts new file mode 100644 index 0000000000..859a2f960b --- /dev/null +++ b/packages/frontend/src/app/modules/manage-users/services/manage-users.service.ts @@ -0,0 +1,79 @@ +import { HttpClient } from "@angular/common/http"; +import { Injectable } from "@angular/core"; +import { UserStructure, UserStructureRole, ApiMessage } from "@domifa/common"; +import { BehaviorSubject, Observable, map } from "rxjs"; +import { + UserStructureEditProfile, + UserStructureProfile, + UsagerLight, +} from "../../../../_common/model"; +import { environment } from "../../../../environments/environment"; +import { userStructureBuilder } from "../../users/services"; + +@Injectable({ + providedIn: "root", +}) +export class ManageUsersService { + private endPoint = environment.apiUrl + "users"; + private usersSubject = new BehaviorSubject([]); + + readonly users$ = this.usersSubject.asObservable(); + readonly usersMap: { [key: number]: string } = {}; + public users: UserStructureProfile[] = []; + + constructor(private readonly http: HttpClient) { + this.http.get(this.endPoint).subscribe((users) => { + this.usersSubject.next(users); + this.users = users; + users.forEach((user) => { + this.usersMap[user.id] = `${user.nom} ${user.prenom}`; + }); + }); + } + + public patch(userInfos: UserStructureEditProfile): Observable { + return this.http.patch(`${this.endPoint}`, userInfos).pipe( + map((response) => { + return userStructureBuilder.buildUserStructure(response); + }) + ); + } + + public getUsers(): Observable { + return this.http.get(`${this.endPoint}`); + } + + public updateRole( + uuid: string, + role: UserStructureRole + ): Observable { + return this.http.patch( + `${this.endPoint}/update-role/${uuid}`, + { + role, + } + ); + } + + public deleteUser(uuid: string): Observable { + return this.http.delete(`${this.endPoint}/${uuid}`); + } + + public getLastPasswordUpdate(): Observable { + return this.http.get(`${this.endPoint}/last-password-update`); + } + + public agenda(): Observable { + return this.http.get(`${environment.apiUrl}agenda`); + } + public updateMyPassword(data: { + passwordConfirmation: string; + password: string; + oldPassword: string; + }): Observable { + return this.http.post( + `${this.endPoint}/edit-my-password`, + data + ); + } +} diff --git a/packages/frontend/src/app/modules/usager-dossier/components/display-etat-civil-decision/display-etat-civil-decision.component.html b/packages/frontend/src/app/modules/usager-dossier/components/display-etat-civil-decision/display-etat-civil-decision.component.html index 548cef2ba7..24e46096a4 100644 --- a/packages/frontend/src/app/modules/usager-dossier/components/display-etat-civil-decision/display-etat-civil-decision.component.html +++ b/packages/frontend/src/app/modules/usager-dossier/components/display-etat-civil-decision/display-etat-civil-decision.component.html @@ -30,6 +30,13 @@ {{ usager.numeroDistribution || "Non renseigné" }}

+ +

+ Référent.e au sein de la structure + + {{ usager.referrerId | referrerName }} + +

diff --git a/packages/frontend/src/app/modules/usager-dossier/components/step-etat-civil/step-etat-civil.component.html b/packages/frontend/src/app/modules/usager-dossier/components/step-etat-civil/step-etat-civil.component.html index faac9424e2..f94eb0dc26 100644 --- a/packages/frontend/src/app/modules/usager-dossier/components/step-etat-civil/step-etat-civil.component.html +++ b/packages/frontend/src/app/modules/usager-dossier/components/step-etat-civil/step-etat-civil.component.html @@ -392,6 +392,22 @@

État civil du demandeur

/> Exemple : BP 102, TSA 11000 +
+ + +
required > -

, private readonly formBuilder: UntypedFormBuilder, private readonly documentService: DocumentService, - private readonly nbgDate: NgbDateCustomParserFormatter + private readonly nbgDate: NgbDateCustomParserFormatter, + private readonly manageUsersService: ManageUsersService ) { super( authService, @@ -166,20 +171,19 @@ export class StepRdvComponent ); this.rdvForm.controls.jourRdv.setValue(this.usager.rdv.jourRdv); - this.editRdv = this.usager.rdv.userId === null; - this.subscription.add( - this.usagerDossierService - .getAllUsersForAgenda() - .subscribe((users: UserStructure[]) => { - this.agents = users; + this.subscription.add( + this.manageUsersService.users$.subscribe( + (users: UserStructureProfile[]) => { + this.users = users.filter((user) => user.role !== "facteur"); const userIdRdv = this.usager.rdv?.userId || this.me?.id; this.rdvForm.controls.userId.setValue(userIdRdv, { onlySelf: true, }); - }) + } + ) ); } @@ -277,7 +281,6 @@ export class StepRdvComponent } const heureRdv = control.value.split(":"); - const dateRdv: Date = setMinutes( setHours(new Date(), heureRdv[0]), heureRdv[1] diff --git a/packages/frontend/src/app/modules/usager-dossier/services/usager-dossier.service.ts b/packages/frontend/src/app/modules/usager-dossier/services/usager-dossier.service.ts index 07783e6e77..c55ba25aba 100644 --- a/packages/frontend/src/app/modules/usager-dossier/services/usager-dossier.service.ts +++ b/packages/frontend/src/app/modules/usager-dossier/services/usager-dossier.service.ts @@ -1,16 +1,15 @@ import { HttpClient } from "@angular/common/http"; import { Injectable } from "@angular/core"; import { Observable } from "rxjs"; -import { map, tap } from "rxjs/operators"; +import { tap } from "rxjs/operators"; import { environment } from "src/environments/environment"; import { UsagerEtatCivilFormData, UsagerLight, } from "../../../../_common/model"; -import { userStructureBuilder } from "../../users/services"; import { Store } from "@ngrx/store"; -import { UserStructure, Usager } from "@domifa/common"; +import { Usager } from "@domifa/common"; import { usagerActions, UsagerState } from "../../../shared"; import { RdvForm } from "../types"; @@ -102,16 +101,4 @@ export class UsagerDossierService { }) ); } - - public getAllUsersForAgenda(): Observable { - return this.http.get(environment.apiUrl + "agenda/users").pipe( - map((response) => { - return Array.isArray(response) - ? response.map((item) => - userStructureBuilder.buildUserStructure(item) - ) - : [userStructureBuilder.buildUserStructure(response)]; - }) - ); - } } diff --git a/packages/frontend/src/app/modules/usager-profil/components/_general-section/display-etat-civil/display-etat-civil.component.html b/packages/frontend/src/app/modules/usager-profil/components/_general-section/display-etat-civil/display-etat-civil.component.html index 6b38f2d35c..48ecf0b5bf 100644 --- a/packages/frontend/src/app/modules/usager-profil/components/_general-section/display-etat-civil/display-etat-civil.component.html +++ b/packages/frontend/src/app/modules/usager-profil/components/_general-section/display-etat-civil/display-etat-civil.component.html @@ -49,6 +49,13 @@ {{ usager.numeroDistribution || "Non renseigné" }}

+ +

+ Référent.e au sein de la structure + + {{ usager.referrerId | referrerName }} + +

diff --git a/packages/frontend/src/app/modules/usager-shared/components/etat-civil-parent-form/etat-civil-parent-form.component.ts b/packages/frontend/src/app/modules/usager-shared/components/etat-civil-parent-form/etat-civil-parent-form.component.ts index ffff0789d5..1fd7d6e833 100644 --- a/packages/frontend/src/app/modules/usager-shared/components/etat-civil-parent-form/etat-civil-parent-form.component.ts +++ b/packages/frontend/src/app/modules/usager-shared/components/etat-civil-parent-form/etat-civil-parent-form.component.ts @@ -34,6 +34,7 @@ import { UsagerEtatCivilFormData, UsagerFormAyantDroit, PREFERRED_COUNTRIES, + UserStructureProfile, } from "../../../../../_common/model"; import { minDateToday, @@ -62,6 +63,7 @@ import { } from "@domifa/common"; import { COUNTRIES } from "@domifa/common"; import { languagesAutocomplete } from "../../utils/languages"; +import { ManageUsersService } from "../../../manage-users/services/manage-users.service"; @Component({ selector: "app-etat-civil-parent-form", @@ -100,8 +102,8 @@ export class EtatCivilParentFormComponent implements OnDestroy { }); public subscription = new Subscription(); - public currentUserSubject$: Observable; + public users: UserStructureProfile[] = []; @ViewChildren("adName") public firstInputs!: QueryList; @@ -117,7 +119,8 @@ export class EtatCivilParentFormComponent implements OnDestroy { constructor( protected readonly formBuilder: UntypedFormBuilder, protected readonly authService: AuthService, - protected readonly changeDetectorRef: ChangeDetectorRef + protected readonly changeDetectorRef: ChangeDetectorRef, + protected readonly manageUsersService: ManageUsersService ) { this.submitted = false; this.loading = false; @@ -125,7 +128,7 @@ export class EtatCivilParentFormComponent implements OnDestroy { this.minDateToday = minDateToday; this.minDateNaissance = minDateNaissance; this.maxDateNaissance = formatDateToNgb(new Date()); - + this.users = this.manageUsersService.users; this.currentUserSubject$ = this.authService.currentUserSubject; } @@ -166,6 +169,7 @@ export class EtatCivilParentFormComponent implements OnDestroy { ], sexe: [this.usager.sexe, Validators.required], surnom: [this.usager.surnom, [Validators.maxLength(100)]], + referrerId: [this.usager.referrerId], villeNaissance: [ this.usager.villeNaissance, [Validators.required, NoWhiteSpaceValidator, Validators.maxLength(100)], @@ -195,7 +199,6 @@ export class EtatCivilParentFormComponent implements OnDestroy { this.updatePlaceHolder(this.usagerForm.value?.telephone?.countryCode); } - // Gestion des ayant-droits public addAyantDroit(ayantDroit: AyantDroit = new AyantDroit()): void { (this.usagerForm.controls.ayantsDroits as UntypedFormArray).push( this.newAyantDroit(ayantDroit) @@ -286,6 +289,9 @@ export class EtatCivilParentFormComponent implements OnDestroy { email: formValue?.email.toLowerCase().trim() || null, telephone, numeroDistribution: formValue?.numeroDistribution || null, + referrerId: formValue?.referrerId + ? parseInt(formValue?.referrerId) + : null, ayantsDroits, contactByPhone: formValue?.contactByPhone, dateNaissance: endOfDay(parseDateFromNgb(formValue.dateNaissance)), diff --git a/packages/frontend/src/app/modules/usager-shared/components/profil-etat-civil-form/profil-etat-civil-form.component.html b/packages/frontend/src/app/modules/usager-shared/components/profil-etat-civil-form/profil-etat-civil-form.component.html index 5027607fe8..87d1e4dd13 100644 --- a/packages/frontend/src/app/modules/usager-shared/components/profil-etat-civil-form/profil-etat-civil-form.component.html +++ b/packages/frontend/src/app/modules/usager-shared/components/profil-etat-civil-form/profil-etat-civil-form.component.html @@ -284,6 +284,21 @@ /> Exemple : BP 102, TSA 11000
+ +
+ + +
diff --git a/packages/frontend/src/app/modules/usager-shared/components/profil-etat-civil-form/profil-etat-civil-form.component.ts b/packages/frontend/src/app/modules/usager-shared/components/profil-etat-civil-form/profil-etat-civil-form.component.ts index 141e9a9ecb..1940c28498 100644 --- a/packages/frontend/src/app/modules/usager-shared/components/profil-etat-civil-form/profil-etat-civil-form.component.ts +++ b/packages/frontend/src/app/modules/usager-shared/components/profil-etat-civil-form/profil-etat-civil-form.component.ts @@ -19,6 +19,7 @@ import { CustomToastService } from "src/app/modules/shared/services/custom-toast import { UsagerFormModel } from "../../interfaces"; import { AuthService } from "../../../shared/services/auth.service"; +import { ManageUsersService } from "../../../manage-users/services/manage-users.service"; @Component({ selector: "app-profil-etat-civil-form", @@ -38,10 +39,11 @@ export class ProfilEtatCivilFormComponent public authService: AuthService, public formBuilder: UntypedFormBuilder, public changeDetectorRef: ChangeDetectorRef, + public manageUsersService: ManageUsersService, private readonly toastService: CustomToastService, private readonly etatCivilService: UsagerService ) { - super(formBuilder, authService, changeDetectorRef); + super(formBuilder, authService, changeDetectorRef, manageUsersService); this.displayContactDetails = false; } diff --git a/packages/frontend/src/app/modules/usager-shared/interfaces/UsagerFormModel.ts b/packages/frontend/src/app/modules/usager-shared/interfaces/UsagerFormModel.ts index 709e9ecfb4..ea8c1cf86b 100644 --- a/packages/frontend/src/app/modules/usager-shared/interfaces/UsagerFormModel.ts +++ b/packages/frontend/src/app/modules/usager-shared/interfaces/UsagerFormModel.ts @@ -82,6 +82,7 @@ export class UsagerFormModel implements Usager { public nationalite: string | null; public nbNotes?: number = 0; + public referrerId?: number | null; constructor(usager?: Usager) { this.pinnedNote = usager?.pinnedNote || null; @@ -94,6 +95,7 @@ export class UsagerFormModel implements Usager { this.prenom = usager?.prenom || ""; this.langue = usager?.langue || ""; this.numeroDistribution = usager?.numeroDistribution || null; + this.referrerId = usager?.referrerId || null; this.surnom = usager?.surnom || ""; this.dateNaissance = usager?.dateNaissance diff --git a/packages/frontend/src/app/modules/usager-shared/pipes/referrer-name.pipe.ts b/packages/frontend/src/app/modules/usager-shared/pipes/referrer-name.pipe.ts new file mode 100644 index 0000000000..a5e10184fb --- /dev/null +++ b/packages/frontend/src/app/modules/usager-shared/pipes/referrer-name.pipe.ts @@ -0,0 +1,16 @@ +import { Pipe, PipeTransform } from "@angular/core"; +import { ManageUsersService } from "../../manage-users/services/manage-users.service"; + +@Pipe({ + name: "referrerName", +}) +export class ReferrerNamePipe implements PipeTransform { + constructor(private manageUsersService: ManageUsersService) {} + + transform(referrerId: number | null): string { + if (!referrerId) { + return "Aucun référent"; + } + return this.manageUsersService.usersMap[referrerId] || "Aucun référent"; + } +} diff --git a/packages/frontend/src/app/modules/usager-shared/usager-shared.module.ts b/packages/frontend/src/app/modules/usager-shared/usager-shared.module.ts index e6f5e632a1..d699cd5779 100644 --- a/packages/frontend/src/app/modules/usager-shared/usager-shared.module.ts +++ b/packages/frontend/src/app/modules/usager-shared/usager-shared.module.ts @@ -37,6 +37,7 @@ import { SortArrayPipe } from "../shared/pipes"; import { DisplayTableImageComponent } from "../shared/components/display-table-image/display-table-image.component"; import { UsagerNomCompletPipe } from "./pipes"; import { EditUsagerDocComponent } from "./components/edit-usager-doc/edit-usager-doc.component"; +import { ReferrerNamePipe } from "./pipes/referrer-name.pipe"; @NgModule({ declarations: [ @@ -57,6 +58,7 @@ import { EditUsagerDocComponent } from "./components/edit-usager-doc/edit-usager SetInteractionOutFormComponent, UploadComponent, EditUsagerDocComponent, + ReferrerNamePipe, ], imports: [ CommonModule, @@ -75,6 +77,7 @@ import { EditUsagerDocComponent } from "./components/edit-usager-doc/edit-usager ], exports: [ DecisionRadiationFormComponent, + ReferrerNamePipe, DeleteUsagerComponent, DeleteUsagerMenuComponent, DisplayAyantsDroitsComponent, diff --git a/packages/frontend/src/app/modules/users/components/reset-password/reset-password.component.ts b/packages/frontend/src/app/modules/users/components/reset-password/reset-password.component.ts index 8e911592e1..51a78fc9ff 100644 --- a/packages/frontend/src/app/modules/users/components/reset-password/reset-password.component.ts +++ b/packages/frontend/src/app/modules/users/components/reset-password/reset-password.component.ts @@ -66,7 +66,7 @@ export class ResetPasswordComponent implements OnInit, OnDestroy { } public ngOnInit(): void { - this.titleService.setTitle("Renouveller mon mot de passe DomiFa"); + this.titleService.setTitle("Renouveler mon mot de passe DomiFa"); if (this.route.snapshot.params.token) { const token = this.route.snapshot.params.token; diff --git a/packages/frontend/src/app/modules/users/services/users.service.ts b/packages/frontend/src/app/modules/users/services/users.service.ts index 3bd232a732..35e96f2314 100644 --- a/packages/frontend/src/app/modules/users/services/users.service.ts +++ b/packages/frontend/src/app/modules/users/services/users.service.ts @@ -1,16 +1,8 @@ import { HttpClient } from "@angular/common/http"; import { Injectable } from "@angular/core"; import { Observable } from "rxjs"; -import { map } from "rxjs/operators"; import { environment } from "src/environments/environment"; -import { - UsagerLight, - UserStructureEditProfile, - UserStructureProfile, -} from "../../../../_common/model"; -import { userStructureBuilder } from "./userStructureBuilder.service"; - -import { UserStructure, ApiMessage, UserStructureRole } from "@domifa/common"; +import { ApiMessage } from "@domifa/common"; @Injectable({ providedIn: "root", @@ -26,42 +18,10 @@ export class UsersService { }); } - public patch(userInfos: UserStructureEditProfile): Observable { - return this.http.patch(`${this.endPoint}`, userInfos).pipe( - map((response) => { - return userStructureBuilder.buildUserStructure(response); - }) - ); - } - - public getUsers(): Observable { - return this.http.get(`${this.endPoint}`); - } - - public updateRole( - uuid: string, - role: UserStructureRole - ): Observable { - return this.http.patch( - `${this.endPoint}/update-role/${uuid}`, - { - role, - } - ); - } - - public deleteUser(uuid: string): Observable { - return this.http.delete(`${this.endPoint}/${uuid}`); - } - public getPasswordToken(data: string) { return this.http.post(`${this.endPoint}/get-password-token`, data); } - public getLastPasswordUpdate(): Observable { - return this.http.get(`${this.endPoint}/last-password-update`); - } - public checkPasswordToken({ userId, token, @@ -83,22 +43,7 @@ export class UsersService { return this.http.post(`${this.endPoint}/reset-password`, data); } - public updateMyPassword(data: { - passwordConfirmation: string; - password: string; - oldPassword: string; - }): Observable { - return this.http.post( - `${this.endPoint}/edit-my-password`, - data - ); - } - public registerUser(data: string): Observable { return this.http.post(`${this.endPoint}/register`, data); } - - public agenda(): Observable { - return this.http.get(`${environment.apiUrl}agenda`); - } } diff --git a/packages/frontend/src/app/modules/users/users-routing.module.ts b/packages/frontend/src/app/modules/users/users-routing.module.ts index cb7ea2c340..c0c98e3a70 100644 --- a/packages/frontend/src/app/modules/users/users-routing.module.ts +++ b/packages/frontend/src/app/modules/users/users-routing.module.ts @@ -1,22 +1,17 @@ import { NgModule } from "@angular/core"; import { Routes, RouterModule } from "@angular/router"; -import { AuthGuard, LoggedGuard, ResponsableGuard } from "../../guards"; -import { EditUserComponent } from "./components/edit-user/edit-user.component"; +import { LoggedGuard } from "../../guards"; import { ResetPasswordComponent } from "./components/reset-password/reset-password.component"; -import { UserProfilComponent } from "./components/user-profil/user-profil.component"; const routes: Routes = [ { - canActivate: [AuthGuard], path: "mon-compte", - component: EditUserComponent, + redirectTo: "/manage-users/my-account", }, - { - canActivate: [AuthGuard, ResponsableGuard], - component: UserProfilComponent, path: "comptes", + redirectTo: "/manage-users/accounts", }, { canActivate: [LoggedGuard], diff --git a/packages/frontend/src/app/modules/users/users.module.ts b/packages/frontend/src/app/modules/users/users.module.ts index 1135682059..2a17a77bff 100644 --- a/packages/frontend/src/app/modules/users/users.module.ts +++ b/packages/frontend/src/app/modules/users/users.module.ts @@ -6,27 +6,15 @@ import { FormsModule, ReactiveFormsModule } from "@angular/forms"; import { NgbModule } from "@ng-bootstrap/ng-bootstrap"; import { ResetPasswordComponent } from "./components/reset-password/reset-password.component"; -import { UserProfilComponent } from "./components/user-profil/user-profil.component"; -import { RegisterUserAdminComponent } from "./components/register-user-admin/register-user-admin.component"; -import { EditUserComponent } from "./components/edit-user/edit-user.component"; import { SharedModule } from "../shared/shared.module"; -import { UsersRoutingModule } from "./users-routing.module"; import { UserStructurePasswordFormComponent } from "./components/user-structure-password-form/user-structure-password-form.component"; -import { TableHeadSortComponent } from "../shared/components/table-head-sort/table-head-sort.component"; -import { SortArrayPipe } from "../shared/pipes"; +import { UsersRoutingModule } from "./users-routing.module"; @NgModule({ - declarations: [ - ResetPasswordComponent, - UserProfilComponent, - RegisterUserAdminComponent, - EditUserComponent, - UserStructurePasswordFormComponent, - ], + declarations: [ResetPasswordComponent, UserStructurePasswordFormComponent], exports: [UserStructurePasswordFormComponent], imports: [ - TableHeadSortComponent, FormsModule, HttpClientModule, NgbModule, @@ -34,7 +22,6 @@ import { SortArrayPipe } from "../shared/pipes"; SharedModule, CommonModule, UsersRoutingModule, - SortArrayPipe, ], providers: [], schemas: [CUSTOM_ELEMENTS_SCHEMA], From 1a9cbf4d1b0b97588f332065e9b7a885a0f26bfb Mon Sep 17 00:00:00 2001 From: "Yassine R." Date: Sun, 26 Jan 2025 23:43:31 +0100 Subject: [PATCH 2/5] fix(frontend): add referrer --- .../db/dumps/domifa_test.postgres.custom.gz | Bin 101469 -> 100944 bytes ...domifa_test.postgres.restore-data-only.sql | 48 ++++------- ...st.postgres.truncate-restore-data-only.sql | 48 ++++------- .../mocks/VERIFIED_USERS_STRUCTURE.const.ts | 22 +++++ packages/backend/src/_common/mocks/index.ts | 3 +- .../_common/mocks/usagers/POST_USAGER.mock.ts | 1 + .../constants/CUSTOM_DOCS_LABELS.const.ts | 1 + .../types/StructureCustomDocKeys.type.ts | 1 + .../user-structure/UserStructure.type.ts | 30 ------- .../UserStructureProfile.type.ts | 14 ---- .../UserStructurePublic.type.ts | 2 +- .../src/_common/model/user-structure/index.ts | 2 - .../1603812391580-pr-env-create-database.ts | 1 + .../src/auth/services/auth-checker.service.ts | 8 +- .../auth/services/structures-auth.service.ts | 3 +- ...tructureSecurityPasswordChecker.service.ts | 4 +- ...eSecurityResetPasswordInitiator.service.ts | 2 +- ...ureSecurityResetPasswordUpdater.service.ts | 6 +- .../USAGER_LIGHT_ATTRIBUTES.const.ts | 1 + .../userStructureRepository.service.ts | 30 ++++++- .../export-structure-usagers.controller.ts | 6 +- .../usagersImportCreator.service.ts | 9 +- .../usager-structure-docs.controller.ts | 13 ++- .../custom-docs/buildCustomDoc.service.ts | 16 ++++ .../tests/buildCustomDoc.service.spec.ts | 8 ++ .../src/usagers/services/usagers.service.ts | 10 +-- .../renderStructureUsagersRows.ts | 49 ++++++----- .../tests/renderStructureUsagersRows.spec.ts | 7 +- .../src/users/controllers/users.controller.ts | 47 ++++------- .../services/user-usager-creator.service.ts | 4 +- .../types}/UserStructureProfile.type.ts | 13 +-- .../common/src/user-structure/types/index.ts | 1 + .../_common/model/usager/UsagerLight.type.ts | 1 + .../src/_common/model/user-structure/index.ts | 1 - .../manage-filters.component.html | 79 ++++++++++++++++++ .../manage-filters.component.ts | 9 ++ .../usager-filter/UsagersFilterCriteria.ts | 2 + .../usager-filter/usagersFilter.service.ts | 44 +++++----- .../user-profil/user-profil.component.ts | 12 +-- .../services/manage-users.service.ts | 17 +++- ...ay-contact-details-decision.component.html | 2 +- ...display-etat-civil-decision.component.html | 8 +- .../components/step-rdv/step-rdv.component.ts | 23 ++--- .../profil-general-section.component.html | 53 +++++++----- .../etat-civil-parent-form.component.ts | 7 +- .../usager-shared/pipes/referrer-name.pipe.ts | 2 +- 46 files changed, 388 insertions(+), 282 deletions(-) create mode 100644 packages/backend/src/_common/mocks/VERIFIED_USERS_STRUCTURE.const.ts delete mode 100644 packages/backend/src/_common/model/user-structure/UserStructure.type.ts delete mode 100644 packages/backend/src/_common/model/user-structure/UserStructureProfile.type.ts rename packages/{frontend/src/_common/model/user-structure => common/src/user-structure/types}/UserStructureProfile.type.ts (72%) diff --git a/_scripts/db/dumps/domifa_test.postgres.custom.gz b/_scripts/db/dumps/domifa_test.postgres.custom.gz index 46662dcc77a662010331e17fbc3ed36ac84415d3..18ac948886bb3f64f912d8115e099abd9961f266 100644 GIT binary patch delta 6994 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 delta 7545 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 N[] = [ + { + id: 1, + nom: "Martin", + prenom: "Sophie", + }, + { + id: 2, + nom: "Dubois", + prenom: "Pierre", + }, + { + id: 3, + nom: "Laurent", + prenom: "Marie", + }, +]; diff --git a/packages/backend/src/_common/mocks/index.ts b/packages/backend/src/_common/mocks/index.ts index acc95cbd0a..8bf45d55e7 100644 --- a/packages/backend/src/_common/mocks/index.ts +++ b/packages/backend/src/_common/mocks/index.ts @@ -1,4 +1,5 @@ // @index('./*', f => `export * from '${f.path}'`) +export * from "./POST_USER_STRUCTURE_BODY.mock"; export * from "./STRUCTURE_MOCK.const"; export * from "./usagers"; -export * from "./POST_USER_STRUCTURE_BODY.mock"; +export * from "./VERIFIED_USERS_STRUCTURE.const"; diff --git a/packages/backend/src/_common/mocks/usagers/POST_USAGER.mock.ts b/packages/backend/src/_common/mocks/usagers/POST_USAGER.mock.ts index 17012e056a..c94a06229f 100644 --- a/packages/backend/src/_common/mocks/usagers/POST_USAGER.mock.ts +++ b/packages/backend/src/_common/mocks/usagers/POST_USAGER.mock.ts @@ -28,6 +28,7 @@ export const POST_USAGER: { surnom: "Surnom ", villeNaissance: "Monaco", nationalite: null, + referrerId: null, }, response: { nationalite: null, diff --git a/packages/backend/src/_common/model/structure-doc/constants/CUSTOM_DOCS_LABELS.const.ts b/packages/backend/src/_common/model/structure-doc/constants/CUSTOM_DOCS_LABELS.const.ts index ae4fec2a17..542c7ec038 100644 --- a/packages/backend/src/_common/model/structure-doc/constants/CUSTOM_DOCS_LABELS.const.ts +++ b/packages/backend/src/_common/model/structure-doc/constants/CUSTOM_DOCS_LABELS.const.ts @@ -40,6 +40,7 @@ export const CUSTOM_DOCS_LABELS: { USAGER_NATIONALITE: "Nationalité", USAGER_DATE_NAISSANCE: "Date naissance", USAGER_LIEU_NAISSANCE: "Lieu naissance", + REFERENT: "Référent.e dans la structure", AYANTS_DROITS_NOMBRE: "Nombre d'ayants-droit", AYANTS_DROITS_LISTE: "La liste des ayants droits avec nom prénom et date de naissance", diff --git a/packages/backend/src/_common/model/structure-doc/types/StructureCustomDocKeys.type.ts b/packages/backend/src/_common/model/structure-doc/types/StructureCustomDocKeys.type.ts index 0a7e29b25c..fc9862c8ca 100644 --- a/packages/backend/src/_common/model/structure-doc/types/StructureCustomDocKeys.type.ts +++ b/packages/backend/src/_common/model/structure-doc/types/StructureCustomDocKeys.type.ts @@ -39,6 +39,7 @@ export type StructureCustomDocKeys = | "USAGER_NUMERO_DISTRIBUTION_SPECIALE" | "AYANTS_DROITS_LISTE" | "AYANTS_DROITS_NOMBRE" + | "REFERENT" // DECISION | "STATUT_DOM" | "TYPE_DOM" diff --git a/packages/backend/src/_common/model/user-structure/UserStructure.type.ts b/packages/backend/src/_common/model/user-structure/UserStructure.type.ts deleted file mode 100644 index be2b1c284e..0000000000 --- a/packages/backend/src/_common/model/user-structure/UserStructure.type.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { - UserStructureRole, - UserStructureMails, - StructureCommon, - AppEntity, -} from "@domifa/common"; - -export type UserStructure = AppEntity & { - id: number; - - prenom: string; - nom: string; - fonction?: string; - structureId: number; - - // login tokens - email: string; - password: string; - - lastLogin: Date; - passwordLastUpdate: Date; - - verified: boolean; - - role: UserStructureRole; // security profile - - mails: UserStructureMails; - acceptTerms: Date | null; - structure?: StructureCommon; -}; diff --git a/packages/backend/src/_common/model/user-structure/UserStructureProfile.type.ts b/packages/backend/src/_common/model/user-structure/UserStructureProfile.type.ts deleted file mode 100644 index 25febf22e5..0000000000 --- a/packages/backend/src/_common/model/user-structure/UserStructureProfile.type.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { UserStructure } from "./UserStructure.type"; - -export type UserStructureProfile = Pick< - UserStructure, - | "id" - | "email" - | "nom" - | "prenom" - | "role" - | "verified" - | "structureId" - | "structure" - | "fonction" ->; diff --git a/packages/backend/src/_common/model/user-structure/UserStructurePublic.type.ts b/packages/backend/src/_common/model/user-structure/UserStructurePublic.type.ts index 504a2fa64e..caa3750735 100644 --- a/packages/backend/src/_common/model/user-structure/UserStructurePublic.type.ts +++ b/packages/backend/src/_common/model/user-structure/UserStructurePublic.type.ts @@ -1,4 +1,4 @@ -import { UserStructure } from "./UserStructure.type"; +import { UserStructure } from "@domifa/common"; // UserStructure: attributs publics (retournés au frontend via UserStructureAuthenticated) export type UserStructurePublic = Pick< diff --git a/packages/backend/src/_common/model/user-structure/index.ts b/packages/backend/src/_common/model/user-structure/index.ts index 70c834d938..3a4ad214e3 100644 --- a/packages/backend/src/_common/model/user-structure/index.ts +++ b/packages/backend/src/_common/model/user-structure/index.ts @@ -1,8 +1,6 @@ // @index('./*', f => `export * from '${f.path}'`) export * from "./_constants"; -export * from "./UserStructure.type"; export * from "./UserStructureAuthenticated.type"; -export * from "./UserStructureProfile.type"; export * from "./UserStructurePublic.type"; export * from "./UserStructureSecurity.type"; export * from "./UserStructureSecurityEvent.type"; diff --git a/packages/backend/src/_migrations/_init-db/1603812391580-pr-env-create-database.ts b/packages/backend/src/_migrations/_init-db/1603812391580-pr-env-create-database.ts index 93ce428fcf..3710d89b52 100644 --- a/packages/backend/src/_migrations/_init-db/1603812391580-pr-env-create-database.ts +++ b/packages/backend/src/_migrations/_init-db/1603812391580-pr-env-create-database.ts @@ -430,6 +430,7 @@ async function createTables(queryRunner: QueryRunner) { "numeroDistribution" text NULL, "pinnedNote" jsonb NULL, nationalite text NULL, + "referrerId" int4 NULL, statut text DEFAULT 'INSTRUCTION'::text NOT NULL, nom_prenom_surnom_ref varchar NOT NULL, CONSTRAINT "PK_1bb36e24229bec446a281573612" PRIMARY KEY (uuid), diff --git a/packages/backend/src/auth/services/auth-checker.service.ts b/packages/backend/src/auth/services/auth-checker.service.ts index c698cdf1fa..6b168691e8 100644 --- a/packages/backend/src/auth/services/auth-checker.service.ts +++ b/packages/backend/src/auth/services/auth-checker.service.ts @@ -1,11 +1,7 @@ import { appLogger } from "../../util"; -import { - UserAuthenticated, - UserProfile, - UserStructure, -} from "../../_common/model"; +import { UserAuthenticated, UserProfile } from "../../_common/model"; import { DOMIFA_ADMIN_STRUCTURE_ID } from "./DOMIFA_ADMIN_STRUCTURE_ID.const"; -import { UserStructureRole } from "@domifa/common"; +import { UserStructure, UserStructureRole } from "@domifa/common"; export const authChecker = { checkRole, diff --git a/packages/backend/src/auth/services/structures-auth.service.ts b/packages/backend/src/auth/services/structures-auth.service.ts index bed1429df2..13cde57f2b 100644 --- a/packages/backend/src/auth/services/structures-auth.service.ts +++ b/packages/backend/src/auth/services/structures-auth.service.ts @@ -5,13 +5,12 @@ import { domifaConfig } from "../../config"; import { userStructureRepository, structureRepository } from "../../database"; import { CURRENT_JWT_PAYLOAD_VERSION, - UserStructure, UserStructureAuthenticated, UserStructureJwtPayload, UserStructurePublic, } from "../../_common/model"; import { isDomifaAdmin } from "./auth-checker.service"; -import { StructureCommon } from "@domifa/common"; +import { UserStructure, StructureCommon } from "@domifa/common"; export const APP_USER_PUBLIC_ATTRIBUTES: (keyof UserStructurePublic)[] = [ "uuid", diff --git a/packages/backend/src/database/services/app-log/user-structure-security/userStructureSecurityPasswordChecker.service.ts b/packages/backend/src/database/services/app-log/user-structure-security/userStructureSecurityPasswordChecker.service.ts index 43f183393d..cc3482ec6f 100644 --- a/packages/backend/src/database/services/app-log/user-structure-security/userStructureSecurityPasswordChecker.service.ts +++ b/packages/backend/src/database/services/app-log/user-structure-security/userStructureSecurityPasswordChecker.service.ts @@ -1,6 +1,6 @@ +import { UserStructure } from "@domifa/common"; +import { passwordGenerator } from "../../../../util"; import { userStructureRepository } from "../../user-structure"; -import { passwordGenerator } from "../../../../util/encoding/passwordGenerator.service"; -import { UserStructure } from "../../../../_common/model"; import { userStructureSecurityEventHistoryManager } from "./userStructureSecurityEventHistoryManager.service"; import { userStructureSecurityRepository } from "./userStructureSecurityRepository.service"; diff --git a/packages/backend/src/database/services/app-log/user-structure-security/userStructureSecurityResetPasswordInitiator.service.ts b/packages/backend/src/database/services/app-log/user-structure-security/userStructureSecurityResetPasswordInitiator.service.ts index b55efe52ad..84906a6a77 100644 --- a/packages/backend/src/database/services/app-log/user-structure-security/userStructureSecurityResetPasswordInitiator.service.ts +++ b/packages/backend/src/database/services/app-log/user-structure-security/userStructureSecurityResetPasswordInitiator.service.ts @@ -3,7 +3,6 @@ import { addDays } from "date-fns"; import { domifaConfig } from "../../../../config"; import { tokenGenerator } from "../../../../util"; import { - UserStructureProfile, UserStructureSecurity, UserStructureTokens, UserStructureTokenType, @@ -11,6 +10,7 @@ import { import { userStructureRepository } from "../../user-structure"; import { userStructureSecurityEventHistoryManager } from "./userStructureSecurityEventHistoryManager.service"; import { userStructureSecurityRepository } from "./userStructureSecurityRepository.service"; +import { UserStructureProfile } from "@domifa/common"; export const userStructureSecurityResetPasswordInitiator = { buildResetPasswordLink, diff --git a/packages/backend/src/database/services/app-log/user-structure-security/userStructureSecurityResetPasswordUpdater.service.ts b/packages/backend/src/database/services/app-log/user-structure-security/userStructureSecurityResetPasswordUpdater.service.ts index c95e6c603c..d10600426f 100644 --- a/packages/backend/src/database/services/app-log/user-structure-security/userStructureSecurityResetPasswordUpdater.service.ts +++ b/packages/backend/src/database/services/app-log/user-structure-security/userStructureSecurityResetPasswordUpdater.service.ts @@ -1,11 +1,9 @@ import { passwordGenerator } from "../../../../util/encoding/passwordGenerator.service"; -import { - UserStructureProfile, - UserStructureSecurity, -} from "../../../../_common/model"; +import { UserStructureSecurity } from "../../../../_common/model"; import { userStructureRepository } from "../../user-structure/userStructureRepository.service"; import { userStructureSecurityEventHistoryManager } from "./userStructureSecurityEventHistoryManager.service"; import { userStructureSecurityRepository } from "./userStructureSecurityRepository.service"; +import { UserStructureProfile } from "@domifa/common"; export const userStructureSecurityResetPasswordUpdater = { checkResetPasswordToken, diff --git a/packages/backend/src/database/services/usager/constants/USAGER_LIGHT_ATTRIBUTES.const.ts b/packages/backend/src/database/services/usager/constants/USAGER_LIGHT_ATTRIBUTES.const.ts index 37d55aa990..409106ddb6 100644 --- a/packages/backend/src/database/services/usager/constants/USAGER_LIGHT_ATTRIBUTES.const.ts +++ b/packages/backend/src/database/services/usager/constants/USAGER_LIGHT_ATTRIBUTES.const.ts @@ -23,6 +23,7 @@ export const USAGER_LIGHT_ATTRIBUTES: (keyof Usager)[] = [ "lastInteraction", "options", "historique", + "referrerId", "ayantsDroits", //"villeNaissance", "telephone", diff --git a/packages/backend/src/database/services/user-structure/userStructureRepository.service.ts b/packages/backend/src/database/services/user-structure/userStructureRepository.service.ts index 3bcdc7de4a..a3dc330213 100644 --- a/packages/backend/src/database/services/user-structure/userStructureRepository.service.ts +++ b/packages/backend/src/database/services/user-structure/userStructureRepository.service.ts @@ -1,8 +1,11 @@ import { In } from "typeorm"; -import { UserStructureProfile } from "../../../_common/model"; import { UserStructureTable } from "../../entities"; import { myDataSource } from "../_postgres"; -import { UserStructureRole, UserStructure } from "@domifa/common"; +import { + UserStructureRole, + UserStructure, + UserStructureProfile, +} from "@domifa/common"; export type AppUserForAdminEmail = Pick< UserStructure, @@ -12,6 +15,29 @@ export type AppUserForAdminEmail = Pick< export const userStructureRepository = myDataSource .getRepository(UserStructureTable) .extend({ + getVerifiedUsersByStructureId( + structureId: number + ): Promise { + return userStructureRepository.find({ + where: { + structureId, + }, + select: { + uuid: true, + id: true, + role: true, + nom: true, + prenom: true, + email: true, + createdAt: true, + lastLogin: true, + verified: true, + }, + order: { + nom: "ASC", + }, + }); + }, findVerifiedStructureUsersByRoles({ structureId, roles, diff --git a/packages/backend/src/usagers/controllers/export-structure-usagers.controller.ts b/packages/backend/src/usagers/controllers/export-structure-usagers.controller.ts index 67883b7a1e..f38c39392e 100644 --- a/packages/backend/src/usagers/controllers/export-structure-usagers.controller.ts +++ b/packages/backend/src/usagers/controllers/export-structure-usagers.controller.ts @@ -28,6 +28,7 @@ import { AppLogsService } from "../../modules/app-logs/app-logs.service"; import { domifaConfig } from "../../config"; import { UsagersFilterCriteriaStatut } from "@domifa/common"; +import { userStructureRepository } from "../../database"; let lastCpuUsage = process.cpuUsage(); let lastTime = Date.now(); @@ -91,6 +92,9 @@ export class ExportStructureUsagersController { const workbook = XLSX.utils.book_new(); + const users = await userStructureRepository.getVerifiedUsersByStructureId( + user.structureId + ); const { firstSheetHeaders, secondSheetHeaders } = renderStructureUsagersHeaders(user.structure); @@ -128,7 +132,7 @@ export class ExportStructureUsagersController { logProcessState(`Processing chunk (${currentRowUsagers}/${count})`); const { firstSheetUsagers, secondSheetEntretiens } = - renderStructureUsagersRows(chunk, user.structure); + renderStructureUsagersRows(chunk, user.structure, users); // Application du format des dates applyDateFormat(firstSheetUsagers, [ diff --git a/packages/backend/src/usagers/controllers/import/step3-create/usagersImportCreator.service.ts b/packages/backend/src/usagers/controllers/import/step3-create/usagersImportCreator.service.ts index 0ffe7b5e49..002800985e 100644 --- a/packages/backend/src/usagers/controllers/import/step3-create/usagersImportCreator.service.ts +++ b/packages/backend/src/usagers/controllers/import/step3-create/usagersImportCreator.service.ts @@ -1,4 +1,3 @@ -import { usagerHistoryStatesRepository } from "./../../../../database/services/usager/usagerHistoryStatesRepository.service"; import { ImportProcessTracker } from "../ImportProcessTracker.type"; import { UsagersImportUsager } from "../step2-validate-row"; import { usagersImportBuilder } from "./usagersImportBuilder.service"; @@ -8,11 +7,13 @@ import { usagerRepository, usagerEntretienRepository, UsagerEntretienTable, + usagerHistoryStatesRepository, } from "../../../../database"; -import { UserStructure } from "../../../../_common/model"; -import { usagersCreator } from "../../../services"; -import { UsagerHistoryStateService } from "../../../services/usagerHistoryState.service"; + +import { UsagerHistoryStateService, usagersCreator } from "../../../services"; + import { Injectable } from "@nestjs/common"; +import { UserStructure } from "@domifa/common"; @Injectable() export class ImportCreatorService { diff --git a/packages/backend/src/usagers/controllers/usager-structure-docs.controller.ts b/packages/backend/src/usagers/controllers/usager-structure-docs.controller.ts index 1dc398ef5b..317b6c77d0 100644 --- a/packages/backend/src/usagers/controllers/usager-structure-docs.controller.ts +++ b/packages/backend/src/usagers/controllers/usager-structure-docs.controller.ts @@ -19,7 +19,10 @@ import { CurrentUsager } from "../../auth/decorators/current-usager.decorator"; import { CurrentUser } from "../../auth/decorators/current-user.decorator"; import { AppUserGuard } from "../../auth/guards"; import { UsagerAccessGuard } from "../../auth/guards/usager-access.guard"; -import { structureDocRepository } from "../../database"; +import { + structureDocRepository, + userStructureRepository, +} from "../../database"; import { UserStructureAuthenticated } from "../../_common/model"; import { buildCustomDoc, @@ -62,6 +65,9 @@ export class UsagerStructureDocsController { uuid: structureDocUuid, }); + const users = await userStructureRepository.getVerifiedUsersByStructureId( + user.structureId + ); if (!doc) { return res .status(HttpStatus.BAD_REQUEST) @@ -98,6 +104,7 @@ export class UsagerStructureDocsController { structure: user.structure, date: new Date(), extraParameters: null, + users, }); try { @@ -136,6 +143,9 @@ export class UsagerStructureDocsController { let content = ""; + const users = await userStructureRepository.getVerifiedUsersByStructureId( + user.structureId + ); if (doc) { const filePath = join( "structure-documents", @@ -172,6 +182,7 @@ export class UsagerStructureDocsController { structure: user.structure, extraParameters, date: new Date(), + users, }); if (docType === "acces_espace_domicilie") { diff --git a/packages/backend/src/usagers/services/custom-docs/buildCustomDoc.service.ts b/packages/backend/src/usagers/services/custom-docs/buildCustomDoc.service.ts index ad8cce4a05..1fecd35284 100644 --- a/packages/backend/src/usagers/services/custom-docs/buildCustomDoc.service.ts +++ b/packages/backend/src/usagers/services/custom-docs/buildCustomDoc.service.ts @@ -15,6 +15,7 @@ import { UsagerOptionsProcuration, Usager, ENTRETIEN_LIEN_COMMUNE, + UserStructureProfile, } from "@domifa/common"; import { StructureCustomDocKeys, @@ -40,6 +41,7 @@ export function buildCustomDoc({ structure, date, extraParameters = null, + users, }: { usager: Usager; structure: StructureCommon; @@ -48,6 +50,7 @@ export function buildCustomDoc({ ESPACE_DOM_ID: string; ESPACE_DOM_MDP: string; } | null; + users: Pick[]; }): { [key in StructureCustomDocKeys]: string | Date | number; } { @@ -135,6 +138,7 @@ export function buildCustomDoc({ ...buildEntretienForDocs(usager), ...buildDecision(usager, structure, DATE_FORMAT.JOUR_LONG), ...buildMonDomifaForDocs(usager, extraParameters), + REFERENT: buildReferrer(usager, users), SMS_ACTIVATION: formatBoolean(usager.contactByPhone), // Transferts @@ -171,6 +175,18 @@ export function buildCustomDoc({ }; } +export const buildReferrer = ( + usager: Pick, + users: Pick[] +): string => { + if (!usager.referrerId) { + return ""; + } + + const referrer = users.find((user) => user.id === usager.referrerId); + return referrer ? `${referrer.nom} ${referrer.prenom}` : "Compte supprimé"; +}; + export const buildDecision = ( usager: Pick< Usager, diff --git a/packages/backend/src/usagers/services/custom-docs/tests/buildCustomDoc.service.spec.ts b/packages/backend/src/usagers/services/custom-docs/tests/buildCustomDoc.service.spec.ts index c0f7856d3c..dff8df63df 100644 --- a/packages/backend/src/usagers/services/custom-docs/tests/buildCustomDoc.service.spec.ts +++ b/packages/backend/src/usagers/services/custom-docs/tests/buildCustomDoc.service.spec.ts @@ -4,6 +4,7 @@ import { STRUCTURE_MOCK, USAGER_REFUS_MOCK, USAGER_VALIDE_MOCK, + VERIFIED_USERS_STRUCTURE, } from "../../../../_common/mocks"; import { StructureCustomDocTags } from "../../../../_common/model"; @@ -29,6 +30,7 @@ describe("buildCustomDoc.service", () => { structure: STRUCTURE_MOCK, date, extraParameters, + users: [...VERIFIED_USERS_STRUCTURE], }); expect(docRadiation).toEqual({ @@ -46,6 +48,7 @@ describe("buildCustomDoc.service", () => { structure: STRUCTURE_MOCK, date, extraParameters: null, + users: [...VERIFIED_USERS_STRUCTURE], }); expect(docActif).toEqual(CUSTOM_DOC_ATTESTATION_POSTALE); @@ -63,6 +66,7 @@ describe("buildCustomDoc.service", () => { structure: STRUCTURE_MOCK, date, extraParameters: null, + users: [...VERIFIED_USERS_STRUCTURE], }); expect(docActif).toEqual(CUSTOM_DOC_COURRIER_REFUS); @@ -73,6 +77,7 @@ describe("buildCustomDoc.service", () => { structure: STRUCTURE_MOCK, date, extraParameters: null, + users: [...VERIFIED_USERS_STRUCTURE], }); expect(docNumeroDistribution.USAGER_NUMERO_DISTRIBUTION_SPECIALE).toEqual( @@ -118,6 +123,7 @@ describe("buildCustomDoc.service", () => { structure: STRUCTURE_MOCK, date, extraParameters: null, + users: [...VERIFIED_USERS_STRUCTURE], }); expect(customDocGenerated.TRANSFERT_ACTIF).toEqual("OUI"); @@ -163,6 +169,7 @@ describe("buildCustomDoc.service", () => { structure: STRUCTURE_MOCK, date, extraParameters: null, + users: [...VERIFIED_USERS_STRUCTURE], }); expect(testDoc.DATE_JOUR_HEURE).toEqual("12/04/2022 à 10:43"); @@ -182,6 +189,7 @@ describe("buildCustomDoc.service", () => { structure: STRUCTURE_MOCK, date, extraParameters: null, + users: [...VERIFIED_USERS_STRUCTURE], }); expect(testDoc.DATE_JOUR_HEURE).toEqual("23/03/2022 à 05:32"); diff --git a/packages/backend/src/usagers/services/usagers.service.ts b/packages/backend/src/usagers/services/usagers.service.ts index e823be5b3e..d22958fa19 100644 --- a/packages/backend/src/usagers/services/usagers.service.ts +++ b/packages/backend/src/usagers/services/usagers.service.ts @@ -5,11 +5,7 @@ import { usagerRepository, UsagerTable, } from "../../database"; -import { - UserStructure, - UserStructureProfile, - UserStructureAuthenticated, -} from "../../_common/model"; +import { UserStructureAuthenticated } from "../../_common/model"; import { usagersCreator } from "./usagersCreator.service"; import { usagerVisibleHistoryManager } from "./usagerVisibleHistoryManager.service"; @@ -27,6 +23,8 @@ import { UsagerDecision, Usager, UsagersFilterCriteriaStatut, + UserStructure, + UserStructureProfile, } from "@domifa/common"; import { UsagerHistoryStateService } from "./usagerHistoryState.service"; import { StructureUsagerExport } from "./xlsx-structure-usagers-renderer"; @@ -38,7 +36,7 @@ export class UsagersService { ) {} public async create( usagerDto: CreateUsagerDto, - user: UserStructureProfile + user: Pick ): Promise { const usager = new UsagerTable(usagerDto); usagersCreator.setUsagerDefaultAttributes(usager); diff --git a/packages/backend/src/usagers/services/xlsx-structure-usagers-renderer/renderStructureUsagersRows.ts b/packages/backend/src/usagers/services/xlsx-structure-usagers-renderer/renderStructureUsagersRows.ts index 83da926821..ee6cdd0d5b 100644 --- a/packages/backend/src/usagers/services/xlsx-structure-usagers-renderer/renderStructureUsagersRows.ts +++ b/packages/backend/src/usagers/services/xlsx-structure-usagers-renderer/renderStructureUsagersRows.ts @@ -1,4 +1,8 @@ -import { StructureCommon, UsagerAyantDroit } from "@domifa/common"; +import { + StructureCommon, + UsagerAyantDroit, + UserStructureProfile, +} from "@domifa/common"; import set from "lodash.set"; import { @@ -72,6 +76,8 @@ export const renderStructureUsagersHeaders = ( CUSTOM_DOCS_LABELS.ENTRETIEN_ORIENTATION_DETAIL, ENTRETIEN_DOMICILIATION_EXISTANTE: CUSTOM_DOCS_LABELS.ENTRETIEN_DOMICILIATION_EXISTANTE, + ENTRETIEN_SITUATION_PROFESSIONNELLE: + CUSTOM_DOCS_LABELS.ENTRETIEN_SITUATION_PROFESSIONNELLE, ENTRETIEN_REVENUS: CUSTOM_DOCS_LABELS.ENTRETIEN_REVENUS, ENTRETIEN_REVENUS_DETAIL: CUSTOM_DOCS_LABELS.ENTRETIEN_REVENUS_DETAIL, ENTRETIEN_LIEN_COMMUNE: CUSTOM_DOCS_LABELS.ENTRETIEN_LIEN_COMMUNE, @@ -85,8 +91,7 @@ export const renderStructureUsagersHeaders = ( ENTRETIEN_ACCOMPAGNEMENT_DETAIL: CUSTOM_DOCS_LABELS.ENTRETIEN_ACCOMPAGNEMENT_DETAIL, ENTRETIEN_RATTACHEMENT: CUSTOM_DOCS_LABELS.ENTRETIEN_RATTACHEMENT, - ENTRETIEN_SITUATION_PROFESSIONNELLE: - CUSTOM_DOCS_LABELS.ENTRETIEN_SITUATION_PROFESSIONNELLE, + ENTRETIEN_COMMENTAIRE: CUSTOM_DOCS_LABELS.ENTRETIEN_COMMENTAIRE, }; @@ -123,7 +128,8 @@ export const renderStructureUsagersHeaders = ( export const renderStructureUsagersRows = ( usagers: StructureUsagerExport[], - structure: StructureCommon + structure: StructureCommon, + users: Pick[] ): { firstSheetUsagers: StructureCustomDocTags[]; secondSheetEntretiens: StructureCustomDocTags[]; @@ -133,21 +139,23 @@ export const renderStructureUsagersRows = ( for (const usagerToExport of usagers) { try { + const firstPartOfData = buildCustomDoc({ + usager: { + ...usagerToExport, + structureId: structure.id, + contactByPhone: null, + etapeDemande: null, + statut: usagerToExport.decision.statut, + rdv: null, + pinnedNote: null, + }, + structure, + date: new Date(), + extraParameters: null, + users, + }); const customData = { - ...buildCustomDoc({ - usager: { - ...usagerToExport, - structureId: structure.id, - contactByPhone: null, - etapeDemande: null, - statut: usagerToExport.decision.statut, - rdv: null, - pinnedNote: null, - }, - structure, - date: new Date(), - extraParameters: null, - }), + ...firstPartOfData, ...buildDecision(usagerToExport, structure, DATE_FORMAT.JOUR), }; @@ -210,6 +218,7 @@ export const renderFirstSheetData = ( DATE_FIN_DOM: usager.DATE_FIN_DOM, DATE_PREMIERE_DOM: usager.DATE_PREMIERE_DOM, DATE_DERNIER_PASSAGE: usager.DATE_DERNIER_PASSAGE, + REFERENT: usager.REFERENT, AYANTS_DROITS_NOMBRE: usager.AYANTS_DROITS_NOMBRE, TRANSFERT_ACTIF: usager.TRANSFERT_ACTIF, TRANSFERT_NOM: usager.TRANSFERT_NOM, @@ -236,6 +245,8 @@ export const renderSecondSheetData = ( ENTRETIEN_ORIENTATION: usager.ENTRETIEN_ORIENTATION, ENTRETIEN_ORIENTATION_DETAIL: usager.ENTRETIEN_ORIENTATION_DETAIL, ENTRETIEN_DOMICILIATION_EXISTANTE: usager.ENTRETIEN_DOMICILIATION_EXISTANTE, + ENTRETIEN_SITUATION_PROFESSIONNELLE: + usager.ENTRETIEN_SITUATION_PROFESSIONNELLE, ENTRETIEN_REVENUS: usager.ENTRETIEN_REVENUS, ENTRETIEN_REVENUS_DETAIL: usager.ENTRETIEN_REVENUS_DETAIL, ENTRETIEN_LIEN_COMMUNE: usager.ENTRETIEN_LIEN_COMMUNE, @@ -246,8 +257,6 @@ export const renderSecondSheetData = ( ENTRETIEN_ACCOMPAGNEMENT: usager.ENTRETIEN_ACCOMPAGNEMENT, ENTRETIEN_ACCOMPAGNEMENT_DETAIL: usager.ENTRETIEN_ACCOMPAGNEMENT_DETAIL, ENTRETIEN_RATTACHEMENT: usager.ENTRETIEN_RATTACHEMENT, - ENTRETIEN_SITUATION_PROFESSIONNELLE: - usager.ENTRETIEN_SITUATION_PROFESSIONNELLE, ENTRETIEN_COMMENTAIRE: usager.ENTRETIEN_COMMENTAIRE, }; }; diff --git a/packages/backend/src/usagers/services/xlsx-structure-usagers-renderer/tests/renderStructureUsagersRows.spec.ts b/packages/backend/src/usagers/services/xlsx-structure-usagers-renderer/tests/renderStructureUsagersRows.spec.ts index c744fc3527..902270f79f 100644 --- a/packages/backend/src/usagers/services/xlsx-structure-usagers-renderer/tests/renderStructureUsagersRows.spec.ts +++ b/packages/backend/src/usagers/services/xlsx-structure-usagers-renderer/tests/renderStructureUsagersRows.spec.ts @@ -3,6 +3,7 @@ import { STRUCTURE_MOCK, USAGER_REFUS_MOCK, USAGER_VALIDE_MOCK, + VERIFIED_USERS_STRUCTURE, } from "../../../../_common/mocks"; import { renderStructureUsagersRows } from "../renderStructureUsagersRows"; import { FIRST_SHEET_USAGERS } from "./FIRST_SHEET_USAGERS.mock"; @@ -14,7 +15,11 @@ describe("renderStructureUsagersRows", () => { ]; it("Generate sheets", async () => { - const chips = renderStructureUsagersRows(usagers, STRUCTURE_MOCK); + const chips = renderStructureUsagersRows( + usagers, + STRUCTURE_MOCK, + VERIFIED_USERS_STRUCTURE + ); expect(chips.firstSheetUsagers.length).toEqual(2); expect(chips.firstSheetUsagers[0]).toEqual(FIRST_SHEET_USAGERS[0]); expect(chips.firstSheetUsagers[1]).toEqual(FIRST_SHEET_USAGERS[1]); diff --git a/packages/backend/src/users/controllers/users.controller.ts b/packages/backend/src/users/controllers/users.controller.ts index 61c9b16836..c09ab12296 100644 --- a/packages/backend/src/users/controllers/users.controller.ts +++ b/packages/backend/src/users/controllers/users.controller.ts @@ -14,11 +14,6 @@ import { import { AuthGuard } from "@nestjs/passport"; import { ApiBearerAuth, ApiOperation, ApiTags } from "@nestjs/swagger"; -import { AllowUserStructureRoles } from "../../auth/decorators"; -import { CurrentChosenUserStructure } from "../../auth/decorators/current-chosen-user-structure.decorator"; -import { CurrentUser } from "../../auth/decorators/current-user.decorator"; -import { AppUserGuard } from "../../auth/guards"; -import { CanGetUserStructureGuard } from "../../auth/guards/CanGetUserStructure.guard"; import { userStructureRepository, structureRepository, @@ -28,19 +23,27 @@ import { import { Response } from "express"; import { - UserStructure, UserStructureAuthenticated, - UserStructureProfile, USER_STRUCTURE_ROLE_ALL, } from "../../_common/model"; -import { EditMyPasswordDto } from "../dto/edit-my-password.dto"; -import { RegisterUserAdminDto } from "../dto/register-user-admin.dto"; -import { UpdateRoleDto } from "../dto/update-role.dto"; -import { UserEditDto } from "../dto/user-edit.dto"; + import { usersDeletor } from "../services"; import { userStructureCreator } from "../services/user-structure-creator.service"; +import { UserStructureProfile, UserStructure } from "@domifa/common"; import { userAccountCreatedByAdminEmailSender } from "../../modules/mails/services/templates-renderers/user-account-created-by-admin"; +import { + UpdateRoleDto, + UserEditDto, + RegisterUserAdminDto, + EditMyPasswordDto, +} from "../dto"; +import { + AllowUserStructureRoles, + CurrentUser, + CurrentChosenUserStructure, +} from "../../auth/decorators"; +import { AppUserGuard, CanGetUserStructureGuard } from "../../auth/guards"; @Controller("users") @ApiTags("users") @UseGuards(AuthGuard("jwt"), AppUserGuard) @@ -52,25 +55,9 @@ export class UsersController { public getUsers( @CurrentUser() user: UserStructureAuthenticated ): Promise { - return userStructureRepository.find({ - where: { - structureId: user.structureId, - }, - select: { - uuid: true, - id: true, - role: true, - nom: true, - prenom: true, - email: true, - createdAt: true, - lastLogin: true, - verified: true, - }, - order: { - nom: "ASC", - }, - }); + return userStructureRepository.getVerifiedUsersByStructureId( + user.structureId + ); } @AllowUserStructureRoles(...USER_STRUCTURE_ROLE_ALL) diff --git a/packages/backend/src/users/services/user-usager-creator.service.ts b/packages/backend/src/users/services/user-usager-creator.service.ts index 3b991dc0b1..5c3dc53d76 100644 --- a/packages/backend/src/users/services/user-usager-creator.service.ts +++ b/packages/backend/src/users/services/user-usager-creator.service.ts @@ -3,9 +3,9 @@ import { userUsagerSecurityRepository, UserUsagerTable, } from "../../database"; -import { UserStructure, UserUsagerSecurity } from "../../_common/model"; +import { UserUsagerSecurity } from "../../_common/model"; import { userUsagerLoginPasswordGenerator } from "./user-usager-login-password-generator.service"; -import { UserUsager } from "@domifa/common"; +import { UserStructure, UserUsager } from "@domifa/common"; export const userUsagerCreator = { createUserWithTmpPassword, diff --git a/packages/frontend/src/_common/model/user-structure/UserStructureProfile.type.ts b/packages/common/src/user-structure/types/UserStructureProfile.type.ts similarity index 72% rename from packages/frontend/src/_common/model/user-structure/UserStructureProfile.type.ts rename to packages/common/src/user-structure/types/UserStructureProfile.type.ts index 431deff858..346acc7fe8 100644 --- a/packages/frontend/src/_common/model/user-structure/UserStructureProfile.type.ts +++ b/packages/common/src/user-structure/types/UserStructureProfile.type.ts @@ -1,14 +1,15 @@ -import { UserStructure } from "@domifa/common"; +import { UserStructure } from "../interfaces"; export type UserStructureProfile = Pick< UserStructure, - | "email" + | "uuid" + | "id" + | "role" | "nom" | "prenom" - | "role" - | "id" - | "verified" - | "uuid" + | "email" | "createdAt" | "lastLogin" + | "structureId" + | "verified" >; diff --git a/packages/common/src/user-structure/types/index.ts b/packages/common/src/user-structure/types/index.ts index 85f3522d99..ff23fdc2dc 100644 --- a/packages/common/src/user-structure/types/index.ts +++ b/packages/common/src/user-structure/types/index.ts @@ -1,4 +1,5 @@ // @index('./*', f => `export * from '${f.path}'`) export * from "./UserRightStatus.type"; export * from "./UserStructureCreatedBy.type"; +export * from "./UserStructureProfile.type"; export * from "./UserStructureRole.type"; diff --git a/packages/frontend/src/_common/model/usager/UsagerLight.type.ts b/packages/frontend/src/_common/model/usager/UsagerLight.type.ts index 8afa9e7283..9b80594efb 100644 --- a/packages/frontend/src/_common/model/usager/UsagerLight.type.ts +++ b/packages/frontend/src/_common/model/usager/UsagerLight.type.ts @@ -33,6 +33,7 @@ export type UsagerLight = AppEntity & | "rdvInfo" | "pinnedNote" | "datePremiereDom" + | "referrerId" | "nbNotes" > & { phoneNumber?: string; diff --git a/packages/frontend/src/_common/model/user-structure/index.ts b/packages/frontend/src/_common/model/user-structure/index.ts index 9bc71f3305..014dcbe5bc 100644 --- a/packages/frontend/src/_common/model/user-structure/index.ts +++ b/packages/frontend/src/_common/model/user-structure/index.ts @@ -1,2 +1 @@ export * from "./UserStructureEditProfile.type"; -export * from "./UserStructureProfile.type"; diff --git a/packages/frontend/src/app/modules/manage-usagers/components/manage-filters/manage-filters.component.html b/packages/frontend/src/app/modules/manage-usagers/components/manage-filters/manage-filters.component.html index 2f7da0367f..2b171fe98b 100644 --- a/packages/frontend/src/app/modules/manage-usagers/components/manage-filters/manage-filters.component.html +++ b/packages/frontend/src/app/modules/manage-usagers/components/manage-filters/manage-filters.component.html @@ -301,6 +301,85 @@
+
+ + + +
= []; + public users: UserStructureProfile[] = []; + public subscription: Subscription = new Subscription(); + + constructor(private readonly manageUsersService: ManageUsersService) {} + ngOnInit(): void { this.sortMenuItems = this.getSortKeys(); + this.users = this.manageUsersService.referrers; } ngOnChanges() { diff --git a/packages/frontend/src/app/modules/manage-usagers/components/usager-filter/UsagersFilterCriteria.ts b/packages/frontend/src/app/modules/manage-usagers/components/usager-filter/UsagersFilterCriteria.ts index dae46d59f5..930734fb41 100644 --- a/packages/frontend/src/app/modules/manage-usagers/components/usager-filter/UsagersFilterCriteria.ts +++ b/packages/frontend/src/app/modules/manage-usagers/components/usager-filter/UsagersFilterCriteria.ts @@ -25,6 +25,7 @@ export class UsagersFilterCriteria extends Search { public interactionType: "courrierIn" | null; public lastInteractionDate: UsagersFilterCriteriaDernierPassage | null; public entretien: UsagersFilterCriteriaEntretien | null; + public referrerId?: number | null; // order by public sortKey: UsagersFilterCriteriaSortKey; @@ -41,5 +42,6 @@ export class UsagersFilterCriteria extends Search { this.page = search?.page || 1; this.sortKey = search?.sortKey || "NOM"; this.sortValue = search?.sortValue || "asc"; + this.referrerId = search?.referrerId || undefined; } } diff --git a/packages/frontend/src/app/modules/manage-usagers/components/usager-filter/usagersFilter.service.ts b/packages/frontend/src/app/modules/manage-usagers/components/usager-filter/usagersFilter.service.ts index 6f7aaa97fe..509f4bf612 100644 --- a/packages/frontend/src/app/modules/manage-usagers/components/usager-filter/usagersFilter.service.ts +++ b/packages/frontend/src/app/modules/manage-usagers/components/usager-filter/usagersFilter.service.ts @@ -26,22 +26,17 @@ function filter( }: { criteria: UsagersFilterCriteria; } -) { +): UsagerLight[] { return filterByCriteria(usagers, criteria); } function filterByCriteria( usagers: UsagerLight[], criteria: UsagersFilterCriteria -) { - const now = new Date().toISOString().split("T")[0]; - - if (criteria.entretien) { - return filterByEntretien(usagers, criteria.entretien, now); - } - +): UsagerLight[] { // Si pas de filtres ni de recherche textuelle après le traitement entretien const activeFilters = buildActiveFilters(criteria); + if (!criteria.searchString && !activeFilters.length) { return usagers; } @@ -94,6 +89,18 @@ function buildActiveFilters(criteria: UsagersFilterCriteria) { // Créer un tableau de fonctions de filtrage actives const activeFilters = []; + if (criteria.entretien) { + activeFilters.push((usager: UsagerLight) => + filterByEntretien(usager, criteria.entretien) + ); + } + + if (typeof criteria.referrerId !== "undefined") { + activeFilters.push( + (usager: UsagerLight) => usager.referrerId === criteria.referrerId + ); + } + if (criteria.statut) { activeFilters.push((usager: UsagerLight) => usagerStatutChecker.check({ usager, statut: criteria.statut }) @@ -127,16 +134,15 @@ function buildActiveFilters(criteria: UsagersFilterCriteria) { } function filterByEntretien( - usagers: UsagerLight[], - entretien: string, - now: string -): UsagerLight[] { - return usagers.filter((usager) => { - if (!usager.rdv?.dateRdv || usager.etapeDemande > ETAPE_ENTRETIEN) { - return false; - } + usager: UsagerLight, + entretien: "COMING" | "OVERDUE" +): boolean { + const now = new Date().toISOString().split("T")[0]; - const dateRdv = new Date(usager.rdv.dateRdv).toISOString().split("T")[0]; - return entretien === "COMING" ? dateRdv > now : dateRdv < now; - }); + if (!usager.rdv?.dateRdv || usager.etapeDemande > ETAPE_ENTRETIEN) { + return false; + } + + const dateRdv = new Date(usager.rdv.dateRdv).toISOString().split("T")[0]; + return entretien === "COMING" ? dateRdv > now : dateRdv < now; } diff --git a/packages/frontend/src/app/modules/manage-users/components/user-profil/user-profil.component.ts b/packages/frontend/src/app/modules/manage-users/components/user-profil/user-profil.component.ts index 374b9a2b9e..a78ae8fc50 100644 --- a/packages/frontend/src/app/modules/manage-users/components/user-profil/user-profil.component.ts +++ b/packages/frontend/src/app/modules/manage-users/components/user-profil/user-profil.component.ts @@ -5,14 +5,16 @@ import { NgbModal, NgbModalRef } from "@ng-bootstrap/ng-bootstrap"; import { CustomToastService } from "src/app/modules/shared/services/custom-toast.service"; -import { - DEFAULT_MODAL_OPTIONS, - UserStructureProfile, -} from "../../../../../_common/model"; +import { DEFAULT_MODAL_OPTIONS } from "../../../../../_common/model"; import { AuthService } from "../../../shared/services/auth.service"; import { differenceInCalendarDays } from "date-fns"; -import { SortValues, UserStructure, UserStructureRole } from "@domifa/common"; +import { + UserStructureProfile, + SortValues, + UserStructure, + UserStructureRole, +} from "@domifa/common"; import { ManageUsersService } from "../../services/manage-users.service"; @Component({ diff --git a/packages/frontend/src/app/modules/manage-users/services/manage-users.service.ts b/packages/frontend/src/app/modules/manage-users/services/manage-users.service.ts index 859a2f960b..4d170b80df 100644 --- a/packages/frontend/src/app/modules/manage-users/services/manage-users.service.ts +++ b/packages/frontend/src/app/modules/manage-users/services/manage-users.service.ts @@ -1,10 +1,14 @@ import { HttpClient } from "@angular/common/http"; import { Injectable } from "@angular/core"; -import { UserStructure, UserStructureRole, ApiMessage } from "@domifa/common"; +import { + UserStructure, + UserStructureRole, + ApiMessage, + UserStructureProfile, +} from "@domifa/common"; import { BehaviorSubject, Observable, map } from "rxjs"; import { UserStructureEditProfile, - UserStructureProfile, UsagerLight, } from "../../../../_common/model"; import { environment } from "../../../../environments/environment"; @@ -18,15 +22,20 @@ export class ManageUsersService { private usersSubject = new BehaviorSubject([]); readonly users$ = this.usersSubject.asObservable(); - readonly usersMap: { [key: number]: string } = {}; + readonly referrersMap: { [key: number]: string } = {}; + public users: UserStructureProfile[] = []; + public referrers: UserStructureProfile[] = []; constructor(private readonly http: HttpClient) { this.http.get(this.endPoint).subscribe((users) => { this.usersSubject.next(users); this.users = users; users.forEach((user) => { - this.usersMap[user.id] = `${user.nom} ${user.prenom}`; + if (user.role !== "facteur") { + this.referrers.push(user); + this.referrersMap[user.id] = `${user.nom} ${user.prenom}`; + } }); }); } diff --git a/packages/frontend/src/app/modules/usager-dossier/components/display-contact-details-decision/display-contact-details-decision.component.html b/packages/frontend/src/app/modules/usager-dossier/components/display-contact-details-decision/display-contact-details-decision.component.html index 0396c21915..aa7e4cf99d 100644 --- a/packages/frontend/src/app/modules/usager-dossier/components/display-contact-details-decision/display-contact-details-decision.component.html +++ b/packages/frontend/src/app/modules/usager-dossier/components/display-contact-details-decision/display-contact-details-decision.component.html @@ -1,5 +1,5 @@

- Téléphone: + Téléphone:  {{ usager.telephone | formatInternationalPhoneNumber }} diff --git a/packages/frontend/src/app/modules/usager-dossier/components/display-etat-civil-decision/display-etat-civil-decision.component.html b/packages/frontend/src/app/modules/usager-dossier/components/display-etat-civil-decision/display-etat-civil-decision.component.html index 24e46096a4..9eeb73634c 100644 --- a/packages/frontend/src/app/modules/usager-dossier/components/display-etat-civil-decision/display-etat-civil-decision.component.html +++ b/packages/frontend/src/app/modules/usager-dossier/components/display-etat-civil-decision/display-etat-civil-decision.component.html @@ -31,11 +31,9 @@

-

- Référent.e au sein de la structure - - {{ usager.referrerId | referrerName }} - +

+ Référent: + {{ usager.referrerId | referrerName }}

diff --git a/packages/frontend/src/app/modules/usager-dossier/components/step-rdv/step-rdv.component.ts b/packages/frontend/src/app/modules/usager-dossier/components/step-rdv/step-rdv.component.ts index 25fb7c2bc2..207bb6e0f1 100644 --- a/packages/frontend/src/app/modules/usager-dossier/components/step-rdv/step-rdv.component.ts +++ b/packages/frontend/src/app/modules/usager-dossier/components/step-rdv/step-rdv.component.ts @@ -10,10 +10,7 @@ import { import { ActivatedRoute, Router } from "@angular/router"; import { NgbDateStruct } from "@ng-bootstrap/ng-bootstrap"; -import { - UsagerLight, - UserStructureProfile, -} from "../../../../../_common/model"; +import { UsagerLight } from "../../../../../_common/model"; import { DocumentService } from "../../../usager-shared/services/document.service"; import { UsagerDossierService } from "../../services/usager-dossier.service"; import { @@ -40,7 +37,7 @@ import { AuthService, CustomToastService, } from "../../../shared/services"; -import { CerfaDocType, Usager } from "@domifa/common"; +import { CerfaDocType, Usager, UserStructureProfile } from "@domifa/common"; import { RdvForm } from "../../types"; import { ManageUsersService } from "../../../manage-users/services/manage-users.service"; @@ -173,18 +170,12 @@ export class StepRdvComponent this.rdvForm.controls.jourRdv.setValue(this.usager.rdv.jourRdv); this.editRdv = this.usager.rdv.userId === null; - this.subscription.add( - this.manageUsersService.users$.subscribe( - (users: UserStructureProfile[]) => { - this.users = users.filter((user) => user.role !== "facteur"); - const userIdRdv = this.usager.rdv?.userId || this.me?.id; + const userIdRdv = this.usager.rdv?.userId || this.me?.id; - this.rdvForm.controls.userId.setValue(userIdRdv, { - onlySelf: true, - }); - } - ) - ); + this.rdvForm.controls.userId.setValue(userIdRdv, { + onlySelf: true, + }); + this.users = this.manageUsersService.referrers; } public setValueRdv(value: boolean): void { diff --git a/packages/frontend/src/app/modules/usager-profil/components/pages/profil-general-section/profil-general-section.component.html b/packages/frontend/src/app/modules/usager-profil/components/pages/profil-general-section/profil-general-section.component.html index fd8c76be62..509eeac605 100644 --- a/packages/frontend/src/app/modules/usager-profil/components/pages/profil-general-section/profil-general-section.component.html +++ b/packages/frontend/src/app/modules/usager-profil/components/pages/profil-general-section/profil-general-section.component.html @@ -249,9 +249,11 @@

Dossier

Référence dossier
+
{{ usager.customRef }}
+
Statut
@@ -263,15 +265,24 @@

Dossier

-
- Ayants-droits -
-
- +
+ Type de domiciliation
+
+
+ Renouvellement +
+
+ Première demande +
+
Échéance
{{ usager?.echeanceInfos?.dateToDisplay | date : "dd MMMM yyyy" }} @@ -291,25 +302,21 @@

Dossier

{{ usager.datePremiereDom | date : "dd MMMM yyyy" }}
-
- Type de domiciliation + +
+ Ayants-droits +
+
+ +
+
+ Référent au sein de la structure
-
- Renouvellement -
- -
- Première demande -
+ {{ usager.referrerId | referrerName }}
-
Date: Mon, 27 Jan 2025 15:16:20 +0100 Subject: [PATCH 3/5] fix(backend): fix referrerId and unit tests --- .../agenda.controller.security-tests.ts | 14 -------------- .../tests/CUSTOM_DOC_ATTESTATION_POSTALE.const.ts | 1 + .../custom-docs/tests/CUSTOM_DOC_COURRIER_REFUS.ts | 2 +- .../src/usagers/services/usagersCreator.service.ts | 1 + .../tests/FIRST_SHEET_USAGERS.mock.ts | 2 ++ .../controllers/users.controller.security-tests.ts | 3 ++- .../src/users/controllers/users.controller.ts | 14 ++++++++++++-- packages/common/src/search/classes/Search.class.ts | 2 +- .../types/UsagersFilterCriteriaEntretien.enum.ts | 4 ++++ packages/common/src/search/types/index.ts | 1 + .../etat-civil-parent-form.component.spec.ts | 1 + 11 files changed, 26 insertions(+), 19 deletions(-) create mode 100644 packages/common/src/search/types/UsagersFilterCriteriaEntretien.enum.ts diff --git a/packages/backend/src/usagers/controllers/security-tests/agenda.controller.security-tests.ts b/packages/backend/src/usagers/controllers/security-tests/agenda.controller.security-tests.ts index 25e0a9dc72..d0fe41face 100644 --- a/packages/backend/src/usagers/controllers/security-tests/agenda.controller.security-tests.ts +++ b/packages/backend/src/usagers/controllers/security-tests/agenda.controller.security-tests.ts @@ -28,18 +28,4 @@ export const AgendaControllerSecurityTests: AppTestHttpClientSecurityTestDef[] = ), }), }, - { - label: `${CONTROLLER}.getAllUsersForAgenda`, - query: async (context: AppTestContext) => ({ - response: await AppTestHttpClient.get("/agenda/users", { - context, - }), - expectedStatus: expectedResponseStatusBuilder.allowStructureOnly( - context.user, - { - roles: ["simple", "responsable", "admin"], - } - ), - }), - }, ]; diff --git a/packages/backend/src/usagers/services/custom-docs/tests/CUSTOM_DOC_ATTESTATION_POSTALE.const.ts b/packages/backend/src/usagers/services/custom-docs/tests/CUSTOM_DOC_ATTESTATION_POSTALE.const.ts index d9cfbab7c6..1ee33960a3 100644 --- a/packages/backend/src/usagers/services/custom-docs/tests/CUSTOM_DOC_ATTESTATION_POSTALE.const.ts +++ b/packages/backend/src/usagers/services/custom-docs/tests/CUSTOM_DOC_ATTESTATION_POSTALE.const.ts @@ -87,4 +87,5 @@ export const CUSTOM_DOC_ATTESTATION_POSTALE: StructureCustomDocTags = { PROCURATION_DATE_FIN: "", PROCURATION_DATE_NAISSANCE: "", PROCURATIONS_NOMBRE: 0, + REFERENT: "", }; diff --git a/packages/backend/src/usagers/services/custom-docs/tests/CUSTOM_DOC_COURRIER_REFUS.ts b/packages/backend/src/usagers/services/custom-docs/tests/CUSTOM_DOC_COURRIER_REFUS.ts index eb4ff53686..75eba7b418 100644 --- a/packages/backend/src/usagers/services/custom-docs/tests/CUSTOM_DOC_COURRIER_REFUS.ts +++ b/packages/backend/src/usagers/services/custom-docs/tests/CUSTOM_DOC_COURRIER_REFUS.ts @@ -40,7 +40,7 @@ export const CUSTOM_DOC_COURRIER_REFUS: StructureCustomDocTags = { DATE_PREMIERE_DOM: "11 janvier 2018", DATE_RADIATION: "", PREMIERE_DOM_NOM_AGENT: "", - + REFERENT: "", ENTRETIEN_ACCOMPAGNEMENT: "NON", ENTRETIEN_ACCOMPAGNEMENT_DETAIL: "", ENTRETIEN_ORIENTATION: "OUI", diff --git a/packages/backend/src/usagers/services/usagersCreator.service.ts b/packages/backend/src/usagers/services/usagersCreator.service.ts index 14a2116ba2..9c26b24622 100644 --- a/packages/backend/src/usagers/services/usagersCreator.service.ts +++ b/packages/backend/src/usagers/services/usagersCreator.service.ts @@ -31,6 +31,7 @@ function setUsagerDefaultAttributes(usager: Partial): void { }; usager.typeDom = usager?.typeDom ?? "PREMIERE_DOM"; usager.pinnedNote = null; + usager.referrerId = null; if (!usager.ayantsDroits) { usager.ayantsDroits = []; diff --git a/packages/backend/src/usagers/services/xlsx-structure-usagers-renderer/tests/FIRST_SHEET_USAGERS.mock.ts b/packages/backend/src/usagers/services/xlsx-structure-usagers-renderer/tests/FIRST_SHEET_USAGERS.mock.ts index 8fdebe759e..09341a0d27 100644 --- a/packages/backend/src/usagers/services/xlsx-structure-usagers-renderer/tests/FIRST_SHEET_USAGERS.mock.ts +++ b/packages/backend/src/usagers/services/xlsx-structure-usagers-renderer/tests/FIRST_SHEET_USAGERS.mock.ts @@ -34,6 +34,7 @@ export const FIRST_SHEET_USAGERS = [ TRANSFERT_NOM: "", MON_DOMIFA_ACTIVATION: "NON", SMS_ACTIVATION: "NON", + REFERENT: "", }, { AD_DATE_NAISSANCE_0: "20/12/1978", @@ -74,5 +75,6 @@ export const FIRST_SHEET_USAGERS = [ TRANSFERT_NOM: "", MON_DOMIFA_ACTIVATION: "NON", SMS_ACTIVATION: "NON", + REFERENT: "", }, ]; diff --git a/packages/backend/src/users/controllers/users.controller.security-tests.ts b/packages/backend/src/users/controllers/users.controller.security-tests.ts index eb51ae1c12..83706e6f37 100644 --- a/packages/backend/src/users/controllers/users.controller.security-tests.ts +++ b/packages/backend/src/users/controllers/users.controller.security-tests.ts @@ -11,6 +11,7 @@ import { expectedResponseStatusBuilder, securityTestDataBuilder, } from "../../_tests"; +import { USER_STRUCTURE_ROLE_ALL } from "../../_common/model"; const CONTROLLER = "UserController"; @@ -24,7 +25,7 @@ export const UserControllerSecurityTests: AppTestHttpClientSecurityTestDef[] = [ expectedStatus: expectedResponseStatusBuilder.allowStructureOnly( context.user, { - roles: ["responsable", "admin"], + roles: USER_STRUCTURE_ROLE_ALL, } ), }), diff --git a/packages/backend/src/users/controllers/users.controller.ts b/packages/backend/src/users/controllers/users.controller.ts index c09ab12296..d87b90275f 100644 --- a/packages/backend/src/users/controllers/users.controller.ts +++ b/packages/backend/src/users/controllers/users.controller.ts @@ -52,12 +52,22 @@ export class UsersController { @ApiBearerAuth() @ApiOperation({ summary: "Liste des utilisateurs" }) @Get("") - public getUsers( + public async getUsers( @CurrentUser() user: UserStructureAuthenticated ): Promise { - return userStructureRepository.getVerifiedUsersByStructureId( + const users = await userStructureRepository.getVerifiedUsersByStructureId( user.structureId ); + if (user.role === "facteur" || user.role === "simple") { + return users.map((user) => { + return { + id: user.id, + nom: user.nom, + prenom: user.prenom, + }; + }) as UserStructureProfile[]; + } + return users; } @AllowUserStructureRoles(...USER_STRUCTURE_ROLE_ALL) diff --git a/packages/common/src/search/classes/Search.class.ts b/packages/common/src/search/classes/Search.class.ts index 2efcabe254..84a6375dea 100644 --- a/packages/common/src/search/classes/Search.class.ts +++ b/packages/common/src/search/classes/Search.class.ts @@ -4,7 +4,7 @@ export class Search { public searchString: string | null; public sortKey: string; public sortValue: SortValues; - public page: number; + public page = 1; constructor(search?: any) { this.searchString = search?.searchString ?? null; diff --git a/packages/common/src/search/types/UsagersFilterCriteriaEntretien.enum.ts b/packages/common/src/search/types/UsagersFilterCriteriaEntretien.enum.ts new file mode 100644 index 0000000000..a2f9c3e1b2 --- /dev/null +++ b/packages/common/src/search/types/UsagersFilterCriteriaEntretien.enum.ts @@ -0,0 +1,4 @@ +export enum UsagersFilterCriteriaEntretien { + COMING = "COMING", + OVERDUE = "OVERDUE", +} diff --git a/packages/common/src/search/types/index.ts b/packages/common/src/search/types/index.ts index 08fa2e722b..045d56ced0 100644 --- a/packages/common/src/search/types/index.ts +++ b/packages/common/src/search/types/index.ts @@ -2,4 +2,5 @@ export * from "./CriteriaSearchField.enum"; export * from "./SortValues.type"; export * from "./Timings.type"; +export * from "./UsagersFilterCriteriaEntretien.enum"; export * from "./UsagersFilterCriteriaStatut.enum"; diff --git a/packages/frontend/src/app/modules/usager-shared/components/etat-civil-parent-form/etat-civil-parent-form.component.spec.ts b/packages/frontend/src/app/modules/usager-shared/components/etat-civil-parent-form/etat-civil-parent-form.component.spec.ts index 2680628960..6744b15ddf 100644 --- a/packages/frontend/src/app/modules/usager-shared/components/etat-civil-parent-form/etat-civil-parent-form.component.spec.ts +++ b/packages/frontend/src/app/modules/usager-shared/components/etat-civil-parent-form/etat-civil-parent-form.component.spec.ts @@ -71,6 +71,7 @@ describe("EtatCivilParentFormComponent", () => { nom: "TEST", prenom: "TEST PRENOM ", sexe: "homme", + referrerId: null, surnom: "Chips", numeroDistribution: "TSA 1000", telephone: { From 2a42f9d5caa5396c9e47730c712f25b3f315c6c3 Mon Sep 17 00:00:00 2001 From: "Yassine R." Date: Mon, 27 Jan 2025 19:15:39 +0100 Subject: [PATCH 4/5] fix(backend): add validators to search --- .../backend/src/_common/decorators/index.ts | 12 +- .../{ => parse-pipes}/ParseRegion.pipe.ts | 0 .../{ => parse-pipes}/ParseString.pipe.ts | 0 .../_common/decorators/parse-pipes/index.ts | 5 + .../parse-hard-reset-token.pipe.ts | 0 .../{ => parse-pipes}/parse-token.pipe.ts | 0 .../tests/parse-hard-reset-token-pipe.spec.ts | 2 +- .../decorators/tests/parse-token-pipe.spec.ts | 2 +- .../{ => transformers}/LowerCaseDecorator.ts | 0 .../PhoneTransformDecorator.ts | 0 .../{ => transformers}/StripTagsDecorator.ts | 0 .../{ => transformers}/TrimDecorator.ts | 0 .../{ => transformers}/TrimOrNullDecorator.ts | 0 .../{ => transformers}/UpperCaseDecorator.ts | 0 .../_common/decorators/transformers/index.ts | 7 + packages/backend/src/app.bootstrap.ts | 4 + .../controllers/search-usagers.controller.ts | 212 ++++++++++++++++++ ...earch-usagers.controller.security-tests.ts | 35 +++ .../usagers.controller.security-tests.ts | 18 -- .../tests/search-usagers.controller.spec.ts | 132 +++++++++++ .../tests/usagers.controller.spec.ts | 2 - .../usagers/controllers/usagers.controller.ts | 165 +------------- .../src/usagers/dto/search-usager.dto.ts | 60 ++--- .../backend/src/usagers/usagers.module.ts | 2 + packages/backend/src/usagers/utils/index.ts | 3 + .../utils/validate-search-field.decorator.ts | 33 +++ .../usagers/utils/validate-search-field.ts | 55 +++++ .../users.public.controller.spec.ts | 1 - .../manage-filters.component.html | 4 +- .../manage-filters.component.ts | 7 +- .../usager-filter/UsagersFilterCriteria.ts | 3 +- .../usager-filter/usagersFilter.service.ts | 7 +- .../services/manage-usagers.service.ts | 33 ++- 33 files changed, 551 insertions(+), 253 deletions(-) rename packages/backend/src/_common/decorators/{ => parse-pipes}/ParseRegion.pipe.ts (100%) rename packages/backend/src/_common/decorators/{ => parse-pipes}/ParseString.pipe.ts (100%) create mode 100644 packages/backend/src/_common/decorators/parse-pipes/index.ts rename packages/backend/src/_common/decorators/{ => parse-pipes}/parse-hard-reset-token.pipe.ts (100%) rename packages/backend/src/_common/decorators/{ => parse-pipes}/parse-token.pipe.ts (100%) rename packages/backend/src/_common/decorators/{ => transformers}/LowerCaseDecorator.ts (100%) rename packages/backend/src/_common/decorators/{ => transformers}/PhoneTransformDecorator.ts (100%) rename packages/backend/src/_common/decorators/{ => transformers}/StripTagsDecorator.ts (100%) rename packages/backend/src/_common/decorators/{ => transformers}/TrimDecorator.ts (100%) rename packages/backend/src/_common/decorators/{ => transformers}/TrimOrNullDecorator.ts (100%) rename packages/backend/src/_common/decorators/{ => transformers}/UpperCaseDecorator.ts (100%) create mode 100644 packages/backend/src/_common/decorators/transformers/index.ts create mode 100644 packages/backend/src/usagers/controllers/search-usagers.controller.ts create mode 100644 packages/backend/src/usagers/controllers/security-tests/search-usagers.controller.security-tests.ts create mode 100644 packages/backend/src/usagers/controllers/tests/search-usagers.controller.spec.ts create mode 100644 packages/backend/src/usagers/utils/index.ts create mode 100644 packages/backend/src/usagers/utils/validate-search-field.decorator.ts create mode 100644 packages/backend/src/usagers/utils/validate-search-field.ts diff --git a/packages/backend/src/_common/decorators/index.ts b/packages/backend/src/_common/decorators/index.ts index 16cc1c51ea..a163f7809a 100644 --- a/packages/backend/src/_common/decorators/index.ts +++ b/packages/backend/src/_common/decorators/index.ts @@ -1,13 +1,5 @@ //@index('./*.ts', f => `export * from '${f.path}'`) export * from "./IsValidPasswordDecorator"; export * from "./IsValidPhoneDecorator"; -export * from "./LowerCaseDecorator"; -export * from "./parse-hard-reset-token.pipe"; -export * from "./parse-token.pipe"; -export * from "./ParseRegion.pipe"; -export * from "./ParseString.pipe"; -export * from "./PhoneTransformDecorator"; -export * from "./StripTagsDecorator"; -export * from "./TrimDecorator"; -export * from "./TrimOrNullDecorator"; -export * from "./UpperCaseDecorator"; +export * from "./transformers"; +export * from "./parse-pipes"; diff --git a/packages/backend/src/_common/decorators/ParseRegion.pipe.ts b/packages/backend/src/_common/decorators/parse-pipes/ParseRegion.pipe.ts similarity index 100% rename from packages/backend/src/_common/decorators/ParseRegion.pipe.ts rename to packages/backend/src/_common/decorators/parse-pipes/ParseRegion.pipe.ts diff --git a/packages/backend/src/_common/decorators/ParseString.pipe.ts b/packages/backend/src/_common/decorators/parse-pipes/ParseString.pipe.ts similarity index 100% rename from packages/backend/src/_common/decorators/ParseString.pipe.ts rename to packages/backend/src/_common/decorators/parse-pipes/ParseString.pipe.ts diff --git a/packages/backend/src/_common/decorators/parse-pipes/index.ts b/packages/backend/src/_common/decorators/parse-pipes/index.ts new file mode 100644 index 0000000000..374b767877 --- /dev/null +++ b/packages/backend/src/_common/decorators/parse-pipes/index.ts @@ -0,0 +1,5 @@ +//@index('./*.ts', f => `export * from '${f.path}'`) +export * from "./parse-hard-reset-token.pipe"; +export * from "./parse-token.pipe"; +export * from "./ParseRegion.pipe"; +export * from "./ParseString.pipe"; diff --git a/packages/backend/src/_common/decorators/parse-hard-reset-token.pipe.ts b/packages/backend/src/_common/decorators/parse-pipes/parse-hard-reset-token.pipe.ts similarity index 100% rename from packages/backend/src/_common/decorators/parse-hard-reset-token.pipe.ts rename to packages/backend/src/_common/decorators/parse-pipes/parse-hard-reset-token.pipe.ts diff --git a/packages/backend/src/_common/decorators/parse-token.pipe.ts b/packages/backend/src/_common/decorators/parse-pipes/parse-token.pipe.ts similarity index 100% rename from packages/backend/src/_common/decorators/parse-token.pipe.ts rename to packages/backend/src/_common/decorators/parse-pipes/parse-token.pipe.ts diff --git a/packages/backend/src/_common/decorators/tests/parse-hard-reset-token-pipe.spec.ts b/packages/backend/src/_common/decorators/tests/parse-hard-reset-token-pipe.spec.ts index 9f4dc94889..ad164dd814 100644 --- a/packages/backend/src/_common/decorators/tests/parse-hard-reset-token-pipe.spec.ts +++ b/packages/backend/src/_common/decorators/tests/parse-hard-reset-token-pipe.spec.ts @@ -1,5 +1,5 @@ import { BadRequestException } from "@nestjs/common"; -import { ParseHardResetTokenPipe } from "../parse-hard-reset-token.pipe"; +import { ParseHardResetTokenPipe } from "../parse-pipes/parse-hard-reset-token.pipe"; describe("ParseHardResetTokenPipe", () => { let pipe: ParseHardResetTokenPipe; diff --git a/packages/backend/src/_common/decorators/tests/parse-token-pipe.spec.ts b/packages/backend/src/_common/decorators/tests/parse-token-pipe.spec.ts index de3abfef4a..a70c1464e8 100644 --- a/packages/backend/src/_common/decorators/tests/parse-token-pipe.spec.ts +++ b/packages/backend/src/_common/decorators/tests/parse-token-pipe.spec.ts @@ -1,6 +1,6 @@ import { BadRequestException } from "@nestjs/common"; import { tokenGenerator } from "../../../util"; -import { ParseTokenPipe } from "../parse-token.pipe"; +import { ParseTokenPipe } from "../parse-pipes/parse-token.pipe"; describe("ParseTokenPipe", () => { let pipe: ParseTokenPipe; diff --git a/packages/backend/src/_common/decorators/LowerCaseDecorator.ts b/packages/backend/src/_common/decorators/transformers/LowerCaseDecorator.ts similarity index 100% rename from packages/backend/src/_common/decorators/LowerCaseDecorator.ts rename to packages/backend/src/_common/decorators/transformers/LowerCaseDecorator.ts diff --git a/packages/backend/src/_common/decorators/PhoneTransformDecorator.ts b/packages/backend/src/_common/decorators/transformers/PhoneTransformDecorator.ts similarity index 100% rename from packages/backend/src/_common/decorators/PhoneTransformDecorator.ts rename to packages/backend/src/_common/decorators/transformers/PhoneTransformDecorator.ts diff --git a/packages/backend/src/_common/decorators/StripTagsDecorator.ts b/packages/backend/src/_common/decorators/transformers/StripTagsDecorator.ts similarity index 100% rename from packages/backend/src/_common/decorators/StripTagsDecorator.ts rename to packages/backend/src/_common/decorators/transformers/StripTagsDecorator.ts diff --git a/packages/backend/src/_common/decorators/TrimDecorator.ts b/packages/backend/src/_common/decorators/transformers/TrimDecorator.ts similarity index 100% rename from packages/backend/src/_common/decorators/TrimDecorator.ts rename to packages/backend/src/_common/decorators/transformers/TrimDecorator.ts diff --git a/packages/backend/src/_common/decorators/TrimOrNullDecorator.ts b/packages/backend/src/_common/decorators/transformers/TrimOrNullDecorator.ts similarity index 100% rename from packages/backend/src/_common/decorators/TrimOrNullDecorator.ts rename to packages/backend/src/_common/decorators/transformers/TrimOrNullDecorator.ts diff --git a/packages/backend/src/_common/decorators/UpperCaseDecorator.ts b/packages/backend/src/_common/decorators/transformers/UpperCaseDecorator.ts similarity index 100% rename from packages/backend/src/_common/decorators/UpperCaseDecorator.ts rename to packages/backend/src/_common/decorators/transformers/UpperCaseDecorator.ts diff --git a/packages/backend/src/_common/decorators/transformers/index.ts b/packages/backend/src/_common/decorators/transformers/index.ts new file mode 100644 index 0000000000..61d50631fe --- /dev/null +++ b/packages/backend/src/_common/decorators/transformers/index.ts @@ -0,0 +1,7 @@ +//@index('./*.ts', f => `export * from '${f.path}'`) +export * from "./LowerCaseDecorator"; +export * from "./PhoneTransformDecorator"; +export * from "./StripTagsDecorator"; +export * from "./TrimDecorator"; +export * from "./TrimOrNullDecorator"; +export * from "./UpperCaseDecorator"; diff --git a/packages/backend/src/app.bootstrap.ts b/packages/backend/src/app.bootstrap.ts index a562d02067..2ab04e3cbb 100644 --- a/packages/backend/src/app.bootstrap.ts +++ b/packages/backend/src/app.bootstrap.ts @@ -74,6 +74,10 @@ export async function bootstrapApplication(): Promise<{ stopAtFirstError: true, enableDebugMessages: true, disableErrorMessages: domifaConfig().envId !== "local", + transform: true, + transformOptions: { + enableImplicitConversion: false, + }, }) ); diff --git a/packages/backend/src/usagers/controllers/search-usagers.controller.ts b/packages/backend/src/usagers/controllers/search-usagers.controller.ts new file mode 100644 index 0000000000..56a58f6100 --- /dev/null +++ b/packages/backend/src/usagers/controllers/search-usagers.controller.ts @@ -0,0 +1,212 @@ +import { + Usager, + UsagerDecision, + CriteriaSearchField, + getUsagerDeadlines, + ETAPE_ENTRETIEN, +} from "@domifa/common"; +import { + Body, + Controller, + Get, + ParseBoolPipe, + Post, + Query, + UseGuards, +} from "@nestjs/common"; +import { AuthGuard } from "@nestjs/passport"; +import { ApiBearerAuth } from "@nestjs/swagger"; +import { format, parse, subMinutes } from "date-fns"; +import { Not } from "typeorm"; +import { + USER_STRUCTURE_ROLE_ALL, + UserStructureAuthenticated, +} from "../../_common/model"; +import { AllowUserStructureRoles, CurrentUser } from "../../auth/decorators"; +import { AppUserGuard } from "../../auth/guards"; +import { + usagerRepository, + USAGER_LIGHT_ATTRIBUTES, + joinSelectFields, +} from "../../database"; + +import { SearchUsagerDto } from "../dto"; + +@Controller("search-usagers") +@UseGuards(AuthGuard("jwt"), AppUserGuard) +@ApiBearerAuth() +export class SearchUsagersController { + @Get() + @AllowUserStructureRoles(...USER_STRUCTURE_ROLE_ALL) + public async findAllByStructure( + @Query("chargerTousRadies", new ParseBoolPipe()) + chargerTousRadies: boolean, + @CurrentUser() user: UserStructureAuthenticated + ) { + const usagersNonRadies = await usagerRepository.find({ + where: { + statut: Not("RADIE"), + structureId: user.structureId, + }, + select: USAGER_LIGHT_ATTRIBUTES, + }); + + const usagersRadiesFirsts = await usagerRepository.find({ + where: { + statut: "RADIE", + structureId: user.structureId, + }, + select: USAGER_LIGHT_ATTRIBUTES, + take: chargerTousRadies ? undefined : 1600, + }); + + const usagersRadiesTotalCount = chargerTousRadies + ? usagersRadiesFirsts.length + : await usagerRepository.count({ + where: { + statut: "RADIE", + structureId: user.structureId, + }, + }); + + const filterHistorique = (usager: Usager) => { + if (usager.historique && Array.isArray(usager.historique)) { + usager.historique = usager.historique.map((item: UsagerDecision) => ({ + statut: item.statut, + dateDecision: item.dateDecision, + dateDebut: item.dateDebut, + dateFin: item.dateFin, + })) as UsagerDecision[]; + } + return usager; + }; + + const usagersMerges = [...usagersNonRadies, ...usagersRadiesFirsts].map( + filterHistorique + ); + + return { + usagersRadiesTotalCount, + usagers: usagersMerges, + }; + } + + @Get("update-manage") + @AllowUserStructureRoles(...USER_STRUCTURE_ROLE_ALL) + public async updateManage(@CurrentUser() user: UserStructureAuthenticated) { + return await usagerRepository + .createQueryBuilder() + .select(joinSelectFields(USAGER_LIGHT_ATTRIBUTES)) + .where( + `"structureId" = :structureId AND "updatedAt" >= :fiveMinutesAgo`, + { + structureId: user.structureId, + fiveMinutesAgo: subMinutes(new Date(), 5), + } + ) + .getRawMany(); + } + + @Post("search-radies") + @AllowUserStructureRoles(...USER_STRUCTURE_ROLE_ALL) + public async searchInRadies( + @Body() search: SearchUsagerDto, + @CurrentUser() user: UserStructureAuthenticated + ) { + const query = usagerRepository + .createQueryBuilder("usager") + .select(joinSelectFields(USAGER_LIGHT_ATTRIBUTES)) + .where(`"structureId" = :structureId and statut = 'RADIE'`, { + structureId: user.structureId, + }); + + if (search.searchString) { + if (search.searchStringField === CriteriaSearchField.DEFAULT) { + query.andWhere("nom_prenom_surnom_ref ILIKE :str", { + str: `%${search.searchString}%`, + }); + } else if (search.searchStringField === CriteriaSearchField.BIRTH_DATE) { + const formattedDate = format( + parse(search.searchString, "ddMMyyyy", new Date()), + "yyyy-MM-dd" + ); + query.andWhere(`DATE("dateNaissance") = DATE(:date)`, { + date: formattedDate, + }); + } else if ( + search.searchStringField === CriteriaSearchField.PHONE_NUMBER + ) { + query.andWhere(`telephone->>'numero' ILIKE :phone`, { + phone: `%${search.searchString}%`, + }); + } + } + + if (search?.lastInteractionDate) { + const deadlines = getUsagerDeadlines(); + const date = deadlines[search.lastInteractionDate].value; + + query.andWhere( + `("lastInteraction"->>'dateInteraction')::timestamp >= :dateRef::timestamp`, + { + dateRef: date, + } + ); + } + + if (typeof search?.referrerId !== "undefined") { + query.andWhere( + search.referrerId === null + ? `"referrerId" IS NULL` + : `"referrerId" = :referrerId`, + { referrerId: search.referrerId } + ); + } + + if (search?.entretien) { + query.andWhere( + `rdv->>'dateRdv' IS NOT NULL AND "etapeDemande" <= :step AND (rdv->>'dateRdv')::date ${ + search.entretien === "COMING" ? ">" : "<" + } CURRENT_DATE`, + { step: ETAPE_ENTRETIEN } + ); + } + + if (search?.echeance) { + const deadlines = getUsagerDeadlines(); + const now = new Date(); + const deadline = deadlines[search.echeance]; + + if (search.echeance === "EXCEEDED") { + query.andWhere(`(decision->>'dateDecision')::timestamp < :now`, { + now, + }); + } else if (search.echeance.startsWith("NEXT_")) { + query.andWhere( + `(decision->>'dateDecision')::timestamp <= :deadline AND (decision->>'dateDecision')::timestamp > :now`, + { + deadline: deadline.value, + now, + } + ); + } else if (search?.echeance.startsWith("PREVIOUS_")) { + query.andWhere(`(decision->>'dateDecision')::timestamp < :deadline`, { + deadline: deadline.value, + now, + }); + } + } + + if ( + !search.searchString && + !search?.echeance && + !search?.entretien && + typeof search?.referrerId !== undefined && + !search?.lastInteractionDate + ) { + query.take(100); + } + + return await query.getRawMany(); + } +} diff --git a/packages/backend/src/usagers/controllers/security-tests/search-usagers.controller.security-tests.ts b/packages/backend/src/usagers/controllers/security-tests/search-usagers.controller.security-tests.ts new file mode 100644 index 0000000000..423faa0408 --- /dev/null +++ b/packages/backend/src/usagers/controllers/security-tests/search-usagers.controller.security-tests.ts @@ -0,0 +1,35 @@ +import { AppTestContext, AppTestHttpClient } from "../../../util/test"; +import { USER_STRUCTURE_ROLE_ALL } from "../../../_common/model"; +import { + AppTestHttpClientSecurityTestDef, + expectedResponseStatusBuilder, +} from "../../../_tests"; + +////////////////// IMPORTANT ////////////////// +// +// Ce fichier doit être importé dans : +// - API_SECURITY_STRUCTURE_CONTROLLER_TEST_DEFS +// + +const CONTROLLER = "SearchUsagersController"; + +export const UsagersControllerSecurityTests: AppTestHttpClientSecurityTestDef[] = + [ + { + label: `${CONTROLLER}.findAllByStructure`, + query: async (context: AppTestContext) => ({ + response: await AppTestHttpClient.get( + "/usagers?chargerTousRadies=false", + { + context, + } + ), + expectedStatus: expectedResponseStatusBuilder.allowStructureOnly( + context.user, + { + roles: USER_STRUCTURE_ROLE_ALL, + } + ), + }), + }, + ]; diff --git a/packages/backend/src/usagers/controllers/security-tests/usagers.controller.security-tests.ts b/packages/backend/src/usagers/controllers/security-tests/usagers.controller.security-tests.ts index 41798ac9a9..08050d6885 100644 --- a/packages/backend/src/usagers/controllers/security-tests/usagers.controller.security-tests.ts +++ b/packages/backend/src/usagers/controllers/security-tests/usagers.controller.security-tests.ts @@ -1,6 +1,5 @@ import { HttpStatus } from "@nestjs/common"; import { AppTestContext, AppTestHttpClient } from "../../../util/test"; -import { USER_STRUCTURE_ROLE_ALL } from "../../../_common/model"; import { AppTestHttpClientSecurityTestDef, expectedResponseStatusBuilder, @@ -16,23 +15,6 @@ const CONTROLLER = "UsagersController"; export const UsagersControllerSecurityTests: AppTestHttpClientSecurityTestDef[] = [ - { - label: `${CONTROLLER}.findAllByStructure`, - query: async (context: AppTestContext) => ({ - response: await AppTestHttpClient.get( - "/usagers?chargerTousRadies=false", - { - context, - } - ), - expectedStatus: expectedResponseStatusBuilder.allowStructureOnly( - context.user, - { - roles: USER_STRUCTURE_ROLE_ALL, - } - ), - }), - }, { label: `${CONTROLLER}.checkDuplicates`, query: async (context: AppTestContext) => ({ diff --git a/packages/backend/src/usagers/controllers/tests/search-usagers.controller.spec.ts b/packages/backend/src/usagers/controllers/tests/search-usagers.controller.spec.ts new file mode 100644 index 0000000000..0ef168c9fa --- /dev/null +++ b/packages/backend/src/usagers/controllers/tests/search-usagers.controller.spec.ts @@ -0,0 +1,132 @@ +import { SearchUsagersController } from "../search-usagers.controller"; +import { TESTS_USERS_STRUCTURE } from "../../../_tests"; +import { UsersModule } from "../../../users/users.module"; +import { + AppTestContext, + AppTestHelper, + AppTestHttpClient, +} from "../../../util/test"; +import { UsagersModule } from "../../usagers.module"; +import { CriteriaSearchField } from "@domifa/common"; + +describe("SearchUsagersController", () => { + let controller: SearchUsagersController; + let context: AppTestContext; + + afterAll(async () => { + await AppTestHelper.tearDownTestApp(context); + }); + + beforeAll(async () => { + context = await AppTestHelper.bootstrapTestApp( + { + controllers: [SearchUsagersController], + imports: [UsagersModule, UsersModule], + }, + { initApp: true } + ); + + const authInfo = + TESTS_USERS_STRUCTURE.BY_EMAIL["preprod.domifa@fabrique.social.gouv.fr"]; + await AppTestHelper.authenticateStructure(authInfo, { context }); + + controller = context.module.get( + SearchUsagersController + ); + }); + + it("should be defined", () => { + expect(controller).toBeDefined(); + }); + + it("should search with all parameters", async () => { + const response = await AppTestHttpClient.post( + "/search-usagers/search-radies", + { + context, + body: { + searchString: "dupuis", + searchStringField: CriteriaSearchField.DEFAULT, + echeance: "NEXT_TWO_WEEKS", + lastInteractionDate: "PREVIOUS_TWO_MONTHS", + entretien: "COMING", + referrerId: 42, + }, + } + ).expect(201); + + expect(response.body).toBeDefined(); + }); + + it("should search with null referrerId", async () => { + const response = await AppTestHttpClient.post( + "/search-usagers/search-radies", + { + context, + body: { + searchString: " DIé", + searchStringField: CriteriaSearchField.DEFAULT, + referrerId: null, + }, + } + ).expect(201); + expect(response.body).toBeDefined(); + expect(response.body.length).toEqual(1); + expect(response.body[0].nom).toEqual("Rara"); + }); + + it("should handle phone number search", async () => { + const response = await AppTestHttpClient.post( + "/search-usagers/search-radies", + { + context, + body: { + searchString: "06. 06-06/06-06", + searchStringField: CriteriaSearchField.PHONE_NUMBER, + }, + } + ).expect(201); + + expect(response.body).toBeDefined(); + expect(response.body.length).toEqual(1); + expect(response.body[0].nom).toEqual("Loumiel"); + }); + + it("should handle birth date search", async () => { + const response = await AppTestHttpClient.post( + "/search-usagers/search-radies", + { + context, + body: { + searchString: "18/04/1990", + searchStringField: CriteriaSearchField.BIRTH_DATE, + }, + } + ).expect(201); + + expect(response.body).toBeDefined(); + expect(response.body.length).toEqual(1); + expect(response.body[0].nom).toEqual("Loumiel"); + }); + + it("should reject invalid birth date", async () => { + await AppTestHttpClient.post("/search-usagers/search-radies", { + context, + body: { + searchString: "32/13/1999", + searchStringField: CriteriaSearchField.BIRTH_DATE, + }, + }).expect(400); + }); + + it("should reject invalid echeance value", async () => { + await AppTestHttpClient.post("/search-usagers/search-radies", { + context, + body: { + searchString: "dupuis", + searchStringField: CriteriaSearchField.DEFAULT, + echeance: "INVALID_VALUE", + }, + }).expect(400); + }); +}); diff --git a/packages/backend/src/usagers/controllers/tests/usagers.controller.spec.ts b/packages/backend/src/usagers/controllers/tests/usagers.controller.spec.ts index bc0314d767..344016636a 100644 --- a/packages/backend/src/usagers/controllers/tests/usagers.controller.spec.ts +++ b/packages/backend/src/usagers/controllers/tests/usagers.controller.spec.ts @@ -77,11 +77,9 @@ describe("Usagers Controller", () => { expect(new Date(usager.decision.dateDecision)).toEqual(new Date()); expect(new Date(usager.historique[0].dateDecision)).toEqual(new Date()); - // Type de dom expect(usager.typeDom).toEqual("PREMIERE_DOM"); expect(usager.decision.typeDom).toEqual("PREMIERE_DOM"); expect(usager.historique[0].typeDom).toEqual("PREMIERE_DOM"); - // Trim et nettoyage des données expect(usager.nom).toEqual(exceptedResponse.nom); expect(usager.prenom).toEqual(exceptedResponse.prenom); diff --git a/packages/backend/src/usagers/controllers/usagers.controller.ts b/packages/backend/src/usagers/controllers/usagers.controller.ts index 1784d8abe8..4d8003a898 100644 --- a/packages/backend/src/usagers/controllers/usagers.controller.ts +++ b/packages/backend/src/usagers/controllers/usagers.controller.ts @@ -5,12 +5,10 @@ import { Get, HttpStatus, Param, - ParseBoolPipe, ParseEnumPipe, ParseIntPipe, Patch, Post, - Query, Res, UseGuards, } from "@nestjs/common"; @@ -25,7 +23,6 @@ import { AppUserGuard } from "../../auth/guards"; import { UsagerAccessGuard } from "../../auth/guards/usager-access.guard"; import { usagerRepository, - USAGER_LIGHT_ATTRIBUTES, joinSelectFields, messageSmsRepository, usagerDocsRepository, @@ -43,7 +40,6 @@ import { EntretienDto, ContactDetailsDto, } from "../dto"; -import { SearchUsagerDto } from "../dto/search-usager.dto"; import { UsagersService } from "../services"; import { AppLogsService } from "../../modules/app-logs/app-logs.service"; import { generateCerfaData } from "../services/cerfa"; @@ -53,19 +49,10 @@ import pdftk = require("node-pdftk"); import { join, resolve } from "path"; import { readFile } from "fs-extra"; import { ExpressResponse } from "../../util/express"; -import { - Usager, - ETAPE_DOCUMENTS, - CerfaDocType, - UsagerDecision, - getUsagerDeadlines, - CriteriaSearchField, -} from "@domifa/common"; +import { Usager, ETAPE_DOCUMENTS, CerfaDocType } from "@domifa/common"; import { UsagerHistoryStateService } from "../services/usagerHistoryState.service"; import { domifaConfig } from "../../config"; import { FileManagerService } from "../../util/file-manager/file-manager.service"; -import { Not } from "typeorm"; -import { subMinutes } from "date-fns"; @Controller("usagers") @ApiTags("usagers") @@ -79,156 +66,6 @@ export class UsagersController { private readonly fileManagerService: FileManagerService ) {} - @Get() - @AllowUserStructureRoles(...USER_STRUCTURE_ROLE_ALL) - public async findAllByStructure( - @Query("chargerTousRadies", new ParseBoolPipe()) - chargerTousRadies: boolean, - @CurrentUser() user: UserStructureAuthenticated - ) { - const usagersNonRadies = await usagerRepository.find({ - where: { - statut: Not("RADIE"), - structureId: user.structureId, - }, - select: USAGER_LIGHT_ATTRIBUTES, - }); - - const usagersRadiesFirsts = await usagerRepository.find({ - where: { - statut: "RADIE", - structureId: user.structureId, - }, - select: USAGER_LIGHT_ATTRIBUTES, - take: chargerTousRadies ? undefined : 1600, - }); - - const usagersRadiesTotalCount = chargerTousRadies - ? usagersRadiesFirsts.length - : await usagerRepository.count({ - where: { - statut: "RADIE", - structureId: user.structureId, - }, - }); - - const filterHistorique = (usager: Usager) => { - if (usager.historique && Array.isArray(usager.historique)) { - usager.historique = usager.historique.map((item: UsagerDecision) => ({ - statut: item.statut, - dateDecision: item.dateDecision, - dateDebut: item.dateDebut, - dateFin: item.dateFin, - })) as UsagerDecision[]; - } - return usager; - }; - - const usagersMerges = [...usagersNonRadies, ...usagersRadiesFirsts].map( - filterHistorique - ); - - return { - usagersRadiesTotalCount, - usagers: usagersMerges, - }; - } - - @Get("update-manage") - @AllowUserStructureRoles(...USER_STRUCTURE_ROLE_ALL) - public async updateManage(@CurrentUser() user: UserStructureAuthenticated) { - return await usagerRepository - .createQueryBuilder() - .select(joinSelectFields(USAGER_LIGHT_ATTRIBUTES)) - .where( - `"structureId" = :structureId AND "updatedAt" >= :fiveMinutesAgo`, - { - structureId: user.structureId, - fiveMinutesAgo: subMinutes(new Date(), 5), - } - ) - .getRawMany(); - } - - @Post("search-radies") - @AllowUserStructureRoles(...USER_STRUCTURE_ROLE_ALL) - public async searchInRadies( - @Body() search: SearchUsagerDto, - @CurrentUser() user: UserStructureAuthenticated - ) { - const query = usagerRepository - .createQueryBuilder("usager") - .select(joinSelectFields(USAGER_LIGHT_ATTRIBUTES)) - .where(`"structureId" = :structureId and statut = 'RADIE'`, { - structureId: user.structureId, - }); - - if (search.searchString) { - if (search.searchStringField === CriteriaSearchField.DEFAULT) { - query.andWhere("nom_prenom_surnom_ref ILIKE :str", { - str: `%${search.searchString}%`, - }); - } else if (search.searchStringField === CriteriaSearchField.BIRTH_DATE) { - query.andWhere(`DATE("dateNaissance") = DATE(:date)`, { - date: search.searchString, - }); - } else if ( - search.searchStringField === CriteriaSearchField.PHONE_NUMBER - ) { - query.andWhere(`telephone->>'numero' ILIKE :phone`, { - phone: `%${search.searchString}%`, - }); - } - } - - if (search?.lastInteractionDate) { - const deadlines = getUsagerDeadlines(); - const date = deadlines[search.lastInteractionDate].value; - - query.andWhere( - `("lastInteraction"->>'dateInteraction')::timestamp >= :date`, - { - date, - } - ); - } - - if (search?.echeance) { - const deadlines = getUsagerDeadlines(); - const now = new Date(); - const deadline = deadlines[search.echeance]; - - if (search.echeance === "EXCEEDED") { - query.andWhere(`(decision->>'dateDecision')::timestamp < :now`, { - now, - }); - } else if (search.echeance.startsWith("NEXT_")) { - query.andWhere( - `(decision->>'dateDecision')::timestamp <= :deadline AND (decision->>'dateDecision')::timestamp > :now`, - { - deadline: deadline.value, - now, - } - ); - } else if (search?.echeance.startsWith("PREVIOUS_")) { - query.andWhere(`(decision->>'dateDecision')::timestamp < :deadline`, { - deadline: deadline.value, - now, - }); - } - } - - if ( - !search.searchString && - !search?.echeance && - !search?.lastInteractionDate - ) { - query.take(100); - } - - return await query.getRawMany(); - } - @Post() @AllowUserStructureRoles("simple", "responsable", "admin") public createUsager( diff --git a/packages/backend/src/usagers/dto/search-usager.dto.ts b/packages/backend/src/usagers/dto/search-usager.dto.ts index eee69f646e..05c3c3f36e 100644 --- a/packages/backend/src/usagers/dto/search-usager.dto.ts +++ b/packages/backend/src/usagers/dto/search-usager.dto.ts @@ -1,54 +1,47 @@ import { ApiProperty } from "@nestjs/swagger"; -import { IsIn, IsOptional, IsString, MinLength } from "class-validator"; import { - LowerCaseTransform, - StripTagsTransform, -} from "../../_common/decorators"; + IsIn, + IsNumber, + IsOptional, + IsString, + MinLength, + ValidateIf, +} from "class-validator"; import { CriteriaSearchField, normalizeString, UsagersFilterCriteriaDernierPassage, UsagersFilterCriteriaEcheance, + UsagersFilterCriteriaEntretien, } from "@domifa/common"; import { Transform } from "class-transformer"; -import { isValid, parse } from "date-fns"; -import { BadRequestException } from "@nestjs/common"; +import { ValidateSearchField } from "../utils"; export class SearchUsagerDto { @ApiProperty({ example: "dupuis", description: "Nom ou prénom", }) - @IsOptional() - @IsString() - @MinLength(1) - @StripTagsTransform() - @LowerCaseTransform() @Transform(({ value, obj }) => { if (!value) { return null; } - switch (obj.searchStringField) { - case CriteriaSearchField.PHONE_NUMBER: - return value.replace(/\D/g, ""); - - case CriteriaSearchField.BIRTH_DATE: - const cleanDate = value.replace(/\D/g, ""); - const parsedDate = parse(cleanDate, "ddMMyyyy", new Date()); - - if (!isValid(parsedDate)) { - throw new BadRequestException( - 'Format de date invalide. Utilisez le format "dd/MM/yyyy"' - ); - } - return cleanDate; - - case CriteriaSearchField.DEFAULT: - default: - return normalizeString(value).trim(); + if ( + [ + CriteriaSearchField.PHONE_NUMBER, + CriteriaSearchField.BIRTH_DATE, + ].includes(obj.searchStringField) + ) { + return value.replace(/\D/g, ""); } + + return normalizeString(value).trim(); }) + @ValidateIf((obj) => obj.searchStringField) + @IsString() + @MinLength(1) + @ValidateSearchField() public searchString!: string; @IsIn(Object.values(CriteriaSearchField)) @@ -67,4 +60,13 @@ export class SearchUsagerDto { @IsIn(["PREVIOUS_TWO_MONTHS", "PREVIOUS_THREE_MONTHS"]) @IsOptional() public readonly lastInteractionDate: UsagersFilterCriteriaDernierPassage; + + @IsIn(Object.values(UsagersFilterCriteriaEntretien)) + @IsOptional() + public readonly entretien: UsagersFilterCriteriaEntretien; + + @IsNumber() + @IsOptional() + @ValidateIf((_object, value) => value !== null) + public readonly referrerId: number | null; } diff --git a/packages/backend/src/usagers/usagers.module.ts b/packages/backend/src/usagers/usagers.module.ts index d0b689c47a..9b9225e5a4 100644 --- a/packages/backend/src/usagers/usagers.module.ts +++ b/packages/backend/src/usagers/usagers.module.ts @@ -23,6 +23,7 @@ import { ImportCreatorService } from "./controllers/import/step3-create"; import { FileManagerService } from "../util/file-manager/file-manager.service"; import { AppLogsService } from "../modules/app-logs/app-logs.service"; import { MailsModule } from "../modules/mails/mails.module"; +import { SearchUsagersController } from "./controllers/search-usagers.controller"; @Module({ controllers: [ @@ -35,6 +36,7 @@ import { MailsModule } from "../modules/mails/mails.module"; UsagerDocsController, ExportStructureUsagersController, UsagerOptionsController, + SearchUsagersController, ], exports: [UsagersService, UsagerHistoryStateService, ImportCreatorService], imports: [ diff --git a/packages/backend/src/usagers/utils/index.ts b/packages/backend/src/usagers/utils/index.ts new file mode 100644 index 0000000000..c727341e0c --- /dev/null +++ b/packages/backend/src/usagers/utils/index.ts @@ -0,0 +1,3 @@ +// @index('./*', f => `export * from '${f.path}'`) +export * from "./validate-search-field.decorator"; +export * from "./validate-search-field"; diff --git a/packages/backend/src/usagers/utils/validate-search-field.decorator.ts b/packages/backend/src/usagers/utils/validate-search-field.decorator.ts new file mode 100644 index 0000000000..42809a8a77 --- /dev/null +++ b/packages/backend/src/usagers/utils/validate-search-field.decorator.ts @@ -0,0 +1,33 @@ +import { + ValidationOptions, + registerDecorator, + ValidationArguments, +} from "class-validator"; +import { validateSearchField } from "./validate-search-field"; + +export function ValidateSearchField(validationOptions?: ValidationOptions) { + return function (object: object, propertyName: string) { + registerDecorator({ + name: "validateSearchField", + target: object.constructor, + propertyName: propertyName, + options: validationOptions, + validator: { + validate(value: string, args: ValidationArguments) { + const searchStringField = args.object["searchStringField"]; + if (!searchStringField) { + return false; + } + return validateSearchField(value, searchStringField); + }, + defaultMessage(args: ValidationArguments) { + const searchStringField = args.object["searchStringField"]; + if (!searchStringField) { + return "Le type de recherche est requis"; + } + return "La valeur de recherche est invalide pour le type spécifié"; + }, + }, + }); + }; +} diff --git a/packages/backend/src/usagers/utils/validate-search-field.ts b/packages/backend/src/usagers/utils/validate-search-field.ts new file mode 100644 index 0000000000..388e8f1756 --- /dev/null +++ b/packages/backend/src/usagers/utils/validate-search-field.ts @@ -0,0 +1,55 @@ +import { CriteriaSearchField, normalizeString } from "@domifa/common"; +import { BadRequestException } from "@nestjs/common"; +import { isValid, parse } from "date-fns"; + +export function validateSearchField( + value: string, + searchField: CriteriaSearchField +): boolean { + if (!value || !searchField) { + return false; + } + + try { + switch (searchField) { + case CriteriaSearchField.BIRTH_DATE: + const cleanDate = value.replace(/\D/g, ""); + if (cleanDate.length !== 8) { + throw new BadRequestException( + 'Format de date invalide. La date doit être au format "jj/mm/aaaa"' + ); + } + + const parsedDate = parse(cleanDate, "ddMMyyyy", new Date()); + if (!isValid(parsedDate)) { + throw new BadRequestException( + "Date invalide. Vérifiez que le jour et le mois sont corrects" + ); + } + return true; + + case CriteriaSearchField.PHONE_NUMBER: + const cleanPhone = value.replace(/\D/g, ""); + if (cleanPhone.length === 0) { + throw new BadRequestException( + "Le numéro de téléphone doit contenir au moins un chiffre" + ); + } + return true; + + default: + const cleanText = normalizeString(value).trim(); + if (cleanText.length === 0) { + throw new BadRequestException( + "Le texte de recherche doit contenir au moins un caractère" + ); + } + return true; + } + } catch (error) { + if (error instanceof BadRequestException) { + throw error; + } + return false; + } +} diff --git a/packages/backend/src/users/controllers/users.public.controller.spec.ts b/packages/backend/src/users/controllers/users.public.controller.spec.ts index ce135963e9..b64ee5bbed 100644 --- a/packages/backend/src/users/controllers/users.public.controller.spec.ts +++ b/packages/backend/src/users/controllers/users.public.controller.spec.ts @@ -18,7 +18,6 @@ describe("Users Public Controller", () => { context = await AppTestHelper.bootstrapTestApp({ controllers: [UsersPublicController], imports: [MailsModule, StructuresModule, UsagersModule, HttpModule], - providers: [], }); controller = context.module.get( UsersPublicController diff --git a/packages/frontend/src/app/modules/manage-usagers/components/manage-filters/manage-filters.component.html b/packages/frontend/src/app/modules/manage-usagers/components/manage-filters/manage-filters.component.html index 2b171fe98b..a89f6d22dd 100644 --- a/packages/frontend/src/app/modules/manage-usagers/components/manage-filters/manage-filters.component.html +++ b/packages/frontend/src/app/modules/manage-usagers/components/manage-filters/manage-filters.component.html @@ -278,7 +278,7 @@ (click)=" updateFilters.emit({ element: 'entretien', - value: 'COMING' + value: UsagersFilterCriteriaEntretien.COMING }) " ngbDropdownItem @@ -291,7 +291,7 @@ (click)=" updateFilters.emit({ element: 'entretien', - value: 'OVERDUE' + value: UsagersFilterCriteriaEntretien.OVERDUE }) " ngbDropdownItem diff --git a/packages/frontend/src/app/modules/manage-usagers/components/manage-filters/manage-filters.component.ts b/packages/frontend/src/app/modules/manage-usagers/components/manage-filters/manage-filters.component.ts index cbab4a03f5..f7e4eea4ec 100644 --- a/packages/frontend/src/app/modules/manage-usagers/components/manage-filters/manage-filters.component.ts +++ b/packages/frontend/src/app/modules/manage-usagers/components/manage-filters/manage-filters.component.ts @@ -14,6 +14,7 @@ import { extractDeadlines, UsagersFilterCriteriaDernierPassage, UsagersFilterCriteriaEcheance, + UsagersFilterCriteriaEntretien, UsagersFilterCriteriaStatut, UserStructureProfile, } from "@domifa/common"; @@ -33,6 +34,8 @@ export class ManageFiltersComponent implements OnInit, OnChanges { @Output() public readonly updateFilters = new EventEmitter(); public readonly UsagersFilterCriteriaStatut = UsagersFilterCriteriaStatut; + public readonly UsagersFilterCriteriaEntretien = + UsagersFilterCriteriaEntretien; public readonly labelsEcheance = extractDeadlines([ @@ -53,7 +56,9 @@ export class ManageFiltersComponent implements OnInit, OnChanges { "PREVIOUS_THREE_MONTHS", ]); - public readonly labelsEntretien = { + public readonly labelsEntretien: { + [key in UsagersFilterCriteriaEntretien]: string; + } = { COMING: "à venir", OVERDUE: "date dépassée", }; diff --git a/packages/frontend/src/app/modules/manage-usagers/components/usager-filter/UsagersFilterCriteria.ts b/packages/frontend/src/app/modules/manage-usagers/components/usager-filter/UsagersFilterCriteria.ts index 930734fb41..b525920d63 100644 --- a/packages/frontend/src/app/modules/manage-usagers/components/usager-filter/UsagersFilterCriteria.ts +++ b/packages/frontend/src/app/modules/manage-usagers/components/usager-filter/UsagersFilterCriteria.ts @@ -3,6 +3,7 @@ import { Search, UsagersFilterCriteriaDernierPassage, UsagersFilterCriteriaEcheance, + UsagersFilterCriteriaEntretien, UsagersFilterCriteriaStatut, } from "@domifa/common"; @@ -13,8 +14,6 @@ export type UsagersFilterCriteriaSortKey = | "RDV" | "ID"; -export type UsagersFilterCriteriaEntretien = "COMING" | "OVERDUE"; - export class UsagersFilterCriteria extends Search { // text search filter // DEFAULT = Nom, prénom du domicilié, nom, prénom d'un des ayant-droits diff --git a/packages/frontend/src/app/modules/manage-usagers/components/usager-filter/usagersFilter.service.ts b/packages/frontend/src/app/modules/manage-usagers/components/usager-filter/usagersFilter.service.ts index 509f4bf612..2ba66a24f4 100644 --- a/packages/frontend/src/app/modules/manage-usagers/components/usager-filter/usagersFilter.service.ts +++ b/packages/frontend/src/app/modules/manage-usagers/components/usager-filter/usagersFilter.service.ts @@ -3,6 +3,7 @@ import { CriteriaSearchField, ETAPE_ENTRETIEN, search, + UsagersFilterCriteriaEntretien, } from "@domifa/common"; import { UsagerLight } from "../../../../../_common/model"; import { @@ -135,7 +136,7 @@ function buildActiveFilters(criteria: UsagersFilterCriteria) { function filterByEntretien( usager: UsagerLight, - entretien: "COMING" | "OVERDUE" + entretien: UsagersFilterCriteriaEntretien ): boolean { const now = new Date().toISOString().split("T")[0]; @@ -144,5 +145,7 @@ function filterByEntretien( } const dateRdv = new Date(usager.rdv.dateRdv).toISOString().split("T")[0]; - return entretien === "COMING" ? dateRdv > now : dateRdv < now; + return entretien === UsagersFilterCriteriaEntretien.COMING + ? dateRdv > now + : dateRdv < now; } diff --git a/packages/frontend/src/app/modules/manage-usagers/services/manage-usagers.service.ts b/packages/frontend/src/app/modules/manage-usagers/services/manage-usagers.service.ts index 9897cb83b2..a4b5973aba 100644 --- a/packages/frontend/src/app/modules/manage-usagers/services/manage-usagers.service.ts +++ b/packages/frontend/src/app/modules/manage-usagers/services/manage-usagers.service.ts @@ -3,9 +3,6 @@ import { Injectable } from "@angular/core"; import { Observable } from "rxjs"; import { map, tap } from "rxjs/operators"; import { environment } from "src/environments/environment"; - -import { UsagerLight } from "../../../../_common/model/usager/UsagerLight.type"; - import { Store } from "@ngrx/store"; import { setUsagerInformation, @@ -13,12 +10,13 @@ import { UsagerState, } from "../../../shared"; import { UsagersFilterCriteria } from "../components/usager-filter"; +import { UsagerLight } from "../../../../_common/model"; @Injectable({ providedIn: "root", }) export class ManageUsagersService { - public endPointUsagers = environment.apiUrl + "usagers"; + public endPoint = environment.apiUrl + "search-usagers"; constructor( private readonly http: HttpClient, @@ -32,7 +30,7 @@ export class ManageUsagersService { }): Observable { return this.http .get<{ usagers: UsagerLight[]; usagersRadiesTotalCount: number }>( - `${environment.apiUrl}usagers/?chargerTousRadies=${chargerTousRadies}` + `${this.endPoint}?chargerTousRadies=${chargerTousRadies}` ) .pipe( map( @@ -64,10 +62,7 @@ export class ManageUsagersService { filters: UsagersFilterCriteria ): Observable { return this.http - .post( - `${environment.apiUrl}usagers/search-radies`, - filters - ) + .post(`${this.endPoint}search-radies`, filters) .pipe( tap((usagers: UsagerLight[]) => { if (usagers?.length) { @@ -83,16 +78,14 @@ export class ManageUsagersService { } public updateManage(): Observable { - return this.http - .get(`${environment.apiUrl}usagers/update-manage`) - .pipe( - tap((usagers: UsagerLight[]) => { - if (usagers?.length) { - this.store.dispatch( - usagerActions.updateManyUsagersForManage({ usagers }) - ); - } - }) - ); + return this.http.get(`${this.endPoint}update-manage`).pipe( + tap((usagers: UsagerLight[]) => { + if (usagers?.length) { + this.store.dispatch( + usagerActions.updateManyUsagersForManage({ usagers }) + ); + } + }) + ); } } From 328bf627ab896852d6d30aa754810b5d25a6add4 Mon Sep 17 00:00:00 2001 From: "Yassine R." Date: Mon, 27 Jan 2025 19:22:56 +0100 Subject: [PATCH 5/5] fix(front): fix unit tests --- .../src/usagers/controllers/search-usagers.controller.ts | 2 +- .../backend/src/users/controllers/users.controller.ts | 2 +- .../manage-users/services/manage-users.service.spec.ts | 9 ++++++++- .../etat-civil-parent-form.component.spec.ts | 1 + 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/packages/backend/src/usagers/controllers/search-usagers.controller.ts b/packages/backend/src/usagers/controllers/search-usagers.controller.ts index 56a58f6100..7669ff9044 100644 --- a/packages/backend/src/usagers/controllers/search-usagers.controller.ts +++ b/packages/backend/src/usagers/controllers/search-usagers.controller.ts @@ -201,7 +201,7 @@ export class SearchUsagersController { !search.searchString && !search?.echeance && !search?.entretien && - typeof search?.referrerId !== undefined && + typeof search?.referrerId !== "undefined" && !search?.lastInteractionDate ) { query.take(100); diff --git a/packages/backend/src/users/controllers/users.controller.ts b/packages/backend/src/users/controllers/users.controller.ts index d87b90275f..57916bc651 100644 --- a/packages/backend/src/users/controllers/users.controller.ts +++ b/packages/backend/src/users/controllers/users.controller.ts @@ -48,9 +48,9 @@ import { AppUserGuard, CanGetUserStructureGuard } from "../../auth/guards"; @ApiTags("users") @UseGuards(AuthGuard("jwt"), AppUserGuard) export class UsersController { - @AllowUserStructureRoles("responsable", "admin") @ApiBearerAuth() @ApiOperation({ summary: "Liste des utilisateurs" }) + @AllowUserStructureRoles(...USER_STRUCTURE_ROLE_ALL) @Get("") public async getUsers( @CurrentUser() user: UserStructureAuthenticated diff --git a/packages/frontend/src/app/modules/manage-users/services/manage-users.service.spec.ts b/packages/frontend/src/app/modules/manage-users/services/manage-users.service.spec.ts index 276c155625..778f11be9f 100644 --- a/packages/frontend/src/app/modules/manage-users/services/manage-users.service.spec.ts +++ b/packages/frontend/src/app/modules/manage-users/services/manage-users.service.spec.ts @@ -1,12 +1,19 @@ import { TestBed } from "@angular/core/testing"; import { ManageUsersService } from "./manage-users.service"; +import { CommonModule, APP_BASE_HREF } from "@angular/common"; +import { HttpClientTestingModule } from "@angular/common/http/testing"; +import { CUSTOM_ELEMENTS_SCHEMA } from "@angular/core"; describe("ManageUsersService", () => { let service: ManageUsersService; beforeEach(() => { - TestBed.configureTestingModule({}); + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule, CommonModule], + providers: [{ provide: APP_BASE_HREF, useValue: "/" }], + schemas: [CUSTOM_ELEMENTS_SCHEMA], + }); service = TestBed.inject(ManageUsersService); }); diff --git a/packages/frontend/src/app/modules/usager-shared/components/etat-civil-parent-form/etat-civil-parent-form.component.spec.ts b/packages/frontend/src/app/modules/usager-shared/components/etat-civil-parent-form/etat-civil-parent-form.component.spec.ts index 6744b15ddf..d8124bd28e 100644 --- a/packages/frontend/src/app/modules/usager-shared/components/etat-civil-parent-form/etat-civil-parent-form.component.spec.ts +++ b/packages/frontend/src/app/modules/usager-shared/components/etat-civil-parent-form/etat-civil-parent-form.component.spec.ts @@ -95,6 +95,7 @@ describe("EtatCivilParentFormComponent", () => { prenom: "AD PRENOM", }, ], + referrerId: null, contactByPhone: true, customRef: null, dateNaissance: new Date("2022-08-03T21:59:59.999Z"),