{i18n.t('Are you sure you want to unlink this relationship?')}
- {errorMessage && (
+ {mutation.isError && (
- {errorMessage}
+ {mutation.error?.message}
)}
@@ -58,7 +65,11 @@ export const UnlinkModal = ({
setOpenModal(false)} secondary>
{i18n.t('No, cancel')}
-
+ mutation.mutate()}
+ disabled={mutation.isLoading}
+ >
{i18n.t('Yes, unlink relationship')}
diff --git a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/Modal/UnlinkModal.types.js b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/Modal/UnlinkModal.types.js
index 9ca15081f7..b2d0b37c85 100644
--- a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/Modal/UnlinkModal.types.js
+++ b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/Modal/UnlinkModal.types.js
@@ -2,4 +2,5 @@
export type Props = {|
setOpenModal: (open: boolean) => void,
relationshipId: string,
+ originEventId: string,
|};
diff --git a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/Modal/useDeleteRelationship.js b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/Modal/useDeleteRelationship.js
deleted file mode 100644
index 10a9f2f1be..0000000000
--- a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/Modal/useDeleteRelationship.js
+++ /dev/null
@@ -1,48 +0,0 @@
-// @flow
-import i18n from '@dhis2/d2-i18n';
-import log from 'loglevel';
-import { errorCreator } from 'capture-core-utils';
-import { useMutation } from 'react-query';
-import { useAlert, useDataEngine } from '@dhis2/app-runtime';
-
-export type OnDeleteRelationship = ({ relationshipId: string }) => void;
-
-const deleteRelationshipMutation = {
- resource: 'tracker?importStrategy=DELETE&async=false',
- type: 'create',
- data: ({ relationshipId }) => ({
- relationships: [
- {
- relationship: relationshipId,
- },
- ],
- }),
-};
-
-export const useDeleteRelationship = (): { onDeleteRelationship: OnDeleteRelationship } => {
- const dataEngine = useDataEngine();
- const { show: showError } = useAlert(
- i18n.t('An error occurred while deleting the relationship.'),
- {
- critical: true,
- },
- );
-
- const { mutate: onDeleteRelationship } = useMutation(
- ({ relationshipId }) =>
- dataEngine.mutate(deleteRelationshipMutation, { variables: { relationshipId } }),
- {
- onError: (error, { relationshipId }) => {
- log.error(
- errorCreator('An error occurred while deleting the relationship')({
- error,
- relationshipId,
- }),
- );
- showError();
- },
- },
- );
-
- return { onDeleteRelationship };
-};
diff --git a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/OverflowMenu.component.js b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/OverflowMenu.component.js
index 03e98ed013..e399dfd673 100644
--- a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/OverflowMenu.component.js
+++ b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/OverflowMenu.component.js
@@ -20,6 +20,7 @@ export const OverflowMenuComponent = ({
linkedEvent,
relationshipId,
orgUnitId,
+ originEventId,
}: Props) => {
const { push } = useHistory();
@@ -79,12 +80,14 @@ export const OverflowMenuComponent = ({
)}
{unlinkAndDeleteModalIsOpen && (
)}
>
diff --git a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/OverflowMenu.types.js b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/OverflowMenu.types.js
index 82a720c374..3282b5ee9a 100644
--- a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/OverflowMenu.types.js
+++ b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/OverflowMenu.types.js
@@ -3,4 +3,5 @@ export type Props = {
linkedEvent: any,
relationshipId: string,
orgUnitId: string,
+ originEventId: string,
};
diff --git a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetTwoEventWorkspace.container.js b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetTwoEventWorkspace.container.js
index 4df13eccfe..e91a4b7630 100644
--- a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetTwoEventWorkspace.container.js
+++ b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetTwoEventWorkspace.container.js
@@ -98,6 +98,7 @@ const WidgetTwoEventWorkspacePlain = ({
linkedEvent={linkedEvent}
relationshipId={relationship}
orgUnitId={orgUnitId}
+ originEventId={eventId}
/>
)}
From e39d360e0dccf264e246a61b962963b02b5c5192 Mon Sep 17 00:00:00 2001
From: henrikmv
Date: Wed, 30 Oct 2024 13:40:25 +0100
Subject: [PATCH 07/19] fix: remove noticebox and add alerterror
---
i18n/en.pot | 13 ++++-------
.../Modal/UnlinkAndDeleteModal.js | 23 +++++++++++--------
.../OverflowMenu/Modal/UnlinkModal.js | 23 +++++++++++--------
3 files changed, 31 insertions(+), 28 deletions(-)
diff --git a/i18n/en.pot b/i18n/en.pot
index 138a2722ee..0914cc6d61 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-10-30T11:27:59.565Z\n"
-"PO-Revision-Date: 2024-10-30T11:27:59.565Z\n"
+"POT-Creation-Date: 2024-10-30T12:38:53.423Z\n"
+"PO-Revision-Date: 2024-10-30T12:38:53.423Z\n"
msgid "Choose one or more dates..."
msgstr "Choose one or more dates..."
@@ -1489,6 +1489,9 @@ msgstr "{{ scheduledEvents }} scheduled"
msgid "Stages and Events"
msgstr "Stages and Events"
+msgid "An error occurred while unlinking and deleting the event."
+msgstr "An error occurred while unlinking and deleting the event."
+
msgid ""
"Are you sure you want to unlink and delete the event? This will permanently "
"remove the event and all related data."
@@ -1496,9 +1499,6 @@ msgstr ""
"Are you sure you want to unlink and delete the event? This will permanently "
"remove the event and all related data."
-msgid "There was a problem unlinking and deleting the event"
-msgstr "There was a problem unlinking and deleting the event"
-
msgid "Yes, unlink and delete event"
msgstr "Yes, unlink and delete event"
@@ -1508,9 +1508,6 @@ msgstr "Unlink relationship"
msgid "Are you sure you want to unlink this relationship?"
msgstr "Are you sure you want to unlink this relationship?"
-msgid "There was a problem unlinking the relationship"
-msgstr "There was a problem unlinking the relationship"
-
msgid "Yes, unlink relationship"
msgstr "Yes, unlink relationship"
diff --git a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/Modal/UnlinkAndDeleteModal.js b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/Modal/UnlinkAndDeleteModal.js
index 6f465f26be..7b755acb41 100644
--- a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/Modal/UnlinkAndDeleteModal.js
+++ b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/Modal/UnlinkAndDeleteModal.js
@@ -8,9 +8,9 @@ import {
ModalActions,
ModalContent,
ModalTitle,
- NoticeBox,
} from '@dhis2/ui';
-import { useDataEngine } from '@dhis2/app-runtime';
+import log from 'loglevel';
+import { useDataEngine, useAlert } from '@dhis2/app-runtime';
import { useMutation, useQueryClient } from 'react-query';
import { ReactQueryAppNamespace } from 'capture-core/utils/reactQueryHelpers';
import type { Props } from './UnlinkAndDeleteModal.types';
@@ -22,6 +22,10 @@ export const UnlinkAndDeleteModal = ({
}: Props) => {
const dataEngine = useDataEngine();
const queryClient = useQueryClient();
+ const { show: showErrorAlert } = useAlert(
+ i18n.t('An error occurred while unlinking and deleting the event.'),
+ { critical: true },
+ );
const deleteEvent = async () => {
const mutation = {
@@ -42,6 +46,13 @@ export const UnlinkAndDeleteModal = ({
]);
setOpenModal(false);
},
+ onError: (error) => {
+ showErrorAlert();
+ log.error(
+ `Failed to unlink and delete event with ID: ${eventId}`,
+ error,
+ );
+ },
});
return (
@@ -53,14 +64,6 @@ export const UnlinkAndDeleteModal = ({
'Are you sure you want to unlink and delete the event? This will permanently remove the event and all related data.',
)}
- {mutation.isError && (
-
- {mutation.error?.message}
-
- )}
diff --git a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/Modal/UnlinkModal.js b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/Modal/UnlinkModal.js
index 50dae38ef7..a312126348 100644
--- a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/Modal/UnlinkModal.js
+++ b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/Modal/UnlinkModal.js
@@ -8,9 +8,9 @@ import {
ModalActions,
ButtonStrip,
Button,
- NoticeBox,
} from '@dhis2/ui';
-import { useDataEngine } from '@dhis2/app-runtime';
+import log from 'loglevel';
+import { useDataEngine, useAlert } from '@dhis2/app-runtime';
import { useMutation, useQueryClient } from 'react-query';
import { ReactQueryAppNamespace } from 'capture-core/utils/reactQueryHelpers';
import type { Props } from './UnlinkModal.types';
@@ -22,6 +22,10 @@ export const UnlinkModal = ({
}: Props) => {
const dataEngine = useDataEngine();
const queryClient = useQueryClient();
+ const { show: showErrorAlert } = useAlert(
+ i18n.t('An error occurred while unlinking and deleting the event.'),
+ { critical: true },
+ );
const deleteRelationship = async () => {
const mutation = {
@@ -42,6 +46,13 @@ export const UnlinkModal = ({
]);
setOpenModal(false);
},
+ onError: (error) => {
+ showErrorAlert();
+ log.error(
+ `Failed to remove relationship with id ${relationshipId}`,
+ error,
+ );
+ },
});
return (
@@ -51,14 +62,6 @@ export const UnlinkModal = ({
{i18n.t('Are you sure you want to unlink this relationship?')}
- {mutation.isError && (
-
- {mutation.error?.message}
-
- )}
From 4cd3f98199772ca6e47a0f1c934d141641dcc0e8 Mon Sep 17 00:00:00 2001
From: henrikmv
Date: Mon, 4 Nov 2024 19:07:05 +0100
Subject: [PATCH 08/19] feat: add validation
---
i18n/en.pot | 30 ++++--
.../Modal/UnlinkAndDeleteModal.js | 2 +-
.../OverflowMenu/Modal/UnlinkModal.js | 2 +-
.../OverflowMenu/OverflowMenu.component.js | 97 +++++++++++--------
.../OverflowMenu/OverflowMenu.types.js | 2 +
.../WidgetTwoEventWorkspace.container.js | 6 +-
.../WidgetTwoEventWorkspace/hooks/index.js | 3 +
.../hooks/useRelationshipTypeAccess.js | 28 ++++++
.../capture-core/dataQueries/index.js | 2 +
.../dataQueries/useOrganisationUnits.js | 25 +++++
10 files changed, 146 insertions(+), 51 deletions(-)
create mode 100644 src/core_modules/capture-core/components/WidgetTwoEventWorkspace/hooks/index.js
create mode 100644 src/core_modules/capture-core/components/WidgetTwoEventWorkspace/hooks/useRelationshipTypeAccess.js
create mode 100644 src/core_modules/capture-core/dataQueries/useOrganisationUnits.js
diff --git a/i18n/en.pot b/i18n/en.pot
index 0914cc6d61..50e6903ab0 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-10-30T12:38:53.423Z\n"
-"PO-Revision-Date: 2024-10-30T12:38:53.423Z\n"
+"POT-Creation-Date: 2024-11-04T17:57:57.557Z\n"
+"PO-Revision-Date: 2024-11-04T17:57:57.557Z\n"
msgid "Choose one or more dates..."
msgstr "Choose one or more dates..."
@@ -1493,11 +1493,11 @@ msgid "An error occurred while unlinking and deleting the event."
msgstr "An error occurred while unlinking and deleting the event."
msgid ""
-"Are you sure you want to unlink and delete the event? This will permanently "
-"remove the event and all related data."
+"Are you sure you want delete the relationsship and the related event? This "
+"will permanently remove the event and all related data."
msgstr ""
-"Are you sure you want to unlink and delete the event? This will permanently "
-"remove the event and all related data."
+"Are you sure you want delete the relationsship and the related event? This "
+"will permanently remove the event and all related data."
msgid "Yes, unlink and delete event"
msgstr "Yes, unlink and delete event"
@@ -1505,8 +1505,12 @@ msgstr "Yes, unlink and delete event"
msgid "Unlink relationship"
msgstr "Unlink relationship"
-msgid "Are you sure you want to unlink this relationship?"
-msgstr "Are you sure you want to unlink this relationship?"
+msgid ""
+"Are you sure you want to delete the relationship between these two events? "
+"This will only delete the relationship, not the other event"
+msgstr ""
+"Are you sure you want to delete the relationship between these two events? "
+"This will only delete the relationship, not the other event"
msgid "Yes, unlink relationship"
msgstr "Yes, unlink relationship"
@@ -1514,9 +1518,19 @@ msgstr "Yes, unlink relationship"
msgid "View linked event"
msgstr "View linked event"
+msgid "You do not have access to remove the relationship between these two events"
+msgstr "You do not have access to remove the relationship between these two events"
+
msgid "Unlink event"
msgstr "Unlink event"
+msgid ""
+"You do not have access remove the relationship between these two events and "
+"delete the other event"
+msgstr ""
+"You do not have access remove the relationship between these two events and "
+"delete the other event"
+
msgid "Unlink and delete event"
msgstr "Unlink and delete event"
diff --git a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/Modal/UnlinkAndDeleteModal.js b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/Modal/UnlinkAndDeleteModal.js
index 7b755acb41..875aefa4e4 100644
--- a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/Modal/UnlinkAndDeleteModal.js
+++ b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/Modal/UnlinkAndDeleteModal.js
@@ -61,7 +61,7 @@ export const UnlinkAndDeleteModal = ({
{i18n.t(
- 'Are you sure you want to unlink and delete the event? This will permanently remove the event and all related data.',
+ 'Are you sure you want delete the relationsship and the related event? This will permanently remove the event and all related data.',
)}
{i18n.t(
- 'Are you sure you want delete the relationsship and the related event? This will permanently remove the event and all related data.',
+ 'Are you sure you want delete the relationship and the related event? This will permanently remove the event and all related data.',
)}
diff --git a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/hooks/useRelationshipTypeAccess.js b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/hooks/useRelationshipTypeAccess.js
index 484dbdf0c6..a4befc955d 100644
--- a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/hooks/useRelationshipTypeAccess.js
+++ b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/hooks/useRelationshipTypeAccess.js
@@ -5,7 +5,6 @@ import { userStores } from '../../../storageControllers/stores';
export const useRelationshipTypeAccess = (relationshipTypeId: string) => {
const storageController = getUserStorageController();
- console.log('relationshipTypeId', relationshipTypeId);
const { data, error, isLoading } = useIndexedDBQuery(
['relationshipTypeAccess', relationshipTypeId],
diff --git a/src/core_modules/capture-core/components/WidgetsChangelog/common/hooks/index.js b/src/core_modules/capture-core/components/WidgetsChangelog/common/hooks/index.js
index 1cc9b74cc8..af7f2f4226 100644
--- a/src/core_modules/capture-core/components/WidgetsChangelog/common/hooks/index.js
+++ b/src/core_modules/capture-core/components/WidgetsChangelog/common/hooks/index.js
@@ -1,3 +1,2 @@
// @flow
-
export { useChangelogData } from './useChangelogData';
diff --git a/src/core_modules/capture-core/dataQueries/index.js b/src/core_modules/capture-core/dataQueries/index.js
index 7b929714ea..c77469ff0c 100644
--- a/src/core_modules/capture-core/dataQueries/index.js
+++ b/src/core_modules/capture-core/dataQueries/index.js
@@ -1,4 +1,2 @@
export { useOrganisationUnit } from './useOrganisationUnit';
-export { useOrganisationUnits } from './useOrganisationUnits';
-
export { useOrgUnitAutoSelect } from './useOrgUnitsForAutoSelect';
diff --git a/src/core_modules/capture-core/dataQueries/useOrganisationUnits.js b/src/core_modules/capture-core/dataQueries/useOrganisationUnits.js
deleted file mode 100644
index 9baac6fe9b..0000000000
--- a/src/core_modules/capture-core/dataQueries/useOrganisationUnits.js
+++ /dev/null
@@ -1,25 +0,0 @@
-// @flow
-import { useApiMetadataQuery } from '../utils/reactQueryHelpers';
-
-export const useOrganisationUnits = (customQueryOptions: Object) => {
- const queryKey = ['organisationUnits'];
- const queryFn = {
- resource: 'organisationUnits',
- params: {
- fields: ['id, displayName~rename(name), path'],
- withinUserHierarchy: true,
- },
- };
- const defaultQueryOptions = {
- select: ({ organisationUnits }) => organisationUnits,
- };
-
- const queryOptions = { ...defaultQueryOptions, ...customQueryOptions };
-
- const { data, isLoading } = useApiMetadataQuery(queryKey, queryFn, queryOptions);
-
- return {
- isLoading,
- data,
- };
-};
From 73eb06b5db882ff0b4951daadf237e5a35bdccb6 Mon Sep 17 00:00:00 2001
From: henrikmv
Date: Wed, 27 Nov 2024 14:39:07 +0100
Subject: [PATCH 11/19] fix: user message improvements
---
i18n/en.pot | 53 ++++++++-----------
.../Modal/UnlinkAndDeleteModal.js | 6 +--
.../OverflowMenu/Modal/UnlinkModal.js | 6 +--
.../OverflowMenu/OverflowMenu.component.js | 6 +--
4 files changed, 32 insertions(+), 39 deletions(-)
diff --git a/i18n/en.pot b/i18n/en.pot
index 8439b6cd95..f5f3c016f4 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-11-06T12:51:22.564Z\n"
-"PO-Revision-Date: 2024-11-06T12:51:22.564Z\n"
+"POT-Creation-Date: 2024-11-21T13:57:21.323Z\n"
+"PO-Revision-Date: 2024-11-21T13:57:21.323Z\n"
msgid "Choose one or more dates..."
msgstr "Choose one or more dates..."
@@ -1496,47 +1496,40 @@ msgstr "Stages and Events"
msgid "An error occurred while unlinking and deleting the event."
msgstr "An error occurred while unlinking and deleting the event."
+msgid "Unlink and delete linked event"
+msgstr "Unlink and delete linked event"
+
msgid ""
-"Are you sure you want delete the relationship and the related event? This "
-"will permanently remove the event and all related data."
+"Are you sure you want to remove the link and delete the linked event? This "
+"action permanently removes the link, linked event, and all related data."
msgstr ""
-"Are you sure you want delete the relationship and the related event? This "
-"will permanently remove the event and all related data."
+"Are you sure you want to remove the link and delete the linked event? This "
+"action permanently removes the link, linked event, and all related data."
-msgid "Yes, unlink and delete event"
-msgstr "Yes, unlink and delete event"
+msgid "Yes, unlink and delete linked event"
+msgstr "Yes, unlink and delete linked event"
-msgid "Unlink relationship"
-msgstr "Unlink relationship"
+msgid "Unlink event"
+msgstr "Unlink event"
msgid ""
-"Are you sure you want to delete the relationship between these two events? "
-"This will only delete the relationship, not the other event"
+"Are you sure you want to remove the link between these events? This action "
+"removes the link itself, but the linked event will remain."
msgstr ""
-"Are you sure you want to delete the relationship between these two events? "
-"This will only delete the relationship, not the other event"
+"Are you sure you want to remove the link between these events? This action "
+"removes the link itself, but the linked event will remain."
-msgid "Yes, unlink relationship"
-msgstr "Yes, unlink relationship"
+msgid "Yes, unlink event"
+msgstr "Yes, unlink event"
msgid "View linked event"
msgstr "View linked event"
-msgid "You do not have access to remove the relationship between these two events"
-msgstr "You do not have access to remove the relationship between these two events"
-
-msgid "Unlink event"
-msgstr "Unlink event"
-
-msgid ""
-"You do not have access remove the relationship between these two events and "
-"delete the other event"
-msgstr ""
-"You do not have access remove the relationship between these two events and "
-"delete the other event"
+msgid "You do not have access to remove the link between these events"
+msgstr "You do not have access to remove the link between these events"
-msgid "Unlink and delete event"
-msgstr "Unlink and delete event"
+msgid "You do not have access to remove the link and delete the linked event"
+msgstr "You do not have access to remove the link and delete the linked event"
msgid "An error occurred while loading the widget."
msgstr "An error occurred while loading the widget."
diff --git a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/Modal/UnlinkAndDeleteModal.js b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/Modal/UnlinkAndDeleteModal.js
index 647a60552f..abe1af1713 100644
--- a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/Modal/UnlinkAndDeleteModal.js
+++ b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/Modal/UnlinkAndDeleteModal.js
@@ -57,11 +57,11 @@ export const UnlinkAndDeleteModal = ({
return (
- {i18n.t('Delete event')}
+ {i18n.t('Unlink and delete linked event')}
{i18n.t(
- 'Are you sure you want delete the relationship and the related event? This will permanently remove the event and all related data.',
+ 'Are you sure you want to remove the link and delete the linked event? This action permanently removes the link, linked event, and all related data.',
)}
{i18n.t('Are you sure you want to delete the relationship between these two events? This will only delete the relationship, not the other event')}
+
{i18n.t('Are you sure you want to remove the link between these events? This action removes the link itself, but the linked event will remain.')}
@@ -73,7 +73,7 @@ export const UnlinkModal = ({
onClick={() => mutation.mutate()}
disabled={mutation.isLoading}
>
- {i18n.t('Yes, unlink relationship')}
+ {i18n.t('Yes, unlink event')}
diff --git a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/OverflowMenu.component.js b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/OverflowMenu.component.js
index 06911a9684..df12185fec 100644
--- a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/OverflowMenu.component.js
+++ b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/OverflowMenu/OverflowMenu.component.js
@@ -66,7 +66,7 @@ export const OverflowMenuComponent = ({
/>
}
disabled={!stageWriteAccess || !relationshipTypeWriteAccess}
dense
From e3284e970b1a833f2dfda06a00ed5e1095132496 Mon Sep 17 00:00:00 2001
From: henrikmv
Date: Thu, 5 Dec 2024 09:14:40 +0100
Subject: [PATCH 12/19] Merge remote-tracking branch 'origin/master' into
hv/feat/DHIS2-18017_AbilityToUnlinkEvent
---
CHANGELOG.md | 85 +
.../EnrollmentAddEventPageForm.feature | 6 +-
.../EnrollmentAddEventPageNavigation.feature | 2 +-
.../e2e/EnrollmentAddEventPage/sharedSteps.js | 6 +-
.../EnrollmentEditEventPageForm.feature | 36 +-
.../EnrollmentEditEventPageForm.js | 20 +-
.../EnrollmentEditEventPageNavigation.feature | 12 +-
.../EnrollmentEditEventPageNavigation.js | 15 +-
.../EnrollmentPageNavigation.js | 11 +-
.../EnrollmentQuickActions.js | 8 +-
cypress/e2e/EnrollmentPage/sharedSteps.js | 4 +
cypress/e2e/NewPage/NewPage.js | 8 +-
.../e2e/ScopeSelector/ScopeSelector.feature | 18 +-
cypress/e2e/ScopeSelector/ScopeSelector.js | 12 +-
.../e2e/TopBarActions/TopBarActions.feature | 4 +-
cypress/e2e/TopBarActions/TopBarActions.js | 8 +-
.../WidgetAssignee/index.js | 3 +-
.../WidgetEventNote/index.js | 8 +-
.../WidgetsForEnrollmentAddEventPage.js | 4 +-
.../WidgetsForEnrollmentDashboard.js | 4 +-
.../EventBulkActions/EventBulkAction.feature | 49 +
.../EventBulkActions/EventBulkAction.js | 73 +
.../EventWorkingListsUser.js | 20 +-
.../TeiBulkActions/TeiBulkActions.feature | 92 +
.../TeiBulkActions/TeiBulkActions.js | 284 ++
cypress/e2e/WorkingLists/sharedSteps.js | 79 +-
.../event-bulk-actions-selected-rows.png | Bin 0 -> 126931 bytes
...tity-bulk-actions-complete-enrollments.png | Bin 0 -> 148631 bytes
...entity-bulk-actions-delete-enrollments.png | Bin 0 -> 159251 bytes
...cked-entity-bulk-actions-selected-rows.png | Bin 0 -> 128972 bytes
docs/user/using-the-capture-app.md | 42 +
i18n/cs.po | 57 +-
i18n/en.pot | 254 +-
i18n/es.po | 25 +-
i18n/es_419.po | 17 +-
i18n/lo.po | 19 +-
i18n/nb.po | 19 +-
i18n/nl.po | 15 +-
i18n/pt.po | 216 +-
i18n/ru.po | 14 +-
i18n/zh.po | 23 +-
i18n/zh_CN.po | 10 +-
package.json | 6 +-
packages/rules-engine/package.json | 2 +-
.../featuresSupport/support.js | 2 +
.../EnrollmentBreadcrumb.js | 171 +
.../hooks/useWorkingListLabel.js | 60 +
.../Breadcrumbs/EnrollmentBreadcrumb/index.js | 3 +
.../EventBreadcrumb/EventBreadcrumb.js | 106 +
.../hooks/useWorkingListLabel.js | 40 +
.../Breadcrumbs/EventBreadcrumb/index.js | 3 +
.../Breadcrumbs/common/BreadcrumbItem.js | 47 +
.../SingleEventRegistrationEntry.component.js | 1 -
.../SingleEventRegistrationEntry.container.js | 3 +-
.../Date/DateFilter.component.js | 142 +-
.../Date/DateFilterManager.component.js | 12 +-
.../FiltersForTypes/Date/End.component.js | 7 -
.../FiltersForTypes/Date/From.component.js | 43 +-
.../Date/RangeFilter.component.js | 6 -
.../FiltersForTypes/Date/To.component.js | 46 +-
.../Date/dateFilterDataGetter.js | 8 +-
.../FiltersForTypes/Date/types/date.types.js | 6 +
.../FiltersForTypes/Date/types/index.js | 2 +-
.../Numeric/NumericFilter.component.js | 6 +
.../DateAndTime/D2Date/D2Date.component.js | 207 +-
.../D2Date/D2DateCalendar.component.js | 158 -
.../D2Date/D2DatePopup.component.js | 94 -
.../DateAndTime/D2Date/customStyles.css | 12 -
.../DateAndTime/D2Date/d2DatePopup.const.js | 16 -
.../FormFields/DateAndTime/D2Date/getTheme.js | 14 -
.../FormFields/Options/FormGroup.component.js | 2 +
.../List/OnlineList/OnlineList.component.js | 62 +-
.../ListViewContextBuilder.component.js | 10 +
.../ListView/Main/ListViewMain.component.js | 36 +-
.../ListView/Main/listViewMain.types.js | 8 +-
.../ListView/types/listView.types.js | 15 +-
.../EnrollmentPageDefault.container.js | 29 +-
.../EnrollmentPageDefault.types.js | 11 +-
...EnrollmentAddEventPageDefault.container.js | 23 +-
.../EnrollmentAddEventPageDefault.types.js | 9 +-
.../EnrollmentEditEventPage.component.js | 8 +
.../EnrollmentEditEventPage.container.js | 28 +-
.../EnrollmentEditEventPage.types.js | 7 +-
.../PageLayout/DefaultPageLayout.constants.js | 4 -
.../EventDetailsSection.component.js | 3 +
.../EventDetailsSection.container.js | 1 +
.../ViewEventComponent/ViewEvent.component.js | 68 +-
.../ViewEventComponent/ViewEvent.container.js | 7 +-
.../ViewEvent/ViewEventPage.component.js | 1 +
.../DefaultEnrollmentLayout.constants.js | 8 -
.../DefaultEnrollmentLayout.types.js | 1 -
.../EnrollmentPageLayout.js | 56 +-
.../WidgetEventEditWrapper.js | 24 +-
.../Pagination/withDefaultNavigation.js | 9 +-
.../Pagination/withRowsPerPageSelector.js | 4 +-
.../ScopeSelector/hooks/useSetProgramId.js | 2 +-
.../DataEntry/fieldValidators/index.js | 5 +
.../orgUnit.validatorContainersGetter.js | 15 +
.../EventChangelogWrapper.component.js | 3 +-
.../EventChangelogWrapper.types.js | 1 +
.../ViewEventDataEntry.component.js | 24 +-
.../viewEventDataEntry.actions.js | 7 +-
.../WidgetEventEdit.container.js | 216 +-
.../WidgetHeader/WidgetHeader.container.js | 129 +
.../WidgetHeader/WidgetHeader.types.js | 16 +
.../WidgetEventEdit/WidgetHeader/index.js | 2 +
.../OverflowMenu/OverflowMenu.component.js | 2 +
.../OverflowMenu/OverflowMenu.container.js | 2 +
.../OverflowMenu/OverflowMenu.types.js | 2 +
...TrackedEntityChangelogWrapper.component.js | 8 +-
.../TrackedEntityChangelogWrapper.types.js | 1 +
.../WidgetProfile/WidgetProfile.component.js | 1 +
.../Stages/Stage/StageDetail/hooks/helpers.js | 7 +-
.../WidgetHeader/WidgetHeader.container.js | 63 +
.../WidgetHeader/WidgetHeader.types.js | 21 +
.../WidgetHeader/index.js | 2 +
.../WidgetTwoEventWorkspace.container.js | 110 +-
.../WidgetTwoEventWorkspace.types.js | 12 +-
.../WidgetTwoEventWorkspaceWrapper.const.js | 5 +
.../WidgetWrapper/WidgetWrapper.container.js | 74 +
.../WidgetWrapper/WidgetWrapper.types.js | 15 +
.../WidgetWrapper/index.js | 2 +
.../WidgetTwoEventWorkspace/index.js | 1 +
.../WidgetEventChangelog.js | 3 +
.../WidgetTrackedEntityChangelog.js | 3 +
.../common/Changelog/Changelog.container.js | 30 +-
.../ChangelogCells/ChangelogValueCell.js | 31 +-
.../ChangelogTable/ChangelogTableRow.js | 4 +-
.../WidgetsChangelog/common/hooks/index.js | 1 +
.../common/hooks/useChangelogData.js | 92 +-
.../common/hooks/useListDataValues.js | 177 +
.../utils/getSubValueForChangelogData.js | 148 +
...ventWorkingListsReduxProvider.container.js | 4 +-
.../eventWorkingListsReduxProvider.types.js | 2 +-
...ventWorkingListsUpdateTrigger.component.js | 7 +-
...ventWorkingListsViewMenuSetup.component.js | 50 +-
.../EventWorkingListsViewMenuSetup.types.js | 7 +
.../Actions/CompleteAction/CompleteAction.js | 169 +
.../hooks/useBulkCompleteEvents.js | 141 +
.../Actions/DeleteAction/DeleteAction.js | 102 +
.../EventBulkActions/Actions/index.js | 4 +
.../EventBulkActions/EventBulkActions.js | 47 +
.../EventBulkActions/index.js | 3 +
.../TeiWorkingListsReduxProvider.container.js | 4 +-
.../teiWorkingListsReduxProvider.types.js | 2 +-
.../Setup/TeiWorkingListsSetup.component.js | 10 +-
.../Setup/teiWorkingListsSetup.types.js | 3 +-
.../Actions/CompleteAction/CompleteAction.js | 239 +
.../hooks/useCompleteBulkEnrollments.js | 267 ++
.../Actions/CompleteAction/index.js | 3 +
.../DeleteEnrollmentsAction.js | 56 +
.../CustomCheckbox/CustomCheckbox.js | 80 +
.../CustomCheckbox/index.js | 3 +
.../EnrollmentDeleteModal.js | 188 +
.../EnrollmentDeleteModal/index.js | 3 +
.../hooks/useDeleteEnrollments.js | 159 +
.../Actions/DeleteEnrollmentsAction/index.js | 3 +
.../DeleteTeiAction/DeleteTeiAction.js | 87 +
.../hooks/useCascadeDeleteTei.js | 51 +
.../Actions/DeleteTeiAction/index.js | 3 +
.../TrackedEntityBulkActions/Actions/index.js | 4 +
.../TrackedEntityBulkActions.component.js | 51 +
.../TrackedEntityBulkActions.container.js | 40 +
.../TrackedEntityBulkActions.types.js | 17 +
.../TrackedEntityBulkActions/index.js | 3 +
...ckerWorkingListsViewMenuSetup.component.js | 45 +-
.../TrackerWorkingListsViewMenuSetup.types.js | 13 +
.../BulkActionBar/BulkActionBar.component.js | 50 +
.../BulkActionBar/BulkActionBar.container.js | 19 +
.../BulkActionBar/BulkActionBar.types.js | 16 +
.../BulkActionBar/hooks/index.js | 3 +
.../hooks/useSelectedRowsController.js | 66 +
.../WorkingListsBase/BulkActionBar/index.js | 4 +
...istViewBuilderContextProvider.component.js | 28 +-
...stsListViewBuilderContextProvider.types.js | 24 +-
.../WorkingListsContextBuilder.component.js | 23 +-
.../workingListsContextBuilder.types.js | 11 +-
.../ListViewBuilder.component.js | 12 +-
.../TemplateSelector.component.js | 3 +
.../TemplateSelectorChip.component.js | 9 +-
.../TemplatesLoader/templatesLoader.types.js | 5 +
.../TemplatesManager.component.js | 10 +-
.../workingListsBase.types.js | 30 +-
.../useWorkingListsCommonStateManagement.js | 1 +
.../capture-core/converters/clientToView.js | 8 +-
.../utils/userInfo/useAuthority.js | 27 +
yarn.lock | 4094 +++++++++--------
187 files changed, 7507 insertions(+), 3282 deletions(-)
create mode 100644 cypress/e2e/WorkingLists/EventWorkingLists/EventBulkActions/EventBulkAction.feature
create mode 100644 cypress/e2e/WorkingLists/EventWorkingLists/EventBulkActions/EventBulkAction.js
create mode 100644 cypress/e2e/WorkingLists/TeiWorkingLists/TeiBulkActions/TeiBulkActions.feature
create mode 100644 cypress/e2e/WorkingLists/TeiWorkingLists/TeiBulkActions/TeiBulkActions.js
create mode 100644 docs/user/resources/images/event-bulk-actions-selected-rows.png
create mode 100644 docs/user/resources/images/tracked-entity-bulk-actions-complete-enrollments.png
create mode 100644 docs/user/resources/images/tracked-entity-bulk-actions-delete-enrollments.png
create mode 100644 docs/user/resources/images/tracked-entity-bulk-actions-selected-rows.png
create mode 100644 src/core_modules/capture-core/components/Breadcrumbs/EnrollmentBreadcrumb/EnrollmentBreadcrumb.js
create mode 100644 src/core_modules/capture-core/components/Breadcrumbs/EnrollmentBreadcrumb/hooks/useWorkingListLabel.js
create mode 100644 src/core_modules/capture-core/components/Breadcrumbs/EnrollmentBreadcrumb/index.js
create mode 100644 src/core_modules/capture-core/components/Breadcrumbs/EventBreadcrumb/EventBreadcrumb.js
create mode 100644 src/core_modules/capture-core/components/Breadcrumbs/EventBreadcrumb/hooks/useWorkingListLabel.js
create mode 100644 src/core_modules/capture-core/components/Breadcrumbs/EventBreadcrumb/index.js
create mode 100644 src/core_modules/capture-core/components/Breadcrumbs/common/BreadcrumbItem.js
delete mode 100644 src/core_modules/capture-core/components/FormFields/DateAndTime/D2Date/D2DateCalendar.component.js
delete mode 100644 src/core_modules/capture-core/components/FormFields/DateAndTime/D2Date/D2DatePopup.component.js
delete mode 100644 src/core_modules/capture-core/components/FormFields/DateAndTime/D2Date/customStyles.css
delete mode 100644 src/core_modules/capture-core/components/FormFields/DateAndTime/D2Date/d2DatePopup.const.js
delete mode 100644 src/core_modules/capture-core/components/FormFields/DateAndTime/D2Date/getTheme.js
create mode 100644 src/core_modules/capture-core/components/WidgetEventEdit/DataEntry/fieldValidators/index.js
create mode 100644 src/core_modules/capture-core/components/WidgetEventEdit/DataEntry/fieldValidators/orgUnit.validatorContainersGetter.js
create mode 100644 src/core_modules/capture-core/components/WidgetEventEdit/WidgetHeader/WidgetHeader.container.js
create mode 100644 src/core_modules/capture-core/components/WidgetEventEdit/WidgetHeader/WidgetHeader.types.js
create mode 100644 src/core_modules/capture-core/components/WidgetEventEdit/WidgetHeader/index.js
create mode 100644 src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetHeader/WidgetHeader.container.js
create mode 100644 src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetHeader/WidgetHeader.types.js
create mode 100644 src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetHeader/index.js
create mode 100644 src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetTwoEventWorkspaceWrapper.const.js
create mode 100644 src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetWrapper/WidgetWrapper.container.js
create mode 100644 src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetWrapper/WidgetWrapper.types.js
create mode 100644 src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetWrapper/index.js
create mode 100644 src/core_modules/capture-core/components/WidgetsChangelog/common/hooks/useListDataValues.js
create mode 100644 src/core_modules/capture-core/components/WidgetsChangelog/common/utils/getSubValueForChangelogData.js
create mode 100644 src/core_modules/capture-core/components/WorkingLists/EventWorkingListsCommon/EventBulkActions/Actions/CompleteAction/CompleteAction.js
create mode 100644 src/core_modules/capture-core/components/WorkingLists/EventWorkingListsCommon/EventBulkActions/Actions/CompleteAction/hooks/useBulkCompleteEvents.js
create mode 100644 src/core_modules/capture-core/components/WorkingLists/EventWorkingListsCommon/EventBulkActions/Actions/DeleteAction/DeleteAction.js
create mode 100644 src/core_modules/capture-core/components/WorkingLists/EventWorkingListsCommon/EventBulkActions/Actions/index.js
create mode 100644 src/core_modules/capture-core/components/WorkingLists/EventWorkingListsCommon/EventBulkActions/EventBulkActions.js
create mode 100644 src/core_modules/capture-core/components/WorkingLists/EventWorkingListsCommon/EventBulkActions/index.js
create mode 100644 src/core_modules/capture-core/components/WorkingLists/TeiWorkingLists/TrackedEntityBulkActions/Actions/CompleteAction/CompleteAction.js
create mode 100644 src/core_modules/capture-core/components/WorkingLists/TeiWorkingLists/TrackedEntityBulkActions/Actions/CompleteAction/hooks/useCompleteBulkEnrollments.js
create mode 100644 src/core_modules/capture-core/components/WorkingLists/TeiWorkingLists/TrackedEntityBulkActions/Actions/CompleteAction/index.js
create mode 100644 src/core_modules/capture-core/components/WorkingLists/TeiWorkingLists/TrackedEntityBulkActions/Actions/DeleteEnrollmentsAction/DeleteEnrollmentsAction.js
create mode 100644 src/core_modules/capture-core/components/WorkingLists/TeiWorkingLists/TrackedEntityBulkActions/Actions/DeleteEnrollmentsAction/EnrollmentDeleteModal/CustomCheckbox/CustomCheckbox.js
create mode 100644 src/core_modules/capture-core/components/WorkingLists/TeiWorkingLists/TrackedEntityBulkActions/Actions/DeleteEnrollmentsAction/EnrollmentDeleteModal/CustomCheckbox/index.js
create mode 100644 src/core_modules/capture-core/components/WorkingLists/TeiWorkingLists/TrackedEntityBulkActions/Actions/DeleteEnrollmentsAction/EnrollmentDeleteModal/EnrollmentDeleteModal.js
create mode 100644 src/core_modules/capture-core/components/WorkingLists/TeiWorkingLists/TrackedEntityBulkActions/Actions/DeleteEnrollmentsAction/EnrollmentDeleteModal/index.js
create mode 100644 src/core_modules/capture-core/components/WorkingLists/TeiWorkingLists/TrackedEntityBulkActions/Actions/DeleteEnrollmentsAction/hooks/useDeleteEnrollments.js
create mode 100644 src/core_modules/capture-core/components/WorkingLists/TeiWorkingLists/TrackedEntityBulkActions/Actions/DeleteEnrollmentsAction/index.js
create mode 100644 src/core_modules/capture-core/components/WorkingLists/TeiWorkingLists/TrackedEntityBulkActions/Actions/DeleteTeiAction/DeleteTeiAction.js
create mode 100644 src/core_modules/capture-core/components/WorkingLists/TeiWorkingLists/TrackedEntityBulkActions/Actions/DeleteTeiAction/hooks/useCascadeDeleteTei.js
create mode 100644 src/core_modules/capture-core/components/WorkingLists/TeiWorkingLists/TrackedEntityBulkActions/Actions/DeleteTeiAction/index.js
create mode 100644 src/core_modules/capture-core/components/WorkingLists/TeiWorkingLists/TrackedEntityBulkActions/Actions/index.js
create mode 100644 src/core_modules/capture-core/components/WorkingLists/TeiWorkingLists/TrackedEntityBulkActions/TrackedEntityBulkActions.component.js
create mode 100644 src/core_modules/capture-core/components/WorkingLists/TeiWorkingLists/TrackedEntityBulkActions/TrackedEntityBulkActions.container.js
create mode 100644 src/core_modules/capture-core/components/WorkingLists/TeiWorkingLists/TrackedEntityBulkActions/TrackedEntityBulkActions.types.js
create mode 100644 src/core_modules/capture-core/components/WorkingLists/TeiWorkingLists/TrackedEntityBulkActions/index.js
create mode 100644 src/core_modules/capture-core/components/WorkingLists/WorkingListsBase/BulkActionBar/BulkActionBar.component.js
create mode 100644 src/core_modules/capture-core/components/WorkingLists/WorkingListsBase/BulkActionBar/BulkActionBar.container.js
create mode 100644 src/core_modules/capture-core/components/WorkingLists/WorkingListsBase/BulkActionBar/BulkActionBar.types.js
create mode 100644 src/core_modules/capture-core/components/WorkingLists/WorkingListsBase/BulkActionBar/hooks/index.js
create mode 100644 src/core_modules/capture-core/components/WorkingLists/WorkingListsBase/BulkActionBar/hooks/useSelectedRowsController.js
create mode 100644 src/core_modules/capture-core/components/WorkingLists/WorkingListsBase/BulkActionBar/index.js
create mode 100644 src/core_modules/capture-core/utils/userInfo/useAuthority.js
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5ec4072b5a..37492757dc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,88 @@
+# [101.18.0](https://github.com/dhis2/capture-app/compare/v101.17.0...v101.18.0) (2024-11-28)
+
+
+### Features
+
+* [DHIS2-16337] Org unit in view event page ([#3882](https://github.com/dhis2/capture-app/issues/3882)) ([c605e82](https://github.com/dhis2/capture-app/commit/c605e828622aa9f9b0c10f96359ba1b037c8e0ee))
+
+# [101.17.0](https://github.com/dhis2/capture-app/compare/v101.16.7...v101.17.0) (2024-11-25)
+
+
+### Features
+
+* [DHIS2-15463] Use dhis2 ui calendarInput component in working list ([#3712](https://github.com/dhis2/capture-app/issues/3712)) ([5f27455](https://github.com/dhis2/capture-app/commit/5f27455b136d6d994adea0788bf2d0683dfe5d06))
+
+## [101.16.7](https://github.com/dhis2/capture-app/compare/v101.16.6...v101.16.7) (2024-11-25)
+
+
+### Bug Fixes
+
+* [DHIS2-16801] events scheduled for today's date not showing today ([#3856](https://github.com/dhis2/capture-app/issues/3856)) ([d63e124](https://github.com/dhis2/capture-app/commit/d63e124d1c0702898b453be8cfbdd0a5f4620ba5))
+
+## [101.16.6](https://github.com/dhis2/capture-app/compare/v101.16.5...v101.16.6) (2024-11-25)
+
+
+### Bug Fixes
+
+* [DHIS2-17519] app crashing when opening new event from view event ([#3781](https://github.com/dhis2/capture-app/issues/3781)) ([93366ef](https://github.com/dhis2/capture-app/commit/93366ef504210cc9aa746f7d10a4cc7d6188586d))
+* [DHIS2-18150] user has to click out of range filter for update button to trigger ([#3855](https://github.com/dhis2/capture-app/issues/3855)) ([f70b205](https://github.com/dhis2/capture-app/commit/f70b2053f753388e994c89698677821fd8032f79))
+
+## [101.16.5](https://github.com/dhis2/capture-app/compare/v101.16.4...v101.16.5) (2024-11-24)
+
+
+### Bug Fixes
+
+* **translations:** sync translations from transifex (master) ([3098faf](https://github.com/dhis2/capture-app/commit/3098faf8b73dbfc0d894535cbdfd5539e80a24fe))
+
+## [101.16.4](https://github.com/dhis2/capture-app/compare/v101.16.3...v101.16.4) (2024-11-20)
+
+
+### Bug Fixes
+
+* [DHIS2-18019] related stages UI tweaks ([#3872](https://github.com/dhis2/capture-app/issues/3872)) ([7ea2240](https://github.com/dhis2/capture-app/commit/7ea2240b68408a0c4e8db624093c058f2b416584))
+
+## [101.16.3](https://github.com/dhis2/capture-app/compare/v101.16.2...v101.16.3) (2024-11-20)
+
+
+### Bug Fixes
+
+* [DHIS2-18444] stabilize possible duplicate modal cypress test ([#3886](https://github.com/dhis2/capture-app/issues/3886)) ([5b5b477](https://github.com/dhis2/capture-app/commit/5b5b477ed3a26c7eb04c4966802769fe973e1631))
+
+## [101.16.2](https://github.com/dhis2/capture-app/compare/v101.16.1...v101.16.2) (2024-11-19)
+
+
+### Bug Fixes
+
+* [DHIS2-16994] Image and File DE and TEA not Displayed in Changelog ([#3837](https://github.com/dhis2/capture-app/issues/3837)) ([9327210](https://github.com/dhis2/capture-app/commit/932721045126e02379f56a85af4f6586b836b4c0))
+
+## [101.16.1](https://github.com/dhis2/capture-app/compare/v101.16.0...v101.16.1) (2024-11-17)
+
+
+### Bug Fixes
+
+* **translations:** sync translations from transifex (master) ([37c32df](https://github.com/dhis2/capture-app/commit/37c32df3e7839e30b493ff8e8185de769c3e2fd4))
+
+# [101.16.0](https://github.com/dhis2/capture-app/compare/v101.15.0...v101.16.0) (2024-11-13)
+
+
+### Features
+
+* [DHIS2-18250] Breadcrumb for event & enrollment pages ([#3849](https://github.com/dhis2/capture-app/issues/3849)) ([d65882e](https://github.com/dhis2/capture-app/commit/d65882eacb711865d9f6a860c65d56a0f4d68157))
+
+# [101.15.0](https://github.com/dhis2/capture-app/compare/v101.14.9...v101.15.0) (2024-11-12)
+
+
+### Features
+
+* [DHIS2-15187][DHIS2-15190] Working list bulk actions ([#3773](https://github.com/dhis2/capture-app/issues/3773)) ([5a12722](https://github.com/dhis2/capture-app/commit/5a127229e984b744fa3ea486d9b5a2632603bcd4))
+
+## [101.14.9](https://github.com/dhis2/capture-app/compare/v101.14.8...v101.14.9) (2024-11-10)
+
+
+### Bug Fixes
+
+* **translations:** sync translations from transifex (master) ([00403fb](https://github.com/dhis2/capture-app/commit/00403fb596e08eba233fd4103c66c66f286a0881))
+
## [101.14.8](https://github.com/dhis2/capture-app/compare/v101.14.7...v101.14.8) (2024-11-05)
diff --git a/cypress/e2e/EnrollmentAddEventPage/EnrollmentAddEventPageForm/EnrollmentAddEventPageForm.feature b/cypress/e2e/EnrollmentAddEventPage/EnrollmentAddEventPageForm/EnrollmentAddEventPageForm.feature
index 869b837615..e74ed7c4f8 100644
--- a/cypress/e2e/EnrollmentAddEventPage/EnrollmentAddEventPageForm/EnrollmentAddEventPageForm.feature
+++ b/cypress/e2e/EnrollmentAddEventPage/EnrollmentAddEventPageForm/EnrollmentAddEventPageForm.feature
@@ -50,7 +50,7 @@ Feature: User interacts with the Enrollment New Event Workspace
Scenario: User should be asked to create new event after completing a stage and choose to cancel
Given you land on the enrollment new event page by having typed #/enrollmentEventNew?enrollmentId=zRfAPUpjoG3&orgUnitId=DiszpKrYNg8&programId=M3xtLkYBlKI&stageId=CWaAcQYKVpq&teiId=S3JjTA4QMNe
- Then you see the following Enrollment: New Event
+ Then you see the new event form
And you see the widget header Foci investigation & classification
And you type 2022-01-01 in the input number 0
And you type x in the input number 20
@@ -62,7 +62,7 @@ Feature: User interacts with the Enrollment New Event Workspace
Scenario: User should be asked to create new event after completing a stage and choose to continue
Given you land on the enrollment new event page by having typed #/enrollmentEventNew?enrollmentId=zRfAPUpjoG3&orgUnitId=DiszpKrYNg8&programId=M3xtLkYBlKI&stageId=CWaAcQYKVpq&teiId=S3JjTA4QMNe
- Then you see the following Enrollment: New Event
+ Then you see the new event form
And you see the widget header Foci investigation & classification
And you type 2022-01-01 in the input number 0
And you type x in the input number 20
@@ -74,7 +74,7 @@ Feature: User interacts with the Enrollment New Event Workspace
Scenario: User is able to schedule an event with a note
Given you land on the enrollment new event page by having typed /#/enrollmentEventNew?enrollmentId=qcFFRp7DpcX&orgUnitId=DiszpKrYNg8&programId=WSGAb5XwJ3Y&stageId=edqlbukwRfQ&teiId=erqa3phUfpI
- And you see the following Enrollment: New Event
+ And you see the new event form
And you select the schedule tab
When you add a note to the event
And the events saves successfully
diff --git a/cypress/e2e/EnrollmentAddEventPage/EnrollmentAddEventPageNavigation/EnrollmentAddEventPageNavigation.feature b/cypress/e2e/EnrollmentAddEventPage/EnrollmentAddEventPageNavigation/EnrollmentAddEventPageNavigation.feature
index 5364d08ec0..de83e45d76 100644
--- a/cypress/e2e/EnrollmentAddEventPage/EnrollmentAddEventPageNavigation/EnrollmentAddEventPageNavigation.feature
+++ b/cypress/e2e/EnrollmentAddEventPage/EnrollmentAddEventPageNavigation/EnrollmentAddEventPageNavigation.feature
@@ -1,7 +1,7 @@
Feature: User interacts with Enrollment Add event page
Scenario: The user can land on the enrollment add event page.
Given you land on the enrollment add event page by having typed /#/enrollmentEventNew?programId=IpHINAT79UW&orgUnitId=DiszpKrYNg8&teiId=tIJu6iqQxNV&enrollmentId=CCBLMntFuzb&stageId=A03MvHHogjR
- Then you see the following Enrollment: New Event
+ Then you see the new event form
And you see the widget header Birth
And you see the following Report date
And you see the add event form details
diff --git a/cypress/e2e/EnrollmentAddEventPage/sharedSteps.js b/cypress/e2e/EnrollmentAddEventPage/sharedSteps.js
index 9be509e28c..354b982afa 100644
--- a/cypress/e2e/EnrollmentAddEventPage/sharedSteps.js
+++ b/cypress/e2e/EnrollmentAddEventPage/sharedSteps.js
@@ -1,4 +1,4 @@
-import { Then, defineStep as And } from '@badeball/cypress-cucumber-preprocessor';
+import { defineStep as And, Then, When } from '@badeball/cypress-cucumber-preprocessor';
Then(/^you see the following (.*)$/, (message) => {
cy.contains(message);
@@ -12,3 +12,7 @@ And(/^you see the widget header (.*)$/, (name) => {
cy.contains(name).should('exist');
});
});
+
+When('you see the new event form', () => {
+ cy.get('[data-test="new-enrollment-event-form"]').should('exist');
+});
diff --git a/cypress/e2e/EnrollmentEditEventPage/EnrollmentEditEventPageForm/EnrollmentEditEventPageForm.feature b/cypress/e2e/EnrollmentEditEventPage/EnrollmentEditEventPageForm/EnrollmentEditEventPageForm.feature
index 594b821007..31ab0f99c8 100644
--- a/cypress/e2e/EnrollmentEditEventPage/EnrollmentEditEventPageForm/EnrollmentEditEventPageForm.feature
+++ b/cypress/e2e/EnrollmentEditEventPage/EnrollmentEditEventPageForm/EnrollmentEditEventPageForm.feature
@@ -25,12 +25,12 @@ And the user see the following text: Yes
Scenario: The user can enter and exit the edit mode.
Given you land on the enrollment event page with selected Person by having typed /#/enrollmentEventEdit?orgUnitId=DiszpKrYNg8&eventId=V1CerIi3sdL
-And the user see the following text: Enrollment: View Event
+And the view enrollment event form is in view mode
And the user see the following text: Apgar Score
When the user clicks on the edit button
-Then the user see the following text: Enrollment: Edit Event
+Then the view enrollment event form is in edit mode
When the user clicks on the cancel button
-And the user see the following text: Enrollment: View Event
+And the view enrollment event form is in view mode
Scenario: The tracker program rules are triggered correctly for the Child Program.
Given you land on the enrollment event page with selected Person by having typed /#/enrollmentEventEdit?orgUnitId=DiszpKrYNg8&eventId=V1CerIi3sdL
@@ -53,58 +53,58 @@ Then the user don't see the following text: Low-dose acetylsalicylic acid given
Scenario: User can modify and save the data in the form
Given you land on the enrollment event page with selected Person by having typed /#/enrollmentEventEdit?orgUnitId=DiszpKrYNg8&eventId=V1CerIi3sdL
-Then the user see the following text: Enrollment: View Event
+Then the view enrollment event form is in view mode
And the apgar score is 11
When the user clicks on the edit button
And the user set the apgar score to 5
And the user clicks on the save button
Then you are redirected to the enrollment dashboard
And you open the Birth stage event
-Then the user see the following text: Enrollment: View Event
+Then the view enrollment event form is in view mode
And the user see the following text: 5
When the user clicks on the edit button
And the user set the apgar score to 11
And the user clicks on the save button
Then you are redirected to the enrollment dashboard
And you open the Birth stage event
-Then the user see the following text: Enrollment: View Event
+Then the view enrollment event form is in view mode
And the user see the following text: 11
Scenario: User goes directly to Edit mode for scheduled events
Given you land on the enrollment event page with selected Person by having typed /#/enrollmentEventEdit?eventId=RIrfCcEP8Uu&orgUnitId=DiszpKrYNg8
- Then the user see the following text: Enrollment: Edit Event
+ Then the view enrollment event form is in edit mode
And the user see the following text: Infant Feeding
When the user clicks on the cancel button
- Then the user see the following text: Enrollment Dashboard
+ Then the user is navigated to the enrollment dashboard
-Scenario: User can update schedule date for a scheduled event
+Scenario: User can update schedule date for a scheduled event
Given you land on the enrollment event page with selected Person by having typed /#/enrollmentEventEdit?eventId=RIrfCcEP8Uu&orgUnitId=DiszpKrYNg8
- Then the user see the following text: Enrollment: Edit Event
+ Then the view enrollment event form is in edit mode
And the user see the following text: Infant Feeding
When the user clicks switch tab to Schedule
And the user selects another schedule date
And the user clicks on the schedule button on widget-enrollment-event
- Then the user see the following text: Enrollment Dashboard
+ Then the user is navigated to the enrollment dashboard
Scenario: User can update schedule date if Hide due date is enabled
Given you land on the enrollment event page with selected Focus area by having typed /#/enrollmentEventNew?enrollmentId=V8uPJuhvlL7&orgUnitId=DiszpKrYNg8&programId=M3xtLkYBlKI&stageId=uvMKOn1oWvd&tab=SCHEDULE&teiId=dNpxRu1mWG5
- Then the user see the following text: Enrollment: New Event
+ Then the add event form is displayed
And the user see the following text: Foci response
And the user see the schedule date and info box
And the user clicks on the schedule button on add-event-enrollment-page-content
- Then the user see the following text: Enrollment Dashboard
+ Then the user is navigated to the enrollment dashboard
Scenario: User can see disabled scheduled date for active event
Given you land on the enrollment event page with selected Person by having typed /#/enrollmentEventEdit?eventId=FV4JCI73wO2&orgUnitId=DiszpKrYNg8
- Then the user see the following text: Enrollment: View Event
+ Then the view enrollment event form is in view mode
When the user clicks on the edit button
- Then the user see the following text: Enrollment: Edit Event
+ Then the view enrollment event form is in edit mode
Then the user see the schedule date field with tooltip: Scheduled date cannot be changed for Active events
-
+
@user:trackerAutoTestRestricted
Scenario: The user cannot enter edit mode for completed events
Given you land on the enrollment event page with selected Person by having typed /#/enrollmentEventEdit?eventId=nUVwTLuQ6FT&orgUnitId=DiszpKrYNg8
- And the user see the following text: Enrollment: View Event
+ And the view enrollment event form is in view mode
Then the edit button should be disabled
Scenario: User can edit the event and complete the enrollment
@@ -113,4 +113,4 @@ Scenario: User can edit the event and complete the enrollment
And the user clicks on the edit button
And the user completes the event
And the user completes the enrollment
- Then the user sees the enrollment status and recently edited event in Case outcome event status is completed
\ No newline at end of file
+ Then the user sees the enrollment status and recently edited event in Case outcome event status is completed
diff --git a/cypress/e2e/EnrollmentEditEventPage/EnrollmentEditEventPageForm/EnrollmentEditEventPageForm.js b/cypress/e2e/EnrollmentEditEventPage/EnrollmentEditEventPageForm/EnrollmentEditEventPageForm.js
index 70468a3a93..93ab1beea1 100644
--- a/cypress/e2e/EnrollmentEditEventPage/EnrollmentEditEventPageForm/EnrollmentEditEventPageForm.js
+++ b/cypress/e2e/EnrollmentEditEventPage/EnrollmentEditEventPageForm/EnrollmentEditEventPageForm.js
@@ -1,4 +1,4 @@
-import { Given, When, Then, defineStep as And } from '@badeball/cypress-cucumber-preprocessor';
+import { defineStep as And, Given, Then, When } from '@badeball/cypress-cucumber-preprocessor';
import { getCurrentYear } from '../../../support/date';
const changeEnrollmentAndEventsStatus = () => (
@@ -61,8 +61,7 @@ Given(/^you land on the enrollment event page with selected (.*) by having typed
When(/^the user clicks on the edit button/, () =>
cy
.get('[data-test="widget-enrollment-event"]')
- .find('[data-test="dhis2-uicore-button"]')
- .eq(1)
+ .find('[data-test="widget-enrollment-event-edit-button"]')
.click(),
);
@@ -208,7 +207,18 @@ And('you open the Birth stage event', () => {
Then('the edit button should be disabled', () => {
cy.get('[data-test="widget-enrollment-event"]')
- .find('[data-test="dhis2-uicore-button"]')
- .eq(1)
+ .find('[data-test="widget-enrollment-event-edit-button"]')
.should('be.disabled');
});
+
+And('the add event form is displayed', () => {
+ cy.get('[data-test="add-event-enrollment-page-content"]').should('exist');
+});
+
+And('the user is navigated to the enrollment dashboard', () => {
+ cy.get('[data-test="enrollment-overview-page"]').should('exist');
+});
+
+And(/^the view enrollment event form is in (.*) mode$/, (mode) => {
+ cy.get(`[data-test="widget-enrollment-event-${mode}"]`).should('exist');
+});
diff --git a/cypress/e2e/EnrollmentEditEventPage/EnrollmentEditEventPageNavigation/EnrollmentEditEventPageNavigation.feature b/cypress/e2e/EnrollmentEditEventPage/EnrollmentEditEventPageNavigation/EnrollmentEditEventPageNavigation.feature
index 118c8e4c14..663543760e 100644
--- a/cypress/e2e/EnrollmentEditEventPage/EnrollmentEditEventPageNavigation/EnrollmentEditEventPageNavigation.feature
+++ b/cypress/e2e/EnrollmentEditEventPage/EnrollmentEditEventPageNavigation/EnrollmentEditEventPageNavigation.feature
@@ -2,18 +2,18 @@ Feature: User interacts with Enrollment event page
Scenario: The user can land on the enrollment event page.
Given you land on the enrollment event page by having typed #/enrollmentEventEdit?orgUnitId=DiszpKrYNg8&eventId=O7IACPx40nQ
- Then you see the following Enrollment: View Event
+ Then the view enrollment event form is in view mode
And you see the following Baby Postnatal
- Scenario: User can navigate back and forward between the enrollment event edit page and the enrollment page
+ Scenario: User can navigate back and forward between the enrollment event edit page and the enrollment page
Given you open the enrollment page which has multiple events and stages
- Then you see the following Enrollment Dashboard
+ Then the user is navigated to the enrollment dashboard
And the widgets are done rendering
And the program stages should be displayed
When the user clicks the first second antenatal care visit event
- Then you see the following Enrollment: View Event
+ Then the view enrollment event form is in view mode
And you see the following antenatal care visit
And you see the following No ARV medication plan
When the user clicks the "Back to all stages and events" button
- Then you see the following Enrollment Dashboard
- And the program stages should be displayed
\ No newline at end of file
+ Then the user is navigated to the enrollment dashboard
+ And the program stages should be displayed
diff --git a/cypress/e2e/EnrollmentEditEventPage/EnrollmentEditEventPageNavigation/EnrollmentEditEventPageNavigation.js b/cypress/e2e/EnrollmentEditEventPage/EnrollmentEditEventPageNavigation/EnrollmentEditEventPageNavigation.js
index 76fbb1915c..d97e01569c 100644
--- a/cypress/e2e/EnrollmentEditEventPage/EnrollmentEditEventPageNavigation/EnrollmentEditEventPageNavigation.js
+++ b/cypress/e2e/EnrollmentEditEventPage/EnrollmentEditEventPageNavigation/EnrollmentEditEventPageNavigation.js
@@ -1,4 +1,4 @@
-import { Given, When, Then, defineStep as And } from '@badeball/cypress-cucumber-preprocessor';
+import { defineStep as And, Given, Then, When } from '@badeball/cypress-cucumber-preprocessor';
Given(/^you land on the enrollment event page by having typed (.*)$/, (url) => {
cy.visit(url);
@@ -21,10 +21,7 @@ When('the user clicks the first second antenatal care visit event', () => {
});
When(/^the user clicks the "Back to all stages and events" button/, () =>
- cy
- .get('[data-test="widget-enrollment-event"]')
- .find('[data-test="dhis2-uicore-button"]')
- .eq(0)
+ cy.get('[data-test="enrollment-edit-event-back-button"]')
.click(),
);
@@ -39,3 +36,11 @@ Then('the program stages should be displayed', () => {
cy.contains('Care at birth').should('exist');
});
});
+
+And('the user is navigated to the enrollment dashboard', () => {
+ cy.get('[data-test="enrollment-overview-page"]').should('exist');
+});
+
+And(/^the view enrollment event form is in (.*) mode$/, (mode) => {
+ cy.get(`[data-test="widget-enrollment-event-${mode}"]`).should('exist');
+});
diff --git a/cypress/e2e/EnrollmentPage/EnrollmentPageNavigation/EnrollmentPageNavigation.js b/cypress/e2e/EnrollmentPage/EnrollmentPageNavigation/EnrollmentPageNavigation.js
index 4d331734b9..46fe376da8 100644
--- a/cypress/e2e/EnrollmentPage/EnrollmentPageNavigation/EnrollmentPageNavigation.js
+++ b/cypress/e2e/EnrollmentPage/EnrollmentPageNavigation/EnrollmentPageNavigation.js
@@ -1,9 +1,8 @@
-import { Given, When, Then, defineStep as And } from '@badeball/cypress-cucumber-preprocessor';
+import { defineStep as And, Given, Then, When } from '@badeball/cypress-cucumber-preprocessor';
Given('you are on an enrollment page', () => {
cy.visit('/#/enrollment?programId=IpHINAT79UW&orgUnitId=UgYg0YW7ZIh&teiId=fhFQhO0xILJ&enrollmentId=gPDueU02tn8');
- cy.get('[data-test="enrollment-page-content"]')
- .contains('Enrollment Dashboard');
+ cy.get('[data-test="enrollment-overview-page"]');
});
And('you select the Inpatient morbidity program', () => {
@@ -90,8 +89,7 @@ Then(/^you should be redirect to (.*)$/, (expectedUrl) => {
Given('you land on the enrollment page by having typed only the enrollmentId in the url', () => {
cy.visit('/#/enrollment?enrollmentId=gPDueU02tn8');
- cy.get('[data-test="enrollment-page-content"]')
- .contains('Enrollment Dashboard');
+ cy.get('[data-test="enrollment-overview-page"]');
cy.contains('[data-test="scope-selector"]', 'Carlos Cruz');
cy.contains('[data-test="scope-selector"]', 'Taninahun (Malen) CHP');
cy.contains('1 event');
@@ -119,8 +117,7 @@ When('you reset the org unit selection', () => {
Then('you see the enrollment page but there is no org unit id in the url', () => {
cy.url().should('not.include', 'orgUnitId');
- cy.get('[data-test="enrollment-page-content"]')
- .contains('Enrollment Dashboard');
+ cy.get('[data-test="enrollment-overview-page"]');
});
When('you reset the enrollment selection', () => {
diff --git a/cypress/e2e/EnrollmentPage/EnrollmentQuickActions/EnrollmentQuickActions.js b/cypress/e2e/EnrollmentPage/EnrollmentQuickActions/EnrollmentQuickActions.js
index a450af97d9..ff2c76281f 100644
--- a/cypress/e2e/EnrollmentPage/EnrollmentQuickActions/EnrollmentQuickActions.js
+++ b/cypress/e2e/EnrollmentPage/EnrollmentQuickActions/EnrollmentQuickActions.js
@@ -1,4 +1,4 @@
-import { Given, When, Then } from '@badeball/cypress-cucumber-preprocessor';
+import { Given, Then, When } from '@badeball/cypress-cucumber-preprocessor';
Given(/^you open the enrollment page by typing (.*)$/, url =>
cy.visit(url),
@@ -6,14 +6,12 @@ Given(/^you open the enrollment page by typing (.*)$/, url =>
Given('you are on an enrollment page with stage available', () => {
cy.visit('/#/enrollment?programId=ur1Edk5Oe2n&orgUnitId=UgYg0YW7ZIh&teiId=zmgVvEZ91Kg&enrollmentId=xRnBV5aJDeF');
- cy.get('[data-test="enrollment-page-content"]')
- .contains('Enrollment Dashboard');
+ cy.get('[data-test="enrollment-overview-page"]');
});
Given('you are on an enrollment page with no stage available', () => {
cy.visit('/#/enrollment?programId=IpHINAT79UW&orgUnitId=UgYg0YW7ZIh&teiId=fhFQhO0xILJ&enrollmentId=gPDueU02tn8');
- cy.get('[data-test="enrollment-page-content"]')
- .contains('Enrollment Dashboard');
+ cy.get('[data-test="enrollment-overview-page"]');
});
When(/^you click the (.*) event-button/, (mode) => {
diff --git a/cypress/e2e/EnrollmentPage/sharedSteps.js b/cypress/e2e/EnrollmentPage/sharedSteps.js
index 8f8e2992ca..b21b9ec516 100644
--- a/cypress/e2e/EnrollmentPage/sharedSteps.js
+++ b/cypress/e2e/EnrollmentPage/sharedSteps.js
@@ -7,3 +7,7 @@ Given('you open the enrollment page', () => {
Given('you open the enrollment page which has multiples events and stages', () => {
cy.visit('#/enrollment?enrollmentId=ek4WWAgXX5i');
});
+
+Given('you are on the enrollment dashboard', () => {
+ cy.url().should('include', '/#/enrollment?');
+});
diff --git a/cypress/e2e/NewPage/NewPage.js b/cypress/e2e/NewPage/NewPage.js
index 9ef6ab3440..a4a7579656 100644
--- a/cypress/e2e/NewPage/NewPage.js
+++ b/cypress/e2e/NewPage/NewPage.js
@@ -469,14 +469,14 @@ And('you fill the WHO RMNCH program registration form with its required unique v
});
And('you fill the WHO RMNCH program registration form with its required values', () => {
- cy.get('[data-test="capture-ui-input"]')
- .eq(2)
- .type('Ava');
-
cy.get('[data-test="capture-ui-input"]')
.eq(3)
.type('Didriksson');
+ cy.get('[data-test="capture-ui-input"]')
+ .eq(2)
+ .type('Ava');
+
cy.get('[data-test="capture-ui-input"]')
.eq(9)
.type('1985-10-01')
diff --git a/cypress/e2e/ScopeSelector/ScopeSelector.feature b/cypress/e2e/ScopeSelector/ScopeSelector.feature
index ec80a5d2b0..5a06714422 100644
--- a/cypress/e2e/ScopeSelector/ScopeSelector.feature
+++ b/cypress/e2e/ScopeSelector/ScopeSelector.feature
@@ -144,14 +144,14 @@ Feature: User uses the ScopeSelector to navigate
Examples:
| url | state | message |
- | /#/enrollment?enrollmentId=gPDueU02tn8 | all | Enrollment Dashboard |
- | /#/enrollment?teiId=fhFQhO0xILJ&enrollmentId=gPDueU02tn8 | all | Enrollment Dashboard |
- | /#/enrollment?orgUnitId=UgYg0YW7ZIh&enrollmentId=gPDueU02tn8 | all | Enrollment Dashboard |
- | /#/enrollment?orgUnitId=UgYg0YW7ZIh&teiId=fhFQhO0xILJ&enrollmentId=gPDueU02tn8 | all | Enrollment Dashboard |
- | /#/enrollment?programId=IpHINAT79UW&enrollmentId=gPDueU02tn8 | all | Enrollment Dashboard |
- | /#/enrollment?programId=IpHINAT79UW&teiId=fhFQhO0xILJ&enrollmentId=gPDueU02tn8 | all | Enrollment Dashboard |
- | /#/enrollment?programId=IpHINAT79UW&orgUnitId=UgYg0YW7ZIh&enrollmentId=gPDueU02tn8 | all | Enrollment Dashboard |
- | /#/enrollment?programId=IpHINAT79UW&orgUnitId=UgYg0YW7ZIh&teiId=fhFQhO0xILJ&enrollmentId=gPDueU02tn8 | all | Enrollment Dashboard |
+ | /#/enrollment?enrollmentId=gPDueU02tn8 | all | Enrollment dashboard |
+ | /#/enrollment?teiId=fhFQhO0xILJ&enrollmentId=gPDueU02tn8 | all | Enrollment dashboard |
+ | /#/enrollment?orgUnitId=UgYg0YW7ZIh&enrollmentId=gPDueU02tn8 | all | Enrollment dashboard |
+ | /#/enrollment?orgUnitId=UgYg0YW7ZIh&teiId=fhFQhO0xILJ&enrollmentId=gPDueU02tn8 | all | Enrollment dashboard |
+ | /#/enrollment?programId=IpHINAT79UW&enrollmentId=gPDueU02tn8 | all | Enrollment dashboard |
+ | /#/enrollment?programId=IpHINAT79UW&teiId=fhFQhO0xILJ&enrollmentId=gPDueU02tn8 | all | Enrollment dashboard |
+ | /#/enrollment?programId=IpHINAT79UW&orgUnitId=UgYg0YW7ZIh&enrollmentId=gPDueU02tn8 | all | Enrollment dashboard |
+ | /#/enrollment?programId=IpHINAT79UW&orgUnitId=UgYg0YW7ZIh&teiId=fhFQhO0xILJ&enrollmentId=gPDueU02tn8 | all | Enrollment dashboard |
| /#/enrollment?orgUnitId=UgYg0YW7ZIh&teiId=fhFQhO0xILJ | teiAndOrgUnit | Choose a program to add new or see existing enrollments for Carlos Cruz |
| /#/enrollment?programId=IpHINAT79UW&teiId=fhFQhO0xILJ | teiAndChildProgram | Choose an enrollment to view the dashboard. |
| /#/enrollment?programId=qDkgAbB5Jlk&teiId=fhFQhO0xILJ | teiAndMalariaProgram | Carlos Cruz is a person and cannot be enrolled in the Malaria case diagnosis, treatment and investigation. Choose another program that allows person enrollment. Enroll a new malaria entity in this program.|
@@ -184,6 +184,8 @@ Feature: User uses the ScopeSelector to navigate
When you reset the program selection
Then you see message explaining you need to select a program
+ # DHIS2-18326
+ @skip
Scenario: Enrollment event edit page > resetting the org unit
Given you land on a enrollment page domain by having typed /#/enrollmentEventEdit?orgUnitId=UgYg0YW7ZIh&eventId=lQQyjR73hHk
When you reset the org unit selection
diff --git a/cypress/e2e/ScopeSelector/ScopeSelector.js b/cypress/e2e/ScopeSelector/ScopeSelector.js
index 00b119713f..c870644462 100644
--- a/cypress/e2e/ScopeSelector/ScopeSelector.js
+++ b/cypress/e2e/ScopeSelector/ScopeSelector.js
@@ -1,4 +1,4 @@
-import { Given, When, Then, defineStep as And } from '@badeball/cypress-cucumber-preprocessor';
+import { defineStep as And, Given, Then, When } from '@badeball/cypress-cucumber-preprocessor';
import { getCurrentYear } from '../../support/date';
Given(/^you land on a enrollment page domain by having typed (.*)$/, (url) => {
@@ -263,8 +263,7 @@ Then(/^you see the following (.*)$/, (message) => {
And('you land on the enrollment page by having typed only the enrollmentId on the url', () => {
cy.visit('/#/enrollment?enrollmentId=gPDueU02tn8');
- cy.get('[data-test="enrollment-page-content"]')
- .contains('Enrollment Dashboard');
+ cy.get('[data-test="enrollment-overview-page"]');
});
And('you reset the tei selection', () => {
@@ -288,7 +287,7 @@ And('you reset the org unit selection', () => {
And('you see the enrollment event Edit page but there is no org unit id in the url', () => {
cy.url().should('eq', `${Cypress.config().baseUrl}/#/enrollmentEventEdit?eventId=lQQyjR73hHk`);
- cy.contains('Enrollment: View Event');
+ cy.get('[data-test="widget-enrollment-event-view"]').should('exist');
});
And('you see the enrollment event New page but there is no org unit id in the url', () => {
@@ -298,13 +297,12 @@ And('you see the enrollment event New page but there is no org unit id in the ur
And('you see the enrollment event New page but there is no stage id in the url', () => {
cy.url().should('eq', `${Cypress.config().baseUrl}/#/enrollmentEventNew?enrollmentId=gPDueU02tn8&orgUnitId=UgYg0YW7ZIh&programId=IpHINAT79UW&teiId=fhFQhO0xILJ`);
- cy.contains('Enrollment: New Event');
+ cy.contains('Choose a stage for a new event');
});
And('you see the enrollment page', () => {
cy.url().should('eq', `${Cypress.config().baseUrl}/#/enrollment?enrollmentId=gPDueU02tn8&orgUnitId=UgYg0YW7ZIh&programId=IpHINAT79UW&teiId=fhFQhO0xILJ`);
- cy.get('[data-test="enrollment-page-content"]')
- .contains('Enrollment Dashboard');
+ cy.get('[data-test="enrollment-overview-page"]');
});
And('you reset the enrollment selection', () => {
diff --git a/cypress/e2e/TopBarActions/TopBarActions.feature b/cypress/e2e/TopBarActions/TopBarActions.feature
index 20f8aa8f74..25135c9328 100644
--- a/cypress/e2e/TopBarActions/TopBarActions.feature
+++ b/cypress/e2e/TopBarActions/TopBarActions.feature
@@ -61,7 +61,7 @@ Feature: User uses the TopBarActions to navigate
Given you land on a enrollment page domain by having typed /#/enrollmentEventEdit?orgUnitId=DwpbWkiqjMy&eventId=KNbStF7YTon
And the user see the following text: Gestational age at visit
When the user clicks on the edit button
- And the user see the following text: Enrollment: Edit Event
+ And the view enrollment event form is in edit mode
When the user clicks the element containing the text: Clear selections
Then the user sees the warning popup
@@ -88,7 +88,7 @@ Feature: User uses the TopBarActions to navigate
Given you land on a enrollment page domain by having typed #/enrollmentEventNew?programId=WSGAb5XwJ3Y&orgUnitId=DwpbWkiqjMy&teiId=yFcOhsM1Yoa&enrollmentId=ek4WWAgXX5i&stageId=edqlbukwRfQ
And the user see the following text: Clear selections
When the user clicks the arrow button to see the dropdown
- And the user clicks the element containing the text: Create new in another program...
+ And the user clicks the element containing the text: Create new in another program...
Then the current url is /#/new?orgUnitId=DwpbWkiqjMy
Scenario: Enrollment Event New page > You go to the new page inside the same program
diff --git a/cypress/e2e/TopBarActions/TopBarActions.js b/cypress/e2e/TopBarActions/TopBarActions.js
index 81d8cc996e..624ca1ac82 100644
--- a/cypress/e2e/TopBarActions/TopBarActions.js
+++ b/cypress/e2e/TopBarActions/TopBarActions.js
@@ -1,4 +1,4 @@
-import { Given, When, Then } from '@badeball/cypress-cucumber-preprocessor';
+import { defineStep as And, Given, Then, When } from '@badeball/cypress-cucumber-preprocessor';
Given(/^you land on a enrollment page domain by having typed (.*)$/, (url) => {
cy.visit(url);
@@ -6,7 +6,7 @@ Given(/^you land on a enrollment page domain by having typed (.*)$/, (url) => {
});
When(/^the user clicks on the edit button/, () =>
- cy.get('[data-test="widget-enrollment-event"]').find('[data-test="dhis2-uicore-button"]').eq(1).click(),
+ cy.get('[data-test="widget-enrollment-event"]').find('[data-test="widget-enrollment-event-edit-button"]').click(),
);
When('the user clicks the arrow button to see the dropdown', () => {
@@ -24,3 +24,7 @@ When(/^the user set the WHOMCH Diastolic blood pressure to (.*)/, score =>
.type(score)
.blur(),
);
+
+And(/^the view enrollment event form is in (.*) mode$/, (mode) => {
+ cy.get(`[data-test="widget-enrollment-event-${mode}"]`).should('exist');
+});
diff --git a/cypress/e2e/WidgetsForEnrollmentPages/WidgetAssignee/index.js b/cypress/e2e/WidgetsForEnrollmentPages/WidgetAssignee/index.js
index 9f45c31e2f..07403f7566 100644
--- a/cypress/e2e/WidgetsForEnrollmentPages/WidgetAssignee/index.js
+++ b/cypress/e2e/WidgetsForEnrollmentPages/WidgetAssignee/index.js
@@ -12,8 +12,7 @@ When('you assign the user Geetha in the view mode', () => {
When('you assign the user Tracker demo User in the edit mode', () => {
cy
.get('[data-test="widget-enrollment-event"]')
- .find('[data-test="dhis2-uicore-button"]')
- .eq(1)
+ .find('[data-test="widget-enrollment-event-edit-button"]')
.click();
cy.get('[data-test="widget-assignee"]').within(() => {
diff --git a/cypress/e2e/WidgetsForEnrollmentPages/WidgetEventNote/index.js b/cypress/e2e/WidgetsForEnrollmentPages/WidgetEventNote/index.js
index 2a1c5200ef..aec7cc2c19 100644
--- a/cypress/e2e/WidgetsForEnrollmentPages/WidgetEventNote/index.js
+++ b/cypress/e2e/WidgetsForEnrollmentPages/WidgetEventNote/index.js
@@ -1,13 +1,15 @@
-import { When, Then } from '@badeball/cypress-cucumber-preprocessor';
+import { Then, When } from '@badeball/cypress-cucumber-preprocessor';
Then('the enrollment widget should be loaded', () => {
cy.contains('The enrollment event data could not be found').should('not.exist');
});
When('you click edit mode', () => {
- cy.contains('[data-test="dhis2-uicore-button"]', 'Edit event')
+ cy
+ .get('[data-test="widget-enrollment-event"]')
+ .find('[data-test="widget-enrollment-event-edit-button"]')
.click();
- cy.contains('Enrollment: Edit Event').should('exist');
+ cy.get('[data-test="widget-enrollment-event-edit"]').should('exist');
});
When(/^you fill in the note: (.*)$/, (note) => {
diff --git a/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentAddEventPage/WidgetsForEnrollmentAddEventPage.js b/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentAddEventPage/WidgetsForEnrollmentAddEventPage.js
index ebb0f1293a..24f07d7593 100644
--- a/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentAddEventPage/WidgetsForEnrollmentAddEventPage.js
+++ b/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentAddEventPage/WidgetsForEnrollmentAddEventPage.js
@@ -16,9 +16,7 @@ Then('you can assign a user when scheduling the event', () => {
});
When(/^the user clicks the "Back to all stages and events" button/, () =>
- cy
- .get('[data-test="widget-enrollment-event"]')
- .contains('Back to all stages and events')
+ cy.get('[data-test="enrollment-edit-event-back-button"]')
.click(),
);
diff --git a/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentDashboard/WidgetsForEnrollmentDashboard.js b/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentDashboard/WidgetsForEnrollmentDashboard.js
index 81034e2507..96a583bd38 100644
--- a/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentDashboard/WidgetsForEnrollmentDashboard.js
+++ b/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentDashboard/WidgetsForEnrollmentDashboard.js
@@ -51,9 +51,7 @@ Then(/^the scope selector list contains the text (.*)$/, (name) => {
});
When(/^the user clicks the "Back to all stages and events" button/, () =>
- cy
- .get('[data-test="widget-enrollment-event"]')
- .contains('Back to all stages and events')
+ cy.get('[data-test="enrollment-edit-event-back-button"]')
.click(),
);
diff --git a/cypress/e2e/WorkingLists/EventWorkingLists/EventBulkActions/EventBulkAction.feature b/cypress/e2e/WorkingLists/EventWorkingLists/EventBulkActions/EventBulkAction.feature
new file mode 100644
index 0000000000..f44f8caa8d
--- /dev/null
+++ b/cypress/e2e/WorkingLists/EventWorkingLists/EventBulkActions/EventBulkAction.feature
@@ -0,0 +1,49 @@
+Feature: User facing tests for bulk actions on event working lists
+
+ Scenario: the user should be able to select rows
+ Given you open the main page with Ngelehun and malaria case context
+ When you select the first 5 rows
+ Then the bulk action bar should say 5 selected
+ And the first 5 rows should be selected
+
+ Scenario: the user should be able to deselect rows
+ Given you open the main page with Ngelehun and malaria case context
+ When you select the first 5 rows
+ And you deselect the first 3 rows
+ Then the bulk action bar should say 2 selected
+
+ Scenario: the user should be able to select all rows
+ Given you open the main page with Ngelehun and malaria case context
+ When you select all rows
+ Then the bulk action bar should say 15 selected
+ And all rows should be selected
+
+ Scenario: the user should be able to deselect all rows
+ Given you open the main page with Ngelehun and malaria case context
+ When you select all rows
+ And all rows should be selected
+ And you select all rows
+ Then the bulk action bar should not be present
+ And no rows should be selected
+
+ Scenario: the filters should be disabled when rows are selected
+ Given you open the main page with Ngelehun and malaria case context
+ When you select the first 5 rows
+ Then the filters should be disabled
+
+ @v<42
+ Scenario: the user should be able to bulk complete events
+ Given you open the main page with Ngelehun and malaria case context
+ And you select the first 3 rows
+ And you click the bulk complete button
+ And the bulk complete modal should open
+ When you click the confirm complete events button
+ Then the bulk complete modal should close
+
+ Scenario: the user should be able to bulk delete events
+ Given you open the main page with Ngelehun and malaria case context
+ And you select the first 3 rows
+ And you click the bulk Delete button
+ And the bulk delete modal should open
+ When you click the confirm delete events button
+ Then the bulk delete modal should close
diff --git a/cypress/e2e/WorkingLists/EventWorkingLists/EventBulkActions/EventBulkAction.js b/cypress/e2e/WorkingLists/EventWorkingLists/EventBulkActions/EventBulkAction.js
new file mode 100644
index 0000000000..3a89c95cba
--- /dev/null
+++ b/cypress/e2e/WorkingLists/EventWorkingLists/EventBulkActions/EventBulkAction.js
@@ -0,0 +1,73 @@
+import { Given, Then, When } from '@badeball/cypress-cucumber-preprocessor';
+import '../../sharedSteps';
+
+Given('you open the main page with Ngelehun and malaria case context', () => {
+ cy.visit('#/?programId=VBqh0ynB2wv&orgUnitId=DiszpKrYNg8');
+});
+
+Then('the bulk complete modal should open', () => {
+ cy.get('[data-test="bulk-complete-events-dialog"]')
+ .should('exist');
+});
+
+When('you click the confirm complete events button', () => {
+ cy.intercept({
+ method: 'POST',
+ url: '**/tracker?async=false**',
+ }, {
+ statusCode: 200,
+ body: {},
+ }).as('completeEvents');
+
+ cy.get('[data-test="bulk-complete-events-dialog"]')
+ .find('[data-test="dhis2-uicore-button"]')
+ .contains('Complete').click();
+
+ cy.wait('@completeEvents')
+ .its('request.body')
+ .should(({ events }) => {
+ expect(events).to.have.length(3);
+ expect(events[0]).to.include({ status: 'COMPLETED' });
+ expect(events[1]).to.include({ status: 'COMPLETED' });
+ expect(events[2]).to.include({ status: 'COMPLETED' });
+ });
+});
+
+Then('the bulk complete modal should close', () => {
+ cy.get('[data-test="bulk-complete-events-dialog"]')
+ .should('not.exist');
+});
+
+Then('the bulk delete modal should open', () => {
+ cy.get('[data-test="bulk-delete-events-dialog"]')
+ .should('exist');
+});
+
+// you click the confirm delete events button
+When('you click the confirm delete events button', () => {
+ cy.intercept({
+ method: 'POST',
+ url: '**/tracker?async=false**',
+ }, {
+ statusCode: 200,
+ body: {},
+ }).as('deleteEvents');
+
+ cy.get('[data-test="bulk-delete-events-dialog"]')
+ .find('[data-test="dhis2-uicore-button"]')
+ .contains('Delete').click();
+
+ cy.wait('@deleteEvents')
+ .its('request.body')
+ .should(({ events }) => {
+ expect(events).to.have.length(3);
+ expect(events).to.deep.include({ event: 'a969f7a3bf1' });
+ expect(events).to.deep.include({ event: 'a6f092d0d44' });
+ expect(events).to.deep.include({ event: 'a5e67163090' });
+ });
+});
+
+Then('the bulk delete modal should close', () => {
+ cy.get('[data-test="bulk-delete-events-dialog"]')
+ .should('not.exist');
+});
diff --git a/cypress/e2e/WorkingLists/EventWorkingLists/EventWorkingListsUser/EventWorkingListsUser.js b/cypress/e2e/WorkingLists/EventWorkingLists/EventWorkingListsUser/EventWorkingListsUser.js
index b1a6158642..019ba97970 100644
--- a/cypress/e2e/WorkingLists/EventWorkingLists/EventWorkingListsUser/EventWorkingListsUser.js
+++ b/cypress/e2e/WorkingLists/EventWorkingLists/EventWorkingListsUser/EventWorkingListsUser.js
@@ -1,7 +1,7 @@
-import { Given, When, Then } from '@badeball/cypress-cucumber-preprocessor';
+import { Given, Then, When } from '@badeball/cypress-cucumber-preprocessor';
import { v4 as uuid } from 'uuid';
import '../sharedSteps';
-import { getCurrentYear, combineDataAndYear } from '../../../../support/date';
+import { combineDataAndYear, getCurrentYear } from '../../../../support/date';
Given('you open the main page with Ngelehun and malaria case context', () => {
cy.visit('#/?programId=VBqh0ynB2wv&orgUnitId=DiszpKrYNg8');
@@ -29,7 +29,7 @@ Then('the default working list should be displayed', () => {
.should('have.length', 16)
.each(($row, index) => {
if (index) {
- cy.wrap($row).find('td').first().invoke('text')
+ cy.wrap($row).find('td').eq(1).invoke('text')
.then((date) => {
const firstArgs = rows[date].length > 1 ?
new RegExp(`${rows[date].map(item => item.split(' ')[0]).join('|')}`, 'g')
@@ -185,7 +185,7 @@ Then('the list should display data for the second page', () => {
.should('have.length', 16)
.each(($row, index) => {
if (index) {
- cy.wrap($row).find('td').first().invoke('text')
+ cy.wrap($row).find('td').eq(1).invoke('text')
.then((date) => {
const firstArgs = rows[date].length > 1 ?
new RegExp(`${rows[date].map(item => item.split(' ')[0]).join('|')}`, 'g')
@@ -219,7 +219,7 @@ Then('the list should display 10 rows of data', () => {
.should('have.length', 11)
.each(($row, index) => {
if (index) {
- cy.wrap($row).find('td').first().invoke('text')
+ cy.wrap($row).find('td').eq(1).invoke('text')
.then((date) => {
const firstArgs = rows[date].length > 1 ?
new RegExp(`${rows[date].map(item => item.split(' ')[0]).join('|')}`, 'g')
@@ -250,9 +250,9 @@ Then('the list should display data ordered descendingly by report date', () => {
.click();
cy.get('input[placeholder="From"]')
- .type(`${lastYear}-01-01`);
+ .type(`${lastYear}-01-01`).blur();
- cy.get('input[placeholder="To"]').click();
+ cy.get('input[placeholder="To"]').click().blur();
cy.contains('Update')
.click({ force: true });
@@ -278,7 +278,7 @@ Then('the list should display data ordered descendingly by report date', () => {
.should('have.length', 16)
.each(($row, index) => {
if (index) {
- cy.wrap($row).find('td').first().invoke('text')
+ cy.wrap($row).find('td').eq(1).invoke('text')
.then((date) => {
const firstArgs = rows[date].length > 1 ?
new RegExp(`${rows[date].map(item => item.split(' ')[0]).join('|')}`, 'g')
@@ -399,10 +399,10 @@ When('you set the date of admission filter', () => {
cy.get('input[type="text"]')
.then(($elements) => {
cy.wrap($elements[0])
- .type('2018-01-01');
+ .type('2018-01-01').blur();
cy.wrap($elements[1])
- .type('2018-12-31');
+ .type('2018-12-31').blur();
});
cy.contains('Update')
diff --git a/cypress/e2e/WorkingLists/TeiWorkingLists/TeiBulkActions/TeiBulkActions.feature b/cypress/e2e/WorkingLists/TeiWorkingLists/TeiBulkActions/TeiBulkActions.feature
new file mode 100644
index 0000000000..f79f4faf87
--- /dev/null
+++ b/cypress/e2e/WorkingLists/TeiWorkingLists/TeiBulkActions/TeiBulkActions.feature
@@ -0,0 +1,92 @@
+Feature: User facing tests for bulk actions on Tracked Entity working lists
+
+ Scenario: the user should be able to select rows
+ Given you open the main page with Ngelehun and child programe context
+ When you select the first 5 rows
+ Then the bulk action bar should say 5 selected
+ And the first 5 rows should be selected
+
+ Scenario: the user should be able to deselect rows
+ Given you open the main page with Ngelehun and child programe context
+ When you select the first 5 rows
+ And you deselect the first 3 rows
+ Then the bulk action bar should say 2 selected
+
+ Scenario: the user should be able to select all rows
+ Given you open the main page with Ngelehun and child programe context
+ When you select all rows
+ Then the bulk action bar should say 15 selected
+ And all rows should be selected
+
+ Scenario: the user should be able to deselect all rows
+ Given you open the main page with Ngelehun and child programe context
+ When you select all rows
+ And all rows should be selected
+ And you select all rows
+ Then the bulk action bar should not be present
+ And no rows should be selected
+
+ Scenario: the filters should be disabled when rows are selected
+ Given you open the main page with Ngelehun and child programe context
+ When you select the first 5 rows
+ Then the filters should be disabled
+
+ Scenario: The user should see an error message when trying to bulk complete enrollments with errors
+ Given you open the main page with Ngelehun and Malaria focus investigation context
+ And you select the first 3 rows
+ And you click the bulk complete enrollments button
+ And the bulk complete enrollments modal should open
+ And the modal content should say: This action will complete 2 active enrollments in your selection. 1 enrollment already marked as completed will not be changed.
+ When you confirm 2 active enrollments with errors
+ Then an error dialog will be displayed to the user
+ And you close the error dialog
+ And the unsuccessful enrollments should still be selected
+
+ Scenario: the user should be able to bulk complete enrollments and events
+ Given you open the main page with Ngelehun and Malaria focus investigation context
+ And you select the first 4 rows
+ And you click the bulk complete enrollments button
+ And the bulk complete enrollments modal should open
+ And the modal content should say: This action will complete 3 active enrollments in your selection. 1 enrollment already marked as completed will not be changed.
+ When you confirm 3 active enrollments successfully
+ Then the bulk complete enrollments modal should close
+
+#DHIS2-18447
+@skip
+ Scenario: the user should be able to bulk complete enrollments without completing events
+ Given you open the main page with Ngelehun and Malaria Case diagnosis context
+ And you select row number 1
+ And you click the bulk complete enrollments button
+ And the bulk complete enrollments modal should open
+ And you deselect the complete events checkbox
+ And the modal content should say: This action will complete 1 active enrollment in your selection.
+ When you confirm 1 active enrollment without completing events successfully
+ Then the bulk complete enrollments modal should close
+
+#DHIS2-18447
+@skip
+ Scenario: the user should be able to bulk delete enrollments
+ Given you open the main page with Ngelehun and Malaria Case diagnosis context
+ And you select the first 3 rows
+ And you click the bulk delete enrollments button
+ And the bulk delete enrollments modal should open
+ When you confirm deleting 3 enrollments
+ Then the bulk delete enrollments modal should close
+
+#DHIS2-18447
+@skip
+ Scenario: the user should be able to bulk delete only active enrollments
+ Given you open the main page with Ngelehun and Malaria Case diagnosis context
+ And you select the first 3 rows
+ And you click the bulk delete enrollments button
+ And the bulk delete enrollments modal should open
+ When you deselect completed enrollments
+ And you confirm deleting 2 active enrollments
+ Then the bulk delete enrollments modal should close
+
+ @user:trackerAutoTestRestricted
+ Scenario: a restricted user should not be able to bulk delete enrollments
+ Given you open the main page with Ngelehun and WHO RMNCH Tracker context
+ And you open the working lists
+ When you select the first 3 rows
+ Then the bulk delete enrollments button should not be visible
diff --git a/cypress/e2e/WorkingLists/TeiWorkingLists/TeiBulkActions/TeiBulkActions.js b/cypress/e2e/WorkingLists/TeiWorkingLists/TeiBulkActions/TeiBulkActions.js
new file mode 100644
index 0000000000..9a8931cc7f
--- /dev/null
+++ b/cypress/e2e/WorkingLists/TeiWorkingLists/TeiBulkActions/TeiBulkActions.js
@@ -0,0 +1,284 @@
+import { Given, Then, When } from '@badeball/cypress-cucumber-preprocessor';
+import '../../sharedSteps';
+
+Given('you open the main page with Ngelehun and child programe context', () => {
+ cy.visit('#/?programId=IpHINAT79UW&orgUnitId=DiszpKrYNg8');
+});
+
+Given('you open the main page with Ngelehun and Malaria Case diagnosis context', () => {
+ cy.visit('#/?programId=qDkgAbB5Jlk&orgUnitId=DiszpKrYNg8');
+});
+
+Given('you open the main page with Ngelehun and Malaria focus investigation context', () => {
+ cy.visit('#/?programId=M3xtLkYBlKI&orgUnitId=DiszpKrYNg8');
+});
+
+Given('you open the main page with Ngelehun and WHO RMNCH Tracker context', () => {
+ cy.visit('#/?programId=WSGAb5XwJ3Y&orgUnitId=DiszpKrYNg8');
+});
+
+// you open the working lists
+Given('you open the working lists', () => {
+ cy.get('[data-test="template-selector-create-list"]')
+ .click();
+});
+
+Then('the bulk complete enrollments modal should open', () => {
+ cy.get('[data-test="bulk-complete-enrollments-dialog"]')
+ .should('exist');
+});
+
+When('it should say there are 2 active enrollments and 1 completed enrollment', () => {
+ cy.get('[data-test="bulk-complete-enrollments-dialog"]')
+ .contains('This action will complete 2 active enrollments in your selection.' +
+ ' 1 enrollment already marked as completed will not be changed.');
+});
+
+Then('you confirm 3 active enrollments successfully', () => {
+ cy.intercept({
+ method: 'POST',
+ url: '**/tracker?async=false&importStrategy=UPDATE&importMode=VALIDATE',
+ }, {
+ statusCode: 200,
+ body: {},
+ }).as('completeEnrollmentsDryRun');
+
+ cy.intercept({
+ method: 'POST',
+ url: '**/tracker?async=false&importStrategy=UPDATE&importMode=COMMIT',
+ }, {
+ statusCode: 200,
+ body: {},
+ }).as('completeEnrollments');
+
+ cy.get('[data-test="bulk-complete-enrollments-dialog"]')
+ .find('[data-test="bulk-complete-enrollments-confirm-button"]')
+ .click();
+
+ cy.wait('@completeEnrollmentsDryRun')
+ .then((interception) => {
+ expect(interception.response.statusCode).to.eq(200);
+ expect(interception.request.body.enrollments).to.have.length(3);
+ });
+
+ cy.wait('@completeEnrollments')
+ .its('request.body')
+ .should(({ enrollments }) => {
+ // Should be 3 enrollments
+ expect(enrollments).to.have.length(3);
+
+ // Assert that all enrollments are completed
+ enrollments.forEach((enrollment) => {
+ expect(enrollment).to.include({ status: 'COMPLETED' });
+
+ enrollment.events.forEach((event) => {
+ expect(event).to.include({ status: 'COMPLETED' });
+ });
+ });
+ });
+});
+
+Then('the bulk complete enrollments modal should close', () => {
+ cy.get('[data-test="bulk-complete-enrollments-dialog"]')
+ .should('not.exist');
+});
+
+When(/^you select row number (.*)$/, (rowNumber) => {
+ cy.get('[data-test="dhis2-uicore-tablebody"]')
+ .find('tr')
+ .eq(rowNumber)
+ .find('[data-test="select-row-checkbox"]')
+ .click();
+});
+
+Then(/^the modal content should say: (.*)$/, (content) => {
+ cy.get('[data-test="bulk-complete-enrollments-dialog"]')
+ .contains(content);
+});
+
+When('you deselect the complete events checkbox', () => {
+ cy.get('[data-test="bulk-complete-enrollments-dialog"]')
+ .find('[data-test="dhis2-uicore-checkbox"]')
+ .click();
+});
+
+When('you confirm 1 active enrollment without completing events successfully', () => {
+ cy.intercept({
+ method: 'POST',
+ url: '**/tracker?async=false&importStrategy=UPDATE&importMode=VALIDATE',
+ }).as('completeEnrollmentsDryRun');
+
+ cy.intercept({
+ method: 'POST',
+ url: '**/tracker?async=false&importStrategy=UPDATE&importMode=COMMIT',
+ }, {
+ statusCode: 200,
+ body: {},
+ }).as('completeEnrollments');
+
+ cy.get('[data-test="bulk-complete-enrollments-dialog"]')
+ .find('[data-test="bulk-complete-enrollments-confirm-button"]')
+ .click();
+
+ cy.wait('@completeEnrollmentsDryRun')
+ .then((interception) => {
+ expect(interception.response.statusCode).to.eq(200);
+ expect(interception.request.body.enrollments).to.have.length(1);
+ });
+
+ cy.wait('@completeEnrollments')
+ .its('request.body')
+ .should(({ enrollments }) => {
+ // Should be 1 enrollment
+ expect(enrollments).to.have.length(1);
+
+ // Assert that first enrollment is completed with one completed event
+ expect(enrollments[0]).to.include({ status: 'COMPLETED' });
+ expect(enrollments[0].events).to.have.length(0);
+ });
+});
+
+When('you confirm 2 active enrollments with errors', () => {
+ cy.intercept({
+ method: 'POST',
+ url: '**/tracker?async=false&importStrategy=UPDATE&importMode=VALIDATE',
+ }).as('completeEnrollmentsDryRun');
+
+ cy.intercept({
+ method: 'POST',
+ url: '**/tracker?async=false&importStrategy=UPDATE&importMode=COMMIT',
+ }, {
+ statusCode: 200,
+ body: {},
+ }).as('completeEnrollments');
+
+ cy.get('[data-test="bulk-complete-enrollments-dialog"]')
+ .find('[data-test="bulk-complete-enrollments-confirm-button"]')
+ .click();
+
+ cy.wait('@completeEnrollmentsDryRun')
+ .then((interception) => {
+ expect(interception.response.statusCode).to.eq(409);
+ expect(interception.request.body.enrollments).to.have.length(2);
+ });
+
+ cy.wait('@completeEnrollments')
+ .its('request.body')
+ .should(({ enrollments }) => {
+ // The bad data should be filtered out and not sent to the server
+ expect(enrollments).to.have.length(1);
+
+ const enrollment = enrollments[0];
+ expect(enrollment).to.include({ enrollment: 'MqSC9Vuckeh', status: 'COMPLETED' });
+ });
+});
+
+Then('an error dialog will be displayed to the user', () => {
+ cy.get('[data-test="bulk-complete-enrollments-dialog"]')
+ .contains('Error completing enrollments');
+
+ cy.get('[data-test="bulk-complete-enrollments-dialog"]')
+ .contains('Some enrollments were completed successfully, ' +
+ 'but there was an error while completing the rest. Please see the details below.');
+
+ cy.get('[data-test="widget-open-close-toggle-button"]')
+ .click();
+
+ cy.get('[data-test="bulk-complete-enrollments-dialog"]')
+ .find('li')
+ .should('have.length', 2);
+
+ cy.get('[data-test="bulk-complete-enrollments-dialog"]')
+ .find('li')
+ .contains('Mandatory DataElement `fjdU9F6EngS` is not present');
+});
+
+When('you close the error dialog', () => {
+ cy.get('[data-test="bulk-complete-enrollments-dialog"]')
+ .find('[data-test="dhis2-uicore-button"]')
+ .contains('Cancel');
+});
+
+Then('the unsuccessful enrollments should still be selected', () => {
+ cy.get('[data-test="dhis2-uicore-tablebody"]')
+ .find('tr')
+ .eq(0)
+ .should('have.class', 'selected');
+
+ cy.get('[data-test="dhis2-uicore-tablebody"]')
+ .find('tr')
+ .eq(2)
+ .should('have.class', 'selected');
+});
+
+Then('the bulk delete enrollments modal should open', () => {
+ cy.get('[data-test="bulk-delete-enrollments-dialog"]')
+ .should('exist');
+
+ cy.contains('Delete selected enrollments');
+});
+
+When('you deselect completed enrollments', () => {
+ cy.get('[data-test="bulk-delete-enrollments-dialog"]')
+ .find('[data-test="bulk-delete-enrollments-completed-checkbox"]')
+ .click();
+});
+
+When('you confirm deleting 2 active enrollments', () => {
+ cy.intercept({
+ method: 'POST',
+ url: '**/tracker?async=false&importStrategy=DELETE',
+ }, {
+ statusCode: 200,
+ body: {},
+ }).as('deleteEnrollments');
+
+ cy.get('[data-test="bulk-delete-enrollments-dialog"]')
+ .find('[data-test="dhis2-uicore-button"]')
+ .contains('Delete 2 enrollments')
+ .click();
+
+ cy.wait('@deleteEnrollments')
+ .its('request.body')
+ .should(({ enrollments }) => {
+ expect(enrollments).to.have.length(2);
+ expect(enrollments).to.deep.include({ enrollment: 'Rkx1QOZeBra' });
+ expect(enrollments).to.deep.include({ enrollment: 'hDVHG1OavhE' });
+ });
+});
+
+Then('the bulk delete enrollments modal should close', () => {
+ cy.get('[data-test="bulk-delete-enrollments-dialog"]')
+ .should('not.exist');
+});
+
+When('you confirm deleting 3 enrollments', () => {
+ cy.intercept({
+ method: 'POST',
+ url: '**/tracker?async=false&importStrategy=DELETE',
+ }, {
+ statusCode: 200,
+ body: {},
+ }).as('deleteEnrollments');
+
+ cy.get('[data-test="bulk-delete-enrollments-dialog"]')
+ .find('[data-test="dhis2-uicore-button"]')
+ .contains('Delete 3 enrollments')
+ .click();
+
+ cy.wait('@deleteEnrollments')
+ .its('request.body')
+ .should(({ enrollments }) => {
+ expect(enrollments).to.have.length(3);
+ expect(enrollments).to.deep.include({ enrollment: 'PvJFfKjNWbq' });
+ expect(enrollments).to.deep.include({ enrollment: 'Rkx1QOZeBra' });
+ expect(enrollments).to.deep.include({ enrollment: 'hDVHG1OavhE' });
+ });
+});
+
+Then('the bulk delete enrollments button should not be visible', () => {
+ cy.get('[data-test="bulk-action-bar"]')
+ .find('[data-test="dhis2-uicore-button"]')
+ .contains('Delete enrollments')
+ .should('not.exist');
+});
diff --git a/cypress/e2e/WorkingLists/sharedSteps.js b/cypress/e2e/WorkingLists/sharedSteps.js
index dbe0bb9928..41c60e6159 100644
--- a/cypress/e2e/WorkingLists/sharedSteps.js
+++ b/cypress/e2e/WorkingLists/sharedSteps.js
@@ -1,4 +1,4 @@
-import { When, Then } from '@badeball/cypress-cucumber-preprocessor';
+import { Then, When } from '@badeball/cypress-cucumber-preprocessor';
Then('for an event program the page navigation should show that you are on the first page', () => {
cy.get('[data-test="event-working-lists"]')
@@ -127,3 +127,80 @@ When('you change rows per page to 10', () => {
.contains('10')
.click();
});
+
+When(/^you select the first (.*) rows$/, (rows) => {
+ cy.get('[data-test="dhis2-uicore-tablebody"]')
+ .find('tr')
+ .each(($tr, index) => {
+ if (index < rows) {
+ cy.wrap($tr).find('[data-test="select-row-checkbox"]').click();
+ }
+ });
+});
+
+Then(/^the bulk action bar should say (.*) selected$/, (rows) => {
+ cy.get('[data-test="bulk-action-bar"]')
+ .contains(`${rows} selected`);
+});
+
+Then(/^the first (.*) rows should be selected$/, (rows) => {
+ cy.get('[data-test="dhis2-uicore-tablebody"]')
+ .find('tr')
+ .each(($tr, index) => {
+ if (index < rows) {
+ cy.wrap($tr)
+ .should('have.class', 'selected')
+ .find('[data-test="select-row-checkbox"]');
+ }
+ });
+});
+
+When('you select all rows', () => {
+ cy.get('[data-test="select-all-rows-checkbox"]').click();
+});
+
+Then('all rows should be selected', () => {
+ cy.get('[data-test="dhis2-uicore-tablebody"]')
+ .find('tr')
+ .each(($tr) => {
+ cy.wrap($tr)
+ .should('have.class', 'selected')
+ .find('[data-test="select-row-checkbox"]');
+ });
+});
+
+Then('the bulk action bar should not be present', () => {
+ cy.get('[data-test="bulk-action-bar"]').should('not.exist');
+});
+
+Then('no rows should be selected', () => {
+ cy.get('[data-test="dhis2-uicore-tablebody"]')
+ .find('tr')
+ .each(($tr) => {
+ cy.wrap($tr).should('not.have.class', 'selected');
+ });
+});
+
+When(/^you deselect the first (.*) rows$/, (rows) => {
+ cy.get('[data-test="dhis2-uicore-tablebody"]')
+ .find('tr')
+ .each(($tr, index) => {
+ if (index < rows) {
+ cy.wrap($tr).find('[data-test="select-row-checkbox"]').click();
+ }
+ });
+});
+
+Then('the filters should be disabled', () => {
+ cy.get('[data-test="workinglist-template-selector-chip"]')
+ .each(($chip) => {
+ cy.wrap($chip).should('have.class', 'disabled');
+ });
+});
+
+When(/^you click the bulk (.*) button$/, (text) => {
+ cy.get('[data-test="bulk-action-bar"]')
+ .find('[data-test="dhis2-uicore-button"]')
+ .contains(text, { matchCase: false })
+ .click();
+});
diff --git a/docs/user/resources/images/event-bulk-actions-selected-rows.png b/docs/user/resources/images/event-bulk-actions-selected-rows.png
new file mode 100644
index 0000000000000000000000000000000000000000..84c93e2626d7fb76da454d30ef759d96810e37c2
GIT binary patch
literal 126931
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