Skip to content

Commit

Permalink
feat: [DHIS2-16922] Delete Tracked entity from profile Widget (#3545)
Browse files Browse the repository at this point in the history
  • Loading branch information
simonadomnisoru authored Mar 5, 2024
1 parent b30d87d commit fb8171e
Show file tree
Hide file tree
Showing 40 changed files with 562 additions and 16 deletions.
39 changes: 38 additions & 1 deletion cypress/e2e/WidgetsForEnrollmentPages/WidgetProfile/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Then } from '@badeball/cypress-cucumber-preprocessor';
import { Then, Given, When } from '@badeball/cypress-cucumber-preprocessor';
import '../../sharedSteps';

Then('the profile details should be displayed', () => {
Expand Down Expand Up @@ -31,3 +31,40 @@ Then(/^the user sees the edit profile modal/, () =>
cy.contains('Cancel without saving').should('exist');
}),
);

Given('you add a new tracked entity in the Malaria focus investigation program', () => {
cy.visit('/#/new?programId=M3xtLkYBlKI&orgUnitId=DiszpKrYNg8');
cy.get('[data-test="capture-ui-input"]')
.eq(2)
.type(`Local id-${Math.round((new Date()).getTime() / 1000)}`)
.blur();
cy.contains('Save focus area')
.click();
cy.url().should('include', 'enrollmentEventEdit?');
});

When('you open the overflow menu and click the "Delete Focus area" button', () => {
cy.get('[data-test="widget-profile-overflow-menu"]')
.click();
cy.contains('Delete Focus area')
.click();
});

Then('you see the delete tracked entity confirmation modal', () => {
cy.get('[data-test="widget-profile-delete-modal"]').within(() => {
cy.contains(
'Are you sure you want to delete this Focus area? This will permanently remove the Focus area and all its associated enrollments and events in all programs.',
).should('exist');
});
});

When('you confirm by clicking the "Yes, delete Focus area" button', () => {
cy.get('[data-test="widget-profile-delete-modal"]').within(() => {
cy.contains('Yes, delete Focus area')
.click();
});
});

Then('you are redirected to the home page', () => {
cy.url().should('include', 'selectedTemplateId=M3xtLkYBlKI');
});
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,15 @@ Feature: The user interacts with the widgets on the enrollment add event page
And the user sees the owner organisation unit
And the user sees the last update date

Scenario: You can delete a tracked entity from the profile widget
Given you add a new tracked entity in the Malaria focus investigation program
When the user clicks the "Back to all stages and events" button
When the user clicks the "New Event" button
When you open the overflow menu and click the "Delete Focus area" button
Then you see the delete tracked entity confirmation modal
When you confirm by clicking the "Yes, delete Focus area" button
Then you are redirected to the home page

# TODO DHIS2-11482 - The test cases related with enrollment status edit are flaky. Move them to unit tests.
# Scenario: User can modify the enrollment from Active to Complete
# Given you land on the enrollment add event page by having typed #/enrollmentEventNew?programId=IpHINAT79UW&orgUnitId=DiszpKrYNg8&teiId=EaOyKGOIGRp&enrollmentId=wBU0RAsYjKE&stageId=A03MvHHogjR
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Then } from '@badeball/cypress-cucumber-preprocessor';
import { When, Then } from '@badeball/cypress-cucumber-preprocessor';
import '../sharedSteps';
import '../WidgetEnrollment';
import '../WidgetProfile';
Expand All @@ -14,3 +14,17 @@ Then('you can assign a user when scheduling the event', () => {
cy.get('[data-test="dhis2-uicore-chip"]').contains('Tracker demo User').should('exist');
});
});

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')
.click(),
);

When(/^the user clicks the "New Event" button/, () =>
cy
.get('[data-test="quick-action-button-report"]')
.contains('New Event')
.click(),
);
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,14 @@ Feature: The user interacts with the widgets on the enrollment dashboard
Then the profile widget attributes list contains the text Maria
And the scope selector list contains the text Maria

