chore: Update feedback fixes

This commit is contained in:
iamsivin
2026-01-16 01:21:51 +05:30
parent 8e5e5692fd
commit ed590a0684
27 changed files with 346 additions and 276 deletions
@@ -6,6 +6,10 @@ import LabelItem from 'dashboard/components-next/label/LabelItem.vue';
import AddLabel from 'dashboard/components-next/label/AddLabel.vue';
const props = defineProps({
wrapperRef: {
type: Object,
default: null,
},
contactId: {
type: [String, Number],
default: null,
@@ -117,10 +121,7 @@ const handleLabelHover = labelId => {
</script>
<template>
<div
class="flex flex-wrap items-center gap-2 ltr:mr-10 rtl:ml-10"
@mouseleave="handleMouseLeave"
>
<div class="flex flex-wrap items-center gap-2" @mouseleave="handleMouseLeave">
<LabelItem
v-for="label in savedLabels"
:key="label.id"
@@ -132,6 +133,7 @@ const handleLabelHover = labelId => {
/>
<AddLabel
:label-menu-items="labelMenuItems"
:wrapper-ref="wrapperRef"
@update-label="handleLabelAction"
/>
</div>
@@ -126,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-5 max-w-md border-l border-n-weak bg-n-surface-2"
class="hidden lg:block overflow-y-auto justify-end min-w-52 w-full py-5 max-w-md ltr:border-l rtl:border-r border-n-weak bg-n-surface-2"
>
<slot name="sidebar" />
</div>
@@ -25,6 +25,8 @@ const emit = defineEmits(['goToContactsList']);
const { t } = useI18n();
const store = useStore();
const wrapperRef = ref(null);
const confirmDeleteContactDialogRef = ref(null);
const avatarFile = ref(null);
@@ -123,7 +125,7 @@ const handleAvatarDelete = async () => {
<template>
<div class="flex flex-col items-start gap-6 pb-6 w-full">
<div class="flex flex-col items-start gap-3 w-full">
<div ref="wrapperRef" class="flex flex-col items-start gap-3 w-full">
<Avatar
:src="avatarSrc || ''"
:name="selectedContact?.name || ''"
@@ -173,7 +175,10 @@ const handleAvatarDelete = async () => {
</span>
</div>
</div>
<ContactLabels :contact-id="selectedContact?.id" />
<ContactLabels
:wrapper-ref="wrapperRef"
:contact-id="selectedContact?.id"
/>
</div>
<div class="flex flex-col items-start gap-6 w-full">
<ContactsForm
@@ -116,7 +116,7 @@ const labelsCountText = computed(() => {
});
}
if (!showAllLabels.value && hiddenLabelsCount.value > 0) {
return `+${hiddenLabelsCount.value}`;
return hiddenLabelsCount.value;
}
return '';
});
@@ -181,8 +181,8 @@ const onShowLabels = e => {
xs
slate
:no-animation="disableToggle"
:icon="labelsCountText ? '' : 'i-lucide-chevron-left'"
class="!py-0 !px-1.5 flex-shrink-0 !rounded-md !bg-n-button-color -outline-offset-1"
:icon="labelsCountText ? 'i-lucide-plus' : 'i-lucide-chevron-left'"
class="!py-0 !px-1.5 flex-shrink-0 !rounded-md !bg-n-button-color -outline-offset-1 !gap-0.5 [&>span:first-child]:!text-n-slate-10 [&>span:last-child]:!text-n-slate-11"
:class="{ 'cursor-default': disableToggle }"
@click="onShowLabels"
/>
@@ -48,11 +48,6 @@ const showSection = computed(() => {
>
<div class="flex-1 min-w-0 flex items-center gap-0.5">
<InboxName v-if="showInboxName" :inbox="inbox" class="min-w-0" />
<div
v-if="showInboxName && hasSlaPolicyId"
class="rounded-lg h-3 w-px bg-n-strong mx-1"
/>
<SLACardLabel v-if="hasSlaPolicyId" ref="slaCardLabel" :chat="chat" />
</div>
<div class="flex items-center gap-1.5 flex-shrink-0">
<CardPriorityIcon
@@ -1,11 +1,12 @@
<script setup>
import { computed } from 'vue';
import { computed, useTemplateRef } from 'vue';
import { getLastMessage } from 'dashboard/helper/conversationHelper';
import CardMetaSection from './CardMetaSection.vue';
import CardAvatar from './CardAvatar.vue';
import CardHeader from './CardHeader.vue';
import CardContent from './CardContent.vue';
import CardLabels from './CardLabels.vue';
import SLACardLabel from 'dashboard/components-next/Conversation/Sla/SLACardLabel.vue';
const props = defineProps({
chat: { type: Object, required: true },
@@ -24,6 +25,8 @@ const props = defineProps({
const emit = defineEmits(['selectConversation', 'click', 'contextmenu']);
const slaCardLabel = useTemplateRef('slaCardLabel');
const lastMessageInChat = computed(() => getLastMessage(props.chat));
const showLabelsSection = computed(() => props.chat.labels?.length > 0);
const showExpandedPreview = computed(
@@ -37,6 +40,10 @@ const voiceCallData = computed(() => ({
const unreadCount = computed(() => props.chat?.unread_count);
const hasSlaPolicyId = computed(
() => props.chat?.sla_policy_id || slaCardLabel.value?.hasSlaThreshold
);
const onSelectConversation = checked => {
emit('selectConversation', checked);
};
@@ -103,8 +110,9 @@ const onSelectConversation = checked => {
v-if="showLabelsSection || isInboxView"
:labels="chat.labels"
>
<template v-if="isInboxView" #before>
<template #before>
<CardMetaSection
v-if="isInboxView"
:chat="chat"
:inbox="inbox"
:show-assignee="showAssignee"
@@ -112,6 +120,12 @@ const onSelectConversation = checked => {
:is-labels-empty="chat.labels.length === 0"
inline
/>
<SLACardLabel
v-else-if="hasSlaPolicyId"
ref="slaCardLabel"
data-before-slot
:chat="chat"
/>
</template>
</CardLabels>
</div>
@@ -116,11 +116,23 @@ const selectedModel = computed({
<InboxName v-if="showInboxName" :inbox="inbox" class="min-w-0" />
</div>
<span
class="text-label-small outline outline-1 -outline-offset-1 outline-n-weak px-1.5 rounded-md h-6 text-n-slate-11 min-w-12 flex-shrink-0 inline-flex items-center justify-center truncate"
<div class="w-px h-3 bg-n-slate-6 flex-shrink-0" />
<div
v-tooltip.top="{
content: chat.id,
delay: { show: 500, hide: 0 },
}"
class="h-6 flex items-center gap-1 max-w-20 w-full min-w-0"
>
{{ chat.id }}
</span>
<Icon
icon="i-woot-hash"
class="size-3.5 text-n-slate-10 flex-shrink-0"
/>
<span class="text-label-small text-n-slate-11 truncate">
{{ chat.id }}
</span>
</div>
<CardAvatar
:contact="currentContact"
@@ -5,6 +5,7 @@ import { useCamelCase } from 'dashboard/composables/useTransformKeys';
import SLAPopoverCard from './SLAPopoverCard.vue';
import Icon from 'dashboard/components-next/icon/Icon.vue';
import Label from 'dashboard/components-next/label/Label.vue';
const props = defineProps({
chat: {
@@ -35,9 +36,6 @@ const appliedSLA = computed(() => props.chat?.applied_sla);
const slaEvents = computed(() => props.chat?.sla_events);
const hasSlaThreshold = computed(() => slaStatus.value?.threshold);
const isSlaMissed = computed(() => slaStatus.value?.isSlaMissed);
const slaTextStyles = computed(() =>
isSlaMissed.value ? 'text-n-ruby-11' : 'text-n-amber-11'
);
const conversation = computed(() => useCamelCase(props.chat, { deep: true }));
@@ -83,16 +81,15 @@ defineExpose({
v-bind="$attrs"
class="relative flex items-center cursor-pointer min-w-fit group"
>
<div class="flex items-center w-full truncate gap-1" :class="slaTextStyles">
<Icon
icon="i-lucide-flame"
class="flex-shrink-0 size-3"
:class="slaTextStyles"
/>
<span class="text-label-small" :class="slaTextStyles">
{{ slaStatus.threshold }}
</span>
</div>
<Label
:label="slaStatus.threshold"
:color="isSlaMissed ? 'ruby' : 'amber'"
compact
>
<template #icon>
<Icon icon="i-lucide-flame" class="flex-shrink-0 size-3.5" />
</template>
</Label>
<SLAPopoverCard
v-if="showSlaPopoverCard"
:sla-missed-events="slaEvents"
@@ -1,17 +1,23 @@
<script setup>
import { ref } from 'vue';
import { ref, watch, nextTick } from 'vue';
import { useI18n } from 'vue-i18n';
import { useDropdownPosition } from 'dashboard/composables/useDropdownPosition';
import { useResizeObserver } from '@vueuse/core';
import { vOnClickOutside } from '@vueuse/components';
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({
const props = defineProps({
labelMenuItems: {
type: Array,
default: () => [],
},
wrapperRef: {
type: Object,
default: null,
},
});
const emit = defineEmits(['updateLabel']);
@@ -22,15 +28,54 @@ const showDropdown = ref(false);
const triggerRef = ref(null);
const dropdownRef = ref(null);
const { positionClasses } = useDropdownPosition(
const { position, updatePosition } = useDropdownPosition(
triggerRef,
dropdownRef,
showDropdown
showDropdown,
{ container: props.wrapperRef }
);
const openDropdown = async () => {
showDropdown.value = !showDropdown.value;
if (showDropdown.value) {
await nextTick();
updatePosition();
}
};
const updateLabel = label => {
emit('updateLabel', label);
};
const closeDropdown = () => {
showDropdown.value = false;
};
// Watch labelMenuItems and update position when they change
watch(
() => props.labelMenuItems,
async () => {
if (showDropdown.value) {
await nextTick();
updatePosition();
}
},
{ deep: true }
);
// Watch wrapper container for size changes (hover effects from parent)
useResizeObserver(props.wrapperRef, () => {
if (showDropdown.value) {
updatePosition();
}
});
</script>
<template>
<div class="relative">
<div
v-on-click-outside="[closeDropdown, { ignore: [triggerRef] }]"
class="relative"
>
<Button
ref="triggerRef"
:label="t('LABEL.TAG_BUTTON')"
@@ -39,17 +84,17 @@ const { positionClasses } = useDropdownPosition(
:variant="showDropdown ? 'faded' : 'solid'"
class="font-460 !-outline-offset-1"
icon="i-lucide-plus"
@click="showDropdown = !showDropdown"
@click="openDropdown()"
/>
<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)"
:class="position.class"
:style="position.style"
@action="updateLabel($event)"
>
<template #thumbnail="{ item }">
<div
@@ -47,10 +47,10 @@ const colorClasses = computed(() => COLOR_CLASSES[props.color]);
<template>
<div
:title="labelDescription"
class="gap-1.5 rounded-lg -outline-offset-1 outline outline-1 inline-flex items-center flex-shrink-0"
class="rounded-lg -outline-offset-1 outline outline-1 inline-flex items-center flex-shrink-0"
:class="[
colorClasses,
compact ? 'px-1.5 h-6 gap-1 rounded-md' : 'px-2.5 h-8 rounded-lg',
compact ? 'px-1.5 h-6 gap-1 rounded-md' : 'px-2.5 h-8 gap-1.5 rounded-lg',
]"
>
<span