From 5b2d3497c16b12de763a67eb074742166621b4dd Mon Sep 17 00:00:00 2001 From: Daniel Bisgrove Date: Wed, 12 Jun 2024 11:32:50 -0400 Subject: [PATCH 01/18] Adding the ability to allow custom cache updates after mutation. --- .../AddAddressModal/AddAddressModal.tsx | 51 +++++++++++-------- .../EditContactAddressModal.tsx | 15 ++++-- 2 files changed, 40 insertions(+), 26 deletions(-) diff --git a/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/AddAddressModal/AddAddressModal.tsx b/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/AddAddressModal/AddAddressModal.tsx index 4d122291e..9e71b9d79 100644 --- a/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/AddAddressModal/AddAddressModal.tsx +++ b/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/AddAddressModal/AddAddressModal.tsx @@ -1,4 +1,5 @@ import React, { ReactElement } from 'react'; +import { ApolloCache } from '@apollo/client'; import { Box, Checkbox, @@ -58,12 +59,14 @@ interface EditContactAddressModalProps { accountListId: string; contactId: string; handleClose: () => void; + handleUpdateCache?: (cache: ApolloCache, object) => void; } export const AddAddressModal: React.FC = ({ accountListId, contactId, handleClose, + handleUpdateCache, }): ReactElement => { const { t } = useTranslation(); const { enqueueSnackbar } = useSnackbar(); @@ -85,30 +88,34 @@ export const AddAddressModal: React.FC = ({ attributes, }, update: (cache, { data: createdAddressData }) => { - const query = { - query: ContactDetailsTabDocument, - variables: { - accountListId, - contactId, - }, - }; - const dataFromCache = cache.readQuery(query); - - if (dataFromCache) { - const data = { - ...dataFromCache, - contact: { - ...dataFromCache.contact, - addresses: { - ...dataFromCache.contact.addresses, - nodes: [ - ...dataFromCache.contact.addresses.nodes, - { ...createdAddressData?.createAddress?.address }, - ], - }, + if (handleUpdateCache) { + handleUpdateCache(cache, createdAddressData); + } else { + const query = { + query: ContactDetailsTabDocument, + variables: { + accountListId, + contactId, }, }; - cache.writeQuery({ ...query, data }); + const dataFromCache = cache.readQuery(query); + + if (dataFromCache) { + const data = { + ...dataFromCache, + contact: { + ...dataFromCache.contact, + addresses: { + ...dataFromCache.contact.addresses, + nodes: [ + ...dataFromCache.contact.addresses.nodes, + { ...createdAddressData?.createAddress?.address }, + ], + }, + }, + }; + cache.writeQuery({ ...query, data }); + } } }, }); diff --git a/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/EditContactAddressModal.tsx b/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/EditContactAddressModal.tsx index 9b6af14a0..8f9925d99 100644 --- a/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/EditContactAddressModal.tsx +++ b/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/EditContactAddressModal.tsx @@ -1,4 +1,5 @@ import React, { ReactElement } from 'react'; +import { ApolloCache } from '@apollo/client'; import { Alert, AlertTitle, @@ -68,7 +69,7 @@ interface EditContactAddressModalProps { accountListId: string; address: ContactMailingFragment['addresses']['nodes'][0]; contactId: string; - handleClose: (deletedAddress: boolean) => void; + handleUpdateCacheOnDelete?: (cache: ApolloCache, object) => void; } export const EditContactAddressModal: React.FC< @@ -78,6 +79,7 @@ export const EditContactAddressModal: React.FC< address, contactId, handleClose, + handleUpdateCacheOnDelete, }): ReactElement => { const { t } = useTranslation(); const { enqueueSnackbar } = useSnackbar(); @@ -125,8 +127,11 @@ export const EditContactAddressModal: React.FC< id: address.id, accountListId, }, - update: (cache, { data: deletedContactAddress }) => { - const deletedAddressId = deletedContactAddress?.deleteAddress?.id; + update: (cache) => { + const deletedAddressId = address.id; + if (handleUpdateCacheOnDelete) { + handleUpdateCacheOnDelete(cache, { deletedAddressId }); + } else { const query = { query: ContactDetailsTabDocument, variables: { @@ -135,7 +140,8 @@ export const EditContactAddressModal: React.FC< }, }; - const dataFromCache = cache.readQuery(query); + const dataFromCache = + cache.readQuery(query); if (dataFromCache) { const data = { @@ -151,6 +157,7 @@ export const EditContactAddressModal: React.FC< }, }; cache.writeQuery({ ...query, data }); + } } enqueueSnackbar(t('Address deleted successfully'), { variant: 'success', From 3e30b78d51978b9f8fbc83dd59bf8928c3896b89 Mon Sep 17 00:00:00 2001 From: Daniel Bisgrove Date: Wed, 12 Jun 2024 11:40:31 -0400 Subject: [PATCH 02/18] Refactor state props and how we handle Modal open and close --- .../FixMailingAddresses.tsx | 58 +++++++++++++------ 1 file changed, 40 insertions(+), 18 deletions(-) diff --git a/src/components/Tool/FixMailingAddresses/FixMailingAddresses.tsx b/src/components/Tool/FixMailingAddresses/FixMailingAddresses.tsx index 841b655b2..3a0655099 100644 --- a/src/components/Tool/FixMailingAddresses/FixMailingAddresses.tsx +++ b/src/components/Tool/FixMailingAddresses/FixMailingAddresses.tsx @@ -14,6 +14,7 @@ import { } from '@mui/material'; import { Trans, useTranslation } from 'react-i18next'; import { makeStyles } from 'tss-react/mui'; +import { AddAddressModal } from 'src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/AddAddressModal/AddAddressModal'; import { EditContactAddressModal } from 'src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/EditContactAddressModal'; import theme from '../../../theme'; import NoData from '../NoData'; @@ -104,34 +105,44 @@ export const emptyAddress: ContactAddressFragment = { interface Props { accountListId: string; } +enum ModalEnum { + New = 'New', + Edit = 'Edit', +} const sourceOptions = ['MPDX', 'DataServer']; const FixSendNewsletter: React.FC = ({ accountListId }: Props) => { const { classes } = useStyles(); - const [modalState, setModalState] = useState({ - open: false, - address: emptyAddress, - contactId: '', - }); const { t } = useTranslation(); + const [showEditAddressModal, setShowEditAddressModal] = useState(false); + const [showNewAddressModal, setShowNewAddressModal] = useState(false); + const [selectedAddress, setSelectedAddress] = useState(emptyAddress); + const [selectedContactId, setSelectedContactId] = useState(''); const [defaultSource, setDefaultSource] = useState('MPDX'); - const { data, loading, refetch } = useInvalidAddressesQuery({ + const { data, loading } = useInvalidAddressesQuery({ variables: { accountListId }, }); - const handleOpen = ( + const handleModalOpen = ( + modal: ModalEnum, address: ContactAddressFragment, contactId: string, ): void => { - setModalState({ open: true, address, contactId }); + if (modal === ModalEnum.Edit) { + setShowEditAddressModal(true); + } else { + setShowNewAddressModal(true); + } + setSelectedAddress(address); + setSelectedContactId(contactId); }; - const handleClose = (deleteAddress: boolean): void => { - if (deleteAddress) { - refetch(); - } - setModalState({ open: false, address: emptyAddress, contactId: '' }); + const handleClose = (): void => { + setShowEditAddressModal(false); + setShowNewAddressModal(false); + setSelectedAddress(emptyAddress); + setSelectedContactId(''); }; const handleSourceChange = (event: SelectChangeEvent): void => { @@ -215,8 +226,12 @@ const FixSendNewsletter: React.FC = ({ accountListId }: Props) => { status={contact.status || ''} key={contact.id} addresses={contact.addresses.nodes} - openEditAddressModal={handleOpen} - openNewAddressModal={handleOpen} + openEditAddressModal={(address, contactId) => + handleModalOpen(ModalEnum.Edit, address, contactId) + } + openNewAddressModal={(address, contactId) => + handleModalOpen(ModalEnum.New, address, contactId) + } /> ))} @@ -237,11 +252,18 @@ const FixSendNewsletter: React.FC = ({ accountListId }: Props) => { )} - {modalState.open && ( + {showEditAddressModal && ( + )} + {showNewAddressModal && ( + )} From fd46a8394fe9e18c59738e9d6ae401d46c2687b5 Mon Sep 17 00:00:00 2001 From: Daniel Bisgrove Date: Wed, 12 Jun 2024 11:41:10 -0400 Subject: [PATCH 03/18] Adding custom cache updates to modals --- .../FixMailingAddresses.tsx | 44 ++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/src/components/Tool/FixMailingAddresses/FixMailingAddresses.tsx b/src/components/Tool/FixMailingAddresses/FixMailingAddresses.tsx index 3a0655099..4d3848df5 100644 --- a/src/components/Tool/FixMailingAddresses/FixMailingAddresses.tsx +++ b/src/components/Tool/FixMailingAddresses/FixMailingAddresses.tsx @@ -1,4 +1,5 @@ -import React, { useState } from 'react'; +import React, { useCallback, useState } from 'react'; +import { ApolloCache } from '@apollo/client'; import { mdiCheckboxMarkedCircle } from '@mdi/js'; import { Icon } from '@mdi/react'; import { @@ -21,6 +22,8 @@ import NoData from '../NoData'; import Contact from './Contact'; import { ContactAddressFragment, + InvalidAddressesDocument, + InvalidAddressesQuery, useInvalidAddressesQuery, } from './GetInvalidAddresses.generated'; @@ -124,6 +127,43 @@ const FixSendNewsletter: React.FC = ({ accountListId }: Props) => { variables: { accountListId }, }); + const handleUpdateCacheForDeleteAddress = useCallback( + (cache: ApolloCache, data) => { + cache.evict({ + id: `Address:${data.deletedAddressId}`, + }); + cache.gc(); + }, + [], + ); + + const handleUpdateCacheForAddAddress = useCallback( + (cache: ApolloCache, { data: createdAddressData }) => { + const InvalidAddressesQuery = { + query: InvalidAddressesDocument, + variables: { + accountListId, + }, + }; + const dataFromInvalidAddressesCache = + cache.readQuery(InvalidAddressesQuery); + + if (dataFromInvalidAddressesCache) { + const data = { + ...dataFromInvalidAddressesCache, + contacts: { + nodes: [ + ...dataFromInvalidAddressesCache.contacts.nodes, + { ...createdAddressData?.createAddress?.address }, + ], + }, + }; + cache.writeQuery({ ...InvalidAddressesQuery, data }); + } + }, + [], + ); + const handleModalOpen = ( modal: ModalEnum, address: ContactAddressFragment, @@ -258,6 +298,7 @@ const FixSendNewsletter: React.FC = ({ accountListId }: Props) => { address={selectedAddress} contactId={selectedContactId} handleClose={handleClose} + handleUpdateCacheOnDelete={handleUpdateCacheForDeleteAddress} /> )} {showNewAddressModal && ( @@ -265,6 +306,7 @@ const FixSendNewsletter: React.FC = ({ accountListId }: Props) => { accountListId={accountListId} contactId={selectedContactId} handleClose={handleClose} + handleUpdateCache={handleUpdateCacheForAddAddress} /> )} From 9343e8d9800006c97c46686831abf3b27db07716 Mon Sep 17 00:00:00 2001 From: Daniel Bisgrove Date: Wed, 12 Jun 2024 11:42:10 -0400 Subject: [PATCH 04/18] No longer need to return a boolean for handleClose as custom cache function will update cache and we do not need to refetch data. --- .../EditContactAddressModal.tsx | 56 +++++++++---------- 1 file changed, 25 insertions(+), 31 deletions(-) diff --git a/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/EditContactAddressModal.tsx b/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/EditContactAddressModal.tsx index 8f9925d99..fe589065a 100644 --- a/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/EditContactAddressModal.tsx +++ b/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/EditContactAddressModal.tsx @@ -69,6 +69,7 @@ interface EditContactAddressModalProps { accountListId: string; address: ContactMailingFragment['addresses']['nodes'][0]; contactId: string; + handleClose: () => void; handleUpdateCacheOnDelete?: (cache: ApolloCache, object) => void; } @@ -117,7 +118,7 @@ export const EditContactAddressModal: React.FC< enqueueSnackbar(t('Address updated successfully'), { variant: 'success', }); - handleClose(false); + handleClose(); }; const deleteContactAddress = async (): Promise => { @@ -132,31 +133,31 @@ export const EditContactAddressModal: React.FC< if (handleUpdateCacheOnDelete) { handleUpdateCacheOnDelete(cache, { deletedAddressId }); } else { - const query = { - query: ContactDetailsTabDocument, - variables: { - accountListId, - contactId, - }, - }; + const query = { + query: ContactDetailsTabDocument, + variables: { + accountListId, + contactId, + }, + }; const dataFromCache = cache.readQuery(query); - if (dataFromCache) { - const data = { - ...dataFromCache, - contact: { - ...dataFromCache.contact, - addresses: { - ...dataFromCache.contact.addresses, - nodes: dataFromCache.contact.addresses.nodes.filter( - (address) => address.id !== deletedAddressId, - ), + if (dataFromCache) { + const data = { + ...dataFromCache, + contact: { + ...dataFromCache.contact, + addresses: { + ...dataFromCache.contact.addresses, + nodes: dataFromCache.contact.addresses.nodes.filter( + (address) => address.id !== deletedAddressId, + ), + }, }, - }, - }; - cache.writeQuery({ ...query, data }); + }; + cache.writeQuery({ ...query, data }); } } enqueueSnackbar(t('Address deleted successfully'), { @@ -165,7 +166,7 @@ export const EditContactAddressModal: React.FC< }, }); } - handleClose(true); + handleClose(); }; const editingDisabled = @@ -179,11 +180,7 @@ export const EditContactAddressModal: React.FC< }); return ( - handleClose(false)} - > + )} - handleClose(false)} - disabled={isSubmitting} - /> + {(updating || deleting || settingPrimaryAddress) && ( From 1fab9fec22c07a47eb9d2214acb44dc557d4d762 Mon Sep 17 00:00:00 2001 From: Daniel Bisgrove Date: Wed, 12 Jun 2024 11:42:49 -0400 Subject: [PATCH 05/18] Replacing add button with new add button. --- .../Tool/FixMailingAddresses/Contact.tsx | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/components/Tool/FixMailingAddresses/Contact.tsx b/src/components/Tool/FixMailingAddresses/Contact.tsx index 8a0bd1eda..22dac10a2 100644 --- a/src/components/Tool/FixMailingAddresses/Contact.tsx +++ b/src/components/Tool/FixMailingAddresses/Contact.tsx @@ -1,6 +1,6 @@ import React, { Fragment } from 'react'; import styled from '@emotion/styled'; -import { mdiCheckboxMarkedCircle, mdiPlus } from '@mdi/js'; +import { mdiCheckboxMarkedCircle } from '@mdi/js'; import { Icon } from '@mdi/react'; import StarIcon from '@mui/icons-material/Star'; import StarOutlineIcon from '@mui/icons-material/StarOutline'; @@ -21,6 +21,9 @@ import { DateTime } from 'luxon'; import { useTranslation } from 'react-i18next'; import { makeStyles } from 'tss-react/mui'; import { + AddButton, + AddIcon, + AddText, EditIcon, LockIcon, } from 'src/components/Contacts/ContactDetails/ContactDetailsTab/StyledComponents'; @@ -52,6 +55,9 @@ const useStyles = makeStyles()(() => ({ confirmButon: { marginRight: theme.spacing(1), }, + AddButton: { + width: '100%', + }, contactCard: { marginBottom: theme.spacing(2), }, @@ -240,11 +246,13 @@ const Contact: React.FC = ({ classes.hoverHighlight, )} > - openNewAddressModal(newAddress, id)} - className={classes.address} - /> - + > + + {t('Add Address')} + From 32956f7ebaf96a6ba1100861c5b14c602c927db0 Mon Sep 17 00:00:00 2001 From: Daniel Bisgrove Date: Wed, 12 Jun 2024 11:44:06 -0400 Subject: [PATCH 06/18] Fix key error causing console errors --- src/components/Tool/FixMailingAddresses/Contact.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Tool/FixMailingAddresses/Contact.tsx b/src/components/Tool/FixMailingAddresses/Contact.tsx index 22dac10a2..04a836c9d 100644 --- a/src/components/Tool/FixMailingAddresses/Contact.tsx +++ b/src/components/Tool/FixMailingAddresses/Contact.tsx @@ -162,7 +162,7 @@ const Contact: React.FC = ({ {addresses.map((address) => ( - + Date: Thu, 13 Jun 2024 14:17:16 -0400 Subject: [PATCH 07/18] Fixing error with updating cache --- .../AddAddressModal/AddAddressModal.tsx | 7 +++++- .../FixMailingAddresses.tsx | 25 +++++++++++++++---- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/AddAddressModal/AddAddressModal.tsx b/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/AddAddressModal/AddAddressModal.tsx index 9e71b9d79..d4fdc61a6 100644 --- a/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/AddAddressModal/AddAddressModal.tsx +++ b/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/AddAddressModal/AddAddressModal.tsx @@ -89,7 +89,12 @@ export const AddAddressModal: React.FC = ({ }, update: (cache, { data: createdAddressData }) => { if (handleUpdateCache) { - handleUpdateCache(cache, createdAddressData); + handleUpdateCache(cache, { + createAddress: { + address: createdAddressData?.createAddress?.address, + contactId: attributes.contactId, + }, + }); } else { const query = { query: ContactDetailsTabDocument, diff --git a/src/components/Tool/FixMailingAddresses/FixMailingAddresses.tsx b/src/components/Tool/FixMailingAddresses/FixMailingAddresses.tsx index 4d3848df5..72e2a43eb 100644 --- a/src/components/Tool/FixMailingAddresses/FixMailingAddresses.tsx +++ b/src/components/Tool/FixMailingAddresses/FixMailingAddresses.tsx @@ -138,7 +138,7 @@ const FixSendNewsletter: React.FC = ({ accountListId }: Props) => { ); const handleUpdateCacheForAddAddress = useCallback( - (cache: ApolloCache, { data: createdAddressData }) => { + (cache: ApolloCache, createdAddressData) => { const InvalidAddressesQuery = { query: InvalidAddressesDocument, variables: { @@ -149,13 +149,28 @@ const FixSendNewsletter: React.FC = ({ accountListId }: Props) => { cache.readQuery(InvalidAddressesQuery); if (dataFromInvalidAddressesCache) { + const newContacts = dataFromInvalidAddressesCache.contacts.nodes.map( + (contact) => { + if (contact.id !== createdAddressData.createAddress.contactId) { + return contact; + } else { + return { + ...contact, + addresses: { + nodes: [ + ...contact.addresses.nodes, + createdAddressData.createAddress.address, + ], + }, + }; + } + }, + ); + const data = { ...dataFromInvalidAddressesCache, contacts: { - nodes: [ - ...dataFromInvalidAddressesCache.contacts.nodes, - { ...createdAddressData?.createAddress?.address }, - ], + nodes: newContacts, }, }; cache.writeQuery({ ...InvalidAddressesQuery, data }); From b9bcd545ab20bf6e9c2e5a12b574ef61c23ebc5e Mon Sep 17 00:00:00 2001 From: Daniel Bisgrove Date: Thu, 13 Jun 2024 14:18:10 -0400 Subject: [PATCH 08/18] Adding tests for Edi and add mailing address modals and functionality. --- .../Tool/FixMailingAddresses/Contact.tsx | 2 + .../FixMailingAddresses.test.tsx | 430 ++++++++++++++++++ .../FixMailingAddresses.tsx | 5 +- .../FixMailingAddressesMock.ts | 54 +++ 4 files changed, 490 insertions(+), 1 deletion(-) create mode 100644 src/components/Tool/FixMailingAddresses/FixMailingAddresses.test.tsx create mode 100644 src/components/Tool/FixMailingAddresses/FixMailingAddressesMock.ts diff --git a/src/components/Tool/FixMailingAddresses/Contact.tsx b/src/components/Tool/FixMailingAddresses/Contact.tsx index 04a836c9d..cb7e4175d 100644 --- a/src/components/Tool/FixMailingAddresses/Contact.tsx +++ b/src/components/Tool/FixMailingAddresses/Contact.tsx @@ -203,6 +203,7 @@ const Contact: React.FC = ({ classes.paddingL2, classes.hoverHighlight, )} + data-testid={`address-${address.id}`} onClick={() => openEditAddressModal(address, id)} > @@ -248,6 +249,7 @@ const Contact: React.FC = ({ > openNewAddressModal(newAddress, id)} > diff --git a/src/components/Tool/FixMailingAddresses/FixMailingAddresses.test.tsx b/src/components/Tool/FixMailingAddresses/FixMailingAddresses.test.tsx new file mode 100644 index 000000000..0767fa3cc --- /dev/null +++ b/src/components/Tool/FixMailingAddresses/FixMailingAddresses.test.tsx @@ -0,0 +1,430 @@ +import { ApolloCache, InMemoryCache } from '@apollo/client'; +import { ThemeProvider } from '@mui/material/styles'; +import { render, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { ApolloErgonoMockMap } from 'graphql-ergonomock'; +import { SnackbarProvider } from 'notistack'; +import TestRouter from '__tests__/util/TestRouter'; +import { GqlMockedProvider } from '__tests__/util/graphqlMocking'; +import { CreateContactAddressMutation } from 'src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/AddAddressModal/CreateContactAddress.generated'; +import theme from 'src/theme'; +import FixSendNewsletter from './FixMailingAddresses'; +import { + mpdxSourcedAddress, + tntSourcedAddress, +} from './FixMailingAddressesMock'; +import { InvalidAddressesQuery } from './GetInvalidAddresses.generated'; + +jest.mock('next-auth/react'); + +const accountListId = 'account-list-1'; +const contactId = 'd4db82ad-ff6f-4ba3-96ee-c143efc07d7c'; +const router = { + isReady: true, +}; + +const mockEnqueue = jest.fn(); +jest.mock('notistack', () => ({ + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + ...jest.requireActual('notistack'), + useSnackbar: () => { + return { + enqueueSnackbar: mockEnqueue, + }; + }, +})); + +const Components = ({ + mocks, + cache, +}: { + mocks: ApolloErgonoMockMap; + cache?: ApolloCache; +}) => ( + + + + + mocks={mocks} + cache={cache} + > + + + + + +); + +describe('FixSendNewsletter', () => { + it('should show noData component', async () => { + const { queryByTestId, getByText } = render( + , + ); + await waitFor(() => + expect(queryByTestId('loading')).not.toBeInTheDocument(), + ); + expect( + getByText('No contacts with mailing addresses need attention'), + ).toBeInTheDocument(); + }); + + it('should not show noData', async () => { + const { queryByTestId, queryByText } = render( + , + ); + await waitFor(() => + expect(queryByTestId('loading')).not.toBeInTheDocument(), + ); + expect( + queryByText('No contacts with mailing addresses need attention'), + ).not.toBeInTheDocument(); + }); + + it('should count total contacts', async () => { + const { queryByTestId, getByText } = render( + , + ); + await waitFor(() => + expect(queryByTestId('loading')).not.toBeInTheDocument(), + ); + expect( + getByText('You have 3 mailing addresses to confirm.'), + ).toBeInTheDocument(); + }); + + describe('Editing an address', () => { + it('should edit Address via EditContactAddressModal', async () => { + const cache = new InMemoryCache(); + jest.spyOn(cache, 'writeFragment'); + const { queryByTestId, getByText, getByRole, findByRole } = render( + , + ); + await waitFor(() => + expect(queryByTestId('loading')).not.toBeInTheDocument(), + ); + + userEvent.click(getByText('100 Lake Hart Drive, Orlando FL. 32832')); + + await waitFor(() => { + expect(getByText('Edit Address')).toBeInTheDocument(); + }); + + const streetInput = getByRole('combobox', { name: 'Street' }); + const cityInput = getByRole('textbox', { name: 'City' }); + const zipInput = getByRole('textbox', { name: 'Zip' }); + const countryInput = getByRole('textbox', { name: 'Country' }); + + userEvent.clear(streetInput); + userEvent.type(streetInput, 'Buckingham Palace'); + userEvent.clear(cityInput); + userEvent.type(cityInput, 'London'); + userEvent.clear(zipInput); + userEvent.type(zipInput, 'SW1A 1AA'); + userEvent.clear(countryInput); + userEvent.type(countryInput, 'United Kingdom'); + + userEvent.click(getByRole('combobox', { name: 'Location' })); + userEvent.click(await findByRole('option', { name: 'Business' })); + + expect(getByRole('button', { name: 'Save' })).not.toBeDisabled(); + userEvent.click(getByRole('button', { name: 'Save' })); + + await waitFor(() => + expect(mockEnqueue).toHaveBeenCalledWith( + 'Address updated successfully', + { + variant: 'success', + }, + ), + ); + }); + + it('should delete address via EditContactAddressModal', async () => { + const cache = new InMemoryCache(); + jest.spyOn(cache, 'writeFragment'); + const { queryByTestId, getByText, getByRole, queryByText } = render( + , + ); + await waitFor(() => + expect(queryByTestId('loading')).not.toBeInTheDocument(), + ); + + userEvent.click(getByText('100 Lake Hart Drive, Orlando FL. 32832')); + + await waitFor(() => { + expect(getByText('Edit Address')).toBeInTheDocument(); + }); + + expect(getByRole('button', { name: 'Delete' })).not.toBeDisabled(); + userEvent.click(getByRole('button', { name: 'Delete' })); + + await waitFor(() => + expect(mockEnqueue).toHaveBeenCalledWith( + 'Address deleted successfully', + { + variant: 'success', + }, + ), + ); + + await waitFor(() => + expect( + queryByText('100 Lake Hart Drive, Orlando FL. 32832'), + ).not.toBeInTheDocument(), + ); + }); + + it("should not allow deletion of address when source isn't MPDX", async () => { + const cache = new InMemoryCache(); + jest.spyOn(cache, 'writeFragment'); + const { getByTestId, getByText, getByRole, queryByTestId, queryByRole } = + render( + , + ); + await waitFor(() => + expect(queryByTestId('loading')).not.toBeInTheDocument(), + ); + + userEvent.click(getByTestId(`address-${tntSourcedAddress.id}`)); + + await waitFor(() => { + expect(getByText('Edit Address')).toBeInTheDocument(); + }); + + expect(queryByRole('button', { name: 'Delete' })).not.toBeInTheDocument(); + + const streetInput = getByRole('combobox', { name: 'Street' }); + const cityInput = getByRole('textbox', { name: 'City' }); + const zipInput = getByRole('textbox', { name: 'Zip' }); + const countryInput = getByRole('textbox', { name: 'Country' }); + const locationSelect = getByRole('combobox', { name: 'Location' }); + + expect(streetInput).toBeDisabled(); + expect(cityInput).toBeDisabled(); + expect(zipInput).toBeDisabled(); + expect(countryInput).toBeDisabled(); + + expect(locationSelect).not.toBeDisabled(); + expect(getByRole('button', { name: 'Save' })).not.toBeDisabled(); + }); + }); + + describe('Added an address', () => { + it('should add address via AddAddressModal and update cache', async () => { + const { + getByTestId, + getByText, + getByRole, + findByRole, + queryByTestId, + queryByText, + } = render( + , + ); + await waitFor(() => + expect(queryByTestId('loading')).not.toBeInTheDocument(), + ); + + await waitFor(() => + expect( + queryByText('Buckingham Palace, College Park England. SW1A 1AA'), + ).not.toBeInTheDocument(), + ); + + userEvent.click(getByTestId(`addAddress-${contactId}`)); + + await waitFor(() => { + expect( + getByRole('heading', { name: 'Add Address' }), + ).toBeInTheDocument(); + }); + + await waitFor(() => { + expect(getByRole('button', { name: 'Save' })).toBeDisabled(); + }); + + const streetInput = getByRole('combobox', { name: 'Street' }); + const cityInput = getByRole('textbox', { name: 'City' }); + const zipInput = getByRole('textbox', { name: 'Zip' }); + const countryInput = getByRole('textbox', { name: 'Country' }); + const locationSelect = getByRole('combobox', { name: 'Location' }); + + userEvent.clear(streetInput); + userEvent.type(streetInput, 'Buckingham Palace'); + userEvent.clear(cityInput); + userEvent.type(cityInput, 'London'); + userEvent.clear(zipInput); + userEvent.type(zipInput, 'SW1A 1AA'); + userEvent.clear(countryInput); + userEvent.type(countryInput, 'United Kingdom'); + + userEvent.click(locationSelect); + userEvent.click(await findByRole('option', { name: 'Business' })); + + await waitFor(() => { + expect(getByRole('button', { name: 'Save' })).not.toBeDisabled(); + }); + userEvent.click(getByRole('button', { name: 'Save' })); + + await waitFor(() => + expect(mockEnqueue).toHaveBeenCalledWith('Address added successfully', { + variant: 'success', + }), + ); + + await waitFor(() => + expect( + getByText('Buckingham Palace, London . SW1A 1AA'), + ).toBeInTheDocument(), + ); + }); + }); +}); diff --git a/src/components/Tool/FixMailingAddresses/FixMailingAddresses.tsx b/src/components/Tool/FixMailingAddresses/FixMailingAddresses.tsx index 72e2a43eb..75c1f2587 100644 --- a/src/components/Tool/FixMailingAddresses/FixMailingAddresses.tsx +++ b/src/components/Tool/FixMailingAddresses/FixMailingAddresses.tsx @@ -217,7 +217,10 @@ const FixSendNewsletter: React.FC = ({ accountListId }: Props) => { {loading && !data && ( - + )} {!loading && data && ( diff --git a/src/components/Tool/FixMailingAddresses/FixMailingAddressesMock.ts b/src/components/Tool/FixMailingAddresses/FixMailingAddressesMock.ts new file mode 100644 index 000000000..a1127c1b5 --- /dev/null +++ b/src/components/Tool/FixMailingAddresses/FixMailingAddressesMock.ts @@ -0,0 +1,54 @@ +export const mpdxSourcedAddress = { + __typename: 'Address', + id: '024319b3-cccf-4000-8928-43abec734d58', + street: '100 Lake Hart Drive', + city: 'Orlando', + state: 'FL', + region: 'Orlando', + metroArea: '', + country: 'United States', + postalCode: '32832', + primaryMailingAddress: true, + source: 'MPDX', + location: 'Home', + createdAt: '2024-06-12T13:07:40-04:00', + historic: false, +}; + +export const tntSourcedAddress = { + id: '2454d81a-7985-460d-ab1e-9daa776c348a', + street: '1001 Denman St', + city: 'Vancouver', + state: 'BC', + region: null, + metroArea: null, + country: null, + postalCode: 'V6G 2M4', + primaryMailingAddress: false, + source: 'DataServer', + location: null, + createdAt: '2022-01-10T16:05:26-05:00', + historic: false, +}; + +export const mockInvalidAddressesResponse = { + contacts: { + nodes: [ + { + id: 'd4db82ad-ff6f-4ba3-96ee-c143efc07d7c', + name: 'Baggins, Frodo', + status: null, + addresses: { + nodes: [ + mpdxSourcedAddress, + tntSourcedAddress, + { + ...tntSourcedAddress, + country: 'Canada', + }, + ], + }, + }, + ], + }, +}; From 69c23866328eb3281e5ad8884bb80650e5d99db2 Mon Sep 17 00:00:00 2001 From: Daniel Bisgrove Date: Thu, 13 Jun 2024 16:53:38 -0400 Subject: [PATCH 09/18] Adding Dynamic components --- .../DynamicAddAddressModal.tsx | 10 + .../DynamicEditContactAddressModal.tsx | 10 + .../FixMailingAddresses.tsx | 200 +++++++++--------- 3 files changed, 121 insertions(+), 99 deletions(-) create mode 100644 src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/AddAddressModal/DynamicAddAddressModal.tsx create mode 100644 src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/DynamicEditContactAddressModal.tsx diff --git a/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/AddAddressModal/DynamicAddAddressModal.tsx b/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/AddAddressModal/DynamicAddAddressModal.tsx new file mode 100644 index 000000000..1d65a72ae --- /dev/null +++ b/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/AddAddressModal/DynamicAddAddressModal.tsx @@ -0,0 +1,10 @@ +import dynamic from 'next/dynamic'; +import { DynamicComponentPlaceholder } from 'src/components/DynamicPlaceholders/DynamicComponentPlaceholder'; + +export const DynamicAddAddressModal = dynamic( + () => + import(/* webpackChunkName: "AddAddressModal" */ './AddAddressModal').then( + ({ AddAddressModal }) => AddAddressModal, + ), + { loading: DynamicComponentPlaceholder }, +); diff --git a/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/DynamicEditContactAddressModal.tsx b/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/DynamicEditContactAddressModal.tsx new file mode 100644 index 000000000..802cf81f5 --- /dev/null +++ b/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/DynamicEditContactAddressModal.tsx @@ -0,0 +1,10 @@ +import dynamic from 'next/dynamic'; +import { DynamicComponentPlaceholder } from 'src/components/DynamicPlaceholders/DynamicComponentPlaceholder'; + +export const DynamicEditContactAddressModal = dynamic( + () => + import( + /* webpackChunkName: "EditContactAddressModal" */ './EditContactAddressModal' + ).then(({ EditContactAddressModal }) => EditContactAddressModal), + { loading: DynamicComponentPlaceholder }, +); diff --git a/src/components/Tool/FixMailingAddresses/FixMailingAddresses.tsx b/src/components/Tool/FixMailingAddresses/FixMailingAddresses.tsx index 75c1f2587..260a4a7dd 100644 --- a/src/components/Tool/FixMailingAddresses/FixMailingAddresses.tsx +++ b/src/components/Tool/FixMailingAddresses/FixMailingAddresses.tsx @@ -15,8 +15,8 @@ import { } from '@mui/material'; import { Trans, useTranslation } from 'react-i18next'; import { makeStyles } from 'tss-react/mui'; -import { AddAddressModal } from 'src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/AddAddressModal/AddAddressModal'; -import { EditContactAddressModal } from 'src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/EditContactAddressModal'; +import { DynamicAddAddressModal } from 'src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/AddAddressModal/DynamicAddAddressModal'; +import { DynamicEditContactAddressModal } from 'src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/DynamicEditContactAddressModal'; import theme from '../../../theme'; import NoData from '../NoData'; import Contact from './Contact'; @@ -40,6 +40,7 @@ const useStyles = makeStyles()(() => ({ display: 'flex', flexDirection: 'row', justifyContent: 'center', + flexWrap: 'wrap', width: '100%', }, divider: { @@ -170,6 +171,7 @@ const FixSendNewsletter: React.FC = ({ accountListId }: Props) => { const data = { ...dataFromInvalidAddressesCache, contacts: { + ...dataFromInvalidAddressesCache.contacts, nodes: newContacts, }, }; @@ -206,112 +208,112 @@ const FixSendNewsletter: React.FC = ({ accountListId }: Props) => { const totalContacts = data?.contacts?.nodes?.length || 0; - //TODO: Make navbar selectId = "fixSendNewsletter" when other branch gets merged - return ( - - - {t('Fix Mailing Addresses')} - - + + + + {t('Fix Mailing Addresses')} + + - {loading && !data && ( - - )} + {loading && !data && ( + + )} - {!loading && data && ( - - {!totalContacts && } - {totalContacts && ( - <> - - - - + {!loading && data && ( + + {!totalContacts && } + {totalContacts && ( + <> + + + + + {t( + 'You have {{amount}} mailing addresses to confirm.', + { + amount: totalContacts, + }, + )} + + + {t( - 'You have {{amount}} mailing addresses to confirm.', - { - amount: totalContacts, - }, + 'Choose below which mailing address will be set as primary. Primary mailing addresses will be used for Newsletter exports.', )} - - - - {t( - 'Choose below which mailing address will be set as primary. Primary mailing addresses will be used for Newsletter exports.', - )} - - - {t('Default Primary Source:')} + + + {t('Default Primary Source:')} - - + + + - - - - {data.contacts.nodes.map((contact) => ( - - handleModalOpen(ModalEnum.Edit, address, contactId) - } - openNewAddressModal={(address, contactId) => - handleModalOpen(ModalEnum.New, address, contactId) - } - /> - ))} - - - - - }} + + + {data.contacts.nodes.map((contact) => ( + + handleModalOpen(ModalEnum.Edit, address, contactId) + } + openNewAddressModal={(address, contactId) => + handleModalOpen(ModalEnum.New, address, contactId) + } /> - - - - - )} - - )} - + ))} + + + + + }} + /> + + + + + )} + + )} + + {showEditAddressModal && ( - = ({ accountListId }: Props) => { /> )} {showNewAddressModal && ( - Date: Thu, 13 Jun 2024 16:53:57 -0400 Subject: [PATCH 10/18] improving tests --- .../FixMailingAddresses.test.tsx | 79 ++----------------- .../FixMailingAddressesMock.ts | 37 +++++---- 2 files changed, 25 insertions(+), 91 deletions(-) diff --git a/src/components/Tool/FixMailingAddresses/FixMailingAddresses.test.tsx b/src/components/Tool/FixMailingAddresses/FixMailingAddresses.test.tsx index 0767fa3cc..ca2cb1413 100644 --- a/src/components/Tool/FixMailingAddresses/FixMailingAddresses.test.tsx +++ b/src/components/Tool/FixMailingAddresses/FixMailingAddresses.test.tsx @@ -10,6 +10,7 @@ import { CreateContactAddressMutation } from 'src/components/Contacts/ContactDet import theme from 'src/theme'; import FixSendNewsletter from './FixMailingAddresses'; import { + mockInvalidAddressesResponse, mpdxSourcedAddress, tntSourcedAddress, } from './FixMailingAddressesMock'; @@ -18,7 +19,7 @@ import { InvalidAddressesQuery } from './GetInvalidAddresses.generated'; jest.mock('next-auth/react'); const accountListId = 'account-list-1'; -const contactId = 'd4db82ad-ff6f-4ba3-96ee-c143efc07d7c'; +const contactId = 'contactId'; const router = { isReady: true, }; @@ -196,32 +197,7 @@ describe('FixSendNewsletter', () => { const cache = new InMemoryCache(); jest.spyOn(cache, 'writeFragment'); const { queryByTestId, getByText, getByRole, queryByText } = render( - , + , ); await waitFor(() => expect(queryByTestId('loading')).not.toBeInTheDocument(), @@ -257,33 +233,7 @@ describe('FixSendNewsletter', () => { jest.spyOn(cache, 'writeFragment'); const { getByTestId, getByText, getByRole, queryByTestId, queryByRole } = render( - , + , ); await waitFor(() => expect(queryByTestId('loading')).not.toBeInTheDocument(), @@ -326,26 +276,7 @@ describe('FixSendNewsletter', () => { Date: Thu, 13 Jun 2024 16:55:33 -0400 Subject: [PATCH 11/18] fixup! improving tests --- .../Tool/FixMailingAddresses/FixMailingAddresses.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Tool/FixMailingAddresses/FixMailingAddresses.test.tsx b/src/components/Tool/FixMailingAddresses/FixMailingAddresses.test.tsx index ca2cb1413..21d007b42 100644 --- a/src/components/Tool/FixMailingAddresses/FixMailingAddresses.test.tsx +++ b/src/components/Tool/FixMailingAddresses/FixMailingAddresses.test.tsx @@ -356,6 +356,6 @@ describe('FixSendNewsletter', () => { getByText('Buckingham Palace, London . SW1A 1AA'), ).toBeInTheDocument(), ); - }); + }, 10000); }); }); From 1adcbf62c902be9d58f75375cf7b1a5dd90d09ae Mon Sep 17 00:00:00 2001 From: Daniel Bisgrove Date: Fri, 14 Jun 2024 10:44:33 -0400 Subject: [PATCH 12/18] REmoving MPDX name and padding/margin --- .../Tool/FixMailingAddresses/Contact.tsx | 7 +++++-- .../FixMailingAddresses.tsx | 19 ++++++++++++------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/components/Tool/FixMailingAddresses/Contact.tsx b/src/components/Tool/FixMailingAddresses/Contact.tsx index e8fe8db7a..431c3a171 100644 --- a/src/components/Tool/FixMailingAddresses/Contact.tsx +++ b/src/components/Tool/FixMailingAddresses/Contact.tsx @@ -105,6 +105,7 @@ interface Props { name: string; status: string; addresses: ContactAddressFragment[]; + appName: string; openEditAddressModal: (address: ContactAddressFragment, id: string) => void; openNewAddressModal: (address: ContactAddressFragment, id: string) => void; } @@ -114,6 +115,7 @@ const Contact: React.FC = ({ name, status, addresses, + appName, openEditAddressModal, openNewAddressModal, }) => { @@ -233,7 +235,9 @@ const Contact: React.FC = ({ {t('Source')}: - MPDX + + {t('{{appName}}', { appName })} + @@ -243,7 +247,6 @@ const Contact: React.FC = ({ justifyContent="flex-start" className={clsx( classes.responsiveBorder, - classes.paddingX, classes.hoverHighlight, )} > diff --git a/src/components/Tool/FixMailingAddresses/FixMailingAddresses.tsx b/src/components/Tool/FixMailingAddresses/FixMailingAddresses.tsx index 3860e3b3d..6f0b53389 100644 --- a/src/components/Tool/FixMailingAddresses/FixMailingAddresses.tsx +++ b/src/components/Tool/FixMailingAddresses/FixMailingAddresses.tsx @@ -92,9 +92,11 @@ const useStyles = makeStyles()(() => ({ }, })); +const appName = process.env.APP_NAME || 'MPDX'; + export const emptyAddress: ContactAddressFragment = { id: 'new', - source: 'MPDX', + source: appName, street: '', region: '', location: '', @@ -114,7 +116,7 @@ enum ModalEnum { Edit = 'Edit', } -const sourceOptions = ['MPDX', 'DataServer']; +const sourceOptions = [appName, 'DataServer']; const FixSendNewsletter: React.FC = ({ accountListId }: Props) => { const { classes } = useStyles(); @@ -123,7 +125,7 @@ const FixSendNewsletter: React.FC = ({ accountListId }: Props) => { const [showNewAddressModal, setShowNewAddressModal] = useState(false); const [selectedAddress, setSelectedAddress] = useState(emptyAddress); const [selectedContactId, setSelectedContactId] = useState(''); - const [defaultSource, setDefaultSource] = useState('MPDX'); + const [defaultSource, setDefaultSource] = useState(appName); const { data, loading } = useInvalidAddressesQuery({ variables: { accountListId }, }); @@ -218,10 +220,12 @@ const FixSendNewsletter: React.FC = ({ accountListId }: Props) => { {loading && !data && ( - + + + )} {!loading && data && ( @@ -286,6 +290,7 @@ const FixSendNewsletter: React.FC = ({ accountListId }: Props) => { status={contact.status || ''} key={contact.id} addresses={contact.addresses.nodes} + appName={appName} openEditAddressModal={(address, contactId) => handleModalOpen(ModalEnum.Edit, address, contactId) } From 68f4d9c9686b9c6b4bdcf7d91a1df151dd0cfaf4 Mon Sep 17 00:00:00 2001 From: Daniel Bisgrove Date: Fri, 14 Jun 2024 13:54:41 -0400 Subject: [PATCH 13/18] Adding set Primary Contact functionality --- .../Tool/FixMailingAddresses/Contact.tsx | 64 ++++++++++++++++--- 1 file changed, 55 insertions(+), 9 deletions(-) diff --git a/src/components/Tool/FixMailingAddresses/Contact.tsx b/src/components/Tool/FixMailingAddresses/Contact.tsx index e8fe8db7a..69862d50f 100644 --- a/src/components/Tool/FixMailingAddresses/Contact.tsx +++ b/src/components/Tool/FixMailingAddresses/Contact.tsx @@ -11,6 +11,7 @@ import { Card, CardContent, CardHeader, + CircularProgress, Grid, Hidden, IconButton, @@ -18,8 +19,11 @@ import { } from '@mui/material'; import clsx from 'clsx'; import { DateTime } from 'luxon'; +import { useSnackbar } from 'notistack'; import { useTranslation } from 'react-i18next'; import { makeStyles } from 'tss-react/mui'; +import { useSetContactPrimaryAddressMutation } from 'src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/SetPrimaryAddress.generated'; +import { useUpdateCache } from 'src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/useUpdateCache'; import { AddButton, AddIcon, @@ -119,10 +123,35 @@ const Contact: React.FC = ({ }) => { const { t } = useTranslation(); const locale = useLocale(); + const { enqueueSnackbar } = useSnackbar(); const { classes } = useStyles(); const newAddress = { ...emptyAddress, newAddress: true }; - //TODO: Add button functionality - //TODO: Make contact name a link to contact page + const [setContactPrimaryAddress, { loading: settingPrimaryAddress }] = + useSetContactPrimaryAddressMutation(); + const { update } = useUpdateCache(id); + + const handleSetPrimaryContact = async (address: ContactAddressFragment) => { + await setContactPrimaryAddress({ + variables: { + contactId: id, + primaryAddressId: address.primaryMailingAddress ? null : address.id, + }, + update, + onCompleted: () => { + enqueueSnackbar(t('Mailing information edited successfully'), { + variant: 'success', + }); + }, + onError: () => { + enqueueSnackbar( + t('Error occurred while updating mailing information'), + { + variant: 'error', + }, + ); + }, + }); + }; return ( @@ -188,13 +217,30 @@ const Contact: React.FC = ({ - - {address.primaryMailingAddress ? ( - - ) : ( - - )} - + {!settingPrimaryAddress && ( + handleSetPrimaryContact(address)} + > + {address.primaryMailingAddress ? ( + + ) : ( + + )} + + )} + {settingPrimaryAddress && ( + + )} From 03ddd9077d8c7a2d09cf0437c82f5f9fbbb5e0f7 Mon Sep 17 00:00:00 2001 From: Daniel Bisgrove Date: Fri, 14 Jun 2024 13:54:53 -0400 Subject: [PATCH 14/18] Adding tests --- .../FixMailingAddresses.test.tsx | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/components/Tool/FixMailingAddresses/FixMailingAddresses.test.tsx b/src/components/Tool/FixMailingAddresses/FixMailingAddresses.test.tsx index 21d007b42..72ac5fc2a 100644 --- a/src/components/Tool/FixMailingAddresses/FixMailingAddresses.test.tsx +++ b/src/components/Tool/FixMailingAddresses/FixMailingAddresses.test.tsx @@ -358,4 +358,42 @@ describe('FixSendNewsletter', () => { ); }, 10000); }); + describe('Set primary mailing address', () => { + it('should set the address as primary', async () => { + const { getByTestId, getAllByTestId, queryByTestId, queryAllByTestId } = + render( + , + ); + await waitFor(() => + expect(queryByTestId('loading')).not.toBeInTheDocument(), + ); + + const primaryAddress = getByTestId('primaryContactStarIcon'); + const secondaryAddresses = getAllByTestId('contactStarIcon'); + + expect(primaryAddress).toBeInTheDocument(); + expect(secondaryAddresses.length).toBe(2); + + expect(queryAllByTestId('settingPrimaryAddress').length).toBe(0); + + userEvent.click(secondaryAddresses[0]); + + expect(getAllByTestId('settingPrimaryAddress').length).toBe(3); + + await waitFor(() => + expect(mockEnqueue).toHaveBeenCalledWith( + 'Mailing information edited successfully', + { + variant: 'success', + }, + ), + ); + }); + }); }); From f303fb31feffc50c2400ee9afdb21b2b00756a04 Mon Sep 17 00:00:00 2001 From: Daniel Bisgrove Date: Fri, 14 Jun 2024 14:55:40 -0400 Subject: [PATCH 15/18] Allow for TNT (DataServer) data to be edited in MPDX as it's imported once and not updated in third party software. --- .../EditContactAddressModal/EditContactAddressModal.tsx | 3 +-- src/components/Tool/FixMailingAddresses/Contact.tsx | 6 +++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/EditContactAddressModal.tsx b/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/EditContactAddressModal.tsx index fe589065a..baed1b6ec 100644 --- a/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/EditContactAddressModal.tsx +++ b/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/EditContactAddressModal.tsx @@ -169,8 +169,7 @@ export const EditContactAddressModal: React.FC< handleClose(); }; - const editingDisabled = - address.source === 'Siebel' || address.source === 'DataServer'; + const editingDisabled = address.source === 'Siebel'; const { data: emailData } = useDonationServicesEmailQuery({ variables: { accountListId, diff --git a/src/components/Tool/FixMailingAddresses/Contact.tsx b/src/components/Tool/FixMailingAddresses/Contact.tsx index 431c3a171..22ee95772 100644 --- a/src/components/Tool/FixMailingAddresses/Contact.tsx +++ b/src/components/Tool/FixMailingAddresses/Contact.tsx @@ -221,7 +221,11 @@ const Contact: React.FC = ({ - {address.source === 'MPDX' ? : } + {['MPDX', 'DataServer'].indexOf(address.source) > -1 ? ( + + ) : ( + + )} From 98084ae8d272fead97554bba9de052b7e1e54486 Mon Sep 17 00:00:00 2001 From: Daniel Bisgrove Date: Fri, 14 Jun 2024 15:00:09 -0400 Subject: [PATCH 16/18] Moving useUpdateCache to hooks folder as more components are using it. --- .../Mailing/AddAddressModal/AddAddressModal.tsx | 2 +- .../Mailing/EditContactAddressModal/EditContactAddressModal.tsx | 2 +- src/components/Tool/FixMailingAddresses/Contact.tsx | 2 +- .../ContactDetailsTab/Mailing => hooks}/useUpdateCache.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename src/{components/Contacts/ContactDetails/ContactDetailsTab/Mailing => hooks}/useUpdateCache.ts (93%) diff --git a/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/AddAddressModal/AddAddressModal.tsx b/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/AddAddressModal/AddAddressModal.tsx index d4fdc61a6..b7a7a930f 100644 --- a/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/AddAddressModal/AddAddressModal.tsx +++ b/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/AddAddressModal/AddAddressModal.tsx @@ -23,6 +23,7 @@ import { SubmitButton, } from 'src/components/common/Modal/ActionButtons/ActionButtons'; import { AddressCreateInput } from 'src/graphql/types.generated'; +import { useUpdateCache } from 'src/hooks/useUpdateCache'; import Modal from '../../../../../common/Modal/Modal'; import { ContactDetailsTabDocument, @@ -34,7 +35,6 @@ import { } from '../AddressLocation'; import { useSetContactPrimaryAddressMutation } from '../SetPrimaryAddress.generated'; import { StreetAutocomplete } from '../StreetAutocomplete/StreetAutocomplete'; -import { useUpdateCache } from '../useUpdateCache'; import { useCreateContactAddressMutation } from './CreateContactAddress.generated'; import { createAddressSchema } from './createAddressSchema'; diff --git a/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/EditContactAddressModal.tsx b/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/EditContactAddressModal.tsx index fe589065a..be105cbff 100644 --- a/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/EditContactAddressModal.tsx +++ b/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/EditContactAddressModal.tsx @@ -27,6 +27,7 @@ import { SubmitButton, } from 'src/components/common/Modal/ActionButtons/ActionButtons'; import { AddressUpdateInput } from 'src/graphql/types.generated'; +import { useUpdateCache } from 'src/hooks/useUpdateCache'; import Modal from '../../../../../common/Modal/Modal'; import { ContactDetailsTabDocument, @@ -39,7 +40,6 @@ import { import { ContactMailingFragment } from '../ContactMailing.generated'; import { useSetContactPrimaryAddressMutation } from '../SetPrimaryAddress.generated'; import { StreetAutocomplete } from '../StreetAutocomplete/StreetAutocomplete'; -import { useUpdateCache } from '../useUpdateCache'; import { useDeleteContactAddressMutation, useDonationServicesEmailQuery, diff --git a/src/components/Tool/FixMailingAddresses/Contact.tsx b/src/components/Tool/FixMailingAddresses/Contact.tsx index 69862d50f..3dc8147b5 100644 --- a/src/components/Tool/FixMailingAddresses/Contact.tsx +++ b/src/components/Tool/FixMailingAddresses/Contact.tsx @@ -23,7 +23,6 @@ import { useSnackbar } from 'notistack'; import { useTranslation } from 'react-i18next'; import { makeStyles } from 'tss-react/mui'; import { useSetContactPrimaryAddressMutation } from 'src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/SetPrimaryAddress.generated'; -import { useUpdateCache } from 'src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/useUpdateCache'; import { AddButton, AddIcon, @@ -32,6 +31,7 @@ import { LockIcon, } from 'src/components/Contacts/ContactDetails/ContactDetailsTab/StyledComponents'; import { useLocale } from 'src/hooks/useLocale'; +import { useUpdateCache } from 'src/hooks/useUpdateCache'; import { dateFormatShort } from 'src/lib/intlFormat'; import { contactPartnershipStatus } from 'src/utils/contacts/contactPartnershipStatus'; import theme from '../../../theme'; diff --git a/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/useUpdateCache.ts b/src/hooks/useUpdateCache.ts similarity index 93% rename from src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/useUpdateCache.ts rename to src/hooks/useUpdateCache.ts index f8af3a1f2..30909e406 100644 --- a/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/useUpdateCache.ts +++ b/src/hooks/useUpdateCache.ts @@ -3,7 +3,7 @@ import { ContactPrimaryAddressRelationFragmentDoc, PrimaryMailingAddressFragmentDoc, SetContactPrimaryAddressMutation, -} from './SetPrimaryAddress.generated'; +} from 'src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/SetPrimaryAddress.generated'; // This hook provides an Apollo cache update function for the setContactPrimaryAddress mutation // used by the add address and edit address modals From 8480284dfbb5151767703afede2e3a8a2fac0900 Mon Sep 17 00:00:00 2001 From: Daniel Bisgrove Date: Fri, 14 Jun 2024 16:29:42 -0400 Subject: [PATCH 17/18] Only MPDX, TNT and manually address are now editable --- .../EditContactAddressModal/EditContactAddressModal.tsx | 4 +++- src/components/Tool/FixMailingAddresses/Contact.tsx | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/EditContactAddressModal.tsx b/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/EditContactAddressModal.tsx index baed1b6ec..698a5d6bf 100644 --- a/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/EditContactAddressModal.tsx +++ b/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/EditContactAddressModal.tsx @@ -65,6 +65,8 @@ const LoadingIndicator = styled(CircularProgress)(({ theme }) => ({ margin: theme.spacing(0, 1, 0, 0), })); +export const editableSources = ['MPDX', 'manual', 'TntImport']; + interface EditContactAddressModalProps { accountListId: string; address: ContactMailingFragment['addresses']['nodes'][0]; @@ -169,7 +171,7 @@ export const EditContactAddressModal: React.FC< handleClose(); }; - const editingDisabled = address.source === 'Siebel'; + const editingDisabled = editableSources.indexOf(address.source) === -1; const { data: emailData } = useDonationServicesEmailQuery({ variables: { accountListId, diff --git a/src/components/Tool/FixMailingAddresses/Contact.tsx b/src/components/Tool/FixMailingAddresses/Contact.tsx index 22ee95772..9b57119a5 100644 --- a/src/components/Tool/FixMailingAddresses/Contact.tsx +++ b/src/components/Tool/FixMailingAddresses/Contact.tsx @@ -20,6 +20,7 @@ import clsx from 'clsx'; import { DateTime } from 'luxon'; import { useTranslation } from 'react-i18next'; import { makeStyles } from 'tss-react/mui'; +import { editableSources } from 'src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/EditContactAddressModal'; import { AddButton, AddIcon, @@ -221,7 +222,7 @@ const Contact: React.FC = ({ - {['MPDX', 'DataServer'].indexOf(address.source) > -1 ? ( + {editableSources.indexOf(address.source) > -1 ? ( ) : ( From 6343904a120e25e8bd8a7d67cb59cc62b70820f5 Mon Sep 17 00:00:00 2001 From: Daniel Bisgrove Date: Fri, 14 Jun 2024 16:49:54 -0400 Subject: [PATCH 18/18] Fixing failing test due to new changes with what source can be edited --- .../EditContactAddressModal/EditContactAddressModal.test.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/EditContactAddressModal.test.tsx b/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/EditContactAddressModal.test.tsx index 1ff480dc6..0bcaf3c43 100644 --- a/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/EditContactAddressModal.test.tsx +++ b/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/EditContactAddressModal.test.tsx @@ -43,6 +43,7 @@ const mockContact: ContactMailingFragment = { historic: true, street: '123 Cool Street', primaryMailingAddress: false, + source: 'MPDX', }, ], },