diff --git a/Gemfile b/Gemfile index a0624fee4..4044c9b66 100644 --- a/Gemfile +++ b/Gemfile @@ -96,12 +96,12 @@ gem 'koala' # slack client gem 'slack-ruby-client', '~> 2.2.0' # for dialogflow integrations -gem 'google-cloud-dialogflow-v2' +gem 'google-cloud-dialogflow-v2', '>= 0.24.0' gem 'grpc' # Translate integrations # 'google-cloud-translate' gem depends on faraday 2.0 version # this dependency breaks the slack-ruby-client gem -gem 'google-cloud-translate-v3' +gem 'google-cloud-translate-v3', '>= 0.7.0' ##-- apm and error monitoring ---# # loaded only when environment variables are set. diff --git a/Gemfile.lock b/Gemfile.lock index 06f479659..46a766b4c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -257,7 +257,7 @@ GEM faraday-net_http_persistent (2.1.0) faraday (~> 2.5) net-http-persistent (~> 4.0) - faraday-retry (2.1.0) + faraday-retry (2.2.1) faraday (~> 2.0) fcm (1.0.8) faraday (>= 1.0.0, < 3.0) @@ -271,7 +271,7 @@ GEM fugit (1.11.1) et-orbi (~> 1, >= 1.2.11) raabro (~> 1.4) - gapic-common (0.18.0) + gapic-common (0.20.0) faraday (>= 1.9, < 3.a) faraday-retry (>= 1.0, < 3.a) google-protobuf (~> 3.14) @@ -301,15 +301,15 @@ GEM google-cloud-core (1.6.0) google-cloud-env (~> 1.0) google-cloud-errors (~> 1.0) - google-cloud-dialogflow-v2 (0.23.0) - gapic-common (>= 0.18.0, < 2.a) + google-cloud-dialogflow-v2 (0.31.0) + gapic-common (>= 0.20.0, < 2.a) google-cloud-errors (~> 1.0) google-cloud-location (>= 0.4, < 2.a) google-cloud-env (1.6.0) faraday (>= 0.17.3, < 3.0) google-cloud-errors (1.3.1) - google-cloud-location (0.4.0) - gapic-common (>= 0.17.1, < 2.a) + google-cloud-location (0.6.0) + gapic-common (>= 0.20.0, < 2.a) google-cloud-errors (~> 1.0) google-cloud-storage (1.44.0) addressable (~> 2.8) @@ -319,17 +319,17 @@ GEM google-cloud-core (~> 1.6) googleauth (>= 0.16.2, < 2.a) mini_mime (~> 1.0) - google-cloud-translate-v3 (0.6.0) - gapic-common (>= 0.17.1, < 2.a) + google-cloud-translate-v3 (0.10.0) + gapic-common (>= 0.20.0, < 2.a) google-cloud-errors (~> 1.0) google-protobuf (3.25.3) google-protobuf (3.25.3-arm64-darwin) google-protobuf (3.25.3-x86_64-darwin) google-protobuf (3.25.3-x86_64-linux) - googleapis-common-protos (1.4.0) - google-protobuf (~> 3.14) - googleapis-common-protos-types (~> 1.2) - grpc (~> 1.27) + googleapis-common-protos (1.6.0) + google-protobuf (>= 3.18, < 5.a) + googleapis-common-protos-types (~> 1.7) + grpc (~> 1.41) googleapis-common-protos-types (1.14.0) google-protobuf (~> 3.18) googleauth (1.5.2) @@ -881,9 +881,9 @@ DEPENDENCIES foreman geocoder gmail_xoauth - google-cloud-dialogflow-v2 + google-cloud-dialogflow-v2 (>= 0.24.0) google-cloud-storage - google-cloud-translate-v3 + google-cloud-translate-v3 (>= 0.7.0) groupdate grpc haikunator diff --git a/app/builders/account_builder.rb b/app/builders/account_builder.rb index ff67eefc5..6f7980586 100644 --- a/app/builders/account_builder.rb +++ b/app/builders/account_builder.rb @@ -94,8 +94,8 @@ class AccountBuilder def blocked_domains domains = GlobalConfigService.load('BLOCKED_EMAIL_DOMAINS', '') - domains.split("\n").map(&:strip) if domains.present? + return [] if domains.blank? - [] + domains.split("\n").map(&:strip) end end diff --git a/app/javascript/dashboard/components/CustomAttribute.vue b/app/javascript/dashboard/components/CustomAttribute.vue index 4bf97320b..ee00c182d 100644 --- a/app/javascript/dashboard/components/CustomAttribute.vue +++ b/app/javascript/dashboard/components/CustomAttribute.vue @@ -331,10 +331,12 @@ export default { ::v-deep { .selector-wrap { @apply m-0 top-1; + .selector-name { @apply ml-0; } } + .name { @apply ml-0; } diff --git a/app/javascript/dashboard/components/widgets/ShowMore.vue b/app/javascript/dashboard/components/widgets/ShowMore.vue index c17c32e2a..37e2f9a68 100644 --- a/app/javascript/dashboard/components/widgets/ShowMore.vue +++ b/app/javascript/dashboard/components/widgets/ShowMore.vue @@ -1,38 +1,34 @@ - @@ -41,16 +37,10 @@ export default { {{ textToBeDisplayed }} - - diff --git a/app/javascript/dashboard/components/widgets/WootWriter/FullEditor.vue b/app/javascript/dashboard/components/widgets/WootWriter/FullEditor.vue index ab4be9342..754182207 100644 --- a/app/javascript/dashboard/components/widgets/WootWriter/FullEditor.vue +++ b/app/javascript/dashboard/components/widgets/WootWriter/FullEditor.vue @@ -120,7 +120,6 @@ export default { if (fileUrl) { this.onImageUploadStart(fileUrl); } - useAlert(this.$t('HELP_CENTER.ARTICLE_EDITOR.IMAGE_UPLOAD.SUCCESS')); } catch (error) { useAlert(this.$t('HELP_CENTER.ARTICLE_EDITOR.IMAGE_UPLOAD.ERROR')); } @@ -173,6 +172,18 @@ export default { blur: () => { this.onBlur(); }, + paste: (view, event) => { + const data = event.clipboardData.files; + if (data.length > 0) { + data.forEach(file => { + // Check if the file is an image + if (file.type.includes('image')) { + this.uploadImageToStorage(file); + } + }); + event.preventDefault(); + } + }, }, }); }, diff --git a/app/javascript/dashboard/helper/featureHelper.js b/app/javascript/dashboard/helper/featureHelper.js index a7fa21616..529c0a44e 100644 --- a/app/javascript/dashboard/helper/featureHelper.js +++ b/app/javascript/dashboard/helper/featureHelper.js @@ -9,6 +9,7 @@ const FEATURE_HELP_URLS = { custom_attributes: 'https://chwt.app/hc/custom-attributes', dashboard_apps: 'https://chwt.app/hc/dashboard-apps', help_center: 'https://chwt.app/hc/help-center', + inboxes: 'https://chwt.app/hc/inboxes', integrations: 'https://chwt.app/hc/integrations', labels: 'https://chwt.app/hc/labels', macros: 'https://chwt.app/hc/macros', @@ -16,6 +17,7 @@ const FEATURE_HELP_URLS = { reports: 'https://chwt.app/hc/reports', sla: 'https://chwt.app/hc/sla', team_management: 'https://chwt.app/hc/teams', + webhook: 'https://chwt.app/hc/webhooks', }; export function getHelpUrlForFeature(featureName) { diff --git a/app/javascript/dashboard/i18n/locale/en/attributesMgmt.json b/app/javascript/dashboard/i18n/locale/en/attributesMgmt.json index a2f7386dc..a83852fb8 100644 --- a/app/javascript/dashboard/i18n/locale/en/attributesMgmt.json +++ b/app/javascript/dashboard/i18n/locale/en/attributesMgmt.json @@ -3,7 +3,8 @@ "HEADER": "Custom Attributes", "HEADER_BTN_TXT": "Add Custom Attribute", "LOADING": "Fetching custom attributes", - "SIDEBAR_TXT": "

