From 945772dc6db9c395c4713f0178f26d810ddc7ffd Mon Sep 17 00:00:00 2001 From: Daniel Bisgrove Date: Tue, 25 Jun 2024 10:55:28 -0400 Subject: [PATCH 1/5] Confirm single contact's addresses --- .../Tool/FixMailingAddresses/Contact.tsx | 19 +++++- .../FixMailingAddresses.tsx | 67 +++++++++++++++++++ .../GetInvalidAddresses.graphql | 23 +++++++ 3 files changed, 107 insertions(+), 2 deletions(-) diff --git a/src/components/Tool/FixMailingAddresses/Contact.tsx b/src/components/Tool/FixMailingAddresses/Contact.tsx index adda7cef4..376b5db2b 100644 --- a/src/components/Tool/FixMailingAddresses/Contact.tsx +++ b/src/components/Tool/FixMailingAddresses/Contact.tsx @@ -38,7 +38,7 @@ import { useUpdateCache } from 'src/hooks/useUpdateCache'; import { dateFormatShort } from 'src/lib/intlFormat'; import { contactPartnershipStatus } from 'src/utils/contacts/contactPartnershipStatus'; import theme from '../../../theme'; -import { emptyAddress } from './FixMailingAddresses'; +import { HandleSingleConfirmProps, emptyAddress } from './FixMailingAddresses'; import { ContactAddressFragment } from './GetInvalidAddresses.generated'; const ContactHeader = styled(CardHeader)(() => ({ @@ -117,6 +117,12 @@ interface Props { openEditAddressModal: (address: ContactAddressFragment, id: string) => void; openNewAddressModal: (address: ContactAddressFragment, id: string) => void; setContactFocus: SetContactFocus; + handleSingleConfirm: ({ + addresses, + id, + name, + onlyErrorOnce, + }: HandleSingleConfirmProps) => void; } const Contact: React.FC = ({ @@ -128,6 +134,7 @@ const Contact: React.FC = ({ openEditAddressModal, openNewAddressModal, setContactFocus, + handleSingleConfirm, }) => { const { t } = useTranslation(); const locale = useLocale(); @@ -161,6 +168,10 @@ const Contact: React.FC = ({ }); }; + const handleConfirm = () => { + handleSingleConfirm({ addresses, id, name }); + }; + const handleContactNameClick = () => { setContactFocus(id); }; @@ -176,7 +187,11 @@ const Contact: React.FC = ({ /> } action={ - diff --git a/src/components/Tool/FixMailingAddresses/FixMailingAddresses.tsx b/src/components/Tool/FixMailingAddresses/FixMailingAddresses.tsx index 086bc13ea..4dbb7857d 100644 --- a/src/components/Tool/FixMailingAddresses/FixMailingAddresses.tsx +++ b/src/components/Tool/FixMailingAddresses/FixMailingAddresses.tsx @@ -13,6 +13,7 @@ import { SelectChangeEvent, Typography, } from '@mui/material'; +import { useSnackbar } from 'notistack'; import { Trans, useTranslation } from 'react-i18next'; import { makeStyles } from 'tss-react/mui'; import { SetContactFocus } from 'pages/accountLists/[accountListId]/tools/useToolsHelper'; @@ -26,8 +27,16 @@ import { InvalidAddressesDocument, InvalidAddressesQuery, useInvalidAddressesQuery, + useUpdateContactAddressMutation, } from './GetInvalidAddresses.generated'; +export type HandleSingleConfirmProps = { + addresses: ContactAddressFragment[]; + id: string; + name: string; + onlyErrorOnce?: boolean; +}; + const useStyles = makeStyles()(() => ({ container: { padding: theme.spacing(3), @@ -134,6 +143,63 @@ const FixSendNewsletter: React.FC = ({ const { data, loading } = useInvalidAddressesQuery({ variables: { accountListId }, }); + const [updateAddress] = useUpdateContactAddressMutation(); + const { enqueueSnackbar } = useSnackbar(); + + const handleSingleConfirm = async ({ + addresses, + id, + name, + onlyErrorOnce = false, + }: HandleSingleConfirmProps) => { + const errors: string[] = []; + + for (let idx = 0; idx < addresses.length; idx++) { + const address = addresses[idx]; + + await updateAddress({ + variables: { + accountListId, + attributes: { + id: address.id, + validValues: true, + // TODO: Fix the Graph QL Input + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + primaryMailingAddress: address.primaryMailingAddress, + }, + }, + update(cache) { + if (idx === addresses.length - 1 && !errors.length) { + cache.evict({ id: `Contact:${id}` }); + } + }, + onError(error) { + errors.push( + `${name} - ${t('Error while saving addresses.')} ${error.cause}`, + ); + }, + }); + } + + if (errors.length) { + if (onlyErrorOnce) { + enqueueSnackbar(t(`Error updating contact ${name}`), { + variant: 'error', + autoHideDuration: 7000, + }); + } else { + errors.forEach((error) => { + enqueueSnackbar(error, { variant: 'error' }); + }); + } + return { success: false }; + } else { + enqueueSnackbar(t(`Updated contact ${name}`), { variant: 'success' }); + return { success: true }; + } + }; + const handleUpdateCacheForDeleteAddress = useCallback( (cache: ApolloCache, data) => { @@ -303,6 +369,7 @@ const FixSendNewsletter: React.FC = ({ handleModalOpen(ModalEnum.New, address, contactId) } setContactFocus={setContactFocus} + handleSingleConfirm={handleSingleConfirm} /> ))} diff --git a/src/components/Tool/FixMailingAddresses/GetInvalidAddresses.graphql b/src/components/Tool/FixMailingAddresses/GetInvalidAddresses.graphql index 93d962087..5e11bb766 100644 --- a/src/components/Tool/FixMailingAddresses/GetInvalidAddresses.graphql +++ b/src/components/Tool/FixMailingAddresses/GetInvalidAddresses.graphql @@ -32,3 +32,26 @@ fragment ContactAddress on Address { createdAt historic } + +mutation UpdateContactAddress( + $accountListId: ID! + $attributes: AddressUpdateInput! +) { + updateAddress( + input: { accountListId: $accountListId, attributes: $attributes } + ) { + address { + city + country + historic + id + location + metroArea + postalCode + primaryMailingAddress + region + state + street + } + } +} From 9ea79bffe7ad6842b5b46d06ad56d66db393b84c Mon Sep 17 00:00:00 2001 From: Daniel Bisgrove Date: Tue, 25 Jun 2024 10:55:52 -0400 Subject: [PATCH 2/5] Confirm all contact's addresses --- .../FixMailingAddresses.tsx | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/src/components/Tool/FixMailingAddresses/FixMailingAddresses.tsx b/src/components/Tool/FixMailingAddresses/FixMailingAddresses.tsx index 4dbb7857d..0c5cf5e67 100644 --- a/src/components/Tool/FixMailingAddresses/FixMailingAddresses.tsx +++ b/src/components/Tool/FixMailingAddresses/FixMailingAddresses.tsx @@ -19,6 +19,7 @@ import { makeStyles } from 'tss-react/mui'; import { SetContactFocus } from 'pages/accountLists/[accountListId]/tools/useToolsHelper'; import { DynamicAddAddressModal } from 'src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/AddAddressModal/DynamicAddAddressModal'; import { DynamicEditContactAddressModal } from 'src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/DynamicEditContactAddressModal'; +import { Confirmation } from 'src/components/common/Modal/Confirmation/Confirmation'; import theme from '../../../theme'; import NoData from '../NoData'; import Contact from './Contact'; @@ -140,6 +141,8 @@ const FixSendNewsletter: React.FC = ({ const [selectedAddress, setSelectedAddress] = useState(emptyAddress); const [selectedContactId, setSelectedContactId] = useState(''); const [defaultSource, setDefaultSource] = useState(appName); + const [openBulkConfirmModal, setOpenBulkConfirmModal] = useState(false); + const { data, loading } = useInvalidAddressesQuery({ variables: { accountListId }, }); @@ -200,6 +203,62 @@ const FixSendNewsletter: React.FC = ({ } }; + const handleBulkConfirm = async () => { + try { + const callsByContact: (() => Promise<{ success: boolean }>)[] = []; + data?.contacts?.nodes.forEach((contact) => { + const primaryAddress = contact.addresses.nodes.find( + (address) => + address.source === defaultSource || + (defaultSource === appName && address.source === 'MPDX'), + ); + if (primaryAddress) { + const addresses: ContactAddressFragment[] = []; + contact.addresses.nodes.forEach((address) => { + addresses.push({ + ...address, + primaryMailingAddress: address.id === primaryAddress?.id, + }); + }); + const callContactMutation = () => + handleSingleConfirm({ + addresses, + id: contact.id, + name: contact.name, + onlyErrorOnce: true, + }); + callsByContact.push(callContactMutation); + } + }); + + if (callsByContact.length) { + const results = await Promise.all(callsByContact.map((call) => call())); + + const failedUpdates = results.filter( + (result) => !result.success, + ).length; + const successfulUpdates = results.length - failedUpdates; + + if (successfulUpdates) { + enqueueSnackbar(t(`Updated ${successfulUpdates} contact(s)`), { + variant: 'success', + }); + } + if (failedUpdates) { + enqueueSnackbar( + t(`Error when updating ${failedUpdates} contact(s)`), + { + variant: 'error', + }, + ); + } + } else { + enqueueSnackbar(t(`No contacts were updated`), { variant: 'warning' }); + } + } catch (error) { + enqueueSnackbar(t(`Error updating contacts`), { variant: 'error' }); + } + }; const handleUpdateCacheForDeleteAddress = useCallback( (cache: ApolloCache, data) => { @@ -279,6 +338,10 @@ const FixSendNewsletter: React.FC = ({ setDefaultSource(event.target.value); }; + const handleBulkConfirmModalClose = () => { + setOpenBulkConfirmModal(false); + }; + const totalContacts = data?.contacts?.nodes?.length || 0; return ( @@ -339,6 +402,7 @@ const FixSendNewsletter: React.FC = ({