From 85a3657dc41ac6891d81d70b4f7bc18734e7b473 Mon Sep 17 00:00:00 2001 From: Daniel Bisgrove Date: Wed, 18 Sep 2024 11:24:00 -0400 Subject: [PATCH 1/2] Removing displayName from contact details and then ensure we don't call the displayName on the contact so it doesn't update GraphQL --- .../ContactHeaderPartnerSection.tsx | 4 +-- .../ContactDetailsPartnerAccounts.test.tsx | 12 ++++----- .../ContactDonationsTab.graphql | 1 - .../ContactDonationsTab.tsx | 1 + .../DonationTable/DonationTable.graphql | 1 + .../DonationTable/DonationTable.test.tsx | 25 +++++++++++++++++++ .../DonationTable/DonationTable.tsx | 14 ++++++++--- .../DonationTable/DonationTable.tsx | 7 ++++-- 8 files changed, 51 insertions(+), 14 deletions(-) diff --git a/src/components/Contacts/ContactDetails/ContactDetailsHeader/ContactHeaderSection/ContactHeaderPartnerSection.tsx b/src/components/Contacts/ContactDetails/ContactDetailsHeader/ContactHeaderSection/ContactHeaderPartnerSection.tsx index 3a2f6d7bc..0cbb74c83 100644 --- a/src/components/Contacts/ContactDetails/ContactDetailsHeader/ContactHeaderSection/ContactHeaderPartnerSection.tsx +++ b/src/components/Contacts/ContactDetails/ContactDetailsHeader/ContactHeaderSection/ContactHeaderPartnerSection.tsx @@ -32,11 +32,11 @@ export const ContactHeaderPartnerSection: React.FC = ({ return ( - {t('Partner Account')} + {t('Partner No.')} {contact?.contactDonorAccounts.nodes.map((donorAccount) => ( - {donorAccount.donorAccount.displayName} + {donorAccount.donorAccount.accountNumber} ))} diff --git a/src/components/Contacts/ContactDetails/ContactDetailsTab/PartnerAccounts/ContactDetailsPartnerAccounts.test.tsx b/src/components/Contacts/ContactDetails/ContactDetailsTab/PartnerAccounts/ContactDetailsPartnerAccounts.test.tsx index ab8ba0796..c2c15cbf4 100644 --- a/src/components/Contacts/ContactDetails/ContactDetailsTab/PartnerAccounts/ContactDetailsPartnerAccounts.test.tsx +++ b/src/components/Contacts/ContactDetails/ContactDetailsTab/PartnerAccounts/ContactDetailsPartnerAccounts.test.tsx @@ -47,7 +47,7 @@ const contact = { donorAccount: { id: 'account1', displayName: 'donor-1', - accountNumber: 'donor-1', + accountNumber: 'accountNumber-1', organization: { id: 'org1', name: 'org1', @@ -59,7 +59,7 @@ const contact = { donorAccount: { id: 'account2', displayName: 'donor-2', - accountNumber: 'donor-2', + accountNumber: 'accountNumber-2', organization: { id: 'org2', name: 'org2', @@ -86,8 +86,8 @@ describe('ContactDetailsPartnerAccounts', () => { , ); - expect(getByText('donor-1')).toBeInTheDocument(); - expect(getByText('donor-2')).toBeInTheDocument(); + expect(getByText('accountNumber-1')).toBeInTheDocument(); + expect(getByText('accountNumber-2')).toBeInTheDocument(); }); it('should render new donor account form', async () => { @@ -188,8 +188,8 @@ describe('ContactDetailsPartnerAccounts', () => { ); userEvent.click(queryAllByRole('button', { name: '' })[0]); - expect(getByText('donor-1')).toBeInTheDocument(); - expect(getByText('donor-2')).toBeInTheDocument(); + expect(getByText('accountNumber-1')).toBeInTheDocument(); + expect(getByText('accountNumber-2')).toBeInTheDocument(); await waitFor(() => expect(mockEnqueue).toHaveBeenCalledWith('Partner account deleted!', { variant: 'success', diff --git a/src/components/Contacts/ContactDetails/ContactDonationsTab/ContactDonationsTab.graphql b/src/components/Contacts/ContactDetails/ContactDonationsTab/ContactDonationsTab.graphql index ebcd88bcf..c14d2d5e5 100644 --- a/src/components/Contacts/ContactDetails/ContactDonationsTab/ContactDonationsTab.graphql +++ b/src/components/Contacts/ContactDetails/ContactDonationsTab/ContactDonationsTab.graphql @@ -68,6 +68,5 @@ fragment DonationMoney on Money { fragment ContactDonorAccount on DonorAccount { id - displayName accountNumber } diff --git a/src/components/Contacts/ContactDetails/ContactDonationsTab/ContactDonationsTab.tsx b/src/components/Contacts/ContactDetails/ContactDonationsTab/ContactDonationsTab.tsx index b8fd3251c..d00aad1d6 100644 --- a/src/components/Contacts/ContactDetails/ContactDonationsTab/ContactDonationsTab.tsx +++ b/src/components/Contacts/ContactDetails/ContactDonationsTab/ContactDonationsTab.tsx @@ -124,6 +124,7 @@ export const ContactDonationsTab: React.FC = ({ /> } visibleColumnsStorageKey="contact-donations" + isOnContactPage={true} /> diff --git a/src/components/DonationTable/DonationTable.graphql b/src/components/DonationTable/DonationTable.graphql index 6352865d5..c6454c304 100644 --- a/src/components/DonationTable/DonationTable.graphql +++ b/src/components/DonationTable/DonationTable.graphql @@ -48,6 +48,7 @@ fragment DonationTableRow on Donation { } } displayName + accountNumber } designationAccount { id diff --git a/src/components/DonationTable/DonationTable.test.tsx b/src/components/DonationTable/DonationTable.test.tsx index 64f745ed0..96ee6d43a 100644 --- a/src/components/DonationTable/DonationTable.test.tsx +++ b/src/components/DonationTable/DonationTable.test.tsx @@ -27,6 +27,7 @@ interface TestComponentProps { hasForeignCurrency?: boolean; hasMultiplePages?: boolean; tableProps?: Partial; + isOnContactPage?: boolean; } const TestComponent: React.FC = ({ @@ -34,6 +35,7 @@ const TestComponent: React.FC = ({ hasForeignCurrency = false, hasMultiplePages = false, tableProps, + isOnContactPage = false, }) => ( @@ -72,6 +74,7 @@ const TestComponent: React.FC = ({ nodes: [{ id: 'contact-1' }], }, displayName: 'Donor 1', + accountNumber: 'accountNumber-1', }, paymentMethod: 'Check', designationAccount: { @@ -94,6 +97,7 @@ const TestComponent: React.FC = ({ nodes: [], }, displayName: 'Donor 2', + accountNumber: 'accountNumber-2', }, paymentMethod: 'Credit Card', designationAccount: { @@ -119,6 +123,7 @@ const TestComponent: React.FC = ({ }} visibleColumnsStorageKey="" emptyPlaceholder={Empty Table} + isOnContactPage={isOnContactPage} {...tableProps} /> @@ -143,6 +148,26 @@ describe('DonationTable', () => { expect(getByRole('cell', { name: 'Appeal 1' })).toBeInTheDocument(); }); + it('renders the partner display name when not on contact page.', async () => { + const { getByRole, findByRole } = render(); + + expect( + await findByRole('columnheader', { name: 'Partner' }), + ).toBeInTheDocument(); + expect(getByRole('cell', { name: 'Donor 2' })).toBeInTheDocument(); + }); + + it('renders the partner account number when on contact page', async () => { + const { getByRole, findByRole } = render( + , + ); + + expect( + await findByRole('columnheader', { name: 'Partner No.' }), + ).toBeInTheDocument(); + expect(getByRole('cell', { name: 'accountNumber-2' })).toBeInTheDocument(); + }); + it('opens and closes the edit donation modal', async () => { const { findByRole, findByText, queryByText, getByTestId, getByRole } = render(); diff --git a/src/components/DonationTable/DonationTable.tsx b/src/components/DonationTable/DonationTable.tsx index 9fdf0e227..9c313b5f0 100644 --- a/src/components/DonationTable/DonationTable.tsx +++ b/src/components/DonationTable/DonationTable.tsx @@ -46,6 +46,7 @@ export interface DonationTableProps { onSelectContact?: (contactId: string) => void; visibleColumnsStorageKey: string; emptyPlaceholder: React.ReactElement; + isOnContactPage?: boolean; } export const StyledGrid = styled(DataGrid)(({ theme }) => ({ @@ -109,11 +110,14 @@ export interface DonationRow { export const createDonationRow = ( data: DonationTableRowFragment, + isOnContactPage: boolean, ): DonationRow => ({ id: data.id, date: DateTime.fromISO(data.donationDate), contactId: data.donorAccount.contacts.nodes[0]?.id ?? null, - donorAccountName: data.donorAccount.displayName, + donorAccountName: isOnContactPage + ? data.donorAccount.accountNumber + : data.donorAccount.displayName, convertedAmount: data.amount.convertedAmount, currency: data.amount.convertedCurrency, foreignAmount: data.amount.amount, @@ -132,6 +136,7 @@ export const DonationTable: React.FC = ({ onSelectContact, visibleColumnsStorageKey, emptyPlaceholder, + isOnContactPage = false, }) => { const { t } = useTranslation(); const locale = useLocale(); @@ -176,7 +181,10 @@ export const DonationTable: React.FC = ({ const accountCurrency = accountListData?.accountList.currency || 'USD'; - const donations = useMemo(() => nodes.map(createDonationRow), [nodes]); + const donations = useMemo( + () => nodes.map((node) => createDonationRow(node, isOnContactPage)), + [nodes, isOnContactPage], + ); const date: RenderCell = ({ row }) => dateFormatShort(row.date, locale); @@ -231,7 +239,7 @@ export const DonationTable: React.FC = ({ }, { field: 'donorAccountName', - headerName: t('Partner'), + headerName: isOnContactPage ? t('Partner No.') : t('Partner'), flex: 3, minWidth: 200, renderCell: donor, diff --git a/src/components/Tool/Appeal/Modals/UpdateDonationsModal/DonationTable/DonationTable.tsx b/src/components/Tool/Appeal/Modals/UpdateDonationsModal/DonationTable/DonationTable.tsx index ce79ea35e..c56ffc361 100644 --- a/src/components/Tool/Appeal/Modals/UpdateDonationsModal/DonationTable/DonationTable.tsx +++ b/src/components/Tool/Appeal/Modals/UpdateDonationsModal/DonationTable/DonationTable.tsx @@ -89,7 +89,7 @@ export const DonationTable: React.FC = ({ () => data?.donations.nodes .filter((donation) => donation.appeal?.id === appealId) - .map(createDonationRow) ?? [], + .map((donation) => createDonationRow(donation, false)) ?? [], [data], ); @@ -108,7 +108,10 @@ export const DonationTable: React.FC = ({ const nodes = data?.donations.nodes || []; - const donations = useMemo(() => nodes.map(createDonationRow), [nodes]); + const donations = useMemo( + () => nodes.map((node) => createDonationRow(node, false)), + [nodes], + ); const isPreselectedDonation = (donation: DonationRow) => preselectedDonations.some((donationRow) => donationRow.id === donation.id); From 729b6fe5e31ced70c2d1db464d689f71b0c41334 Mon Sep 17 00:00:00 2001 From: Daniel Bisgrove Date: Wed, 18 Sep 2024 11:24:28 -0400 Subject: [PATCH 2/2] Add the displayName back in but altering how it updates the cache --- .../ContactDetailsTab.graphql | 1 - .../ContactDetailsTab/ContactDetailsTab.tsx | 25 ++- .../ContactDetailsPartnerAccounts.test.tsx | 157 +++++++++--------- .../ContactDetailsPartnerAccounts.tsx | 53 ++++-- .../ContactPartnerAccounts.graphql | 7 + 5 files changed, 133 insertions(+), 110 deletions(-) diff --git a/src/components/Contacts/ContactDetails/ContactDetailsTab/ContactDetailsTab.graphql b/src/components/Contacts/ContactDetails/ContactDetailsTab/ContactDetailsTab.graphql index 529758ba7..56812d035 100644 --- a/src/components/Contacts/ContactDetails/ContactDetailsTab/ContactDetailsTab.graphql +++ b/src/components/Contacts/ContactDetails/ContactDetailsTab/ContactDetailsTab.graphql @@ -11,7 +11,6 @@ query ContactDetailsTab($accountListId: ID!, $contactId: ID!) { ...ContactPeople ...ContactOther ...ContactMailing - ...ContactPartnerAccounts } } diff --git a/src/components/Contacts/ContactDetails/ContactDetailsTab/ContactDetailsTab.tsx b/src/components/Contacts/ContactDetails/ContactDetailsTab/ContactDetailsTab.tsx index 1bdc88f98..e8b14cda3 100644 --- a/src/components/Contacts/ContactDetails/ContactDetailsTab/ContactDetailsTab.tsx +++ b/src/components/Contacts/ContactDetails/ContactDetailsTab/ContactDetailsTab.tsx @@ -36,11 +36,13 @@ const ContactDetailHeadingText = styled(Typography)(() => ({ fontWeight: 'bold', })); -const ContactDetailLoadingPlaceHolder = styled(Skeleton)(({ theme }) => ({ - width: '100%', - height: '24px', - margin: theme.spacing(2, 0), -})); +export const ContactDetailLoadingPlaceHolder = styled(Skeleton)( + ({ theme }) => ({ + width: '100%', + height: '24px', + margin: theme.spacing(2, 0), + }), +); interface ContactDetailTabProps { accountListId: string; @@ -149,15 +151,10 @@ export const ContactDetailsTab: React.FC = ({ {t('Partner Accounts')} - {!data ? ( - <> - - - - - ) : ( - - )} + diff --git a/src/components/Contacts/ContactDetails/ContactDetailsTab/PartnerAccounts/ContactDetailsPartnerAccounts.test.tsx b/src/components/Contacts/ContactDetails/ContactDetailsTab/PartnerAccounts/ContactDetailsPartnerAccounts.test.tsx index c2c15cbf4..a45e1e081 100644 --- a/src/components/Contacts/ContactDetails/ContactDetailsTab/PartnerAccounts/ContactDetailsPartnerAccounts.test.tsx +++ b/src/components/Contacts/ContactDetails/ContactDetailsTab/PartnerAccounts/ContactDetailsPartnerAccounts.test.tsx @@ -8,6 +8,10 @@ import { GqlMockedProvider } from '__tests__/util/graphqlMocking'; import theme from '../../../../../theme'; import { ContactDetailProvider } from '../../ContactDetailContext'; import { ContactDetailsPartnerAccounts } from './ContactDetailsPartnerAccounts'; +import { + ContactDonorAccountsQuery, + GetAccountListSalaryOrganizationQuery, +} from './ContactPartnerAccounts.generated'; const accountListId = 'account-list-1'; const contactId = 'contact-1'; @@ -18,6 +22,7 @@ const router = { }; const mockEnqueue = jest.fn(); +const mutationSpy = jest.fn(); const getOperationByName = (mutationSpy: jest.Mock, operationName: string) => { const operations = mutationSpy.mock.calls @@ -38,72 +43,80 @@ jest.mock('notistack', () => ({ }, })); -const contact = { - id: '123', - contactDonorAccounts: { - nodes: [ - { - id: 'donor1', - donorAccount: { - id: 'account1', - displayName: 'donor-1', - accountNumber: 'accountNumber-1', - organization: { - id: 'org1', - name: 'org1', +const ContactDonorAccountsMock: ContactDonorAccountsQuery = { + contact: { + id: contactId, + contactDonorAccounts: { + nodes: [ + { + id: 'donor1', + donorAccount: { + id: 'account1', + displayName: 'donor-1', + accountNumber: 'accountNumber-1', + organization: { + id: 'org1', + name: 'org1', + }, }, }, - }, - { - id: 'donor2', - donorAccount: { - id: 'account2', - displayName: 'donor-2', - accountNumber: 'accountNumber-2', - organization: { - id: 'org2', - name: 'org2', + { + id: 'donor2', + donorAccount: { + id: 'account2', + displayName: 'donor-2', + accountNumber: 'accountNumber-2', + organization: { + id: 'org2', + name: 'org2', + }, }, }, - }, - ], + ], + }, }, }; +const Components = () => ( + + + + mocks={{ + ContactDonorAccounts: ContactDonorAccountsMock, + GetAccountListSalaryOrganization: { + accountList: { salaryOrganizationId: 'organizationId' }, + }, + }} + onCall={mutationSpy} + > + + + + + + + + +); + describe('ContactDetailsPartnerAccounts', () => { it('should render donor account', async () => { - const { getByText } = render( - - - - - - - - - - - , - ); + const { findByText, getByText } = render(); - expect(getByText('accountNumber-1')).toBeInTheDocument(); + expect(await findByText('accountNumber-1')).toBeInTheDocument(); expect(getByText('accountNumber-2')).toBeInTheDocument(); }); it('should render new donor account form', async () => { - const { getByRole, queryByRole } = render( - - - - - - - - - - - , - ); + const { getByRole, findByText, queryByRole } = render(); + + expect(await findByText('accountNumber-1')).toBeInTheDocument(); expect( queryByRole('textbox', { name: 'Account Number' }), @@ -115,20 +128,9 @@ describe('ContactDetailsPartnerAccounts', () => { }); it('handle submitting new donor account', async () => { - const mutationSpy = jest.fn(); - const { queryByRole, getByRole } = render( - - - - - - - - - - - , - ); + const { queryByRole, findByText, getByRole } = render(); + + expect(await findByText('accountNumber-1')).toBeInTheDocument(); expect( queryByRole('textbox', { name: 'Account Number' }), @@ -158,11 +160,11 @@ describe('ContactDetailsPartnerAccounts', () => { ).toEqual({ accountListId, attributes: { - id: contact.id, + id: contactId, donorAccount: { accountNumber: 'new-account', name: 'new-account', - organizationId: '', + organizationId: 'organizationId', }, }, }); @@ -172,20 +174,9 @@ describe('ContactDetailsPartnerAccounts', () => { }); it('handle clicking delete button', async () => { - const mutationSpy = jest.fn(); - const { getByText, queryAllByRole } = render( - - - - - - - - - - - , - ); + const { getByText, findByText, queryAllByRole } = render(); + + expect(await findByText('accountNumber-1')).toBeInTheDocument(); userEvent.click(queryAllByRole('button', { name: '' })[0]); expect(getByText('accountNumber-1')).toBeInTheDocument(); @@ -199,7 +190,7 @@ describe('ContactDetailsPartnerAccounts', () => { expect( getOperationByName(mutationSpy, 'DeleteDonorAccount').variables, ).toEqual({ - contactId: contact.id, + contactId: contactId, donorAccountId: 'account1', }); }); diff --git a/src/components/Contacts/ContactDetails/ContactDetailsTab/PartnerAccounts/ContactDetailsPartnerAccounts.tsx b/src/components/Contacts/ContactDetails/ContactDetailsTab/PartnerAccounts/ContactDetailsPartnerAccounts.tsx index 7d904d1a8..458bcff4c 100644 --- a/src/components/Contacts/ContactDetails/ContactDetailsTab/PartnerAccounts/ContactDetailsPartnerAccounts.tsx +++ b/src/components/Contacts/ContactDetails/ContactDetailsTab/PartnerAccounts/ContactDetailsPartnerAccounts.tsx @@ -8,11 +8,11 @@ import { useSnackbar } from 'notistack'; import { useTranslation } from 'react-i18next'; import * as yup from 'yup'; import { ActionButton } from 'src/components/common/Modal/ActionButtons/ActionButtons'; -import { useAccountListId } from 'src/hooks/useAccountListId'; +import { ContactDetailLoadingPlaceHolder } from '../ContactDetailsTab'; import { ContactDetailsTabDocument } from '../ContactDetailsTab.generated'; import { useUpdateContactOtherMutation } from '../Other/EditContactOtherModal/EditContactOther.generated'; import { - ContactPartnerAccountsFragment, + useContactDonorAccountsQuery, useGetAccountListSalaryOrganizationQuery, } from './ContactPartnerAccounts.generated'; import { useDeleteDonorAccountMutation } from './DeleteDonorAccount.generated'; @@ -37,19 +37,33 @@ const ContactPartnerAccountsTextContainer = styled(Box)(({ theme }) => ({ margin: theme.spacing(2), alignItems: 'center', })); +const PartnerAccountTextContainer = styled(Box)(() => ({ + display: 'flex', + alignItems: 'center', +})); +const PartnerAccountTextLabel = styled(Typography)(({ theme }) => ({ + color: theme.palette.text.secondary, + marginRight: '5px', + fontSize: '13px', +})); interface ContactDetailsPartnerAccountsProps { - contact: ContactPartnerAccountsFragment; + accountListId: string; + contactId: string; } export const ContactDetailsPartnerAccounts: React.FC< ContactDetailsPartnerAccountsProps -> = ({ contact }) => { +> = ({ accountListId, contactId }) => { + const { data } = useContactDonorAccountsQuery({ + variables: { accountListId, contactId }, + fetchPolicy: 'no-cache', + }); + const [deleteDonorAccount] = useDeleteDonorAccountMutation(); const [updateContact, { loading: updating }] = useUpdateContactOtherMutation(); const [showForm, setShowForm] = useState(false); - const accountListId = useAccountListId() ?? ''; const { data: accountListData } = useGetAccountListSalaryOrganizationQuery({ variables: { accountListId: accountListId }, }); @@ -59,7 +73,7 @@ export const ContactDetailsPartnerAccounts: React.FC< const deleteContactDonorAccount = async (id: string) => { await deleteDonorAccount({ variables: { - contactId: contact.id, + contactId, donorAccountId: id, }, refetchQueries: [ @@ -67,7 +81,7 @@ export const ContactDetailsPartnerAccounts: React.FC< query: ContactDetailsTabDocument, variables: { accountListId, - contactId: contact.id, + contactId, }, }, ], @@ -80,7 +94,7 @@ export const ContactDetailsPartnerAccounts: React.FC< variables: { accountListId, attributes: { - id: contact.id, + id: contactId, donorAccount: { name: fields.accountNumber, accountNumber: fields.accountNumber, @@ -94,7 +108,7 @@ export const ContactDetailsPartnerAccounts: React.FC< query: ContactDetailsTabDocument, variables: { accountListId, - contactId: contact.id, + contactId, }, }, ], @@ -103,7 +117,13 @@ export const ContactDetailsPartnerAccounts: React.FC< enqueueSnackbar('Partner account added!', { variant: 'success' }); }; - return ( + return !data ? ( + <> + + + + + ) : ( setShowForm(!showForm)}> @@ -153,7 +173,7 @@ export const ContactDetailsPartnerAccounts: React.FC< )} )} - {contact.contactDonorAccounts.nodes.map((donorAccount) => ( + {data.contact.contactDonorAccounts.nodes.map((donorAccount) => ( {donorAccount.donorAccount.organization.id && ( @@ -161,7 +181,16 @@ export const ContactDetailsPartnerAccounts: React.FC< {donorAccount.donorAccount.organization.name} )} - {donorAccount.donorAccount.accountNumber} + + Account No: + {donorAccount.donorAccount.accountNumber} + + + Account Name: + + {donorAccount.donorAccount.displayName} + + diff --git a/src/components/Contacts/ContactDetails/ContactDetailsTab/PartnerAccounts/ContactPartnerAccounts.graphql b/src/components/Contacts/ContactDetails/ContactDetailsTab/PartnerAccounts/ContactPartnerAccounts.graphql index d240af615..9e586dc52 100644 --- a/src/components/Contacts/ContactDetails/ContactDetailsTab/PartnerAccounts/ContactPartnerAccounts.graphql +++ b/src/components/Contacts/ContactDetails/ContactDetailsTab/PartnerAccounts/ContactPartnerAccounts.graphql @@ -1,3 +1,10 @@ +query ContactDonorAccounts($accountListId: ID!, $contactId: ID!) { + contact(accountListId: $accountListId, id: $contactId) { + id + ...ContactPartnerAccounts + } +} + fragment ContactPartnerAccounts on Contact { id contactDonorAccounts(first: 25) {