Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: [DHIS2-18017] Ability to unlink event from edit/view event page #3846

Merged
merged 27 commits into from
Dec 17, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
519196d
feat: add menu items for unlik and delete event
henrikmv Oct 14, 2024
31f0b4a
feat: temp
henrikmv Oct 18, 2024
919a15d
Merge branch 'master' of https://github.com/dhis2/capture-app into hv…
henrikmv Oct 18, 2024
2509447
feat: delete and unlink function
henrikmv Oct 18, 2024
8c02716
Merge branch 'master' of https://github.com/dhis2/capture-app into hv…
henrikmv Oct 21, 2024
dba31d1
fix: review changes
henrikmv Oct 28, 2024
9d443b1
fix: remove update data
henrikmv Oct 28, 2024
74a1fe9
fix: use invalidatequeries
henrikmv Oct 30, 2024
f993afd
Merge branch 'master' of https://github.com/dhis2/capture-app into hv…
henrikmv Oct 30, 2024
e39d360
fix: remove noticebox and add alerterror
henrikmv Oct 30, 2024
4cd3f98
feat: add validation
henrikmv Nov 4, 2024
4c596fc
fix: indexeddb write access
henrikmv Nov 5, 2024
7a7678b
Merge branch 'master' of https://github.com/dhis2/capture-app into hv…
henrikmv Nov 6, 2024
f6c9610
fix: review comments
henrikmv Nov 6, 2024
73eb06b
fix: user message improvements
henrikmv Nov 27, 2024
e3284e9
Merge remote-tracking branch 'origin/master' into hv/feat/DHIS2-18017…
henrikmv Dec 5, 2024
1ce3a78
Revert "Merge remote-tracking branch 'origin/master' into hv/feat/DHI…
henrikmv Dec 5, 2024
1a3db3f
fix: merge conflict
henrikmv Dec 5, 2024
87f249a
Merge branch 'master' into hv/feat/DHIS2-18017_AbilityToUnlinkEvent
henrikmv Dec 5, 2024
9143053
fix: dublicate code
henrikmv Dec 5, 2024
a0c848f
feat: update dhis ui
henrikmv Dec 6, 2024
1890238
Merge remote-tracking branch 'origin/master' into hv/feat/DHIS2-18017…
henrikmv Dec 9, 2024
93579fa
fix: merge error in package json
henrikmv Dec 10, 2024
c502b84
Merge remote-tracking branch 'origin/master' into hv/feat/DHIS2-18017…
henrikmv Dec 14, 2024
aec168d
Merge remote-tracking branch 'origin/master' into hv/feat/DHIS2-18017…
henrikmv Dec 16, 2024
6b7dd5f
Revert "feat: update dhis ui"
henrikmv Dec 17, 2024
df83ca1
fix: revert changes in version after dhis2 ui update
henrikmv Dec 17, 2024
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
50 changes: 46 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-10-25T18:18:11.518Z\n"
"PO-Revision-Date: 2024-10-25T18:18:11.518Z\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..."
Expand Down Expand Up @@ -1493,12 +1493,54 @@ msgstr "{{ scheduledEvents }} scheduled"
msgid "Stages and Events"
msgstr "Stages and Events"

msgid "An error occurred while loading the widget."
msgstr "An error occurred while loading the widget."
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 delete the relationsship and the related event? This "
"will permanently remove the event and all related data."
msgstr ""
"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"

msgid "Unlink relationship"
msgstr "Unlink 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"

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"

msgid "An error occurred while loading the widget."
msgstr "An error occurred while loading the widget."

msgid "Scheduled"
msgstr "Scheduled"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ type Props = {
...CssClasses,
}