Custom Attributes

A custom attribute tracks facts about your contacts/conversation — like the subscription plan, or when they ordered the first item etc.

For creating a Custom Attribute, just click on the Add Custom Attribute. You can also edit or delete an existing Custom Attribute by clicking on the Edit or Delete button.

", + "DESCRIPTION": "A custom attribute tracks additional details about your contacts or conversations—such as the subscription plan or the date of their first purchase. You can add different types of custom attributes, such as text, lists, or numbers, to capture the specific information you need.", + "LEARN_MORE": "Learn more about custom attributes", "ADD": { "TITLE": "Add Custom Attribute", "SUBMIT": "Create", @@ -91,7 +92,12 @@ "CONTACT": "Contact" }, "LIST": { - "TABLE_HEADER": ["Name", "Description", "Type", "Key"], + "TABLE_HEADER": [ + "Name", + "Description", + "Type", + "Key" + ], "BUTTONS": { "EDIT": "Edit", "DELETE": "Delete" diff --git a/app/javascript/dashboard/i18n/locale/en/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/en/inboxMgmt.json index eec8ccde6..5bd1b6989 100644 --- a/app/javascript/dashboard/i18n/locale/en/inboxMgmt.json +++ b/app/javascript/dashboard/i18n/locale/en/inboxMgmt.json @@ -1,7 +1,8 @@ { "INBOX_MGMT": { "HEADER": "Inboxes", - "SIDEBAR_TXT": "

