Skip to content

Commit

Permalink
feat: show related stages Widget on registration page
Browse files Browse the repository at this point in the history
  • Loading branch information
simonadomnisoru committed Nov 14, 2024
1 parent bc2b962 commit ba81d9c
Show file tree
Hide file tree
Showing 33 changed files with 324 additions and 144 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import {
withAOCFieldBuilder,
withDataEntryFields,
} from '../../DataEntryDhis2Helpers';
import type { RelatedStageRefPayload } from '../../WidgetRelatedStages';

const overrideMessagePropNames = {
errorMessage: 'validationError',
Expand Down Expand Up @@ -335,6 +336,7 @@ type FinalTeiDataEntryProps = {
onUpdateFormFieldAsync: Function,
onUpdateFormField: Function,
firstStageMetaData?: ?{ stage: ProgramStage },
relatedStageRef?: { current: ?RelatedStageRefPayload },
formFoundation: RenderFoundation,
};
// final step before the generic dataEntry is inserted
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ import type { Props } from './EnrollmentWithFirstStageDataEntry.types';
import { FirstStageDataEntry } from './EnrollmentWithFirstStageDataEntry.component';
import { useDataEntrySections } from './hooks';
import { Section } from '../../../../metaData';
import { WidgetRelatedStages } from '../../../WidgetRelatedStages';

const getSectionId = sectionId =>
(sectionId === Section.MAIN_SECTION_ID ? `${Section.MAIN_SECTION_ID}-stage` : sectionId);

export const EnrollmentWithFirstStageDataEntry = (props: Props) => {
const { firstStageMetaData, ...passOnProps } = props;
const { firstStageMetaData, relatedStageRef, ...passOnProps } = props;
const {
stage: { stageForm: firstStageFormFoundation, name: stageName },
} = firstStageMetaData;
Expand All @@ -19,10 +20,18 @@ export const EnrollmentWithFirstStageDataEntry = (props: Props) => {
const dataEntrySections = useDataEntrySections(stageName, beforeSectionId);

return (
<FirstStageDataEntry
{...passOnProps}
firstStageMetaData={firstStageMetaData}
dataEntrySections={dataEntrySections}
/>
<>
<FirstStageDataEntry
{...passOnProps}
firstStageMetaData={firstStageMetaData}
dataEntrySections={dataEntrySections}
/>
<WidgetRelatedStages
ref={relatedStageRef}
programId={passOnProps.programId}
programStageId={firstStageMetaData.stage?.id}
currentStageLabel={firstStageMetaData.stage?.name}
/>
</>
);
};
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
// @flow
import type { ProgramStage, RenderFoundation } from '../../../../metaData';
import type { RelatedStageRefPayload } from '../../../WidgetRelatedStages';

export type Props = {
firstStageMetaData: {
stage: ProgramStage,
},
formFoundation: RenderFoundation,
programId: string,
relatedStageRef?: { current: ?RelatedStageRefPayload },
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// @flow
import React from 'react';
import React, { useRef } from 'react';
import type { ComponentType } from 'react';
import { useSelector } from 'react-redux';
import { EnrollmentRegistrationEntryComponent } from './EnrollmentRegistrationEntry.component';
Expand All @@ -10,6 +10,7 @@ import { dataEntryHasChanges } from '../../DataEntry/common/dataEntryHasChanges'
import {
useBuildEnrollmentPayload,
} from './hooks/useBuildEnrollmentPayload';
import type { RelatedStageRefPayload } from '../../WidgetRelatedStages';

export const EnrollmentRegistrationEntry: ComponentType<OwnProps> = ({
selectedScopeId,
Expand All @@ -22,6 +23,7 @@ export const EnrollmentRegistrationEntry: ComponentType<OwnProps> = ({
onCancel,
...passOnProps
}) => {
const relatedStageRef = useRef<?RelatedStageRefPayload>(null);
const { orgUnit, error } = useCoreOrgUnit(orgUnitId);
const {
ready,
Expand Down Expand Up @@ -57,13 +59,15 @@ export const EnrollmentRegistrationEntry: ComponentType<OwnProps> = ({
}

const onSaveWithEnrollment = () => {
const teiWithEnrollment = buildTeiWithEnrollment();
onSave(teiWithEnrollment);
const { teiWithEnrollment, formHasError, programStageIdLinkedEventToRedirectTo } =
buildTeiWithEnrollment(relatedStageRef);
!formHasError && onSave(teiWithEnrollment, programStageIdLinkedEventToRedirectTo);
};

return (
<EnrollmentRegistrationEntryComponent
{...passOnProps}
relatedStageRef={relatedStageRef}
firstStageMetaData={firstStageMetaData}
selectedScopeId={selectedScopeId}
formId={formId}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type { SaveForDuplicateCheck } from '../common/TEIAndEnrollment/Duplicate
import type { ExistingUniqueValueDialogActionsComponent } from '../withErrorMessagePostProcessor';
import type { InputAttribute } from './hooks/useFormValues';
import { RenderFoundation, ProgramStage } from '../../../metaData';
import type { RelatedStageRefPayload } from '../../WidgetRelatedStages';

type TrackedEntityAttributes = Array<{
attribute: string,
Expand All @@ -32,6 +33,21 @@ export type EnrollmentPayload = {|
attributes: TrackedEntityAttributes,
geometry: any,
|}
],
relationships?: [
{
relationshipType: string,
from: {
event: {
event: string,
},
},
to: {
event: {
event: string,
},
},
}
]
|}

Expand All @@ -51,6 +67,7 @@ export type OwnProps = $ReadOnly<{|
trackedEntityInstanceAttributes?: Array<InputAttribute>,
saveButtonText: (trackedEntityName: string) => string,
firstStageMetaData?: ?{ stage: ?ProgramStage },
relatedStageRef?: { current: ?RelatedStageRefPayload },
|}>;

type ContainerProps = {|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,12 @@ import {
import {
deriveAutoGenerateEvents,
deriveFirstStageDuringRegistrationEvent,
deriveRelatedStageEvent,
} from '../../../Pages/New/RegistrationDataEntry/helpers';
import type { EnrollmentPayload } from '../EnrollmentRegistrationEntry.types';
import { geometryType, getPossibleTetFeatureTypeKey, buildGeometryProp } from '../../common/TEIAndEnrollment/geometry';
import { RelatedStageModes } from '../../../WidgetRelatedStages';
import type { RelatedStageRefPayload } from '../../../WidgetRelatedStages';

type DataEntryReduxConverterProps = {
programId: string;
Expand Down Expand Up @@ -88,7 +91,11 @@ export const useBuildEnrollmentPayload = ({
const { firstStageMetaData } = useBuildFirstStageRegistration(programId);
const { formFoundation } = useMergeFormFoundationsIfApplicable(scopeFormFoundation, firstStageMetaData);

const buildTeiWithEnrollment = (): EnrollmentPayload => {
const buildTeiWithEnrollment = (relatedStageRef?: {current: ?RelatedStageRefPayload}): {
teiWithEnrollment: EnrollmentPayload,
formHasError: boolean,
programStageIdLinkedEventToRedirectTo?: string,
} => {
if (!formFoundation) throw Error('form foundation object not found');
const firstStage = firstStageMetaData && firstStageMetaData.stage;
const clientValues = getClientValuesForFormData(formValues, formFoundation);
Expand Down Expand Up @@ -126,8 +133,17 @@ export const useBuildEnrollmentPayload = ({
serverMinorVersion: minor,
});

const { formHasError, linkedEvent: relatedStageLinkedEvent, relationship, linkMode } = deriveRelatedStageEvent({
clientRequestEvent: firstStageDuringRegistrationEvent,
relatedStageRef,
firstStageMetaData,
programId,
teiId,
});

const autoGenerateEvents = deriveAutoGenerateEvents({
firstStageMetadata: firstStage,
firstStageDuringRegistrationEvent,
relatedStageLinkedEvent,
stages,
enrolledAt,
occurredAt,
Expand All @@ -137,9 +153,8 @@ export const useBuildEnrollmentPayload = ({
serverMinorVersion: minor,
});

const allEventsToBeCreated = firstStageDuringRegistrationEvent
? [firstStageDuringRegistrationEvent, ...autoGenerateEvents]
: autoGenerateEvents;
const allEventsToBeCreated = [firstStageDuringRegistrationEvent, relatedStageLinkedEvent, ...autoGenerateEvents]
.filter(Boolean);

const attributes = deriveAttributesFromFormValues(formServerValues);

Expand All @@ -155,17 +170,23 @@ export const useBuildEnrollmentPayload = ({
};

const tetFeatureTypeKey = getPossibleTetFeatureTypeKey(formServerValues);
const tetGeometry = tetFeatureTypeKey ?
buildGeometryProp(tetFeatureTypeKey, formValues)
: undefined;
const tetGeometry = tetFeatureTypeKey ? buildGeometryProp(tetFeatureTypeKey, formValues) : undefined;

return {
trackedEntity: teiId || generateUID(),
orgUnit: orgUnitId,
trackedEntityType: trackedEntityTypeId,
attributes,
geometry: tetGeometry,
enrollments: [enrollment],
teiWithEnrollment: {
trackedEntity: teiId || generateUID(),
orgUnit: orgUnitId,
trackedEntityType: trackedEntityTypeId,
attributes,
geometry: tetGeometry,
enrollments: [enrollment],
relationships: relationship ? [relationship] : undefined,
},
formHasError,
programStageIdLinkedEventToRedirectTo:
relatedStageLinkedEvent && linkMode === RelatedStageModes.ENTER_DATA
? relatedStageLinkedEvent.programStage
: undefined,
};
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ import type { TeiPayload } from '../../../../../Pages/common/TEIRelationshipsWid

export type SaveForDuplicateCheck = (
teiWithEnrollment: EnrollmentPayload | TeiPayload,
programStageIdLinkedEventToRedirectTo?: string,
) => void;
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@ import { CompleteModal } from './CompleteModal';
import { statusTypes as eventStatuses } from '../../../../../events/statusTypes';
import { type RenderFoundation } from '../../../../../metaData';
import { addEventSaveTypes } from '../../../../WidgetEnrollmentEventNew/DataEntry/addEventSaveTypes';
import { actions as LinkModes } from '../../../../WidgetRelatedStages/constants';
import type { RelatedStageRefPayload } from '../../../../WidgetEnrollmentEventNew/Validated/validated.types';
import { RelatedStageModes } from '../../../../WidgetRelatedStages';
import type { RelatedStageRefPayload } from '../../../../WidgetRelatedStages';

type Props = {
onSave: (eventId: string, dataEntryId: string, formFoundation: RenderFoundation, saveType?: ?string) => void,
askCompleteEnrollmentOnEventComplete?: ?boolean,
isCompleted?: boolean,
eventId?: ?string,
formFoundation: RenderFoundation,
relatedStageRef: { current?: ?RelatedStageRefPayload },
relatedStageRef?: { current: ?RelatedStageRefPayload },
onSaveAndCompleteEnrollment: (
eventId: string,
dataEntryId: string,
Expand Down Expand Up @@ -60,7 +60,11 @@ const getAskToCompleteEnrollment = (InnerComponent: ComponentType<any>) => (prop
) => {
const { linkMode } = relatedStageRef?.current?.getLinkedStageValues() ?? {};
eventDataToSave.current = { itemId, dataEntryId, formFoundation, saveType };
if (askCompleteEnrollmentOnEventComplete && (isCompleted || saveType === addEventSaveTypes.COMPLETE) && linkMode !== LinkModes.ENTER_DATA) {
if (
askCompleteEnrollmentOnEventComplete &&
(isCompleted || saveType === addEventSaveTypes.COMPLETE) &&
linkMode !== RelatedStageModes.ENTER_DATA
) {
setOpenCompleteModal(true);
} else {
onSave(itemId, dataEntryId, formFoundation, saveType);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
// @flow
import log from 'loglevel';
import { generateUID } from '../../../../utils/uid/generateUID';
import { actions as RelatedStageModes } from '../../../WidgetRelatedStages/constants';
import type { ConvertedRelatedStageEventProps } from './getConvertedRelatedStageEvent.types';
import { RelatedStageModes } from '../../../WidgetRelatedStages';
import type { LinkedRequestEvent, ConvertedRelatedStageEventProps } from './getConvertedRelatedStageEvent.types';
import { errorCreator, pipe } from '../../../../../capture-core-utils';
import { type LinkedRequestEvent } from '../validated.types';
import { convertClientToServer, convertFormToClient } from '../../../../converters';
import { dataElementTypes } from '../../../../metaData';

Expand Down Expand Up @@ -69,7 +68,7 @@ const getEventDetailsByLinkMode = ({
return ({
linkedEvent: {
...baseEventDetails,
scheduledAt: convertFn(clientRequestEvent.scheduledAt, dataElementTypes.DATE),
scheduledAt: clientRequestEvent.scheduledAt,
orgUnit: convertFn(linkedEventOrgUnit, dataElementTypes.ORGANISATION_UNIT),
},
linkedEventId: baseEventDetails.event,
Expand Down Expand Up @@ -100,7 +99,8 @@ export const getConvertedRelatedStageEvent = ({
enrollmentId,
relatedStageType,
}: ConvertedRelatedStageEventProps) => {
const requestEventIsFromConstraint = relatedStageType.fromConstraint.programStage.id === currentProgramStageId;
const requestEventIsFromConstraint =
relatedStageType.fromConstraint.programStage.id === currentProgramStageId;

const { linkedEvent, linkedEventId } = getEventDetailsByLinkMode({
relatedStageDataValues,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// @flow
import type { RelatedStageDataValueStates, RelatedStageRelationshipType } from '../../../WidgetRelatedStages';
import { RelatedStageModes } from '../../../WidgetRelatedStages';

type CommonEventDetails = {
event: string,
program: string,
programStage: string,
orgUnit: string,
trackedEntity?: ?string,
enrollment?: string,
scheduledAt: string,
dataValues: Array<{ dataElement: string, value: any }>,
status?: string,
}

export type RequestEvent = {
...CommonEventDetails,
occurredAt: string,
notes?: Array<{ value: string }>,
completedAt?: string,
}

export type LinkedRequestEvent = {
...CommonEventDetails,
occurredAt?: string,
completedAt?: string,
}

export type ConvertedRelatedStageEventProps = {|
linkMode: $Keys<typeof RelatedStageModes>,
relatedStageDataValues: RelatedStageDataValueStates,
programId: string,
teiId?: ?string,
currentProgramStageId: string,
enrollmentId?: string,
relatedStageType: RelatedStageRelationshipType,
clientRequestEvent: RequestEvent,
|}

Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// @flow

export { getConvertedRelatedStageEvent } from './getConvertedRelatedStageEvent';
export type { RequestEvent, LinkedRequestEvent } from './getConvertedRelatedStageEvent.types';
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@ export {
convertStatusOut,
getConvertGeometryIn,
} from './converters';

export { getConvertedRelatedStageEvent } from './getConvertedRelatedStageEvent';
export type { RequestEvent, LinkedRequestEvent } from './getConvertedRelatedStageEvent';
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ export {
convertStatusIn,
convertStatusOut,
getConvertGeometryIn,
} from './converters/converters';
getConvertedRelatedStageEvent,
} from './converters';
export { EnrollmentRegistrationEntry } from './EnrollmentRegistrationEntry/EnrollmentRegistrationEntry.container';
export { TeiRegistrationEntry } from './TeiRegistrationEntry/TeiRegistrationEntry.container';
export { SingleEventRegistrationEntry } from './SingleEventRegistrationEntry/SingleEventRegistrationEntry.container';
export type { SaveForDuplicateCheck as SaveForEnrollmentAndTeiRegistration } from './common/TEIAndEnrollment/DuplicateCheckOnSave';
export type { ExistingUniqueValueDialogActionsComponent } from './withErrorMessagePostProcessor';
export { withAskToCompleteEnrollment } from './common/trackerEvent';
export type { RequestEvent, LinkedRequestEvent } from './converters';
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
rollbackEnrollmentEvents,
saveFailed,
} from '../common/EnrollmentOverviewDomain/enrollment.actions';
import { actions as RelatedStageActions } from '../../WidgetRelatedStages/constants';
import { RelatedStageModes } from '../../WidgetRelatedStages';
import { buildUrlQueryString } from '../../../utils/routing';

const shouldNavigateWithRelatedStage = ({
Expand All @@ -21,7 +21,7 @@ const shouldNavigateWithRelatedStage = ({
history,
}) => {
if (linkMode && linkedEventId) {
if (linkMode === RelatedStageActions.ENTER_DATA) {
if (linkMode === RelatedStageModes.ENTER_DATA) {
const navigate = () => history.push(`/enrollmentEventEdit?${buildUrlQueryString({
eventId: linkedEventId,
orgUnitId: linkedOrgUnitId,
Expand Down
Loading

0 comments on commit ba81d9c

Please sign in to comment.