From b5339808808872863a63789041c649773a38dfd7 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 7 May 2025 13:06:15 +0530 Subject: [PATCH] feat: Add support for minutes in auto resolve feature (#11269) ### Summary - Converts conversation auto-resolution duration from days to minutes for more granular control - Updates validation to allow values from 10 minutes (minimum) to 999 days (maximum) - Implements smart messaging to show appropriate time units in activity messages ### Changes - Created migration to convert existing durations from days to minutes (x1440) - Updated conversation resolver to use minutes instead of days - Added dynamic translation key selection based on duration value - Updated related specs and documentation - Added support for displaying durations in days, hours, or minutes based on value ### Test plan - Verify account validation accepts new minute-based ranges - Confirm existing account settings are correctly migrated - Test auto-resolution works properly with minute values - Ensure proper time unit display in activity messages --------- Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> --- app/controllers/api/v1/accounts_controller.rb | 9 +- app/controllers/api/v2/accounts_controller.rb | 2 +- .../components-next/input/DurationInput.vue | 66 +++ .../components-next/switch/Switch.vue | 4 +- .../dashboard/composables/useAccount.js | 10 +- .../dashboard/i18n/locale/en/components.json | 6 + .../i18n/locale/en/generalSettings.json | 17 +- .../dashboard/settings/account/Index.vue | 395 +++++------------- .../account/components/AccountDelete.vue | 149 +++++++ .../settings/account/components/AccountId.vue | 22 + .../account/components/AutoResolve.vue | 121 ++++++ .../settings/account/components/BuildInfo.vue | 56 +++ .../account/components/SectionLayout.vue | 40 ++ .../dashboard/store/modules/accounts.js | 3 +- .../modules/specs/account/actions.spec.js | 5 +- .../v3/components/Form/WithLabel.vue | 54 +-- .../conversations_resolution_scheduler_job.rb | 2 +- app/jobs/conversations/resolution_job.rb | 9 +- app/models/account.rb | 20 +- .../concerns/activity_message_handler.rb | 13 +- app/models/concerns/json_schema_validator.rb | 12 + app/models/conversation.rb | 8 +- .../template/auto_resolve.rb | 25 ++ .../api/v1/models/_account.json.jbuilder | 2 +- config/locales/en.yml | 4 +- ...21082927_add_settings_column_to_account.rb | 5 + ...1085134_update_auto_resolve_to_mminutes.rb | 8 + db/schema.rb | 3 +- .../api/v1/accounts_controller_spec.rb | 11 +- ...ersations_resolution_scheduler_job_spec.rb | 2 +- .../jobs/conversations/resolution_job_spec.rb | 9 +- spec/models/account_spec.rb | 85 +++- .../concerns/json_schema_validator_spec.rb | 43 +- spec/models/conversation_spec.rb | 11 +- 34 files changed, 864 insertions(+), 367 deletions(-) create mode 100644 app/javascript/dashboard/components-next/input/DurationInput.vue create mode 100644 app/javascript/dashboard/routes/dashboard/settings/account/components/AccountDelete.vue create mode 100644 app/javascript/dashboard/routes/dashboard/settings/account/components/AccountId.vue create mode 100644 app/javascript/dashboard/routes/dashboard/settings/account/components/AutoResolve.vue create mode 100644 app/javascript/dashboard/routes/dashboard/settings/account/components/BuildInfo.vue create mode 100644 app/javascript/dashboard/routes/dashboard/settings/account/components/SectionLayout.vue create mode 100644 app/services/message_templates/template/auto_resolve.rb create mode 100644 db/migrate/20250421082927_add_settings_column_to_account.rb create mode 100644 db/migrate/20250421085134_update_auto_resolve_to_mminutes.rb diff --git a/app/controllers/api/v1/accounts_controller.rb b/app/controllers/api/v1/accounts_controller.rb index c0dac6d9e..b247b9715 100644 --- a/app/controllers/api/v1/accounts_controller.rb +++ b/app/controllers/api/v1/accounts_controller.rb @@ -44,8 +44,9 @@ class Api::V1::AccountsController < Api::BaseController end def update - @account.assign_attributes(account_params.slice(:name, :locale, :domain, :support_email, :auto_resolve_duration)) + @account.assign_attributes(account_params.slice(:name, :locale, :domain, :support_email)) @account.custom_attributes.merge!(custom_attributes_params) + @account.settings.merge!(settings_params) @account.custom_attributes['onboarding_step'] = 'invite_team' if @account.custom_attributes['onboarding_step'] == 'account_update' @account.save! end @@ -83,13 +84,17 @@ class Api::V1::AccountsController < Api::BaseController end def account_params - params.permit(:account_name, :email, :name, :password, :locale, :domain, :support_email, :auto_resolve_duration, :user_full_name) + params.permit(:account_name, :email, :name, :password, :locale, :domain, :support_email, :user_full_name) end def custom_attributes_params params.permit(:industry, :company_size, :timezone) end + def settings_params + params.permit(:auto_resolve_after, :auto_resolve_message) + end + def check_signup_enabled raise ActionController::RoutingError, 'Not Found' if GlobalConfigService.load('ENABLE_ACCOUNT_SIGNUP', 'false') == 'false' end diff --git a/app/controllers/api/v2/accounts_controller.rb b/app/controllers/api/v2/accounts_controller.rb index 45faca3d0..bed0a212a 100644 --- a/app/controllers/api/v2/accounts_controller.rb +++ b/app/controllers/api/v2/accounts_controller.rb @@ -54,7 +54,7 @@ class Api::V2::AccountsController < Api::BaseController end def account_params - params.permit(:account_name, :email, :name, :password, :locale, :domain, :support_email, :auto_resolve_duration, :user_full_name) + params.permit(:account_name, :email, :name, :password, :locale, :domain, :support_email, :user_full_name) end def check_signup_enabled diff --git a/app/javascript/dashboard/components-next/input/DurationInput.vue b/app/javascript/dashboard/components-next/input/DurationInput.vue new file mode 100644 index 000000000..15c6d102a --- /dev/null +++ b/app/javascript/dashboard/components-next/input/DurationInput.vue @@ -0,0 +1,66 @@ + + + diff --git a/app/javascript/dashboard/components-next/switch/Switch.vue b/app/javascript/dashboard/components-next/switch/Switch.vue index 4428154a0..766442953 100644 --- a/app/javascript/dashboard/components-next/switch/Switch.vue +++ b/app/javascript/dashboard/components-next/switch/Switch.vue @@ -27,10 +27,10 @@ const updateValue = () => { > {{ t('SWITCH.TOGGLE') }} diff --git a/app/javascript/dashboard/composables/useAccount.js b/app/javascript/dashboard/composables/useAccount.js index 8ace2a5e1..9a2ba012b 100644 --- a/app/javascript/dashboard/composables/useAccount.js +++ b/app/javascript/dashboard/composables/useAccount.js @@ -1,6 +1,6 @@ import { computed } from 'vue'; import { useRoute } from 'vue-router'; -import { useMapGetter } from './store'; +import { useMapGetter, useStore } from './store'; /** * Composable for account-related operations. @@ -12,6 +12,7 @@ export function useAccount() { * @type {import('vue').ComputedRef} */ const route = useRoute(); + const store = useStore(); const getAccountFn = useMapGetter('accounts/getAccount'); const isOnChatwootCloud = useMapGetter('globalConfig/isOnChatwootCloud'); const isFeatureEnabledonAccount = useMapGetter( @@ -44,6 +45,12 @@ export function useAccount() { }; }; + const updateAccount = async data => { + await store.dispatch('accounts/update', { + ...data, + }); + }; + return { accountId, route, @@ -52,5 +59,6 @@ export function useAccount() { accountScopedRoute, isCloudFeatureEnabled, isOnChatwootCloud, + updateAccount, }; } diff --git a/app/javascript/dashboard/i18n/locale/en/components.json b/app/javascript/dashboard/i18n/locale/en/components.json index c7230b1fc..e44c6e039 100644 --- a/app/javascript/dashboard/i18n/locale/en/components.json +++ b/app/javascript/dashboard/i18n/locale/en/components.json @@ -43,5 +43,11 @@ "FEATURE_SPOTLIGHT": { "LEARN_MORE": "Learn more", "WATCH_VIDEO": "Watch video" + }, + "DURATION_INPUT": { + "MINUTES": "Minutes", + "HOURS": "Hours", + "DAYS": "Days", + "PLACEHOLDER": "Enter duration" } } diff --git a/app/javascript/dashboard/i18n/locale/en/generalSettings.json b/app/javascript/dashboard/i18n/locale/en/generalSettings.json index cfda6c7da..b09376b71 100644 --- a/app/javascript/dashboard/i18n/locale/en/generalSettings.json +++ b/app/javascript/dashboard/i18n/locale/en/generalSettings.json @@ -44,6 +44,10 @@ "TITLE": "Account ID", "NOTE": "This ID is required if you are building an API based integration" }, + "AUTO_RESOLVE": { + "TITLE": "Auto-resolve conversations", + "NOTE": "This configuration would allow you to automatically end the conversation after a certain period. Set the duration and customize the message to the user below." + }, "NAME": { "LABEL": "Account name", "PLACEHOLDER": "Your account name", @@ -65,9 +69,18 @@ "ERROR": "" }, "AUTO_RESOLVE_DURATION": { - "LABEL": "Number of days after a ticket should auto resolve if there is no activity", + "LABEL": "Inactivity duration for resolution", + "HELP": "Duration after a ticket should auto resolve if there is no activity", "PLACEHOLDER": "30", - "ERROR": "Please enter a valid auto resolve duration (minimum 1 day and maximum 999 days)" + "ERROR": "Please enter a valid auto resolve duration (minimum 1 day and maximum 999 days)", + "API": { + "SUCCESS": "Auto resolve settings updated successfully", + "ERROR": "Failed to update auto resolve settings" + }, + "UPDATE_BUTTON": "Update Auto-resolve", + "MESSAGE_LABEL": "Custom resolution message", + "MESSAGE_PLACEHOLDER": "Conversation was marked resolved by system due to 15 days of inactivity", + "MESSAGE_HELP": "This message is sent to the customer when a conversation is automatically resolved by the system due to inactivity." }, "FEATURES": { "INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.", diff --git a/app/javascript/dashboard/routes/dashboard/settings/account/Index.vue b/app/javascript/dashboard/routes/dashboard/settings/account/Index.vue index 7afb3cf00..073ef9a7a 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/account/Index.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/account/Index.vue @@ -1,25 +1,34 @@ diff --git a/app/javascript/dashboard/routes/dashboard/settings/account/components/AccountDelete.vue b/app/javascript/dashboard/routes/dashboard/settings/account/components/AccountDelete.vue new file mode 100644 index 000000000..66c969a0a --- /dev/null +++ b/app/javascript/dashboard/routes/dashboard/settings/account/components/AccountDelete.vue @@ -0,0 +1,149 @@ + + + diff --git a/app/javascript/dashboard/routes/dashboard/settings/account/components/AccountId.vue b/app/javascript/dashboard/routes/dashboard/settings/account/components/AccountId.vue new file mode 100644 index 000000000..f02efcdc2 --- /dev/null +++ b/app/javascript/dashboard/routes/dashboard/settings/account/components/AccountId.vue @@ -0,0 +1,22 @@ + + + diff --git a/app/javascript/dashboard/routes/dashboard/settings/account/components/AutoResolve.vue b/app/javascript/dashboard/routes/dashboard/settings/account/components/AutoResolve.vue new file mode 100644 index 000000000..bc76fee83 --- /dev/null +++ b/app/javascript/dashboard/routes/dashboard/settings/account/components/AutoResolve.vue @@ -0,0 +1,121 @@ + + +