chore: Enable flexible whatsapp onboarding (Manual + Embedded Signup) options (#12344)
We recently introduced the WhatsApp Embedded Signup flow in Chatwoot to simplify onboarding. However, we discovered two important limitations: Some customers’ numbers are already linked to an Embedded Signup, which blocks re-use. Tech providers cannot onboard their own numbers via Embedded Signup. As a result, we need to support both Manual and Embedded Signup flows to cover all scenarios. ### Problem - Current UI only offers the Embedded Signup option. - Customers who need to reuse existing numbers (already connected to WABA) or tech providers testing their own numbers get stuck. - Manual flow exists but is no longer exposed in the UX **Current Embedded Signup screen** <img width="2564" height="1250" alt="CleanShot 2025-08-21 at 21 58 07@2x" src="https://github.com/user-attachments/assets/c3de4cf1-cae6-4a0e-aa9c-5fa4e2249c0e" /> **Current Manual Setup screen** <img width="2568" height="1422" alt="CleanShot 2025-08-21 at 22 00 25@2x" src="https://github.com/user-attachments/assets/96408f97-3ffe-42d1-9019-a511e808f5ac" /> ### Solution - Design a dual-path UX in the Create WhatsApp Inbox step that: - Offers Embedded Signup (default/recommended) for new numbers and businesses. - Offers Manual Setup for advanced users, existing linked numbers, and tech providers. <img width="2030" height="1376" alt="CleanShot 2025-09-01 at 14 13 16@2x" src="https://github.com/user-attachments/assets/6f17e5a2-a2fd-40fb-826a-c9ee778be795" /> --------- Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> Co-authored-by: iamsivin <iamsivin@gmail.com>
This commit is contained in:
co-authored by
Sivin Varghese
iamsivin
parent
300d68f3f7
commit
458ed1e26d
@@ -1,25 +1,23 @@
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useStore } from 'vuex';
|
||||
import { useI18n, I18nT } from 'vue-i18n';
|
||||
import Twilio from './Twilio.vue';
|
||||
import ThreeSixtyDialogWhatsapp from './360DialogWhatsapp.vue';
|
||||
import CloudWhatsapp from './CloudWhatsapp.vue';
|
||||
import WhatsappEmbeddedSignup from './WhatsappEmbeddedSignup.vue';
|
||||
import ChannelSelector from 'dashboard/components/ChannelSelector.vue';
|
||||
import { FEATURE_FLAGS } from 'dashboard/featureFlags';
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const { t } = useI18n();
|
||||
const store = useStore();
|
||||
|
||||
const PROVIDER_TYPES = {
|
||||
WHATSAPP: 'whatsapp',
|
||||
TWILIO: 'twilio',
|
||||
WHATSAPP_CLOUD: 'whatsapp_cloud',
|
||||
WHATSAPP_EMBEDDED: 'whatsapp_embedded',
|
||||
WHATSAPP_MANUAL: 'whatsapp_manual',
|
||||
THREE_SIXTY_DIALOG: '360dialog',
|
||||
};
|
||||
|
||||
@@ -30,14 +28,6 @@ const hasWhatsappAppId = computed(() => {
|
||||
);
|
||||
});
|
||||
|
||||
const isWhatsappEmbeddedSignupEnabled = computed(() => {
|
||||
const accountId = route.params.accountId;
|
||||
return store.getters['accounts/isFeatureEnabledonAccount'](
|
||||
accountId,
|
||||
FEATURE_FLAGS.WHATSAPP_EMBEDDED_SIGNUP
|
||||
);
|
||||
});
|
||||
|
||||
const selectedProvider = computed(() => route.query.provider);
|
||||
|
||||
const showProviderSelection = computed(() => !selectedProvider.value);
|
||||
@@ -67,28 +57,15 @@ const selectProvider = providerValue => {
|
||||
});
|
||||
};
|
||||
|
||||
const shouldShowEmbeddedSignup = provider => {
|
||||
// Check if the feature is enabled for the account
|
||||
if (!isWhatsappEmbeddedSignupEnabled.value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const shouldShowCloudWhatsapp = provider => {
|
||||
return (
|
||||
(provider === PROVIDER_TYPES.WHATSAPP && hasWhatsappAppId.value) ||
|
||||
provider === PROVIDER_TYPES.WHATSAPP_EMBEDDED
|
||||
provider === PROVIDER_TYPES.WHATSAPP_MANUAL ||
|
||||
(provider === PROVIDER_TYPES.WHATSAPP && !hasWhatsappAppId.value)
|
||||
);
|
||||
};
|
||||
|
||||
const shouldShowCloudWhatsapp = provider => {
|
||||
// If embedded signup feature is enabled and app ID is configured, don't show cloud whatsapp
|
||||
if (isWhatsappEmbeddedSignupEnabled.value && hasWhatsappAppId.value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Show cloud whatsapp when:
|
||||
// 1. Provider is whatsapp AND
|
||||
// 2. Either no app ID is configured OR embedded signup feature is disabled
|
||||
return provider === PROVIDER_TYPES.WHATSAPP;
|
||||
const handleManualLinkClick = () => {
|
||||
selectProvider(PROVIDER_TYPES.WHATSAPP_MANUAL);
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -117,18 +94,52 @@ const shouldShowCloudWhatsapp = provider => {
|
||||
</div>
|
||||
|
||||
<div v-else-if="showConfiguration">
|
||||
<WhatsappEmbeddedSignup
|
||||
v-if="shouldShowEmbeddedSignup(selectedProvider)"
|
||||
/>
|
||||
<CloudWhatsapp v-else-if="shouldShowCloudWhatsapp(selectedProvider)" />
|
||||
<Twilio
|
||||
v-else-if="selectedProvider === PROVIDER_TYPES.TWILIO"
|
||||
type="whatsapp"
|
||||
/>
|
||||
<ThreeSixtyDialogWhatsapp
|
||||
v-else-if="selectedProvider === PROVIDER_TYPES.THREE_SIXTY_DIALOG"
|
||||
/>
|
||||
<CloudWhatsapp v-else />
|
||||
<div class="px-6 py-5 rounded-2xl border border-n-weak">
|
||||
<!-- Show embedded signup if app ID is configured -->
|
||||
<div
|
||||
v-if="
|
||||
hasWhatsappAppId && selectedProvider === PROVIDER_TYPES.WHATSAPP
|
||||
"
|
||||
>
|
||||
<WhatsappEmbeddedSignup />
|
||||
|
||||
<!-- Manual setup fallback option -->
|
||||
<div class="pt-6 mt-6 border-t border-n-weak">
|
||||
<I18nT
|
||||
keypath="INBOX_MGMT.ADD.WHATSAPP.EMBEDDED_SIGNUP.MANUAL_FALLBACK"
|
||||
tag="p"
|
||||
class="text-sm text-n-slate-11"
|
||||
>
|
||||
<template #link>
|
||||
<a
|
||||
href="#"
|
||||
class="underline text-n-brand"
|
||||
@click.prevent="handleManualLinkClick"
|
||||
>
|
||||
{{
|
||||
$t(
|
||||
'INBOX_MGMT.ADD.WHATSAPP.EMBEDDED_SIGNUP.MANUAL_LINK_TEXT'
|
||||
)
|
||||
}}
|
||||
</a>
|
||||
</template>
|
||||
</I18nT>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Show manual setup -->
|
||||
<CloudWhatsapp v-else-if="shouldShowCloudWhatsapp(selectedProvider)" />
|
||||
|
||||
<!-- Other providers -->
|
||||
<Twilio
|
||||
v-else-if="selectedProvider === PROVIDER_TYPES.TWILIO"
|
||||
type="whatsapp"
|
||||
/>
|
||||
<ThreeSixtyDialogWhatsapp
|
||||
v-else-if="selectedProvider === PROVIDER_TYPES.THREE_SIXTY_DIALOG"
|
||||
/>
|
||||
<CloudWhatsapp v-else />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
+24
-27
@@ -2,12 +2,13 @@
|
||||
import { ref, computed, onMounted, onBeforeUnmount } from 'vue';
|
||||
import { useStore } from 'vuex';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useI18n, I18nT } from 'vue-i18n';
|
||||
import { useAlert } from 'dashboard/composables';
|
||||
import Icon from 'next/icon/Icon.vue';
|
||||
import NextButton from 'next/button/Button.vue';
|
||||
import LoadingState from 'dashboard/components/widgets/LoadingState.vue';
|
||||
import { parseAPIErrorResponse } from 'dashboard/store/utils/api';
|
||||
import globalConstants from 'dashboard/constants/globals.js';
|
||||
import {
|
||||
setupFacebookSdk,
|
||||
initWhatsAppEmbeddedSignup,
|
||||
@@ -28,9 +29,6 @@ const authCode = ref(null);
|
||||
const businessData = ref(null);
|
||||
const isAuthenticating = ref(false);
|
||||
|
||||
// Computed
|
||||
const whatsappIconPath = '/assets/images/dashboard/channels/whatsapp.png';
|
||||
|
||||
const benefits = computed(() => [
|
||||
{
|
||||
key: 'EASY_SETUP',
|
||||
@@ -235,14 +233,9 @@ onBeforeUnmount(() => {
|
||||
<div class="flex flex-col items-start mb-6 text-start">
|
||||
<div class="flex justify-start mb-6">
|
||||
<div
|
||||
class="flex justify-center items-center w-12 h-12 rounded-full bg-n-alpha-2"
|
||||
class="flex size-11 items-center justify-center rounded-full bg-n-alpha-2"
|
||||
>
|
||||
<img
|
||||
:src="whatsappIconPath"
|
||||
:alt="$t('INBOX_MGMT.ADD.WHATSAPP.PROVIDERS.WHATSAPP_CLOUD')"
|
||||
class="object-contain w-8 h-8"
|
||||
draggable="false"
|
||||
/>
|
||||
<Icon icon="i-woot-whatsapp" class="text-n-slate-10 size-6" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -266,22 +259,26 @@ onBeforeUnmount(() => {
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-2 mb-6">
|
||||
<span class="text-sm text-n-slate-11">
|
||||
{{ $t('INBOX_MGMT.ADD.WHATSAPP.EMBEDDED_SIGNUP.LEARN_MORE.TEXT') }}
|
||||
{{ ' ' }}
|
||||
<a
|
||||
:href="
|
||||
$t('INBOX_MGMT.ADD.WHATSAPP.EMBEDDED_SIGNUP.LEARN_MORE.LINK_URL')
|
||||
"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="underline text-primary"
|
||||
>
|
||||
{{
|
||||
$t('INBOX_MGMT.ADD.WHATSAPP.EMBEDDED_SIGNUP.LEARN_MORE.LINK_TEXT')
|
||||
}}
|
||||
</a>
|
||||
</span>
|
||||
<I18nT
|
||||
keypath="INBOX_MGMT.ADD.WHATSAPP.EMBEDDED_SIGNUP.LEARN_MORE.TEXT"
|
||||
tag="span"
|
||||
class="text-sm text-n-slate-11"
|
||||
>
|
||||
<template #link>
|
||||
<a
|
||||
:href="globalConstants.WHATSAPP_EMBEDDED_SIGNUP_DOCS_URL"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="underline text-n-brand"
|
||||
>
|
||||
{{
|
||||
$t(
|
||||
'INBOX_MGMT.ADD.WHATSAPP.EMBEDDED_SIGNUP.LEARN_MORE.LINK_TEXT'
|
||||
)
|
||||
}}
|
||||
</a>
|
||||
</template>
|
||||
</I18nT>
|
||||
</div>
|
||||
|
||||
<div class="flex mt-4">
|
||||
|
||||
Reference in New Issue
Block a user