diff --git a/src/components/Contacts/ContactDetails/ContactDetailsHeader/DeleteContactModal/DeleteContactModal.test.tsx b/src/components/Contacts/ContactDetails/ContactDetailsHeader/DeleteContactModal/DeleteContactModal.test.tsx index 317cfa530..222d304e2 100644 --- a/src/components/Contacts/ContactDetails/ContactDetailsHeader/DeleteContactModal/DeleteContactModal.test.tsx +++ b/src/components/Contacts/ContactDetails/ContactDetailsHeader/DeleteContactModal/DeleteContactModal.test.tsx @@ -156,15 +156,13 @@ describe('DeleteContactModal', () => { ]; test.each(tests)('$testName', async ({ props }) => { - const { findByText, getByRole } = render(); + const { findByText } = render(); expect( await findByText( - /This contact cannot be deleted because part or all of the contact's data syncs with Donation Services/, + /its data may sync with Donation Services or other third-party systems/, ), ).toBeInTheDocument(); - - expect(getByRole('button', { name: 'delete contact' })).toBeDisabled(); }); }); diff --git a/src/components/Contacts/ContactDetails/ContactDetailsHeader/DeleteContactModal/DeleteContactModal.tsx b/src/components/Contacts/ContactDetails/ContactDetailsHeader/DeleteContactModal/DeleteContactModal.tsx index 64b4efedf..02e40d2f2 100644 --- a/src/components/Contacts/ContactDetails/ContactDetailsHeader/DeleteContactModal/DeleteContactModal.tsx +++ b/src/components/Contacts/ContactDetails/ContactDetailsHeader/DeleteContactModal/DeleteContactModal.tsx @@ -1,9 +1,14 @@ import React, { useMemo } from 'react'; +import { Email, Person, Phone, Place } from '@mui/icons-material'; import { CircularProgress, DialogActions, DialogContent, - DialogContentText, + List, + ListItem, + ListItemIcon, + ListItemText, + Typography, } from '@mui/material'; import { styled } from '@mui/material/styles'; import { useTranslation } from 'react-i18next'; @@ -13,13 +18,22 @@ import { } from 'src/components/common/Modal/ActionButtons/ActionButtons'; import Modal from 'src/components/common/Modal/Modal'; import { useAccountListId } from 'src/hooks/useAccountListId'; -import { isEditableSource } from 'src/utils/sourceHelper'; +import { isEditableSource, sourceToStr } from 'src/utils/sourceHelper'; import { useContactSourceQuery } from './ContactSource.generated'; const LoadingIndicator = styled(CircularProgress)(({ theme }) => ({ margin: theme.spacing(0, 1, 0, 0), })); +interface DataInfo { + canDelete: boolean; + uniqueSources: { + contact: string[]; + address: string[]; + email: string[]; + phone: string[]; + }; +} interface DeleteContactModalProps { open: boolean; setOpen: (open: boolean) => void; @@ -44,9 +58,17 @@ export const DeleteContactModal: React.FC = ({ }); const contactSources = data?.contact; - const canDelete = useMemo(() => { + const dataInfo: DataInfo = useMemo(() => { if (!contactSources) { - return true; + return { + canDelete: true, + uniqueSources: { + contact: [], + address: [], + email: [], + phone: [], + }, + }; } // We ensure the contact was created on MPDX and that all the data is editable. // If any data is not editable, this means it was created by a third party. @@ -55,26 +77,67 @@ export const DeleteContactModal: React.FC = ({ const isContactNonEditable = !isEditableSource(contactSources.source ?? ''); - const isAddressNonEditable = contactSources.addresses?.nodes.some( - (address) => !isEditableSource(address.source ?? ''), + const sources: Map = new Map(); + sources.set('contact', [contactSources.source ?? '']); + + const isAddressNonEditable = contactSources.addresses?.nodes.reduce( + (acc, address) => { + sources.set('address', [ + ...(sources.get('address') ?? []), + address.source, + ]); + return acc || !isEditableSource(address.source ?? ''); + }, + false, ); - const hasNonEditablePersonData = contactSources.people?.nodes?.some( - (people) => { - const foundNonEditableEmailAddress = people.emailAddresses.nodes.some( - (email) => !isEditableSource(email.source), + const hasNonEditablePersonData = contactSources.people?.nodes?.reduce( + (acc, person) => { + const foundNonEditableEmailAddress = person.emailAddresses.nodes.reduce( + (emailAcc, email) => { + sources.set('email', [ + ...(sources.get('email') ?? []), + email.source, + ]); + return emailAcc || !isEditableSource(email.source); + }, + false, ); - const foundNonEditablePhone = people.phoneNumbers.nodes.some( - (phone) => !isEditableSource(phone.source), + const foundNonEditablePhone = person.phoneNumbers.nodes.reduce( + (phoneAcc, phone) => { + sources.set('phone', [ + ...(sources.get('phone') ?? []), + phone.source, + ]); + return phoneAcc || !isEditableSource(phone.source); + }, + false, ); - return foundNonEditableEmailAddress || foundNonEditablePhone; + return acc || foundNonEditableEmailAddress || foundNonEditablePhone; }, + false, ); + const uniqueSources: DataInfo['uniqueSources'] = { + contact: [], + address: [], + email: [], + phone: [], + ...Object.fromEntries( + Array.from(sources, ([dataType, ArrOfSources]) => [ + dataType, + [...new Set(ArrOfSources)].map((source) => sourceToStr(t, source)), + ]), + ), + }; + const contactIsNotEditable = isContactNonEditable || isAddressNonEditable || hasNonEditablePersonData; - return !contactIsNotEditable; + return { + canDelete: !contactIsNotEditable, + uniqueSources, + }; }, [contactSources]); return ( @@ -84,16 +147,80 @@ export const DeleteContactModal: React.FC = ({ handleClose={() => setOpen(false)} > - - {canDelete && - t( - 'Are you sure you want to permanently delete this contact? Doing so will permanently delete this contacts information, as well as task history. This cannot be undone. If you wish to keep this information, you can try hiding this contact instead.', + {dataInfo.canDelete ? ( + + {t( + `Are you sure you want to permanently delete this contact? Doing so will permanently delete this contacts information, as well as task history. This cannot be undone. If you wish to keep this information, you can try hiding this contact instead.`, )} - {!canDelete && - t( - "This contact cannot be deleted because part or all of the contact's data syncs with Donation Services. Please email Donation Services to request that this contact be deleted, or you can hide this contact instead.", - )} - + + ) : ( + <> + + {t( + `Be cautious when deleting this contact, as its data may sync with Donation Services or other third-party systems. Deleting the contact will not remove it from those systems; consider hiding the contact instead.`, + )} + + + {t( + `For contacts originating from Siebel or Donor Hub, email Donation Services to request deletion.`, + )} + +
+
+ {t('Data sources:')} + + {!!dataInfo.uniqueSources.contact.length && ( + + + + + + + )} + + {!!dataInfo.uniqueSources.address.length && ( + + + + + + + )} + {!!dataInfo.uniqueSources.email.length && ( + + + + + + + )} + {!!dataInfo.uniqueSources.phone.length && ( + + + + + + + )} + + + )}
= ({