Skip to content

Commit

Permalink
Merge branch '354-generic-resource-list' of github.com:beda-software/…
Browse files Browse the repository at this point in the history
…fhir-emr into update-app-layout
  • Loading branch information
vesnushka committed Dec 18, 2024
2 parents 531711a + 083ad27 commit 7589ce7
Show file tree
Hide file tree
Showing 11 changed files with 160 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,13 @@ export function useAnswerReference<R extends Resource = any, IR extends Resource
return overrideGetDisplay;
}

return (resource: R) => evaluate(resource, choiceColumn![0]!.path!, context)[0];
return (resource: R, includedResources: ResourcesMap<R|IR>) => evaluate(
resource, choiceColumn![0]!.path!,
{
...context,
...includedResources,
resource,
})[0];
}, [choiceColumn, context, overrideGetDisplay]);

// TODO: add support for fhirpath and application/x-fhir-query
Expand Down
8 changes: 7 additions & 1 deletion src/components/SearchBar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,25 @@ import { SearchBarColumn } from './SearchBarColumn';
import { S } from './styles';
import { SearchBarData } from './types';
import { SearchBarMobile } from './SearchBarMobile';
import { isSearchBarFilter } from './utils';
import { useMemo } from 'react';

interface SearchBarProps extends SearchBarData {
showInDrawerOnMobile?: boolean;
}

