From d0a145dd88a9f255350bd197028d11155a1f2d69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20RIVALS?= Date: Thu, 12 Dec 2024 15:28:37 +0100 Subject: [PATCH] Update the regional prescription laboratory by group (#75) --- frontend/index.html | 2 +- .../RegionalPrescriptionCard.tsx | 32 ++++- .../RegionalPrescriptionLaboratory.tsx | 2 +- .../PrescriptionListGroupedUpdate.tsx | 109 ++++++++++++++++++ .../PrescriptionListHeader.tsx | 39 ++++++- .../PrescriptionListView.scss | 17 +++ .../PrescriptionListView.tsx | 66 +++++++++-- 7 files changed, 250 insertions(+), 17 deletions(-) create mode 100644 frontend/src/views/PrescriptionListView/PrescriptionListGroupedUpdate.tsx create mode 100644 frontend/src/views/PrescriptionListView/PrescriptionListView.scss diff --git a/frontend/index.html b/frontend/index.html index 93af0ecb..e3bc0cf3 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -7,7 +7,7 @@ - + diff --git a/frontend/src/components/Prescription/RegionalPrescriptionCard/RegionalPrescriptionCard.tsx b/frontend/src/components/Prescription/RegionalPrescriptionCard/RegionalPrescriptionCard.tsx index e00b4a9c..cba80c24 100644 --- a/frontend/src/components/Prescription/RegionalPrescriptionCard/RegionalPrescriptionCard.tsx +++ b/frontend/src/components/Prescription/RegionalPrescriptionCard/RegionalPrescriptionCard.tsx @@ -1,3 +1,4 @@ +import Checkbox from '@codegouvfr/react-dsfr/Checkbox'; import { cx } from '@codegouvfr/react-dsfr/fr/cx'; import clsx from 'clsx'; import { MatrixLabels } from 'shared/referential/Matrix/MatrixLabels'; @@ -17,13 +18,17 @@ interface Props { prescription: Prescription; regionalPrescription?: RegionalPrescription; onChangeLaboratory: (laboratoryId: string) => Promise; + isSelected?: boolean; + onToggleSelection?: () => void; } const RegionalPrescriptionCard = ({ programmingPlan, prescription, regionalPrescription, - onChangeLaboratory + onChangeLaboratory, + isSelected, + onToggleSelection }: Props) => { const { hasUserRegionalPrescriptionPermission } = useAuthentication(); @@ -41,8 +46,29 @@ const RegionalPrescriptionCard = ({ >
-

- {MatrixLabels[prescription.matrix]} +

+
+ {MatrixLabels[prescription.matrix]} +
+ {hasUserRegionalPrescriptionPermission( + programmingPlan, + regionalPrescription + )?.updateLaboratory && ( + + )}

{prescription.notes && (
diff --git a/frontend/src/components/Prescription/RegionalPrescriptionLaboratory/RegionalPrescriptionLaboratory.tsx b/frontend/src/components/Prescription/RegionalPrescriptionLaboratory/RegionalPrescriptionLaboratory.tsx index 67b4846e..27068ee5 100644 --- a/frontend/src/components/Prescription/RegionalPrescriptionLaboratory/RegionalPrescriptionLaboratory.tsx +++ b/frontend/src/components/Prescription/RegionalPrescriptionLaboratory/RegionalPrescriptionLaboratory.tsx @@ -43,7 +43,7 @@ const RegionalPrescriptionLaboratory = ({ ]; const currentLaboratory = laboratories?.find( - (laboratory) => laboratory.id === value + (laboratory) => laboratory.id === regionalPrescription.laboratoryId ); return ( diff --git a/frontend/src/views/PrescriptionListView/PrescriptionListGroupedUpdate.tsx b/frontend/src/views/PrescriptionListView/PrescriptionListGroupedUpdate.tsx new file mode 100644 index 00000000..1a9dbbd6 --- /dev/null +++ b/frontend/src/views/PrescriptionListView/PrescriptionListGroupedUpdate.tsx @@ -0,0 +1,109 @@ +import Button from '@codegouvfr/react-dsfr/Button'; +import { cx } from '@codegouvfr/react-dsfr/fr/cx'; +import Select from '@codegouvfr/react-dsfr/Select'; +import clsx from 'clsx'; +import React from 'react'; +import { Laboratory } from 'shared/schema/Laboratory/Laboratory'; +import { + AppSelectOption, + defaultAppSelectOption +} from 'src/components/_app/AppSelect/AppSelectOption'; +import { useFindLaboratoriesQuery } from 'src/services/laboratory.service'; +import { pluralize } from 'src/utils/stringUtils'; +import './PrescriptionListView.scss'; +interface Props { + selectedCount: number; + totalCount: number; + onSubmit: (laboratoryId?: string) => Promise; + onCancel: () => void; + onSelectAll: () => void; +} + +const PrescriptionListGroupedUpdate = ({ + selectedCount, + totalCount, + onSubmit, + onCancel, + onSelectAll +}: Props) => { + const [laboratoryId, setLaboratoryId] = React.useState(); + + const { data: laboratories } = useFindLaboratoriesQuery(); + + const laboratoriesOptions = ( + laboratories: Laboratory[] = [] + ): AppSelectOption[] => [ + defaultAppSelectOption('Sélectionner un laboratoire'), + ...laboratories.map((laboratory) => ({ + label: laboratory.name, + value: laboratory.id + })) + ]; + + return ( +
+
+
+
+ Action groupée + + • {selectedCount} {pluralize(selectedCount)('sélectionnée')} + +
+ +
+ +
+ +
+
+ +
+
+
+ ); +}; + +export default PrescriptionListGroupedUpdate; diff --git a/frontend/src/views/PrescriptionListView/PrescriptionListHeader.tsx b/frontend/src/views/PrescriptionListView/PrescriptionListHeader.tsx index a48b0f9d..9a4b31f0 100644 --- a/frontend/src/views/PrescriptionListView/PrescriptionListHeader.tsx +++ b/frontend/src/views/PrescriptionListView/PrescriptionListHeader.tsx @@ -5,6 +5,7 @@ import { SegmentedControl } from '@codegouvfr/react-dsfr/SegmentedControl'; import clsx from 'clsx'; import { t } from 'i18next'; import _ from 'lodash'; +import React from 'react'; import { Matrix } from 'shared/referential/Matrix/Matrix'; import { FindPrescriptionOptions } from 'shared/schema/Prescription/FindPrescriptionOptions'; import { Prescription } from 'shared/schema/Prescription/Prescription'; @@ -15,13 +16,18 @@ import { useAppDispatch, useAppSelector } from 'src/hooks/useStore'; import useWindowSize from 'src/hooks/useWindowSize'; import { getPrescriptionsExportURL } from 'src/services/prescription.service'; import prescriptionsSlice from 'src/store/reducers/prescriptionsSlice'; - +import PrescriptionListGroupedUpdate from 'src/views/PrescriptionListView/PrescriptionListGroupedUpdate'; +import './PrescriptionListView.scss'; interface Props { programmingPlan: ProgrammingPlan; findPrescriptionOptions: FindPrescriptionOptions; prescriptions: Prescription[]; addMatrix: (matrix: Matrix) => Promise; sampleCount?: number; + hasGroupedUpdatePermission?: boolean; + selectedCount?: number; + onGroupedUpdate?: (laboratoryId?: string) => Promise; + onSelectAll: () => void; } const PrescriptionListHeader = ({ @@ -29,7 +35,11 @@ const PrescriptionListHeader = ({ findPrescriptionOptions, prescriptions, addMatrix, - sampleCount + sampleCount, + hasGroupedUpdatePermission, + selectedCount, + onGroupedUpdate, + onSelectAll }: Props) => { const dispatch = useAppDispatch(); const { isMobile } = useWindowSize(); @@ -40,6 +50,8 @@ const PrescriptionListHeader = ({ (state) => state.prescriptions ); + const [isGroupedUpdate, setIsGroupedUpdate] = React.useState(false); + return ( <>
@@ -105,6 +117,17 @@ const PrescriptionListHeader = ({ className={cx('fr-mr-3w')} /> )} + {hasGroupedUpdatePermission && ( +
+ {isGroupedUpdate && onGroupedUpdate && ( + { + await onGroupedUpdate(laboratoryId); + setIsGroupedUpdate(false); + }} + onCancel={() => setIsGroupedUpdate(false)} + onSelectAll={onSelectAll} + /> + )} ); }; diff --git a/frontend/src/views/PrescriptionListView/PrescriptionListView.scss b/frontend/src/views/PrescriptionListView/PrescriptionListView.scss new file mode 100644 index 00000000..ac54007e --- /dev/null +++ b/frontend/src/views/PrescriptionListView/PrescriptionListView.scss @@ -0,0 +1,17 @@ +.grouped-update-container { + width: 100%; + + .grouped-update-card { + background-color: var(--blue-france-975-75); + border: 1px solid var(--blue-ecume-950-100-hover); + display: flex; + + div:has(h6) { + flex-grow: 1; + } + + div:has(.fr-btn) { + align-self: flex-end; + } + } +} diff --git a/frontend/src/views/PrescriptionListView/PrescriptionListView.tsx b/frontend/src/views/PrescriptionListView/PrescriptionListView.tsx index d9180ec2..f0bc36e7 100644 --- a/frontend/src/views/PrescriptionListView/PrescriptionListView.tsx +++ b/frontend/src/views/PrescriptionListView/PrescriptionListView.tsx @@ -2,7 +2,7 @@ import { cx } from '@codegouvfr/react-dsfr/fr/cx'; import { SegmentedControl } from '@codegouvfr/react-dsfr/SegmentedControl'; import clsx from 'clsx'; import _, { default as fp } from 'lodash'; -import { useCallback, useEffect, useMemo } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import { useSearchParams } from 'react-router-dom'; import { Matrix } from 'shared/referential/Matrix/Matrix'; import { MatrixLabels } from 'shared/referential/Matrix/MatrixLabels'; @@ -63,8 +63,15 @@ const PrescriptionListView = () => { useAppSelector((state) => state.prescriptions); const [searchParams, setSearchParams] = useSearchParams(); - const { userInfos, hasNationalView, hasUserPrescriptionPermission } = - useAuthentication(); + const { + userInfos, + hasNationalView, + hasUserPrescriptionPermission, + hasUserRegionalPrescriptionPermission + } = useAuthentication(); + + const [selectedRegionalPrescriptionIds, setSelectedRegionalPrescriptionIds] = + useState([]); const [addPrescription, { isSuccess: isAddSuccess }] = useAddPrescriptionMutation(); @@ -246,11 +253,17 @@ const PrescriptionListView = () => { [changeRegionalPrescription] // eslint-disable-line react-hooks/exhaustive-deps ); - const changeRegionalPrescriptionLaboratory = useCallback( - async (prescriptionId: string, laboratoryId?: string) => - changeRegionalPrescription(prescriptionId, region, { - laboratoryId - }), + const changeRegionalPrescriptionsLaboratory = useCallback( + async (prescriptionIds: string[], laboratoryId?: string) => { + await Promise.all( + prescriptionIds.map((prescriptionId) => + changeRegionalPrescription(prescriptionId, region, { + laboratoryId + }) + ) + ); + return; + }, [changeRegionalPrescription, region] // eslint-disable-line react-hooks/exhaustive-deps ); @@ -344,6 +357,29 @@ const PrescriptionListView = () => { prescriptions={prescriptions} addMatrix={(matrix) => addMatrix(programmingPlan.id, matrix)} sampleCount={_.sumBy(regionalPrescriptions, 'sampleCount')} + hasGroupedUpdatePermission={regionalPrescriptions.some( + (regionalPrescription) => + hasUserRegionalPrescriptionPermission( + programmingPlan, + regionalPrescription + )?.updateLaboratory + )} + selectedCount={selectedRegionalPrescriptionIds.length} + onGroupedUpdate={async (laboratoryId) => { + await changeRegionalPrescriptionsLaboratory( + selectedRegionalPrescriptionIds, + laboratoryId + ); + setSelectedRegionalPrescriptionIds([]); + }} + onSelectAll={() => { + setSelectedRegionalPrescriptionIds( + selectedRegionalPrescriptionIds.length === + prescriptions.length + ? [] + : prescriptions.map((p) => p.id) + ); + }} /> }
@@ -382,11 +418,21 @@ const PrescriptionListView = () => { regionalPrescription.region === region )} onChangeLaboratory={(laboratoryId) => - changeRegionalPrescriptionLaboratory( - prescription.id, + changeRegionalPrescriptionsLaboratory( + [prescription.id], laboratoryId ) } + isSelected={selectedRegionalPrescriptionIds.includes( + prescription.id + )} + onToggleSelection={() => { + setSelectedRegionalPrescriptionIds((prevState) => + prevState.includes(prescription.id) + ? prevState.filter((id) => id !== prescription.id) + : [...prevState, prescription.id] + ); + }} /> ))}