mirror of
https://github.com/lingble/chatwoot.git
synced 2025-11-01 03:27:52 +00:00
feat: Add conversation delete feature (#11677)
<img width="1240" alt="Screenshot 2025-06-05 at 12 39 04 AM" src="https://github.com/user-attachments/assets/0071cd23-38c3-4638-946e-f1fbd11ec845" /> ## Changes Give the admins an option to delete conversation via the context menu - enable conversation deletion in routes and controller - expose delete API on conversations - add delete option in conversation context menu and integrate with card and list - implement store action and mutation for delete - update i18n with new strings fixes: https://github.com/chatwoot/chatwoot/issues/947 --------- Co-authored-by: iamsivin <iamsivin@gmail.com> Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> Co-authored-by: Pranav <pranavrajs@gmail.com>
This commit is contained in:
@@ -22,6 +22,7 @@ import {
|
||||
// https://tanstack.com/virtual/latest/docs/framework/vue/examples/variable
|
||||
import { DynamicScroller, DynamicScrollerItem } from 'vue-virtual-scroller';
|
||||
import ChatListHeader from './ChatListHeader.vue';
|
||||
import Dialog from 'dashboard/components-next/dialog/Dialog.vue';
|
||||
import ConversationFilter from 'next/filter/ConversationFilter.vue';
|
||||
import SaveCustomView from 'next/filter/SaveCustomView.vue';
|
||||
import ChatTypeTabs from './widgets/ChatTypeTabs.vue';
|
||||
@@ -82,6 +83,7 @@ const emit = defineEmits(['conversationLoad']);
|
||||
const { uiSettings } = useUISettings();
|
||||
const { t } = useI18n();
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const store = useStore();
|
||||
|
||||
const conversationListRef = ref(null);
|
||||
@@ -646,6 +648,30 @@ function openLastItemAfterDeleteInFolder() {
|
||||
}
|
||||
}
|
||||
|
||||
function redirectToConversationList() {
|
||||
const {
|
||||
params: { accountId, inbox_id: inboxId, label, teamId },
|
||||
name,
|
||||
} = route;
|
||||
|
||||
let conversationType = '';
|
||||
if (isOnMentionsView({ route: { name } })) {
|
||||
conversationType = 'mention';
|
||||
} else if (isOnUnattendedView({ route: { name } })) {
|
||||
conversationType = 'unattended';
|
||||
}
|
||||
router.push(
|
||||
conversationListPageURL({
|
||||
accountId,
|
||||
conversationType: conversationType,
|
||||
customViewId: props.foldersId,
|
||||
inboxId,
|
||||
label,
|
||||
teamId,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
async function assignPriority(priority, conversationId = null) {
|
||||
store.dispatch('setCurrentChatPriority', {
|
||||
priority,
|
||||
@@ -670,26 +696,7 @@ async function markAsUnread(conversationId) {
|
||||
await store.dispatch('markMessagesUnread', {
|
||||
id: conversationId,
|
||||
});
|
||||
const {
|
||||
params: { accountId, inbox_id: inboxId, label, teamId },
|
||||
name,
|
||||
} = useRoute();
|
||||
let conversationType = '';
|
||||
if (isOnMentionsView({ route: { name } })) {
|
||||
conversationType = 'mention';
|
||||
} else if (isOnUnattendedView({ route: { name } })) {
|
||||
conversationType = 'unattended';
|
||||
}
|
||||
router.push(
|
||||
conversationListPageURL({
|
||||
accountId,
|
||||
conversationType: conversationType,
|
||||
customViewId: props.foldersId,
|
||||
inboxId,
|
||||
label,
|
||||
teamId,
|
||||
})
|
||||
);
|
||||
redirectToConversationList();
|
||||
} catch (error) {
|
||||
// Ignore error
|
||||
}
|
||||
@@ -703,6 +710,7 @@ async function markAsRead(conversationId) {
|
||||
// Ignore error
|
||||
}
|
||||
}
|
||||
|
||||
async function onAssignTeam(team, conversationId = null) {
|
||||
try {
|
||||
await store.dispatch('assignTeam', {
|
||||
@@ -764,6 +772,26 @@ onMounted(() => {
|
||||
}
|
||||
});
|
||||
|
||||
const deleteConversationDialogRef = ref(null);
|
||||
const selectedConversationId = ref(null);
|
||||
|
||||
async function deleteConversation() {
|
||||
try {
|
||||
await store.dispatch('deleteConversation', selectedConversationId.value);
|
||||
redirectToConversationList();
|
||||
selectedConversationId.value = null;
|
||||
deleteConversationDialogRef.value.close();
|
||||
useAlert(t('CONVERSATION.SUCCESS_DELETE_CONVERSATION'));
|
||||
} catch (error) {
|
||||
useAlert(t('CONVERSATION.FAIL_DELETE_CONVERSATION'));
|
||||
}
|
||||
}
|
||||
|
||||
const handleDelete = conversationId => {
|
||||
selectedConversationId.value = conversationId;
|
||||
deleteConversationDialogRef.value.open();
|
||||
};
|
||||
|
||||
provide('selectConversation', selectConversation);
|
||||
provide('deSelectConversation', deSelectConversation);
|
||||
provide('assignAgent', onAssignAgent);
|
||||
@@ -775,6 +803,7 @@ provide('markAsUnread', markAsUnread);
|
||||
provide('markAsRead', markAsRead);
|
||||
provide('assignPriority', assignPriority);
|
||||
provide('isConversationSelected', isConversationSelected);
|
||||
provide('deleteConversation', handleDelete);
|
||||
|
||||
watch(activeTeam, () => resetAndFetchData());
|
||||
|
||||
@@ -938,6 +967,19 @@ watch(conversationFilters, (newVal, oldVal) => {
|
||||
</template>
|
||||
</DynamicScroller>
|
||||
</div>
|
||||
<Dialog
|
||||
ref="deleteConversationDialogRef"
|
||||
type="alert"
|
||||
:title="
|
||||
$t('CONVERSATION.DELETE_CONVERSATION.TITLE', {
|
||||
conversationId: selectedConversationId,
|
||||
})
|
||||
"
|
||||
:description="$t('CONVERSATION.DELETE_CONVERSATION.DESCRIPTION')"
|
||||
:confirm-button-label="$t('CONVERSATION.DELETE_CONVERSATION.CONFIRM')"
|
||||
@confirm="deleteConversation"
|
||||
@close="selectedConversationId = null"
|
||||
/>
|
||||
<TeleportWithDirection
|
||||
v-if="showAdvancedFilters"
|
||||
to="#conversationFilterTeleportTarget"
|
||||
|
||||
Reference in New Issue
Block a user