diff --git a/src/components/CreationCohort/DiagramView/components/LogicalOperator/components/CriteriaRightPanel/BiologyForm/components/Hierarchy/BiologyHierarchy.tsx b/src/components/CreationCohort/DiagramView/components/LogicalOperator/components/CriteriaRightPanel/BiologyForm/components/Hierarchy/BiologyHierarchy.tsx index 4d27840b1..ab819154d 100644 --- a/src/components/CreationCohort/DiagramView/components/LogicalOperator/components/CriteriaRightPanel/BiologyForm/components/Hierarchy/BiologyHierarchy.tsx +++ b/src/components/CreationCohort/DiagramView/components/LogicalOperator/components/CriteriaRightPanel/BiologyForm/components/Hierarchy/BiologyHierarchy.tsx @@ -24,7 +24,12 @@ import KeyboardBackspaceIcon from '@mui/icons-material/KeyboardBackspace' import { useAppDispatch, useAppSelector } from 'state' import { BiologyListType } from 'state/biology' -import { checkIfIndeterminated, expandItem, findEquivalentRowInItemAndSubItems, getSelectedPmsi } from 'utils/pmsi' +import { + checkIfIndeterminated, + expandItem, + findEquivalentRowInItemAndSubItems, + getHierarchySelection +} from 'utils/pmsi' import useStyles from './styles' import { PmsiListType } from 'state/pmsi' @@ -71,7 +76,7 @@ const BiologyListItem: React.FC = (props) => { const handleClickOnHierarchy = async (biologyItem: PmsiListType) => { if (isLoadingsyncHierarchyTable > 0 || isLoadingPmsi > 0) return dispatch(incrementLoadingSyncHierarchyTable()) - const newSelectedItems = getSelectedPmsi(biologyItem, selectedItems || [], biologyHierarchy) + const newSelectedItems = getHierarchySelection(biologyItem, selectedItems || [], biologyHierarchy) await handleClick(newSelectedItems) dispatch(decrementLoadingSyncHierarchyTable()) } diff --git a/src/components/CreationCohort/DiagramView/components/LogicalOperator/components/CriteriaRightPanel/CCAM/components/Hierarchy/CCAMHierarchy.tsx b/src/components/CreationCohort/DiagramView/components/LogicalOperator/components/CriteriaRightPanel/CCAM/components/Hierarchy/CCAMHierarchy.tsx index cf0792d15..9b30bb416 100644 --- a/src/components/CreationCohort/DiagramView/components/LogicalOperator/components/CriteriaRightPanel/CCAM/components/Hierarchy/CCAMHierarchy.tsx +++ b/src/components/CreationCohort/DiagramView/components/LogicalOperator/components/CriteriaRightPanel/CCAM/components/Hierarchy/CCAMHierarchy.tsx @@ -24,7 +24,12 @@ import KeyboardBackspaceIcon from '@mui/icons-material/KeyboardBackspace' import { useAppDispatch, useAppSelector } from 'state' import { PmsiListType } from 'state/pmsi' -import { checkIfIndeterminated, expandItem, findEquivalentRowInItemAndSubItems, getSelectedPmsi } from 'utils/pmsi' +import { + checkIfIndeterminated, + expandItem, + findEquivalentRowInItemAndSubItems, + getHierarchySelection +} from 'utils/pmsi' import useStyles from './styles' import { decrementLoadingSyncHierarchyTable, incrementLoadingSyncHierarchyTable } from 'state/syncHierarchyTable' @@ -74,7 +79,7 @@ const ProcedureListItem: React.FC = (props) => { const handleClickOnHierarchy = async (procedureItem: PmsiListType) => { if (isLoadingsyncHierarchyTable > 0 || isLoadingPmsi > 0) return dispatch(incrementLoadingSyncHierarchyTable()) - const newSelectedItems = getSelectedPmsi(procedureItem, selectedItems || [], procedureHierarchy) + const newSelectedItems = getHierarchySelection(procedureItem, selectedItems || [], procedureHierarchy) await handleClick(newSelectedItems) dispatch(decrementLoadingSyncHierarchyTable()) } diff --git a/src/components/CreationCohort/DiagramView/components/LogicalOperator/components/CriteriaRightPanel/Cim10Form/components/Hierarchy/Cim10Hierarchy.tsx b/src/components/CreationCohort/DiagramView/components/LogicalOperator/components/CriteriaRightPanel/Cim10Form/components/Hierarchy/Cim10Hierarchy.tsx index 1a0ae4006..c64531d7b 100644 --- a/src/components/CreationCohort/DiagramView/components/LogicalOperator/components/CriteriaRightPanel/Cim10Form/components/Hierarchy/Cim10Hierarchy.tsx +++ b/src/components/CreationCohort/DiagramView/components/LogicalOperator/components/CriteriaRightPanel/Cim10Form/components/Hierarchy/Cim10Hierarchy.tsx @@ -24,7 +24,12 @@ import KeyboardBackspaceIcon from '@mui/icons-material/KeyboardBackspace' import { useAppDispatch, useAppSelector } from 'state' import { PmsiListType } from 'state/pmsi' -import { checkIfIndeterminated, expandItem, findEquivalentRowInItemAndSubItems, getSelectedPmsi } from 'utils/pmsi' +import { + checkIfIndeterminated, + expandItem, + findEquivalentRowInItemAndSubItems, + getHierarchySelection +} from 'utils/pmsi' import useStyles from './styles' import { findSelectedInListAndSubItems } from 'utils/cohortCreation' @@ -70,7 +75,7 @@ const CimListItem: React.FC = (props) => { const handleClickOnHierarchy = async (cim10Item: PmsiListType) => { if (isLoadingsyncHierarchyTable > 0 || isLoadingPmsi > 0) return dispatch(incrementLoadingSyncHierarchyTable()) - const newSelectedItems = getSelectedPmsi(cim10Item, selectedItems || [], cim10Hierarchy) + const newSelectedItems = getHierarchySelection(cim10Item, selectedItems || [], cim10Hierarchy) await handleClick(newSelectedItems) dispatch(decrementLoadingSyncHierarchyTable()) } diff --git a/src/components/CreationCohort/DiagramView/components/LogicalOperator/components/CriteriaRightPanel/GHM/components/Hierarchy/GhmHierarchy.tsx b/src/components/CreationCohort/DiagramView/components/LogicalOperator/components/CriteriaRightPanel/GHM/components/Hierarchy/GhmHierarchy.tsx index 57a59a53e..64b920e0c 100644 --- a/src/components/CreationCohort/DiagramView/components/LogicalOperator/components/CriteriaRightPanel/GHM/components/Hierarchy/GhmHierarchy.tsx +++ b/src/components/CreationCohort/DiagramView/components/LogicalOperator/components/CriteriaRightPanel/GHM/components/Hierarchy/GhmHierarchy.tsx @@ -24,7 +24,12 @@ import KeyboardBackspaceIcon from '@mui/icons-material/KeyboardBackspace' import { useAppDispatch, useAppSelector } from 'state' import { PmsiListType } from 'state/pmsi' -import { checkIfIndeterminated, expandItem, findEquivalentRowInItemAndSubItems, getSelectedPmsi } from 'utils/pmsi' +import { + checkIfIndeterminated, + expandItem, + findEquivalentRowInItemAndSubItems, + getHierarchySelection +} from 'utils/pmsi' import useStyles from './styles' import { decrementLoadingSyncHierarchyTable, incrementLoadingSyncHierarchyTable } from 'state/syncHierarchyTable' @@ -64,7 +69,7 @@ const GhmListItem: React.FC = (props) => { const handleClickOnHierarchy = async (ghmItem: PmsiListType) => { if (isLoadingsyncHierarchyTable > 0 || isLoadingPmsi > 0) return dispatch(incrementLoadingSyncHierarchyTable()) - const newSelectedItems = getSelectedPmsi(ghmItem, selectedItems || [], ghmHierarchy) + const newSelectedItems = getHierarchySelection(ghmItem, selectedItems || [], ghmHierarchy) await handleClick(newSelectedItems) dispatch(decrementLoadingSyncHierarchyTable()) } diff --git a/src/components/CreationCohort/DiagramView/components/LogicalOperator/components/CriteriaRightPanel/MedicationForm/components/Hierarchy/MedicationHierarchy.tsx b/src/components/CreationCohort/DiagramView/components/LogicalOperator/components/CriteriaRightPanel/MedicationForm/components/Hierarchy/MedicationHierarchy.tsx index 38083f5e8..102f2653a 100644 --- a/src/components/CreationCohort/DiagramView/components/LogicalOperator/components/CriteriaRightPanel/MedicationForm/components/Hierarchy/MedicationHierarchy.tsx +++ b/src/components/CreationCohort/DiagramView/components/LogicalOperator/components/CriteriaRightPanel/MedicationForm/components/Hierarchy/MedicationHierarchy.tsx @@ -24,7 +24,12 @@ import KeyboardBackspaceIcon from '@mui/icons-material/KeyboardBackspace' import { useAppDispatch, useAppSelector } from 'state' import { MedicationListType } from 'state/medication' -import { checkIfIndeterminated, expandItem, findEquivalentRowInItemAndSubItems, getSelectedPmsi } from 'utils/pmsi' +import { + checkIfIndeterminated, + expandItem, + findEquivalentRowInItemAndSubItems, + getHierarchySelection +} from 'utils/pmsi' import useStyles from './styles' import { findSelectedInListAndSubItems } from 'utils/cohortCreation' @@ -77,7 +82,7 @@ const MedicationListItem: React.FC = (props) => { const handleClickOnHierarchy = (medicationItem: MedicationListType) => { if (isLoadingsyncHierarchyTable > 0 || isLoadingMedication > 0) return dispatch(incrementLoadingSyncHierarchyTable()) - const newSelectedItems = getSelectedPmsi(medicationItem, selectedItems || [], medicationHierarchy) + const newSelectedItems = getHierarchySelection(medicationItem, selectedItems || [], medicationHierarchy) handleClick(newSelectedItems) dispatch(decrementLoadingSyncHierarchyTable()) } diff --git a/src/components/CreationCohort/DiagramView/components/LogicalOperator/components/CriteriaRightPanel/SupportedForm/SupportedForm.tsx b/src/components/CreationCohort/DiagramView/components/LogicalOperator/components/CriteriaRightPanel/SupportedForm/SupportedForm.tsx index 4ae906db1..92748fd48 100644 --- a/src/components/CreationCohort/DiagramView/components/LogicalOperator/components/CriteriaRightPanel/SupportedForm/SupportedForm.tsx +++ b/src/components/CreationCohort/DiagramView/components/LogicalOperator/components/CriteriaRightPanel/SupportedForm/SupportedForm.tsx @@ -205,6 +205,7 @@ const SupportedForm: React.FC = (props) => { title={STRUCTURE_HOSPITALIERE_DE_PRIS_EN_CHARGE} executiveUnits={defaultValues?.encounterService ?? []} isAcceptEmptySelection={true} + isDeleteIcon={true} onChangeExecutiveUnits={_onSubmitExecutiveUnits} /> diff --git a/src/components/CreationCohort/DiagramView/components/PopulationCard/PopulationCard.tsx b/src/components/CreationCohort/DiagramView/components/PopulationCard/PopulationCard.tsx index 3c8dff961..45053506a 100644 --- a/src/components/CreationCohort/DiagramView/components/PopulationCard/PopulationCard.tsx +++ b/src/components/CreationCohort/DiagramView/components/PopulationCard/PopulationCard.tsx @@ -79,8 +79,7 @@ const PopulationCard: React.FC = (props) => { ...selectedPopulations, subItems: [] })) - - dispatch(buildCohortCreation({ selectedPopulation: _selectedPopulations })) + setUpdatedItems(updatedSelection) onChangeOpenDrawer(false) } diff --git a/src/components/ScopeTree/ScopeTree.tsx b/src/components/ScopeTree/ScopeTree.tsx index 6cd20453b..1e412b506 100644 --- a/src/components/ScopeTree/ScopeTree.tsx +++ b/src/components/ScopeTree/ScopeTree.tsx @@ -1,21 +1,21 @@ import React, { useEffect, useRef, useState } from 'react' import { + Breadcrumbs, Checkbox, CircularProgress, Grid, IconButton, - Skeleton, - TableCell, - TableRow, - Typography, - Breadcrumbs, + Pagination, Paper, + Skeleton, Table, TableBody, + TableCell, TableContainer, TableHead, - Pagination + TableRow, + Typography } from '@mui/material' import KeyboardArrowRightIcon from '@mui/icons-material/ChevronRight' @@ -24,14 +24,19 @@ import EnhancedTable from 'components/ScopeTree/ScopeTreeTable' import { ScopeTreeRow, TreeElement } from 'types' import { useAppDispatch, useAppSelector } from 'state' -import { expandScopeElement, fetchScopesList, ScopeState } from 'state/scope' +import { expandScopeElement, fetchScopesList, ScopeState, updateScopeList } from 'state/scope' import displayDigit from 'utils/displayDigit' import { useDebounce } from 'utils/debounce' import useStyles from './styles' -import { findEquivalentRowInItemAndSubItems, getSelectedPmsi } from 'utils/pmsi' -import servicesPerimeters from '../../services/aphp/servicePerimeters' +import { + checkIfIndeterminated, + findEquivalentRowInItemAndSubItems, + getHierarchySelection, + optimizeHierarchySelection +} from 'utils/pmsi' +import servicesPerimeters, { loadingItem } from '../../services/aphp/servicePerimeters' import { findSelectedInListAndSubItems } from '../../utils/cohortCreation' type ScopeTreeListItemProps = { @@ -95,6 +100,7 @@ const ScopeTreeListItem: React.FC = (props) => { )} + = ({ const [count, setCount] = useState(0) const [isAllSelected, setIsAllSelected] = useState(false) const controllerRef = useRef() + const isHeadChecked: boolean = + isAllSelected || + scopesList.filter((row) => selectedItems.find((item: { id: any }) => item.id === row.id) !== undefined).length === + scopesList.length + const isHeadIndetermined: boolean = + !isAllSelected && selectedItems && selectedItems.length > 0 && rootRows && !isHeadChecked - const fetchScopeTree = async (signal?: AbortSignal) => { - return dispatch(fetchScopesList(signal)).unwrap() + const fetchScopeTree = async (executiveUnitType?: string, signal?: AbortSignal) => { + return dispatch(fetchScopesList({ signal })).unwrap() } const _cancelPendingRequest = () => { @@ -191,17 +203,108 @@ const ScopeTree: React.FC = ({ const _init = async () => { setSearchLoading(true) _cancelPendingRequest() - const fetchScopeTreeResponse = await fetchScopeTree(controllerRef.current?.signal) + + let newPerimetersList: ScopeTreeRow[] = [] + const fetchScopeTreeResponse = await fetchScopeTree(executiveUnitType, controllerRef.current?.signal) if (fetchScopeTreeResponse && !fetchScopeTreeResponse.aborted) { - const newPerimetersList = fetchScopeTreeResponse.scopesList + newPerimetersList = fetchScopeTreeResponse.scopesList setRootRows(newPerimetersList) setOpenPopulations([]) setCount(newPerimetersList?.length) setIsEmpty(!newPerimetersList || newPerimetersList.length < 0) } + await _expandSelectedItems(newPerimetersList ?? rootRows) setSearchLoading(false) } + const getFetchedSelectedItems = (selectedItems: ScopeTreeRow[], rootRows: ScopeTreeRow[]) => { + const fetchedSelectedItems: ScopeTreeRow[] = [] + const notFetchedSelectedItemsIds: string[] = [] + selectedItems.forEach((item: ScopeTreeRow) => { + if (findEquivalentRowInItemAndSubItems({ id: item.id }, rootRows).equivalentRow) { + fetchedSelectedItems.push(item) + } else { + notFetchedSelectedItemsIds.push(item.id) + } + }) + return { + fetchedSelectedItems: fetchedSelectedItems, + notFetchedSelectedItemsIds: notFetchedSelectedItemsIds + } + } + + const getAllParentsIds = async (selectedItems: ScopeTreeRow[], rootRows: ScopeTreeRow[]) => { + const { fetchedSelectedItems: fetchedSelectedItems, notFetchedSelectedItemsIds: notFetchedSelectedItemsIds } = + getFetchedSelectedItems(selectedItems, rootRows) + + const notFetchedSelectedItems: ScopeTreeRow[] = + notFetchedSelectedItemsIds?.length > 0 + ? await servicesPerimeters.buildScopeTreeRowList( + await servicesPerimeters.getPerimeters(notFetchedSelectedItemsIds) + ) + : [] + const allParentsIds: string[] = [...fetchedSelectedItems, ...notFetchedSelectedItems] + .map((item: ScopeTreeRow) => (item?.above_levels_ids ?? '').split(',')) + .flat() + ?.filter((idValue, index, array) => { + return idValue && array.indexOf(idValue) === index + }) + return allParentsIds + } + + const getParents = async (allParentsIds: string[]) => { + const fetchedParents: ScopeTreeRow[] = [] + const notFetchedSubItemsIds: string[] = [] + const notFetchedParentsIds: string[] = allParentsIds.filter((parentId) => { + const foundItem = findEquivalentRowInItemAndSubItems({ id: parentId }, rootRows).equivalentRow + if (!foundItem) return true + fetchedParents.push(foundItem) + if (!foundItem.subItems || foundItem.subItems.length < 1 || foundItem.subItems[0]?.id === loadingItem.id) { + notFetchedSubItemsIds.push(foundItem?.inferior_levels_ids?.split(',')) + } + return false + }) + const notFetchedItems: string[] = [...notFetchedParentsIds, ...notFetchedSubItemsIds]?.filter( + (idValue, index, array) => { + return idValue && array.indexOf(idValue) === index + } + ) + const notFetchedParents: ScopeTreeRow[] = + notFetchedItems?.length > 0 + ? await servicesPerimeters.buildScopeTreeRowList(await servicesPerimeters.getPerimeters(notFetchedItems)) + : [] + return [...fetchedParents, ...notFetchedParents] + } + + const _updateRootRows = (newRootRows: ScopeTreeRow[], parents: ScopeTreeRow[]) => { + for (let i = 0; i < newRootRows.length; i++) { + const updatedSubItems: ScopeTreeRow[] = parents?.filter((item) => newRootRows[i].id === item.parentId) + if (updatedSubItems?.length > 0) { + const newSubItems = newRootRows[i].subItems?.filter( + (item) => item.id !== loadingItem.id && !updatedSubItems?.map((item) => item.id).includes(item?.id) + ) + newRootRows[i] = { ...newRootRows[i], subItems: [...newSubItems, ...updatedSubItems] } + } + if (newRootRows[i]?.subItems?.length > 0 && newRootRows[i]?.subItems[0]?.id !== 'loading') { + _updateRootRows(newRootRows[i].subItems, parents) + } + } + } + + const _expandSelectedItems = async (rootRows: ScopeTreeRow[]) => { + if (!selectedItems || selectedItems.length < 1) return + + const allParentsIds: string[] = await getAllParentsIds(selectedItems, rootRows) + + const parents: ScopeTreeRow[] = await getParents(allParentsIds) + parents.push(...selectedItems) + + const newRootRows: ScopeTreeRow[] = [...rootRows] + + _updateRootRows(newRootRows, parents) + dispatch(updateScopeList(newRootRows)) + } + const _searchInPerimeters = async (_isAllSelected?: boolean) => { setSearchLoading(true) _cancelPendingRequest() @@ -274,10 +377,11 @@ const ScopeTree: React.FC = ({ return findSelectedInListAndSubItems(selectedItems, searchedItem, allItems) } const _onSelect = (row: ScopeTreeRow) => { - const savedSelectedItems: any[] = getSelectedPmsi(row, selectedItems, scopesList) + const hierarchySelection: any[] = getHierarchySelection(row, selectedItems, scopesList) + const optimizedHierarchySelection: any[] = optimizeHierarchySelection(hierarchySelection, scopesList) - onChangeSelectedItem(savedSelectedItems) - return savedSelectedItems + onChangeSelectedItem(optimizedHierarchySelection) + return optimizedHierarchySelection } const _onSelectAll = () => { @@ -304,34 +408,7 @@ const ScopeTree: React.FC = ({ onChangeSelectedItem(results) } - const _isIndeterminated: (_row: any) => boolean | undefined = (_row) => { - // Si que un loading => false - if (_row.subItems && _row.subItems.length > 0 && _row.subItems[0].id === 'loading') { - return false - } - const checkChild: (item: any) => boolean = (item) => { - const numberOfSubItemsSelected = item.subItems?.filter((subItem: any) => - selectedItems.find((item: { id: any }) => item.id === subItem.id) - )?.length - - if (numberOfSubItemsSelected && numberOfSubItemsSelected !== item.subItems.length) { - // Si un des sub elem qui est check => true - return true - } else if (item.subItems?.length >= numberOfSubItemsSelected) { - // Si un des sub-sub (ou sub-sub-sub ...) elem qui est check => true - let isCheck = false - for (const child of item.subItems) { - if (isCheck) continue - isCheck = !!checkChild(child) - } - return isCheck - } else { - // Sinon => false - return false - } - } - return checkChild(_row) - } + const _isIndeterminated: (_row: any) => boolean | undefined = (_row) => checkIfIndeterminated(_row, selectedItems) useEffect(() => { if (debouncedSearchTerm) { @@ -376,18 +453,8 @@ const ScopeTree: React.FC = ({
0 && - rootRows && - selectedItems.length !== rootRows.length - } - checked={ - isAllSelected || - scopesList.filter((row) => selectedItems.find((item: { id: any }) => item.id === row.id) !== undefined) - .length === scopesList.length - } + checked={isHeadChecked} + indeterminate={isHeadIndetermined} onClick={_onSelectAll} />
diff --git a/src/data/scope_type.json b/src/data/scope_type.json index 10bc8500b..2df744c93 100644 --- a/src/data/scope_type.json +++ b/src/data/scope_type.json @@ -8,5 +8,5 @@ "document": "Hôpital", "supported": "AP-HP" }, - "typeLevel": [["AP-HP"], ["GH", "GHU"], ["Hôpital"], ["Pôle/DMU"], ["Unité Fonctionnelle (UF)"]] + "typeLevel": [["AP-HP"], ["Groupe hospitalier (GH)", "GHU"], ["Hôpital"], ["Pôle/DMU"], ["Unité Fonctionnelle (UF)"]] } diff --git a/src/services/aphp/servicePerimeters.ts b/src/services/aphp/servicePerimeters.ts index 3e79e37bb..43d37e858 100644 --- a/src/services/aphp/servicePerimeters.ts +++ b/src/services/aphp/servicePerimeters.ts @@ -1,4 +1,4 @@ -import { CohortData, ScopePage, ScopeTreeRow } from 'types' +import { CohortData, ScopeElement, ScopePage, ScopeTreeRow } from 'types' import { IGroup } from '@ahryman40k/ts-fhir-types/lib/R4' import { getAgeRepartitionMapAphp, @@ -14,7 +14,7 @@ import apiBackend from '../apiBackend' import { sortByQuantityAndName } from 'utils/scopeTree' import scopeType from '../../data/scope_type.json' -const loadingItem: ScopeTreeRow = { id: 'loading', name: 'loading', quantity: 0, subItems: [] } +export const loadingItem: ScopeTreeRow = { id: 'loading', name: 'loading', quantity: 0, subItems: [] } export interface IServicePerimeters { /** @@ -111,13 +111,15 @@ export interface IServicePerimeters { /** * construire une liste de ScopeTreeRow à travers une liste de ScopePage */ - buildScopeTreeRow: ( + buildScopeTreeRowList: ( subScopes: ScopePage[], getSubItem?: boolean | undefined, type?: string, signal?: AbortSignal ) => Promise + buildScopeTreeRowItem: (scopeElement: ScopeElement, type?: string) => ScopeTreeRow + /** * à travers un ScopePage on retourne 'Nominatif' ou 'Pseudonymisé' selon les droits d'accès * @param perimeterItem @@ -227,7 +229,7 @@ const servicesPerimeters: IServicePerimeters = { try { if (!cohortId) return undefined const scopeItemList: ScopePage[] = (await servicesPerimeters.getPerimeters(undefined, [cohortId], true)) ?? [] - const scopeTreeRowList: ScopeTreeRow[] = await servicesPerimeters.buildScopeTreeRow(scopeItemList) + const scopeTreeRowList: ScopeTreeRow[] = await servicesPerimeters.buildScopeTreeRowList(scopeItemList) return scopeTreeRowList && scopeTreeRowList.length > 0 ? scopeTreeRowList[0] : undefined } catch (error) { return undefined @@ -303,7 +305,7 @@ const servicesPerimeters: IServicePerimeters = { const scopeItemList: ScopePage[] = (await servicesPerimeters.getPerimeters(undefined, undefined, undefined, type, signal)) ?? [] - const scopeTreeRowList: ScopeTreeRow[] = await servicesPerimeters.buildScopeTreeRow( + const scopeTreeRowList: ScopeTreeRow[] = await servicesPerimeters.buildScopeTreeRowList( scopeItemList, undefined, type, @@ -326,7 +328,12 @@ const servicesPerimeters: IServicePerimeters = { type, signal ) - const scopeRowList: ScopeTreeRow[] = await servicesPerimeters.buildScopeTreeRow(subScopes, getSubItem, type, signal) + const scopeRowList: ScopeTreeRow[] = await servicesPerimeters.buildScopeTreeRowList( + subScopes, + getSubItem, + type, + signal + ) return scopeRowList }, @@ -382,7 +389,7 @@ const servicesPerimeters: IServicePerimeters = { { signal: signal } ) if (backCohortResponse && backCohortResponse.data && backCohortResponse.data.results) { - const newPerimetersList: ScopeTreeRow[] = await servicesPerimeters.buildScopeTreeRow( + const newPerimetersList: ScopeTreeRow[] = await servicesPerimeters.buildScopeTreeRowList( backCohortResponse.data.results ) result = { @@ -394,7 +401,7 @@ const servicesPerimeters: IServicePerimeters = { return result }, - buildScopeTreeRow: async ( + buildScopeTreeRowList: async ( subScopes: ScopePage[], getSubItem?: boolean | undefined, type?: string, @@ -402,14 +409,8 @@ const servicesPerimeters: IServicePerimeters = { ) => { let scopeRowList: ScopeTreeRow[] = [] for (const scopeItem of subScopes) { - const scopeRowItem: ScopeTreeRow = { id: '', name: '', quantity: 0, subItems: [] } - scopeRowItem.id = '' + scopeItem.perimeter.id - scopeRowItem.cohort_id = scopeItem.perimeter.cohort_id - scopeRowItem.name = servicesPerimeters.getScopeName(scopeItem.perimeter) - scopeRowItem.full_path = scopeItem.perimeter.full_path - scopeRowItem.quantity = +scopeItem.perimeter.cohort_size + const scopeRowItem = servicesPerimeters.buildScopeTreeRowItem(scopeItem.perimeter) scopeRowItem.access = servicesPerimeters.getAccessFromScope(scopeItem) - scopeRowItem.inferior_levels_ids = scopeItem.perimeter.inferior_levels_ids scopeRowItem.subItems = getSubItem === true ? await servicesPerimeters.getScopesWithSubItems( @@ -419,13 +420,26 @@ const servicesPerimeters: IServicePerimeters = { signal ) : [loadingItem] - scopeRowItem.type = scopeItem.perimeter.type - scopeRowList = [...scopeRowList, scopeRowItem] + scopeRowList.push(scopeRowItem) } scopeRowList = sortByQuantityAndName(scopeRowList) return scopeRowList }, + buildScopeTreeRowItem: (scopeElement: ScopeElement) => { + const scopeRowItem: ScopeTreeRow = { id: '', name: '', quantity: 0, subItems: [loadingItem] } + scopeRowItem.id = '' + scopeElement.id + scopeRowItem.cohort_id = scopeElement.cohort_id + scopeRowItem.name = servicesPerimeters.getScopeName(scopeElement) + scopeRowItem.full_path = scopeElement.full_path + scopeRowItem.quantity = +scopeElement.cohort_size + scopeRowItem.above_levels_ids = scopeElement.above_levels_ids + scopeRowItem.inferior_levels_ids = scopeElement.inferior_levels_ids + scopeRowItem.type = scopeElement.type + scopeRowItem.parentId = scopeElement.parent_id + return scopeRowItem + }, + getAccessFromScope: (perimeterItem: ScopePage) => { return perimeterItem.read_access === 'DATA_NOMINATIVE' || perimeterItem.right_read_patient_nominative === true ? 'Nominatif' diff --git a/src/state/scope.ts b/src/state/scope.ts index 603590648..ce3018c55 100644 --- a/src/state/scope.ts +++ b/src/state/scope.ts @@ -1,11 +1,11 @@ -import { createSlice, createAsyncThunk } from '@reduxjs/toolkit' +import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit' import { RootState } from 'state' import { logout, login } from './me' import services from 'services/aphp' -import { ScopeTreeRow } from 'types' +import { Cohort, ScopeTreeRow } from 'types' export type ScopeState = { loading: boolean @@ -190,6 +190,12 @@ const scopeSlice = createSlice({ ...state, openPopulation: [] } + }, + updateScopeList: (state, action: PayloadAction) => { + return { + ...state, + scopesList: action.payload + } } }, extraReducers: (builder) => { @@ -224,4 +230,4 @@ const scopeSlice = createSlice({ export default scopeSlice.reducer export { fetchScopesList, expandScopeElement } -export const { clearScope, closeAllOpenedPopulation } = scopeSlice.actions +export const { clearScope, closeAllOpenedPopulation, updateScopeList } = scopeSlice.actions diff --git a/src/types.ts b/src/types.ts index ff9aebae5..16e859110 100644 --- a/src/types.ts +++ b/src/types.ts @@ -242,9 +242,10 @@ export type ScopeTreeRow = { name: string full_path?: string quantity: number - parentId?: string + parentId?: string | null subItems: ScopeTreeRow[] managingEntity?: any | undefined + above_levels_ids?: string inferior_levels_ids?: string cohort_id?: string cohort_size?: string @@ -761,6 +762,7 @@ export type ScopeElement = { source_value: string parent_id: string | null type: string + above_levels_ids: string inferior_levels_ids: string cohort_id: string cohort_size: string diff --git a/src/utils/pmsi.ts b/src/utils/pmsi.ts index 1177362de..5a1405fb6 100644 --- a/src/utils/pmsi.ts +++ b/src/utils/pmsi.ts @@ -16,7 +16,7 @@ import { expandBiologyElement } from '../state/biology' * This function is called when a user select an element of pmsi hierarchy * return selected items that should be saved */ -export const getSelectedPmsi = (row: any, selectedItems: any[] | undefined, rootRows: any[]) => { +export const getHierarchySelection = (row: any, selectedItems: any[] | undefined, rootRows: any[]) => { selectedItems = (selectedItems ?? []).map( (selectedItem) => findEquivalentRowInItemAndSubItems(selectedItem, rootRows).equivalentRow ?? selectedItem ) @@ -69,7 +69,9 @@ export const getSelectedPmsi = (row: any, selectedItems: any[] | undefined, root const selectedChildren = row.subItems ? // eslint-disable-next-line - row.subItems.filter((child: { id: any }) => savedSelectedItems.find((selectedChild) => selectedChild.id === child.id)) + row.subItems.filter((child: { id: any }) => + savedSelectedItems.find((selectedChild) => selectedChild.id === child.id) + ) : [] const foundItem = savedSelectedItems.find(({ id }) => id === row.id) @@ -109,12 +111,12 @@ export const getSelectedPmsi = (row: any, selectedItems: any[] | undefined, root return savedSelectedItems } -export const optimizeHierarchySelection = (selectedItems: PmsiListType[], rootRows: PmsiListType[]) => { +export const optimizeHierarchySelection = (selectedItems: any[], rootRows: any[]) => { // If you chenge this code, change it too inside: PopulationCard.tsx:31 and Scope.jsx:25 selectedItems = selectedItems.map( (selectedItem) => findEquivalentRowInItemAndSubItems(selectedItem, rootRows).equivalentRow ?? selectedItem ) - const updatedRows: PmsiListType[] = [] + const updatedRows: any[] = [] const newSelectedItems = selectedItems.filter((item, index, array) => { // reemove double item const foundItem = array.find(({ id }) => item.id === id) @@ -126,11 +128,11 @@ export const optimizeHierarchySelection = (selectedItems: PmsiListType[], rootRo updatedRows.push({ ...item }) } } - const returnParentElement: ( - _array: PmsiListType[], - parentArray: PmsiListType | undefined - ) => PmsiListType | undefined = (_array, parentArray) => { - let parentElement: PmsiListType | undefined = undefined + const returnParentElement: (_array: any[], parentArray: any | undefined) => any | undefined = ( + _array, + parentArray + ) => { + let parentElement: any | undefined = undefined if (!_array) return for (const element of _array) { if (parentElement) continue @@ -152,12 +154,12 @@ export const optimizeHierarchySelection = (selectedItems: PmsiListType[], rootRo return parentElement } - const parentItem: PmsiListType | undefined = returnParentElement(rootRows, undefined) + const parentItem: any | undefined = returnParentElement(rootRows, undefined) if (parentItem !== undefined) { const selectedChildren = parentItem.subItems && parentItem.subItems.length > 0 - ? parentItem.subItems.filter((subItem) => !!array.find(({ id }) => id === subItem.id)) + ? parentItem.subItems.filter((subItem: { id: any }) => !!array.find(({ id }) => id === subItem.id)) : [] if (