Skip to content

Commit

Permalink
feat: add aoc and first stage
Browse files Browse the repository at this point in the history
  • Loading branch information
eirikhaugstulen committed Oct 9, 2023
1 parent a93bef9 commit 937f271
Show file tree
Hide file tree
Showing 9 changed files with 137 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export const EnrollmentRegistrationEntry: ComponentType<OwnProps> = ({
formId,
enrollmentMetadata,
formFoundation,
} = useLifecycle(selectedScopeId, id, trackedEntityInstanceAttributes, orgUnit);
} = useLifecycle(selectedScopeId, id, trackedEntityInstanceAttributes, orgUnit, teiId, selectedScopeId);

const isUserInteractionInProgress: boolean = useSelector(
state =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { useEffect, useRef } from 'react';
import type { OrgUnit } from '@dhis2/rules-engine-javascript';
import { startNewEnrollmentDataEntryInitialisation } from '../EnrollmentRegistrationEntry.actions';
import { scopeTypes, getProgramThrowIfNotFound } from '../../../../metaData';
import { useLocationQuery } from '../../../../utils/routing';
import { useScopeInfo } from '../../../../hooks/useScopeInfo';
import { useFormValues } from './index';
import type { InputAttribute } from './useFormValues';
Expand All @@ -19,8 +18,8 @@ export const useLifecycle = (
trackedEntityInstanceAttributes?: Array<InputAttribute>,
orgUnit: ?OrgUnit,
teiId: ?string,
programId: string,
) => {
const { programId } = useLocationQuery();
const dataEntryReadyRef = useRef(false);
const dispatch = useDispatch();
const program = programId && getProgramThrowIfNotFound(programId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export const deriveAutoGenerateEvents = ({
occurredAt: string,
programId: string,
orgUnitId: string,
firstStageMetadata: ProgramStage,
firstStageMetadata: ?ProgramStage,
attributeCategoryOptions: { [categoryId: string]: string } | string,
}) => {
// in case we have a program that does not have an incident date (occurredAt), such as Malaria case diagnosis,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export const deriveFirstStageDuringRegistrationEvent = ({
fieldsValue,
attributeCategoryOptions,
}: {
firstStageMetadata: ProgramStage,
firstStageMetadata: ?ProgramStage,
programId: string,
orgUnitId: string,
currentEventValues?: { [id: string]: any },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ import { DataEntryTrackedEntityInstance } from './TrackedEntityInstance';
type Props = {
showDataEntry: boolean,
programId: string,
onSaveWithoutEnrollment: () => void,
onSaveWithEnrollment: () => void,
};

export class RegisterTeiDataEntryComponent extends React.Component<Props> {
render() {
const { showDataEntry, programId, ...passOnProps } = this.props;
const { showDataEntry, programId, onSaveWithoutEnrollment, onSaveWithEnrollment, ...passOnProps } = this.props;

if (!showDataEntry) {
return null;
Expand All @@ -20,13 +22,15 @@ export class RegisterTeiDataEntryComponent extends React.Component<Props> {
return (
<DataEntryEnrollment
{...passOnProps}
onSave={onSaveWithEnrollment}
/>
);
}

return (
<DataEntryTrackedEntityInstance
{...passOnProps}
onSave={onSaveWithoutEnrollment}
/>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ const DialogButtons = ({ onCancel, onSave, trackedEntityName }) => (
const RegisterTeiPlain = ({
dataEntryId,
onLink,
onSave,
onSaveWithoutEnrollment,
onSaveWithEnrollment,
onGetUnsavedAttributeValues,
trackedEntityName,
trackedEntityTypeId,
Expand Down Expand Up @@ -101,7 +102,8 @@ const RegisterTeiPlain = ({
/>
<RegisterTeiDataEntry
onLink={onLink}
onSave={onSave}
onSaveWithoutEnrollment={onSaveWithoutEnrollment}
onSaveWithEnrollment={onSaveWithEnrollment}
trackedEntityTypeId={trackedEntityTypeId}
onGetUnsavedAttributeValues={onGetUnsavedAttributeValues}
duplicatesReviewPageSize={resultsPageSize}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,32 @@ export const RegisterTei = ({
const error = useSelector(({ newRelationshipRegisterTei }) => (newRelationshipRegisterTei.error));
const selectedScopeId = suggestedProgramId || trackedEntityTypeId;
const { trackedEntityName } = useScopeInfo(selectedScopeId);
const { buildTeiPayload } = useDataEntryReduxConverter({
const {
buildTeiWithEnrollment,
buildTeiWithoutEnrollment,
} = useDataEntryReduxConverter({
selectedScopeId,
dataEntryId,
itemId,
trackedEntityTypeId,
});

const onCreateNewTei = () => {
const teiPayload = buildTeiPayload();
const onCreateNewTeiWithoutEnrollment = () => {
const teiPayload = buildTeiWithoutEnrollment();
onSave(teiPayload);
};

const onCreateNewTeiWithEnrollment = () => {
const teiPayload = buildTeiWithEnrollment();
onSave(teiPayload);
};

return (
<RegisterTeiComponent
dataEntryId={dataEntryId}
onLink={onLink}
onSave={onCreateNewTei}
onSaveWithoutEnrollment={onCreateNewTeiWithoutEnrollment}
onSaveWithEnrollment={onCreateNewTeiWithEnrollment}
onGetUnsavedAttributeValues={onGetUnsavedAttributeValues}
trackedEntityName={trackedEntityName}
selectedScopeId={selectedScopeId}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ export type ComponentProps = {|
error: string,
dataEntryId: string,
trackedEntityName: ?string,
onSave: () => void,
onSaveWithEnrollment: () => void,
onSaveWithoutEnrollment: () => void,
...SharedProps,
...CssClasses,
|};
Original file line number Diff line number Diff line change
@@ -1,45 +1,39 @@
// @flow
import { useSelector } from 'react-redux';
import moment from 'moment';
import { getDataEntryKey } from '../../../../../DataEntry/common/getDataEntryKey';
import { getTrackedEntityTypeThrowIfNotFound, getTrackerProgramThrowIfNotFound } from '../../../../../../metaData';
import {
getTrackerProgramThrowIfNotFound,
Section,
} from '../../../../../../metaData';
import type { RenderFoundation } from '../../../../../../metaData';
import { convertClientToServer, convertFormToClient } from '../../../../../../converters';
import {
convertDataEntryValuesToClientValues,
} from '../../../../../DataEntry/common/convertDataEntryValuesToClientValues';
import { getFormattedStringFromMomentUsingEuropeanGlyphs } from '../../../../../../../capture-core-utils/date';
import { capitalizeFirstLetter } from '../../../../../../../capture-core-utils/string';
import { generateUID } from '../../../../../../utils/uid/generateUID';
import {
useBuildFirstStageRegistration,
} from '../../../../../DataEntries/EnrollmentRegistrationEntry/hooks/useBuildFirstStageRegistration';
import {
useMetadataForRegistrationForm,
} from '../../../../../DataEntries/common/TEIAndEnrollment/useMetadataForRegistrationForm';
import {
useMergeFormFoundationsIfApplicable,
} from '../../../../../DataEntries/EnrollmentRegistrationEntry/hooks/useMergeFormFoundationsIfApplicable';
import {
deriveAutoGenerateEvents,
deriveFirstStageDuringRegistrationEvent,
} from '../../../../New/RegistrationDataEntry/helpers';
import { FEATURETYPE } from '../../../../../../constants';

type DataEntryReduxConverterProps = {
selectedScopeId: string;
dataEntryId: string;
itemId: string;
trackedEntityTypeId: string;
};

function getMetadata(programId: ?string, tetId: string) {
return programId ? getTrackerProgramMetadata(programId) : getTETMetadata(tetId);
}

function getTrackerProgramMetadata(programId: string) {
const program = getTrackerProgramThrowIfNotFound(programId);
return {
form: program.enrollment.enrollmentForm,
attributes: program.trackedEntityType.attributes,
tetName: program.trackedEntityType.name,
};
}

function getTETMetadata(tetId: string) {
const tet = getTrackedEntityTypeThrowIfNotFound(tetId);
return {
form: tet.teiRegistration.form,
attributes: tet.attributes,
tetName: tet.name,
};
}

function getClientValuesForFormData(formValues: Object, formFoundation: RenderFoundation) {
const clientValues = formFoundation.convertValues(formValues, convertFormToClient);
return clientValues;
Expand Down Expand Up @@ -86,7 +80,15 @@ function buildGeometryProp(key: string, serverValues: Object) {
};
}

const geometryType = formValuesKey => Object.values(FEATURETYPE).find(geometryKey => geometryKey === formValuesKey);

const deriveAttributesFromFormValues = (formValues = {}) =>
Object.keys(formValues)
.filter(key => !geometryType(key))
.map<{ attribute: string, value: ?any }>(key => ({ attribute: key, value: formValues[key] }));

export const useDataEntryReduxConverter = ({
selectedScopeId,
dataEntryId,
itemId,
trackedEntityTypeId,
Expand All @@ -96,31 +98,68 @@ export const useDataEntryReduxConverter = ({
const dataEntryFieldValues = useSelector(({ dataEntriesFieldsValue }) => dataEntriesFieldsValue[dataEntryKey]);
const dataEntryFieldsMeta = useSelector(({ dataEntriesFieldsMeta }) => dataEntriesFieldsMeta[dataEntryKey]);
const { programId, orgUnit } = useSelector(({ newRelationshipRegisterTei }) => newRelationshipRegisterTei);
const { formFoundation: scopeFormFoundation } = useMetadataForRegistrationForm({ selectedScopeId });
const { firstStageMetaData } = useBuildFirstStageRegistration(programId, !programId);
const { formFoundation } = useMergeFormFoundationsIfApplicable(scopeFormFoundation, firstStageMetaData);

const buildTeiPayload = () => {
const { form: formFoundation } = getMetadata(programId, trackedEntityTypeId);
const buildTeiWithEnrollment = () => {
if (!formFoundation) return null;
const firstStage = firstStageMetaData && firstStageMetaData.stage;
const clientValues = getClientValuesForFormData(formValues, formFoundation);
const serverValuesForFormValues = formFoundation.convertValues(clientValues, convertClientToServer);
const serverValuesForFormValues = formFoundation.convertAndGroupBySection(clientValues, convertClientToServer);
const serverValuesForMainValues = getServerValuesForMainValues(
dataEntryFieldValues,
dataEntryFieldsMeta,
formFoundation,
);

// $FlowFixMe
const attributes = Object.keys(serverValuesForFormValues)
.map(key => ({
attribute: key,
value: serverValuesForFormValues[key],
}));
const { enrolledAt, occurredAt } = serverValuesForMainValues;

const { stages } = getTrackerProgramThrowIfNotFound(programId);

const attributeCategoryOptionsId = 'attributeCategoryOptions';
const attributeCategoryOptions = Object.keys(serverValuesForMainValues)
.filter(key => key.startsWith(attributeCategoryOptionsId))
.reduce((acc, key) => {
const categoryId = key.split('-')[1];
acc[categoryId] = serverValuesForMainValues[key];
return acc;
}, {});

const formServerValues = serverValuesForFormValues[Section.groups.ENROLLMENT];
const currentEventValues = serverValuesForFormValues[Section.groups.EVENT];


const firstStageDuringRegistrationEvent = deriveFirstStageDuringRegistrationEvent({
firstStageMetadata: firstStage,
programId,
orgUnitId: orgUnit.id,
currentEventValues,
fieldsValue: dataEntryFieldValues,
attributeCategoryOptions,
});

const autoGenerateEvents = deriveAutoGenerateEvents({
firstStageMetadata: firstStage,
stages,
enrolledAt,
occurredAt,
programId,
orgUnitId: orgUnit.id,
attributeCategoryOptions,
});

const allEventsToBeCreated = firstStageDuringRegistrationEvent
? [firstStageDuringRegistrationEvent, ...autoGenerateEvents]
: autoGenerateEvents;

const enrollment = programId ? {
program: programId,
status: 'ACTIVE',
orgUnit: orgUnit.id,
occurredAt: getFormattedStringFromMomentUsingEuropeanGlyphs(moment()),
attributes,
...serverValuesForMainValues,
occurredAt,
enrolledAt,
attributes: deriveAttributesFromFormValues(formServerValues),
events: allEventsToBeCreated,
} : null;

const tetFeatureTypeKey = getPossibleTetFeatureTypeKey(serverValuesForFormValues);
Expand All @@ -131,18 +170,45 @@ export const useDataEntryReduxConverter = ({
}

return {
// $FlowFixMe
attributes: !enrollment ? attributes : undefined,
trackedEntity: generateUID(),
orgUnit: orgUnit.id,
trackedEntityType: trackedEntityTypeId,
geometry,
enrollments: enrollment ? [enrollment] : [],
enrollments: [enrollment],
};
};

const buildTeiWithoutEnrollment = () => {
if (!scopeFormFoundation) return null;
const clientValues = getClientValuesForFormData(formValues, scopeFormFoundation);
const serverValuesForFormValues = scopeFormFoundation.convertValues(clientValues, convertClientToServer);

// $FlowFixMe
const attributes = Object.keys(serverValuesForFormValues)
.map(key => ({
attribute: key,
value: serverValuesForFormValues[key],
}));

const tetFeatureTypeKey = getPossibleTetFeatureTypeKey(serverValuesForFormValues);
let geometry;
if (tetFeatureTypeKey) {
geometry = buildGeometryProp(tetFeatureTypeKey, serverValuesForFormValues);
delete serverValuesForFormValues[tetFeatureTypeKey];
}

return {
attributes,
trackedEntity: generateUID(),
orgUnit: orgUnit.id,
trackedEntityType: trackedEntityTypeId,
geometry,
enrollments: [],
};
};

return {
buildTeiPayload,
buildTeiWithEnrollment,
buildTeiWithoutEnrollment,
};
};

0 comments on commit 937f271

Please sign in to comment.