Scenario: You can delete a tracked entity from the profile widget
Given you add a new tracked entity in the Malaria focus investigation program
When the user clicks the "Back to all stages and events" button
When you open the overflow menu and click the "Delete Focus area" button
Then you see the delete tracked entity confirmation modal
When you confirm by clicking the "Yes, delete Focus area" button
Then you are redirected to the home page

Scenario: User can close the Enrollment Widget
Given you land on the enrollment dashboard page by having typed #/enrollment?enrollmentId=wBU0RAsYjKE
And the enrollment widget should be opened
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,11 @@ Then(/^the scope selector list contains the text (.*)$/, (name) => {
cy.contains(name).should('exist');
});
});

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')
.click(),
);

Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@ Feature: The user interacts with the widgets on the enrollment edit event
And the user sees the owner organisation unit
And the user sees the last update date

Scenario: You can delete a tracked entity from the profile widget
Given you add a new tracked entity in the Malaria focus investigation program
When you open the overflow menu and click the "Delete Focus area" button
Then you see the delete tracked entity confirmation modal
When you confirm by clicking the "Yes, delete Focus area" button
Then you are redirected to the home page

# TODO DHIS2-11482 - The test cases related with enrollment status edit are flaky. Move them to unit tests.
# Scenario: User can modify the enrollment from Active to Complete
# Given you land on the enrollment edit event page by having typed #/enrollmentEventEdit?programId=IpHINAT79UW&orgUnitId=DiszpKrYNg8&teiId=EaOyKGOIGRp&enrollmentId=wBU0RAsYjKE&stageId=A03MvHHogjR
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions docs/user/using-the-capture-app.md
Original file line number Diff line number Diff line change
Expand Up @@ -1039,6 +1039,10 @@ Click the **Edit** button to make changes to the tracked entity instance profile

![](resources/images/enrollment-dash-tei-profile-widget-edit.png)

Click the **Delete ${tracked entity type}** button to delete the tracked entity. You can confirm the action from the dialog. Once confirmed, tracked entity and all its associated enrollment and events across all programs will be deleted. To delete a tracked entity that has any enrollments, the user needs the authority **Delete tracked entity instance and associated enrollments and events**.

![](resources/images/enrollment-dash-tei-profile-widget-delete.png)

### Feedback widget

![](resources/images/enrollment-dash-feedback-widget-1.png)
Expand Down
32 changes: 28 additions & 4 deletions i18n/en.pot
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ msgstr ""
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"POT-Creation-Date: 2024-03-05T07:44:33.812Z\n"
"PO-Revision-Date: 2024-03-05T07:44:33.812Z\n"
"POT-Creation-Date: 2024-03-05T12:36:05.806Z\n"
"PO-Revision-Date: 2024-03-05T12:36:05.806Z\n"

msgid "Choose one or more dates..."
msgstr "Choose one or more dates..."
Expand Down Expand Up @@ -1392,11 +1392,35 @@ msgstr "Try again or contact your system administrator for support"
msgid "Fix errors in the form to continue."
msgstr "Fix errors in the form to continue."

msgid "You do not have access to delete this {{trackedEntityTypeName}}"
msgstr "You do not have access to delete this {{trackedEntityTypeName}}"

msgid "Delete {{trackedEntityTypeName}}"
msgstr "Delete {{trackedEntityTypeName}}"

msgid ""
"Are you sure you want to delete this {{trackedEntityTypeName}}? This will "
"permanently remove the {{trackedEntityTypeName}} and all its associated "
"enrollments and events in all programs."
msgstr ""
"Are you sure you want to delete this {{trackedEntityTypeName}}? This will "
"permanently remove the {{trackedEntityTypeName}} and all its associated "
"enrollments and events in all programs."

