Compare commits
46
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
70fd1e27dc | ||
|
|
a2f9cd6e38 | ||
|
|
758c4bd361 | ||
|
|
4a48569ec4 | ||
|
|
20c609a615 | ||
|
|
010998d541 | ||
|
|
4b88ae906d | ||
|
|
c1ce9fe215 | ||
|
|
94b86a1244 | ||
|
|
ac947eedf5 | ||
|
|
6fb3e96bc9 | ||
|
|
72f64836a5 | ||
|
|
33d93310d2 | ||
|
|
9ec4ec94ed | ||
|
|
04e829e639 | ||
|
|
69182f748b | ||
|
|
b6244c870c | ||
|
|
9d063b730b | ||
|
|
4146da5a6d | ||
|
|
b53730bb73 | ||
|
|
75b57d6a0a | ||
|
|
e3cd3283a0 | ||
|
|
554395b6c9 | ||
|
|
8b2f656372 | ||
|
|
b4686ec07a | ||
|
|
4d995b16f0 | ||
|
|
8de4911fb3 | ||
|
|
a870d1c8a2 | ||
|
|
ab0ab9b032 | ||
|
|
ac4ce529bf | ||
|
|
f19474c681 | ||
|
|
7359f10be2 | ||
|
|
778dbaf812 | ||
|
|
faa7a5f585 | ||
|
|
eaddb253a5 | ||
|
|
8d200b8a99 | ||
|
|
34aef51064 | ||
|
|
139b0d8a4c | ||
|
|
440e357c84 | ||
|
|
58959d42f5 | ||
|
|
01264d45c2 | ||
|
|
8548f6e00b | ||
|
|
0763cc477f | ||
|
|
2fc5c6f236 | ||
|
|
5935a4ff6c | ||
|
|
43de19f685 |
@@ -8,7 +8,7 @@
|
|||||||
:chat="currentChat"
|
:chat="currentChat"
|
||||||
:is-inbox-view="isInboxView"
|
:is-inbox-view="isInboxView"
|
||||||
:is-contact-panel-open="isContactPanelOpen"
|
:is-contact-panel-open="isContactPanelOpen"
|
||||||
:show-back-button="isOnExpandedLayout"
|
:show-back-button="isOnExpandedLayout && !isInboxView"
|
||||||
@contact-panel-toggle="onToggleContactPanel"
|
@contact-panel-toggle="onToggleContactPanel"
|
||||||
/>
|
/>
|
||||||
<woot-tabs
|
<woot-tabs
|
||||||
@@ -35,7 +35,10 @@
|
|||||||
:is-contact-panel-open="isContactPanelOpen"
|
:is-contact-panel-open="isContactPanelOpen"
|
||||||
@contact-panel-toggle="onToggleContactPanel"
|
@contact-panel-toggle="onToggleContactPanel"
|
||||||
/>
|
/>
|
||||||
<empty-state v-else :is-on-expanded-layout="isOnExpandedLayout" />
|
<empty-state
|
||||||
|
v-if="!currentChat.id && !isInboxView"
|
||||||
|
:is-on-expanded-layout="isOnExpandedLayout"
|
||||||
|
/>
|
||||||
<div
|
<div
|
||||||
v-show="showContactPanel"
|
v-show="showContactPanel"
|
||||||
class="conversation-sidebar-wrap basis-full sm:basis-[17.5rem] md:basis-[18.75rem] lg:basis-[19.375rem] xl:basis-[20.625rem] 2xl:basis-[25rem] rtl:border-r border-slate-50 dark:border-slate-700 h-auto overflow-auto z-10 flex-shrink-0 flex-grow-0"
|
class="conversation-sidebar-wrap basis-full sm:basis-[17.5rem] md:basis-[18.75rem] lg:basis-[19.375rem] xl:basis-[20.625rem] 2xl:basis-[25rem] rtl:border-r border-slate-50 dark:border-slate-700 h-auto overflow-auto z-10 flex-shrink-0 flex-grow-0"
|
||||||
|
|||||||
@@ -3,11 +3,10 @@
|
|||||||
class="bg-white dark:bg-slate-900 flex justify-between items-center py-2 px-4 border-b border-slate-50 dark:border-slate-800/50 flex-col md:flex-row"
|
class="bg-white dark:bg-slate-900 flex justify-between items-center py-2 px-4 border-b border-slate-50 dark:border-slate-800/50 flex-col md:flex-row"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="flex-1 w-full min-w-0 flex flex-col md:flex-row items-center justify-center"
|
class="flex-1 w-full min-w-0 flex flex-col items-center justify-center gap-4"
|
||||||
|
:class="isInboxView ? 'sm:flex-row' : 'md:flex-row'"
|
||||||
>
|
>
|
||||||
<div
|
<div class="flex justify-start items-center min-w-0 w-fit max-w-full">
|
||||||
class="flex justify-start items-center mr-4 rtl:mr-0 rtl:ml-4 min-w-0 w-[inherit]"
|
|
||||||
>
|
|
||||||
<back-button
|
<back-button
|
||||||
v-if="showBackButton"
|
v-if="showBackButton"
|
||||||
:back-url="backButtonUrl"
|
:back-url="backButtonUrl"
|
||||||
@@ -20,9 +19,11 @@
|
|||||||
:status="currentContact.availability_status"
|
:status="currentContact.availability_status"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
class="items-start flex flex-col ml-2 rtl:ml-0 rtl:mr-2 min-w-0 w-[inherit] overflow-hidden"
|
class="items-start flex flex-col ml-2 rtl:ml-0 rtl:mr-2 min-w-0 w-fit overflow-hidden"
|
||||||
>
|
>
|
||||||
<div class="flex items-center flex-row gap-1 m-0 p-0 w-[inherit]">
|
<div
|
||||||
|
class="flex items-center flex-row gap-1 m-0 p-0 w-fit max-w-full"
|
||||||
|
>
|
||||||
<woot-button
|
<woot-button
|
||||||
variant="link"
|
variant="link"
|
||||||
color-scheme="secondary"
|
color-scheme="secondary"
|
||||||
@@ -66,7 +67,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="header-actions-wrap items-center flex flex-row flex-grow justify-end mt-3 lg:mt-0 rtl:relative rtl:left-6"
|
class="header-actions-wrap items-center flex flex-row flex-grow justify-end mt-3 lg:mt-0"
|
||||||
:class="{ 'justify-end': isContactPanelOpen }"
|
:class="{ 'justify-end': isContactPanelOpen }"
|
||||||
>
|
>
|
||||||
<more-actions :conversation-id="currentChat.id" />
|
<more-actions :conversation-id="currentChat.id" />
|
||||||
@@ -87,7 +88,6 @@ import Thumbnail from '../Thumbnail.vue';
|
|||||||
import wootConstants from 'dashboard/constants/globals';
|
import wootConstants from 'dashboard/constants/globals';
|
||||||
import { conversationListPageURL } from 'dashboard/helper/URLHelper';
|
import { conversationListPageURL } from 'dashboard/helper/URLHelper';
|
||||||
import { snoozedReopenTime } from 'dashboard/helper/snoozeHelpers';
|
import { snoozedReopenTime } from 'dashboard/helper/snoozeHelpers';
|
||||||
import { frontendURL } from 'dashboard/helper/URLHelper';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@@ -128,9 +128,6 @@ export default {
|
|||||||
params: { accountId, inbox_id: inboxId, label, teamId },
|
params: { accountId, inbox_id: inboxId, label, teamId },
|
||||||
name,
|
name,
|
||||||
} = this.$route;
|
} = this.$route;
|
||||||
if (this.isInboxView) {
|
|
||||||
return frontendURL(`accounts/${accountId}/inbox`);
|
|
||||||
}
|
|
||||||
return conversationListPageURL({
|
return conversationListPageURL({
|
||||||
accountId,
|
accountId,
|
||||||
inboxId,
|
inboxId,
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
"TITLE": "Inbox",
|
"TITLE": "Inbox",
|
||||||
"DISPLAY_DROPDOWN": "Display",
|
"DISPLAY_DROPDOWN": "Display",
|
||||||
"LOADING": "Fetching notifications",
|
"LOADING": "Fetching notifications",
|
||||||
"EOF": "All notifications loaded 🎉",
|
|
||||||
"404": "There are no active notifications in this group.",
|
"404": "There are no active notifications in this group.",
|
||||||
"NO_NOTIFICATIONS": "No notifications",
|
"NO_NOTIFICATIONS": "No notifications",
|
||||||
"NOTE": "Notifications from all subscribed inboxes",
|
"NOTE": "Notifications from all subscribed inboxes",
|
||||||
@@ -14,7 +13,8 @@
|
|||||||
},
|
},
|
||||||
"ACTION_HEADER": {
|
"ACTION_HEADER": {
|
||||||
"SNOOZE": "Snooze notification",
|
"SNOOZE": "Snooze notification",
|
||||||
"DELETE": "Delete notification"
|
"DELETE": "Delete notification",
|
||||||
|
"BACK": "Back"
|
||||||
},
|
},
|
||||||
"TYPES": {
|
"TYPES": {
|
||||||
"CONVERSATION_MENTION": "You have been mentioned in a conversation",
|
"CONVERSATION_MENTION": "You have been mentioned in a conversation",
|
||||||
|
|||||||
@@ -18,3 +18,4 @@ export const CMD_AI_ASSIST = 'CMD_AI_ASSIST';
|
|||||||
|
|
||||||
// Inbox Commands (Notifications)
|
// Inbox Commands (Notifications)
|
||||||
export const CMD_SNOOZE_NOTIFICATION = 'CMD_SNOOZE_NOTIFICATION';
|
export const CMD_SNOOZE_NOTIFICATION = 'CMD_SNOOZE_NOTIFICATION';
|
||||||
|
export const CMD_TOGGLE_SNOOZE_NOTIFICATION = 'CMD_TOGGLE_SNOOZE_NOTIFICATION';
|
||||||
|
|||||||
@@ -6,11 +6,12 @@
|
|||||||
hideBreadcrumbs
|
hideBreadcrumbs
|
||||||
:placeholder="placeholder"
|
:placeholder="placeholder"
|
||||||
@selected="onSelected"
|
@selected="onSelected"
|
||||||
|
@closed="onClosed"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import 'ninja-keys';
|
import '@chatwoot/ninja-keys';
|
||||||
import conversationHotKeysMixin from './conversationHotKeys';
|
import conversationHotKeysMixin from './conversationHotKeys';
|
||||||
import inboxHotKeysMixin from './inboxHotKeys';
|
import inboxHotKeysMixin from './inboxHotKeys';
|
||||||
import goToCommandHotKeys from './goToCommandHotKeys';
|
import goToCommandHotKeys from './goToCommandHotKeys';
|
||||||
@@ -32,6 +33,11 @@ export default {
|
|||||||
appearanceHotKeys,
|
appearanceHotKeys,
|
||||||
goToCommandHotKeys,
|
goToCommandHotKeys,
|
||||||
],
|
],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
modalObserver: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
placeholder() {
|
placeholder() {
|
||||||
return this.$t('COMMAND_BAR.SEARCH_PLACEHOLDER');
|
return this.$t('COMMAND_BAR.SEARCH_PLACEHOLDER');
|
||||||
@@ -55,6 +61,10 @@ export default {
|
|||||||
routeName() {
|
routeName() {
|
||||||
this.setCommandbarData();
|
this.setCommandbarData();
|
||||||
},
|
},
|
||||||
|
hotKeys() {
|
||||||
|
// This is to update the command bar data when the hot keys are updated dynamically
|
||||||
|
this.setCommandbarData();
|
||||||
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.setCommandbarData();
|
this.setCommandbarData();
|
||||||
@@ -72,6 +82,9 @@ export default {
|
|||||||
});
|
});
|
||||||
this.setCommandbarData();
|
this.setCommandbarData();
|
||||||
},
|
},
|
||||||
|
onClosed() {
|
||||||
|
this.showSnoozeNotificationItems = false;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import wootConstants from 'dashboard/constants/globals';
|
import wootConstants from 'dashboard/constants/globals';
|
||||||
|
|
||||||
import { CMD_SNOOZE_NOTIFICATION } from './commandBarBusEvents';
|
import {
|
||||||
|
CMD_SNOOZE_NOTIFICATION,
|
||||||
|
CMD_TOGGLE_SNOOZE_NOTIFICATION,
|
||||||
|
} from './commandBarBusEvents';
|
||||||
import { ICON_SNOOZE_NOTIFICATION } from './CommandBarIcons';
|
import { ICON_SNOOZE_NOTIFICATION } from './CommandBarIcons';
|
||||||
|
|
||||||
import { isAInboxViewRoute } from 'dashboard/helper/routeHelpers';
|
import { isAInboxViewRoute } from 'dashboard/helper/routeHelpers';
|
||||||
@@ -61,14 +64,28 @@ const INBOX_SNOOZE_EVENTS = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
export default {
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showSnoozeNotificationItems: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
inboxHotKeys() {
|
inboxHotKeys() {
|
||||||
if (isAInboxViewRoute(this.$route.name)) {
|
if (
|
||||||
|
isAInboxViewRoute(this.$route.name) ||
|
||||||
|
this.showSnoozeNotificationItems
|
||||||
|
) {
|
||||||
return this.prepareActions(INBOX_SNOOZE_EVENTS);
|
return this.prepareActions(INBOX_SNOOZE_EVENTS);
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
mounted() {
|
||||||
|
bus.$on(CMD_TOGGLE_SNOOZE_NOTIFICATION, this.toggleSnoozeOptions);
|
||||||
|
},
|
||||||
|
destroyed() {
|
||||||
|
bus.$off(CMD_TOGGLE_SNOOZE_NOTIFICATION, this.toggleSnoozeOptions);
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
prepareActions(actions) {
|
prepareActions(actions) {
|
||||||
return actions.map(action => ({
|
return actions.map(action => ({
|
||||||
@@ -77,5 +94,9 @@ export default {
|
|||||||
section: this.$t(action.section),
|
section: this.$t(action.section),
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
toggleSnoozeOptions() {
|
||||||
|
// Used to show/hide snooze notification items in cmd bar dynamically
|
||||||
|
this.showSnoozeNotificationItems = true;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,28 +1,9 @@
|
|||||||
/* eslint arrow-body-style: 0 */
|
/* eslint arrow-body-style: 0 */
|
||||||
import { frontendURL } from '../../../helper/URLHelper';
|
import { frontendURL } from '../../../helper/URLHelper';
|
||||||
const ConversationView = () => import('./ConversationView');
|
const ConversationView = () => import('./ConversationView');
|
||||||
const InboxView = () => import('../inbox/InboxView.vue');
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
routes: [
|
routes: [
|
||||||
{
|
|
||||||
path: frontendURL('accounts/:accountId/inbox-view'),
|
|
||||||
name: 'inbox_view',
|
|
||||||
roles: ['administrator', 'agent'],
|
|
||||||
component: InboxView,
|
|
||||||
props: () => {
|
|
||||||
return { inboxId: 0 };
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: frontendURL('accounts/:accountId/inbox-view/:conversation_id'),
|
|
||||||
name: 'inbox_view_conversation',
|
|
||||||
roles: ['administrator', 'agent'],
|
|
||||||
component: InboxView,
|
|
||||||
props: route => {
|
|
||||||
return { inboxId: 0, conversationId: route.params.conversation_id };
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: frontendURL('accounts/:accountId/dashboard'),
|
path: frontendURL('accounts/:accountId/dashboard'),
|
||||||
name: 'home',
|
name: 'home',
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import conversation from './conversation/conversation.routes';
|
|||||||
import { routes as searchRoutes } from '../../modules/search/search.routes';
|
import { routes as searchRoutes } from '../../modules/search/search.routes';
|
||||||
import { routes as contactRoutes } from './contacts/routes';
|
import { routes as contactRoutes } from './contacts/routes';
|
||||||
import { routes as notificationRoutes } from './notifications/routes';
|
import { routes as notificationRoutes } from './notifications/routes';
|
||||||
|
import { routes as inboxRoutes } from './inbox/routes';
|
||||||
import { frontendURL } from '../../helper/URLHelper';
|
import { frontendURL } from '../../helper/URLHelper';
|
||||||
import helpcenterRoutes from './helpcenter/helpcenter.routes';
|
import helpcenterRoutes from './helpcenter/helpcenter.routes';
|
||||||
|
|
||||||
@@ -16,6 +17,7 @@ export default {
|
|||||||
path: frontendURL('accounts/:account_id'),
|
path: frontendURL('accounts/:account_id'),
|
||||||
component: AppContainer,
|
component: AppContainer,
|
||||||
children: [
|
children: [
|
||||||
|
...inboxRoutes,
|
||||||
...conversation.routes,
|
...conversation.routes,
|
||||||
...settings.routes,
|
...settings.routes,
|
||||||
...contactRoutes,
|
...contactRoutes,
|
||||||
|
|||||||
@@ -0,0 +1,153 @@
|
|||||||
|
<template>
|
||||||
|
<div class="flex flex-col h-full w-full md:w-[calc(100%-360px)]">
|
||||||
|
<inbox-item-header
|
||||||
|
:total-length="totalNotifications"
|
||||||
|
:current-index="activeNotificationPosition"
|
||||||
|
:active-notification="activeNotification"
|
||||||
|
@next="onClickNext"
|
||||||
|
@prev="onClickPrev"
|
||||||
|
/>
|
||||||
|
<conversation-box
|
||||||
|
class="h-[calc(100%-56px)]"
|
||||||
|
is-inbox-view
|
||||||
|
:inbox-id="inboxId"
|
||||||
|
:is-contact-panel-open="isContactPanelOpen"
|
||||||
|
:is-on-expanded-layout="false"
|
||||||
|
@contact-panel-toggle="onToggleContactPanel"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapGetters } from 'vuex';
|
||||||
|
import InboxItemHeader from './components/InboxItemHeader.vue';
|
||||||
|
import ConversationBox from 'dashboard/components/widgets/conversation/ConversationBox.vue';
|
||||||
|
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
||||||
|
import { BUS_EVENTS } from 'shared/constants/busEvents';
|
||||||
|
import { INBOX_EVENTS } from 'dashboard/helper/AnalyticsHelper/events';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
InboxItemHeader,
|
||||||
|
ConversationBox,
|
||||||
|
},
|
||||||
|
mixins: [uiSettingsMixin],
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
notifications: 'notifications/getNotifications',
|
||||||
|
activeNotificationById: 'notifications/getActiveNotificationById',
|
||||||
|
currentChat: 'getSelectedChat',
|
||||||
|
conversationById: 'getConversationById',
|
||||||
|
uiFlags: 'notifications/getUIFlags',
|
||||||
|
}),
|
||||||
|
inboxId() {
|
||||||
|
return Number(this.$route.params.inboxId);
|
||||||
|
},
|
||||||
|
notificationId() {
|
||||||
|
return Number(this.$route.params.notification_id);
|
||||||
|
},
|
||||||
|
activeNotification() {
|
||||||
|
return this.activeNotificationById(this.notificationId);
|
||||||
|
},
|
||||||
|
conversationId() {
|
||||||
|
return this.activeNotification?.primary_actor?.id;
|
||||||
|
},
|
||||||
|
totalNotifications() {
|
||||||
|
return this.notifications?.length ?? 0;
|
||||||
|
},
|
||||||
|
activeNotificationPosition() {
|
||||||
|
const notificationIndex = this.notifications.findIndex(
|
||||||
|
n => n.id === this.notificationId
|
||||||
|
);
|
||||||
|
return notificationIndex >= 0 ? notificationIndex + 1 : 0;
|
||||||
|
},
|
||||||
|
isContactPanelOpen() {
|
||||||
|
if (this.currentChat.id) {
|
||||||
|
const { is_contact_sidebar_open: isContactSidebarOpen } =
|
||||||
|
this.uiSettings;
|
||||||
|
return isContactSidebarOpen;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
conversationId: {
|
||||||
|
immediate: true,
|
||||||
|
handler(newVal, oldVal) {
|
||||||
|
if (newVal !== oldVal) {
|
||||||
|
this.fetchConversationById();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.$store.dispatch('agents/get');
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async fetchConversationById() {
|
||||||
|
if (!this.notificationId || !this.conversationId) return;
|
||||||
|
this.$store.dispatch('clearSelectedState');
|
||||||
|
const existingChat = this.findConversation();
|
||||||
|
if (existingChat) {
|
||||||
|
this.setActiveChat(existingChat);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await this.$store.dispatch('getConversation', this.conversationId);
|
||||||
|
this.setActiveChat();
|
||||||
|
},
|
||||||
|
setActiveChat(conversation = null) {
|
||||||
|
const selectedConversation = conversation || this.findConversation();
|
||||||
|
if (!selectedConversation) return;
|
||||||
|
|
||||||
|
this.$store
|
||||||
|
.dispatch('setActiveChat', { data: selectedConversation })
|
||||||
|
.then(() => {
|
||||||
|
bus.$emit(BUS_EVENTS.SCROLL_TO_MESSAGE);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
findConversation() {
|
||||||
|
return this.conversationById(this.conversationId);
|
||||||
|
},
|
||||||
|
navigateToConversation(activeIndex, direction) {
|
||||||
|
const indexOffset = direction === 'next' ? 0 : -2;
|
||||||
|
const targetNotification = this.notifications[activeIndex + indexOffset];
|
||||||
|
if (targetNotification) {
|
||||||
|
const {
|
||||||
|
id,
|
||||||
|
primary_actor_id: primaryActorId,
|
||||||
|
primary_actor_type: primaryActorType,
|
||||||
|
primary_actor: { inbox_id: inboxId, meta: { unreadCount } = {} },
|
||||||
|
notification_type: notificationType,
|
||||||
|
} = targetNotification;
|
||||||
|
|
||||||
|
this.$track(INBOX_EVENTS.OPEN_CONVERSATION_VIA_INBOX, {
|
||||||
|
notificationType,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.$store.dispatch('notifications/read', {
|
||||||
|
id,
|
||||||
|
primaryActorId,
|
||||||
|
primaryActorType,
|
||||||
|
unreadCount,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.$router.push({
|
||||||
|
name: 'inbox_view_conversation',
|
||||||
|
params: { inboxId, notification_id: id },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onClickNext() {
|
||||||
|
this.navigateToConversation(this.activeNotificationPosition, 'next');
|
||||||
|
},
|
||||||
|
onClickPrev() {
|
||||||
|
this.navigateToConversation(this.activeNotificationPosition, 'prev');
|
||||||
|
},
|
||||||
|
onToggleContactPanel() {
|
||||||
|
this.updateUISettings({
|
||||||
|
is_contact_sidebar_open: !this.isContactPanelOpen,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="text-center bg-slate-25 dark:bg-slate-800 justify-center w-full h-full hidden md:flex items-center"
|
||||||
|
>
|
||||||
|
<span v-if="uiFlags.isFetching" class="spinner mt-4 mb-4" />
|
||||||
|
<div v-else class="flex flex-col items-center gap-2">
|
||||||
|
<fluent-icon
|
||||||
|
icon="mail-inbox"
|
||||||
|
size="40"
|
||||||
|
class="text-slate-600 dark:text-slate-400"
|
||||||
|
/>
|
||||||
|
<span class="text-slate-500 text-sm font-medium dark:text-slate-300">
|
||||||
|
{{ $t('INBOX.LIST.NOTE') }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapGetters } from 'vuex';
|
||||||
|
export default {
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
uiFlags: 'notifications/getUIFlags',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -1,9 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="flex flex-col h-full w-full ltr:border-r border-slate-50 dark:border-slate-800/50"
|
class="flex-col h-full w-full md:min-w-[360px] md:max-w-[360px] ltr:border-r border-slate-50 dark:border-slate-800/50"
|
||||||
:class="isOnExpandedLayout ? '' : 'min-w-[360px] max-w-[360px]'"
|
:class="!notificationId ? 'flex' : 'hidden md:flex'"
|
||||||
>
|
>
|
||||||
<inbox-list-header @filter="onFilterChange" />
|
<inbox-list-header
|
||||||
|
:is-context-menu-open="isInboxContextMenuOpen"
|
||||||
|
@filter="onFilterChange"
|
||||||
|
@redirect="redirectToInbox"
|
||||||
|
/>
|
||||||
<div
|
<div
|
||||||
ref="notificationList"
|
ref="notificationList"
|
||||||
class="flex flex-col w-full h-[calc(100%-56px)] overflow-x-hidden overflow-y-auto"
|
class="flex flex-col w-full h-[calc(100%-56px)] overflow-x-hidden overflow-y-auto"
|
||||||
@@ -15,6 +19,9 @@
|
|||||||
@mark-notification-as-read="markNotificationAsRead"
|
@mark-notification-as-read="markNotificationAsRead"
|
||||||
@mark-notification-as-unread="markNotificationAsUnRead"
|
@mark-notification-as-unread="markNotificationAsUnRead"
|
||||||
@delete-notification="deleteNotification"
|
@delete-notification="deleteNotification"
|
||||||
|
@snooze-notification="openSnoozeNotificationModal"
|
||||||
|
@context-menu-open="isInboxContextMenuOpen = true"
|
||||||
|
@context-menu-close="isInboxContextMenuOpen = false"
|
||||||
/>
|
/>
|
||||||
<div v-if="uiFlags.isFetching" class="text-center">
|
<div v-if="uiFlags.isFetching" class="text-center">
|
||||||
<span class="spinner mt-4 mb-4" />
|
<span class="spinner mt-4 mb-4" />
|
||||||
@@ -25,18 +32,21 @@
|
|||||||
>
|
>
|
||||||
{{ $t('INBOX.LIST.NO_NOTIFICATIONS') }}
|
{{ $t('INBOX.LIST.NO_NOTIFICATIONS') }}
|
||||||
</p>
|
</p>
|
||||||
<p
|
|
||||||
v-if="showEndOfListMessage"
|
|
||||||
class="text-center text-slate-400 dark:text-slate-400 p-4"
|
|
||||||
>
|
|
||||||
{{ $t('INBOX.LIST.EOF') }}
|
|
||||||
</p>
|
|
||||||
<intersection-observer
|
<intersection-observer
|
||||||
v-if="!showEndOfList && !uiFlags.isFetching"
|
v-if="!showEndOfList && !uiFlags.isFetching"
|
||||||
:options="infiniteLoaderOptions"
|
:options="infiniteLoaderOptions"
|
||||||
@observed="loadMoreNotifications"
|
@observed="loadMoreNotifications"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<woot-modal
|
||||||
|
:show.sync="showCustomSnoozeModal"
|
||||||
|
:on-close="hideCustomSnoozeModal"
|
||||||
|
>
|
||||||
|
<custom-snooze-modal
|
||||||
|
@close="hideCustomSnoozeModal"
|
||||||
|
@choose-time="scheduleCustomSnooze"
|
||||||
|
/>
|
||||||
|
</woot-modal>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -46,26 +56,25 @@ import wootConstants from 'dashboard/constants/globals';
|
|||||||
import InboxCard from './components/InboxCard.vue';
|
import InboxCard from './components/InboxCard.vue';
|
||||||
import InboxListHeader from './components/InboxListHeader.vue';
|
import InboxListHeader from './components/InboxListHeader.vue';
|
||||||
import { INBOX_EVENTS } from 'dashboard/helper/AnalyticsHelper/events';
|
import { INBOX_EVENTS } from 'dashboard/helper/AnalyticsHelper/events';
|
||||||
|
import {
|
||||||
|
CMD_TOGGLE_SNOOZE_NOTIFICATION,
|
||||||
|
CMD_SNOOZE_NOTIFICATION,
|
||||||
|
} from 'dashboard/routes/dashboard/commands/commandBarBusEvents';
|
||||||
|
import { getUnixTime } from 'date-fns';
|
||||||
|
import { findSnoozeTime } from 'dashboard/helper/snoozeHelpers';
|
||||||
import IntersectionObserver from 'dashboard/components/IntersectionObserver.vue';
|
import IntersectionObserver from 'dashboard/components/IntersectionObserver.vue';
|
||||||
import alertMixin from 'shared/mixins/alertMixin';
|
import alertMixin from 'shared/mixins/alertMixin';
|
||||||
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
||||||
|
import CustomSnoozeModal from 'dashboard/components/CustomSnoozeModal.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
InboxCard,
|
InboxCard,
|
||||||
InboxListHeader,
|
InboxListHeader,
|
||||||
IntersectionObserver,
|
IntersectionObserver,
|
||||||
|
CustomSnoozeModal,
|
||||||
},
|
},
|
||||||
mixins: [alertMixin, uiSettingsMixin],
|
mixins: [alertMixin, uiSettingsMixin],
|
||||||
props: {
|
|
||||||
conversationId: {
|
|
||||||
type: [String, Number],
|
|
||||||
default: 0,
|
|
||||||
},
|
|
||||||
isOnExpandedLayout: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
infiniteLoaderOptions: {
|
infiniteLoaderOptions: {
|
||||||
@@ -76,6 +85,9 @@ export default {
|
|||||||
status: '',
|
status: '',
|
||||||
type: '',
|
type: '',
|
||||||
sortOrder: wootConstants.INBOX_SORT_BY.NEWEST,
|
sortOrder: wootConstants.INBOX_SORT_BY.NEWEST,
|
||||||
|
isInboxContextMenuOpen: false,
|
||||||
|
showCustomSnoozeModal: false,
|
||||||
|
notificationIdToSnooze: null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -85,6 +97,9 @@ export default {
|
|||||||
uiFlags: 'notifications/getUIFlags',
|
uiFlags: 'notifications/getUIFlags',
|
||||||
notification: 'notifications/getFilteredNotifications',
|
notification: 'notifications/getFilteredNotifications',
|
||||||
}),
|
}),
|
||||||
|
notificationId() {
|
||||||
|
return Number(this.$route.params.notification_id);
|
||||||
|
},
|
||||||
inboxFilters() {
|
inboxFilters() {
|
||||||
return {
|
return {
|
||||||
page: this.page,
|
page: this.page,
|
||||||
@@ -102,13 +117,14 @@ export default {
|
|||||||
showEmptyState() {
|
showEmptyState() {
|
||||||
return !this.uiFlags.isFetching && !this.notifications.length;
|
return !this.uiFlags.isFetching && !this.notifications.length;
|
||||||
},
|
},
|
||||||
showEndOfListMessage() {
|
|
||||||
return this.showEndOfList && this.notifications.length;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.setSavedFilter();
|
this.setSavedFilter();
|
||||||
this.fetchNotifications();
|
this.fetchNotifications();
|
||||||
|
bus.$on(CMD_SNOOZE_NOTIFICATION, this.onCmdSnoozeNotification);
|
||||||
|
},
|
||||||
|
destroyed() {
|
||||||
|
bus.$off(CMD_SNOOZE_NOTIFICATION, this.onCmdSnoozeNotification);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
fetchNotifications() {
|
fetchNotifications() {
|
||||||
@@ -119,17 +135,15 @@ export default {
|
|||||||
this.$store.dispatch('notifications/index', filter);
|
this.$store.dispatch('notifications/index', filter);
|
||||||
},
|
},
|
||||||
redirectToInbox() {
|
redirectToInbox() {
|
||||||
if (!this.conversationId) return;
|
if (!this.notificationId) return;
|
||||||
if (this.$route.name === 'inbox_view') return;
|
if (this.$route.name === 'inbox_view') return;
|
||||||
this.$router.push({ name: 'inbox_view' });
|
this.$router.push({ name: 'inbox_view' });
|
||||||
},
|
},
|
||||||
loadMoreNotifications() {
|
loadMoreNotifications() {
|
||||||
if (this.uiFlags.isAllNotificationsLoaded) return;
|
if (this.uiFlags.isAllNotificationsLoaded) return;
|
||||||
this.$store.dispatch('notifications/index', {
|
this.$store.dispatch('notifications/index', {
|
||||||
|
...this.inboxFilters,
|
||||||
page: this.page + 1,
|
page: this.page + 1,
|
||||||
status: this.status,
|
|
||||||
type: this.type,
|
|
||||||
sortOrder: this.sortOrder,
|
|
||||||
});
|
});
|
||||||
this.page += 1;
|
this.page += 1;
|
||||||
},
|
},
|
||||||
@@ -195,6 +209,47 @@ export default {
|
|||||||
this.type = type;
|
this.type = type;
|
||||||
this.sortOrder = sortBy || wootConstants.INBOX_SORT_BY.NEWEST;
|
this.sortOrder = sortBy || wootConstants.INBOX_SORT_BY.NEWEST;
|
||||||
},
|
},
|
||||||
|
openSnoozeNotificationModal(notificationItem) {
|
||||||
|
this.notificationIdToSnooze = notificationItem.id;
|
||||||
|
// There is an bus event to toggle snooze modal, because
|
||||||
|
// We need to show the snooze notification items in the command bar Dynamically
|
||||||
|
// If cmd + k is pressed from inbox view (In list screen) and search snooze notification items then we no need to show,
|
||||||
|
// snooze notification because we don't get notification id.
|
||||||
|
// Only show when the snooze button is clicked from the notification item card context menu
|
||||||
|
if (!this.notificationId) bus.$emit(CMD_TOGGLE_SNOOZE_NOTIFICATION);
|
||||||
|
this.$nextTick(() => {
|
||||||
|
const ninja = document.querySelector('ninja-keys');
|
||||||
|
ninja.open({ parent: 'snooze_notification' });
|
||||||
|
});
|
||||||
|
},
|
||||||
|
hideCustomSnoozeModal() {
|
||||||
|
this.showCustomSnoozeModal = false;
|
||||||
|
},
|
||||||
|
snoozeNotification(snoozedUntil) {
|
||||||
|
this.$store
|
||||||
|
.dispatch('notifications/snooze', {
|
||||||
|
id: this.notificationId || this.notificationIdToSnooze,
|
||||||
|
snoozedUntil,
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
this.showAlert(this.$t('INBOX.ALERTS.SNOOZE'));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onCmdSnoozeNotification(snoozeType) {
|
||||||
|
if (snoozeType === wootConstants.SNOOZE_OPTIONS.UNTIL_CUSTOM_TIME) {
|
||||||
|
this.showCustomSnoozeModal = true;
|
||||||
|
} else {
|
||||||
|
const snoozedUntil = findSnoozeTime(snoozeType) || null;
|
||||||
|
this.snoozeNotification(snoozedUntil);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
scheduleCustomSnooze(customSnoozeTime) {
|
||||||
|
this.showCustomSnoozeModal = false;
|
||||||
|
if (customSnoozeTime) {
|
||||||
|
const snoozedUntil = getUnixTime(customSnoozeTime) || null;
|
||||||
|
this.snoozeNotification(snoozedUntil);
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,144 +1,25 @@
|
|||||||
<template>
|
<template>
|
||||||
<section class="flex w-full h-full bg-white dark:bg-slate-900">
|
<section class="flex w-full h-full bg-white dark:bg-slate-900">
|
||||||
<inbox-list
|
<router-view name="default" />
|
||||||
v-show="showConversationList"
|
<router-view name="detailView" />
|
||||||
:conversation-id="conversationId"
|
|
||||||
:is-on-expanded-layout="isOnExpandedLayout"
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
v-if="showInboxMessageView"
|
|
||||||
class="flex flex-col h-full"
|
|
||||||
:class="isOnExpandedLayout ? 'w-full' : 'w-[calc(100%-360px)]'"
|
|
||||||
>
|
|
||||||
<inbox-item-header
|
|
||||||
:total-length="totalNotifications"
|
|
||||||
:current-index="activeNotificationIndex"
|
|
||||||
:active-notification="activeNotification"
|
|
||||||
@next="onClickNext"
|
|
||||||
@prev="onClickPrev"
|
|
||||||
/>
|
|
||||||
<conversation-box
|
|
||||||
class="h-[calc(100%-56px)]"
|
|
||||||
is-inbox-view
|
|
||||||
:inbox-id="inboxId"
|
|
||||||
:is-contact-panel-open="isContactPanelOpen"
|
|
||||||
:is-on-expanded-layout="isOnExpandedLayout"
|
|
||||||
@contact-panel-toggle="onToggleContactPanel"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-if="!showInboxMessageView && !isOnExpandedLayout"
|
|
||||||
class="text-center bg-slate-25 dark:bg-slate-800 justify-center w-full h-full flex items-center"
|
|
||||||
>
|
|
||||||
<span v-if="uiFlags.isFetching" class="spinner mt-4 mb-4" />
|
|
||||||
<div v-else class="flex flex-col items-center gap-2">
|
|
||||||
<fluent-icon
|
|
||||||
icon="mail-inbox"
|
|
||||||
size="40"
|
|
||||||
class="text-slate-600 dark:text-slate-400"
|
|
||||||
/>
|
|
||||||
<span class="text-slate-500 text-sm font-medium dark:text-slate-300">
|
|
||||||
{{ $t('INBOX.LIST.NOTE') }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import InboxList from './InboxList.vue';
|
|
||||||
import InboxItemHeader from './components/InboxItemHeader.vue';
|
|
||||||
import ConversationBox from 'dashboard/components/widgets/conversation/ConversationBox.vue';
|
|
||||||
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
|
||||||
import wootConstants from 'dashboard/constants/globals';
|
|
||||||
import { FEATURE_FLAGS } from 'dashboard/featureFlags';
|
import { FEATURE_FLAGS } from 'dashboard/featureFlags';
|
||||||
import { BUS_EVENTS } from 'shared/constants/busEvents';
|
|
||||||
import { INBOX_EVENTS } from 'dashboard/helper/AnalyticsHelper/events';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
|
||||||
InboxList,
|
|
||||||
InboxItemHeader,
|
|
||||||
ConversationBox,
|
|
||||||
},
|
|
||||||
mixins: [uiSettingsMixin],
|
|
||||||
props: {
|
|
||||||
inboxId: {
|
|
||||||
type: [String, Number],
|
|
||||||
default: 0,
|
|
||||||
},
|
|
||||||
conversationId: {
|
|
||||||
type: [String, Number],
|
|
||||||
default: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
currentAccountId: 'getCurrentAccountId',
|
currentAccountId: 'getCurrentAccountId',
|
||||||
notifications: 'notifications/getNotifications',
|
|
||||||
currentChat: 'getSelectedChat',
|
|
||||||
allConversation: 'getAllConversations',
|
|
||||||
uiFlags: 'notifications/getUIFlags',
|
|
||||||
}),
|
}),
|
||||||
activeNotification() {
|
|
||||||
return this.notifications.find(
|
|
||||||
n => n.primary_actor.id === Number(this.conversationId)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
isInboxViewEnabled() {
|
isInboxViewEnabled() {
|
||||||
return this.$store.getters['accounts/isFeatureEnabledGlobally'](
|
return this.$store.getters['accounts/isFeatureEnabledGlobally'](
|
||||||
this.currentAccountId,
|
this.currentAccountId,
|
||||||
FEATURE_FLAGS.INBOX_VIEW
|
FEATURE_FLAGS.INBOX_VIEW
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
showConversationList() {
|
|
||||||
return this.isOnExpandedLayout ? !this.conversationId : true;
|
|
||||||
},
|
|
||||||
isFetchingInitialData() {
|
|
||||||
return this.uiFlags.isFetching && !this.notifications.length;
|
|
||||||
},
|
|
||||||
showInboxMessageView() {
|
|
||||||
return (
|
|
||||||
Boolean(this.conversationId) &&
|
|
||||||
Boolean(this.currentChat.id) &&
|
|
||||||
!this.isFetchingInitialData
|
|
||||||
);
|
|
||||||
},
|
|
||||||
totalNotifications() {
|
|
||||||
return this.notifications?.length ?? 0;
|
|
||||||
},
|
|
||||||
activeNotificationIndex() {
|
|
||||||
const conversationId = Number(this.conversationId);
|
|
||||||
const notificationIndex = this.notifications.findIndex(
|
|
||||||
n => n.primary_actor.id === conversationId
|
|
||||||
);
|
|
||||||
return notificationIndex >= 0 ? notificationIndex + 1 : 0;
|
|
||||||
},
|
|
||||||
isOnExpandedLayout() {
|
|
||||||
const {
|
|
||||||
LAYOUT_TYPES: { CONDENSED },
|
|
||||||
} = wootConstants;
|
|
||||||
const { conversation_display_type: conversationDisplayType = CONDENSED } =
|
|
||||||
this.uiSettings;
|
|
||||||
return conversationDisplayType !== CONDENSED;
|
|
||||||
},
|
|
||||||
isContactPanelOpen() {
|
|
||||||
if (this.currentChat.id) {
|
|
||||||
const { is_contact_sidebar_open: isContactSidebarOpen } =
|
|
||||||
this.uiSettings;
|
|
||||||
return isContactSidebarOpen;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
conversationId: {
|
|
||||||
immediate: true,
|
|
||||||
handler() {
|
|
||||||
this.fetchConversationById();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
// Open inbox view if inbox view feature is enabled, else redirect to dashboard
|
// Open inbox view if inbox view feature is enabled, else redirect to dashboard
|
||||||
@@ -148,70 +29,6 @@ export default {
|
|||||||
name: 'home',
|
name: 'home',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.$store.dispatch('agents/get');
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
async fetchConversationById() {
|
|
||||||
if (!this.conversationId) return;
|
|
||||||
const chat = this.findConversation();
|
|
||||||
if (!chat) {
|
|
||||||
await this.$store.dispatch('getConversation', this.conversationId);
|
|
||||||
}
|
|
||||||
this.setActiveChat();
|
|
||||||
},
|
|
||||||
setActiveChat() {
|
|
||||||
const selectedConversation = this.findConversation();
|
|
||||||
if (!selectedConversation) return;
|
|
||||||
this.$store
|
|
||||||
.dispatch('setActiveChat', { data: selectedConversation })
|
|
||||||
.then(() => {
|
|
||||||
bus.$emit(BUS_EVENTS.SCROLL_TO_MESSAGE);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
findConversation() {
|
|
||||||
const conversationId = Number(this.conversationId);
|
|
||||||
return this.allConversation.find(c => c.id === conversationId);
|
|
||||||
},
|
|
||||||
navigateToConversation(activeIndex, direction) {
|
|
||||||
const indexOffset = direction === 'next' ? 0 : -2;
|
|
||||||
const targetNotification = this.notifications[activeIndex + indexOffset];
|
|
||||||
if (targetNotification) {
|
|
||||||
const {
|
|
||||||
id,
|
|
||||||
primary_actor_id: primaryActorId,
|
|
||||||
primary_actor_type: primaryActorType,
|
|
||||||
primary_actor: { id: conversationId, meta: { unreadCount } = {} },
|
|
||||||
notification_type: notificationType,
|
|
||||||
} = targetNotification;
|
|
||||||
|
|
||||||
this.$track(INBOX_EVENTS.OPEN_CONVERSATION_VIA_INBOX, {
|
|
||||||
notificationType,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.$store.dispatch('notifications/read', {
|
|
||||||
id,
|
|
||||||
primaryActorId,
|
|
||||||
primaryActorType,
|
|
||||||
unreadCount,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.$router.push({
|
|
||||||
name: 'inbox_view_conversation',
|
|
||||||
params: { conversation_id: conversationId },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onClickNext() {
|
|
||||||
this.navigateToConversation(this.activeNotificationIndex, 'next');
|
|
||||||
},
|
|
||||||
onClickPrev() {
|
|
||||||
this.navigateToConversation(this.activeNotificationIndex, 'prev');
|
|
||||||
},
|
|
||||||
onToggleContactPanel() {
|
|
||||||
this.updateUISettings({
|
|
||||||
is_contact_sidebar_open: !this.isContactPanelOpen,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ export default {
|
|||||||
return {
|
return {
|
||||||
isContextMenuOpen: false,
|
isContextMenuOpen: false,
|
||||||
contextMenuPosition: { x: null, y: null },
|
contextMenuPosition: { x: null, y: null },
|
||||||
|
activeNotificationId: null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -96,7 +97,9 @@ export default {
|
|||||||
return this.notificationItem?.primary_actor;
|
return this.notificationItem?.primary_actor;
|
||||||
},
|
},
|
||||||
isInboxCardActive() {
|
isInboxCardActive() {
|
||||||
return this.$route.params.conversation_id === this.primaryActor?.id;
|
return (
|
||||||
|
Number(this.activeNotificationId) === Number(this.notificationItem?.id)
|
||||||
|
);
|
||||||
},
|
},
|
||||||
inbox() {
|
inbox() {
|
||||||
return this.$store.getters['inboxes/getInbox'](
|
return this.$store.getters['inboxes/getInbox'](
|
||||||
@@ -109,6 +112,9 @@ export default {
|
|||||||
meta() {
|
meta() {
|
||||||
return this.primaryActor?.meta;
|
return this.primaryActor?.meta;
|
||||||
},
|
},
|
||||||
|
isNotSnoozed() {
|
||||||
|
return !this.notificationItem?.snoozed_until;
|
||||||
|
},
|
||||||
assigneeMeta() {
|
assigneeMeta() {
|
||||||
return this.meta?.assignee;
|
return this.meta?.assignee;
|
||||||
},
|
},
|
||||||
@@ -131,6 +137,12 @@ export default {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if (this.isNotSnoozed) {
|
||||||
|
items.push({
|
||||||
|
key: 'snooze',
|
||||||
|
label: this.$t('INBOX.MENU_ITEM.SNOOZE'),
|
||||||
|
});
|
||||||
|
}
|
||||||
if (!this.isUnread) {
|
if (!this.isUnread) {
|
||||||
items.push({
|
items.push({
|
||||||
key: 'mark_as_unread',
|
key: 'mark_as_unread',
|
||||||
@@ -157,6 +169,14 @@ export default {
|
|||||||
return '';
|
return '';
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
'$route.params.notification_id': {
|
||||||
|
immediate: true,
|
||||||
|
handler(newVal) {
|
||||||
|
this.activeNotificationId = newVal;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
unmounted() {
|
unmounted() {
|
||||||
this.closeContextMenu();
|
this.closeContextMenu();
|
||||||
},
|
},
|
||||||
@@ -166,11 +186,11 @@ export default {
|
|||||||
id,
|
id,
|
||||||
primary_actor_id: primaryActorId,
|
primary_actor_id: primaryActorId,
|
||||||
primary_actor_type: primaryActorType,
|
primary_actor_type: primaryActorType,
|
||||||
primary_actor: { id: conversationId, inbox_id: inboxId },
|
primary_actor: { inbox_id: inboxId },
|
||||||
notification_type: notificationType,
|
notification_type: notificationType,
|
||||||
} = notification;
|
} = notification;
|
||||||
|
|
||||||
if (this.$route.params.conversation_id !== conversationId) {
|
if (this.$route.params.notification_id !== id) {
|
||||||
this.$track(INBOX_EVENTS.OPEN_CONVERSATION_VIA_INBOX, {
|
this.$track(INBOX_EVENTS.OPEN_CONVERSATION_VIA_INBOX, {
|
||||||
notificationType,
|
notificationType,
|
||||||
});
|
});
|
||||||
@@ -184,13 +204,14 @@ export default {
|
|||||||
|
|
||||||
this.$router.push({
|
this.$router.push({
|
||||||
name: 'inbox_view_conversation',
|
name: 'inbox_view_conversation',
|
||||||
params: { inboxId, conversation_id: conversationId },
|
params: { inboxId, notification_id: id },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
closeContextMenu() {
|
closeContextMenu() {
|
||||||
this.isContextMenuOpen = false;
|
this.isContextMenuOpen = false;
|
||||||
this.contextMenuPosition = { x: null, y: null };
|
this.contextMenuPosition = { x: null, y: null };
|
||||||
|
this.$emit('context-menu-close');
|
||||||
},
|
},
|
||||||
openContextMenu(e) {
|
openContextMenu(e) {
|
||||||
this.closeContextMenu();
|
this.closeContextMenu();
|
||||||
@@ -200,6 +221,7 @@ export default {
|
|||||||
y: e.pageY || e.clientY,
|
y: e.pageY || e.clientY,
|
||||||
};
|
};
|
||||||
this.isContextMenuOpen = true;
|
this.isContextMenuOpen = true;
|
||||||
|
this.$emit('context-menu-open');
|
||||||
},
|
},
|
||||||
handleAction(key) {
|
handleAction(key) {
|
||||||
switch (key) {
|
switch (key) {
|
||||||
@@ -212,6 +234,9 @@ export default {
|
|||||||
case 'delete':
|
case 'delete':
|
||||||
this.$emit('delete-notification', this.notificationItem);
|
this.$emit('delete-notification', this.notificationItem);
|
||||||
break;
|
break;
|
||||||
|
case 'snooze':
|
||||||
|
this.$emit('snooze-notification', this.notificationItem);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,6 +2,16 @@
|
|||||||
<div
|
<div
|
||||||
class="flex gap-2 py-2 ltr:pl-4 rtl:pl-2 h-14 ltr:pr-2 rtl:pr-4 rtl:border-r justify-between items-center w-full border-b border-slate-50 dark:border-slate-800/50"
|
class="flex gap-2 py-2 ltr:pl-4 rtl:pl-2 h-14 ltr:pr-2 rtl:pr-4 rtl:border-r justify-between items-center w-full border-b border-slate-50 dark:border-slate-800/50"
|
||||||
>
|
>
|
||||||
|
<woot-button
|
||||||
|
variant="clear link"
|
||||||
|
class="flex md:hidden !pt-1 !pb-1 rounded-md ltr:pr-1 rtl:pl-1 !no-underline"
|
||||||
|
size="medium"
|
||||||
|
color-scheme="primary"
|
||||||
|
icon="chevron-left"
|
||||||
|
@click="onClickGoToInboxList"
|
||||||
|
>
|
||||||
|
{{ $t('INBOX.ACTION_HEADER.BACK') }}
|
||||||
|
</woot-button>
|
||||||
<pagination-button
|
<pagination-button
|
||||||
v-if="totalLength > 1"
|
v-if="totalLength > 1"
|
||||||
:total-length="totalLength"
|
:total-length="totalLength"
|
||||||
@@ -12,10 +22,12 @@
|
|||||||
<div v-else />
|
<div v-else />
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<woot-button
|
<woot-button
|
||||||
|
v-if="!isNotificationAlreadySnoozed"
|
||||||
variant="hollow"
|
variant="hollow"
|
||||||
size="small"
|
size="small"
|
||||||
color-scheme="secondary"
|
color-scheme="secondary"
|
||||||
icon="snooze"
|
icon="snooze"
|
||||||
|
class="[&>span]:hidden md:[&>span]:inline-flex"
|
||||||
@click="openSnoozeNotificationModal"
|
@click="openSnoozeNotificationModal"
|
||||||
>
|
>
|
||||||
{{ $t('INBOX.ACTION_HEADER.SNOOZE') }}
|
{{ $t('INBOX.ACTION_HEADER.SNOOZE') }}
|
||||||
@@ -25,37 +37,23 @@
|
|||||||
size="small"
|
size="small"
|
||||||
color-scheme="secondary"
|
color-scheme="secondary"
|
||||||
variant="hollow"
|
variant="hollow"
|
||||||
|
class="[&>span]:hidden md:[&>span]:inline-flex"
|
||||||
@click="deleteNotification"
|
@click="deleteNotification"
|
||||||
>
|
>
|
||||||
{{ $t('INBOX.ACTION_HEADER.DELETE') }}
|
{{ $t('INBOX.ACTION_HEADER.DELETE') }}
|
||||||
</woot-button>
|
</woot-button>
|
||||||
</div>
|
</div>
|
||||||
<woot-modal
|
|
||||||
:show.sync="showCustomSnoozeModal"
|
|
||||||
:on-close="hideCustomSnoozeModal"
|
|
||||||
>
|
|
||||||
<custom-snooze-modal
|
|
||||||
@close="hideCustomSnoozeModal"
|
|
||||||
@choose-time="scheduleCustomSnooze"
|
|
||||||
/>
|
|
||||||
</woot-modal>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import { getUnixTime } from 'date-fns';
|
|
||||||
import { CMD_SNOOZE_NOTIFICATION } from 'dashboard/routes/dashboard/commands/commandBarBusEvents';
|
|
||||||
import wootConstants from 'dashboard/constants/globals';
|
|
||||||
import { findSnoozeTime } from 'dashboard/helper/snoozeHelpers';
|
|
||||||
import { INBOX_EVENTS } from 'dashboard/helper/AnalyticsHelper/events';
|
import { INBOX_EVENTS } from 'dashboard/helper/AnalyticsHelper/events';
|
||||||
import PaginationButton from './PaginationButton.vue';
|
import PaginationButton from './PaginationButton.vue';
|
||||||
import CustomSnoozeModal from 'dashboard/components/CustomSnoozeModal.vue';
|
|
||||||
import alertMixin from 'shared/mixins/alertMixin';
|
import alertMixin from 'shared/mixins/alertMixin';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
PaginationButton,
|
PaginationButton,
|
||||||
CustomSnoozeModal,
|
|
||||||
},
|
},
|
||||||
mixins: [alertMixin],
|
mixins: [alertMixin],
|
||||||
props: {
|
props: {
|
||||||
@@ -72,55 +70,19 @@ export default {
|
|||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
showCustomSnoozeModal: false,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
meta: 'notifications/getMeta',
|
meta: 'notifications/getMeta',
|
||||||
}),
|
}),
|
||||||
},
|
isNotificationAlreadySnoozed() {
|
||||||
mounted() {
|
return this.activeNotification?.snoozed_until;
|
||||||
bus.$on(CMD_SNOOZE_NOTIFICATION, this.onCmdSnoozeNotification);
|
},
|
||||||
},
|
|
||||||
destroyed() {
|
|
||||||
bus.$off(CMD_SNOOZE_NOTIFICATION, this.onCmdSnoozeNotification);
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
openSnoozeNotificationModal() {
|
openSnoozeNotificationModal() {
|
||||||
const ninja = document.querySelector('ninja-keys');
|
const ninja = document.querySelector('ninja-keys');
|
||||||
ninja.open({ parent: 'snooze_notification' });
|
ninja.open({ parent: 'snooze_notification' });
|
||||||
},
|
},
|
||||||
hideCustomSnoozeModal() {
|
|
||||||
this.showCustomSnoozeModal = false;
|
|
||||||
},
|
|
||||||
snoozeNotification(snoozedUntil) {
|
|
||||||
this.$store
|
|
||||||
.dispatch('notifications/snooze', {
|
|
||||||
id: this.activeNotification?.id,
|
|
||||||
snoozedUntil,
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
this.showAlert(this.$t('INBOX.ALERTS.SNOOZE'));
|
|
||||||
});
|
|
||||||
},
|
|
||||||
onCmdSnoozeNotification(snoozeType) {
|
|
||||||
if (snoozeType === wootConstants.SNOOZE_OPTIONS.UNTIL_CUSTOM_TIME) {
|
|
||||||
this.showCustomSnoozeModal = true;
|
|
||||||
} else {
|
|
||||||
const snoozedUntil = findSnoozeTime(snoozeType) || null;
|
|
||||||
this.snoozeNotification(snoozedUntil);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
scheduleCustomSnooze(customSnoozeTime) {
|
|
||||||
this.showCustomSnoozeModal = false;
|
|
||||||
if (customSnoozeTime) {
|
|
||||||
const snoozedUntil = getUnixTime(customSnoozeTime) || null;
|
|
||||||
this.snoozeNotification(snoozedUntil);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
deleteNotification() {
|
deleteNotification() {
|
||||||
this.$track(INBOX_EVENTS.DELETE_NOTIFICATION);
|
this.$track(INBOX_EVENTS.DELETE_NOTIFICATION);
|
||||||
this.$store
|
this.$store
|
||||||
@@ -140,6 +102,9 @@ export default {
|
|||||||
onClickPrev() {
|
onClickPrev() {
|
||||||
this.$emit('prev');
|
this.$emit('prev');
|
||||||
},
|
},
|
||||||
|
onClickGoToInboxList() {
|
||||||
|
this.$router.push({ name: 'inbox_view' });
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -49,7 +49,7 @@
|
|||||||
<inbox-option-menu
|
<inbox-option-menu
|
||||||
v-if="showInboxOptionMenu"
|
v-if="showInboxOptionMenu"
|
||||||
v-on-clickaway="openInboxOptionsMenu"
|
v-on-clickaway="openInboxOptionsMenu"
|
||||||
class="absolute top-9"
|
class="absolute top-9 ltr:right-0 ltr:md:right-[unset] rtl:left-0 rtl:md:left-[unset]"
|
||||||
@option-click="onInboxOptionMenuClick"
|
@option-click="onInboxOptionMenuClick"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -69,12 +69,29 @@ export default {
|
|||||||
InboxDisplayMenu,
|
InboxDisplayMenu,
|
||||||
},
|
},
|
||||||
mixins: [clickaway, alertMixin],
|
mixins: [clickaway, alertMixin],
|
||||||
|
props: {
|
||||||
|
isContextMenuOpen: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
showInboxDisplayMenu: false,
|
showInboxDisplayMenu: false,
|
||||||
showInboxOptionMenu: false,
|
showInboxOptionMenu: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
isContextMenuOpen: {
|
||||||
|
handler(val) {
|
||||||
|
if (val) {
|
||||||
|
this.showInboxDisplayMenu = false;
|
||||||
|
this.showInboxOptionMenu = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
immediate: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
markAllRead() {
|
markAllRead() {
|
||||||
this.$track(INBOX_EVENTS.MARK_ALL_NOTIFICATIONS_AS_READ);
|
this.$track(INBOX_EVENTS.MARK_ALL_NOTIFICATIONS_AS_READ);
|
||||||
@@ -100,21 +117,19 @@ export default {
|
|||||||
},
|
},
|
||||||
onInboxOptionMenuClick(key) {
|
onInboxOptionMenuClick(key) {
|
||||||
this.showInboxOptionMenu = false;
|
this.showInboxOptionMenu = false;
|
||||||
if (key === 'mark_all_read') {
|
const actions = {
|
||||||
this.markAllRead();
|
mark_all_read: () => this.markAllRead(),
|
||||||
}
|
delete_all: () => this.deleteAll(),
|
||||||
if (key === 'delete_all') {
|
delete_all_read: () => this.deleteAllRead(),
|
||||||
this.deleteAll();
|
};
|
||||||
}
|
const action = actions[key];
|
||||||
if (key === 'delete_all_read') {
|
if (action) action();
|
||||||
this.deleteAllRead();
|
this.$emit('redirect');
|
||||||
}
|
|
||||||
},
|
},
|
||||||
onFilterChange(option) {
|
onFilterChange(option) {
|
||||||
this.$emit('filter', option);
|
this.$emit('filter', option);
|
||||||
this.showInboxDisplayMenu = false;
|
this.showInboxDisplayMenu = false;
|
||||||
if (this.$route.name === 'inbox_view') return;
|
this.$emit('redirect');
|
||||||
this.$router.push({ name: 'inbox_view' });
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-1 whitespace-nowrap">
|
<div class="flex items-center gap-1 whitespace-nowrap">
|
||||||
<span class="text-sm font-medium text-gray-600">
|
<span class="text-sm font-medium text-gray-600 tabular-nums">
|
||||||
{{ totalLength <= 1 ? '1' : currentIndex }}
|
{{ totalLength <= 1 ? '1' : currentIndex }}
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
>
|
>
|
||||||
/
|
/
|
||||||
</span>
|
</span>
|
||||||
<span v-if="totalLength > 1" class="text-sm text-slate-400">
|
<span v-if="totalLength > 1" class="text-sm text-slate-400 tabular-nums">
|
||||||
{{ totalLength }}
|
{{ totalLength }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import { frontendURL } from 'dashboard/helper/URLHelper';
|
||||||
|
const InboxView = () => import('./InboxView.vue');
|
||||||
|
const InboxListView = () => import('./InboxList.vue');
|
||||||
|
const InboxDetailView = () => import('./InboxDetail.vue');
|
||||||
|
const InboxEmptyStateView = () => import('./InboxEmptyState.vue');
|
||||||
|
|
||||||
|
export const routes = [
|
||||||
|
{
|
||||||
|
path: frontendURL('accounts/:accountId/inbox-view'),
|
||||||
|
component: InboxView,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
name: 'inbox_view',
|
||||||
|
components: {
|
||||||
|
default: InboxListView,
|
||||||
|
detailView: InboxEmptyStateView,
|
||||||
|
},
|
||||||
|
roles: ['administrator', 'agent'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: ':notification_id',
|
||||||
|
name: 'inbox_view_conversation',
|
||||||
|
components: {
|
||||||
|
default: InboxListView,
|
||||||
|
detailView: InboxDetailView,
|
||||||
|
},
|
||||||
|
roles: ['administrator', 'agent'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
@@ -11,6 +11,9 @@ export const getters = {
|
|||||||
);
|
);
|
||||||
return sortedNotifications;
|
return sortedNotifications;
|
||||||
},
|
},
|
||||||
|
getActiveNotificationById: $state => id => {
|
||||||
|
return $state.records[id] || {};
|
||||||
|
},
|
||||||
getUIFlags($state) {
|
getUIFlags($state) {
|
||||||
return $state.uiFlags;
|
return $state.uiFlags;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -44,6 +44,16 @@ describe('#getters', () => {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('getActiveNotificationById', () => {
|
||||||
|
const state = {
|
||||||
|
records: {
|
||||||
|
1: { id: 1 },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(getters.getActiveNotificationById(state)(1)).toEqual({ id: 1 });
|
||||||
|
expect(getters.getActiveNotificationById(state)(2)).toEqual({});
|
||||||
|
});
|
||||||
|
|
||||||
it('getUIFlags', () => {
|
it('getUIFlags', () => {
|
||||||
const state = {
|
const state = {
|
||||||
uiFlags: {
|
uiFlags: {
|
||||||
|
|||||||
+1
-1
@@ -67,7 +67,7 @@
|
|||||||
"markdown-it": "^13.0.1",
|
"markdown-it": "^13.0.1",
|
||||||
"markdown-it-link-attributes": "^4.0.1",
|
"markdown-it-link-attributes": "^4.0.1",
|
||||||
"md5": "^2.3.0",
|
"md5": "^2.3.0",
|
||||||
"ninja-keys": "^1.2.2",
|
"@chatwoot/ninja-keys": "1.2.3",
|
||||||
"opus-recorder": "^8.0.5",
|
"opus-recorder": "^8.0.5",
|
||||||
"postcss": "^8.4.31",
|
"postcss": "^8.4.31",
|
||||||
"postcss-loader": "^4.2.0",
|
"postcss-loader": "^4.2.0",
|
||||||
|
|||||||
@@ -3156,6 +3156,15 @@
|
|||||||
"@braid/vue-formulate-i18n" "^1.16.0"
|
"@braid/vue-formulate-i18n" "^1.16.0"
|
||||||
is-plain-object "^3.0.1"
|
is-plain-object "^3.0.1"
|
||||||
|
|
||||||
|
"@chatwoot/ninja-keys@1.2.3":
|
||||||
|
version "1.2.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@chatwoot/ninja-keys/-/ninja-keys-1.2.3.tgz#3c3f2b505f091ef4707fd1da39bb2ec6d12e7824"
|
||||||
|
integrity sha512-xM8d9P5ikDMZm2WbaCTk/TW5HFauylrU3cJ75fq5je6ixKwyhl/0kZbVN/vbbZN4+AUX/OaSIn6IJbtCgIF67g==
|
||||||
|
dependencies:
|
||||||
|
"@material/mwc-icon" "0.25.3"
|
||||||
|
hotkeys-js "3.8.7"
|
||||||
|
lit "2.2.6"
|
||||||
|
|
||||||
"@chatwoot/prosemirror-schema@1.0.5":
|
"@chatwoot/prosemirror-schema@1.0.5":
|
||||||
version "1.0.5"
|
version "1.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/@chatwoot/prosemirror-schema/-/prosemirror-schema-1.0.5.tgz#d6053692beae59d466ac0b04128fa157f59eb176"
|
resolved "https://registry.yarnpkg.com/@chatwoot/prosemirror-schema/-/prosemirror-schema-1.0.5.tgz#d6053692beae59d466ac0b04128fa157f59eb176"
|
||||||
@@ -15077,15 +15086,6 @@ nice-try@^1.0.4:
|
|||||||
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
|
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
|
||||||
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
|
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
|
||||||
|
|
||||||
ninja-keys@^1.2.2:
|
|
||||||
version "1.2.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/ninja-keys/-/ninja-keys-1.2.2.tgz#c1e1ec1a98aee3a977ee77157ac4aa865348be88"
|
|
||||||
integrity sha512-ylo8jzKowi3XBHkgHRjBJaKQkl32WRLr7kRiA0ajiku11vHRDJ2xANtTScR5C7XlDwKEOYvUPesCKacUeeLAYw==
|
|
||||||
dependencies:
|
|
||||||
"@material/mwc-icon" "0.25.3"
|
|
||||||
hotkeys-js "3.8.7"
|
|
||||||
lit "2.2.6"
|
|
||||||
|
|
||||||
no-case@^3.0.4:
|
no-case@^3.0.4:
|
||||||
version "3.0.4"
|
version "3.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d"
|
resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d"
|
||||||
|
|||||||
Reference in New Issue
Block a user