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-16372] Delete Relationships #3520

Merged
merged 8 commits into from
Mar 3, 2024
23 changes: 18 additions & 5 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-01-25T12:12:47.253Z\n"
"PO-Revision-Date: 2024-01-25T12:12:47.253Z\n"
"POT-Creation-Date: 2024-01-30T15:04:26.286Z\n"
"PO-Revision-Date: 2024-01-30T15:04:26.286Z\n"

msgid "Choose one or more dates..."
msgstr "Choose one or more dates..."
Expand Down Expand Up @@ -1377,9 +1377,6 @@ msgstr "This stage can only have one event"
msgid "Events could not be retrieved. Please try again later."
msgstr "Events could not be retrieved. Please try again later."

msgid "Assigned to"
msgstr "Assigned to"

msgid "{{ totalEvents }} events"
msgstr "{{ totalEvents }} events"

Expand Down Expand Up @@ -1416,6 +1413,22 @@ msgstr "Something went wrong while loading relationships. Please try again later
msgid "{{trackedEntityTypeName}} relationships"
msgstr "{{trackedEntityTypeName}} relationships"

msgid "Delete relationship"
msgstr "Delete relationship"

msgid ""
"Deleting the relationship is permanent and cannot be undone. Are you sure "
"you want to delete this relationship?"
msgstr ""
"Deleting the relationship is permanent and cannot be undone. Are you sure "
"you want to delete this relationship?"

msgid "Yes, delete relationship"
msgstr "Yes, delete relationship"

msgid "An error occurred while deleting the relationship."
msgstr "An error occurred while deleting the relationship."

msgid "To open this relationship, please wait until saving is complete"
msgstr "To open this relationship, please wait until saving is complete"

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// @flow

import React, { useState } from 'react';
import i18n from '@dhis2/d2-i18n';
import {
DataTableCell,
IconDelete16,
Modal,
ModalContent,
ModalTitle,
ModalActions,
ButtonStrip,
Button,
} from '@dhis2/ui';
import { IconButton } from 'capture-ui';
import { withStyles } from '@material-ui/core/styles';

type Props = {
handleDeleteRelationship: () => void,
disabled?: boolean,
classes: {
tableCell: string,
},
}

const styles = {
tableCell: {
display: 'flex',
justifyContent: 'center',
zIndex: 1000,
},
};

export const DeleteRelationshipPlain = ({ handleDeleteRelationship, disabled, classes }: Props) => {
const [isModalOpen, setIsModalOpen] = useState(false);
return (
<>
<DataTableCell className={classes.tableCell}>
<IconButton
simonadomnisoru marked this conversation as resolved.
Show resolved Hide resolved
onClick={() => {
if (disabled) return;
setIsModalOpen(true);
}}
>
<IconDelete16 />
</IconButton>
</DataTableCell>

{isModalOpen && (
<Modal
hide={!isModalOpen}
onClose={() => setIsModalOpen(false)}
>
<ModalTitle>{i18n.t('Delete relationship')}</ModalTitle>
<ModalContent>
{i18n.t('Deleting the relationship is permanent and cannot be undone. Are you sure you want to delete this relationship?')}
</ModalContent>

<ModalActions>
<ButtonStrip>
<Button onClick={() => setIsModalOpen(false)}>
{i18n.t('No, cancel')}
</Button>

<Button
destructive
onClick={() => {
handleDeleteRelationship();
setIsModalOpen(false);
}}
>
{i18n.t('Yes, delete relationship')}
</Button>
</ButtonStrip>
</ModalActions>
</Modal>
)}
</>
);
};

export const DeleteRelationship = withStyles(styles)(DeleteRelationshipPlain);
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// @flow

export { DeleteRelationship } from './DeleteRelationship';
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// @flow
import i18n from '@dhis2/d2-i18n';
import { useMutation, useQueryClient } from 'react-query';
import { useAlert, useDataEngine } from '@dhis2/app-runtime';
import { ReactQueryAppNamespace } from '../../../../../utils/reactQueryHelpers';

