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

feat: [DHIS2-17655] Two event workspace #3726

Merged
merged 22 commits into from
Aug 14, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
e03ea54
feat: add plugin functionality
eirikhaugstulen Jun 17, 2024
1b23658
Merge remote-tracking branch 'origin/master' into eh/feat/DHIS2-17591…
eirikhaugstulen Jun 18, 2024
5b5497b
chore: flow
eirikhaugstulen Jun 18, 2024
2504789
feat: support plugins in program stages
eirikhaugstulen Jun 18, 2024
e599d05
chore: flow
eirikhaugstulen Jun 18, 2024
5e96878
fix: option set logic
eirikhaugstulen Jun 19, 2024
1cb4612
fix: review
eirikhaugstulen Jun 30, 2024
c9aab55
fix: pass in cached element definitions
eirikhaugstulen Jul 1, 2024
5dd3ac6
Merge remote-tracking branch 'refs/remotes/origin/master' into eh/fea…
eirikhaugstulen Jul 1, 2024
0c446d1
chore: temp
eirikhaugstulen Jul 3, 2024
ff6e800
Merge remote-tracking branch 'refs/remotes/origin/master' into eh/fea…
eirikhaugstulen Jul 3, 2024
0ed89ce
Merge remote-tracking branch 'refs/remotes/origin/master' into eh/fea…
eirikhaugstulen Jul 9, 2024
b44e501
fix: temp
eirikhaugstulen Jul 10, 2024
4e41c05
Merge remote-tracking branch 'refs/remotes/origin/master' into eh/fea…
eirikhaugstulen Jul 15, 2024
a0e58b8
feat: add two event workspace functionality
eirikhaugstulen Jul 23, 2024
6a8e097
Merge remote-tracking branch 'refs/remotes/origin/master' into eh/fea…
eirikhaugstulen Jul 23, 2024
b25455b
feat: lint
eirikhaugstulen Jul 23, 2024
5561bd5
Merge remote-tracking branch 'refs/remotes/origin/master' into eh/fea…
eirikhaugstulen Jul 31, 2024
aea077f
feat: add subvalues
eirikhaugstulen Aug 8, 2024
ffcf09d
Merge remote-tracking branch 'refs/remotes/origin/master' into eh/fea…
eirikhaugstulen Aug 8, 2024
97d11ba
chore: remove attribute
eirikhaugstulen Aug 8, 2024
656b471
fix: add fallback for v39 and v40
eirikhaugstulen Aug 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// @flow
import React from 'react';
import { useOrgUnitName } from '../../../metadataRetrieval/orgUnitName';

type Props = {
orgUnitId: string,
}

