Skip to content

Commit

Permalink
Merge branch 'master' into detail-page
Browse files Browse the repository at this point in the history
  • Loading branch information
ir4y committed Feb 26, 2025
2 parents 1f8ed36 + 09753ee commit e7415ce
Show file tree
Hide file tree
Showing 38 changed files with 574 additions and 240 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"@ant-design/colors": "^7.0.0",
"@ant-design/icons": "^5.1.4",
"@beda.software/emr-config": "*",
"@beda.software/fhir-react": "^1.8.6",
"@beda.software/fhir-react": "^1.8.7",
"@beda.software/remote-data": "^1.1.3",
"@fullcalendar/core": "^6.1.15",
"@fullcalendar/daygrid": "^6.1.8",
Expand Down
7 changes: 7 additions & 0 deletions src/components/BaseLayout/Footer/Footer.styles.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import styled from 'styled-components';

import { Text } from 'src/components/Typography';

export const S = {
Footer: styled.footer`
position: absolute;
Expand Down Expand Up @@ -34,4 +36,9 @@ export const S = {
text-decoration: underline;
}
`,
Text: styled(Text)`
color: var(--footer-text);
font-size: 14px;
line-height: 22px;
`,
};
6 changes: 5 additions & 1 deletion src/components/BaseLayout/Footer/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { t } from '@lingui/macro';

import { S } from './Footer.styles';

