Skip to content

Commit

Permalink
feat: show related stages widget when adding a new relationship
Browse files Browse the repository at this point in the history
  • Loading branch information
simonadomnisoru committed Dec 2, 2024
1 parent ddb3f0e commit 3eb3c93
Show file tree
Hide file tree
Showing 21 changed files with 270 additions and 276 deletions.
13 changes: 8 additions & 5 deletions i18n/en.pot
Original file line number Diff line number Diff line change
Expand Up @@ -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..."
Expand Down Expand Up @@ -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"

Expand Down Expand Up @@ -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"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -30,6 +30,7 @@ export const EnrollmentWithFirstStageDataEntry = (props: Props) => {
ref={relatedStageRef}
programId={passOnProps.programId}
programStageId={firstStageMetaData.stage?.id}
actionTypesOptions={relatedStageModesOptions}
/>
</>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// @flow
import type { ProgramStage, RenderFoundation } from '../../../../metaData';
import type { RelatedStageRefPayload } from '../../../WidgetRelatedStages';
import { RelatedStageModes } from '../../../WidgetRelatedStages';

export type Props = {
firstStageMetaData: {
Expand All @@ -9,4 +10,11 @@ export type Props = {
formFoundation: RenderFoundation,
programId: string,
relatedStageRef?: { current: ?RelatedStageRefPayload },
relatedStageModesOptions?: {
[key: $Keys<typeof RelatedStageModes>]: {
hidden?: boolean,
disabled?: boolean,
disabledMessage?: string
},
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -68,6 +69,13 @@ export type OwnProps = $ReadOnly<{|
saveButtonText: (trackedEntityName: string) => string,
firstStageMetaData?: ?{ stage: ?ProgramStage },
relatedStageRef?: { current: ?RelatedStageRefPayload },
relatedStageModesOptions?: {
[key: $Keys<typeof RelatedStageModes>]: {
hidden?: boolean,
disabled?: boolean,
disabledMessage?: string
},
},
|}>;

type ContainerProps = {|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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(),
Expand All @@ -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 },
Expand All @@ -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);
}));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,17 @@ 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<OwnProps> = (props) => {
const dispatch = useDispatch();
useEffect(() => () => {
dispatch(cleanUpUid());
}, [dispatch]);

return <EnrollmentRegistrationEntry {...props} />;
const relatedStageModesOptions = {
[RelatedStageModes.LINK_EXISTING_RESPONSE]: { hidden: true },
};

return <EnrollmentRegistrationEntry {...props} relatedStageModesOptions={relatedStageModesOptions} />;
};
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
({
Expand All @@ -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 (
<EnrollmentRegistrationEntry
Expand All @@ -37,6 +45,7 @@ const NewEnrollmentRelationshipPlain =
renderDuplicatesDialogActions={renderDuplicatesDialogActions}
renderDuplicatesCardActions={renderDuplicatesCardActions}
ExistingUniqueValueDialogActions={ExistingUniqueValueDialogActions}
relatedStageModesOptions={relatedStageModesOptions}
/>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down Expand Up @@ -95,8 +99,8 @@ const RegisterTeiPlain = ({
</Button>
), [onLink]);

const handleSave = useCallback(() => {
onSave(itemId, dataEntryId);
const handleSave = useCallback((payload: EnrollmentPayload | TeiPayload) => {
onSave(itemId, dataEntryId, payload);
}, [onSave, itemId, dataEntryId]);

return (
Expand Down
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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 |}
Original file line number Diff line number Diff line change
@@ -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) {
Expand Down Expand Up @@ -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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ export {
export {
loadSearchGroupDuplicatesForReviewEpic,
} from '../../../PossibleDuplicatesDialog/possibleDuplicatesDialog.epics';
export { getRelationshipNewTei } from './exposedHelpers/getRelationshipNewTei';
export { getRelationshipNewTeiName } from './exposedHelpers/getRelationshipNewTei';
Original file line number Diff line number Diff line change
Expand Up @@ -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<typeof findModes>,
Expand Down Expand Up @@ -74,10 +77,16 @@ class TeiRelationshipPlain extends React.Component<Props> {
});
}

handleAddRelationshipWithNewTei = (itemId: string, dataEntryId: string) => {
handleAddRelationshipWithNewTei = (
itemId: string,
dataEntryId: string,
payload: EnrollmentPayload | TeiPayload,
) => {
this.props.onAddRelationship({
itemId,
dataEntryId,
data: payload,
id: payload?.trackedEntity,
});
}

Expand Down
Loading

0 comments on commit 3eb3c93

Please sign in to comment.