Inbox

When you connect a website or a facebook Page to Chatwoot, it is called an Inbox. You can have unlimited inboxes in your Chatwoot account.

Click on Add Inbox to connect a website or a Facebook Page.

In the Dashboard, you can see all the conversations from all your inboxes in a single place and respond to them under the `Conversations` tab.

You can also see conversations specific to an inbox by clicking on the inbox name on the left pane of the dashboard.

", + "DESCRIPTION": "A channel is the mode of communication your customer chooses to interact with you. An inbox is where you manage interactions for a specific channel. It can include communications from various sources such as email, live chat, and social media.", + "LEARN_MORE": "Learn more about inboxes", "RECONNECTION_REQUIRED": "Your inbox is disconnected. You won't receive new messages until you reauthorize it.", "CLICK_TO_RECONNECT": "Click here to reconnect.", "LIST": { @@ -745,6 +746,18 @@ "MICROSOFT": "Microsoft", "GOOGLE": "Google", "OTHER_PROVIDERS": "Other Providers" + }, + "CHANNELS": { + "MESSENGER": "Messenger", + "WEB_WIDGET": "Website", + "TWITTER_PROFILE": "Twitter", + "TWILIO_SMS": "Twilio SMS", + "WHATSAPP": "WhatsApp", + "SMS": "SMS", + "EMAIL": "Email", + "TELEGRAM": "Telegram", + "LINE": "Line", + "API": "API Channel" } } } diff --git a/app/javascript/dashboard/i18n/locale/en/integrations.json b/app/javascript/dashboard/i18n/locale/en/integrations.json index 47349d1b1..f478b71b4 100644 --- a/app/javascript/dashboard/i18n/locale/en/integrations.json +++ b/app/javascript/dashboard/i18n/locale/en/integrations.json @@ -12,6 +12,7 @@ }, "WEBHOOK": { "SUBSCRIBED_EVENTS": "Subscribed Events", + "LEARN_MORE": "Learn more about webhooks", "FORM": { "CANCEL": "Cancel", "DESC": "Webhook events provide you the realtime information about what's happening in your Chatwoot account. Please enter a valid URL to configure a callback.", diff --git a/app/javascript/dashboard/routes/dashboard/settings/SettingsLayout.vue b/app/javascript/dashboard/routes/dashboard/settings/SettingsLayout.vue index 916f821ad..0e6a42fe8 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/SettingsLayout.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/SettingsLayout.vue @@ -22,17 +22,21 @@ defineProps({ diff --git a/app/javascript/dashboard/routes/dashboard/settings/attributes/AddAttribute.vue b/app/javascript/dashboard/routes/dashboard/settings/attributes/AddAttribute.vue index 1ff257b7e..33f9ab7a5 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/attributes/AddAttribute.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/attributes/AddAttribute.vue @@ -12,6 +12,12 @@ export default { type: Function, default: () => {}, }, + // Passes 0 or 1 based on the selected AttributeModel tab selected in the UI + // Needs a better data type, todo: refactor this component later + selectedAttributeModelTab: { + type: Number, + default: 0, + }, }, setup() { return { v$: useVuelidate() }; @@ -20,7 +26,10 @@ export default { return { displayName: '', description: '', - attributeModel: 0, + // Using the prop as default. There is no side effect here as the component + // is destroyed completely when the modal is closed. The prop doesn't change + // dynamically when the modal is active. + attributeModel: this.selectedAttributeModelTab || 0, attributeType: 0, attributeKey: '', regexPattern: null, @@ -280,13 +289,16 @@ export default { padding: 0 var(--space-small) var(--space-small) 0; font-family: monospace; } + .multiselect--wrap { margin-bottom: var(--space-normal); + .error-message { color: var(--r-400); font-size: var(--font-size-small); font-weight: var(--font-weight-normal); } + .invalid { ::v-deep { .multiselect__tags { @@ -295,13 +307,16 @@ export default { } } } + ::v-deep { .multiselect { margin-bottom: 0; } + .multiselect__content-wrapper { display: none; } + .multiselect--active .multiselect__tags { border-radius: var(--border-radius-normal); } diff --git a/app/javascript/dashboard/routes/dashboard/settings/attributes/CustomAttribute.vue b/app/javascript/dashboard/routes/dashboard/settings/attributes/CustomAttribute.vue index 5c234ec03..9e55c619d 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/attributes/CustomAttribute.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/attributes/CustomAttribute.vue @@ -1,195 +1,141 @@ -