diff --git a/src/components/Tool/FixPhoneNumbers/FixPhoneNumbers.test.tsx b/src/components/Tool/FixPhoneNumbers/FixPhoneNumbers.test.tsx index ff004eb63..5fa75de44 100644 --- a/src/components/Tool/FixPhoneNumbers/FixPhoneNumbers.test.tsx +++ b/src/components/Tool/FixPhoneNumbers/FixPhoneNumbers.test.tsx @@ -1,7 +1,9 @@ import React from 'react'; +import { ApolloCache, InMemoryCache } from '@apollo/client'; import { ThemeProvider } from '@mui/material/styles'; import userEvent from '@testing-library/user-event'; import { ErgonoMockShape } from 'graphql-ergonomock'; +import { SnackbarProvider } from 'notistack'; import TestRouter from '__tests__/util/TestRouter'; import TestWrapper from '__tests__/util/TestWrapper'; import { GqlMockedProvider } from '__tests__/util/graphqlMocking'; @@ -73,30 +75,46 @@ const testData: ErgonoMockShape[] = [ }, ]; -const Components: React.FC<{ data?: ErgonoMockShape[] }> = ({ - data = testData, -}) => ( +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: React.FC<{ + data?: ErgonoMockShape[]; + cache?: ApolloCache; +}> = ({ data = testData, cache }) => ( - - - - mocks={{ - GetInvalidPhoneNumbers: { - people: { - nodes: data, + + + + + mocks={{ + GetInvalidPhoneNumbers: { + people: { + nodes: data, + }, }, - }, - }} - > - - - - + }} + cache={cache} + > + + + + + ); @@ -214,4 +232,30 @@ describe('FixPhoneNumbers-Home', () => { ).not.toBeInTheDocument(); }); }); + it('should bulk confirm all phone numbers', async () => { + const cache = new InMemoryCache(); + + const { getByTestId, queryByTestId, getByText } = render( + , + ); + await waitFor(() => { + expect(queryByTestId('loading')).not.toBeInTheDocument(); + expect(getByTestId('starOutlineIcon-testid-1')).toBeInTheDocument(); + }); + + userEvent.click(getByTestId(`starOutlineIcon-testid-1`)); + + const confirmAllButton = getByTestId('source-button'); + userEvent.click(confirmAllButton); + + await waitFor(() => { + expect(mockEnqueue).toHaveBeenCalledWith(`Phone numbers updated!`, { + variant: 'success', + autoHideDuration: 7000, + }); + expect( + getByText('No people with phone numbers need attention'), + ).toBeVisible(); + }); + }); }); diff --git a/src/components/Tool/FixPhoneNumbers/FixPhoneNumbers.tsx b/src/components/Tool/FixPhoneNumbers/FixPhoneNumbers.tsx index 61e27f84f..9fa118526 100644 --- a/src/components/Tool/FixPhoneNumbers/FixPhoneNumbers.tsx +++ b/src/components/Tool/FixPhoneNumbers/FixPhoneNumbers.tsx @@ -1,5 +1,4 @@ import React, { useEffect, useState } from 'react'; -import { useApolloClient } from '@apollo/client'; import { mdiCheckboxMarkedCircle } from '@mdi/js'; import Icon from '@mdi/react'; import { @@ -15,17 +14,16 @@ import { useSnackbar } from 'notistack'; import { Trans, useTranslation } from 'react-i18next'; import { makeStyles } from 'tss-react/mui'; import { SetContactFocus } from 'pages/accountLists/[accountListId]/tools/useToolsHelper'; -import { PersonPhoneNumberInput } from 'src/graphql/types.generated'; +import { + PersonPhoneNumberInput, + PersonUpdateInput, +} from 'src/graphql/types.generated'; import theme from '../../../theme'; import NoData from '../NoData'; import { StyledInput } from '../StyledInput'; import Contact from './Contact'; import DeleteModal from './DeleteModal'; -import { - GetInvalidPhoneNumbersDocument, - GetInvalidPhoneNumbersQuery, - useGetInvalidPhoneNumbersQuery, -} from './GetInvalidPhoneNumbers.generated'; +import { useGetInvalidPhoneNumbersQuery } from './GetInvalidPhoneNumbers.generated'; import { useUpdateInvalidPhoneNumbersMutation } from './UpdateInvalidPhoneNumbers.generated'; const useStyles = makeStyles()(() => ({ @@ -131,7 +129,6 @@ const FixPhoneNumbers: React.FC = ({ }: Props) => { const { classes } = useStyles(); const { enqueueSnackbar } = useSnackbar(); - const client = useApolloClient(); const [defaultSource, setDefaultSource] = useState('MPDX'); const [deleteModalState, setDeleteModalState] = useState( defaultDeleteModalState, @@ -173,6 +170,34 @@ const FixPhoneNumbers: React.FC = ({ [loading], ); + const determineBulkDataToSend = ( + dataState: { [key: string]: PersonPhoneNumbers }, + defaultSource: string, + ): PersonUpdateInput[] => { + const dataToSend = [] as PersonUpdateInput[]; + + Object.entries(dataState).forEach((value) => { + const primaryNumber = value[1].phoneNumbers.find( + (number) => number.source === defaultSource, + ); + if (primaryNumber) { + dataToSend.push({ + id: value[0], + phoneNumbers: value[1].phoneNumbers.map( + (number) => + ({ + id: number.id, + primary: number.id === primaryNumber.id, + number: number.number, + validValues: true, + } as PersonPhoneNumberInput), + ), + }); + } + }); + return dataToSend; + }; + const handleDeleteModalOpen = ( personId: string, numberIndex: number, @@ -256,6 +281,7 @@ const FixPhoneNumbers: React.FC = ({ id: phoneNumber.id, primary: phoneNumber.primary, number: phoneNumber.number, + validValues: true, })), id: personId, }, @@ -267,6 +293,9 @@ const FixPhoneNumbers: React.FC = ({ attributes, }, }, + update: (cache) => { + cache.evict({ id: `Person:${personId}` }); + }, onError() { enqueueSnackbar(t(`Error updating ${name}'s phone numbers`), { variant: 'error', @@ -278,33 +307,42 @@ const FixPhoneNumbers: React.FC = ({ variant: 'success', autoHideDuration: 7000, }); - hideContactFromView(personId); }, }); }; - const hideContactFromView = (hideId: string): void => { - const query = { - query: GetInvalidPhoneNumbersDocument, - variables: { - accountListId, - }, - }; + const handleBulkConfirm = async () => { + const dataToSend = determineBulkDataToSend(dataState, defaultSource ?? ''); - const dataFromCache = client.readQuery(query); + if (!dataToSend.length) { + return; + } - if (dataFromCache) { - const data = { - ...dataFromCache, - people: { - ...dataFromCache.people, - nodes: dataFromCache.people.nodes.filter( - (person) => person.id !== hideId, - ), + await updateInvalidPhoneNumbers({ + variables: { + input: { + accountListId, + attributes: dataToSend, }, - }; - client.writeQuery({ ...query, data }); - } + }, + update: (cache) => { + data?.people.nodes.forEach((person) => { + cache.evict({ id: `Person:${person.id}` }); + }); + }, + onError: () => { + enqueueSnackbar(t(`Error updating phone numbers`), { + variant: 'error', + autoHideDuration: 7000, + }); + }, + onCompleted: () => { + enqueueSnackbar(t(`Phone numbers updated!`), { + variant: 'success', + autoHideDuration: 7000, + }); + }, + }); }; return ( @@ -361,6 +399,7 @@ const FixPhoneNumbers: React.FC = ({