- {i18n.t('Link to an existing {{linkableStageLabel}}', {
+ {i18n.t('Choose a {{linkableStageLabel}} event', {
linkableStageLabel,
})}
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 38bbb6e24b..acf6c6e0a5 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
@@ -4,7 +4,7 @@ import i18n from '@dhis2/d2-i18n';
import { Button, colors, Radio, spacers, spacersNum } 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';
+import { relatedStageActions, mainOptionTranslatedTexts, relatedStageStatus } from '../constants';
import { useCanAddNewEventToStage } from '../hooks/useCanAddNewEventToStage';
import { DataSection } from '../../DataSection';
import { ScheduleInOrgUnit } from '../ScheduleInOrgUnit';
@@ -40,7 +40,138 @@ const styles = () => ({
},
});
-export const RelatedStagesActionsPlain = ({
+const Schedule = ({
+ actionsOptions,
+ linkableEvents,
+ selectedAction,
+ updateSelectedAction,
+ programStage,
+ canAddNewEventToStage,
+}) => {
+ const { hidden, disabled, disabledMessage } =
+ (actionsOptions && actionsOptions[relatedStageActions.SCHEDULE_IN_ORG]) || {};
+ if (hidden) {
+ return null;
+ }
+
+ const tooltipEnabled = disabled || !canAddNewEventToStage;
+ let tooltipContent = '';
+ if (disabled) {
+ tooltipContent = disabledMessage;
+ } else if (!linkableEvents.length) {
+ tooltipContent = i18n.t('{{ linkableStageLabel }} is not repeatable', {
+ linkableStageLabel: programStage.stageForm.name,
+ interpolation: { escapeValue: false },
+ });
+ }
+
+ return (
+
+ updateSelectedAction(e.value)}
+ value={relatedStageActions.SCHEDULE_IN_ORG}
+ />
+
+ );
+};
+
+const EnterData = ({
+ actionsOptions,
+ linkableEvents,
+ selectedAction,
+ updateSelectedAction,
+ programStage,
+ canAddNewEventToStage,
+}) => {
+ const { hidden, disabled, disabledMessage } =
+ (actionsOptions && actionsOptions[relatedStageActions.ENTER_DATA]) || {};
+ if (hidden) {
+ return null;
+ }
+
+ const tooltipEnabled = disabled || !canAddNewEventToStage;
+ let tooltipContent = '';
+ if (disabled) {
+ tooltipContent = disabledMessage;
+ } else if (!linkableEvents.length) {
+ tooltipContent = i18n.t('{{ linkableStageLabel }} is not repeatable', {
+ linkableStageLabel: programStage.stageForm.name,
+ interpolation: { escapeValue: false },
+ });
+ }
+
+ return (
+
+ updateSelectedAction(e.value)}
+ value={relatedStageActions.ENTER_DATA}
+ />
+
+ );
+};
+
+const LinkExistingResponse = ({
+ actionsOptions,
+ linkableEvents,
+ selectedAction,
+ updateSelectedAction,
+ programStage,
+}) => {
+ const { hidden, disabled, disabledMessage } =
+ (actionsOptions && actionsOptions[relatedStageActions.LINK_EXISTING_RESPONSE]) || {};
+ if (hidden) {
+ return null;
+ }
+
+ const tooltipEnabled = disabled || !linkableEvents.length;
+ let tooltipContent = '';
+ if (disabled) {
+ tooltipContent = disabledMessage;
+ } else if (!linkableEvents.length) {
+ tooltipContent = i18n.t('{{ linkableStageLabel }} has no linkable events', {
+ linkableStageLabel: programStage.stageForm.name,
+ interpolation: { escapeValue: false },
+ });
+ }
+
+ return (
+
+ updateSelectedAction(e.value)}
+ value={relatedStageActions.LINK_EXISTING_RESPONSE}
+ />
+
+ );
+};
+
+const RelatedStagesActionsPlain = ({
classes,
type,
relationshipName,
@@ -50,15 +181,15 @@ export const RelatedStagesActionsPlain = ({
relatedStagesDataValues,
setRelatedStagesDataValues,
constraint,
- currentStageLabel,
errorMessages,
saveAttempted,
+ actionsOptions,
}: Props) => {
const { programStage } = useProgramStageInfo(constraint?.programStage?.id);
const selectedAction = useMemo(() => relatedStagesDataValues.linkMode, [relatedStagesDataValues.linkMode]);
- const updateSelectedAction = (action: ?$Values
) => {
+ const updateSelectedAction = (action: ?$Values) => {
setRelatedStagesDataValues(prevState => ({
...prevState,
linkMode: action,
@@ -73,65 +204,34 @@ export const RelatedStagesActionsPlain = ({
return (
{type === relatedStageStatus.LINKABLE && (
<>
-
- updateSelectedAction(e.value)}
- value={RelatedStagesActionTypes.SCHEDULE_IN_ORG}
- />
-
-
- updateSelectedAction(e.value)}
- value={RelatedStagesActionTypes.ENTER_DATA}
- />
-
-
- updateSelectedAction(e.value)}
- value={RelatedStagesActionTypes.LINK_EXISTING_RESPONSE}
- />
-
+
+
+
>
)}
@@ -152,7 +252,7 @@ export const RelatedStagesActionsPlain = ({
)}
- {selectedAction === RelatedStagesActionTypes.SCHEDULE_IN_ORG && (
+ {selectedAction === relatedStageActions.SCHEDULE_IN_ORG && (
)}
- {selectedAction === RelatedStagesActionTypes.ENTER_DATA && (
+ {selectedAction === relatedStageActions.ENTER_DATA && (
)}
- {selectedAction === RelatedStagesActionTypes.LINK_EXISTING_RESPONSE && (
+ {selectedAction === relatedStageActions.LINK_EXISTING_RESPONSE && (
void,
setRelatedStagesDataValues: (() => Object) => void,
- currentStageLabel: string,
+ actionsOptions?: {
+ [key: $Keys]: {
+ hidden?: boolean,
+ disabled?: boolean,
+ disabledMessage?: string
+ },
+ },
...CssClasses
|}
diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.component.js b/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.component.js
index 4c3013ca3c..9ce0628481 100644
--- a/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.component.js
+++ b/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.component.js
@@ -14,7 +14,6 @@ const WidgetRelatedStagesPlain = ({
programId,
enrollmentId,
programStageId,
- currentStageLabel,
...passOnProps
}: Props, ref) => {
const { currentRelatedStagesStatus, selectedRelationshipType, constraint } = useRelatedStages({
@@ -110,7 +109,6 @@ const WidgetRelatedStagesPlain = ({
saveAttempted={saveAttempted}
errorMessages={errorMessages}
constraint={constraint}
- currentStageLabel={currentStageLabel}
{...passOnProps}
/>
);
diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.types.js b/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.types.js
index 0274e06456..7876e528c1 100644
--- a/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.types.js
+++ b/src/core_modules/capture-core/components/WidgetRelatedStages/WidgetRelatedStages.types.js
@@ -1,5 +1,5 @@
// @flow
-import { actions as LinkModes } from './constants';
+import { relatedStageActions } from './index';
import type { Constraint } from './RelatedStagesActions/RelatedStagesActions.types';
export type RelationshipType = {|
@@ -17,12 +17,18 @@ export type RelationshipType = {|
export type Props = {|
programId: string,
- enrollmentId: string,
+ enrollmentId?: string,
programStageId: string,
- currentStageLabel: string,
+ actionsOptions?: {
+ [key: $Keys]: {
+ hidden?: boolean,
+ disabled?: boolean,
+ disabledMessage?: string
+ },
+ },
|}
export type RelatedStageDataValueStates = {|
- linkMode: ?$Keys,
+ linkMode: ?$Keys,
scheduledAt: string,
scheduledAtFormatError: ?{error: ?string, errorCode: ?string},
orgUnit: ?{
@@ -32,3 +38,27 @@ export type RelatedStageDataValueStates = {|
},
linkedEventId: ?string,
|}
+
+export type RelatedStageRelationshipType = {|
+ id: string,
+ fromConstraint: {|
+ programStage: {
+ id: string,
+ },
+ |},
+ toConstraint: {
+ programStage: {
+ id: string,
+ },
+ }
+|}
+
+export type RelatedStageRefPayload = {
+ getLinkedStageValues: () => {
+ selectedRelationshipType: RelatedStageRelationshipType,
+ relatedStageDataValues: RelatedStageDataValueStates,
+ linkMode: ?$Keys,
+ },
+ eventHasLinkableStageRelationship: () => boolean,
+ formIsValidOnSave: () => boolean,
+};
diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/constants.js b/src/core_modules/capture-core/components/WidgetRelatedStages/constants.js
index bb46ba298d..7b021f5fc5 100644
--- a/src/core_modules/capture-core/components/WidgetRelatedStages/constants.js
+++ b/src/core_modules/capture-core/components/WidgetRelatedStages/constants.js
@@ -6,14 +6,14 @@ export const relatedStageStatus = Object.freeze({
AMBIGUOUS_RELATIONSHIPS: 'AMBIGUOUS_RELATIONSHIPS',
});
-export const actions = Object.freeze({
+export const relatedStageActions = Object.freeze({
SCHEDULE_IN_ORG: 'SCHEDULE_IN_ORG',
LINK_EXISTING_RESPONSE: 'LINK_EXISTING_RESPONSE',
ENTER_DATA: 'ENTER_DATA',
});
export const mainOptionTranslatedTexts = {
- [actions.SCHEDULE_IN_ORG]: i18n.t('Schedule'),
- [actions.ENTER_DATA]: i18n.t('Enter details now'),
- [actions.LINK_EXISTING_RESPONSE]: i18n.t('Link to an existing'),
+ [relatedStageActions.SCHEDULE_IN_ORG]: i18n.t('Schedule'),
+ [relatedStageActions.ENTER_DATA]: i18n.t('Enter details now'),
+ [relatedStageActions.LINK_EXISTING_RESPONSE]: i18n.t('Link to an existing event'),
};
diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/hooks/useRelatedStageEvents.js b/src/core_modules/capture-core/components/WidgetRelatedStages/hooks/useRelatedStageEvents.js
index 8a5e8a7c17..058ab02f41 100644
--- a/src/core_modules/capture-core/components/WidgetRelatedStages/hooks/useRelatedStageEvents.js
+++ b/src/core_modules/capture-core/components/WidgetRelatedStages/hooks/useRelatedStageEvents.js
@@ -7,7 +7,7 @@ import { handleAPIResponse, REQUESTED_ENTITIES } from '../../../utils/api';
type Props = {
stageId: ?string,
- enrollmentId: string,
+ enrollmentId: ?string,
scheduledLabel: string,
occurredLabel: string,
relationshipTypeId: ?string,
@@ -41,7 +41,7 @@ export const useRelatedStageEvents = ({
['availableRelatedStageEvents', stageId, enrollmentId, relationshipTypeId],
query,
{
- enabled: !!stageId && enabled,
+ enabled: !!stageId && !!enrollmentId && enabled,
cacheTime: 0,
staleTime: 0,
select: (response: any) => {
diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/index.js b/src/core_modules/capture-core/components/WidgetRelatedStages/index.js
index 1cba4d9943..e75b93c8cc 100644
--- a/src/core_modules/capture-core/components/WidgetRelatedStages/index.js
+++ b/src/core_modules/capture-core/components/WidgetRelatedStages/index.js
@@ -1,4 +1,9 @@
// @flow
export { WidgetRelatedStages } from './WidgetRelatedStages.component';
-export type { RelatedStageDataValueStates } from './WidgetRelatedStages.types';
+export type {
+ RelatedStageDataValueStates,
+ RelatedStageRefPayload,
+ RelatedStageRelationshipType,
+} from './WidgetRelatedStages.types';
+export { relatedStageActions } from './constants';
export { relatedStageWidgetIsValid } from './relatedStageEventIsValid/relatedStageEventIsValid';
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 219e354ded..187f20b625 100644
--- a/src/core_modules/capture-core/components/WidgetRelatedStages/relatedStageEventIsValid/ValidationFunctions.js
+++ b/src/core_modules/capture-core/components/WidgetRelatedStages/relatedStageEventIsValid/ValidationFunctions.js
@@ -2,7 +2,7 @@
import i18n from '@dhis2/d2-i18n';
import { isValidOrgUnit } from '../../../../capture-core-utils/validators/form';
import { isValidDate } from '../../../utils/validation/validators/form';
-import { actions as RelatedStageModes } from '../constants';
+import { relatedStageActions } from '../constants';
type Props = {
scheduledAt: ?string,
@@ -87,8 +87,8 @@ const linkToExistingResponse = (props) => {
export const ValidationFunctionsByLinkMode: { [key: string]: (props: ?Props) => boolean } = {
- [RelatedStageModes.SCHEDULE_IN_ORG]: props => scheduleInOrgUnit(props),
- [RelatedStageModes.ENTER_DATA]: props => enterData(props),
- [RelatedStageModes.LINK_EXISTING_RESPONSE]: props => linkToExistingResponse(props),
+ [relatedStageActions.SCHEDULE_IN_ORG]: props => scheduleInOrgUnit(props),
+ [relatedStageActions.ENTER_DATA]: props => enterData(props),
+ [relatedStageActions.LINK_EXISTING_RESPONSE]: props => linkToExistingResponse(props),
};
diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/relatedStageEventIsValid/relatedStageEventIsValid.types.js b/src/core_modules/capture-core/components/WidgetRelatedStages/relatedStageEventIsValid/relatedStageEventIsValid.types.js
index 1c5e56a676..1f4fa5cb35 100644
--- a/src/core_modules/capture-core/components/WidgetRelatedStages/relatedStageEventIsValid/relatedStageEventIsValid.types.js
+++ b/src/core_modules/capture-core/components/WidgetRelatedStages/relatedStageEventIsValid/relatedStageEventIsValid.types.js
@@ -1,9 +1,9 @@
// @flow
import type { ErrorMessagesForRelatedStages } from '../RelatedStagesActions';
-import { actions as LinkModes } from '../constants';
+import { relatedStageActions } from '../index';
export type RelatedStageIsValidProps = {|
- linkMode: ?$Keys,
+ linkMode: ?$Keys,
scheduledAt: ?string,
scheduledAtFormatError: ?{error: ?string, errorCode: ?string},
orgUnit: ?{
diff --git a/src/core_modules/capture-core/components/WidgetsRelationship/WidgetTrackedEntityRelationship/NewTrackedEntityRelationship/hooks/useAddRelationship.js b/src/core_modules/capture-core/components/WidgetsRelationship/WidgetTrackedEntityRelationship/NewTrackedEntityRelationship/hooks/useAddRelationship.js
index b57a46e4f0..e0e2055d29 100644
--- a/src/core_modules/capture-core/components/WidgetsRelationship/WidgetTrackedEntityRelationship/NewTrackedEntityRelationship/hooks/useAddRelationship.js
+++ b/src/core_modules/capture-core/components/WidgetsRelationship/WidgetTrackedEntityRelationship/NewTrackedEntityRelationship/hooks/useAddRelationship.js
@@ -69,13 +69,20 @@ export const useAddRelationship = ({ teiId, onMutate, onSuccess }: Props) => {
});
},
onSuccess: async (apiResponse, requestData) => {
- const apiRelationshipId = apiResponse.bundleReport.typeReportMap.RELATIONSHIP.objectReports[0].uid;
+ const apiRelationshipIds = apiResponse.bundleReport.typeReportMap.RELATIONSHIP.objectReports.reduce(
+ (acc, report) => [...acc, report.uid],
+ [],
+ );
const currentRelationships = queryClient.getQueryData([ReactQueryAppNamespace, 'relationships', teiId]);
const apiRelationships = handleAPIResponse(REQUESTED_ENTITIES.relationships, currentRelationships);
if (apiRelationships.length === 0) return;
const newRelationships = apiRelationships.map((relationship) => {
- if (relationship.relationship === apiRelationshipId) {
+ if (
+ apiRelationshipIds.find(
+ apiRelationshipId => apiRelationshipId === relationship.relationship,
+ )
+ ) {
return {
...relationship,
pendingApiResponse: false,