Skip to content

Commit

Permalink
feat: added pmsi view to dashboard - Ref gestion-de-projet#2682
Browse files Browse the repository at this point in the history
  • Loading branch information
ManelleG authored and Mehdi-BOUYAHIA committed Sep 26, 2024
1 parent b81f46a commit 5e81fa8
Show file tree
Hide file tree
Showing 17 changed files with 996 additions and 169 deletions.
558 changes: 558 additions & 0 deletions src/components/Dashboard/PMSIList/index.tsx

Large diffs are not rendered by default.

85 changes: 51 additions & 34 deletions src/components/DataTable/DataTablePmsi.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,32 @@
import React, { useContext } from 'react'
import { Claim, Condition, Procedure } from 'fhir/r4'
import { AppConfig } from 'config'

import { CircularProgress, Grid, Typography, TableRow } from '@mui/material'
import { CircularProgress, Grid, Typography, TableRow, IconButton } from '@mui/material'
import { TableCellWrapper } from 'components/ui/TableCell/styles'

import DataTable from 'components/DataTable/DataTable'
import SearchIcon from 'assets/icones/search.svg?react'

import { Column, PMSIEntry } from 'types'

import useStyles from './styles'
import { Claim, Condition, Procedure } from 'fhir/r4'
import { CohortPMSI, Column, PMSIEntry } from 'types'
import { Order, OrderBy } from 'types/searchCriterias'
import { ResourceType } from 'types/requestCriterias'
import { PMSIResourceTypes, ResourceType } from 'types/requestCriterias'
import { getExtension } from 'utils/fhir'
import { AppConfig } from 'config'

import useStyles from './styles'
import { mapToDate, mapToLabelSingular } from 'mappers/pmsi'

