mirror of
https://github.com/lingble/chatwoot.git
synced 2025-11-01 19:48:08 +00:00
feat: Adds support for all snooze option in bulk actions (#9361)
* feat: Add support for bulk snooze until * feat: Adds support for all snooze option in bulk actions * chore: Adds comment * chore: Review fixes * chore: Minor fix * chore: Minor fix * chore: Review fixes * chore: yarn changes * fix: terminal waring * chore: Adds spec * Update conversationHotKeys.js --------- Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
This commit is contained in:
@@ -288,7 +288,6 @@ export default {
|
||||
foldersQuery: {},
|
||||
showAddFoldersModal: false,
|
||||
showDeleteFoldersModal: false,
|
||||
selectedConversations: [],
|
||||
selectedInboxes: [],
|
||||
isContextMenuOpen: false,
|
||||
appliedFilter: [],
|
||||
@@ -329,6 +328,7 @@ export default {
|
||||
inboxesList: 'inboxes/getInboxes',
|
||||
campaigns: 'campaigns/getAllCampaigns',
|
||||
labels: 'labels/getLabels',
|
||||
selectedConversations: 'bulkActions/getSelectedConversationIds',
|
||||
}),
|
||||
hasAppliedFilters() {
|
||||
return this.appliedFilters.length !== 0;
|
||||
@@ -799,7 +799,7 @@ export default {
|
||||
});
|
||||
},
|
||||
resetBulkActions() {
|
||||
this.selectedConversations = [];
|
||||
this.$store.dispatch('bulkActions/clearSelectedConversationIds');
|
||||
this.selectedInboxes = [];
|
||||
},
|
||||
onBasicFilterChange(value, type) {
|
||||
@@ -830,12 +830,16 @@ export default {
|
||||
return this.selectedConversations.includes(id);
|
||||
},
|
||||
selectConversation(conversationId, inboxId) {
|
||||
this.selectedConversations.push(conversationId);
|
||||
this.$store.dispatch(
|
||||
'bulkActions/setSelectedConversationIds',
|
||||
conversationId
|
||||
);
|
||||
this.selectedInboxes.push(inboxId);
|
||||
},
|
||||
deSelectConversation(conversationId, inboxId) {
|
||||
this.selectedConversations = this.selectedConversations.filter(
|
||||
item => item !== conversationId
|
||||
this.$store.dispatch(
|
||||
'bulkActions/removeSelectedConversationIds',
|
||||
conversationId
|
||||
);
|
||||
this.selectedInboxes = this.selectedInboxes.filter(
|
||||
item => item !== inboxId
|
||||
@@ -843,7 +847,10 @@ export default {
|
||||
},
|
||||
selectAllConversations(check) {
|
||||
if (check) {
|
||||
this.selectedConversations = this.conversationList.map(item => item.id);
|
||||
this.$store.dispatch(
|
||||
'bulkActions/setSelectedConversationIds',
|
||||
this.conversationList.map(item => item.id)
|
||||
);
|
||||
this.selectedInboxes = this.conversationList.map(item => item.inbox_id);
|
||||
} else {
|
||||
this.resetBulkActions();
|
||||
@@ -859,7 +866,7 @@ export default {
|
||||
assignee_id: agent.id,
|
||||
},
|
||||
});
|
||||
this.selectedConversations = [];
|
||||
this.$store.dispatch('bulkActions/clearSelectedConversationIds');
|
||||
if (conversationId) {
|
||||
this.showAlert(
|
||||
this.$t(
|
||||
@@ -957,7 +964,7 @@ export default {
|
||||
add: labels,
|
||||
},
|
||||
});
|
||||
this.selectedConversations = [];
|
||||
this.$store.dispatch('bulkActions/clearSelectedConversationIds');
|
||||
if (conversationId) {
|
||||
this.showAlert(
|
||||
this.$t(
|
||||
@@ -984,13 +991,13 @@ export default {
|
||||
team_id: team.id,
|
||||
},
|
||||
});
|
||||
this.selectedConversations = [];
|
||||
this.$store.dispatch('bulkActions/clearSelectedConversationIds');
|
||||
this.showAlert(this.$t('BULK_ACTION.TEAMS.ASSIGN_SUCCESFUL'));
|
||||
} catch (err) {
|
||||
this.showAlert(this.$t('BULK_ACTION.TEAMS.ASSIGN_FAILED'));
|
||||
}
|
||||
},
|
||||
async onUpdateConversations(status) {
|
||||
async onUpdateConversations(status, snoozedUntil) {
|
||||
try {
|
||||
await this.$store.dispatch('bulkActions/process', {
|
||||
type: 'Conversation',
|
||||
@@ -998,8 +1005,9 @@ export default {
|
||||
fields: {
|
||||
status,
|
||||
},
|
||||
snoozed_until: snoozedUntil,
|
||||
});
|
||||
this.selectedConversations = [];
|
||||
this.$store.dispatch('bulkActions/clearSelectedConversationIds');
|
||||
this.showAlert(this.$t('BULK_ACTION.UPDATE.UPDATE_SUCCESFUL'));
|
||||
} catch (err) {
|
||||
this.showAlert(this.$t('BULK_ACTION.UPDATE.UPDATE_FAILED'));
|
||||
|
||||
@@ -95,20 +95,40 @@
|
||||
<div v-if="allConversationsSelected" class="bulk-action__alert">
|
||||
{{ $t('BULK_ACTION.ALL_CONVERSATIONS_SELECTED_ALERT') }}
|
||||
</div>
|
||||
<woot-modal
|
||||
:show.sync="showCustomTimeSnoozeModal"
|
||||
:on-close="hideCustomSnoozeModal"
|
||||
>
|
||||
<custom-snooze-modal
|
||||
@close="hideCustomSnoozeModal"
|
||||
@choose-time="customSnoozeTime"
|
||||
/>
|
||||
</woot-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getUnixTime } from 'date-fns';
|
||||
import { findSnoozeTime } from 'dashboard/helper/snoozeHelpers';
|
||||
import wootConstants from 'dashboard/constants/globals';
|
||||
import {
|
||||
CMD_BULK_ACTION_SNOOZE_CONVERSATION,
|
||||
CMD_BULK_ACTION_REOPEN_CONVERSATION,
|
||||
CMD_BULK_ACTION_RESOLVE_CONVERSATION,
|
||||
} from 'dashboard/routes/dashboard/commands/commandBarBusEvents';
|
||||
|
||||
import AgentSelector from './AgentSelector.vue';
|
||||
import UpdateActions from './UpdateActions.vue';
|
||||
import LabelActions from './LabelActions.vue';
|
||||
import TeamActions from './TeamActions.vue';
|
||||
import CustomSnoozeModal from 'dashboard/components/CustomSnoozeModal.vue';
|
||||
export default {
|
||||
components: {
|
||||
AgentSelector,
|
||||
UpdateActions,
|
||||
LabelActions,
|
||||
TeamActions,
|
||||
CustomSnoozeModal,
|
||||
},
|
||||
props: {
|
||||
conversations: {
|
||||
@@ -143,17 +163,56 @@ export default {
|
||||
showLabelActions: false,
|
||||
showTeamsList: false,
|
||||
popoverPositions: {},
|
||||
showCustomTimeSnoozeModal: false,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
bus.$on(CMD_BULK_ACTION_SNOOZE_CONVERSATION, this.onCmdSnoozeConversation);
|
||||
bus.$on(CMD_BULK_ACTION_REOPEN_CONVERSATION, this.onCmdReopenConversation);
|
||||
bus.$on(
|
||||
CMD_BULK_ACTION_RESOLVE_CONVERSATION,
|
||||
this.onCmdResolveConversation
|
||||
);
|
||||
},
|
||||
destroyed() {
|
||||
bus.$off(CMD_BULK_ACTION_SNOOZE_CONVERSATION, this.onCmdSnoozeConversation);
|
||||
bus.$off(CMD_BULK_ACTION_REOPEN_CONVERSATION, this.onCmdReopenConversation);
|
||||
bus.$off(
|
||||
CMD_BULK_ACTION_RESOLVE_CONVERSATION,
|
||||
this.onCmdResolveConversation
|
||||
);
|
||||
},
|
||||
methods: {
|
||||
onCmdSnoozeConversation(snoozeType) {
|
||||
if (snoozeType === wootConstants.SNOOZE_OPTIONS.UNTIL_CUSTOM_TIME) {
|
||||
this.showCustomTimeSnoozeModal = true;
|
||||
} else {
|
||||
this.updateConversations('snoozed', findSnoozeTime(snoozeType) || null);
|
||||
}
|
||||
},
|
||||
onCmdReopenConversation() {
|
||||
this.updateConversations('open', null);
|
||||
},
|
||||
onCmdResolveConversation() {
|
||||
this.updateConversations('resolved', null);
|
||||
},
|
||||
customSnoozeTime(customSnoozedTime) {
|
||||
this.showCustomTimeSnoozeModal = false;
|
||||
if (customSnoozedTime) {
|
||||
this.updateConversations('snoozed', getUnixTime(customSnoozedTime));
|
||||
}
|
||||
},
|
||||
hideCustomSnoozeModal() {
|
||||
this.showCustomTimeSnoozeModal = false;
|
||||
},
|
||||
selectAll(e) {
|
||||
this.$emit('select-all-conversations', e.target.checked);
|
||||
},
|
||||
submit(agent) {
|
||||
this.$emit('assign-agent', agent);
|
||||
},
|
||||
updateConversations(status) {
|
||||
this.$emit('update-conversations', status);
|
||||
updateConversations(status, snoozedUntil) {
|
||||
this.$emit('update-conversations', status, snoozedUntil);
|
||||
},
|
||||
assignLabels(labels) {
|
||||
this.$emit('assign-labels', labels);
|
||||
|
||||
@@ -1,12 +1,98 @@
|
||||
<script setup>
|
||||
import { useI18n } from 'dashboard/composables/useI18n';
|
||||
import { ref } from 'vue';
|
||||
|
||||
import WootDropdownItem from 'shared/components/ui/dropdown/DropdownItem.vue';
|
||||
import WootDropdownMenu from 'shared/components/ui/dropdown/DropdownMenu.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const emits = defineEmits(['update', 'close']);
|
||||
|
||||
const props = defineProps({
|
||||
selectedInboxes: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
conversationCount: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
showResolve: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
showReopen: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
showSnooze: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
});
|
||||
|
||||
const actions = ref([
|
||||
{ icon: 'checkmark', key: 'resolved' },
|
||||
{ icon: 'arrow-redo', key: 'open' },
|
||||
{ icon: 'send-clock', key: 'snoozed' },
|
||||
]);
|
||||
|
||||
const updateConversations = key => {
|
||||
if (key === 'snoozed') {
|
||||
// If the user clicks on the snooze option from the bulk action change status dropdown.
|
||||
// Open the snooze option for bulk action in the cmd bar.
|
||||
const ninja = document.querySelector('ninja-keys');
|
||||
ninja?.open({ parent: 'bulk_action_snooze_conversation' });
|
||||
} else {
|
||||
emits('update', key);
|
||||
}
|
||||
};
|
||||
|
||||
const onClose = () => {
|
||||
emits('close');
|
||||
};
|
||||
|
||||
const showAction = key => {
|
||||
const actionsMap = {
|
||||
resolved: props.showResolve,
|
||||
open: props.showReopen,
|
||||
snoozed: props.showSnooze,
|
||||
};
|
||||
return actionsMap[key] || false;
|
||||
};
|
||||
|
||||
const actionLabel = key => {
|
||||
const labelsMap = {
|
||||
resolved: t('CONVERSATION.HEADER.RESOLVE_ACTION'),
|
||||
open: t('CONVERSATION.HEADER.REOPEN_ACTION'),
|
||||
snoozed: t('BULK_ACTION.UPDATE.SNOOZE_UNTIL'),
|
||||
};
|
||||
return labelsMap[key] || '';
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-on-clickaway="onClose" class="actions-container">
|
||||
<div class="triangle">
|
||||
<div
|
||||
v-on-clickaway="onClose"
|
||||
class="absolute right-2 top-12 origin-top-right w-auto z-20 bg-white dark:bg-slate-800 rounded-lg border border-solid border-slate-50 dark:border-slate-700 shadow-md"
|
||||
>
|
||||
<div
|
||||
class="right-[var(--triangle-position)] block z-10 absolute text-left -top-3"
|
||||
>
|
||||
<svg height="12" viewBox="0 0 24 12" width="24">
|
||||
<path d="M20 12l-8-8-12 12" fill-rule="evenodd" stroke-width="1px" />
|
||||
<path
|
||||
d="M20 12l-8-8-12 12"
|
||||
fill-rule="evenodd"
|
||||
stroke-width="1px"
|
||||
class="fill-white dark:fill-slate-800 stroke-slate-50 dark:stroke-slate-600/50"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="header flex items-center justify-between">
|
||||
<span>{{ $t('BULK_ACTION.UPDATE.CHANGE_STATUS') }}</span>
|
||||
<div class="p-2.5 flex gap-1 items-center justify-between">
|
||||
<span class="text-sm font-medium text-slate-600 dark:text-slate-100">
|
||||
{{ $t('BULK_ACTION.UPDATE.CHANGE_STATUS') }}
|
||||
</span>
|
||||
<woot-button
|
||||
size="tiny"
|
||||
variant="clear"
|
||||
@@ -15,8 +101,8 @@
|
||||
@click="onClose"
|
||||
/>
|
||||
</div>
|
||||
<div class="container">
|
||||
<woot-dropdown-menu>
|
||||
<div class="px-2.5 pt-0 pb-2.5">
|
||||
<woot-dropdown-menu class="m-0 list-none">
|
||||
<template v-for="action in actions">
|
||||
<woot-dropdown-item v-if="showAction(action.key)" :key="action.key">
|
||||
<woot-button
|
||||
@@ -34,114 +120,3 @@
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import WootDropdownItem from 'shared/components/ui/dropdown/DropdownItem.vue';
|
||||
import WootDropdownMenu from 'shared/components/ui/dropdown/DropdownMenu.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
WootDropdownItem,
|
||||
WootDropdownMenu,
|
||||
},
|
||||
props: {
|
||||
selectedInboxes: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
conversationCount: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
showResolve: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
showReopen: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
showSnooze: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
query: '',
|
||||
selectedAction: null,
|
||||
actions: [
|
||||
{
|
||||
icon: 'checkmark',
|
||||
key: 'resolved',
|
||||
},
|
||||
{
|
||||
icon: 'arrow-redo',
|
||||
key: 'open',
|
||||
},
|
||||
{
|
||||
icon: 'send-clock',
|
||||
key: 'snoozed',
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
updateConversations(key) {
|
||||
this.$emit('update', key);
|
||||
},
|
||||
goBack() {
|
||||
this.selectedAgent = null;
|
||||
},
|
||||
onClose() {
|
||||
this.$emit('close');
|
||||
},
|
||||
showAction(key) {
|
||||
const actionsMap = {
|
||||
resolved: this.showResolve,
|
||||
open: this.showReopen,
|
||||
snoozed: this.showSnooze,
|
||||
};
|
||||
return actionsMap[key] || false;
|
||||
},
|
||||
actionLabel(key) {
|
||||
const labelsMap = {
|
||||
resolved: this.$t('CONVERSATION.HEADER.RESOLVE_ACTION'),
|
||||
open: this.$t('CONVERSATION.HEADER.REOPEN_ACTION'),
|
||||
snoozed: this.$t('BULK_ACTION.UPDATE.SNOOZE_UNTIL_NEXT_REPLY'),
|
||||
};
|
||||
return labelsMap[key] || '';
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.actions-container {
|
||||
@apply absolute right-2 top-12 origin-top-right w-auto z-20 bg-white dark:bg-slate-800 rounded-lg border border-solid border-slate-50 dark:border-slate-700 shadow-md;
|
||||
|
||||
.header {
|
||||
@apply p-2.5;
|
||||
|
||||
span {
|
||||
@apply text-sm font-medium;
|
||||
}
|
||||
}
|
||||
.container {
|
||||
@apply px-2.5 pt-0 pb-2.5;
|
||||
}
|
||||
|
||||
.triangle {
|
||||
right: var(--triangle-position);
|
||||
@apply block z-10 absolute text-left -top-3;
|
||||
|
||||
svg path {
|
||||
@apply fill-white dark:fill-slate-800 stroke-slate-50 dark:stroke-slate-600/50;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
@apply m-0 list-none;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"AGENT_LIST_LOADING": "Loading agents",
|
||||
"UPDATE": {
|
||||
"CHANGE_STATUS": "Change status",
|
||||
"SNOOZE_UNTIL_NEXT_REPLY": "Snooze until next reply.",
|
||||
"SNOOZE_UNTIL": "Snooze",
|
||||
"UPDATE_SUCCESFUL": "Conversation status updated successfully.",
|
||||
"UPDATE_FAILED": "Failed to update conversations. Please try again."
|
||||
},
|
||||
|
||||
@@ -107,6 +107,7 @@
|
||||
"GENERAL": "General",
|
||||
"REPORTS": "Reports",
|
||||
"CONVERSATION": "Conversation",
|
||||
"BULK_ACTIONS": "Bulk Actions",
|
||||
"CHANGE_ASSIGNEE": "Change Assignee",
|
||||
"CHANGE_PRIORITY": "Change Priority",
|
||||
"CHANGE_TEAM": "Change Team",
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
import { mapGetters } from 'vuex';
|
||||
import wootConstants from 'dashboard/constants/globals';
|
||||
|
||||
import {
|
||||
CMD_BULK_ACTION_SNOOZE_CONVERSATION,
|
||||
CMD_BULK_ACTION_REOPEN_CONVERSATION,
|
||||
CMD_BULK_ACTION_RESOLVE_CONVERSATION,
|
||||
} from './commandBarBusEvents';
|
||||
import {
|
||||
ICON_SNOOZE_CONVERSATION,
|
||||
ICON_REOPEN_CONVERSATION,
|
||||
ICON_RESOLVE_CONVERSATION,
|
||||
} from './CommandBarIcons';
|
||||
|
||||
const SNOOZE_OPTIONS = wootConstants.SNOOZE_OPTIONS;
|
||||
|
||||
export const SNOOZE_CONVERSATION_BULK_ACTIONS = [
|
||||
{
|
||||
id: 'bulk_action_snooze_conversation',
|
||||
title: 'COMMAND_BAR.COMMANDS.SNOOZE_CONVERSATION',
|
||||
section: 'COMMAND_BAR.SECTIONS.BULK_ACTIONS',
|
||||
icon: ICON_SNOOZE_CONVERSATION,
|
||||
children: Object.values(SNOOZE_OPTIONS),
|
||||
},
|
||||
|
||||
{
|
||||
id: SNOOZE_OPTIONS.UNTIL_NEXT_REPLY,
|
||||
title: 'COMMAND_BAR.COMMANDS.UNTIL_NEXT_REPLY',
|
||||
parent: 'bulk_action_snooze_conversation',
|
||||
section: 'COMMAND_BAR.SECTIONS.BULK_ACTIONS',
|
||||
icon: ICON_SNOOZE_CONVERSATION,
|
||||
handler: () =>
|
||||
bus.$emit(
|
||||
CMD_BULK_ACTION_SNOOZE_CONVERSATION,
|
||||
SNOOZE_OPTIONS.UNTIL_NEXT_REPLY
|
||||
),
|
||||
},
|
||||
{
|
||||
id: SNOOZE_OPTIONS.AN_HOUR_FROM_NOW,
|
||||
title: 'COMMAND_BAR.COMMANDS.AN_HOUR_FROM_NOW',
|
||||
parent: 'bulk_action_snooze_conversation',
|
||||
section: 'COMMAND_BAR.SECTIONS.BULK_ACTIONS',
|
||||
icon: ICON_SNOOZE_CONVERSATION,
|
||||
handler: () =>
|
||||
bus.$emit(
|
||||
CMD_BULK_ACTION_SNOOZE_CONVERSATION,
|
||||
SNOOZE_OPTIONS.AN_HOUR_FROM_NOW
|
||||
),
|
||||
},
|
||||
{
|
||||
id: SNOOZE_OPTIONS.UNTIL_TOMORROW,
|
||||
title: 'COMMAND_BAR.COMMANDS.UNTIL_TOMORROW',
|
||||
section: 'COMMAND_BAR.SECTIONS.BULK_ACTIONS',
|
||||
parent: 'bulk_action_snooze_conversation',
|
||||
icon: ICON_SNOOZE_CONVERSATION,
|
||||
handler: () =>
|
||||
bus.$emit(
|
||||
CMD_BULK_ACTION_SNOOZE_CONVERSATION,
|
||||
SNOOZE_OPTIONS.UNTIL_TOMORROW
|
||||
),
|
||||
},
|
||||
{
|
||||
id: SNOOZE_OPTIONS.UNTIL_NEXT_WEEK,
|
||||
title: 'COMMAND_BAR.COMMANDS.UNTIL_NEXT_WEEK',
|
||||
section: 'COMMAND_BAR.SECTIONS.BULK_ACTIONS',
|
||||
parent: 'bulk_action_snooze_conversation',
|
||||
icon: ICON_SNOOZE_CONVERSATION,
|
||||
handler: () =>
|
||||
bus.$emit(
|
||||
CMD_BULK_ACTION_SNOOZE_CONVERSATION,
|
||||
SNOOZE_OPTIONS.UNTIL_NEXT_WEEK
|
||||
),
|
||||
},
|
||||
{
|
||||
id: SNOOZE_OPTIONS.UNTIL_NEXT_MONTH,
|
||||
title: 'COMMAND_BAR.COMMANDS.UNTIL_NEXT_MONTH',
|
||||
section: 'COMMAND_BAR.SECTIONS.BULK_ACTIONS',
|
||||
parent: 'bulk_action_snooze_conversation',
|
||||
icon: ICON_SNOOZE_CONVERSATION,
|
||||
handler: () =>
|
||||
bus.$emit(
|
||||
CMD_BULK_ACTION_SNOOZE_CONVERSATION,
|
||||
SNOOZE_OPTIONS.UNTIL_NEXT_MONTH
|
||||
),
|
||||
},
|
||||
{
|
||||
id: SNOOZE_OPTIONS.UNTIL_CUSTOM_TIME,
|
||||
title: 'COMMAND_BAR.COMMANDS.CUSTOM',
|
||||
section: 'COMMAND_BAR.SECTIONS.BULK_ACTIONS',
|
||||
parent: 'bulk_action_snooze_conversation',
|
||||
icon: ICON_SNOOZE_CONVERSATION,
|
||||
handler: () =>
|
||||
bus.$emit(
|
||||
CMD_BULK_ACTION_SNOOZE_CONVERSATION,
|
||||
SNOOZE_OPTIONS.UNTIL_CUSTOM_TIME
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
export const RESOLVED_CONVERSATION_BULK_ACTIONS = [
|
||||
{
|
||||
id: 'bulk_action_reopen_conversation',
|
||||
title: 'COMMAND_BAR.COMMANDS.REOPEN_CONVERSATION',
|
||||
section: 'COMMAND_BAR.SECTIONS.BULK_ACTIONS',
|
||||
icon: ICON_REOPEN_CONVERSATION,
|
||||
handler: () => bus.$emit(CMD_BULK_ACTION_REOPEN_CONVERSATION),
|
||||
},
|
||||
];
|
||||
|
||||
export const OPEN_CONVERSATION_BULK_ACTIONS = [
|
||||
{
|
||||
id: 'bulk_action_resolve_conversation',
|
||||
title: 'COMMAND_BAR.COMMANDS.RESOLVE_CONVERSATION',
|
||||
section: 'COMMAND_BAR.SECTIONS.BULK_ACTIONS',
|
||||
icon: ICON_RESOLVE_CONVERSATION,
|
||||
handler: () => bus.$emit(CMD_BULK_ACTION_RESOLVE_CONVERSATION),
|
||||
},
|
||||
];
|
||||
|
||||
export default {
|
||||
computed: {
|
||||
...mapGetters({
|
||||
selectedConversations: 'bulkActions/getSelectedConversationIds',
|
||||
}),
|
||||
bulkActionsHotKeys() {
|
||||
let actions = [];
|
||||
if (this.selectedConversations.length > 0) {
|
||||
actions = [
|
||||
...SNOOZE_CONVERSATION_BULK_ACTIONS,
|
||||
...RESOLVED_CONVERSATION_BULK_ACTIONS,
|
||||
...OPEN_CONVERSATION_BULK_ACTIONS,
|
||||
];
|
||||
}
|
||||
return this.prepareActions(actions);
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
selectedConversations() {
|
||||
this.setCommandbarData();
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
prepareActions(actions) {
|
||||
return actions.map(action => ({
|
||||
...action,
|
||||
title: this.$t(action.title),
|
||||
section: this.$t(action.section),
|
||||
}));
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -16,5 +16,13 @@ export const CMD_RESOLVE_CONVERSATION = 'CMD_RESOLVE_CONVERSATION';
|
||||
export const CMD_SNOOZE_CONVERSATION = 'CMD_SNOOZE_CONVERSATION';
|
||||
export const CMD_AI_ASSIST = 'CMD_AI_ASSIST';
|
||||
|
||||
// Bulk Actions
|
||||
export const CMD_BULK_ACTION_SNOOZE_CONVERSATION =
|
||||
'CMD_BULK_ACTION_SNOOZE_CONVERSATION';
|
||||
export const CMD_BULK_ACTION_REOPEN_CONVERSATION =
|
||||
'CMD_BULK_ACTION_REOPEN_CONVERSATION';
|
||||
export const CMD_BULK_ACTION_RESOLVE_CONVERSATION =
|
||||
'CMD_BULK_ACTION_RESOLVE_CONVERSATION';
|
||||
|
||||
// Inbox Commands (Notifications)
|
||||
export const CMD_SNOOZE_NOTIFICATION = 'CMD_SNOOZE_NOTIFICATION';
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
<script>
|
||||
import 'ninja-keys';
|
||||
import conversationHotKeysMixin from './conversationHotKeys';
|
||||
import bulkActionsHotKeysMixin from './bulkActionsHotKeys';
|
||||
import inboxHotKeysMixin from './inboxHotKeys';
|
||||
import goToCommandHotKeys from './goToCommandHotKeys';
|
||||
import appearanceHotKeys from './appearanceHotKeys';
|
||||
@@ -26,6 +27,7 @@ export default {
|
||||
adminMixin,
|
||||
agentMixin,
|
||||
conversationHotKeysMixin,
|
||||
bulkActionsHotKeysMixin,
|
||||
inboxHotKeysMixin,
|
||||
conversationLabelMixin,
|
||||
conversationTeamMixin,
|
||||
@@ -46,6 +48,7 @@ export default {
|
||||
return [
|
||||
...this.inboxHotKeys,
|
||||
...this.conversationHotKeys,
|
||||
...this.bulkActionsHotKeys,
|
||||
...this.goToCommandHotKeys,
|
||||
...this.goToAppearanceHotKeys,
|
||||
];
|
||||
|
||||
@@ -2,6 +2,7 @@ import types from '../mutation-types';
|
||||
import BulkActionsAPI from '../../api/bulkActions';
|
||||
|
||||
export const state = {
|
||||
selectedConversationIds: [],
|
||||
uiFlags: {
|
||||
isUpdating: false,
|
||||
},
|
||||
@@ -11,6 +12,9 @@ export const getters = {
|
||||
getUIFlags(_state) {
|
||||
return _state.uiFlags;
|
||||
},
|
||||
getSelectedConversationIds(_state) {
|
||||
return _state.selectedConversationIds;
|
||||
},
|
||||
};
|
||||
|
||||
export const actions = {
|
||||
@@ -24,6 +28,15 @@ export const actions = {
|
||||
commit(types.SET_BULK_ACTIONS_FLAG, { isUpdating: false });
|
||||
}
|
||||
},
|
||||
setSelectedConversationIds({ commit }, id) {
|
||||
commit(types.SET_SELECTED_CONVERSATION_IDS, id);
|
||||
},
|
||||
removeSelectedConversationIds({ commit }, id) {
|
||||
commit(types.REMOVE_SELECTED_CONVERSATION_IDS, id);
|
||||
},
|
||||
clearSelectedConversationIds({ commit }) {
|
||||
commit(types.CLEAR_SELECTED_CONVERSATION_IDS);
|
||||
},
|
||||
};
|
||||
|
||||
export const mutations = {
|
||||
@@ -33,6 +46,23 @@ export const mutations = {
|
||||
...data,
|
||||
};
|
||||
},
|
||||
[types.SET_SELECTED_CONVERSATION_IDS](_state, ids) {
|
||||
// Check if ids is an array, if not, convert it to an array
|
||||
const idsArray = Array.isArray(ids) ? ids : [ids];
|
||||
|
||||
// Concatenate the new IDs ensuring no duplicates
|
||||
_state.selectedConversationIds = [
|
||||
...new Set([..._state.selectedConversationIds, ...idsArray]),
|
||||
];
|
||||
},
|
||||
[types.REMOVE_SELECTED_CONVERSATION_IDS](_state, id) {
|
||||
_state.selectedConversationIds = _state.selectedConversationIds.filter(
|
||||
item => item !== id
|
||||
);
|
||||
},
|
||||
[types.CLEAR_SELECTED_CONVERSATION_IDS](_state) {
|
||||
_state.selectedConversationIds = [];
|
||||
},
|
||||
};
|
||||
|
||||
export default {
|
||||
|
||||
@@ -25,4 +25,28 @@ describe('#actions', () => {
|
||||
]);
|
||||
});
|
||||
});
|
||||
describe('#setSelectedConversationIds', () => {
|
||||
it('sends correct actions if API is success', async () => {
|
||||
await actions.setSelectedConversationIds({ commit }, payload.ids);
|
||||
expect(commit.mock.calls).toEqual([
|
||||
[types.default.SET_SELECTED_CONVERSATION_IDS, payload.ids],
|
||||
]);
|
||||
});
|
||||
});
|
||||
describe('#removeSelectedConversationIds', () => {
|
||||
it('sends correct actions if API is success', async () => {
|
||||
await actions.removeSelectedConversationIds({ commit }, payload.ids);
|
||||
expect(commit.mock.calls).toEqual([
|
||||
[types.default.REMOVE_SELECTED_CONVERSATION_IDS, payload.ids],
|
||||
]);
|
||||
});
|
||||
});
|
||||
describe('#clearSelectedConversationIds', () => {
|
||||
it('sends correct actions if API is success', async () => {
|
||||
await actions.clearSelectedConversationIds({ commit });
|
||||
expect(commit.mock.calls).toEqual([
|
||||
[types.default.CLEAR_SELECTED_CONVERSATION_IDS],
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -11,4 +11,10 @@ describe('#getters', () => {
|
||||
isUpdating: false,
|
||||
});
|
||||
});
|
||||
it('getSelectedConversationIds', () => {
|
||||
const state = {
|
||||
selectedConversationIds: [1, 2, 3],
|
||||
};
|
||||
expect(getters.getSelectedConversationIds(state)).toEqual([1, 2, 3]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,4 +9,25 @@ describe('#mutations', () => {
|
||||
expect(state.uiFlags.isUpdating).toEqual(true);
|
||||
});
|
||||
});
|
||||
describe('#setSelectedConversationIds', () => {
|
||||
it('set selected conversation ids', () => {
|
||||
const state = { selectedConversationIds: [] };
|
||||
mutations[types.SET_SELECTED_CONVERSATION_IDS](state, [1, 2, 3]);
|
||||
expect(state.selectedConversationIds).toEqual([1, 2, 3]);
|
||||
});
|
||||
});
|
||||
describe('#removeSelectedConversationIds', () => {
|
||||
it('remove selected conversation ids', () => {
|
||||
const state = { selectedConversationIds: [1, 2, 3] };
|
||||
mutations[types.REMOVE_SELECTED_CONVERSATION_IDS](state, 1);
|
||||
expect(state.selectedConversationIds).toEqual([2, 3]);
|
||||
});
|
||||
});
|
||||
describe('#clearSelectedConversationIds', () => {
|
||||
it('clear selected conversation ids', () => {
|
||||
const state = { selectedConversationIds: [1, 2, 3] };
|
||||
mutations[types.CLEAR_SELECTED_CONVERSATION_IDS](state);
|
||||
expect(state.selectedConversationIds).toEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -160,6 +160,9 @@ export default {
|
||||
SET_CONVERSATION_LABELS_UI_FLAG: 'SET_CONVERSATION_LABELS_UI_FLAG',
|
||||
SET_CONVERSATION_LABELS: 'SET_CONVERSATION_LABELS',
|
||||
SET_BULK_CONVERSATION_LABELS: 'SET_BULK_CONVERSATION_LABELS',
|
||||
SET_SELECTED_CONVERSATION_IDS: 'SET_SELECTED_CONVERSATION_IDS',
|
||||
CLEAR_SELECTED_CONVERSATION_IDS: 'CLEAR_SELECTED_CONVERSATION_IDS',
|
||||
REMOVE_SELECTED_CONVERSATION_IDS: 'REMOVE_SELECTED_CONVERSATION_IDS',
|
||||
|
||||
// Reports
|
||||
SET_ACCOUNT_REPORTS: 'SET_ACCOUNT_REPORTS',
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
</button>
|
||||
</template>
|
||||
<script setup>
|
||||
import { computed, defineProps, useAttrs } from 'vue';
|
||||
import { computed, useAttrs } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
variant: {
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineProps, defineEmits, computed } from 'vue';
|
||||
import { computed } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
isChecked: {
|
||||
|
||||
Reference in New Issue
Block a user