diff --git a/src/components/SearchValueSet/ValueSetField.tsx b/src/components/SearchValueSet/ValueSetField.tsx index 2d0bbbc94..487e96527 100644 --- a/src/components/SearchValueSet/ValueSetField.tsx +++ b/src/components/SearchValueSet/ValueSetField.tsx @@ -40,12 +40,12 @@ const ValueSetField = ({ value, references, placeholder, disabled = false, onSel {!value.length && {placeholder}} - {isExtended && ( + {isExtended && value.length > 0 && ( setIsExtended(false)}> )} - {!isExtended && ( + {!isExtended && value.length > 0 && ( setIsExtended(true)}> diff --git a/src/components/SearchValueSet/ValueSetTable.tsx b/src/components/SearchValueSet/ValueSetTable.tsx index 0d1e73272..53da259a1 100644 --- a/src/components/SearchValueSet/ValueSetTable.tsx +++ b/src/components/SearchValueSet/ValueSetTable.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useMemo, useState } from 'react' +import React, { useEffect, useState } from 'react' import { Checkbox, @@ -13,7 +13,7 @@ import { Typography } from '@mui/material' import { LoadingStatus, SelectedStatus } from 'types' -import { FhirHierarchy, FhirItem, Hierarchy, HierarchyInfo, SearchMode } from 'types/hierarchy' +import { FhirHierarchy, FhirItem, Hierarchy, HierarchyInfo, Mode, SearchMode } from 'types/hierarchy' import { KeyboardArrowDown, KeyboardArrowRight, IndeterminateCheckBoxOutlined } from '@mui/icons-material' import { CellWrapper, RowContainerWrapper, RowWrapper } from '../Hierarchy/styles' import { sortArray } from 'utils/arrays' @@ -25,18 +25,21 @@ import { getLabelFromCode, isDisplayedWithCode } from 'utils/valueSets' type ValueSetRowProps = { item: Hierarchy loading: { expand: LoadingStatus; list: LoadingStatus } - selectionDisabled?: boolean + isSelectionDisabled: (node: Hierarchy) => boolean path: string[] mode: SearchMode isHierarchy: boolean onExpand: (node: Hierarchy) => void - onSelect: (node: Hierarchy, toAdd: boolean) => void + onSelect: ( + nodes: Hierarchy[], + mode: Mode.SELECT | Mode.UNSELECT | Mode.SELECT_ALL | Mode.UNSELECT_ALL + ) => void } const ValueSetRow = ({ item, loading, - selectionDisabled = false, + isSelectionDisabled, path, mode, isHierarchy, @@ -80,11 +83,11 @@ const ValueSetRow = ({ } - onChange={(event, checked) => onSelect(item, checked)} + onChange={(event, checked) => onSelect([item], checked ? Mode.SELECT : Mode.UNSELECT)} color="info" inputProps={{ 'aria-labelledby': label }} /> @@ -99,6 +102,7 @@ const ValueSetRow = ({ ) => boolean onExpand: (node: Hierarchy) => void - onSelect: (node: Hierarchy, toAdd: boolean) => void - onSelectAll: (toAdd: boolean) => void + onSelect: ( + nodes: Hierarchy[], + mode: Mode.SELECT | Mode.UNSELECT | Mode.SELECT_ALL | Mode.UNSELECT_ALL + ) => void onChangePage: (page: number) => void } @@ -130,15 +137,18 @@ const ValueSetTable = ({ loading, mode, isHierarchy = true, + isSelectionDisabled = () => false, onSelect, - onSelectAll, onExpand, onChangePage }: ValueSetTableProps) => { - const tree = useMemo(() => { - console.log('test exp change') - return mode === SearchMode.RESEARCH || isHierarchy ? hierarchy.tree : hierarchy.tree[0].subItems || [] - }, [hierarchy]) + + const handleSelect = (checked: boolean) => { + if (mode === SearchMode.RESEARCH) { + const notDisabled = hierarchy.tree.filter((node) => !isSelectionDisabled(node)) + onSelect(notDisabled, checked ? Mode.SELECT : Mode.UNSELECT) + } else onSelect(hierarchy.tree, checked ? Mode.SELECT_ALL : Mode.UNSELECT_ALL) + } return ( @@ -154,13 +164,14 @@ const ValueSetTable = ({ {hierarchy.count ? `${hierarchy.count} résultat(s)` : ` Aucun résultat à afficher`} !isSelectionDisabled(node)).length + } checked={selectAllStatus === SelectedStatus.SELECTED} indeterminate={selectAllStatus === SelectedStatus.INDETERMINATE} indeterminateIcon={} - onChange={(event, checked) => { - if (mode === SearchMode.RESEARCH) onSelectAll(checked) - else onSelect(hierarchy.tree[0], checked) - }} + onChange={(event, checked) => handleSelect(checked)} /> @@ -168,16 +179,16 @@ const ValueSetTable = ({ )} {loading.list === LoadingStatus.SUCCESS && (
- {tree.map((item) => + {hierarchy.tree.map((item) => item ? ( @@ -201,7 +212,7 @@ const ValueSetTable = ({ onChangePage(page)} color="#0063AF" /> diff --git a/src/components/SearchValueSet/index.tsx b/src/components/SearchValueSet/index.tsx index faa1cd98f..ed23a046c 100644 --- a/src/components/SearchValueSet/index.tsx +++ b/src/components/SearchValueSet/index.tsx @@ -9,8 +9,9 @@ import SelectedCodes from 'components/Hierarchy/SelectedCodes' import { useSearchValueSet } from 'hooks/valueSet/useSearchValueSet' import { Displayer } from 'components/ui/Displayer/styles' import ClearIcon from '@mui/icons-material/Clear' -import { FhirItem, Hierarchy, SearchMode, SearchModeLabel } from 'types/hierarchy' +import { FhirItem, Hierarchy, Mode, SearchMode, SearchModeLabel } from 'types/hierarchy' import { cleanNode } from 'utils/hierarchy/hierarchy' +import { HIERARCHY_ROOT } from 'services/aphp/serviceValueSets' type SearchValueSetProps = { references: Reference[] @@ -23,10 +24,12 @@ const SearchValueSet = ({ references, selectedNodes, onSelect }: SearchValueSetP mode, searchInput, onChangeMode, + onDelete, selectedCodes, + isSelectionDisabled, loadingStatus, parameters: { refs, onChangeReferences, onChangeSearchInput, onChangePage }, - hierarchy: { exploration, research, selectAllStatus, expand, select, selectAll } + hierarchy: { exploration, research, selectAllStatus, expand, select } } = useSearchValueSet(references, selectedNodes) const tabs: TabType[] = [ @@ -98,8 +101,8 @@ const SearchValueSet = ({ references, selectedNodes, onSelect }: SearchValueSetP loading={{ list: loadingStatus.init, expand: loadingStatus.expand }} hierarchy={exploration} selectAllStatus={selectAllStatus} + isSelectionDisabled={isSelectionDisabled} onSelect={select} - onSelectAll={selectAll} onExpand={expand} onChangePage={onChangePage} /> @@ -111,8 +114,8 @@ const SearchValueSet = ({ references, selectedNodes, onSelect }: SearchValueSetP isHierarchy={false} loading={{ list: loadingStatus.search, expand: loadingStatus.expand }} hierarchy={research} + isSelectionDisabled={isSelectionDisabled} onSelect={select} - onSelectAll={selectAll} onExpand={expand} onChangePage={onChangePage} /> @@ -121,7 +124,7 @@ const SearchValueSet = ({ references, selectedNodes, onSelect }: SearchValueSetP
- select(code, false)} /> +
diff --git a/src/hooks/hierarchy/useHierarchy.ts b/src/hooks/hierarchy/useHierarchy.ts index 48aa20767..3deefbac0 100644 --- a/src/hooks/hierarchy/useHierarchy.ts +++ b/src/hooks/hierarchy/useHierarchy.ts @@ -2,20 +2,20 @@ import { buildTree, buildMultipleTrees, getHierarchyDisplay, - getItemSelectedStatus, getListDisplay, getMissingCodes, getMissingCodesWithSystems, groupBySystem, getHierarchyRootCodes, mapHierarchyToMap, - getSelectedCodesFromTrees + getSelectedCodesFromTrees, + createHierarchyRoot } from '../../utils/hierarchy/hierarchy' -import { useEffect, useMemo, useRef, useState } from 'react' +import { useEffect, useRef, useState } from 'react' import { Back_API_Response, LoadingStatus, SelectedStatus } from 'types' -import { getSelectedCodesFromTree } from 'utils/hierarchy/hierarchy' import { Codes, Hierarchy, HierarchyInfo, HierarchyLoadingStatus, Mode, SearchMode } from '../../types/hierarchy' import { replaceInMap } from 'utils/map' +import { HIERARCHY_ROOT } from 'services/aphp/serviceValueSets' /** * @param {Hierarchy[]} selectedNodes - Nodes selected in the hierarchy. * @param {Codes>} fetchedCodes - All the codes that have already been fetched and saved. @@ -28,12 +28,12 @@ export const useHierarchy = ( onCache: (codes: Codes>) => void, fetchHandler: (ids: string, system: string) => Promise[]> ) => { - console.log('test loop hier') const DEFAULT_HIERARCHY_INFO = { tree: [], count: 0, page: 1, - system: '' + system: '', + selectAllStatus: SelectedStatus.INDETERMINATE } const [trees, setTrees] = useState[]>>(new Map()) const [hierarchies, setHierarchies] = useState>>(new Map()) @@ -50,11 +50,6 @@ export const useHierarchy = ( expand: LoadingStatus.SUCCESS }) - const selectAllStatus = useMemo(() => { - const node = { id: 'parent', subItems: searchResults.tree } as Hierarchy - return getItemSelectedStatus(node) - }, [searchResults.tree]) - useEffect(() => { latestCodes.current = codes }, [codes]) @@ -64,7 +59,10 @@ export const useHierarchy = ( }, []) const initTrees = async ( - initHandlers: { system: string; fetchBaseTree: () => Promise>> }[] + initHandlers: { + system: string + fetchBaseTree: () => Promise>> + }[] ) => { const newTrees: Map[]> = new Map() const newHierarchies: Map> = new Map() @@ -72,7 +70,8 @@ export const useHierarchy = ( for (const handler of initHandlers) { const { results: baseTree, count } = await handler.fetchBaseTree() const currentSelected = selectedCodes.get(handler.system) || new Map() - const toFind = [...baseTree, ...currentSelected.values()] + const toAdd = currentSelected.get(HIERARCHY_ROOT) ? new Map() : currentSelected + const toFind = [...baseTree, ...toAdd.values()] const currentCodes = codes.get(handler.system) || new Map() const newCodes = await getMissingCodes(baseTree, currentCodes, toFind, handler.system, Mode.INIT, fetchHandler) const newTree = buildTree(baseTree, handler.system, toFind, newCodes, currentSelected, Mode.INIT) @@ -89,12 +88,9 @@ export const useHierarchy = ( const search = async (fetchSearch: () => Promise>>) => { const { results: endCodes, count } = await fetchSearch() - console.log('test select endCodes', endCodes) const bySystem = groupBySystem(endCodes) const newCodes = await getMissingCodesWithSystems(trees, bySystem, codes, fetchHandler) - console.log('test select newCodes', newCodes) const newTrees = buildMultipleTrees(trees, bySystem, newCodes, selectedCodes, Mode.SEARCH) - console.log('test select newTrees', newTrees) setCodes(newCodes) setTrees(newTrees) return { display: getListDisplay(endCodes, newTrees), count } @@ -115,32 +111,32 @@ export const useHierarchy = ( setLoadingStatus({ ...loadingStatus, search: LoadingStatus.SUCCESS }) } - const select = (node: Hierarchy, toAdd: boolean) => { - const hierarchyId = node.system - const bySystem = groupBySystem([node]) - const currentHierarchy = hierarchies.get(hierarchyId) || DEFAULT_HIERARCHY_INFO - console.log('test select root', currentHierarchy) - const mode = toAdd ? Mode.SELECT : Mode.UNSELECT - const newTrees = buildMultipleTrees(trees, bySystem, codes, selectedCodes, mode) - const newTree = newTrees.get(node.system) || [] - const displayHierarchy = getHierarchyDisplay(currentHierarchy.tree, newTree) + const select = (nodes: Hierarchy[], mode: Mode.SELECT | Mode.UNSELECT | Mode.SELECT_ALL | Mode.UNSELECT_ALL) => { + console.log('test bug', nodes, mode) + const bySystem = groupBySystem(nodes) + const selectMode = mode === Mode.SELECT_ALL ? Mode.SELECT : mode === Mode.UNSELECT_ALL ? Mode.UNSELECT : mode + const newTrees = buildMultipleTrees(trees, bySystem, codes, selectedCodes, selectMode) const displaySearch = getListDisplay(searchResults.tree, newTrees) - const newSelectedCodes = getSelectedCodesFromTree(newTree) - setTrees(newTrees) - setHierarchies(replaceInMap(hierarchyId, { ...currentHierarchy, tree: displayHierarchy }, hierarchies)) - setSearchResults({ ...searchResults, tree: displaySearch }) - setSelectedCodes(replaceInMap(hierarchyId, newSelectedCodes, selectedCodes)) - } - - const selectAll = (toAdd: boolean) => { - const mode = toAdd ? Mode.SELECT_ALL : Mode.UNSELECT_ALL - const bySystem = groupBySystem(searchResults.tree) - const newTrees = buildMultipleTrees(trees, bySystem, codes, selectedCodes, mode) - const displaySearch = getListDisplay(searchResults.tree, newTrees) - const newSelectedCodes = getSelectedCodesFromTrees(trees) + if (mode === Mode.SELECT_ALL || mode === Mode.UNSELECT_ALL) { + const system = nodes?.[0].system || '' + const root = new Map() + if (mode === Mode.SELECT_ALL) root.set(HIERARCHY_ROOT, createHierarchyRoot(system)) + setSelectedCodes(replaceInMap(system, root, selectedCodes)) + } else setSelectedCodes(getSelectedCodesFromTrees(newTrees, selectedCodes)) setTrees(newTrees) + let newHierarchies = new Map(hierarchies) + hierarchies.forEach((hierarchy) => { + if (bySystem.find((elem) => elem.system === hierarchy.system)) { + const newTree = newTrees.get(hierarchy.system) || [] + newHierarchies = replaceInMap( + hierarchy.system, + { ...hierarchy, tree: getHierarchyDisplay(hierarchy.tree, newTree) }, + newHierarchies + ) + } + }) + setHierarchies(newHierarchies) setSearchResults({ ...searchResults, tree: displaySearch }) - setSelectedCodes(newSelectedCodes) } const expand = async (node: Hierarchy) => { @@ -164,10 +160,8 @@ export const useHierarchy = ( searchResults, selectedCodes, loadingStatus, - selectAllStatus, initTrees, select, - selectAll, expand, fetchMore } diff --git a/src/hooks/valueSet/useSearchValueSet.ts b/src/hooks/valueSet/useSearchValueSet.ts index 471adb3ae..34a0fc5a4 100644 --- a/src/hooks/valueSet/useSearchValueSet.ts +++ b/src/hooks/valueSet/useSearchValueSet.ts @@ -2,12 +2,17 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { Reference } from 'types/valueSet' import { LIMIT_PER_PAGE, SearchParameters, useSearchParameters } from '../search/useSearchParameters' import { useHierarchy } from 'hooks/hierarchy/useHierarchy' -import { getChildrenFromCodes, getHierarchyRoots, searchInValueSets } from 'services/aphp/serviceValueSets' +import { + HIERARCHY_ROOT, + getChildrenFromCodes, + getHierarchyRoots, + searchInValueSets +} from 'services/aphp/serviceValueSets' import { useDebounceAction } from 'hooks/useDebounceAction' -import { Codes, FhirItem, Hierarchy, SearchMode } from 'types/hierarchy' +import { Codes, FhirItem, Hierarchy, Mode, SearchMode } from 'types/hierarchy' import { saveValueSets, selectValueSetCodes } from 'state/valueSets' import { useAppDispatch, useAppSelector } from 'state' -import { addHierarchyRoot, mapCodesToCache } from 'utils/hierarchy/hierarchy' +import { getItemSelectedStatus, mapCodesToCache } from 'utils/hierarchy/hierarchy' export const useSearchValueSet = (references: Reference[], selectedNodes: Hierarchy[]) => { const researchParameters = useSearchParameters() @@ -33,28 +38,14 @@ export const useSearchValueSet = (references: Reference[], selectedNodes: Hierar const codes = useAppSelector((state) => selectValueSetCodes(state, urls)) - const { - hierarchies, - searchResults, - selectedCodes, - loadingStatus, - selectAllStatus, - initTrees, - fetchMore, - expand, - select, - selectAll - } = useHierarchy(selectedNodes, codes, handleSaveCodes, fetchChildren) + const { hierarchies, searchResults, selectedCodes, loadingStatus, initTrees, fetchMore, expand, select } = + useHierarchy(selectedNodes, codes, handleSaveCodes, fetchChildren) const controllerRef = useRef(null) const currentHierarchy = useMemo(() => { - const system = explorationParameters.options.references.find((ref) => ref.checked)?.url || '' - let current = hierarchies.get(system) const ref = explorationParameters.options.references.find((ref) => ref.checked) - const notHierarchy = ref ? !ref.isHierarchy : false - if (current && ref && notHierarchy) current.tree = addHierarchyRoot(current.tree, ref) return ( - current || { + hierarchies.get(ref?.url || '') || { tree: [], count: 0, page: 1, @@ -63,6 +54,27 @@ export const useSearchValueSet = (references: Reference[], selectedNodes: Hierar ) }, [hierarchies, explorationParameters.options.references]) + const selectAllStatus = useMemo(() => { + const node = { + id: 'parent', + subItems: mode === SearchMode.EXPLORATION ? currentHierarchy.tree : searchResults.tree + } as Hierarchy + return getItemSelectedStatus(node) + }, [currentHierarchy, searchResults, mode]) + + const isSelectionDisabled = useCallback( + (node: Hierarchy) => { + const isAll = selectedCodes.get(node.system)?.get(HIERARCHY_ROOT) ? true : false + if (mode === SearchMode.RESEARCH && isAll) return true + else { + const ref = explorationParameters.options.references.find((ref) => ref.checked) + if (ref && !ref.isHierarchy && isAll) return true + } + return false + }, + [explorationParameters.options.references, mode, selectedCodes] + ) + const selected = useMemo(() => { return [...selectedCodes.values()].flatMap((innerMap) => [...innerMap.values()]) }, [selectedCodes]) @@ -107,6 +119,13 @@ export const useSearchValueSet = (references: Reference[], selectedNodes: Hierar return mode === SearchMode.EXPLORATION ? explorationParameters.options[param] : researchParameters.options[param] } + const handleDeleteSelectedCodes = (code: Hierarchy) => { + const isRoot = code.id === HIERARCHY_ROOT + const toSelect = isRoot ? hierarchies.get(code.system)?.tree || [] : [code] + const mode = isRoot ? Mode.UNSELECT_ALL : Mode.UNSELECT + select(toSelect, mode) + } + const handleChangeReferences = (references: Reference[]) => { if (mode === SearchMode.EXPLORATION) explorationParameters.onChangeReferences(references) else { @@ -143,10 +162,12 @@ export const useSearchValueSet = (references: Reference[], selectedNodes: Hierar return { selectedCodes: selected, + isSelectionDisabled, mode, loadingStatus, searchInput, onChangeMode: setMode, + onDelete: handleDeleteSelectedCodes, parameters: { refs: getSearchParameter('references') as Reference[], onChangeSearchInput: handleChangeSearchInput, @@ -158,8 +179,7 @@ export const useSearchValueSet = (references: Reference[], selectedNodes: Hierar research: searchResults, selectAllStatus, expand, - select, - selectAll + select } } } diff --git a/src/services/aphp/serviceValueSets.ts b/src/services/aphp/serviceValueSets.ts index c7fdf9ee8..b3b7f33a2 100644 --- a/src/services/aphp/serviceValueSets.ts +++ b/src/services/aphp/serviceValueSets.ts @@ -116,7 +116,6 @@ export const getChildrenFromCodes = async ( count: 0 } } - console.log('test codes', codes) const json = { resourceType: 'Parameters', parameter: [ diff --git a/src/utils/hierarchy/hierarchy.ts b/src/utils/hierarchy/hierarchy.ts index 6e99a14f8..5de392f58 100644 --- a/src/utils/hierarchy/hierarchy.ts +++ b/src/utils/hierarchy/hierarchy.ts @@ -1,8 +1,7 @@ import { SelectedStatus } from 'types' import { Codes, CodesCache, GroupedBySystem, Hierarchy, InfiniteMap, Mode } from 'types/hierarchy' import { arrayToMap } from '../arrays' -import { UNKOWN_HIERARCHY_CHAPTER } from 'services/aphp/serviceValueSets' -import { Reference } from 'types/valueSet' +import { HIERARCHY_ROOT, UNKOWN_HIERARCHY_CHAPTER } from 'services/aphp/serviceValueSets' export const mapCodesToCache = (codes: Codes>) => { const valueSetOptions: CodesCache[] = [] @@ -188,11 +187,11 @@ export const buildTree = ( } } else { if (mode === Mode.EXPAND && !node.subItems) node.subItems = getMissingSubItems(node, codes) - if (mode === Mode.SELECT || mode === Mode.SELECT_ALL) node.status = SelectedStatus.SELECTED - if (mode === Mode.UNSELECT || mode === Mode.UNSELECT_ALL) node.status = SelectedStatus.NOT_SELECTED + if (mode === Mode.SELECT) node.status = SelectedStatus.SELECTED + if (mode === Mode.UNSELECT) node.status = SelectedStatus.NOT_SELECTED if (mode !== Mode.SEARCH && mode !== Mode.INIT) updateBranchStatus(node, node.status) } - if ((mode === Mode.SEARCH || mode === Mode.INIT) && selected.get(currentPath)) { + if ((mode === Mode.SEARCH || mode === Mode.INIT) && (selected.get(currentPath) || selected.get(HIERARCHY_ROOT))) { node.status = SelectedStatus.SELECTED updateBranchStatus(node, SelectedStatus.SELECTED) } @@ -201,7 +200,7 @@ export const buildTree = ( const paths = getPaths(baseTree, endCodes, mode === Mode.UNSELECT_ALL || mode === Mode.SELECT_ALL) const uniquePaths = getUniquePath(paths) console.log('test select paths', uniquePaths) - if (mode === Mode.INIT) baseTree = [] + if (mode === Mode.INIT || mode === Mode.SEARCH) baseTree = [] for (const [key, value] of uniquePaths) { const index = baseTree.findIndex((elem) => elem.id === key) const branch = buildBranch(baseTree[index] || null, system, [key, value], codes, selected, mode) @@ -322,9 +321,13 @@ export const getSelectedCodesFromTree = (tree: Hierarchy[]) => { return selectedCodes } -export const getSelectedCodesFromTrees = (trees: Map[]>) => { +export const getSelectedCodesFromTrees = (trees: Map[]>, prevCodes: Codes) => { const selectedCodes: Codes> = new Map() - trees.forEach((tree, key) => selectedCodes.set(key, getSelectedCodesFromTree(tree))) + trees.forEach((tree, key) => { + const isFound = prevCodes.get(key) + if (isFound && isFound.get(HIERARCHY_ROOT)) selectedCodes.set(key, isFound) + else selectedCodes.set(key, getSelectedCodesFromTree(tree)) + }) return selectedCodes } @@ -351,16 +354,13 @@ const getInferiorLevels = (hierarchy: Hierarchy[]) => { ) } -export const addHierarchyRoot = (hierarchy: Hierarchy[], reference: Reference) => { -console.log("test") - return [ - { - id: '*', - label: "Toute la hiérarchie", - system: reference.url, - above_levels_ids: '', - inferior_levels_ids: '', - subItems: hierarchy - } - ] +export const createHierarchyRoot = (system: string, status?: SelectedStatus) => { + return { + id: HIERARCHY_ROOT, + label: 'Toute la hiérarchie', + above_levels_ids: '', + inferior_levels_ids: '', + system, + status + } }