feat: Update contact list page (#13020)

This commit is contained in:
Sivin Varghese
2025-12-10 19:47:20 +05:30
committed by GitHub
parent ee82a63b35
commit 8d191a70b1
44 changed files with 1266 additions and 379 deletions
@@ -0,0 +1,69 @@
<script setup>
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { useDropdownPosition } from 'dashboard/composables/useDropdownPosition';
import Button from 'dashboard/components-next/button/Button.vue';
import Icon from 'dashboard/components-next/icon/Icon.vue';
import DropdownMenu from 'dashboard/components-next/dropdown-menu/DropdownMenu.vue';
defineProps({
labelMenuItems: {
type: Array,
default: () => [],
},
});
const emit = defineEmits(['updateLabel']);
const { t } = useI18n();
const showDropdown = ref(false);
const triggerRef = ref(null);
const dropdownRef = ref(null);
const { positionClasses } = useDropdownPosition(
triggerRef,
dropdownRef,
showDropdown
);
</script>
<template>
<div class="relative">
<Button
ref="triggerRef"
:label="t('LABEL.TAG_BUTTON')"
sm
slate
:variant="showDropdown ? 'faded' : 'solid'"
class="font-460 !-outline-offset-1"
icon="i-lucide-plus"
@click="showDropdown = !showDropdown"
/>
<DropdownMenu
v-if="showDropdown"
ref="dropdownRef"
v-on-clickaway="() => (showDropdown = false)"
:menu-items="labelMenuItems"
show-search
class="z-[100] w-48 overflow-y-auto max-h-52"
:class="positionClasses"
@action="emit('updateLabel', $event)"
>
<template #thumbnail="{ item }">
<div
class="rounded-sm size-2"
:style="{ backgroundColor: item.thumbnail.color }"
/>
</template>
<template #trailing-icon="{ item }">
<Icon
v-if="item.isSelected"
icon="i-lucide-check"
class="size-4 text-n-blue-11 flex-shrink-0"
/>
</template>
</DropdownMenu>
</div>
</template>
@@ -0,0 +1,33 @@
<script setup>
defineProps({
label: {
type: Object,
required: true,
},
compact: {
type: Boolean,
default: false,
},
});
</script>
<template>
<div
:title="label.description"
class="bg-n-button-color gap-1.5 rounded-lg -outline-offset-1 outline outline-1 outline-n-container inline-flex items-center flex-shrink-0"
:class="compact ? 'px-1.5 h-6 gap-1 rounded-md' : 'px-2.5 h-8 rounded-lg'"
>
<span
class="rounded-sm flex-shrink-0"
:class="compact ? 'size-1.5' : 'size-2'"
:style="{ background: label.color }"
/>
<span
class="text-n-slate-12 whitespace-nowrap"
:class="compact ? 'font-440 text-xs' : 'font-420 text-sm'"
>
{{ label.title }}
</span>
<slot name="action" />
</div>
</template>
@@ -0,0 +1,54 @@
<script setup>
import Label from 'dashboard/components-next/label/Label.vue';
import Button from 'dashboard/components-next/button/Button.vue';
const props = defineProps({
label: {
type: Object,
default: null,
},
isHovered: {
type: Boolean,
default: false,
},
compact: {
type: Boolean,
default: false,
},
});
const emit = defineEmits(['remove', 'hover']);
const handleRemoveLabel = () => {
emit('remove', props.label);
};
const handleMouseEnter = () => {
// Notify parent component when this label is hovered
// Added this to show the remove button with transition when hovering over the label
// This will solve the flickering issue when hovering over the last label item
emit('hover', props.label?.id);
};
</script>
<template>
<Label :label="label" :compact="compact" @mouseenter="handleMouseEnter">
<template #action>
<div
class="w-0 flex relative ltr:left-0.5 rtl:right-0.5 flex-shrink-0 overflow-hidden transition-[width] duration-300 ease-out"
:class="{ 'w-6': isHovered }"
>
<Button
class="transition-opacity duration-200 !h-7 ltr:rounded-r-md rtl:rounded-l-md ltr:rounded-l-none rtl:rounded-r-none w-6 bg-transparent"
:class="{ 'opacity-0': !isHovered, 'opacity-100': isHovered }"
type="button"
slate
xs
faded
icon="i-lucide-x"
@click="handleRemoveLabel"
/>
</div>
</template>
</Label>
</template>
@@ -0,0 +1,27 @@
<script setup>
import AddLabel from '../AddLabel.vue';
import { labelMenuItems } from './fixtures';
function onUpdateLabel(label) {
console.log('Label updated:', label);
}
</script>
<template>
<Story title="Components/Label/Add Label">
<Variant title="Default (button with label menu items with active state)">
<div class="h-[300px] p-4">
<AddLabel
:label-menu-items="labelMenuItems"
@update-label="onUpdateLabel"
/>
</div>
</Variant>
<Variant title="Empty List (button with empty label menu)">
<div class="h-[300px] p-4">
<AddLabel :label-menu-items="[]" @update-label="onUpdateLabel" />
</div>
</Variant>
</Story>
</template>
@@ -0,0 +1,21 @@
<script setup>
import Label from '../LabelItem.vue';
import { label } from './fixtures';
</script>
<template>
<Story title="Components/Label/Label item">
<Variant title="Default">
<Label :label="label" />
</Variant>
<Variant title="Custom Label">
<Label
:label="{
title: 'Custom Label',
color: '#FF5733',
}"
/>
</Variant>
</Story>
</template>
@@ -0,0 +1,62 @@
export const label = {
id: 1,
title: 'delivery',
color: '#A2FDD5',
};
export const labelMenuItems = [
{
label: 'delivery',
value: 3,
thumbnail: {
color: '#A2FDD5',
},
isSelected: true,
action: 'addLabel',
},
{
label: 'lead',
value: 6,
thumbnail: {
color: '#F161C8',
},
isSelected: false,
action: 'addLabel',
},
{
label: 'ops-handover',
value: 4,
thumbnail: {
color: '#A53326',
},
isSelected: false,
action: 'addLabel',
},
{
label: 'billing',
value: 1,
thumbnail: {
color: '#28AD21',
},
isSelected: false,
action: 'addLabel',
},
{
label: 'premium-customer',
value: 5,
thumbnail: {
color: '#6FD4EF',
},
isSelected: false,
action: 'addLabel',
},
{
label: 'software',
value: 2,
thumbnail: {
color: '#8F6EF2',
},
isSelected: false,
action: 'addLabel',
},
];