Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make Data Requirements Human Readable #46

Merged
merged 7 commits into from
Jul 6, 2023
140 changes: 140 additions & 0 deletions app/src/components/DataRequirements.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import { Accordion, Center, createStyles, em, getBreakpointValue, List, Paper, rem } from '@mantine/core';

interface CodeInterface {
code: string | undefined;
system: string | undefined;
}

const useStyles = createStyles(theme => ({
card: {
borderRadius: 12,
border: `${rem(2)} solid ${theme.colors.gray[3]}`,
width: '1200px',
[`@media (max-width: ${em(getBreakpointValue(theme.breakpoints.lg) - 1)})`]: {
width: '100%'
}
}
}));

/**
* Component which displays all data requirements of a specified resource and displays them
* as resource cards that contain all required information
*/
function DataRequirements({ type, extension, dateFilter, codeFilter }: fhir4.DataRequirement) {
const { classes } = useStyles();
const codes: CodeInterface[] = [];

const codeFiltersWithVS = codeFilter?.filter(cf => cf?.valueSet);

codeFilter?.forEach(cf => {
cf?.code?.forEach(c => {
const code: CodeInterface = { code: c.code, system: c.system };
codes.push(code);
});
});

return (
<Center>
<Paper className={classes.card} shadow="sm" p="md">
<Accordion variant="contained" radius="lg" defaultValue="data-req-card">
<Accordion.Item value="data-req-card">
<Accordion.Control>
<Center>
<h2>{type} </h2>
</Center>
</Accordion.Control>
<Accordion.Panel>
<List>
<List.Item>
<b>Extension(s): </b>
<br />
</List.Item>
<List withPadding>
{extension?.map(e => (
<>
<List.Item key={e.url}>
<b>URL:</b>
{e.url}
</List.Item>
{e.valueString && (
<List.Item key={e.valueString}>
<b> ValueString: </b>
{e.valueString}
</List.Item>
)}
<br />
</>
))}
</List>
{dateFilter && dateFilter?.length > 0 && (
<>
<List.Item>
<b>DateFilter(s):</b>
</List.Item>
<List withPadding>
{dateFilter.map(date => (
<>
<List.Item key={date.valuePeriod?.start}>
<b> Start Date: </b>
{date.valuePeriod?.start} <br />
</List.Item>
<List.Item key={date.valuePeriod?.end}>
<b> End Date: </b> {date.valuePeriod?.end} <br />
</List.Item>
</>
))}
</List>
</>
)}
<br />
<List.Item>
<b>CodeFilter(s):</b>
</List.Item>
{codes.length > 0 && (
<List withPadding>
<List.Item>
<b>Code(s):</b>
</List.Item>
<List withPadding>
{codes.map(codeInterface => (
<>
{codeInterface.code && (
<List.Item key={codeInterface.code}>
<b> Code: </b> {codeInterface.code} <br />
</List.Item>
)}
{codeInterface.system && (
<List.Item key={codeInterface.system}>
<b> System: </b> {codeInterface.system} <br />
</List.Item>
)}
<br />
</>
))}
</List>
</List>
)}
{codeFiltersWithVS && codeFiltersWithVS.length > 0 && (
<List withPadding>
<List.Item>
<b>Value Set(s):</b>
</List.Item>
<List withPadding>
{codeFiltersWithVS.map((c, index) => (
<List.Item key={index}>
{c.valueSet} <br />
</List.Item>
))}
</List>
</List>
)}
</List>
</Accordion.Panel>
</Accordion.Item>
</Accordion>
</Paper>
</Center>
);
}

export default DataRequirements;
58 changes: 52 additions & 6 deletions app/src/pages/[resourceType]/[id].tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Prism } from '@mantine/prism';
import { Button, Divider, Group, Space, Stack, Tabs, Text } from '@mantine/core';
import { Button, Center, Divider, Group, SegmentedControl, ScrollArea, Space, Stack, Tabs, Text } from '@mantine/core';
import { notifications } from '@mantine/notifications';
import React, { useEffect, useMemo } from 'react';
import React, { useEffect, useMemo, useState } from 'react';
import { GetServerSideProps, InferGetServerSidePropsType } from 'next';
import { FhirArtifact } from '@/util/types/fhir';
import CQLRegex from '../../util/prismCQL';
Expand All @@ -11,6 +11,7 @@ import { AlertCircle, CircleCheck, AbacusOff } from 'tabler-icons-react';
import { useRouter } from 'next/router';
import { modifyResourceToDraft } from '@/util/modifyResourceFields';
import { trpc } from '@/util/trpc';
import DataReqs from '@/components/DataRequirements';

/**
* Component which displays the JSON/ELM/CQL/narrative/Data Requirements content of an individual resource using
Expand All @@ -19,6 +20,8 @@ import { trpc } from '@/util/trpc';
*/
export default function ResourceIDPage({ jsonData }: InferGetServerSidePropsType<typeof getServerSideProps>) {
const resourceType = jsonData.resourceType;
const [dataReqsView, setDataReqsView] = useState('raw');
const [height, setWindowHeight] = useState(0);

const decodedCql = useMemo(() => {
return decode('text/cql', jsonData);
Expand All @@ -39,6 +42,14 @@ export default function ResourceIDPage({ jsonData }: InferGetServerSidePropsType
/* eslint-enable @typescript-eslint/no-explicit-any */
}, []);

useEffect(() => {
const handleResize = () => {
setWindowHeight(window.innerHeight);
};
handleResize();
window.addEventListener('resize', handleResize);
}, []);

const {
data: dataRequirements,
refetch,
Expand Down Expand Up @@ -158,11 +169,46 @@ export default function ResourceIDPage({ jsonData }: InferGetServerSidePropsType
{parse(jsonData.text.div)}
</Tabs.Panel>
)}
{dataRequirements?.resourceType === 'Library' && (
{dataRequirements?.dataRequirement && (
<Tabs.Panel value="data-requirements">
<Prism language="json" colorScheme="light">
{JSON.stringify(dataRequirements, null, 2)}
</Prism>
{dataRequirements?.dataRequirement.length > 0 && (
<>
<Space h="md" />
<Center>
<SegmentedControl
fullWidth
value={dataReqsView}
onChange={setDataReqsView}
data={[
{ label: 'Raw Data Requirements', value: 'raw' },
{ label: 'Formatted Data Requirements', value: 'formatted' }
]}
/>
</Center>
</>
)}
{dataReqsView === 'raw' && (
<Prism language="json" colorScheme="light">
{JSON.stringify(dataRequirements, null, 2)}
</Prism>
)}
<ScrollArea.Autosize mah={height * 0.8} type="always">
<Space h="md" />
<Text c="dimmed">
Number of Requirements:<b> {dataRequirements?.dataRequirement.length} </b>
</Text>
<Space h="md" />
{dataReqsView === 'formatted' &&
dataRequirements?.dataRequirement.map((data: fhir4.DataRequirement, index) => (
<DataReqs
key={index}
type={data.type}
codeFilter={data.codeFilter}
dateFilter={data.dateFilter}
extension={data.extension}
/>
))}
</ScrollArea.Autosize>
</Tabs.Panel>
)}
</Tabs>
Expand Down