Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
23 changes: 23 additions & 0 deletions src/assets/images/icons/SideDrawer/WhatsappForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
const WhatsAppForm = ({ color }: { color: string }) => {
return (
<svg
className="w-6 h-6 "
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
fill="none"
viewBox="0 0 24 24"
color={color}
>
<path
stroke={'currentColor'}
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M15 4h3a1 1 0 0 1 1 1v15a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h3m0 3h6m-6 5h6m-6 4h6M10 3v4h4V3h-4Z"
/>
</svg>
);
};
export default WhatsAppForm;
5 changes: 5 additions & 0 deletions src/common/HelpData.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -139,3 +139,8 @@ export const certificatesInfo: HelpDataProps = {
heading: 'An overview of all the certificates created to date',
link: 'https://glific.github.io/docs/docs/Product%20Features/Custom%20Certificates',
};

export const whatsappFormsInfo: HelpDataProps = {
heading: 'An overview of all the whatsapp forms created to date',
link: 'https://glific.github.io/docs/docs/Product%20Features/Custom%20Certificates',
};
2 changes: 2 additions & 0 deletions src/components/UI/ListIcon/ListIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import FiberNewIcon from '@mui/icons-material/FiberNew';
import { Badge } from '@mui/material';
import DiscordIcon from 'assets/images/icons/Discord/DiscordIcon';
import CertificateIcon from 'assets/images/icons/SideDrawer/CertificateIcon';
import WhatsAppForms from 'assets/images/icons/SideDrawer/WhatsappForm';

export interface ListIconProps {
icon: string | undefined;
Expand Down Expand Up @@ -79,6 +80,7 @@ export const ListIcon = ({ icon = '', selected = false, count }: ListIconProps)
discord: DiscordIcon,
waPolls: WaPolls,
certificate: CertificateIcon,
form: WhatsAppForms,
};

