diff --git a/Rakefile b/Rakefile index 591d2c4c6..2e996417e 100644 --- a/Rakefile +++ b/Rakefile @@ -2,9 +2,8 @@ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. require_relative 'config/application' +# Load Enterprise Edition rake tasks if they exist +enterprise_tasks_path = Rails.root.join('enterprise/tasks_railtie.rb').to_s +require enterprise_tasks_path if File.exist?(enterprise_tasks_path) Rails.application.load_tasks - -# Load Enterprise Edition rake tasks if they exist -enterprise_tasks_path = Rails.root.join('enterprise/lib/tasks.rb').to_s -require enterprise_tasks_path if File.exist?(enterprise_tasks_path) diff --git a/app/builders/agent_builder.rb b/app/builders/agent_builder.rb index 54f478920..2fe11cae0 100644 --- a/app/builders/agent_builder.rb +++ b/app/builders/agent_builder.rb @@ -52,3 +52,5 @@ class AgentBuilder }.compact)) end end + +AgentBuilder.prepend_mod_with('AgentBuilder') diff --git a/app/controllers/api/v1/accounts/portals_controller.rb b/app/controllers/api/v1/accounts/portals_controller.rb index af96441f8..57344cc1e 100644 --- a/app/controllers/api/v1/accounts/portals_controller.rb +++ b/app/controllers/api/v1/accounts/portals_controller.rb @@ -85,7 +85,8 @@ class Api::V1::Accounts::PortalsController < Api::V1::Accounts::BaseController def live_chat_widget_params permitted_params = params.permit(:inbox_id) - return {} if permitted_params[:inbox_id].blank? + return {} unless permitted_params.key?(:inbox_id) + return { channel_web_widget_id: nil } if permitted_params[:inbox_id].blank? inbox = Inbox.find(permitted_params[:inbox_id]) return {} unless inbox.web_widget? diff --git a/app/controllers/api/v1/accounts/whatsapp/authorizations_controller.rb b/app/controllers/api/v1/accounts/whatsapp/authorizations_controller.rb index 3e7d876c3..d52f396fc 100644 --- a/app/controllers/api/v1/accounts/whatsapp/authorizations_controller.rb +++ b/app/controllers/api/v1/accounts/whatsapp/authorizations_controller.rb @@ -1,5 +1,4 @@ class Api::V1::Accounts::Whatsapp::AuthorizationsController < Api::V1::Accounts::BaseController - before_action :validate_feature_enabled! before_action :fetch_and_validate_inbox, if: -> { params[:inbox_id].present? } # POST /api/v1/accounts/:account_id/whatsapp/authorization @@ -65,15 +64,6 @@ class Api::V1::Accounts::Whatsapp::AuthorizationsController < Api::V1::Accounts: }, status: :unprocessable_entity end - def validate_feature_enabled! - return if Current.account.feature_whatsapp_embedded_signup? - - render json: { - success: false, - error: 'WhatsApp embedded signup is not enabled for this account' - }, status: :forbidden - end - def validate_embedded_signup_params! missing_params = [] missing_params << 'code' if params[:code].blank? diff --git a/app/controllers/widgets_controller.rb b/app/controllers/widgets_controller.rb index 4be690ffe..9a6a376f7 100644 --- a/app/controllers/widgets_controller.rb +++ b/app/controllers/widgets_controller.rb @@ -70,7 +70,12 @@ class WidgetsController < ActionController::Base end def allow_iframe_requests - response.headers.delete('X-Frame-Options') + if @web_widget.allowed_domains.blank? + response.headers.delete('X-Frame-Options') + else + domains = @web_widget.allowed_domains.split(',').map(&:strip).join(' ') + response.headers['Content-Security-Policy'] = "frame-ancestors #{domains}" + end end end diff --git a/app/javascript/dashboard/api/samlSettings.js b/app/javascript/dashboard/api/samlSettings.js new file mode 100644 index 000000000..7c0f5b266 --- /dev/null +++ b/app/javascript/dashboard/api/samlSettings.js @@ -0,0 +1,26 @@ +/* global axios */ +import ApiClient from './ApiClient'; + +class SamlSettingsAPI extends ApiClient { + constructor() { + super('saml_settings', { accountScoped: true }); + } + + get() { + return axios.get(this.url); + } + + create(data) { + return axios.post(this.url, { saml_settings: data }); + } + + update(data) { + return axios.put(this.url, { saml_settings: data }); + } + + delete() { + return axios.delete(this.url); + } +} + +export default new SamlSettingsAPI(); diff --git a/app/javascript/dashboard/components-next/AssignmentPolicy/components/AddDataDropdown.vue b/app/javascript/dashboard/components-next/AssignmentPolicy/components/AddDataDropdown.vue index c664d8929..a078b9cc7 100644 --- a/app/javascript/dashboard/components-next/AssignmentPolicy/components/AddDataDropdown.vue +++ b/app/javascript/dashboard/components-next/AssignmentPolicy/components/AddDataDropdown.vue @@ -1,9 +1,10 @@ + + diff --git a/app/javascript/dashboard/components-next/AssignmentPolicy/components/InboxCapacityLimits.vue b/app/javascript/dashboard/components-next/AssignmentPolicy/components/InboxCapacityLimits.vue new file mode 100644 index 000000000..b31248653 --- /dev/null +++ b/app/javascript/dashboard/components-next/AssignmentPolicy/components/InboxCapacityLimits.vue @@ -0,0 +1,177 @@ + + + diff --git a/app/javascript/dashboard/components-next/AssignmentPolicy/components/story/AddDataDropdown.story.vue b/app/javascript/dashboard/components-next/AssignmentPolicy/components/story/AddDataDropdown.story.vue index 2ac4d8854..e69aa798f 100644 --- a/app/javascript/dashboard/components-next/AssignmentPolicy/components/story/AddDataDropdown.story.vue +++ b/app/javascript/dashboard/components-next/AssignmentPolicy/components/story/AddDataDropdown.story.vue @@ -34,6 +34,29 @@ const mockInboxes = [ }, ]; +const mockTags = [ + { + id: 1, + name: 'urgent', + color: '#ff4757', + }, + { + id: 2, + name: 'bug', + color: '#ff6b6b', + }, + { + id: 3, + name: 'feature-request', + color: '#4834d4', + }, + { + id: 4, + name: 'documentation', + color: '#26de81', + }, +]; + const handleAdd = item => { console.log('Add item:', item); }; @@ -42,9 +65,9 @@ const handleAdd = item => { diff --git a/app/javascript/dashboard/components-next/AssignmentPolicy/components/story/DataTable.story.vue b/app/javascript/dashboard/components-next/AssignmentPolicy/components/story/DataTable.story.vue index 912b1fdfc..a81a29976 100644 --- a/app/javascript/dashboard/components-next/AssignmentPolicy/components/story/DataTable.story.vue +++ b/app/javascript/dashboard/components-next/AssignmentPolicy/components/story/DataTable.story.vue @@ -22,6 +22,21 @@ const mockItems = [ }, ]; +const mockAgentList = [ + { + id: 1, + name: 'John Doe', + email: 'john.doe@example.com', + avatarUrl: 'https://i.pravatar.cc/150?img=1', + }, + { + id: 2, + name: 'Jane Smith', + email: 'jane.smith@example.com', + avatarUrl: 'https://i.pravatar.cc/150?img=2', + }, +]; + const handleDelete = itemId => { console.log('Delete item:', itemId); }; @@ -30,7 +45,7 @@ const handleDelete = itemId => { diff --git a/app/javascript/dashboard/components-next/sidebar/Sidebar.vue b/app/javascript/dashboard/components-next/sidebar/Sidebar.vue index 27736c3a2..ab6537031 100644 --- a/app/javascript/dashboard/components-next/sidebar/Sidebar.vue +++ b/app/javascript/dashboard/components-next/sidebar/Sidebar.vue @@ -494,6 +494,12 @@ const menuItems = computed(() => { icon: 'i-lucide-clock-alert', to: accountScopedRoute('sla_list'), }, + { + name: 'Settings Security', + label: t('SIDEBAR.SECURITY'), + icon: 'i-lucide-shield', + to: accountScopedRoute('security_settings_index'), + }, { name: 'Settings Billing', label: t('SIDEBAR.BILLING'), diff --git a/app/javascript/dashboard/components/widgets/conversation/MessagesView.vue b/app/javascript/dashboard/components/widgets/conversation/MessagesView.vue index 1f850cd74..3cb46c05f 100644 --- a/app/javascript/dashboard/components/widgets/conversation/MessagesView.vue +++ b/app/javascript/dashboard/components/widgets/conversation/MessagesView.vue @@ -4,6 +4,7 @@ import { ref, provide } from 'vue'; import { useConfig } from 'dashboard/composables/useConfig'; import { useKeyboardEvents } from 'dashboard/composables/useKeyboardEvents'; import { useAI } from 'dashboard/composables/useAI'; +import { useSnakeCase } from 'dashboard/composables/useTransformKeys'; // components import ReplyBox from './ReplyBox.vue'; @@ -437,6 +438,11 @@ export default { makeMessagesRead() { this.$store.dispatch('markMessagesRead', { id: this.currentChat.id }); }, + async handleMessageRetry(message) { + if (!message) return; + const payload = useSnakeCase(message); + await this.$store.dispatch('sendMessageWithData', payload); + }, }, }; @@ -465,6 +471,7 @@ export default { :is-an-email-channel="isAnEmailChannel" :inbox-supports-reply-to="inboxSupportsReplyTo" :messages="getMessages" + @retry="handleMessageRetry" > diff --git a/app/javascript/dashboard/routes/dashboard/settings/assignmentPolicy/pages/components/AgentCapacityPolicyForm.vue b/app/javascript/dashboard/routes/dashboard/settings/assignmentPolicy/pages/components/AgentCapacityPolicyForm.vue new file mode 100644 index 000000000..24c4f0a38 --- /dev/null +++ b/app/javascript/dashboard/routes/dashboard/settings/assignmentPolicy/pages/components/AgentCapacityPolicyForm.vue @@ -0,0 +1,214 @@ + + + diff --git a/app/javascript/dashboard/routes/dashboard/settings/inbox/Settings.vue b/app/javascript/dashboard/routes/dashboard/settings/inbox/Settings.vue index 609fd3fd4..1f455f112 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/inbox/Settings.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/inbox/Settings.vue @@ -328,7 +328,7 @@ export default { this.continuityViaEmail = this.inbox.continuity_via_email; this.channelWebsiteUrl = this.inbox.website_url; this.channelWelcomeTitle = this.inbox.welcome_title; - this.channelWelcomeTagline = this.inbox.welcome_tagline; + this.channelWelcomeTagline = this.inbox.welcome_tagline || ''; this.selectedFeatureFlags = this.inbox.selected_feature_flags || []; this.replyTime = this.inbox.reply_time; this.locktoSingleConversation = this.inbox.lock_to_single_conversation; diff --git a/app/javascript/dashboard/routes/dashboard/settings/inbox/channels/Whatsapp.vue b/app/javascript/dashboard/routes/dashboard/settings/inbox/channels/Whatsapp.vue index 0691c4b4f..c6c021a45 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/inbox/channels/Whatsapp.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/inbox/channels/Whatsapp.vue @@ -1,25 +1,23 @@ @@ -117,18 +94,52 @@ const shouldShowCloudWhatsapp = provider => {
- - - - - +
+ +
+ + + +
+ + + +
+
+ + + + + + + + +
diff --git a/app/javascript/dashboard/routes/dashboard/settings/inbox/channels/WhatsappEmbeddedSignup.vue b/app/javascript/dashboard/routes/dashboard/settings/inbox/channels/WhatsappEmbeddedSignup.vue index 583ca2413..cf5c1310e 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/inbox/channels/WhatsappEmbeddedSignup.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/inbox/channels/WhatsappEmbeddedSignup.vue @@ -2,12 +2,13 @@ import { ref, computed, onMounted, onBeforeUnmount } from 'vue'; import { useStore } from 'vuex'; import { useRouter } from 'vue-router'; -import { useI18n } from 'vue-i18n'; +import { useI18n, I18nT } from 'vue-i18n'; import { useAlert } from 'dashboard/composables'; import Icon from 'next/icon/Icon.vue'; import NextButton from 'next/button/Button.vue'; import LoadingState from 'dashboard/components/widgets/LoadingState.vue'; import { parseAPIErrorResponse } from 'dashboard/store/utils/api'; +import globalConstants from 'dashboard/constants/globals.js'; import { setupFacebookSdk, initWhatsAppEmbeddedSignup, @@ -28,9 +29,6 @@ const authCode = ref(null); const businessData = ref(null); const isAuthenticating = ref(false); -// Computed -const whatsappIconPath = '/assets/images/dashboard/channels/whatsapp.png'; - const benefits = computed(() => [ { key: 'EASY_SETUP', @@ -235,14 +233,9 @@ onBeforeUnmount(() => {
- +
@@ -266,22 +259,26 @@ onBeforeUnmount(() => {
- - {{ $t('INBOX_MGMT.ADD.WHATSAPP.EMBEDDED_SIGNUP.LEARN_MORE.TEXT') }} - {{ ' ' }} - - {{ - $t('INBOX_MGMT.ADD.WHATSAPP.EMBEDDED_SIGNUP.LEARN_MORE.LINK_TEXT') - }} - - + + +
diff --git a/app/javascript/dashboard/routes/dashboard/settings/inbox/helpers/timezones.json b/app/javascript/dashboard/routes/dashboard/settings/inbox/helpers/timezones.json index b238add21..810415ffb 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/inbox/helpers/timezones.json +++ b/app/javascript/dashboard/routes/dashboard/settings/inbox/helpers/timezones.json @@ -86,7 +86,7 @@ "Riyadh (GMT+03:00)": "Asia/Riyadh", "Nairobi (GMT+03:00)": "Africa/Nairobi", "Baghdad (GMT+03:00)": "Asia/Baghdad", - "Tehran (GMT+04:30)": "Asia/Tehran", + "Tehran (GMT+03:30)": "Asia/Tehran", "Abu Dhabi (GMT+04:00)": "Asia/Muscat", "Muscat (GMT+04:00)": "Asia/Muscat", "Baku (GMT+04:00)": "Asia/Baku", diff --git a/app/javascript/dashboard/routes/dashboard/settings/security/Index.vue b/app/javascript/dashboard/routes/dashboard/settings/security/Index.vue new file mode 100644 index 000000000..0ac35c9e2 --- /dev/null +++ b/app/javascript/dashboard/routes/dashboard/settings/security/Index.vue @@ -0,0 +1,41 @@ + + + diff --git a/app/javascript/dashboard/routes/dashboard/settings/security/components/SamlAttributeMap.vue b/app/javascript/dashboard/routes/dashboard/settings/security/components/SamlAttributeMap.vue new file mode 100644 index 000000000..442b426df --- /dev/null +++ b/app/javascript/dashboard/routes/dashboard/settings/security/components/SamlAttributeMap.vue @@ -0,0 +1,50 @@ + + + diff --git a/app/javascript/dashboard/routes/dashboard/settings/security/components/SamlInfoSection.vue b/app/javascript/dashboard/routes/dashboard/settings/security/components/SamlInfoSection.vue new file mode 100644 index 000000000..66820cafa --- /dev/null +++ b/app/javascript/dashboard/routes/dashboard/settings/security/components/SamlInfoSection.vue @@ -0,0 +1,102 @@ + + + diff --git a/app/javascript/dashboard/routes/dashboard/settings/security/components/SamlPaywall.vue b/app/javascript/dashboard/routes/dashboard/settings/security/components/SamlPaywall.vue new file mode 100644 index 000000000..a08cce9e0 --- /dev/null +++ b/app/javascript/dashboard/routes/dashboard/settings/security/components/SamlPaywall.vue @@ -0,0 +1,41 @@ + + + diff --git a/app/javascript/dashboard/routes/dashboard/settings/security/components/SamlSettings.vue b/app/javascript/dashboard/routes/dashboard/settings/security/components/SamlSettings.vue new file mode 100644 index 000000000..dca12200b --- /dev/null +++ b/app/javascript/dashboard/routes/dashboard/settings/security/components/SamlSettings.vue @@ -0,0 +1,251 @@ + + +