Merge branch 'develop' into chore/improve-conversation-snooze

This commit is contained in:
Sivin Varghese
2026-02-20 20:31:24 +05:30
committed by GitHub
41 changed files with 228 additions and 98 deletions
@@ -82,7 +82,13 @@ const attributeIcon = computed(() => {
sm
@click="emit('edit', attribute)"
/>
<Button icon="i-woot-bin" slate sm @click="emit('delete', attribute)" />
<Button
icon="i-woot-bin"
slate
sm
class="hover:enabled:text-n-ruby-11 hover:enabled:bg-n-ruby-2"
@click="emit('delete', attribute)"
/>
</div>
</div>
</div>
@@ -100,6 +100,9 @@ export default {
v-if="currentChat.id"
:chat="currentChat"
:show-back-button="isOnExpandedLayout && !isInboxView"
:class="{
'border-b border-b-n-weak !pt-2': !dashboardApps.length,
}"
/>
<woot-tabs
v-if="dashboardApps.length && currentChat.id"
@@ -182,6 +182,7 @@ onMounted(() => {
icon="i-woot-bin"
slate
sm
class="hover:enabled:text-n-ruby-11 hover:enabled:bg-n-ruby-2"
:is-loading="loading[bot.id]"
@click="openDeletePopup(bot)"
/>
@@ -268,6 +268,7 @@ const confirmDeletion = () => {
icon="i-woot-bin"
slate
sm
class="hover:enabled:text-n-ruby-11 hover:enabled:bg-n-ruby-2"
:is-loading="loading[agent.id]"
@click="openDeletePopup(agent, index)"
/>
@@ -70,14 +70,6 @@ const automationActive = computed({
:is-loading="loading"
@click="$emit('edit', automation)"
/>
<Button
v-tooltip.top="$t('AUTOMATION.FORM.DELETE')"
:is-loading="loading"
icon="i-woot-bin"
slate
sm
@click="$emit('delete', automation)"
/>
<Button
v-tooltip.top="$t('AUTOMATION.CLONE.TOOLTIP')"
icon="i-woot-clone"
@@ -86,6 +78,15 @@ const automationActive = computed({
:is-loading="loading"
@click="$emit('clone', automation)"
/>
<Button
v-tooltip.top="$t('AUTOMATION.FORM.DELETE')"
:is-loading="loading"
icon="i-woot-bin"
slate
sm
class="hover:enabled:text-n-ruby-11 hover:enabled:bg-n-ruby-2"
@click="$emit('delete', automation)"
/>
</div>
</BaseTableCell>
</template>
@@ -232,6 +232,7 @@ const tableHeaders = computed(() => {
icon="i-woot-bin"
slate
sm
class="hover:enabled:text-n-ruby-11 hover:enabled:bg-n-ruby-2"
:is-loading="loading[cannedItem.id]"
@click="openDeletePopup(cannedItem)"
/>
@@ -66,6 +66,7 @@ const getFormattedPermissions = role => {
icon="i-woot-bin"
slate
sm
class="hover:enabled:text-n-ruby-11 hover:enabled:bg-n-ruby-2"
:is-loading="loading[customRole.id]"
@click="emit('delete', customRole)"
/>
@@ -116,17 +116,15 @@ onMounted(() => {
</script>
<template>
<div class="w-full p-8 overflow-auto">
<div
class="grid max-w-3xl grid-cols-1 xs:grid-cols-2 mx-0 gap-6 sm:grid-cols-3"
>
<ChannelItem
v-for="channel in channelList"
:key="channel.key"
:channel="channel"
:enabled-features="enabledFeatures"
@channel-item-click="initChannelAuth"
/>
</div>
<div
class="grid max-w-3xl grid-cols-1 xs:grid-cols-2 mx-0 gap-6 sm:grid-cols-3 p-8"
>
<ChannelItem
v-for="channel in channelList"
:key="channel.key"
:channel="channel"
:enabled-features="enabledFeatures"
@channel-item-click="initChannelAuth"
/>
</div>
</template>
@@ -115,7 +115,7 @@ export default {
<woot-input
v-model="address"
:class="{ error: v$.address.$error }"
class="max-w-[75%] w-full"
class="w-full"
:label="$t('INBOX_MGMT.IMAP.ADDRESS.LABEL')"
:placeholder="$t('INBOX_MGMT.IMAP.ADDRESS.PLACE_HOLDER')"
@blur="v$.address.$touch"
@@ -124,7 +124,7 @@ export default {
v-model="port"
type="number"
:class="{ error: v$.port.$error }"
class="max-w-[75%] w-full"
class="w-full"
:label="$t('INBOX_MGMT.IMAP.PORT.LABEL')"
:placeholder="$t('INBOX_MGMT.IMAP.PORT.PLACE_HOLDER')"
@blur="v$.port.$touch"
@@ -132,7 +132,7 @@ export default {
<woot-input
v-model="login"
:class="{ error: v$.login.$error }"
class="max-w-[75%] w-full"
class="w-full"
:label="$t('INBOX_MGMT.IMAP.LOGIN.LABEL')"
:placeholder="$t('INBOX_MGMT.IMAP.LOGIN.PLACE_HOLDER')"
@blur="v$.login.$touch"
@@ -140,7 +140,7 @@ export default {
<woot-input
v-model="password"
:class="{ error: v$.password.$error }"
class="max-w-[75%] w-full"
class="w-full"
:label="$t('INBOX_MGMT.IMAP.PASSWORD.LABEL')"
:placeholder="$t('INBOX_MGMT.IMAP.PASSWORD.PLACE_HOLDER')"
type="password"
@@ -62,14 +62,14 @@ const items = computed(() => {
<div class="mx-auto flex flex-col gap-6 mb-8 max-w-7xl w-full !px-6">
<PageHeader class="block lg:hidden !mb-0" :header-title="pageTitle" />
<div
class="grid grid-cols-1 lg:grid-cols-8 lg:divide-x lg:divide-n-weak rounded-xl border border-n-weak min-h-[52rem]"
class="grid grid-cols-1 lg:grid-cols-8 lg:divide-x lg:divide-n-weak rounded-xl border border-n-weak h-full min-h-[50dvh]"
>
<woot-wizard
class="hidden lg:block col-span-2 h-fit py-8 px-6"
:global-config="globalConfig"
:items="items"
/>
<div class="col-span-6 overflow-hidden">
<div class="col-span-6 flex flex-col overflow-y-auto">
<router-view />
</div>
</div>
@@ -170,6 +170,7 @@ const openDelete = inbox => {
icon="i-woot-bin"
slate
sm
class="hover:enabled:text-n-ruby-11 hover:enabled:bg-n-ruby-2"
@click="openDelete(inbox)"
/>
</div>
@@ -214,6 +214,18 @@ export default {
const { medium, channel_type: type } = this.inbox;
return getInboxIconByType(type, medium, 'line');
},
bannerMaxWidth() {
const narrowTabs = [
'collaborators',
'configuration',
'bot-configuration',
];
if (narrowTabs.includes(this.selectedTabKey)) return 'max-w-4xl';
if (this.selectedTabKey === 'inbox-settings') {
return this.isAWebWidgetInbox ? 'max-w-7xl' : 'max-w-4xl';
}
return 'max-w-7xl';
},
inboxName() {
if (this.isATwilioSMSChannel || this.isATwilioWhatsAppChannel) {
return `${this.inbox.name} (${
@@ -571,44 +583,57 @@ export default {
v-if="microsoftUnauthorized"
:inbox="inbox"
class="mb-4"
:class="bannerMaxWidth"
/>
<FacebookReauthorize
v-if="facebookUnauthorized"
:inbox="inbox"
class="mb-4"
:class="bannerMaxWidth"
/>
<GoogleReauthorize
v-if="googleUnauthorized"
:inbox="inbox"
class="mb-4"
:class="bannerMaxWidth"
/>
<InstagramReauthorize
v-if="instagramUnauthorized"
:inbox="inbox"
class="mb-4"
:class="bannerMaxWidth"
/>
<TiktokReauthorize
v-if="tiktokUnauthorized"
:inbox="inbox"
class="mb-4"
:class="bannerMaxWidth"
/>
<WhatsappReauthorize
v-if="whatsappUnauthorized"
:whatsapp-registration-incomplete="whatsappRegistrationIncomplete"
:inbox="inbox"
class="mb-4"
:class="bannerMaxWidth"
/>
<DuplicateInboxBanner
v-if="hasDuplicateInstagramInbox"
:content="$t('INBOX_MGMT.ADD.INSTAGRAM.DUPLICATE_INBOX_BANNER')"
class="mx-6 mb-4"
:class="bannerMaxWidth"
/>
<div
v-if="selectedTabKey === 'inbox-settings'"
class="flex flex-col md:flex-row items-center lg:items-start justify-between gap-5 lg:gap-10 mx-6"
>
<div class="max-w-2xl flex-1 flex flex-col min-w-0">
<div
class="flex-1 flex flex-col min-w-0"
:class="{
'max-w-2xl': isAWebWidgetInbox,
'max-w-4xl': !isAWebWidgetInbox,
}"
>
<div class="flex flex-col gap-1 items-start mb-4">
<label class="text-heading-3 text-n-slate-12">
{{ $t('INBOX_MGMT.ADD.WEBSITE_CHANNEL.CHANNEL_AVATAR.LABEL') }}
@@ -755,6 +780,7 @@ export default {
<SenderNameExamplePreview
:sender-name-type="senderNameType"
:business-name="businessName"
:is-website-channel="isAWebWidgetInbox"
@update="toggleSenderNameType"
/>
</template>
@@ -1107,10 +1133,10 @@ export default {
</div>
</div>
<div v-if="selectedTabKey === 'collaborators'" class="mx-6 max-w-3xl">
<div v-if="selectedTabKey === 'collaborators'" class="mx-6 max-w-4xl">
<CollaboratorsPage :inbox="inbox" />
</div>
<div v-if="selectedTabKey === 'configuration'">
<div v-if="selectedTabKey === 'configuration'" class="mx-6 max-w-4xl">
<ConfigurationPage :inbox="inbox" />
</div>
<div v-if="selectedTabKey === 'csat'">
@@ -175,7 +175,7 @@ export default {
<woot-input
v-model="address"
:class="{ error: v$.address.$error }"
class="max-w-[75%] w-full"
class="w-full"
:label="$t('INBOX_MGMT.SMTP.ADDRESS.LABEL')"
:placeholder="$t('INBOX_MGMT.SMTP.ADDRESS.PLACE_HOLDER')"
@blur="v$.address.$touch"
@@ -184,7 +184,7 @@ export default {
v-model="port"
type="number"
:class="{ error: v$.port.$error }"
class="max-w-[75%] w-full"
class="w-full"
:label="$t('INBOX_MGMT.SMTP.PORT.LABEL')"
:placeholder="$t('INBOX_MGMT.SMTP.PORT.PLACE_HOLDER')"
@blur="v$.port.$touch"
@@ -192,7 +192,7 @@ export default {
<woot-input
v-model="login"
:class="{ error: v$.login.$error }"
class="max-w-[75%] w-full"
class="w-full"
:label="$t('INBOX_MGMT.SMTP.LOGIN.LABEL')"
:placeholder="$t('INBOX_MGMT.SMTP.LOGIN.PLACE_HOLDER')"
@blur="v$.login.$touch"
@@ -200,7 +200,7 @@ export default {
<woot-input
v-model="password"
:class="{ error: v$.password.$error }"
class="max-w-[75%] w-full"
class="w-full"
:label="$t('INBOX_MGMT.SMTP.PASSWORD.LABEL')"
:placeholder="$t('INBOX_MGMT.SMTP.PASSWORD.PLACE_HOLDER')"
type="password"
@@ -209,7 +209,7 @@ export default {
<woot-input
v-model="domain"
:class="{ error: v$.domain.$error }"
class="max-w-[75%] w-full"
class="w-full"
:label="$t('INBOX_MGMT.SMTP.DOMAIN.LABEL')"
:placeholder="$t('INBOX_MGMT.SMTP.DOMAIN.PLACE_HOLDER')"
@blur="v$.domain.$touch"
@@ -220,14 +220,14 @@ export default {
:action="handleEncryptionChange"
/>
<SingleSelectDropdown
class="max-w-[75%] w-full"
class="w-full"
:label="$t('INBOX_MGMT.SMTP.OPEN_SSL_VERIFY_MODE')"
:selected="openSSLVerifyMode"
:options="openSSLVerifyModes"
:action="handleSSLModeChange"
/>
<SingleSelectDropdown
class="max-w-[75%] w-full"
class="w-full"
:label="$t('INBOX_MGMT.SMTP.AUTH_MECHANISM')"
:selected="authMechanism"
:options="authMechanisms"
@@ -84,7 +84,7 @@ export default {
</script>
<template>
<div class="mx-6 max-w-3xl">
<div class="mx-6 max-w-4xl">
<LoadingState v-if="uiFlags.isFetching || uiFlags.isFetchingAgentBot" />
<form v-else @submit.prevent="updateActiveAgentBot">
<SettingsFieldSection
@@ -13,6 +13,10 @@ const props = defineProps({
type: String,
default: '',
},
isWebsiteChannel: {
type: Boolean,
default: false,
},
});
const emit = defineEmits(['update']);
@@ -56,7 +60,10 @@ const toggleSenderNameType = key => {
<template>
<div
class="flex flex-col sm:flex-row md:flex-col xl:flex-row items-start gap-4 mt-3 min-w-0"
class="flex flex-col items-start gap-4 mt-3 min-w-0"
:class="
isWebsiteChannel ? 'sm:flex-row md:flex-col xl:flex-row' : 'sm:flex-row'
"
>
<RadioCard
v-for="keyOption in senderNameKeyOptions"
@@ -149,7 +149,7 @@ export default {
</script>
<template>
<div v-if="isATwilioChannel" class="mx-6">
<div v-if="isATwilioChannel">
<SettingsFieldSection
:label="$t('INBOX_MGMT.ADD.TWILIO.API_CALLBACK.TITLE')"
:help-text="$t('INBOX_MGMT.ADD.TWILIO.API_CALLBACK.SUBTITLE')"
@@ -168,7 +168,7 @@ export default {
</NextButton>
</SettingsFieldSection>
</div>
<div v-else-if="isAVoiceChannel" class="mx-6">
<div v-else-if="isAVoiceChannel">
<SettingsFieldSection
:label="$t('INBOX_MGMT.ADD.VOICE.CONFIGURATION.TWILIO_VOICE_URL_TITLE')"
:help-text="
@@ -187,7 +187,7 @@ export default {
</SettingsFieldSection>
</div>
<div v-else-if="isALineChannel" class="mx-6">
<div v-else-if="isALineChannel">
<SettingsFieldSection
:label="$t('INBOX_MGMT.ADD.LINE_CHANNEL.API_CALLBACK.TITLE')"
:help-text="$t('INBOX_MGMT.ADD.LINE_CHANNEL.API_CALLBACK.SUBTITLE')"
@@ -196,7 +196,7 @@ export default {
</SettingsFieldSection>
</div>
<div v-else-if="isAWebWidgetInbox">
<div class="mx-6">
<div>
<SettingsFieldSection
:label="$t('INBOX_MGMT.SETTINGS_POPUP.ALLOWED_DOMAINS.TITLE')"
:help-text="$t('INBOX_MGMT.SETTINGS_POPUP.ALLOWED_DOMAINS.SUBTITLE')"
@@ -266,7 +266,7 @@ export default {
</SettingsFieldSection>
</div>
</div>
<div v-else-if="isAPIInbox" class="mx-6">
<div v-else-if="isAPIInbox">
<SettingsFieldSection
:label="$t('INBOX_MGMT.SETTINGS_POPUP.INBOX_IDENTIFIER')"
:help-text="$t('INBOX_MGMT.SETTINGS_POPUP.INBOX_IDENTIFIER_SUB_TEXT')"
@@ -297,7 +297,7 @@ export default {
</div>
</SettingsFieldSection>
</div>
<div v-else-if="isAnEmailChannel" class="mx-6">
<div v-else-if="isAnEmailChannel">
<div>
<SettingsFieldSection
:label="$t('INBOX_MGMT.SETTINGS_POPUP.FORWARD_EMAIL_TITLE')"
@@ -325,7 +325,7 @@ export default {
<SmtpSettings v-if="inbox.imap_enabled" :inbox="inbox" />
</div>
<div v-else-if="isAWhatsAppChannel && !isATwilioChannel">
<div v-if="inbox.provider_config" class="mx-6">
<div v-if="inbox.provider_config">
<!-- Embedded Signup Section -->
<template v-if="isEmbeddedSignupWhatsApp">
<SettingsFieldSection
@@ -51,6 +51,7 @@ defineEmits(['edit', 'delete']);
icon="i-woot-bin"
slate
sm
class="hover:enabled:text-n-ruby-11 hover:enabled:bg-n-ruby-2"
@click="$emit('delete', app)"
/>
</div>
@@ -133,6 +133,7 @@ const inboxName = hook => (hook.inbox ? hook.inbox.name : '');
icon="i-woot-bin"
slate
sm
class="hover:enabled:text-n-ruby-11 hover:enabled:bg-n-ruby-2"
@click="$emit('delete', hook)"
/>
</div>
@@ -73,6 +73,7 @@ const subscribedEvents = computed(() => {
icon="i-woot-bin"
slate
sm
class="hover:enabled:text-n-ruby-11 hover:enabled:bg-n-ruby-2"
@click="emit('delete', webhook, index)"
/>
</div>
@@ -174,6 +174,7 @@ onBeforeMount(() => {
icon="i-woot-bin"
slate
sm
class="hover:enabled:text-n-ruby-11 hover:enabled:bg-n-ruby-2"
:is-loading="loading[label.id]"
@click="openDeletePopup(label)"
/>
@@ -96,6 +96,7 @@ const visibilityLabel = computed(() => {
icon="i-woot-bin"
slate
sm
class="hover:enabled:text-n-ruby-11 hover:enabled:bg-n-ruby-2"
@click="$emit('delete')"
/>
</div>
@@ -288,6 +288,7 @@ export default {
icon="i-woot-bin"
slate
sm
class="hover:enabled:text-n-ruby-11 hover:enabled:bg-n-ruby-2"
:is-loading="loading[sla.id]"
@click="openDeletePopup(sla)"
/>
@@ -88,19 +88,14 @@ export default {
</script>
<template>
<div class="h-full w-full p-6 col-span-6">
<form
class="flex flex-wrap mx-0 overflow-x-auto"
@submit.prevent="addAgents"
>
<div class="w-full">
<PageHeader
:header-title="headerTitle"
:header-content="$t('TEAMS_SETTINGS.ADD.DESC')"
/>
</div>
<div class="h-full w-full p-8 col-span-6 overflow-auto">
<form class="flex flex-col gap-4 mx-0" @submit.prevent="addAgents">
<PageHeader
:header-title="headerTitle"
:header-content="$t('TEAMS_SETTINGS.ADD.DESC')"
/>
<div class="w-full">
<div class="w-full h-full">
<div v-if="v$.selectedAgents.$error">
<p class="error-message pb-2">
{{ $t('TEAMS_SETTINGS.ADD.AGENT_VALIDATION_ERROR') }}
@@ -37,7 +37,7 @@ export default {
</script>
<template>
<div class="h-full w-full p-6 col-span-6">
<div class="h-full w-full p-6 col-span-6 overflow-y-auto">
<PageHeader
:header-title="$t('TEAMS_SETTINGS.CREATE_FLOW.CREATE.TITLE')"
:header-content="$t('TEAMS_SETTINGS.CREATE_FLOW.CREATE.DESC')"
@@ -25,7 +25,7 @@ export default {
<template>
<div class="flex flex-col gap-6 mb-8 max-w-7xl mx-auto w-full !px-6">
<div
class="grid grid-cols-1 lg:grid-cols-8 lg:divide-x lg:divide-n-weak rounded-xl border border-n-weak min-h-[43rem] w-full"
class="grid grid-cols-1 lg:grid-cols-8 lg:divide-x lg:divide-n-weak rounded-xl border border-n-weak h-full min-h-[50dvh] w-full"
>
<woot-wizard
class="hidden lg:block col-span-2 h-fit py-8 px-6"
@@ -103,19 +103,14 @@ export default {
</script>
<template>
<div class="h-full w-full p-8 col-span-6">
<form
class="flex flex-wrap mx-0 overflow-x-auto"
@submit.prevent="addAgents"
>
<div class="w-full">
<PageHeader
:header-title="headerTitle"
:header-content="$t('TEAMS_SETTINGS.EDIT_FLOW.AGENTS.DESC')"
/>
</div>
<div class="h-full w-full p-8 col-span-6 overflow-auto">
<form class="flex flex-col gap-4 mx-0" @submit.prevent="addAgents">
<PageHeader
:header-title="headerTitle"
:header-content="$t('TEAMS_SETTINGS.EDIT_FLOW.AGENTS.DESC')"
/>
<div class="w-full">
<div class="w-full h-full">
<div v-if="v$.selectedAgents.$error">
<p class="error-message pb-2">
{{ $t('TEAMS_SETTINGS.ADD.AGENT_VALIDATION_ERROR') }}
@@ -57,7 +57,7 @@ export default {
</script>
<template>
<div class="h-full w-full p-8 col-span-6">
<div class="h-full w-full p-8 col-span-6 overflow-y-auto">
<PageHeader
:header-title="$t('TEAMS_SETTINGS.EDIT_FLOW.CREATE.TITLE')"
:header-content="$t('TEAMS_SETTINGS.EDIT_FLOW.CREATE.DESC')"
@@ -29,7 +29,7 @@ export default {
<template>
<div class="flex flex-col gap-6 mb-8 max-w-7xl mx-auto w-full !px-6">
<div
class="grid grid-cols-1 lg:grid-cols-8 lg:divide-x lg:divide-n-weak rounded-xl border border-n-weak min-h-[43rem] w-full"
class="grid grid-cols-1 lg:grid-cols-8 lg:divide-x lg:divide-n-weak rounded-xl border border-n-weak h-full min-h-[50dvh] w-full"
>
<woot-wizard
class="hidden lg:block col-span-2 h-fit py-8 px-6"
@@ -161,6 +161,7 @@ const confirmPlaceHolderText = computed(() =>
icon="i-woot-bin"
slate
sm
class="hover:enabled:text-n-ruby-11 hover:enabled:bg-n-ruby-2"
:is-loading="loading[team.id]"
@click="openDelete(team)"
/>
@@ -24,7 +24,7 @@ describe('GoogleOAuthButton.vue', () => {
const googleAuthUrl = new URL(wrapper.vm.getGoogleAuthUrl());
const params = googleAuthUrl.searchParams;
expect(googleAuthUrl.origin).toBe('https://accounts.google.com');
expect(googleAuthUrl.pathname).toBe('/o/oauth2/auth/oauthchooseaccount');
expect(googleAuthUrl.pathname).toBe('/o/oauth2/auth');
expect(params.get('client_id')).toBe('clientId');
expect(params.get('redirect_uri')).toBe(
'http://localhost:3000/test-callback'
@@ -6,8 +6,7 @@ export default {
// Creating the URL manually because the devise-token-auth with
// omniauth has a standing issue on redirecting the post request
// https://github.com/lynndylanhurley/devise_token_auth/issues/1466
const baseUrl =
'https://accounts.google.com/o/oauth2/auth/oauthchooseaccount';
const baseUrl = 'https://accounts.google.com/o/oauth2/auth';
const clientId = window.chatwootConfig.googleOAuthClientId;
const redirectUri = window.chatwootConfig.googleOAuthCallbackUrl;
const responseType = 'code';