Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
43a436b
feat: add uid to error
henrikmv Jul 25, 2026
43392d0
feat: integrate BulkActionErrorReports component for error handling i…
henrikmv Jul 25, 2026
9b7b560
feat: add generic error handling
henrikmv Jul 25, 2026
dac99bd
feat: enhance error handling by adding knownEventUids for bulk actions
henrikmv Jul 25, 2026
c9345a4
feat: remove temporary sabotage for error trigger
henrikmv Jul 25, 2026
5b5d516
feat: refactor error handling and update loading states in bulk actions
henrikmv Jul 25, 2026
a10feba
Merge remote-tracking branch 'origin/master' into hv/feat/DHIS2-18573…
henrikmv Aug 5, 2026
a91bc0e
Merge remote-tracking branch 'origin/master' into hv/feat/DHIS2-18573…
henrikmv Aug 6, 2026
34cbd20
feat: refactor temp
henrikmv Aug 6, 2026
7291b63
fix: remove comments
henrikmv Aug 6, 2026
432da64
fix: remove fully deleted records from list when bulk partial success
henrikmv Aug 6, 2026
36ab58b
feat: enhance bulk enrollment and deletion handling with validation i…
henrikmv Aug 6, 2026
ae0f6f5
fix: sonar qube
henrikmv Aug 6, 2026
657da0c
Merge branch 'master' into hv/feat/DHIS2-18573_bulk-error-uid-header
henrikmv Aug 7, 2026
0c0a2ad
Merge remote-tracking branch 'origin/master' into hv/feat/DHIS2-18573…
henrikmv Aug 12, 2026
4df3af8
Merge branch 'master' into hv/feat/DHIS2-18573_bulk-error-uid-header
henrikmv Aug 13, 2026
ec3f78f
Merge branch 'master' into hv/feat/DHIS2-18573_bulk-error-uid-header
henrikmv Aug 17, 2026
f8c0a51
fix: (review) remove org unit check in hrefResolvers
henrikmv Aug 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 27 additions & 28 deletions i18n/en.pot
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ msgstr ""
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"POT-Creation-Date: 2026-08-17T06:08:25.172Z\n"
"PO-Revision-Date: 2026-08-17T06:08:25.172Z\n"
"POT-Creation-Date: 2026-08-19T10:38:49.855Z\n"
"PO-Revision-Date: 2026-08-19T10:38:49.855Z\n"

msgid "The application could not be loaded."
msgstr "The application could not be loaded."
Expand Down Expand Up @@ -1989,21 +1989,12 @@ msgstr "Error completing events"
msgid "There was an error completing the events."
msgstr "There was an error completing the events."

msgid "Details (Advanced)"
msgstr "Details (Advanced)"

msgid "An unknown error occurred."
msgstr "An unknown error occurred."

msgid "An error occurred while completing events"
msgstr "An error occurred while completing events"

msgid "You do not have access to delete events"
msgstr "You do not have access to delete events"

msgid "An error occurred while deleting the events"
msgstr "An error occurred while deleting the events"

msgid "Delete events"
msgstr "Delete events"

Expand All @@ -2013,6 +2004,15 @@ msgstr "This cannot be undone."
msgid "Are you sure you want to delete the selected events?"
msgstr "Are you sure you want to delete the selected events?"

msgid "Error deleting events"
msgstr "Error deleting events"

msgid "There was an error while deleting the events. Please see the details below."
msgstr "There was an error while deleting the events. Please see the details below."

msgid "An error occurred while deleting the events"
msgstr "An error occurred while deleting the events"

msgid "Owner organisation unit"
msgstr "Owner organisation unit"

Expand Down Expand Up @@ -2103,6 +2103,16 @@ msgstr "Delete selected enrollments"
msgid "An error occurred while loading the selected enrollments. Please try again."
msgstr "An error occurred while loading the selected enrollments. Please try again."

msgid "Error deleting enrollments"
msgstr "Error deleting enrollments"

msgid ""
"There was an error while deleting the enrollments. Please see the details "
"below."
msgstr ""
"There was an error while deleting the enrollments. Please see the details "
"below."

msgid ""
"This action will permanently delete the selected enrollments, including all "
"associated data and events."
Expand Down Expand Up @@ -2133,23 +2143,6 @@ msgstr[1] "Delete {{count}} enrollments"
msgid "An error occurred when deleting enrollments"
msgstr "An error occurred when deleting enrollments"

msgid "Delete {{ trackedEntityName }} with all enrollments"
msgstr "Delete {{ trackedEntityName }} with all enrollments"

msgid "Delete {{count}} {{ trackedEntityName }}"
msgid_plural "Delete {{count}} {{ trackedEntityName }}"
msgstr[0] "Delete {{count}} {{ trackedEntityName }}"
msgstr[1] "Delete {{count}} {{ trackedEntityName }}"

msgid "Deleting records will also delete any associated enrollments and events."
msgstr "Deleting records will also delete any associated enrollments and events."

msgid "Are you sure you want to delete?"
msgstr "Are you sure you want to delete?"

msgid "An error occurred while deleting the records"
msgstr "An error occurred while deleting the records"

msgid "Working list could not be updated"
msgstr "Working list could not be updated"

Expand Down Expand Up @@ -2212,6 +2205,12 @@ msgstr[1] "{{count}} selected"
msgid "Deselect all"
msgstr "Deselect all"

msgid "Details (Advanced)"
msgstr "Details (Advanced)"

msgid "An unknown error occurred."
msgstr "An unknown error occurred."