msgid "There was a problem deleting the {{trackedEntityTypeName}}"
msgstr "There was a problem deleting the {{trackedEntityTypeName}}"

msgid "Yes, delete {{trackedEntityTypeName}}"
msgstr "Yes, delete {{trackedEntityTypeName}}"

msgid "View changelog"
msgstr "View changelog"

msgid "Profile widget could not be loaded. Please try again later"
msgstr "Profile widget could not be loaded. Please try again later"

msgid "{{TETName}} profile"
msgstr "{{TETName}} profile"
msgid "{{trackedEntityTypeName}} profile"
msgstr "{{trackedEntityTypeName}} profile"

msgid "tracked entity instance"
msgstr "tracked entity instance"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// @flow
import * as React from 'react';
import { useRef, useState } from 'react';
import { Button, Layer, Popper } from '@dhis2/ui';

type Props = {
label?: string,
primary?: boolean,
secondary?: boolean,
icon?: React.Node,
onClick?: () => void,
open?: boolean,
component: React.Node,
dataTest?: string,
small?: boolean,
large?: boolean,
className: string,
};

export const OverflowButton = ({
label,
primary,
secondary,
small,
large,
onClick: handleClick,
open: propsOpen,
icon,
dataTest,
component,
className,
}: Props) => {
const [isOpen, setIsOpen] = useState(false);
const anchorRef = useRef(null);
const open = propsOpen !== undefined ? propsOpen : isOpen;

const toggle = () => {
if (propsOpen === undefined) {
setIsOpen(prev => !prev);
}
handleClick && handleClick();
};

return (
<div ref={anchorRef}>
<Button
primary={primary}
secondary={secondary}
dataTest={dataTest}
small={small}
large={large}
onClick={toggle}
icon={icon}
className={className}
>
{label}
</Button>

{open && (
<Layer onBackdropClick={toggle} transparent>
<Popper reference={anchorRef} placement="bottom-end">
{component}
</Popper>
</Layer>
)}
</div>
);
};
1 change: 1 addition & 0 deletions src/core_modules/capture-core/components/Buttons/index.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
// @flow
export { SimpleSplitButton } from './SimpleSplitButton.component';
export { OverflowButton } from './OverflowButton.component';
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ export const EnrollmentPageDefault = () => {
const outputEffects = useFilteredWidgetData(ruleEffects);
const hideWidgets = useHideWidgetByRuleLocations(program.programRules);

const onDeleteTrackedEntitySuccess = useCallback(() => {
history.push(`/?${buildUrlQueryString({ orgUnitId, programId })}`);
}, [history, orgUnitId, programId]);

const onDelete = () => {
history.push(`/enrollment?${buildUrlQueryString({ orgUnitId, programId, teiId })}`);
dispatch(deleteEnrollment({ enrollmentId }));
Expand Down Expand Up @@ -169,6 +173,7 @@ export const EnrollmentPageDefault = () => {
enrollmentId={enrollmentId}
onAddNew={onAddNew}
onDelete={onDelete}
onDeleteTrackedEntitySuccess={onDeleteTrackedEntitySuccess}
onViewAll={onViewAll}
onCreateNew={onCreateNew}
widgetEffects={outputEffects}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export type Props = {|
widgetEnrollmentStatus: ?string,
pageLayout: PageLayoutConfig,
availableWidgets: $ReadOnly<{ [key: string]: WidgetConfig }>,
onDeleteTrackedEntitySuccess: () => void,
|};

export type PlainProps = {|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ export const EnrollmentAddEventPageDefault = ({
history.push(`enrollment?${buildUrlQueryString({ programId, orgUnitId, teiId, enrollmentId })}`);
}, [history, programId, orgUnitId, teiId, enrollmentId]);

const onDeleteTrackedEntitySuccess = useCallback(() => {
history.push(`/?${buildUrlQueryString({ orgUnitId, programId })}`);
}, [history, orgUnitId, programId]);

const onUpdateEnrollmentStatus = useCallback((enrollmentToUpdate) => {
dispatch(updateEnrollmentAndEvents(enrollmentToUpdate));
}, [dispatch]);
Expand Down Expand Up @@ -169,6 +173,7 @@ export const EnrollmentAddEventPageDefault = ({
onSaveAndCompleteEnrollment={handleSaveAndCompleteEnrollment}
onCancel={handleCancel}
onDelete={handleDelete}
onDeleteTrackedEntitySuccess={onDeleteTrackedEntitySuccess}
onAddNew={handleAddNew}
widgetEffects={outputEffects}
hideWidgets={hideWidgets}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export type Props = {|
onUpdateEnrollmentStatusError: (message: string) => void,
pageLayout: PageLayoutConfig,
availableWidgets: $ReadOnly<{ [key: string]: WidgetConfig }>,
onDeleteTrackedEntitySuccess: () => void,
...CssClasses,
|};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export const EnrollmentEditEventPageComponent = ({
getAssignedUserSaveContext,
onSaveAssignee,
onSaveAssigneeError,
onDeleteTrackedEntitySuccess,
onAccessLostFromTransfer,
}: PlainProps) => (
<OrgUnitFetcher orgUnitId={orgUnitId}>
Expand Down Expand Up @@ -103,6 +104,7 @@ export const EnrollmentEditEventPageComponent = ({
getAssignedUserSaveContext={getAssignedUserSaveContext}
onSaveAssignee={onSaveAssignee}
onSaveAssigneeError={onSaveAssigneeError}
onDeleteTrackedEntitySuccess={onDeleteTrackedEntitySuccess}
onAccessLostFromTransfer={onAccessLostFromTransfer}
/>
<NoticeBox formId={`${dataEntryIds.ENROLLMENT_EVENT}-${mode}`} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,10 @@ const EnrollmentEditEventPageWithContextPlain = ({
const programStage = [...program.stages?.values()].find(item => item.id === stageId);
const hideWidgets = useHideWidgetByRuleLocations(program.programRules.concat(programStage?.programRules));

const onDeleteTrackedEntitySuccess = useCallback(() => {
history.push(`/?${buildUrlQueryString({ orgUnitId, programId })}`);
}, [history, orgUnitId, programId]);

const onDelete = () => {
history.push(`/enrollment?${buildUrlQueryString({ orgUnitId, programId, teiId })}`);
dispatch(deleteEnrollment({ enrollmentId }));
Expand Down Expand Up @@ -238,6 +242,7 @@ const EnrollmentEditEventPageWithContextPlain = ({
trackedEntityName={trackedEntityName}
program={program}
onDelete={onDelete}
onDeleteTrackedEntitySuccess={onDeleteTrackedEntitySuccess}
onAddNew={onAddNew}
orgUnitId={orgUnitId}
eventDate={eventDate}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export type PlainProps = {|
assignee: UserFormField | null,
onSaveAssignee: (newAssignee: UserFormField) => void,
onSaveAssigneeError: (prevAssignee: UserFormField | null) => void,
onDeleteTrackedEntitySuccess: () => void,
events: Array<any>,
|};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,11 +119,18 @@ export const ProfileWidget: WidgetConfig = {
getCustomSettings: ({ readOnlyMode = true }) => ({
readOnlyMode,
}),
getProps: ({ teiId, program, orgUnitId, onUpdateTeiAttributeValues }): WidgetProfileProps => ({
getProps: ({
teiId,
program,
orgUnitId,
onUpdateTeiAttributeValues,
onDeleteTrackedEntitySuccess,
}): WidgetProfileProps => ({
teiId,
programId: program.id,
orgUnitId,
onUpdateTeiAttributeValues,
onDeleteSuccess: onDeleteTrackedEntitySuccess,
}),
};

Expand Down
Loading

0 comments on commit fb8171e

Please sign in to comment.