type Props = {
sourceId: string,
};

export type OnDeleteRelationship = ({ relationshipId: string }) => void;

const deleteRelationshipMutation = {
resource: 'tracker?importStrategy=DELETE&async=false',
type: 'create',
data: ({ relationshipId }) => ({
relationships: [
{
relationship: relationshipId,
},
],
}),
};
export const useDeleteRelationship = ({ sourceId }: Props): { onDeleteRelationship: OnDeleteRelationship } => {
const dataEngine = useDataEngine();
const queryClient = useQueryClient();
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 } }),
{
onMutate: ({ relationshipId }) => {
const currentRelationships = queryClient
.getQueryData([ReactQueryAppNamespace, 'relationships', sourceId]);

const newRelationships = currentRelationships
?.instances
.filter(({ relationship }) => relationship !== relationshipId);

queryClient.setQueryData(
[ReactQueryAppNamespace, 'relationships', sourceId],
{ instances: newRelationships });
},
onError: showError,
simonadomnisoru marked this conversation as resolved.
Show resolved Hide resolved
},
);

return { onDeleteRelationship };
};
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,12 @@ const styles = {
};


const LinkedEntitiesViewerPlain = ({ groupedLinkedEntities, onLinkedRecordClick, classes }: StyledProps) => (
const LinkedEntitiesViewerPlain = ({
groupedLinkedEntities,
onLinkedRecordClick,
onDeleteRelationship,
classes,
}: StyledProps) => (
<div
data-test="relationships"
className={classes.container}
Expand All @@ -35,6 +40,7 @@ const LinkedEntitiesViewerPlain = ({ groupedLinkedEntities, onLinkedRecordClick,
linkedEntities={linkedEntities}
columns={columns}
onLinkedRecordClick={onLinkedRecordClick}
onDeleteRelationship={onDeleteRelationship}
context={context}
/>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,14 @@ const styles = {
},
};

const LinkedEntityTablePlain = ({ linkedEntities, columns, onLinkedRecordClick, context, classes }: StyledProps) => {
const LinkedEntityTablePlain = ({
linkedEntities,
columns,
onLinkedRecordClick,
onDeleteRelationship,
context,
classes,
}: StyledProps) => {
const [visibleRowsCount, setVisibleRowsCount] = useState(DEFAULT_VISIBLE_ROWS_COUNT);

const visibleLinkedEntities = useMemo(() =>
Expand All @@ -39,12 +46,14 @@ const LinkedEntityTablePlain = ({ linkedEntities, columns, onLinkedRecordClick,
<DataTable>
<LinkedEntityTableHeader
columns={columns}
context={context}
/>
<LinkedEntityTableBody
linkedEntities={visibleLinkedEntities}
columns={columns}
onLinkedRecordClick={onLinkedRecordClick}
context={context}
onDeleteRelationship={onDeleteRelationship}
/>
</DataTable>
{showMoreButtonVisible && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
import i18n from '@dhis2/d2-i18n';
import { convertServerToClient, convertClientToList } from '../../../../converters';
import type { Props, StyledProps } from './linkedEntityTableBody.types';
import { DeleteRelationship } from './DeleteRelationship';

const styles = {
row: {
Expand All @@ -26,13 +27,14 @@ const LinkedEntityTableBodyPlain = ({
columns,
onLinkedRecordClick,
context,
onDeleteRelationship,
classes,
}: StyledProps) => (
<DataTableBody>
{
linkedEntities
.map(({ id: entityId, values, baseValues, navigation }) => {
const { pendingApiResponse } = baseValues || {};
const { pendingApiResponse, relationshipId } = baseValues || {};
return (
<DataTableRow
key={entityId}
Expand Down Expand Up @@ -76,6 +78,12 @@ const LinkedEntityTableBodyPlain = ({
</Tooltip>
);
})}
{context.display.showDeleteButton && (
<DeleteRelationship
handleDeleteRelationship={() => onDeleteRelationship({ relationshipId })}
disabled={pendingApiResponse}
/>
)}
</DataTableRow>
);
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
} from '@dhis2/ui';
import type { Props } from './linkedEntityTableHeader.types';

export const LinkedEntityTableHeader = ({ columns }: Props) => (
export const LinkedEntityTableHeader = ({ columns, context }: Props) => (
<DataTableHead>
<DataTableRow>
{
Expand All @@ -20,6 +20,9 @@ export const LinkedEntityTableHeader = ({ columns }: Props) => (
</DataTableColumnHeader>
))
}
{context.display.showDeleteButton && (
<DataTableColumnHeader />
)}
</DataTableRow>
</DataTableHead>
);
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { useGroupedLinkedEntities } from './useGroupedLinkedEntities';
import { LinkedEntitiesViewer } from './LinkedEntitiesViewer.component';
import type { Props, StyledProps } from './relationshipsWidget.types';
import { LoadingMaskElementCenter } from '../../../LoadingMasks';
import { useDeleteRelationship } from './DeleteRelationship/useDeleteRelationship';

const styles = {
header: {
Expand All @@ -30,6 +31,7 @@ const RelationshipsWidgetPlain = ({
}: StyledProps) => {
const [open, setOpenStatus] = useState(true);
const groupedLinkedEntities = useGroupedLinkedEntities(sourceId, relationshipTypes, relationships);
const { onDeleteRelationship } = useDeleteRelationship({ sourceId });

if (isLoading) {
return (
Expand Down Expand Up @@ -79,6 +81,7 @@ const RelationshipsWidgetPlain = ({
<LinkedEntitiesViewer
groupedLinkedEntities={groupedLinkedEntities}
onLinkedRecordClick={onLinkedRecordClick}
onDeleteRelationship={onDeleteRelationship}
/>
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// @flow
import type { GroupedLinkedEntities, LinkedRecordClick } from './types';
import type { OnDeleteRelationship } from './DeleteRelationship/useDeleteRelationship';

export type Props = $ReadOnly<{|
groupedLinkedEntities: GroupedLinkedEntities,
onLinkedRecordClick: LinkedRecordClick,
onDeleteRelationship: OnDeleteRelationship,
|}>;

export type StyledProps = $ReadOnly<{|
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
// @flow
import type { LinkedEntityData, TableColumn, LinkedRecordClick, Context } from './types';
import type { OnDeleteRelationship } from './DeleteRelationship/useDeleteRelationship';

export type Props = $ReadOnly<{|
linkedEntities: $ReadOnlyArray<LinkedEntityData>,
columns: $ReadOnlyArray<TableColumn>,
onLinkedRecordClick: LinkedRecordClick,
onDeleteRelationship: OnDeleteRelationship,
context: Context,
|}>;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
// @flow
import type { LinkedEntityData, TableColumn, Context, LinkedRecordClick } from './types';
import type { OnDeleteRelationship } from './DeleteRelationship/useDeleteRelationship';

export type Props = $ReadOnly<{|
linkedEntities: $ReadOnlyArray<LinkedEntityData>,
columns: $ReadOnlyArray<TableColumn>,
onLinkedRecordClick: LinkedRecordClick,
context: Context,
onDeleteRelationship: OnDeleteRelationship,
|}>;

export type StyledProps = $ReadOnly<{|
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// @flow
import type { TableColumn } from './types';
import type { Context, TableColumn } from './types';

export type Props = $ReadOnly<{|
columns: $ReadOnlyArray<TableColumn>,
context: Context,
|}>;
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export type LinkedEntityData = $ReadOnly<{|
values: $ReadOnly<{| [id: string]: ?string |}>,
baseValues?: {
relationshipCreatedAt?: string,
relationshipId: string,
pendingApiResponse?: boolean,
},
navigation?: {
Expand Down
Loading
Loading