interface Props {
Expand All @@ -10,7 +12,9 @@ export function AppFooter(props: Props) {
return (
<S.Footer className={`_${type}`}>
<S.Content>
Made with &#10084;&#65039; by{' '}
<S.Text>
{t`Made with`} &#10084;&#65039; {t`by`}{' '}
</S.Text>
<S.Link href="https://beda.software/emr" target="_blank" rel="noreferrer">
Beda Software
</S.Link>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useContext } from 'react';
import { FormProvider, useForm } from 'react-hook-form';
import {
calcInitialContext,
Expand All @@ -8,6 +9,13 @@ import {
QuestionnaireResponseFormProvider,
} from 'sdc-qrf';

import {
ItemControlGroupItemReadonlyWidgetsContext,
ItemControlQuestionItemReadonlyWidgetsContext,
} from 'src/components/BaseQuestionnaireResponseForm/context';
import { MarkdownRenderControl } from 'src/components/BaseQuestionnaireResponseForm/readonly-widgets/MarkdownRender';

import { AudioAttachment } from './readonly-widgets/AudioAttachment';
import { QuestionBoolean } from './readonly-widgets/boolean';
import { QuestionChoice } from './readonly-widgets/choice';
import { QuestionDateTime } from './readonly-widgets/date';
Expand All @@ -19,7 +27,6 @@ import { AnxietyScore, DepressionScore } from './readonly-widgets/score';
import { QuestionText, TextWithInput } from './readonly-widgets/string';
import { TimeRangePickerControl } from './readonly-widgets/TimeRangePickerControl';
import { UploadFile } from './readonly-widgets/UploadFile';
import { AudioAttachment } from './readonly-widgets/AudioAttachment';

interface Props extends Partial<QRFContextData> {
formData: QuestionnaireResponseFormData;
Expand All @@ -40,6 +47,9 @@ export function ReadonlyQuestionnaireResponseForm(props: Props) {

const formValues = watch();

const ItemControlQuestionItemReadonlyWidgetsFromContext = useContext(ItemControlQuestionItemReadonlyWidgetsContext);
const ItemControlGroupItemReadonlyWidgetsFromContext = useContext(ItemControlGroupItemReadonlyWidgetsContext);

return (
<FormProvider {...methods}>
<form>
Expand All @@ -54,6 +64,7 @@ export function ReadonlyQuestionnaireResponseForm(props: Props) {
row: Row,
'time-range-picker': TimeRangePickerControl,
...itemControlGroupItemComponents,
...ItemControlGroupItemReadonlyWidgetsFromContext,
}}
questionItemComponents={{
text: QuestionText,
Expand All @@ -76,7 +87,9 @@ export function ReadonlyQuestionnaireResponseForm(props: Props) {
'depression-score': DepressionScore,
'input-inside-text': TextWithInput,
'audio-recorder-uploader': AudioAttachment,
'markdown-editor': MarkdownRenderControl,
...itemControlQuestionItemComponents,
...ItemControlQuestionItemReadonlyWidgetsFromContext,
}}
>
<>
Expand Down
3 changes: 3 additions & 0 deletions src/components/BaseQuestionnaireResponseForm/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import { BaseQuestionnaireResponseFormProps } from '.';
export const ItemControlQuestionItemWidgetsContext = createContext<ItemControlQuestionItemComponentMapping>({});
export const ItemControlGroupItemWidgetsContext = createContext<ItemControlGroupItemComponentMapping>({});

export const ItemControlQuestionItemReadonlyWidgetsContext = createContext<ItemControlQuestionItemComponentMapping>({});
export const ItemControlGroupItemReadonlyWidgetsContext = createContext<ItemControlGroupItemComponentMapping>({});

interface BaseQuestionnaireResponseFormPropsContextProps extends BaseQuestionnaireResponseFormProps {
submitting: boolean;
debouncedSaveDraft?: DebouncedFunc<(currentFormValues: FormItems) => Promise<void>>;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import classNames from 'classnames';
import Markdown from 'react-markdown';
import { QuestionItemProps } from 'sdc-qrf';

import { useFieldController } from 'src/components/BaseQuestionnaireResponseForm/hooks';

import s from '../ReadonlyWidgets.module.scss';
import { S } from '../ReadonlyWidgets.styles';

export function MarkdownRenderControl({ parentPath, questionItem }: QuestionItemProps) {
const { linkId, text } = questionItem;
const fieldName = [...parentPath, linkId, 0, 'value', 'string'];
const { value } = useFieldController(fieldName, questionItem);

return (
<S.Question className={classNames(s.question, s.column, 'form__question')}>
<span className={s.questionText}>{text}</span>
<Markdown>{value || '-'}</Markdown>
</S.Question>
);
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { t } from '@lingui/macro';
import { Meta, StoryObj } from '@storybook/react';
import { ItemContext } from 'sdc-qrf/lib/types';

Expand All @@ -19,7 +20,7 @@ export const DateTime: Story = {
<QuestionDateTime
parentPath={[]}
questionItem={{
text: 'Select date',
text: t`Select date`,
type: 'dateTime',
linkId: 'date',
}}
Expand All @@ -33,7 +34,7 @@ export const Date: Story = {
<QuestionDateTime
parentPath={[]}
questionItem={{
text: 'Birth date',
text: t`Birth date`,
type: 'date',
linkId: 'birth-date',
}}
Expand All @@ -47,7 +48,7 @@ export const Time: Story = {
<QuestionDateTime
parentPath={[]}
questionItem={{
text: 'Select time',
text: t`Select time`,
type: 'time',
linkId: 'time',
}}
Expand All @@ -61,7 +62,7 @@ export const FormatedTime: Story = {
<QuestionDateTime
parentPath={[]}
questionItem={{
text: 'Time with no seconsd',
text: t`Time with no seconds`,
type: 'time',
linkId: 'time',
regex: 'HH:mm',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Form, InputNumber, Select } from 'antd';
import { Coding } from 'fhir/r4b';
import _ from 'lodash';
import { useState } from 'react';
import { QuestionItemProps } from 'sdc-qrf';

Expand Down Expand Up @@ -27,6 +28,7 @@ export function QuestionInteger({ parentPath, questionItem }: QuestionItemProps)
value={value}
required={required}
placeholder={placeholder}
parser={(displayValue) => _.toInteger(displayValue)}
/>
</Form.Item>
);
Expand Down
66 changes: 66 additions & 0 deletions src/components/LinkToEdit/__tests__/LinkToEdit.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { Provenance } from 'fhir/r4b';
import { describe } from 'vitest';

import { getLinkToEditUrl } from 'src/components/LinkToEdit/utils';

describe('getSourceFromProvenance', () => {
test('getSourceFromProvenance for empty Provenance', () => {
const provenance = {} as Provenance;
const pathname = '/patients/patient1/resources';

expect(getLinkToEditUrl({ provenance, pathname })).toBeUndefined();
});

test('getSourceFromProvenance for undefined Provenance', () => {
expect(getLinkToEditUrl({})).toBeUndefined();
});

test('getSourceFromProvenance for Provenance with source reference', () => {
const provenance: Provenance = {
resourceType: 'Provenance',
entity: [
{
role: 'source',
what: {
reference: 'QuestionnaireResponse/getme',
},
},
],
agent: [
{
who: {},
},
],
target: [{}],
recorded: '',
};
const pathname = '/patients/patient1/resources';

expect(getLinkToEditUrl({ provenance, pathname })).toEqual('/patients/patient1/documents/getme');
});

test('getSourceFromProvenance for Provenance with source reference and custom to', () => {
const provenance: Provenance = {
resourceType: 'Provenance',
entity: [
{
role: 'source',
what: {
reference: 'QuestionnaireResponse/getme',
},
},
],
agent: [
{
who: {},
},
],
target: [{}],
recorded: '',
};
const pathname = '/patients/patient1/resources';
const to = '/custom';

expect(getLinkToEditUrl({ provenance, pathname, to })).toEqual('/custom/getme');
});
});
11 changes: 11 additions & 0 deletions src/components/LinkToEdit/context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { createContext } from 'react';

import { getLinkToEditUrl, GetLinkToEditUrlProps } from 'src/components/LinkToEdit/utils';

export type LinkToEditContextType = {
getLinkToEditUrl: (props: GetLinkToEditUrlProps) => string | undefined;
};

export const LinkToEditContext = createContext<LinkToEditContextType>({
getLinkToEditUrl,
});
38 changes: 38 additions & 0 deletions src/components/LinkToEdit/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Resource, Provenance } from 'fhir/r4b';
import { useContext } from 'react';
import { Link, useLocation } from 'react-router-dom';
import { fromFHIRReference } from 'sdc-qrf';

import { LinkToEditContext } from 'src/components/LinkToEdit/context';

interface LinkToEditProps {
name?: string;
resource: Resource;
provenanceList: Provenance[];
to?: string;
}

export function LinkToEdit(props: LinkToEditProps) {
const { name, resource, provenanceList, to } = props;

const location = useLocation();
const provenance = provenanceList
.filter((provenance) =>
provenance.target.find(
(target) =>
fromFHIRReference(target)?.id === resource.id &&
fromFHIRReference(target)?.resourceType === resource.resourceType,
),
)
.sort((a, b) => new Date(b.recorded).getTime() - new Date(a.recorded).getTime())[0];

const { getLinkToEditUrl } = useContext(LinkToEditContext);

const customPathTo = getLinkToEditUrl({ provenance, pathname: location.pathname, to });

if (!customPathTo) {
return <>{name}</>;
}

return <Link to={`${customPathTo}`}>{name}</Link>;
}
30 changes: 30 additions & 0 deletions src/components/LinkToEdit/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Provenance } from 'fhir/r4b';
import { fromFHIRReference } from 'sdc-qrf';

export type GetLinkToEditUrlProps = {
provenance?: Provenance;
pathname?: string;
to?: string;
};

export function getLinkToEditUrl(props: GetLinkToEditUrlProps): string | undefined {
const { provenance, pathname, to } = props;

const entity = provenance?.entity?.[0]?.what;
const qrId = fromFHIRReference(entity)?.id;
const baseUrl = pathname?.split('/').slice(0, 3).join('/');

if (!qrId) {
return undefined;
}

if (to) {
return `${to}/${qrId}`;
}

if (!baseUrl) {
return undefined;
}

return `${baseUrl}/documents/${qrId}`;
}
21 changes: 0 additions & 21 deletions src/components/ResourceTable/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ import { Empty } from 'antd';
import { ColumnsType } from 'antd/lib/table';
import { Provenance, Resource } from 'fhir/r4b';
import { ReactNode } from 'react';
import { Link, useLocation } from 'react-router-dom';
import { fromFHIRReference } from 'sdc-qrf';

import { RenderRemoteData, SearchParams, extractBundleResources, ResourcesMap } from '@beda.software/fhir-react';

Expand Down Expand Up @@ -77,22 +75,3 @@ export function ResourceTable<R extends Resource>(props: ResourceTableProps<R>)
</RenderRemoteData>
);
}

export function LinkToEdit(props: { name?: string; resource: Resource; provenanceList: Provenance[] }) {
const { name, resource, provenanceList } = props;
const location = useLocation();
const provenance = provenanceList.find(
(p) =>
fromFHIRReference(p.target[0])?.id === resource.id &&
fromFHIRReference(p.target[0])?.resourceType === resource.resourceType,
);
const entity = provenance?.entity?.[0]?.what;
const qrId = fromFHIRReference(entity)?.id;
const pathname = location.pathname.split('/').slice(0, 3).join('/');

if (qrId) {
return <Link to={`${pathname}/documents/${qrId}`}>{name}</Link>;
}

return <>{name}</>;
}
12 changes: 7 additions & 5 deletions src/components/SearchBar/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Trans } from '@lingui/macro';
import { Button } from 'antd';
import { useMemo } from 'react';

import { SearchBarColumn } from './SearchBarColumn';
import { SearchBarMobile } from './SearchBarMobile';
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;
Expand All @@ -33,9 +33,11 @@ export function SearchBar(props: SearchBarProps) {
))}
</S.LeftColumn>

<Button onClick={onResetFilters}>
<Trans>Clear filters</Trans>
</Button>
{searchBarFilterValues.length > 1 ? (
<Button onClick={onResetFilters}>
<Trans>Clear filters</Trans>
</Button>
) : null}
</S.SearchBar>
<S.MobileFilters $showInDrawerOnMobile={showInDrawerOnMobile} $level={level}>
<SearchBarMobile {...props} />
Expand Down
Loading

0 comments on commit e7415ce

Please sign in to comment.