feat: Update conversation side panel layout and styling (#12983)

This commit is contained in:
Sivin Varghese
2025-12-04 13:55:07 +05:30
committed by GitHub
parent 832d61c004
commit 604d4a9c30
68 changed files with 2336 additions and 2553 deletions
@@ -1,5 +1,5 @@
<script setup>
import { ref } from 'vue';
import { ref, computed } from 'vue';
import Button from 'dashboard/components-next/button/Button.vue';
import Switch from 'dashboard/components-next/switch/Switch.vue';
@@ -19,28 +19,35 @@ const emit = defineEmits(['update', 'delete']);
const attributeValue = ref(Boolean(props.attribute.value));
const isAttributePresent = computed(() => {
return (
props.attribute.value !== undefined &&
props.attribute.value !== null &&
props.attribute.value !== ''
);
});
const handleChange = value => {
emit('update', value);
};
const handleDelete = () => {
attributeValue.value = false;
emit('delete');
};
</script>
<template>
<div
class="flex items-center w-full gap-2"
:class="{
'justify-start': isEditingView,
'justify-end': !isEditingView,
}"
>
<div class="flex items-center w-full gap-2 justify-end">
<Switch v-model="attributeValue" @change="handleChange" />
<Button
v-if="isEditingView"
variant="faded"
color="ruby"
v-if="isEditingView && isAttributePresent"
ghost
ruby
icon="i-lucide-trash"
size="xs"
class="flex-shrink-0 opacity-0 group-hover/attribute:opacity-100 hover:no-underline"
@click="emit('delete')"
xs
class="flex-shrink-0 !size-5 !rounded"
@click="handleDelete"
/>
</div>
</template>
@@ -75,20 +75,15 @@ const handleInputUpdate = async () => {
</script>
<template>
<div
class="flex items-center w-full min-w-0 gap-2"
:class="{
'justify-start': isEditingView,
'justify-end': !isEditingView,
}"
>
<div class="flex items-center w-full min-w-0 gap-1.5 justify-end">
<span
v-if="!isEditingValue"
class="min-w-0 text-sm"
:class="{
'cursor-pointer text-n-slate-11 hover:text-n-slate-12 py-2 select-none font-medium':
!isEditingView,
'text-n-slate-12 truncate': isEditingView,
'text-n-slate-12 truncate': isEditingView && attribute.value,
'text-n-slate-10 truncate': isEditingView && !attribute.value,
}"
@click="toggleEditValue(!isEditingView)"
>
@@ -100,19 +95,20 @@ const handleInputUpdate = async () => {
class="flex items-center gap-1"
>
<Button
variant="faded"
color="slate"
icon="i-lucide-pencil"
size="xs"
class="flex-shrink-0 opacity-0 group-hover/attribute:opacity-100 hover:no-underline"
ghost
slate
icon="i-lucide-pen-line"
xs
class="flex-shrink-0 !size-5 !rounded"
@click="toggleEditValue(true)"
/>
<Button
variant="faded"
color="ruby"
v-if="attribute.value"
ghost
ruby
icon="i-lucide-trash"
size="xs"
class="flex-shrink-0 opacity-0 group-hover/attribute:opacity-100 hover:no-underline"
xs
class="flex-shrink-0 !size-5 !rounded"
@click="emit('delete')"
/>
</div>
@@ -125,7 +121,7 @@ const handleInputUpdate = async () => {
<Input
v-model="defaultDateValue"
type="date"
class="w-full [&>p]:absolute [&>p]:mt-0.5 [&>p]:top-8 ltr:[&>p]:left-0 rtl:[&>p]:right-0"
class="w-full [&>p]:absolute [&>p]:mt-1 [&>p]:top-6 ltr:[&>p]:left-0 rtl:[&>p]:right-0"
:message="
hasError
? t('CONTACTS_LAYOUT.SIDEBAR.ATTRIBUTES.VALIDATIONS.INVALID_DATE')
@@ -133,14 +129,15 @@ const handleInputUpdate = async () => {
"
:message-type="hasError ? 'error' : 'info'"
autofocus
custom-input-class="h-8 ltr:rounded-r-none rtl:rounded-l-none"
@keyup.enter="handleInputUpdate"
custom-input-class="h-7 py-1 ltr:rounded-r-none rtl:rounded-l-none"
@enter="handleInputUpdate"
/>
<Button
icon="i-lucide-check"
:color="hasError ? 'ruby' : 'blue'"
size="sm"
class="flex-shrink-0 ltr:rounded-l-none rtl:rounded-r-none"
type="button"
class="flex-shrink-0 ltr:rounded-l-none rtl:rounded-r-none !h-7"
@click="handleInputUpdate"
/>
</div>
@@ -4,6 +4,7 @@ import { useI18n } from 'vue-i18n';
import { useToggle } from '@vueuse/core';
import DropdownMenu from 'dashboard/components-next/dropdown-menu/DropdownMenu.vue';
import Button from 'dashboard/components-next/button/Button.vue';
import Icon from 'dashboard/components-next/icon/Icon.vue';
const props = defineProps({
attribute: {
@@ -40,61 +41,55 @@ const handleAttributeAction = async action => {
</script>
<template>
<div
class="flex items-center w-full min-w-0 gap-2"
:class="{
'justify-start': isEditingView,
'justify-end': !isEditingView,
}"
>
<div class="flex items-center w-full min-w-0 gap-1.5 justify-end">
<div
v-on-clickaway="() => toggleAttributeListDropdown(false)"
class="relative flex items-center"
class="relative flex items-center min-w-0 flex-1 justify-end gap-2 cursor-pointer"
@click="toggleAttributeListDropdown()"
>
<span
class="min-w-0 text-sm"
v-tooltip.top="{
content: attribute.value,
delay: { show: 1000, hide: 0 },
}"
class="min-w-0 text-sm truncate font-420"
:class="{
'cursor-pointer text-n-slate-11 hover:text-n-slate-12 py-2 select-none font-medium':
'text-n-slate-11 hover:text-n-slate-12 py-2 select-none font-medium':
!isEditingView,
'text-n-slate-12 truncate flex-1': isEditingView,
'text-n-slate-12': isEditingView && attribute.value,
'text-n-slate-10': isEditingView && !attribute.value,
}"
@click="toggleAttributeListDropdown(!props.isEditingView)"
>
{{
attribute.value ||
t('CONTACTS_LAYOUT.SIDEBAR.ATTRIBUTES.TRIGGER.SELECT')
}}
</span>
<Icon
:icon="
showAttributeListDropdown
? 'i-lucide-chevron-up'
: 'i-lucide-chevron-down'
"
class="text-n-slate-11 flex-shrink-0 size-4"
/>
<DropdownMenu
v-if="showAttributeListDropdown"
:menu-items="attributeListMenuItems"
show-search
class="w-48 mt-2 top-full"
:class="{
'ltr:right-0 rtl:left-0': !isEditingView,
'ltr:left-0 rtl:right-0': isEditingView,
}"
class="w-48 mt-2 top-full ltr:right-0 rtl:left-0"
@action="handleAttributeAction($event)"
/>
</div>
<div v-if="isEditingView" class="flex items-center gap-1">
<Button
variant="faded"
color="slate"
icon="i-lucide-pencil"
size="xs"
class="flex-shrink-0 opacity-0 group-hover/attribute:opacity-100 hover:no-underline"
@click="toggleAttributeListDropdown()"
/>
<Button
variant="faded"
color="ruby"
icon="i-lucide-trash"
size="xs"
class="flex-shrink-0 opacity-0 group-hover/attribute:opacity-100 hover:no-underline"
@click="emit('delete')"
/>
</div>
<Button
v-if="isEditingView && attribute.value"
ghost
ruby
icon="i-lucide-trash"
xs
class="flex-shrink-0 !size-5 !rounded"
@click="emit('delete')"
/>
</div>
</template>
@@ -114,24 +114,24 @@ const handleInputUpdate = async () => {
</script>
<template>
<div
class="flex items-center w-full min-w-0 gap-2"
:class="{
'justify-start': isEditingView,
'justify-end': !isEditingView,
}"
>
<div class="flex items-center w-full min-w-0 gap-1.5 justify-end">
<span
v-if="!isEditingValue"
class="min-w-0 text-sm"
:class="{
'cursor-pointer text-n-slate-11 hover:text-n-slate-12 py-2 select-none font-medium':
!isEditingView,
'text-n-slate-12 truncate': isEditingView && !isAttributeTypeLink,
'truncate hover:text-n-brand text-n-blue-text':
isEditingView && isAttributeTypeLink,
v-tooltip.top="{
content: !isAttributeTypeLink ? attribute.value : '',
delay: { show: 1000, hide: 0 },
}"
@click="toggleEditValue(!isEditingView)"
class="min-w-0 text-sm cursor-pointer font-420"
:class="{
'text-n-slate-11 hover:text-n-slate-12 py-2 select-none font-medium':
!isEditingView,
'text-n-slate-12 truncate':
isEditingView && !isAttributeTypeLink && attribute.value,
'text-n-slate-10 truncate': isEditingView && !attribute.value,
'truncate hover:text-n-brand text-n-blue-10':
isEditingView && isAttributeTypeLink && attribute.value,
}"
@click="toggleEditValue(true)"
>
<a
v-if="isAttributeTypeLink && attribute.value && isEditingView"
@@ -156,19 +156,20 @@ const handleInputUpdate = async () => {
class="flex items-center gap-1"
>
<Button
variant="faded"
color="slate"
icon="i-lucide-pencil"
size="xs"
class="flex-shrink-0 opacity-0 group-hover/attribute:opacity-100 hover:no-underline"
ghost
slate
icon="i-lucide-pen-line"
xs
class="flex-shrink-0 !size-5 !rounded"
@click="toggleEditValue(true)"
/>
<Button
variant="faded"
color="ruby"
v-if="attribute.value"
ghost
ruby
icon="i-lucide-trash"
size="xs"
class="flex-shrink-0 opacity-0 group-hover/attribute:opacity-100 hover:no-underline"
xs
class="flex-shrink-0 !size-5 !rounded"
@click="emit('delete')"
/>
</div>
@@ -182,18 +183,19 @@ const handleInputUpdate = async () => {
v-model="editedValue"
:placeholder="t('CONTACTS_LAYOUT.SIDEBAR.ATTRIBUTES.TRIGGER.INPUT')"
:type="getInputType"
class="w-full [&>p]:absolute [&>p]:mt-0.5 [&>p]:top-8 ltr:[&>p]:left-0 rtl:[&>p]:right-0"
class="w-full [&>p]:absolute [&>p]:mt-1 [&>p]:top-6 ltr:[&>p]:left-0 rtl:[&>p]:right-0"
autofocus
:message="attributeErrorMessage"
:message-type="hasError ? 'error' : 'info'"
custom-input-class="h-8 ltr:rounded-r-none rtl:rounded-l-none"
@keyup.enter="handleInputUpdate"
custom-input-class="h-7 py-1 ltr:rounded-r-none rtl:rounded-l-none"
@enter="handleInputUpdate"
/>
<Button
icon="i-lucide-check"
:color="hasError ? 'ruby' : 'blue'"
size="sm"
class="flex-shrink-0 ltr:rounded-l-none rtl:rounded-r-none"
type="button"
class="flex-shrink-0 ltr:rounded-l-none rtl:rounded-r-none !h-7"
@click="handleInputUpdate"
/>
</div>