feat: Update contact details page UI (#13041)

This commit is contained in:
Sivin Varghese
2025-12-10 20:14:24 +05:30
committed by GitHub
parent 8d191a70b1
commit 417f28ce4a
14 changed files with 164 additions and 109 deletions
@@ -76,12 +76,12 @@ const closeMobileSidebar = () => {
class="flex w-full h-full overflow-hidden justify-evenly bg-n-surface-1"
>
<div
class="flex flex-col w-full h-full transition-all duration-300 ltr:2xl:ml-56 rtl:2xl:mr-56"
class="flex flex-col w-full h-full transition-all duration-300 ltr:3xl:ml-56 rtl:3xl:mr-56"
>
<header class="sticky top-0 z-10 px-6 3xl:px-0">
<div class="w-full mx-auto max-w-[40.625rem]">
<div class="w-full mx-auto max-w-4xl">
<div
class="flex flex-col xs:flex-row items-start xs:items-center justify-between w-full py-7 gap-2"
class="flex flex-col xs:flex-row items-start xs:items-center justify-between w-full py-4 gap-2"
>
<Breadcrumb
:items="breadcrumbItems"
@@ -94,23 +94,20 @@ const closeMobileSidebar = () => {
? $t('CONTACTS_LAYOUT.HEADER.BLOCK_CONTACT')
: $t('CONTACTS_LAYOUT.HEADER.UNBLOCK_CONTACT')
"
size="sm"
slate
:is-loading="isUpdating"
:disabled="isUpdating"
@click="toggleBlock"
/>
<VoiceCallButton
:phone="selectedContact?.phoneNumber"
:contact-id="contactId"
size="md"
:label="$t('CONTACT_PANEL.CALL')"
size="sm"
/>
<ComposeConversation :contact-id="contactId">
<template #trigger="{ toggle }">
<Button
:label="$t('CONTACTS_LAYOUT.HEADER.SEND_MESSAGE')"
size="sm"
@click="toggle"
/>
</template>
@@ -120,7 +117,7 @@ const closeMobileSidebar = () => {
</div>
</header>
<main class="flex-1 px-6 overflow-y-auto 3xl:px-px">
<div class="w-full py-4 mx-auto max-w-[40.625rem]">
<div class="w-full pt-2.5 pb-4 mx-auto max-w-4xl">
<slot name="default" />
</div>
</main>
@@ -129,7 +126,7 @@ const closeMobileSidebar = () => {
<!-- Desktop sidebar -->
<div
v-if="slots.sidebar"
class="hidden lg:block overflow-y-auto justify-end min-w-52 w-full py-6 max-w-md border-l border-n-weak bg-n-solid-2"
class="hidden lg:block overflow-y-auto justify-end min-w-52 w-full py-5 max-w-md border-l border-n-weak bg-n-surface-2"
>
<slot name="sidebar" />
</div>
@@ -1,7 +1,8 @@
<script setup>
import { useI18n } from 'vue-i18n';
import Avatar from 'dashboard/components-next/avatar/Avatar.vue';
import ComboBox from 'dashboard/components-next/combobox/ComboBox.vue';
import { useI18n } from 'vue-i18n';
defineProps({
selectedContact: {
@@ -37,13 +38,13 @@ const { t } = useI18n();
<template>
<div class="flex flex-col">
<div class="flex flex-col gap-2">
<div class="flex items-center justify-between h-5 gap-2">
<div class="flex flex-col gap-3">
<div class="flex items-center justify-between h-6 gap-2">
<label class="text-sm text-n-slate-12">
{{ t('CONTACTS_LAYOUT.SIDEBAR.MERGE.PRIMARY') }}
</label>
<span
class="flex items-center justify-center w-24 h-5 text-xs rounded-md text-n-teal-11 bg-n-alpha-2"
class="flex items-center justify-center px-1.5 h-6 font-420 text-xs rounded-md text-n-teal-11 outline outline-1 outline-n-weak"
>
{{ t('CONTACTS_LAYOUT.SIDEBAR.MERGE.PRIMARY_HELP_LABEL') }}
</span>
@@ -69,7 +70,7 @@ const { t } = useI18n();
@search="query => emit('search', query)"
/>
</div>
<div class="relative flex justify-center gap-2 top-4">
<div class="relative flex justify-center gap-2 mt-4 mb-3">
<div v-for="i in 3" :key="i" class="relative w-4 h-8">
<div
class="absolute w-0 h-0 border-l-[4px] border-r-[4px] border-b-[6px] border-l-transparent border-r-transparent border-n-strong ltr:translate-x-[4px] rtl:-translate-x-[4px] -translate-y-[4px]"
@@ -79,13 +80,13 @@ const { t } = useI18n();
/>
</div>
</div>
<div class="flex flex-col gap-2">
<div class="flex items-center justify-between h-5 gap-2">
<div class="flex flex-col gap-3">
<div class="flex items-center justify-between h-6 gap-2">
<label class="text-sm text-n-slate-12">
{{ t('CONTACTS_LAYOUT.SIDEBAR.MERGE.PARENT') }}
</label>
<span
class="flex items-center justify-center w-24 h-5 text-xs rounded-md text-n-ruby-11 bg-n-alpha-2"
class="flex items-center justify-center px-1.5 h-6 font-420 text-xs rounded-md text-n-ruby-11 outline outline-1 outline-n-weak"
>
{{ t('CONTACTS_LAYOUT.SIDEBAR.MERGE.PARENT_HELP_LABEL') }}
</span>
@@ -4,11 +4,11 @@ import { useI18n } from 'vue-i18n';
import { required, email } from '@vuelidate/validators';
import { useVuelidate } from '@vuelidate/core';
import { splitName } from '@chatwoot/utils';
import countries from 'shared/constants/countries.js';
import Input from 'dashboard/components-next/input/Input.vue';
import ComboBox from 'dashboard/components-next/combobox/ComboBox.vue';
import CountryDropdown from 'dashboard/components-next/Countries/CountryDropdown.vue';
import Icon from 'dashboard/components-next/icon/Icon.vue';
import PhoneNumberInput from 'dashboard/components-next/phonenumberinput/PhoneNumberInput.vue';
import CompaniesDropdown from 'dashboard/components-next/Companies/CompaniesDropdown.vue';
const props = defineProps({
contactData: {
@@ -122,10 +122,6 @@ const prepareStateBasedOnProps = () => {
});
};
const countryOptions = computed(() =>
countries.map(({ name, id }) => ({ label: name, value: id }))
);
const editDetailsForm = computed(() =>
Object.keys(FORM_CONFIG).map(key => ({
key,
@@ -204,9 +200,14 @@ const getMessageType = key => {
: 'info';
};
const handleCountrySelection = value => {
const selectedCountry = countries.find(option => option.id === value);
state.additionalAttributes.country = selectedCountry?.name || '';
const handleCountryChange = country => {
state.additionalAttributes.countryCode = country?.id || '';
state.additionalAttributes.country = country?.name || '';
emit('update', state);
};
const handleCompanyChange = company => {
state.additionalAttributes.companyName = company?.name || '';
emit('update', state);
};
@@ -236,41 +237,49 @@ defineExpose({
</script>
<template>
<div class="flex flex-col gap-6">
<div class="flex flex-col items-start gap-2">
<span class="py-1 text-sm font-medium text-n-slate-12">
<div class="flex flex-col gap-6 w-full">
<div class="flex flex-col items-start gap-5 w-full">
<span class="text-sm font-medium text-n-slate-12">
{{ t('CONTACTS_LAYOUT.CARD.EDIT_DETAILS_FORM.TITLE') }}
</span>
<div class="grid w-full grid-cols-1 gap-4 sm:grid-cols-2">
<template v-for="item in editDetailsForm" :key="item.key">
<ComboBox
<CountryDropdown
v-if="item.key === 'COUNTRY'"
v-model="state.additionalAttributes.countryCode"
:options="countryOptions"
:placeholder="item.placeholder"
class="[&>div>button]:h-8"
:class="{
'[&>div>button]:bg-n-alpha-black2 [&>div>button:not(.focused)]:!outline-transparent':
!isDetailsView,
'[&>div>button]:!bg-n-alpha-black2': isDetailsView,
'[&>div>button]:h-8': !isDetailsView,
'[&_button]:!h-10 [&_button]:!rounded-[10px]': isDetailsView,
}"
@update:model-value="handleCountrySelection"
@change="handleCountryChange"
/>
<PhoneNumberInput
v-else-if="item.key === 'PHONE_NUMBER'"
v-model="getFormBinding(item.key).value"
:placeholder="item.placeholder"
:show-border="isDetailsView"
:compact="!isDetailsView"
/>
<CompaniesDropdown
v-else-if="item.key === 'COMPANY_NAME'"
:placeholder="item.placeholder"
:selected-company-name="state.additionalAttributes.companyName"
:class="{
'[&>div>button]:h-8': !isDetailsView,
'[&_button]:!h-10 [&_button]:!rounded-[10px]': isDetailsView,
}"
@change="handleCompanyChange"
/>
<Input
v-else
v-model="getFormBinding(item.key).value"
:placeholder="item.placeholder"
:message-type="getMessageType(item.key)"
:custom-input-class="`h-8 !pt-1 !pb-1 ${
:custom-input-class="`!pt-1 !pb-1 ${
!isDetailsView
? '[&:not(.error,.focus)]:!outline-transparent'
: ''
? '[&:not(.error,.focus)]:!outline-transparent h-8'
: 'h-10 !rounded-[10px]'
}`"
class="w-full"
@input="
@@ -285,18 +294,19 @@ defineExpose({
</template>
</div>
</div>
<div class="flex flex-col items-start gap-2">
<span class="py-1 text-sm font-medium text-n-slate-12">
<div class="flex flex-col items-start gap-5">
<span class="text-sm font-medium text-n-slate-12">
{{ t('CONTACTS_LAYOUT.CARD.SOCIAL_MEDIA.TITLE') }}
</span>
<div class="flex flex-wrap gap-2">
<div
v-for="item in socialProfilesForm"
:key="item.key"
class="flex items-center h-8 gap-2 px-2 rounded-lg"
class="flex items-center h-8 gap-2 px-2 bg-n-alpha-2 dark:bg-n-solid-2"
:class="{
'bg-n-alpha-2 dark:bg-n-solid-2': isDetailsView,
'bg-n-alpha-2 dark:bg-n-solid-3': !isDetailsView,
'rounded-[10px] outline-1 outline outline-n-weak -outline-offset-1 hover:outline-n-slate-6':
isDetailsView,
'rounded-lg': !isDetailsView,
}"
>
<Icon
@@ -9,6 +9,7 @@ import ListAttribute from 'dashboard/components-next/CustomAttributes/ListAttrib
import CheckboxAttribute from 'dashboard/components-next/CustomAttributes/CheckboxAttribute.vue';
import DateAttribute from 'dashboard/components-next/CustomAttributes/DateAttribute.vue';
import OtherAttribute from 'dashboard/components-next/CustomAttributes/OtherAttribute.vue';
import HelperTextPopup from 'dashboard/components/ui/HelperTextPopup.vue';
const props = defineProps({
attribute: {
@@ -75,11 +76,15 @@ const CurrentAttributeComponent = computed(() => {
<template>
<div
class="grid grid-cols-[140px,1fr] group/attribute items-center w-full gap-2"
class="grid grid-cols-[auto,auto] group/attribute items-center w-full gap-4"
:class="isEditingView ? 'min-h-10' : 'min-h-11'"
>
<div class="flex items-center justify-between truncate">
<span class="text-sm font-medium truncate text-n-slate-12">
<div class="flex items-center gap-1.5 min-w-0">
<HelperTextPopup
v-if="attribute.attributeDescription"
:message="attribute.attributeDescription"
/>
<span class="text-sm font-420 truncate text-n-slate-12">
{{ attribute.attributeDisplayName }}
</span>
</div>
@@ -108,7 +108,7 @@ const hasNoUsedAttributes = computed(() => usedAttributes.value.length === 0);
</script>
<template>
<div v-if="hasContactAttributes" class="flex flex-col gap-6 px-6 py-6">
<div v-if="hasContactAttributes" class="flex flex-col gap-4 px-5 pt-4 pb-6">
<div v-if="!hasNoUsedAttributes" class="flex flex-col gap-2">
<ContactCustomAttributeItem
v-for="attribute in usedAttributes"
@@ -117,16 +117,16 @@ const hasNoUsedAttributes = computed(() => usedAttributes.value.length === 0);
:attribute="attribute"
/>
</div>
<div v-if="!hasNoUnusedAttributes" class="flex items-center gap-3">
<div v-if="!hasNoUnusedAttributes" class="flex items-center gap-2">
<div class="flex-1 h-[1px] bg-n-slate-5" />
<span class="text-sm font-medium text-n-slate-10">{{
<span class="text-xs font-420 text-n-slate-10">{{
t('CONTACTS_LAYOUT.SIDEBAR.ATTRIBUTES.UNUSED_ATTRIBUTES', {
count: unusedAttributesCount,
})
}}</span>
<div class="flex-1 h-[1px] bg-n-slate-5" />
</div>
<div class="flex flex-col gap-3">
<div class="flex flex-col gap-4">
<div v-if="!hasNoUnusedAttributes" class="relative">
<span class="absolute i-lucide-search size-3.5 top-2 left-3" />
<input
@@ -135,7 +135,7 @@ const hasNoUsedAttributes = computed(() => usedAttributes.value.length === 0);
:placeholder="
t('CONTACTS_LAYOUT.SIDEBAR.ATTRIBUTES.SEARCH_PLACEHOLDER')
"
class="w-full h-8 py-2 pl-10 pr-2 text-sm reset-base outline-none border-none rounded-lg bg-n-alpha-black2 dark:bg-n-solid-1 text-n-slate-12"
class="w-full h-8 py-2 ltr:pl-10 ltr:pr-2 rtl:pr-10 rtl:pl-2 mb-0 reset-base outline-n-weak outline-1 outline hover:outline-n-slate-6 text-sm border-none rounded-lg bg-n-alpha-black2 dark:bg-n-solid-1 text-n-slate-12 outline-offset-0"
/>
</div>
<div
@@ -32,7 +32,7 @@ const contactConversations = computed(() =>
</div>
<div
v-else-if="contactConversations.length > 0"
class="px-6 py-4 divide-y divide-n-weak"
class="px-6 pt-4 pb-6 divide-y divide-n-weak"
>
<ConversationCard
v-for="conversation in contactConversations"
@@ -103,12 +103,12 @@ const onMergeContacts = async () => {
</script>
<template>
<div class="flex flex-col gap-8 px-6 py-6">
<div class="flex flex-col gap-2">
<h4 class="text-base text-n-slate-12">
<div class="flex flex-col gap-6 px-5 pb-6 pt-4">
<div class="flex flex-col gap-2 mt-4">
<h4 class="text-base font-medium text-n-slate-12">
{{ t('CONTACTS_LAYOUT.SIDEBAR.MERGE.TITLE') }}
</h4>
<p class="text-sm text-n-slate-11">
<p class="text-sm font-420 text-n-slate-11">
{{ t('CONTACTS_LAYOUT.SIDEBAR.MERGE.DESCRIPTION') }}
</p>
</div>
@@ -126,13 +126,6 @@ const onMergeContacts = async () => {
@search="onContactSearch"
/>
<div class="flex items-center justify-between gap-3">
<Button
variant="faded"
color="slate"
:label="t('CONTACTS_LAYOUT.SIDEBAR.MERGE.BUTTONS.CANCEL')"
class="w-full bg-n-alpha-2 text-n-blue-text hover:bg-n-alpha-3"
@click="resetState"
/>
<Button
:label="t('CONTACTS_LAYOUT.SIDEBAR.MERGE.BUTTONS.CONFIRM')"
class="w-full"
@@ -140,6 +133,12 @@ const onMergeContacts = async () => {
:disabled="isMergingContact"
@click="onMergeContacts"
/>
<Button
slate
:label="t('CONTACTS_LAYOUT.SIDEBAR.MERGE.BUTTONS.CANCEL')"
class="w-full"
@click="resetState"
/>
</div>
</div>
</template>
@@ -55,12 +55,12 @@ useKeyboardEvents(keyboardEvents);
</script>
<template>
<div class="flex flex-col gap-6 py-6">
<div class="flex flex-col gap-4 pt-4 pb-6">
<Editor
v-model="state.message"
:placeholder="t('CONTACTS_LAYOUT.SIDEBAR.NOTES.PLACEHOLDER')"
focus-on-mount
class="[&>div]:!border-transparent [&>div]:px-4 [&>div]:py-4 px-6"
class="[&>div]:px-4 [&>div]:py-4 px-5"
>
<template #actions>
<div class="flex items-center gap-3">
@@ -87,7 +87,7 @@ useKeyboardEvents(keyboardEvents);
<ContactNoteItem
v-for="note in notes"
:key="note.id"
class="mx-6 py-4"
class="mx-5 py-4"
:note="note"
:written-by="getWrittenBy(note)"
allow-delete
@@ -11,6 +11,7 @@ import ContactLabels from 'dashboard/components-next/Contacts/ContactLabels/Cont
import ContactsForm from 'dashboard/components-next/Contacts/ContactsForm/ContactsForm.vue';
import ConfirmContactDeleteDialog from 'dashboard/components-next/Contacts/ContactsForm/ConfirmContactDeleteDialog.vue';
import Policy from 'dashboard/components/policy.vue';
import Icon from 'dashboard/components-next/icon/Icon.vue';
const props = defineProps({
selectedContact: {
@@ -121,17 +122,17 @@ const handleAvatarDelete = async () => {
</script>
<template>
<div class="flex flex-col items-start gap-8 pb-6">
<div class="flex flex-col items-start gap-3">
<div class="flex flex-col items-start gap-6 pb-6 w-full">
<div class="flex flex-col items-start gap-3 w-full">
<Avatar
:src="avatarSrc || ''"
:name="selectedContact?.name || ''"
:size="72"
:size="64"
allow-upload
@upload="handleAvatarUpload"
@delete="handleAvatarDelete"
/>
<div class="flex flex-col gap-1">
<div class="flex flex-col gap-2">
<h3 class="text-base font-medium text-n-slate-12">
{{ selectedContact?.name }}
</h3>
@@ -140,27 +141,41 @@ const handleAvatarDelete = async () => {
v-if="selectedContact?.identifier"
class="inline-flex items-center gap-1 text-sm text-n-slate-11"
>
<span class="i-ph-user-gear text-n-slate-10 size-4" />
<Icon
icon="i-ph-user-gear"
class="text-n-slate-10 size-4 flex-shrink-0"
/>
{{ selectedContact?.identifier }}
</span>
<span class="inline-flex items-center gap-1 text-sm text-n-slate-11">
<span
<span class="inline-flex items-start gap-1 text-sm text-n-slate-11">
<Icon
v-if="selectedContact?.identifier"
class="i-ph-activity text-n-slate-10 size-4"
icon="i-ph-activity"
class="text-n-slate-10 size-4 flex-shrink-0 mt-0.5"
/>
{{ $t('CONTACTS_LAYOUT.DETAILS.CREATED_AT', { date: createdAt }) }}
{{
$t('CONTACTS_LAYOUT.DETAILS.LAST_ACTIVITY', {
date: lastActivityAt,
})
}}
<div
class="flex flex-col sm:flex-row sm:inline-flex items-start sm:items-center gap-1 sm:gap-3"
>
<span>
{{
$t('CONTACTS_LAYOUT.DETAILS.CREATED_AT', { date: createdAt })
}}
</span>
<div class="w-px h-3 rounded-xl bg-n-weak hidden sm:block" />
<span>
{{
$t('CONTACTS_LAYOUT.DETAILS.LAST_ACTIVITY', {
date: lastActivityAt,
})
}}
</span>
</div>
</span>
</div>
</div>
<ContactLabels :contact-id="selectedContact?.id" />
</div>
<div class="flex flex-col items-start gap-6">
<div class="flex flex-col items-start gap-6 w-full">
<ContactsForm
ref="contactsFormRef"
:contact-data="contactData"
@@ -175,21 +190,23 @@ const handleAvatarDelete = async () => {
@click="updateContact"
/>
</div>
<Policy :permissions="['administrator']">
<Policy :permissions="['administrator']" class="w-full">
<div class="w-full h-px bg-n-slate-3 mt-1 mb-7" />
<div
class="flex flex-col items-start w-full gap-4 pt-6 border-t border-n-strong"
class="flex items-end justify-between w-full gap-4 outline outline-1 outline-n-weak rounded-2xl p-4"
>
<div class="flex flex-col gap-2">
<h6 class="text-base font-medium text-n-slate-12">
{{ t('CONTACTS_LAYOUT.DETAILS.DELETE_CONTACT') }}
</h6>
<span class="text-sm text-n-slate-11">
<span class="text-sm font-420 text-n-slate-11">
{{ t('CONTACTS_LAYOUT.DETAILS.DELETE_CONTACT_DESCRIPTION') }}
</span>
</div>
<Button
:label="t('CONTACTS_LAYOUT.DETAILS.DELETE_CONTACT')"
color="ruby"
ruby
class="flex-shrink-0"
@click="openConfirmDeleteContactDialog"
/>
</div>
@@ -101,7 +101,6 @@ const onPickInbox = async inbox => {
:label="label"
:icon="icon"
:size="size"
class="!px-2"
@click="onClick"
/>
@@ -78,6 +78,7 @@ const handleAttributeAction = async action => {
:menu-items="attributeListMenuItems"
show-search
class="w-48 mt-2 top-full ltr:right-0 rtl:left-0"
@click.stop
@action="handleAttributeAction($event)"
/>
</div>
@@ -12,6 +12,8 @@ import {
import Input from 'dashboard/components-next/input/Input.vue';
import Button from 'dashboard/components-next/button/Button.vue';
import Flag from 'dashboard/components-next/flag/Flag.vue';
import Icon from 'dashboard/components-next/icon/Icon.vue';
import DropdownMenu from 'dashboard/components-next/dropdown-menu/DropdownMenu.vue';
const props = defineProps({
@@ -27,6 +29,10 @@ const props = defineProps({
type: Boolean,
default: true,
},
compact: {
type: Boolean,
default: true,
},
});
const modelValue = defineModel({
@@ -67,7 +73,6 @@ const countryList = computed(() => {
value: country.id,
label: country.name,
dialCode: country.dial_code,
emoji: country.emoji,
isSelected: String(activeCountryCode.value) === String(country.id),
action: 'phoneNumberInput',
}));
@@ -163,39 +168,56 @@ watch(
<div>
<div
v-on-clickaway="() => closeCountryDropdown()"
class="relative flex items-center h-8 transition-all duration-500 ease-in-out outline outline-1 outline-offset-[-1px] rounded-lg bg-n-alpha-black2"
:class="[inputBorderClass, { 'cursor-not-allowed opacity-50': disabled }]"
class="relative flex items-center transition-all duration-500 ease-in-out outline outline-1 outline-offset-[-1px] rounded-lg bg-n-alpha-black2"
:class="[
inputBorderClass,
{
'cursor-not-allowed opacity-50': disabled,
'h-8': compact,
'h-10': !compact,
},
]"
>
<Input
v-model="phoneNumber"
type="tel"
:placeholder="placeholder"
:disabled="disabled"
custom-input-class="!border-0 !outline-none h-8 !py-0.5 !bg-transparent ltr:!pl-1 rtl:!pr-1"
custom-input-class="!border-0 !outline-none !py-0.5 !bg-transparent ltr:!pl-1 rtl:!pr-1"
class="w-full !flex-row"
:class="{
'[&>input]:!h-8': compact,
'[&>input]:h-10': !compact,
}"
>
<template #prefix>
<div class="flex items-center flex-shrink-0">
<Button
:label="activeCountry?.emoji || ''"
color="slate"
size="sm"
:icon="
!activeCountry ? 'i-lucide-globe' : 'i-lucide-chevron-down'
"
trailing-icon
slate
sm
:icon="!activeCountry ? 'i-lucide-globe' : ''"
:disabled="disabled"
no-animation
type="button"
class="!h-[1.875rem] top-1 ltr:ml-px rtl:mr-px !px-2 outline-0 !outline-none !rounded-lg border-0 ltr:!rounded-r-none rtl:!rounded-l-none"
class="top-1 ltr:ml-px rtl:mr-px !px-2 outline-0 !outline-none !rounded-lg border-0 ltr:!rounded-r-none rtl:!rounded-l-none flex-shrink-0"
:class="{
'!h-[1.875rem]': compact,
'!h-[2.375rem]': !compact,
}"
@click="toggleCountryDropdown"
>
<span
v-if="activeCountry"
class="inline-flex justify-center text-sm whitespace-nowrap"
>
{{ activeCountry?.emoji }}
</span>
<template v-if="activeCountry" #icon>
<Flag :country="activeCountry.value" class="size-3" />
</template>
<Icon
:icon="
!showDropdown
? 'i-lucide-chevron-down'
: 'i-lucide-chevron-up'
"
class="flex-shrink-0 text-n-slate-11"
/>
</Button>
<span
v-if="activeCountry"
@@ -212,7 +234,11 @@ watch(
show-search
class="z-[100] w-48 mt-2 overflow-y-auto ltr:left-0 rtl:right-0 top-full max-h-52"
@action="onSelectCountry"
/>
>
<template #icon="{ item }">
<Flag :country="item.value" class="size-3" />
</template>
</DropdownMenu>
</div>
<template v-if="phoneNumberError">
<p
@@ -560,7 +560,7 @@
"PRIMARY": "Primary contact",
"PRIMARY_HELP_LABEL": "To be saved",
"PRIMARY_REQUIRED_ERROR": "Please select a contact to merge with before proceeding",
"PARENT": "To be merged",
"PARENT": "Contact to be merged",
"PARENT_HELP_LABEL": "To be deleted",
"EMPTY_STATE": "No contacts found",
"PLACEHOLDER": "Search for primary contact",
@@ -150,7 +150,7 @@ onMounted(() => {
@go-to-contacts-list="goToContactsList"
/>
<template #sidebar>
<div class="px-6">
<div class="px-5">
<TabBar
:tabs="tabs"
:initial-active-tab="activeTabIndex"