diff --git a/.gitignore b/.gitignore index 349500c11..97963f95a 100644 --- a/.gitignore +++ b/.gitignore @@ -30,4 +30,5 @@ yarn-error.log* .env* !.env.example .mp4 -public/config.dev.json \ No newline at end of file +public/config.dev.json +public/config.example.json diff --git a/public/config.example.json b/public/config.example.json new file mode 100644 index 000000000..980c5b5da --- /dev/null +++ b/public/config.example.json @@ -0,0 +1,163 @@ +{ + "core": { + "codeSystems": { + "docStatus": "http://hl7.org/fhir/CodeSystem/composition-status" + }, + "extensions": { + }, + "perimeterSourceTypeHierarchy": [ + "hospital","hospital_depart","hospital_serv" + ], + "valueSets": { + "demographicGender": { + "url": "https://terminology.eds.aphp.fr/aphp-orbis-patient-genre" + }, + "encounterAdmission": { + "url": "https://terminology.eds.aphp.fr/aphp-orbis-visit-type-admission" + }, + "encounterAdmissionMode": { + "url": "https://terminology.eds.aphp.fr/aphp-orbis-visit-motif-admission" + }, + "encounterDestination": { + "url": "https://terminology.eds.aphp.fr/aphp-orbis-visit-destination" + }, + "encounterEntryMode": { + "url": "https://terminology.eds.aphp.fr/aphp-orbis-visit-mode-entree" + }, + "encounterExitMode": { + "url": "https://terminology.eds.aphp.fr/aphp-orbis-visit-mode-sortie" + }, + "encounterExitType": { + "url": "https://terminology.eds.aphp.fr/aphp-orbis-visit-type-sortie" + }, + "encounterFileStatus": { + "url": "https://terminology.eds.aphp.fr/aphp-orbis-visite-status" + }, + "encounterProvenance": { + "url": "https://terminology.eds.aphp.fr/aphp-orbis-visit-provenance" + }, + "encounterSejourType": { + "url": "https://terminology.eds.aphp.fr/aphp-orbis-type-sejour" + }, + "encounterStatus": { + "url": "http://hl7.org/fhir/CodeSystem/encounter-status" + }, + "encounterVisitType": { + "url": "https://terminology.eds.aphp.fr/aphp-orbis-visit-type" + } + }, + "fhir": { + "valueSetExploration": false, + "facetsExtensions": false, + "filterActive": false, + "totalCount": true, + "selectedCodeOnly": false, + "extraSearchParams": false, + "resourceLists": false, + "textSearch": false, + "valuesetExpansion": false + } + }, + "features": { + "claim": { + "enabled": false + }, + "cohort": { + "enabled": true, + "shortCohortLimit": 2000 + }, + "condition": { + "enabled": true, + "valueSets": { + "conditionHierarchy": { + "url": "https://smt.esante.gouv.fr/terminologie-cim-10/" + }, + "conditionStatus": { + "url": "https://terminology.eds.aphp.fr/aphp-orbis-condition-status" + } + } + }, + "contact": { + "enabled": false + }, + "export": { + "enabled": false, + "exportLinesLimit": 300000 + }, + "feasabilityReport": { + "enabled": false + }, + "imaging": { + "enabled": true, + "extensions": {}, + "valueSets": { + "imagingModalities": { + "url": "https://dicom.nema.org/medical/dicom/current/output/chtml/part16/sect_CID_33.html" + } + } + }, + "locationMap": { + "enabled": false + }, + "medication": { + "enabled": true, + "valueSets": { + "medicationAdministrations": { + "url": "https://terminology.eds.aphp.fr/aphp-orbis-medicament-voie-administration" + }, + "medicationAtc": { + "url": "https://terminology.eds.aphp.fr/atc" + }, + "medicationAtcOrbis": { + "url": "https://terminology.eds.aphp.fr/aphp-orbis-medicament-atc-article" + }, + "medicationPrescriptionTypes": { + "url": "https://terminology.eds.aphp.fr/aphp-medicament-type-prescription" + }, + "medicationUcd": { + "url": "https://terminology.eds.aphp.fr/smt-medicament-ucd" + } + }, + "useMedicationAtcUcdCodes": false + }, + "observation": { + "enabled": true, + "valueSets": { + "biologyHierarchyAnabio": { + "url": "https://terminology.eds.aphp.fr/aphp-itm-anabio" + }, + "biologyHierarchyLoinc": { + "url": "https://terminology.eds.aphp.fr/aphp-itm-loinc" + } + }, + "useObservationValueRestriction": false, + "useObservationDefaultValidated": false + }, + "procedure": { + "enabled": true, + "valueSets": { + "procedureHierarchy": { + "url": "https://www.atih.sante.fr/plateformes-de-transmission-et-logiciels/logiciels-espace-de-telechargement/id_lot/3550" + } + } + }, + "questionnaires": { + "enabled": false + } + }, + "labels": { + "exploration": "Exploration" + }, + "system": { + "codeDisplayJWT": "ArrowUp,ArrowUp,ArrowDown,ArrowDown,ArrowLeft,ArrowRight,ArrowLeft,ArrowRight,b,a,Enter", + "oidc": { + "clientId": "cohort360", + "issuer": "http://172.28.0.20:8080/realms/cohort/protocol/openid-connect/auth", + "redirectUri": "http://localhost:8003", + "responseType": "code", + "scope": "openid", + "state": "mlTPASu3bwdFmFUSK4G4ImecrsKW9pQ7SDdf7uB" + }, + "wsProtocol": "ws://" + } +} diff --git a/src/components/CreationCohort/DataList_Criteria.tsx b/src/components/CreationCohort/DataList_Criteria.tsx index e65ae7acf..3ed6dcb68 100644 --- a/src/components/CreationCohort/DataList_Criteria.tsx +++ b/src/components/CreationCohort/DataList_Criteria.tsx @@ -25,6 +25,7 @@ const criteriaList: () => CriteriaItemType[] = () => { const ODD_IMAGING = getConfig().features.imaging.enabled const ODD_MEDICATION = getConfig().features.medication.enabled const ODD_DOCUMENT_REFERENCE = getConfig().features.documentReference.enabled + const ODD_CLAIM = getConfig().features.claim.enabled return [ { id: CriteriaType.REQUEST, @@ -112,17 +113,22 @@ const criteriaList: () => CriteriaItemType[] = () => { encounterStatus: services.cohortCreation.fetchEncounterStatus } }, - { - id: CriteriaType.CLAIM, - title: CriteriaTypeLabels.CLAIM, - color: '#0063AF', - fontWeight: 'normal', - components: GhmForm, - fetch: { - ghmData: services.cohortCreation.fetchGhmData, - encounterStatus: services.cohortCreation.fetchEncounterStatus - } - } + ...(ODD_CLAIM + ? [ + { + id: CriteriaType.CLAIM, + title: CriteriaTypeLabels.CLAIM, + color: '#0063AF', + fontWeight: 'normal', + components: GhmForm, + fetch: { + ghmData: services.cohortCreation.fetchGhmData, + encounterStatus: services.cohortCreation.fetchEncounterStatus + }, + disabled: !ODD_CLAIM + } + ] + : []) ] }, { diff --git a/src/components/CreationCohort/DiagramView/components/LogicalOperator/components/CriteriaRightPanel/BiologyForm/index.tsx b/src/components/CreationCohort/DiagramView/components/LogicalOperator/components/CriteriaRightPanel/BiologyForm/index.tsx index 459cf14d1..685894a07 100644 --- a/src/components/CreationCohort/DiagramView/components/LogicalOperator/components/CriteriaRightPanel/BiologyForm/index.tsx +++ b/src/components/CreationCohort/DiagramView/components/LogicalOperator/components/CriteriaRightPanel/BiologyForm/index.tsx @@ -32,10 +32,10 @@ export const defaultBiology: Omit = { const Index = (props: CriteriaDrawerComponentProps) => { const { criteriaData, selectedCriteria, onChangeSelectedCriteria, goBack } = props - const config = useContext(AppConfig) + const appConfig = useContext(AppConfig) const { classes } = useStyles() const [selectedTab, setSelectedTab] = useState<'form' | 'hierarchy' | 'search'>( - selectedCriteria ? 'form' : 'hierarchy' + selectedCriteria || !appConfig.core.fhir.valueSetExploration ? 'form' : 'hierarchy' ) const [defaultCriteria, setDefaultCriteria] = useState( (selectedCriteria as ObservationDataType) || defaultBiology @@ -83,8 +83,8 @@ const Index = (props: CriteriaDrawerComponentProps) => { value={selectedTab} onChange={(e, tab) => setSelectedTab(tab)} > - - + {appConfig.core.fhir.valueSetExploration && } + {appConfig.core.fhir.valueSetExploration && } diff --git a/src/components/CreationCohort/DiagramView/components/LogicalOperator/components/CriteriaRightPanel/CCAM/index.tsx b/src/components/CreationCohort/DiagramView/components/LogicalOperator/components/CriteriaRightPanel/CCAM/index.tsx index c395cf165..2427dfd9b 100644 --- a/src/components/CreationCohort/DiagramView/components/LogicalOperator/components/CriteriaRightPanel/CCAM/index.tsx +++ b/src/components/CreationCohort/DiagramView/components/LogicalOperator/components/CriteriaRightPanel/CCAM/index.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react' +import React, { useContext, useEffect, useState } from 'react' import { Tab, Tabs } from '@mui/material' import useStyles from './styles' @@ -13,6 +13,7 @@ import { EXPLORATION } from '../../../../../../../..//constants' import { CriteriaDrawerComponentProps } from 'types' import { CcamDataType, Comparators, CriteriaType } from 'types/requestCriterias' import { Hierarchy } from 'types/hierarchy' +import { AppConfig } from 'config' export const defaultProcedure: Omit = { type: CriteriaType.PROCEDURE, @@ -32,7 +33,10 @@ export const defaultProcedure: Omit = { const Index = (props: CriteriaDrawerComponentProps) => { const { criteriaData, selectedCriteria, onChangeSelectedCriteria, goBack } = props - const [selectedTab, setSelectedTab] = useState<'form' | 'hierarchy'>(selectedCriteria ? 'form' : 'hierarchy') + const appConfig = useContext(AppConfig) + const [selectedTab, setSelectedTab] = useState<'form' | 'hierarchy'>( + selectedCriteria || !appConfig.core.fhir.valueSetExploration ? 'form' : 'hierarchy' + ) const [defaultCriteria, setDefaultCriteria] = useState( (selectedCriteria as CcamDataType) || defaultProcedure ) @@ -81,7 +85,7 @@ const Index = (props: CriteriaDrawerComponentProps) => { value={selectedTab} onChange={(e, tab) => setSelectedTab(tab)} > - + {appConfig.core.fhir.valueSetExploration && } diff --git a/src/components/CreationCohort/DiagramView/components/LogicalOperator/components/CriteriaRightPanel/Cim10Form/components/Form/Cim10Form.tsx b/src/components/CreationCohort/DiagramView/components/LogicalOperator/components/CriteriaRightPanel/Cim10Form/components/Form/Cim10Form.tsx index f474e53a3..fef16b997 100644 --- a/src/components/CreationCohort/DiagramView/components/LogicalOperator/components/CriteriaRightPanel/Cim10Form/components/Form/Cim10Form.tsx +++ b/src/components/CreationCohort/DiagramView/components/LogicalOperator/components/CriteriaRightPanel/Cim10Form/components/Form/Cim10Form.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react' +import React, { useContext, useState } from 'react' import { Alert, @@ -31,6 +31,7 @@ import { BlockWrapper } from 'components/ui/Layout' import OccurenceInput from 'components/ui/Inputs/Occurences' import { SourceType } from 'types/scope' import { Hierarchy } from 'types/hierarchy' +import { AppConfig } from 'config' type Cim10FormProps = { isOpen: boolean @@ -50,7 +51,7 @@ enum Error { const Cim10Form: React.FC = (props) => { const { isOpen, isEdition, criteriaData, selectedCriteria, onChangeValue, onChangeSelectedCriteria, goBack } = props - + const appConfig = useContext(AppConfig) const { classes } = useStyles() const dispatch = useAppDispatch() const initialState: HierarchyTree | null = useAppSelector((state) => state.syncHierarchyTable) @@ -205,17 +206,19 @@ const Cim10Form: React.FC = (props) => { onChangeValue('code', value) }} /> - option.label} - isOptionEqualToValue={(option, value) => option.id === value.id} - value={defaultValuesType} - onChange={(e, value) => onChangeValue('diagnosticType', value)} - renderInput={(params) => } - /> + {appConfig.core.fhir.extraSearchParams && ( + option.label} + isOptionEqualToValue={(option, value) => option.id === value.id} + value={defaultValuesType} + onChange={(e, value) => onChangeValue('diagnosticType', value)} + renderInput={(params) => } + /> + )} = { type: CriteriaType.CONDITION, @@ -31,7 +32,10 @@ export const defaultCondition: Omit = { const Index = (props: CriteriaDrawerComponentProps) => { const { criteriaData, selectedCriteria, onChangeSelectedCriteria, goBack } = props - const [selectedTab, setSelectedTab] = useState<'form' | 'hierarchy'>(selectedCriteria ? 'form' : 'hierarchy') + const appConfig = useContext(AppConfig) + const [selectedTab, setSelectedTab] = useState<'form' | 'hierarchy'>( + selectedCriteria || !appConfig.core.fhir.valueSetExploration ? 'form' : 'hierarchy' + ) const [defaultCriteria, setDefaultCriteria] = useState( (selectedCriteria as Cim10DataType) || defaultCondition ) @@ -80,7 +84,7 @@ const Index = (props: CriteriaDrawerComponentProps) => { value={selectedTab} onChange={(e, tab) => setSelectedTab(tab)} > - + {appConfig.core.fhir.valueSetExploration && } diff --git a/src/components/CreationCohort/DiagramView/components/LogicalOperator/components/CriteriaRightPanel/DemographicForm/index.tsx b/src/components/CreationCohort/DiagramView/components/LogicalOperator/components/CriteriaRightPanel/DemographicForm/index.tsx index 2b42ac562..8facc3224 100644 --- a/src/components/CreationCohort/DiagramView/components/LogicalOperator/components/CriteriaRightPanel/DemographicForm/index.tsx +++ b/src/components/CreationCohort/DiagramView/components/LogicalOperator/components/CriteriaRightPanel/DemographicForm/index.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react' +import React, { useContext, useState } from 'react' import { Alert, @@ -26,6 +26,7 @@ import { CriteriaDataKey, DemographicDataType, CriteriaType } from 'types/reques import { BlockWrapper } from 'components/ui/Layout' import { CriteriaDrawerComponentProps, CriteriaItemDataCache } from 'types' import { CriteriaLabel } from 'components/ui/CriteriaLabel' +import { AppConfig } from 'config' enum Error { INCOHERENT_AGE_ERROR, @@ -61,6 +62,7 @@ const DemographicForm = (props: CriteriaDrawerComponentProps) => { const [isInclusive, setIsInclusive] = useState( selectedCriteria?.isInclusive === undefined ? true : selectedCriteria?.isInclusive ) + const appConfig = useContext(AppConfig) const selectedPopulation = useAppSelector((state) => state.cohortCreation.request.selectedPopulation || []) @@ -183,24 +185,26 @@ const DemographicForm = (props: CriteriaDrawerComponentProps) => { )} - - status.label === VitalStatusLabel.DECEASED) - ? VitalStatusOptionsLabel.deceasedAge - : VitalStatusOptionsLabel.age - } - /> - setAge(value)} - onError={(isError) => setError(isError ? Error.INCOHERENT_AGE_ERROR : Error.NO_ERROR)} - deidentified={deidentified} - /> - + {appConfig.core.fhir.extraSearchParams && ( + + status.label === VitalStatusLabel.DECEASED) + ? VitalStatusOptionsLabel.deceasedAge + : VitalStatusOptionsLabel.age + } + /> + setAge(value)} + onError={(isError) => setError(isError ? Error.INCOHERENT_AGE_ERROR : Error.NO_ERROR)} + deidentified={deidentified} + /> + + )} {!deidentified && vitalStatus && (vitalStatus.length === 0 || diff --git a/src/components/CreationCohort/DiagramView/components/LogicalOperator/components/CriteriaRightPanel/DocumentsForm/DocumentsForm.tsx b/src/components/CreationCohort/DiagramView/components/LogicalOperator/components/CriteriaRightPanel/DocumentsForm/DocumentsForm.tsx index c43918b47..c3edbbc30 100644 --- a/src/components/CreationCohort/DiagramView/components/LogicalOperator/components/CriteriaRightPanel/DocumentsForm/DocumentsForm.tsx +++ b/src/components/CreationCohort/DiagramView/components/LogicalOperator/components/CriteriaRightPanel/DocumentsForm/DocumentsForm.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react' +import React, { useContext, useEffect, useState } from 'react' import _ from 'lodash' import { @@ -38,6 +38,7 @@ import SearchInput from 'components/ui/Searchbar/SearchInput' import { BlockWrapper } from 'components/ui/Layout' import OccurenceInput from 'components/ui/Inputs/Occurences' import { SourceType } from 'types/scope' +import { AppConfig } from 'config' const defaultComposition: Omit = { type: CriteriaType.DOCUMENTS, @@ -64,7 +65,7 @@ enum Error { const DocumentsForm: React.FC = (props) => { const { criteriaData, selectedCriteria, onChangeSelectedCriteria, goBack } = props - + const appConfig = useContext(AppConfig) const { classes } = useStyles() const [defaultValues, setDefaultValues] = useState( (selectedCriteria as DocumentDataType) || defaultComposition @@ -272,16 +273,18 @@ const DocumentsForm: React.FC = (props) => { }} /> - - _onChangeValue('docStatuses', value)} - options={docStatuses} - value={defaultValues.docStatuses || undefined} - renderInput={(params) => } - /> - + {appConfig.core.fhir.extraSearchParams && ( + + _onChangeValue('docStatuses', value)} + options={docStatuses} + value={defaultValues.docStatuses || undefined} + renderInput={(params) => } + /> + + )} { + const appConfig = useContext(AppConfig) const criteria = selectedCriteria as EncounterDataType const [title, setTitle] = useState(criteria?.title || 'Critère de prise en charge') const [age, setAge] = useState(criteria?.age || [null, null]) @@ -247,42 +250,46 @@ const EncounterForm = ({ /> - - - setDuration(value)} - onError={(isError) => setError(isError ? Error.INCOHERENT_AGE_ERROR : Error.NO_ERROR)} - /> - + {appConfig.core.fhir.extraSearchParams && ( + <> + + + setDuration(value)} + onError={(isError) => setError(isError ? Error.INCOHERENT_AGE_ERROR : Error.NO_ERROR)} + /> + - - + + - - Début de prise en charge - - setEncounterStartDate(newStartDate)} - onError={(isError) => setError(isError ? Error.INCOHERENT_AGE_ERROR : Error.NO_ERROR)} - includeNullValues={includeEncounterStartDateNull} - onChangeIncludeNullValues={(includeNullValues) => setIncludeEncounterStartDateNull(includeNullValues)} - /> - - Fin de prise en charge - - setEncounterEndDate(newEndDate)} - onError={(isError) => setError(isError ? Error.INCOHERENT_AGE_ERROR : Error.NO_ERROR)} - includeNullValues={includeEncounterEndDateNull} - onChangeIncludeNullValues={(includeNullValues) => setIncludeEncounterEndDateNull(includeNullValues)} - /> - + + Début de prise en charge + + setEncounterStartDate(newStartDate)} + onError={(isError) => setError(isError ? Error.INCOHERENT_AGE_ERROR : Error.NO_ERROR)} + includeNullValues={includeEncounterStartDateNull} + onChangeIncludeNullValues={(includeNullValues) => setIncludeEncounterStartDateNull(includeNullValues)} + /> + + Fin de prise en charge + + setEncounterEndDate(newEndDate)} + onError={(isError) => setError(isError ? Error.INCOHERENT_AGE_ERROR : Error.NO_ERROR)} + includeNullValues={includeEncounterEndDateNull} + onChangeIncludeNullValues={(includeNullValues) => setIncludeEncounterEndDateNull(includeNullValues)} + /> + + + )} @@ -298,18 +305,20 @@ const EncounterForm = ({ renderInput={(params) => } /> - - option.label} - isOptionEqualToValue={(option, value) => option.id === value.id} - value={typeDeSejour} - onChange={(e, value) => setTypeDeSejour(value)} - renderInput={(params) => } - /> - + {appConfig.core.fhir.extraSearchParams && ( + + option.label} + isOptionEqualToValue={(option, value) => option.id === value.id} + value={typeDeSejour} + onChange={(e, value) => setTypeDeSejour(value)} + renderInput={(params) => } + /> + + )} setAdmissionMode(value)} renderInput={(params) => } /> - - option.label} - isOptionEqualToValue={(option, value) => option.id === value.id} - value={admission} - onChange={(e, value) => setAdmission(value)} - renderInput={(params) => } - /> + {appConfig.core.fhir.extraSearchParams && ( + option.label} + isOptionEqualToValue={(option, value) => option.id === value.id} + value={admission} + onChange={(e, value) => setAdmission(value)} + renderInput={(params) => } + /> + )} - - - option.label} - isOptionEqualToValue={(option, value) => option.id === value.id} - value={entryMode} - onChange={(e, value) => setEntryMode(value)} - renderInput={(params) => } - /> + {appConfig.core.fhir.extraSearchParams && ( + + + option.label} + isOptionEqualToValue={(option, value) => option.id === value.id} + value={entryMode} + onChange={(e, value) => setEntryMode(value)} + renderInput={(params) => } + /> - option.label} - isOptionEqualToValue={(option, value) => option.id === value.id} - value={exitMode} - onChange={(e, value) => setExitMode(value)} - renderInput={(params) => } - /> + option.label} + isOptionEqualToValue={(option, value) => option.id === value.id} + value={exitMode} + onChange={(e, value) => setExitMode(value)} + renderInput={(params) => } + /> - option.label} - isOptionEqualToValue={(option, value) => option.id === value.id} - value={reason} - onChange={(e, value) => setReason(value)} - renderInput={(params) => } - /> - - - - - option.label} - isOptionEqualToValue={(option, value) => option.id === value.id} - value={destination} - onChange={(e, value) => setDestination(value)} - renderInput={(params) => } - /> + option.label} + isOptionEqualToValue={(option, value) => option.id === value.id} + value={reason} + onChange={(e, value) => setReason(value)} + renderInput={(params) => } + /> + + + )} + {appConfig.core.fhir.extraSearchParams && ( + + + option.label} + isOptionEqualToValue={(option, value) => option.id === value.id} + value={destination} + onChange={(e, value) => setDestination(value)} + renderInput={(params) => } + /> - option.label} - isOptionEqualToValue={(option, value) => option.id === value.id} - value={provenance} - onChange={(e, value) => setProvenance(value)} - renderInput={(params) => } - /> - - + option.label} + isOptionEqualToValue={(option, value) => option.id === value.id} + value={provenance} + onChange={(e, value) => setProvenance(value)} + renderInput={(params) => } + /> + + + )} {!isEdition && (