Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HelpScout - 1154070 - Limit contact merge #947

Merged
merged 2 commits into from
May 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -199,4 +199,87 @@ describe('MassActionsMergeModal', () => {
expect(deselectAll).toHaveBeenCalled();
});
});

it('should show contact error message if 9 contacts or over', async () => {
const { getByText, queryByText } = render(
<ThemeProvider theme={theme}>
<TestRouter>
<GqlMockedProvider<{
GetContactsForMerging: GetContactsForMergingQuery;
}>
mocks={mocks}
>
<ContactsContext.Provider
value={{ deselectAll: jest.fn() } as unknown as ContactsType}
>
<MassActionsMergeModal
accountListId={accountListId}
ids={[
'contact-1',
'contact-2',
'contact-3',
'contact-4',
'contact-5',
'contact-6',
'contact-7',
'contact-8',
'contact-9',
]}
handleClose={handleClose}
/>
</ContactsContext.Provider>
</GqlMockedProvider>
</TestRouter>
</ThemeProvider>,
);
await waitFor(() => {
expect(
getByText('You can only merge up to 8 contacts at a time.'),
).toBeInTheDocument();

expect(
queryByText('This action cannot be undone!'),
).not.toBeInTheDocument();
});
});

it('should not show contact error message if 8 contacts', async () => {
const { getByText, queryByText } = render(
<ThemeProvider theme={theme}>
<TestRouter>
<GqlMockedProvider<{
GetContactsForMerging: GetContactsForMergingQuery;
}>
mocks={mocks}
>
<ContactsContext.Provider
value={{ deselectAll: jest.fn() } as unknown as ContactsType}
>
<MassActionsMergeModal
accountListId={accountListId}
ids={[
'contact-1',
'contact-2',
'contact-3',
'contact-4',
'contact-5',
'contact-6',
'contact-7',
'contact-8',
]}
handleClose={handleClose}
/>
</ContactsContext.Provider>
</GqlMockedProvider>
</TestRouter>
</ThemeProvider>,
);
await waitFor(() => {
expect(
queryByText('You can only merge up to 8 contacts at a time.'),
).not.toBeInTheDocument();

expect(getByText('This action cannot be undone!')).toBeInTheDocument();
});
});
});
179 changes: 101 additions & 78 deletions src/components/Contacts/MassActions/Merge/MassActionsMergeModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,96 +85,119 @@ export const MassActionsMergeModal: React.FC<MassActionsMergeModalProps> = ({
handleClose();
};

const contactLimitExceeded = ids.length > 8;

return (
<Modal title={t('Merge Contacts')} isOpen={true} handleClose={handleClose}>
<DialogContent data-testid="MergeModal">
<Alert
severity="warning"
sx={(theme) => ({
marginBottom: theme.spacing(2),
})}
>
{t('This action cannot be undone!')}
</Alert>
<Typography variant="subtitle1">
{t('Are you sure you want to merge the selected contacts?')}
</Typography>
<Typography variant="subtitle1">
{t(
' Data from the "losers" will get copied to the "winner". Select the winner below. No data will be lost by merging.',
)}
</Typography>
{data?.contacts.nodes.map((contact) => (
<Box
my={2}
p={2}
key={contact.id}
onClick={() => setPrimaryContactId(contact.id)}
aria-selected={primaryContactId === contact.id}
{contactLimitExceeded && (
<Alert
severity="warning"
sx={(theme) => ({
display: 'flex',
gap: theme.spacing(2),
cursor: 'pointer',
borderWidth: 3,
borderStyle: 'solid',
borderColor:
primaryContactId === contact.id
? theme.palette.mpdxGreen.main
: theme.palette.cruGrayLight.main,
marginBottom: theme.spacing(2),
})}
data-testid="MassActionsMergeModalContact"
>
<Avatar
src={contact.avatar}
alt={`${contact.name} avatar}`}
style={{
width: theme.spacing(6),
height: theme.spacing(6),
}}
/>
<Box sx={{ flex: 1 }}>
<Typography variant="subtitle1">{contact.name}</Typography>
<Typography variant="subtitle2">
{t('Status')}: {getLocalizedContactStatus(t, contact.status)}
<br />
{contact.primaryAddress && (
<>
{contact.primaryAddress.street}
<br />
{contact.primaryAddress.city},{' '}
{contact.primaryAddress.state}{' '}
{contact.primaryAddress.postalCode}
{t('You can only merge up to 8 contacts at a time.')}
</Alert>
)}
{!contactLimitExceeded && (
<>
<Alert
severity="warning"
sx={(theme) => ({
marginBottom: theme.spacing(2),
})}
>
{t('This action cannot be undone!')}
</Alert>
<Typography variant="subtitle1">
{t('Are you sure you want to merge the selected contacts?')}
</Typography>
<Typography variant="subtitle1">
{t(
' Data from the "losers" will get copied to the "winner". Select the winner below. No data will be lost by merging.',
)}
</Typography>
{data?.contacts.nodes.map((contact) => (
<Box
my={2}
p={2}
key={contact.id}
onClick={() => setPrimaryContactId(contact.id)}
aria-selected={primaryContactId === contact.id}
sx={(theme) => ({
display: 'flex',
gap: theme.spacing(2),
cursor: 'pointer',
borderWidth: 3,
borderStyle: 'solid',
borderColor:
primaryContactId === contact.id
? theme.palette.mpdxGreen.main
: theme.palette.cruGrayLight.main,
})}
data-testid="MassActionsMergeModalContact"
>
<Avatar
src={contact.avatar}
alt={`${contact.name} avatar}`}
style={{
width: theme.spacing(6),
height: theme.spacing(6),
}}
/>
<Box sx={{ flex: 1 }}>
<Typography variant="subtitle1">{contact.name}</Typography>
<Typography variant="subtitle2">
{t('Status')}:{' '}
{getLocalizedContactStatus(t, contact.status)}
<br />
{t('From')}: {contact.primaryAddress.source}
<br />
</>
{contact.primaryAddress && (
<>
{contact.primaryAddress.street}
<br />
{contact.primaryAddress.city},{' '}
{contact.primaryAddress.state}{' '}
{contact.primaryAddress.postalCode}
<br />
{t('From')}: {contact.primaryAddress.source}
<br />
</>
)}
{t('On')}:{' '}
{dateFormatShort(
DateTime.fromISO(contact.createdAt),
locale,
)}
</Typography>
</Box>
{primaryContactId === contact.id && (
<Box sx={{ display: 'flex', flexDirection: 'column' }}>
<Typography
sx={(theme) => ({
backgroundColor: theme.palette.mpdxGreen.main,
color: 'white',
padding: theme.spacing(0.5),
margin: theme.spacing(-2),
})}
variant="subtitle2"
>
{t('Use This One')}
</Typography>
<Box sx={{ flex: 1 }} />
</Box>
)}
{t('On')}:{' '}
{dateFormatShort(DateTime.fromISO(contact.createdAt), locale)}
</Typography>
</Box>
{primaryContactId === contact.id && (
<Box sx={{ display: 'flex', flexDirection: 'column' }}>
<Typography
sx={(theme) => ({
backgroundColor: theme.palette.mpdxGreen.main,
color: 'white',
padding: theme.spacing(0.5),
margin: theme.spacing(-2),
})}
variant="subtitle2"
>
{t('Use This One')}
</Typography>
<Box sx={{ flex: 1 }} />
</Box>
)}
</Box>
)) ?? <LoadingIndicator size={20} />}
)) ?? <LoadingIndicator size={20} />}
</>
)}
</DialogContent>
<DialogActions>
<CancelButton onClick={handleClose} disabled={loading || updating} />
<SubmitButton onClick={mergeContacts} disabled={loading || updating}>
<SubmitButton
onClick={mergeContacts}
disabled={loading || updating || contactLimitExceeded}
>
{updating && <CircularProgress color="primary" size={20} />}
{t('Merge')}
</SubmitButton>
Expand Down
Loading