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
142 changes: 142 additions & 0 deletions app/src/components/DataRequirements.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
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 => {
return cf?.valueSet !== undefined;
});
BriannaVH marked this conversation as resolved.
Show resolved Hide resolved

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="customization">
<Accordion.Item value="customization">
BriannaVH marked this conversation as resolved.
Show resolved Hide resolved
<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 />
BriannaVH marked this conversation as resolved.
Show resolved Hide resolved
</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 />
BriannaVH marked this conversation as resolved.
Show resolved Hide resolved
</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;
59 changes: 53 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,15 @@ export default function ResourceIDPage({ jsonData }: InferGetServerSidePropsType
/* eslint-enable @typescript-eslint/no-explicit-any */
}, []);

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

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