diff --git a/app/javascript/dashboard/components/ChatList.vue b/app/javascript/dashboard/components/ChatList.vue index 348d8572a..60966b476 100644 --- a/app/javascript/dashboard/components/ChatList.vue +++ b/app/javascript/dashboard/components/ChatList.vue @@ -7,79 +7,17 @@ ]" > -
-
-

- {{ pageTitle }} -

- - {{ $t(`CHAT_LIST.CHAT_STATUS_FILTER_ITEMS.${activeStatus}.TEXT`) }} - -
-
-
- - -
-
- - -
- - -
-
+ + + + @@ -180,8 +127,8 @@ import { mapGetters } from 'vuex'; import VirtualList from 'vue-virtual-scroll-list'; +import ChatListHeader from './ChatListHeader.vue'; import ConversationAdvancedFilter from './widgets/conversation/ConversationAdvancedFilter.vue'; -import ConversationBasicFilter from './widgets/conversation/ConversationBasicFilter.vue'; import ChatTypeTabs from './widgets/ChatTypeTabs.vue'; import ConversationItem from './ConversationItem.vue'; import timeMixin from '../mixins/time'; @@ -205,10 +152,15 @@ import { isOnUnattendedView, } from '../store/modules/conversations/helpers/actionHelpers'; import { CONVERSATION_EVENTS } from '../helper/AnalyticsHelper/events'; +import { CMD_SNOOZE_CONVERSATION } from 'dashboard/routes/dashboard/commands/commandBarBusEvents'; +import { findSnoozeTime } from 'dashboard/helper/snoozeHelpers'; +import { getUnixTime } from 'date-fns'; +import CustomSnoozeModal from 'dashboard/components/CustomSnoozeModal.vue'; import IntersectionObserver from './IntersectionObserver.vue'; export default { components: { + ChatListHeader, AddCustomViews, ChatTypeTabs, // eslint-disable-next-line vue/no-unused-components @@ -216,9 +168,9 @@ export default { ConversationAdvancedFilter, DeleteCustomViews, ConversationBulkActions, - ConversationBasicFilter, IntersectionObserver, VirtualList, + CustomSnoozeModal, }, mixins: [ timeMixin, @@ -295,6 +247,7 @@ export default { root: this.$refs.conversationList, rootMargin: '100px 0px 100px 0px', }, + showCustomSnoozeModal: false, itemComponent: ConversationItem, // virtualListExtraProps is to pass the props to the conversationItem component. @@ -329,12 +282,13 @@ export default { campaigns: 'campaigns/getAllCampaigns', labels: 'labels/getLabels', selectedConversations: 'bulkActions/getSelectedConversationIds', + contextMenuChatId: 'getContextMenuChatId', }), hasAppliedFilters() { return this.appliedFilters.length !== 0; }, hasActiveFolders() { - return this.activeFolder && this.foldersId !== 0; + return Boolean(this.activeFolder && this.foldersId !== 0); }, hasAppliedFiltersOrActiveFolders() { return this.hasAppliedFilters || this.hasActiveFolders; @@ -558,6 +512,11 @@ export default { bus.$on('fetch_conversation_stats', () => { this.$store.dispatch('conversationStats/get', this.conversationFilters); }); + + bus.$on(CMD_SNOOZE_CONVERSATION, this.onCmdSnoozeConversation); + }, + beforeDestroy() { + bus.$off(CMD_SNOOZE_CONVERSATION, this.onCmdSnoozeConversation); }, methods: { updateVirtualListProps(key, value) { @@ -1034,6 +993,43 @@ export default { onContextMenuToggle(state) { this.isContextMenuOpen = state; }, + onCmdSnoozeConversation(snoozeType) { + if (snoozeType === wootConstants.SNOOZE_OPTIONS.UNTIL_CUSTOM_TIME) { + this.showCustomSnoozeModal = true; + } else { + this.toggleStatus( + wootConstants.STATUS_TYPE.SNOOZED, + findSnoozeTime(snoozeType) || null + ); + } + }, + chooseSnoozeTime(customSnoozeTime) { + this.showCustomSnoozeModal = false; + if (customSnoozeTime) { + this.toggleStatus( + wootConstants.STATUS_TYPE.SNOOZED, + getUnixTime(customSnoozeTime) + ); + } + }, + toggleStatus(status, snoozedUntil) { + this.$store + .dispatch('toggleStatus', { + conversationId: this.currentChat?.id || this.contextMenuChatId, + status, + snoozedUntil, + }) + .then(() => { + this.$store.dispatch('setContextMenuChatId', null); + this.showAlert(this.$t('CONVERSATION.CHANGE_STATUS')); + }); + }, + hideCustomSnoozeModal() { + // if we select custom snooze and then the custom snooze modal is open + // Then if the custom snooze modal is closed and set the context menu chat id to null + this.$store.dispatch('setContextMenuChatId', null); + this.showCustomSnoozeModal = false; + }, }, }; diff --git a/app/javascript/dashboard/components/ChatListHeader.vue b/app/javascript/dashboard/components/ChatListHeader.vue new file mode 100644 index 000000000..d2df2bea3 --- /dev/null +++ b/app/javascript/dashboard/components/ChatListHeader.vue @@ -0,0 +1,115 @@ + + + diff --git a/app/javascript/dashboard/components/buttons/ResolveAction.vue b/app/javascript/dashboard/components/buttons/ResolveAction.vue index 61375dc21..c56f27e14 100644 --- a/app/javascript/dashboard/components/buttons/ResolveAction.vue +++ b/app/javascript/dashboard/components/buttons/ResolveAction.vue @@ -73,25 +73,13 @@ - - - diff --git a/app/javascript/dashboard/routes/dashboard/commands/conversationHotKeys.js b/app/javascript/dashboard/routes/dashboard/commands/conversationHotKeys.js index 5d0b1960d..aaa73c5d4 100644 --- a/app/javascript/dashboard/routes/dashboard/commands/conversationHotKeys.js +++ b/app/javascript/dashboard/routes/dashboard/commands/conversationHotKeys.js @@ -55,11 +55,15 @@ export default { replyMode() { this.setCommandbarData(); }, + contextMenuChatId() { + this.setCommandbarData(); + }, }, computed: { ...mapGetters({ currentChat: 'getSelectedChat', replyMode: 'draftMessages/getReplyEditorMode', + contextMenuChatId: 'getContextMenuChatId', }), draftMessage() { return this.$store.getters['draftMessages/get'](this.draftKey); @@ -93,6 +97,7 @@ export default { } return this.prepareActions(actions); }, + priorityOptions() { return [ { @@ -327,25 +332,42 @@ export default { ]; }, - conversationHotKeys() { - if ( + isConversationOrInboxRoute() { + return ( isAConversationRoute(this.$route.name) || isAInboxViewRoute(this.$route.name) - ) { - const defaultConversationHotKeys = [ - ...this.statusActions, - ...this.conversationAdditionalActions, - ...this.assignAgentActions, - ...this.assignTeamActions, - ...this.labelActions, - ...this.assignPriorityActions, - ]; - if (this.isAIIntegrationEnabled) { - return [...defaultConversationHotKeys, ...this.AIAssistActions]; - } - return defaultConversationHotKeys; - } + ); + }, + shouldShowSnoozeOption() { + return ( + isAConversationRoute(this.$route.name, true, false) && + this.contextMenuChatId + ); + }, + + getDefaultConversationHotKeys() { + const defaultConversationHotKeys = [ + ...this.statusActions, + ...this.conversationAdditionalActions, + ...this.assignAgentActions, + ...this.assignTeamActions, + ...this.labelActions, + ...this.assignPriorityActions, + ]; + if (this.isAIIntegrationEnabled) { + return [...defaultConversationHotKeys, ...this.AIAssistActions]; + } + return defaultConversationHotKeys; + }, + + conversationHotKeys() { + if (this.shouldShowSnoozeOption) { + return this.prepareActions(SNOOZE_CONVERSATION_ACTIONS); + } + if (this.isConversationOrInboxRoute) { + return this.getDefaultConversationHotKeys; + } return []; }, }, diff --git a/app/javascript/dashboard/store/modules/conversations/actions.js b/app/javascript/dashboard/store/modules/conversations/actions.js index 0a9d23c6b..12877078a 100644 --- a/app/javascript/dashboard/store/modules/conversations/actions.js +++ b/app/javascript/dashboard/store/modules/conversations/actions.js @@ -466,6 +466,10 @@ const actions = { commit(types.ASSIGN_PRIORITY, { priority, conversationId }); }, + setContextMenuChatId({ commit }, chatId) { + commit(types.SET_CONTEXT_MENU_CHAT_ID, chatId); + }, + ...messageReadActions, ...messageTranslateActions, }; diff --git a/app/javascript/dashboard/store/modules/conversations/getters.js b/app/javascript/dashboard/store/modules/conversations/getters.js index 62e9ef4ef..4ba517616 100644 --- a/app/javascript/dashboard/store/modules/conversations/getters.js +++ b/app/javascript/dashboard/store/modules/conversations/getters.js @@ -100,6 +100,10 @@ const getters = { getConversationLastSeen: _state => { return _state.conversationLastSeen; }, + + getContextMenuChatId: _state => { + return _state.contextMenuChatId; + }, }; export default getters; diff --git a/app/javascript/dashboard/store/modules/conversations/index.js b/app/javascript/dashboard/store/modules/conversations/index.js index 6bce92e02..83250fa38 100644 --- a/app/javascript/dashboard/store/modules/conversations/index.js +++ b/app/javascript/dashboard/store/modules/conversations/index.js @@ -15,6 +15,7 @@ const state = { currentInbox: null, selectedChatId: null, appliedFilters: [], + contextMenuChatId: null, conversationParticipants: [], conversationLastSeen: null, syncConversationsMessages: {}, @@ -281,6 +282,10 @@ export const mutations = { ) { _state.syncConversationsMessages[conversationId] = messageId; }, + + [types.SET_CONTEXT_MENU_CHAT_ID](_state, chatId) { + _state.contextMenuChatId = chatId; + }, }; export default { diff --git a/app/javascript/dashboard/store/modules/specs/conversations/actions.spec.js b/app/javascript/dashboard/store/modules/specs/conversations/actions.spec.js index 648ab2a46..ad42d24f7 100644 --- a/app/javascript/dashboard/store/modules/specs/conversations/actions.spec.js +++ b/app/javascript/dashboard/store/modules/specs/conversations/actions.spec.js @@ -652,4 +652,11 @@ describe('#addMentions', () => { ]); }); }); + + describe('#setContextMenuChatId', () => { + it('sets the context menu chat id', () => { + actions.setContextMenuChatId({ commit }, 1); + expect(commit.mock.calls).toEqual([[types.SET_CONTEXT_MENU_CHAT_ID, 1]]); + }); + }); }); diff --git a/app/javascript/dashboard/store/modules/specs/conversations/getters.spec.js b/app/javascript/dashboard/store/modules/specs/conversations/getters.spec.js index 1a2225da6..6126692e3 100644 --- a/app/javascript/dashboard/store/modules/specs/conversations/getters.spec.js +++ b/app/javascript/dashboard/store/modules/specs/conversations/getters.spec.js @@ -272,4 +272,11 @@ describe('#getters', () => { ]); }); }); + + describe('#getContextMenuChatId', () => { + it('returns the context menu chat id', () => { + const state = { contextMenuChatId: 1 }; + expect(getters.getContextMenuChatId(state)).toEqual(1); + }); + }); }); diff --git a/app/javascript/dashboard/store/modules/specs/conversations/mutations.spec.js b/app/javascript/dashboard/store/modules/specs/conversations/mutations.spec.js index 279b36872..93a617ddd 100644 --- a/app/javascript/dashboard/store/modules/specs/conversations/mutations.spec.js +++ b/app/javascript/dashboard/store/modules/specs/conversations/mutations.spec.js @@ -403,4 +403,12 @@ describe('#mutations', () => { expect(state.allConversations[0].attachments).toHaveLength(1); }); }); + + describe('#SET_CONTEXT_MENU_CHAT_ID', () => { + it('sets the context menu chat id', () => { + const state = { contextMenuChatId: 1 }; + mutations[types.SET_CONTEXT_MENU_CHAT_ID](state, 2); + expect(state.contextMenuChatId).toEqual(2); + }); + }); }); diff --git a/app/javascript/dashboard/store/mutation-types.js b/app/javascript/dashboard/store/mutation-types.js index 40db99212..e424d56b8 100644 --- a/app/javascript/dashboard/store/mutation-types.js +++ b/app/javascript/dashboard/store/mutation-types.js @@ -58,6 +58,8 @@ export default { SET_CONVERSATION_CAN_REPLY: 'SET_CONVERSATION_CAN_REPLY', + SET_CONTEXT_MENU_CHAT_ID: 'SET_CONTEXT_MENU_CHAT_ID', + // Inboxes SET_INBOXES_UI_FLAG: 'SET_INBOXES_UI_FLAG', SET_INBOXES: 'SET_INBOXES', diff --git a/package.json b/package.json index 1063ddf16..78fb2fed1 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,7 @@ "markdown-it": "^13.0.2", "markdown-it-link-attributes": "^4.0.1", "md5": "^2.3.0", - "ninja-keys": "^1.2.2", + "@chatwoot/ninja-keys": "1.2.3", "opus-recorder": "^8.0.5", "postcss": "^8.4.31", "postcss-loader": "^4.2.0", diff --git a/yarn.lock b/yarn.lock index 0aefd8413..daecee4b6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3156,6 +3156,15 @@ "@braid/vue-formulate-i18n" "^1.16.0" 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": version "1.0.5" resolved "https://registry.yarnpkg.com/@chatwoot/prosemirror-schema/-/prosemirror-schema-1.0.5.tgz#d6053692beae59d466ac0b04128fa157f59eb176" @@ -15083,15 +15092,6 @@ nice-try@^1.0.4: resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" 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: version "3.0.4" resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d"