export function SearchBar(props: SearchBarProps) {
const { columnsFilterValues, onChangeColumnFilter, onResetFilters, showInDrawerOnMobile = true } = props;
const searchBarFilterValues = useMemo(
() => columnsFilterValues.filter((filter) => isSearchBarFilter(filter)),
[JSON.stringify(columnsFilterValues)],
);

return (
<>
<S.SearchBar $showInDrawerOnMobile={showInDrawerOnMobile}>
<S.LeftColumn>
{columnsFilterValues.map((columnFilterValue) => (
{searchBarFilterValues.map((columnFilterValue) => (
<SearchBarColumn
key={`search-bar-column-${columnFilterValue.column.id}`}
columnFilterValue={columnFilterValue}
Expand Down
3 changes: 3 additions & 0 deletions src/components/SearchBar/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,11 @@ export interface SearchBarProps {
}

type SearchBarColumnBase = {
// if placement is table then id should be matched with table column key
id: string;
searchParam?: string;
// placement = 'search-bar' by default
placement?: Array<'search-bar' | 'table'>;
};

export type SearchBarStringColumn = SearchBarColumnBase & {
Expand Down
12 changes: 12 additions & 0 deletions src/components/SearchBar/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,15 @@ export function getSearchBarColumnFilterValue(filterValue: ColumnFilterValue) {

throw new Error('Unsupported column type');
}

export function isSearchBarFilter(filter: ColumnFilterValue) {
const { placement = ['search-bar'] } = filter.column;

return placement.includes('search-bar');
}

export function isTableFilter(filter: ColumnFilterValue) {
const { placement = ['search-bar'] } = filter.column;

return placement.includes('table');
}
25 changes: 25 additions & 0 deletions src/components/Table/TableFilter/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { ColumnFilterValue } from 'src/components/SearchBar/types';
import { SearchBarColumn } from 'src/components/SearchBar/SearchBarColumn';
import { S } from './styles';
import { FilterDropdownProps } from 'antd/lib/table/interface';

interface Props extends FilterDropdownProps {
filter: ColumnFilterValue;
onChange: (value: ColumnFilterValue['value'], key: string) => void;
}

export function TableFilter(props: Props) {
const { filter, onChange, close } = props;

return (
<S.Filter>
<SearchBarColumn
columnFilterValue={filter}
onChange={(value, key) => {
onChange(value, key);
close();
}}
/>
</S.Filter>
);
}
38 changes: 38 additions & 0 deletions src/components/Table/TableFilter/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import styled from 'styled-components';

export const S = {
Filter: styled.div`
display: flex;
flex-direction: column;
padding: 8px;
border-radius: 8px;
box-shadow:
0px 6px 16px 0px ${({ theme }) => theme.neutral.dividers},
0px 3px 6px -4px ${({ theme }) => theme.neutral.border},
0px 9px 28px 8px ${({ theme }) => theme.neutral.background};
min-width: 240px;
.ant-input-search,
.ant-picker,
.react-select__control {
width: 100%;
}
.react-select__menu {
position: static;
}
.react-select__menu {
box-shadow: none;
padding: 8px 4px 4px;
margin: 8px -8px -8px;
border-top: 1px solid ${({ theme }) => theme.neutral.dividers};
border-radius: 0;
width: auto;
}
.react-select__menu-list {
padding: 0;
}
`,
};
35 changes: 35 additions & 0 deletions src/components/Table/utils.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Bundle, Resource } from 'fhir/r4b';
import { ColumnsType, ColumnType } from 'antd/lib/table';
import { ColumnFilterValue } from 'src/components/SearchBar/types';
import { TableFilter } from './TableFilter';
import { FilterDropdownProps } from 'antd/lib/table/interface';
import _ from 'lodash';

export type RecordType<R extends Resource> = { resource: R; bundle: Bundle };

interface Props<R extends Resource> {
tableColumns: ColumnsType<RecordType<R>>;
filters: ColumnFilterValue[];
onChange: (value: ColumnFilterValue['value'], key: string) => void;
}

export function populateTableColumnsWithFiltersAndSorts<R extends Resource>(
props: Props<R>,
): ColumnsType<RecordType<R>> {
const { tableColumns, filters, onChange } = props;
const result = tableColumns.map((column) => {
const filter = filters.find((f) => f.column.id === column.key);

const updatedColumn: ColumnType<RecordType<R>> = {
...column,
filtered: !_.isUndefined(filter?.value) && !_.isNull(filter?.value) && filter?.value === '',
filterDropdown: filter
? (props: FilterDropdownProps) => <TableFilter {...props} filter={filter} onChange={onChange} />
: undefined,
};

return updatedColumn;
});

return result;
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
} from 'fhir/r4b';
import _ from 'lodash';
import moment from 'moment';
import { extractExtension, fromFHIRReference } from 'sdc-qrf';
import { extractExtension } from 'sdc-qrf';

import { WithId, extractBundleResources, formatFHIRDate, parseFHIRDateTime } from '@beda.software/fhir-react';

Expand Down Expand Up @@ -266,19 +266,7 @@ export function prepareAppointments(bundle: Bundle<WithId<Appointment | Encounte
}

export function prepareAppointmentDetails(appointment: Appointment) {
const [name, specialty] =
appointment.participant
.find((p) => fromFHIRReference(p.actor)?.resourceType === 'PractitionerRole')
?.actor?.display?.split(' - ') || [];
const appointmentDetails = [
{
title: t`Practitioner`,
value: name || '-',
},
{
title: t`Service`,
value: specialty || '-',
},
{
title: t`Date`,
value: appointment.start ? formatHumanDate(appointment.start) : '-',
Expand All @@ -292,7 +280,15 @@ export function prepareAppointmentDetails(appointment: Appointment) {
},
];

return appointmentDetails;
//TODO agree on terminology for Appointment.particioant.type and use it
const participants = appointment.participant
.filter((p) => p.type?.[0]?.coding?.[0]?.code !== 'patient')
.map((participant) => ({
title: participant.type?.[0]?.text,
value: participant.actor?.display,
}));

return [...appointmentDetails, ...participants];
}

export function prepareServiceRequest(
Expand Down
2 changes: 2 additions & 0 deletions src/containers/PatientResourceListExample/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export function PatientResourceListExample() {
dataIndex: 'name',
key: 'name',
render: (_text, { resource }) => renderHumanName(resource.name?.[0]),
width: 250,
},
{
title: <Trans>Birth date</Trans>,
Expand All @@ -65,6 +66,7 @@ export function PatientResourceListExample() {
searchParam: 'name',
type: SearchBarColumnType.STRING,
placeholder: t`Find patient`,
placement: ['search-bar'],
},
]}
getRecordActions={(record) => [
Expand Down
25 changes: 19 additions & 6 deletions src/uberComponents/ResourceListPage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ import {
export { navigationAction, customAction, questionnaireAction } from './actions';
import { useResourceListPage } from './hooks';
import { SearchBarColumn } from '../../components/SearchBar/types';
import { populateTableColumnsWithFiltersAndSorts } from 'src/components/Table/utils';
import { isTableFilter } from 'src/components/SearchBar/utils';
import { useMemo } from 'react';

type RecordType<R extends Resource> = { resource: R; bundle: Bundle };

Expand Down Expand Up @@ -107,12 +110,15 @@ export function ResourceListPage<R extends Resource>({
defaultLaunchContext,
}: ResourceListPageProps<R>) {
const allFilters = getFilters?.() ?? [];
// TODO: filter out column filters
const searchBarColumns = allFilters;

const { columnsFilterValues, onChangeColumnFilter, onResetFilters } = useSearchBar({
columns: searchBarColumns ?? [],
});
const { columnsFilterValues, onChangeColumnFilter, onResetFilters } =
useSearchBar({
columns: allFilters ?? [],
});
const tableFilterValues = useMemo(
() => columnsFilterValues.filter((filter) => isTableFilter(filter)),
[JSON.stringify(columnsFilterValues)],
);

const {
recordResponse,
Expand All @@ -124,6 +130,13 @@ export function ResourceListPage<R extends Resource>({
selectedResourcesBundle,
} = useResourceListPage(resourceType, extractPrimaryResources, columnsFilterValues, searchParams ?? {});

// TODO: move to hooks
const initialTableColumns = getTableColumns({ reload });
const tableColumns = populateTableColumnsWithFiltersAndSorts({
tableColumns: initialTableColumns,
filters: tableFilterValues,
onChange: onChangeColumnFilter,
});
const headerActions = getHeaderActions?.() ?? [];
const batchActions = getBatchActions?.() ?? [];

Expand Down Expand Up @@ -204,7 +217,7 @@ export function ResourceListPage<R extends Resource>({
rowKey={(p) => p.resource.id!}
dataSource={isSuccess(recordResponse) ? recordResponse.data : []}
columns={[
...getTableColumns({ reload }),
...tableColumns,
...(getRecordActions
? [
getRecordActionsColumn({
Expand Down
4 changes: 2 additions & 2 deletions src/utils/fhirpath.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ type NonEmptyArray<T> = [T, ...T[]];
export function compileAsArray<SRC, DST = unknown, REQ = false>(expression: string) {
const path = fhirpath.compile(expression);

return (s: SRC) => path(s) as REQ extends true ? NonEmptyArray<DST> : Array<DST>;
return (s: SRC, context?: Context | undefined) => path(s, context) as REQ extends true ? NonEmptyArray<DST> : Array<DST>;
}

export function compileAsFirst<SRC, DST = unknown, REQ = false>(expression: string) {
const path = fhirpath.compile(expression);

return (s: SRC) => path(s)[0] as REQ extends true ? DST : DST | undefined;
return (s: SRC, context?: Context | undefined) => path(s, context)[0] as REQ extends true ? DST : DST | undefined;
}

0 comments on commit 7589ce7

Please sign in to comment.