From f17dbb87c7fc0865d5a45e08be95cf82a059e748 Mon Sep 17 00:00:00 2001 From: Tony Valle Date: Mon, 12 Feb 2024 23:58:32 +0100 Subject: [PATCH 1/3] feat: assign user on first stage registration --- i18n/en.pot | 10 +++--- ...llmentWithFirstStageDataEntry.component.js | 34 ++++++++++++++++++- ...llmentWithFirstStageDataEntry.constants.js | 1 + .../getDataEntryPropsToInclude.js | 13 ++++--- .../hooks/useDataEntrySections.js | 6 ++++ .../Enrollment/actions/open.actionBatchs.js | 2 +- .../hooks/useBuildEnrollmentPayload.js | 3 +- ...deriveFirstStageDuringRegistrationEvent.js | 10 ++++-- 8 files changed, 65 insertions(+), 14 deletions(-) diff --git a/i18n/en.pot b/i18n/en.pot index 00c7be83bf..25d91fd466 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-01-26T09:31:09.168Z\n" -"PO-Revision-Date: 2024-01-26T09:31:09.168Z\n" +"POT-Creation-Date: 2024-02-09T11:22:44.980Z\n" +"PO-Revision-Date: 2024-02-09T11:22:44.980Z\n" msgid "Choose one or more dates..." msgstr "Choose one or more dates..." @@ -152,6 +152,9 @@ msgstr "Complete event" msgid "{{ stageName }} - Basic info" msgstr "{{ stageName }} - Basic info" +msgid "{{ stageName }} - Assignee" +msgstr "{{ stageName }} - Assignee" + msgid "{{ stageName }} - Status" msgstr "{{ stageName }} - Status" @@ -1365,9 +1368,6 @@ msgstr "This stage can only have one event" msgid "Events could not be retrieved. Please try again later." msgstr "Events could not be retrieved. Please try again later." -msgid "Assigned to" -msgstr "Assigned to" - msgid "{{ totalEvents }} events" msgstr "{{ totalEvents }} events" diff --git a/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/EnrollmentWithFirstStageDataEntry.component.js b/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/EnrollmentWithFirstStageDataEntry.component.js index c1b0b328b0..ddbc928b0d 100644 --- a/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/EnrollmentWithFirstStageDataEntry.component.js +++ b/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/EnrollmentWithFirstStageDataEntry.component.js @@ -1,6 +1,7 @@ // @flow import i18n from '@dhis2/d2-i18n'; import { DataEntry } from '../../../DataEntry'; +import { Assignee } from '../../SingleEventRegistrationEntry/DataEntryWrapper/DataEntry/Assignee'; import { withInternalChangeHandler, withLabel, @@ -23,6 +24,7 @@ import labelTypeClasses from './fieldLabels.module.css'; import { withCleanUp } from './withCleanUp'; import { getEventDateValidatorContainers } from './fieldValidators/eventDate.validatorContainersGetter'; import { stageMainDataIds } from './getDataEntryPropsToInclude'; +import { withTransformPropName } from '../../../../HOC'; const overrideMessagePropNames = { errorMessage: 'validationError', @@ -226,6 +228,36 @@ const getReportDateSettingsFn = () => { return reportDateSettings; }; +const getAssigneeSettingsFn = () => { + const assigneeComponent = + withTransformPropName(['onBlur', 'onSet'])( + withFocusSaver()( + withFilterProps((props: Object) => { + const defaultFiltred = defaultFilterProps(props); + const { validationAttempted, touched, ...passOnProps } = defaultFiltred; + return passOnProps; + })(Assignee), + ), + ); + + return { + isApplicable: (props: Object) => { + const enableUserAssignment = props.firstStageMetaData && props.firstStageMetaData.stage.enableUserAssignment; + return !!enableUserAssignment; + }, + getComponent: () => assigneeComponent, + getComponentProps: (props: Object) => createComponentProps({}, { + orientation: getOrientation(props.formHorizontal), + }), + getPropName: () => 'assignee', + getValidatorContainers: () => [], + getMeta: () => ({ + section: sectionKeysForFirstStageDataEntry.ASSIGNEE, + }), + }; +}; + const StageLocationHOC = withDataEntryFieldIfApplicable(getStageGeometrySettings())(withCleanUp()(DataEntry)); const CompleteHOC = withDataEntryFieldIfApplicable(getCompleteFieldSettingsFn())(StageLocationHOC); -export const FirstStageDataEntry = withDataEntryFieldIfApplicable(getReportDateSettingsFn())(CompleteHOC); +const AssigneeHOC = withDataEntryFieldIfApplicable(getAssigneeSettingsFn())(CompleteHOC); +export const FirstStageDataEntry = withDataEntryFieldIfApplicable(getReportDateSettingsFn())(AssigneeHOC); diff --git a/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/EnrollmentWithFirstStageDataEntry.constants.js b/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/EnrollmentWithFirstStageDataEntry.constants.js index 1abc295aa9..611a8b8a93 100644 --- a/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/EnrollmentWithFirstStageDataEntry.constants.js +++ b/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/EnrollmentWithFirstStageDataEntry.constants.js @@ -4,4 +4,5 @@ export const sectionKeysForFirstStageDataEntry = { ENROLLMENT: 'enrollment', STATUS: 'status', STAGE_BASIC_INFO: 'stageBasicInfo', + ASSIGNEE: 'assignee', }; diff --git a/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/getDataEntryPropsToInclude.js b/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/getDataEntryPropsToInclude.js index aef6ec85cf..e24194c800 100644 --- a/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/getDataEntryPropsToInclude.js +++ b/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/getDataEntryPropsToInclude.js @@ -1,5 +1,5 @@ // @flow -import type { RenderFoundation } from '../../../../metaData'; +import type { ProgramStage } from '../../../../metaData'; import { getEventDateValidatorContainers } from './fieldValidators'; import { getConvertGeometryIn, convertGeometryOut, convertStatusIn, convertStatusOut } from '../../converters'; @@ -7,6 +7,7 @@ export const stageMainDataIds = { OCCURRED_AT: 'stageOccurredAt', COMPLETE: 'stageComplete', GEOMETRY: 'stageGeometry', + ASSIGNEE: 'assignee', }; const stageMainDataRulesEngineIds = { @@ -17,7 +18,7 @@ const stageMainDataRulesEngineIds = { export const convertToRulesEngineIds = (id: string) => stageMainDataRulesEngineIds[id]; -export const getDataEntryPropsToInclude = (formFoundation: RenderFoundation) => [ +export const getDataEntryPropsToInclude = (firstStage: ProgramStage) => [ { id: stageMainDataIds.OCCURRED_AT, type: 'DATE', @@ -32,8 +33,12 @@ export const getDataEntryPropsToInclude = (formFoundation: RenderFoundation) => { clientId: stageMainDataIds.GEOMETRY, dataEntryId: stageMainDataIds.GEOMETRY, - onConvertIn: getConvertGeometryIn(formFoundation), + onConvertIn: getConvertGeometryIn(firstStage.stageForm), onConvertOut: convertGeometryOut, - featureType: formFoundation.featureType, + featureType: firstStage.stageForm.featureType, }, + ...(firstStage.enableUserAssignment ? [{ + id: stageMainDataIds.ASSIGNEE, + type: 'ASSIGNEE', + }] : []), ]; diff --git a/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/hooks/useDataEntrySections.js b/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/hooks/useDataEntrySections.js index 3aab5bd6d2..0121a18917 100644 --- a/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/hooks/useDataEntrySections.js +++ b/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/hooks/useDataEntrySections.js @@ -23,6 +23,12 @@ export const useDataEntrySections = (stageName: string, beforeSectionId: string) beforeSectionId, placement: placements.BEFORE_METADATA_BASED_SECTION, }, + [sectionKeysForFirstStageDataEntry.ASSIGNEE]: { + placement: placements.BOTTOM, + name: i18n.t('{{ stageName }} - Assignee', { + stageName, + }), + }, [sectionKeysForFirstStageDataEntry.STATUS]: { placement: placements.BOTTOM, name: i18n.t('{{ stageName }} - Status', { diff --git a/src/core_modules/capture-core/components/DataEntries/Enrollment/actions/open.actionBatchs.js b/src/core_modules/capture-core/components/DataEntries/Enrollment/actions/open.actionBatchs.js index 8e120d4beb..b6dcff586f 100644 --- a/src/core_modules/capture-core/components/DataEntries/Enrollment/actions/open.actionBatchs.js +++ b/src/core_modules/capture-core/components/DataEntries/Enrollment/actions/open.actionBatchs.js @@ -70,7 +70,7 @@ export const openDataEntryForNewEnrollmentBatchAsync = async ({ }) => { const formId = getDataEntryKey(dataEntryId, itemId); const addFormDataActions = addFormData(`${dataEntryId}-${itemId}`, formValues); - const firstStageDataEntryPropsToInclude = firstStage && getDataEntryPropsToInclude(firstStage.stageForm); + const firstStageDataEntryPropsToInclude = firstStage && getDataEntryPropsToInclude(firstStage); const defaultDataEntryValues = { enrolledAt: convertDateObjectToDateFormatString(new Date()) }; const dataEntryPropsToInclude = [ ...enrollmentDataEntryPropsToInclude, 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 d6ae0f8adf..248e132738 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 @@ -116,7 +116,7 @@ export const useBuildEnrollmentPayload = ({ dataEntryFieldsMeta, formFoundation, ); - const { enrolledAt, occurredAt } = serverValuesForMainValues; + const { enrolledAt, occurredAt, assignee } = serverValuesForMainValues; const { stages } = getTrackerProgramThrowIfNotFound(programId); @@ -140,6 +140,7 @@ export const useBuildEnrollmentPayload = ({ currentEventValues, fieldsValue: dataEntryFieldValues, attributeCategoryOptions, + assignee, }); const autoGenerateEvents = deriveAutoGenerateEvents({ 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 2e4f952f92..b67260a482 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 @@ -15,6 +15,7 @@ export const deriveFirstStageDuringRegistrationEvent = ({ currentEventValues, fieldsValue, attributeCategoryOptions, + assignee, }: { firstStageMetadata: ?ProgramStage, programId: string, @@ -22,6 +23,7 @@ export const deriveFirstStageDuringRegistrationEvent = ({ currentEventValues?: { [id: string]: any }, fieldsValue: { [id: string]: any }, attributeCategoryOptions: { [categoryId: string]: string } | string, + assignee?: ApiAssignedUser, }) => { if (!firstStageMetadata) { return null; @@ -32,7 +34,7 @@ export const deriveFirstStageDuringRegistrationEvent = ({ ? { attributeCategoryOptions: convertCategoryOptionsToServer(attributeCategoryOptions) } : {}; - const event = { + const event: any = { status: convertStatusOut(stageComplete), geometry: standardGeoJson(stageGeometry), occurredAt: convertFn(stageOccurredAt, dataElementTypes.DATE), @@ -49,7 +51,11 @@ export const deriveFirstStageDuringRegistrationEvent = ({ }, []) : undefined; if (dataValues) { - return { ...event, dataValues }; + event.dataValues = dataValues; + } + + if (assignee) { + event.assignedUser = assignee; } return event; }; From 6a5c6b248c6dfdc5fc3e4be2462d06a85805aac1 Mon Sep 17 00:00:00 2001 From: Tony Valle Date: Mon, 12 Feb 2024 23:59:30 +0100 Subject: [PATCH 2/3] refactor: simplify some converter code --- .../convertDataEntryValuesToClientValues.js | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/core_modules/capture-core/components/DataEntry/common/convertDataEntryValuesToClientValues.js b/src/core_modules/capture-core/components/DataEntry/common/convertDataEntryValuesToClientValues.js index 6e86756a1f..b9e1d93bb7 100644 --- a/src/core_modules/capture-core/components/DataEntry/common/convertDataEntryValuesToClientValues.js +++ b/src/core_modules/capture-core/components/DataEntry/common/convertDataEntryValuesToClientValues.js @@ -1,9 +1,7 @@ // @flow -/* eslint-disable no-new-func */ import { convertValue } from '../../../converters/formToClient'; import type { RenderFoundation } from '../../../metaData'; -// $FlowSuppress // $FlowFixMe[prop-missing] automated comment const getFunctionFromString = (functionAsString: string) => Function(`return ${functionAsString}`)(); @@ -17,12 +15,13 @@ export function convertDataEntryValuesToClientValues( } const eventValues = Object .keys(dataEntryValues) - // eslint-disable-next-line complexity .reduce((accEventValues, key) => { - const type = dataEntryValuesMeta[key] && dataEntryValuesMeta[key].type; - const onConvertOut = dataEntryValuesMeta[key] && dataEntryValuesMeta[key].onConvertOut; - const clientIgnore = dataEntryValuesMeta[key] && dataEntryValuesMeta[key].clientIgnore; - const customFeatureType = dataEntryValuesMeta[key] && dataEntryValuesMeta[key].featureType; + const { + type, + onConvertOut, + clientIgnore, + featureType: customFeatureType, + } = dataEntryValuesMeta[key] || {}; if (clientIgnore) { return accEventValues; } @@ -30,7 +29,7 @@ export function convertDataEntryValuesToClientValues( const value = dataEntryValues[key]; accEventValues[key] = convertValue(value, type); } else if (onConvertOut) { - const clientId = dataEntryValuesMeta[key] && dataEntryValuesMeta[key].clientId; + const clientId = dataEntryValuesMeta[key].clientId; const dataEntryValue = dataEntryValues[key]; const onConvertOutFn = getFunctionFromString(onConvertOut); accEventValues[clientId] = onConvertOutFn(dataEntryValue, foundation, customFeatureType); From 639aa1f83d3090a784b8be20ff25f607f9453ac8 Mon Sep 17 00:00:00 2001 From: Tony Valle Date: Wed, 14 Feb 2024 14:53:30 +0100 Subject: [PATCH 3/3] fix: field label style --- .../DataEntryWrapper/DataEntry/Assignee/Assignee.component.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core_modules/capture-core/components/DataEntries/SingleEventRegistrationEntry/DataEntryWrapper/DataEntry/Assignee/Assignee.component.js b/src/core_modules/capture-core/components/DataEntries/SingleEventRegistrationEntry/DataEntryWrapper/DataEntry/Assignee/Assignee.component.js index 44910d1616..76c914a7fb 100644 --- a/src/core_modules/capture-core/components/DataEntries/SingleEventRegistrationEntry/DataEntryWrapper/DataEntry/Assignee/Assignee.component.js +++ b/src/core_modules/capture-core/components/DataEntries/SingleEventRegistrationEntry/DataEntryWrapper/DataEntry/Assignee/Assignee.component.js @@ -17,6 +17,9 @@ const getStyles = () => ({ }, label: { flexBasis: 200, + fontSize: 14, + paddingLeft: 5, + color: 'rgba(0, 0, 0, 0.87)', }, field: { flexBasis: 150,