Skip to content

Commit

Permalink
feat: [DHIS2-12362][DHIS2-12455] View and filter relationships (#3241)
Browse files Browse the repository at this point in the history
Co-authored-by: jasminenguyennn <[email protected]>
Co-authored-by: JasmineNg <[email protected]>
Co-authored-by: Joakim Storløkken Melseth <[email protected]>
  • Loading branch information
4 people authored Jul 28, 2023
1 parent 5eefb06 commit 438d2cd
Show file tree
Hide file tree
Showing 90 changed files with 2,509 additions and 163 deletions.
37 changes: 35 additions & 2 deletions i18n/en.pot
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ msgstr ""
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"POT-Creation-Date: 2023-06-01T08:11:59.116Z\n"
"PO-Revision-Date: 2023-06-01T08:11:59.116Z\n"
"POT-Creation-Date: 2023-07-25T09:23:57.018Z\n"
"PO-Revision-Date: 2023-07-25T09:23:57.018Z\n"

msgid "Choose one or more dates..."
msgstr "Choose one or more dates..."
Expand Down Expand Up @@ -938,6 +938,9 @@ msgstr "Event could not be loaded"
msgid "Organisation unit could not be loaded"
msgstr "Organisation unit could not be loaded"

msgid "Could not retrieve metadata. Please try again later."
msgstr "Could not retrieve metadata. Please try again later."

msgid "Possible duplicates found"
msgstr "Possible duplicates found"

Expand Down Expand Up @@ -1347,6 +1350,36 @@ msgstr "{{ scheduledEvents }} scheduled"
msgid "Stages and Events"
msgstr "Stages and Events"

msgid "New TEI Relationship"
msgstr "New TEI Relationship"

msgid "Missing implementation step"
msgstr "Missing implementation step"

msgid "Go back without saving relationship"
msgstr "Go back without saving relationship"

msgid "New Relationship"
msgstr "New Relationship"

msgid "Link to an existing {{tetName}}"
msgstr "Link to an existing {{tetName}}"

msgid "Something went wrong while loading relationships. Please try again later."
msgstr "Something went wrong while loading relationships. Please try again later."

msgid "TEI's Relationships"
msgstr "TEI's Relationships"

msgid "Type"
msgstr "Type"

msgid "Created date"
msgstr "Created date"

msgid "Program stage name"
msgstr "Program stage name"

msgid "Working list could not be loaded"
msgstr "Working list could not be loaded"

Expand Down
26 changes: 15 additions & 11 deletions src/components/App/AppPages.component.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,21 @@ import { EnrollmentPage } from 'capture-core/components/Pages/Enrollment';
import { StageEventListPage } from 'capture-core/components/Pages/StageEvent';
import { EnrollmentEditEventPage } from 'capture-core/components/Pages/EnrollmentEditEvent';
import { EnrollmentAddEventPage } from 'capture-core/components/Pages/EnrollmentAddEvent';
import { ReactQueryDevtools } from 'react-query/devtools';

export const AppPages = () => (
<Switch>
<Route path="/viewEvent" component={ViewEventPage} />
<Route path="/search" component={SearchPage} />
<Route path="/new" component={NewPage} />
<Route path="/enrollment/stageEvents" component={StageEventListPage} />
<Route path="/enrollmentEventEdit" component={EnrollmentEditEventPage} />
<Route path="/enrollmentEventNew" component={EnrollmentAddEventPage} />
<Route path="/enrollment" component={EnrollmentPage} />
<Route path="/:keys" component={MainPage} />
<Route path="/" component={MainPage} />
</Switch>
<>
<ReactQueryDevtools />
<Switch>
<Route path="/viewEvent" component={ViewEventPage} />
<Route path="/search" component={SearchPage} />
<Route path="/new" component={NewPage} />
<Route path="/enrollment/stageEvents" component={StageEventListPage} />
<Route path="/enrollmentEventEdit" component={EnrollmentEditEventPage} />
<Route path="/enrollmentEventNew" component={EnrollmentAddEventPage} />
<Route path="/enrollment" component={EnrollmentPage} />
<Route path="/:keys" component={MainPage} />
<Route path="/" component={MainPage} />
</Switch>
</>
);
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export const useEnrollmentFormFoundation = ({
locale,
}: Props) => {
const { data: enrollment, isLoading, error } = useIndexedDBQuery(
// $FlowFixMe
['enrollmentForm', program?.id],
() => buildEnrollmentForm({
// $FlowFixMe - Flow does not understand that the values are not null here
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export const useTrackedEntityTypeCollection = ({
locale,
}: Props): ReturnValues => {
const { data: trackedEntityAttributes } = useIndexedDBQuery(
// $FlowFixMe
['trackedEntityAttributes', trackedEntityType?.id],
() => getTrackedEntityAttributes(
trackedEntityType
Expand All @@ -40,6 +41,7 @@ export const useTrackedEntityTypeCollection = ({
);

const { data: trackedEntityTypeCollection } = useIndexedDBQuery(
// $FlowFixMe
['trackedEntityTypeCollection', trackedEntityType?.id],
() => buildTrackedEntityTypeCollection({
// $FlowFixMe
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,3 +207,4 @@ export const openEnrollmentPageEpic = (action$: InputObservable, store: ReduxSto
},
),
);

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// @flow
import React, { type ComponentType } from 'react';
import React, { type ComponentType, useState, useCallback } from 'react';
import withStyles from '@material-ui/core/styles/withStyles';
import { spacersNum, spacers, colors } from '@dhis2/ui';
import i18n from '@dhis2/d2-i18n';
Expand All @@ -13,8 +13,13 @@ import { WidgetError } from '../../../WidgetErrorAndWarning/WidgetError';
import { WidgetIndicator } from '../../../WidgetIndicator';
import { WidgetEnrollmentComment } from '../../../WidgetEnrollmentComment';
import { EnrollmentQuickActions } from './EnrollmentQuickActions';
import { TrackedEntityRelationshipsWrapper } from '../../common/TEIRelationshipsWidget/TrackedEntityRelationshipsWrapper';
import { AddRelationshipRefWrapper } from '../../EnrollmentEditEvent/AddRelationshipRefWrapper';

const getStyles = () => ({
container: {
position: 'relative',
},
columns: {
display: 'flex',
},
Expand Down Expand Up @@ -59,61 +64,88 @@ export const EnrollmentPageDefaultPlain = ({
hideWidgets,
classes,
onEventClick,
onLinkedRecordClick,
onUpdateTeiAttributeValues,
onEnrollmentError,
}: PlainProps) => (
<>
<div className={classes.title}>{i18n.t('Enrollment Dashboard')}</div>
<div className={classes.columns}>
<div className={classes.leftColumn}>
<EnrollmentQuickActions
stages={stages}
events={events}
/>
<WidgetStagesAndEvents
programId={program.id}
stages={stages}
events={events}
onViewAll={onViewAll}
onCreateNew={onCreateNew}
onEventClick={onEventClick}
/>
</div>
<div className={classes.rightColumn}>
<WidgetEnrollmentComment />
<WidgetError error={widgetEffects?.errors} />
<WidgetWarning warning={widgetEffects?.warnings} />
{!hideWidgets.indicator && (
<WidgetIndicator
indicators={widgetEffects?.indicators}
emptyText={i18n.t('No indicator output for this enrollment yet')}
/>
)}
{!hideWidgets.feedback && (
<WidgetFeedback
feedback={widgetEffects?.feedbacks}
emptyText={i18n.t('No feedback for this enrollment yet')}
/>
)}
<WidgetProfile
teiId={teiId}
programId={program.id}
orgUnitId={orgUnitId}
onUpdateTeiAttributeValues={onUpdateTeiAttributeValues}
showEdit
/>
{enrollmentId !== 'AUTO' && <WidgetEnrollment
teiId={teiId}
enrollmentId={enrollmentId}
programId={program.id}
onDelete={onDelete}
onAddNew={onAddNew}
onError={onEnrollmentError}
/>}
}: PlainProps) => {
const [mainContentVisible, setMainContentVisibility] = useState(true);
const [addRelationShipContainerElement, setAddRelationshipContainerElement] =
useState<?HTMLDivElement>(undefined);

const toggleVisibility = useCallback(() => setMainContentVisibility(current => !current), []);

return (
<>
<AddRelationshipRefWrapper setRelationshipRef={setAddRelationshipContainerElement} />
<div
className={classes.container}
style={!mainContentVisible ? { display: 'none' } : undefined}
>
<div className={classes.title}>{i18n.t('Enrollment Dashboard')}</div>
<div className={classes.columns}>
<div className={classes.leftColumn}>
<EnrollmentQuickActions
stages={stages}
events={events}
/>
<WidgetStagesAndEvents
programId={program.id}
stages={stages}
events={events}
onViewAll={onViewAll}
onCreateNew={onCreateNew}
onEventClick={onEventClick}
/>
</div>
<div className={classes.rightColumn}>
{addRelationShipContainerElement &&
<TrackedEntityRelationshipsWrapper
trackedEntityTypeId={program.trackedEntityType.id}
programId={program.id}
addRelationshipRenderElement={addRelationShipContainerElement}
onAddRelationship={() => {}}
onOpenAddRelationship={toggleVisibility}
onCloseAddRelationship={toggleVisibility}
teiId={teiId}
onLinkedRecordClick={onLinkedRecordClick}
/>
}
<WidgetEnrollmentComment />
<WidgetError error={widgetEffects?.errors} />
<WidgetWarning warning={widgetEffects?.warnings} />
{!hideWidgets.indicator && (
<WidgetIndicator
indicators={widgetEffects?.indicators}
emptyText={i18n.t('No indicator output for this enrollment yet')}
/>
)}
{!hideWidgets.feedback && (
<WidgetFeedback
feedback={widgetEffects?.feedbacks}
emptyText={i18n.t('No feedback for this enrollment yet')}
/>
)}
<WidgetProfile
teiId={teiId}
programId={program.id}
orgUnitId={orgUnitId}
onUpdateTeiAttributeValues={onUpdateTeiAttributeValues}
showEdit
/>
{enrollmentId !== 'AUTO' && <WidgetEnrollment
teiId={teiId}
enrollmentId={enrollmentId}
programId={program.id}
onDelete={onDelete}
onAddNew={onAddNew}
onError={onEnrollmentError}
/>}
</div>
</div>
</div>
</div>
</>
);
</>
);
};


export const EnrollmentPageDefaultComponent: ComponentType<Props> = withStyles(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@ import {
import { buildUrlQueryString, useLocationQuery } from '../../../../utils/routing';
import { deleteEnrollment, updateTeiDisplayName } from '../EnrollmentPage.actions';
import { useFilteredWidgetData } from './hooks/useFilteredWidgetData';
import { useLinkedRecordClick } from '../../common/TEIRelationshipsWidget';

export const EnrollmentPageDefault = () => {
const history = useHistory();
const dispatch = useDispatch();
const { enrollmentId, programId, teiId, orgUnitId } = useLocationQuery();
const { orgUnit, error } = useRulesEngineOrgUnit(orgUnitId);
const { onLinkedRecordClick } = useLinkedRecordClick();

const program = useTrackerProgram(programId);
const {
Expand Down Expand Up @@ -74,6 +76,7 @@ export const EnrollmentPageDefault = () => {
const onEventClick = (eventId: string) => {
history.push(`/enrollmentEventEdit?${buildUrlQueryString({ orgUnitId, eventId })}`);
};

const onUpdateTeiAttributeValues = useCallback((updatedAttributeValues, teiDisplayName) => {
dispatch(updateEnrollmentAttributeValues(updatedAttributeValues
.map(({ attribute, value }) => ({ id: attribute, value })),
Expand All @@ -86,6 +89,7 @@ export const EnrollmentPageDefault = () => {
};

const onEnrollmentError = message => dispatch(showEnrollmentError({ message }));

if (error) {
return error.errorComponent;
}
Expand All @@ -106,6 +110,7 @@ export const EnrollmentPageDefault = () => {
widgetEffects={outputEffects}
hideWidgets={hideWidgets}
onEventClick={onEventClick}
onLinkedRecordClick={onLinkedRecordClick}
onUpdateTeiAttributeValues={onUpdateTeiAttributeValues}
onEnrollmentError={onEnrollmentError}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
// @flow
import type { Program } from 'capture-core/metaData';
import type { TrackerProgram } from 'capture-core/metaData';
import type { Stage } from 'capture-core/components/WidgetStagesAndEvents/types/common.types';
import type { WidgetEffects, HideWidgets } from '../../common/EnrollmentOverviewDomain';
import type { Event } from '../../common/EnrollmentOverviewDomain/useCommonEnrollmentDomainData';
import type { LinkedRecordClick } from '../../../WidgetsRelationship/WidgetTrackedEntityRelationship';

export type Props = {|
program: Program,
program: TrackerProgram,
enrollmentId: string,
teiId: string,
events: ?Array<Event>,
Expand All @@ -19,6 +20,7 @@ export type Props = {|
onCreateNew: (stageId: string) => void,
onEventClick: (eventId: string) => void,
onUpdateTeiAttributeValues: (attributes: Array<{ [key: string]: string }>, teiDisplayName: string) => void,
onLinkedRecordClick: LinkedRecordClick,
onEnrollmentError: (message: string) => void,
|};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// @flow
import React, { useEffect, useRef } from 'react';

type Props = {
setRelationshipRef: (HTMLDivElement) => void,
}

export const AddRelationshipRefWrapper = ({ setRelationshipRef }: Props) => {
const renderRelationshipRef = useRef<?HTMLDivElement>(undefined);

// Extracting the logic to separate component because of the OrgUnitFetcher
useEffect(() => {
if (renderRelationshipRef.current) {
setRelationshipRef(renderRelationshipRef.current);
}
}, [setRelationshipRef]);

return (
<div ref={renderRelationshipRef} />
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// @flow

export { AddRelationshipRefWrapper } from './AddRelationshipRefWrapper.component';
Loading

0 comments on commit 438d2cd

Please sign in to comment.