From 60f6234e8a366abee9ec002eee64c228fd5268e8 Mon Sep 17 00:00:00 2001 From: Benjamin Bohec Date: Wed, 8 Jan 2025 16:32:00 +0100 Subject: [PATCH] refacto/clean for agency right to review --- front/src/app/components/UserDetail.tsx | 239 ---------------- .../user-profile/AgenciesTablesSection.tsx | 265 ++++++++++++++++++ .../PersonnalInformationsSection.tsx | 40 +++ .../components/user-profile/UserProfile.tsx | 34 +++ front/src/app/pages/admin/AdminUserDetail.tsx | 4 +- front/src/app/pages/{ => user}/MyProfile.tsx | 4 +- front/src/app/routes/Router.tsx | 2 +- 7 files changed, 344 insertions(+), 244 deletions(-) delete mode 100644 front/src/app/components/UserDetail.tsx create mode 100644 front/src/app/components/user-profile/AgenciesTablesSection.tsx create mode 100644 front/src/app/components/user-profile/PersonnalInformationsSection.tsx create mode 100644 front/src/app/components/user-profile/UserProfile.tsx rename front/src/app/pages/{ => user}/MyProfile.tsx (96%) diff --git a/front/src/app/components/UserDetail.tsx b/front/src/app/components/UserDetail.tsx deleted file mode 100644 index e8f950a61d..0000000000 --- a/front/src/app/components/UserDetail.tsx +++ /dev/null @@ -1,239 +0,0 @@ -import { fr } from "@codegouvfr/react-dsfr"; -import { Button } from "@codegouvfr/react-dsfr/Button"; -import { createModal } from "@codegouvfr/react-dsfr/Modal"; -import { Table } from "@codegouvfr/react-dsfr/Table"; -import React, { ReactNode, useState } from "react"; -import { createPortal } from "react-dom"; -import { - AgencyRight, - InclusionConnectedUser, - User, - UserParamsForAgency, - activeAgencyStatuses, - addressDtoToString, - agencyKindToLabelIncludingIF, - domElementIds, -} from "shared"; -import { AgencyStatusBadge } from "src/app/components/agency/AgencyStatusBadge"; -import { AgencyTag } from "src/app/components/agency/AgencyTag"; -import { AgencyUserModificationForm } from "src/app/components/agency/AgencyUserModificationForm"; -import { agencyRoleToDisplay } from "src/app/components/agency/AgencyUsers"; -import { Feedback } from "src/app/components/feedback/Feedback"; -import { routes } from "src/app/routes/routes"; - -type UserDetailProps = { - title: string; - currentUser: InclusionConnectedUser; - userWithRights: InclusionConnectedUser; - editInformationsLink?: string; - onUserUpdateRequested: (userParamsForAgency: UserParamsForAgency) => void; -}; - -const manageUserModal = createModal({ - isOpenedByDefault: false, - id: domElementIds.admin.agencyTab.editAgencyManageUserModal, -}); - -export const UserDetail = ({ - title, - currentUser, - userWithRights, - editInformationsLink, - onUserUpdateRequested, -}: UserDetailProps) => { - return ( -
-

{title}

- -

Informations personnelles

- - - - {editInformationsLink && ( - - )} - - - a.agency.name.localeCompare(b.agency.name), - )} - isBackofficeAdmin={currentUser.isBackofficeAdmin} - onUserUpdateRequested={onUserUpdateRequested} - /> -
- ); -}; - -const AgenciesTable = ({ - user, - agencyRights, - isBackofficeAdmin, - onUserUpdateRequested, -}: { - user: User; - agencyRights: AgencyRight[]; - isBackofficeAdmin?: boolean; - onUserUpdateRequested: (userParamsForAgency: UserParamsForAgency) => void; -}) => { - if (!agencyRights.length) - return

Cet utilisateur n'est lié à aucune agence

; - - const [selectedAgencyRight, setSelectedAgencyRight] = - useState(null); - - const onUpdateClicked = (agencyRight: AgencyRight) => { - setSelectedAgencyRight(agencyRight); - manageUserModal.open(); - }; - - const availableActions = (agencyRight: AgencyRight): ReactNode => { - const editRolesButton = ( - - ); - const viewAgencyButton = ( - - ); - - if (isBackofficeAdmin) { - return ( - <> - {editRolesButton} - {viewAgencyButton} - - ); - } - - return <>{editRolesButton}; - }; - - return ( - <> -

- Organismes rattachés au profil ({agencyRights.length} agences) -

- - [ -
- {agencyRight.agency.name} - - {addressDtoToString(agencyRight.agency.address)} - - - {agencyRight.roles.includes("agency-admin") && ( - - Voir l'agence - - )} -
, -
    -
  • - -
  • - {!activeAgencyStatuses.includes(agencyRight.agency.status) && ( -
  • - -
  • - )} -
  • - Type : {agencyKindToLabelIncludingIF[agencyRight.agency.kind]} -
  • -
, - agencyRight.roles - .map((role) => agencyRoleToDisplay[role].label) - .join(", "), - agencyRight.isNotifiedByEmail ? "Oui" : "Non", - availableActions(agencyRight), - ])} - /> - {createPortal( - - {selectedAgencyRight && ( - manageUserModal.close()} - agencyHasRefersTo={!!selectedAgencyRight.agency.refersToAgencyId} - isEmailDisabled={true} - areRolesDisabled={ - !isBackofficeAdmin && - !selectedAgencyRight.roles.includes("agency-admin") - } - onSubmit={onUserUpdateRequested} - routeName="myProfile" - /> - )} - , - document.body, - )} - - ); -}; diff --git a/front/src/app/components/user-profile/AgenciesTablesSection.tsx b/front/src/app/components/user-profile/AgenciesTablesSection.tsx new file mode 100644 index 0000000000..8998bb1473 --- /dev/null +++ b/front/src/app/components/user-profile/AgenciesTablesSection.tsx @@ -0,0 +1,265 @@ +import { fr } from "@codegouvfr/react-dsfr"; +import Button from "@codegouvfr/react-dsfr/Button"; +import { createModal } from "@codegouvfr/react-dsfr/Modal"; +import Table from "@codegouvfr/react-dsfr/Table"; +import { ReactNode, useState } from "react"; +import { createPortal } from "react-dom"; +import { + AgencyRight, + User, + UserParamsForAgency, + activeAgencyStatuses, + addressDtoToString, + agencyKindToLabelIncludingIF, + domElementIds, +} from "shared"; +import { routes } from "src/app/routes/routes"; +import { AgencyStatusBadge } from "../agency/AgencyStatusBadge"; +import { AgencyTag } from "../agency/AgencyTag"; +import { AgencyUserModificationForm } from "../agency/AgencyUserModificationForm"; +import { agencyRoleToDisplay } from "../agency/AgencyUsers"; +import { Feedback } from "../feedback/Feedback"; + +const manageUserModal = createModal({ + isOpenedByDefault: false, + id: domElementIds.admin.agencyTab.editAgencyManageUserModal, +}); + +export const AgenciesTablesSection = ({ + user, + agencyRights, + isBackofficeAdmin, + onUserUpdateRequested, +}: { + user: User; + agencyRights: AgencyRight[]; + isBackofficeAdmin?: boolean; + onUserUpdateRequested: (userParamsForAgency: UserParamsForAgency) => void; +}) => { + if (!agencyRights.length) + return

Cet utilisateur n'est lié à aucune agence

; + + const [selectedAgencyRight, setSelectedAgencyRight] = + useState(null); + + const onUpdateClicked = (agencyRight: AgencyRight) => { + setSelectedAgencyRight(agencyRight); + manageUserModal.open(); + }; + + const toReviewAgencyRights = agencyRights.filter((agencyRight) => + agencyRight.roles.includes("to-review"), + ); + const activeAgencyRights = agencyRights.filter( + (agencyRight) => !agencyRight.roles.includes("to-review"), + ); + + return ( + <> + + {toReviewAgencyRights.length > 0 && ( + + )} + {activeAgencyRights.length > 0 && ( + + )} + {createPortal( + + {selectedAgencyRight && ( + manageUserModal.close()} + agencyHasRefersTo={!!selectedAgencyRight.agency.refersToAgencyId} + isEmailDisabled={true} + areRolesDisabled={ + !isBackofficeAdmin && + !selectedAgencyRight.roles.includes("agency-admin") + } + onSubmit={onUserUpdateRequested} + routeName="myProfile" + /> + )} + , + document.body, + )} + + ); +}; + +const ActiveAgencyRightsTable = ({ + agenciesWithoutToReviewRights, + onUpdateClicked, + isBackofficeAdmin, +}: { + agenciesWithoutToReviewRights: AgencyRight[]; + onUpdateClicked: (agencyRight: AgencyRight) => void; + isBackofficeAdmin?: boolean; +}) => ( + <> +

+ Organismes rattachés au profil ({agenciesWithoutToReviewRights.length}{" "} + {agenciesWithoutToReviewRights.length === 1 ? "agence" : "agences"}) +

+ +
a.agency.name.localeCompare(b.agency.name)) + .map(makeAgencyRightLine(onUpdateClicked, isBackofficeAdmin))} + /> + +); + +const OnGoingAgencyRightsTable = ({ + agenciesWithToReviewRights, +}: { agenciesWithToReviewRights: AgencyRight[] }) => ( + <> +

+ Demandes d'accès en cours ({agenciesWithToReviewRights.length}{" "} + {agenciesWithToReviewRights.length === 1 ? "agence" : "agences"}) +

+
a.agency.name.localeCompare(b.agency.name)) + .map(makeAgencyWithToReviewRightLine())} + /> + +); + +const makeAgencyRightLine = + ( + onUpdateClicked: (agencyRight: AgencyRight) => void, + isBackofficeAdmin?: boolean, + ) => + (agencyRight: AgencyRight): ReactNode[] => [ + makeAgencyName({ agencyRight }), + makeAgencyCaracteristics({ agencyRight }), + makeAgencyAdminEmails({ agencyRight }), + agencyRight.roles.map((role) => agencyRoleToDisplay[role].label).join(", "), + agencyRight.isNotifiedByEmail ? "Oui" : "Non", + makeWithAgencyRightsCTAs({ + agencyRight, + onUpdateClicked, + isBackofficeAdmin, + }), + ]; + +const makeAgencyWithToReviewRightLine = + () => + (agencyRight: AgencyRight): ReactNode[] => [ + makeAgencyName({ agencyRight }), + makeAgencyCaracteristics({ agencyRight }), + makeAgencyAdminEmails({ agencyRight }), + ]; + +const makeAgencyName = ({ + agencyRight, +}: { agencyRight: AgencyRight }): ReactNode => ( +
+ {agencyRight.agency.name} + + {addressDtoToString(agencyRight.agency.address)} + + + {agencyRight.roles.includes("agency-admin") && ( + + Voir l'agence + + )} +
+); + +const makeAgencyCaracteristics = ({ + agencyRight, +}: { agencyRight: AgencyRight }): ReactNode => ( +
    +
  • + +
  • + {!activeAgencyStatuses.includes(agencyRight.agency.status) && ( +
  • + +
  • + )} +
  • Type : {agencyKindToLabelIncludingIF[agencyRight.agency.kind]}
  • +
+); + +const makeAgencyAdminEmails = ({ + agencyRight, +}: { agencyRight: AgencyRight }): ReactNode => ( +
    + { + // missing admin emails on agencyRight + ["fake@email.com", "fake2@email2.com"].map((admin) => ( +
  • {admin}
  • + )) + } +
+); + +const makeWithAgencyRightsCTAs = ({ + agencyRight, + isBackofficeAdmin, + onUpdateClicked, +}: { + agencyRight: AgencyRight; + onUpdateClicked: (agencyRight: AgencyRight) => void; + isBackofficeAdmin: boolean | undefined; +}): ReactNode => ( + <> + + {isBackofficeAdmin && ( + + )} + +); diff --git a/front/src/app/components/user-profile/PersonnalInformationsSection.tsx b/front/src/app/components/user-profile/PersonnalInformationsSection.tsx new file mode 100644 index 0000000000..4364386ecc --- /dev/null +++ b/front/src/app/components/user-profile/PersonnalInformationsSection.tsx @@ -0,0 +1,40 @@ +import { fr } from "@codegouvfr/react-dsfr"; +import Button from "@codegouvfr/react-dsfr/Button"; +import { InclusionConnectedUser, domElementIds } from "shared"; + +export const PersonnalInformationsSection = ({ + user, + editInformationsLink, +}: { + user: InclusionConnectedUser; + editInformationsLink?: string; +}) => ( + <> +

Informations personnelles

+ +
    +
  • Id de l'utilisateur: {user.id}
  • +
  • Email : {user.email}
  • + {user.firstName && ( +
  • Prénom : {user.firstName}
  • + )} + {user.lastName && ( +
  • Nom : {user.lastName}
  • + )} +
+ + {editInformationsLink && ( + + )} + +); diff --git a/front/src/app/components/user-profile/UserProfile.tsx b/front/src/app/components/user-profile/UserProfile.tsx new file mode 100644 index 0000000000..ca82ee0cbe --- /dev/null +++ b/front/src/app/components/user-profile/UserProfile.tsx @@ -0,0 +1,34 @@ +import React from "react"; +import { InclusionConnectedUser, UserParamsForAgency } from "shared"; +import { AgenciesTablesSection } from "./AgenciesTablesSection"; +import { PersonnalInformationsSection } from "./PersonnalInformationsSection"; + +type UserProfileProps = { + title: string; + currentUser: InclusionConnectedUser; + userWithRights: InclusionConnectedUser; + editInformationsLink?: string; + onUserUpdateRequested: (userParamsForAgency: UserParamsForAgency) => void; +}; + +export const UserProfile = ({ + title, + currentUser, + userWithRights, + editInformationsLink, + onUserUpdateRequested, +}: UserProfileProps) => ( +
+

{title}

+ + +
+); diff --git a/front/src/app/pages/admin/AdminUserDetail.tsx b/front/src/app/pages/admin/AdminUserDetail.tsx index 31a6266bbe..0293cc6e71 100644 --- a/front/src/app/pages/admin/AdminUserDetail.tsx +++ b/front/src/app/pages/admin/AdminUserDetail.tsx @@ -2,7 +2,7 @@ import React, { useEffect } from "react"; import { Loader } from "react-design-system"; import { useDispatch } from "react-redux"; import { UserParamsForAgency } from "shared"; -import { UserDetail } from "src/app/components/UserDetail"; +import { UserProfile } from "src/app/components/user-profile/UserProfile"; import { useAppSelector } from "src/app/hooks/reduxHooks"; import { routes } from "src/app/routes/routes"; import { adminFetchUserSelectors } from "src/core-logic/domain/admin/fetchUser/fetchUser.selectors"; @@ -61,7 +61,7 @@ export const AdminUserDetail = ({ route }: AdminUserDetailProps) => { : icUser.email; return ( - { return ( <> -