export const ScheduleInOrgUnitPlain = ({
const ScheduleInOrgUnitPlain = ({
relatedStagesDataValues,
setRelatedStagesDataValues,
saveAttempted,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// @flow
import React from 'react';
import i18n from '@dhis2/d2-i18n';
import {
Button,
ButtonStrip,
Modal,
ModalActions,
ModalContent,
ModalTitle,
} from '@dhis2/ui';
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';

export const UnlinkAndDeleteModal = ({
setOpenModal,
eventId,
originEventId,
}: 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 = {
resource: 'tracker?async=false&importStrategy=DELETE',
simonadomnisoru marked this conversation as resolved.
Show resolved Hide resolved
type: 'create',
data: { events: [{ event: eventId }] },
};

return dataEngine.mutate(mutation);
};

const mutation = useMutation(deleteEvent, {
onSuccess: () => {
queryClient.invalidateQueries([
ReactQueryAppNamespace,
'linkedEventByOriginEvent',
originEventId,
]);
setOpenModal(false);
},
onError: (error) => {
showErrorAlert();
log.error(
`Failed to unlink and delete event with ID: ${eventId}`,
error,
);
},
});

return (
<Modal dataTest="event-unlink-and-delete-modal" position="middle">
henrikmv marked this conversation as resolved.
Show resolved Hide resolved
<ModalTitle>{i18n.t('Delete event')}</ModalTitle>
<ModalContent>
<p>
{i18n.t(
'Are you sure you want delete the relationsship and the related event? This will permanently remove the event and all related data.',
henrikmv marked this conversation as resolved.
Show resolved Hide resolved
)}
</p>
</ModalContent>
<ModalActions>
<ButtonStrip end>
<Button
onClick={() => setOpenModal(false)}
secondary
>
{i18n.t('No, cancel')}
</Button>
<Button
destructive
onClick={() => mutation.mutate()}
disabled={mutation.isLoading}
>
{i18n.t('Yes, unlink and delete event')}
</Button>
</ButtonStrip>
</ModalActions>
</Modal>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// @flow
export type Props = {|
setOpenModal: (open: boolean) => void,
eventId: string,
originEventId: string,
|};
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// @flow
import React from 'react';
import i18n from '@dhis2/d2-i18n';
import {
Modal,
ModalContent,
ModalTitle,
ModalActions,
ButtonStrip,
Button,
} from '@dhis2/ui';
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';

export const UnlinkModal = ({
setOpenModal,
relationshipId,
originEventId,
}: 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 = {
resource: 'tracker?importStrategy=DELETE&async=false',
type: 'create',
data: { relationships: [{ relationship: relationshipId }] },
};

return dataEngine.mutate(mutation);
};

const mutation = useMutation(deleteRelationship, {
onSuccess: () => {
queryClient.invalidateQueries([
ReactQueryAppNamespace,
'linkedEventByOriginEvent',
originEventId,
]);
setOpenModal(false);
},
onError: (error) => {
showErrorAlert();
log.error(
`Failed to remove relationship with id ${relationshipId}`,
error,
);
},
});

return (
<Modal dataTest="event-unlink-modal">
<ModalTitle>
{i18n.t('Unlink relationship')}
</ModalTitle>
<ModalContent>
<p>{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')}</p>
</ModalContent>
<ModalActions>
<ButtonStrip end>
<Button onClick={() => setOpenModal(false)} secondary>
{i18n.t('No, cancel')}
</Button>
<Button
destructive
onClick={() => mutation.mutate()}
disabled={mutation.isLoading}
>
{i18n.t('Yes, unlink relationship')}
</Button>
</ButtonStrip>
</ModalActions>
</Modal>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// @flow
export type Props = {|
setOpenModal: (open: boolean) => void,
relationshipId: string,
originEventId: string,
|};
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// @flow
export { UnlinkModal } from './UnlinkModal';
export { UnlinkAndDeleteModal } from './UnlinkAndDeleteModal';
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// @flow
import React, { useState } from 'react';
import { useHistory } from 'react-router-dom';
import {
Divider,
FlyoutMenu,
IconDelete16,
IconLink16,
IconMore16,
IconView16,
MenuItem,
} from '@dhis2/ui';
import i18n from '@dhis2/d2-i18n';
import { ConditionalTooltip } from '../../Tooltips/ConditionalTooltip';
import { OverflowButton } from '../../Buttons';
import { UnlinkModal, UnlinkAndDeleteModal } from './Modal';
import { buildUrlQueryString } from '../../../utils/routing';
import type { Props } from './OverflowMenu.types';
import { useRelationshipTypeAccess } from '../hooks';

export const OverflowMenuComponent = ({
linkedEvent,
relationshipId,
orgUnitId,
originEventId,
stageWriteAccess,
relationshipType,
}: Props) => {
const { push } = useHistory();
const [isActionsOpen, setIsActionsOpen] = useState(false);
const [isUnlinkModalOpen, setIsUnlinkModalOpen] = useState(false);
const [isUnlinkAndDeleteModalOpen, setIsUnlinkAndDeleteModalOpen] = useState(false);
const { relationshipTypeWriteAccess } = useRelationshipTypeAccess(relationshipType);

const handleViewLinkedEvent = () => {
push(`/enrollmentEventEdit?${buildUrlQueryString({ eventId: linkedEvent.event, orgUnitId })}`);
setIsActionsOpen(false);
};

const handleUnlinkEvent = () => {
setIsUnlinkModalOpen(true);
setIsActionsOpen(false);
};

const handleUnlinkAndDeleteEvent = () => {
setIsUnlinkAndDeleteModalOpen(true);
setIsActionsOpen(false);
};

return (
<>
<OverflowButton
open={isActionsOpen}
onClick={() => setIsActionsOpen(prev => !prev)}
icon={<IconMore16 />}
small
secondary
dataTest="widget-linked-event-overflow-menu"
component={
<FlyoutMenu dense maxWidth="250px">
<MenuItem
label={i18n.t('View linked event')}
icon={<IconView16 />}
dataTest="event-overflow-view-linked-event"
onClick={handleViewLinkedEvent}
/>
<Divider />
<ConditionalTooltip
content={i18n.t('You do not have access to remove the relationship between these two events')}
enabled={!relationshipTypeWriteAccess}
>
<MenuItem
label={i18n.t('Unlink event')}
icon={<IconLink16 />}
disabled={!relationshipTypeWriteAccess}
dense
dataTest="event-overflow-unlink-event"
onClick={handleUnlinkEvent}
/>
</ConditionalTooltip>
<ConditionalTooltip
content={i18n.t('You do not have access remove the relationship between these two events and delete the other event')}
enabled={!stageWriteAccess || !relationshipTypeWriteAccess}
>
<MenuItem
label={i18n.t('Unlink and delete event')}
icon={<IconDelete16 />}
disabled={!stageWriteAccess || !relationshipTypeWriteAccess}
dense
destructive
dataTest="event-overflow-unlink-and-delete-event"
onClick={handleUnlinkAndDeleteEvent}
/>
</ConditionalTooltip>
</FlyoutMenu>
}
/>
{isUnlinkModalOpen && (
<UnlinkModal
setOpenModal={setIsUnlinkModalOpen}
relationshipId={relationshipId}
originEventId={originEventId}
/>
)}
{isUnlinkAndDeleteModalOpen && (
<UnlinkAndDeleteModal
setOpenModal={setIsUnlinkAndDeleteModalOpen}
eventId={linkedEvent.event}
originEventId={originEventId}
/>
)}
</>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// @flow
export type Props = {
linkedEvent: any,
relationshipId: string,
orgUnitId: string,
originEventId: string,
stageWriteAccess: boolean,
relationshipType: string,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// @flow
export { OverflowMenuComponent } from './OverflowMenu.component';
Loading
Loading