type DataTablePmsiProps = {
loading: boolean
deidentified: boolean
selectedTab: ResourceType.CONDITION | ResourceType.PROCEDURE | ResourceType.CLAIM
selectedTab: PMSIResourceTypes
pmsiList: PMSIEntry<Procedure | Condition | Claim>[]
orderBy: OrderBy
setOrderBy?: (order: OrderBy) => void
page?: number
setPage?: (page: number) => void
total?: number
showIpp?: boolean
groupId?: string
}
const DataTablePmsi: React.FC<DataTablePmsiProps> = ({
loading,
Expand All @@ -34,12 +37,15 @@ const DataTablePmsi: React.FC<DataTablePmsiProps> = ({
setOrderBy,
page,
setPage,
total
total,
showIpp,
groupId
}) => {
const { classes } = useStyles()

const columns = [
{ label: `NDA${deidentified ? ' chiffré' : ''}`, align: 'left' },
...(showIpp ? [{ label: `IPP${deidentified ? ' chiffré' : ''}`, align: 'left' }] : []),
{ label: `NDA${deidentified ? ' chiffré' : ''}`, align: showIpp ? 'center' : 'left' },
{ label: 'Codage le', code: Order.DATE },
{ label: 'source' },
{ label: 'Code', code: Order.CODE },
Expand All @@ -53,28 +59,30 @@ const DataTablePmsi: React.FC<DataTablePmsiProps> = ({
{!loading && pmsiList?.length > 0 && (
<>
{pmsiList.map((pmsi) => {
return <DataTablePmsiLine key={pmsi.id} pmsi={pmsi} selectedTab={selectedTab} />
return (
<DataTablePmsiLine
key={pmsi.id}
pmsi={pmsi}
selectedTab={selectedTab}
showIpp={showIpp}
groupId={groupId}
/>
)
})}
</>
)}
{!loading && pmsiList?.length < 1 && (
<TableRow className={classes.emptyTableRow}>
<TableCellWrapper colSpan={selectedTab === ResourceType.CONDITION ? 7 : 6} align="left">
<TableCellWrapper colSpan={columns.length} align="left">
<Grid container justifyContent="center">
<Typography variant="button">{`Aucun ${
selectedTab !== ResourceType.CONDITION
? selectedTab !== ResourceType.PROCEDURE
? 'GHM'
: 'acte'
: 'diagnostic'
} à afficher`}</Typography>
<Typography variant="button">{`Aucun ${mapToLabelSingular(selectedTab)} à afficher`}</Typography>
</Grid>
</TableCellWrapper>
</TableRow>
)}
{loading && (
<TableRow className={classes.emptyTableRow}>
<TableCellWrapper colSpan={selectedTab === ResourceType.CONDITION ? 7 : 6} align="left">
<TableCellWrapper colSpan={columns.length} align="left">
<Grid container justifyContent="center">
<CircularProgress />
</Grid>
Expand All @@ -86,21 +94,17 @@ const DataTablePmsi: React.FC<DataTablePmsiProps> = ({
}

const DataTablePmsiLine: React.FC<{
pmsi: PMSIEntry<Procedure | Condition | Claim>
selectedTab: ResourceType.CONDITION | ResourceType.PROCEDURE | ResourceType.CLAIM
}> = ({ pmsi, selectedTab }) => {
pmsi: CohortPMSI
selectedTab: PMSIResourceTypes
showIpp?: boolean
groupId?: string
}> = ({ pmsi, selectedTab, showIpp, groupId }) => {
const { classes } = useStyles()
const appConfig = useContext(AppConfig)

const ipp = pmsi.IPP
const nda = pmsi.NDA
const date =
pmsi.resourceType === ResourceType.CONDITION && pmsi.recordedDate
? new Date(pmsi.recordedDate).toLocaleDateString('fr-FR') ?? 'Date inconnue'
: pmsi.resourceType === ResourceType.CLAIM && pmsi.created
? new Date(pmsi.created).toLocaleDateString('fr-FR') ?? 'Date inconnue'
: pmsi.resourceType === ResourceType.PROCEDURE && pmsi.performedDateTime
? new Date(pmsi.performedDateTime).toLocaleDateString('fr-FR') ?? 'Date inconnue'
: 'Date inconnue'
const date = mapToDate(selectedTab, pmsi)

const filterCode = pmsi.resourceType === ResourceType.CLAIM ? pmsi.diagnosis?.[0]?.packageCode : pmsi.code

Expand All @@ -112,12 +116,25 @@ const DataTablePmsiLine: React.FC<{
getExtension(
pmsi,
appConfig.features.condition.extensions.orbisStatus
)?.valueCodeableConcept?.coding?.[0].code?.toUpperCase() || '-'
)?.valueCodeableConcept?.coding?.[0].code?.toUpperCase() ?? '-'
const serviceProvider = pmsi.serviceProvider ?? 'Non renseigné'

const groupIdSearch = groupId ? `?groupId=${groupId}` : ''

return (
<TableRow className={classes.tableBodyRows} key={pmsi.id}>
<TableCellWrapper align="left">{nda ?? 'Inconnu'}</TableCellWrapper>
{showIpp && (
<TableCellWrapper style={{ minWidth: 150 }}>
{ipp}
<IconButton
onClick={() => window.open(`/patients/${pmsi.idPatient}${groupIdSearch}`, '_blank')}
className={classes.searchIcon}
>
<SearchIcon height="15px" fill="#ED6D91" className={classes.iconMargin} />
</IconButton>
</TableCellWrapper>
)}
<TableCellWrapper align={showIpp ? 'center' : 'left'}>{nda ?? 'Inconnu'}</TableCellWrapper>
<TableCellWrapper>{date}</TableCellWrapper>
<TableCellWrapper>
<Typography className={classes.libelle}>{source}</Typography>
Expand Down
39 changes: 14 additions & 25 deletions src/components/Patient/PatientPMSI/PatientPMSI.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import DataTablePmsi from 'components/DataTable/DataTablePmsi'

import { useAppSelector, useAppDispatch } from 'state'
import { fetchPmsi } from 'state/patient'
import { LoadingStatus, TabType } from 'types'
import { CohortPMSI, LoadingStatus, PmsiTab, PmsiTabs } from 'types'
import useStyles from './styles'
import { cancelPendingRequest } from 'utils/abortController'
import { CanceledError } from 'axios'
Expand All @@ -34,33 +34,33 @@ import { ResourceType } from 'types/requestCriterias'
import { useSavedFilters } from 'hooks/filters/useSavedFilters'
import { Save, SavedSearch } from '@mui/icons-material'
import TextInput from 'components/Filters/TextInput'
import { Claim, Condition, Procedure } from 'fhir/r4'
import { mapToAttribute, mapToLabel } from 'mappers/pmsi'
import { mapToAttribute, mapToLabel, mapToSourceType } from 'mappers/pmsi'
import List from 'components/ui/List'
import { fetchClaimCodes, fetchConditionCodes, fetchProcedureCodes } from 'services/aphp/servicePmsi'
import EncounterStatusFilter from 'components/Filters/EncounterStatusFilter'
import { AlertWrapper } from 'components/ui/Alert'
import { SourceType } from 'types/scope'
import { Hierarchy } from 'types/hierarchy'
import { useSearchParams } from 'react-router-dom'
import { checkIfPageAvailable, handlePageError } from 'utils/paginationUtils'

export type PatientPMSIProps = {
type PatientPMSIProps = {
groupId?: string
}

type PmsiTab = TabType<ResourceType.CLAIM | ResourceType.CONDITION | ResourceType.PROCEDURE, PMSILabel>

type PmsiTabs = PmsiTab[]

type PmsiSearchResults = {
deidentified: boolean
list: Condition[] | Procedure[] | Claim[]
list: CohortPMSI[]
nb: number
total: number
label: PMSILabel
}

export const PMSITabs: PmsiTabs = [
{ label: PMSILabel.DIAGNOSTIC, id: ResourceType.CONDITION },
{ label: PMSILabel.CCAM, id: ResourceType.PROCEDURE },
{ label: PMSILabel.GHM, id: ResourceType.CLAIM }
]

const PatientPMSI = ({ groupId }: PatientPMSIProps) => {
const { classes } = useStyles()
const [searchParams, setSearchParams] = useSearchParams()
Expand All @@ -80,19 +80,10 @@ const PatientPMSI = ({ groupId }: PatientPMSIProps) => {
label: PMSILabel.DIAGNOSTIC
})
const [oldTabs, setOldTabs] = useState<PmsiTab | null>(null)
const PMSITabs: PmsiTabs = [
{ label: PMSILabel.DIAGNOSTIC, id: ResourceType.CONDITION },
{ label: PMSILabel.CCAM, id: ResourceType.PROCEDURE },
{ label: PMSILabel.GHM, id: ResourceType.CLAIM }
]
const sourceType =
selectedTab.id === ResourceType.CONDITION
? SourceType.CIM10
: selectedTab.id === ResourceType.PROCEDURE
? SourceType.CCAM
: SourceType.GHM
const [page, setPage] = useState(getPageParam ? parseInt(getPageParam, 10) : 1)

const sourceType = mapToSourceType(selectedTab.id)

const [page, setPage] = useState(1)
const {
allSavedFilters,
savedFiltersErrors,
Expand Down Expand Up @@ -305,9 +296,7 @@ const PatientPMSI = ({ groupId }: PatientPMSIProps) => {
<Tabs
values={PMSITabs}
active={selectedTab}
onchange={(
value: TabType<ResourceType.CONDITION | ResourceType.PROCEDURE | ResourceType.CLAIM, PMSILabel>
) => {
onchange={(value: PmsiTab) => {
setOldTabs(selectedTab)
setSelectedTab(value)
}}
Expand Down
4 changes: 3 additions & 1 deletion src/config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,9 @@ let config: AppConfig = {
conditionHierarchy: { url: '' },
conditionStatus: { url: '' }
},
extensions: {}
extensions: {
orbisStatus: ''
}
},
procedure: {
enabled: true,
Expand Down
9 changes: 6 additions & 3 deletions src/mappers/filters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ export enum ConditionParamsKeys {
DATE = 'recorded-date',
EXECUTIVE_UNITS = 'encounter.encounter-care-site',
SOURCE = '_source',
ENCOUNTER_STATUS = 'encounter.status'
ENCOUNTER_STATUS = 'encounter.status',
IPP = 'subject.identifier'
}

export enum ProcedureParamsKeys {
Expand All @@ -92,15 +93,17 @@ export enum ProcedureParamsKeys {
SOURCE = '_source',
DATE = 'date',
EXECUTIVE_UNITS = 'encounter.encounter-care-site',
ENCOUNTER_STATUS = 'encounter.status'
ENCOUNTER_STATUS = 'encounter.status',
IPP = 'subject.identifier'
}

export enum ClaimParamsKeys {
NDA = 'encounter.identifier',
CODE = 'diagnosis',
DATE = 'created',
EXECUTIVE_UNITS = 'encounter.encounter-care-site',
ENCOUNTER_STATUS = 'encounter.status'
ENCOUNTER_STATUS = 'encounter.status',
IPP = 'patient.identifier'
}

export enum PrescriptionParamsKeys {
Expand Down
81 changes: 64 additions & 17 deletions src/mappers/pmsi.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,16 @@
import { getConfig } from 'config'
import { Claim, Condition, Procedure } from 'fhir/r4'
import { Medication, Pmsi } from 'state/patient'
import { CriteriaName } from 'types'
import { CohortPMSI } from 'types'
import { PMSILabel } from 'types/patient'
import { ResourceType } from 'types/requestCriterias'

export const mapToCriteriaName = (
criteria: ResourceType.CLAIM | ResourceType.CONDITION | ResourceType.PROCEDURE
): CriteriaName => {
const mapping: { [key: string]: CriteriaName } = {
diagnostic: CriteriaName.Cim10,
ghm: CriteriaName.Ghm,
ccam: CriteriaName.Ccam
}
const index = criteria === ResourceType.CLAIM ? 'ghm' : ResourceType.CONDITION ? 'diagnostic' : 'ccam'
if (index in mapping) return mapping[index]
throw new Error(`Unknown criteria ${criteria}`)
}
import { PMSIResourceTypes, ResourceType } from 'types/requestCriterias'
import { SourceType } from 'types/scope'
import { Order } from 'types/searchCriterias'

export function mapToAttribute(
type: ResourceType.MEDICATION_ADMINISTRATION | ResourceType.MEDICATION_REQUEST
): keyof Medication
export function mapToAttribute(type: ResourceType.CONDITION | ResourceType.CLAIM | ResourceType.PROCEDURE): keyof Pmsi
export function mapToAttribute(type: PMSIResourceTypes): keyof Pmsi
export function mapToAttribute(type: ResourceType) {
switch (type) {
case ResourceType.MEDICATION_ADMINISTRATION:
Expand All @@ -38,7 +29,7 @@ export function mapToAttribute(type: ResourceType) {
export function mapToLabel(
type: ResourceType.MEDICATION_ADMINISTRATION | ResourceType.MEDICATION_REQUEST
): 'administration(s)' | 'prescription(s)'
export function mapToLabel(type: ResourceType.CONDITION | ResourceType.CLAIM | ResourceType.PROCEDURE): PMSILabel
export function mapToLabel(type: PMSIResourceTypes): PMSILabel
export function mapToLabel(type: ResourceType) {
switch (type) {
case ResourceType.CONDITION:
Expand All @@ -53,3 +44,59 @@ export function mapToLabel(type: ResourceType) {
return 'prescription(s)'
}
}

export const mapToLabelSingular = (tabId: PMSIResourceTypes) => {
const mapToLabel = {
[ResourceType.CONDITION]: 'GHM',
[ResourceType.PROCEDURE]: 'acte',
[ResourceType.CLAIM]: 'diagnostic'
}

return mapToLabel[tabId]
}

export const mapToUrlCode = (tabId: PMSIResourceTypes) => {
const mapToUrlCode = {
[ResourceType.CONDITION]: getConfig().features.condition.valueSets.conditionHierarchy.url,
[ResourceType.PROCEDURE]: getConfig().features.procedure.valueSets.procedureHierarchy.url,
[ResourceType.CLAIM]: getConfig().features.claim.valueSets.claimHierarchy.url
}

return mapToUrlCode[tabId]
}

export const mapToSourceType = (tabId: PMSIResourceTypes) => {
const tabIdMapper = {
[ResourceType.CONDITION]: SourceType.CIM10,
[ResourceType.PROCEDURE]: SourceType.CCAM,
[ResourceType.CLAIM]: SourceType.GHM
}

return tabIdMapper[tabId]
}

export const mapToDate = (tabId: PMSIResourceTypes, pmsiItem: CohortPMSI) => {
const dateMapper = {
[ResourceType.CONDITION]: (pmsiItem as Condition).recordedDate,
[ResourceType.PROCEDURE]: (pmsiItem as Procedure).performedDateTime,
[ResourceType.CLAIM]: (pmsiItem as Claim).created
}

return dateMapper[tabId] ? new Date(dateMapper[tabId] as string).toLocaleDateString('fr-FR') : 'Date inconnue'
}

export const mapToOrderByCode = (code: Order, resourceType: PMSIResourceTypes) => {
const dateName = {
[ResourceType.CONDITION]: Order.RECORDED_DATE,
[ResourceType.PROCEDURE]: Order.DATE,
[ResourceType.CLAIM]: Order.CREATED
}

const codeName = {
[ResourceType.CONDITION]: Order.CODE,
[ResourceType.PROCEDURE]: Order.CODE,
[ResourceType.CLAIM]: Order.DIAGNOSIS
}

return code === Order.CODE ? codeName[resourceType] : dateName[resourceType]
}
1 change: 1 addition & 0 deletions src/reducers/searchCriteriasReducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export const initPmsiSearchCriterias: SearchCriterias<PMSIFilters> = {
filters: {
code: [],
nda: '',
ipp: '',
source: '',
diagnosticTypes: [],
startDate: null,
Expand Down
Loading

0 comments on commit 5e81fa8

Please sign in to comment.