diff --git a/app/javascript/dashboard/i18n/locale/en/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/en/inboxMgmt.json
index d0d1573b3..8ab345b60 100644
--- a/app/javascript/dashboard/i18n/locale/en/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/en/inboxMgmt.json
@@ -628,7 +628,17 @@
"WIDGET_BUILDER": "Widget Builder",
"BOT_CONFIGURATION": "Bot Configuration",
"ACCOUNT_HEALTH": "Account Health",
- "CSAT": "CSAT"
+ "CSAT": "CSAT",
+ "VOICE": "Voice"
+ },
+ "VOICE_CONFIGURATION": {
+ "ENABLE_VOICE": {
+ "LABEL": "Enable Voice Calling",
+ "DESCRIPTION": "Enable voice calling on this inbox. Agents will be able to make and receive phone calls."
+ },
+ "CREDENTIALS": {
+ "DESCRIPTION": "Voice calling requires Twilio API Key credentials. These are used to generate tokens for agent WebRTC connections."
+ }
},
"CHANNEL_PREFERENCES": "Channel Preferences",
"WIDGET_FEATURES": "Widget features",
diff --git a/app/javascript/dashboard/routes/dashboard/settings/inbox/Settings.vue b/app/javascript/dashboard/routes/dashboard/settings/inbox/Settings.vue
index c36852c8c..b577195ff 100644
--- a/app/javascript/dashboard/routes/dashboard/settings/inbox/Settings.vue
+++ b/app/javascript/dashboard/routes/dashboard/settings/inbox/Settings.vue
@@ -21,6 +21,7 @@ import PreChatFormSettings from './PreChatForm/Settings.vue';
import WeeklyAvailability from './components/WeeklyAvailability.vue';
import GreetingsEditor from 'shared/components/GreetingsEditor.vue';
import ConfigurationPage from './settingsPage/ConfigurationPage.vue';
+import VoiceConfigurationPage from './settingsPage/VoiceConfigurationPage.vue';
import CustomerSatisfactionPage from './settingsPage/CustomerSatisfactionPage.vue';
import CollaboratorsPage from './settingsPage/CollaboratorsPage.vue';
import BotConfiguration from './components/BotConfiguration.vue';
@@ -44,6 +45,7 @@ export default {
BotConfiguration,
CollaboratorsPage,
ConfigurationPage,
+ VoiceConfigurationPage,
CustomerSatisfactionPage,
FacebookReauthorize,
GreetingsEditor,
@@ -205,6 +207,20 @@ export default {
];
}
+ if (
+ this.isATwilioChannel &&
+ this.inbox.phone_number &&
+ this.inbox.medium === 'sms'
+ ) {
+ visibleToAllChannelTabs = [
+ ...visibleToAllChannelTabs,
+ {
+ key: 'voice-configuration',
+ name: this.$t('INBOX_MGMT.TABS.VOICE'),
+ },
+ ];
+ }
+
return visibleToAllChannelTabs;
},
currentInboxId() {
@@ -1170,6 +1186,12 @@ export default {
>
+
+
+
diff --git a/app/javascript/dashboard/routes/dashboard/settings/inbox/settingsPage/ConfigurationPage.vue b/app/javascript/dashboard/routes/dashboard/settings/inbox/settingsPage/ConfigurationPage.vue
index a174c947d..9cd1665bf 100644
--- a/app/javascript/dashboard/routes/dashboard/settings/inbox/settingsPage/ConfigurationPage.vue
+++ b/app/javascript/dashboard/routes/dashboard/settings/inbox/settingsPage/ConfigurationPage.vue
@@ -189,26 +189,7 @@ export default {
-
-
-
-
-
-
-
-
-
-
+
+import { useAlert } from 'dashboard/composables';
+import SettingsFieldSection from 'dashboard/components-next/Settings/SettingsFieldSection.vue';
+import SettingsToggleSection from 'dashboard/components-next/Settings/SettingsToggleSection.vue';
+import NextInput from 'dashboard/components-next/input/Input.vue';
+import NextButton from 'dashboard/components-next/button/Button.vue';
+
+export default {
+ components: {
+ SettingsFieldSection,
+ SettingsToggleSection,
+ NextInput,
+ NextButton,
+ },
+ props: {
+ inbox: {
+ type: Object,
+ default: () => ({}),
+ },
+ },
+ data() {
+ return {
+ voiceEnabled: this.inbox.voice_enabled || false,
+ apiKeySid: this.inbox.api_key_sid || '',
+ apiKeySecret: '',
+ };
+ },
+ computed: {
+ isVoiceConfigured() {
+ return !!this.inbox.voice_configured;
+ },
+ needsCredentials() {
+ return this.voiceEnabled && !this.isVoiceConfigured;
+ },
+ isSubmitDisabled() {
+ if (!this.voiceEnabled) return false;
+ if (this.needsCredentials) {
+ return !this.apiKeySid || !this.apiKeySecret;
+ }
+ return false;
+ },
+ },
+ methods: {
+ async updateVoiceSettings() {
+ try {
+ const channelPayload = { voice_enabled: this.voiceEnabled };
+
+ if (this.needsCredentials) {
+ channelPayload.api_key_sid = this.apiKeySid;
+ channelPayload.api_key_secret = this.apiKeySecret;
+ }
+
+ await this.$store.dispatch('inboxes/updateInbox', {
+ id: this.inbox.id,
+ formData: false,
+ channel: channelPayload,
+ });
+ useAlert(this.$t('INBOX_MGMT.EDIT.API.SUCCESS_MESSAGE'));
+ } catch (error) {
+ useAlert(this.$t('INBOX_MGMT.EDIT.API.ERROR_MESSAGE'));
+ }
+ },
+ },
+};
+
+
+
+
+
+
+
+
+ {{ $t('INBOX_MGMT.VOICE_CONFIGURATION.CREDENTIALS.DESCRIPTION') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/views/api/v1/models/_inbox.json.jbuilder b/app/views/api/v1/models/_inbox.json.jbuilder
index cd45471cb..f59a14438 100644
--- a/app/views/api/v1/models/_inbox.json.jbuilder
+++ b/app/views/api/v1/models/_inbox.json.jbuilder
@@ -131,8 +131,11 @@ if resource.whatsapp?
end
## Voice attributes for TwilioSms
-if resource.twilio? && resource.channel.respond_to?(:voice_enabled?) && resource.channel.voice_enabled?
- json.voice_enabled true
- json.voice_call_webhook_url resource.channel.try(:voice_call_webhook_url)
- json.voice_status_webhook_url resource.channel.try(:voice_status_webhook_url)
+if resource.twilio? && resource.channel.respond_to?(:voice_enabled?)
+ json.voice_enabled resource.channel.voice_enabled?
+ json.voice_configured resource.channel.try(:twiml_app_sid).present?
+ if resource.channel.try(:twiml_app_sid).present?
+ json.voice_call_webhook_url resource.channel.try(:voice_call_webhook_url)
+ json.voice_status_webhook_url resource.channel.try(:voice_status_webhook_url)
+ end
end
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 7c51b2f66..c6a52b8aa 100644
--- a/enterprise/app/controllers/enterprise/api/v1/accounts/inboxes_controller.rb
+++ b/enterprise/app/controllers/enterprise/api/v1/accounts/inboxes_controller.rb
@@ -31,6 +31,12 @@ module Enterprise::Api::V1::Accounts::InboxesController
super
end
+ def get_channel_attributes(channel_type)
+ attrs = super
+ attrs += [:voice_enabled, :api_key_sid, :api_key_secret] if channel_type == 'Channel::TwilioSms'
+ attrs
+ end
+
def create_voice_channel
voice_params = params.require(:channel).permit(
:phone_number, :provider,