const iconImage = stringsToIcons[icon] && (
Expand Down
8 changes: 8 additions & 0 deletions src/config/menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,14 @@ const menus = (): Menu[] => [
type: 'sideDrawer',
roles: managerLevel,
},
{
title: 'WhatsApp Forms',
path: '/whatsapp-forms',
icon: 'form',
type: 'sideDrawer',
roles: managerLevel,
show: !getOrganizationServices('whatsappFormsEnabled'),
},
{
title: 'Triggers',
path: '/trigger',
Expand Down
76 changes: 76 additions & 0 deletions src/containers/WhatsAppForms/WhatsAppForm.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
.FlowBuilderInfo {
display: flex;
align-items: center;
justify-content: space-between;
padding: 1.5rem;
margin: 2rem;
margin-bottom: 0;
background: linear-gradient(135deg, #f0fdf4 0%, #ecfdf5 100%);
border: 1px solid #bbf7d0;
border-radius: 12px;
box-shadow: 0 1px 3px 0 rgba(34, 197, 94, 0.1);
transition: all 0.2s ease-in-out;
}

.FlowBuilderInfo:hover {
box-shadow: 0 4px 6px -1px rgba(34, 197, 94, 0.15);
transform: translateY(-1px);
}

.FlowBuilderInfo p {
margin-bottom: 0;
}

.InfoContent {
display: flex;
align-items: flex-start;
gap: 1rem;
flex: 1;
}

.IconWrapper {
display: flex;
align-items: center;
justify-content: center;
width: 3rem;
height: 3rem;
background: linear-gradient(135deg, #22c55e 0%, #16a34a 100%);
border-radius: 12px;
flex-shrink: 0;
}

.Icon {
color: white !important;
font-size: 24px !important;
}

.Title {
margin: 0 0 8px 0;
font-size: 1.125rem;
font-weight: 600;
color: #14532d;
line-height: 1.4;
}

.Description {
margin: 0;
font-size: 0.875rem;
color: #16a34a;
line-height: 1.5;
}

.FlowBuilderButton {
padding: 0.75rem 1.5rem !important;
font-weight: 500 !important;
border-radius: 8px !important;
white-space: nowrap;
min-width: 160px;
transition: all 0.2s ease-in-out !important;
color: #fff !important;
}

.FlowBuilderButton:hover {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(34, 197, 94, 0.3) !important;

}
86 changes: 86 additions & 0 deletions src/containers/WhatsAppForms/WhatsAppForm.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { MockedProvider } from '@apollo/client/testing';
import { fireEvent, render, waitFor } from '@testing-library/react';
import * as Notification from 'common/notification';
import { formJson, WHATSAPP_FORM_MOCKS } from 'mocks/WhatsApp';
import { MemoryRouter, Route, Routes } from 'react-router';
import WhatsAppFormList from './WhatsAppFormList/WhatsAppFormList';
import WhatsAppForms from './WhatsAppForms';

describe('<WhatsAppForms />', () => {
const notificationSpy = vi.spyOn(Notification, 'setNotification');
const wrapper = (initialEntry: string = '/whatsapp-forms/add') => (
<MockedProvider mocks={WHATSAPP_FORM_MOCKS}>
<MemoryRouter initialEntries={[initialEntry]}>
<Routes>
<Route path="/whatsapp-forms/add" element={<WhatsAppForms />} />
<Route path="/whatsapp-forms/:id/edit" element={<WhatsAppForms />} />
<Route path="/whatsapp-forms" element={<WhatsAppFormList />} />
</Routes>
</MemoryRouter>
</MockedProvider>
);

test('it should render the WhatsApp Form page initially', async () => {
const { getByText } = render(wrapper());
expect(getByText('Loading...')).toBeInTheDocument();

await waitFor(() => {
expect(getByText('Create WhatsApp Form')).toBeInTheDocument();
});
});

test('it should create WhatsApp Form page with form fields', async () => {
const { getByTestId, getByText, getAllByRole } = render(wrapper());
expect(getByText('Loading...')).toBeInTheDocument();

await waitFor(() => {
expect(getByText('Create WhatsApp Form')).toBeInTheDocument();
});

const inputs = getAllByRole('textbox');

fireEvent.change(inputs[0], { target: { value: 'Test Form' } });
fireEvent.change(inputs[1], { target: { value: 'This is a test form' } });
fireEvent.change(inputs[2], { target: { value: 'sign_up' } });

fireEvent.click(getByTestId('submitActionButton'));

await waitFor(() => {
expect(getByText('Must be valid JSON')).toBeInTheDocument();
});

const autocomplete = getByTestId('AutocompleteInput');

autocomplete.focus();
fireEvent.keyDown(autocomplete, { key: 'ArrowDown' });

fireEvent.click(getByText('Other'), { key: 'Enter' });
fireEvent.change(inputs[2], { target: { value: JSON.stringify(formJson) } });

fireEvent.click(getByTestId('submitActionButton'));

await waitFor(() => {
expect(notificationSpy).toHaveBeenCalledWith('Whatsapp Form created successfully!');
});
});

test('it should edit WhatsApp Form page with form fields', async () => {
const { getByTestId, getByText, getAllByRole } = render(wrapper('/whatsapp-forms/1/edit'));
expect(getByText('Loading...')).toBeInTheDocument();

await waitFor(() => {
expect(getByText('Edit WhatsApp Form')).toBeInTheDocument();
});

const inputs = getAllByRole('textbox');

fireEvent.change(inputs[0], { target: { value: 'Updated Form Name' } });
fireEvent.change(inputs[1], { target: { value: 'This is an updated test form' } });

fireEvent.click(getByTestId('submitActionButton'));

await waitFor(() => {
expect(notificationSpy).toHaveBeenCalled();
});
});
});
32 changes: 32 additions & 0 deletions src/containers/WhatsAppForms/WhatsAppFormList/WhatsAppFormList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import HelpIcon from 'components/UI/HelpIcon/HelpIcon';
import styles from '../../List/List.module.css';
import { whatsappFormsInfo } from 'common/HelpData';
import { Button } from 'components/UI/Form/Button/Button';
import { useNavigate } from 'react-router';

export const WhatsAppFormList = () => {
const navigate = useNavigate();
return (
<div>
{/* TODO: Add <List /> Component and remove this header component*/}
<div className={styles.Header} data-testid="listHeader">
<div>
<div className={styles.Title}>
<div className={styles.TitleText}> WhatsApp Forms</div>
<HelpIcon helpData={whatsappFormsInfo} />
</div>
</div>
<div>
<div className={styles.ButtonGroup}>
<Button variant="contained" color="primary" onClick={() => navigate('/whatsapp-forms/add')}>
Create New Form
</Button>
</div>
</div>
</div>
WhatsApp Forms List
</div>
);
};

export default WhatsAppFormList;
Loading
Loading