From 885b041a83f91387df3dcf16188c9f3048e30c22 Mon Sep 17 00:00:00 2001 From: TheDanniCraft Date: Tue, 27 Jan 2026 03:08:20 +0100 Subject: [PATCH 01/24] fix: Update help center sitemap XML structure (#13357) # Pull Request Template ## Description The Help Center sitemap endpoint (`/hc/:portal_slug/sitemap.xml`) previously rendered a `` element while embedding article URLs directly, which does not align with the sitemap specification. This change fixes the structure by: - Replacing `` with `` - Adding the required sitemap XML namespace - Rendering each published article as a `` entry with `` and `` This ensures the endpoint outputs a valid, self-contained sitemap document. Fixes #13334 ## Type of change Please delete options that are not relevant. - [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? - Updated the existing `portals_controller_spec.rb` - Adjusted assertions to validate a `` root element and the sitemap XML namespace - Verified that the sitemap returns only published article URLs - Ran the updated RSpec controller specs locally ## 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 - [x] 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 - [x] Any dependent changes have been merged and published in downstream modules --- .../public/api/v1/portals/sitemap.xml.erb | 10 +++--- .../public/api/v1/portals_controller_spec.rb | 31 +++++++++++++------ 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/app/views/public/api/v1/portals/sitemap.xml.erb b/app/views/public/api/v1/portals/sitemap.xml.erb index d3e2b8301..1ea828165 100644 --- a/app/views/public/api/v1/portals/sitemap.xml.erb +++ b/app/views/public/api/v1/portals/sitemap.xml.erb @@ -1,9 +1,9 @@ - + <% @portal.articles.where(status: :published).each do |article| %> - + <%= @help_center_url %><%= generate_article_link(@portal.slug, article.slug, false, false) %> - <%= article.updated_at.strftime("%Y-%m-%d") %> - + <%= article.updated_at.to_date.iso8601 %> + <% end %> - \ No newline at end of file + diff --git a/spec/controllers/public/api/v1/portals_controller_spec.rb b/spec/controllers/public/api/v1/portals_controller_spec.rb index 92ab56e5b..9cc77a19c 100644 --- a/spec/controllers/public/api/v1/portals_controller_spec.rb +++ b/spec/controllers/public/api/v1/portals_controller_spec.rb @@ -60,24 +60,35 @@ RSpec.describe Public::Api::V1::PortalsController, type: :request do describe 'GET /public/api/v1/portals/{portal_slug}/sitemap' do context 'when custom_domain is present' do - it 'gets a valid sitemap' do + it 'returns a valid urlset sitemap with the correct namespace' do get "/hc/#{portal.slug}/sitemap.xml" + expect(response).to have_http_status(:success) - expect(response.body).to match(/ Date: Tue, 27 Jan 2026 11:36:20 +0400 Subject: [PATCH 02/24] feat: Conversation workflows(EE) (#13040) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We are expanding Chatwoot’s automation capabilities by introducing **Conversation Workflows**, a dedicated section in settings where teams can configure rules that govern how conversations are closed and what information agents must fill before resolving. This feature helps teams enforce data consistency, collect structured resolution information, and ensure downstream reporting is accurate. Instead of having auto‑resolution buried inside Account Settings, we introduced a new sidebar item: - Auto‑resolve conversations (existing behaviour) - Required attributes on resolution (new) This groups all conversation‑closing logic into a single place. #### Required Attributes on Resolve Admins can now pick which custom conversation attributes must be filled before an agent can resolve a conversation. **How it works** - Admin selects one or more attributes from the list of existing conversation level custom attributes. - These selected attributes become mandatory during resolution. - List all the attributes configured via Required Attributes (Text, Number, Link, Date, List, Checkbox) - When an agent clicks Resolve Conversation: If attributes already have values → the conversation resolves normally. If attributes are missing → a modal appears prompting the agent to fill them. CleanShot 2025-12-10 at 11 42
23@2x #### Custom Attributes Integration On the Custom Attributes page, we will surfaced indicators showing how each attribute is being used. Each attribute will show badges such as: - Resolution → used in the required‑on‑resolve workflow - Pre‑chat form → already existing CleanShot 2025-12-10 at 11 43
42@2x #### Admin Flow - Navigate to Settings → Conversation Workflows. - Under Required attributes on resolve, click Add Required Attribute. - Pick from the dropdown list of conversation attributes. - Save changes. Agents will now be prompted automatically whenever they resolve. CleanShot 2025-12-10 at 11 44 42@2x #### NOTES - The Required Attributes on Resolve modal should only appear when values are missing. - Required attributes must block the resolution action until satisfied. - Bulk‑resolve actions should follow the same rules — any conversation missing attributes cannot be bulk‑resolved, rest will be resolved, show a notification that the resolution cannot be done. - API resolution does not respect the attributes. --------- Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: iamsivin Co-authored-by: Pranav --- app/controllers/api/v1/accounts_controller.rb | 9 +- .../ConversationRequiredEmpty.vue | 17 + .../ConversationRequiredAttributeItem.vue | 56 +++ .../ConversationRequiredAttributes.vue | 186 ++++++++++ .../ConversationResolveAttributesModal.vue | 248 +++++++++++++ .../ConversationWorkflow/constants.js | 8 + .../components-next/input/ChoiceToggle.vue | 45 +++ .../components-next/sidebar/Sidebar.vue | 10 +- .../components-next/textarea/TextArea.vue | 8 +- .../dashboard/components/ChatList.vue | 92 ++++- .../components/buttons/ResolveAction.vue | 74 +++- .../composables/chatlist/useBulkActions.js | 65 +++- .../useConversationRequiredAttributes.spec.js | 348 ++++++++++++++++++ .../useConversationRequiredAttributes.js | 97 +++++ app/javascript/dashboard/featureFlags.js | 2 + .../i18n/locale/en/attributesMgmt.json | 4 + .../dashboard/i18n/locale/en/bulkActions.json | 4 + .../dashboard/i18n/locale/en/general.json | 4 + .../dashboard/i18n/locale/en/settings.json | 55 ++- .../customAttributes/CustomAttributes.vue | 5 +- .../dashboard/settings/account/Index.vue | 9 - .../account/components/AutoResolve.vue | 152 ++++---- .../settings/attributes/CustomAttribute.vue | 176 --------- .../conversationWorkflow.routes.js | 22 ++ .../settings/conversationWorkflow/index.vue | 48 +++ .../dashboard/settings/settings.routes.js | 2 + .../store/modules/conversations/actions.js | 19 +- .../store/modules/conversations/index.js | 16 +- .../specs/conversations/actions.spec.js | 8 +- .../specs/conversations/mutations.spec.js | 10 +- app/models/account.rb | 2 +- app/models/custom_attribute_definition.rb | 10 +- config/features.yml | 4 + .../enterprise/api/v1/accounts_settings.rb | 7 + .../app/models/enterprise/concerns/account.rb | 2 + .../concerns/custom_attribute_definition.rb | 16 + .../billing/handle_stripe_event_service.rb | 2 +- enterprise/config/premium_features.yml | 1 + .../custom_attribute_definition_spec.rb | 0 39 files changed, 1514 insertions(+), 329 deletions(-) create mode 100644 app/javascript/dashboard/components-next/Conversation/ConversationRequiredEmpty.vue create mode 100644 app/javascript/dashboard/components-next/ConversationWorkflow/ConversationRequiredAttributeItem.vue create mode 100644 app/javascript/dashboard/components-next/ConversationWorkflow/ConversationRequiredAttributes.vue create mode 100644 app/javascript/dashboard/components-next/ConversationWorkflow/ConversationResolveAttributesModal.vue create mode 100644 app/javascript/dashboard/components-next/ConversationWorkflow/constants.js create mode 100644 app/javascript/dashboard/components-next/input/ChoiceToggle.vue create mode 100644 app/javascript/dashboard/composables/spec/useConversationRequiredAttributes.spec.js create mode 100644 app/javascript/dashboard/composables/useConversationRequiredAttributes.js delete mode 100644 app/javascript/dashboard/routes/dashboard/settings/attributes/CustomAttribute.vue create mode 100644 app/javascript/dashboard/routes/dashboard/settings/conversationWorkflow/conversationWorkflow.routes.js create mode 100644 app/javascript/dashboard/routes/dashboard/settings/conversationWorkflow/index.vue create mode 100644 enterprise/app/controllers/enterprise/api/v1/accounts_settings.rb create mode 100644 enterprise/app/models/enterprise/concerns/custom_attribute_definition.rb rename spec/{ => enterprise}/models/custom_attribute_definition_spec.rb (100%) diff --git a/app/controllers/api/v1/accounts_controller.rb b/app/controllers/api/v1/accounts_controller.rb index 57062a5b2..bcbf80355 100644 --- a/app/controllers/api/v1/accounts_controller.rb +++ b/app/controllers/api/v1/accounts_controller.rb @@ -92,8 +92,11 @@ class Api::V1::AccountsController < Api::BaseController end def settings_params - params.permit(:auto_resolve_after, :auto_resolve_message, :auto_resolve_ignore_waiting, :audio_transcriptions, :auto_resolve_label, - conversation_required_attributes: []) + params.permit(*permitted_settings_attributes) + end + + def permitted_settings_attributes + [:auto_resolve_after, :auto_resolve_message, :auto_resolve_ignore_waiting, :audio_transcriptions, :auto_resolve_label] end def check_signup_enabled @@ -112,3 +115,5 @@ class Api::V1::AccountsController < Api::BaseController } end end + +Api::V1::AccountsController.prepend_mod_with('Api::V1::AccountsSettings') diff --git a/app/javascript/dashboard/components-next/Conversation/ConversationRequiredEmpty.vue b/app/javascript/dashboard/components-next/Conversation/ConversationRequiredEmpty.vue new file mode 100644 index 000000000..36687245e --- /dev/null +++ b/app/javascript/dashboard/components-next/Conversation/ConversationRequiredEmpty.vue @@ -0,0 +1,17 @@ + + + diff --git a/app/javascript/dashboard/components-next/ConversationWorkflow/ConversationRequiredAttributeItem.vue b/app/javascript/dashboard/components-next/ConversationWorkflow/ConversationRequiredAttributeItem.vue new file mode 100644 index 000000000..70b4a7af5 --- /dev/null +++ b/app/javascript/dashboard/components-next/ConversationWorkflow/ConversationRequiredAttributeItem.vue @@ -0,0 +1,56 @@ + + + diff --git a/app/javascript/dashboard/components-next/ConversationWorkflow/ConversationRequiredAttributes.vue b/app/javascript/dashboard/components-next/ConversationWorkflow/ConversationRequiredAttributes.vue new file mode 100644 index 000000000..e611dd637 --- /dev/null +++ b/app/javascript/dashboard/components-next/ConversationWorkflow/ConversationRequiredAttributes.vue @@ -0,0 +1,186 @@ + + + diff --git a/app/javascript/dashboard/components-next/ConversationWorkflow/ConversationResolveAttributesModal.vue b/app/javascript/dashboard/components-next/ConversationWorkflow/ConversationResolveAttributesModal.vue new file mode 100644 index 000000000..12e13f11e --- /dev/null +++ b/app/javascript/dashboard/components-next/ConversationWorkflow/ConversationResolveAttributesModal.vue @@ -0,0 +1,248 @@ + + +