diff --git a/src/components/PatientsBoard/SavedFiltersSection/EditSavedFilter.tsx b/src/components/PatientsBoard/SavedFiltersSection/EditSavedFilter.tsx index 00db8c905..c9b3f62df 100644 --- a/src/components/PatientsBoard/SavedFiltersSection/EditSavedFilter.tsx +++ b/src/components/PatientsBoard/SavedFiltersSection/EditSavedFilter.tsx @@ -8,7 +8,8 @@ import { vitalStatusesOptions, SearchCriterias, searchByListPatients, - SearchByTypes + SearchByTypes, + Filters } from 'types/searchCriterias' import { SelectedFilter } from 'hooks/filters/useSavedFilters' import Select from 'components/ui/Searchbar/Select' @@ -19,18 +20,12 @@ type EditSavedFilterProps = { deidentified: boolean readonly?: boolean open: boolean - criteria: SelectedFilter + criteria: SelectedFilter disabled?: boolean - onEdit: (name: string, newSearchCriterias: SearchCriterias, deidentified: boolean) => void + onEdit: (name: string, newSearchCriterias: SearchCriterias, deidentified: boolean) => void onClose: () => void } -type DynamicFilters = { - [key: string]: string[] -} - -export type DynamicSelectedFilter = SelectedFilter - const EditSavedFilter = ({ deidentified, criteria, @@ -44,7 +39,7 @@ const EditSavedFilter = ({ handleSubmit, reset, formState: { isDirty, errors, isValid } - } = useForm({ + } = useForm>({ defaultValues: criteria, mode: 'onChange', reValidateMode: 'onChange' @@ -52,7 +47,7 @@ const EditSavedFilter = ({ useEffect(() => { reset(criteria) - }, [criteria, reset]) + }, [criteria]) return ( void } -const SaveFilterAction = ({ disabled = false, error, onSubmit }: SaveFilterActionProps) => { +const SaveFilterAction = ({ disabled = false, onSubmit }: SaveFilterActionProps) => { const [toggleModal, setToggleModal] = useState(false) const { control, @@ -27,10 +27,6 @@ const SaveFilterAction = ({ disabled = false, error, onSubmit }: SaveFilterActio reset({ name: '' }) }, [toggleModal]) - useEffect(() => { - console.log('test save name', errors.name, isValid, errors) - }, [isValid]) - return ( <> @@ -81,7 +77,7 @@ const SaveFilterAction = ({ disabled = false, error, onSubmit }: SaveFilterActio {...field} label="Nom :" placeholder="Choisir un nom compris entre 2 et 50 caractères" - errorMessage={isDirty && errors.name?.message} + errorMessage={isDirty ? errors.name?.message : ''} /> )} /> diff --git a/src/components/PatientsBoard/SavedFiltersSection/SavedFilters.tsx b/src/components/PatientsBoard/SavedFiltersSection/SavedFilters.tsx index 0304fc96b..f044ada2b 100644 --- a/src/components/PatientsBoard/SavedFiltersSection/SavedFilters.tsx +++ b/src/components/PatientsBoard/SavedFiltersSection/SavedFilters.tsx @@ -6,7 +6,7 @@ import { Item } from 'components/ui/List/ListItem' import { DeleteOutline, Edit, SavedSearch, Visibility } from '@mui/icons-material' import { SelectedFilter } from 'hooks/filters/useSavedFilters' import { Filters, SearchCriterias } from 'types/searchCriterias' -import EditSavedFilter, { DynamicSelectedFilter } from './EditSavedFilter' +import EditSavedFilter from './EditSavedFilter' type SavedFiltersProps = { deidentified: boolean @@ -105,7 +105,10 @@ const SavedFilters = ({ width="250px" open={toggleDeleteModal} onClose={() => setToggleDeleteModal(false)} - onSubmit={() => onDelete(selectedItems)} + onSubmit={() => { + onDelete(selectedItems) + setToggleDeleteModal(false) + }} > Êtes-vous sur de vouloir supprimer les éléments sélectionnés ? @@ -116,7 +119,7 @@ const SavedFilters = ({ { onEdit(name, filters, deidentified) setToggleDisplayModal(false) diff --git a/src/components/PatientsBoard/SavedFiltersSection/index.tsx b/src/components/PatientsBoard/SavedFiltersSection/index.tsx index 5e3e738b1..7c396d9ed 100644 --- a/src/components/PatientsBoard/SavedFiltersSection/index.tsx +++ b/src/components/PatientsBoard/SavedFiltersSection/index.tsx @@ -5,6 +5,7 @@ import { Filters, PatientsFilters, SearchCriterias } from 'types/searchCriterias import { ResourceType } from 'types/requestCriterias' import { useSavedFilters } from 'hooks/filters/useSavedFilters' import SavedFilters from './SavedFilters' +import { FetchStatus } from 'types' type SavedFiltersSectionProps = { deidentified: boolean @@ -16,9 +17,9 @@ type SavedFiltersSectionProps = { const SavedFiltersSection = ({ deidentified, criterias, canSave, onSelect }: SavedFiltersSectionProps) => { const { allSavedFilters, - savedFiltersErrors, + fetchStatus, selectedSavedFilter, - methods: { next, postSavedFilter, deleteSavedFilters, patchSavedFilter, selectFilter, resetSavedFilterError } + methods: { next, postSavedFilter, deleteSavedFilters, patchSavedFilter, selectFilter, resetFetchStatus } } = useSavedFilters(ResourceType.PATIENT) const asListItems = useMemo( @@ -38,14 +39,18 @@ const SavedFiltersSection = ({ deidentified, criterias, canSave, onSelect }: Sav /> )} - resetSavedFilterError()} - anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }} - autoHideDuration={6000} - > - {savedFiltersErrors.errorMessage} - + {fetchStatus && ( + resetFetchStatus()} + anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }} + autoHideDuration={6000} + > + + {fetchStatus?.message} + + + )} {asListItems.length > 0 && ( { - /*const { - inputs, - inputs: { genders, vitalStatuses, birthdatesRanges }, - changeFormError, - changeInput, - hasErrors - } = useForm(filters)*/ + const { + control, + handleSubmit, + reset, + formState: { isDirty, errors, isValid } + } = useForm({ + defaultValues: filters, + mode: 'onChange', + reValidateMode: 'onChange' + }) const [toggleModal, setToggleModal] = useState(false) + useEffect(() => { + reset(filters) + }, [filters]) + return ( <> - {/* setToggleModal(false)} - onSubmit={() => { - onSubmit(inputs) + onSubmit={handleSubmit((data) => { + onSubmit(data) setToggleModal(false) - }} + })} > - changeInput(FilterKeys.GENDERS, value)} - /> - changeInput(FilterKeys.VITAL_STATUSES, value)} - /> - } + /> + )} + {FilterKeys.VITAL_STATUSES in filters && ( + } + /> + )} + {/* changeInput(FilterKeys.BIRTHDATES, value)} onError={changeFormError} - /> - */} + />*/} + ) } diff --git a/src/components/PatientsBoard/SearchSection/index.tsx b/src/components/PatientsBoard/SearchSection/index.tsx index 652bab1a9..451ce8625 100644 --- a/src/components/PatientsBoard/SearchSection/index.tsx +++ b/src/components/PatientsBoard/SearchSection/index.tsx @@ -11,7 +11,6 @@ type SearchSectionProps = { } const SearchSection = ({ deidentified, criterias, onSearch }: SearchSectionProps) => { - return ( diff --git a/src/components/PatientsBoard/index.tsx b/src/components/PatientsBoard/index.tsx index 262fab909..6bb1c6285 100644 --- a/src/components/PatientsBoard/index.tsx +++ b/src/components/PatientsBoard/index.tsx @@ -3,6 +3,8 @@ import SearchSection from './SearchSection' import SavedFiltersSection from './SavedFiltersSection' import CriteriasSection from './CriteriasSection' import { usePatientBoard } from './usePatientsBoard' +import { useData } from './useData' +import { ResourceType } from 'types/requestCriterias' type PatientsBoardProps = { deidentified: boolean | null @@ -10,6 +12,7 @@ type PatientsBoardProps = { const PatientsBoard = ({ deidentified }: PatientsBoardProps) => { const { criterias, searchCriterias, onSaveSearchCriterias, removeFilter } = usePatientBoard() + const { data, loadingStatus } = useData(ResourceType.PATIENT, searchCriterias, 0) return ( diff --git a/src/components/PatientsBoard/useData.ts b/src/components/PatientsBoard/useData.ts new file mode 100644 index 000000000..01670bb71 --- /dev/null +++ b/src/components/PatientsBoard/useData.ts @@ -0,0 +1,36 @@ +import { CanceledError } from 'axios' +import { useEffect, useState } from 'react' +import servicesCohorts from 'services/aphp/serviceCohorts' +import { LoadingStatus } from 'types' +import { ResourceType } from 'types/requestCriterias' +import { Filters, SearchCriterias } from 'types/searchCriterias' + +export const useData = (type: ResourceType, searchCriterias: SearchCriterias, page: number) => { + const [loadingStatus, setLoadingStatus] = useState(LoadingStatus.IDDLE) + const [data, setData] = useState(null) + + const fetchData = async (searchCriterias: SearchCriterias, page: number) => { + try { + setLoadingStatus(LoadingStatus.FETCHING) + const fetcher = servicesCohorts.getPatientBoardFetcher(type) + const results = await fetcher({ page, searchCriterias }) + setData(results) + setLoadingStatus(LoadingStatus.SUCCESS) + } catch (error) { + if (error instanceof CanceledError) { + setLoadingStatus(LoadingStatus.FETCHING) + } + setLoadingStatus(LoadingStatus.SUCCESS) + } + } + + useEffect(() => { + const fetch = async () => await fetchData(searchCriterias, page) + fetch() + }, [searchCriterias, page]) + + return { + data, + loadingStatus + } +} diff --git a/src/components/PatientsBoard/usePatientsBoard.ts b/src/components/PatientsBoard/usePatientsBoard.ts index b8b81daf0..c207de086 100644 --- a/src/components/PatientsBoard/usePatientsBoard.ts +++ b/src/components/PatientsBoard/usePatientsBoard.ts @@ -11,6 +11,11 @@ export const usePatientBoard = () => { return filters ? selectFiltersAsArray(filters) : [] }, [filters]) + const searchCriterias = useMemo( + () => ({ orderBy, searchBy, searchInput, filters }), + [orderBy, searchBy, searchInput, filters] + ) + const onSaveSearchCriterias = ({ searchBy, searchInput, filters }: SearchCriterias) => { if (searchBy) changeSearchBy(searchBy) if (searchInput !== undefined) changeSearchInput(searchInput) @@ -18,7 +23,7 @@ export const usePatientBoard = () => { } return { - searchCriterias: { orderBy, searchBy, searchInput, filters }, + searchCriterias, criterias, onSaveSearchCriterias, removeFilter diff --git a/src/hooks/filters/useSavedFilters.ts b/src/hooks/filters/useSavedFilters.ts index 0b9312b1a..e7c90c1e2 100644 --- a/src/hooks/filters/useSavedFilters.ts +++ b/src/hooks/filters/useSavedFilters.ts @@ -2,31 +2,36 @@ import { Item } from 'components/ui/List/ListItem' import { mapRequestParamsToSearchCriteria } from 'mappers/filters' import { useEffect, useState } from 'react' import { + deleteFilterService, deleteFiltersService, getFiltersService, patchFiltersService, postFiltersService } from 'services/aphp/servicePatients' -import { ErrorType } from 'types/error' +import { FetchStatus } from 'types' import { ResourceType } from 'types/requestCriterias' import { Filters, SavedFilter, SavedFiltersResults, SearchCriterias } from 'types/searchCriterias' +type FetchResponse = { + status: FetchStatus + message: string +} + export type SelectedFilter = { filterUuid: string filterName: string - filterParams: SearchCriterias + filterParams: SearchCriterias } export const useSavedFilters = (type: ResourceType) => { const [allSavedFilters, setAllSavedFilters] = useState(null) - const [savedFiltersErrors, setSavedFiltersErrors] = useState({ isError: false }) + const [fetchStatus, setFetchStatus] = useState(null) const [selectedSavedFilter, setSelectedSavedFilter] = useState | null>(null) useEffect(() => { getSavedFilters() }, [type]) - const getSavedFilters = async (next?: string | null) => { try { const response = await getFiltersService(type, next) @@ -40,23 +45,31 @@ export const useSavedFilters = (type: ResourceType) => { } } catch (err) { setAllSavedFilters(null) + setFetchStatus({ status: FetchStatus.ERROR, message: 'Erreur lors de la récupération des filtres.' }) } } const postSavedFilter = async (name: string, searchCriterias: SearchCriterias, deidentified: boolean) => { try { await postFiltersService(type, name, searchCriterias, deidentified) - setSavedFiltersErrors({ isError: false }) + setFetchStatus({ status: FetchStatus.SUCCESS, message: 'Le filtre a bien été sauvegardé.' }) await getSavedFilters() } catch { - setSavedFiltersErrors({ isError: true, errorMessage: "Il y a eu une erreur lors de l'enregistrement du filtre. Vérifiez que le nom n'existe pas déjà." }) - throw 'Nom déjà existant' + setFetchStatus({ + status: FetchStatus.ERROR, + message: "Erreur lors de l'enregistrement du filtre. Vérifiez que le nom n'existe pas déjà." + }) } } const deleteSavedFilters = async (filtersUuids: string[]) => { - await deleteFiltersService(filtersUuids) - await getSavedFilters() + try { + if (filtersUuids.length > 1) await deleteFiltersService(filtersUuids) + else deleteFilterService(filtersUuids[0]) + await getSavedFilters() + } catch { + setFetchStatus({ status: FetchStatus.ERROR, message: 'Erreur lors de la suppression des filtres.' }) + } } const patchSavedFilter = async ( @@ -65,8 +78,13 @@ export const useSavedFilters = (type: ResourceType) => { deidentified: boolean ): Promise => { if (selectedSavedFilter) { - await patchFiltersService(type, selectedSavedFilter?.filterUuid, name, newSearchCriterias, deidentified) - await getSavedFilters() + try { + await patchFiltersService(type, selectedSavedFilter?.filterUuid, name, newSearchCriterias, deidentified) + } catch { + setFetchStatus({ status: FetchStatus.ERROR, message: 'Erreur lors de la modification du filtre.' }) + } finally { + await getSavedFilters() + } } } @@ -84,21 +102,21 @@ export const useSavedFilters = (type: ResourceType) => { else setSelectedSavedFilter(null) } - const resetSavedFilterError = () => { - setSavedFiltersErrors({ isError: false }) + const resetFetchStatus = () => { + setFetchStatus(null) } return { allSavedFilters, selectedSavedFilter, - savedFiltersErrors, + fetchStatus, methods: { next: () => getSavedFilters(allSavedFilters?.next), postSavedFilter, deleteSavedFilters, patchSavedFilter, selectFilter, - resetSavedFilterError, + resetFetchStatus, mapToSelectedFilter } } diff --git a/src/services/aphp/serviceCohorts.ts b/src/services/aphp/serviceCohorts.ts index 75a2a3f6d..c7c3e9ded 100644 --- a/src/services/aphp/serviceCohorts.ts +++ b/src/services/aphp/serviceCohorts.ts @@ -243,6 +243,12 @@ export interface IServiceCohorts { signal?: AbortSignal ) => Promise> + /** + * + * Retourne le service de récupération de donnée en fonction de la ressource + */ + getPatientBoardFetcher: (resourceType: ResourceType) => Function + /** * Permet de vérifier si le champ de recherche textuelle est correct * @@ -1094,6 +1100,15 @@ const servicesCohorts: IServiceCohorts = { } }, + getPatientBoardFetcher: (resourceType: ResourceType) => { + switch (resourceType) { + case ResourceType.PATIENT: + return ({ searchCriterias, page }, deidentified: boolean) => + servicesCohorts.fetchPatientList({ searchCriterias, page }, deidentified) + } + return () => {} + }, + fetchDocumentContent: async (compositionId) => { const documentContent = await fetchDocumentReferenceContent(compositionId) return getApiResponseResource(documentContent) diff --git a/src/services/aphp/servicePatients.ts b/src/services/aphp/servicePatients.ts index b5489565f..09f4c0ad2 100644 --- a/src/services/aphp/servicePatients.ts +++ b/src/services/aphp/servicePatients.ts @@ -901,37 +901,27 @@ export const postFiltersService = async ( ) => { const criteriasString = mapSearchCriteriasToRequestParams(criterias, fhir_resource, deidentified) const response = await postFilters(fhir_resource, name, criteriasString) - return response.data } export const getFiltersService = async (fhir_resource: ResourceType, next?: string | null, limit = 10) => { const LIMIT = limit const OFFSET = 0 - try { - const response = await getFilters(fhir_resource, LIMIT, OFFSET, next) - return response.data - } catch { - throw "Les filtres sauvegardés n'ont pas pu être récupérés." - } + const response = await getFilters(fhir_resource, LIMIT, OFFSET, next) + if (response.status !== 200) throw new Error("Les filtres sauvegardés n'ont pas pu être récupérés.") + return response.data } export const deleteFilterService = async (fhir_resource_uuid: string) => { - try { - const response = await deleteFilter(fhir_resource_uuid) - return response - } catch { - throw "Le filtre n'a pas pu être supprimé." - } + const response = await deleteFilter(fhir_resource_uuid) + if (response.status !== 200) throw new Error("Le filtre n'a pas pu être supprimé.") + return response } export const deleteFiltersService = async (fhir_resource_uuids: string[]) => { - try { - const response = await deleteFilters(fhir_resource_uuids) - return response - } catch { - throw "Les filtres n'ont pas pu être supprimés." - } + const response = await deleteFilters(fhir_resource_uuids) + if (response.status !== 200) throw new Error("Les filtres n'ont pas pu être supprimés.") + return response } export const patchFiltersService = async ( @@ -943,6 +933,5 @@ export const patchFiltersService = async ( ) => { const criteriasString = mapSearchCriteriasToRequestParams(criterias, fhir_resource, deidentified) const response = await patchFilters(fhir_resource, uuid, name, criteriasString) - return response } diff --git a/src/types.ts b/src/types.ts index 3437a0a64..7063f5fc0 100644 --- a/src/types.ts +++ b/src/types.ts @@ -72,6 +72,11 @@ export enum LoadingStatus { SUCCESS = 'SUCCESS' } +export enum FetchStatus { + ERROR = 'ERROR', + SUCCESS = 'SUCCESS' +} + export enum TemporalConstraintsKind { NONE = 'none', SAME_ENCOUNTER = 'sameEncounter', diff --git a/src/utils/filters.ts b/src/utils/filters.ts index 9b8b0553a..f0c937b1f 100644 --- a/src/utils/filters.ts +++ b/src/utils/filters.ts @@ -87,7 +87,7 @@ export const removeFilter = (key: FilterKeys, value: FilterValue, filters: F) break } } - return castedFilters + return {...castedFilters} } export const getFilterLabel = (key: FilterKeys, value: FilterValue): string => {