|
-
|
@@ -137,7 +151,7 @@ export default {
:title="$t('INBOX_MGMT.SETTINGS_POPUP.HMAC_MANDATORY_VERIFICATION')"
:sub-title="$t('INBOX_MGMT.SETTINGS_POPUP.HMAC_MANDATORY_DESCRIPTION')"
>
-
+
-
+
diff --git a/app/javascript/dashboard/routes/dashboard/settings/profile/NotificationPreferences.vue b/app/javascript/dashboard/routes/dashboard/settings/profile/NotificationPreferences.vue
index 3c239a3c8..9aab4a5be 100644
--- a/app/javascript/dashboard/routes/dashboard/settings/profile/NotificationPreferences.vue
+++ b/app/javascript/dashboard/routes/dashboard/settings/profile/NotificationPreferences.vue
@@ -9,13 +9,13 @@ import {
verifyServiceWorkerExistence,
} from 'dashboard/helper/pushHelper.js';
import { FEATURE_FLAGS } from 'dashboard/featureFlags';
-import FormSwitch from 'v3/components/Form/Switch.vue';
+import ToggleSwitch from 'dashboard/components-next/switch/Switch.vue';
import { NOTIFICATION_TYPES } from './constants';
export default {
components: {
TableHeaderCell,
- FormSwitch,
+ ToggleSwitch,
CheckBox,
},
data() {
@@ -284,9 +284,9 @@ export default {
{{ $t('PROFILE_SETTINGS.FORM.NOTIFICATIONS.BROWSER_PERMISSION') }}
-
diff --git a/app/javascript/dashboard/routes/dashboard/settings/reports/components/FilterSelector.vue b/app/javascript/dashboard/routes/dashboard/settings/reports/components/FilterSelector.vue
index 4e66f6350..7454c46d4 100644
--- a/app/javascript/dashboard/routes/dashboard/settings/reports/components/FilterSelector.vue
+++ b/app/javascript/dashboard/routes/dashboard/settings/reports/components/FilterSelector.vue
@@ -10,6 +10,7 @@ import ReportsFiltersRatings from './Filters/Ratings.vue';
import subDays from 'date-fns/subDays';
import { DATE_RANGE_OPTIONS } from '../constants';
import { getUnixStartOfDay, getUnixEndOfDay } from 'helpers/DateHelper';
+import ToggleSwitch from 'dashboard/components-next/switch/Switch.vue';
export default {
components: {
@@ -21,6 +22,7 @@ export default {
ReportsFiltersInboxes,
ReportsFiltersTeams,
ReportsFiltersRatings,
+ ToggleSwitch,
},
props: {
showGroupByFilter: {
@@ -106,11 +108,6 @@ export default {
return this.validGroupOptions[0];
},
},
- watch: {
- businessHoursSelected() {
- this.emitChange();
- },
- },
mounted() {
this.emitChange();
},
@@ -224,7 +221,7 @@ export default {
{{ $t('REPORT.BUSINESS_HOURS') }}
-
+
diff --git a/app/javascript/dashboard/routes/dashboard/settings/reports/components/ReportFilters.vue b/app/javascript/dashboard/routes/dashboard/settings/reports/components/ReportFilters.vue
index d7409dc22..fe348417d 100644
--- a/app/javascript/dashboard/routes/dashboard/settings/reports/components/ReportFilters.vue
+++ b/app/javascript/dashboard/routes/dashboard/settings/reports/components/ReportFilters.vue
@@ -5,6 +5,7 @@ import startOfDay from 'date-fns/startOfDay';
import subDays from 'date-fns/subDays';
import Thumbnail from 'dashboard/components/widgets/Thumbnail.vue';
import WootDateRangePicker from 'dashboard/components/ui/DateRangePicker.vue';
+import ToggleSwitch from 'dashboard/components-next/switch/Switch.vue';
import { GROUP_BY_FILTER } from '../constants';
const CUSTOM_DATE_RANGE_ID = 5;
@@ -13,6 +14,7 @@ export default {
components: {
WootDateRangePicker,
Thumbnail,
+ ToggleSwitch,
},
props: {
currentFilter: {
@@ -125,9 +127,6 @@ export default {
groupByFilterItemsList() {
this.currentSelectedGroupByFilter = this.selectedGroupByFilter;
},
- businessHoursSelected() {
- this.$emit('businessHoursToggle', this.businessHoursSelected);
- },
},
mounted() {
this.onDateRangeChange();
@@ -140,6 +139,9 @@ export default {
groupBy: this.groupBy,
});
},
+ onBusinessHoursToggle() {
+ this.$emit('businessHoursToggle', this.businessHoursSelected);
+ },
fromCustomDate(date) {
return getUnixTime(startOfDay(date));
},
@@ -303,7 +305,10 @@ export default {
{{ $t('REPORT.BUSINESS_HOURS') }}
-
+
diff --git a/app/javascript/dashboard/routes/dashboard/settings/sla/SlaForm.vue b/app/javascript/dashboard/routes/dashboard/settings/sla/SlaForm.vue
index 5dc6e16f4..8dd30a99c 100644
--- a/app/javascript/dashboard/routes/dashboard/settings/sla/SlaForm.vue
+++ b/app/javascript/dashboard/routes/dashboard/settings/sla/SlaForm.vue
@@ -5,11 +5,13 @@ import validations from './validations';
import SlaTimeInput from './SlaTimeInput.vue';
import NextButton from 'dashboard/components-next/button/Button.vue';
import { useVuelidate } from '@vuelidate/core';
+import ToggleSwitch from 'dashboard/components-next/switch/Switch.vue';
export default {
components: {
SlaTimeInput,
NextButton,
+ ToggleSwitch,
},
props: {
selectedResponse: {
@@ -203,7 +205,7 @@ export default {
{{ $t('SLA.FORM.BUSINESS_HOURS.PLACEHOLDER') }}
-
+
diff --git a/app/javascript/dashboard/store/modules/inboxes.js b/app/javascript/dashboard/store/modules/inboxes.js
index e642ce5c5..a325a28e9 100644
--- a/app/javascript/dashboard/store/modules/inboxes.js
+++ b/app/javascript/dashboard/store/modules/inboxes.js
@@ -281,6 +281,13 @@ export const actions = {
throw new Error(error);
}
},
+ syncTemplates: async (_, inboxId) => {
+ try {
+ await InboxesAPI.syncTemplates(inboxId);
+ } catch (error) {
+ throw new Error(error);
+ }
+ },
};
export const mutations = {
diff --git a/app/javascript/dashboard/store/modules/specs/inboxes/actions.spec.js b/app/javascript/dashboard/store/modules/specs/inboxes/actions.spec.js
index a91addaa3..edbc3f764 100644
--- a/app/javascript/dashboard/store/modules/specs/inboxes/actions.spec.js
+++ b/app/javascript/dashboard/store/modules/specs/inboxes/actions.spec.js
@@ -231,4 +231,28 @@ describe('#actions', () => {
).rejects.toThrow(Error);
});
});
+
+ describe('#syncTemplates', () => {
+ it('sends correct API call when sync is successful', async () => {
+ axios.post.mockResolvedValue({
+ data: { message: 'Template sync initiated successfully' },
+ });
+
+ await actions.syncTemplates({ commit }, 123);
+
+ expect(axios.post).toHaveBeenCalledWith(
+ '/api/v1/inboxes/123/sync_templates'
+ );
+ });
+
+ it('throws error when API call fails', async () => {
+ const errorMessage =
+ 'Template sync is only available for WhatsApp channels';
+ axios.post.mockRejectedValue(new Error(errorMessage));
+
+ await expect(actions.syncTemplates({ commit }, 123)).rejects.toThrow(
+ errorMessage
+ );
+ });
+ });
});
diff --git a/app/javascript/entrypoints/dashboard.js b/app/javascript/entrypoints/dashboard.js
index a2184d074..88bd35926 100644
--- a/app/javascript/entrypoints/dashboard.js
+++ b/app/javascript/entrypoints/dashboard.js
@@ -7,7 +7,6 @@ import hljsVuePlugin from '@highlightjs/vue-plugin';
import Multiselect from 'vue-multiselect';
import { plugin, defaultConfig } from '@formkit/vue';
-import WootSwitch from 'components/ui/Switch.vue';
import WootWizard from 'components/ui/Wizard.vue';
import FloatingVue from 'floating-vue';
import WootUiKit from 'dashboard/components';
@@ -90,7 +89,6 @@ app.use(FloatingVue, {
app.use(hljsVuePlugin);
app.component('multiselect', Multiselect);
-app.component('woot-switch', WootSwitch);
app.component('woot-wizard', WootWizard);
app.component('fluent-icon', FluentIcon);
diff --git a/app/javascript/v3/components/Form/Switch.vue b/app/javascript/v3/components/Form/Switch.vue
deleted file mode 100644
index c7e9e93fb..000000000
--- a/app/javascript/v3/components/Form/Switch.vue
+++ /dev/null
@@ -1,36 +0,0 @@
-
-
-
-
-
diff --git a/app/policies/inbox_policy.rb b/app/policies/inbox_policy.rb
index 9c2d3c094..8a6f81484 100644
--- a/app/policies/inbox_policy.rb
+++ b/app/policies/inbox_policy.rb
@@ -57,4 +57,8 @@ class InboxPolicy < ApplicationPolicy
def avatar?
@account_user.administrator?
end
+
+ def sync_templates?
+ @account_user.administrator?
+ end
end
diff --git a/config/routes.rb b/config/routes.rb
index 681c21e44..0627c2c6d 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -185,6 +185,7 @@ Rails.application.routes.draw do
get :agent_bot, on: :member
post :set_agent_bot, on: :member
delete :avatar, on: :member
+ post :sync_templates, on: :member
end
resources :inbox_members, only: [:create, :show], param: :inbox_id do
collection do
diff --git a/spec/controllers/api/v1/accounts/inboxes_controller_spec.rb b/spec/controllers/api/v1/accounts/inboxes_controller_spec.rb
index 96272f9ac..cc235cada 100644
--- a/spec/controllers/api/v1/accounts/inboxes_controller_spec.rb
+++ b/spec/controllers/api/v1/accounts/inboxes_controller_spec.rb
@@ -904,4 +904,80 @@ RSpec.describe 'Inboxes API', type: :request do
end
end
end
+
+ describe 'POST /api/v1/accounts/{account.id}/inboxes/:id/sync_templates' do
+ let(:whatsapp_channel) do
+ create(:channel_whatsapp, account: account, provider: 'whatsapp_cloud', sync_templates: false, validate_provider_config: false)
+ end
+ let(:whatsapp_inbox) { create(:inbox, account: account, channel: whatsapp_channel) }
+ let(:non_whatsapp_inbox) { create(:inbox, account: account) }
+
+ context 'when it is an unauthenticated user' do
+ it 'returns unauthorized' do
+ post "/api/v1/accounts/#{account.id}/inboxes/#{whatsapp_inbox.id}/sync_templates"
+
+ expect(response).to have_http_status(:unauthorized)
+ end
+ end
+
+ context 'when it is an authenticated agent' do
+ it 'returns unauthorized for agent' do
+ post "/api/v1/accounts/#{account.id}/inboxes/#{whatsapp_inbox.id}/sync_templates",
+ headers: agent.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:unauthorized)
+ end
+ end
+
+ context 'when it is an authenticated administrator' do
+ context 'with WhatsApp inbox' do
+ it 'successfully initiates template sync' do
+ expect(Channels::Whatsapp::TemplatesSyncJob).to receive(:perform_later).with(whatsapp_channel)
+
+ post "/api/v1/accounts/#{account.id}/inboxes/#{whatsapp_inbox.id}/sync_templates",
+ headers: admin.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:success)
+ json_response = response.parsed_body
+ expect(json_response['message']).to eq('Template sync initiated successfully')
+ end
+
+ it 'handles job errors gracefully' do
+ allow(Channels::Whatsapp::TemplatesSyncJob).to receive(:perform_later).and_raise(StandardError, 'Job failed')
+
+ post "/api/v1/accounts/#{account.id}/inboxes/#{whatsapp_inbox.id}/sync_templates",
+ headers: admin.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:internal_server_error)
+ json_response = response.parsed_body
+ expect(json_response['error']).to eq('Job failed')
+ end
+ end
+
+ context 'with non-WhatsApp inbox' do
+ it 'returns unprocessable entity error' do
+ post "/api/v1/accounts/#{account.id}/inboxes/#{non_whatsapp_inbox.id}/sync_templates",
+ headers: admin.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:unprocessable_entity)
+ json_response = response.parsed_body
+ expect(json_response['error']).to eq('Template sync is only available for WhatsApp channels')
+ end
+ end
+
+ context 'with non-existent inbox' do
+ it 'returns not found error' do
+ post "/api/v1/accounts/#{account.id}/inboxes/999999/sync_templates",
+ headers: admin.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:not_found)
+ end
+ end
+ end
+ end
end
|