From f6852c33cbb55f15e6b914b0980a53c1c5d7137b Mon Sep 17 00:00:00 2001
From: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
Date: Mon, 1 Dec 2025 10:18:14 +0530
Subject: [PATCH] feat: Redesign conversation card component UI (#12957)
---
.../dashboard/assets/scss/_next-colors.scss | 4 +
.../ContactsSidebar/ContactHistory.vue | 19 +-
.../ConversationCard/CardAvatar.vue | 58 +++
.../ConversationCard/CardContent.vue | 47 ++
.../ConversationCard/CardHeader.vue | 23 +
.../ConversationCard/CardLabels.vue | 206 +++++---
.../ConversationCard/CardMessagePreview.vue | 67 ---
.../CardMessagePreviewWithMeta.vue | 108 ----
.../ConversationCard/CardMetaSection.vue | 108 ++++
.../ConversationCard/CardPriorityIcon.vue | 225 ++-------
.../ConversationCard/CardStatusIcon.vue | 42 ++
.../ConversationCard.story.vue | 477 ------------------
.../ConversationCard/ConversationCard.vue | 306 +++++++----
.../ConversationCardCompact.vue | 120 +++++
.../ConversationCardExpanded.vue | 172 +++++++
.../ConversationCard/MessagePreview.vue | 156 ++++++
.../ConversationCard/UnreadBadge.vue | 28 +
.../ConversationCard}/VoiceCallStatus.vue | 14 +-
.../Conversation}/InboxName.vue | 12 +-
.../Conversation/Sla}/SLACardLabel.vue | 87 +---
.../Conversation/Sla}/SLAEventItem.vue | 0
.../Conversation/Sla}/SLAPopoverCard.vue | 0
.../components-next/Inbox/InboxCard.vue | 3 +-
.../SLACardLabel.vue | 0
.../components-next/avatar/Avatar.vue | 17 +-
.../components-next/checkbox/Checkbox.vue | 8 +-
.../dashboard/components/ChatList.vue | 122 +----
.../dashboard/components/ConversationItem.vue | 91 ++--
.../dashboard/components/ConversationList.vue | 119 +++++
.../components/buttons/ResolveAction.vue | 14 +-
.../dashboard/components/ui/TimeAgo.vue | 188 ++++---
.../widgets/conversation/ConversationCard.vue | 397 ---------------
.../conversation/ConversationHeader.vue | 4 +-
.../widgets/conversation/MessagePreview.vue | 109 ----
.../widgets/conversation/PriorityMark.vue | 54 --
.../widgets/conversation/ReplyToMessage.vue | 12 +-
.../conversation/LabelSuggestion.vue | 4 -
.../conversationCardComponents/CardLabels.vue | 99 ----
.../chatlist/useChatListKeyboardEvents.js | 60 ---
app/javascript/dashboard/constants/globals.js | 1 +
.../dashboard/i18n/locale/en/chatlist.json | 3 +-
.../SearchResultConversationItem.vue | 2 +-
.../conversation/ContactConversations.vue | 91 ++--
.../reports/components/SLA/SLAReportItem.vue | 2 +-
.../reports/components/SLA/SLAViewDetails.vue | 2 +-
app/javascript/shared/constants/messages.js | 9 -
package.json | 4 +-
pnpm-lock.yaml | 66 ++-
tailwind.config.js | 1 +
theme/colors.js | 4 +
theme/icons.js | 63 +++
51 files changed, 1654 insertions(+), 2174 deletions(-)
create mode 100644 app/javascript/dashboard/components-next/Conversation/ConversationCard/CardAvatar.vue
create mode 100644 app/javascript/dashboard/components-next/Conversation/ConversationCard/CardContent.vue
create mode 100644 app/javascript/dashboard/components-next/Conversation/ConversationCard/CardHeader.vue
delete mode 100644 app/javascript/dashboard/components-next/Conversation/ConversationCard/CardMessagePreview.vue
delete mode 100644 app/javascript/dashboard/components-next/Conversation/ConversationCard/CardMessagePreviewWithMeta.vue
create mode 100644 app/javascript/dashboard/components-next/Conversation/ConversationCard/CardMetaSection.vue
create mode 100644 app/javascript/dashboard/components-next/Conversation/ConversationCard/CardStatusIcon.vue
delete mode 100644 app/javascript/dashboard/components-next/Conversation/ConversationCard/ConversationCard.story.vue
create mode 100644 app/javascript/dashboard/components-next/Conversation/ConversationCard/ConversationCardCompact.vue
create mode 100644 app/javascript/dashboard/components-next/Conversation/ConversationCard/ConversationCardExpanded.vue
create mode 100644 app/javascript/dashboard/components-next/Conversation/ConversationCard/MessagePreview.vue
create mode 100644 app/javascript/dashboard/components-next/Conversation/ConversationCard/UnreadBadge.vue
rename app/javascript/dashboard/{components/widgets/conversation => components-next/Conversation/ConversationCard}/VoiceCallStatus.vue (83%)
rename app/javascript/dashboard/{components/widgets => components-next/Conversation}/InboxName.vue (54%)
rename app/javascript/dashboard/{components/widgets/conversation/components => components-next/Conversation/Sla}/SLACardLabel.vue (50%)
rename app/javascript/dashboard/{components/widgets/conversation/components => components-next/Conversation/Sla}/SLAEventItem.vue (100%)
rename app/javascript/dashboard/{components/widgets/conversation/components => components-next/Conversation/Sla}/SLAPopoverCard.vue (100%)
rename app/javascript/dashboard/components-next/{Conversation/ConversationCard => Inbox}/SLACardLabel.vue (100%)
create mode 100644 app/javascript/dashboard/components/ConversationList.vue
delete mode 100644 app/javascript/dashboard/components/widgets/conversation/ConversationCard.vue
delete mode 100644 app/javascript/dashboard/components/widgets/conversation/MessagePreview.vue
delete mode 100644 app/javascript/dashboard/components/widgets/conversation/PriorityMark.vue
delete mode 100644 app/javascript/dashboard/components/widgets/conversation/conversationCardComponents/CardLabels.vue
delete mode 100644 app/javascript/dashboard/composables/chatlist/useChatListKeyboardEvents.js
diff --git a/app/javascript/dashboard/assets/scss/_next-colors.scss b/app/javascript/dashboard/assets/scss/_next-colors.scss
index b6a91c6a5..f1abaea76 100644
--- a/app/javascript/dashboard/assets/scss/_next-colors.scss
+++ b/app/javascript/dashboard/assets/scss/_next-colors.scss
@@ -120,6 +120,8 @@
--card-color: 255 255 255;
--overlay: 0, 0, 0, 0.12;
--overlay-avatar: 255, 255, 255, 0.67;
+ --button-color: 255 255 255;
+ --button-hover-color: 255, 255, 255, 0.2;
--alpha-1: 215, 215, 215, 0.22;
--alpha-2: 196, 197, 198, 0.22;
@@ -248,6 +250,8 @@
--card-color: 28 30 34;
--overlay: 0, 0, 0, 0.4;
--overlay-avatar: 0, 0, 0, 0.05;
+ --button-color: 42 43 51;
+ --button-hover-color: 0, 0, 0, 0.15;
--alpha-1: 35, 36, 42, 0.8;
--alpha-2: 147, 153, 176, 0.12;
diff --git a/app/javascript/dashboard/components-next/Contacts/ContactsSidebar/ContactHistory.vue b/app/javascript/dashboard/components-next/Contacts/ContactsSidebar/ContactHistory.vue
index 3763d198a..d22aa524a 100644
--- a/app/javascript/dashboard/components-next/Contacts/ContactsSidebar/ContactHistory.vue
+++ b/app/javascript/dashboard/components-next/Contacts/ContactsSidebar/ContactHistory.vue
@@ -3,6 +3,7 @@ import { computed } from 'vue';
import { useMapGetter } from 'dashboard/composables/store';
import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n';
+import { useSnakeCase } from 'dashboard/composables/useTransformKeys';
import Spinner from 'dashboard/components-next/spinner/Spinner.vue';
import ConversationCard from 'dashboard/components-next/Conversation/ConversationCard/ConversationCard.vue';
@@ -13,11 +14,6 @@ const route = useRoute();
const conversations = useMapGetter(
'contactConversations/getAllConversationsByContactId'
);
-const contactsById = useMapGetter('contacts/getContactById');
-const stateInbox = useMapGetter('inboxes/getInboxById');
-const accountLabels = useMapGetter('labels/getLabels');
-
-const accountLabelsValue = computed(() => accountLabels.value);
const uiFlags = useMapGetter('contactConversations/getUIFlags');
const isFetching = computed(() => uiFlags.value.isFetching);
@@ -36,16 +32,17 @@ const contactConversations = computed(() =>
diff --git a/app/javascript/dashboard/components-next/Conversation/ConversationCard/CardAvatar.vue b/app/javascript/dashboard/components-next/Conversation/ConversationCard/CardAvatar.vue
new file mode 100644
index 000000000..5fb509dbc
--- /dev/null
+++ b/app/javascript/dashboard/components-next/Conversation/ConversationCard/CardAvatar.vue
@@ -0,0 +1,58 @@
+
+
+
+
+
diff --git a/app/javascript/dashboard/components-next/Conversation/ConversationCard/CardContent.vue b/app/javascript/dashboard/components-next/Conversation/ConversationCard/CardContent.vue
new file mode 100644
index 000000000..755f6fa49
--- /dev/null
+++ b/app/javascript/dashboard/components-next/Conversation/ConversationCard/CardContent.vue
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+ {{ $t(`CHAT_LIST.NO_MESSAGES`) }}
+
+
+
+
+
diff --git a/app/javascript/dashboard/components-next/Conversation/ConversationCard/CardHeader.vue b/app/javascript/dashboard/components-next/Conversation/ConversationCard/CardHeader.vue
new file mode 100644
index 000000000..e39fcff2b
--- /dev/null
+++ b/app/javascript/dashboard/components-next/Conversation/ConversationCard/CardHeader.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+ {{ contactName }}
+
+
+
+
+
diff --git a/app/javascript/dashboard/components-next/Conversation/ConversationCard/CardLabels.vue b/app/javascript/dashboard/components-next/Conversation/ConversationCard/CardLabels.vue
index dfd4115e8..c09098921 100644
--- a/app/javascript/dashboard/components-next/Conversation/ConversationCard/CardLabels.vue
+++ b/app/javascript/dashboard/components-next/Conversation/ConversationCard/CardLabels.vue
@@ -1,95 +1,167 @@
-
-
-
-
- {{ label.title }}
-
-
-
+
+
+
+
+
+ {{ label.title }}
+
+
+
+
diff --git a/app/javascript/dashboard/components-next/Conversation/ConversationCard/CardMessagePreview.vue b/app/javascript/dashboard/components-next/Conversation/ConversationCard/CardMessagePreview.vue
deleted file mode 100644
index c8c397785..000000000
--- a/app/javascript/dashboard/components-next/Conversation/ConversationCard/CardMessagePreview.vue
+++ /dev/null
@@ -1,67 +0,0 @@
-
-
-
-
-
- {{ lastNonActivityMessageContent }}
-
-
-
-
-
- {{ unreadMessagesCount }}
-
-
-
-
-
diff --git a/app/javascript/dashboard/components-next/Conversation/ConversationCard/CardMessagePreviewWithMeta.vue b/app/javascript/dashboard/components-next/Conversation/ConversationCard/CardMessagePreviewWithMeta.vue
deleted file mode 100644
index df2b22b7e..000000000
--- a/app/javascript/dashboard/components-next/Conversation/ConversationCard/CardMessagePreviewWithMeta.vue
+++ /dev/null
@@ -1,108 +0,0 @@
-
-
-
-
-
-
- {{ lastNonActivityMessageContent }}
-
-
-
-
- {{ unreadMessagesCount }}
-
-
-
-
-
-
-
diff --git a/app/javascript/dashboard/components-next/Conversation/ConversationCard/CardMetaSection.vue b/app/javascript/dashboard/components-next/Conversation/ConversationCard/CardMetaSection.vue
new file mode 100644
index 000000000..1e95d3c24
--- /dev/null
+++ b/app/javascript/dashboard/components-next/Conversation/ConversationCard/CardMetaSection.vue
@@ -0,0 +1,108 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/javascript/dashboard/components-next/Conversation/ConversationCard/CardPriorityIcon.vue b/app/javascript/dashboard/components-next/Conversation/ConversationCard/CardPriorityIcon.vue
index d51764ef5..c90c4fb00 100644
--- a/app/javascript/dashboard/components-next/Conversation/ConversationCard/CardPriorityIcon.vue
+++ b/app/javascript/dashboard/components-next/Conversation/ConversationCard/CardPriorityIcon.vue
@@ -1,207 +1,42 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
diff --git a/app/javascript/dashboard/components-next/Conversation/ConversationCard/CardStatusIcon.vue b/app/javascript/dashboard/components-next/Conversation/ConversationCard/CardStatusIcon.vue
new file mode 100644
index 000000000..d08d2e5ea
--- /dev/null
+++ b/app/javascript/dashboard/components-next/Conversation/ConversationCard/CardStatusIcon.vue
@@ -0,0 +1,42 @@
+
+
+
+
+
diff --git a/app/javascript/dashboard/components-next/Conversation/ConversationCard/ConversationCard.story.vue b/app/javascript/dashboard/components-next/Conversation/ConversationCard/ConversationCard.story.vue
deleted file mode 100644
index a71ab24d7..000000000
--- a/app/javascript/dashboard/components-next/Conversation/ConversationCard/ConversationCard.story.vue
+++ /dev/null
@@ -1,477 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/javascript/dashboard/components-next/Conversation/ConversationCard/ConversationCard.vue b/app/javascript/dashboard/components-next/Conversation/ConversationCard/ConversationCard.vue
index f9a2507a1..45bad6f4e 100644
--- a/app/javascript/dashboard/components-next/Conversation/ConversationCard/ConversationCard.vue
+++ b/app/javascript/dashboard/components-next/Conversation/ConversationCard/ConversationCard.vue
@@ -1,133 +1,243 @@
- = 1024px -->
+
+
+
+
+
+
-
-
-
-
- {{ currentContactName }}
-
-
-
-
-
-
-
- {{ lastActivityAt }}
-
-
-
-
-
-
-
+
diff --git a/app/javascript/dashboard/components-next/Conversation/ConversationCard/ConversationCardCompact.vue b/app/javascript/dashboard/components-next/Conversation/ConversationCard/ConversationCardCompact.vue
new file mode 100644
index 000000000..51f2ad544
--- /dev/null
+++ b/app/javascript/dashboard/components-next/Conversation/ConversationCard/ConversationCardCompact.vue
@@ -0,0 +1,120 @@
+
+
+
+
+
diff --git a/app/javascript/dashboard/components-next/Conversation/ConversationCard/ConversationCardExpanded.vue b/app/javascript/dashboard/components-next/Conversation/ConversationCard/ConversationCardExpanded.vue
new file mode 100644
index 000000000..2326703a4
--- /dev/null
+++ b/app/javascript/dashboard/components-next/Conversation/ConversationCard/ConversationCardExpanded.vue
@@ -0,0 +1,172 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ chat.id }}
+
+
+
+
+
+
+
+ {{ currentContact.name }}
+
+
+
+
+
+
+
+
+
diff --git a/app/javascript/dashboard/components-next/Conversation/ConversationCard/MessagePreview.vue b/app/javascript/dashboard/components-next/Conversation/ConversationCard/MessagePreview.vue
new file mode 100644
index 000000000..1220b9364
--- /dev/null
+++ b/app/javascript/dashboard/components-next/Conversation/ConversationCard/MessagePreview.vue
@@ -0,0 +1,156 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t('CHAT_LIST.ATTACHMENTS.image.CONTENT') }}
+
+
+
+ {{ parsedLastMessage }}
+
+
+
+
+
+ {{ $t(attachmentMessageContent) }}
+
+
+
+
+ {{ defaultEmptyMessage || $t('CHAT_LIST.NO_CONTENT') }}
+
+
+
+
diff --git a/app/javascript/dashboard/components-next/Conversation/ConversationCard/UnreadBadge.vue b/app/javascript/dashboard/components-next/Conversation/ConversationCard/UnreadBadge.vue
new file mode 100644
index 000000000..3d16a49b0
--- /dev/null
+++ b/app/javascript/dashboard/components-next/Conversation/ConversationCard/UnreadBadge.vue
@@ -0,0 +1,28 @@
+
+
+
+
+ {{ displayCount }}
+
+
+
diff --git a/app/javascript/dashboard/components/widgets/conversation/VoiceCallStatus.vue b/app/javascript/dashboard/components-next/Conversation/ConversationCard/VoiceCallStatus.vue
similarity index 83%
rename from app/javascript/dashboard/components/widgets/conversation/VoiceCallStatus.vue
rename to app/javascript/dashboard/components-next/Conversation/ConversationCard/VoiceCallStatus.vue
index f5fa54e75..a42c5797e 100644
--- a/app/javascript/dashboard/components/widgets/conversation/VoiceCallStatus.vue
+++ b/app/javascript/dashboard/components-next/Conversation/ConversationCard/VoiceCallStatus.vue
@@ -9,7 +9,6 @@ import Icon from 'dashboard/components-next/icon/Icon.vue';
const props = defineProps({
status: { type: String, default: '' },
direction: { type: String, default: '' },
- messagePreviewClass: { type: [String, Array, Object], default: '' },
});
const LABEL_KEYS = {
@@ -61,16 +60,9 @@ const statusColor = computed(
-
-
-
+
+
+
{{ $t(labelKey) }}
diff --git a/app/javascript/dashboard/components/widgets/InboxName.vue b/app/javascript/dashboard/components-next/Conversation/InboxName.vue
similarity index 54%
rename from app/javascript/dashboard/components/widgets/InboxName.vue
rename to app/javascript/dashboard/components-next/Conversation/InboxName.vue
index 2e89dba17..072811d65 100644
--- a/app/javascript/dashboard/components/widgets/InboxName.vue
+++ b/app/javascript/dashboard/components-next/Conversation/InboxName.vue
@@ -10,12 +10,12 @@ defineProps({
-
-
-
+
+
+
{{ inbox.name }}
diff --git a/app/javascript/dashboard/components/widgets/conversation/components/SLACardLabel.vue b/app/javascript/dashboard/components-next/Conversation/Sla/SLACardLabel.vue
similarity index 50%
rename from app/javascript/dashboard/components/widgets/conversation/components/SLACardLabel.vue
rename to app/javascript/dashboard/components-next/Conversation/Sla/SLACardLabel.vue
index bb6ee48d2..8d9545002 100644
--- a/app/javascript/dashboard/components/widgets/conversation/components/SLACardLabel.vue
+++ b/app/javascript/dashboard/components-next/Conversation/Sla/SLACardLabel.vue
@@ -1,8 +1,10 @@
-
-
-
-
-
- {{ slaStatusText }}
-
-
-
+
+
+
{{ slaStatus.threshold }}
@@ -137,4 +99,5 @@ onUnmounted(() => {
class="start-0 xl:start-auto xl:end-0 top-7 hidden group-hover:flex"
/>
+
diff --git a/app/javascript/dashboard/components/widgets/conversation/components/SLAEventItem.vue b/app/javascript/dashboard/components-next/Conversation/Sla/SLAEventItem.vue
similarity index 100%
rename from app/javascript/dashboard/components/widgets/conversation/components/SLAEventItem.vue
rename to app/javascript/dashboard/components-next/Conversation/Sla/SLAEventItem.vue
diff --git a/app/javascript/dashboard/components/widgets/conversation/components/SLAPopoverCard.vue b/app/javascript/dashboard/components-next/Conversation/Sla/SLAPopoverCard.vue
similarity index 100%
rename from app/javascript/dashboard/components/widgets/conversation/components/SLAPopoverCard.vue
rename to app/javascript/dashboard/components-next/Conversation/Sla/SLAPopoverCard.vue
diff --git a/app/javascript/dashboard/components-next/Inbox/InboxCard.vue b/app/javascript/dashboard/components-next/Inbox/InboxCard.vue
index 958f25048..c7f28274f 100644
--- a/app/javascript/dashboard/components-next/Inbox/InboxCard.vue
+++ b/app/javascript/dashboard/components-next/Inbox/InboxCard.vue
@@ -12,7 +12,7 @@ import { NOTIFICATION_TYPES_MAPPING } from 'dashboard/routes/dashboard/inbox/hel
import Icon from 'dashboard/components-next/icon/Icon.vue';
import Avatar from 'dashboard/components-next/avatar/Avatar.vue';
import CardPriorityIcon from 'dashboard/components-next/Conversation/ConversationCard/CardPriorityIcon.vue';
-import SLACardLabel from 'dashboard/components-next/Conversation/ConversationCard/SLACardLabel.vue';
+import SLACardLabel from 'dashboard/components-next/Inbox/SLACardLabel.vue';
import InboxContextMenu from 'dashboard/routes/dashboard/inbox/components/InboxContextMenu.vue';
const props = defineProps({
@@ -221,7 +221,6 @@ onBeforeMount(contextMenuActions.close);
({
height: `${props.size}px`,
}));
+const borderRadiusClass = computed(() => {
+ if (props.roundedFull) {
+ return 'rounded-full';
+ }
+
+ // Approximates 25% of size
+ if (props.size <= 16) return 'rounded'; // 4px
+ if (props.size <= 24) return 'rounded-md'; // 6px
+ if (props.size <= 32) return 'rounded-lg'; // 8px
+ if (props.size <= 48) return 'rounded-xl'; // 12px
+ return 'rounded-2xl'; // 16px
+});
+
const avatarStyles = computed(() => ({
...containerStyles.value,
backgroundColor:
@@ -184,7 +197,7 @@ watch(
@@ -218,7 +231,7 @@ watch(
role="img"
class="relative inline-flex items-center justify-center object-cover overflow-hidden font-medium"
:class="[
- roundedFull ? 'rounded-full' : 'rounded-xl',
+ borderRadiusClass,
{
'dark:!bg-[var(--dark-bg)] dark:!text-[var(--dark-text)]':
!showDefaultAvatar && (!src || !isImageValid),
diff --git a/app/javascript/dashboard/components-next/checkbox/Checkbox.vue b/app/javascript/dashboard/components-next/checkbox/Checkbox.vue
index 986867c37..1d5100663 100644
--- a/app/javascript/dashboard/components-next/checkbox/Checkbox.vue
+++ b/app/javascript/dashboard/components-next/checkbox/Checkbox.vue
@@ -24,20 +24,20 @@ const handleChange = event => {
-
+