From bad24f97abd3f839d40f890b24f797c09e25a9c6 Mon Sep 17 00:00:00 2001 From: Fayaz Ahmed <15716057+fayazara@users.noreply.github.com> Date: Tue, 7 Jun 2022 17:33:33 +0530 Subject: [PATCH] feat: Add support for Whatsapp template messages in the UI (#4711) Co-authored-by: Pranav Raj S --- app/builders/messages/message_builder.rb | 6 +- app/javascript/dashboard/api/inbox/message.js | 4 + app/javascript/dashboard/components/Modal.vue | 3 + .../widgets/WootWriter/ReplyBottomPanel.vue | 17 ++ .../widgets/conversation/ReplyBox.vue | 63 ++++-- .../conversation/WhatsappTemplates/Modal.vue | 76 ++++++++ .../WhatsappTemplates/TemplateParser.vue | 183 ++++++++++++++++++ .../WhatsappTemplates/TemplatesPicker.vue | 163 ++++++++++++++++ .../widgets/conversation/bubble/Actions.vue | 8 +- .../components/widgets/forms/Input.vue | 5 + .../dashboard/i18n/locale/en/index.js | 2 + .../i18n/locale/en/whatsappTemplates.json | 25 +++ .../conversation/contact/ConversationForm.vue | 58 ++++-- .../contact/WhatsappTemplates.vue | 59 ++++++ .../dashboard/settings/inbox/Settings.vue | 2 +- .../dashboard/store/modules/inboxes.js | 14 ++ .../FluentIcon/dashboard-icons.json | 1 + app/models/channel/whatsapp.rb | 34 ++-- app/models/inbox.rb | 4 + .../whatsapp/send_on_whatsapp_service.rb | 26 ++- app/views/api/v1/models/_inbox.json.jbuilder | 3 + .../whatsapp/send_on_whatsapp_service_spec.rb | 31 +++ 22 files changed, 733 insertions(+), 54 deletions(-) create mode 100644 app/javascript/dashboard/components/widgets/conversation/WhatsappTemplates/Modal.vue create mode 100644 app/javascript/dashboard/components/widgets/conversation/WhatsappTemplates/TemplateParser.vue create mode 100644 app/javascript/dashboard/components/widgets/conversation/WhatsappTemplates/TemplatesPicker.vue create mode 100644 app/javascript/dashboard/i18n/locale/en/whatsappTemplates.json create mode 100644 app/javascript/dashboard/routes/dashboard/conversation/contact/WhatsappTemplates.vue diff --git a/app/builders/messages/message_builder.rb b/app/builders/messages/message_builder.rb index 5c8cadbcd..e9bf0802b 100644 --- a/app/builders/messages/message_builder.rb +++ b/app/builders/messages/message_builder.rb @@ -73,6 +73,10 @@ class Messages::MessageBuilder @params[:campaign_id].present? ? { additional_attributes: { campaign_id: @params[:campaign_id] } } : {} end + def template_params + @params[:template_params].present? ? { additional_attributes: { template_params: JSON.parse(@params[:template_params].to_json) } } : {} + end + def message_sender return if @params[:sender_type] != 'AgentBot' @@ -91,6 +95,6 @@ class Messages::MessageBuilder items: @items, in_reply_to: @in_reply_to, echo_id: @params[:echo_id] - }.merge(external_created_at).merge(automation_rule_id).merge(campaign_id) + }.merge(external_created_at).merge(automation_rule_id).merge(campaign_id).merge(template_params) end end diff --git a/app/javascript/dashboard/api/inbox/message.js b/app/javascript/dashboard/api/inbox/message.js index f3c5e83e9..f0096cf23 100644 --- a/app/javascript/dashboard/api/inbox/message.js +++ b/app/javascript/dashboard/api/inbox/message.js @@ -10,6 +10,7 @@ export const buildCreatePayload = ({ files, ccEmails = '', bccEmails = '', + templateParams, }) => { let payload; if (files && files.length !== 0) { @@ -32,6 +33,7 @@ export const buildCreatePayload = ({ content_attributes: contentAttributes, cc_emails: ccEmails, bcc_emails: bccEmails, + template_params: templateParams, }; } return payload; @@ -51,6 +53,7 @@ class MessageApi extends ApiClient { files, ccEmails = '', bccEmails = '', + templateParams, }) { return axios({ method: 'post', @@ -63,6 +66,7 @@ class MessageApi extends ApiClient { files, ccEmails, bccEmails, + templateParams, }), }); } diff --git a/app/javascript/dashboard/components/Modal.vue b/app/javascript/dashboard/components/Modal.vue index f86935959..f4cad844a 100644 --- a/app/javascript/dashboard/components/Modal.vue +++ b/app/javascript/dashboard/components/Modal.vue @@ -98,4 +98,7 @@ export default { width: 48rem; } } +.modal-big { + width: 60%; +} diff --git a/app/javascript/dashboard/components/widgets/WootWriter/ReplyBottomPanel.vue b/app/javascript/dashboard/components/widgets/WootWriter/ReplyBottomPanel.vue index 6dcb47532..e7a0b5995 100644 --- a/app/javascript/dashboard/components/widgets/WootWriter/ReplyBottomPanel.vue +++ b/app/javascript/dashboard/components/widgets/WootWriter/ReplyBottomPanel.vue @@ -79,6 +79,16 @@ :title="signatureToggleTooltip" @click="toggleMessageSignature" /> +
+
@@ -137,7 +146,7 @@ import messageFormatterMixin from 'shared/mixins/messageFormatterMixin'; import { checkFileSizeLimit } from 'shared/helpers/FileHelper'; import { MAXIMUM_FILE_UPLOAD_SIZE } from 'shared/constants/messages'; import { BUS_EVENTS } from 'shared/constants/busEvents'; - +import WhatsappTemplates from './WhatsappTemplates/Modal.vue'; import { isEscape, isEnter, @@ -162,6 +171,7 @@ export default { WootMessageEditor, WootAudioRecorder, Banner, + WhatsappTemplates, }, mixins: [ clickaway, @@ -201,6 +211,7 @@ export default { hasSlashCommand: false, bccEmails: '', ccEmails: '', + showWhatsAppTemplatesModal: false, }; }, computed: { @@ -212,7 +223,6 @@ export default { globalConfig: 'globalConfig/get', accountId: 'getCurrentAccountId', }), - showRichContentEditor() { if (this.isOnPrivateNote) { return true; @@ -256,7 +266,9 @@ export default { return false; }, - + hasWhatsappTemplates() { + return !!this.inbox.message_templates; + }, enterToSendEnabled() { return !!this.uiSettings.enter_to_send_enabled; }, @@ -484,7 +496,7 @@ export default { hasSendOnEnterEnabled && !hasPressedShift(e) && this.isFocused; if (shouldSendMessage) { e.preventDefault(); - this.sendMessage(); + this.onSendReply(); } } else if (hasPressedCommandPlusKKey(e)) { this.openCommandBar(); @@ -497,6 +509,12 @@ export default { toggleEnterToSend(enterToSendEnabled) { this.updateUISettings({ enter_to_send_enabled: enterToSendEnabled }); }, + openWhatsappTemplateModal() { + this.showWhatsAppTemplatesModal = true; + }, + hideWhatsappTemplatesModal() { + this.showWhatsAppTemplatesModal = false; + }, onClickSelfAssign() { const { account_id, @@ -520,7 +538,7 @@ export default { }; this.assignedAgent = selfAssign; }, - async sendMessage() { + async onSendReply() { if (this.isReplyButtonDisabled) { return; } @@ -531,22 +549,31 @@ export default { } const messagePayload = this.getMessagePayload(newMessage); this.clearMessage(); - try { - await this.$store.dispatch( - 'createPendingMessageAndSend', - messagePayload - ); - bus.$emit(BUS_EVENTS.SCROLL_TO_MESSAGE); - } catch (error) { - const errorMessage = - error?.response?.data?.error || - this.$t('CONVERSATION.MESSAGE_ERROR'); - this.showAlert(errorMessage); - } + this.sendMessage(messagePayload); this.hideEmojiPicker(); this.$emit('update:popoutReplyBox', false); } }, + async sendMessage(messagePayload) { + try { + await this.$store.dispatch( + 'createPendingMessageAndSend', + messagePayload + ); + bus.$emit(BUS_EVENTS.SCROLL_TO_MESSAGE); + } catch (error) { + const errorMessage = + error?.response?.data?.error || this.$t('CONVERSATION.MESSAGE_ERROR'); + this.showAlert(errorMessage); + } + }, + async onSendWhatsAppReply(messagePayload) { + this.sendMessage({ + conversationId: this.currentChat.id, + ...messagePayload, + }); + this.hideWhatsappTemplatesModal(); + }, replaceText(message) { setTimeout(() => { this.message = message; diff --git a/app/javascript/dashboard/components/widgets/conversation/WhatsappTemplates/Modal.vue b/app/javascript/dashboard/components/widgets/conversation/WhatsappTemplates/Modal.vue new file mode 100644 index 000000000..1c888408b --- /dev/null +++ b/app/javascript/dashboard/components/widgets/conversation/WhatsappTemplates/Modal.vue @@ -0,0 +1,76 @@ + + + + + diff --git a/app/javascript/dashboard/components/widgets/conversation/WhatsappTemplates/TemplateParser.vue b/app/javascript/dashboard/components/widgets/conversation/WhatsappTemplates/TemplateParser.vue new file mode 100644 index 000000000..7fc8ac431 --- /dev/null +++ b/app/javascript/dashboard/components/widgets/conversation/WhatsappTemplates/TemplateParser.vue @@ -0,0 +1,183 @@ + + + + + diff --git a/app/javascript/dashboard/components/widgets/conversation/WhatsappTemplates/TemplatesPicker.vue b/app/javascript/dashboard/components/widgets/conversation/WhatsappTemplates/TemplatesPicker.vue new file mode 100644 index 000000000..b9e9ba71c --- /dev/null +++ b/app/javascript/dashboard/components/widgets/conversation/WhatsappTemplates/TemplatesPicker.vue @@ -0,0 +1,163 @@ + + + + + diff --git a/app/javascript/dashboard/components/widgets/conversation/bubble/Actions.vue b/app/javascript/dashboard/components/widgets/conversation/bubble/Actions.vue index c1c3440bc..7f454fa20 100644 --- a/app/javascript/dashboard/components/widgets/conversation/bubble/Actions.vue +++ b/app/javascript/dashboard/components/widgets/conversation/bubble/Actions.vue @@ -7,7 +7,7 @@ @@ -47,6 +48,10 @@ export default { type: Boolean, deafaut: false, }, + styles: { + type: Object, + default: () => {}, + }, }, methods: { onChange(e) { diff --git a/app/javascript/dashboard/i18n/locale/en/index.js b/app/javascript/dashboard/i18n/locale/en/index.js index b21009322..d49420335 100644 --- a/app/javascript/dashboard/i18n/locale/en/index.js +++ b/app/javascript/dashboard/i18n/locale/en/index.js @@ -21,6 +21,7 @@ import { default as _setNewPassword } from './setNewPassword.json'; import { default as _settings } from './settings.json'; import { default as _signup } from './signup.json'; import { default as _teamsSettings } from './teamsSettings.json'; +import { default as _whatsappTemplates } from './whatsappTemplates.json'; import { default as _bulkActions } from './bulkActions.json'; export default { @@ -47,5 +48,6 @@ export default { ..._settings, ..._signup, ..._teamsSettings, + ..._whatsappTemplates, ..._bulkActions, }; diff --git a/app/javascript/dashboard/i18n/locale/en/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/en/whatsappTemplates.json new file mode 100644 index 000000000..bbcf28156 --- /dev/null +++ b/app/javascript/dashboard/i18n/locale/en/whatsappTemplates.json @@ -0,0 +1,25 @@ +{ + "WHATSAPP_TEMPLATES": { + "MODAL": { + "TITLE": "Whatsapp Templates", + "SUBTITLE": "Select the whatsapp template you want to send", + "TEMPLATE_SELECTED_SUBTITLE": "Process %{templateName}" + }, + "PICKER": { + "SEARCH_PLACEHOLDER": "Search Templates", + "NO_TEMPLATES_FOUND": "No templates found for", + "LABELS": { + "LANGUAGE": "Language", + "TEMPLATE_BODY": "Template Body", + "CATEGORY": "Category" + } + }, + "PARSER": { + "VARIABLES_LABEL": "Variables", + "VARIABLE_PLACEHOLDER": "Enter %{variable} value", + "GO_BACK_LABEL": "Go Back", + "SEND_MESSAGE_LABEL": "Send Message", + "FORM_ERROR_MESSAGE": "Please fill all variables before sending" + } + } +} diff --git a/app/javascript/dashboard/routes/dashboard/conversation/contact/ConversationForm.vue b/app/javascript/dashboard/routes/dashboard/conversation/contact/ConversationForm.vue index f2d262644..9dda5c02c 100644 --- a/app/javascript/dashboard/routes/dashboard/conversation/contact/ConversationForm.vue +++ b/app/javascript/dashboard/routes/dashboard/conversation/contact/ConversationForm.vue @@ -1,12 +1,12 @@