diff --git a/app/javascript/dashboard/components-next/Conversation/ConversationCard/SLACardLabel.vue b/app/javascript/dashboard/components-next/Conversation/ConversationCard/SLACardLabel.vue
index a9db5d99b..ff57d6c93 100644
--- a/app/javascript/dashboard/components-next/Conversation/ConversationCard/SLACardLabel.vue
+++ b/app/javascript/dashboard/components-next/Conversation/ConversationCard/SLACardLabel.vue
@@ -31,13 +31,17 @@ const convertObjectCamelCaseToSnakeCase = object => {
const appliedSLA = computed(() => props.conversation?.appliedSla);
const isSlaMissed = computed(() => slaStatus.value?.isSlaMissed);
+const hasSlaThreshold = computed(() => {
+ return slaStatus.value?.threshold && appliedSLA.value?.id;
+});
+
const slaStatusText = computed(() => {
return slaStatus.value?.type?.toUpperCase();
});
const updateSlaStatus = () => {
slaStatus.value = evaluateSLAStatus({
- appliedSla: convertObjectCamelCaseToSnakeCase(appliedSLA.value),
+ appliedSla: convertObjectCamelCaseToSnakeCase(appliedSLA.value || {}),
chat: props.conversation,
});
};
@@ -61,6 +65,21 @@ onUnmounted(() => {
});
watch(() => props.conversation, updateSlaStatus);
+
+// This expose is to provide context to the parent component, so that it can decided weather
+// a new row has to be added to the conversation card or not
+// SLACardLabel > CardMessagePreviewWithMeta > ConversationCard
+//
+// We need to do this becuase each SLA card has it's own SLA timer
+// and it's just convenient to have this logic in the SLACardLabel component
+// However this is a bit hacky, and we should change this in the future
+//
+// TODO: A better implementation would be to have the timer as a shared composable, just like the provider pattern
+// we use across the next components. Have the calculation be done on the top ConversationCard component
+// and then the value be injected to the SLACardLabel component
+defineExpose({
+ hasSlaThreshold,
+});
diff --git a/app/javascript/dashboard/components-next/Inbox/InboxCard.vue b/app/javascript/dashboard/components-next/Inbox/InboxCard.vue
new file mode 100644
index 000000000..056c4c2d8
--- /dev/null
+++ b/app/javascript/dashboard/components-next/Inbox/InboxCard.vue
@@ -0,0 +1,244 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ snoozedText }}
+
+
+
+
+
+ {{ notificationDetails.text }}
+
+
+
+
+
+
+
+
+
+
+
+ {{ lastActivityAt }}
+
+
+
+
+
+
diff --git a/app/javascript/dashboard/components-next/sidebar/Sidebar.vue b/app/javascript/dashboard/components-next/sidebar/Sidebar.vue
index d5e968f4a..9339348be 100644
--- a/app/javascript/dashboard/components-next/sidebar/Sidebar.vue
+++ b/app/javascript/dashboard/components-next/sidebar/Sidebar.vue
@@ -84,6 +84,7 @@ const menuItems = computed(() => {
label: t('SIDEBAR.INBOX'),
icon: 'i-lucide-inbox',
to: accountScopedRoute('inbox_view'),
+ activeOn: ['inbox_view', 'inbox_view_conversation'],
},
{
name: 'Conversation',
diff --git a/app/javascript/dashboard/components/widgets/BackButton.vue b/app/javascript/dashboard/components/widgets/BackButton.vue
index 5051b2599..9a5557f0b 100644
--- a/app/javascript/dashboard/components/widgets/BackButton.vue
+++ b/app/javascript/dashboard/components/widgets/BackButton.vue
@@ -1,4 +1,5 @@
diff --git a/app/javascript/dashboard/components/widgets/conversation/ConversationHeader.vue b/app/javascript/dashboard/components/widgets/conversation/ConversationHeader.vue
index 031a6c3ea..ae03e6a8b 100644
--- a/app/javascript/dashboard/components/widgets/conversation/ConversationHeader.vue
+++ b/app/javascript/dashboard/components/widgets/conversation/ConversationHeader.vue
@@ -140,7 +140,7 @@ export default {
{
}
return snoozedUntil ? format(date, 'd MMM, h.mmaaa') : null;
};
+
+export const snoozedReopenTimeToTimestamp = snoozedUntil => {
+ return snoozedUntil ? getUnixTime(new Date(snoozedUntil)) : null;
+};
+export const shortenSnoozeTime = snoozedUntil => {
+ if (!snoozedUntil) {
+ return null;
+ }
+ const unitMap = {
+ minutes: 'm',
+ minute: 'm',
+ hours: 'h',
+ hour: 'h',
+ days: 'd',
+ day: 'd',
+ months: 'mo',
+ month: 'mo',
+ years: 'y',
+ year: 'y',
+ };
+ const shortenTime = snoozedUntil
+ .replace(/^in\s+/i, '')
+ .replace(
+ /\s(minute|hour|day|month|year)s?\b/gi,
+ (match, unit) => unitMap[unit.toLowerCase()] || match
+ );
+
+ return shortenTime;
+};
diff --git a/app/javascript/dashboard/helper/specs/snoozeHelpers.spec.js b/app/javascript/dashboard/helper/specs/snoozeHelpers.spec.js
index 27a4d1b63..0fb87eb41 100644
--- a/app/javascript/dashboard/helper/specs/snoozeHelpers.spec.js
+++ b/app/javascript/dashboard/helper/specs/snoozeHelpers.spec.js
@@ -5,6 +5,8 @@ import {
findStartOfNextMonth,
findNextDay,
setHoursToNine,
+ snoozedReopenTimeToTimestamp,
+ shortenSnoozeTime,
} from '../snoozeHelpers';
describe('#Snooze Helpers', () => {
@@ -107,4 +109,45 @@ describe('#Snooze Helpers', () => {
expect(findNextDay(today)).toEqual(nextDay);
});
});
+
+ describe('snoozedReopenTimeToTimestamp', () => {
+ it('should return timestamp if snoozedUntil is not nil', () => {
+ expect(snoozedReopenTimeToTimestamp('2023-06-07T09:00:00.000Z')).toEqual(
+ 1686128400
+ );
+ });
+ it('should return nil if snoozedUntil is nil', () => {
+ expect(snoozedReopenTimeToTimestamp(null)).toEqual(null);
+ });
+ });
+
+ describe('shortenSnoozeTime', () => {
+ it('should return shortened time if snoozedUntil is not nil and day is passed', () => {
+ expect(shortenSnoozeTime('1 day')).toEqual('1d');
+ });
+
+ it('should return shortened time if snoozedUntil is not nil and month is passed', () => {
+ expect(shortenSnoozeTime('1 month')).toEqual('1mo');
+ });
+
+ it('should return shortened time if snoozedUntil is not nil and year is passed', () => {
+ expect(shortenSnoozeTime('1 year')).toEqual('1y');
+ });
+
+ it('should return shortened time if snoozedUntil is not nil and hour is passed', () => {
+ expect(shortenSnoozeTime('1 hour')).toEqual('1h');
+ });
+
+ it('should return shortened time if snoozedUntil is not nil and minutes is passed', () => {
+ expect(shortenSnoozeTime('1 minutes')).toEqual('1m');
+ });
+
+ it('should return shortened time if snoozedUntil is not nil and in is passed', () => {
+ expect(shortenSnoozeTime('in 1 hour')).toEqual('1h');
+ });
+
+ it('should return nil if snoozedUntil is nil', () => {
+ expect(shortenSnoozeTime(null)).toEqual(null);
+ });
+ });
});
diff --git a/app/javascript/dashboard/i18n/locale/en/inbox.json b/app/javascript/dashboard/i18n/locale/en/inbox.json
index 137aac54b..a07bae4af 100644
--- a/app/javascript/dashboard/i18n/locale/en/inbox.json
+++ b/app/javascript/dashboard/i18n/locale/en/inbox.json
@@ -1,7 +1,7 @@
{
"INBOX": {
"LIST": {
- "TITLE": "Inbox",
+ "TITLE": "My Inbox",
"DISPLAY_DROPDOWN": "Display",
"LOADING": "Fetching notifications",
"404": "There are no active notifications in this group.",
@@ -27,6 +27,19 @@
"SLA_MISSED_NEXT_RESPONSE": "SLA target next response missed for conversation",
"SLA_MISSED_RESOLUTION": "SLA target resolution missed for conversation"
},
+ "TYPES_NEXT": {
+ "CONVERSATION_MENTION": "Mentioned",
+ "CONVERSATION_ASSIGNMENT": "Assigned to you",
+ "CONVERSATION_CREATION": "New Conversation",
+ "SLA_MISSED_FIRST_RESPONSE": "SLA breach",
+ "SLA_MISSED_NEXT_RESPONSE": "SLA breach",
+ "SLA_MISSED_RESOLUTION": "SLA breach",
+ "PARTICIPATING_CONVERSATION_NEW_MESSAGE": "New message",
+ "ASSIGNED_CONVERSATION_NEW_MESSAGE": "New message",
+ "SNOOZED_UNTIL": "Snoozed for {time}",
+ "SNOOZED_ENDS": "Snooze ended"
+ },
+ "NO_CONTENT": "No content available",
"MENU_ITEM": {
"MARK_AS_READ": "Mark as read",
"MARK_AS_UNREAD": "Mark as unread",
diff --git a/app/javascript/dashboard/i18n/locale/en/settings.json b/app/javascript/dashboard/i18n/locale/en/settings.json
index 5da12cec8..6ef3a8ad5 100644
--- a/app/javascript/dashboard/i18n/locale/en/settings.json
+++ b/app/javascript/dashboard/i18n/locale/en/settings.json
@@ -254,7 +254,7 @@
"SWITCH": "Switch",
"INBOX_VIEW": "Inbox View",
"CONVERSATIONS": "Conversations",
- "INBOX": "Inbox",
+ "INBOX": "My Inbox",
"ALL_CONVERSATIONS": "All Conversations",
"MENTIONED_CONVERSATIONS": "Mentions",
"PARTICIPATING_CONVERSATIONS": "Participating",
diff --git a/app/javascript/dashboard/routes/dashboard/inbox/InboxEmptyState.vue b/app/javascript/dashboard/routes/dashboard/inbox/InboxEmptyState.vue
index a325ebbe1..2276037ce 100644
--- a/app/javascript/dashboard/routes/dashboard/inbox/InboxEmptyState.vue
+++ b/app/javascript/dashboard/routes/dashboard/inbox/InboxEmptyState.vue
@@ -23,16 +23,16 @@ export default {