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,69 +0,0 @@
<script setup>
import { computed } from 'vue';
import Avatar from 'next/avatar/Avatar.vue';
const props = defineProps({
usersList: {
type: Array,
default: () => [],
},
size: {
type: Number,
default: 24,
},
showMoreThumbnailsCount: {
type: Boolean,
default: false,
},
moreThumbnailsText: {
type: String,
default: '',
},
gap: {
type: String,
default: 'normal',
validator(value) {
// The value must match one of these strings
return ['normal', 'tight'].includes(value);
},
},
});
const gapClass = computed(() => {
if (props.gap === 'tight') {
return 'ltr:[&:not(:first-child)]:-ml-2 rtl:[&:not(:first-child)]:-mr-2';
}
return 'ltr:[&:not(:first-child)]:-ml-1 rtl:[&:not(:first-child)]:-mr-1';
});
const moreThumbnailsClass = computed(() => {
if (props.gap === 'tight') {
return 'ltr:-ml-2 rtl:-mr-2';
}
return 'ltr:-ml-1 rtl:-mr-1';
});
</script>
<template>
<div class="flex">
<Avatar
v-for="user in usersList"
:key="user.id"
v-tooltip="user.name"
:title="user.name"
:src="user.thumbnail"
:name="user.name"
:size="size"
class="[&>span]:outline [&>span]:outline-1 [&>span]:outline-n-background [&>span]:shadow"
:class="gapClass"
rounded-full
/>
<span
v-if="showMoreThumbnailsCount"
class="text-n-slate-11 bg-n-slate-4 outline outline-1 outline-n-background text-xs font-medium rounded-full px-2 inline-flex items-center shadow relative"
:class="moreThumbnailsClass"
>
{{ moreThumbnailsText }}
</span>
</div>
</template>
@@ -92,6 +92,7 @@ onMounted(() => {
:chat="currentChat"
:is-on-expanded-view="isOnExpandedLayout"
:show-back-button="isOnExpandedLayout && !isInboxView"
:class="{ 'border-b border-n-weak !pb-3': !dashboardApps.length }"
/>
<woot-tabs
v-if="dashboardApps.length && currentChat.id"
@@ -12,6 +12,7 @@ import { conversationListPageURL } from 'dashboard/helper/URLHelper';
import { snoozedReopenTime } from 'dashboard/helper/snoozeHelpers';
import { useInbox } from 'dashboard/composables/useInbox';
import { useI18n } from 'vue-i18n';
import { useUISettings } from 'dashboard/composables/useUISettings';
const props = defineProps({
chat: {
@@ -35,6 +36,15 @@ const conversationHeader = ref(null);
const { width } = useElementSize(conversationHeader);
const { isAWebWidgetInbox } = useInbox();
const { uiSettings } = useUISettings();
const isSidepanelOpen = computed(
() =>
uiSettings.value.is_contact_sidebar_open ||
uiSettings.value.is_copilot_panel_open ||
false
);
const currentChat = computed(() => store.getters.getSelectedChat);
const accountId = computed(() => store.getters.getCurrentAccountId);
@@ -91,20 +101,24 @@ const hasSlaPolicyId = computed(() => props.chat?.sla_policy_id);
ref="conversationHeader"
class="flex flex-col items-start gap-x-6 gap-y-2 w-full min-w-0 pt-3 pb-2 ltr:pl-4 rtl:pr-4 ltr:pr-1 rtl:pl-1"
:class="{
'md:flex-row md:items-center': isOnExpandedView,
'lg:flex-row lg:items-center': !isOnExpandedView,
'md:flex-row md:items-center': isOnExpandedView && !isSidepanelOpen,
'lg:flex-row lg:items-center': isOnExpandedView && isSidepanelOpen,
'lg:flex-row lg:items-center': !isOnExpandedView && !isSidepanelOpen,
'xl:flex-row xl:items-center': !isOnExpandedView && isSidepanelOpen,
}"
>
<div
class="flex items-center gap-2 min-w-0 w-full"
:class="{
'md:flex-1 md:basis-0': isOnExpandedView,
'lg:flex-1 lg:basis-0': !isOnExpandedView,
'md:flex-1 md:basis-0': isOnExpandedView && !isSidepanelOpen,
'lg:flex-1 lg:basis-0': isOnExpandedView && isSidepanelOpen,
'lg:flex-1 lg:basis-0': !isOnExpandedView && !isSidepanelOpen,
'xl:flex-1 xl:basis-0': !isOnExpandedView && isSidepanelOpen,
}"
>
<BackButton v-if="showBackButton" :back-url="backButtonUrl" />
<div class="flex items-center gap-1 min-w-0 flex-1">
<div class="flex items-center gap-1 min-w-0 ltr:mr-2 rtl:ml-2">
<span class="text-base font-medium text-n-slate-12 truncate min-w-0">
{{ currentContact.name }}
</span>
@@ -120,8 +134,14 @@ const hasSlaPolicyId = computed(() => props.chat?.sla_policy_id);
<div
class="flex items-center gap-2 min-w-0 w-full"
:class="{
'md:flex-1 md:basis-0 md:justify-end': isOnExpandedView,
'lg:flex-1 lg:basis-0 lg:justify-end': !isOnExpandedView,
'md:flex-1 md:basis-0 md:justify-end':
isOnExpandedView && !isSidepanelOpen,
'lg:flex-1 lg:basis-0 lg:justify-end':
isOnExpandedView && isSidepanelOpen,
'lg:flex-1 lg:basis-0 lg:justify-end':
!isOnExpandedView && !isSidepanelOpen,
'xl:flex-1 xl:basis-0 xl:justify-end':
!isOnExpandedView && isSidepanelOpen,
}"
>
<div v-if="isSnoozed" class="flex items-center gap-1 min-w-0 shrink">
@@ -42,7 +42,7 @@ const closeContactPanel = () => {
<template>
<div
v-on-click-outside="() => closeContactPanel()"
class="bg-n-surface-2 h-full overflow-hidden flex flex-col fixed top-0 z-40 w-full max-w-sm transition-transform duration-300 ease-in-out ltr:right-0 rtl:left-0 md:static md:w-[320px] md:min-w-[320px] ltr:border-l rtl:border-r border-n-weak 2xl:min-w-[360px] 2xl:w-[360px] shadow-lg md:shadow-none"
class="bg-n-surface-2 h-full overflow-hidden flex flex-col fixed top-0 z-40 w-full max-w-sm transition-transform duration-300 ease-in-out ltr:right-0 rtl:left-0 md:static ltr:border-l rtl:border-r border-n-weak md:min-w-[360px] md:w-[360px] shadow-lg md:shadow-none"
:class="[
{
'md:flex': activeTab === 0,
@@ -4,6 +4,7 @@ import { useFunctionGetter } from 'dashboard/composables/store';
import Spinner from 'dashboard/components-next/spinner/Spinner.vue';
import ShopifyAPI from '../../../api/integrations/shopify';
import ShopifyOrderItem from './ShopifyOrderItem.vue';
import SidePanelEmptyState from 'dashboard/routes/dashboard/conversation/SidePanelEmptyState.vue';
const props = defineProps({
contactId: {
@@ -47,7 +48,7 @@ watch(
</script>
<template>
<div class="px-4 py-2 text-n-slate-12">
<div class="py-2 px-3 text-n-slate-12">
<div v-if="!hasSearchableInfo" class="text-center text-n-slate-12">
{{ $t('CONVERSATION_SIDEBAR.SHOPIFY.NO_SHOPIFY_ORDERS') }}
</div>
@@ -57,8 +58,10 @@ watch(
<div v-else-if="error" class="text-center text-n-ruby-12">
{{ error }}
</div>
<div v-else-if="!orders.length" class="text-center text-n-slate-12">
{{ $t('CONVERSATION_SIDEBAR.SHOPIFY.NO_SHOPIFY_ORDERS') }}
<div v-else-if="!orders.length" class="mt-2">
<SidePanelEmptyState
:message="$t('CONVERSATION_SIDEBAR.SHOPIFY.NO_SHOPIFY_ORDERS')"
/>
</div>
<div v-else>
<ShopifyOrderItem
@@ -67,7 +67,7 @@ const handleCancel = () => {
};
onMounted(() => {
store.dispatch('teams/fetch');
store.dispatch('teams/get');
});
</script>
@@ -8,6 +8,7 @@ import CreateOrLinkIssue from './CreateOrLinkIssue.vue';
import LinearIssueItem from './LinearIssueItem.vue';
import NextButton from 'dashboard/components-next/button/Button.vue';
import Spinner from 'dashboard/components-next/spinner/Spinner.vue';
import SidePanelEmptyState from 'dashboard/routes/dashboard/conversation/SidePanelEmptyState.vue';
import { LINEAR_EVENTS } from 'dashboard/helper/AnalyticsHelper/events';
import { parseLinearAPIErrorResponse } from 'dashboard/store/utils/api';
@@ -86,10 +87,10 @@ onMounted(() => {
<template>
<div>
<div class="px-4 pt-3 pb-2">
<div class="pt-3 px-3 pb-2">
<NextButton
ghost
xs
sm
slate
icon="i-lucide-plus"
:label="$t('INTEGRATION_SETTINGS.LINEAR.ADD_OR_LINK_BUTTON')"
@click="openCreateModal"
@@ -100,17 +101,17 @@ onMounted(() => {
<Spinner />
</div>
<div v-else-if="!hasIssues" class="flex justify-center p-4">
<p class="text-sm text-n-slate-11">
{{ $t('INTEGRATION_SETTINGS.LINEAR.NO_LINKED_ISSUES') }}
</p>
<div v-else-if="!hasIssues" class="mt-2">
<SidePanelEmptyState
:message="$t('INTEGRATION_SETTINGS.LINEAR.NO_LINKED_ISSUES')"
/>
</div>
<div v-else class="max-h-[300px] overflow-y-auto">
<div v-else class="max-h-[300px] overflow-y-auto px-2">
<LinearIssueItem
v-for="linkedIssue in linkedIssues"
:key="linkedIssue.id"
class="px-4 pt-3 pb-4 border-b border-n-weak last:border-b-0"
class="px-1 pt-3 pb-4 border-b border-n-weak last:border-b-0"
:linked-issue="linkedIssue"
@unlink-issue="unlinkIssue"
/>
@@ -73,11 +73,11 @@ const variableKey = (item = {}) => {
class="bg-n-solid-1 p-1 rounded-xl overflow-auto absolute w-full z-20 shadow-md left-0 bottom-full max-h-[9.75rem] border border-solid border-n-strong mention--box"
>
<ul class="mb-0 vertical dropdown menu">
<woot-dropdown-item
<li
v-for="(item, index) in items"
:id="`mention-item-${index}`"
:key="item.key"
class="!mb-1"
class="mb-1 list-none"
@mouseover="onHover(index)"
>
<button
@@ -106,7 +106,7 @@ const variableKey = (item = {}) => {
</p>
</slot>
</button>
</woot-dropdown-item>
</li>
</ul>
</div>
</template>