fix: merge develop and address PR review comments

This commit is contained in:
Tanmay Deep Sharma
2026-03-18 15:31:59 +05:30
1814 changed files with 54531 additions and 10714 deletions
@@ -191,7 +191,7 @@ onMounted(() => {
<template>
<PageLayout
:header-title="$t('CAPTAIN.DOCUMENTS.HEADER')"
:header-title="$t('CAPTAIN.ASSISTANTS.SCENARIOS.TITLE')"
:is-fetching="isFetching"
:show-know-more="false"
:show-pagination-footer="false"
@@ -31,6 +31,8 @@ const toggleStatus = async (status, snoozedUntil) => {
const onCmdSnoozeConversation = snoozeType => {
if (snoozeType === wootConstants.SNOOZE_OPTIONS.UNTIL_CUSTOM_TIME) {
showCustomSnoozeModal.value = true;
} else if (typeof snoozeType === 'number') {
toggleStatus(wootConstants.STATUS_TYPE.SNOOZED, snoozeType);
} else {
toggleStatus(
wootConstants.STATUS_TYPE.SNOOZED,
@@ -4,16 +4,29 @@ import { ref, computed, watchEffect, onMounted } from 'vue';
import { useStore } from 'dashboard/composables/store';
import { useTrack } from 'dashboard/composables';
import { useI18n } from 'vue-i18n';
import { useLocale } from 'shared/composables/useLocale';
import { useAppearanceHotKeys } from 'dashboard/composables/commands/useAppearanceHotKeys';
import { useInboxHotKeys } from 'dashboard/composables/commands/useInboxHotKeys';
import { useGoToCommandHotKeys } from 'dashboard/composables/commands/useGoToCommandHotKeys';
import { useBulkActionsHotKeys } from 'dashboard/composables/commands/useBulkActionsHotKeys';
import { useConversationHotKeys } from 'dashboard/composables/commands/useConversationHotKeys';
import wootConstants from 'dashboard/constants/globals';
import { GENERAL_EVENTS } from 'dashboard/helper/AnalyticsHelper/events';
import {
GENERAL_EVENTS,
SNOOZE_EVENTS,
} from 'dashboard/helper/AnalyticsHelper/events';
import { generateSnoozeSuggestions } from 'dashboard/helper/snoozeHelpers';
import { ICON_SNOOZE_CONVERSATION } from 'dashboard/helper/commandbar/icons';
import {
CMD_SNOOZE_CONVERSATION,
CMD_SNOOZE_NOTIFICATION,
CMD_BULK_ACTION_SNOOZE_CONVERSATION,
} from 'dashboard/helper/commandbar/events';
import { emitter } from 'shared/helpers/mitt';
const store = useStore();
const { t } = useI18n();
const { t, tm } = useI18n();
const { resolvedLocale } = useLocale();
const ninjakeys = ref(null);
@@ -28,48 +41,168 @@ const { goToCommandHotKeys } = useGoToCommandHotKeys();
const { bulkActionsHotKeys } = useBulkActionsHotKeys();
const { conversationHotKeys } = useConversationHotKeys();
const placeholder = computed(() => t('COMMAND_BAR.SEARCH_PLACEHOLDER'));
const SNOOZE_PARENT_IDS = [
'snooze_conversation',
'snooze_notification',
'bulk_action_snooze_conversation',
];
const DYNAMIC_SNOOZE_PREFIX = 'dynamic_snooze_';
const hotKeys = computed(() => [
...inboxHotKeys.value,
...goToCommandHotKeys.value,
...goToAppearanceHotKeys.value,
...bulkActionsHotKeys.value,
...conversationHotKeys.value,
]);
const CUSTOM_SNOOZE = wootConstants.SNOOZE_OPTIONS.UNTIL_CUSTOM_TIME;
const dynamicSnoozeActions = ref([]);
const currentCommandRoot = ref(null);
const placeholder = computed(() =>
SNOOZE_PARENT_IDS.includes(currentCommandRoot.value)
? t('COMMAND_BAR.SNOOZE_PLACEHOLDER')
: t('COMMAND_BAR.SEARCH_PLACEHOLDER')
);
const SNOOZE_PRESET_IDS = new Set(Object.values(wootConstants.SNOOZE_OPTIONS));
const hotKeys = computed(() => {
const allActions = [
...dynamicSnoozeActions.value,
...inboxHotKeys.value,
...goToCommandHotKeys.value,
...goToAppearanceHotKeys.value,
...bulkActionsHotKeys.value,
...conversationHotKeys.value,
];
// When dynamic NLP snooze suggestions exist, hide all preset snooze actions to avoid duplication
if (!dynamicSnoozeActions.value.length) return allActions;
return allActions.filter(
a => !SNOOZE_PRESET_IDS.has(a.id) || !SNOOZE_PARENT_IDS.includes(a.parent)
);
});
const setCommandBarData = () => {
ninjakeys.value.data = hotKeys.value;
};
const onSelected = item => {
const {
detail: { action: { title = null, section = null, id = null } = {} } = {},
} = item;
// Added this condition to prevent setting the selectedSnoozeType to null
// When we select the "custom snooze" (CMD bar will close and the custom snooze modal will open)
if (id === wootConstants.SNOOZE_OPTIONS.UNTIL_CUSTOM_TIME) {
selectedSnoozeType.value = wootConstants.SNOOZE_OPTIONS.UNTIL_CUSTOM_TIME;
} else {
selectedSnoozeType.value = null;
const SNOOZE_EVENT_MAP = {
snooze_conversation: CMD_SNOOZE_CONVERSATION,
snooze_notification: CMD_SNOOZE_NOTIFICATION,
bulk_action_snooze_conversation: CMD_BULK_ACTION_SNOOZE_CONVERSATION,
};
const SNOOZE_SECTION_MAP = {
snooze_conversation: 'COMMAND_BAR.SECTIONS.SNOOZE_CONVERSATION',
snooze_notification: 'COMMAND_BAR.SECTIONS.SNOOZE_NOTIFICATION',
bulk_action_snooze_conversation: 'COMMAND_BAR.SECTIONS.BULK_ACTIONS',
};
const snoozeTranslations = computed(() => {
const raw = tm('SNOOZE_PARSER');
if (!raw || typeof raw !== 'object') return {};
return JSON.parse(JSON.stringify(raw));
});
const buildDynamicSnoozeActions = (search, parentId) => {
const suggestions = generateSnoozeSuggestions(search, new Date(), {
translations: snoozeTranslations.value,
locale: resolvedLocale.value,
});
if (!suggestions.length) return [];
const busEvent = SNOOZE_EVENT_MAP[parentId];
const section = t(SNOOZE_SECTION_MAP[parentId]);
return suggestions.map((parsed, index) => ({
id: `${DYNAMIC_SNOOZE_PREFIX}${index}`,
title:
parsed.label !== parsed.formattedDate
? `${parsed.label} - ${parsed.formattedDate}`
: parsed.formattedDate,
parent: parentId,
section,
icon: ICON_SNOOZE_CONVERSATION,
keywords: search,
handler: () => {
emitter.emit(busEvent, parsed.resolve());
useTrack(SNOOZE_EVENTS.NLP_SNOOZE_APPLIED, { label: parsed.label });
},
}));
};
const resetSnoozeState = () => {
currentCommandRoot.value = null;
dynamicSnoozeActions.value = [];
};
const patchNinjaKeysOpenClose = el => {
if (!el || typeof el.open !== 'function' || typeof el.close !== 'function') {
return;
}
useTrack(GENERAL_EVENTS.COMMAND_BAR, {
section,
action: title,
});
const originalOpen = el.open.bind(el);
const originalClose = el.close.bind(el);
el.open = (...args) => {
const [options = {}] = args;
currentCommandRoot.value = options.parent || null;
dynamicSnoozeActions.value = [];
return originalOpen(...args);
};
el.close = (...args) => {
resetSnoozeState();
return originalClose(...args);
};
};
const onSelected = item => {
const {
detail: {
action: { title = null, section = null, id = null, children = null } = {},
} = {},
} = item;
selectedSnoozeType.value = id === CUSTOM_SNOOZE ? id : null;
if (Array.isArray(children) && children.length) {
currentCommandRoot.value = id;
}
useTrack(GENERAL_EVENTS.COMMAND_BAR, { section, action: title });
setCommandBarData();
};
const onClosed = () => {
// If the selectedSnoozeType is not "SNOOZE_OPTIONS.UNTIL_CUSTOM_TIME (custom snooze)" then we set the context menu chat id to null
// Else we do nothing and its handled in the ChatList.vue hideCustomSnoozeModal() method
const onCommandBarChange = item => {
const { detail: { search = '', actions = [] } = {} } = item;
const normalizedSearch = search.trim();
if (actions.length > 0) {
const uniqueParents = [
...new Set(actions.map(action => action.parent).filter(Boolean)),
];
if (uniqueParents.length === 1) {
currentCommandRoot.value = uniqueParents[0];
} else {
currentCommandRoot.value = null;
}
}
if (
selectedSnoozeType.value !== wootConstants.SNOOZE_OPTIONS.UNTIL_CUSTOM_TIME
!normalizedSearch ||
!SNOOZE_PARENT_IDS.includes(currentCommandRoot.value || '')
) {
dynamicSnoozeActions.value = [];
return;
}
dynamicSnoozeActions.value = buildDynamicSnoozeActions(
normalizedSearch,
currentCommandRoot.value
);
};
const onClosed = () => {
if (selectedSnoozeType.value !== CUSTOM_SNOOZE) {
store.dispatch('setContextMenuChatId', null);
}
resetSnoozeState();
};
watchEffect(() => {
@@ -78,7 +211,10 @@ watchEffect(() => {
}
});
onMounted(setCommandBarData);
onMounted(() => {
setCommandBarData();
patchNinjaKeysOpenClose(ninjakeys.value);
});
</script>
<!-- eslint-disable vue/attribute-hyphenation -->
@@ -88,6 +224,7 @@ onMounted(setCommandBarData);
noAutoLoadMdIcons
hideBreadcrumbs
:placeholder="placeholder"
@change="onCommandBarChange"
@selected="onSelected"
@closed="onClosed"
/>
@@ -22,6 +22,7 @@ const allowedLocales = computed(() => {
id: locale?.code,
name: allLocales[locale?.code],
code: locale?.code,
isDraft: locale?.draft || false,
articlesCount: locale?.articles_count || 0,
categoriesCount: locale?.categories_count || 0,
};
@@ -69,6 +69,8 @@ export default {
onCmdSnoozeNotification(snoozeType) {
if (snoozeType === wootConstants.SNOOZE_OPTIONS.UNTIL_CUSTOM_TIME) {
this.showCustomSnoozeModal = true;
} else if (typeof snoozeType === 'number') {
this.snoozeNotification(snoozeType);
} else {
const snoozedUntil = findSnoozeTime(snoozeType) || null;
this.snoozeNotification(snoozedUntil);
@@ -99,6 +99,7 @@ export default {
healthData: null,
isLoadingHealth: false,
healthError: null,
isRegisteringWebhook: false,
widgetBubblePosition: 'right',
widgetBubbleType: 'standard',
widgetBubbleLauncherTitle: '',
@@ -217,13 +218,10 @@ export default {
return getInboxIconByType(type, medium, 'line');
},
bannerMaxWidth() {
const narrowTabs = [
'collaborators',
'configuration',
'bot-configuration',
];
const narrowTabs = ['collaborators', 'bot-configuration'];
const wideIfWebWidget = ['configuration', 'inbox-settings'];
if (narrowTabs.includes(this.selectedTabKey)) return 'max-w-4xl';
if (this.selectedTabKey === 'inbox-settings') {
if (wideIfWebWidget.includes(this.selectedTabKey)) {
return this.isAWebWidgetInbox ? 'max-w-7xl' : 'max-w-4xl';
}
return 'max-w-7xl';
@@ -353,6 +351,8 @@ export default {
this.$nextTick(() => {
this.setTabFromRouteParam();
});
} else {
this.selectedFeatureFlags = newInbox?.selected_feature_flags || [];
}
},
immediate: true,
@@ -424,6 +424,23 @@ export default {
this.isLoadingHealth = false;
}
},
async registerWebhook() {
if (!this.inbox) return;
try {
this.isRegisteringWebhook = true;
await InboxHealthAPI.registerWebhook(this.inbox.id);
useAlert(this.$t('INBOX_MGMT.ACCOUNT_HEALTH.WEBHOOK.REGISTER_SUCCESS'));
await this.fetchHealthData();
} catch (error) {
useAlert(
error.message ||
this.$t('INBOX_MGMT.ACCOUNT_HEALTH.WEBHOOK.REGISTER_ERROR')
);
} finally {
this.isRegisteringWebhook = false;
}
},
handleFeatureFlag(e) {
this.selectedFeatureFlags = this.toggleInput(
this.selectedFeatureFlags,
@@ -1146,7 +1163,11 @@ export default {
<div v-if="selectedTabKey === 'collaborators'" class="mx-6 max-w-4xl">
<CollaboratorsPage :inbox="inbox" />
</div>
<div v-if="selectedTabKey === 'configuration'" class="mx-6 max-w-4xl">
<div
v-if="selectedTabKey === 'configuration'"
class="mx-6"
:class="isAWebWidgetInbox ? 'max-w-7xl' : 'max-w-4xl'"
>
<ConfigurationPage :inbox="inbox" />
</div>
<div v-if="selectedTabKey === 'csat'">
@@ -1162,7 +1183,11 @@ export default {
<BotConfiguration :inbox="inbox" />
</div>
<div v-if="selectedTabKey === 'whatsapp-health'">
<AccountHealth :health-data="healthData" />
<AccountHealth
:health-data="healthData"
:is-registering-webhook="isRegisteringWebhook"
@register-webhook="registerWebhook"
/>
</div>
</div>
</section>
@@ -10,8 +10,14 @@ const props = defineProps({
type: Object,
default: null,
},
isRegisteringWebhook: {
type: Boolean,
default: false,
},
});
const emit = defineEmits(['registerWebhook']);
const { t } = useI18n();
const QUALITY_COLORS = {
@@ -133,6 +139,28 @@ const formatModeDisplay = mode =>
const getModeStatusTextColor = mode => MODE_COLORS[mode] || 'text-n-slate-12';
const getStatusTextColor = status => STATUS_COLORS[status] || 'text-n-slate-12';
const showWebhookSection = computed(
() => props.healthData?.webhook_configuration !== undefined
);
const webhookUrl = computed(
() =>
props.healthData?.webhook_configuration?.whatsapp_business_account ||
props.healthData?.webhook_configuration?.application
);
const webhookConfigured = computed(() => !!webhookUrl.value);
const webhookUrlMismatch = computed(
() =>
webhookConfigured.value &&
webhookUrl.value !== props.healthData?.expected_webhook_url
);
const handleRegisterWebhook = () => {
emit('registerWebhook');
};
</script>
<template>
@@ -211,6 +239,55 @@ const getStatusTextColor = status => STATUS_COLORS[status] || 'text-n-slate-12';
}}</span>
</div>
</div>
<!-- Webhook configuration card -->
<div
v-if="showWebhookSection"
class="flex flex-col gap-2 p-4 rounded-lg border border-n-weak bg-n-solid-1"
>
<div class="flex gap-2 items-center">
<span class="text-body-main font-medium text-n-slate-11">
{{ t('INBOX_MGMT.ACCOUNT_HEALTH.WEBHOOK.TITLE') }}
</span>
<Icon
v-tooltip.top="t('INBOX_MGMT.ACCOUNT_HEALTH.WEBHOOK.DESCRIPTION')"
icon="i-lucide-info"
class="flex-shrink-0 w-4 h-4 cursor-help text-n-slate-9"
/>
</div>
<div class="flex items-center justify-between gap-3">
<span
v-if="webhookConfigured && !webhookUrlMismatch"
class="inline-flex items-center gap-1.5 px-2 py-0.5 min-h-6 text-label-small rounded-md bg-n-alpha-2 text-n-teal-11"
>
<Icon icon="i-lucide-check-circle" class="w-3.5 h-3.5" />
{{ t('INBOX_MGMT.ACCOUNT_HEALTH.WEBHOOK.CONFIGURED_SUCCESS') }}
</span>
<span
v-else
class="inline-flex items-center gap-1.5 px-2 py-0.5 min-h-6 text-label-small rounded-md bg-n-alpha-2 text-n-amber-11"
>
<Icon icon="i-lucide-alert-triangle" class="w-3.5 h-3.5" />
{{
webhookUrlMismatch
? t('INBOX_MGMT.ACCOUNT_HEALTH.WEBHOOK.URL_MISMATCH')
: t('INBOX_MGMT.ACCOUNT_HEALTH.WEBHOOK.ACTION_REQUIRED')
}}
</span>
<ButtonV4
v-if="!webhookConfigured || webhookUrlMismatch"
sm
solid
blue
:loading="isRegisteringWebhook"
:disabled="isRegisteringWebhook"
class="flex-shrink-0"
@click="handleRegisterWebhook"
>
{{ t('INBOX_MGMT.ACCOUNT_HEALTH.WEBHOOK.REGISTER_BUTTON') }}
</ButtonV4>
</div>
</div>
</div>
<div v-else class="pt-8">
@@ -2,6 +2,8 @@
import { useAlert } from 'dashboard/composables';
import inboxMixin from 'shared/mixins/inboxMixin';
import SettingsFieldSection from 'dashboard/components-next/Settings/SettingsFieldSection.vue';
import SettingsToggleSection from 'dashboard/components-next/Settings/SettingsToggleSection.vue';
import SettingsAccordion from 'dashboard/components-next/Settings/SettingsAccordion.vue';
import ImapSettings from '../ImapSettings.vue';
import SmtpSettings from '../SmtpSettings.vue';
import { useVuelidate } from '@vuelidate/core';
@@ -14,6 +16,8 @@ import { sanitizeAllowedDomains } from 'dashboard/helper/URLHelper';
export default {
components: {
SettingsFieldSection,
SettingsToggleSection,
SettingsAccordion,
ImapSettings,
SmtpSettings,
NextButton,
@@ -33,12 +37,14 @@ export default {
data() {
return {
hmacMandatory: false,
allowMobileWebview: false,
whatsAppInboxAPIKey: '',
isRequestingReauthorization: false,
isSyncingTemplates: false,
allowedDomains: '',
isUpdatingAllowedDomains: false,
callingEnabled: false,
isSettingDefaults: false,
};
},
validations: {
@@ -59,16 +65,30 @@ export default {
inbox() {
this.setDefaults();
},
allowMobileWebview() {
if (!this.isSettingDefaults) this.handleMobileWebviewFlag();
},
hmacMandatory() {
if (!this.isSettingDefaults && this.isAWebWidgetInbox)
this.handleHmacFlag();
},
},
mounted() {
this.setDefaults();
},
methods: {
setDefaults() {
this.isSettingDefaults = true;
this.hmacMandatory = this.inbox.hmac_mandatory || false;
this.allowMobileWebview = (
this.inbox.selected_feature_flags || []
).includes('allow_mobile_webview');
this.allowedDomains = this.inbox.allowed_domains || '';
this.callingEnabled =
this.inbox.provider_config?.calling_enabled || false;
this.$nextTick(() => {
this.isSettingDefaults = false;
});
},
handleHmacFlag() {
this.updateInbox();
@@ -88,6 +108,26 @@ export default {
useAlert(this.$t('INBOX_MGMT.EDIT.API.ERROR_MESSAGE'));
}
},
async handleMobileWebviewFlag() {
try {
const currentFlags = this.inbox.selected_feature_flags || [];
const selectedFlags = this.allowMobileWebview
? [...currentFlags, 'allow_mobile_webview']
: currentFlags.filter(f => f !== 'allow_mobile_webview');
const payload = {
id: this.inbox.id,
formData: false,
channel: {
selected_feature_flags: selectedFlags,
},
};
await this.$store.dispatch('inboxes/updateInbox', payload);
useAlert(this.$t('INBOX_MGMT.EDIT.API.SUCCESS_MESSAGE'));
} catch (error) {
useAlert(this.$t('INBOX_MGMT.EDIT.API.ERROR_MESSAGE'));
}
},
async updateAllowedDomains() {
this.isUpdatingAllowedDomains = true;
const sanitizedAllowedDomains = sanitizeAllowedDomains(
@@ -216,75 +256,86 @@ export default {
</SettingsFieldSection>
</div>
<div v-else-if="isAWebWidgetInbox">
<div>
<SettingsFieldSection
:label="$t('INBOX_MGMT.SETTINGS_POPUP.ALLOWED_DOMAINS.TITLE')"
:help-text="$t('INBOX_MGMT.SETTINGS_POPUP.ALLOWED_DOMAINS.SUBTITLE')"
class="[&>div]:!items-start"
<div class="space-y-4">
<SettingsToggleSection
:header="$t('INBOX_MGMT.SETTINGS_POPUP.ALLOWED_DOMAINS.TITLE')"
:description="
$t('INBOX_MGMT.SETTINGS_POPUP.ALLOWED_DOMAINS.DESCRIPTION')
"
hide-toggle
>
<TextArea
v-model="allowedDomains"
:placeholder="
$t('INBOX_MGMT.SETTINGS_POPUP.ALLOWED_DOMAINS.PLACEHOLDER')
"
auto-height
min-height="8rem"
class="w-full"
/>
<template #extra>
<div class="grid grid-cols-1 lg:grid-cols-8">
<div class="col-span-1 lg:col-span-2 invisible" />
<div class="col-span-1 lg:col-span-6 mt-4 justify-self-end">
<NextButton
:label="$t('INBOX_MGMT.SETTINGS_POPUP.UPDATE')"
:is-loading="isUpdatingAllowedDomains"
@click="updateAllowedDomains"
/>
</div>
</div>
</template>
</SettingsFieldSection>
<SettingsFieldSection
:label="$t('INBOX_MGMT.SETTINGS_POPUP.HMAC_VERIFICATION')"
>
<woot-code :script="inbox.hmac_token" />
<template #extra>
<div class="grid grid-cols-1 lg:grid-cols-8">
<div class="col-span-1 lg:col-span-2 invisible" />
<p
class="col-span-1 lg:col-span-6 mt-1.5 text-label-small text-n-slate-11 ltr:ml-1 rtl:mr-1"
>
{{ $t('INBOX_MGMT.SETTINGS_POPUP.HMAC_DESCRIPTION') }}
<a
target="_blank"
rel="noopener noreferrer"
href="https://www.chatwoot.com/docs/product/channels/live-chat/sdk/identity-validation/"
class="text-n-blue-11 hover:underline text-label-small"
>
{{ $t('INBOX_MGMT.SETTINGS_POPUP.HMAC_LINK_TO_DOCS') }}
</a>
</p>
</div>
</template>
</SettingsFieldSection>
<SettingsFieldSection
:label="$t('INBOX_MGMT.SETTINGS_POPUP.HMAC_MANDATORY_VERIFICATION')"
:help-text="$t('INBOX_MGMT.SETTINGS_POPUP.HMAC_MANDATORY_DESCRIPTION')"
>
<div class="flex gap-2 items-center">
<input
id="hmacMandatory"
v-model="hmacMandatory"
type="checkbox"
@change="handleHmacFlag"
<template #editor>
<TextArea
v-model="allowedDomains"
:placeholder="
$t('INBOX_MGMT.SETTINGS_POPUP.ALLOWED_DOMAINS.PLACEHOLDER')
"
auto-height
resize
class="w-full [&>div]:!bg-transparent [&>div]:!border-none [&>div]:!border-0 [&>div]:px-0 [&>div]:pb-0 [&>div]:pt-0"
/>
<label for="hmacMandatory" class="text-body-main text-n-slate-12">
{{ $t('INBOX_MGMT.EDIT.ENABLE_HMAC.LABEL') }}
</label>
</div>
</SettingsFieldSection>
<div class="mt-3 flex justify-end">
<NextButton
:label="$t('INBOX_MGMT.SETTINGS_POPUP.UPDATE')"
:is-loading="isUpdatingAllowedDomains"
@click="updateAllowedDomains"
/>
</div>
</template>
</SettingsToggleSection>
<SettingsToggleSection
v-model="allowMobileWebview"
:header="$t('INBOX_MGMT.SETTINGS_POPUP.ALLOW_MOBILE_WEBVIEW.LABEL')"
:description="
$t('INBOX_MGMT.SETTINGS_POPUP.ALLOW_MOBILE_WEBVIEW.SUBTITLE')
"
/>
</div>
<SettingsAccordion
:title="$t('INBOX_MGMT.SETTINGS_POPUP.IDENTITY_VALIDATION.TITLE')"
class="mt-6"
>
<SettingsToggleSection
:header="$t('INBOX_MGMT.SETTINGS_POPUP.IDENTITY_VALIDATION.TITLE')"
:description="
$t('INBOX_MGMT.SETTINGS_POPUP.IDENTITY_VALIDATION.DESCRIPTION')
"
hide-toggle
>
<template #editor>
<p class="mb-1 text-sm font-medium text-n-slate-12">
{{ $t('INBOX_MGMT.SETTINGS_POPUP.IDENTITY_VALIDATION.SECRET_KEY') }}
</p>
<woot-code :script="inbox.hmac_token" />
<p class="mt-1.5 text-label-small text-n-slate-11">
{{ $t('INBOX_MGMT.SETTINGS_POPUP.HMAC_DESCRIPTION') }}
<a
target="_blank"
rel="noopener noreferrer"
href="https://www.chatwoot.com/docs/product/channels/live-chat/sdk/identity-validation/"
class="text-n-blue-11 hover:underline text-label-small"
>
{{
$t('INBOX_MGMT.SETTINGS_POPUP.IDENTITY_VALIDATION.VIEW_DOCS')
}}
</a>
</p>
</template>
</SettingsToggleSection>
<SettingsToggleSection
v-model="hmacMandatory"
:header="
$t('INBOX_MGMT.SETTINGS_POPUP.IDENTITY_VALIDATION.REQUIRE_LABEL')
"
:description="
$t(
'INBOX_MGMT.SETTINGS_POPUP.IDENTITY_VALIDATION.REQUIRE_DESCRIPTION'
)
"
/>
</SettingsAccordion>
</div>
<div v-else-if="isAPIInbox">
<SettingsFieldSection