Skip to content
Open
Show file tree
Hide file tree
Changes from 9 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',
};
3 changes: 2 additions & 1 deletion src/common/RichEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -144,13 +144,14 @@ export const WhatsAppTemplateButton = (text: string) => {
value: null,
type: 'call-to-action',
tooltip: 'Currently not supported',
icon: <CallIcon />,
};
if (link) {
const [url] = link;
callToActionButton.value = url;
callToActionButton.tooltip = '';
callToActionButton.icon = <OpenInNewIcon />;
} else if (/\d/.test(value)) {
callToActionButton.icon = <CallIcon />;
}
return callToActionButton;
}
Expand Down
3 changes: 3 additions & 0 deletions src/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,10 +174,13 @@ export const GUPSHUP_CALL_TO_ACTION =
export const GUPSHUP_QUICK_REPLY =
'You may get user responses via buttons. Whatsapp allows 3 quick replies. These are static unline the call to actions where you can define call or link actions.';

export const GUPSHUP_WHATSAPP_FORM = 'Whatsapp Forms allow you to collect structured data from users.';

// Call to action button
export const CALL_TO_ACTION = 'CALL_TO_ACTION';
export const LIST = 'LIST';
export const QUICK_REPLY = 'QUICK_REPLY';
export const WHATSAPP_FORM = 'WHATSAPP_FORM';
export const LOCATION_REQUEST = 'LOCATION_REQUEST_MESSAGE';
export const TERMS_OF_USE_LINK = 'https://glific.org/glific-terms-and-conditions/';
export const COMPACT_MESSAGE_LENGTH = 35;
Expand Down
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
18 changes: 17 additions & 1 deletion src/containers/HSM/HSM.helper.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CALL_TO_ACTION, MEDIA_MESSAGE_TYPES, QUICK_REPLY } from 'common/constants';
import { CALL_TO_ACTION, MEDIA_MESSAGE_TYPES, QUICK_REPLY, WHATSAPP_FORM } from 'common/constants';

export interface CallToActionTemplate {
type: string;
Expand All @@ -10,6 +10,12 @@ export interface QuickReplyTemplate {
value: string;
}

export interface WhatsappFormTemplate {
form_id: string;
text: string;
navigate_screen: string;
}

export const mediaOptions = MEDIA_MESSAGE_TYPES.filter((media) => media !== 'AUDIO' && media !== 'STICKER').map(
(option: string) => ({
id: option,
Expand Down Expand Up @@ -37,6 +43,9 @@ export const convertButtonsToTemplate = (templateButtons: Array<any>, templateTy
if (templateType === QUICK_REPLY && value) {
result.push(`[${value}]`);
}
if (templateType === WHATSAPP_FORM && temp.form_id && temp.text && temp.navigate_screen) {
result.push(`[${temp.text}, ${temp.navigate_screen}, ${temp.form_id}]`);
}
return result;
}, []);

Expand Down Expand Up @@ -68,6 +77,13 @@ export const getTemplateAndButtons = (templateType: string, message: string, but
});
}

if (templateType === WHATSAPP_FORM) {
result = templateButtons.map((button: any) => {
const { flow_id, text, navigate_screen } = button;
return { form_id: flow_id, text, navigate_screen };
});
}

// Getting in template format of gupshup
const templateFormat = convertButtonsToTemplate(result, templateType);
// Pre-pending message with buttons
Expand Down
Loading
Loading