From 88cb5ba56ff86daff43006afce6c58d196d1db8b Mon Sep 17 00:00:00 2001 From: Pranav Date: Thu, 28 Aug 2025 06:02:27 -0700 Subject: [PATCH 1/9] fix: Widget now shows articles based on correct locale (#12316) # Pull Request Template ## Description This PR fixes an issue (CW-5529) where articles displayed on the widget were not respecting the current locale set in the URL (e.g., `/ar` was showing English articles). The root cause was that the article queries in the `_featured_articles.html.erb` and `_uncategorized-block.html.erb` view templates were missing locale filtering. The fix involves adding `locale: @locale` to the `articles.where` clauses in these templates to ensure that only articles matching the current portal locale are displayed. Fixes #CW-5529 ## Type of change - [x] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality not to work as expected) - [ ] This change requires a documentation update ## How Has This Been Tested? The changes were verified against existing test cases that confirm the expected behavior for locale-specific article filtering. Specifically, tests for the articles controller's `index` action and article count by locale were reviewed, which align with the implemented fixes. ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [ ] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works (N/A, relied on existing tests) - [ ] New and existing unit tests pass locally with my changes (Unable to run tests in this environment) - [ ] Any dependent changes have been merged and published in downstream modules --- Linear Issue: [CW-5529](https://linear.app/chatwoot/issue/CW-5529/articles-from-the-correct-locale-is-not-shown-on-the-widget) Open in Cursor Open in Web --------- Co-authored-by: Cursor Agent Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> --- .../pageComponents/Home/Article/ArticleContainer.vue | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/app/javascript/widget/components/pageComponents/Home/Article/ArticleContainer.vue b/app/javascript/widget/components/pageComponents/Home/Article/ArticleContainer.vue index e093d4316..07dcd2220 100644 --- a/app/javascript/widget/components/pageComponents/Home/Article/ArticleContainer.vue +++ b/app/javascript/widget/components/pageComponents/Home/Article/ArticleContainer.vue @@ -1,5 +1,5 @@ From 99997a701a18cf8db137d735dade1b3c6d5e7f02 Mon Sep 17 00:00:00 2001 From: Muhsin Keloth Date: Fri, 29 Aug 2025 16:13:25 +0530 Subject: [PATCH 2/9] feat: Add twilio content templates (#12277) Implements comprehensive Twilio WhatsApp content template support (Phase 1) enabling text, media, and quick reply templates with proper parameter conversion, sync capabilities, and feature flag protection. ### Features Implemented **Template Types Supported** - Basic Text Templates: Simple text with variables ({{1}}, {{2}}) - Media Templates: Image/Video/Document templates with text variables - Quick Reply Templates: Interactive button templates - Phase 2 (Future): List Picker, Call-to-Action, Catalog, Carousel, Authentication templates **Template Synchronization** - API Endpoint: POST /api/v1/accounts/{account_id}/inboxes/{inbox_id}/sync_templates - Background Job: Channels::Twilio::TemplatesSyncJob - Storage: JSONB format in channel_twilio_sms.content_templates - Auto-categorization: UTILITY, MARKETING, AUTHENTICATION categories ### Template Examples Tested #### Text template ``` { "name": "greet", "language": "en" } ``` #### Template with variables ``` { "name": "order_status", "parameters": [{"type": "body", "parameters": [{"text": "John"}]}] } ``` #### Media template with image ``` { "name": "product_showcase", "parameters": [ {"type": "header", "parameters": [{"image": {"link": "image.jpg"}}]}, {"type": "body", "parameters": [{"text": "iPhone"}, {"text": "$999"}]} ]} ``` #### Preview CleanShot 2025-08-26 at 10 01
51@2x CleanShot 2025-08-26 at 10 02
02@2x #### User guide https://www.chatwoot.com/hc/user-guide/articles/1756195741-twilio-content-templates --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> --- .../components/ActionButtons.vue | 34 ++- .../components/ComposeNewConversationForm.vue | 29 +- .../components/ContentTemplateForm.vue | 56 ++++ .../components/ContentTemplateSelector.vue | 124 ++++++++ .../helpers/composeConversationHelper.js | 3 +- .../ContentTemplateParser.vue | 278 ++++++++++++++++++ .../widgets/WootWriter/ReplyBottomPanel.vue | 16 +- .../ContentTemplatesModal.vue | 97 ++++++ .../ContentTemplatesPicker.vue | 169 +++++++++++ .../widgets/conversation/ReplyBox.vue | 29 ++ app/javascript/dashboard/helper/URLHelper.js | 20 ++ .../dashboard/helper/specs/URLHelper.spec.js | 55 ++++ .../dashboard/i18n/locale/en/contact.json | 9 + .../i18n/locale/en/contentTemplates.json | 51 ++++ .../dashboard/i18n/locale/en/index.js | 2 + .../inbox/settingsPage/ConfigurationPage.vue | 13 + app/javascript/shared/constants/messages.js | 6 + app/services/twilio/template_sync_service.rb | 33 ++- config/features.yml | 1 + 19 files changed, 1001 insertions(+), 24 deletions(-) create mode 100644 app/javascript/dashboard/components-next/NewConversation/components/ContentTemplateForm.vue create mode 100644 app/javascript/dashboard/components-next/NewConversation/components/ContentTemplateSelector.vue create mode 100644 app/javascript/dashboard/components-next/content-templates/ContentTemplateParser.vue create mode 100644 app/javascript/dashboard/components/widgets/conversation/ContentTemplates/ContentTemplatesModal.vue create mode 100644 app/javascript/dashboard/components/widgets/conversation/ContentTemplates/ContentTemplatesPicker.vue create mode 100644 app/javascript/dashboard/i18n/locale/en/contentTemplates.json diff --git a/app/javascript/dashboard/components-next/NewConversation/components/ActionButtons.vue b/app/javascript/dashboard/components-next/NewConversation/components/ActionButtons.vue index 6f2178408..773ebe315 100644 --- a/app/javascript/dashboard/components-next/NewConversation/components/ActionButtons.vue +++ b/app/javascript/dashboard/components-next/NewConversation/components/ActionButtons.vue @@ -11,12 +11,14 @@ import { extractTextFromMarkdown } from 'dashboard/helper/editorHelper'; import Button from 'dashboard/components-next/button/Button.vue'; import WhatsAppOptions from './WhatsAppOptions.vue'; +import ContentTemplateSelector from './ContentTemplateSelector.vue'; const props = defineProps({ attachedFiles: { type: Array, default: () => [] }, isWhatsappInbox: { type: Boolean, default: false }, isEmailOrWebWidgetInbox: { type: Boolean, default: false }, isTwilioSmsInbox: { type: Boolean, default: false }, + isTwilioWhatsAppInbox: { type: Boolean, default: false }, messageTemplates: { type: Array, default: () => [] }, channelType: { type: String, default: '' }, isLoading: { type: Boolean, default: false }, @@ -32,6 +34,7 @@ const emit = defineEmits([ 'discard', 'sendMessage', 'sendWhatsappMessage', + 'sendTwilioMessage', 'insertEmoji', 'addSignature', 'removeSignature', @@ -63,6 +66,20 @@ const sendWithSignature = computed(() => { return fetchSignatureFlagFromUISettings(props.channelType); }); +const showTwilioContentTemplates = computed(() => { + return props.isTwilioWhatsAppInbox && props.inboxId; +}); + +const shouldShowEmojiButton = computed(() => { + return ( + !props.isWhatsappInbox && !props.isTwilioWhatsAppInbox && !props.hasNoInbox + ); +}); + +const isRegularMessageMode = computed(() => { + return !props.isWhatsappInbox && !props.isTwilioWhatsAppInbox; +}); + const setSignature = () => { if (signatureToApply.value) { if (sendWithSignature.value) { @@ -125,7 +142,7 @@ const keyboardEvents = { action: () => { if ( isEditorHotKeyEnabled('enter') && - !props.isWhatsappInbox && + isRegularMessageMode.value && !props.isDropdownActive ) { emit('sendMessage'); @@ -136,7 +153,7 @@ const keyboardEvents = { action: () => { if ( isEditorHotKeyEnabled('cmd_enter') && - !props.isWhatsappInbox && + isRegularMessageMode.value && !props.isDropdownActive ) { emit('sendMessage'); @@ -158,8 +175,13 @@ useKeyboardEvents(keyboardEvents); :message-templates="messageTemplates" @send-message="emit('sendWhatsappMessage', $event)" /> +
@@ -172,7 +194,7 @@ useKeyboardEvents(keyboardEvents); />
@@ -199,7 +221,7 @@ useKeyboardEvents(keyboardEvents); />