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
@@ -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