msgid "Download as JSON"
msgstr "Download as JSON"

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import React, { type ComponentType, useState } from 'react';
import React, { type ComponentType, useMemo, useState } from 'react';
import i18n from '@dhis2/d2-i18n';
import { withStyles, type WithStyles } from 'capture-core-utils/styles';
import { Button, ButtonStrip, colors, Modal, ModalActions, ModalContent, ModalTitle } from '@dhis2/ui';
import {
Button, ButtonStrip, colors, Modal, ModalActions, ModalContent, ModalTitle,
} from '@dhis2/ui';
import { useBulkCompleteEvents } from './hooks/useBulkCompleteEvents';
import { ConditionalTooltip } from '../../../../../Tooltips/ConditionalTooltip';
import { Widget } from '../../../../../Widget';
import { BulkActionErrorModal } from '../../../../WorkingListsCommon/BulkActionBar/BulkActionErrorModal';
import { createEventErrorHrefResolver } from '../../../../WorkingListsCommon/BulkActionBar/utils';
import { useLocationQuery } from '../../../../../../utils/routing';
import type { Props } from './CompleteAction.types';

const styles: Readonly<any> = {
Expand All @@ -16,9 +20,6 @@ const styles: Readonly<any> = {
flexDirection: 'column',
gap: '8px',
},
errorContainer: {
padding: '0px 20px',
},
};

const getTooltipContent = (stageDataWriteAccess?: boolean, bulkDataEntryIsActive?: boolean) => {
Expand All @@ -41,9 +42,10 @@ const CompleteActionPlain = ({
classes,
}: Props & WithStyles<typeof styles>) => {
const [isCompleteDialogOpen, setIsCompleteDialogOpen] = useState(false);
const [openAccordion, setOpenAccordion] = useState(false);
const { orgUnitId } = useLocationQuery();
const disabled = !stageDataWriteAccess || Boolean(bulkDataEntryIsActive);
const tooltipContent = getTooltipContent(stageDataWriteAccess, bulkDataEntryIsActive);
const disabled = Boolean(!stageDataWriteAccess || bulkDataEntryIsActive);

const {
eventCounts,
isLoading,
Expand All @@ -59,6 +61,17 @@ const CompleteActionPlain = ({
programId,
});

const getRecordHref = useMemo(
() => createEventErrorHrefResolver({
programId,
orgUnitId,
knownEventUids: new Set(Object.keys(selectedRows)),
}),
[programId, orgUnitId, selectedRows],
);

const closeDialog = () => setIsCompleteDialogOpen(false);

return (
<>
<ConditionalTooltip
Expand All @@ -77,97 +90,48 @@ const CompleteActionPlain = ({
{isCompleteDialogOpen && eventCounts && !validationError && (
<Modal
small
onClose={() => setIsCompleteDialogOpen(false)}
dataTest={'bulk-complete-events-dialog'}
onClose={closeDialog}
dataTest="bulk-complete-events-dialog"
>
<ModalTitle>
{i18n.t('Complete events')}
</ModalTitle>

<ModalTitle>{i18n.t('Complete events')}</ModalTitle>
<ModalContent>
<span className={classes.container}>
{eventCounts.active > 0 ?
i18n.t('Are you sure you want to complete all active events in selection?')
:
i18n.t('There are no active events to complete in the current selection.')
{eventCounts.active > 0
? i18n.t('Are you sure you want to complete all active events in selection?')
: i18n.t('There are no active events to complete in the current selection.')
}
</span>
</ModalContent>

<ModalActions>
<ButtonStrip>
<Button
secondary
onClick={() => setIsCompleteDialogOpen(false)}
onClick={closeDialog}
>
{i18n.t('Cancel')}
</Button>

<Button
primary
onClick={onCompleteEvents}
disabled={isLoading || eventCounts?.active === 0}
disabled={isLoading || eventCounts.active === 0}
loading={isCompletingEvents}
>
{i18n.t('Complete')}
</Button>
</ButtonStrip>
</ModalActions>

</Modal>
)}

{isCompleteDialogOpen && validationError && (
<Modal
small
onClose={() => setIsCompleteDialogOpen(false)}
dataTest={'bulk-complete-events-dialog'}
>
<ModalTitle>
{i18n.t('Error completing events')}
</ModalTitle>

<ModalContent>
<span className={classes.container}>
{i18n.t('There was an error completing the events.')}

<Widget
open={openAccordion}
onOpen={() => setOpenAccordion(true)}
onClose={() => setOpenAccordion(false)}
borderless
header={i18n.t('Details (Advanced)')}
>
<span className={classes.errorContainer}>
<ul>
{validationError?.validationReport?.errorReports ?
validationError.validationReport.errorReports.map(errorReport => (
<li key={`${errorReport.uid}-${errorReport.errorCode}`}>
{errorReport?.message}
</li>
)) : (
<li>
{i18n.t('An unknown error occurred.')}
</li>
)
}
</ul>
</span>
</Widget>
</span>
</ModalContent>

<ModalActions>
<ButtonStrip>
<Button
secondary
onClick={() => setIsCompleteDialogOpen(false)}
>
{i18n.t('Close')}
</Button>
</ButtonStrip>
</ModalActions>
</Modal>
<BulkActionErrorModal
title={i18n.t('Error completing events')}
introText={i18n.t('There was an error completing the events.')}
errorReports={validationError.validationReport.errorReports}
getRecordHref={getRecordHref}
onClose={closeDialog}
dataTest="bulk-complete-events-dialog"
/>
)}
</>
);
Expand Down
Loading
Loading