export const FlatListOrgUnitField = ({
orgUnitId,
}: Props) => {
const { displayName } = useOrgUnitName(orgUnitId);

return (
<span>
{displayName}
</span>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// @flow

export { FlatListOrgUnitField } from './FlatListOrgUnitField';
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@
import React, { useMemo } from 'react';
import { spacers } from '@dhis2/ui';
import { FlatList } from 'capture-ui';
import { pipe } from 'capture-core-utils';
import withStyles from '@material-ui/core/styles/withStyles';
import { convertClientToView, convertServerToClient } from '../../converters';
import type { RenderFoundation } from '../../metaData';
import { getDataEntryDetails, Placements } from './utils/getDataEntryDetails';

Expand All @@ -26,8 +24,6 @@ const styles = {
},
};

const convertFn = pipe(convertServerToClient, convertClientToView);

const WidgetTwoEventWorkspacePlain = ({ linkedEvent, dataValues, formFoundation, classes }: Props) => {
const dataEntryValues = useMemo(() => getDataEntryDetails(
linkedEvent,
Expand All @@ -36,10 +32,9 @@ const WidgetTwoEventWorkspacePlain = ({ linkedEvent, dataValues, formFoundation,

const listValues = useMemo(() => {
const elements = formFoundation.getElements();
const convertedValues = formFoundation.convertAndGroupBySection(dataValues, convertFn);

return elements.map((dataElement) => {
const value = convertedValues[dataElement.id];
const value = dataValues[dataElement.id];
return {
key: dataElement.formName,
value: value ?? '',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
EnrollmentPageKeys,
} from '../Pages/common/EnrollmentOverviewDomain/EnrollmentPageLayout/DefaultEnrollmentLayout.constants';
import { NonBundledDhis2Icon } from '../NonBundledDhis2Icon';
import { useClientDataValues } from './hooks/useClientDataValues';

const styles = {
menu: {
Expand Down Expand Up @@ -66,11 +67,21 @@ const WidgetTwoEventWorkspacePlain = ({
stageId: linkedEvent?.programStage,
});

if (isLinkedEventLoading || isLoadingMetadata) {
const {
clientValuesWithSubValues,
isLoading: isLoadingClientValues,
isError: isClientValuesError,
} = useClientDataValues({
linkedEventId: linkedEvent?.event,
dataValues,
formFoundation,
});

if (isLinkedEventLoading || isLoadingMetadata || isLoadingClientValues) {
return null;
}

if (isLinkedEventError || isMetadataError) {
if (isLinkedEventError || isMetadataError || isClientValuesError) {
return (
<div>
{i18n.t('An error occurred while loading the widget.')}
Expand Down Expand Up @@ -134,7 +145,7 @@ const WidgetTwoEventWorkspacePlain = ({
<WidgetTwoEventWorkspaceComponent
linkedEvent={linkedEvent}
formFoundation={formFoundation}
dataValues={dataValues}
dataValues={clientValuesWithSubValues}
/>
</Widget>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// @flow
import { useConfig, useDataEngine } from '@dhis2/app-runtime';
import { useQuery } from 'react-query';
import type { RenderFoundation } from '../../../metaData';
import { convertClientToView, convertServerToClient } from '../../../converters';
import { buildUrl, pipe } from '../../../../capture-core-utils';
import { subValueGetterByElementType } from '../utils/getSubValueForDataValue';
import { makeQuerySingleResource } from '../../../utils/api';

type Props = {|
linkedEventId: string,
dataValues: Array<{| dataElement: string, value: any |}>,
formFoundation: ?RenderFoundation,
|}

const convertFn = pipe(convertServerToClient, convertClientToView);

const formatDataValues = async (
dataValues: Array<{| dataElement: string, value: any |}>,
formFoundation: RenderFoundation,
querySingleResource,
linkedEventId,
absoluteApiPath,
) => {
const elements = formFoundation.getElements();

const formattedDataValues = dataValues.map(async (dataValue) => {
const element = elements.find(({ id }) => id === dataValue.dataElement);
if (!element) {
return null;
}

let value = dataValue.value;
// $FlowFixMe - subValueGetterByElementType is only a subset of the full dataElementTypes
if (subValueGetterByElementType[element.type]) {
// $FlowFixMe
value = await subValueGetterByElementType[element.type]({
dataElement: {
id: element.id,
value: dataValue.value,
},
querySingleResource,
eventId: linkedEventId,
absoluteApiPath,
});
}

return {
id: element.id,
value: convertFn(value, element.type, element),
};
});

const resolvedDataValues = await Promise.all(formattedDataValues);
return resolvedDataValues.reduce((acc, dataValue) => {
if (dataValue) {
acc[dataValue.id] = dataValue.value;
}
return acc;
}, {});
};

export const useClientDataValues = ({
linkedEventId,
dataValues,
formFoundation,
}: Props) => {
const dataEngine = useDataEngine();
const { baseUrl, apiVersion } = useConfig();
const querySingleResource = makeQuerySingleResource(dataEngine.query.bind(dataEngine));
const { data: clientValuesWithSubValues, isError, isLoading } = useQuery(
['formattedDataValues', linkedEventId, dataValues],
() => formatDataValues(
dataValues,
// $FlowFixMe
formFoundation,
querySingleResource,
linkedEventId,
buildUrl(baseUrl, `api/${apiVersion}`),
),
{
enabled: !!dataValues &&
!!formFoundation &&
!!linkedEventId &&
!!baseUrl &&
!!apiVersion &&
!!querySingleResource,
},
);

return {
clientValuesWithSubValues,
isError,
isLoading,
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,6 @@ type Props = {|
originEventId: string,
|}

const formatDataValues = (dataValues: Array<{ dataElement: string, value: any }>) => {
if (!dataValues || dataValues.length === 0) {
return {};
}

return dataValues.reduce((acc, { dataElement, value }) => {
acc[dataElement] = value;
return acc;
}, {});
};

const calculateRelatedStageRelationships = (event) => {
if (!event || !event.relationships || event.relationships.length === 0) {
return null;
Expand Down Expand Up @@ -87,7 +76,7 @@ export const useLinkedEventByOriginId = ({ originEventId }: Props) => {
return {
linkedEvent: relatedStageRelationship.linkedEvent,
relationshipType: relatedStageRelationship.relationshipType,
dataValues: formatDataValues(relatedStageRelationship.linkedEvent.dataValues),
dataValues: relatedStageRelationship.linkedEvent.dataValues,
};
}, [data]);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// @flow
import React from 'react';
import { pipe } from 'capture-core-utils';
import i18n from '@dhis2/d2-i18n';
import { dataElementTypes, type RenderFoundation } from '../../../metaData';
import { convertClientToView, convertServerToClient } from '../../../converters';
import type { LinkedEvent } from '../WidgetTwoEventWorkspace.types';
import { FlatListOrgUnitField } from '../FlatListOrgUnitField';

const convertFn = pipe(convertServerToClient, convertClientToView);

Expand Down Expand Up @@ -35,8 +37,7 @@ const DataEntryFieldsToInclude = {
type: dataElementTypes.ORGANISATION_UNIT,
placement: Placements.TOP,
label: i18n.t('Organisation unit'),
// TODO - This issue will be fixed in DHIS2-17792
convertFn: value => value,
convertFn: orgUnitId => <FlatListOrgUnitField orgUnitId={orgUnitId} />,
},
status: {
apiKey: 'status',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// @flow
import { dataElementTypes } from '../../../metaData';
import type { QuerySingleResource } from '../../../utils/api';
import { featureAvailable, FEATURES } from '../../../../capture-core-utils';

type SubValueFunctionProps = {
dataElement: Object,
querySingleResource: QuerySingleResource,
eventId: string,
absoluteApiPath: string,
}

const getFileResourceSubvalue = async ({ dataElement, querySingleResource, eventId, absoluteApiPath }: SubValueFunctionProps) => {
const { value } = dataElement;
if (!value) return null;

const { id, displayName: name } = await querySingleResource({ resource: `fileResources/${value}` });
return {
id,
name,
url: `${absoluteApiPath}/events/files?dataElementUid=${dataElement.id}&eventUid=${eventId}`,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A reminder that this URL is not working in 2.41. The fix can be handled along with the rest of the places in DHIS2-17537. Thanks!

};
};

const getImageSubvalue = async ({ dataElement, querySingleResource, eventId, absoluteApiPath }: SubValueFunctionProps) => {
const { id: dataElementId, value } = dataElement;
if (!value) return null;

const { id, displayName: name } = await querySingleResource({ resource: `fileResources/${value}` });
return {
id,
name,
...(featureAvailable(FEATURES.trackerImageEndpoint) ?
{
url: `${absoluteApiPath}/tracker/events/${eventId}/dataValues/${dataElementId}/image`,
previewUrl: `${absoluteApiPath}/tracker/events/${eventId}/dataValues/${dataElementId}/image?dimension=small`,
} : {
url: `${absoluteApiPath}/events/files?dataElementUid=${dataElementId}&eventUid=${eventId}`,
previewUrl: `${absoluteApiPath}/events/files?dataElementUid=${dataElementId}&eventUid=${eventId}`,
}
),
};
};

const getOrganisationUnitSubvalue = async ({ dataElement, querySingleResource }: SubValueFunctionProps) => {
const organisationUnit = await querySingleResource({
resource: 'organisationUnits',
id: dataElement.value,
params: {
fields: 'id,name',
},
});
return { ...organisationUnit };
};

export const subValueGetterByElementType = {
[dataElementTypes.FILE_RESOURCE]: getFileResourceSubvalue,
[dataElementTypes.IMAGE]: getImageSubvalue,
[dataElementTypes.ORGANISATION_UNIT]: getOrganisationUnitSubvalue,
};