-
+
+
+
+
+ {{ buildSelectedCountLabel }}
+
+
{{
$t('CAPTAIN.RESPONSES.SELECTED', {
@@ -322,17 +338,23 @@ onMounted(() => {
-
+
From cc8866a84ec2b36b77ff8e979dbcc66ebbacbc87 Mon Sep 17 00:00:00 2001
From: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
Date: Tue, 24 Jun 2025 19:52:44 +0530
Subject: [PATCH 07/11] feat: Sync Popular Articles locale with widget locale
(#11754)
# Pull Request Template
## Description
This PR includes the following improvements:
* **Popular Articles Locale Selection based on Widget Locale**
* Implements priority-based locale matching:
* Exact locale match (e.g., "fr" === "fr")
* Base language match (e.g., "fr" when selected is "fr_CA")
* Variant match (e.g., "fr_BE" when selected is "fr")
* Removes default locale fallback - if no locale match is found, popular
articles section is hidden.
* Fixed **API** filter issue where the locale parameter was previously
ignored
* Hides Popular Articles section completely when no locale match is
found and Only shows relevant articles in the user's language
* **RTL Direction Handling Improvements**
* Now directly reads the `lang` attribute from HTML element `` instead of relying on `.locale-switcher` and sets direction
attribute based on language.
* Adds `data-dir-applied` attribute to prevent overlapping direction
settings between global helpers and components (eg case: Insert article
in editor dashboard)
* Update `IframeLoader.vue` to Composition API and improve the **dir**
logic
Fixes
1.
[CW-4505](https://linear.app/chatwoot/issue/CW-4505/popular-articles-not-displayed-based-on-user-locale-in-live-chat),
https://github.com/chatwoot/chatwoot/issues/11745
2. RTL direction is not working in widget article view after merging
this PR https://github.com/chatwoot/chatwoot/pull/11692
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
## How Has This Been Tested?
### Loom video
**Popular Articles**
https://www.loom.com/share/7cecbaaa77eb48e19263398b6ba8ddef?sid=a2452b8e-7d7e-46a3-b5c8-aed5ab5bc801
**RTL improvements**
https://www.loom.com/share/3ccad77174a0412097e802641df5f3e0?sid=e10ac57f-5c49-4084-84d3-5ad58aee54fa
## Checklist:
- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my code
- [x] 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
- [x] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged and published in downstream
modules
---------
Co-authored-by: Muhsin Keloth
---
.../api/v1/portals/articles_controller.rb | 4 +
.../components/ArticleSearch/ArticleView.vue | 5 +-
app/javascript/portal/portalHelpers.js | 11 +-
.../shared/components/IframeLoader.vue | 105 ++++++++----------
app/javascript/shared/helpers/portalHelper.js | 40 +++++++
.../shared/helpers/specs/portalHelper.spec.js | 28 +++++
.../Home/Article/ArticleContainer.vue | 18 +--
.../widget/store/modules/articles.js | 1 +
app/javascript/widget/views/ArticleViewer.vue | 10 +-
.../v1/portals/articles_controller_spec.rb | 8 ++
10 files changed, 148 insertions(+), 82 deletions(-)
create mode 100644 app/javascript/shared/helpers/portalHelper.js
create mode 100644 app/javascript/shared/helpers/specs/portalHelper.spec.js
diff --git a/app/controllers/public/api/v1/portals/articles_controller.rb b/app/controllers/public/api/v1/portals/articles_controller.rb
index 32a147d34..02023559b 100644
--- a/app/controllers/public/api/v1/portals/articles_controller.rb
+++ b/app/controllers/public/api/v1/portals/articles_controller.rb
@@ -7,7 +7,11 @@ class Public::Api::V1::Portals::ArticlesController < Public::Api::V1::Portals::B
def index
@articles = @portal.articles.published.includes(:category, :author)
+
+ @articles = @articles.where(locale: permitted_params[:locale]) if permitted_params[:locale].present?
+
@articles_count = @articles.count
+
search_articles
order_by_sort_param
limit_results
diff --git a/app/javascript/dashboard/routes/dashboard/helpcenter/components/ArticleSearch/ArticleView.vue b/app/javascript/dashboard/routes/dashboard/helpcenter/components/ArticleSearch/ArticleView.vue
index f2a2ab4d6..7c7dd0040 100644
--- a/app/javascript/dashboard/routes/dashboard/helpcenter/components/ArticleSearch/ArticleView.vue
+++ b/app/javascript/dashboard/routes/dashboard/helpcenter/components/ArticleSearch/ArticleView.vue
@@ -1,6 +1,7 @@
@@ -66,6 +58,7 @@ export default {
diff --git a/app/javascript/shared/helpers/portalHelper.js b/app/javascript/shared/helpers/portalHelper.js
new file mode 100644
index 000000000..a9e92a92a
--- /dev/null
+++ b/app/javascript/shared/helpers/portalHelper.js
@@ -0,0 +1,40 @@
+/**
+ * Determine the best-matching locale from the list of locales allowed by the portal.
+ *
+ * The matching happens in the following order:
+ * 1. Exact match – the visitor-selected locale equals one in the `allowedLocales` list
+ * (e.g., `fr` ➜ `fr`).
+ * 2. Base language match – the base part of a compound locale (before the underscore)
+ * matches (e.g., `fr_CA` ➜ `fr`).
+ * 3. Variant match – when the base language is selected but a regional variant exists
+ * in the portal list (e.g., `fr` ➜ `fr_BE`).
+ *
+ * If none of these rules find a match, the function returns `null`,
+ * Don't show popular articles if locale doesn't match with allowed locales
+ *
+ * @export
+ * @param {string} selectedLocale The locale selected by the visitor (e.g., `fr_CA`).
+ * @param {string[]} allowedLocales Array of locales enabled for the portal.
+ * @returns {(string|null)} A locale string that should be used, or `null` if no suitable match.
+ */
+export const getMatchingLocale = (selectedLocale = '', allowedLocales = []) => {
+ // Ensure inputs are valid
+ if (
+ !selectedLocale ||
+ !Array.isArray(allowedLocales) ||
+ !allowedLocales.length
+ ) {
+ return null;
+ }
+
+ const [lang] = selectedLocale.split('_');
+
+ const priorityMatches = [
+ selectedLocale, // exact match
+ lang, // base language match
+ allowedLocales.find(l => l.startsWith(`${lang}_`)), // first variant match
+ ];
+
+ // Return the first match that exists in the allowed list, or null
+ return priorityMatches.find(l => l && allowedLocales.includes(l)) ?? null;
+};
diff --git a/app/javascript/shared/helpers/specs/portalHelper.spec.js b/app/javascript/shared/helpers/specs/portalHelper.spec.js
new file mode 100644
index 000000000..e79c9a3d9
--- /dev/null
+++ b/app/javascript/shared/helpers/specs/portalHelper.spec.js
@@ -0,0 +1,28 @@
+import { getMatchingLocale } from 'shared/helpers/portalHelper';
+
+describe('portalHelper - getMatchingLocale', () => {
+ it('returns exact match when present', () => {
+ const result = getMatchingLocale('fr', ['en', 'fr']);
+ expect(result).toBe('fr');
+ });
+
+ it('returns base language match when exact variant not present', () => {
+ const result = getMatchingLocale('fr_CA', ['en', 'fr']);
+ expect(result).toBe('fr');
+ });
+
+ it('returns variant match when base language not present', () => {
+ const result = getMatchingLocale('fr', ['en', 'fr_BE']);
+ expect(result).toBe('fr_BE');
+ });
+
+ it('returns null when no match found', () => {
+ const result = getMatchingLocale('de', ['en', 'fr']);
+ expect(result).toBeNull();
+ });
+
+ it('returns null for invalid inputs', () => {
+ expect(getMatchingLocale('', [])).toBeNull();
+ expect(getMatchingLocale(null, null)).toBeNull();
+ });
+});
diff --git a/app/javascript/widget/components/pageComponents/Home/Article/ArticleContainer.vue b/app/javascript/widget/components/pageComponents/Home/Article/ArticleContainer.vue
index 1d5fdc99d..e093d4316 100644
--- a/app/javascript/widget/components/pageComponents/Home/Article/ArticleContainer.vue
+++ b/app/javascript/widget/components/pageComponents/Home/Article/ArticleContainer.vue
@@ -7,6 +7,7 @@ import { useRouter } from 'vue-router';
import { useStore } from 'dashboard/composables/store';
import { useMapGetter } from 'dashboard/composables/store.js';
import { useDarkMode } from 'widget/composables/useDarkMode';
+import { getMatchingLocale } from 'shared/helpers/portalHelper';
const store = useStore();
const router = useRouter();
@@ -20,17 +21,8 @@ const articleUiFlags = useMapGetter('article/uiFlags');
const locale = computed(() => {
const { locale: selectedLocale } = i18n;
- const {
- allowed_locales: allowedLocales,
- default_locale: defaultLocale = 'en',
- } = portal.value.config;
- // IMPORTANT: Variation strict locale matching, Follow iso_639_1_code
- // If the exact match of a locale is available in the list of portal locales, return it
- // Else return the default locale. Eg: `es` will not work if `es_ES` is available in the list
- if (allowedLocales.includes(selectedLocale)) {
- return locale;
- }
- return defaultLocale;
+ const { allowed_locales: allowedLocales } = portal.value.config;
+ return getMatchingLocale(selectedLocale.value, allowedLocales);
});
const fetchArticles = () => {
@@ -46,6 +38,7 @@ const openArticleInArticleViewer = link => {
const params = new URLSearchParams({
show_plain_layout: 'true',
theme: prefersDarkMode.value ? 'dark' : 'light',
+ ...(locale.value && { locale: locale.value }),
});
// Combine link with query parameters
@@ -64,7 +57,8 @@ const hasArticles = computed(
() =>
!articleUiFlags.value.isFetching &&
!articleUiFlags.value.isError &&
- !!popularArticles.value.length
+ !!popularArticles.value.length &&
+ !!locale.value
);
onMounted(() => fetchArticles());
diff --git a/app/javascript/widget/store/modules/articles.js b/app/javascript/widget/store/modules/articles.js
index 27faa297b..2ad6ae1dd 100644
--- a/app/javascript/widget/store/modules/articles.js
+++ b/app/javascript/widget/store/modules/articles.js
@@ -23,6 +23,7 @@ export const actions = {
commit('setError', false);
try {
+ if (!locale) return;
const cachedData = getFromCache(`${CACHE_KEY_PREFIX}${slug}_${locale}`);
if (cachedData) {
commit('setArticles', cachedData);
diff --git a/app/javascript/widget/views/ArticleViewer.vue b/app/javascript/widget/views/ArticleViewer.vue
index d3e1f9437..9289d0546 100644
--- a/app/javascript/widget/views/ArticleViewer.vue
+++ b/app/javascript/widget/views/ArticleViewer.vue
@@ -1,24 +1,16 @@
-
+
diff --git a/spec/controllers/public/api/v1/portals/articles_controller_spec.rb b/spec/controllers/public/api/v1/portals/articles_controller_spec.rb
index e6429a1de..2d8c5fcd5 100644
--- a/spec/controllers/public/api/v1/portals/articles_controller_spec.rb
+++ b/spec/controllers/public/api/v1/portals/articles_controller_spec.rb
@@ -65,6 +65,14 @@ RSpec.describe 'Public Articles API', type: :request do
expect(response).to have_http_status(:success)
response_data = JSON.parse(response.body, symbolize_names: true)[:payload]
expect(response_data.length).to eq(2)
+ # Only count articles in the current locale (category.locale is 'en')
+ expect(JSON.parse(response.body, symbolize_names: true)[:meta][:articles_count]).to eq(3)
+ end
+
+ it 'returns articles count from all locales when locale parameter is not present' do
+ get "/hc/#{portal.slug}/articles.json"
+
+ expect(response).to have_http_status(:success)
expect(JSON.parse(response.body, symbolize_names: true)[:meta][:articles_count]).to eq(5)
end
From c42dd8a23e949e746b666852f712350e2d0d5cd1 Mon Sep 17 00:00:00 2001
From: Tanmay Deep Sharma <32020192+tds-1@users.noreply.github.com>
Date: Wed, 25 Jun 2025 07:30:20 +0530
Subject: [PATCH 08/11] feat: captain should be able to access private notes
(#11768)
# Pull Request Template
## Linear task:
https://linear.app/chatwoot/issue/CW-4482/captain-should-be-able-to-access-private-notes-only-on-copilot
## Description
Captain should be able to access private notes (only on copilot)
## Type of change
- [x] New feature (non-breaking change which adds functionality)
## How Has This Been Tested?


## Checklist:
- [ ] My code follows the style guidelines of this project
- [ ] 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
- [ ] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged and published in downstream
modules
---------
Co-authored-by: Muhsin Keloth
---
.../conversation_llm_formatter.rb | 12 +++++++---
.../tools/copilot/get_conversation_service.rb | 2 +-
.../copilot/get_conversation_service_spec.rb | 23 +++++++++++++++++++
3 files changed, 33 insertions(+), 4 deletions(-)
diff --git a/app/services/llm_formatter/conversation_llm_formatter.rb b/app/services/llm_formatter/conversation_llm_formatter.rb
index a43471e5b..8654e0adf 100644
--- a/app/services/llm_formatter/conversation_llm_formatter.rb
+++ b/app/services/llm_formatter/conversation_llm_formatter.rb
@@ -5,7 +5,7 @@ class LlmFormatter::ConversationLlmFormatter < LlmFormatter::DefaultLlmFormatter
sections << "Channel: #{@record.inbox.channel.name}"
sections << 'Message History:'
sections << if @record.messages.any?
- build_messages
+ build_messages(config)
else
'No messages in this conversation'
end
@@ -23,11 +23,16 @@ class LlmFormatter::ConversationLlmFormatter < LlmFormatter::DefaultLlmFormatter
private
- def build_messages
+ def build_messages(config = {})
return "No messages in this conversation\n" if @record.messages.empty?
message_text = ''
- @record.messages.chat.order(created_at: :asc).each do |message|
+ messages = @record.messages.where.not(message_type: :activity).order(created_at: :asc)
+
+ messages.each do |message|
+ # Skip private messages unless explicitly included in config
+ next if message.private? && !config[:include_private_messages]
+
message_text << format_message(message)
end
message_text
@@ -35,6 +40,7 @@ class LlmFormatter::ConversationLlmFormatter < LlmFormatter::DefaultLlmFormatter
def format_message(message)
sender = message.message_type == 'incoming' ? 'User' : 'Support agent'
+ sender = "[Private Note] #{sender}" if message.private?
"#{sender}: #{message.content}\n"
end
diff --git a/enterprise/app/services/captain/tools/copilot/get_conversation_service.rb b/enterprise/app/services/captain/tools/copilot/get_conversation_service.rb
index 64b52d012..6f942ee8e 100644
--- a/enterprise/app/services/captain/tools/copilot/get_conversation_service.rb
+++ b/enterprise/app/services/captain/tools/copilot/get_conversation_service.rb
@@ -30,7 +30,7 @@ class Captain::Tools::Copilot::GetConversationService < Captain::Tools::BaseServ
conversation = Conversation.find_by(display_id: conversation_id, account_id: @assistant.account_id)
return 'Conversation not found' if conversation.blank?
- conversation.to_llm_text
+ conversation.to_llm_text(include_private_messages: true)
end
def active?
diff --git a/spec/enterprise/services/captain/tools/copilot/get_conversation_service_spec.rb b/spec/enterprise/services/captain/tools/copilot/get_conversation_service_spec.rb
index 4d7f1adc7..b8265bbbf 100644
--- a/spec/enterprise/services/captain/tools/copilot/get_conversation_service_spec.rb
+++ b/spec/enterprise/services/captain/tools/copilot/get_conversation_service_spec.rb
@@ -128,6 +128,29 @@ RSpec.describe Captain::Tools::Copilot::GetConversationService do
expect(result).to eq(conversation.to_llm_text)
end
+ it 'includes private messages in the llm text format' do
+ # Create a regular message
+ create(:message,
+ conversation: conversation,
+ message_type: 'outgoing',
+ content: 'Regular message',
+ private: false)
+
+ # Create a private message
+ create(:message,
+ conversation: conversation,
+ message_type: 'outgoing',
+ content: 'Private note content',
+ private: true)
+
+ result = service.execute({ 'conversation_id' => conversation.display_id })
+
+ # Verify that the result includes both regular and private messages
+ expect(result).to include('Regular message')
+ expect(result).to include('Private note content')
+ expect(result).to include('[Private Note]')
+ end
+
context 'when conversation belongs to different account' do
let(:other_account) { create(:account) }
let(:other_inbox) { create(:inbox, account: other_account) }
From a0bf6055fc16224d9d98471d46fb1aee60813f02 Mon Sep 17 00:00:00 2001
From: Pranav
Date: Wed, 25 Jun 2025 10:45:11 -0700
Subject: [PATCH 09/11] fix: Upgrade letter_opener to fix `cannot load such
file -- kconv error` (#11809)
The email jobs were failing in local development due to a cannot load
such file -- kconv error. This was caused by a removed dependency in the
latest version of the letter_opener gem. Upgrading to the latest version
resolves the issue.
---
Gemfile.lock | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/Gemfile.lock b/Gemfile.lock
index d9908f5e1..8315a5374 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -172,6 +172,8 @@ GEM
bundler (>= 1.2.0, < 3)
thor (~> 1.0)
byebug (11.1.3)
+ childprocess (5.1.0)
+ logger (~> 1.5)
climate_control (1.2.0)
coderay (1.1.3)
commonmarker (0.23.10)
@@ -433,10 +435,12 @@ GEM
json (>= 1.8)
rexml
language_server-protocol (3.17.0.5)
- launchy (2.5.2)
+ launchy (3.1.1)
addressable (~> 2.8)
- letter_opener (1.8.1)
- launchy (>= 2.2, < 3)
+ childprocess (~> 5.0)
+ logger (~> 1.6)
+ letter_opener (1.10.0)
+ launchy (>= 2.2, < 4)
line-bot-api (1.28.0)
lint_roller (1.1.0)
liquid (5.4.0)
@@ -563,7 +567,7 @@ GEM
method_source (~> 1.0)
pry-rails (0.3.9)
pry (>= 0.10.4)
- public_suffix (6.0.0)
+ public_suffix (6.0.2)
puma (6.4.3)
nio4r (~> 2.0)
pundit (2.3.0)
From 97efd36bc56defcbfc3b0928a4a00c0bc061b9eb Mon Sep 17 00:00:00 2001
From: Muhsin Keloth
Date: Thu, 26 Jun 2025 01:19:58 +0530
Subject: [PATCH 10/11] fix: Add missing CSAT URL in email reply templates
(#11808)
As part of PR [#11622](https://github.com/chatwoot/chatwoot/pull/11622),
we intentionally hide CSAT survey URLs from agents in the dashboard to
avoid exposing those links internally. However, email replies being
external communications to contacts should still include the survey
URLs.
We missed handling that specific case for email channels. All other
external channels like WhatsApp, Instagram, Telegram, etc., were already
working correctly and continue to include the survey URLs as intended.
---------
Co-authored-by: Pranav
---
.../email_reply.html.erb | 2 +-
.../mailers/conversation_reply_mailer_spec.rb | 21 +++++++++++++++++++
.../message_content_presenter_spec.rb | 17 ++++++++-------
3 files changed, 31 insertions(+), 9 deletions(-)
diff --git a/app/views/mailers/conversation_reply_mailer/email_reply.html.erb b/app/views/mailers/conversation_reply_mailer/email_reply.html.erb
index 319ba4b4e..feb5dff96 100644
--- a/app/views/mailers/conversation_reply_mailer/email_reply.html.erb
+++ b/app/views/mailers/conversation_reply_mailer/email_reply.html.erb
@@ -1,5 +1,5 @@
<% if @message.content %>
- <%= ChatwootMarkdownRenderer.new(@message.content).render_message %>
+ <%= ChatwootMarkdownRenderer.new(@message.outgoing_content).render_message %>
<% end %>
<% if @large_attachments.present? %>
Attachments:
diff --git a/spec/mailers/conversation_reply_mailer_spec.rb b/spec/mailers/conversation_reply_mailer_spec.rb
index 8485fcf7a..2a0d6c8b0 100644
--- a/spec/mailers/conversation_reply_mailer_spec.rb
+++ b/spec/mailers/conversation_reply_mailer_spec.rb
@@ -154,6 +154,27 @@ RSpec.describe ConversationReplyMailer do
expect(mail.message_id).to eq message.source_id
end
+ context 'when message is a CSAT survey' do
+ let(:csat_message) do
+ create(:message, conversation: conversation, account: account, message_type: 'template',
+ content_type: 'input_csat', content: 'How would you rate our support?', sender: agent)
+ end
+
+ it 'includes CSAT survey URL in outgoing_content' do
+ with_modified_env 'FRONTEND_URL' => 'https://app.chatwoot.com' do
+ mail = described_class.email_reply(csat_message).deliver_now
+ expect(mail.decoded).to include "https://app.chatwoot.com/survey/responses/#{conversation.uuid}"
+ end
+ end
+
+ it 'uses outgoing_content for CSAT message body' do
+ with_modified_env 'FRONTEND_URL' => 'https://app.chatwoot.com' do
+ mail = described_class.email_reply(csat_message).deliver_now
+ expect(mail.decoded).to include csat_message.outgoing_content
+ end
+ end
+ end
+
context 'with email attachments' do
it 'includes small attachments as email attachments' do
message_with_attachment = create(:message, conversation: conversation, account: account, message_type: 'outgoing',
diff --git a/spec/presenters/message_content_presenter_spec.rb b/spec/presenters/message_content_presenter_spec.rb
index b1be37ef2..f85bdb8d1 100644
--- a/spec/presenters/message_content_presenter_spec.rb
+++ b/spec/presenters/message_content_presenter_spec.rb
@@ -34,20 +34,21 @@ RSpec.describe MessageContentPresenter do
before do
allow(message.inbox).to receive(:web_widget?).and_return(false)
- allow(ENV).to receive(:fetch).with('FRONTEND_URL', nil).and_return('https://app.chatwoot.com')
end
it 'returns I18n default message when no CSAT config and dynamically generates survey URL' do
- expected_url = "https://app.chatwoot.com/survey/responses/#{conversation.uuid}"
- allow(I18n).to receive(:t).with('conversations.survey.response', link: expected_url)
- .and_return("Please rate this conversation, #{expected_url}")
- expect(presenter.outgoing_content).to eq("Please rate this conversation, #{expected_url}")
+ with_modified_env 'FRONTEND_URL' => 'https://app.chatwoot.com' do
+ expected_url = "https://app.chatwoot.com/survey/responses/#{conversation.uuid}"
+ expect(presenter.outgoing_content).to include(expected_url)
+ end
end
it 'returns CSAT config message when config exists and dynamically generates survey URL' do
- allow(message.inbox).to receive(:csat_config).and_return({ 'message' => 'Custom CSAT message' })
- expected_url = "https://app.chatwoot.com/survey/responses/#{conversation.uuid}"
- expect(presenter.outgoing_content).to eq("Custom CSAT message #{expected_url}")
+ with_modified_env 'FRONTEND_URL' => 'https://app.chatwoot.com' do
+ allow(message.inbox).to receive(:csat_config).and_return({ 'message' => 'Custom CSAT message' })
+ expected_url = "https://app.chatwoot.com/survey/responses/#{conversation.uuid}"
+ expect(presenter.outgoing_content).to eq("Custom CSAT message #{expected_url}")
+ end
end
end
end
From b7f2c151bf6384dd6a604edc58c64f0280b7c39a Mon Sep 17 00:00:00 2001
From: Sojan Jose
Date: Wed, 25 Jun 2025 14:21:03 -0700
Subject: [PATCH 11/11] feat: Voice channel creation Flow (#11775)
This PR introduces a new channel type for voice conversations.
ref: #11481
## Changes
- Add database migration for channel_voice table with phone_number and
provider_config
- Create Channel::Voice model with E.164 phone number validation and
Twilio config validation
- Add voice channel association to Account model
- Extend inbox helpers and types to support voice channels
- Add voice channel setup UI with Twilio configuration form
- Include voice channel in channel factory and list components
- Add API routes and store actions for voice channel creation
- Add comprehensive translations for voice channel management
---------
Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
Co-authored-by: Muhsin Keloth
Co-authored-by: iamsivin
---
.../api/v1/accounts/inboxes_controller.rb | 6 +-
.../components-next/icon/provider.js | 1 +
.../icon/specs/provider.spec.js | 6 +
.../dashboard/components-next/input/Input.vue | 60 ++----
.../dashboard/components/ChannelSelector.vue | 2 +-
.../components/widgets/ChannelItem.vue | 5 +
.../dashboard/composables/useInbox.js | 5 +
app/javascript/dashboard/helper/inbox.js | 10 +
.../dashboard/i18n/locale/en/inboxMgmt.json | 43 +++-
.../settings/inbox/ChannelFactory.vue | 2 +
.../dashboard/settings/inbox/ChannelList.vue | 1 +
.../settings/inbox/channels/Voice.vue | 184 ++++++++++++++++++
.../settings/inbox/components/ChannelName.vue | 1 +
.../dashboard/store/modules/inboxes.js | 30 +--
.../store/modules/inboxes/channelActions.js | 52 +++++
.../modules/specs/inboxes/actions.spec.js | 22 +++
app/javascript/shared/mixins/inboxMixin.js | 3 +
config/features.yml | 6 +-
.../20250620120000_create_channel_voice.rb | 16 ++
db/schema.rb | 16 +-
.../api/v1/accounts/inboxes_controller.rb | 24 +++
enterprise/app/models/channel/voice.rb | 64 ++++++
.../app/models/enterprise/concerns/account.rb | 1 +
.../images/dashboard/channels/voice.png | Bin 0 -> 1352277 bytes
.../v1/accounts/inboxes_controller_spec.rb | 16 ++
spec/enterprise/models/channel/voice_spec.rb | 60 ++++++
spec/factories/channel/channel_voice.rb | 20 ++
27 files changed, 584 insertions(+), 72 deletions(-)
create mode 100644 app/javascript/dashboard/routes/dashboard/settings/inbox/channels/Voice.vue
create mode 100644 app/javascript/dashboard/store/modules/inboxes/channelActions.js
create mode 100644 db/migrate/20250620120000_create_channel_voice.rb
create mode 100644 enterprise/app/models/channel/voice.rb
create mode 100644 public/assets/images/dashboard/channels/voice.png
create mode 100644 spec/enterprise/models/channel/voice_spec.rb
create mode 100644 spec/factories/channel/channel_voice.rb
diff --git a/app/controllers/api/v1/accounts/inboxes_controller.rb b/app/controllers/api/v1/accounts/inboxes_controller.rb
index 61d16b2ca..e7b3b197b 100644
--- a/app/controllers/api/v1/accounts/inboxes_controller.rb
+++ b/app/controllers/api/v1/accounts/inboxes_controller.rb
@@ -81,11 +81,15 @@ class Api::V1::Accounts::InboxesController < Api::V1::Accounts::BaseController
end
def create_channel
- return unless %w[web_widget api email line telegram whatsapp sms].include?(permitted_params[:channel][:type])
+ return unless allowed_channel_types.include?(permitted_params[:channel][:type])
account_channels_method.create!(permitted_params(channel_type_from_params::EDITABLE_ATTRS)[:channel].except(:type))
end
+ def allowed_channel_types
+ %w[web_widget api email line telegram whatsapp sms]
+ end
+
def update_inbox_working_hours
@inbox.update_working_hours(params.permit(working_hours: Inbox::OFFISABLE_ATTRS)[:working_hours]) if params[:working_hours]
end
diff --git a/app/javascript/dashboard/components-next/icon/provider.js b/app/javascript/dashboard/components-next/icon/provider.js
index 9c0a24925..36dd6216e 100644
--- a/app/javascript/dashboard/components-next/icon/provider.js
+++ b/app/javascript/dashboard/components-next/icon/provider.js
@@ -13,6 +13,7 @@ export function useChannelIcon(inbox) {
'Channel::WebWidget': 'i-ri-global-fill',
'Channel::Whatsapp': 'i-ri-whatsapp-fill',
'Channel::Instagram': 'i-ri-instagram-fill',
+ 'Channel::Voice': 'i-ri-phone-fill',
};
const providerIconMap = {
diff --git a/app/javascript/dashboard/components-next/icon/specs/provider.spec.js b/app/javascript/dashboard/components-next/icon/specs/provider.spec.js
index df30d7138..5860e30ea 100644
--- a/app/javascript/dashboard/components-next/icon/specs/provider.spec.js
+++ b/app/javascript/dashboard/components-next/icon/specs/provider.spec.js
@@ -19,6 +19,12 @@ describe('useChannelIcon', () => {
expect(icon).toBe('i-ri-whatsapp-fill');
});
+ it('returns correct icon for Voice channel', () => {
+ const inbox = { channel_type: 'Channel::Voice' };
+ const { value: icon } = useChannelIcon(inbox);
+ expect(icon).toBe('i-ri-phone-fill');
+ });
+
describe('Email channel', () => {
it('returns mail icon for generic email channel', () => {
const inbox = { channel_type: 'Channel::Email' };
diff --git a/app/javascript/dashboard/components-next/input/Input.vue b/app/javascript/dashboard/components-next/input/Input.vue
index ea6eb0417..ed6d7a20b 100644
--- a/app/javascript/dashboard/components-next/input/Input.vue
+++ b/app/javascript/dashboard/components-next/input/Input.vue
@@ -1,51 +1,21 @@
+
+
+
+
diff --git a/app/javascript/dashboard/routes/dashboard/settings/inbox/components/ChannelName.vue b/app/javascript/dashboard/routes/dashboard/settings/inbox/components/ChannelName.vue
index b7b0a2c1d..4ff8b1ab1 100644
--- a/app/javascript/dashboard/routes/dashboard/settings/inbox/components/ChannelName.vue
+++ b/app/javascript/dashboard/routes/dashboard/settings/inbox/components/ChannelName.vue
@@ -29,6 +29,7 @@ const i18nMap = {
'Channel::Line': 'LINE',
'Channel::Api': 'API',
'Channel::Instagram': 'INSTAGRAM',
+ 'Channel::Voice': 'VOICE',
};
const twilioChannelName = () => {
diff --git a/app/javascript/dashboard/store/modules/inboxes.js b/app/javascript/dashboard/store/modules/inboxes.js
index 32d91fb8e..aef95d1b1 100644
--- a/app/javascript/dashboard/store/modules/inboxes.js
+++ b/app/javascript/dashboard/store/modules/inboxes.js
@@ -9,29 +9,7 @@ import { throwErrorMessage } from '../utils/api';
import AnalyticsHelper from '../../helper/AnalyticsHelper';
import camelcaseKeys from 'camelcase-keys';
import { ACCOUNT_EVENTS } from '../../helper/AnalyticsHelper/events';
-
-const buildInboxData = inboxParams => {
- const formData = new FormData();
- const { channel = {}, ...inboxProperties } = inboxParams;
- Object.keys(inboxProperties).forEach(key => {
- formData.append(key, inboxProperties[key]);
- });
- const { selectedFeatureFlags, ...channelParams } = channel;
- // selectedFeatureFlags needs to be empty when creating a website channel
- if (selectedFeatureFlags) {
- if (selectedFeatureFlags.length) {
- selectedFeatureFlags.forEach(featureFlag => {
- formData.append(`channel[selected_feature_flags][]`, featureFlag);
- });
- } else {
- formData.append('channel[selected_feature_flags][]', '');
- }
- }
- Object.keys(channelParams).forEach(key => {
- formData.append(`channel[${key}]`, channel[key]);
- });
- return formData;
-};
+import { channelActions, buildInboxData } from './inboxes/channelActions';
export const state = {
records: [],
@@ -220,6 +198,12 @@ export const actions = {
throw new Error(error);
}
},
+ ...channelActions,
+ // TODO: Extract other create channel methods to separate files to reduce file size
+ // - createChannel
+ // - createWebsiteChannel
+ // - createTwilioChannel
+ // - createFBChannel
updateInbox: async ({ commit }, { id, formData = true, ...inboxParams }) => {
commit(types.default.SET_INBOXES_UI_FLAG, { isUpdating: true });
try {
diff --git a/app/javascript/dashboard/store/modules/inboxes/channelActions.js b/app/javascript/dashboard/store/modules/inboxes/channelActions.js
new file mode 100644
index 000000000..9975d8d1f
--- /dev/null
+++ b/app/javascript/dashboard/store/modules/inboxes/channelActions.js
@@ -0,0 +1,52 @@
+import * as types from '../../mutation-types';
+import InboxesAPI from '../../../api/inboxes';
+import AnalyticsHelper from '../../../helper/AnalyticsHelper';
+import { ACCOUNT_EVENTS } from '../../../helper/AnalyticsHelper/events';
+
+export const buildInboxData = inboxParams => {
+ const formData = new FormData();
+ const { channel = {}, ...inboxProperties } = inboxParams;
+ Object.keys(inboxProperties).forEach(key => {
+ formData.append(key, inboxProperties[key]);
+ });
+ const { selectedFeatureFlags, ...channelParams } = channel;
+ // selectedFeatureFlags needs to be empty when creating a website channel
+ if (selectedFeatureFlags) {
+ if (selectedFeatureFlags.length) {
+ selectedFeatureFlags.forEach(featureFlag => {
+ formData.append(`channel[selected_feature_flags][]`, featureFlag);
+ });
+ } else {
+ formData.append('channel[selected_feature_flags][]', '');
+ }
+ }
+ Object.keys(channelParams).forEach(key => {
+ formData.append(`channel[${key}]`, channel[key]);
+ });
+ return formData;
+};
+
+const sendAnalyticsEvent = channelType => {
+ AnalyticsHelper.track(ACCOUNT_EVENTS.ADDED_AN_INBOX, {
+ channelType,
+ });
+};
+
+export const channelActions = {
+ createVoiceChannel: async ({ commit }, params) => {
+ try {
+ commit(types.default.SET_INBOXES_UI_FLAG, { isCreating: true });
+ const response = await InboxesAPI.create({
+ name: params.name,
+ channel: { ...params.voice, type: 'voice' },
+ });
+ commit(types.default.ADD_INBOXES, response.data);
+ commit(types.default.SET_INBOXES_UI_FLAG, { isCreating: false });
+ sendAnalyticsEvent('voice');
+ return response.data;
+ } catch (error) {
+ commit(types.default.SET_INBOXES_UI_FLAG, { isCreating: false });
+ throw error;
+ }
+ },
+};
diff --git a/app/javascript/dashboard/store/modules/specs/inboxes/actions.spec.js b/app/javascript/dashboard/store/modules/specs/inboxes/actions.spec.js
index 8e917467a..a91addaa3 100644
--- a/app/javascript/dashboard/store/modules/specs/inboxes/actions.spec.js
+++ b/app/javascript/dashboard/store/modules/specs/inboxes/actions.spec.js
@@ -62,6 +62,28 @@ describe('#actions', () => {
});
});
+ describe('#createVoiceChannel', () => {
+ it('sends correct actions if API is success', async () => {
+ axios.post.mockResolvedValue({ data: inboxList[0] });
+ await actions.createVoiceChannel({ commit }, inboxList[0]);
+ expect(commit.mock.calls).toEqual([
+ [types.default.SET_INBOXES_UI_FLAG, { isCreating: true }],
+ [types.default.ADD_INBOXES, inboxList[0]],
+ [types.default.SET_INBOXES_UI_FLAG, { isCreating: false }],
+ ]);
+ });
+ it('sends correct actions if API is error', async () => {
+ axios.post.mockRejectedValue({ message: 'Incorrect header' });
+ await expect(actions.createVoiceChannel({ commit })).rejects.toThrow(
+ Error
+ );
+ expect(commit.mock.calls).toEqual([
+ [types.default.SET_INBOXES_UI_FLAG, { isCreating: true }],
+ [types.default.SET_INBOXES_UI_FLAG, { isCreating: false }],
+ ]);
+ });
+ });
+
describe('#createFBChannel', () => {
it('sends correct actions if API is success', async () => {
axios.post.mockResolvedValue({ data: inboxList[0] });
diff --git a/app/javascript/shared/mixins/inboxMixin.js b/app/javascript/shared/mixins/inboxMixin.js
index 273e9f8b4..bf8ec492b 100644
--- a/app/javascript/shared/mixins/inboxMixin.js
+++ b/app/javascript/shared/mixins/inboxMixin.js
@@ -63,6 +63,9 @@ export default {
isATelegramChannel() {
return this.channelType === INBOX_TYPES.TELEGRAM;
},
+ isAVoiceChannel() {
+ return this.channelType === INBOX_TYPES.VOICE;
+ },
isATwilioSMSChannel() {
const { medium: medium = '' } = this.inbox;
return this.isATwilioChannel && medium === 'sms';
diff --git a/config/features.yml b/config/features.yml
index 131456c72..95f7e33d4 100644
--- a/config/features.yml
+++ b/config/features.yml
@@ -168,4 +168,8 @@
enabled: true
- name: crm_integration
display_name: CRM Integration
- enabled: false
\ No newline at end of file
+ enabled: false
+- name: channel_voice
+ display_name: Voice Channel
+ enabled: false
+ chatwoot_internal: true
diff --git a/db/migrate/20250620120000_create_channel_voice.rb b/db/migrate/20250620120000_create_channel_voice.rb
new file mode 100644
index 000000000..9e2a25723
--- /dev/null
+++ b/db/migrate/20250620120000_create_channel_voice.rb
@@ -0,0 +1,16 @@
+class CreateChannelVoice < ActiveRecord::Migration[7.0]
+ def change
+ create_table :channel_voice do |t|
+ t.string :phone_number, null: false
+ t.string :provider, null: false, default: 'twilio'
+ t.jsonb :provider_config, null: false
+ t.integer :account_id, null: false
+ t.jsonb :additional_attributes, default: {}
+
+ t.timestamps
+ end
+
+ add_index :channel_voice, :phone_number, unique: true
+ add_index :channel_voice, :account_id
+ end
+end
\ No newline at end of file
diff --git a/db/schema.rb b/db/schema.rb
index d065a8ff4..fdd0cce82 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema[7.1].define(version: 2025_05_23_031839) do
+ActiveRecord::Schema[7.1].define(version: 2025_06_20_120000) do
# These extensions should be enabled to support this database
enable_extension "pg_stat_statements"
enable_extension "pg_trgm"
@@ -443,6 +443,18 @@ ActiveRecord::Schema[7.1].define(version: 2025_05_23_031839) do
t.index ["account_id", "profile_id"], name: "index_channel_twitter_profiles_on_account_id_and_profile_id", unique: true
end
+ create_table "channel_voice", force: :cascade do |t|
+ t.string "phone_number", null: false
+ t.string "provider", default: "twilio", null: false
+ t.jsonb "provider_config", null: false
+ t.integer "account_id", null: false
+ t.jsonb "additional_attributes", default: {}
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.index ["account_id"], name: "index_channel_voice_on_account_id"
+ t.index ["phone_number"], name: "index_channel_voice_on_phone_number", unique: true
+ end
+
create_table "channel_web_widgets", id: :serial, force: :cascade do |t|
t.string "website_url"
t.integer "account_id"
@@ -907,7 +919,7 @@ ActiveRecord::Schema[7.1].define(version: 2025_05_23_031839) do
t.text "header_text"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
- t.jsonb "config", default: {"allowed_locales"=>["en"]}
+ t.jsonb "config", default: {"allowed_locales" => ["en"]}
t.boolean "archived", default: false
t.bigint "channel_web_widget_id"
t.index ["channel_web_widget_id"], name: "index_portals_on_channel_web_widget_id"
diff --git a/enterprise/app/controllers/enterprise/api/v1/accounts/inboxes_controller.rb b/enterprise/app/controllers/enterprise/api/v1/accounts/inboxes_controller.rb
index b39db609d..396c3a91d 100644
--- a/enterprise/app/controllers/enterprise/api/v1/accounts/inboxes_controller.rb
+++ b/enterprise/app/controllers/enterprise/api/v1/accounts/inboxes_controller.rb
@@ -6,4 +6,28 @@ module Enterprise::Api::V1::Accounts::InboxesController
def ee_inbox_attributes
[auto_assignment_config: [:max_assignment_limit]]
end
+
+ private
+
+ def allowed_channel_types
+ super + ['voice']
+ end
+
+ def channel_type_from_params
+ case permitted_params[:channel][:type]
+ when 'voice'
+ Channel::Voice
+ else
+ super
+ end
+ end
+
+ def account_channels_method
+ case permitted_params[:channel][:type]
+ when 'voice'
+ Current.account.voice_channels
+ else
+ super
+ end
+ end
end
diff --git a/enterprise/app/models/channel/voice.rb b/enterprise/app/models/channel/voice.rb
new file mode 100644
index 000000000..a313129e2
--- /dev/null
+++ b/enterprise/app/models/channel/voice.rb
@@ -0,0 +1,64 @@
+# == Schema Information
+#
+# Table name: channel_voice
+#
+# id :bigint not null, primary key
+# additional_attributes :jsonb
+# phone_number :string not null
+# provider :string default("twilio"), not null
+# provider_config :jsonb not null
+# created_at :datetime not null
+# updated_at :datetime not null
+# account_id :integer not null
+#
+# Indexes
+#
+# index_channel_voice_on_account_id (account_id)
+# index_channel_voice_on_phone_number (phone_number) UNIQUE
+#
+class Channel::Voice < ApplicationRecord
+ include Channelable
+
+ self.table_name = 'channel_voice'
+
+ validates :phone_number, presence: true, uniqueness: true
+ validates :provider, presence: true
+ validates :provider_config, presence: true
+
+ # Validate phone number format (E.164 format)
+ validates :phone_number, format: { with: /\A\+[1-9]\d{1,14}\z/ }
+
+ # Provider-specific configs stored in JSON
+ validate :validate_provider_config
+
+ EDITABLE_ATTRS = [:phone_number, :provider, { provider_config: {} }].freeze
+
+ def name
+ "Voice (#{phone_number})"
+ end
+
+ def messaging_window_enabled?
+ false
+ end
+
+ private
+
+ def validate_provider_config
+ return if provider_config.blank?
+
+ case provider
+ when 'twilio'
+ validate_twilio_config
+ end
+ end
+
+ def validate_twilio_config
+ config = provider_config.with_indifferent_access
+ required_keys = %w[account_sid auth_token api_key_sid api_key_secret]
+
+ required_keys.each do |key|
+ errors.add(:provider_config, "#{key} is required for Twilio provider") if config[key].blank?
+ end
+ end
+end
+
diff --git a/enterprise/app/models/enterprise/concerns/account.rb b/enterprise/app/models/enterprise/concerns/account.rb
index 4a573a4c4..c31b6c10e 100644
--- a/enterprise/app/models/enterprise/concerns/account.rb
+++ b/enterprise/app/models/enterprise/concerns/account.rb
@@ -11,5 +11,6 @@ module Enterprise::Concerns::Account
has_many :captain_documents, dependent: :destroy_async, class_name: 'Captain::Document'
has_many :copilot_threads, dependent: :destroy_async
+ has_many :voice_channels, dependent: :destroy_async, class_name: '::Channel::Voice'
end
end
diff --git a/public/assets/images/dashboard/channels/voice.png b/public/assets/images/dashboard/channels/voice.png
new file mode 100644
index 0000000000000000000000000000000000000000..7c9481faf21b399155f187dab0fd594e93d98061
GIT binary patch
literal 1352277
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
m{FEq5%4b*U
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