Skip to content

Commit

Permalink
feat: [DHIS2-13299][DHIS2-16291] Related Stages (#3488)
Browse files Browse the repository at this point in the history
  • Loading branch information
eirikhaugstulen authored Apr 25, 2024
1 parent eb9152a commit 85f38be
Show file tree
Hide file tree
Showing 69 changed files with 1,952 additions and 471 deletions.
31 changes: 31 additions & 0 deletions i18n/en.pot
Original file line number Diff line number Diff line change
Expand Up @@ -1448,6 +1448,37 @@ 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}}"
msgstr "Choose a {{linkableStageLabel}}"

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"

msgid "Link to an existing"
msgstr "Link to an existing"

msgid "Scheduled date"
msgstr "Scheduled date"

msgid "Report date"
msgstr "Report date"

msgid "Please select a valid event"
msgstr "Please select a valid event"

msgid "New {{ eventName }} event"
msgstr "New {{ eventName }} event"

Expand Down
10 changes: 6 additions & 4 deletions src/core_modules/capture-core-utils/storage/IndexedDBAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export class IndexedDBAdapter {
});
}

static get(store, key, db, keyPath) {
static get(store, key, options, db, keyPath) {
return new Promise((resolve, reject) => {
let tx;
let catchError;
Expand All @@ -105,11 +105,13 @@ export class IndexedDBAdapter {
const request = objectStore.get(key);
request.onsuccess = (e) => {
const object = e.target.result;
const { project } = options || {};

if (isDefined(object)) {
object[keyPath] = key;
}
resultObject = object;

resultObject = project ? project(object) : object;
};
} catch (error) {
if (tx) {
Expand Down Expand Up @@ -392,8 +394,8 @@ export class IndexedDBAdapter {
});
}

get(store, key) {
return IndexedDBAdapter.get(store, key, this.db, this.keyPath);
get(store, key, options) {
return IndexedDBAdapter.get(store, key, options, this.db, this.keyPath);
}

// eslint-disable-next-line class-methods-use-this
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ export class StorageController {
}

// using async ensures that the the return value is wrapped in a promise
async get(store, key) {
async get(store, key, options) {
this.throwIfNotOpen();
this.throwIfStoreNotFound(store, 'get');

Expand All @@ -200,7 +200,7 @@ export class StorageController {
);
}

return this.adapter.get(store, key);
return this.adapter.get(store, key, options);
}

// using async ensures that the the return value is wrapped in a promise
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ type OrgUnitValue = {
path: string,
}

export const isValidOrgUnit = (value: OrgUnitValue) => {
export const isValidOrgUnit = (value: ?OrgUnitValue) => {
const valid = !!(value && value.id && value.name);
return valid;
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@ 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';

type Props = {
onSave: (eventId: string, dataEntryId: string, formFoundation: RenderFoundation, saveType?: ?string) => void,
askCompleteEnrollmentOnEventComplete?: ?boolean,
isCompleted?: boolean,
eventId?: ?string,
formFoundation: RenderFoundation,
relatedStageRef: { current?: ?RelatedStageRefPayload },
onSaveAndCompleteEnrollment: (
eventId: string,
dataEntryId: string,
Expand All @@ -28,6 +31,7 @@ const getAskToCompleteEnrollment = (InnerComponent: ComponentType<any>) => (prop
isCompleted,
onSaveAndCompleteEnrollment,
eventId,
relatedStageRef,
...passOnProps
} = props;
const enrollment = useSelector(({ enrollmentDomain }) => enrollmentDomain?.enrollment);
Expand All @@ -54,8 +58,9 @@ const getAskToCompleteEnrollment = (InnerComponent: ComponentType<any>) => (prop
formFoundation: RenderFoundation,
saveType?: string,
) => {
const { linkMode } = relatedStageRef?.current?.getLinkedStageValues() ?? {};
eventDataToSave.current = { itemId, dataEntryId, formFoundation, saveType };
if (askCompleteEnrollmentOnEventComplete && (isCompleted || saveType === addEventSaveTypes.COMPLETE)) {
if (askCompleteEnrollmentOnEventComplete && (isCompleted || saveType === addEventSaveTypes.COMPLETE) && linkMode !== LinkModes.ENTER_DATA) {
setOpenCompleteModal(true);
} else {
onSave(itemId, dataEntryId, formFoundation, saveType);
Expand All @@ -66,6 +71,7 @@ const getAskToCompleteEnrollment = (InnerComponent: ComponentType<any>) => (prop
<>
<InnerComponent
{...passOnProps}
relatedStageRef={relatedStageRef}
askCompleteEnrollmentOnEventComplete={askCompleteEnrollmentOnEventComplete}
onSave={handleOnSave}
isCompleted={isCompleted}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { defaultDialogProps } from '../Dialogs/DiscardDialog.constants';

type Props = {
dataEntryHasChanges: boolean,
disabled: boolean,
onCancel: () => void,
}

Expand Down Expand Up @@ -36,6 +37,7 @@ export class CancelButtonComponent extends React.Component<Props, State> {
<div>
<Button
onClick={this.handleCancel}
disabled={this.props.disabled}
secondary
>
{ i18n.t('Cancel') }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ const styles = theme => ({
verticalFormContainer: {
flexGrow: 10,
maxWidth: '100%',
marginBottom: spacers.dp12,
},
verticalFormInnerContainer: {
maxWidth: theme.typography.pxToRem(892),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { CancelButton } from './CancelButton.container';
type Props = {
id: string,
onCancel: () => void,
cancelButtonIsDisabled?: boolean,
cancelButtonRef?: ?Function,
};

Expand All @@ -22,7 +23,7 @@ const getCancelButton = (InnerComponent: React.ComponentType<any>, optionsFn?: ?
getWrappedInstance = () => this.innerInstance;

render() {
const { onCancel, cancelButtonRef, ...passOnProps } = this.props;
const { onCancel, cancelButtonIsDisabled, cancelButtonRef, ...passOnProps } = this.props;
const options = (optionsFn && optionsFn(this.props)) || {};

return (
Expand All @@ -34,6 +35,7 @@ const getCancelButton = (InnerComponent: React.ComponentType<any>, optionsFn?: ?
id={this.props.id}
onCancel={onCancel}
options={options}
disabled={cancelButtonIsDisabled}
/>
}
{...passOnProps}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import { withStyles } from '@material-ui/core';
import type { Props } from './dataSection.type';


const styles = {
const styles = theme => ({
sectionWrapper: {
border: `1px solid ${colors.grey300}`,
borderRadius: '3px',
marginBottom: spacersNum.dp16,
maxWidth: theme.typography.pxToRem(892),
},
sectionHeader: {
color: colors.grey900,
Expand All @@ -20,7 +21,7 @@ const styles = {
marginBottom: '8px',
width: 'fit-content',
},
};
});

const DataSectionPlain = ({ sectionName, children, classes, dataTest }: Props) => (
<div
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,92 @@ import {
addEnrollmentEventPageDefaultActionTypes,
} from './EnrollmentAddEventPageDefault/EnrollmentAddEventPageDefault.actions';
import {
commitEnrollmentEventWithoutId,
rollbackEnrollmentEventWithoutId,
addPersistedEnrollmentEvents,
commitEnrollmentEvents,
rollbackEnrollmentEvents,
saveFailed,
commitEnrollmentAndEvents,
rollbackEnrollmentAndEvents,
} from '../common/EnrollmentOverviewDomain/enrollment.actions';
import { actions as RelatedStageActions } from '../../WidgetRelatedStages/constants';
import { buildUrlQueryString } from '../../../utils/routing';

export const saveNewEventSucceededEpic = (action$: InputObservable) =>
const shouldNavigateWithRelatedStage = ({
linkMode,
linkedEventId,
linkedOrgUnitId,
history,
}) => {
if (linkMode && linkedEventId) {
if (linkMode === RelatedStageActions.ENTER_DATA) {
const navigate = () => history.push(`/enrollmentEventEdit?${buildUrlQueryString({
eventId: linkedEventId,
orgUnitId: linkedOrgUnitId,
})}`);
return { navigate };
}
}
return {};
};

export const saveNewEventSucceededEpic = (action$: InputObservable, state: ReduxStore, { history }: ApiUtils) =>
action$.pipe(
ofType(
addEnrollmentEventPageDefaultActionTypes.EVENT_SAVE_SUCCESS,
addEnrollmentEventPageDefaultActionTypes.EVENT_SCHEDULE_SUCCESS,
),
map((action) => {
const meta = action.meta;
const eventId = action.payload.bundleReport.typeReportMap.EVENT.objectReports[0].uid;
return commitEnrollmentEventWithoutId(meta.uid, eventId);
const actions = [];
const { enrollmentDomain } = state.value;
const eventsFromApi = action.payload.bundleReport.typeReportMap.EVENT.objectReports;
const { serverData: { events, enrollments } } = action.meta;
const serverDataEvents = events ?? enrollments[0].events;
const enrollmentEvents = enrollmentDomain.enrollment.events;

const { eventsToCommit, eventsToAdd } = serverDataEvents.reduce((acc, event) => {
const eventFromRedux = enrollmentEvents.find(e => e.event === event.event);

if (!eventFromRedux) {
acc.eventsToAdd.push(event);
} else if (eventFromRedux.pendingApiResponse) {
const eventToCommit = eventsFromApi.find(e => e.uid === event.event);
acc.eventsToCommit.push(eventToCommit);
}
return acc;
}, { eventsToCommit: [], eventsToAdd: [] });

if (eventsToAdd.length > 0) {
actions.push(
addPersistedEnrollmentEvents({ events: eventsToAdd }),
);
}

if (eventsToCommit.length > 0) {
actions.push(
commitEnrollmentEvents({ events: eventsToCommit }),
);
}

if (enrollmentDomain.eventSaveInProgress) {
const {
linkMode,
requestEventId,
linkedEventId,
linkedOrgUnitId,
} = enrollmentDomain.eventSaveInProgress;
const requestEvent = eventsFromApi.find(event => event.uid === requestEventId);

if (requestEvent) {
const { navigate } = shouldNavigateWithRelatedStage({
linkMode,
linkedEventId,
linkedOrgUnitId,
history,
});

navigate && navigate();
}
}

return batchActions(actions);
}),
);

Expand All @@ -33,30 +102,15 @@ export const saveNewEventFailedEpic = (action$: InputObservable) =>
addEnrollmentEventPageDefaultActionTypes.EVENT_SCHEDULE_ERROR,
),
map((action) => {
const meta = action.meta;
return batchActions([saveFailed(), rollbackEnrollmentEventWithoutId(meta.uid)]);
}),
);
const { serverData: { events, enrollments } } = action.meta;
const rollbackEvents = events ?? enrollments[0].events;

export const saveEventAndCompleteEnrollmentSucceededEpic = (action$: InputObservable) =>
action$.pipe(
ofType(addEnrollmentEventPageDefaultActionTypes.EVENT_SAVE_ENROLLMENT_COMPLETE_SUCCESS),
map((action) => {
const meta = action.meta;
// the bundleReport returns the events in the same order as the payload order. Therefore, we know that the first event is the newly added one.
const eventId = action.payload.bundleReport.typeReportMap.EVENT.objectReports[0].uid;
return commitEnrollmentAndEvents(meta.uid, eventId);
return batchActions([
saveFailed(),
rollbackEnrollmentEvents({
events: rollbackEvents,
}),
]);
}),
);

export const saveEventAndCompleteEnrollmentFailedEpic = (action$: InputObservable) =>
action$.pipe(
ofType(addEnrollmentEventPageDefaultActionTypes.EVENT_SAVE_ENROLLMENT_COMPLETE_ERROR),
map((action) => {
const meta = action.meta;
return batchActions(
[saveFailed(), rollbackEnrollmentAndEvents(meta.uid)],
'NewEvent.saveEventAndCompleteEnrollmentFailed',
);
}),
);
Loading

0 comments on commit 85f38be

Please sign in to comment.