feat: Update Inbox/Team creation UI (#12305)

This commit is contained in:
Sivin Varghese
2025-09-02 14:11:38 +05:30
committed by GitHub
parent 8606aa1310
commit b0112a2869
34 changed files with 600 additions and 465 deletions

View File

@@ -2,23 +2,23 @@ import { computed } from 'vue';
export function useChannelIcon(inbox) { export function useChannelIcon(inbox) {
const channelTypeIconMap = { const channelTypeIconMap = {
'Channel::Api': 'i-ri-cloudy-fill', 'Channel::Api': 'i-woot-api',
'Channel::Email': 'i-ri-mail-fill', 'Channel::Email': 'i-woot-mail',
'Channel::FacebookPage': 'i-ri-messenger-fill', 'Channel::FacebookPage': 'i-woot-messenger',
'Channel::Line': 'i-ri-line-fill', 'Channel::Line': 'i-woot-line',
'Channel::Sms': 'i-ri-chat-1-fill', 'Channel::Sms': 'i-woot-sms',
'Channel::Telegram': 'i-ri-telegram-fill', 'Channel::Telegram': 'i-woot-telegram',
'Channel::TwilioSms': 'i-ri-chat-1-fill', 'Channel::TwilioSms': 'i-woot-sms',
'Channel::TwitterProfile': 'i-ri-twitter-x-fill', 'Channel::TwitterProfile': 'i-ri-twitter-x-fill',
'Channel::WebWidget': 'i-ri-global-fill', 'Channel::WebWidget': 'i-woot-website',
'Channel::Whatsapp': 'i-ri-whatsapp-fill', 'Channel::Whatsapp': 'i-woot-whatsapp',
'Channel::Instagram': 'i-ri-instagram-fill', 'Channel::Instagram': 'i-woot-instagram',
'Channel::Voice': 'i-ri-phone-fill', 'Channel::Voice': 'i-ri-phone-fill',
}; };
const providerIconMap = { const providerIconMap = {
microsoft: 'i-ri-microsoft-fill', microsoft: 'i-woot-outlook',
google: 'i-ri-google-fill', google: 'i-woot-gmail',
}; };
const channelIcon = computed(() => { const channelIcon = computed(() => {
@@ -34,7 +34,7 @@ export function useChannelIcon(inbox) {
// Special case for Twilio whatsapp // Special case for Twilio whatsapp
if (type === 'Channel::TwilioSms' && inboxDetails.medium === 'whatsapp') { if (type === 'Channel::TwilioSms' && inboxDetails.medium === 'whatsapp') {
icon = 'i-ri-whatsapp-fill'; icon = 'i-woot-whatsapp';
} }
return icon ?? 'i-ri-global-fill'; return icon ?? 'i-ri-global-fill';

View File

@@ -4,19 +4,19 @@ describe('useChannelIcon', () => {
it('returns correct icon for API channel', () => { it('returns correct icon for API channel', () => {
const inbox = { channel_type: 'Channel::Api' }; const inbox = { channel_type: 'Channel::Api' };
const { value: icon } = useChannelIcon(inbox); const { value: icon } = useChannelIcon(inbox);
expect(icon).toBe('i-ri-cloudy-fill'); expect(icon).toBe('i-woot-api');
}); });
it('returns correct icon for Facebook channel', () => { it('returns correct icon for Facebook channel', () => {
const inbox = { channel_type: 'Channel::FacebookPage' }; const inbox = { channel_type: 'Channel::FacebookPage' };
const { value: icon } = useChannelIcon(inbox); const { value: icon } = useChannelIcon(inbox);
expect(icon).toBe('i-ri-messenger-fill'); expect(icon).toBe('i-woot-messenger');
}); });
it('returns correct icon for WhatsApp channel', () => { it('returns correct icon for WhatsApp channel', () => {
const inbox = { channel_type: 'Channel::Whatsapp' }; const inbox = { channel_type: 'Channel::Whatsapp' };
const { value: icon } = useChannelIcon(inbox); const { value: icon } = useChannelIcon(inbox);
expect(icon).toBe('i-ri-whatsapp-fill'); expect(icon).toBe('i-woot-whatsapp');
}); });
it('returns correct icon for Voice channel', () => { it('returns correct icon for Voice channel', () => {
@@ -28,19 +28,19 @@ describe('useChannelIcon', () => {
it('returns correct icon for Line channel', () => { it('returns correct icon for Line channel', () => {
const inbox = { channel_type: 'Channel::Line' }; const inbox = { channel_type: 'Channel::Line' };
const { value: icon } = useChannelIcon(inbox); const { value: icon } = useChannelIcon(inbox);
expect(icon).toBe('i-ri-line-fill'); expect(icon).toBe('i-woot-line');
}); });
it('returns correct icon for SMS channel', () => { it('returns correct icon for SMS channel', () => {
const inbox = { channel_type: 'Channel::Sms' }; const inbox = { channel_type: 'Channel::Sms' };
const { value: icon } = useChannelIcon(inbox); const { value: icon } = useChannelIcon(inbox);
expect(icon).toBe('i-ri-chat-1-fill'); expect(icon).toBe('i-woot-sms');
}); });
it('returns correct icon for Telegram channel', () => { it('returns correct icon for Telegram channel', () => {
const inbox = { channel_type: 'Channel::Telegram' }; const inbox = { channel_type: 'Channel::Telegram' };
const { value: icon } = useChannelIcon(inbox); const { value: icon } = useChannelIcon(inbox);
expect(icon).toBe('i-ri-telegram-fill'); expect(icon).toBe('i-woot-telegram');
}); });
it('returns correct icon for Twitter channel', () => { it('returns correct icon for Twitter channel', () => {
@@ -52,20 +52,20 @@ describe('useChannelIcon', () => {
it('returns correct icon for WebWidget channel', () => { it('returns correct icon for WebWidget channel', () => {
const inbox = { channel_type: 'Channel::WebWidget' }; const inbox = { channel_type: 'Channel::WebWidget' };
const { value: icon } = useChannelIcon(inbox); const { value: icon } = useChannelIcon(inbox);
expect(icon).toBe('i-ri-global-fill'); expect(icon).toBe('i-woot-website');
}); });
it('returns correct icon for Instagram channel', () => { it('returns correct icon for Instagram channel', () => {
const inbox = { channel_type: 'Channel::Instagram' }; const inbox = { channel_type: 'Channel::Instagram' };
const { value: icon } = useChannelIcon(inbox); const { value: icon } = useChannelIcon(inbox);
expect(icon).toBe('i-ri-instagram-fill'); expect(icon).toBe('i-woot-instagram');
}); });
describe('TwilioSms channel', () => { describe('TwilioSms channel', () => {
it('returns chat icon for regular Twilio SMS channel', () => { it('returns chat icon for regular Twilio SMS channel', () => {
const inbox = { channel_type: 'Channel::TwilioSms' }; const inbox = { channel_type: 'Channel::TwilioSms' };
const { value: icon } = useChannelIcon(inbox); const { value: icon } = useChannelIcon(inbox);
expect(icon).toBe('i-ri-chat-1-fill'); expect(icon).toBe('i-woot-sms');
}); });
it('returns WhatsApp icon for Twilio SMS with WhatsApp medium', () => { it('returns WhatsApp icon for Twilio SMS with WhatsApp medium', () => {
@@ -74,7 +74,7 @@ describe('useChannelIcon', () => {
medium: 'whatsapp', medium: 'whatsapp',
}; };
const { value: icon } = useChannelIcon(inbox); const { value: icon } = useChannelIcon(inbox);
expect(icon).toBe('i-ri-whatsapp-fill'); expect(icon).toBe('i-woot-whatsapp');
}); });
it('returns chat icon for Twilio SMS with non-WhatsApp medium', () => { it('returns chat icon for Twilio SMS with non-WhatsApp medium', () => {
@@ -83,7 +83,7 @@ describe('useChannelIcon', () => {
medium: 'sms', medium: 'sms',
}; };
const { value: icon } = useChannelIcon(inbox); const { value: icon } = useChannelIcon(inbox);
expect(icon).toBe('i-ri-chat-1-fill'); expect(icon).toBe('i-woot-sms');
}); });
it('returns chat icon for Twilio SMS with undefined medium', () => { it('returns chat icon for Twilio SMS with undefined medium', () => {
@@ -92,7 +92,7 @@ describe('useChannelIcon', () => {
medium: undefined, medium: undefined,
}; };
const { value: icon } = useChannelIcon(inbox); const { value: icon } = useChannelIcon(inbox);
expect(icon).toBe('i-ri-chat-1-fill'); expect(icon).toBe('i-woot-sms');
}); });
}); });
@@ -100,7 +100,7 @@ describe('useChannelIcon', () => {
it('returns mail icon for generic email channel', () => { it('returns mail icon for generic email channel', () => {
const inbox = { channel_type: 'Channel::Email' }; const inbox = { channel_type: 'Channel::Email' };
const { value: icon } = useChannelIcon(inbox); const { value: icon } = useChannelIcon(inbox);
expect(icon).toBe('i-ri-mail-fill'); expect(icon).toBe('i-woot-mail');
}); });
it('returns Microsoft icon for Microsoft email provider', () => { it('returns Microsoft icon for Microsoft email provider', () => {
@@ -109,7 +109,7 @@ describe('useChannelIcon', () => {
provider: 'microsoft', provider: 'microsoft',
}; };
const { value: icon } = useChannelIcon(inbox); const { value: icon } = useChannelIcon(inbox);
expect(icon).toBe('i-ri-microsoft-fill'); expect(icon).toBe('i-woot-outlook');
}); });
it('returns Google icon for Google email provider', () => { it('returns Google icon for Google email provider', () => {
@@ -118,7 +118,7 @@ describe('useChannelIcon', () => {
provider: 'google', provider: 'google',
}; };
const { value: icon } = useChannelIcon(inbox); const { value: icon } = useChannelIcon(inbox);
expect(icon).toBe('i-ri-google-fill'); expect(icon).toBe('i-woot-gmail');
}); });
}); });

View File

@@ -25,7 +25,7 @@ const reauthorizationRequired = computed(() => {
<template> <template>
<span <span
class="size-4 grid place-content-center rounded-full bg-n-alpha-2" class="size-5 grid place-content-center rounded-full bg-n-alpha-2"
:class="{ 'bg-n-solid-blue': active }" :class="{ 'bg-n-solid-blue': active }"
> >
<ChannelIcon :inbox="inbox" class="size-3" /> <ChannelIcon :inbox="inbox" class="size-3" />

View File

@@ -1,11 +1,16 @@
<script> <script setup>
export default { import Icon from 'next/icon/Icon.vue';
props: {
defineProps({
title: { title: {
type: String, type: String,
required: true, required: true,
}, },
src: { description: {
type: String,
default: '',
},
icon: {
type: String, type: String,
required: true, required: true,
}, },
@@ -13,38 +18,40 @@ export default {
type: Boolean, type: Boolean,
default: false, default: false,
}, },
}, });
};
</script> </script>
<template> <template>
<button <button
class="relative bg-n-background cursor-pointer flex flex-col justify-end transition-all duration-200 ease-in -m-px py-4 px-0 items-center border border-solid border-n-weak hover:border-n-brand hover:shadow-md hover:z-50 disabled:opacity-60" class="relative bg-n-solid-1 gap-6 cursor-pointer rounded-2xl flex flex-col justify-start transition-all duration-200 ease-in -m-px py-6 px-5 items-start border border-solid border-n-weak"
:class="{
'hover:enabled:border-n-blue-9 hover:enabled:shadow-md disabled:opacity-60 disabled:cursor-not-allowed':
!isComingSoon,
'cursor-not-allowed disabled:opacity-80': isComingSoon,
}"
> >
<img :src="src" :alt="title" draggable="false" class="w-1/2 my-4 mx-auto" /> <div
<h3 class="text-n-slate-12 text-base text-center capitalize"> class="flex size-10 items-center justify-center rounded-full bg-n-alpha-2"
>
<Icon :icon="icon" class="text-n-slate-10 size-6" />
</div>
<div class="flex flex-col items-start gap-1.5">
<h3 class="text-n-slate-12 text-sm text-start font-medium capitalize">
{{ title }} {{ title }}
</h3> </h3>
<p class="text-n-slate-11 text-start text-sm">
{{ description }}
</p>
</div>
<div <div
v-if="isComingSoon" v-if="isComingSoon"
class="absolute inset-0 flex items-center justify-center backdrop-blur-[2px] rounded-md bg-gradient-to-br from-n-background/90 via-n-background/70 to-n-background/95" class="absolute inset-0 flex items-center justify-center backdrop-blur-[2px] rounded-2xl bg-gradient-to-br from-n-background/90 via-n-background/70 to-n-background/95 cursor-not-allowed"
> >
<span class="text-n-slate-12 font-medium text-base"> <span class="text-n-slate-12 font-medium text-sm">
{{ $t('CHANNEL_SELECTOR.COMING_SOON') }} 🚀 {{ $t('CHANNEL_SELECTOR.COMING_SOON') }} 🚀
</span> </span>
</div> </div>
</button> </button>
</template> </template>
<style scoped lang="scss">
.inactive {
img {
filter: grayscale(100%);
}
&:hover {
@apply border-n-strong shadow-none cursor-not-allowed;
}
}
</style>

View File

@@ -1,97 +1,75 @@
<script> <script setup>
export default { import { computed } from 'vue';
props: { import { useRoute } from 'vue-router';
const props = defineProps({
items: { items: {
type: Array, type: Array,
default: () => [], default: () => [],
}, },
}, });
computed: {
classObject() { const route = useRoute();
return 'w-full';
}, const activeIndex = computed(() => {
activeIndex() { return props.items.findIndex(i => i.route === route.name);
return this.items.findIndex(i => i.route === this.$route.name); });
},
}, const isActive = item => {
methods: { return props.items.indexOf(item) === activeIndex.value;
isActive(item) { };
return this.items.indexOf(item) === this.activeIndex;
}, const isOver = item => {
isOver(item) { return props.items.indexOf(item) < activeIndex.value;
return this.items.indexOf(item) < this.activeIndex;
},
},
}; };
</script> </script>
<template> <template>
<transition-group <transition-group tag="div">
name="wizard-items" <div
tag="div" v-for="(item, index) in items"
class="wizard-box" :key="item.route"
:class="classObject" class="cursor-pointer flex items-start gap-6 relative after:content-[''] after:absolute after:w-0.5 after:h-full after:top-5 ltr:after:left-4 rtl:after:right-4 before:content-[''] before:absolute before:w-0.5 before:h-4 before:top-0 before:left-4 rtl:before:right-4 last:after:hidden last:before:hidden"
:class="
isOver(item)
? 'after:bg-n-blue-9 before:bg-n-blue-9'
: 'after:bg-n-weak before:bg-n-weak'
"
> >
<div <div
v-for="item in items" class="rounded-2xl flex-shrink-0 size-8 flex items-center justify-center left-2 outline outline-2 leading-4 z-10 top-5 bg-n-background"
:key="item.route" :class="
class="item" isActive(item) || isOver(item) ? 'outline-n-blue-9' : 'outline-n-weak'
:class="{ active: isActive(item), over: isOver(item) }" "
> >
<span
class="text-xs font-bold"
:class="
isActive(item) || isOver(item)
? 'text-n-blue-11'
: 'text-n-slate-11'
"
>
{{ index + 1 }}
</span>
</div>
<div class="flex flex-col items-start gap-1.5 pb-10 pt-1">
<div class="flex items-center"> <div class="flex items-center">
<h3 <h3
class="text-n-slate-12 text-base font-medium pl-6 overflow-hidden whitespace-nowrap mt-0.5 text-ellipsis leading-tight" class="text-sm font-medium overflow-hidden whitespace-nowrap mt-0.5 text-ellipsis leading-tight"
:class="
isActive(item) || isOver(item)
? 'text-n-blue-11'
: 'text-n-slate-12'
"
> >
{{ item.title }} {{ item.title }}
</h3> </h3>
<span v-if="isOver(item)" class="mx-1 mt-0.5 text-n-teal-9">
<fluent-icon icon="checkmark" />
</span>
</div> </div>
<span class="step"> <p class="m-0 mt-1.5 text-sm text-n-slate-11">
{{ items.indexOf(item) + 1 }}
</span>
<p class="pl-6 m-0 mt-1.5 text-sm text-n-slate-11">
{{ item.body }} {{ item.body }}
</p> </p>
</div> </div>
</div>
</transition-group> </transition-group>
</template> </template>
<style lang="scss" scoped>
.wizard-box {
.item {
@apply cursor-pointer after:bg-n-slate-6 before:bg-n-slate-6 py-4 ltr:pr-4 rtl:pl-4 ltr:pl-6 rtl:pr-6 relative before:h-4 before:top-0 last:before:h-0 first:before:h-0 last:after:h-0 before:content-[''] before:absolute before:w-0.5 after:content-[''] after:h-full after:absolute after:top-5 after:w-0.5 rtl:after:left-6 rtl:before:left-6;
&.active {
h3 {
@apply text-n-blue-text dark:text-n-blue-text;
}
.step {
@apply bg-n-brand dark:bg-n-brand;
}
}
&.over {
&::after {
@apply bg-n-brand dark:bg-n-brand;
}
.step {
@apply bg-n-brand dark:bg-n-brand;
}
& + .item {
&::before {
@apply bg-n-brand dark:bg-n-brand;
}
}
}
.step {
@apply bg-n-slate-7 rounded-2xl font-medium w-4 left-4 leading-4 z-10 absolute text-center text-white dark:text-white text-xxs top-5;
}
}
}
</style>

View File

@@ -1,8 +1,8 @@
<script> <script setup>
import { computed } from 'vue';
import ChannelSelector from '../ChannelSelector.vue'; import ChannelSelector from '../ChannelSelector.vue';
export default {
components: { ChannelSelector }, const props = defineProps({
props: {
channel: { channel: {
type: Object, type: Object,
required: true, required: true,
@@ -11,38 +11,41 @@ export default {
type: Object, type: Object,
required: true, required: true,
}, },
}, });
emits: ['channelItemClick'],
computed: { const emit = defineEmits(['channelItemClick']);
hasFbConfigured() {
const hasFbConfigured = computed(() => {
return window.chatwootConfig?.fbAppId; return window.chatwootConfig?.fbAppId;
}, });
hasInstagramConfigured() {
const hasInstagramConfigured = computed(() => {
return window.chatwootConfig?.instagramAppId; return window.chatwootConfig?.instagramAppId;
}, });
isActive() {
const { key } = this.channel; const isActive = computed(() => {
if (Object.keys(this.enabledFeatures).length === 0) { const { key } = props.channel;
if (Object.keys(props.enabledFeatures).length === 0) {
return false; return false;
} }
if (key === 'website') { if (key === 'website') {
return this.enabledFeatures.channel_website; return props.enabledFeatures.channel_website;
} }
if (key === 'facebook') { if (key === 'facebook') {
return this.enabledFeatures.channel_facebook && this.hasFbConfigured; return props.enabledFeatures.channel_facebook && hasFbConfigured.value;
} }
if (key === 'email') { if (key === 'email') {
return this.enabledFeatures.channel_email; return props.enabledFeatures.channel_email;
} }
if (key === 'instagram') { if (key === 'instagram') {
return ( return (
this.enabledFeatures.channel_instagram && this.hasInstagramConfigured props.enabledFeatures.channel_instagram && hasInstagramConfigured.value
); );
} }
if (key === 'voice') { if (key === 'voice') {
return this.enabledFeatures.channel_voice; return props.enabledFeatures.channel_voice;
} }
return [ return [
@@ -56,36 +59,29 @@ export default {
'instagram', 'instagram',
'voice', 'voice',
].includes(key); ].includes(key);
}, });
isComingSoon() {
const { key } = this.channel; const isComingSoon = computed(() => {
const { key } = props.channel;
// Show "Coming Soon" only if the channel is marked as coming soon // Show "Coming Soon" only if the channel is marked as coming soon
// and the corresponding feature flag is not enabled yet. // and the corresponding feature flag is not enabled yet.
return ['voice'].includes(key) && !this.isActive; return ['voice'].includes(key) && !isActive.value;
}, });
},
methods: { const onItemClick = () => {
getChannelThumbnail() { if (isActive.value) {
if (this.channel.key === 'api' && this.channel.thumbnail) { emit('channelItemClick', props.channel.key);
return this.channel.thumbnail;
} }
return `/assets/images/dashboard/channels/${this.channel.key}.png`;
},
onItemClick() {
if (this.isActive) {
this.$emit('channelItemClick', this.channel.key);
}
},
},
}; };
</script> </script>
<template> <template>
<ChannelSelector <ChannelSelector
:class="{ inactive: !isActive }" :title="channel.title"
:title="channel.name" :description="channel.description"
:src="getChannelThumbnail()" :icon="channel.icon"
:is-coming-soon="isComingSoon" :is-coming-soon="isComingSoon"
:disabled="!isActive"
@click="onItemClick" @click="onItemClick"
/> />
</template> </template>

View File

@@ -13,7 +13,7 @@ defineProps({
<div class="flex items-center text-n-slate-11 text-xs min-w-0"> <div class="flex items-center text-n-slate-11 text-xs min-w-0">
<ChannelIcon <ChannelIcon
:inbox="inbox" :inbox="inbox"
class="size-3 ltr:mr-0.5 rtl:ml-0.5 flex-shrink-0" class="size-3 ltr:mr-1 rtl:ml-1 flex-shrink-0"
/> />
<span class="truncate"> <span class="truncate">
{{ inbox.name }} {{ inbox.name }}

View File

@@ -250,7 +250,6 @@ const deleteConversation = () => {
:src="currentContact.thumbnail" :src="currentContact.thumbnail"
:size="32" :size="32"
:status="currentContact.availability_status" :status="currentContact.availability_status"
:inbox="inbox"
:class="!showInboxName ? 'mt-4' : 'mt-8'" :class="!showInboxName ? 'mt-4' : 'mt-8'"
hide-offline-status hide-offline-status
rounded-full rounded-full

View File

@@ -424,7 +424,51 @@
}, },
"AUTH": { "AUTH": {
"TITLE": "Choose a channel", "TITLE": "Choose a channel",
"DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below." "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below.",
"TITLE_NEXT": "Complete the setup",
"TITLE_FINISH": "Voilà!",
"CHANNEL": {
"WEBSITE": {
"TITLE": "Website",
"DESCRIPTION": "Create a live-chat widget"
},
"FACEBOOK": {
"TITLE": "Facebook",
"DESCRIPTION": "Connect your Facebook page"
},
"WHATSAPP": {
"TITLE": "WhatsApp",
"DESCRIPTION": "Support your customers on WhatsApp"
},
"EMAIL": {
"TITLE": "Email",
"DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
},
"SMS": {
"TITLE": "SMS",
"DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
},
"API": {
"TITLE": "API",
"DESCRIPTION": "Make a custom channel using our API"
},
"TELEGRAM": {
"TITLE": "Telegram",
"DESCRIPTION": "Configure Telegram channel using Bot token"
},
"LINE": {
"TITLE": "Line",
"DESCRIPTION": "Integrate your Line channel"
},
"INSTAGRAM": {
"TITLE": "Instagram",
"DESCRIPTION": "Connect your instagram account"
},
"VOICE": {
"TITLE": "Voice",
"DESCRIPTION": "Integrate with Twilio Voice"
}
}
}, },
"AGENTS": { "AGENTS": {
"TITLE": "Agents", "TITLE": "Agents",
@@ -871,9 +915,18 @@
"SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};" "SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};"
}, },
"EMAIL_PROVIDERS": { "EMAIL_PROVIDERS": {
"MICROSOFT": "Microsoft", "MICROSOFT": {
"GOOGLE": "Google", "TITLE": "Microsoft",
"OTHER_PROVIDERS": "Other Providers" "DESCRIPTION": "Connect with Microsoft"
},
"GOOGLE": {
"TITLE": "Google",
"DESCRIPTION": "Connect with Google"
},
"OTHER_PROVIDERS": {
"TITLE": "Other Providers",
"DESCRIPTION": "Connect with Other Providers"
}
}, },
"CHANNELS": { "CHANNELS": {
"MESSENGER": "Messenger", "MESSENGER": "Messenger",

View File

@@ -63,9 +63,7 @@ export default {
</script> </script>
<template> <template>
<div <div class="h-full w-full p-6 col-span-6">
class="border border-n-weak bg-n-solid-1 rounded-t-lg border-b-0 h-full w-full p-6 col-span-6 overflow-auto"
>
<form class="flex flex-wrap flex-col mx-0" @submit.prevent="addAgents()"> <form class="flex flex-wrap flex-col mx-0" @submit.prevent="addAgents()">
<div class="w-full"> <div class="w-full">
<PageHeader <PageHeader

View File

@@ -1,83 +1,108 @@
<script> <script setup>
import ChannelItem from 'dashboard/components/widgets/ChannelItem.vue'; import { ref, computed, onMounted } from 'vue';
import router from '../../../index'; import { useI18n } from 'vue-i18n';
import PageHeader from '../SettingsSubPageHeader.vue'; import { useRouter } from 'vue-router';
import { mapGetters } from 'vuex'; import { useMapGetter } from 'dashboard/composables/store';
import { useBranding } from 'shared/composables/useBranding';
export default { import { useAccount } from 'dashboard/composables/useAccount';
components: {
ChannelItem, import ChannelItem from 'dashboard/components/widgets/ChannelItem.vue';
PageHeader,
}, const { t } = useI18n();
setup() { const router = useRouter();
const { replaceInstallationName } = useBranding(); const { accountId, currentAccount } = useAccount();
return {
replaceInstallationName, const globalConfig = useMapGetter('globalConfig/get');
};
}, const enabledFeatures = ref({});
data() {
return { const channelList = computed(() => {
enabledFeatures: {}, const { apiChannelName } = globalConfig.value;
};
},
computed: {
account() {
return this.$store.getters['accounts/getAccount'](this.accountId);
},
channelList() {
const { apiChannelName, apiChannelThumbnail } = this.globalConfig;
return [ return [
{ key: 'website', name: 'Website' }, {
{ key: 'facebook', name: 'Messenger' }, key: 'website',
{ key: 'whatsapp', name: 'WhatsApp' }, title: t('INBOX_MGMT.ADD.AUTH.CHANNEL.WEBSITE.TITLE'),
{ key: 'sms', name: 'SMS' }, description: t('INBOX_MGMT.ADD.AUTH.CHANNEL.WEBSITE.DESCRIPTION'),
{ key: 'email', name: 'Email' }, icon: 'i-woot-website',
},
{
key: 'facebook',
title: t('INBOX_MGMT.ADD.AUTH.CHANNEL.FACEBOOK.TITLE'),
description: t('INBOX_MGMT.ADD.AUTH.CHANNEL.FACEBOOK.DESCRIPTION'),
icon: 'i-woot-messenger',
},
{
key: 'whatsapp',
title: t('INBOX_MGMT.ADD.AUTH.CHANNEL.WHATSAPP.TITLE'),
description: t('INBOX_MGMT.ADD.AUTH.CHANNEL.WHATSAPP.DESCRIPTION'),
icon: 'i-woot-whatsapp',
},
{
key: 'sms',
title: t('INBOX_MGMT.ADD.AUTH.CHANNEL.SMS.TITLE'),
description: t('INBOX_MGMT.ADD.AUTH.CHANNEL.SMS.DESCRIPTION'),
icon: 'i-woot-sms',
},
{
key: 'email',
title: t('INBOX_MGMT.ADD.AUTH.CHANNEL.EMAIL.TITLE'),
description: t('INBOX_MGMT.ADD.AUTH.CHANNEL.EMAIL.DESCRIPTION'),
icon: 'i-woot-mail',
},
{ {
key: 'api', key: 'api',
name: apiChannelName || 'API', title: apiChannelName || t('INBOX_MGMT.ADD.AUTH.CHANNEL.API.TITLE'),
thumbnail: apiChannelThumbnail, description: t('INBOX_MGMT.ADD.AUTH.CHANNEL.API.DESCRIPTION'),
icon: 'i-woot-api',
},
{
key: 'telegram',
title: t('INBOX_MGMT.ADD.AUTH.CHANNEL.TELEGRAM.TITLE'),
description: t('INBOX_MGMT.ADD.AUTH.CHANNEL.TELEGRAM.DESCRIPTION'),
icon: 'i-woot-telegram',
},
{
key: 'line',
title: t('INBOX_MGMT.ADD.AUTH.CHANNEL.LINE.TITLE'),
description: t('INBOX_MGMT.ADD.AUTH.CHANNEL.LINE.DESCRIPTION'),
icon: 'i-woot-line',
},
{
key: 'instagram',
title: t('INBOX_MGMT.ADD.AUTH.CHANNEL.INSTAGRAM.TITLE'),
description: t('INBOX_MGMT.ADD.AUTH.CHANNEL.INSTAGRAM.DESCRIPTION'),
icon: 'i-woot-instagram',
},
{
key: 'voice',
title: t('INBOX_MGMT.ADD.AUTH.CHANNEL.VOICE.TITLE'),
description: t('INBOX_MGMT.ADD.AUTH.CHANNEL.VOICE.DESCRIPTION'),
icon: 'i-ri-phone-fill',
}, },
{ key: 'telegram', name: 'Telegram' },
{ key: 'line', name: 'Line' },
{ key: 'instagram', name: 'Instagram' },
{ key: 'voice', name: 'Voice' },
]; ];
}, });
...mapGetters({
accountId: 'getCurrentAccountId', const initializeEnabledFeatures = async () => {
globalConfig: 'globalConfig/get', enabledFeatures.value = currentAccount.value.features;
}), };
},
mounted() { const initChannelAuth = channel => {
this.initializeEnabledFeatures();
},
methods: {
async initializeEnabledFeatures() {
this.enabledFeatures = this.account.features;
},
initChannelAuth(channel) {
const params = { const params = {
sub_page: channel, sub_page: channel,
accountId: this.accountId, accountId: accountId.value,
}; };
router.push({ name: 'settings_inboxes_page_channel', params }); router.push({ name: 'settings_inboxes_page_channel', params });
},
},
}; };
onMounted(() => {
initializeEnabledFeatures();
});
</script> </script>
<template> <template>
<div class="w-full p-8 overflow-auto">
<div <div
class="w-full h-full col-span-6 p-6 overflow-auto border border-b-0 rounded-t-lg border-n-weak bg-n-solid-1" class="grid max-w-3xl grid-cols-1 xs:grid-cols-2 mx-0 gap-6 sm:grid-cols-3"
>
<PageHeader
class="max-w-4xl"
:header-title="$t('INBOX_MGMT.ADD.AUTH.TITLE')"
:header-content="replaceInstallationName($t('INBOX_MGMT.ADD.AUTH.DESC'))"
/>
<div
class="grid max-w-3xl grid-cols-2 mx-0 mt-6 sm:grid-cols-3 lg:grid-cols-4"
> >
<ChannelItem <ChannelItem
v-for="channel in channelList" v-for="channel in channelList"

View File

@@ -168,9 +168,7 @@ onMounted(() => {
</script> </script>
<template> <template>
<div <div class="w-full h-full col-span-6 p-6 overflow-auto">
class="overflow-auto col-span-6 p-6 w-full h-full rounded-t-lg border border-b-0 border-n-weak bg-n-solid-1"
>
<DuplicateInboxBanner <DuplicateInboxBanner
v-if="hasDuplicateInstagramInbox" v-if="hasDuplicateInstagramInbox"
:content="$t('INBOX_MGMT.ADD.INSTAGRAM.NEW_INBOX_SUGGESTION')" :content="$t('INBOX_MGMT.ADD.INSTAGRAM.NEW_INBOX_SUGGESTION')"

View File

@@ -1,19 +1,36 @@
<script> <script setup>
import { mapGetters } from 'vuex'; import { computed } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router';
import { useMapGetter } from 'dashboard/composables/store';
import { useBranding } from 'shared/composables/useBranding'; import { useBranding } from 'shared/composables/useBranding';
export default { import PageHeader from '../SettingsSubPageHeader.vue';
setup() { import Icon from 'next/icon/Icon.vue';
const { t } = useI18n();
const route = useRoute();
const { replaceInstallationName } = useBranding(); const { replaceInstallationName } = useBranding();
return {
replaceInstallationName, const globalConfig = useMapGetter('globalConfig/get');
};
}, const ALL_CHANNEL_ICONS = [
computed: { 'i-woot-line',
...mapGetters({ 'i-woot-facebook',
globalConfig: 'globalConfig/get', 'i-woot-whatsapp',
}), 'i-woot-instagram',
createFlowSteps() { 'i-woot-messenger',
'i-woot-website',
'i-woot-mail',
'i-woot-sms',
'i-woot-telegram',
'i-woot-api',
'i-woot-twilio',
'i-woot-gmail',
'i-woot-outlook',
];
const createFlowSteps = computed(() => {
const steps = ['CHANNEL', 'INBOX', 'AGENT', 'FINISH']; const steps = ['CHANNEL', 'INBOX', 'AGENT', 'FINISH'];
const routes = { const routes = {
@@ -25,31 +42,65 @@ export default {
return steps.map(step => { return steps.map(step => {
return { return {
title: this.$t(`INBOX_MGMT.CREATE_FLOW.${step}.TITLE`), title: t(`INBOX_MGMT.CREATE_FLOW.${step}.TITLE`),
body: this.$t(`INBOX_MGMT.CREATE_FLOW.${step}.BODY`), body: t(`INBOX_MGMT.CREATE_FLOW.${step}.BODY`),
route: routes[step], route: routes[step],
}; };
}); });
}, });
items() {
return this.createFlowSteps.map(item => ({ const isFirstStep = computed(() => {
return route.name === 'settings_inbox_new';
});
const isFinishStep = computed(() => {
return route.name === 'settings_inbox_finish';
});
const pageTitle = computed(() => {
if (isFirstStep.value) {
return t('INBOX_MGMT.ADD.AUTH.TITLE');
}
if (isFinishStep.value) {
return t('INBOX_MGMT.ADD.AUTH.TITLE_FINISH');
}
return t('INBOX_MGMT.ADD.AUTH.TITLE_NEXT');
});
const items = computed(() => {
return createFlowSteps.value.map(item => ({
...item, ...item,
body: this.replaceInstallationName(item.body), body: replaceInstallationName(item.body),
})); }));
}, });
},
};
</script> </script>
<template> <template>
<div class="grid grid-cols-1 md:grid-cols-8 overflow-auto h-full"> <div class="mx-2 flex flex-col gap-6 mb-8">
<PageHeader class="block lg:hidden !mb-0" :header-title="pageTitle" />
<div class="hidden lg:grid grid-cols-1 lg:grid-cols-8 items-center gap-2">
<div class="col-span-2 w-full" />
<div class="flex items-center gap-2 col-span-6 ltr:ml-8 rtl:mr-8">
<div
v-for="icon in ALL_CHANNEL_ICONS"
:key="icon"
class="size-8 bg-n-alpha-2 flex items-center flex-shrink-0 justify-center rounded-full"
>
<Icon :icon="icon" class="size-4 text-n-slate-10" />
</div>
</div>
</div>
<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]"
>
<woot-wizard <woot-wizard
class="hidden md:block col-span-2" class="hidden lg:block col-span-2 h-fit py-8 px-6"
:global-config="globalConfig" :global-config="globalConfig"
:items="items" :items="items"
/> />
<div class="col-span-6"> <div class="col-span-6 overflow-hidden">
<router-view /> <router-view />
</div> </div>
</div> </div>
</div>
</template> </template>

View File

@@ -116,7 +116,7 @@ const openDelete = inbox => {
v-else v-else
class="size-12 flex justify-center items-center bg-n-alpha-3 rounded-full p-2 ring ring-n-solid-1 border border-n-strong shadow-sm" class="size-12 flex justify-center items-center bg-n-alpha-3 rounded-full p-2 ring ring-n-solid-1 border border-n-strong shadow-sm"
> >
<ChannelIcon class="size-5" :inbox="inbox" /> <ChannelIcon class="size-5 text-n-slate-10" :inbox="inbox" />
</div> </div>
<div> <div>
<span class="block font-medium capitalize"> <span class="block font-medium capitalize">

View File

@@ -65,9 +65,7 @@ export default {
</script> </script>
<template> <template>
<div <div class="h-full w-full p-6 col-span-6">
class="border border-n-weak bg-n-solid-1 rounded-t-lg border-b-0 h-full w-full p-6 col-span-6 overflow-auto"
>
<PageHeader <PageHeader
:header-title="$t('INBOX_MGMT.ADD.API_CHANNEL.TITLE')" :header-title="$t('INBOX_MGMT.ADD.API_CHANNEL.TITLE')"
:header-content="$t('INBOX_MGMT.ADD.API_CHANNEL.DESC')" :header-content="$t('INBOX_MGMT.ADD.API_CHANNEL.DESC')"

View File

@@ -20,22 +20,25 @@ const isAChatwootInstance = getters['globalConfig/isAChatwootInstance'];
const emailProviderList = computed(() => { const emailProviderList = computed(() => {
return [ return [
{ {
title: t('INBOX_MGMT.EMAIL_PROVIDERS.MICROSOFT'), title: t('INBOX_MGMT.EMAIL_PROVIDERS.MICROSOFT.TITLE'),
description: t('INBOX_MGMT.EMAIL_PROVIDERS.MICROSOFT.DESCRIPTION'),
isEnabled: !!globalConfig.value.azureAppId, isEnabled: !!globalConfig.value.azureAppId,
key: 'microsoft', key: 'microsoft',
src: '/assets/images/dashboard/channels/microsoft.png', icon: 'i-woot-outlook',
}, },
{ {
title: t('INBOX_MGMT.EMAIL_PROVIDERS.GOOGLE'), title: t('INBOX_MGMT.EMAIL_PROVIDERS.GOOGLE.TITLE'),
description: t('INBOX_MGMT.EMAIL_PROVIDERS.GOOGLE.DESCRIPTION'),
isEnabled: !!window.chatwootConfig.googleOAuthClientId, isEnabled: !!window.chatwootConfig.googleOAuthClientId,
key: 'google', key: 'google',
src: '/assets/images/dashboard/channels/google.png', icon: 'i-woot-gmail',
}, },
{ {
title: t('INBOX_MGMT.EMAIL_PROVIDERS.OTHER_PROVIDERS'), title: t('INBOX_MGMT.EMAIL_PROVIDERS.OTHER_PROVIDERS.TITLE'),
description: t('INBOX_MGMT.EMAIL_PROVIDERS.OTHER_PROVIDERS.DESCRIPTION'),
isEnabled: true, isEnabled: true,
key: 'other_provider', key: 'other_provider',
src: '/assets/images/dashboard/channels/email.png', icon: 'i-woot-mail',
}, },
].filter(providerConfig => { ].filter(providerConfig => {
if (isAChatwootInstance.value) { if (isAChatwootInstance.value) {
@@ -53,22 +56,20 @@ function onClick(emailProvider) {
</script> </script>
<template> <template>
<div <div v-if="!provider" class="h-full w-full p-6 col-span-6">
v-if="!provider"
class="border border-n-weak bg-n-solid-1 rounded-t-lg border-b-0 h-full w-full p-6 col-span-6 overflow-auto"
>
<PageHeader <PageHeader
class="max-w-4xl" class="max-w-4xl"
:header-title="$t('INBOX_MGMT.ADD.EMAIL_PROVIDER.TITLE')" :header-title="$t('INBOX_MGMT.ADD.EMAIL_PROVIDER.TITLE')"
:header-content="$t('INBOX_MGMT.ADD.EMAIL_PROVIDER.DESCRIPTION')" :header-content="$t('INBOX_MGMT.ADD.EMAIL_PROVIDER.DESCRIPTION')"
/> />
<div class="grid max-w-3xl grid-cols-4 mx-0 mt-6"> <div class="grid max-w-3xl grid-cols-4 gap-6 mx-0 mt-6">
<ChannelSelector <ChannelSelector
v-for="emailProvider in emailProviderList" v-for="emailProvider in emailProviderList"
:key="emailProvider.key" :key="emailProvider.key"
:class="{ inactive: !emailProvider.isEnabled }"
:title="emailProvider.title" :title="emailProvider.title"
:src="emailProvider.src" :description="emailProvider.description"
:icon="emailProvider.icon"
:disabled="!emailProvider.isEnabled"
@click="() => onClick(emailProvider)" @click="() => onClick(emailProvider)"
/> />
</div> </div>

View File

@@ -206,16 +206,14 @@ export default {
</script> </script>
<template> <template>
<div <div class="w-full h-full col-span-6 p-6 overflow-auto">
class="w-full h-full col-span-6 p-6 overflow-auto border border-b-0 rounded-t-lg border-n-weak bg-n-solid-1"
>
<div <div
v-if="!hasLoginStarted" v-if="!hasLoginStarted"
class="flex flex-col items-center justify-center h-full text-center" class="flex flex-col items-center justify-center h-full text-center"
> >
<a href="#" @click="startLogin()"> <a href="#" @click="startLogin()">
<img <img
class="w-auto h-10" class="w-auto h-10 rounded-md"
src="~dashboard/assets/images/channels/facebook_login.png" src="~dashboard/assets/images/channels/facebook_login.png"
alt="Facebook-logo" alt="Facebook-logo"
/> />

View File

@@ -71,9 +71,7 @@ export default {
</script> </script>
<template> <template>
<div <div class="h-full w-full p-6 col-span-6">
class="border border-n-weak bg-n-solid-1 rounded-t-lg border-b-0 h-full w-full p-6 col-span-6 overflow-auto"
>
<PageHeader <PageHeader
:header-title="$t('INBOX_MGMT.ADD.LINE_CHANNEL.TITLE')" :header-title="$t('INBOX_MGMT.ADD.LINE_CHANNEL.TITLE')"
:header-content="$t('INBOX_MGMT.ADD.LINE_CHANNEL.DESC')" :header-content="$t('INBOX_MGMT.ADD.LINE_CHANNEL.DESC')"

View File

@@ -18,9 +18,7 @@ export default {
</script> </script>
<template> <template>
<div <div class="h-full w-full p-6 col-span-6">
class="border border-n-weak bg-n-solid-1 rounded-t-lg border-b-0 h-full w-full p-6 col-span-6 overflow-auto"
>
<PageHeader <PageHeader
:header-title="$t('INBOX_MGMT.ADD.SMS.TITLE')" :header-title="$t('INBOX_MGMT.ADD.SMS.TITLE')"
:header-content="$t('INBOX_MGMT.ADD.SMS.DESC')" :header-content="$t('INBOX_MGMT.ADD.SMS.DESC')"

View File

@@ -65,9 +65,7 @@ export default {
</script> </script>
<template> <template>
<div <div class="h-full w-full p-6 col-span-6">
class="border border-n-weak bg-n-solid-1 rounded-t-lg border-b-0 h-full w-full p-6 col-span-6 overflow-auto"
>
<PageHeader <PageHeader
:header-title="$t('INBOX_MGMT.ADD.TELEGRAM_CHANNEL.TITLE')" :header-title="$t('INBOX_MGMT.ADD.TELEGRAM_CHANNEL.TITLE')"
:header-content="$t('INBOX_MGMT.ADD.TELEGRAM_CHANNEL.DESC')" :header-content="$t('INBOX_MGMT.ADD.TELEGRAM_CHANNEL.DESC')"

View File

@@ -30,9 +30,7 @@ export default {
</script> </script>
<template> <template>
<div <div class="h-full w-full p-6 col-span-6">
class="border border-n-weak bg-n-solid-1 rounded-t-lg border-b-0 h-full w-full p-6 col-span-6 overflow-auto"
>
<div class="login-init h-full text-center"> <div class="login-init h-full text-center">
<form @submit.prevent="requestAuthorization"> <form @submit.prevent="requestAuthorization">
<NextButton <NextButton

View File

@@ -93,9 +93,7 @@ async function createChannel() {
</script> </script>
<template> <template>
<div <div class="overflow-auto col-span-6 p-6 w-full h-full">
class="overflow-auto col-span-6 p-6 w-full h-full rounded-t-lg border border-b-0 border-n-weak bg-n-solid-1"
>
<PageHeader <PageHeader
:header-title="t('INBOX_MGMT.ADD.VOICE.TITLE')" :header-title="t('INBOX_MGMT.ADD.VOICE.TITLE')"
:header-content="t('INBOX_MGMT.ADD.VOICE.DESC')" :header-content="t('INBOX_MGMT.ADD.VOICE.DESC')"

View File

@@ -78,9 +78,7 @@ export default {
</script> </script>
<template> <template>
<div <div class="h-full w-full p-6 col-span-6">
class="border border-n-weak bg-n-solid-1 rounded-t-lg border-b-0 h-full w-full p-6 col-span-6 overflow-auto"
>
<PageHeader <PageHeader
:header-title="$t('INBOX_MGMT.ADD.WEBSITE_CHANNEL.TITLE')" :header-title="$t('INBOX_MGMT.ADD.WEBSITE_CHANNEL.TITLE')"
:header-content="$t('INBOX_MGMT.ADD.WEBSITE_CHANNEL.DESC')" :header-content="$t('INBOX_MGMT.ADD.WEBSITE_CHANNEL.DESC')"

View File

@@ -7,6 +7,7 @@ import Twilio from './Twilio.vue';
import ThreeSixtyDialogWhatsapp from './360DialogWhatsapp.vue'; import ThreeSixtyDialogWhatsapp from './360DialogWhatsapp.vue';
import CloudWhatsapp from './CloudWhatsapp.vue'; import CloudWhatsapp from './CloudWhatsapp.vue';
import WhatsappEmbeddedSignup from './WhatsappEmbeddedSignup.vue'; import WhatsappEmbeddedSignup from './WhatsappEmbeddedSignup.vue';
import ChannelSelector from 'dashboard/components/ChannelSelector.vue';
import { FEATURE_FLAGS } from 'dashboard/featureFlags'; import { FEATURE_FLAGS } from 'dashboard/featureFlags';
const route = useRoute(); const route = useRoute();
@@ -45,16 +46,16 @@ const showConfiguration = computed(() => Boolean(selectedProvider.value));
const availableProviders = computed(() => [ const availableProviders = computed(() => [
{ {
value: PROVIDER_TYPES.WHATSAPP, key: PROVIDER_TYPES.WHATSAPP,
label: t('INBOX_MGMT.ADD.WHATSAPP.PROVIDERS.WHATSAPP_CLOUD'), title: t('INBOX_MGMT.ADD.WHATSAPP.PROVIDERS.WHATSAPP_CLOUD'),
description: t('INBOX_MGMT.ADD.WHATSAPP.PROVIDERS.WHATSAPP_CLOUD_DESC'), description: t('INBOX_MGMT.ADD.WHATSAPP.PROVIDERS.WHATSAPP_CLOUD_DESC'),
icon: '/assets/images/dashboard/channels/whatsapp.png', icon: 'i-woot-whatsapp',
}, },
{ {
value: PROVIDER_TYPES.TWILIO, key: PROVIDER_TYPES.TWILIO,
label: t('INBOX_MGMT.ADD.WHATSAPP.PROVIDERS.TWILIO'), title: t('INBOX_MGMT.ADD.WHATSAPP.PROVIDERS.TWILIO'),
description: t('INBOX_MGMT.ADD.WHATSAPP.PROVIDERS.TWILIO_DESC'), description: t('INBOX_MGMT.ADD.WHATSAPP.PROVIDERS.TWILIO_DESC'),
icon: '/assets/images/dashboard/channels/twilio.png', icon: 'i-woot-twilio',
}, },
]); ]);
@@ -92,9 +93,7 @@ const shouldShowCloudWhatsapp = provider => {
</script> </script>
<template> <template>
<div <div class="overflow-auto col-span-6 p-6 w-full h-full">
class="overflow-auto col-span-6 p-6 w-full h-full rounded-t-lg border border-b-0 border-n-weak bg-n-solid-1"
>
<div v-if="showProviderSelection"> <div v-if="showProviderSelection">
<div class="mb-10 text-left"> <div class="mb-10 text-left">
<h1 class="mb-2 text-lg font-medium text-slate-12"> <h1 class="mb-2 text-lg font-medium text-slate-12">
@@ -106,38 +105,18 @@ const shouldShowCloudWhatsapp = provider => {
</div> </div>
<div class="flex gap-6 justify-start"> <div class="flex gap-6 justify-start">
<div <ChannelSelector
v-for="provider in availableProviders" v-for="provider in availableProviders"
:key="provider.value" :key="provider.key"
class="gap-6 px-5 py-6 w-96 rounded-2xl border transition-all duration-200 cursor-pointer border-n-weak hover:bg-n-slate-3" :title="provider.title"
@click="selectProvider(provider.value)" :description="provider.description"
> :icon="provider.icon"
<div class="flex justify-start mb-5"> @click="selectProvider(provider.key)"
<div
class="flex justify-center items-center rounded-full size-10 bg-n-alpha-2"
>
<img
:src="provider.icon"
:alt="provider.label"
class="object-contain size-[26px]"
/> />
</div> </div>
</div> </div>
<div class="text-start">
<h3 class="mb-1.5 text-sm font-medium text-slate-12">
{{ provider.label }}
</h3>
<p class="text-sm text-slate-11">
{{ provider.description }}
</p>
</div>
</div>
</div>
</div>
<div v-else-if="showConfiguration"> <div v-else-if="showConfiguration">
<div class="px-6 py-5 rounded-2xl border bg-n-solid-2 border-n-weak">
<WhatsappEmbeddedSignup <WhatsappEmbeddedSignup
v-if="shouldShowEmbeddedSignup(selectedProvider)" v-if="shouldShowEmbeddedSignup(selectedProvider)"
/> />
@@ -152,5 +131,4 @@ const shouldShowCloudWhatsapp = provider => {
<CloudWhatsapp v-else /> <CloudWhatsapp v-else />
</div> </div>
</div> </div>
</div>
</template> </template>

View File

@@ -70,9 +70,7 @@ export default {
</script> </script>
<template> <template>
<div <div class="h-full w-full p-6 col-span-6">
class="border border-n-weak bg-n-solid-1 rounded-t-lg border-b-0 h-full w-full p-6 col-span-6 overflow-auto"
>
<PageHeader <PageHeader
:header-title="$t('INBOX_MGMT.ADD.EMAIL_CHANNEL.TITLE')" :header-title="$t('INBOX_MGMT.ADD.EMAIL_CHANNEL.TITLE')"
:header-content="$t('INBOX_MGMT.ADD.EMAIL_CHANNEL.DESC')" :header-content="$t('INBOX_MGMT.ADD.EMAIL_CHANNEL.DESC')"

View File

@@ -60,9 +60,7 @@ async function requestAuthorization() {
</script> </script>
<template> <template>
<div <div class="h-full w-full p-6 col-span-6">
class="border border-n-weak bg-n-solid-1 rounded-t-lg border-b-0 h-full w-full p-6 col-span-6 overflow-auto"
>
<SettingsSubPageHeader <SettingsSubPageHeader
:header-title="title" :header-title="title"
:header-content="description" :header-content="description"

View File

@@ -88,9 +88,7 @@ export default {
</script> </script>
<template> <template>
<div <div class="h-full w-full p-6 col-span-6">
class="border border-n-weak bg-n-solid-1 rounded-t-lg border-b-0 h-full w-full p-6 col-span-6 overflow-auto"
>
<form <form
class="flex flex-wrap mx-0 overflow-x-auto" class="flex flex-wrap mx-0 overflow-x-auto"
@submit.prevent="addAgents" @submit.prevent="addAgents"

View File

@@ -37,9 +37,7 @@ export default {
</script> </script>
<template> <template>
<div <div class="h-full w-full p-6 col-span-6">
class="border border-n-weak bg-n-solid-1 rounded-t-lg border-b-0 h-full w-full p-6 col-span-6 overflow-auto"
>
<PageHeader <PageHeader
:header-title="$t('TEAMS_SETTINGS.CREATE_FLOW.CREATE.TITLE')" :header-title="$t('TEAMS_SETTINGS.CREATE_FLOW.CREATE.TITLE')"
:header-content="$t('TEAMS_SETTINGS.CREATE_FLOW.CREATE.DESC')" :header-content="$t('TEAMS_SETTINGS.CREATE_FLOW.CREATE.DESC')"

View File

@@ -23,8 +23,15 @@ export default {
</script> </script>
<template> <template>
<div class="grid grid-cols-1 md:grid-cols-8 overflow-auto h-full px-5 flex-1"> <div class="mx-2 flex flex-col gap-6 mb-8">
<woot-wizard class="hidden md:block col-span-2" :items="items" /> <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]"
>
<woot-wizard
class="hidden lg:block col-span-2 h-fit py-8 px-6"
:items="items"
/>
<router-view /> <router-view />
</div> </div>
</div>
</template> </template>

View File

@@ -103,9 +103,7 @@ export default {
</script> </script>
<template> <template>
<div <div class="h-full w-full p-8 col-span-6">
class="border border-n-weak bg-n-solid-1 rounded-t-lg border-b-0 h-full w-full p-6 col-span-6 overflow-auto"
>
<form <form
class="flex flex-wrap mx-0 overflow-x-auto" class="flex flex-wrap mx-0 overflow-x-auto"
@submit.prevent="addAgents" @submit.prevent="addAgents"

View File

@@ -57,9 +57,7 @@ export default {
</script> </script>
<template> <template>
<div <div class="h-full w-full p-8 col-span-6">
class="border border-n-weak bg-n-solid-1 rounded-t-lg border-b-0 h-full w-full p-6 col-span-6 overflow-auto"
>
<PageHeader <PageHeader
:header-title="$t('TEAMS_SETTINGS.EDIT_FLOW.CREATE.TITLE')" :header-title="$t('TEAMS_SETTINGS.EDIT_FLOW.CREATE.TITLE')"
:header-content="$t('TEAMS_SETTINGS.EDIT_FLOW.CREATE.DESC')" :header-content="$t('TEAMS_SETTINGS.EDIT_FLOW.CREATE.DESC')"

View File

@@ -27,8 +27,15 @@ export default {
</script> </script>
<template> <template>
<div class="grid grid-cols-1 md:grid-cols-8 overflow-auto h-full px-5 flex-1"> <div class="mx-2 flex flex-col gap-6 mb-8">
<woot-wizard class="hidden md:block col-span-2" :items="items" /> <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]"
>
<woot-wizard
class="hidden lg:block col-span-2 h-fit py-8 px-6"
:items="items"
/>
<router-view /> <router-view />
</div> </div>
</div>
</template> </template>

View File

@@ -11,9 +11,7 @@ export default {
</script> </script>
<template> <template>
<div <div class="h-full w-full p-6 col-span-6">
class="border border-n-weak bg-n-solid-1 rounded-t-lg border-b-0 h-full w-full p-6 col-span-6 overflow-auto"
>
<EmptyState <EmptyState
:title="$t('TEAMS_SETTINGS.FINISH.TITLE')" :title="$t('TEAMS_SETTINGS.FINISH.TITLE')"
:message="$t('TEAMS_SETTINGS.FINISH.MESSAGE')" :message="$t('TEAMS_SETTINGS.FINISH.MESSAGE')"

View File

@@ -113,4 +113,71 @@ export const icons = {
width: 16, width: 16,
height: 20, height: 20,
}, },
/** Channels Starts */
website: {
body: `<path d="m3.023 1.664.126-.001.418-.001 1.286-.004h.34l1.596-.003q.92 0 1.84-.004l1.425-.003.85-.002a108 108 0 0 1 1.092-.003c.628-.004 1.163.04 1.706.388.554.593.645 1.218.64 2.004v.192l-.002.624v.435l-.004 1.14-.002 1.166-.006 2.283a4 4 0 0 1-.798-.15l-.182-.056-.193-.061-.2-.062-.634-.198-1.03-.32q-.088-.03-.18-.057l-.158-.05c-.39-.117-.764-.18-1.151-.013-.261.15-.403.338-.513.616-.059.432.047.817.18 1.224l.055.177q.086.278.175.554l.285.904.05.157.044.138c.031.127.031.127.031.361a1215 1215 0 0 1-4.146.014 337 337 0 0 1-1.753.005l-.68.001-.2.002c-.606-.002-1.133-.094-1.603-.493a1.78 1.78 0 0 1-.54-1.298l-.002-.703-.002-1.102-.002-1.481q0-.69-.003-1.378l-.002-1.185-.001-.707v-.666l-.002-.358c.004-.622.125-1.103.557-1.563.395-.347.776-.462 1.293-.463m-.648 1.121c-.48.604-.293.955-.293 1.875h11.309c.135-1.332.135-1.332-.235-1.816-.303-.28-.518-.3-.924-.302h-.12l-.687-.002-.943-.003h-.325q-.762-.003-1.525-.003-.88 0-1.76-.005L5.51 2.527l-.813-.003a99 99 0 0 0-1.044-.002c-.675-.009-.675-.009-1.279.263" fill="currentColor"/><path d="M10.52 9.406c.193.044.193.044.396.105l.112.034.621.189.534.163a164 164 0 0 0 1.211.369l.252.076.554.17c.507.179.507.179.597.359.034.365.034.365-.092.532-.176.14-.355.197-.567.258l-.251.079q-.196.06-.394.118c-.498.13-.498.13-.908.416a2 2 0 0 0-.132.413q-.057.177-.117.351-.09.28-.176.561l-.058.183-.052.164a.93.93 0 0 1-.3.382c-.205.03-.205.03-.41 0-.226-.176-.292-.356-.374-.625l-.037-.118-.116-.384-.081-.268-.17-.562q-.107-.36-.218-.717a136 136 0 0 1-.36-1.19q-.03-.105-.064-.212a2 2 0 0 1-.045-.611c.174-.272.34-.252.645-.235M6.184 3.162c.175.033.175.033.318.158.092.168.092.168.092.351-.068.194-.11.253-.293.344-.147.026-.147.026-.293 0-.136-.095-.136-.095-.235-.234-.035-.215-.028-.31.096-.49.139-.096.139-.096.315-.129m-1.172.033c.139.096.139.096.234.235.027.186.034.296-.066.458a.74.74 0 0 1-.461.128c-.172-.121-.172-.121-.293-.293 0-.191.026-.3.128-.462.161-.1.272-.092.458-.066m-1.231.059c.117.117.117.117.14.348-.006.168-.01.222-.114.359-.203.078-.345.046-.553-.004-.117-.117-.117-.117-.14-.348.006-.168.01-.222.114-.359.203-.078.345-.046.553.004" fill="currentColor"/>`,
width: 16,
height: 16,
},
line: {
body: `<g clip-path="url(#a)"><path d="M2.778.778a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2v-8a2 2 0 0 0-2-2zm4 2.25c2.344 0 4.25 1.528 4.25 3.407 0 .751-.295 1.429-.912 2.096-.892 1.014-2.885 2.249-3.339 2.437-.453.189-.386-.12-.367-.226l.06-.36c.014-.107.03-.273-.013-.38-.048-.116-.236-.177-.375-.206-2.042-.267-3.554-1.677-3.554-3.361 0-1.879 1.907-3.407 4.25-3.407M4.27 5.443a.22.22 0 0 0-.223.22v1.669c0 .122.1.22.223.22h.844a.22.22 0 0 0 .223-.22c0-.121-.1-.22-.223-.22h-.621V5.664c0-.122-.1-.22-.223-.22m1.495 0a.22.22 0 0 0-.223.22v1.669c0 .122.1.22.223.22a.22.22 0 0 0 .223-.22V5.664c0-.122-.1-.22-.223-.22m.766 0a.22.22 0 0 0-.224.22v1.669c0 .122.101.22.224.22a.22.22 0 0 0 .223-.22V6.3l.865 1.164a.225.225 0 0 0 .25.077.22.22 0 0 0 .151-.209V5.664c0-.122-.1-.22-.222-.22a.22.22 0 0 0-.223.22v1.032l-.866-1.164a.23.23 0 0 0-.178-.089m2.011 0a.22.22 0 0 0-.222.221v1.668c0 .122.1.22.222.22h.845a.22.22 0 0 0 .223-.22c0-.121-.1-.22-.223-.22h-.622v-.394h.622a.22.22 0 0 0 .223-.22c0-.122-.1-.22-.223-.22h-.622v-.394h.622a.22.22 0 0 0 .223-.22c0-.121-.1-.22-.223-.22z" fill="currentColor"/></g><defs><clipPath id="a"><path fill="currentColor" d="M.333.333h13.333v13.333H.333z"/></clipPath></defs>`,
width: 13,
height: 13,
},
facebook: {
body: `<path d="M7 .333a6.667 6.667 0 0 1 1 13.258v-4.59h.666a1 1 0 0 0 0-2H8V5.665c0-.183.15-.333.333-.333h.333a1 1 0 0 0 0-2h-.333A2.333 2.333 0 0 0 6 5.668V7h-.667a1 1 0 1 0 0 2H6v4.592A6.668 6.668 0 0 1 7 .333" fill="currentColor"/>`,
width: 14,
height: 14,
},
whatsapp: {
body: `<path d="M7 .333A6.667 6.667 0 1 1 3.554 12.71l-2.02.594a.673.673 0 0 1-.836-.836l.593-2.021A6.667 6.667 0 0 1 7 .334M5.524 3.84a.46.46 0 0 0-.456-.05 1.86 1.86 0 0 0-1.072 1.258l-.004.02c-.026.101-.047.22-.042.35.016.455.194 1.743 1.543 3.091 1.347 1.348 2.634 1.527 3.09 1.543.13.005.249-.017.35-.042l.023-.006a1.85 1.85 0 0 0 1.254-1.067.47.47 0 0 0-.06-.469c-.355-.455-.832-.784-1.256-1.077l-.085-.058a.474.474 0 0 0-.649.105l-.4.61a.153.153 0 0 1-.203.05 5 5 0 0 1-.951-.703 4.4 4.4 0 0 1-.671-.915.15.15 0 0 1 .045-.194l.615-.457a.474.474 0 0 0 .081-.626l-.09-.133c-.28-.413-.607-.898-1.062-1.23" fill="currentColor"/>`,
width: 14,
height: 14,
},
instagram: {
body: `<path d="M8.667 0A3.333 3.333 0 0 1 12 3.333v5.334A3.333 3.333 0 0 1 8.667 12H3.333A3.333 3.333 0 0 1 0 8.667V3.333A3.333 3.333 0 0 1 3.333 0zM6 3.333a2.667 2.667 0 1 0 0 5.334 2.667 2.667 0 0 0 0-5.334m0 1.334a1.333 1.333 0 1 1 0 2.666 1.333 1.333 0 0 1 0-2.666m3-2.334a.667.667 0 1 0 0 1.334.667.667 0 0 0 0-1.334" fill="currentColor"/>`,
width: 12,
height: 12,
},
messenger: {
body: `<path fill-rule="evenodd" clip-rule="evenodd" d="M.333 7a6.667 6.667 0 1 1 3.221 5.709l-2.033.597a.667.667 0 0 1-.827-.827l.598-2.033A6.64 6.64 0 0 1 .333 7M5.53 5.53c.26-.26.682-.26.942 0L8 7.057 9.529 5.53a.667.667 0 1 1 .942.943l-2 2a.667.667 0 0 1-.942 0L6 6.943 4.471 8.472a.667.667 0 1 1-.942-.943z" fill="currentColor"/>`,
width: 14,
height: 14,
},
mail: {
body: `<g clip-path="url(#a)" fill="currentColor"><path d="M0 4.676q.058.03.116.063l.065.036c.105.06.204.128.304.196l.068.047.54.366.083.056q.421.284.847.562.598.393 1.19.793.673.454 1.352.9a57 57 0 0 1 1.1.738c.525.356 1.087.543 1.725.437.46-.096.845-.36 1.225-.621q.193-.134.387-.265l.081-.055q.418-.284.843-.56.597-.392 1.19-.793.669-.452 1.345-.896.38-.249.757-.501l.076-.05.358-.24.127-.086.059-.04c.132-.087.132-.087.162-.087l.007 2.49v.354l.006 1.92.001.545.002.703c.004.486-.045.832-.387 1.2-.262.242-.547.369-.907.369h-.409l-.403.001h-.434l-.85.001h-.692l-1.96.001H7.76l-1.715.001-2.75.002a262 262 0 0 1-1.664 0l-.142.001c-.422.003-.775-.061-1.098-.354l-.063-.07-.04-.04c-.17-.181-.292-.427-.291-.678V9.407l.001-1.933v-.06z"/><path d="M1.278 1.743h.263l.549-.001h.434l1.542-.002H6.24q.858 0 1.715-.002a1231 1231 0 0 1 3.592-.002h.964c.422-.003.775.061 1.098.354l.063.07.04.04c.26.272.3.613.296.97-.013.348-.198.612-.44.85a2 2 0 0 1-.303.221l-.07.046-.15.097-.407.265-.084.055q-.421.276-.837.56-.63.425-1.265.842-.565.371-1.124.75a123 123 0 0 1-1.22.817c-.46.305-.85.525-1.419.433a1.6 1.6 0 0 1-.46-.213l-.056-.036q-.33-.211-.651-.433A98 98 0 0 0 4.21 6.55q-.634-.417-1.262-.843-.531-.358-1.065-.71l-.654-.43-.062-.041-.47-.312C.451 4.047.235 3.873.108 3.6l-.046-.097a1.37 1.37 0 0 1-.005-.94c.16-.352.434-.613.79-.76a1.5 1.5 0 0 1 .43-.061"/></g><defs><clipPath id="a"><path fill="currentColor" d="M0 0h14v14H0z"/></clipPath></defs>`,
width: 14,
height: 14,
},
sms: {
body: `<path fill-rule="evenodd" clip-rule="evenodd" d="M.333 2a2 2 0 0 1 2-2h9.334a2 2 0 0 1 2 2v6a2 2 0 0 1-2 2h-1.448a.67.67 0 0 0-.472.195L8.414 11.53a2 2 0 0 1-2.828 0l-1.333-1.334A.67.67 0 0 0 3.78 10H2.333a2 2 0 0 1-2-2zm3.334.667a.667.667 0 1 0 0 1.333h6.666a.667.667 0 1 0 0-1.333zm0 2.666a.667.667 0 1 0 0 1.334H7a.667.667 0 1 0 0-1.334z" fill="currentColor"/>`,
width: 14,
height: 12,
},
telegram: {
body: `<path fill-rule="evenodd" clip-rule="evenodd" d="M12.185.287A1 1 0 0 1 13.56 1.37l-1.513 9.171c-.146.884-1.117 1.392-1.928.951a39 39 0 0 1-2.594-1.53c-.453-.296-1.841-1.245-1.67-1.921C6 7.464 8.332 5.292 9.666 4c.524-.507.285-.8-.334-.333C7.8 4.826 5.336 6.587 4.521 7.083c-.719.438-1.094.512-1.542.438a10.2 10.2 0 0 1-2.194-.604c-.836-.346-.795-1.496 0-1.83z" fill="currentColor"/>`,
width: 14,
height: 12,
},
api: {
body: `<path d="M4.468 11.5q-1.14 0-1.776-.636-.624-.636-.624-1.632v-.996q0-.504-.12-.792a.7.7 0 0 0-.348-.408q-.24-.132-.624-.132V5.488q.384 0 .624-.12a.7.7 0 0 0 .348-.408q.12-.3.12-.804V3.16q0-.996.624-1.62.636-.636 1.776-.636h.756v1.548q-.756-.036-1.104.204-.336.24-.336.792v.828q0 .852-.42 1.332-.408.48-.972.552v.072q.564.06.972.552.42.48.42 1.332v.84q0 .552.336.816.348.252 1.104.228v1.5zm6.855 0h-.756V10q.756.024 1.092-.228.348-.264.348-.816v-.84q0-.852.408-1.332.42-.492.984-.552V6.16q-.552-.072-.972-.552t-.42-1.332v-.828q0-.552-.348-.792-.336-.24-1.092-.204V.904h.756q1.152 0 1.776.636.624.624.624 1.62v.996q0 .504.108.804.12.288.36.408t.624.12v1.416q-.384 0-.624.132a.76.76 0 0 0-.36.408q-.108.288-.108.792v.996q0 .996-.624 1.632t-1.776.636" fill="currentColor"/>`,
width: 15,
height: 12,
},
twilio: {
body: `<path d="M8 .5c4.14 0 7.5 3.36 7.5 7.5s-3.36 7.5-7.5 7.5S.5 12.14.5 8 3.86.5 8 .5m0 1.98C4.94 2.48 2.48 4.94 2.48 8S4.94 13.52 8 13.52s5.52-2.46 5.52-5.52S11.06 2.48 8 2.48M9.86 8.3a1.56 1.56 0 1 1 0 3.12 1.56 1.56 0 0 1 0-3.12m-3.72 0a1.56 1.56 0 1 1 0 3.12 1.56 1.56 0 0 1 0-3.12m3.72-3.72a1.56 1.56 0 1 1 0 3.12 1.56 1.56 0 0 1 0-3.12m-3.72 0a1.56 1.56 0 1 1 0 3.12 1.56 1.56 0 0 1 0-3.12" fill="currentColor"/>`,
width: 16,
height: 16,
},
gmail: {
body: `<path d="M3.182 12.253H.955A.954.954 0 0 1 0 11.298V3.185c0-1.18 1.346-1.854 2.29-1.146L7 5.571l4.71-3.532c.942-.708 2.29-.035 2.29 1.146v8.113a.954.954 0 0 1-.955.955h-2.227v-5.41L7 9.708 3.182 6.844z" fill="currentColor"/>`,
width: 14,
height: 14,
},
outlook: {
body: `<path d="M6.18.365a1 1 0 0 0-.267.028l-4.405.978A1.505 1.505 0 0 0 .333 2.836v8.328c0 .7.492 1.313 1.175 1.465l4.405.98v-.001c.718.16 1.42-.404 1.42-1.14V1.532C7.333.888 6.796.376 6.18.365m2.153 2.319v3.713l1.17.826 4.494-3.1c-.03-.8-.69-1.44-1.497-1.44zM3.833 4.5c1.028 0 1.834 1.172 1.834 2.667S4.86 9.834 3.833 9.834 2 8.662 2 7.167 2.805 4.5 3.833 4.5M14 5.337 9.783 8.244a.49.49 0 0 1-.573-.003l-.877-.621v4.064H12.5c.827 0 1.5-.674 1.5-1.5zM3.833 5.5C3.44 5.5 3 6.213 3 7.167s.44 1.667.833 1.667c.394 0 .834-.713.834-1.667S4.227 5.5 3.833 5.5" fill="currentColor"/>`,
width: 14,
height: 14,
},
/** Ends */
}; };