Skip to content

Commit

Permalink
Merge pull request #2992 from glific/feature/bulk-contact
Browse files Browse the repository at this point in the history
Contact import automation
  • Loading branch information
kurund authored Aug 28, 2024
2 parents a21626c + 1a43366 commit cab883c
Show file tree
Hide file tree
Showing 30 changed files with 625 additions and 476 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/cypress-testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ jobs:
git clone https://github.com/glific/cypress-testing.git
echo done. go to dir.
cd cypress-testing
git checkout main
git checkout feat/contacts-import
cd ..
cp -r cypress-testing/cypress cypress
yarn add [email protected]
Expand Down
4 changes: 2 additions & 2 deletions src/assets/images/icons/Info.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 10 additions & 1 deletion src/components/UI/Form/Checkbox/Checkbox.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
.Checkbox {
margin-bottom: -10px;
display: flex;
align-items: center;
flex-direction: column;
}

.Label {
Expand Down Expand Up @@ -33,3 +33,12 @@
.Disabled {
opacity: 60%;
}

.DangerText {
margin-left: 30px;
font-size: 12px;
margin-top: -12px;
line-height: 18px;
font-weight: 400;
color: #fb5c5c;
}
67 changes: 36 additions & 31 deletions src/components/UI/Form/Checkbox/Checkbox.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Checkbox as CheckboxElement, FormControlLabel } from '@mui/material';
import { Checkbox as CheckboxElement, FormControlLabel, FormHelperText } from '@mui/material';
import InfoIcon from 'assets/images/icons/Info.svg?react';
import Tooltip from 'components/UI/Tooltip/Tooltip';
import styles from './Checkbox.module.css';
Expand Down Expand Up @@ -38,37 +38,42 @@ export const Checkbox = ({

return (
<div className={`${styles.Checkbox} ${className} ${disabled && styles.Disabled}`}>
<FormControlLabel
control={
<CheckboxElement
data-testid="checkboxLabel"
classes={darkCheckbox ? { colorPrimary: styles.CheckboxColor } : null}
{...field}
color="primary"
checked={field.value ? field.value : false}
onChange={handleChangeCallback}
disabled={disabled}
/>
}
labelPlacement="end"
label={title}
classes={{
label: addLabelStyle ? styles.Label : undefined,
root: styles.Root,
}}
/>
{info?.title && infoType === 'tooltip' && (
<Tooltip tooltipClass={styles.Tooltip} title={info.title} placement="right">
<InfoIcon className={styles.InfoIcon} />
</Tooltip>
)}
{info && infoType === 'dialog' && (
<InfoIcon
className={styles.InfoIcon}
data-testid="info-icon"
onClick={() => handleInfoClick()}
<div>
<FormControlLabel
control={
<CheckboxElement
data-testid="checkboxLabel"
classes={darkCheckbox ? { colorPrimary: styles.CheckboxColor } : null}
{...field}
color="primary"
checked={field.value ? field.value : false}
onChange={handleChangeCallback}
disabled={disabled}
/>
}
labelPlacement="end"
label={title}
classes={{
label: addLabelStyle ? styles.Label : undefined,
root: styles.Root,
}}
/>
)}
{info?.title && infoType === 'tooltip' && (
<Tooltip tooltipClass={styles.Tooltip} title={info.title} placement="right">
<InfoIcon className={styles.InfoIcon} />
</Tooltip>
)}
{info && infoType === 'dialog' && (
<InfoIcon
className={styles.InfoIcon}
data-testid="info-icon"
onClick={() => handleInfoClick()}
/>
)}
</div>
{form && form.errors[field.name] && form.touched[field.name] ? (
<FormHelperText className={styles.DangerText}>{form.errors[field.name]}</FormHelperText>
) : null}
</div>
);
};
2 changes: 1 addition & 1 deletion src/components/UI/ImportButton/ImportButton.module.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.ImportIcon {
.FileIcon {
height: 24px;
width: 24px;
margin-right: 5px;
Expand Down
24 changes: 18 additions & 6 deletions src/components/UI/ImportButton/ImportButton.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,33 @@
import { useRef } from 'react';
import ImportIcon from 'assets/images/icons/Flow/Import.svg?react';
import FileIcon from 'assets/images/icons/Document/Light.svg?react';
import { Button } from 'components/UI/Form/Button/Button';
import styles from './ImportButton.module.css';

export interface ImportButtonProps {
title: string;
onImport: any;
onImport?: any;
afterImport: any;
id?: string;
fileType?: string;
}

export const ImportButton = ({ title, onImport, afterImport }: ImportButtonProps) => {
export const ImportButton = ({
title,
onImport,
afterImport,
id,
fileType = '*',
}: ImportButtonProps) => {
const inputRef = useRef<HTMLInputElement>(null);
const changeHandler = (event: any) => {
const media = event.target.files[0];
const fileReader = new FileReader();
fileReader.onload = function setImport() {
afterImport(fileReader.result, media);
};
onImport();
if (onImport) {
onImport();
}
fileReader.readAsText(media);
};
return (
Expand All @@ -29,15 +39,17 @@ export const ImportButton = ({ title, onImport, afterImport }: ImportButtonProps
name="file"
onChange={changeHandler}
data-testid="import"
id={id}
accept={fileType}
/>
<Button
onClick={() => {
inputRef.current?.click();
}}
variant="outlined"
color="primary"
color="secondary"
>
<ImportIcon data-testid="import-icon" className={styles.ImportIcon} />
<FileIcon data-testid="import-icon" className={styles.FileIcon} />
{title}
</Button>
</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ const SideMenus = ({ opened }: SideMenusProps) => {
variables: {
filter: {
is_read: false,
severity: 'critical',
},
},
fetchPolicy: 'cache-and-network',
Expand Down
3 changes: 2 additions & 1 deletion src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ export const ONBOARD_URL_UPDATE = `${GLIFIC_API_URL}/v1/onboard/update-registrat
export const ONBOARD_URL_REACT_OUT = `${GLIFIC_API_URL}/v1/onboard/reachout`;
export const ONBOARD_URL = `${GLIFIC_API_URL}/v1/onboard/setup`;
export const RECAPTCHA_CLIENT_KEY = envVariables.VITE_RECAPTCHA_CLIENT_KEY;
export const UPLOAD_CONTACTS_SAMPLE = 'https://storage.googleapis.com/cc-tides/sample_import.csv';
export const UPLOAD_CONTACTS_SAMPLE =
'https://storage.googleapis.com/cc-tides/sample_contacts_import.csv';
export const UPLOAD_CONTACTS_ADMIN_SAMPLE =
'https://storage.googleapis.com/cc-tides/sample_import_admin.csv';
export const REGISTRATION_HELP_LINK =
Expand Down
20 changes: 16 additions & 4 deletions src/containers/Collection/CollectionList/CollectionList.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,28 @@ import {
updateCollectionWaGroupQuery,
} from 'mocks/Groups';
import { setNotification } from 'common/notification';
import { setVariables } from 'common/constants';
import { CONTACTS_COLLECTION, setVariables } from 'common/constants';
import { setUserRolePermissions } from 'context/role';

const variables = {
filter: {
groupType: CONTACTS_COLLECTION,
},
opts: {
limit: 50,
offset: 0,
order: 'ASC',
orderWith: 'label',
},
};

const mocks = [
countCollectionQuery,
countCollectionQuery,
countCollectionQuery,
filterCollectionQuery,
filterCollectionQuery,
filterCollectionQuery,
filterCollectionQuery(variables),
filterCollectionQuery(variables),
filterCollectionQuery(variables),
getPublishedFlowQuery,
getPublishedFlowQuery,
getCollectionContactsQuery,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
.Container {
padding-left: 40px;
margin-top: 36px;
display: flex;
flex-direction: column;
justify-content: space-between;
width: 50%;
max-height: 65%;
background-color: #fff;
border-radius: 15px;
padding: 1rem 2rem;
}

.Instructions {
max-width: 500px;
color: #93a29b;
font-size: 16px;
}

Expand Down Expand Up @@ -67,10 +72,6 @@
cursor: not-allowed;
}

.FileIcon {
margin-right: 10px;
}

.WaitUpload {
font-size: 14px;
margin-left: 16px;
Expand Down Expand Up @@ -105,3 +106,8 @@
position: absolute;
right: 12px;
}

.Buttons button {
border-radius: 8px;
width: 30%;
}
Original file line number Diff line number Diff line change
@@ -1,24 +1,22 @@
import { render, screen, waitFor } from '@testing-library/react';
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { MockedProvider } from '@apollo/client/testing';

import { BrowserRouter as Router } from 'react-router-dom';

import { getAllOrganizations } from 'mocks/Organization';
import { moveContacts } from 'mocks/Contact';

import { setUserSession } from 'services/AuthService';
import { setNotification } from 'common/notification';
import { AdminContactManagement } from './AdminContactManagement';

const mocks = [...getAllOrganizations, moveContacts];
const mocks = getAllOrganizations;

setUserSession(JSON.stringify({ roles: [{ label: 'Admin' }], organization: { id: '1' } }));

const contactManagement = (
<MockedProvider mocks={mocks} addTypename={false}>
<Router>
<AdminContactManagement />
<AdminContactManagement setShowStatus={vi.fn()} />
</Router>
</MockedProvider>
);
Expand All @@ -43,7 +41,7 @@ test('Admin contact management form renders correctly', async () => {
test('the page should have a disabled upload button by default', async () => {
render(contactManagement);

const uploadButton = await screen.getByTestId('uploadButton');
const uploadButton = await screen.getByTestId('moveContactsBtn');
expect(uploadButton).toBeInTheDocument();
expect(uploadButton).toHaveAttribute('disabled');
});
Expand All @@ -63,10 +61,11 @@ test('Files other than .csv should raise a warning message upon upload', async (
});
});

test('Success Notification should be called upon successful CSV upload', async () => {
test('it removes the selected file', async () => {
render(contactManagement);

// Valid CSV
fireEvent.click(screen.getByTestId('uploadFile'));

const csvContent = `name,phone,collection
John Doe,919876543210,"Optin collection,Optout Collection"
Virat Kohli,919876543220,Cricket`;
Expand All @@ -76,15 +75,15 @@ test('Success Notification should be called upon successful CSV upload', async (
const fileInput = screen.getByTestId('uploadFile');
userEvent.upload(fileInput, file);
});

await waitFor(() => {
// the filename should be visible instead of Select .csv after upload
expect(screen.getByText('test.csv')).toBeInTheDocument();
});

const uploadBtn = screen.getByTestId('uploadButton');
userEvent.click(uploadBtn);
fireEvent.click(screen.getByTestId('cross-icon'));

await waitFor(() => {
expect(setNotification).toHaveBeenCalled();
expect(screen.queryByText('test.csv')).not.toBeInTheDocument();
});
});
Loading

0 comments on commit cab883c

Please sign in to comment.