From ba81d9c95dedcc39e039b181a0fea1b5551f8565 Mon Sep 17 00:00:00 2001 From: Simona Domnisoru Date: Thu, 14 Nov 2024 12:47:40 +0100 Subject: [PATCH 01/28] feat: show related stages Widget on registration page --- .../EnrollmentDataEntry.component.js | 2 + ...llmentWithFirstStageDataEntry.container.js | 21 ++++-- ...EnrollmentWithFirstStageDataEntry.types.js | 3 + .../EnrollmentRegistrationEntry.container.js | 10 ++- .../EnrollmentRegistrationEntry.types.js | 17 +++++ .../hooks/useBuildEnrollmentPayload.js | 49 +++++++++---- .../types/duplicateCheckOnSave.types.js | 1 + .../withAskToCompleteEnrollment.js | 12 ++- .../getConvertedRelatedStageEvent.js | 10 +-- .../getConvertedRelatedStageEvent.types.js | 40 ++++++++++ .../getConvertedRelatedStageEvent/index.js | 4 + .../DataEntries/converters/index.js | 3 + .../components/DataEntries/index.js | 4 +- .../EnrollmentAddEventPage.epics.js | 4 +- ...EnrollmentAddEventPageDefault.container.js | 2 +- .../RegistrationDataEntry.actions.js | 13 +++- .../RegistrationDataEntry.container.js | 10 ++- .../RegistrationDataEntry.epics.js | 23 +++--- .../helpers/deriveAutoGenerateEvents.js | 14 ++-- ...deriveFirstStageDuringRegistrationEvent.js | 2 + .../helpers/deriveRelatedStageEvent.js | 73 +++++++++++++++++++ ...erEnrollment.js => getPageToRedirectTo.js} | 25 +++++-- .../RegistrationDataEntry/helpers/index.js | 3 +- .../Validated/Validated.container.js | 3 +- .../getConvertedRelatedStageEvent.types.js | 29 -------- .../getConvertedRelatedStageEvent/index.js | 2 - .../Validated/useBuildNewEventPayload.js | 9 ++- .../Validated/validated.actions.js | 4 +- .../Validated/validated.types.js | 31 -------- .../WidgetRelatedStages.types.js | 30 +++++++- .../hooks/useRelatedStageEvents.js | 4 +- .../components/WidgetRelatedStages/index.js | 7 +- .../relatedStageEventIsValid.types.js | 4 +- 33 files changed, 324 insertions(+), 144 deletions(-) rename src/core_modules/capture-core/components/{WidgetEnrollmentEventNew/Validated => DataEntries/converters}/getConvertedRelatedStageEvent/getConvertedRelatedStageEvent.js (89%) create mode 100644 src/core_modules/capture-core/components/DataEntries/converters/getConvertedRelatedStageEvent/getConvertedRelatedStageEvent.types.js create mode 100644 src/core_modules/capture-core/components/DataEntries/converters/getConvertedRelatedStageEvent/index.js create mode 100644 src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/deriveRelatedStageEvent.js rename src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/{getStageWithOpenAfterEnrollment.js => getPageToRedirectTo.js} (62%) delete mode 100644 src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/getConvertedRelatedStageEvent/getConvertedRelatedStageEvent.types.js delete mode 100644 src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/getConvertedRelatedStageEvent/index.js diff --git a/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentDataEntry.component.js b/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentDataEntry.component.js index 9a902bab07..f2773e6fbb 100644 --- a/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentDataEntry.component.js +++ b/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentDataEntry.component.js @@ -42,6 +42,7 @@ import { withAOCFieldBuilder, withDataEntryFields, } from '../../DataEntryDhis2Helpers'; +import type { RelatedStageRefPayload } from '../../WidgetRelatedStages'; const overrideMessagePropNames = { errorMessage: 'validationError', @@ -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 diff --git a/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/EnrollmentWithFirstStageDataEntry.container.js b/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/EnrollmentWithFirstStageDataEntry.container.js index b831b9f197..d2edc31e0d 100644 --- a/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/EnrollmentWithFirstStageDataEntry.container.js +++ b/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/EnrollmentWithFirstStageDataEntry.container.js @@ -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; @@ -19,10 +20,18 @@ export const EnrollmentWithFirstStageDataEntry = (props: Props) => { const dataEntrySections = useDataEntrySections(stageName, beforeSectionId); return ( - + <> + + + ); }; diff --git a/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/EnrollmentWithFirstStageDataEntry.types.js b/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/EnrollmentWithFirstStageDataEntry.types.js index 6c2a054867..bccf388600 100644 --- a/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/EnrollmentWithFirstStageDataEntry.types.js +++ b/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/EnrollmentWithFirstStageDataEntry.types.js @@ -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 }, }; diff --git a/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/EnrollmentRegistrationEntry.container.js b/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/EnrollmentRegistrationEntry.container.js index 92ff9612b7..acaff25f1b 100644 --- a/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/EnrollmentRegistrationEntry.container.js +++ b/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/EnrollmentRegistrationEntry.container.js @@ -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'; @@ -10,6 +10,7 @@ import { dataEntryHasChanges } from '../../DataEntry/common/dataEntryHasChanges' import { useBuildEnrollmentPayload, } from './hooks/useBuildEnrollmentPayload'; +import type { RelatedStageRefPayload } from '../../WidgetRelatedStages'; export const EnrollmentRegistrationEntry: ComponentType = ({ selectedScopeId, @@ -22,6 +23,7 @@ export const EnrollmentRegistrationEntry: ComponentType = ({ onCancel, ...passOnProps }) => { + const relatedStageRef = useRef(null); const { orgUnit, error } = useCoreOrgUnit(orgUnitId); const { ready, @@ -57,13 +59,15 @@ export const EnrollmentRegistrationEntry: ComponentType = ({ } const onSaveWithEnrollment = () => { - const teiWithEnrollment = buildTeiWithEnrollment(); - onSave(teiWithEnrollment); + const { teiWithEnrollment, formHasError, programStageIdLinkedEventToRedirectTo } = + buildTeiWithEnrollment(relatedStageRef); + !formHasError && onSave(teiWithEnrollment, programStageIdLinkedEventToRedirectTo); }; return ( , saveButtonText: (trackedEntityName: string) => string, firstStageMetaData?: ?{ stage: ?ProgramStage }, + relatedStageRef?: { current: ?RelatedStageRefPayload }, |}>; type ContainerProps = {| diff --git a/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/hooks/useBuildEnrollmentPayload.js b/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/hooks/useBuildEnrollmentPayload.js index 65be698174..808161d25e 100644 --- a/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/hooks/useBuildEnrollmentPayload.js +++ b/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/hooks/useBuildEnrollmentPayload.js @@ -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; @@ -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); @@ -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, @@ -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); @@ -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, }; }; diff --git a/src/core_modules/capture-core/components/DataEntries/common/TEIAndEnrollment/DuplicateCheckOnSave/types/duplicateCheckOnSave.types.js b/src/core_modules/capture-core/components/DataEntries/common/TEIAndEnrollment/DuplicateCheckOnSave/types/duplicateCheckOnSave.types.js index f55dc39c0e..43e78f4ec8 100644 --- a/src/core_modules/capture-core/components/DataEntries/common/TEIAndEnrollment/DuplicateCheckOnSave/types/duplicateCheckOnSave.types.js +++ b/src/core_modules/capture-core/components/DataEntries/common/TEIAndEnrollment/DuplicateCheckOnSave/types/duplicateCheckOnSave.types.js @@ -4,4 +4,5 @@ import type { TeiPayload } from '../../../../../Pages/common/TEIRelationshipsWid export type SaveForDuplicateCheck = ( teiWithEnrollment: EnrollmentPayload | TeiPayload, + programStageIdLinkedEventToRedirectTo?: string, ) => void; diff --git a/src/core_modules/capture-core/components/DataEntries/common/trackerEvent/withAskToCompleteEnrollment/withAskToCompleteEnrollment.js b/src/core_modules/capture-core/components/DataEntries/common/trackerEvent/withAskToCompleteEnrollment/withAskToCompleteEnrollment.js index b1d2cee393..f41e682ea4 100644 --- a/src/core_modules/capture-core/components/DataEntries/common/trackerEvent/withAskToCompleteEnrollment/withAskToCompleteEnrollment.js +++ b/src/core_modules/capture-core/components/DataEntries/common/trackerEvent/withAskToCompleteEnrollment/withAskToCompleteEnrollment.js @@ -6,8 +6,8 @@ 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, @@ -15,7 +15,7 @@ type Props = { isCompleted?: boolean, eventId?: ?string, formFoundation: RenderFoundation, - relatedStageRef: { current?: ?RelatedStageRefPayload }, + relatedStageRef?: { current: ?RelatedStageRefPayload }, onSaveAndCompleteEnrollment: ( eventId: string, dataEntryId: string, @@ -60,7 +60,11 @@ const getAskToCompleteEnrollment = (InnerComponent: ComponentType) => (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); diff --git a/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/getConvertedRelatedStageEvent/getConvertedRelatedStageEvent.js b/src/core_modules/capture-core/components/DataEntries/converters/getConvertedRelatedStageEvent/getConvertedRelatedStageEvent.js similarity index 89% rename from src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/getConvertedRelatedStageEvent/getConvertedRelatedStageEvent.js rename to src/core_modules/capture-core/components/DataEntries/converters/getConvertedRelatedStageEvent/getConvertedRelatedStageEvent.js index 07832fe83f..5653e4179c 100644 --- a/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/getConvertedRelatedStageEvent/getConvertedRelatedStageEvent.js +++ b/src/core_modules/capture-core/components/DataEntries/converters/getConvertedRelatedStageEvent/getConvertedRelatedStageEvent.js @@ -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'; @@ -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, @@ -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, diff --git a/src/core_modules/capture-core/components/DataEntries/converters/getConvertedRelatedStageEvent/getConvertedRelatedStageEvent.types.js b/src/core_modules/capture-core/components/DataEntries/converters/getConvertedRelatedStageEvent/getConvertedRelatedStageEvent.types.js new file mode 100644 index 0000000000..4c0e720f7c --- /dev/null +++ b/src/core_modules/capture-core/components/DataEntries/converters/getConvertedRelatedStageEvent/getConvertedRelatedStageEvent.types.js @@ -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, + relatedStageDataValues: RelatedStageDataValueStates, + programId: string, + teiId?: ?string, + currentProgramStageId: string, + enrollmentId?: string, + relatedStageType: RelatedStageRelationshipType, + clientRequestEvent: RequestEvent, +|} + diff --git a/src/core_modules/capture-core/components/DataEntries/converters/getConvertedRelatedStageEvent/index.js b/src/core_modules/capture-core/components/DataEntries/converters/getConvertedRelatedStageEvent/index.js new file mode 100644 index 0000000000..b6ecc885ea --- /dev/null +++ b/src/core_modules/capture-core/components/DataEntries/converters/getConvertedRelatedStageEvent/index.js @@ -0,0 +1,4 @@ +// @flow + +export { getConvertedRelatedStageEvent } from './getConvertedRelatedStageEvent'; +export type { RequestEvent, LinkedRequestEvent } from './getConvertedRelatedStageEvent.types'; diff --git a/src/core_modules/capture-core/components/DataEntries/converters/index.js b/src/core_modules/capture-core/components/DataEntries/converters/index.js index 29835b7f31..5bef2944fb 100644 --- a/src/core_modules/capture-core/components/DataEntries/converters/index.js +++ b/src/core_modules/capture-core/components/DataEntries/converters/index.js @@ -7,3 +7,6 @@ export { convertStatusOut, getConvertGeometryIn, } from './converters'; + +export { getConvertedRelatedStageEvent } from './getConvertedRelatedStageEvent'; +export type { RequestEvent, LinkedRequestEvent } from './getConvertedRelatedStageEvent'; diff --git a/src/core_modules/capture-core/components/DataEntries/index.js b/src/core_modules/capture-core/components/DataEntries/index.js index 6d73c67fbd..ff85f696cc 100644 --- a/src/core_modules/capture-core/components/DataEntries/index.js +++ b/src/core_modules/capture-core/components/DataEntries/index.js @@ -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'; diff --git a/src/core_modules/capture-core/components/Pages/EnrollmentAddEvent/EnrollmentAddEventPage.epics.js b/src/core_modules/capture-core/components/Pages/EnrollmentAddEvent/EnrollmentAddEventPage.epics.js index ea90f8b0fc..6b18a26e4c 100644 --- a/src/core_modules/capture-core/components/Pages/EnrollmentAddEvent/EnrollmentAddEventPage.epics.js +++ b/src/core_modules/capture-core/components/Pages/EnrollmentAddEvent/EnrollmentAddEventPage.epics.js @@ -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 = ({ @@ -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, diff --git a/src/core_modules/capture-core/components/Pages/EnrollmentAddEvent/EnrollmentAddEventPageDefault/EnrollmentAddEventPageDefault.container.js b/src/core_modules/capture-core/components/Pages/EnrollmentAddEvent/EnrollmentAddEventPageDefault/EnrollmentAddEventPageDefault.container.js index 88b39abb5d..a3ef25c3e6 100644 --- a/src/core_modules/capture-core/components/Pages/EnrollmentAddEvent/EnrollmentAddEventPageDefault/EnrollmentAddEventPageDefault.container.js +++ b/src/core_modules/capture-core/components/Pages/EnrollmentAddEvent/EnrollmentAddEventPageDefault/EnrollmentAddEventPageDefault.container.js @@ -11,7 +11,7 @@ import { buildUrlQueryString, useLocationQuery } from '../../../../utils/routing import { useProgramInfo } from '../../../../hooks/useProgramInfo'; import { useEnrollmentAddEventTopBar, EnrollmentAddEventTopBar } from '../TopBar'; import { deleteEnrollment, fetchEnrollments } from '../../Enrollment/EnrollmentPage.actions'; -import { actions as RelatedStageModes } from '../../../WidgetRelatedStages/constants'; +import { RelatedStageModes } from '../../../WidgetRelatedStages'; import { useWidgetDataFromStore } from '../hooks'; import { diff --git a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.actions.js b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.actions.js index 6beb555bc6..b8000eacf9 100644 --- a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.actions.js +++ b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.actions.js @@ -46,21 +46,26 @@ export const saveNewTrackedEntityInstance = (candidateForRegistration: any) => ); // with enrollment -export const startSavingNewTrackedEntityInstanceWithEnrollment = (enrollmentPayload: EnrollmentPayload, uid: string) => +export const startSavingNewTrackedEntityInstanceWithEnrollment = ( + enrollmentPayload: EnrollmentPayload, + uid: string, + programStageIdLinkedEventToRedirectTo?: string, +) => actionCreator(registrationFormActionTypes.NEW_TRACKED_ENTITY_INSTANCE_WITH_ENROLLMENT_SAVE_START)({ enrollmentPayload, uid, + programStageIdLinkedEventToRedirectTo, }); export const saveNewTrackedEntityInstanceWithEnrollment = ({ candidateForRegistration, - redirectTo, + pageToRedirectTo, uid, stageId, eventIndex, }: { candidateForRegistration: any, - redirectTo: string, + pageToRedirectTo: string, uid: string, stageId?: string, eventIndex: number, @@ -76,7 +81,7 @@ export const saveNewTrackedEntityInstanceWithEnrollment = ({ }, commit: { type: registrationFormActionTypes.NEW_TRACKED_ENTITY_INSTANCE_WITH_ENROLLMENT_SAVE_COMPLETED, - meta: { redirectTo, stageId, uid, eventIndex }, + meta: { pageToRedirectTo, stageId, uid, eventIndex }, }, rollback: { type: registrationFormActionTypes.NEW_TRACKED_ENTITY_INSTANCE_WITH_ENROLLMENT_SAVE_FAILED, diff --git a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.container.js b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.container.js index 8aeda04f1f..83398364d6 100644 --- a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.container.js +++ b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.container.js @@ -30,9 +30,15 @@ export const RegistrationDataEntry: ComponentType = ({ [dispatch]); const dispatchOnSaveWithEnrollment = useCallback( - (enrollmentPayload) => { + (enrollmentPayload, programStageIdLinkedEventToRedirectTo) => { const uid = uuid(); - dispatch(startSavingNewTrackedEntityInstanceWithEnrollment(enrollmentPayload, uid)); + dispatch( + startSavingNewTrackedEntityInstanceWithEnrollment( + enrollmentPayload, + uid, + programStageIdLinkedEventToRedirectTo, + ), + ); }, [dispatch]); diff --git a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.epics.js b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.epics.js index 4ee21bd46c..787922f840 100644 --- a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.epics.js +++ b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.epics.js @@ -14,7 +14,7 @@ import { } from '../../../../actions/navigateToEnrollmentOverview/navigateToEnrollmentOverview.actions'; import { buildUrlQueryString } from '../../../../utils/routing'; import { - getStageWithOpenAfterEnrollment, + getPageToRedirectTo, PAGES, } from './helpers'; import { cleanUpUid } from '../NewPage.actions'; @@ -54,15 +54,16 @@ export const startSavingNewTrackedEntityInstanceWithEnrollmentEpic: Epic = ( ofType(registrationFormActionTypes.NEW_TRACKED_ENTITY_INSTANCE_WITH_ENROLLMENT_SAVE_START), map((action) => { const { currentSelections: { programId } } = store.value; - const { enrollmentPayload, uid } = action.payload; + const { enrollmentPayload, uid, programStageIdLinkedEventToRedirectTo } = action.payload; const { stages, useFirstStageDuringRegistration } = getTrackerProgramThrowIfNotFound(programId); - const { stageWithOpenAfterEnrollment, redirectTo } = getStageWithOpenAfterEnrollment( + const { programStageIdToRedirectTo, pageToRedirectTo } = getPageToRedirectTo( stages, useFirstStageDuringRegistration, + programStageIdLinkedEventToRedirectTo, ); const eventIndex = enrollmentPayload.enrollments[0]?.events.findIndex( - eventsToBeCreated => eventsToBeCreated.programStage === stageWithOpenAfterEnrollment?.id, + eventsToBeCreated => eventsToBeCreated.programStage === programStageIdToRedirectTo, ); return saveNewTrackedEntityInstanceWithEnrollment({ @@ -71,9 +72,9 @@ export const startSavingNewTrackedEntityInstanceWithEnrollmentEpic: Epic = ( enrollmentPayload, ], }, - redirectTo, + pageToRedirectTo, eventIndex, - stageId: stageWithOpenAfterEnrollment?.id, + stageId: programStageIdToRedirectTo, uid, }); }), @@ -91,7 +92,7 @@ export const completeSavingNewTrackedEntityInstanceWithEnrollmentEpic = ( payload: { bundleReport: { typeReportMap }, }, - meta: { uid, redirectTo, stageId, eventIndex }, + meta: { uid, pageToRedirectTo, stageId, eventIndex }, } = action; const { currentSelections: { orgUnitId, programId }, @@ -106,9 +107,9 @@ export const completeSavingNewTrackedEntityInstanceWithEnrollmentEpic = ( return EMPTY; } - if (redirectTo === PAGES.enrollmentEventNew) { + if (pageToRedirectTo === PAGES.enrollmentEventNew) { history.push( - `/${redirectTo}?${buildUrlQueryString({ + `/${pageToRedirectTo}?${buildUrlQueryString({ programId, orgUnitId, teiId, @@ -119,9 +120,9 @@ export const completeSavingNewTrackedEntityInstanceWithEnrollmentEpic = ( return EMPTY; } - if (redirectTo === PAGES.enrollmentEventEdit) { + if (pageToRedirectTo === PAGES.enrollmentEventEdit) { history.push( - `/${redirectTo}?${buildUrlQueryString({ + `/${pageToRedirectTo}?${buildUrlQueryString({ eventId, orgUnitId, initMode: dataEntryKeys.EDIT, diff --git a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/deriveAutoGenerateEvents.js b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/deriveAutoGenerateEvents.js index 215b32f57b..e4e67bad96 100644 --- a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/deriveAutoGenerateEvents.js +++ b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/deriveAutoGenerateEvents.js @@ -3,9 +3,10 @@ import moment from 'moment'; import { dataElementTypes, ProgramStage } from '../../../../../metaData'; import { convertClientToServer } from '../../../../../converters'; import { convertCategoryOptionsToServer } from '../../../../../converters/clientToServer'; +import type { RequestEvent, LinkedRequestEvent } from '../../../../DataEntries'; -const ignoreAutoGenerateIfApplicable = (stage, firstStageDuringRegistrationEvent) => - !firstStageDuringRegistrationEvent || firstStageDuringRegistrationEvent.id !== stage.id; +const ignoreAutoGenerateIfApplicable = (stage, stageToSkip) => + !stageToSkip || stageToSkip.programStage !== stage.id; export const deriveAutoGenerateEvents = ({ stages, @@ -13,7 +14,8 @@ export const deriveAutoGenerateEvents = ({ occurredAt, programId, orgUnitId, - firstStageMetadata, + firstStageDuringRegistrationEvent, + relatedStageLinkedEvent, attributeCategoryOptions, serverMinorVersion, }: { @@ -22,7 +24,8 @@ export const deriveAutoGenerateEvents = ({ occurredAt: string, programId: string, orgUnitId: string, - firstStageMetadata: ?ProgramStage, + firstStageDuringRegistrationEvent: ?RequestEvent, + relatedStageLinkedEvent: ?LinkedRequestEvent, attributeCategoryOptions: { [categoryId: string]: string } | string, serverMinorVersion: number, }) => { @@ -33,7 +36,8 @@ export const deriveAutoGenerateEvents = ({ // $FlowFixMe[missing-annot] return [...stages.values()] .filter(({ autoGenerateEvent }) => autoGenerateEvent) - .filter(stage => ignoreAutoGenerateIfApplicable(stage, firstStageMetadata)) + .filter(stage => ignoreAutoGenerateIfApplicable(stage, firstStageDuringRegistrationEvent)) + .filter(stage => ignoreAutoGenerateIfApplicable(stage, relatedStageLinkedEvent)) .map( ({ id: programStage, diff --git a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/deriveFirstStageDuringRegistrationEvent.js b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/deriveFirstStageDuringRegistrationEvent.js index c0eac9af7b..2e59a8e195 100644 --- a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/deriveFirstStageDuringRegistrationEvent.js +++ b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/deriveFirstStageDuringRegistrationEvent.js @@ -1,5 +1,6 @@ // @flow import { pipe } from 'capture-core-utils'; +import { generateUID } from '../../../../../utils/uid/generateUID'; import { dataElementTypes, ProgramStage } from '../../../../../metaData'; import { convertFormToClient, convertClientToServer } from '../../../../../converters'; import { convertCategoryOptionsToServer } from '../../../../../converters/clientToServer'; @@ -37,6 +38,7 @@ export const deriveFirstStageDuringRegistrationEvent = ({ : {}; const event: any = { + event: generateUID(), status: convertStatusOut(stageComplete), geometry: standardGeoJson(stageGeometry), occurredAt: convertFn(stageOccurredAt, dataElementTypes.DATE), diff --git a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/deriveRelatedStageEvent.js b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/deriveRelatedStageEvent.js new file mode 100644 index 0000000000..d5210a555b --- /dev/null +++ b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/deriveRelatedStageEvent.js @@ -0,0 +1,73 @@ +// @flow +import { ProgramStage } from '../../../../../metaData'; +import { getConvertedRelatedStageEvent } from '../../../../DataEntries'; +import type { RequestEvent } from '../../../../DataEntries'; +import type { RelatedStageRefPayload } from '../../../../WidgetRelatedStages'; + +export const deriveRelatedStageEvent = ({ + clientRequestEvent, + relatedStageRef, + firstStageMetaData, + programId, + teiId, +}: { + clientRequestEvent: ?RequestEvent, + relatedStageRef?: { current: ?RelatedStageRefPayload}, + firstStageMetaData: ?{ stage: ?ProgramStage}, + programId: string, + teiId?: ?string; +}) => { + if (relatedStageRef?.current && relatedStageRef.current.eventHasLinkableStageRelationship()) { + const isValid = relatedStageRef.current.formIsValidOnSave(); + const currentProgramStageId = firstStageMetaData?.stage?.id; + if ( + !isValid || + !relatedStageRef.current?.getLinkedStageValues || + !currentProgramStageId || + !clientRequestEvent + ) { + return { + formHasError: true, + linkedEvent: null, + relationship: null, + linkMode: null, + }; + } + + const { selectedRelationshipType, relatedStageDataValues, linkMode } = + relatedStageRef.current.getLinkedStageValues(); + + if (!linkMode) { + return { + formHasError: false, + linkedEvent: null, + relationship: null, + linkMode: null, + }; + } + + const { linkedEvent, relationship } = getConvertedRelatedStageEvent({ + linkMode, + relatedStageDataValues, + clientRequestEvent, + + relatedStageType: selectedRelationshipType, + programId, + currentProgramStageId, + teiId, + }); + + return { + formHasError: false, + linkedEvent, + relationship, + linkMode, + }; + } + return { + formHasError: false, + linkedEvent: null, + relationship: null, + linkMode: null, + }; +}; diff --git a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/getStageWithOpenAfterEnrollment.js b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/getPageToRedirectTo.js similarity index 62% rename from src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/getStageWithOpenAfterEnrollment.js rename to src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/getPageToRedirectTo.js index 3688d29b4c..baee7b2d40 100644 --- a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/getStageWithOpenAfterEnrollment.js +++ b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/getPageToRedirectTo.js @@ -6,18 +6,27 @@ export const PAGES = { enrollmentEventEdit: 'enrollmentEventEdit', enrollmentDashboard: 'enrollmentDashboard', }; - -// an event can be created either during first stage registration or autogenerated -// when the event will be created redirect to enrollmentEventEdit -// when the event will not be created redirect to enrollmentEventNew -export const getStageWithOpenAfterEnrollment = ( +/** + * An event can be + * - created during first stage registration + * - created and linked during first stage registration + * - autogenerated + * When the event will be created redirect to enrollmentEventEdit + * When the event will not be created redirect to enrollmentEventNew + */ +export const getPageToRedirectTo = ( stages: Map, useFirstStageDuringRegistration: boolean, + programStageIdLinkedEventToRedirectTo?: string, ) => { const stagesArray = [...stages.values()]; const [firstStageWithOpenAfterEnrollment] = stagesArray.filter(({ openAfterEnrollment }) => openAfterEnrollment); - const redirectTo = (() => { + const pageToRedirectTo = (() => { + // event will be created and linked during first stage registration + if (programStageIdLinkedEventToRedirectTo) { + return PAGES.enrollmentEventEdit; + } if (firstStageWithOpenAfterEnrollment) { // event will be created during first stage registration if ( @@ -38,7 +47,7 @@ export const getStageWithOpenAfterEnrollment = ( })(); return { - stageWithOpenAfterEnrollment: firstStageWithOpenAfterEnrollment, - redirectTo, + programStageIdToRedirectTo: programStageIdLinkedEventToRedirectTo || firstStageWithOpenAfterEnrollment?.id, + pageToRedirectTo, }; }; diff --git a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/index.js b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/index.js index fe7c884a9e..8dddd26bf7 100644 --- a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/index.js +++ b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/index.js @@ -1,5 +1,6 @@ // @flow export { deriveFirstStageDuringRegistrationEvent } from './deriveFirstStageDuringRegistrationEvent'; +export { deriveRelatedStageEvent } from './deriveRelatedStageEvent'; export { deriveAutoGenerateEvents } from './deriveAutoGenerateEvents'; -export { getStageWithOpenAfterEnrollment, PAGES } from './getStageWithOpenAfterEnrollment'; +export { getPageToRedirectTo, PAGES } from './getPageToRedirectTo'; export { standardGeoJson } from './standardGeoJson'; diff --git a/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/Validated.container.js b/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/Validated.container.js index 61c961a257..5f29998ba1 100644 --- a/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/Validated.container.js +++ b/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/Validated.container.js @@ -14,11 +14,12 @@ import { setSaveEnrollmentEventInProgress, startCreateNewAfterCompleting, } from './validated.actions'; -import type { ContainerProps, RelatedStageRefPayload } from './validated.types'; +import type { ContainerProps } from './validated.types'; import type { RenderFoundation } from '../../../metaData'; import { addEventSaveTypes } from '../DataEntry/addEventSaveTypes'; import { useAvailableProgramStages } from '../../../hooks'; import { createServerData, useBuildNewEventPayload } from './useBuildNewEventPayload'; +import type { RelatedStageRefPayload } from '../../WidgetRelatedStages'; const SaveHandlerHOC = withSaveHandler()(ValidatedComponent); const AskToCreateNewHandlerHOC = withAskToCreateNew()(SaveHandlerHOC); diff --git a/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/getConvertedRelatedStageEvent/getConvertedRelatedStageEvent.types.js b/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/getConvertedRelatedStageEvent/getConvertedRelatedStageEvent.types.js deleted file mode 100644 index ef2ae727a7..0000000000 --- a/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/getConvertedRelatedStageEvent/getConvertedRelatedStageEvent.types.js +++ /dev/null @@ -1,29 +0,0 @@ -// @flow -import type { RelatedStageDataValueStates } from '../../../WidgetRelatedStages'; -import { actions as LinkModes } from '../../../WidgetRelatedStages/constants'; -import type { RequestEvent } from '../validated.types'; - -type RelatedStageType = {| - id: string, - fromConstraint: {| - programStage: { - id: string, - }, - |}, - toConstraint: { - programStage: { - id: string, - }, - } -|} - -export type ConvertedRelatedStageEventProps = {| - linkMode: $Keys, - relatedStageDataValues: RelatedStageDataValueStates, - programId: string, - teiId: string, - currentProgramStageId: string, - enrollmentId: string, - relatedStageType: RelatedStageType, - clientRequestEvent: RequestEvent, -|} diff --git a/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/getConvertedRelatedStageEvent/index.js b/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/getConvertedRelatedStageEvent/index.js deleted file mode 100644 index 1771ccf5b3..0000000000 --- a/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/getConvertedRelatedStageEvent/index.js +++ /dev/null @@ -1,2 +0,0 @@ -// @flow -export { getConvertedRelatedStageEvent } from './getConvertedRelatedStageEvent'; diff --git a/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/useBuildNewEventPayload.js b/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/useBuildNewEventPayload.js index e4269a87e1..791737876c 100644 --- a/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/useBuildNewEventPayload.js +++ b/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/useBuildNewEventPayload.js @@ -6,8 +6,9 @@ import { getAddEventEnrollmentServerData } from './getConvertedAddEvent'; import { convertDataEntryToClientValues } from '../../DataEntry/common/convertDataEntryToClientValues'; import { generateUID } from '../../../utils/uid/generateUID'; import { addEventSaveTypes } from '../DataEntry/addEventSaveTypes'; -import { getConvertedRelatedStageEvent } from './getConvertedRelatedStageEvent'; -import type { LinkedRequestEvent, RelatedStageRefPayload, RequestEvent } from './validated.types'; +import { getConvertedRelatedStageEvent } from '../../DataEntries'; +import type { LinkedRequestEvent, RequestEvent } from '../../DataEntries'; +import type { RelatedStageRefPayload } from '../../WidgetRelatedStages'; type Props = { dataEntryId: string, @@ -68,7 +69,7 @@ export const useBuildNewEventPayload = ({ const buildRelatedStageEventPayload = (clientRequestEvent, saveType: ?$Values, relatedStageRef) => { if ( - relatedStageRef.current + relatedStageRef?.current && relatedStageRef.current.eventHasLinkableStageRelationship() ) { const isValid = relatedStageRef.current.formIsValidOnSave(); @@ -121,7 +122,7 @@ export const useBuildNewEventPayload = ({ const buildNewEventPayload = ( saveType: ?$Values, - relatedStageRef: {| current: (?RelatedStageRefPayload) |}, + relatedStageRef?: {| current: (?RelatedStageRefPayload) |}, ) => { const requestEventId = generateUID(); diff --git a/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/validated.actions.js b/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/validated.actions.js index 0aa4c1292b..ddd8c75878 100644 --- a/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/validated.actions.js +++ b/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/validated.actions.js @@ -1,8 +1,8 @@ // @flow import { actionCreator } from '../../../actions/actions.utils'; import { effectMethods } from '../../../trackerOffline'; -import { actions as RelatedStageModes } from '../../WidgetRelatedStages/constants'; -import type { RequestEvent, LinkedRequestEvent } from './validated.types'; +import { RelatedStageModes } from '../../WidgetRelatedStages'; +import type { RequestEvent, LinkedRequestEvent } from '../../DataEntries'; import type { ExternalSaveHandler } from '../common.types'; export const newEventBatchActionTypes = { diff --git a/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/validated.types.js b/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/validated.types.js index e0af3da0ff..8d8e58fa74 100644 --- a/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/validated.types.js +++ b/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/validated.types.js @@ -7,37 +7,6 @@ import type { RulesExecutionDependenciesClientFormatted, } from '../common.types'; -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 RelatedStageRefPayload = {| - getLinkedStageValues: () => any, - eventHasLinkableStageRelationship: () => boolean, - formIsValidOnSave: () => boolean, -|} - export type ContainerProps = {| ...CommonValidatedProps, orgUnit: OrgUnit, diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.types.js b/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.types.js index 6f9288bb30..25284949c7 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.types.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.types.js @@ -1,5 +1,5 @@ // @flow -import { actions as LinkModes } from './constants'; +import { RelatedStageModes } from './index'; import type { Constraint } from './RelatedStagesActions/RelatedStagesActions.types'; export type RelationshipType = {| @@ -17,12 +17,12 @@ export type RelationshipType = {| export type Props = {| programId: string, - enrollmentId: string, + enrollmentId?: string, programStageId: string, currentStageLabel: string, |} export type RelatedStageDataValueStates = {| - linkMode: ?$Keys, + linkMode: ?$Keys, scheduledAt: string, orgUnit: ?{ path: string, @@ -31,3 +31,27 @@ export type RelatedStageDataValueStates = {| }, linkedEventId: ?string, |} + +export type RelatedStageRelationshipType = {| + id: string, + fromConstraint: {| + programStage: { + id: string, + }, + |}, + toConstraint: { + programStage: { + id: string, + }, + } +|} + +export type RelatedStageRefPayload = { + getLinkedStageValues: () => { + selectedRelationshipType: RelatedStageRelationshipType, + relatedStageDataValues: RelatedStageDataValueStates, + linkMode: ?$Keys, + }, + eventHasLinkableStageRelationship: () => boolean, + formIsValidOnSave: () => boolean, +}; diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/hooks/useRelatedStageEvents.js b/src/core_modules/capture-core/components/WidgetRelatedStages/hooks/useRelatedStageEvents.js index 8a5e8a7c17..bc9d59d53d 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/hooks/useRelatedStageEvents.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/hooks/useRelatedStageEvents.js @@ -7,7 +7,7 @@ import { handleAPIResponse, REQUESTED_ENTITIES } from '../../../utils/api'; type Props = { stageId: ?string, - enrollmentId: string, + enrollmentId?: string, scheduledLabel: string, occurredLabel: string, relationshipTypeId: ?string, @@ -41,7 +41,7 @@ export const useRelatedStageEvents = ({ ['availableRelatedStageEvents', stageId, enrollmentId, relationshipTypeId], query, { - enabled: !!stageId && enabled, + enabled: !!stageId && !!enrollmentId && enabled, cacheTime: 0, staleTime: 0, select: (response: any) => { diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/index.js b/src/core_modules/capture-core/components/WidgetRelatedStages/index.js index 1cba4d9943..082bdd7c93 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/index.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/index.js @@ -1,4 +1,9 @@ // @flow export { WidgetRelatedStages } from './WidgetRelatedStages.component'; -export type { RelatedStageDataValueStates } from './WidgetRelatedStages.types'; +export type { + RelatedStageDataValueStates, + RelatedStageRefPayload, + RelatedStageRelationshipType, +} from './WidgetRelatedStages.types'; +export { actions as RelatedStageModes } from './constants'; export { relatedStageWidgetIsValid } from './relatedStageEventIsValid/relatedStageEventIsValid'; diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/relatedStageEventIsValid/relatedStageEventIsValid.types.js b/src/core_modules/capture-core/components/WidgetRelatedStages/relatedStageEventIsValid/relatedStageEventIsValid.types.js index c97648e958..f3521b8cc5 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/relatedStageEventIsValid/relatedStageEventIsValid.types.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/relatedStageEventIsValid/relatedStageEventIsValid.types.js @@ -1,9 +1,9 @@ // @flow import type { ErrorMessagesForRelatedStages } from '../RelatedStagesActions'; -import { actions as LinkModes } from '../constants'; +import { RelatedStageModes } from '../index'; export type RelatedStageIsValidProps = {| - linkMode: ?$Keys, + linkMode: ?$Keys, scheduledAt: ?string, orgUnit: ?{ id: string, From 293c35908d08e979e46262e776b91e4b7b9bd719 Mon Sep 17 00:00:00 2001 From: Simona Domnisoru Date: Mon, 18 Nov 2024 08:33:29 +0100 Subject: [PATCH 02/28] feat: add enableLinkExistingEvent prop --- i18n/en.pot | 7 +++- .../getConvertedRelatedStageEvent.js | 3 +- .../Validated/Validated.component.js | 1 + .../RelatedStagesActions.component.js | 41 ++++++++++--------- .../RelatedStagesActions.types.js | 1 + .../WidgetRelatedStages.types.js | 1 + 6 files changed, 31 insertions(+), 23 deletions(-) diff --git a/i18n/en.pot b/i18n/en.pot index 370523a4b2..5e5420d6b5 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -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: 2024-10-25T18:18:11.518Z\n" -"PO-Revision-Date: 2024-10-25T18:18:11.518Z\n" +"POT-Creation-Date: 2024-11-18T07:23:16.381Z\n" +"PO-Revision-Date: 2024-11-18T07:23:16.381Z\n" msgid "Choose one or more dates..." msgstr "Choose one or more dates..." @@ -1415,6 +1415,9 @@ msgstr "Link to an existing {{linkableStageLabel}}" msgid "Choose a {{linkableStageLabel}}" msgstr "Choose a {{linkableStageLabel}}" +msgid "Actions - {{relationshipName}}" +msgstr "Actions - {{relationshipName}}" + msgid "{{ linkableStageLabel }} is not repeatable" msgstr "{{ linkableStageLabel }} is not repeatable" diff --git a/src/core_modules/capture-core/components/DataEntries/converters/getConvertedRelatedStageEvent/getConvertedRelatedStageEvent.js b/src/core_modules/capture-core/components/DataEntries/converters/getConvertedRelatedStageEvent/getConvertedRelatedStageEvent.js index 5653e4179c..e2299cf418 100644 --- a/src/core_modules/capture-core/components/DataEntries/converters/getConvertedRelatedStageEvent/getConvertedRelatedStageEvent.js +++ b/src/core_modules/capture-core/components/DataEntries/converters/getConvertedRelatedStageEvent/getConvertedRelatedStageEvent.js @@ -99,8 +99,7 @@ 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, diff --git a/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/Validated.component.js b/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/Validated.component.js index b3ba7c2d14..3af5f487f6 100644 --- a/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/Validated.component.js +++ b/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/Validated.component.js @@ -53,6 +53,7 @@ const ValidatedPlain = ({ programId={programId} programStageId={stage?.id} currentStageLabel={stage.name} + enableLinkExistingEvent /> { const { programStage } = useProgramStageInfo(constraint?.programStage?.id); @@ -73,7 +74,7 @@ export const RelatedStagesActionsPlain = ({ return (
{type === relatedStageStatus.LINKABLE && ( @@ -114,24 +115,26 @@ export const RelatedStagesActionsPlain = ({ value={RelatedStagesActionTypes.ENTER_DATA} /> - - updateSelectedAction(e.value)} - value={RelatedStagesActionTypes.LINK_EXISTING_RESPONSE} - /> - + {enableLinkExistingEvent && ( + + updateSelectedAction(e.value)} + value={RelatedStagesActionTypes.LINK_EXISTING_RESPONSE} + /> + + )} )} diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.types.js b/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.types.js index a99f2d0758..0e2084b0d9 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.types.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.types.js @@ -37,5 +37,6 @@ export type Props = {| addErrorMessage: (ErrorMessagesForRelatedStages) => void, setRelatedStagesDataValues: (() => Object) => void, currentStageLabel: string, + enableLinkExistingEvent?: boolean, ...CssClasses |} diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.types.js b/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.types.js index 25284949c7..b0b2a87978 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.types.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.types.js @@ -20,6 +20,7 @@ export type Props = {| enrollmentId?: string, programStageId: string, currentStageLabel: string, + enableLinkExistingEvent?: boolean, |} export type RelatedStageDataValueStates = {| linkMode: ?$Keys, From ddb3f0efd33abda13a58560884261e2914f613b0 Mon Sep 17 00:00:00 2001 From: Simona Domnisoru Date: Wed, 27 Nov 2024 15:55:34 +0100 Subject: [PATCH 03/28] chore: improve wording --- i18n/en.pot | 21 ++++++------------- ...llmentWithFirstStageDataEntry.container.js | 1 - .../Validated/Validated.component.js | 1 - .../EnterDataInOrgUnit/EnterData.component.js | 7 ++----- .../LinkToExisting.component.js | 2 +- .../RelatedStagesActions.component.js | 2 -- .../RelatedStagesActions.types.js | 1 - .../WidgetRelatedStages.component.js | 2 -- .../WidgetRelatedStages.types.js | 1 - .../WidgetRelatedStages/constants.js | 2 +- 10 files changed, 10 insertions(+), 30 deletions(-) diff --git a/i18n/en.pot b/i18n/en.pot index 9de1bab609..95b582c37a 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -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: 2024-11-07T11:57:59.094Z\n" -"PO-Revision-Date: 2024-11-07T11:57:59.094Z\n" +"POT-Creation-Date: 2024-11-26T11:09:35.292Z\n" +"PO-Revision-Date: 2024-11-26T11:09:35.292Z\n" msgid "Choose one or more dates..." msgstr "Choose one or more dates..." @@ -1424,8 +1424,8 @@ msgstr "{{trackedEntityTypeName}} profile" msgid "tracked entity instance" msgstr "tracked entity instance" -msgid "Link to an existing {{linkableStageLabel}}" -msgstr "Link to an existing {{linkableStageLabel}}" +msgid "Choose a {{linkableStageLabel}} event" +msgstr "Choose a {{linkableStageLabel}} event" msgid "Choose a {{linkableStageLabel}}" msgstr "Choose a {{linkableStageLabel}}" @@ -1445,8 +1445,8 @@ msgstr "Ambiguous relationships, contact system administrator" msgid "Enter details now" msgstr "Enter details now" -msgid "Link to an existing" -msgstr "Link to an existing" +msgid "Link to an existing event" +msgstr "Link to an existing event" msgid "Scheduled date" msgstr "Scheduled date" @@ -1681,15 +1681,6 @@ msgstr "Follow up" msgid "Choose a program stage to filter by {{label}}" msgstr "Choose a program stage to filter by {{label}}" -msgid "Active enrollments" -msgstr "Active enrollments" - -msgid "Completed enrollments" -msgstr "Completed enrollments" - -msgid "Cancelled enrollments" -msgstr "Cancelled enrollments" - msgid "" "Some enrollments were completed successfully, but there was an error while " "completing the rest. Please see the details below." diff --git a/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/EnrollmentWithFirstStageDataEntry.container.js b/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/EnrollmentWithFirstStageDataEntry.container.js index d2edc31e0d..69050725e9 100644 --- a/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/EnrollmentWithFirstStageDataEntry.container.js +++ b/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/EnrollmentWithFirstStageDataEntry.container.js @@ -30,7 +30,6 @@ export const EnrollmentWithFirstStageDataEntry = (props: Props) => { ref={relatedStageRef} programId={passOnProps.programId} programStageId={firstStageMetaData.stage?.id} - currentStageLabel={firstStageMetaData.stage?.name} /> ); diff --git a/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/Validated.component.js b/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/Validated.component.js index 3af5f487f6..104cbcf420 100644 --- a/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/Validated.component.js +++ b/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/Validated.component.js @@ -52,7 +52,6 @@ const ValidatedPlain = ({ enrollmentId={enrollmentId} programId={programId} programStageId={stage?.id} - currentStageLabel={stage.name} enableLinkExistingEvent /> Object) => void, - currentStageLabel: string, saveAttempted: boolean, errorMessages: ErrorMessagesForRelatedStages, ...CssClasses @@ -60,7 +59,6 @@ export const EnterDataInOrgUnitPlain = ({ setRelatedStagesDataValues, saveAttempted, errorMessages, - currentStageLabel, classes, }: Props) => { const onSelectOrgUnit = (e: { id: string, displayName: string, path: string }) => { @@ -98,11 +96,10 @@ export const EnterDataInOrgUnitPlain = ({ {i18n.t( relatedStagesDataValues?.orgUnit?.name - ? 'Enter {{linkableStageLabel}} details for {{orgUnitLabel}} in the next step after completing this {{currentStageLabel}}.' - : 'Select organisation unit and enter {{linkableStageLabel}} details in the next step after completing this {{currentStageLabel}}.', + ? 'Enter {{linkableStageLabel}} details for {{orgUnitLabel}} in the next step' + : 'Select organisation unit and enter {{linkableStageLabel}} details in the next step', { linkableStageLabel, - currentStageLabel, orgUnitLabel: relatedStagesDataValues?.orgUnit?.name, }, )} diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/LinkToExisting/LinkToExisting.component.js b/src/core_modules/capture-core/components/WidgetRelatedStages/LinkToExisting/LinkToExisting.component.js index a53c05eb3f..dbc8d442d5 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/LinkToExisting/LinkToExisting.component.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/LinkToExisting/LinkToExisting.component.js @@ -40,7 +40,7 @@ export const LinkToExistingPlain = ({ return (

- {i18n.t('Link to an existing {{linkableStageLabel}}', { + {i18n.t('Choose a {{linkableStageLabel}} event', { linkableStageLabel, })}

diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.component.js b/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.component.js index defb1f55cd..8be0e97898 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.component.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.component.js @@ -50,7 +50,6 @@ export const RelatedStagesActionsPlain = ({ relatedStagesDataValues, setRelatedStagesDataValues, constraint, - currentStageLabel, errorMessages, saveAttempted, enableLinkExistingEvent, @@ -170,7 +169,6 @@ export const RelatedStagesActionsPlain = ({ linkableStageLabel={programStage.stageForm.name} relatedStagesDataValues={relatedStagesDataValues} setRelatedStagesDataValues={setRelatedStagesDataValues} - currentStageLabel={currentStageLabel} saveAttempted={saveAttempted} errorMessages={errorMessages} /> diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.types.js b/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.types.js index 0e2084b0d9..dee2a54806 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.types.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.types.js @@ -36,7 +36,6 @@ export type Props = {| constraint: ?Constraint, addErrorMessage: (ErrorMessagesForRelatedStages) => void, setRelatedStagesDataValues: (() => Object) => void, - currentStageLabel: string, enableLinkExistingEvent?: boolean, ...CssClasses |} diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.component.js b/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.component.js index 722515d992..31460917ac 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.component.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.component.js @@ -14,7 +14,6 @@ const WidgetRelatedStagesPlain = ({ programId, enrollmentId, programStageId, - currentStageLabel, ...passOnProps }: Props, ref) => { const { currentRelatedStagesStatus, selectedRelationshipType, constraint } = useRelatedStages({ @@ -108,7 +107,6 @@ const WidgetRelatedStagesPlain = ({ saveAttempted={saveAttempted} errorMessages={errorMessages} constraint={constraint} - currentStageLabel={currentStageLabel} {...passOnProps} /> ); diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.types.js b/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.types.js index b0b2a87978..8995ddaf82 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.types.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.types.js @@ -19,7 +19,6 @@ export type Props = {| programId: string, enrollmentId?: string, programStageId: string, - currentStageLabel: string, enableLinkExistingEvent?: boolean, |} export type RelatedStageDataValueStates = {| diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/constants.js b/src/core_modules/capture-core/components/WidgetRelatedStages/constants.js index bb46ba298d..d8e57bb9a4 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/constants.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/constants.js @@ -15,5 +15,5 @@ export const actions = Object.freeze({ export const mainOptionTranslatedTexts = { [actions.SCHEDULE_IN_ORG]: i18n.t('Schedule'), [actions.ENTER_DATA]: i18n.t('Enter details now'), - [actions.LINK_EXISTING_RESPONSE]: i18n.t('Link to an existing'), + [actions.LINK_EXISTING_RESPONSE]: i18n.t('Link to an existing event'), }; From 3eb3c93af34a75a48007bcd975af3ae65f833e64 Mon Sep 17 00:00:00 2001 From: Simona Domnisoru Date: Mon, 2 Dec 2024 13:10:24 +0100 Subject: [PATCH 04/28] feat: show related stages widget when adding a new relationship --- i18n/en.pot | 13 +- ...llmentWithFirstStageDataEntry.container.js | 3 +- ...EnrollmentWithFirstStageDataEntry.types.js | 8 + .../EnrollmentRegistrationEntry.types.js | 8 + .../addRelationshipForNewSingleEvent.epics.js | 12 +- ...lmentRegistrationEntryWrapper.component.js | 7 +- .../DataEntryEnrollment.component.js | 9 + .../RegisterTei/RegisterTei.component.js | 8 +- .../RegisterTei/RegisterTei.types.js | 7 +- .../exposedHelpers/getRelationshipNewTei.js | 97 +------- .../NewRelationship/RegisterTei/index.js | 2 +- .../TeiRelationship.component.js | 13 +- .../ViewEventRelationships.epics.js | 7 +- .../DataEntryEnrollment.component.js | 9 + .../exposedHelpers/getRelationshipNewTei.js | 100 +-------- .../RegisterTei/index.js | 2 +- .../Validated/Validated.component.js | 1 - .../RelatedStagesActions.component.js | 212 +++++++++++++----- .../RelatedStagesActions.types.js | 9 +- .../WidgetRelatedStages.types.js | 8 +- .../hooks/useAddRelationship.js | 11 +- 21 files changed, 270 insertions(+), 276 deletions(-) diff --git a/i18n/en.pot b/i18n/en.pot index 95b582c37a..66fbc5b05b 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -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: 2024-11-26T11:09:35.292Z\n" -"PO-Revision-Date: 2024-11-26T11:09:35.292Z\n" +"POT-Creation-Date: 2024-12-02T11:16:49.543Z\n" +"PO-Revision-Date: 2024-12-02T11:16:49.543Z\n" msgid "Choose one or more dates..." msgstr "Choose one or more dates..." @@ -828,6 +828,9 @@ msgstr "Save {{trackedEntityTypeName}}" msgid "Save {{trackedEntityName}}" msgstr "Save {{trackedEntityName}}" +msgid "Enter details now is not available when creating a relationship" +msgstr "Enter details now is not available when creating a relationship" + msgid "Save new {{trackedEntityTypeName}} and link" msgstr "Save new {{trackedEntityTypeName}} and link" @@ -1430,15 +1433,15 @@ msgstr "Choose a {{linkableStageLabel}} event" msgid "Choose a {{linkableStageLabel}}" msgstr "Choose a {{linkableStageLabel}}" -msgid "Actions - {{relationshipName}}" -msgstr "Actions - {{relationshipName}}" - msgid "{{ linkableStageLabel }} is not repeatable" msgstr "{{ linkableStageLabel }} is not repeatable" msgid "{{ linkableStageLabel }} has no linkable events" msgstr "{{ linkableStageLabel }} has no linkable events" +msgid "Actions - {{relationshipName}}" +msgstr "Actions - {{relationshipName}}" + msgid "Ambiguous relationships, contact system administrator" msgstr "Ambiguous relationships, contact system administrator" diff --git a/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/EnrollmentWithFirstStageDataEntry.container.js b/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/EnrollmentWithFirstStageDataEntry.container.js index 69050725e9..bcb91759f0 100644 --- a/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/EnrollmentWithFirstStageDataEntry.container.js +++ b/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/EnrollmentWithFirstStageDataEntry.container.js @@ -10,7 +10,7 @@ const getSectionId = sectionId => (sectionId === Section.MAIN_SECTION_ID ? `${Section.MAIN_SECTION_ID}-stage` : sectionId); export const EnrollmentWithFirstStageDataEntry = (props: Props) => { - const { firstStageMetaData, relatedStageRef, ...passOnProps } = props; + const { firstStageMetaData, relatedStageRef, relatedStageModesOptions, ...passOnProps } = props; const { stage: { stageForm: firstStageFormFoundation, name: stageName }, } = firstStageMetaData; @@ -30,6 +30,7 @@ export const EnrollmentWithFirstStageDataEntry = (props: Props) => { ref={relatedStageRef} programId={passOnProps.programId} programStageId={firstStageMetaData.stage?.id} + actionTypesOptions={relatedStageModesOptions} /> ); diff --git a/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/EnrollmentWithFirstStageDataEntry.types.js b/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/EnrollmentWithFirstStageDataEntry.types.js index bccf388600..cbfbef6ea7 100644 --- a/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/EnrollmentWithFirstStageDataEntry.types.js +++ b/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/EnrollmentWithFirstStageDataEntry.types.js @@ -1,6 +1,7 @@ // @flow import type { ProgramStage, RenderFoundation } from '../../../../metaData'; import type { RelatedStageRefPayload } from '../../../WidgetRelatedStages'; +import { RelatedStageModes } from '../../../WidgetRelatedStages'; export type Props = { firstStageMetaData: { @@ -9,4 +10,11 @@ export type Props = { formFoundation: RenderFoundation, programId: string, relatedStageRef?: { current: ?RelatedStageRefPayload }, + relatedStageModesOptions?: { + [key: $Keys]: { + hidden?: boolean, + disabled?: boolean, + disabledMessage?: string + }, + }, }; diff --git a/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/EnrollmentRegistrationEntry.types.js b/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/EnrollmentRegistrationEntry.types.js index 11e594cd34..edbe849b06 100644 --- a/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/EnrollmentRegistrationEntry.types.js +++ b/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/EnrollmentRegistrationEntry.types.js @@ -8,6 +8,7 @@ import type { ExistingUniqueValueDialogActionsComponent } from '../withErrorMess import type { InputAttribute } from './hooks/useFormValues'; import { RenderFoundation, ProgramStage } from '../../../metaData'; import type { RelatedStageRefPayload } from '../../WidgetRelatedStages'; +import { RelatedStageModes } from '../../WidgetRelatedStages'; type TrackedEntityAttributes = Array<{ attribute: string, @@ -68,6 +69,13 @@ export type OwnProps = $ReadOnly<{| saveButtonText: (trackedEntityName: string) => string, firstStageMetaData?: ?{ stage: ?ProgramStage }, relatedStageRef?: { current: ?RelatedStageRefPayload }, + relatedStageModesOptions?: { + [key: $Keys]: { + hidden?: boolean, + disabled?: boolean, + disabledMessage?: string + }, + }, |}>; type ContainerProps = {| diff --git a/src/core_modules/capture-core/components/DataEntries/SingleEventRegistrationEntry/DataEntryWrapper/DataEntry/epics/addRelationshipForNewSingleEvent.epics.js b/src/core_modules/capture-core/components/DataEntries/SingleEventRegistrationEntry/DataEntryWrapper/DataEntry/epics/addRelationshipForNewSingleEvent.epics.js index 73107c5710..65fd3ae6ac 100644 --- a/src/core_modules/capture-core/components/DataEntries/SingleEventRegistrationEntry/DataEntryWrapper/DataEntry/epics/addRelationshipForNewSingleEvent.epics.js +++ b/src/core_modules/capture-core/components/DataEntries/SingleEventRegistrationEntry/DataEntryWrapper/DataEntry/epics/addRelationshipForNewSingleEvent.epics.js @@ -29,7 +29,7 @@ import { } from '../../../../../DataEntry/actions/dataEntry.actions'; import { getDataEntryKey } from '../../../../../DataEntry/common/getDataEntryKey'; import { convertClientRelationshipToServer } from '../../../../../../relationships/convertClientToServer'; -import { getRelationshipNewTei } from '../../../../../Pages/NewRelationship/RegisterTei'; +import { getRelationshipNewTeiName } from '../../../../../Pages/NewRelationship/RegisterTei'; const dataEntryId = 'singleEvent'; const itemId = 'newEvent'; @@ -47,13 +47,8 @@ export const addRelationshipForNewSingleEventEpic = (action$: InputObservable, s const state = store.value; const existingRelationships = state.dataEntriesRelationships[dataEntryKey] || []; const payload = action.payload; - const entity = payload.entity; + const toEntity = payload.entity; - const toEntity = entity.id ? entity : getRelationshipNewTei(entity.dataEntryId, entity.itemId, state); - const toEntityIsNew = !entity.id; - const newToEntity = toEntityIsNew ? { - dataEntryId: entity.dataEntryId, - } : null; const newRelationship = { clientId: uuid(), @@ -64,6 +59,7 @@ export const addRelationshipForNewSingleEventEpic = (action$: InputObservable, s }, to: { ...toEntity, + name: toEntity.name || getRelationshipNewTeiName(toEntity.dataEntryId, toEntity.itemId, state), type: payload.entityType, }, relationshipType: { ...payload.relationshipType }, @@ -87,7 +83,7 @@ export const addRelationshipForNewSingleEventEpic = (action$: InputObservable, s return batchActions([ recentlyAddedRelationship(newRelationship.clientId), - addRelationship(dataEntryId, itemId, newRelationship, newToEntity), + addRelationship(dataEntryId, itemId, newRelationship, toEntity), ], newEventNewRelationshipBatchActionTypes.ADD_RELATIONSHIP_BATCH); })); diff --git a/src/core_modules/capture-core/components/Pages/New/EnrollmentRegistrationEntryWrapper.component.js b/src/core_modules/capture-core/components/Pages/New/EnrollmentRegistrationEntryWrapper.component.js index e701d15e88..ac0b51288b 100644 --- a/src/core_modules/capture-core/components/Pages/New/EnrollmentRegistrationEntryWrapper.component.js +++ b/src/core_modules/capture-core/components/Pages/New/EnrollmentRegistrationEntryWrapper.component.js @@ -4,6 +4,7 @@ import { useDispatch } from 'react-redux'; import { cleanUpUid } from './NewPage.actions'; import { EnrollmentRegistrationEntry } from '../../DataEntries/EnrollmentRegistrationEntry/EnrollmentRegistrationEntry.container'; import type { OwnProps } from '../../DataEntries/EnrollmentRegistrationEntry/EnrollmentRegistrationEntry.types'; +import { RelatedStageModes } from '../../WidgetRelatedStages'; export const EnrollmentRegistrationEntryWrapper: ComponentType = (props) => { const dispatch = useDispatch(); @@ -11,5 +12,9 @@ export const EnrollmentRegistrationEntryWrapper: ComponentType = (prop dispatch(cleanUpUid()); }, [dispatch]); - return ; + const relatedStageModesOptions = { + [RelatedStageModes.LINK_EXISTING_RESPONSE]: { hidden: true }, + }; + + return ; }; diff --git a/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/DataEntry/Enrollment/DataEntryEnrollment.component.js b/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/DataEntry/Enrollment/DataEntryEnrollment.component.js index 252a34e61a..fb4dbb4837 100644 --- a/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/DataEntry/Enrollment/DataEntryEnrollment.component.js +++ b/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/DataEntry/Enrollment/DataEntryEnrollment.component.js @@ -6,6 +6,7 @@ import { DATA_ENTRY_ID } from '../../registerTei.const'; import enrollmentClasses from './enrollment.module.css'; import { EnrollmentRegistrationEntry } from '../../../../../DataEntries'; import type { Props } from './dataEntryEnrollment.types'; +import { RelatedStageModes } from '../../../../../WidgetRelatedStages'; const NewEnrollmentRelationshipPlain = ({ @@ -20,6 +21,13 @@ const NewEnrollmentRelationshipPlain = ExistingUniqueValueDialogActions, }: Props) => { const fieldOptions = { theme, fieldLabelMediaBasedClass: enrollmentClasses.fieldLabelMediaBased }; + const relatedStageModesOptions = { + [RelatedStageModes.ENTER_DATA]: { + disabled: true, + disabledMessage: i18n.t('Enter details now is not available when creating a relationship'), + }, + [RelatedStageModes.LINK_EXISTING_RESPONSE]: { hidden: true }, + }; return ( ); }; diff --git a/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/RegisterTei.component.js b/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/RegisterTei.component.js index 2c159e7aba..49df1bf07a 100644 --- a/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/RegisterTei.component.js +++ b/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/RegisterTei.component.js @@ -10,6 +10,10 @@ import { DataEntryWidgetOutput } from '../../../DataEntryWidgetOutput/DataEntryW import { ResultsPageSizeContext } from '../../shared-contexts'; import type { Props } from './RegisterTei.types'; import { withErrorMessageHandler } from '../../../../HOC'; +import type { EnrollmentPayload } from + '../../../DataEntries/EnrollmentRegistrationEntry/EnrollmentRegistrationEntry.types'; +import type { TeiPayload } from + '../../common/TEIRelationshipsWidget/RegisterTei/DataEntry/TrackedEntityInstance/dataEntryTrackedEntityInstance.types'; const getStyles = () => ({ container: { @@ -95,8 +99,8 @@ const RegisterTeiPlain = ({ ), [onLink]); - const handleSave = useCallback(() => { - onSave(itemId, dataEntryId); + const handleSave = useCallback((payload: EnrollmentPayload | TeiPayload) => { + onSave(itemId, dataEntryId, payload); }, [onSave, itemId, dataEntryId]); return ( diff --git a/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/RegisterTei.types.js b/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/RegisterTei.types.js index 759abe2e6f..2cce109427 100644 --- a/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/RegisterTei.types.js +++ b/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/RegisterTei.types.js @@ -1,4 +1,9 @@ // @flow +import type { EnrollmentPayload } from + '../../../DataEntries/EnrollmentRegistrationEntry/EnrollmentRegistrationEntry.types'; +import type { TeiPayload } from + '../../common/TEIRelationshipsWidget/RegisterTei/DataEntry/TrackedEntityInstance/dataEntryTrackedEntityInstance.types'; + type PropsFromRedux = {| dataEntryId: string, itemId: string, @@ -12,7 +17,7 @@ export type OwnProps = {| onLink: (teiId: string, values: Object) => void, onCancel: () => void, onGetUnsavedAttributeValues?: ?Function, - onSave: (itemId: string, dataEntryId: string) => void, + onSave: (itemId: string, dataEntryId: string, payload: EnrollmentPayload | TeiPayload) => void, |}; export type Props = {|...PropsFromRedux, ...OwnProps, ...CssClasses |} diff --git a/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/exposedHelpers/getRelationshipNewTei.js b/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/exposedHelpers/getRelationshipNewTei.js index 7eeef73978..e3e29b02f4 100644 --- a/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/exposedHelpers/getRelationshipNewTei.js +++ b/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/exposedHelpers/getRelationshipNewTei.js @@ -1,16 +1,11 @@ // @flow -import uuid from 'd2-utilizr/src/uuid'; -import moment from 'moment'; -import { getFormattedStringFromMomentUsingEuropeanGlyphs } from 'capture-core-utils/date'; -import { capitalizeFirstLetter } from 'capture-core-utils/string/capitalizeFirstLetter'; import { getTrackerProgramThrowIfNotFound, getTrackedEntityTypeThrowIfNotFound, type RenderFoundation, } from '../../../../../metaData'; -import { convertFormToClient, convertClientToServer } from '../../../../../converters'; +import { convertFormToClient } from '../../../../../converters'; import { getDisplayName } from '../../../../../trackedEntityInstances/getDisplayName'; -import { convertDataEntryValuesToClientValues } from '../../../../DataEntry/common/convertDataEntryValuesToClientValues'; import { getDataEntryKey } from '../../../../DataEntry/common/getDataEntryKey'; function getTrackerProgramMetadata(programId: string) { @@ -41,99 +36,15 @@ function getClientValuesForFormData(formValues: Object, formFoundation: RenderFo return clientValues; } -function getServerValuesForMainValues( - values: Object, - meta: Object, - formFoundation: RenderFoundation, -) { - const clientValues = convertDataEntryValuesToClientValues( - values, - meta, - formFoundation, - ) || {}; - - // potientally run this through a server to client converter for enrollment, the same way as for event - const serverValues = Object - .keys(clientValues) - .reduce((acc, key) => { - const value = clientValues[key]; - const type = meta[key].type; - acc[key] = convertClientToServer(value, type); - return acc; - }, {}); - - return serverValues; -} - -function getPossibleTetFeatureTypeKey(serverValues: Object) { - return Object - .keys(serverValues) - .find(key => key.startsWith('FEATURETYPE_')); -} - -function buildGeometryProp(key: string, serverValues: Object) { - if (!serverValues[key]) { - return undefined; - } - const type = capitalizeFirstLetter(key.replace('FEATURETYPE_', '').toLocaleLowerCase()); - return { - type, - coordinates: serverValues[key], - }; -} - -export function getRelationshipNewTei(dataEntryId: string, itemId: string, state: ReduxState) { +export function getRelationshipNewTeiName(dataEntryId: string, itemId: string, state: ReduxState) { const dataEntryKey = getDataEntryKey(dataEntryId, itemId); const formValues = state.formsValues[dataEntryKey]; - const { programId, orgUnit } = state.newRelationshipRegisterTei; + const { programId } = state.newRelationshipRegisterTei; const tetId = state.newRelationship.selectedRelationshipType.to.trackedEntityTypeId; const { attributes: metaDataAttributes, form: formFoundation, tetName } = getMetadata(programId, tetId); const clientValuesForFormData = getClientValuesForFormData(formValues, formFoundation); const displayName = getDisplayName(clientValuesForFormData, metaDataAttributes, tetName); - const serverValuesForFormValues = formFoundation.convertValues(clientValuesForFormData, convertClientToServer); - const serverValuesForMainValues = getServerValuesForMainValues( - state.dataEntriesFieldsValue[dataEntryKey], - state.dataEntriesFieldsMeta[dataEntryKey], - formFoundation, - ); - - // $FlowFixMe - const attributes = Object.keys(serverValuesForFormValues) - .map(key => ({ - attribute: key, - value: serverValuesForFormValues[key], - })); - - const enrollment = programId ? { - program: programId, - status: 'ACTIVE', - orgUnit: orgUnit.id, - occurredAt: getFormattedStringFromMomentUsingEuropeanGlyphs(moment()), - attributes, - ...serverValuesForMainValues, - } : null; - - const tetFeatureTypeKey = getPossibleTetFeatureTypeKey(serverValuesForFormValues); - let geometry; - if (tetFeatureTypeKey) { - geometry = buildGeometryProp(tetFeatureTypeKey, serverValuesForFormValues); - delete serverValuesForFormValues[tetFeatureTypeKey]; - } - - const teiPayload = { - // $FlowFixMe - attributes: !enrollment ? attributes : undefined, - orgUnit: orgUnit.id, - trackedEntityType: tetId, - geometry, - enrollments: enrollment ? [enrollment] : [], - }; - - return { - data: teiPayload, - name: displayName, - id: uuid(), - }; + return displayName; } diff --git a/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/index.js b/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/index.js index 7a77e9ec0d..b615fc1939 100644 --- a/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/index.js +++ b/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/index.js @@ -10,4 +10,4 @@ export { export { loadSearchGroupDuplicatesForReviewEpic, } from '../../../PossibleDuplicatesDialog/possibleDuplicatesDialog.epics'; -export { getRelationshipNewTei } from './exposedHelpers/getRelationshipNewTei'; +export { getRelationshipNewTeiName } from './exposedHelpers/getRelationshipNewTei'; diff --git a/src/core_modules/capture-core/components/Pages/NewRelationship/TeiRelationship/TeiRelationship.component.js b/src/core_modules/capture-core/components/Pages/NewRelationship/TeiRelationship/TeiRelationship.component.js index 863327b37f..87869ce23a 100644 --- a/src/core_modules/capture-core/components/Pages/NewRelationship/TeiRelationship/TeiRelationship.component.js +++ b/src/core_modules/capture-core/components/Pages/NewRelationship/TeiRelationship/TeiRelationship.component.js @@ -13,7 +13,10 @@ import type { TrackedEntityType } from '../../../../metaData'; import { findModes } from '../findModes'; import { getDisplayName } from '../../../../trackedEntityInstances/getDisplayName'; import { ResultsPageSizeContext } from '../../shared-contexts'; - +import type { EnrollmentPayload } from + '../../../DataEntries/EnrollmentRegistrationEntry/EnrollmentRegistrationEntry.types'; +import type { TeiPayload } from + '../../common/TEIRelationshipsWidget/RegisterTei/DataEntry/TrackedEntityInstance/dataEntryTrackedEntityInstance.types'; type Props = { findMode?: ?$Values, @@ -74,10 +77,16 @@ class TeiRelationshipPlain extends React.Component { }); } - handleAddRelationshipWithNewTei = (itemId: string, dataEntryId: string) => { + handleAddRelationshipWithNewTei = ( + itemId: string, + dataEntryId: string, + payload: EnrollmentPayload | TeiPayload, + ) => { this.props.onAddRelationship({ itemId, dataEntryId, + data: payload, + id: payload?.trackedEntity, }); } diff --git a/src/core_modules/capture-core/components/Pages/ViewEvent/Relationship/ViewEventRelationships.epics.js b/src/core_modules/capture-core/components/Pages/ViewEvent/Relationship/ViewEventRelationships.epics.js index d1d27a8cc1..c662eae579 100644 --- a/src/core_modules/capture-core/components/Pages/ViewEvent/Relationship/ViewEventRelationships.epics.js +++ b/src/core_modules/capture-core/components/Pages/ViewEvent/Relationship/ViewEventRelationships.epics.js @@ -27,7 +27,7 @@ import { convertClientRelationshipToServer, getRelationshipsForEvent, } from '../../../../relationships'; -import { getRelationshipNewTei } from '../../NewRelationship/RegisterTei'; +import { getRelationshipNewTeiName } from '../../NewRelationship/RegisterTei'; const relationshipKey = 'viewEvent'; @@ -78,9 +78,7 @@ export const addRelationshipForViewEventEpic = (action$: InputObservable, store: const eventId = state.viewEventPage.eventId; const existingRelationships = state.dataEntriesRelationships[relationshipKey] || []; const payload = action.payload; - const entity = payload.entity; - - const toEntity = entity.id ? entity : getRelationshipNewTei(entity.dataEntryId, entity.itemId, state); + const toEntity = payload.entity; const relationshipClientId = uuid(); const clientRelationship = { @@ -92,6 +90,7 @@ export const addRelationshipForViewEventEpic = (action$: InputObservable, store: }, to: { ...toEntity, + name: toEntity.name || getRelationshipNewTeiName(toEntity.dataEntryId, toEntity.itemId, state), type: payload.entityType, }, relationshipType: { ...payload.relationshipType }, diff --git a/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/Enrollment/DataEntryEnrollment.component.js b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/Enrollment/DataEntryEnrollment.component.js index cea2f79f8f..406eacdc5f 100644 --- a/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/Enrollment/DataEntryEnrollment.component.js +++ b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/Enrollment/DataEntryEnrollment.component.js @@ -6,6 +6,7 @@ import { DATA_ENTRY_ID } from '../../registerTei.const'; import enrollmentClasses from './enrollment.module.css'; import { EnrollmentRegistrationEntry } from '../../../../../../DataEntries'; import type { Props } from './dataEntryEnrollment.types'; +import { RelatedStageModes } from '../../../../../../WidgetRelatedStages'; const NewEnrollmentRelationshipPlain = ({ @@ -21,6 +22,13 @@ const NewEnrollmentRelationshipPlain = ExistingUniqueValueDialogActions, }: Props) => { const fieldOptions = { theme, fieldLabelMediaBasedClass: enrollmentClasses.fieldLabelMediaBased }; + const relatedStageModesOptions = { + [RelatedStageModes.ENTER_DATA]: { + disabled: true, + disabledMessage: i18n.t('Enter details now is not available when creating a relationship'), + }, + [RelatedStageModes.LINK_EXISTING_RESPONSE]: { hidden: true }, + }; return ( ); }; diff --git a/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/exposedHelpers/getRelationshipNewTei.js b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/exposedHelpers/getRelationshipNewTei.js index d5c862fcd6..9a264ea2a5 100644 --- a/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/exposedHelpers/getRelationshipNewTei.js +++ b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/exposedHelpers/getRelationshipNewTei.js @@ -1,18 +1,11 @@ // @flow -import uuid from 'd2-utilizr/src/uuid'; -import moment from 'moment'; -import { getFormattedStringFromMomentUsingEuropeanGlyphs } from 'capture-core-utils/date'; -import { capitalizeFirstLetter } from 'capture-core-utils/string/capitalizeFirstLetter'; import { getTrackerProgramThrowIfNotFound, getTrackedEntityTypeThrowIfNotFound, type RenderFoundation, } from '../../../../../../metaData'; -import { convertFormToClient, convertClientToServer } from '../../../../../../converters'; +import { convertFormToClient } from '../../../../../../converters'; import { getDisplayName } from '../../../../../../trackedEntityInstances/getDisplayName'; -import { - convertDataEntryValuesToClientValues, -} from '../../../../../DataEntry/common/convertDataEntryValuesToClientValues'; import { getDataEntryKey } from '../../../../../DataEntry/common/getDataEntryKey'; function getTrackerProgramMetadata(programId: string) { @@ -43,99 +36,16 @@ function getClientValuesForFormData(formValues: Object, formFoundation: RenderFo return clientValues; } -function getServerValuesForMainValues( - values: Object, - meta: Object, - formFoundation: RenderFoundation, -) { - const clientValues = convertDataEntryValuesToClientValues( - values, - meta, - formFoundation, - ) || {}; - - // potientally run this through a server to client converter for enrollment, the same way as for event - const serverValues = Object - .keys(clientValues) - .reduce((acc, key) => { - const value = clientValues[key]; - const type = meta[key].type; - acc[key] = convertClientToServer(value, type); - return acc; - }, {}); - - return serverValues; -} - -function getPossibleTetFeatureTypeKey(serverValues: Object) { - return Object - .keys(serverValues) - .find(key => key.startsWith('FEATURETYPE_')); -} - -function buildGeometryProp(key: string, serverValues: Object) { - if (!serverValues[key]) { - return undefined; - } - const type = capitalizeFirstLetter(key.replace('FEATURETYPE_', '').toLocaleLowerCase()); - return { - type, - coordinates: serverValues[key], - }; -} - -export function getRelationshipNewTei(dataEntryId: string, itemId: string, state: ReduxState) { +export function getRelationshipNewTeiName(dataEntryId: string, itemId: string, state: ReduxState) { const dataEntryKey = getDataEntryKey(dataEntryId, itemId); const formValues = state.formsValues[dataEntryKey]; - const { programId, orgUnit } = state.newRelationshipRegisterTei; + const { programId } = state.newRelationshipRegisterTei; const tetId = state.newRelationship.selectedRelationshipType.to.trackedEntityTypeId; const { attributes: metaDataAttributes, form: formFoundation, tetName } = getMetadata(programId, tetId); const clientValuesForFormData = getClientValuesForFormData(formValues, formFoundation); const displayName = getDisplayName(clientValuesForFormData, metaDataAttributes, tetName); - const serverValuesForFormValues = formFoundation.convertValues(clientValuesForFormData, convertClientToServer); - const serverValuesForMainValues = getServerValuesForMainValues( - state.dataEntriesFieldsValue[dataEntryKey], - state.dataEntriesFieldsMeta[dataEntryKey], - formFoundation, - ); - - // $FlowFixMe - const attributes = Object.keys(serverValuesForFormValues) - .map(key => ({ - attribute: key, - value: serverValuesForFormValues[key], - })); - - const enrollment = programId ? { - program: programId, - status: 'ACTIVE', - orgUnit: orgUnit.id, - occurredAt: getFormattedStringFromMomentUsingEuropeanGlyphs(moment()), - attributes, - ...serverValuesForMainValues, - } : null; - - const tetFeatureTypeKey = getPossibleTetFeatureTypeKey(serverValuesForFormValues); - let geometry; - if (tetFeatureTypeKey) { - geometry = buildGeometryProp(tetFeatureTypeKey, serverValuesForFormValues); - delete serverValuesForFormValues[tetFeatureTypeKey]; - } - - const teiPayload = { - // $FlowFixMe - attributes: !enrollment ? attributes : undefined, - orgUnit: orgUnit.id, - trackedEntityType: tetId, - geometry, - enrollments: enrollment ? [enrollment] : [], - }; - - return { - data: teiPayload, - name: displayName, - id: uuid(), - }; + // $FlowFixM + return displayName; } diff --git a/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/index.js b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/index.js index dea1f66c95..b8aa107828 100644 --- a/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/index.js +++ b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/index.js @@ -9,4 +9,4 @@ export { export { loadSearchGroupDuplicatesForReviewEpic, } from '../../../../PossibleDuplicatesDialog/possibleDuplicatesDialog.epics'; -export { getRelationshipNewTei } from './exposedHelpers/getRelationshipNewTei'; +export { getRelationshipNewTeiName } from './exposedHelpers/getRelationshipNewTei'; diff --git a/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/Validated.component.js b/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/Validated.component.js index 104cbcf420..663348da1b 100644 --- a/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/Validated.component.js +++ b/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/Validated.component.js @@ -52,7 +52,6 @@ const ValidatedPlain = ({ enrollmentId={enrollmentId} programId={programId} programStageId={stage?.id} - enableLinkExistingEvent /> ({ }, }); +const Schedule = ({ + actionTypesOptions, + linkableEvents, + selectedAction, + updateSelectedAction, + programStage, + canAddNewEventToStage, +}) => { + const { hidden, disabled, disabledMessage } = + (actionTypesOptions && actionTypesOptions[RelatedStagesActionTypes.SCHEDULE_IN_ORG]) || {}; + if (hidden) { + return null; + } + + const tooltipEnabled = disabled || !canAddNewEventToStage; + let tooltipContent = ''; + if (disabled) { + tooltipContent = disabledMessage; + } else if (!linkableEvents.length) { + tooltipContent = i18n.t('{{ linkableStageLabel }} is not repeatable', { + linkableStageLabel: programStage.stageForm.name, + interpolation: { escapeValue: false }, + }); + } + + return ( + + updateSelectedAction(e.value)} + value={RelatedStagesActionTypes.SCHEDULE_IN_ORG} + /> + + ); +}; + +const EnterData = ({ + actionTypesOptions, + linkableEvents, + selectedAction, + updateSelectedAction, + programStage, + canAddNewEventToStage, +}) => { + const { hidden, disabled, disabledMessage } = + (actionTypesOptions && actionTypesOptions[RelatedStagesActionTypes.ENTER_DATA]) || {}; + if (hidden) { + return null; + } + + const tooltipEnabled = disabled || !canAddNewEventToStage; + let tooltipContent = ''; + if (disabled) { + tooltipContent = disabledMessage; + } else if (!linkableEvents.length) { + tooltipContent = i18n.t('{{ linkableStageLabel }} is not repeatable', { + linkableStageLabel: programStage.stageForm.name, + interpolation: { escapeValue: false }, + }); + } + + return ( + + updateSelectedAction(e.value)} + value={RelatedStagesActionTypes.ENTER_DATA} + /> + + ); +}; + +const LinkExistingResponse = ({ + actionTypesOptions, + linkableEvents, + selectedAction, + updateSelectedAction, + programStage, +}) => { + const { hidden, disabled, disabledMessage } = + (actionTypesOptions && actionTypesOptions[RelatedStagesActionTypes.LINK_EXISTING_RESPONSE]) || {}; + if (hidden) { + return null; + } + + const tooltipEnabled = disabled || !linkableEvents.length; + let tooltipContent = ''; + if (disabled) { + tooltipContent = disabledMessage; + } else if (!linkableEvents.length) { + tooltipContent = i18n.t('{{ linkableStageLabel }} has no linkable events', { + linkableStageLabel: programStage.stageForm.name, + interpolation: { escapeValue: false }, + }); + } + + return ( + + updateSelectedAction(e.value)} + value={RelatedStagesActionTypes.LINK_EXISTING_RESPONSE} + /> + + ); +}; + export const RelatedStagesActionsPlain = ({ classes, type, @@ -52,7 +183,7 @@ export const RelatedStagesActionsPlain = ({ constraint, errorMessages, saveAttempted, - enableLinkExistingEvent, + actionTypesOptions, }: Props) => { const { programStage } = useProgramStageInfo(constraint?.programStage?.id); @@ -78,62 +209,29 @@ export const RelatedStagesActionsPlain = ({
{type === relatedStageStatus.LINKABLE && ( <> - - updateSelectedAction(e.value)} - value={RelatedStagesActionTypes.SCHEDULE_IN_ORG} - /> - - - updateSelectedAction(e.value)} - value={RelatedStagesActionTypes.ENTER_DATA} - /> - - {enableLinkExistingEvent && ( - - updateSelectedAction(e.value)} - value={RelatedStagesActionTypes.LINK_EXISTING_RESPONSE} - /> - - )} + + + )} diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.types.js b/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.types.js index dee2a54806..050c3350cb 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.types.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.types.js @@ -1,5 +1,6 @@ // @flow import type { RelatedStageDataValueStates } from '../WidgetRelatedStages.types'; +import { RelatedStageModes } from '../index'; export type Constraint = {| programStage: { @@ -36,6 +37,12 @@ export type Props = {| constraint: ?Constraint, addErrorMessage: (ErrorMessagesForRelatedStages) => void, setRelatedStagesDataValues: (() => Object) => void, - enableLinkExistingEvent?: boolean, + actionTypesOptions?: { + [key: $Keys]: { + hidden?: boolean, + disabled?: boolean, + disabledMessage?: string + }, + }, ...CssClasses |} diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.types.js b/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.types.js index 8995ddaf82..bb00b94c33 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.types.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.types.js @@ -19,7 +19,13 @@ export type Props = {| programId: string, enrollmentId?: string, programStageId: string, - enableLinkExistingEvent?: boolean, + actionTypesOptions?: { + [key: $Keys]: { + hidden?: boolean, + disabled?: boolean, + disabledMessage?: string + }, + }, |} export type RelatedStageDataValueStates = {| linkMode: ?$Keys, diff --git a/src/core_modules/capture-core/components/WidgetsRelationship/WidgetTrackedEntityRelationship/NewTrackedEntityRelationship/hooks/useAddRelationship.js b/src/core_modules/capture-core/components/WidgetsRelationship/WidgetTrackedEntityRelationship/NewTrackedEntityRelationship/hooks/useAddRelationship.js index b57a46e4f0..e0e2055d29 100644 --- a/src/core_modules/capture-core/components/WidgetsRelationship/WidgetTrackedEntityRelationship/NewTrackedEntityRelationship/hooks/useAddRelationship.js +++ b/src/core_modules/capture-core/components/WidgetsRelationship/WidgetTrackedEntityRelationship/NewTrackedEntityRelationship/hooks/useAddRelationship.js @@ -69,13 +69,20 @@ export const useAddRelationship = ({ teiId, onMutate, onSuccess }: Props) => { }); }, onSuccess: async (apiResponse, requestData) => { - const apiRelationshipId = apiResponse.bundleReport.typeReportMap.RELATIONSHIP.objectReports[0].uid; + const apiRelationshipIds = apiResponse.bundleReport.typeReportMap.RELATIONSHIP.objectReports.reduce( + (acc, report) => [...acc, report.uid], + [], + ); const currentRelationships = queryClient.getQueryData([ReactQueryAppNamespace, 'relationships', teiId]); const apiRelationships = handleAPIResponse(REQUESTED_ENTITIES.relationships, currentRelationships); if (apiRelationships.length === 0) return; const newRelationships = apiRelationships.map((relationship) => { - if (relationship.relationship === apiRelationshipId) { + if ( + apiRelationshipIds.find( + apiRelationshipId => apiRelationshipId === relationship.relationship, + ) + ) { return { ...relationship, pendingApiResponse: false, From 2a27f5773cd7887edee66cbcb2b96154adcfb5fa Mon Sep 17 00:00:00 2001 From: Simona Domnisoru Date: Mon, 9 Dec 2024 16:49:10 +0100 Subject: [PATCH 05/28] chore: typo --- .../RegistrationDataEntry/helpers/deriveRelatedStageEvent.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/deriveRelatedStageEvent.js b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/deriveRelatedStageEvent.js index d5210a555b..972cfe0486 100644 --- a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/deriveRelatedStageEvent.js +++ b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/deriveRelatedStageEvent.js @@ -15,7 +15,7 @@ export const deriveRelatedStageEvent = ({ relatedStageRef?: { current: ?RelatedStageRefPayload}, firstStageMetaData: ?{ stage: ?ProgramStage}, programId: string, - teiId?: ?string; + teiId?: ?string, }) => { if (relatedStageRef?.current && relatedStageRef.current.eventHasLinkableStageRelationship()) { const isValid = relatedStageRef.current.formIsValidOnSave(); @@ -50,7 +50,6 @@ export const deriveRelatedStageEvent = ({ linkMode, relatedStageDataValues, clientRequestEvent, - relatedStageType: selectedRelationshipType, programId, currentProgramStageId, From 9ecf999307c65b34ca6a7941b7bca3e31d621532 Mon Sep 17 00:00:00 2001 From: Simona Domnisoru Date: Wed, 11 Dec 2024 12:54:21 +0100 Subject: [PATCH 06/28] chore: fix PR comments --- .../Pages/NewRelationship/RegisterTei/RegisterTei.component.js | 3 +-- .../RegisterTei/DataEntry/TrackedEntityInstance/index.js | 1 + .../RelatedStagesActions/RelatedStagesActions.component.js | 2 +- .../WidgetRelatedStages/hooks/useRelatedStageEvents.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/RegisterTei.component.js b/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/RegisterTei.component.js index 49df1bf07a..fcbdf474a4 100644 --- a/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/RegisterTei.component.js +++ b/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/RegisterTei.component.js @@ -12,8 +12,7 @@ import type { Props } from './RegisterTei.types'; import { withErrorMessageHandler } from '../../../../HOC'; import type { EnrollmentPayload } from '../../../DataEntries/EnrollmentRegistrationEntry/EnrollmentRegistrationEntry.types'; -import type { TeiPayload } from - '../../common/TEIRelationshipsWidget/RegisterTei/DataEntry/TrackedEntityInstance/dataEntryTrackedEntityInstance.types'; +import type { TeiPayload } from '../../common/TEIRelationshipsWidget/RegisterTei/DataEntry/TrackedEntityInstance'; const getStyles = () => ({ container: { diff --git a/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/TrackedEntityInstance/index.js b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/TrackedEntityInstance/index.js index 5bb8975389..d60b166b08 100644 --- a/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/TrackedEntityInstance/index.js +++ b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/TrackedEntityInstance/index.js @@ -1,2 +1,3 @@ // @flow export { DataEntryTrackedEntityInstance } from './DataEntryTrackedEntityInstance'; +export type { TeiPayload } from './dataEntryTrackedEntityInstance.types'; diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.component.js b/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.component.js index 7e22c2407a..519cb05cea 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.component.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.component.js @@ -171,7 +171,7 @@ const LinkExistingResponse = ({ ); }; -export const RelatedStagesActionsPlain = ({ +const RelatedStagesActionsPlain = ({ classes, type, relationshipName, diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/hooks/useRelatedStageEvents.js b/src/core_modules/capture-core/components/WidgetRelatedStages/hooks/useRelatedStageEvents.js index bc9d59d53d..058ab02f41 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/hooks/useRelatedStageEvents.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/hooks/useRelatedStageEvents.js @@ -7,7 +7,7 @@ import { handleAPIResponse, REQUESTED_ENTITIES } from '../../../utils/api'; type Props = { stageId: ?string, - enrollmentId?: string, + enrollmentId: ?string, scheduledLabel: string, occurredLabel: string, relationshipTypeId: ?string, From 28e26b876d90cae198387b5a775fc9d9ff2fda2c Mon Sep 17 00:00:00 2001 From: Simona Domnisoru Date: Tue, 17 Dec 2024 09:22:45 +0100 Subject: [PATCH 07/28] chore: correct the variable name to match its format --- .../hooks/useBuildEnrollmentPayload.js | 2 +- .../getConvertedRelatedStageEvent.js | 12 ++++++------ .../getConvertedRelatedStageEvent.types.js | 2 +- .../helpers/deriveRelatedStageEvent.js | 8 ++++---- .../Validated/Validated.container.js | 8 ++++---- .../Validated/useBuildNewEventPayload.js | 16 ++++++++-------- 6 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/hooks/useBuildEnrollmentPayload.js b/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/hooks/useBuildEnrollmentPayload.js index 808161d25e..04d449f302 100644 --- a/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/hooks/useBuildEnrollmentPayload.js +++ b/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/hooks/useBuildEnrollmentPayload.js @@ -134,7 +134,7 @@ export const useBuildEnrollmentPayload = ({ }); const { formHasError, linkedEvent: relatedStageLinkedEvent, relationship, linkMode } = deriveRelatedStageEvent({ - clientRequestEvent: firstStageDuringRegistrationEvent, + serverRequestEvent: firstStageDuringRegistrationEvent, relatedStageRef, firstStageMetaData, programId, diff --git a/src/core_modules/capture-core/components/DataEntries/converters/getConvertedRelatedStageEvent/getConvertedRelatedStageEvent.js b/src/core_modules/capture-core/components/DataEntries/converters/getConvertedRelatedStageEvent/getConvertedRelatedStageEvent.js index e2299cf418..5603b4d6ff 100644 --- a/src/core_modules/capture-core/components/DataEntries/converters/getConvertedRelatedStageEvent/getConvertedRelatedStageEvent.js +++ b/src/core_modules/capture-core/components/DataEntries/converters/getConvertedRelatedStageEvent/getConvertedRelatedStageEvent.js @@ -17,7 +17,7 @@ const getEventDetailsByLinkMode = ({ programId, teiId, enrollmentId, - clientRequestEvent, + serverRequestEvent, }): { linkedEvent: ?LinkedRequestEvent, linkedEventId: ?string, @@ -68,7 +68,7 @@ const getEventDetailsByLinkMode = ({ return ({ linkedEvent: { ...baseEventDetails, - scheduledAt: clientRequestEvent.scheduledAt, + scheduledAt: serverRequestEvent.scheduledAt, orgUnit: convertFn(linkedEventOrgUnit, dataElementTypes.ORGANISATION_UNIT), }, linkedEventId: baseEventDetails.event, @@ -95,7 +95,7 @@ export const getConvertedRelatedStageEvent = ({ programId, teiId, currentProgramStageId, - clientRequestEvent, + serverRequestEvent, enrollmentId, relatedStageType, }: ConvertedRelatedStageEventProps) => { @@ -109,19 +109,19 @@ export const getConvertedRelatedStageEvent = ({ programId, teiId, enrollmentId, - clientRequestEvent, + serverRequestEvent, }); const relationship = linkedEventId && { relationshipType: relatedStageType.id, from: { event: { - event: requestEventIsFromConstraint ? clientRequestEvent.event : linkedEventId, + event: requestEventIsFromConstraint ? serverRequestEvent.event : linkedEventId, }, }, to: { event: { - event: requestEventIsFromConstraint ? linkedEventId : clientRequestEvent.event, + event: requestEventIsFromConstraint ? linkedEventId : serverRequestEvent.event, }, }, }; diff --git a/src/core_modules/capture-core/components/DataEntries/converters/getConvertedRelatedStageEvent/getConvertedRelatedStageEvent.types.js b/src/core_modules/capture-core/components/DataEntries/converters/getConvertedRelatedStageEvent/getConvertedRelatedStageEvent.types.js index 4c0e720f7c..7a40b70c2e 100644 --- a/src/core_modules/capture-core/components/DataEntries/converters/getConvertedRelatedStageEvent/getConvertedRelatedStageEvent.types.js +++ b/src/core_modules/capture-core/components/DataEntries/converters/getConvertedRelatedStageEvent/getConvertedRelatedStageEvent.types.js @@ -35,6 +35,6 @@ export type ConvertedRelatedStageEventProps = {| currentProgramStageId: string, enrollmentId?: string, relatedStageType: RelatedStageRelationshipType, - clientRequestEvent: RequestEvent, + serverRequestEvent: RequestEvent, |} diff --git a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/deriveRelatedStageEvent.js b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/deriveRelatedStageEvent.js index 972cfe0486..631e690645 100644 --- a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/deriveRelatedStageEvent.js +++ b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/deriveRelatedStageEvent.js @@ -5,13 +5,13 @@ import type { RequestEvent } from '../../../../DataEntries'; import type { RelatedStageRefPayload } from '../../../../WidgetRelatedStages'; export const deriveRelatedStageEvent = ({ - clientRequestEvent, + serverRequestEvent, relatedStageRef, firstStageMetaData, programId, teiId, }: { - clientRequestEvent: ?RequestEvent, + serverRequestEvent: ?RequestEvent, relatedStageRef?: { current: ?RelatedStageRefPayload}, firstStageMetaData: ?{ stage: ?ProgramStage}, programId: string, @@ -24,7 +24,7 @@ export const deriveRelatedStageEvent = ({ !isValid || !relatedStageRef.current?.getLinkedStageValues || !currentProgramStageId || - !clientRequestEvent + !serverRequestEvent ) { return { formHasError: true, @@ -49,7 +49,7 @@ export const deriveRelatedStageEvent = ({ const { linkedEvent, relationship } = getConvertedRelatedStageEvent({ linkMode, relatedStageDataValues, - clientRequestEvent, + serverRequestEvent, relatedStageType: selectedRelationshipType, programId, currentProgramStageId, diff --git a/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/Validated.container.js b/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/Validated.container.js index 5f29998ba1..a376001e37 100644 --- a/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/Validated.container.js +++ b/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/Validated.container.js @@ -84,7 +84,7 @@ export const Validated = ({ // Creating a promise to be able to stop navigation if related stages has an error window.scrollTo(0, 0); const { - clientRequestEvent, + serverRequestEvent, linkedEvent, relationship, linkMode, @@ -100,7 +100,7 @@ export const Validated = ({ } const serverData = createServerData({ - clientRequestEvent, + serverRequestEvent, linkedEvent, relationship, enrollment, @@ -108,7 +108,7 @@ export const Validated = ({ dispatch(batchActions([ requestSaveEvent({ - requestEvent: clientRequestEvent, + requestEvent: serverRequestEvent, linkedEvent, relationship, serverData, @@ -120,7 +120,7 @@ export const Validated = ({ // stores meta in redux to be used when navigating after save setSaveEnrollmentEventInProgress({ - requestEventId: clientRequestEvent?.event, + requestEventId: serverRequestEvent?.event, linkedEventId: linkedEvent?.event, linkedOrgUnitId: linkedEvent?.orgUnit, linkMode, diff --git a/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/useBuildNewEventPayload.js b/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/useBuildNewEventPayload.js index 791737876c..ab8c0f3683 100644 --- a/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/useBuildNewEventPayload.js +++ b/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/useBuildNewEventPayload.js @@ -22,18 +22,18 @@ type Props = { }; export const createServerData = ({ - clientRequestEvent, + serverRequestEvent, linkedEvent, relationship, enrollment, }: { - clientRequestEvent: RequestEvent, + serverRequestEvent: RequestEvent, linkedEvent: ?LinkedRequestEvent, relationship: ?Object, enrollment: ?Object, }) => { const relationships = relationship ? [relationship] : undefined; - const newEvents = linkedEvent ? [clientRequestEvent, linkedEvent] : [clientRequestEvent]; + const newEvents = linkedEvent ? [serverRequestEvent, linkedEvent] : [serverRequestEvent]; if (enrollment) { const updatedEnrollment = { ...enrollment, events: [...(enrollment.events || []), ...newEvents] }; @@ -67,7 +67,7 @@ export const useBuildNewEventPayload = ({ const notes = useSelector(({ dataEntriesNotes }) => dataEntriesNotes[dataEntryKey]); const { fromClientDate } = useTimeZoneConversion(); - const buildRelatedStageEventPayload = (clientRequestEvent, saveType: ?$Values, relatedStageRef) => { + const buildRelatedStageEventPayload = (serverRequestEvent, saveType: ?$Values, relatedStageRef) => { if ( relatedStageRef?.current && relatedStageRef.current.eventHasLinkableStageRelationship() @@ -97,7 +97,7 @@ export const useBuildNewEventPayload = ({ const { linkedEvent, relationship } = getConvertedRelatedStageEvent({ linkMode, relatedStageDataValues, - clientRequestEvent, + serverRequestEvent, relatedStageType: selectedRelationshipType, programId, currentProgramStageId: formFoundation.id, @@ -134,7 +134,7 @@ export const useBuildNewEventPayload = ({ ); const notesValues = notes ? notes.map(note => ({ value: note.value })) : []; - const clientRequestEvent = getAddEventEnrollmentServerData({ + const serverRequestEvent = getAddEventEnrollmentServerData({ formFoundation, formClientValues, eventId: requestEventId, @@ -154,11 +154,11 @@ export const useBuildNewEventPayload = ({ linkedEvent, relationship, linkMode, - } = buildRelatedStageEventPayload(clientRequestEvent, saveType, relatedStageRef); + } = buildRelatedStageEventPayload(serverRequestEvent, saveType, relatedStageRef); return { formHasError, - clientRequestEvent, + serverRequestEvent, linkedEvent, relationship, linkMode, From d137e8e24e88c512dd0989a545548e2f463806a7 Mon Sep 17 00:00:00 2001 From: Simona Domnisoru Date: Tue, 17 Dec 2024 09:32:27 +0100 Subject: [PATCH 08/28] chore: move helpers files to DataEntries/EnrollmentRegistrationEntry --- .../helpers/deriveAutoGenerateEvents.js | 8 ++++---- .../helpers/deriveFirstStageDuringRegistrationEvent.js | 10 +++++----- .../helpers/deriveRelatedStageEvent.js | 8 ++++---- .../EnrollmentRegistrationEntry/helpers/index.js | 5 +++++ .../helpers/standardGeoJson.js | 0 .../hooks/useBuildEnrollmentPayload.js | 2 +- .../Pages/New/RegistrationDataEntry/helpers/index.js | 4 ---- 7 files changed, 19 insertions(+), 18 deletions(-) rename src/core_modules/capture-core/components/{Pages/New/RegistrationDataEntry => DataEntries/EnrollmentRegistrationEntry}/helpers/deriveAutoGenerateEvents.js (93%) rename src/core_modules/capture-core/components/{Pages/New/RegistrationDataEntry => DataEntries/EnrollmentRegistrationEntry}/helpers/deriveFirstStageDuringRegistrationEvent.js (86%) rename src/core_modules/capture-core/components/{Pages/New/RegistrationDataEntry => DataEntries/EnrollmentRegistrationEntry}/helpers/deriveRelatedStageEvent.js (87%) create mode 100644 src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/helpers/index.js rename src/core_modules/capture-core/components/{Pages/New/RegistrationDataEntry => DataEntries/EnrollmentRegistrationEntry}/helpers/standardGeoJson.js (100%) diff --git a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/deriveAutoGenerateEvents.js b/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/helpers/deriveAutoGenerateEvents.js similarity index 93% rename from src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/deriveAutoGenerateEvents.js rename to src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/helpers/deriveAutoGenerateEvents.js index e4e67bad96..c4eb7ddc85 100644 --- a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/deriveAutoGenerateEvents.js +++ b/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/helpers/deriveAutoGenerateEvents.js @@ -1,9 +1,9 @@ // @flow import moment from 'moment'; -import { dataElementTypes, ProgramStage } from '../../../../../metaData'; -import { convertClientToServer } from '../../../../../converters'; -import { convertCategoryOptionsToServer } from '../../../../../converters/clientToServer'; -import type { RequestEvent, LinkedRequestEvent } from '../../../../DataEntries'; +import { dataElementTypes, ProgramStage } from '../../../../metaData'; +import { convertClientToServer } from '../../../../converters'; +import { convertCategoryOptionsToServer } from '../../../../converters/clientToServer'; +import type { RequestEvent, LinkedRequestEvent } from '../../../DataEntries'; const ignoreAutoGenerateIfApplicable = (stage, stageToSkip) => !stageToSkip || stageToSkip.programStage !== stage.id; diff --git a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/deriveFirstStageDuringRegistrationEvent.js b/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/helpers/deriveFirstStageDuringRegistrationEvent.js similarity index 86% rename from src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/deriveFirstStageDuringRegistrationEvent.js rename to src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/helpers/deriveFirstStageDuringRegistrationEvent.js index 2e59a8e195..38a5697e61 100644 --- a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/deriveFirstStageDuringRegistrationEvent.js +++ b/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/helpers/deriveFirstStageDuringRegistrationEvent.js @@ -1,10 +1,10 @@ // @flow import { pipe } from 'capture-core-utils'; -import { generateUID } from '../../../../../utils/uid/generateUID'; -import { dataElementTypes, ProgramStage } from '../../../../../metaData'; -import { convertFormToClient, convertClientToServer } from '../../../../../converters'; -import { convertCategoryOptionsToServer } from '../../../../../converters/clientToServer'; -import { convertStatusOut } from '../../../../DataEntries'; +import { generateUID } from '../../../../utils/uid/generateUID'; +import { dataElementTypes, ProgramStage } from '../../../../metaData'; +import { convertFormToClient, convertClientToServer } from '../../../../converters'; +import { convertCategoryOptionsToServer } from '../../../../converters/clientToServer'; +import { convertStatusOut } from '../../../DataEntries'; import { standardGeoJson } from './standardGeoJson'; const convertFn = pipe(convertFormToClient, convertClientToServer); diff --git a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/deriveRelatedStageEvent.js b/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/helpers/deriveRelatedStageEvent.js similarity index 87% rename from src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/deriveRelatedStageEvent.js rename to src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/helpers/deriveRelatedStageEvent.js index 631e690645..08d779bd3a 100644 --- a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/deriveRelatedStageEvent.js +++ b/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/helpers/deriveRelatedStageEvent.js @@ -1,8 +1,8 @@ // @flow -import { ProgramStage } from '../../../../../metaData'; -import { getConvertedRelatedStageEvent } from '../../../../DataEntries'; -import type { RequestEvent } from '../../../../DataEntries'; -import type { RelatedStageRefPayload } from '../../../../WidgetRelatedStages'; +import { ProgramStage } from '../../../../metaData'; +import { getConvertedRelatedStageEvent } from '../../../DataEntries'; +import type { RequestEvent } from '../../../DataEntries'; +import type { RelatedStageRefPayload } from '../../../WidgetRelatedStages'; export const deriveRelatedStageEvent = ({ serverRequestEvent, diff --git a/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/helpers/index.js b/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/helpers/index.js new file mode 100644 index 0000000000..b0c3744c1f --- /dev/null +++ b/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/helpers/index.js @@ -0,0 +1,5 @@ +// @flow +export { deriveFirstStageDuringRegistrationEvent } from './deriveFirstStageDuringRegistrationEvent'; +export { deriveRelatedStageEvent } from './deriveRelatedStageEvent'; +export { deriveAutoGenerateEvents } from './deriveAutoGenerateEvents'; +export { standardGeoJson } from './standardGeoJson'; diff --git a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/standardGeoJson.js b/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/helpers/standardGeoJson.js similarity index 100% rename from src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/standardGeoJson.js rename to src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/helpers/standardGeoJson.js diff --git a/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/hooks/useBuildEnrollmentPayload.js b/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/hooks/useBuildEnrollmentPayload.js index 04d449f302..20fc060c42 100644 --- a/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/hooks/useBuildEnrollmentPayload.js +++ b/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/hooks/useBuildEnrollmentPayload.js @@ -25,7 +25,7 @@ import { deriveAutoGenerateEvents, deriveFirstStageDuringRegistrationEvent, deriveRelatedStageEvent, -} from '../../../Pages/New/RegistrationDataEntry/helpers'; +} from '../helpers'; import type { EnrollmentPayload } from '../EnrollmentRegistrationEntry.types'; import { geometryType, getPossibleTetFeatureTypeKey, buildGeometryProp } from '../../common/TEIAndEnrollment/geometry'; import { RelatedStageModes } from '../../../WidgetRelatedStages'; diff --git a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/index.js b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/index.js index 8dddd26bf7..a82f1974d9 100644 --- a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/index.js +++ b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/index.js @@ -1,6 +1,2 @@ // @flow -export { deriveFirstStageDuringRegistrationEvent } from './deriveFirstStageDuringRegistrationEvent'; -export { deriveRelatedStageEvent } from './deriveRelatedStageEvent'; -export { deriveAutoGenerateEvents } from './deriveAutoGenerateEvents'; export { getPageToRedirectTo, PAGES } from './getPageToRedirectTo'; -export { standardGeoJson } from './standardGeoJson'; From 0dc2f01bbfcd6db9a2245ab902819fd9bcf45b4c Mon Sep 17 00:00:00 2001 From: Simona Domnisoru Date: Tue, 17 Dec 2024 09:49:21 +0100 Subject: [PATCH 09/28] chore: use action naming throughout the code --- ...llmentWithFirstStageDataEntry.container.js | 4 +- ...EnrollmentWithFirstStageDataEntry.types.js | 6 +- .../EnrollmentRegistrationEntry.types.js | 6 +- .../hooks/useBuildEnrollmentPayload.js | 4 +- .../withAskToCompleteEnrollment.js | 4 +- .../getConvertedRelatedStageEvent.js | 8 +-- .../getConvertedRelatedStageEvent.types.js | 4 +- .../EnrollmentAddEventPage.epics.js | 4 +- ...EnrollmentAddEventPageDefault.container.js | 4 +- ...lmentRegistrationEntryWrapper.component.js | 8 +-- .../DataEntryEnrollment.component.js | 10 ++-- .../DataEntryEnrollment.component.js | 10 ++-- .../Validated/validated.actions.js | 6 +- .../RelatedStagesActions.component.js | 60 +++++++++---------- .../RelatedStagesActions.types.js | 6 +- .../WidgetRelatedStages.types.js | 10 ++-- .../WidgetRelatedStages/constants.js | 8 +-- .../components/WidgetRelatedStages/index.js | 2 +- .../ValidationFunctions.js | 8 +-- .../relatedStageEventIsValid.types.js | 4 +- 20 files changed, 88 insertions(+), 88 deletions(-) diff --git a/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/EnrollmentWithFirstStageDataEntry.container.js b/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/EnrollmentWithFirstStageDataEntry.container.js index bcb91759f0..420c663669 100644 --- a/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/EnrollmentWithFirstStageDataEntry.container.js +++ b/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/EnrollmentWithFirstStageDataEntry.container.js @@ -10,7 +10,7 @@ const getSectionId = sectionId => (sectionId === Section.MAIN_SECTION_ID ? `${Section.MAIN_SECTION_ID}-stage` : sectionId); export const EnrollmentWithFirstStageDataEntry = (props: Props) => { - const { firstStageMetaData, relatedStageRef, relatedStageModesOptions, ...passOnProps } = props; + const { firstStageMetaData, relatedStageRef, relatedStageActionsOptions, ...passOnProps } = props; const { stage: { stageForm: firstStageFormFoundation, name: stageName }, } = firstStageMetaData; @@ -30,7 +30,7 @@ export const EnrollmentWithFirstStageDataEntry = (props: Props) => { ref={relatedStageRef} programId={passOnProps.programId} programStageId={firstStageMetaData.stage?.id} - actionTypesOptions={relatedStageModesOptions} + actionsOptions={relatedStageActionsOptions} /> ); diff --git a/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/EnrollmentWithFirstStageDataEntry.types.js b/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/EnrollmentWithFirstStageDataEntry.types.js index cbfbef6ea7..135a01646b 100644 --- a/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/EnrollmentWithFirstStageDataEntry.types.js +++ b/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/EnrollmentWithFirstStageDataEntry.types.js @@ -1,7 +1,7 @@ // @flow import type { ProgramStage, RenderFoundation } from '../../../../metaData'; import type { RelatedStageRefPayload } from '../../../WidgetRelatedStages'; -import { RelatedStageModes } from '../../../WidgetRelatedStages'; +import { relatedStageActions } from '../../../WidgetRelatedStages'; export type Props = { firstStageMetaData: { @@ -10,8 +10,8 @@ export type Props = { formFoundation: RenderFoundation, programId: string, relatedStageRef?: { current: ?RelatedStageRefPayload }, - relatedStageModesOptions?: { - [key: $Keys]: { + relatedStageActionsOptions?: { + [key: $Keys]: { hidden?: boolean, disabled?: boolean, disabledMessage?: string diff --git a/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/EnrollmentRegistrationEntry.types.js b/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/EnrollmentRegistrationEntry.types.js index edbe849b06..6eff575ea5 100644 --- a/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/EnrollmentRegistrationEntry.types.js +++ b/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/EnrollmentRegistrationEntry.types.js @@ -8,7 +8,7 @@ import type { ExistingUniqueValueDialogActionsComponent } from '../withErrorMess import type { InputAttribute } from './hooks/useFormValues'; import { RenderFoundation, ProgramStage } from '../../../metaData'; import type { RelatedStageRefPayload } from '../../WidgetRelatedStages'; -import { RelatedStageModes } from '../../WidgetRelatedStages'; +import { relatedStageActions } from '../../WidgetRelatedStages'; type TrackedEntityAttributes = Array<{ attribute: string, @@ -69,8 +69,8 @@ export type OwnProps = $ReadOnly<{| saveButtonText: (trackedEntityName: string) => string, firstStageMetaData?: ?{ stage: ?ProgramStage }, relatedStageRef?: { current: ?RelatedStageRefPayload }, - relatedStageModesOptions?: { - [key: $Keys]: { + relatedStageActionsOptions?: { + [key: $Keys]: { hidden?: boolean, disabled?: boolean, disabledMessage?: string diff --git a/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/hooks/useBuildEnrollmentPayload.js b/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/hooks/useBuildEnrollmentPayload.js index 20fc060c42..6a5566696a 100644 --- a/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/hooks/useBuildEnrollmentPayload.js +++ b/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/hooks/useBuildEnrollmentPayload.js @@ -28,7 +28,7 @@ import { } from '../helpers'; import type { EnrollmentPayload } from '../EnrollmentRegistrationEntry.types'; import { geometryType, getPossibleTetFeatureTypeKey, buildGeometryProp } from '../../common/TEIAndEnrollment/geometry'; -import { RelatedStageModes } from '../../../WidgetRelatedStages'; +import { relatedStageActions } from '../../../WidgetRelatedStages'; import type { RelatedStageRefPayload } from '../../../WidgetRelatedStages'; type DataEntryReduxConverterProps = { @@ -184,7 +184,7 @@ export const useBuildEnrollmentPayload = ({ }, formHasError, programStageIdLinkedEventToRedirectTo: - relatedStageLinkedEvent && linkMode === RelatedStageModes.ENTER_DATA + relatedStageLinkedEvent && linkMode === relatedStageActions.ENTER_DATA ? relatedStageLinkedEvent.programStage : undefined, }; diff --git a/src/core_modules/capture-core/components/DataEntries/common/trackerEvent/withAskToCompleteEnrollment/withAskToCompleteEnrollment.js b/src/core_modules/capture-core/components/DataEntries/common/trackerEvent/withAskToCompleteEnrollment/withAskToCompleteEnrollment.js index f41e682ea4..c19649a0d7 100644 --- a/src/core_modules/capture-core/components/DataEntries/common/trackerEvent/withAskToCompleteEnrollment/withAskToCompleteEnrollment.js +++ b/src/core_modules/capture-core/components/DataEntries/common/trackerEvent/withAskToCompleteEnrollment/withAskToCompleteEnrollment.js @@ -6,7 +6,7 @@ import { CompleteModal } from './CompleteModal'; import { statusTypes as eventStatuses } from '../../../../../events/statusTypes'; import { type RenderFoundation } from '../../../../../metaData'; import { addEventSaveTypes } from '../../../../WidgetEnrollmentEventNew/DataEntry/addEventSaveTypes'; -import { RelatedStageModes } from '../../../../WidgetRelatedStages'; +import { relatedStageActions } from '../../../../WidgetRelatedStages'; import type { RelatedStageRefPayload } from '../../../../WidgetRelatedStages'; type Props = { @@ -63,7 +63,7 @@ const getAskToCompleteEnrollment = (InnerComponent: ComponentType) => (prop if ( askCompleteEnrollmentOnEventComplete && (isCompleted || saveType === addEventSaveTypes.COMPLETE) && - linkMode !== RelatedStageModes.ENTER_DATA + linkMode !== relatedStageActions.ENTER_DATA ) { setOpenCompleteModal(true); } else { diff --git a/src/core_modules/capture-core/components/DataEntries/converters/getConvertedRelatedStageEvent/getConvertedRelatedStageEvent.js b/src/core_modules/capture-core/components/DataEntries/converters/getConvertedRelatedStageEvent/getConvertedRelatedStageEvent.js index 5603b4d6ff..ad19d0ec9e 100644 --- a/src/core_modules/capture-core/components/DataEntries/converters/getConvertedRelatedStageEvent/getConvertedRelatedStageEvent.js +++ b/src/core_modules/capture-core/components/DataEntries/converters/getConvertedRelatedStageEvent/getConvertedRelatedStageEvent.js @@ -1,7 +1,7 @@ // @flow import log from 'loglevel'; import { generateUID } from '../../../../utils/uid/generateUID'; -import { RelatedStageModes } from '../../../WidgetRelatedStages'; +import { relatedStageActions } from '../../../WidgetRelatedStages'; import type { LinkedRequestEvent, ConvertedRelatedStageEventProps } from './getConvertedRelatedStageEvent.types'; import { errorCreator, pipe } from '../../../../../capture-core-utils'; import { convertClientToServer, convertFormToClient } from '../../../../converters'; @@ -35,7 +35,7 @@ const getEventDetailsByLinkMode = ({ status: 'SCHEDULE', }; - if (linkMode === RelatedStageModes.SCHEDULE_IN_ORG) { + if (linkMode === relatedStageActions.SCHEDULE_IN_ORG) { const { scheduledAt: linkedEventScheduledAt, orgUnit: linkedEventOrgUnit } = relatedStageDataValues; if (!linkedEventScheduledAt || !linkedEventOrgUnit) { // Business logic dictates that these values will not be null here @@ -56,7 +56,7 @@ const getEventDetailsByLinkMode = ({ }); } - if (linkMode === RelatedStageModes.ENTER_DATA) { + if (linkMode === relatedStageActions.ENTER_DATA) { const { orgUnit: linkedEventOrgUnit } = relatedStageDataValues; if (!linkedEventOrgUnit) { throw new Error( @@ -75,7 +75,7 @@ const getEventDetailsByLinkMode = ({ }); } - if (linkMode === RelatedStageModes.LINK_EXISTING_RESPONSE) { + if (linkMode === relatedStageActions.LINK_EXISTING_RESPONSE) { const { linkedEventId } = relatedStageDataValues; return { linkedEvent: null, diff --git a/src/core_modules/capture-core/components/DataEntries/converters/getConvertedRelatedStageEvent/getConvertedRelatedStageEvent.types.js b/src/core_modules/capture-core/components/DataEntries/converters/getConvertedRelatedStageEvent/getConvertedRelatedStageEvent.types.js index 7a40b70c2e..360a0ab676 100644 --- a/src/core_modules/capture-core/components/DataEntries/converters/getConvertedRelatedStageEvent/getConvertedRelatedStageEvent.types.js +++ b/src/core_modules/capture-core/components/DataEntries/converters/getConvertedRelatedStageEvent/getConvertedRelatedStageEvent.types.js @@ -1,6 +1,6 @@ // @flow import type { RelatedStageDataValueStates, RelatedStageRelationshipType } from '../../../WidgetRelatedStages'; -import { RelatedStageModes } from '../../../WidgetRelatedStages'; +import { relatedStageActions } from '../../../WidgetRelatedStages'; type CommonEventDetails = { event: string, @@ -28,7 +28,7 @@ export type LinkedRequestEvent = { } export type ConvertedRelatedStageEventProps = {| - linkMode: $Keys, + linkMode: $Keys, relatedStageDataValues: RelatedStageDataValueStates, programId: string, teiId?: ?string, diff --git a/src/core_modules/capture-core/components/Pages/EnrollmentAddEvent/EnrollmentAddEventPage.epics.js b/src/core_modules/capture-core/components/Pages/EnrollmentAddEvent/EnrollmentAddEventPage.epics.js index 6b18a26e4c..7fd2050e30 100644 --- a/src/core_modules/capture-core/components/Pages/EnrollmentAddEvent/EnrollmentAddEventPage.epics.js +++ b/src/core_modules/capture-core/components/Pages/EnrollmentAddEvent/EnrollmentAddEventPage.epics.js @@ -11,7 +11,7 @@ import { rollbackEnrollmentEvents, saveFailed, } from '../common/EnrollmentOverviewDomain/enrollment.actions'; -import { RelatedStageModes } from '../../WidgetRelatedStages'; +import { relatedStageActions } from '../../WidgetRelatedStages'; import { buildUrlQueryString } from '../../../utils/routing'; const shouldNavigateWithRelatedStage = ({ @@ -21,7 +21,7 @@ const shouldNavigateWithRelatedStage = ({ history, }) => { if (linkMode && linkedEventId) { - if (linkMode === RelatedStageModes.ENTER_DATA) { + if (linkMode === relatedStageActions.ENTER_DATA) { const navigate = () => history.push(`/enrollmentEventEdit?${buildUrlQueryString({ eventId: linkedEventId, orgUnitId: linkedOrgUnitId, diff --git a/src/core_modules/capture-core/components/Pages/EnrollmentAddEvent/EnrollmentAddEventPageDefault/EnrollmentAddEventPageDefault.container.js b/src/core_modules/capture-core/components/Pages/EnrollmentAddEvent/EnrollmentAddEventPageDefault/EnrollmentAddEventPageDefault.container.js index c7c32a4f76..a3500cd1f3 100644 --- a/src/core_modules/capture-core/components/Pages/EnrollmentAddEvent/EnrollmentAddEventPageDefault/EnrollmentAddEventPageDefault.container.js +++ b/src/core_modules/capture-core/components/Pages/EnrollmentAddEvent/EnrollmentAddEventPageDefault/EnrollmentAddEventPageDefault.container.js @@ -11,7 +11,7 @@ import { buildUrlQueryString, useLocationQuery } from '../../../../utils/routing import { useProgramInfo } from '../../../../hooks/useProgramInfo'; import { EnrollmentAddEventTopBar, useEnrollmentAddEventTopBar } from '../TopBar'; import { deleteEnrollment, fetchEnrollments } from '../../Enrollment/EnrollmentPage.actions'; -import { RelatedStageModes } from '../../../WidgetRelatedStages'; +import { relatedStageActions } from '../../../WidgetRelatedStages'; import { useWidgetDataFromStore } from '../hooks'; import { useHideWidgetByRuleLocations } from '../../Enrollment/EnrollmentPageDefault/hooks'; @@ -70,7 +70,7 @@ export const EnrollmentAddEventPageDefault = ({ const handleSave = useCallback( ({ enrollments, events, linkMode }) => { - if (linkMode && linkMode === RelatedStageModes.ENTER_DATA) return; + if (linkMode && linkMode === relatedStageActions.ENTER_DATA) return; const nowClient = fromClientDate(new Date()); const nowServer = new Date(nowClient.getServerZonedISOString()); diff --git a/src/core_modules/capture-core/components/Pages/New/EnrollmentRegistrationEntryWrapper.component.js b/src/core_modules/capture-core/components/Pages/New/EnrollmentRegistrationEntryWrapper.component.js index ac0b51288b..8c65e761b5 100644 --- a/src/core_modules/capture-core/components/Pages/New/EnrollmentRegistrationEntryWrapper.component.js +++ b/src/core_modules/capture-core/components/Pages/New/EnrollmentRegistrationEntryWrapper.component.js @@ -4,7 +4,7 @@ import { useDispatch } from 'react-redux'; import { cleanUpUid } from './NewPage.actions'; import { EnrollmentRegistrationEntry } from '../../DataEntries/EnrollmentRegistrationEntry/EnrollmentRegistrationEntry.container'; import type { OwnProps } from '../../DataEntries/EnrollmentRegistrationEntry/EnrollmentRegistrationEntry.types'; -import { RelatedStageModes } from '../../WidgetRelatedStages'; +import { relatedStageActions } from '../../WidgetRelatedStages'; export const EnrollmentRegistrationEntryWrapper: ComponentType = (props) => { const dispatch = useDispatch(); @@ -12,9 +12,9 @@ export const EnrollmentRegistrationEntryWrapper: ComponentType = (prop dispatch(cleanUpUid()); }, [dispatch]); - const relatedStageModesOptions = { - [RelatedStageModes.LINK_EXISTING_RESPONSE]: { hidden: true }, + const relatedStageActionsOptions = { + [relatedStageActions.LINK_EXISTING_RESPONSE]: { hidden: true }, }; - return ; + return ; }; diff --git a/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/DataEntry/Enrollment/DataEntryEnrollment.component.js b/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/DataEntry/Enrollment/DataEntryEnrollment.component.js index fb4dbb4837..710ff035c1 100644 --- a/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/DataEntry/Enrollment/DataEntryEnrollment.component.js +++ b/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/DataEntry/Enrollment/DataEntryEnrollment.component.js @@ -6,7 +6,7 @@ import { DATA_ENTRY_ID } from '../../registerTei.const'; import enrollmentClasses from './enrollment.module.css'; import { EnrollmentRegistrationEntry } from '../../../../../DataEntries'; import type { Props } from './dataEntryEnrollment.types'; -import { RelatedStageModes } from '../../../../../WidgetRelatedStages'; +import { relatedStageActions } from '../../../../../WidgetRelatedStages'; const NewEnrollmentRelationshipPlain = ({ @@ -21,12 +21,12 @@ const NewEnrollmentRelationshipPlain = ExistingUniqueValueDialogActions, }: Props) => { const fieldOptions = { theme, fieldLabelMediaBasedClass: enrollmentClasses.fieldLabelMediaBased }; - const relatedStageModesOptions = { - [RelatedStageModes.ENTER_DATA]: { + const relatedStageActionsOptions = { + [relatedStageActions.ENTER_DATA]: { disabled: true, disabledMessage: i18n.t('Enter details now is not available when creating a relationship'), }, - [RelatedStageModes.LINK_EXISTING_RESPONSE]: { hidden: true }, + [relatedStageActions.LINK_EXISTING_RESPONSE]: { hidden: true }, }; return ( @@ -45,7 +45,7 @@ const NewEnrollmentRelationshipPlain = renderDuplicatesDialogActions={renderDuplicatesDialogActions} renderDuplicatesCardActions={renderDuplicatesCardActions} ExistingUniqueValueDialogActions={ExistingUniqueValueDialogActions} - relatedStageModesOptions={relatedStageModesOptions} + relatedStageActionsOptions={relatedStageActionsOptions} /> ); }; diff --git a/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/Enrollment/DataEntryEnrollment.component.js b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/Enrollment/DataEntryEnrollment.component.js index 406eacdc5f..7d20b9f6ca 100644 --- a/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/Enrollment/DataEntryEnrollment.component.js +++ b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/Enrollment/DataEntryEnrollment.component.js @@ -6,7 +6,7 @@ import { DATA_ENTRY_ID } from '../../registerTei.const'; import enrollmentClasses from './enrollment.module.css'; import { EnrollmentRegistrationEntry } from '../../../../../../DataEntries'; import type { Props } from './dataEntryEnrollment.types'; -import { RelatedStageModes } from '../../../../../../WidgetRelatedStages'; +import { relatedStageActions } from '../../../../../../WidgetRelatedStages'; const NewEnrollmentRelationshipPlain = ({ @@ -22,12 +22,12 @@ const NewEnrollmentRelationshipPlain = ExistingUniqueValueDialogActions, }: Props) => { const fieldOptions = { theme, fieldLabelMediaBasedClass: enrollmentClasses.fieldLabelMediaBased }; - const relatedStageModesOptions = { - [RelatedStageModes.ENTER_DATA]: { + const relatedStageActionsOptions = { + [relatedStageActions.ENTER_DATA]: { disabled: true, disabledMessage: i18n.t('Enter details now is not available when creating a relationship'), }, - [RelatedStageModes.LINK_EXISTING_RESPONSE]: { hidden: true }, + [relatedStageActions.LINK_EXISTING_RESPONSE]: { hidden: true }, }; return ( @@ -47,7 +47,7 @@ const NewEnrollmentRelationshipPlain = renderDuplicatesCardActions={renderDuplicatesCardActions} ExistingUniqueValueDialogActions={ExistingUniqueValueDialogActions} trackedEntityInstanceAttributes={inheritedAttributes} - relatedStageModesOptions={relatedStageModesOptions} + relatedStageActionsOptions={relatedStageActionsOptions} /> ); }; diff --git a/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/validated.actions.js b/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/validated.actions.js index ddd8c75878..f14942ced5 100644 --- a/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/validated.actions.js +++ b/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/validated.actions.js @@ -1,7 +1,7 @@ // @flow import { actionCreator } from '../../../actions/actions.utils'; import { effectMethods } from '../../../trackerOffline'; -import { RelatedStageModes } from '../../WidgetRelatedStages'; +import { relatedStageActions } from '../../WidgetRelatedStages'; import type { RequestEvent, LinkedRequestEvent } from '../../DataEntries'; import type { ExternalSaveHandler } from '../common.types'; @@ -37,7 +37,7 @@ export const requestSaveEvent = ({ linkedEvent: ?LinkedRequestEvent, relationship: ?Object, serverData: Object, - linkMode: ?$Keys, + linkMode: ?$Keys, onSaveExternal: ?ExternalSaveHandler, onSaveSuccessActionType?: string, onSaveErrorActionType?: string, @@ -62,7 +62,7 @@ export const setSaveEnrollmentEventInProgress = ({ requestEventId: string, linkedEventId: ?string, linkedOrgUnitId: ?string, - linkMode: ?$Keys, + linkMode: ?$Keys, }) => actionCreator(newEventWidgetActionTypes.SET_SAVE_ENROLLMENT_EVENT_IN_PROGRESS)({ requestEventId, linkedEventId, diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.component.js b/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.component.js index 519cb05cea..acf6c6e0a5 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.component.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.component.js @@ -4,7 +4,7 @@ import i18n from '@dhis2/d2-i18n'; import { Button, colors, Radio, spacers, spacersNum } from '@dhis2/ui'; import { withStyles } from '@material-ui/core'; import { ConditionalTooltip } from 'capture-core/components/Tooltips/ConditionalTooltip'; -import { actions as RelatedStagesActionTypes, mainOptionTranslatedTexts, relatedStageStatus } from '../constants'; +import { relatedStageActions, mainOptionTranslatedTexts, relatedStageStatus } from '../constants'; import { useCanAddNewEventToStage } from '../hooks/useCanAddNewEventToStage'; import { DataSection } from '../../DataSection'; import { ScheduleInOrgUnit } from '../ScheduleInOrgUnit'; @@ -41,7 +41,7 @@ const styles = () => ({ }); const Schedule = ({ - actionTypesOptions, + actionsOptions, linkableEvents, selectedAction, updateSelectedAction, @@ -49,7 +49,7 @@ const Schedule = ({ canAddNewEventToStage, }) => { const { hidden, disabled, disabledMessage } = - (actionTypesOptions && actionTypesOptions[RelatedStagesActionTypes.SCHEDULE_IN_ORG]) || {}; + (actionsOptions && actionsOptions[relatedStageActions.SCHEDULE_IN_ORG]) || {}; if (hidden) { return null; } @@ -67,25 +67,25 @@ const Schedule = ({ return ( updateSelectedAction(e.value)} - value={RelatedStagesActionTypes.SCHEDULE_IN_ORG} + value={relatedStageActions.SCHEDULE_IN_ORG} /> ); }; const EnterData = ({ - actionTypesOptions, + actionsOptions, linkableEvents, selectedAction, updateSelectedAction, @@ -93,7 +93,7 @@ const EnterData = ({ canAddNewEventToStage, }) => { const { hidden, disabled, disabledMessage } = - (actionTypesOptions && actionTypesOptions[RelatedStagesActionTypes.ENTER_DATA]) || {}; + (actionsOptions && actionsOptions[relatedStageActions.ENTER_DATA]) || {}; if (hidden) { return null; } @@ -111,32 +111,32 @@ const EnterData = ({ return ( updateSelectedAction(e.value)} - value={RelatedStagesActionTypes.ENTER_DATA} + value={relatedStageActions.ENTER_DATA} /> ); }; const LinkExistingResponse = ({ - actionTypesOptions, + actionsOptions, linkableEvents, selectedAction, updateSelectedAction, programStage, }) => { const { hidden, disabled, disabledMessage } = - (actionTypesOptions && actionTypesOptions[RelatedStagesActionTypes.LINK_EXISTING_RESPONSE]) || {}; + (actionsOptions && actionsOptions[relatedStageActions.LINK_EXISTING_RESPONSE]) || {}; if (hidden) { return null; } @@ -154,18 +154,18 @@ const LinkExistingResponse = ({ return ( updateSelectedAction(e.value)} - value={RelatedStagesActionTypes.LINK_EXISTING_RESPONSE} + value={relatedStageActions.LINK_EXISTING_RESPONSE} /> ); @@ -183,13 +183,13 @@ const RelatedStagesActionsPlain = ({ constraint, errorMessages, saveAttempted, - actionTypesOptions, + actionsOptions, }: Props) => { const { programStage } = useProgramStageInfo(constraint?.programStage?.id); const selectedAction = useMemo(() => relatedStagesDataValues.linkMode, [relatedStagesDataValues.linkMode]); - const updateSelectedAction = (action: ?$Values) => { + const updateSelectedAction = (action: ?$Values) => { setRelatedStagesDataValues(prevState => ({ ...prevState, linkMode: action, @@ -210,7 +210,7 @@ const RelatedStagesActionsPlain = ({ {type === relatedStageStatus.LINKABLE && ( <> )} - {selectedAction === RelatedStagesActionTypes.SCHEDULE_IN_ORG && ( + {selectedAction === relatedStageActions.SCHEDULE_IN_ORG && ( )} - {selectedAction === RelatedStagesActionTypes.ENTER_DATA && ( + {selectedAction === relatedStageActions.ENTER_DATA && ( )} - {selectedAction === RelatedStagesActionTypes.LINK_EXISTING_RESPONSE && ( + {selectedAction === relatedStageActions.LINK_EXISTING_RESPONSE && ( void, setRelatedStagesDataValues: (() => Object) => void, - actionTypesOptions?: { - [key: $Keys]: { + actionsOptions?: { + [key: $Keys]: { hidden?: boolean, disabled?: boolean, disabledMessage?: string diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.types.js b/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.types.js index c433e40c89..7876e528c1 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.types.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.types.js @@ -1,5 +1,5 @@ // @flow -import { RelatedStageModes } from './index'; +import { relatedStageActions } from './index'; import type { Constraint } from './RelatedStagesActions/RelatedStagesActions.types'; export type RelationshipType = {| @@ -19,8 +19,8 @@ export type Props = {| programId: string, enrollmentId?: string, programStageId: string, - actionTypesOptions?: { - [key: $Keys]: { + actionsOptions?: { + [key: $Keys]: { hidden?: boolean, disabled?: boolean, disabledMessage?: string @@ -28,7 +28,7 @@ export type Props = {| }, |} export type RelatedStageDataValueStates = {| - linkMode: ?$Keys, + linkMode: ?$Keys, scheduledAt: string, scheduledAtFormatError: ?{error: ?string, errorCode: ?string}, orgUnit: ?{ @@ -57,7 +57,7 @@ export type RelatedStageRefPayload = { getLinkedStageValues: () => { selectedRelationshipType: RelatedStageRelationshipType, relatedStageDataValues: RelatedStageDataValueStates, - linkMode: ?$Keys, + linkMode: ?$Keys, }, eventHasLinkableStageRelationship: () => boolean, formIsValidOnSave: () => boolean, diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/constants.js b/src/core_modules/capture-core/components/WidgetRelatedStages/constants.js index d8e57bb9a4..7b021f5fc5 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/constants.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/constants.js @@ -6,14 +6,14 @@ export const relatedStageStatus = Object.freeze({ AMBIGUOUS_RELATIONSHIPS: 'AMBIGUOUS_RELATIONSHIPS', }); -export const actions = Object.freeze({ +export const relatedStageActions = Object.freeze({ SCHEDULE_IN_ORG: 'SCHEDULE_IN_ORG', LINK_EXISTING_RESPONSE: 'LINK_EXISTING_RESPONSE', ENTER_DATA: 'ENTER_DATA', }); export const mainOptionTranslatedTexts = { - [actions.SCHEDULE_IN_ORG]: i18n.t('Schedule'), - [actions.ENTER_DATA]: i18n.t('Enter details now'), - [actions.LINK_EXISTING_RESPONSE]: i18n.t('Link to an existing event'), + [relatedStageActions.SCHEDULE_IN_ORG]: i18n.t('Schedule'), + [relatedStageActions.ENTER_DATA]: i18n.t('Enter details now'), + [relatedStageActions.LINK_EXISTING_RESPONSE]: i18n.t('Link to an existing event'), }; diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/index.js b/src/core_modules/capture-core/components/WidgetRelatedStages/index.js index 082bdd7c93..e75b93c8cc 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/index.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/index.js @@ -5,5 +5,5 @@ export type { RelatedStageRefPayload, RelatedStageRelationshipType, } from './WidgetRelatedStages.types'; -export { actions as RelatedStageModes } from './constants'; +export { relatedStageActions } from './constants'; export { relatedStageWidgetIsValid } from './relatedStageEventIsValid/relatedStageEventIsValid'; diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/relatedStageEventIsValid/ValidationFunctions.js b/src/core_modules/capture-core/components/WidgetRelatedStages/relatedStageEventIsValid/ValidationFunctions.js index 219e354ded..0092d76bab 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/relatedStageEventIsValid/ValidationFunctions.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/relatedStageEventIsValid/ValidationFunctions.js @@ -2,7 +2,7 @@ import i18n from '@dhis2/d2-i18n'; import { isValidOrgUnit } from '../../../../capture-core-utils/validators/form'; import { isValidDate } from '../../../utils/validation/validators/form'; -import { actions as RelatedStageModes } from '../constants'; +import { relatedStageActions } from '../index'; type Props = { scheduledAt: ?string, @@ -87,8 +87,8 @@ const linkToExistingResponse = (props) => { export const ValidationFunctionsByLinkMode: { [key: string]: (props: ?Props) => boolean } = { - [RelatedStageModes.SCHEDULE_IN_ORG]: props => scheduleInOrgUnit(props), - [RelatedStageModes.ENTER_DATA]: props => enterData(props), - [RelatedStageModes.LINK_EXISTING_RESPONSE]: props => linkToExistingResponse(props), + [relatedStageActions.SCHEDULE_IN_ORG]: props => scheduleInOrgUnit(props), + [relatedStageActions.ENTER_DATA]: props => enterData(props), + [relatedStageActions.LINK_EXISTING_RESPONSE]: props => linkToExistingResponse(props), }; diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/relatedStageEventIsValid/relatedStageEventIsValid.types.js b/src/core_modules/capture-core/components/WidgetRelatedStages/relatedStageEventIsValid/relatedStageEventIsValid.types.js index a8211930b7..1f4fa5cb35 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/relatedStageEventIsValid/relatedStageEventIsValid.types.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/relatedStageEventIsValid/relatedStageEventIsValid.types.js @@ -1,9 +1,9 @@ // @flow import type { ErrorMessagesForRelatedStages } from '../RelatedStagesActions'; -import { RelatedStageModes } from '../index'; +import { relatedStageActions } from '../index'; export type RelatedStageIsValidProps = {| - linkMode: ?$Keys, + linkMode: ?$Keys, scheduledAt: ?string, scheduledAtFormatError: ?{error: ?string, errorCode: ?string}, orgUnit: ?{ From 6b97817d0a8410797789aaa9f09e8f20d15984f7 Mon Sep 17 00:00:00 2001 From: Simona Domnisoru Date: Tue, 17 Dec 2024 09:55:48 +0100 Subject: [PATCH 10/28] chore: rename helper file to getRelationshipNewTeiName --- .../{getRelationshipNewTei.js => getRelationshipNewTeiName.js} | 0 .../Pages/common/TEIRelationshipsWidget/RegisterTei/index.js | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/exposedHelpers/{getRelationshipNewTei.js => getRelationshipNewTeiName.js} (100%) diff --git a/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/exposedHelpers/getRelationshipNewTei.js b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/exposedHelpers/getRelationshipNewTeiName.js similarity index 100% rename from src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/exposedHelpers/getRelationshipNewTei.js rename to src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/exposedHelpers/getRelationshipNewTeiName.js diff --git a/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/index.js b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/index.js index b8aa107828..bdb73c3680 100644 --- a/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/index.js +++ b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/index.js @@ -9,4 +9,4 @@ export { export { loadSearchGroupDuplicatesForReviewEpic, } from '../../../../PossibleDuplicatesDialog/possibleDuplicatesDialog.epics'; -export { getRelationshipNewTeiName } from './exposedHelpers/getRelationshipNewTei'; +export { getRelationshipNewTeiName } from './exposedHelpers/getRelationshipNewTeiName'; From 743bcbf471a906cdda89786d8d6d11d57183750c Mon Sep 17 00:00:00 2001 From: Simona Domnisoru Date: Wed, 18 Dec 2024 12:30:52 +0100 Subject: [PATCH 11/28] feat: simplify how we determine what event to redirect to --- .../EnrollmentRegistrationEntry.container.js | 4 +- .../helpers/deriveAutoGenerateEvents.js | 2 + .../hooks/useBuildEnrollmentPayload.js | 16 ++-- .../types/duplicateCheckOnSave.types.js | 5 +- .../RegistrationDataEntry.actions.js | 13 ++-- .../RegistrationDataEntry.container.js | 4 +- .../RegistrationDataEntry.epics.js | 20 ++--- .../helpers/getPageToRedirectTo.js | 77 ++++++++++++------- .../ValidationFunctions.js | 2 +- 9 files changed, 87 insertions(+), 56 deletions(-) diff --git a/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/EnrollmentRegistrationEntry.container.js b/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/EnrollmentRegistrationEntry.container.js index acaff25f1b..e4f014b325 100644 --- a/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/EnrollmentRegistrationEntry.container.js +++ b/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/EnrollmentRegistrationEntry.container.js @@ -59,9 +59,9 @@ export const EnrollmentRegistrationEntry: ComponentType = ({ } const onSaveWithEnrollment = () => { - const { teiWithEnrollment, formHasError, programStageIdLinkedEventToRedirectTo } = + const { teiWithEnrollment, formHasError, relatedStageLinkedEvent } = buildTeiWithEnrollment(relatedStageRef); - !formHasError && onSave(teiWithEnrollment, programStageIdLinkedEventToRedirectTo); + !formHasError && onSave(teiWithEnrollment, relatedStageLinkedEvent); }; return ( diff --git a/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/helpers/deriveAutoGenerateEvents.js b/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/helpers/deriveAutoGenerateEvents.js index c4eb7ddc85..6af15e19be 100644 --- a/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/helpers/deriveAutoGenerateEvents.js +++ b/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/helpers/deriveAutoGenerateEvents.js @@ -4,6 +4,7 @@ import { dataElementTypes, ProgramStage } from '../../../../metaData'; import { convertClientToServer } from '../../../../converters'; import { convertCategoryOptionsToServer } from '../../../../converters/clientToServer'; import type { RequestEvent, LinkedRequestEvent } from '../../../DataEntries'; +import { generateUID } from '../../../../utils/uid/generateUID'; const ignoreAutoGenerateIfApplicable = (stage, stageToSkip) => !stageToSkip || stageToSkip.programStage !== stage.id; @@ -75,6 +76,7 @@ export const deriveAutoGenerateEvents = ({ return { ...eventInfo, ...eventAttributeCategoryOptions, + event: generateUID(), programStage, program: programId, orgUnit: orgUnitId, diff --git a/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/hooks/useBuildEnrollmentPayload.js b/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/hooks/useBuildEnrollmentPayload.js index 6a5566696a..8d0a1c4b01 100644 --- a/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/hooks/useBuildEnrollmentPayload.js +++ b/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/hooks/useBuildEnrollmentPayload.js @@ -94,7 +94,10 @@ export const useBuildEnrollmentPayload = ({ const buildTeiWithEnrollment = (relatedStageRef?: {current: ?RelatedStageRefPayload}): { teiWithEnrollment: EnrollmentPayload, formHasError: boolean, - programStageIdLinkedEventToRedirectTo?: string, + relatedStageLinkedEvent?: { + programStageId: string, + eventId: string, + }, } => { if (!formFoundation) throw Error('form foundation object not found'); const firstStage = firstStageMetaData && firstStageMetaData.stage; @@ -183,10 +186,13 @@ export const useBuildEnrollmentPayload = ({ relationships: relationship ? [relationship] : undefined, }, formHasError, - programStageIdLinkedEventToRedirectTo: - relatedStageLinkedEvent && linkMode === relatedStageActions.ENTER_DATA - ? relatedStageLinkedEvent.programStage - : undefined, + relatedStageLinkedEvent: + relatedStageLinkedEvent && linkMode === relatedStageActions.ENTER_DATA + ? { + programStageId: relatedStageLinkedEvent.programStage, + eventId: relatedStageLinkedEvent.event, + } + : undefined, }; }; diff --git a/src/core_modules/capture-core/components/DataEntries/common/TEIAndEnrollment/DuplicateCheckOnSave/types/duplicateCheckOnSave.types.js b/src/core_modules/capture-core/components/DataEntries/common/TEIAndEnrollment/DuplicateCheckOnSave/types/duplicateCheckOnSave.types.js index 43e78f4ec8..da1e6bcc39 100644 --- a/src/core_modules/capture-core/components/DataEntries/common/TEIAndEnrollment/DuplicateCheckOnSave/types/duplicateCheckOnSave.types.js +++ b/src/core_modules/capture-core/components/DataEntries/common/TEIAndEnrollment/DuplicateCheckOnSave/types/duplicateCheckOnSave.types.js @@ -4,5 +4,8 @@ import type { TeiPayload } from '../../../../../Pages/common/TEIRelationshipsWid export type SaveForDuplicateCheck = ( teiWithEnrollment: EnrollmentPayload | TeiPayload, - programStageIdLinkedEventToRedirectTo?: string, + relatedStageLinkedEvent?: { + programStageId: string, + eventId: string, + }, ) => void; diff --git a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.actions.js b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.actions.js index b8000eacf9..55731792cd 100644 --- a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.actions.js +++ b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.actions.js @@ -49,12 +49,15 @@ export const saveNewTrackedEntityInstance = (candidateForRegistration: any) => export const startSavingNewTrackedEntityInstanceWithEnrollment = ( enrollmentPayload: EnrollmentPayload, uid: string, - programStageIdLinkedEventToRedirectTo?: string, + relatedStageLinkedEvent?: { + programStageId: string, + eventId: string, + }, ) => actionCreator(registrationFormActionTypes.NEW_TRACKED_ENTITY_INSTANCE_WITH_ENROLLMENT_SAVE_START)({ enrollmentPayload, uid, - programStageIdLinkedEventToRedirectTo, + relatedStageLinkedEvent, }); export const saveNewTrackedEntityInstanceWithEnrollment = ({ @@ -62,13 +65,13 @@ export const saveNewTrackedEntityInstanceWithEnrollment = ({ pageToRedirectTo, uid, stageId, - eventIndex, + eventId, }: { candidateForRegistration: any, pageToRedirectTo: string, uid: string, stageId?: string, - eventIndex: number, + eventId?: string, }) => actionCreator(registrationFormActionTypes.NEW_TRACKED_ENTITY_INSTANCE_WITH_ENROLLMENT_SAVE)( { ...candidateForRegistration }, @@ -81,7 +84,7 @@ export const saveNewTrackedEntityInstanceWithEnrollment = ({ }, commit: { type: registrationFormActionTypes.NEW_TRACKED_ENTITY_INSTANCE_WITH_ENROLLMENT_SAVE_COMPLETED, - meta: { pageToRedirectTo, stageId, uid, eventIndex }, + meta: { pageToRedirectTo, stageId, uid, eventId }, }, rollback: { type: registrationFormActionTypes.NEW_TRACKED_ENTITY_INSTANCE_WITH_ENROLLMENT_SAVE_FAILED, diff --git a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.container.js b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.container.js index 83398364d6..2527f67c65 100644 --- a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.container.js +++ b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.container.js @@ -30,13 +30,13 @@ export const RegistrationDataEntry: ComponentType = ({ [dispatch]); const dispatchOnSaveWithEnrollment = useCallback( - (enrollmentPayload, programStageIdLinkedEventToRedirectTo) => { + (enrollmentPayload, relatedStageLinkedEvent) => { const uid = uuid(); dispatch( startSavingNewTrackedEntityInstanceWithEnrollment( enrollmentPayload, uid, - programStageIdLinkedEventToRedirectTo, + relatedStageLinkedEvent, ), ); }, diff --git a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.epics.js b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.epics.js index 787922f840..390e95aabc 100644 --- a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.epics.js +++ b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.epics.js @@ -54,17 +54,14 @@ export const startSavingNewTrackedEntityInstanceWithEnrollmentEpic: Epic = ( ofType(registrationFormActionTypes.NEW_TRACKED_ENTITY_INSTANCE_WITH_ENROLLMENT_SAVE_START), map((action) => { const { currentSelections: { programId } } = store.value; - const { enrollmentPayload, uid, programStageIdLinkedEventToRedirectTo } = action.payload; + const { enrollmentPayload, uid, relatedStageLinkedEvent } = action.payload; const { stages, useFirstStageDuringRegistration } = getTrackerProgramThrowIfNotFound(programId); - const { programStageIdToRedirectTo, pageToRedirectTo } = getPageToRedirectTo( + const { stageId, eventId, pageToRedirectTo } = getPageToRedirectTo({ stages, + events: enrollmentPayload.enrollments[0]?.events, useFirstStageDuringRegistration, - programStageIdLinkedEventToRedirectTo, - ); - - const eventIndex = enrollmentPayload.enrollments[0]?.events.findIndex( - eventsToBeCreated => eventsToBeCreated.programStage === programStageIdToRedirectTo, - ); + relatedStageLinkedEvent, + }); return saveNewTrackedEntityInstanceWithEnrollment({ candidateForRegistration: { @@ -73,8 +70,8 @@ export const startSavingNewTrackedEntityInstanceWithEnrollmentEpic: Epic = ( ], }, pageToRedirectTo, - eventIndex, - stageId: programStageIdToRedirectTo, + eventId, + stageId, uid, }); }), @@ -92,7 +89,7 @@ export const completeSavingNewTrackedEntityInstanceWithEnrollmentEpic = ( payload: { bundleReport: { typeReportMap }, }, - meta: { uid, pageToRedirectTo, stageId, eventIndex }, + meta: { uid, pageToRedirectTo, stageId, eventId }, } = action; const { currentSelections: { orgUnitId, programId }, @@ -101,7 +98,6 @@ export const completeSavingNewTrackedEntityInstanceWithEnrollmentEpic = ( const { uid: stateUid } = newPage || {}; const teiId = typeReportMap.TRACKED_ENTITY.objectReports[0].uid; const enrollmentId = typeReportMap.ENROLLMENT.objectReports[0].uid; - const eventId = typeReportMap.EVENT.objectReports?.[eventIndex]?.uid; if (stateUid !== uid) { return EMPTY; diff --git a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/getPageToRedirectTo.js b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/getPageToRedirectTo.js index baee7b2d40..1b99b1c8fa 100644 --- a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/getPageToRedirectTo.js +++ b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/getPageToRedirectTo.js @@ -14,40 +14,61 @@ export const PAGES = { * When the event will be created redirect to enrollmentEventEdit * When the event will not be created redirect to enrollmentEventNew */ -export const getPageToRedirectTo = ( +export const getPageToRedirectTo = ({ + stages, + events, + useFirstStageDuringRegistration, + relatedStageLinkedEvent, +}: { stages: Map, + events: any, useFirstStageDuringRegistration: boolean, - programStageIdLinkedEventToRedirectTo?: string, -) => { + relatedStageLinkedEvent?: { + programStageId: string, + eventId: string, + }, +}): { stageId?: string, eventId?: string, pageToRedirectTo: string } => { const stagesArray = [...stages.values()]; const [firstStageWithOpenAfterEnrollment] = stagesArray.filter(({ openAfterEnrollment }) => openAfterEnrollment); - const pageToRedirectTo = (() => { - // event will be created and linked during first stage registration - if (programStageIdLinkedEventToRedirectTo) { - return PAGES.enrollmentEventEdit; + // event will be created and linked during first stage registration + if (relatedStageLinkedEvent?.eventId) { + const { programStageId, eventId } = relatedStageLinkedEvent; + return { + stageId: programStageId, + eventId, + pageToRedirectTo: PAGES.enrollmentEventEdit, + }; + } + + if (firstStageWithOpenAfterEnrollment) { + // event will be created during first stage registration + if (useFirstStageDuringRegistration && stagesArray[0].id === firstStageWithOpenAfterEnrollment.id) { + const eventId = events.find(e => e.programStage === firstStageWithOpenAfterEnrollment.id).event; + + return { + programStageIdToRedirectTo: firstStageWithOpenAfterEnrollment.id, + eventId, + pageToRedirectTo: PAGES.enrollmentEventEdit, + }; } - if (firstStageWithOpenAfterEnrollment) { - // event will be created during first stage registration - if ( - useFirstStageDuringRegistration - && stagesArray[0].id === firstStageWithOpenAfterEnrollment.id - ) { - return PAGES.enrollmentEventEdit; - } - // event will be autogenerated - if ( - stagesArray.find(stage => stage.autoGenerateEvent && stage.id === firstStageWithOpenAfterEnrollment.id) - ) { - return PAGES.enrollmentEventEdit; - } - return PAGES.enrollmentEventNew; + + // event will be autogenerated + if (stagesArray.find(stage => stage.autoGenerateEvent && stage.id === firstStageWithOpenAfterEnrollment.id)) { + const eventId = events.find(e => e.programStage === firstStageWithOpenAfterEnrollment.id).event; + + return { + programStageIdToRedirectTo: firstStageWithOpenAfterEnrollment.id, + eventId, + pageToRedirectTo: PAGES.enrollmentEventEdit, + }; } - return PAGES.enrollmentDashboard; - })(); - return { - programStageIdToRedirectTo: programStageIdLinkedEventToRedirectTo || firstStageWithOpenAfterEnrollment?.id, - pageToRedirectTo, - }; + return { + programStageIdToRedirectTo: firstStageWithOpenAfterEnrollment.id, + pageToRedirectTo: PAGES.enrollmentEventNew, + }; + } + + return { pageToRedirectTo: PAGES.enrollmentDashboard }; }; diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/relatedStageEventIsValid/ValidationFunctions.js b/src/core_modules/capture-core/components/WidgetRelatedStages/relatedStageEventIsValid/ValidationFunctions.js index 0092d76bab..187f20b625 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/relatedStageEventIsValid/ValidationFunctions.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/relatedStageEventIsValid/ValidationFunctions.js @@ -2,7 +2,7 @@ import i18n from '@dhis2/d2-i18n'; import { isValidOrgUnit } from '../../../../capture-core-utils/validators/form'; import { isValidDate } from '../../../utils/validation/validators/form'; -import { relatedStageActions } from '../index'; +import { relatedStageActions } from '../constants'; type Props = { scheduledAt: ?string, From 1e9b3f9cd479e4a3db49494ee540d2b9d44fc073 Mon Sep 17 00:00:00 2001 From: Simona Domnisoru Date: Thu, 19 Dec 2024 15:07:00 +0100 Subject: [PATCH 12/28] feat: show related stages widget in view event page --- i18n/en.pot | 16 ++- ...llmentWithFirstStageDataEntry.container.js | 4 +- .../getConvertedRelatedStageEvent.types.js | 4 +- .../EnrollmentEditEventPage.component.js | 2 + .../EnrollmentEditEventPage.container.js | 12 ++ .../EnrollmentEditEventPage.types.js | 1 + .../PageLayout/DefaultPageLayout.constants.js | 6 + .../DefaultEnrollmentLayout.types.js | 1 + .../LayoutComponentConfig.js | 30 +++++ .../Validated/Validated.component.js | 4 +- .../RelatedStagesActions.component.js | 76 +++++++---- .../RelatedStagesActions.container.js} | 26 ++-- .../RelatedStagesActions.types.js | 18 ++- .../RelatedStagesActions/index.js | 2 +- .../WidgetRelatedStages.container.js | 126 ++++++++++++++++++ .../WidgetRelatedStages.types.js | 11 +- .../WidgetRelatedStages/hooks/index.js | 7 + .../hooks/useAddEventWithRelationship.js | 61 +++++++++ .../hooks/useBuildRelatedStageEventPayload.js | 95 +++++++++++++ .../{ => hooks}/useRelatedStages.js | 10 +- .../components/WidgetRelatedStages/index.js | 3 +- .../WidgetTwoEventWorkspace/hooks/index.js | 2 + .../WidgetTwoEventWorkspace/index.js | 1 + 23 files changed, 458 insertions(+), 60 deletions(-) rename src/core_modules/capture-core/components/WidgetRelatedStages/{WidgetRelatedStages.component.js => RelatedStagesActions/RelatedStagesActions.container.js} (82%) create mode 100644 src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.container.js create mode 100644 src/core_modules/capture-core/components/WidgetRelatedStages/hooks/index.js create mode 100644 src/core_modules/capture-core/components/WidgetRelatedStages/hooks/useAddEventWithRelationship.js create mode 100644 src/core_modules/capture-core/components/WidgetRelatedStages/hooks/useBuildRelatedStageEventPayload.js rename src/core_modules/capture-core/components/WidgetRelatedStages/{ => hooks}/useRelatedStages.js (90%) diff --git a/i18n/en.pot b/i18n/en.pot index d4d4fb3813..88458264ab 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -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: 2024-12-05T11:39:04.447Z\n" -"PO-Revision-Date: 2024-12-05T11:39:04.447Z\n" +"POT-Creation-Date: 2024-12-11T08:18:31.184Z\n" +"PO-Revision-Date: 2024-12-11T08:18:31.184Z\n" msgid "Choose one or more dates..." msgstr "Choose one or more dates..." @@ -1397,12 +1397,21 @@ msgstr "Actions - {{relationshipName}}" msgid "Ambiguous relationships, contact system administrator" msgstr "Ambiguous relationships, contact system administrator" +msgid "Enter details" +msgstr "Enter details" + +msgid "Linked event" +msgstr "Linked event" + msgid "Enter details now" msgstr "Enter details now" msgid "Link to an existing event" msgstr "Link to an existing event" +msgid "An error occurred while linking the event" +msgstr "An error occurred while linking the event" + msgid "Scheduled date" msgstr "Scheduled date" @@ -1513,9 +1522,6 @@ msgstr "You do not have access to remove the link and delete the linked event" msgid "An error occurred while loading the widget." msgstr "An error occurred while loading the widget." -msgid "Linked event" -msgstr "Linked event" - msgid "" "This {{stageName}} event is linked to a {{linkedStageName}} event. Review " "the linked event details before entering data below" diff --git a/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/EnrollmentWithFirstStageDataEntry.container.js b/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/EnrollmentWithFirstStageDataEntry.container.js index 420c663669..dcce7692a4 100644 --- a/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/EnrollmentWithFirstStageDataEntry.container.js +++ b/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/EnrollmentWithFirstStageDataEntry.container.js @@ -4,7 +4,7 @@ import type { Props } from './EnrollmentWithFirstStageDataEntry.types'; import { FirstStageDataEntry } from './EnrollmentWithFirstStageDataEntry.component'; import { useDataEntrySections } from './hooks'; import { Section } from '../../../../metaData'; -import { WidgetRelatedStages } from '../../../WidgetRelatedStages'; +import { RelatedStagesActions } from '../../../WidgetRelatedStages'; const getSectionId = sectionId => (sectionId === Section.MAIN_SECTION_ID ? `${Section.MAIN_SECTION_ID}-stage` : sectionId); @@ -26,7 +26,7 @@ export const EnrollmentWithFirstStageDataEntry = (props: Props) => { firstStageMetaData={firstStageMetaData} dataEntrySections={dataEntrySections} /> - , - status?: string, + status: 'ACTIVE' | 'VISITED' | 'COMPLETED' | 'SCHEDULE' | 'OVERDUE' | 'SKIPPED', } export type RequestEvent = { ...CommonEventDetails, occurredAt: string, notes?: Array<{ value: string }>, - completedAt?: string, } export type LinkedRequestEvent = { ...CommonEventDetails, occurredAt?: string, - completedAt?: string, } export type ConvertedRelatedStageEventProps = {| diff --git a/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.component.js b/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.component.js index 679bb616c4..4dd2ff5341 100644 --- a/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.component.js +++ b/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.component.js @@ -57,6 +57,7 @@ export const EnrollmentEditEventPageComponent = ({ onSaveAssigneeError, onDeleteTrackedEntitySuccess, onAccessLostFromTransfer, + onNavigateToEvent, }: PlainProps) => ( diff --git a/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.container.js b/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.container.js index f459cff54d..3d81fcba59 100644 --- a/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.container.js +++ b/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.container.js @@ -187,6 +187,17 @@ const EnrollmentEditEventPageWithContextPlain = ({ const onGoBack = () => history.push(`/enrollment?${buildUrlQueryString({ enrollmentId })}`); + const onNavigateToEvent = (eventIdToRedirectTo: string) => { + history.push( + `/enrollmentEventEdit?${buildUrlQueryString({ + eventId: eventIdToRedirectTo, + orgUnitId, + programId, + enrollmentId, + })}`, + ); + }; + const onHandleScheduleSave = (eventData: Object) => { dispatch(updateEnrollmentEvent(eventId, eventData)); history.push(`enrollment?${buildUrlQueryString({ enrollmentId })}`); @@ -291,6 +302,7 @@ const EnrollmentEditEventPageWithContextPlain = ({ onSaveAssigneeError={onSaveAssigneeError} events={enrollmentSite?.events} onAccessLostFromTransfer={onAccessLostFromTransfer} + onNavigateToEvent={onNavigateToEvent} /> ); }; diff --git a/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.types.js b/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.types.js index 56e2d5c5de..a32173f2c2 100644 --- a/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.types.js +++ b/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.types.js @@ -29,6 +29,7 @@ export type PlainProps = {| onDelete: () => void, onAddNew: () => void, onGoBack: () => void, + onNavigateToEvent: (eventId: string) => void, onBackToMainPage: () => void, onBackToDashboard: () => void, onBackToViewEvent: () => void, diff --git a/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/PageLayout/DefaultPageLayout.constants.js b/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/PageLayout/DefaultPageLayout.constants.js index 0a8065a405..9e1486eae8 100644 --- a/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/PageLayout/DefaultPageLayout.constants.js +++ b/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/PageLayout/DefaultPageLayout.constants.js @@ -10,11 +10,13 @@ import { AssigneeWidget, WidgetTypes, TwoEventWorkspace, + WidgetRelatedStagesWorkspace, } from '../../common/EnrollmentOverviewDomain/EnrollmentPageLayout'; export const WidgetsForEnrollmentEventEdit: $ReadOnly<{ [key: string]: WidgetConfig }> = Object.freeze({ EditEventWorkspace, TwoEventWorkspace, + WidgetRelatedStagesWorkspace, EventNote, AssigneeWidget, ...DefaultWidgetsForEnrollmentOverview, @@ -26,6 +28,10 @@ export const DefaultPageLayout: PageLayoutConfig = { type: WidgetTypes.COMPONENT, name: 'EditEventWorkspace', }, + { + type: WidgetTypes.COMPONENT, + name: 'WidgetRelatedStagesWorkspace', + }, ], rightColumn: [ { diff --git a/src/core_modules/capture-core/components/Pages/common/EnrollmentOverviewDomain/EnrollmentPageLayout/DefaultEnrollmentLayout.types.js b/src/core_modules/capture-core/components/Pages/common/EnrollmentOverviewDomain/EnrollmentPageLayout/DefaultEnrollmentLayout.types.js index cbb1bea87a..b37abd6507 100644 --- a/src/core_modules/capture-core/components/Pages/common/EnrollmentOverviewDomain/EnrollmentPageLayout/DefaultEnrollmentLayout.types.js +++ b/src/core_modules/capture-core/components/Pages/common/EnrollmentOverviewDomain/EnrollmentPageLayout/DefaultEnrollmentLayout.types.js @@ -8,6 +8,7 @@ type DefaultComponents = 'QuickActions' | 'AssigneeWidget' | 'NewEventWorkspace' | 'EditEventWorkspace' + | 'WidgetRelatedStagesWorkspace' | 'EnrollmentNote' | 'EventNote' | 'TrackedEntityRelationship' diff --git a/src/core_modules/capture-core/components/Pages/common/EnrollmentOverviewDomain/EnrollmentPageLayout/LayoutComponentConfig/LayoutComponentConfig.js b/src/core_modules/capture-core/components/Pages/common/EnrollmentOverviewDomain/EnrollmentPageLayout/LayoutComponentConfig/LayoutComponentConfig.js index 2baee53485..1bf2275e72 100644 --- a/src/core_modules/capture-core/components/Pages/common/EnrollmentOverviewDomain/EnrollmentPageLayout/LayoutComponentConfig/LayoutComponentConfig.js +++ b/src/core_modules/capture-core/components/Pages/common/EnrollmentOverviewDomain/EnrollmentPageLayout/LayoutComponentConfig/LayoutComponentConfig.js @@ -29,6 +29,10 @@ import type { InputIndicatorProps, } from '../../../../../WidgetFeedback/WidgetFeedback.types'; import { WidgetTwoEventWorkspace } from '../../../../../WidgetTwoEventWorkspace'; +import { WidgetRelatedStages } from '../../../../../WidgetRelatedStages'; +import { + EnrollmentPageKeys, +} from '../DefaultEnrollmentLayout.constants'; export const QuickActions: WidgetConfig = { Component: EnrollmentQuickActions, @@ -286,3 +290,29 @@ export const EventNote: WidgetConfig = { dataEntryId, }), }; + +export const WidgetRelatedStagesWorkspace: WidgetConfig = { + Component: WidgetRelatedStages, + shouldHideWidget: ({ currentPage }) => currentPage === EnrollmentPageKeys.EDIT_EVENT, + getProps: ({ + program, + stageId, + enrollmentId, + eventId, + teiId, + onUpdateEnrollmentStatus, + onUpdateEnrollmentStatusSuccess, + onUpdateEnrollmentStatusError, + onNavigateToEvent, + }) => ({ + programId: program.id, + programStageId: stageId, + enrollmentId, + eventId, + teiId, + onUpdateEnrollment: onUpdateEnrollmentStatus, + onUpdateEnrollmentSuccess: onUpdateEnrollmentStatusSuccess, + onUpdateEnrollmentError: onUpdateEnrollmentStatusError, + onNavigateToEvent, + }), +}; diff --git a/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/Validated.component.js b/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/Validated.component.js index 663348da1b..7f34363548 100644 --- a/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/Validated.component.js +++ b/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/Validated.component.js @@ -6,7 +6,7 @@ import { Widget } from '../../Widget'; import { DataEntry } from '../DataEntry'; import { FinishButtons } from '../FinishButtons'; import { SavingText } from '../SavingText'; -import { WidgetRelatedStages } from '../../WidgetRelatedStages'; +import { RelatedStagesActions } from '../../WidgetRelatedStages'; import type { Props } from './validated.types'; const styles = () => ({ @@ -47,7 +47,7 @@ const ValidatedPlain = ({ id={id} orgUnit={orgUnit} /> - ({ clearSelections: { padding: spacers.dp8, }, + link: { + padding: spacers.dp8, + }, }); const Schedule = ({ @@ -171,6 +174,16 @@ const LinkExistingResponse = ({ ); }; +const LinkButton = withStyles(styles)(({ onLink, label, loading, classes }) => ( + onLink ? ( +
+ +
+ ) : null +)); + const RelatedStagesActionsPlain = ({ classes, type, @@ -184,7 +197,8 @@ const RelatedStagesActionsPlain = ({ errorMessages, saveAttempted, actionsOptions, -}: Props) => { + onLink, +}: PlainProps) => { const { programStage } = useProgramStageInfo(constraint?.programStage?.id); const selectedAction = useMemo(() => relatedStagesDataValues.linkMode, [relatedStagesDataValues.linkMode]); @@ -253,37 +267,47 @@ const RelatedStagesActionsPlain = ({ )} {selectedAction === relatedStageActions.SCHEDULE_IN_ORG && ( - + <> + + + )} {selectedAction === relatedStageActions.ENTER_DATA && ( - + <> + + + )} {selectedAction === relatedStageActions.LINK_EXISTING_RESPONSE && ( - + <> + + + )} + ); }; -export const RelatedStagesActions: ComponentType<$Diff> = withStyles(styles)(RelatedStagesActionsPlain); +export const RelatedStagesActions: ComponentType<$Diff> = withStyles(styles)(RelatedStagesActionsPlain); diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.component.js b/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.container.js similarity index 82% rename from src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.component.js rename to src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.container.js index 9ce0628481..e50803fb6f 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.component.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.container.js @@ -1,19 +1,18 @@ // @flow import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useState } from 'react'; -import { useRelatedStages } from './useRelatedStages'; -import { useOrgUnitAutoSelect } from '../../dataQueries'; -import type { Props, RelatedStageDataValueStates } from './WidgetRelatedStages.types'; -import type { ErrorMessagesForRelatedStages } from './RelatedStagesActions'; -import { RelatedStagesActions } from './RelatedStagesActions'; -import { relatedStageStatus } from './constants'; -import { useStageLabels } from './hooks/useStageLabels'; -import { relatedStageWidgetIsValid } from './relatedStageEventIsValid/relatedStageEventIsValid'; -import { useRelatedStageEvents } from './hooks/useRelatedStageEvents'; +import { useOrgUnitAutoSelect } from '../../../dataQueries'; +import type { RelatedStageDataValueStates } from '../WidgetRelatedStages.types'; +import type { Props, ErrorMessagesForRelatedStages } from './RelatedStagesActions.types'; +import { RelatedStagesActions as RelatedStagesActionsComponent } from './RelatedStagesActions.component'; +import { relatedStageStatus } from '../constants'; +import { useStageLabels, useRelatedStageEvents, useRelatedStages } from '../hooks'; +import { relatedStageWidgetIsValid } from '../relatedStageEventIsValid/relatedStageEventIsValid'; -const WidgetRelatedStagesPlain = ({ +const RelatedStagesActionsPlain = ({ programId, enrollmentId, programStageId, + onLink, ...passOnProps }: Props, ref) => { const { currentRelatedStagesStatus, selectedRelationshipType, constraint } = useRelatedStages({ @@ -97,7 +96,7 @@ const WidgetRelatedStagesPlain = ({ } return ( - ); }; -export const WidgetRelatedStages = forwardRef < Props, {| +export const RelatedStagesActions = forwardRef < Props, {| eventHasLinkableStageRelationship: Function, formIsValidOnSave: Function, getLinkedStageValues: Function - |}>(WidgetRelatedStagesPlain); + |}>(RelatedStagesActionsPlain); diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.types.js b/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.types.js index 18aedce6ac..3dfe97c009 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.types.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.types.js @@ -25,7 +25,7 @@ export type RelatedStagesEvents = { status: string, } -export type Props = {| +export type PlainProps = {| type: string, relationshipName: string, relatedStagesDataValues: RelatedStageDataValueStates, @@ -37,6 +37,7 @@ export type Props = {| constraint: ?Constraint, addErrorMessage: (ErrorMessagesForRelatedStages) => void, setRelatedStagesDataValues: (() => Object) => void, + onLink?: () => void, actionsOptions?: { [key: $Keys]: { hidden?: boolean, @@ -46,3 +47,18 @@ export type Props = {| }, ...CssClasses |} + +export type Props = {| + programId: string, + enrollmentId?: string, + programStageId: string, + onLink?: () => void, + actionsOptions?: { + [key: $Keys]: { + hidden?: boolean, + disabled?: boolean, + disabledMessage?: string + }, + }, +|} + diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/index.js b/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/index.js index 9655a91302..7d46218f32 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/index.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/index.js @@ -1,3 +1,3 @@ // @flow -export { RelatedStagesActions } from './RelatedStagesActions.component'; +export { RelatedStagesActions } from './RelatedStagesActions.container'; export type { ErrorMessagesForRelatedStages } from './RelatedStagesActions.types'; diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.container.js b/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.container.js new file mode 100644 index 0000000000..16144ab6db --- /dev/null +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.container.js @@ -0,0 +1,126 @@ +// @flow +import React, { type ComponentType, useRef, useCallback } from 'react'; +import { IconLink24, spacers } from '@dhis2/ui'; +import { withStyles } from '@material-ui/core'; +import i18n from '@dhis2/d2-i18n'; +import { Widget } from '../Widget'; +import { type RelatedStageRefPayload } from './index'; +import { RelatedStagesActions } from './RelatedStagesActions'; +import { useLinkedEventByOriginId } from '../WidgetTwoEventWorkspace/hooks'; +import type { Props } from './WidgetRelatedStages.types'; +import { + useRelatedStages, + useBuildRelatedStageEventPayload, + useAddEventWithRelationship, + createServerData, +} from './hooks'; +import { relatedStageStatus } from './constants'; +import { useCommonEnrollmentDomainData } from '../Pages/common/EnrollmentOverviewDomain'; +import { type RequestEvent } from '../DataEntries'; + +const styles = { + header: { + display: 'flex', + alignItems: 'center', + }, + icon: { + paddingRight: spacers.dp8, + }, + actions: { + margin: `0 ${spacers.dp16} ${spacers.dp16} ${spacers.dp16}`, + }, +}; + +export const WidgetRelatedStagesPlain = ({ + programId, + eventId, + enrollmentId, + programStageId, + teiId, + actionsOptions, + onUpdateEnrollment, + onUpdateEnrollmentSuccess, + onUpdateEnrollmentError, + onNavigateToEvent, + classes, +}: Props) => { + const { enrollment } = useCommonEnrollmentDomainData(teiId, enrollmentId, programId); + const { currentRelatedStagesStatus } = useRelatedStages({ programStageId, programId }); + const { + linkedEvent, + isLoading: isLinkedEventLoading, + } = useLinkedEventByOriginId({ originEventId: eventId }); + const relatedStageRef = useRef(null); + const { buildRelatedStageEventPayload } = useBuildRelatedStageEventPayload(); + const { addEventWithRelationship } = useAddEventWithRelationship({ + eventId, + onUpdateEnrollment, + onUpdateEnrollmentSuccess, + onUpdateEnrollmentError, + onNavigateToEvent, + }); + + const onLink = useCallback(() => { + // $FlowFixMe[incompatible-type] + const serverRequestEvent: ?RequestEvent = enrollment?.events.find(e => e.event === eventId); + + const { + formHasError, + linkedEvent: relatedStageLinkedEvent, + relationship, + linkMode, + } = buildRelatedStageEventPayload({ + serverRequestEvent, + relatedStageRef, + programStageId, + programId, + teiId, + enrollmentId, + }); + + if (!formHasError && relationship && linkMode) { + const serverData = createServerData({ enrollment, linkedEvent: relatedStageLinkedEvent, relationship }); + addEventWithRelationship({ serverData, linkMode, eventIdToRedirectTo: relatedStageLinkedEvent?.event }); + } + }, [ + programStageId, + programId, + teiId, + eventId, + enrollmentId, + enrollment, + buildRelatedStageEventPayload, + addEventWithRelationship, + ]); + + if (isLinkedEventLoading || linkedEvent || currentRelatedStagesStatus !== relatedStageStatus.LINKABLE) { + return null; + } + + return ( + + + + + {i18n.t('Linked event')} +
+ } + > +
+ +
+ + ); +}; + +export const WidgetRelatedStages: ComponentType = withStyles(styles)(WidgetRelatedStagesPlain); diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.types.js b/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.types.js index 7876e528c1..9288f6f008 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.types.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.types.js @@ -17,8 +17,14 @@ export type RelationshipType = {| export type Props = {| programId: string, - enrollmentId?: string, + eventId: string, + teiId: string, + enrollmentId: string, programStageId: string, + onUpdateEnrollment: (enrollment: Object) => void, + onUpdateEnrollmentSuccess: ({ redirect?: boolean }) => void, + onUpdateEnrollmentError: (message: string) => void, + onNavigateToEvent: (eventId: string) => void, actionsOptions?: { [key: $Keys]: { hidden?: boolean, @@ -26,7 +32,9 @@ export type Props = {| disabledMessage?: string }, }, + ...CssClasses, |} + export type RelatedStageDataValueStates = {| linkMode: ?$Keys, scheduledAt: string, @@ -41,6 +49,7 @@ export type RelatedStageDataValueStates = {| export type RelatedStageRelationshipType = {| id: string, + displayName: string, fromConstraint: {| programStage: { id: string, diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/hooks/index.js b/src/core_modules/capture-core/components/WidgetRelatedStages/hooks/index.js new file mode 100644 index 0000000000..54d9aa0537 --- /dev/null +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/hooks/index.js @@ -0,0 +1,7 @@ +// @flow +export { useRelatedStages } from './useRelatedStages'; +export { useStageLabels } from './useStageLabels'; +export { useRelatedStageEvents } from './useRelatedStageEvents'; +export { useCanAddNewEventToStage } from './useCanAddNewEventToStage'; +export { useBuildRelatedStageEventPayload, createServerData } from './useBuildRelatedStageEventPayload'; +export { useAddEventWithRelationship } from './useAddEventWithRelationship'; diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/hooks/useAddEventWithRelationship.js b/src/core_modules/capture-core/components/WidgetRelatedStages/hooks/useAddEventWithRelationship.js new file mode 100644 index 0000000000..39230785ff --- /dev/null +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/hooks/useAddEventWithRelationship.js @@ -0,0 +1,61 @@ +// @flow +import i18n from '@dhis2/d2-i18n'; +import { useDataEngine } from '@dhis2/app-runtime'; +import { useMutation, useQueryClient } from 'react-query'; +import { relatedStageActions } from '../constants'; + +const ReactQueryAppNamespace = 'capture'; + +const addEventWithRelationshipMutation = { + resource: '/tracker?async=false&importStrategy=CREATE_AND_UPDATE', + type: 'create', + data: ({ serverData }) => serverData, +}; + +export const useAddEventWithRelationship = ({ + eventId, + onUpdateEnrollment, + onUpdateEnrollmentSuccess, + onUpdateEnrollmentError, + onNavigateToEvent, +}: { + eventId: string, + onUpdateEnrollment: (enrollment: Object) => void, + onUpdateEnrollmentSuccess: ({ redirect?: boolean }) => void, + onUpdateEnrollmentError: (message: string) => void, + onNavigateToEvent: (eventId: string) => void, +}) => { + const dataEngine = useDataEngine(); + const queryClient = useQueryClient(); + + const { mutate } = useMutation( + ({ serverData }: Object) => + dataEngine.mutate(addEventWithRelationshipMutation, { + variables: { + serverData, + }, + }), + { + onMutate: (payload: { serverData: Object }) => { + const enrollmentToUpdate = payload.serverData.enrollments?.[0]; + enrollmentToUpdate && onUpdateEnrollment(enrollmentToUpdate); + }, + onSuccess: (_, payload: { linkMode: string, eventIdToRedirectTo?: string }) => { + const queryKey = [ReactQueryAppNamespace, 'linkedEventByOriginEvent', eventId]; + queryClient.refetchQueries(queryKey); + onUpdateEnrollmentSuccess({}); + + if (payload.linkMode === relatedStageActions.ENTER_DATA && payload.eventIdToRedirectTo) { + onNavigateToEvent(payload.eventIdToRedirectTo); + } + }, + onError: () => { + onUpdateEnrollmentError(i18n.t('An error occurred while linking the event')); + }, + }, + ); + + return { + addEventWithRelationship: mutate, + }; +}; diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/hooks/useBuildRelatedStageEventPayload.js b/src/core_modules/capture-core/components/WidgetRelatedStages/hooks/useBuildRelatedStageEventPayload.js new file mode 100644 index 0000000000..f235cd07fb --- /dev/null +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/hooks/useBuildRelatedStageEventPayload.js @@ -0,0 +1,95 @@ +// @flow +import { getConvertedRelatedStageEvent, type RequestEvent, type LinkedRequestEvent } from '../../DataEntries'; +import type { RelatedStageRefPayload } from '../index'; + +export const createServerData = ({ + linkedEvent, + relationship, + enrollment, +}: { + linkedEvent: ?LinkedRequestEvent, + relationship: ?Object, + enrollment: Object, +}) => { + const updatedEnrollment = { ...enrollment, events: [...enrollment.events, linkedEvent] }; + if (linkedEvent) { + return { + enrollments: [updatedEnrollment], + relationships: [relationship], + }; + } + return { + relationships: [relationship], + }; +}; + +export const useBuildRelatedStageEventPayload = () => { + const buildRelatedStageEventPayload = ({ + serverRequestEvent, + relatedStageRef, + programStageId, + programId, + teiId, + enrollmentId, + }: { + serverRequestEvent: ?RequestEvent, + relatedStageRef?: { current: ?RelatedStageRefPayload }, + programStageId: string, + programId: string, + teiId: string, + enrollmentId: string, + }) => { + if (relatedStageRef?.current && relatedStageRef.current.eventHasLinkableStageRelationship()) { + const isValid = relatedStageRef.current.formIsValidOnSave(); + + if (!isValid || !relatedStageRef.current?.getLinkedStageValues || !programStageId || !serverRequestEvent) { + return { + formHasError: true, + linkedEvent: null, + relationship: null, + linkMode: null, + }; + } + + const { selectedRelationshipType, relatedStageDataValues, linkMode } = + relatedStageRef.current.getLinkedStageValues(); + + if (!linkMode) { + return { + formHasError: false, + linkedEvent: null, + relationship: null, + linkMode: null, + }; + } + + const { linkedEvent, relationship } = getConvertedRelatedStageEvent({ + linkMode, + relatedStageDataValues, + serverRequestEvent, + relatedStageType: selectedRelationshipType, + programId, + currentProgramStageId: programStageId, + teiId, + enrollmentId, + }); + + return { + formHasError: false, + linkedEvent, + relationship, + linkMode, + }; + } + return { + formHasError: false, + linkedEvent: null, + relationship: null, + linkMode: null, + }; + }; + + return { + buildRelatedStageEventPayload, + }; +}; diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/useRelatedStages.js b/src/core_modules/capture-core/components/WidgetRelatedStages/hooks/useRelatedStages.js similarity index 90% rename from src/core_modules/capture-core/components/WidgetRelatedStages/useRelatedStages.js rename to src/core_modules/capture-core/components/WidgetRelatedStages/hooks/useRelatedStages.js index 6472c0b801..6622c8df31 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/useRelatedStages.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/hooks/useRelatedStages.js @@ -1,10 +1,10 @@ // @flow import { useMemo } from 'react'; -import { relatedStageStatus } from './constants'; -import { getUserStorageController, userStores } from '../../storageControllers'; -import { useIndexedDBQuery } from '../../utils/reactQueryHelpers'; -import { RELATIONSHIP_ENTITIES } from './WidgetRelatedStages.constants'; -import type { RelationshipType } from './WidgetRelatedStages.types'; +import { relatedStageStatus } from '../constants'; +import { getUserStorageController, userStores } from '../../../storageControllers'; +import { useIndexedDBQuery } from '../../../utils/reactQueryHelpers'; +import { RELATIONSHIP_ENTITIES } from '../WidgetRelatedStages.constants'; +import type { RelationshipType } from '../WidgetRelatedStages.types'; const getRelationshipTypeFromIndexedDB = () => { const storageController = getUserStorageController(); diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/index.js b/src/core_modules/capture-core/components/WidgetRelatedStages/index.js index e75b93c8cc..b23f2475ec 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/index.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/index.js @@ -1,5 +1,6 @@ // @flow -export { WidgetRelatedStages } from './WidgetRelatedStages.component'; +export { RelatedStagesActions } from './RelatedStagesActions'; +export { WidgetRelatedStages } from './WidgetRelatedStages.container'; export type { RelatedStageDataValueStates, RelatedStageRefPayload, diff --git a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/hooks/index.js b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/hooks/index.js index 195e176eef..0596291a5b 100644 --- a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/hooks/index.js +++ b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/hooks/index.js @@ -1,3 +1,5 @@ +// @flow + export { useClientDataValues } from './useClientDataValues'; export { useLinkedEventByOriginId } from './useLinkedEventByOriginId'; export { useRelationshipTypeAccess } from './useRelationshipTypeAccess'; diff --git a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/index.js b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/index.js index b057d60b99..69e57db145 100644 --- a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/index.js +++ b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/index.js @@ -2,3 +2,4 @@ export { WidgetTwoEventWorkspace } from './WidgetTwoEventWorkspace.container'; export { WidgetTwoEventWorkspaceWrapperTypes } from './WidgetTwoEventWorkspaceWrapper.const'; +export { useLinkedEventByOriginId } from './hooks'; From c61951191251dbe707a80242b0f671fb665a315a Mon Sep 17 00:00:00 2001 From: Simona Domnisoru Date: Mon, 6 Jan 2025 17:11:18 +0100 Subject: [PATCH 13/28] feat: show related stages widget in view event page --- .../EnrollmentRegistrationEntry.types.js | 9 ++--- .../RelatedStagesActions.component.js | 37 +++++++++++++++---- 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/EnrollmentRegistrationEntry.types.js b/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/EnrollmentRegistrationEntry.types.js index 6eff575ea5..e0db0b6a92 100644 --- a/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/EnrollmentRegistrationEntry.types.js +++ b/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/EnrollmentRegistrationEntry.types.js @@ -1,12 +1,11 @@ // @flow import type { Node } from 'react'; import type { OrgUnit } from '@dhis2/rules-engine-javascript'; -import type { RegistrationFormMetadata } from '../common/TEIAndEnrollment/useMetadataForRegistrationForm/types'; import type { RenderCustomCardActions } from '../../CardList'; import type { SaveForDuplicateCheck } from '../common/TEIAndEnrollment/DuplicateCheckOnSave'; import type { ExistingUniqueValueDialogActionsComponent } from '../withErrorMessagePostProcessor'; import type { InputAttribute } from './hooks/useFormValues'; -import { RenderFoundation, ProgramStage } from '../../../metaData'; +import { RenderFoundation, ProgramStage, Enrollment } from '../../../metaData'; import type { RelatedStageRefPayload } from '../../WidgetRelatedStages'; import { relatedStageActions } from '../../WidgetRelatedStages'; @@ -67,7 +66,7 @@ export type OwnProps = $ReadOnly<{| skipDuplicateCheck?: ?boolean, trackedEntityInstanceAttributes?: Array, saveButtonText: (trackedEntityName: string) => string, - firstStageMetaData?: ?{ stage: ?ProgramStage }, + firstStageMetaData?: ?{ stage: ProgramStage }, relatedStageRef?: { current: ?RelatedStageRefPayload }, relatedStageActionsOptions?: { [key: $Keys]: { @@ -85,8 +84,8 @@ type ContainerProps = {| onCancel: () => void, isUserInteractionInProgress: boolean, isSavingInProgress: boolean, - enrollmentMetadata: RegistrationFormMetadata, - formFoundation: ?RenderFoundation, + enrollmentMetadata: Enrollment, + formFoundation: RenderFoundation, formId: ?string, saveButtonText: string, |}; diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.component.js b/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.component.js index af35348fc9..0c73a5a731 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.component.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.component.js @@ -174,15 +174,21 @@ const LinkExistingResponse = ({ ); }; -const LinkButton = withStyles(styles)(({ onLink, label, loading, classes }) => ( - onLink ? ( +const LinkButton = withStyles(styles)(({ onLink, label, saveAttempted, errorMessages, classes }) => { + if (!onLink) { + return null; + } + + const disabled = saveAttempted && Object.values(errorMessages).filter(Boolean).length !== 0; + + return (
-
- ) : null -)); + ); +}); const RelatedStagesActionsPlain = ({ classes, @@ -275,7 +281,12 @@ const RelatedStagesActionsPlain = ({ saveAttempted={saveAttempted} errorMessages={errorMessages} /> - + )} @@ -288,7 +299,12 @@ const RelatedStagesActionsPlain = ({ saveAttempted={saveAttempted} errorMessages={errorMessages} /> - + )} @@ -302,7 +318,12 @@ const RelatedStagesActionsPlain = ({ errorMessages={errorMessages} saveAttempted={saveAttempted} /> - + )} From e4a16cae5be2795e15701398bc2e5247dec1f122 Mon Sep 17 00:00:00 2001 From: Simona Domnisoru Date: Mon, 13 Jan 2025 10:05:50 +0100 Subject: [PATCH 14/28] fix: simplify how we determine what event to redirect to --- .../EnrollmentRegistrationEntry.container.js | 4 +- .../hooks/getRedirectIds.js | 63 ++++++++++++++++ .../hooks/useBuildEnrollmentPayload.js | 24 +++--- .../types/duplicateCheckOnSave.types.js | 6 +- .../RegistrationDataEntry.actions.js | 21 +++--- .../RegistrationDataEntry.container.js | 4 +- .../RegistrationDataEntry.epics.js | 34 +++------ .../helpers/getPageToRedirectTo.js | 74 ------------------- .../RegistrationDataEntry/helpers/index.js | 2 - 9 files changed, 102 insertions(+), 130 deletions(-) create mode 100644 src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/hooks/getRedirectIds.js delete mode 100644 src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/getPageToRedirectTo.js delete mode 100644 src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/index.js diff --git a/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/EnrollmentRegistrationEntry.container.js b/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/EnrollmentRegistrationEntry.container.js index e4f014b325..44db04adf5 100644 --- a/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/EnrollmentRegistrationEntry.container.js +++ b/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/EnrollmentRegistrationEntry.container.js @@ -59,9 +59,9 @@ export const EnrollmentRegistrationEntry: ComponentType = ({ } const onSaveWithEnrollment = () => { - const { teiWithEnrollment, formHasError, relatedStageLinkedEvent } = + const { teiWithEnrollment, formHasError, redirect } = buildTeiWithEnrollment(relatedStageRef); - !formHasError && onSave(teiWithEnrollment, relatedStageLinkedEvent); + !formHasError && onSave(teiWithEnrollment, redirect); }; return ( diff --git a/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/hooks/getRedirectIds.js b/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/hooks/getRedirectIds.js new file mode 100644 index 0000000000..dfae1d70aa --- /dev/null +++ b/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/hooks/getRedirectIds.js @@ -0,0 +1,63 @@ +// @flow +import { ProgramStage } from '../../../../metaData'; +import { relatedStageActions } from '../../../WidgetRelatedStages'; + +/** + * An event can be + * - created during first stage registration + * - created and linked during first stage registration + * - autogenerated + * When the event will be created redirect to enrollmentEventEdit + * When the event will not be created redirect to enrollmentEventNew + */ +export const getRedirectIds = ({ + stages, + relatedStageLinkedEvent, + linkMode, + firstStageDuringRegistrationEvent, + autoGenerateEvents, +}: { + stages: Map, + linkMode: ?string, + relatedStageLinkedEvent: ?{ + programStage: string, + event: string, + }, + firstStageDuringRegistrationEvent: ?{ + programStage: string, + event: string, + }, + autoGenerateEvents: Array<{ + programStage: string, + event: string, + orgUnit: string, + }>, +}): { programStageId?: string, eventId?: string } => { + if (relatedStageLinkedEvent && linkMode === relatedStageActions.ENTER_DATA) { + return { eventId: relatedStageLinkedEvent.event }; + } + + const [firstStageWithOpenAfterEnrollment] = [...stages.values()].filter( + ({ openAfterEnrollment }) => openAfterEnrollment, + ); + + if (firstStageWithOpenAfterEnrollment) { + // event will be created during first stage registration + if ( + firstStageDuringRegistrationEvent && + firstStageDuringRegistrationEvent.programStage === firstStageWithOpenAfterEnrollment.id + ) { + return { eventId: firstStageDuringRegistrationEvent.event }; + } + + // event will be autogenerated + const autoGenerateEvent = autoGenerateEvents.find(e => e.programStage === firstStageWithOpenAfterEnrollment.id); + if (autoGenerateEvent) { + return { eventId: autoGenerateEvent.event }; + } + + return { programStageId: firstStageWithOpenAfterEnrollment.id }; + } + + return {}; +}; diff --git a/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/hooks/useBuildEnrollmentPayload.js b/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/hooks/useBuildEnrollmentPayload.js index 8d0a1c4b01..f996eeb263 100644 --- a/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/hooks/useBuildEnrollmentPayload.js +++ b/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/hooks/useBuildEnrollmentPayload.js @@ -28,8 +28,8 @@ import { } from '../helpers'; import type { EnrollmentPayload } from '../EnrollmentRegistrationEntry.types'; import { geometryType, getPossibleTetFeatureTypeKey, buildGeometryProp } from '../../common/TEIAndEnrollment/geometry'; -import { relatedStageActions } from '../../../WidgetRelatedStages'; import type { RelatedStageRefPayload } from '../../../WidgetRelatedStages'; +import { getRedirectIds } from './getRedirectIds'; type DataEntryReduxConverterProps = { programId: string; @@ -94,9 +94,9 @@ export const useBuildEnrollmentPayload = ({ const buildTeiWithEnrollment = (relatedStageRef?: {current: ?RelatedStageRefPayload}): { teiWithEnrollment: EnrollmentPayload, formHasError: boolean, - relatedStageLinkedEvent?: { - programStageId: string, - eventId: string, + redirect: { + programStageId?: string, + eventId?: string, }, } => { if (!formFoundation) throw Error('form foundation object not found'); @@ -156,6 +156,14 @@ export const useBuildEnrollmentPayload = ({ serverMinorVersion: minor, }); + const redirect = getRedirectIds({ + stages, + relatedStageLinkedEvent, + linkMode, + firstStageDuringRegistrationEvent, + autoGenerateEvents, + }); + const allEventsToBeCreated = [firstStageDuringRegistrationEvent, relatedStageLinkedEvent, ...autoGenerateEvents] .filter(Boolean); @@ -186,13 +194,7 @@ export const useBuildEnrollmentPayload = ({ relationships: relationship ? [relationship] : undefined, }, formHasError, - relatedStageLinkedEvent: - relatedStageLinkedEvent && linkMode === relatedStageActions.ENTER_DATA - ? { - programStageId: relatedStageLinkedEvent.programStage, - eventId: relatedStageLinkedEvent.event, - } - : undefined, + redirect, }; }; diff --git a/src/core_modules/capture-core/components/DataEntries/common/TEIAndEnrollment/DuplicateCheckOnSave/types/duplicateCheckOnSave.types.js b/src/core_modules/capture-core/components/DataEntries/common/TEIAndEnrollment/DuplicateCheckOnSave/types/duplicateCheckOnSave.types.js index da1e6bcc39..ee773e5d33 100644 --- a/src/core_modules/capture-core/components/DataEntries/common/TEIAndEnrollment/DuplicateCheckOnSave/types/duplicateCheckOnSave.types.js +++ b/src/core_modules/capture-core/components/DataEntries/common/TEIAndEnrollment/DuplicateCheckOnSave/types/duplicateCheckOnSave.types.js @@ -4,8 +4,8 @@ import type { TeiPayload } from '../../../../../Pages/common/TEIRelationshipsWid export type SaveForDuplicateCheck = ( teiWithEnrollment: EnrollmentPayload | TeiPayload, - relatedStageLinkedEvent?: { - programStageId: string, - eventId: string, + redirect: { + programStageId?: string, + eventId?: string, }, ) => void; diff --git a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.actions.js b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.actions.js index 55731792cd..6cd66b0309 100644 --- a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.actions.js +++ b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.actions.js @@ -49,29 +49,28 @@ export const saveNewTrackedEntityInstance = (candidateForRegistration: any) => export const startSavingNewTrackedEntityInstanceWithEnrollment = ( enrollmentPayload: EnrollmentPayload, uid: string, - relatedStageLinkedEvent?: { - programStageId: string, - eventId: string, + redirect: { + programStageId?: string, + eventId?: string, }, ) => actionCreator(registrationFormActionTypes.NEW_TRACKED_ENTITY_INSTANCE_WITH_ENROLLMENT_SAVE_START)({ enrollmentPayload, uid, - relatedStageLinkedEvent, + redirect, }); export const saveNewTrackedEntityInstanceWithEnrollment = ({ candidateForRegistration, - pageToRedirectTo, uid, - stageId, - eventId, + redirect, }: { candidateForRegistration: any, - pageToRedirectTo: string, uid: string, - stageId?: string, - eventId?: string, + redirect: { + programStageId?: string, + eventId?: string, + } }) => actionCreator(registrationFormActionTypes.NEW_TRACKED_ENTITY_INSTANCE_WITH_ENROLLMENT_SAVE)( { ...candidateForRegistration }, @@ -84,7 +83,7 @@ export const saveNewTrackedEntityInstanceWithEnrollment = ({ }, commit: { type: registrationFormActionTypes.NEW_TRACKED_ENTITY_INSTANCE_WITH_ENROLLMENT_SAVE_COMPLETED, - meta: { pageToRedirectTo, stageId, uid, eventId }, + meta: { redirect, uid }, }, rollback: { type: registrationFormActionTypes.NEW_TRACKED_ENTITY_INSTANCE_WITH_ENROLLMENT_SAVE_FAILED, diff --git a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.container.js b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.container.js index 2527f67c65..04032e012f 100644 --- a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.container.js +++ b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.container.js @@ -30,13 +30,13 @@ export const RegistrationDataEntry: ComponentType = ({ [dispatch]); const dispatchOnSaveWithEnrollment = useCallback( - (enrollmentPayload, relatedStageLinkedEvent) => { + (enrollmentPayload, redirect) => { const uid = uuid(); dispatch( startSavingNewTrackedEntityInstanceWithEnrollment( enrollmentPayload, uid, - relatedStageLinkedEvent, + redirect, ), ); }, diff --git a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.epics.js b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.epics.js index 390e95aabc..9c78d3e97f 100644 --- a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.epics.js +++ b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.epics.js @@ -8,15 +8,10 @@ import { saveNewTrackedEntityInstance, saveNewTrackedEntityInstanceWithEnrollment, } from './RegistrationDataEntry.actions'; -import { getTrackerProgramThrowIfNotFound } from '../../../../metaData'; import { navigateToEnrollmentOverview, } from '../../../../actions/navigateToEnrollmentOverview/navigateToEnrollmentOverview.actions'; import { buildUrlQueryString } from '../../../../utils/routing'; -import { - getPageToRedirectTo, - PAGES, -} from './helpers'; import { cleanUpUid } from '../NewPage.actions'; export const startSavingNewTrackedEntityInstanceEpic: Epic = (action$: InputObservable) => @@ -48,20 +43,11 @@ export const completeSavingNewTrackedEntityInstanceEpic: Epic = (action$: InputO export const startSavingNewTrackedEntityInstanceWithEnrollmentEpic: Epic = ( action$: InputObservable, - store: ReduxStore, ) => action$.pipe( ofType(registrationFormActionTypes.NEW_TRACKED_ENTITY_INSTANCE_WITH_ENROLLMENT_SAVE_START), map((action) => { - const { currentSelections: { programId } } = store.value; - const { enrollmentPayload, uid, relatedStageLinkedEvent } = action.payload; - const { stages, useFirstStageDuringRegistration } = getTrackerProgramThrowIfNotFound(programId); - const { stageId, eventId, pageToRedirectTo } = getPageToRedirectTo({ - stages, - events: enrollmentPayload.enrollments[0]?.events, - useFirstStageDuringRegistration, - relatedStageLinkedEvent, - }); + const { enrollmentPayload, uid, redirect } = action.payload; return saveNewTrackedEntityInstanceWithEnrollment({ candidateForRegistration: { @@ -69,9 +55,7 @@ export const startSavingNewTrackedEntityInstanceWithEnrollmentEpic: Epic = ( enrollmentPayload, ], }, - pageToRedirectTo, - eventId, - stageId, + redirect, uid, }); }), @@ -89,7 +73,7 @@ export const completeSavingNewTrackedEntityInstanceWithEnrollmentEpic = ( payload: { bundleReport: { typeReportMap }, }, - meta: { uid, pageToRedirectTo, stageId, eventId }, + meta: { uid, redirect }, } = action; const { currentSelections: { orgUnitId, programId }, @@ -103,23 +87,23 @@ export const completeSavingNewTrackedEntityInstanceWithEnrollmentEpic = ( return EMPTY; } - if (pageToRedirectTo === PAGES.enrollmentEventNew) { + if (redirect.programStageId) { history.push( - `/${pageToRedirectTo}?${buildUrlQueryString({ + `/enrollmentEventNew?${buildUrlQueryString({ programId, orgUnitId, teiId, enrollmentId, - stageId, + stageId: redirect.programStageId, })}`, ); return EMPTY; } - if (pageToRedirectTo === PAGES.enrollmentEventEdit) { + if (redirect.eventId) { history.push( - `/${pageToRedirectTo}?${buildUrlQueryString({ - eventId, + `/enrollmentEventEdit?${buildUrlQueryString({ + eventId: redirect.eventId, orgUnitId, initMode: dataEntryKeys.EDIT, })}`, diff --git a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/getPageToRedirectTo.js b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/getPageToRedirectTo.js deleted file mode 100644 index 1b99b1c8fa..0000000000 --- a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/getPageToRedirectTo.js +++ /dev/null @@ -1,74 +0,0 @@ -// @flow -import { ProgramStage } from '../../../../../metaData'; - -export const PAGES = { - enrollmentEventNew: 'enrollmentEventNew', - enrollmentEventEdit: 'enrollmentEventEdit', - enrollmentDashboard: 'enrollmentDashboard', -}; -/** - * An event can be - * - created during first stage registration - * - created and linked during first stage registration - * - autogenerated - * When the event will be created redirect to enrollmentEventEdit - * When the event will not be created redirect to enrollmentEventNew - */ -export const getPageToRedirectTo = ({ - stages, - events, - useFirstStageDuringRegistration, - relatedStageLinkedEvent, -}: { - stages: Map, - events: any, - useFirstStageDuringRegistration: boolean, - relatedStageLinkedEvent?: { - programStageId: string, - eventId: string, - }, -}): { stageId?: string, eventId?: string, pageToRedirectTo: string } => { - const stagesArray = [...stages.values()]; - const [firstStageWithOpenAfterEnrollment] = stagesArray.filter(({ openAfterEnrollment }) => openAfterEnrollment); - - // event will be created and linked during first stage registration - if (relatedStageLinkedEvent?.eventId) { - const { programStageId, eventId } = relatedStageLinkedEvent; - return { - stageId: programStageId, - eventId, - pageToRedirectTo: PAGES.enrollmentEventEdit, - }; - } - - if (firstStageWithOpenAfterEnrollment) { - // event will be created during first stage registration - if (useFirstStageDuringRegistration && stagesArray[0].id === firstStageWithOpenAfterEnrollment.id) { - const eventId = events.find(e => e.programStage === firstStageWithOpenAfterEnrollment.id).event; - - return { - programStageIdToRedirectTo: firstStageWithOpenAfterEnrollment.id, - eventId, - pageToRedirectTo: PAGES.enrollmentEventEdit, - }; - } - - // event will be autogenerated - if (stagesArray.find(stage => stage.autoGenerateEvent && stage.id === firstStageWithOpenAfterEnrollment.id)) { - const eventId = events.find(e => e.programStage === firstStageWithOpenAfterEnrollment.id).event; - - return { - programStageIdToRedirectTo: firstStageWithOpenAfterEnrollment.id, - eventId, - pageToRedirectTo: PAGES.enrollmentEventEdit, - }; - } - - return { - programStageIdToRedirectTo: firstStageWithOpenAfterEnrollment.id, - pageToRedirectTo: PAGES.enrollmentEventNew, - }; - } - - return { pageToRedirectTo: PAGES.enrollmentDashboard }; -}; diff --git a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/index.js b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/index.js deleted file mode 100644 index a82f1974d9..0000000000 --- a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/index.js +++ /dev/null @@ -1,2 +0,0 @@ -// @flow -export { getPageToRedirectTo, PAGES } from './getPageToRedirectTo'; From 9d9879b47fc7ac8d1215dbf7033b12e1533d6eea Mon Sep 17 00:00:00 2001 From: eirikhaugstulen Date: Thu, 16 Jan 2025 10:47:42 +0100 Subject: [PATCH 15/28] fix: remove breaking tests --- .../WidgetsForEnrollmentAddEventPage.feature | 9 --------- .../WidgetsForEnrollmentEditEvent.feature | 13 +++---------- 2 files changed, 3 insertions(+), 19 deletions(-) diff --git a/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentAddEventPage/WidgetsForEnrollmentAddEventPage.feature b/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentAddEventPage/WidgetsForEnrollmentAddEventPage.feature index cd6062c7fe..f4dfa1cc08 100644 --- a/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentAddEventPage/WidgetsForEnrollmentAddEventPage.feature +++ b/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentAddEventPage/WidgetsForEnrollmentAddEventPage.feature @@ -38,15 +38,6 @@ Feature: The user interacts with the widgets on the enrollment add event page And the user sees the owner organisation unit And the user sees the last update date - Scenario: You can delete a tracked entity from the profile widget - Given you add a new tracked entity in the Malaria focus investigation program - When the user clicks the "Back to all stages and events" button - When the user clicks the "New Event" button - When you open the overflow menu and click the "Delete Focus area" button - Then you see the delete tracked entity confirmation modal - When you confirm by clicking the "Yes, delete Focus area" button - Then you are redirected to the home page - Scenario: User can open the delete modal Given you land on the enrollment add event page by having typed #/enrollmentEventNew?programId=IpHINAT79UW&orgUnitId=DiszpKrYNg8&teiId=EaOyKGOIGRp&enrollmentId=wBU0RAsYjKE&stageId=A03MvHHogjR Then the enrollment widget should be opened diff --git a/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentEditEvent/WidgetsForEnrollmentEditEvent.feature b/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentEditEvent/WidgetsForEnrollmentEditEvent.feature index defa075922..174e58dcc6 100644 --- a/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentEditEvent/WidgetsForEnrollmentEditEvent.feature +++ b/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentEditEvent/WidgetsForEnrollmentEditEvent.feature @@ -38,13 +38,6 @@ Feature: The user interacts with the widgets on the enrollment edit event And the user sees the owner organisation unit And the user sees the last update date - Scenario: You can delete a tracked entity from the profile widget - Given you add a new tracked entity in the Malaria focus investigation program - When you open the overflow menu and click the "Delete Focus area" button - Then you see the delete tracked entity confirmation modal - When you confirm by clicking the "Yes, delete Focus area" button - Then you are redirected to the home page - Scenario: User can open the delete modal Given you land on the enrollment edit event page by having typed /#/enrollmentEventEdit?eventId=XGLkLlOXgmE&orgUnitId=DiszpKrYNg8 Then the enrollment widget should be opened @@ -76,7 +69,7 @@ Feature: The user interacts with the widgets on the enrollment edit event Then the event has the user Tracker demo User assigned When you remove the assigned user Then the event has no assignd user - + @v>=41 Scenario: The user can view an event changelog on the enrollment edit event Given you land on the enrollment edit event page by having typed /#/enrollmentEventEdit?eventId=QsAhMiZtnl2&orgUnitId=DiszpKrYNg8 @@ -85,7 +78,7 @@ Feature: The user interacts with the widgets on the enrollment edit event And the changelog modal should contain data # One row is filtered out as the metadata is no longer there And the number of changelog table rows should be 9 - + @v>=41 Scenario: The user can change changelog page size Given you land on the enrollment edit event page by having typed /#/enrollmentEventEdit?eventId=QsAhMiZtnl2&orgUnitId=DiszpKrYNg8 @@ -96,7 +89,7 @@ Feature: The user interacts with the widgets on the enrollment edit event And you change the page size to 100 Then the number of changelog table rows should be 37 Then the table footer should display page 1 - + @v>=41 Scenario: The user can move to the next page in the changelog Given you land on the enrollment edit event page by having typed /#/enrollmentEventEdit?eventId=QsAhMiZtnl2&orgUnitId=DiszpKrYNg8 From e1b9836475364d92f79b05eb2b4e03eb2e70fd05 Mon Sep 17 00:00:00 2001 From: eirikhaugstulen Date: Thu, 16 Jan 2025 10:58:55 +0100 Subject: [PATCH 16/28] Revert "fix: remove breaking tests" This reverts commit 9d9879b47fc7ac8d1215dbf7033b12e1533d6eea. --- .../WidgetsForEnrollmentAddEventPage.feature | 9 +++++++++ .../WidgetsForEnrollmentEditEvent.feature | 13 ++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentAddEventPage/WidgetsForEnrollmentAddEventPage.feature b/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentAddEventPage/WidgetsForEnrollmentAddEventPage.feature index f4dfa1cc08..cd6062c7fe 100644 --- a/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentAddEventPage/WidgetsForEnrollmentAddEventPage.feature +++ b/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentAddEventPage/WidgetsForEnrollmentAddEventPage.feature @@ -38,6 +38,15 @@ Feature: The user interacts with the widgets on the enrollment add event page And the user sees the owner organisation unit And the user sees the last update date + Scenario: You can delete a tracked entity from the profile widget + Given you add a new tracked entity in the Malaria focus investigation program + When the user clicks the "Back to all stages and events" button + When the user clicks the "New Event" button + When you open the overflow menu and click the "Delete Focus area" button + Then you see the delete tracked entity confirmation modal + When you confirm by clicking the "Yes, delete Focus area" button + Then you are redirected to the home page + Scenario: User can open the delete modal Given you land on the enrollment add event page by having typed #/enrollmentEventNew?programId=IpHINAT79UW&orgUnitId=DiszpKrYNg8&teiId=EaOyKGOIGRp&enrollmentId=wBU0RAsYjKE&stageId=A03MvHHogjR Then the enrollment widget should be opened diff --git a/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentEditEvent/WidgetsForEnrollmentEditEvent.feature b/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentEditEvent/WidgetsForEnrollmentEditEvent.feature index 174e58dcc6..defa075922 100644 --- a/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentEditEvent/WidgetsForEnrollmentEditEvent.feature +++ b/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentEditEvent/WidgetsForEnrollmentEditEvent.feature @@ -38,6 +38,13 @@ Feature: The user interacts with the widgets on the enrollment edit event And the user sees the owner organisation unit And the user sees the last update date + Scenario: You can delete a tracked entity from the profile widget + Given you add a new tracked entity in the Malaria focus investigation program + When you open the overflow menu and click the "Delete Focus area" button + Then you see the delete tracked entity confirmation modal + When you confirm by clicking the "Yes, delete Focus area" button + Then you are redirected to the home page + Scenario: User can open the delete modal Given you land on the enrollment edit event page by having typed /#/enrollmentEventEdit?eventId=XGLkLlOXgmE&orgUnitId=DiszpKrYNg8 Then the enrollment widget should be opened @@ -69,7 +76,7 @@ Feature: The user interacts with the widgets on the enrollment edit event Then the event has the user Tracker demo User assigned When you remove the assigned user Then the event has no assignd user - + @v>=41 Scenario: The user can view an event changelog on the enrollment edit event Given you land on the enrollment edit event page by having typed /#/enrollmentEventEdit?eventId=QsAhMiZtnl2&orgUnitId=DiszpKrYNg8 @@ -78,7 +85,7 @@ Feature: The user interacts with the widgets on the enrollment edit event And the changelog modal should contain data # One row is filtered out as the metadata is no longer there And the number of changelog table rows should be 9 - + @v>=41 Scenario: The user can change changelog page size Given you land on the enrollment edit event page by having typed /#/enrollmentEventEdit?eventId=QsAhMiZtnl2&orgUnitId=DiszpKrYNg8 @@ -89,7 +96,7 @@ Feature: The user interacts with the widgets on the enrollment edit event And you change the page size to 100 Then the number of changelog table rows should be 37 Then the table footer should display page 1 - + @v>=41 Scenario: The user can move to the next page in the changelog Given you land on the enrollment edit event page by having typed /#/enrollmentEventEdit?eventId=QsAhMiZtnl2&orgUnitId=DiszpKrYNg8 From 88d021e9bd8aceb03cb8384a9604e7514f8ecc86 Mon Sep 17 00:00:00 2001 From: Simona Domnisoru Date: Thu, 23 Jan 2025 15:42:11 +0100 Subject: [PATCH 17/28] chore: imporve Link button UI --- .../RelatedStagesActions.component.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.component.js b/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.component.js index 0c73a5a731..3a7c16cd12 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.component.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.component.js @@ -174,16 +174,16 @@ const LinkExistingResponse = ({ ); }; -const LinkButton = withStyles(styles)(({ onLink, label, saveAttempted, errorMessages, classes }) => { +const LinkButton = withStyles(styles)(({ onLink, label, errorMessages, classes }) => { if (!onLink) { return null; } - const disabled = saveAttempted && Object.values(errorMessages).filter(Boolean).length !== 0; + const disabled = Object.values(errorMessages).some(Boolean); return (
-
@@ -284,7 +284,6 @@ const RelatedStagesActionsPlain = ({ @@ -302,7 +301,6 @@ const RelatedStagesActionsPlain = ({ @@ -321,7 +319,6 @@ const RelatedStagesActionsPlain = ({ From 91c18938c217a5a9d0ce939092a7255cef5cc0e6 Mon Sep 17 00:00:00 2001 From: Simona Domnisoru Date: Thu, 23 Jan 2025 15:42:53 +0100 Subject: [PATCH 18/28] chore: fix 409 error for enrolledAt date in the future --- .../hooks/useBuildRelatedStageEventPayload.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/hooks/useBuildRelatedStageEventPayload.js b/src/core_modules/capture-core/components/WidgetRelatedStages/hooks/useBuildRelatedStageEventPayload.js index f235cd07fb..86b359ce25 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/hooks/useBuildRelatedStageEventPayload.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/hooks/useBuildRelatedStageEventPayload.js @@ -11,10 +11,9 @@ export const createServerData = ({ relationship: ?Object, enrollment: Object, }) => { - const updatedEnrollment = { ...enrollment, events: [...enrollment.events, linkedEvent] }; if (linkedEvent) { return { - enrollments: [updatedEnrollment], + events: [...enrollment.events, linkedEvent], relationships: [relationship], }; } From 874ed03e6549b8b2a4a3a548ebd7ba76bc404021 Mon Sep 17 00:00:00 2001 From: Simona Domnisoru Date: Mon, 27 Jan 2025 11:47:13 +0100 Subject: [PATCH 19/28] test: [DHIS2-17853] Related stages Cypress tests (#3939) --- .../e2e/RelatedStages/RelatedStages.feature | 100 +++++++ cypress/e2e/RelatedStages/RelatedStages.js | 258 ++++++++++++++++++ .../OrgUnitField/OrgUnitField.component.js | 5 +- .../OrgUnitSelectorForRelatedStages.js | 1 + .../LinkToExisting.component.js | 1 + .../RelatedStagesActions.component.js | 10 +- .../DeleteActionButton/DeleteActionButton.js | 1 + .../OverflowMenu/Modal/UnlinkModal.js | 1 + 8 files changed, 374 insertions(+), 3 deletions(-) create mode 100644 cypress/e2e/RelatedStages/RelatedStages.feature create mode 100644 cypress/e2e/RelatedStages/RelatedStages.js diff --git a/cypress/e2e/RelatedStages/RelatedStages.feature b/cypress/e2e/RelatedStages/RelatedStages.feature new file mode 100644 index 0000000000..fb955688e4 --- /dev/null +++ b/cypress/e2e/RelatedStages/RelatedStages.feature @@ -0,0 +1,100 @@ +Feature: Related stages + + Scenario: Edit event -> User is able to link and unlink an existing event + Given you land on a enrollment page domain by having typed #/enrollmentEventEdit?eventId=TwoGi1mUFFw&orgUnitId=lyONqUkY1Bq + And the Related stages Actions is visible at the bottom of the page + And the schedule and enter details actions are disabled + And you select the Link to an existing event action + When you select the first existing Baby Postnatal event in the list + And you click the Link button + Then you can see the Baby Postnatal linked event + And the Related stages Actions is not visible at the bottom of the page + When you unlink the Baby Postnatal linked event + And the Related stages Actions is visible at the bottom of the page + + Scenario: Edit event -> User is able to schedule an event in a different orgUnit + Given you land on a enrollment page domain by having typed #/enrollment?enrollmentId=VOU2Qe7T49r&orgUnitId=VFF7f43dJv4&programId=IpHINAT79UW&teiId=rBqwRgXvjk0 + And you delete the events of the enrollmentId VOU2Qe7T49r + And you open the Birth new event page and fill in the required data in the form + And you click the Complete button + And you open the Birth event edit page + And the Related stages Actions is visible at the bottom of the page + And the link to an existing actions is disabled + And you select the Schedule event action + When you fill in the required values for the Baby postnatal event when scheduling + And you click the Schedule action button + Then you can see the Baby Postnatal linked event + And the Related stages Actions is not visible at the bottom of the page + + Scenario: Edit event -> User is able to enter details in a different orgUnit + Given you land on a enrollment page domain by having typed #/enrollment?enrollmentId=VOU2Qe7T49r&orgUnitId=VFF7f43dJv4&programId=IpHINAT79UW&teiId=rBqwRgXvjk0 + And you delete the events of the enrollmentId VOU2Qe7T49r + And you open the Birth new event page and fill in the required data in the form + And you click the Complete button + And you open the Birth event edit page + And the Related stages Actions is visible at the bottom of the page + And the link to an existing actions is disabled + And you select the Enter details now action + When you fill in the required values for the Baby postnatal event when entering details + And you click the Enter details action button + Then you can see the Birth linked event + And you can see the Baby postnatal new event form where you can enter details + + Scenario: New event -> User is able to link and unlink an existing event + Given you land on a enrollment page domain by having typed #/enrollment?enrollmentId=EOxeNf2MdBf&orgUnitId=VFF7f43dJv4&programId=IpHINAT79UW&teiId=QhoMgzeGuGq + And you delete the Birth event + And you open the Birth new event page and fill in the required data in the form + And the Related stages Actions is visible at the bottom of the page + And the schedule and enter details actions are disabled + And you select the Link to an existing event action + When you select the first existing Baby Postnatal event in the list + And you click the Complete button + And you open the Birth event edit page + Then you can see the Baby Postnatal linked event + And the Related stages Actions is not visible at the bottom of the page + When you unlink the Baby Postnatal linked event + And the Related stages Actions is visible at the bottom of the page + + Scenario: New event -> User is able to schedule an event in a different orgUnit + Given you land on a enrollment page domain by having typed #/enrollment?enrollmentId=EOxeNf2MdBf&orgUnitId=VFF7f43dJv4&programId=IpHINAT79UW&teiId=QhoMgzeGuGq + And you delete the events of the enrollmentId EOxeNf2MdBf + And you open the Birth new event page and fill in the required data in the form + And the Related stages Actions is visible at the bottom of the page + And the link to an existing actions is disabled + And you select the Schedule event action + When you fill in the required values for the Baby postnatal event when scheduling + And you click the Complete button + Then you are redirect to the enrollment dasboard and you see the 2 linked events in different orgUnits + + Scenario: New event -> User is able to enter details in a different orgUnit + Given you land on a enrollment page domain by having typed #/enrollment?enrollmentId=YRgcBLZbsb6&orgUnitId=VFF7f43dJv4&programId=IpHINAT79UW&teiId=zFbWLBo4jta + And you delete the events of the enrollmentId YRgcBLZbsb6 + And you open the Birth new event page and fill in the required data in the form + And the Related stages Actions is visible at the bottom of the page + And the link to an existing actions is disabled + And you select the Enter details now action + When you fill in the required values for the Baby postnatal event when entering details + And you click the Complete button + Then you can see the Birth linked event + And you can see the Baby postnatal new event form where you can enter details + + Scenario: Enroll trackedEntity -> User is able to schedule an event in a different orgUnit + Given you are in Child programme and Tombo Wallah CHP organization unit registration page + When you fill the Child Program program registration form with unique values + And you select the Schedule event action + And you fill in the required values for the Baby postnatal event when scheduling + And you click the save person submit button + Then you are redirect to the enrollment dasboard and you see the 2 linked events in different orgUnits + And you delete the recently added tracked entity + + Scenario: Enroll trackedEntity -> User is able to enter details in a different orgUnit + Given you are in Child programme and Tombo Wallah CHP organization unit registration page + When you fill the Child Program program registration form with unique values + And you select the Enter details now action + And you fill in the required values for the Baby postnatal event when entering details + And you click the save person submit button + Then you can see the Birth linked event + And you can see the Baby postnatal new event form where you can enter details + And you navigate to the Enrollment dashboard + And you are redirect to the enrollment dasboard and you see the 2 linked events in different orgUnits + And you delete the recently added tracked entity \ No newline at end of file diff --git a/cypress/e2e/RelatedStages/RelatedStages.js b/cypress/e2e/RelatedStages/RelatedStages.js new file mode 100644 index 0000000000..ecc3b4898c --- /dev/null +++ b/cypress/e2e/RelatedStages/RelatedStages.js @@ -0,0 +1,258 @@ +import { Given, Then, When, defineStep as And } from '@badeball/cypress-cucumber-preprocessor'; +import { getCurrentYear } from '../../support/date'; + +Given(/^you land on a enrollment page domain by having typed (.*)$/, (url) => { + cy.visit(url); + cy.get('[data-test="person-selector-container"]').contains('Person'); +}); + +And(/^the Related stages Actions is ?(.*) visible at the bottom of the page/, (not) => { + cy.get('[data-test="related-stages-section"]') + .should(not ? 'not.exist' : 'exist'); +}); + +And('you select the Link to an existing event action', () => { + cy.get('[data-test="related-stages-section"]') + .and('contain', 'Actions - Birth to Baby postnatal'); + cy.get('[data-test="related-stages-actions-link-existing-response"]') + .click(); +}); + +And('you select the Schedule event action', () => { + cy.get('[data-test="related-stages-section"]') + .and('contain', 'Actions - Birth to Baby postnatal'); + cy.get('[data-test="related-stages-actions-schedule"]') + .click(); +}); + +And('you select the Enter details now action', () => { + cy.get('[data-test="related-stages-section"]') + .and('contain', 'Actions - Birth to Baby postnatal'); + cy.get('[data-test="related-stages-actions-enter-details"]') + .click(); +}); + +When('you select the first existing Baby Postnatal event in the list', () => { + cy.get('[data-test="related-stages-existing-response-list-content"]') + .click(); + cy.get('[data-test="dhis2-uicore-singleselectoption"]') + .eq(0) + .click(); +}); + +When('you click the Link button', () => { + cy.get('[data-test="related-stages-buttons-link-existing-response"]') + .click(); +}); + +Then('you can see the Baby Postnatal linked event', () => { + cy.get('[data-test="enrollment-viewEvent-page"]') + .should('contain', 'This Birth event is linked to a Baby Postnatal event. Review the linked event details before entering data below'); +}); + +Then('you can see the Birth linked event', () => { + cy.get('[data-test="enrollment-editEvent-page"]') + .should('contain', 'This Baby Postnatal event is linked to a Birth event. Review the linked event details before entering data below'); +}); + +When('you unlink the Baby Postnatal linked event', () => { + cy.get('[data-test="widget-linked-event-overflow-menu"]') + .click(); + cy.get('[data-test="event-overflow-unlink-event"]') + .click(); + cy.get('[data-test="event-overflow-unlink-event-confirm"]') + .click(); +}); + +And('you delete the Birth event', () => { + cy.get('[data-test="overflow-button"]') + .eq(0) + .click(); + cy.get('[data-test="stages-and-events-delete"]') + .click(); + cy.get('[data-test="dhis2-uicore-modal"]').within(() => { + cy.contains('button', 'Yes, delete event') + .click(); + }); +}); + +And('you open the Birth event edit page', () => { + cy.get('[data-test="stages-and-events-widget"]') + .find('[data-test="widget-contents"]') + .contains('[data-test="stage-content"]', 'Birth') + .find('[data-test="dhis2-uicore-datatablerow"]') + .eq(1) + .click(); +}); + +And('you open the Birth new event page and fill in the required data in the form', () => { + cy.get('[data-test="stages-and-events-widget"]') + .find('[data-test="widget-contents"]') + .contains('[data-test="stage-content"]', 'Birth') + .find('[data-test="create-new-button"]') + .click(); + + cy.get('[data-test="dataentry-field-occurredAt"]') + .within(() => { + cy.get('input[type="text"]') + .eq(0) + .type(`${getCurrentYear()}-07-01`) + .blur(); + }); +}); + +When('you fill in the required values for the Baby postnatal event when scheduling', () => { + cy.get('[data-test="related-stages-section"]') + .within(() => { + cy.get('input[type="text"]') + .eq(0) + .type(`${getCurrentYear()}-08-01`) + .blur(); + + cy.get('input[type="text"]') + .eq(1) + .type('Barmoi CH'); + + cy.contains('Barmoi CHP') + .click(); + }); +}); + +And('you click the Schedule action button', () => { + cy.get('[data-test="related-stages-buttons-schedule"]') + .click(); +}); + +When('you fill in the required values for the Baby postnatal event when entering details', () => { + cy.get('[data-test="related-stages-section"]') + .within(() => { + cy.get('input[type="text"]') + .eq(0) + .type('Barmoi CH'); + + cy.contains('Barmoi CHP') + .click(); + }); +}); + +And('you click the Enter details action button', () => { + cy.get('[data-test="related-stages-buttons-enter-details"]') + .click(); +}); + +Then('you can see the Baby postnatal new event form where you can enter details', () => { + cy.get('[data-test="edit-event-report-tab"]') + .should('contain', 'Report'); + cy.get('[data-test="edit-event-schedule-tab"]') + .should('contain', 'Schedule'); + cy.get('[data-test="dhis2-uicore-button"]') + .contains('Cancel') + .should('exist'); +}); + +And('the schedule and enter details actions are disabled', () => { + cy.get('input[name="related-stage-action-SCHEDULE_IN_ORG"]') + .should('be.disabled'); + cy.get('input[name="related-stage-action-ENTER_DATA"]') + .should('be.disabled'); +}); + +And('the link to an existing actions is disabled', () => { + cy.get('input[name="related-stage-action-LINK_EXISTING_RESPONSE"]') + .should('be.disabled'); +}); + +And('you click the Complete button', () => { + cy.get('[data-test="dhis2-uicore-button"]') + .contains('Complete') + .click(); +}); + +Then('you are redirect to the enrollment dasboard and you see the 2 linked events in different orgUnits', () => { + cy.get('[data-test="stages-and-events-widget"]') + .find('[data-test="widget-contents"]') + .contains('[data-test="stage-content"]', 'Birth') + .find('[data-test="dhis2-uicore-datatablerow"]') + .eq(1) + .contains('Tombo Wallah CHP'); + + cy.get('[data-test="stages-and-events-widget"]') + .find('[data-test="widget-contents"]') + .contains('[data-test="stage-content"]', 'Baby Postnatal') + .find('[data-test="dhis2-uicore-datatablerow"]') + .eq(1) + .contains('Barmoi CHP'); +}); + +And('you fill the Child Program program registration form with unique values', () => { + cy.get('input[type="text"]') + .eq(1) + .type('2021-01-01') + .blur(); + cy.get('input[type="text"]') + .eq(2) + .type(20); + cy.get('input[type="text"]') + .eq(3) + .type(30) + .blur(); + cy.get('input[type="text"]') + .eq(4) + .type(`Sarah-${Math.round((new Date()).getTime() / 1000)}`) + .blur(); + cy.get('input[type="text"]') + .eq(5) + .type(`Beth-${Math.round((new Date()).getTime() / 1000)}`) + .blur(); + cy.get('input[type="text"]') + .eq(7) + .type('2021-01-01') + .blur(); +}); + +And('you delete the recently added tracked entity', () => { + cy.get('[data-test="profile-widget"]') + .contains('Person profile') + .should('exist'); + cy.get('[data-test="widget-profile-overflow-menu"]') + .click(); + cy.contains('Delete Person') + .click(); + cy.get('[data-test="widget-profile-delete-modal"]').within(() => { + cy.contains('Yes, delete Person') + .click(); + }); + cy.url().should('include', 'selectedTemplateId=IpHINAT79UW'); +}); + +And(/^you click the save (.*) submit button$/, (TEType) => { + cy.contains(`Save ${TEType}`) + .click(); +}); + +And('you navigate to the Enrollment dashboard', () => { + cy.contains('Enrollment dashboard') + .click(); + cy.contains('Yes, discard changes') + .click(); +}); + +And('you are in Child programme and Tombo Wallah CHP organization unit registration page', () => { + cy.visit('/#/new?programId=IpHINAT79UW&orgUnitId=VFF7f43dJv4'); +}); + +And(/^you delete the events of the enrollmentId (.*)$/, (enrollmentId) => { + cy.buildApiUrl('tracker', `enrollments/${enrollmentId}?fields=events[event]`) + .then(url => cy.request(url)) + .then(({ body }) => { + const { events } = body; + + if (events) { + cy.buildApiUrl('tracker?async=false&importStrategy=DELETE').then((eventUrl) => { + cy.request('POST', eventUrl, { events }); + cy.reload(); + }); + } + }); +}); + diff --git a/src/core_modules/capture-core/components/FormFields/New/Fields/OrgUnitField/OrgUnitField.component.js b/src/core_modules/capture-core/components/FormFields/New/Fields/OrgUnitField/OrgUnitField.component.js index 0abf732539..2ab02dd90d 100644 --- a/src/core_modules/capture-core/components/FormFields/New/Fields/OrgUnitField/OrgUnitField.component.js +++ b/src/core_modules/capture-core/components/FormFields/New/Fields/OrgUnitField/OrgUnitField.component.js @@ -51,7 +51,8 @@ type Props = { debounceFieldContainer: string, orgUnitTreeContainer: string, }, - previousOrgUnitId?: string + previousOrgUnitId?: string, + dataTest?: string, }; const OrgUnitFieldPlain = (props: Props) => { @@ -63,6 +64,7 @@ const OrgUnitFieldPlain = (props: Props) => { maxTreeHeight, disabled, previousOrgUnitId, + dataTest, } = props; const [searchText, setSearchText] = React.useState(undefined); const [key, setKey] = React.useState(undefined); @@ -156,6 +158,7 @@ const OrgUnitFieldPlain = (props: Props) => { placeholder={i18n.t('Search')} classes={classes} disabled={disabled} + dataTest={dataTest} />
{!disabled && diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/FormComponents/OrgUnitSelectorForRelatedStages.js b/src/core_modules/capture-core/components/WidgetRelatedStages/FormComponents/OrgUnitSelectorForRelatedStages.js index 400494ed0c..c8b53c46ed 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/FormComponents/OrgUnitSelectorForRelatedStages.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/FormComponents/OrgUnitSelectorForRelatedStages.js @@ -72,6 +72,7 @@ export const OrgUnitSelectorForRelatedStages = ({ onBlur={handleDeselect} styles={baseInputStyles} errorMessage={shouldShowError && errorMessages?.orgUnit} + data-test="relates-stages-org-unit" /> ); }; diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/LinkToExisting/LinkToExisting.component.js b/src/core_modules/capture-core/components/WidgetRelatedStages/LinkToExisting/LinkToExisting.component.js index dbc8d442d5..71e4cff931 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/LinkToExisting/LinkToExisting.component.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/LinkToExisting/LinkToExisting.component.js @@ -53,6 +53,7 @@ export const LinkToExistingPlain = ({ className={classes.singleSelectField} error={saveAttempted && !!errorMessages.linkedEventId} validationText={saveAttempted && errorMessages.linkedEventId} + dataTest="related-stages-existing-response-list" > {linkableEvents .map(event => ( diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.component.js b/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.component.js index 3a7c16cd12..51cc7d7fd3 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.component.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.component.js @@ -82,6 +82,7 @@ const Schedule = ({ label={mainOptionTranslatedTexts[relatedStageActions.SCHEDULE_IN_ORG]} onChange={(e: Object) => updateSelectedAction(e.value)} value={relatedStageActions.SCHEDULE_IN_ORG} + dataTest="related-stages-actions-schedule" /> ); @@ -126,6 +127,7 @@ const EnterData = ({ label={mainOptionTranslatedTexts[relatedStageActions.ENTER_DATA]} onChange={(e: Object) => updateSelectedAction(e.value)} value={relatedStageActions.ENTER_DATA} + dataTest="related-stages-actions-enter-details" /> ); @@ -169,12 +171,13 @@ const LinkExistingResponse = ({ label={mainOptionTranslatedTexts[relatedStageActions.LINK_EXISTING_RESPONSE]} onChange={(e: Object) => updateSelectedAction(e.value)} value={relatedStageActions.LINK_EXISTING_RESPONSE} + dataTest="related-stages-actions-link-existing-response" /> ); }; -const LinkButton = withStyles(styles)(({ onLink, label, errorMessages, classes }) => { +const LinkButton = withStyles(styles)(({ onLink, label, errorMessages, dataTest, classes }) => { if (!onLink) { return null; } @@ -183,7 +186,7 @@ const LinkButton = withStyles(styles)(({ onLink, label, errorMessages, classes } return (
-
@@ -285,6 +288,7 @@ const RelatedStagesActionsPlain = ({ onLink={onLink} label={i18n.t('Schedule')} errorMessages={errorMessages} + dataTest="related-stages-buttons-schedule" /> )} @@ -302,6 +306,7 @@ const RelatedStagesActionsPlain = ({ onLink={onLink} label={i18n.t('Enter details')} errorMessages={errorMessages} + dataTest="related-stages-buttons-enter-details" /> )} @@ -320,6 +325,7 @@ const RelatedStagesActionsPlain = ({ onLink={onLink} label={i18n.t('Link')} errorMessages={errorMessages} + dataTest="related-stages-buttons-link-existing-response" /> )} diff --git a/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageDetail/EventRow/DeleteActionButton/DeleteActionButton.js b/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageDetail/EventRow/DeleteActionButton/DeleteActionButton.js index bef374d0c5..5ff3bd18a9 100644 --- a/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageDetail/EventRow/DeleteActionButton/DeleteActionButton.js +++ b/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageDetail/EventRow/DeleteActionButton/DeleteActionButton.js @@ -21,6 +21,7 @@ export const DeleteActionButton = ({ dense icon={} label={i18n.t('Delete')} + dataTest="stages-and-events-delete" onClick={() => { setDeleteModalOpen(true); setActionsOpen(false); diff --git a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/Modal/UnlinkModal.js b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/Modal/UnlinkModal.js index 66cf139e74..6016cea015 100644 --- a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/Modal/UnlinkModal.js +++ b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/Modal/UnlinkModal.js @@ -72,6 +72,7 @@ export const UnlinkModal = ({ destructive onClick={() => mutation.mutate()} disabled={mutation.isLoading} + dataTest="event-overflow-unlink-event-confirm" > {i18n.t('Yes, unlink event')} From 497820dd0e051e3b9b76de5ae0634c794a139c54 Mon Sep 17 00:00:00 2001 From: Simona Domnisoru Date: Wed, 29 Jan 2025 15:33:43 +0100 Subject: [PATCH 20/28] fix: add success link messageand skipBidirectionalChecks if required --- i18n/en.pot | 10 +++++----- .../PageLayout/DefaultPageLayout.constants.js | 6 +++--- .../DefaultEnrollmentLayout.types.js | 2 +- .../LayoutComponentConfig/LayoutComponentConfig.js | 2 +- .../WidgetRelatedStages.container.js | 2 +- .../hooks/useAddEventWithRelationship.js | 5 ++++- .../hooks/useLinkedEventByOriginId.js | 11 ++++++----- 7 files changed, 21 insertions(+), 17 deletions(-) diff --git a/i18n/en.pot b/i18n/en.pot index c88bba3923..f227750c4b 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -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: 2025-01-02T13:08:32.303Z\n" -"PO-Revision-Date: 2025-01-02T13:08:32.303Z\n" +"POT-Creation-Date: 2025-01-29T14:33:36.560Z\n" +"PO-Revision-Date: 2025-01-29T14:33:36.560Z\n" msgid "Choose one or more dates..." msgstr "Choose one or more dates..." @@ -1231,9 +1231,6 @@ msgstr "Add coordinates" msgid "Add area" msgstr "Add area" -msgid "Please add or cancel the note before saving the event" -msgstr "Please add or cancel the note before saving the event" - msgid "Please provide an valid organisation unit" msgstr "Please provide an valid organisation unit" @@ -1427,6 +1424,9 @@ msgstr "Enter details now" msgid "Link to an existing event" msgstr "Link to an existing event" +msgid "The event was succesfully linked" +msgstr "The event was succesfully linked" + msgid "An error occurred while linking the event" msgstr "An error occurred while linking the event" diff --git a/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/PageLayout/DefaultPageLayout.constants.js b/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/PageLayout/DefaultPageLayout.constants.js index 9e1486eae8..7fd01d6d58 100644 --- a/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/PageLayout/DefaultPageLayout.constants.js +++ b/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/PageLayout/DefaultPageLayout.constants.js @@ -10,13 +10,13 @@ import { AssigneeWidget, WidgetTypes, TwoEventWorkspace, - WidgetRelatedStagesWorkspace, + RelatedStagesWorkspace, } from '../../common/EnrollmentOverviewDomain/EnrollmentPageLayout'; export const WidgetsForEnrollmentEventEdit: $ReadOnly<{ [key: string]: WidgetConfig }> = Object.freeze({ EditEventWorkspace, TwoEventWorkspace, - WidgetRelatedStagesWorkspace, + RelatedStagesWorkspace, EventNote, AssigneeWidget, ...DefaultWidgetsForEnrollmentOverview, @@ -30,7 +30,7 @@ export const DefaultPageLayout: PageLayoutConfig = { }, { type: WidgetTypes.COMPONENT, - name: 'WidgetRelatedStagesWorkspace', + name: 'RelatedStagesWorkspace', }, ], rightColumn: [ diff --git a/src/core_modules/capture-core/components/Pages/common/EnrollmentOverviewDomain/EnrollmentPageLayout/DefaultEnrollmentLayout.types.js b/src/core_modules/capture-core/components/Pages/common/EnrollmentOverviewDomain/EnrollmentPageLayout/DefaultEnrollmentLayout.types.js index b37abd6507..4f66eba33d 100644 --- a/src/core_modules/capture-core/components/Pages/common/EnrollmentOverviewDomain/EnrollmentPageLayout/DefaultEnrollmentLayout.types.js +++ b/src/core_modules/capture-core/components/Pages/common/EnrollmentOverviewDomain/EnrollmentPageLayout/DefaultEnrollmentLayout.types.js @@ -8,7 +8,7 @@ type DefaultComponents = 'QuickActions' | 'AssigneeWidget' | 'NewEventWorkspace' | 'EditEventWorkspace' - | 'WidgetRelatedStagesWorkspace' + | 'RelatedStagesWorkspace' | 'EnrollmentNote' | 'EventNote' | 'TrackedEntityRelationship' diff --git a/src/core_modules/capture-core/components/Pages/common/EnrollmentOverviewDomain/EnrollmentPageLayout/LayoutComponentConfig/LayoutComponentConfig.js b/src/core_modules/capture-core/components/Pages/common/EnrollmentOverviewDomain/EnrollmentPageLayout/LayoutComponentConfig/LayoutComponentConfig.js index 1bf2275e72..47d626f1bb 100644 --- a/src/core_modules/capture-core/components/Pages/common/EnrollmentOverviewDomain/EnrollmentPageLayout/LayoutComponentConfig/LayoutComponentConfig.js +++ b/src/core_modules/capture-core/components/Pages/common/EnrollmentOverviewDomain/EnrollmentPageLayout/LayoutComponentConfig/LayoutComponentConfig.js @@ -291,7 +291,7 @@ export const EventNote: WidgetConfig = { }), }; -export const WidgetRelatedStagesWorkspace: WidgetConfig = { +export const RelatedStagesWorkspace: WidgetConfig = { Component: WidgetRelatedStages, shouldHideWidget: ({ currentPage }) => currentPage === EnrollmentPageKeys.EDIT_EVENT, getProps: ({ diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.container.js b/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.container.js index 16144ab6db..6edcb7d0a9 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.container.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.container.js @@ -49,7 +49,7 @@ export const WidgetRelatedStagesPlain = ({ const { linkedEvent, isLoading: isLinkedEventLoading, - } = useLinkedEventByOriginId({ originEventId: eventId }); + } = useLinkedEventByOriginId({ originEventId: eventId, skipBidirectionalChecks: true }); const relatedStageRef = useRef(null); const { buildRelatedStageEventPayload } = useBuildRelatedStageEventPayload(); const { addEventWithRelationship } = useAddEventWithRelationship({ diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/hooks/useAddEventWithRelationship.js b/src/core_modules/capture-core/components/WidgetRelatedStages/hooks/useAddEventWithRelationship.js index 39230785ff..67a61e6a04 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/hooks/useAddEventWithRelationship.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/hooks/useAddEventWithRelationship.js @@ -1,6 +1,6 @@ // @flow import i18n from '@dhis2/d2-i18n'; -import { useDataEngine } from '@dhis2/app-runtime'; +import { useAlert, useDataEngine } from '@dhis2/app-runtime'; import { useMutation, useQueryClient } from 'react-query'; import { relatedStageActions } from '../constants'; @@ -27,6 +27,7 @@ export const useAddEventWithRelationship = ({ }) => { const dataEngine = useDataEngine(); const queryClient = useQueryClient(); + const { show: showAlert } = useAlert(({ message }) => message, { success: true }); const { mutate } = useMutation( ({ serverData }: Object) => @@ -47,6 +48,8 @@ export const useAddEventWithRelationship = ({ if (payload.linkMode === relatedStageActions.ENTER_DATA && payload.eventIdToRedirectTo) { onNavigateToEvent(payload.eventIdToRedirectTo); + } else { + showAlert({ message: i18n.t('The event was succesfully linked') }); } }, onError: () => { diff --git a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/hooks/useLinkedEventByOriginId.js b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/hooks/useLinkedEventByOriginId.js index c2453dbcc1..4760e2b0be 100644 --- a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/hooks/useLinkedEventByOriginId.js +++ b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/hooks/useLinkedEventByOriginId.js @@ -4,9 +4,10 @@ import { useApiDataQuery } from '../../../utils/reactQueryHelpers'; type Props = {| originEventId: string, + skipBidirectionalChecks?: boolean, |}; -const calculateRelatedStageRelationships = (event) => { +const calculateRelatedStageRelationships = (event, skipBidirectionalChecks) => { if (!event || !event.relationships || event.relationships.length === 0) { return null; } @@ -25,7 +26,7 @@ const calculateRelatedStageRelationships = (event) => { const stageToStageRelationship = stageToStageRelationships[0]; const eventIsOrigin = stageToStageRelationship.from.event.event === event.event; - if (eventIsOrigin && !stageToStageRelationship.bidirectional) { + if (!skipBidirectionalChecks && eventIsOrigin && !stageToStageRelationship.bidirectional) { return null; } @@ -38,7 +39,7 @@ const calculateRelatedStageRelationships = (event) => { }; }; -export const useLinkedEventByOriginId = ({ originEventId }: Props) => { +export const useLinkedEventByOriginId = ({ originEventId, skipBidirectionalChecks }: Props) => { const eventByIdQuery = useMemo(() => ({ resource: 'tracker/events', id: originEventId, @@ -63,7 +64,7 @@ export const useLinkedEventByOriginId = ({ originEventId }: Props) => { const { linkedEvent, relationship, relationshipType, dataValues } = useMemo(() => { if (!data) return {}; - const relatedStageRelationship = calculateRelatedStageRelationships(data); + const relatedStageRelationship = calculateRelatedStageRelationships(data, skipBidirectionalChecks); if (!relatedStageRelationship) return {}; return { @@ -72,7 +73,7 @@ export const useLinkedEventByOriginId = ({ originEventId }: Props) => { relationshipType: relatedStageRelationship.relationshipType, dataValues: relatedStageRelationship.linkedEvent.dataValues, }; - }, [data]); + }, [data, skipBidirectionalChecks]); const { data: fallbackDataValues, isLoading: isLoadingFallback } = useApiDataQuery( ['linkedEventDataValuesFallback', linkedEvent?.event], From c751be5c52def43227519b05cebe0e13f18780fd Mon Sep 17 00:00:00 2001 From: Simona Domnisoru Date: Thu, 6 Feb 2025 11:18:19 +0100 Subject: [PATCH 21/28] fix: update enrollmentDomain value in Redux and fix the 409 error --- .../getConvertedRelatedStageEvent.js | 1 + .../EnrollmentEditEventPage.component.js | 10 +++++++ .../EnrollmentEditEventPage.container.js | 30 +++++++++++++++++++ .../EnrollmentEditEventPage.types.js | 5 ++++ .../LayoutComponentConfig.js | 16 ++++++---- .../enrollment.actions.js | 4 +++ .../common/EnrollmentOverviewDomain/index.js | 4 +++ .../WidgetEnrollment/enrollment.types.js | 4 +-- .../WidgetEventEdit.container.js | 4 +++ .../WidgetEventEdit/widgetEventEdit.types.js | 2 ++ .../WidgetRelatedStages.container.js | 12 ++++---- .../WidgetRelatedStages.types.js | 6 ++-- .../hooks/useAddEventWithRelationship.js | 29 +++++++++--------- .../hooks/useBuildRelatedStageEventPayload.js | 12 +++++++- .../Modal/UnlinkAndDeleteModal.js | 5 ++++ .../Modal/UnlinkAndDeleteModal.types.js | 3 ++ .../OverflowMenu/Modal/UnlinkModal.js | 2 ++ .../OverflowMenu/Modal/UnlinkModal.types.js | 2 ++ .../OverflowMenu/OverflowMenu.component.js | 6 ++++ .../OverflowMenu/OverflowMenu.types.js | 2 ++ .../WidgetHeader/WidgetHeader.container.js | 4 +++ .../WidgetHeader/WidgetHeader.types.js | 2 ++ .../WidgetTwoEventWorkspace.container.js | 4 +++ .../WidgetTwoEventWorkspace.types.js | 2 ++ .../enrollmentDomain.reducerDescription.js | 11 +++++++ 25 files changed, 149 insertions(+), 33 deletions(-) diff --git a/src/core_modules/capture-core/components/DataEntries/converters/getConvertedRelatedStageEvent/getConvertedRelatedStageEvent.js b/src/core_modules/capture-core/components/DataEntries/converters/getConvertedRelatedStageEvent/getConvertedRelatedStageEvent.js index ad19d0ec9e..096078ae2e 100644 --- a/src/core_modules/capture-core/components/DataEntries/converters/getConvertedRelatedStageEvent/getConvertedRelatedStageEvent.js +++ b/src/core_modules/capture-core/components/DataEntries/converters/getConvertedRelatedStageEvent/getConvertedRelatedStageEvent.js @@ -114,6 +114,7 @@ export const getConvertedRelatedStageEvent = ({ const relationship = linkedEventId && { relationshipType: relatedStageType.id, + relationship: generateUID(), from: { event: { event: requestEventIsFromConstraint ? serverRequestEvent.event : linkedEventId, diff --git a/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.component.js b/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.component.js index 4dd2ff5341..fc3a9d045b 100644 --- a/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.component.js +++ b/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.component.js @@ -58,6 +58,11 @@ export const EnrollmentEditEventPageComponent = ({ onDeleteTrackedEntitySuccess, onAccessLostFromTransfer, onNavigateToEvent, + onDeleteEvent, + onDeleteEventRelationship, + onUpdateOrAddEnrollmentEvents, + onUpdateEnrollmentEventsSuccess, + onUpdateEnrollmentEventsError, }: PlainProps) => ( diff --git a/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.container.js b/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.container.js index 3d81fcba59..01ebe1b56b 100644 --- a/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.container.js +++ b/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.container.js @@ -13,6 +13,11 @@ import { updateEnrollmentAndEvents, updateEnrollmentEvent, useCommonEnrollmentDomainData, + deleteEnrollmentEvent, + deleteEnrollmentEventRelationship, + updateOrAddEnrollmentEvents, + commitEnrollmentEvents, + rollbackEnrollmentEvents, } from '../common/EnrollmentOverviewDomain'; import { useTeiDisplayName } from '../common/EnrollmentOverviewDomain/useTeiDisplayName'; import { useProgramInfo } from '../../../hooks/useProgramInfo'; @@ -166,6 +171,26 @@ const EnrollmentEditEventPageWithContextPlain = ({ redirect && history.push(`enrollment?${buildUrlQueryString({ programId, orgUnitId, teiId, enrollmentId })}`); }, [dispatch, history, programId, orgUnitId, teiId, enrollmentId]); + const onDeleteEvent = useCallback((linkedEventId: string) => { + dispatch(deleteEnrollmentEvent(linkedEventId)); + }, [dispatch]); + + const onDeleteEventRelationship = useCallback((relationshipId: string) => { + dispatch(deleteEnrollmentEventRelationship(relationshipId)); + }, [dispatch]); + + const onUpdateOrAddEnrollmentEvents = useCallback((events) => { + dispatch(updateOrAddEnrollmentEvents({ events })); + }, [dispatch]); + + const onUpdateEnrollmentEventsSuccess = useCallback((events) => { + dispatch(commitEnrollmentEvents({ events })); + }, [dispatch]); + + const onUpdateEnrollmentEventsError = useCallback((events) => { + dispatch(rollbackEnrollmentEvents({ events })); + }, [dispatch]); + const onSaveAndCompleteEnrollment = useCallback((enrollmentToUpdate) => { dispatch(setExternalEnrollmentStatus(statusTypes.COMPLETED)); dispatch(updateEnrollmentAndEvents(enrollmentToUpdate)); @@ -303,6 +328,11 @@ const EnrollmentEditEventPageWithContextPlain = ({ events={enrollmentSite?.events} onAccessLostFromTransfer={onAccessLostFromTransfer} onNavigateToEvent={onNavigateToEvent} + onDeleteEvent={onDeleteEvent} + onDeleteEventRelationship={onDeleteEventRelationship} + onUpdateOrAddEnrollmentEvents={onUpdateOrAddEnrollmentEvents} + onUpdateEnrollmentEventsSuccess={onUpdateEnrollmentEventsSuccess} + onUpdateEnrollmentEventsError={onUpdateEnrollmentEventsError} /> ); }; diff --git a/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.types.js b/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.types.js index a32173f2c2..0cb7e3ae35 100644 --- a/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.types.js +++ b/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.types.js @@ -56,6 +56,11 @@ export type PlainProps = {| onSaveAssigneeError: (prevAssignee: UserFormField | null) => void, onDeleteTrackedEntitySuccess: () => void, events: Array, + onDeleteEvent?: (eventId: string) => void, + onDeleteEventRelationship?: (relationshipId: string) => void, + onUpdateOrAddEnrollmentEvents: (events: Array) => void, + onUpdateEnrollmentEventsSuccess: (events: Array) => void, + onUpdateEnrollmentEventsError: (events: Array) => void, |}; export type Props = {| diff --git a/src/core_modules/capture-core/components/Pages/common/EnrollmentOverviewDomain/EnrollmentPageLayout/LayoutComponentConfig/LayoutComponentConfig.js b/src/core_modules/capture-core/components/Pages/common/EnrollmentOverviewDomain/EnrollmentPageLayout/LayoutComponentConfig/LayoutComponentConfig.js index 47d626f1bb..c211cf217a 100644 --- a/src/core_modules/capture-core/components/Pages/common/EnrollmentOverviewDomain/EnrollmentPageLayout/LayoutComponentConfig/LayoutComponentConfig.js +++ b/src/core_modules/capture-core/components/Pages/common/EnrollmentOverviewDomain/EnrollmentPageLayout/LayoutComponentConfig/LayoutComponentConfig.js @@ -233,6 +233,8 @@ export const EditEventWorkspace: WidgetConfig = { onSaveAndCompleteEnrollment, onSaveAndCompleteEnrollmentErrorActionType, onSaveAndCompleteEnrollmentSuccessActionType, + onDeleteEvent, + onDeleteEventRelationship, }): WidgetEventEditProps => ({ onGoBack, programId: program.id, @@ -250,6 +252,8 @@ export const EditEventWorkspace: WidgetConfig = { onSaveAndCompleteEnrollment, onSaveAndCompleteEnrollmentErrorActionType, onSaveAndCompleteEnrollmentSuccessActionType, + onDeleteEvent, + onDeleteEventRelationship, }), }; @@ -300,9 +304,9 @@ export const RelatedStagesWorkspace: WidgetConfig = { enrollmentId, eventId, teiId, - onUpdateEnrollmentStatus, - onUpdateEnrollmentStatusSuccess, - onUpdateEnrollmentStatusError, + onUpdateOrAddEnrollmentEvents, + onUpdateEnrollmentEventsSuccess, + onUpdateEnrollmentEventsError, onNavigateToEvent, }) => ({ programId: program.id, @@ -310,9 +314,9 @@ export const RelatedStagesWorkspace: WidgetConfig = { enrollmentId, eventId, teiId, - onUpdateEnrollment: onUpdateEnrollmentStatus, - onUpdateEnrollmentSuccess: onUpdateEnrollmentStatusSuccess, - onUpdateEnrollmentError: onUpdateEnrollmentStatusError, + onUpdateOrAddEnrollmentEvents, + onUpdateEnrollmentEventsSuccess, + onUpdateEnrollmentEventsError, onNavigateToEvent, }), }; diff --git a/src/core_modules/capture-core/components/Pages/common/EnrollmentOverviewDomain/enrollment.actions.js b/src/core_modules/capture-core/components/Pages/common/EnrollmentOverviewDomain/enrollment.actions.js index 1d5bfbc2bd..667548944d 100644 --- a/src/core_modules/capture-core/components/Pages/common/EnrollmentOverviewDomain/enrollment.actions.js +++ b/src/core_modules/capture-core/components/Pages/common/EnrollmentOverviewDomain/enrollment.actions.js @@ -13,6 +13,7 @@ export const enrollmentSiteActionTypes = { ROLLBACK_ENROLLMENT_EVENT: 'Enrollment.RollbackEnrollmentEvent', ROLLBACK_ENROLLMENT_EVENTS: 'Enrollment.RollbackEnrollmentEvents', DELETE_ENROLLMENT_EVENT: 'Enrollment.DeleteEnrollmentEvent', + DELETE_ENROLLMENT_EVENT_RELATIONSHIP: 'Enrollment.DeleteEnrollmentEventRelationship', UPDATE_ENROLLMENT_EVENT_STATUS: 'Enrollment.UpdateEnrollmentEventStatus', COMMIT_ENROLLMENT_EVENT: 'Enrollment.CommitEnrollmentEvent', COMMIT_ENROLLMENT_EVENTS: 'Enrollment.CommitEnrollmentEvents', @@ -60,6 +61,9 @@ export const updateOrAddEnrollmentEvents = ({ events }: EventReducerProps) => export const deleteEnrollmentEvent = (eventId: string) => actionCreator(enrollmentSiteActionTypes.DELETE_ENROLLMENT_EVENT)({ eventId }); +export const deleteEnrollmentEventRelationship = (relationshipId: string) => + actionCreator(enrollmentSiteActionTypes.DELETE_ENROLLMENT_EVENT_RELATIONSHIP)({ relationshipId }); + export const updateEnrollmentEventStatus = (eventId: string, status: string, updatedAt: string) => actionCreator(enrollmentSiteActionTypes.UPDATE_ENROLLMENT_EVENT_STATUS)({ eventId, diff --git a/src/core_modules/capture-core/components/Pages/common/EnrollmentOverviewDomain/index.js b/src/core_modules/capture-core/components/Pages/common/EnrollmentOverviewDomain/index.js index a4447b6310..442b793186 100644 --- a/src/core_modules/capture-core/components/Pages/common/EnrollmentOverviewDomain/index.js +++ b/src/core_modules/capture-core/components/Pages/common/EnrollmentOverviewDomain/index.js @@ -15,6 +15,10 @@ export { commitEnrollmentAndEvents, rollbackEnrollmentAndEvents, setExternalEnrollmentStatus, + deleteEnrollmentEvent, + deleteEnrollmentEventRelationship, + commitEnrollmentEvents, + rollbackEnrollmentEvents, } from './enrollment.actions'; export { useCommonEnrollmentDomainData } from './useCommonEnrollmentDomainData'; export { useRuleEffects } from './useRuleEffects'; diff --git a/src/core_modules/capture-core/components/WidgetEnrollment/enrollment.types.js b/src/core_modules/capture-core/components/WidgetEnrollment/enrollment.types.js index f9b317a953..3417c900d5 100644 --- a/src/core_modules/capture-core/components/WidgetEnrollment/enrollment.types.js +++ b/src/core_modules/capture-core/components/WidgetEnrollment/enrollment.types.js @@ -3,9 +3,7 @@ import type { FetchError, QueryRefetchFunction } from '@dhis2/app-runtime'; export type EventReducerProps = {| enrollments?: ?Array, - events: Array<{ - event: string, - }> + events: Array |} export type Props = {| diff --git a/src/core_modules/capture-core/components/WidgetEventEdit/WidgetEventEdit.container.js b/src/core_modules/capture-core/components/WidgetEventEdit/WidgetEventEdit.container.js index 21b2e372ea..5ef8023def 100644 --- a/src/core_modules/capture-core/components/WidgetEventEdit/WidgetEventEdit.container.js +++ b/src/core_modules/capture-core/components/WidgetEventEdit/WidgetEventEdit.container.js @@ -87,6 +87,8 @@ export const WidgetEventEditPlain = ({ onSaveAndCompleteEnrollment, onSaveAndCompleteEnrollmentSuccessActionType, onSaveAndCompleteEnrollmentErrorActionType, + onDeleteEvent, + onDeleteEventRelationship, classes, }: PlainProps) => { useEffect(() => inMemoryFileStore.clear, []); @@ -113,6 +115,8 @@ export const WidgetEventEditPlain = ({ orgUnitId={orgUnitId} stageId={stageId} stage={stage} + onDeleteEvent={onDeleteEvent} + onDeleteEventRelationship={onDeleteEventRelationship} />
void, onSaveAndCompleteEnrollmentSuccessActionType?: string, onSaveAndCompleteEnrollmentErrorActionType?: string, + onDeleteEvent?: (eventId: string) => void, + onDeleteEventRelationship?: (relationshipId: string) => void, |}; export type ComponentProps = {| diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.container.js b/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.container.js index 6edcb7d0a9..7f5d88d78c 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.container.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.container.js @@ -38,9 +38,9 @@ export const WidgetRelatedStagesPlain = ({ programStageId, teiId, actionsOptions, - onUpdateEnrollment, - onUpdateEnrollmentSuccess, - onUpdateEnrollmentError, + onUpdateOrAddEnrollmentEvents, + onUpdateEnrollmentEventsSuccess, + onUpdateEnrollmentEventsError, onNavigateToEvent, classes, }: Props) => { @@ -54,9 +54,9 @@ export const WidgetRelatedStagesPlain = ({ const { buildRelatedStageEventPayload } = useBuildRelatedStageEventPayload(); const { addEventWithRelationship } = useAddEventWithRelationship({ eventId, - onUpdateEnrollment, - onUpdateEnrollmentSuccess, - onUpdateEnrollmentError, + onUpdateOrAddEnrollmentEvents, + onUpdateEnrollmentEventsSuccess, + onUpdateEnrollmentEventsError, onNavigateToEvent, }); diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.types.js b/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.types.js index 9288f6f008..8689f035e7 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.types.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.types.js @@ -21,9 +21,9 @@ export type Props = {| teiId: string, enrollmentId: string, programStageId: string, - onUpdateEnrollment: (enrollment: Object) => void, - onUpdateEnrollmentSuccess: ({ redirect?: boolean }) => void, - onUpdateEnrollmentError: (message: string) => void, + onUpdateOrAddEnrollmentEvents: (events: Array) => void, + onUpdateEnrollmentEventsSuccess: (events: Array) => void, + onUpdateEnrollmentEventsError: (events: Array) => void, onNavigateToEvent: (eventId: string) => void, actionsOptions?: { [key: $Keys]: { diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/hooks/useAddEventWithRelationship.js b/src/core_modules/capture-core/components/WidgetRelatedStages/hooks/useAddEventWithRelationship.js index 67a61e6a04..43ab09be22 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/hooks/useAddEventWithRelationship.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/hooks/useAddEventWithRelationship.js @@ -14,20 +14,21 @@ const addEventWithRelationshipMutation = { export const useAddEventWithRelationship = ({ eventId, - onUpdateEnrollment, - onUpdateEnrollmentSuccess, - onUpdateEnrollmentError, + onUpdateOrAddEnrollmentEvents, + onUpdateEnrollmentEventsSuccess, + onUpdateEnrollmentEventsError, onNavigateToEvent, }: { eventId: string, - onUpdateEnrollment: (enrollment: Object) => void, - onUpdateEnrollmentSuccess: ({ redirect?: boolean }) => void, - onUpdateEnrollmentError: (message: string) => void, + onUpdateOrAddEnrollmentEvents: (events: Array) => void, + onUpdateEnrollmentEventsSuccess: (events: Array) => void, + onUpdateEnrollmentEventsError: (events: Array) => void, onNavigateToEvent: (eventId: string) => void, }) => { const dataEngine = useDataEngine(); const queryClient = useQueryClient(); - const { show: showAlert } = useAlert(({ message }) => message, { success: true }); + const { show: showSccess } = useAlert(({ message }) => message, { success: true }); + const { show: showAlert } = useAlert(({ message }) => message, { critical: true }); const { mutate } = useMutation( ({ serverData }: Object) => @@ -38,22 +39,22 @@ export const useAddEventWithRelationship = ({ }), { onMutate: (payload: { serverData: Object }) => { - const enrollmentToUpdate = payload.serverData.enrollments?.[0]; - enrollmentToUpdate && onUpdateEnrollment(enrollmentToUpdate); + onUpdateOrAddEnrollmentEvents && onUpdateOrAddEnrollmentEvents(payload.serverData.events); }, - onSuccess: (_, payload: { linkMode: string, eventIdToRedirectTo?: string }) => { + onSuccess: (_, payload: { linkMode: string, eventIdToRedirectTo?: string, serverData: Object }) => { const queryKey = [ReactQueryAppNamespace, 'linkedEventByOriginEvent', eventId]; queryClient.refetchQueries(queryKey); - onUpdateEnrollmentSuccess({}); + onUpdateEnrollmentEventsSuccess && onUpdateEnrollmentEventsSuccess(payload.serverData.events); if (payload.linkMode === relatedStageActions.ENTER_DATA && payload.eventIdToRedirectTo) { onNavigateToEvent(payload.eventIdToRedirectTo); } else { - showAlert({ message: i18n.t('The event was succesfully linked') }); + showSccess({ message: i18n.t('The event was succesfully linked') }); } }, - onError: () => { - onUpdateEnrollmentError(i18n.t('An error occurred while linking the event')); + onError: (_, payload: { serverData: Object }) => { + showAlert({ message: i18n.t('An error occurred while linking the event') }); + onUpdateEnrollmentEventsError && onUpdateEnrollmentEventsError(payload.serverData.events); }, }, ); diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/hooks/useBuildRelatedStageEventPayload.js b/src/core_modules/capture-core/components/WidgetRelatedStages/hooks/useBuildRelatedStageEventPayload.js index 86b359ce25..6348eee416 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/hooks/useBuildRelatedStageEventPayload.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/hooks/useBuildRelatedStageEventPayload.js @@ -11,13 +11,23 @@ export const createServerData = ({ relationship: ?Object, enrollment: Object, }) => { + const exisitingEvents = enrollment.events.map(event => ( + (event.event === relationship?.from.event.event || event.event === relationship?.to.event.event) + ? { ...event, pendingApiResponse: true, relationships: [relationship], uid: event.event } + : event + )); + if (linkedEvent) { return { - events: [...enrollment.events, linkedEvent], + events: [ + ...exisitingEvents, + { ...linkedEvent, pendingApiResponse: true, relationships: [relationship], uid: linkedEvent.event }, + ], relationships: [relationship], }; } return { + events: exisitingEvents, relationships: [relationship], }; }; diff --git a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/Modal/UnlinkAndDeleteModal.js b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/Modal/UnlinkAndDeleteModal.js index abe1af1713..dba58601e6 100644 --- a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/Modal/UnlinkAndDeleteModal.js +++ b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/Modal/UnlinkAndDeleteModal.js @@ -19,6 +19,9 @@ export const UnlinkAndDeleteModal = ({ setOpenModal, eventId, originEventId, + relationshipId, + onDeleteEvent, + onDeleteEventRelationship, }: Props) => { const dataEngine = useDataEngine(); const queryClient = useQueryClient(); @@ -45,6 +48,8 @@ export const UnlinkAndDeleteModal = ({ originEventId, ]); setOpenModal(false); + onDeleteEvent && onDeleteEvent(eventId); + onDeleteEventRelationship && onDeleteEventRelationship(relationshipId); }, onError: (error) => { showErrorAlert(); diff --git a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/Modal/UnlinkAndDeleteModal.types.js b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/Modal/UnlinkAndDeleteModal.types.js index d154e7396d..c8f34576ec 100644 --- a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/Modal/UnlinkAndDeleteModal.types.js +++ b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/Modal/UnlinkAndDeleteModal.types.js @@ -3,4 +3,7 @@ export type Props = {| setOpenModal: (open: boolean) => void, eventId: string, originEventId: string, + relationshipId: string, + onDeleteEvent?: (eventId: string) => void, + onDeleteEventRelationship?: (relationshipId: string) => void, |}; diff --git a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/Modal/UnlinkModal.js b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/Modal/UnlinkModal.js index 6016cea015..e39389a2d1 100644 --- a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/Modal/UnlinkModal.js +++ b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/Modal/UnlinkModal.js @@ -19,6 +19,7 @@ export const UnlinkModal = ({ setOpenModal, relationshipId, originEventId, + onDeleteEventRelationship, }: Props) => { const dataEngine = useDataEngine(); const queryClient = useQueryClient(); @@ -44,6 +45,7 @@ export const UnlinkModal = ({ 'linkedEventByOriginEvent', originEventId, ]); + onDeleteEventRelationship && onDeleteEventRelationship(relationshipId); setOpenModal(false); }, onError: (error) => { diff --git a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/Modal/UnlinkModal.types.js b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/Modal/UnlinkModal.types.js index b2d0b37c85..f5c2bb7cf1 100644 --- a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/Modal/UnlinkModal.types.js +++ b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/Modal/UnlinkModal.types.js @@ -3,4 +3,6 @@ export type Props = {| setOpenModal: (open: boolean) => void, relationshipId: string, originEventId: string, + onDeleteEvent?: (eventId: string) => void, + onDeleteEventRelationship?: (relationshipId: string) => void, |}; diff --git a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/OverflowMenu.component.js b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/OverflowMenu.component.js index df12185fec..bb23386168 100644 --- a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/OverflowMenu.component.js +++ b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/OverflowMenu.component.js @@ -25,6 +25,8 @@ export const OverflowMenuComponent = ({ originEventId, stageWriteAccess, relationshipType, + onDeleteEvent, + onDeleteEventRelationship, }: Props) => { const { push } = useHistory(); const [isActionsOpen, setIsActionsOpen] = useState(false); @@ -100,6 +102,7 @@ export const OverflowMenuComponent = ({ setOpenModal={setIsUnlinkModalOpen} relationshipId={relationshipId} originEventId={originEventId} + onDeleteEventRelationship={onDeleteEventRelationship} /> )} {isUnlinkAndDeleteModalOpen && ( @@ -107,6 +110,9 @@ export const OverflowMenuComponent = ({ setOpenModal={setIsUnlinkAndDeleteModalOpen} eventId={linkedEvent.event} originEventId={originEventId} + relationshipId={relationshipId} + onDeleteEvent={onDeleteEvent} + onDeleteEventRelationship={onDeleteEventRelationship} /> )} diff --git a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/OverflowMenu.types.js b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/OverflowMenu.types.js index 61b1f85de3..7a8359888a 100644 --- a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/OverflowMenu.types.js +++ b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/OverflowMenu.types.js @@ -6,4 +6,6 @@ export type Props = { originEventId: string, stageWriteAccess: boolean, relationshipType: string, + onDeleteEvent?: (eventId: string) => void, + onDeleteEventRelationship?: (relationshipId: string) => void, }; diff --git a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetHeader/WidgetHeader.container.js b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetHeader/WidgetHeader.container.js index cc52138727..8ce893c77e 100644 --- a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetHeader/WidgetHeader.container.js +++ b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetHeader/WidgetHeader.container.js @@ -27,6 +27,8 @@ const WidgetHeaderPlain = ({ stage, eventId, classes, + onDeleteEvent, + onDeleteEventRelationship, }: Props) => { const { icon } = linkedStage; return ( @@ -52,6 +54,8 @@ const WidgetHeaderPlain = ({ orgUnitId={orgUnitId} originEventId={eventId} stageWriteAccess={stage?.access?.data?.write} + onDeleteEvent={onDeleteEvent} + onDeleteEventRelationship={onDeleteEventRelationship} />
)} diff --git a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetHeader/WidgetHeader.types.js b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetHeader/WidgetHeader.types.js index 383251fdbb..944a6eab7f 100644 --- a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetHeader/WidgetHeader.types.js +++ b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetHeader/WidgetHeader.types.js @@ -12,6 +12,8 @@ export type PlainProps = {| relationshipType: string, stage: ProgramStage, eventId: string, + onDeleteEvent?: (eventId: string) => void, + onDeleteEventRelationship?: (relationshipId: string) => void, |}; export type Props = {| diff --git a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetTwoEventWorkspace.container.js b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetTwoEventWorkspace.container.js index 1851fb8d7b..0e51459a1b 100644 --- a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetTwoEventWorkspace.container.js +++ b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetTwoEventWorkspace.container.js @@ -17,6 +17,8 @@ export const WidgetTwoEventWorkspace = ({ currentPage, stage, type, + onDeleteEvent, + onDeleteEventRelationship, }: Props) => { const { linkedEvent, @@ -80,6 +82,8 @@ export const WidgetTwoEventWorkspace = ({ eventId={eventId} relationship={relationship} relationshipType={relationshipType} + onDeleteEvent={onDeleteEvent} + onDeleteEventRelationship={onDeleteEventRelationship} /> } noncollapsible diff --git a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetTwoEventWorkspace.types.js b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetTwoEventWorkspace.types.js index 71bc0471fa..d04d39ed0f 100644 --- a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetTwoEventWorkspace.types.js +++ b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetTwoEventWorkspace.types.js @@ -13,6 +13,8 @@ export type Props = {| currentPage: $Values | string, stage?: ProgramStage, type?: $Values, + onDeleteEvent?: (eventId: string) => void, + onDeleteEventRelationship?: (relationshipId: string) => void, |} export type LinkedEvent = {| diff --git a/src/core_modules/capture-core/reducers/descriptions/enrollmentDomain.reducerDescription.js b/src/core_modules/capture-core/reducers/descriptions/enrollmentDomain.reducerDescription.js index f7ca795289..6cf3042d52 100644 --- a/src/core_modules/capture-core/reducers/descriptions/enrollmentDomain.reducerDescription.js +++ b/src/core_modules/capture-core/reducers/descriptions/enrollmentDomain.reducerDescription.js @@ -25,6 +25,7 @@ const { COMMIT_ENROLLMENT_AND_EVENTS, DELETE_ENROLLMENT_EVENT, UPDATE_ENROLLMENT_EVENT_STATUS, + DELETE_ENROLLMENT_EVENT_RELATIONSHIP, } = enrollmentSiteActionTypes; const setAssignee = (state, action) => { @@ -104,6 +105,16 @@ export const enrollmentDomainDesc = createReducerDescription( const events = state.enrollment.events?.filter(event => event.event !== eventId); return { ...state, enrollment: { ...state.enrollment, events } }; }, + [DELETE_ENROLLMENT_EVENT_RELATIONSHIP]: (state, { payload: { relationshipId } }) => { + const events = state.enrollment.events?.reduce((acc, event) => { + const relationships = event.relationships?.filter( + relationship => relationship.relationship !== relationshipId, + ); + return [...acc, { ...event, relationships }]; + }, []); + + return { ...state, enrollment: { ...state.enrollment, events } }; + }, [UPDATE_ENROLLMENT_EVENT_STATUS]: (state, { payload: { eventId, status, updatedAt } }) => { const events = state.enrollment.events?.map(event => (event.event === eventId From f1b0917cb17da6852e20b8b3e78b652889856c26 Mon Sep 17 00:00:00 2001 From: Simona Domnisoru Date: Thu, 6 Feb 2025 11:19:17 +0100 Subject: [PATCH 22/28] fix: add loading logic to the Link action button --- .../RelatedStagesActions.component.js | 13 ++++++------- .../RelatedStagesActions.container.js | 2 ++ .../RelatedStagesActions.types.js | 2 ++ .../WidgetRelatedStages.container.js | 6 +++++- .../hooks/useAddEventWithRelationship.js | 3 +++ 5 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.component.js b/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.component.js index 51cc7d7fd3..33fc0e7586 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.component.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.component.js @@ -177,16 +177,14 @@ const LinkExistingResponse = ({ ); }; -const LinkButton = withStyles(styles)(({ onLink, label, errorMessages, dataTest, classes }) => { +const LinkButton = withStyles(styles)(({ onLink, label, dataTest, isLinking, classes }) => { if (!onLink) { return null; } - const disabled = Object.values(errorMessages).some(Boolean); - return (
-
@@ -207,6 +205,7 @@ const RelatedStagesActionsPlain = ({ saveAttempted, actionsOptions, onLink, + isLinking, }: PlainProps) => { const { programStage } = useProgramStageInfo(constraint?.programStage?.id); @@ -287,7 +286,7 @@ const RelatedStagesActionsPlain = ({ @@ -305,7 +304,7 @@ const RelatedStagesActionsPlain = ({ @@ -324,7 +323,7 @@ const RelatedStagesActionsPlain = ({ diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.container.js b/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.container.js index e50803fb6f..406326f415 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.container.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.container.js @@ -12,6 +12,7 @@ const RelatedStagesActionsPlain = ({ programId, enrollmentId, programStageId, + isLinking, onLink, ...passOnProps }: Props, ref) => { @@ -107,6 +108,7 @@ const RelatedStagesActionsPlain = ({ addErrorMessage={addErrorMessage} saveAttempted={saveAttempted} errorMessages={errorMessages} + isLinking={isLinking} constraint={constraint} onLink={onLink} {...passOnProps} diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.types.js b/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.types.js index 3dfe97c009..f6df04d49f 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.types.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.types.js @@ -38,6 +38,7 @@ export type PlainProps = {| addErrorMessage: (ErrorMessagesForRelatedStages) => void, setRelatedStagesDataValues: (() => Object) => void, onLink?: () => void, + isLinking?: boolean, actionsOptions?: { [key: $Keys]: { hidden?: boolean, @@ -53,6 +54,7 @@ export type Props = {| enrollmentId?: string, programStageId: string, onLink?: () => void, + isLinking?: boolean, actionsOptions?: { [key: $Keys]: { hidden?: boolean, diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.container.js b/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.container.js index 7f5d88d78c..66fdbab2c2 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.container.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.container.js @@ -1,5 +1,5 @@ // @flow -import React, { type ComponentType, useRef, useCallback } from 'react'; +import React, { type ComponentType, useRef, useCallback, useState } from 'react'; import { IconLink24, spacers } from '@dhis2/ui'; import { withStyles } from '@material-ui/core'; import i18n from '@dhis2/d2-i18n'; @@ -44,6 +44,7 @@ export const WidgetRelatedStagesPlain = ({ onNavigateToEvent, classes, }: Props) => { + const [isLinking, setIsLinking] = useState(false); const { enrollment } = useCommonEnrollmentDomainData(teiId, enrollmentId, programId); const { currentRelatedStagesStatus } = useRelatedStages({ programStageId, programId }); const { @@ -58,9 +59,11 @@ export const WidgetRelatedStagesPlain = ({ onUpdateEnrollmentEventsSuccess, onUpdateEnrollmentEventsError, onNavigateToEvent, + setIsLinking, }); const onLink = useCallback(() => { + setIsLinking(true); // $FlowFixMe[incompatible-type] const serverRequestEvent: ?RequestEvent = enrollment?.events.find(e => e.event === eventId); @@ -117,6 +120,7 @@ export const WidgetRelatedStagesPlain = ({ programStageId={programStageId} actionsOptions={actionsOptions} onLink={onLink} + isLinking={isLinking} /> diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/hooks/useAddEventWithRelationship.js b/src/core_modules/capture-core/components/WidgetRelatedStages/hooks/useAddEventWithRelationship.js index 43ab09be22..af955ba986 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/hooks/useAddEventWithRelationship.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/hooks/useAddEventWithRelationship.js @@ -18,12 +18,14 @@ export const useAddEventWithRelationship = ({ onUpdateEnrollmentEventsSuccess, onUpdateEnrollmentEventsError, onNavigateToEvent, + setIsLinking, }: { eventId: string, onUpdateOrAddEnrollmentEvents: (events: Array) => void, onUpdateEnrollmentEventsSuccess: (events: Array) => void, onUpdateEnrollmentEventsError: (events: Array) => void, onNavigateToEvent: (eventId: string) => void, + setIsLinking: (isLinking: boolean) => void, }) => { const dataEngine = useDataEngine(); const queryClient = useQueryClient(); @@ -53,6 +55,7 @@ export const useAddEventWithRelationship = ({ } }, onError: (_, payload: { serverData: Object }) => { + setIsLinking(false); showAlert({ message: i18n.t('An error occurred while linking the event') }); onUpdateEnrollmentEventsError && onUpdateEnrollmentEventsError(payload.serverData.events); }, From 0dc73994383321312e1c464fa8bcbf947c8b6ebc Mon Sep 17 00:00:00 2001 From: Simona Domnisoru Date: Thu, 6 Feb 2025 11:21:49 +0100 Subject: [PATCH 23/28] Merge branch 'master' into DHIS2-18018 --- .../EnrollmentEditEvent/EnrollmentEditEventPage.container.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.container.js b/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.container.js index 1593df39a8..c3cc19f6b4 100644 --- a/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.container.js +++ b/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.container.js @@ -212,7 +212,7 @@ const EnrollmentEditEventPageWithContextPlain = ({ navigate(`/enrollment?${buildUrlQueryString({ enrollmentId })}`); const onNavigateToEvent = (eventIdToRedirectTo: string) => { - history.push( + navigate( `/enrollmentEventEdit?${buildUrlQueryString({ eventId: eventIdToRedirectTo, orgUnitId, From 177a849b462de0af40fa2f98585b04aba36ba93f Mon Sep 17 00:00:00 2001 From: Simona Domnisoru Date: Fri, 7 Feb 2025 10:10:34 +0100 Subject: [PATCH 24/28] fix: loading spinner spins indefinitely --- .../WidgetRelatedStages/WidgetRelatedStages.container.js | 7 ++++++- .../hooks/useAddEventWithRelationship.js | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.container.js b/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.container.js index 66fdbab2c2..a4efcca13e 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.container.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.container.js @@ -81,7 +81,12 @@ export const WidgetRelatedStagesPlain = ({ enrollmentId, }); - if (!formHasError && relationship && linkMode) { + if (formHasError) { + setIsLinking(false); + return; + } + + if (relationship && linkMode) { const serverData = createServerData({ enrollment, linkedEvent: relatedStageLinkedEvent, relationship }); addEventWithRelationship({ serverData, linkMode, eventIdToRedirectTo: relatedStageLinkedEvent?.event }); } diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/hooks/useAddEventWithRelationship.js b/src/core_modules/capture-core/components/WidgetRelatedStages/hooks/useAddEventWithRelationship.js index af955ba986..a5014a4466 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/hooks/useAddEventWithRelationship.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/hooks/useAddEventWithRelationship.js @@ -44,6 +44,7 @@ export const useAddEventWithRelationship = ({ onUpdateOrAddEnrollmentEvents && onUpdateOrAddEnrollmentEvents(payload.serverData.events); }, onSuccess: (_, payload: { linkMode: string, eventIdToRedirectTo?: string, serverData: Object }) => { + setIsLinking(false); const queryKey = [ReactQueryAppNamespace, 'linkedEventByOriginEvent', eventId]; queryClient.refetchQueries(queryKey); onUpdateEnrollmentEventsSuccess && onUpdateEnrollmentEventsSuccess(payload.serverData.events); From 53c507c4f8675a1e43b2c28d9f0a3c6e7aa40f3f Mon Sep 17 00:00:00 2001 From: Simona Domnisoru Date: Mon, 10 Feb 2025 09:27:02 +0100 Subject: [PATCH 25/28] chore: typos --- i18n/en.pot | 8 ++++---- .../hooks/useAddEventWithRelationship.js | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/i18n/en.pot b/i18n/en.pot index 5410a62172..d792d1e3ef 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -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: 2025-01-27T10:03:03.122Z\n" -"PO-Revision-Date: 2025-01-27T10:03:03.122Z\n" +"POT-Creation-Date: 2025-02-10T08:26:56.998Z\n" +"PO-Revision-Date: 2025-02-10T08:26:56.998Z\n" msgid "Choose one or more dates..." msgstr "Choose one or more dates..." @@ -1449,8 +1449,8 @@ msgstr "Enter details now" msgid "Link to an existing event" msgstr "Link to an existing event" -msgid "The event was succesfully linked" -msgstr "The event was succesfully linked" +msgid "The event was successfully linked" +msgstr "The event was successfully linked" msgid "An error occurred while linking the event" msgstr "An error occurred while linking the event" diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/hooks/useAddEventWithRelationship.js b/src/core_modules/capture-core/components/WidgetRelatedStages/hooks/useAddEventWithRelationship.js index a5014a4466..c06c4348e5 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/hooks/useAddEventWithRelationship.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/hooks/useAddEventWithRelationship.js @@ -29,7 +29,7 @@ export const useAddEventWithRelationship = ({ }) => { const dataEngine = useDataEngine(); const queryClient = useQueryClient(); - const { show: showSccess } = useAlert(({ message }) => message, { success: true }); + const { show: showSuccess } = useAlert(({ message }) => message, { success: true }); const { show: showAlert } = useAlert(({ message }) => message, { critical: true }); const { mutate } = useMutation( @@ -52,7 +52,7 @@ export const useAddEventWithRelationship = ({ if (payload.linkMode === relatedStageActions.ENTER_DATA && payload.eventIdToRedirectTo) { onNavigateToEvent(payload.eventIdToRedirectTo); } else { - showSccess({ message: i18n.t('The event was succesfully linked') }); + showSuccess({ message: i18n.t('The event was successfully linked') }); } }, onError: (_, payload: { serverData: Object }) => { From 0b171505c8946ff251c4674bb9168955772dc3bd Mon Sep 17 00:00:00 2001 From: Simona Domnisoru Date: Mon, 10 Feb 2025 16:41:58 +0100 Subject: [PATCH 26/28] fix: empty tooltip --- .../RelatedStagesActions.component.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.component.js b/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.component.js index 33fc0e7586..4aec0be5aa 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.component.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.component.js @@ -45,7 +45,6 @@ const styles = () => ({ const Schedule = ({ actionsOptions, - linkableEvents, selectedAction, updateSelectedAction, programStage, @@ -61,7 +60,7 @@ const Schedule = ({ let tooltipContent = ''; if (disabled) { tooltipContent = disabledMessage; - } else if (!linkableEvents.length) { + } else { tooltipContent = i18n.t('{{ linkableStageLabel }} is not repeatable', { linkableStageLabel: programStage.stageForm.name, interpolation: { escapeValue: false }, @@ -90,7 +89,6 @@ const Schedule = ({ const EnterData = ({ actionsOptions, - linkableEvents, selectedAction, updateSelectedAction, programStage, @@ -106,7 +104,7 @@ const EnterData = ({ let tooltipContent = ''; if (disabled) { tooltipContent = disabledMessage; - } else if (!linkableEvents.length) { + } else { tooltipContent = i18n.t('{{ linkableStageLabel }} is not repeatable', { linkableStageLabel: programStage.stageForm.name, interpolation: { escapeValue: false }, @@ -233,7 +231,6 @@ const RelatedStagesActionsPlain = ({ <> Date: Mon, 10 Feb 2025 16:51:22 +0100 Subject: [PATCH 27/28] fix: update tooltip text --- i18n/en.pot | 8 ++++---- .../RelatedStagesActions.component.js | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/i18n/en.pot b/i18n/en.pot index b0d4e748a9..126fd8dbd0 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -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: 2025-02-06T18:07:20.352Z\n" -"PO-Revision-Date: 2025-02-06T18:07:20.352Z\n" +"POT-Creation-Date: 2025-02-10T15:51:26.000Z\n" +"PO-Revision-Date: 2025-02-10T15:51:26.000Z\n" msgid "Choose one or more dates..." msgstr "Choose one or more dates..." @@ -1419,8 +1419,8 @@ msgstr "Choose a {{linkableStageLabel}} event" msgid "Choose a {{linkableStageLabel}}" msgstr "Choose a {{linkableStageLabel}}" -msgid "{{ linkableStageLabel }} is not repeatable" -msgstr "{{ linkableStageLabel }} is not repeatable" +msgid "{{ linkableStageLabel }} can only have only event" +msgstr "{{ linkableStageLabel }} can only have only event" msgid "{{ linkableStageLabel }} has no linkable events" msgstr "{{ linkableStageLabel }} has no linkable events" diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.component.js b/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.component.js index 4aec0be5aa..1f02c256e9 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.component.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.component.js @@ -61,7 +61,7 @@ const Schedule = ({ if (disabled) { tooltipContent = disabledMessage; } else { - tooltipContent = i18n.t('{{ linkableStageLabel }} is not repeatable', { + tooltipContent = i18n.t('{{ linkableStageLabel }} can only have only event', { linkableStageLabel: programStage.stageForm.name, interpolation: { escapeValue: false }, }); @@ -105,7 +105,7 @@ const EnterData = ({ if (disabled) { tooltipContent = disabledMessage; } else { - tooltipContent = i18n.t('{{ linkableStageLabel }} is not repeatable', { + tooltipContent = i18n.t('{{ linkableStageLabel }} can only have only event', { linkableStageLabel: programStage.stageForm.name, interpolation: { escapeValue: false }, }); From 153c5a640718864399b59da3c8fe3bd16b15c59f Mon Sep 17 00:00:00 2001 From: Simona Domnisoru Date: Mon, 10 Feb 2025 16:54:16 +0100 Subject: [PATCH 28/28] fix: update tooltip text --- i18n/en.pot | 8 ++++---- .../RelatedStagesActions.component.js | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/i18n/en.pot b/i18n/en.pot index 126fd8dbd0..e3161d5fd9 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -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: 2025-02-10T15:51:26.000Z\n" -"PO-Revision-Date: 2025-02-10T15:51:26.000Z\n" +"POT-Creation-Date: 2025-02-10T15:54:20.259Z\n" +"PO-Revision-Date: 2025-02-10T15:54:20.260Z\n" msgid "Choose one or more dates..." msgstr "Choose one or more dates..." @@ -1419,8 +1419,8 @@ msgstr "Choose a {{linkableStageLabel}} event" msgid "Choose a {{linkableStageLabel}}" msgstr "Choose a {{linkableStageLabel}}" -msgid "{{ linkableStageLabel }} can only have only event" -msgstr "{{ linkableStageLabel }} can only have only event" +msgid "{{ linkableStageLabel }} can only have one event" +msgstr "{{ linkableStageLabel }} can only have one event" msgid "{{ linkableStageLabel }} has no linkable events" msgstr "{{ linkableStageLabel }} has no linkable events" diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.component.js b/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.component.js index 1f02c256e9..15031c1a7b 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.component.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/RelatedStagesActions/RelatedStagesActions.component.js @@ -61,7 +61,7 @@ const Schedule = ({ if (disabled) { tooltipContent = disabledMessage; } else { - tooltipContent = i18n.t('{{ linkableStageLabel }} can only have only event', { + tooltipContent = i18n.t('{{ linkableStageLabel }} can only have one event', { linkableStageLabel: programStage.stageForm.name, interpolation: { escapeValue: false }, }); @@ -105,7 +105,7 @@ const EnterData = ({ if (disabled) { tooltipContent = disabledMessage; } else { - tooltipContent = i18n.t('{{ linkableStageLabel }} can only have only event', { + tooltipContent = i18n.t('{{ linkableStageLabel }} can only have one event', { linkableStageLabel: programStage.stageForm.name, interpolation: { escapeValue: false }, });