From 32b73b970e20decb317100e827054042a0b074bb Mon Sep 17 00:00:00 2001 From: henrikmv Date: Tue, 1 Oct 2024 11:18:10 +0200 Subject: [PATCH 1/3] feat: add org unit selector --- i18n/en.pot | 18 +-- .../EnterDataInOrgUnit/EnterData.component.js | 111 ++++++++++++++++++ .../EnterDataInOrgUnit/index.js | 2 + .../LinkToExisting.component.js | 3 +- .../RelatedStagesActions.component.js | 30 ++--- ...iner.js => ScheduleInOrgUnit.component.js} | 0 .../ScheduleInOrgUnit/index.js | 2 +- 7 files changed, 134 insertions(+), 32 deletions(-) create mode 100644 src/core_modules/capture-core/components/WidgetRelatedStages/EnterDataInOrgUnit/EnterData.component.js create mode 100644 src/core_modules/capture-core/components/WidgetRelatedStages/EnterDataInOrgUnit/index.js rename src/core_modules/capture-core/components/WidgetRelatedStages/ScheduleInOrgUnit/{ScheduleInOrgUnit.container.js => ScheduleInOrgUnit.component.js} (100%) diff --git a/i18n/en.pot b/i18n/en.pot index 5d63ac1048..7ff369eb2f 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-09-02T11:08:16.281Z\n" -"PO-Revision-Date: 2024-09-02T11:08:16.281Z\n" +"POT-Creation-Date: 2024-10-01T08:07:34.506Z\n" +"PO-Revision-Date: 2024-10-01T08:07:34.506Z\n" msgid "Choose one or more dates..." msgstr "Choose one or more dates..." @@ -1456,6 +1456,13 @@ msgstr "{{trackedEntityTypeName}} profile" msgid "tracked entity instance" msgstr "tracked entity instance" +msgid "" +"Enter {{linkableStageLabel}} details in the next step after completing this " +"{{currentStageLabel}}." +msgstr "" +"Enter {{linkableStageLabel}} details in the next step after completing this " +"{{currentStageLabel}}." + msgid "Link to an existing {{linkableStageLabel}}" msgstr "Link to an existing {{linkableStageLabel}}" @@ -1471,13 +1478,6 @@ msgstr "{{ linkableStageLabel }} has no linkable events" msgid "Ambiguous relationships, contact system administrator" msgstr "Ambiguous relationships, contact system administrator" -msgid "" -"Enter {{linkableStageLabel}} details in the next step after completing this " -"{{currentStageLabel}}." -msgstr "" -"Enter {{linkableStageLabel}} details in the next step after completing this " -"{{currentStageLabel}}." - msgid "Enter details now" msgstr "Enter details now" diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/EnterDataInOrgUnit/EnterData.component.js b/src/core_modules/capture-core/components/WidgetRelatedStages/EnterDataInOrgUnit/EnterData.component.js new file mode 100644 index 0000000000..c23f176651 --- /dev/null +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/EnterDataInOrgUnit/EnterData.component.js @@ -0,0 +1,111 @@ +// @flow +import React from 'react'; +import i18n from '@dhis2/d2-i18n'; +import type { ComponentType } from 'react'; +import { withStyles } from '@material-ui/core'; +import { colors, spacers, spacersNum, IconInfo16 } from '@dhis2/ui'; +import { OrgUnitSelectorForRelatedStages } from '../FormComponents'; +import type { ErrorMessagesForRelatedStages } from '../RelatedStagesActions'; +import type { RelatedStageDataValueStates } from '../WidgetRelatedStages.types'; + +const styles = { + wrapper: { + padding: `${spacers.dp16} 0`, + maxWidth: '55.75rem', + }, + fieldWrapper: { + display: 'flex', + flexWrap: 'wrap', + alignItems: 'start', + justifyContent: 'space-between', + padding: `${spacers.dp8} ${spacers.dp16}`, + }, + fieldLabel: { + color: colors.grey900, + paddingTop: spacersNum.dp12, + paddingRight: spacersNum.dp16, + flexBasis: '200px', + }, + fieldContent: { + flexBasis: '150px', + flexGrow: 1, + }, + alternateColor: { + backgroundColor: colors.grey100, + }, + infoBox: { + margin: '8px 8px', + display: 'flex', + fontSize: '14px', + gap: '5px', + background: colors.grey100, + padding: '12px 8px', + border: `1px solid ${colors.grey600}`, + }, +}; + +type Props = { + linkableStageLabel: string, + relatedStagesDataValues: RelatedStageDataValueStates, + setRelatedStagesDataValues: (() => Object) => void, + currentStageLabel: string, + saveAttempted: boolean, + errorMessages: ErrorMessagesForRelatedStages, + ...CssClasses +} + +export const EnterDataInOrgUnitPlain = ({ + linkableStageLabel, + relatedStagesDataValues, + setRelatedStagesDataValues, + saveAttempted, + errorMessages, + currentStageLabel, + classes, +}: Props) => { + const onSelectOrgUnit = (e: { id: string, displayName: string, path: string }) => { + const orgUnit = { + id: e.id, + name: e.displayName, + path: e.path, + }; + + setRelatedStagesDataValues(prevValues => ({ + ...prevValues, + orgUnit, + })); + }; + + const onDeselectOrgUnit = () => { + setRelatedStagesDataValues(prevValues => ({ + ...prevValues, + orgUnit: null, + })); + }; + + return ( +
+
+ +
+
+ + {i18n.t( + 'Enter {{linkableStageLabel}} details in the next step after completing this {{currentStageLabel}}.', + { + linkableStageLabel, + currentStageLabel, + }, + )} +
+
+ ); +}; + +export const EnterDataInOrgUnit: ComponentType<$Diff> = withStyles(styles)(EnterDataInOrgUnitPlain); diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/EnterDataInOrgUnit/index.js b/src/core_modules/capture-core/components/WidgetRelatedStages/EnterDataInOrgUnit/index.js new file mode 100644 index 0000000000..0d5a3fe03e --- /dev/null +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/EnterDataInOrgUnit/index.js @@ -0,0 +1,2 @@ +// @flow +export { EnterDataInOrgUnit } from './EnterData.component'; 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 85bf2dae37..e24d11d04e 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 @@ -70,5 +70,4 @@ export const LinkToExistingPlain = ({ ); }; -export const LinkToExisting = - withStyles(styles)(LinkToExistingPlain); +export const LinkToExisting = withStyles(styles)(LinkToExistingPlain); 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 5bd127a887..31dc328b39 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 @@ -1,7 +1,7 @@ // @flow import React, { type ComponentType, useMemo } from 'react'; import i18n from '@dhis2/d2-i18n'; -import { Radio, colors, spacers, spacersNum, IconInfo16, Button } from '@dhis2/ui'; +import { Radio, colors, spacers, spacersNum, Button } 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'; @@ -11,6 +11,7 @@ import { ScheduleInOrgUnit } from '../ScheduleInOrgUnit'; import { useProgramStageInfo } from '../../../metaDataMemoryStores/programCollection/helpers'; import type { Props } from './RelatedStagesActions.types'; import { LinkToExisting } from '../LinkToExisting'; +import { EnterDataInOrgUnit } from '../EnterDataInOrgUnit/EnterData.component'; const styles = () => ({ wrapper: { @@ -37,15 +38,6 @@ const styles = () => ({ clearSelections: { padding: spacers.dp8, }, - infoBox: { - margin: '8px 8px', - display: 'flex', - fontSize: '14px', - gap: '5px', - background: colors.grey100, - padding: '12px 8px', - border: `1px solid ${colors.grey600}`, - }, }); export const RelatedStagesActionsPlain = ({ @@ -170,16 +162,14 @@ export const RelatedStagesActionsPlain = ({ )} {selectedAction === RelatedStagesActionTypes.ENTER_DATA && ( -
- - {i18n.t( - 'Enter {{linkableStageLabel}} details in the next step after completing this {{currentStageLabel}}.', - { - linkableStageLabel: programStage.stageForm.name, - currentStageLabel, - }, - )} -
+ )} {selectedAction === RelatedStagesActionTypes.LINK_EXISTING_RESPONSE && ( diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/ScheduleInOrgUnit/ScheduleInOrgUnit.container.js b/src/core_modules/capture-core/components/WidgetRelatedStages/ScheduleInOrgUnit/ScheduleInOrgUnit.component.js similarity index 100% rename from src/core_modules/capture-core/components/WidgetRelatedStages/ScheduleInOrgUnit/ScheduleInOrgUnit.container.js rename to src/core_modules/capture-core/components/WidgetRelatedStages/ScheduleInOrgUnit/ScheduleInOrgUnit.component.js diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/ScheduleInOrgUnit/index.js b/src/core_modules/capture-core/components/WidgetRelatedStages/ScheduleInOrgUnit/index.js index 2e1a21bf88..c547d9d70d 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/ScheduleInOrgUnit/index.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/ScheduleInOrgUnit/index.js @@ -1,2 +1,2 @@ // @flow -export { ScheduleInOrgUnit } from './ScheduleInOrgUnit.container'; +export { ScheduleInOrgUnit } from './ScheduleInOrgUnit.component'; From 8d0ac6342bc760006a540b10aca6ce37c16189dc Mon Sep 17 00:00:00 2001 From: henrikmv Date: Wed, 2 Oct 2024 09:35:00 +0200 Subject: [PATCH 2/3] feat: select org unit for linked event --- i18n/en.pot | 11 ++-------- .../getConvertedRelatedStageEvent.js | 20 ++++++++++++++----- .../EnterDataInOrgUnit/EnterData.component.js | 5 ++++- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/i18n/en.pot b/i18n/en.pot index 7ff369eb2f..4311092768 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-01T08:07:34.506Z\n" -"PO-Revision-Date: 2024-10-01T08:07:34.506Z\n" +"POT-Creation-Date: 2024-10-02T07:31:11.724Z\n" +"PO-Revision-Date: 2024-10-02T07:31:11.724Z\n" msgid "Choose one or more dates..." msgstr "Choose one or more dates..." @@ -1456,13 +1456,6 @@ msgstr "{{trackedEntityTypeName}} profile" msgid "tracked entity instance" msgstr "tracked entity instance" -msgid "" -"Enter {{linkableStageLabel}} details in the next step after completing this " -"{{currentStageLabel}}." -msgstr "" -"Enter {{linkableStageLabel}} details in the next step after completing this " -"{{currentStageLabel}}." - msgid "Link to an existing {{linkableStageLabel}}" msgstr "Link to an existing {{linkableStageLabel}}" diff --git a/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/getConvertedRelatedStageEvent/getConvertedRelatedStageEvent.js b/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/getConvertedRelatedStageEvent/getConvertedRelatedStageEvent.js index 061f7a5fe4..07832fe83f 100644 --- a/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/getConvertedRelatedStageEvent/getConvertedRelatedStageEvent.js +++ b/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/Validated/getConvertedRelatedStageEvent/getConvertedRelatedStageEvent.js @@ -47,7 +47,6 @@ const getEventDetailsByLinkMode = ({ }), ); } - return ({ linkedEvent: { ...baseEventDetails, @@ -56,23 +55,34 @@ const getEventDetailsByLinkMode = ({ }, linkedEventId: baseEventDetails.event, }); - } else if (linkMode === RelatedStageModes.ENTER_DATA) { + } + + if (linkMode === RelatedStageModes.ENTER_DATA) { + const { orgUnit: linkedEventOrgUnit } = relatedStageDataValues; + if (!linkedEventOrgUnit) { + throw new Error( + errorCreator('Missing required data for creating related stage event')({ + linkedEventOrgUnit, + }), + ); + } return ({ linkedEvent: { ...baseEventDetails, scheduledAt: convertFn(clientRequestEvent.scheduledAt, dataElementTypes.DATE), - orgUnit: convertFn(clientRequestEvent.orgUnit, dataElementTypes.ORGANISATION_UNIT), + orgUnit: convertFn(linkedEventOrgUnit, dataElementTypes.ORGANISATION_UNIT), }, linkedEventId: baseEventDetails.event, }); - } else if (linkMode === RelatedStageModes.LINK_EXISTING_RESPONSE) { + } + + if (linkMode === RelatedStageModes.LINK_EXISTING_RESPONSE) { const { linkedEventId } = relatedStageDataValues; return { linkedEvent: null, linkedEventId, }; } - log.error(errorCreator(`Referral mode ${linkMode} is not supported`)()); return { linkedEvent: null, diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/EnterDataInOrgUnit/EnterData.component.js b/src/core_modules/capture-core/components/WidgetRelatedStages/EnterDataInOrgUnit/EnterData.component.js index c23f176651..47ff354bed 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/EnterDataInOrgUnit/EnterData.component.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/EnterDataInOrgUnit/EnterData.component.js @@ -97,10 +97,13 @@ export const EnterDataInOrgUnitPlain = ({
{i18n.t( - 'Enter {{linkableStageLabel}} details in the next step after completing this {{currentStageLabel}}.', + 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}}.', { linkableStageLabel, currentStageLabel, + orgUnitLabel: relatedStagesDataValues?.orgUnit?.name, }, )}
From a520044a622a3c952cea7ac9cad3652e91952b4d Mon Sep 17 00:00:00 2001 From: henrikmv Date: Mon, 14 Oct 2024 18:18:53 +0200 Subject: [PATCH 3/3] feat: add validation --- .../ValidationFunctions.js | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) 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 07ad6a17a8..b02e1f5ae5 100644 --- a/src/core_modules/capture-core/components/WidgetRelatedStages/relatedStageEventIsValid/ValidationFunctions.js +++ b/src/core_modules/capture-core/components/WidgetRelatedStages/relatedStageEventIsValid/ValidationFunctions.js @@ -44,6 +44,23 @@ const scheduleInOrgUnit = (props) => { return scheduledAtIsValid && orgUnitIsValid; }; +const enterData = (props) => { + const { orgUnit, setErrorMessages } = props ?? {}; + const orgUnitIsValid = isValidOrgUnit(orgUnit); + + if (!orgUnitIsValid) { + setErrorMessages({ + orgUnit: i18n.t('Please provide a valid organisation unit'), + }); + } else { + setErrorMessages({ + orgUnit: null, + }); + } + + return orgUnitIsValid; +}; + const linkToExistingResponse = (props) => { const { linkedEventId, setErrorMessages } = props ?? {}; const linkedEventIdIsValid = !!linkedEventId; @@ -64,7 +81,7 @@ const linkToExistingResponse = (props) => { export const ValidationFunctionsByLinkMode: { [key: string]: (props: ?Props) => boolean } = { [RelatedStageModes.SCHEDULE_IN_ORG]: props => scheduleInOrgUnit(props), - [RelatedStageModes.ENTER_DATA]: () => true, + [RelatedStageModes.ENTER_DATA]: props => enterData(props), [RelatedStageModes.LINK_EXISTING_RESPONSE]: props => linkToExistingResponse(props), };