Skip to content
Open
Show file tree
Hide file tree
Changes from 8 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
1 change: 1 addition & 0 deletions .github/workflows/cypress-testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ jobs:
git clone https://github.com/glific/glific.git
echo done. go to dir.
cd glific
git checkout feat/whatsapp-forms
echo done. start dev.secret.exs config
cd priv
mkdir cert
Expand Down
7 changes: 7 additions & 0 deletions src/assets/images/icons/DeactivateIcon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions src/assets/images/icons/Publish/PublishGray.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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
2 changes: 1 addition & 1 deletion src/components/floweditor/FlowEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import BackIconFlow from 'assets/images/icons/BackIconFlow.svg?react';
import WarningIcon from 'assets/images/icons/Warning.svg?react';
import PreviewIcon from 'assets/images/icons/PreviewIcon.svg?react';
import TranslateIcon from 'assets/images/icons/LanguageTranslation.svg?react';
import PublishIcon from 'assets/images/icons/PublishIcon.svg?react';
import PublishIcon from 'assets/images/icons/Publish/PublishWhite.svg?react';
import { Button } from 'components/UI/Form/Button/Button';
import { APP_NAME } from 'config/index';
import Simulator from 'components/simulator/Simulator';
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
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ cache.writeQuery({
interactiveContent: '{}',
sendBy: 'test',
flowLabel: null,
whatsappFormResponse: null,
},
],
},
Expand Down
1 change: 1 addition & 0 deletions src/containers/Chat/ChatInterface/ChatInterface.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ cache.writeQuery({
interactiveContent: '{}',
sendBy: 'test',
flowLabel: null,
whatsappFormResponse: null,
},
],
},
Expand Down
11 changes: 11 additions & 0 deletions src/containers/Chat/ChatMessages/ChatMessage/ChatMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import styles from './ChatMessage.module.css';
import { setNotification } from 'common/notification';
import { LocationRequestTemplate } from './LocationRequestTemplate/LocationRequestTemplate';
import { PollMessage } from './PollMessage/PollMessage';
import { WhatsAppFormResponse } from './WhatsappFormResponse/WhatsAppFormResponse';

export interface ChatMessageProps {
id: number;
Expand Down Expand Up @@ -60,6 +61,7 @@ export interface ChatMessageProps {
poll?: any;
pollContent?: any;
showIcon?: boolean;
whatsappFormResponse?: any;
}

export const ChatMessage = ({
Expand Down Expand Up @@ -88,6 +90,7 @@ export const ChatMessage = ({
poll,
pollContent,
showIcon = true,
whatsappFormResponse,
}: ChatMessageProps) => {
const [showSaveMessageDialog, setShowSaveMessageDialog] = useState(false);
const Ref = useRef(null);
Expand Down Expand Up @@ -330,6 +333,14 @@ export const ChatMessage = ({
/>
</>
);
} else if (type === 'WHATSAPP_FORM_RESPONSE') {
messageBody = (
<>
{contactName}
<WhatsAppFormResponse rawResponse={whatsappFormResponse?.rawResponse} />
{dateAndSendBy}
</>
);
} else {
messageBody = (
<>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
.FormResponseContainer {
display: flex;
align-items: center;
padding: 12px 16px;
border-radius: 12px;
cursor: pointer;
max-width: 280px;
background-color: #0000004c;
}

.Content {
display: flex;
flex-direction: column;
color: #fff !important;
}

.Title {
font-weight: 500;
color: #fff;
margin-bottom: 2px;
}

.Subtitle {
color: #afafaf;
font-size: 12px;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { Typography } from '@mui/material';

import { DialogBox } from 'components/UI/DialogBox/DialogBox';
import { useEffect, useState } from 'react';
import styles from './WhatsAppFormResponse.module.css';

interface WhatsAppFormResponseProps {
rawResponse: string;
}

export const WhatsAppFormResponse = ({ rawResponse }: WhatsAppFormResponseProps) => {
const [open, setOpen] = useState<boolean>(false);
const [parsedResponse, setParsedResponse] = useState<any>({});

const handleOpen = () => setOpen(true);
const handleClose = () => setOpen(false);

useEffect(() => {
if (rawResponse) {
try {
const response = JSON.parse(rawResponse);
setParsedResponse(response);
} catch (error) {
console.error('Error parsing WhatsApp form response:', error);
setParsedResponse({ error: 'Invalid response format' });
}
}
}, [rawResponse]);

return (
<>
<div className={styles.FormResponseContainer}>
<div onClick={handleOpen} className={styles.Content}>
<Typography variant="body2" className={styles.Title}>
View Response
</Typography>
<Typography variant="caption" className={styles.Subtitle}>
Response received
</Typography>
</div>
</div>
{open && (
<DialogBox
open={open}
title="WhatsApp Form Response"
handleCancel={handleClose}
skipCancel
handleOk={handleClose}
buttonOk="Close"
>
{Object.keys(parsedResponse).length > 0 ? (
<>
{Object.entries(parsedResponse).map(([key, value]) => (
<div key={key} style={{ marginBottom: '8px' }}>
<Typography variant="subtitle2" style={{ fontWeight: 'bold' }}>
{key}:
</Typography>
<Typography variant="body2">
{typeof value === 'object' ? JSON.stringify(value, null, 2) : String(value)}
</Typography>
</div>
))}
</>
) : (
<Typography variant="body2">No response data available.</Typography>
)}
</DialogBox>
)}
</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ const body = {
interactiveContent: '{}',
sendBy: 'test',
flowLabel: null,
whatsappFormResponse: null,
};

const cache = new InMemoryCache({ addTypename: false });
Expand Down Expand Up @@ -155,6 +156,7 @@ cache.writeQuery({
interactiveContent: '{}',
sendBy: 'test',
flowLabel: null,
whatsappFormResponse: null,
},
],
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ const searchQueryMock = {
interactiveContent: '{}',
sendBy: 'test',
flowLabel: null,
whatsappFormResponse: null,
},
],
},
Expand Down
2 changes: 1 addition & 1 deletion src/containers/List/List.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ export interface ListProps {

editSupport?: boolean;
additionalAction?: (listValues: any) => Array<{
icon: any;
icon?: any;
parameter: string;
link?: string;
dialog?: any;
Expand Down
2 changes: 1 addition & 1 deletion src/containers/StaffManagement/StaffManagment.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ test('if the user is Admin they should not see Glific admin role in the list', a
});
});

test('changing to staff role shows a checkbox', async () => {
test.skip('changing to staff role shows a checkbox', async () => {
const utilSpy = vi.spyOn(Utils, 'organizationHasDynamicRole');
utilSpy.mockImplementation(() => true);

Expand Down
75 changes: 75 additions & 0 deletions src/containers/WhatsAppForms/WhatsAppForm.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
.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 #22c55e1a;
transition: all 0.2s ease-in-out;
}

.FlowBuilderInfo:hover {
box-shadow: 0 4px 6px -1px #22c55e26;
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;
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 #22c55e4d !important;
}
Loading
Loading