feat: Updated conversation list UI (#12971)
This commit is contained in:
@@ -1,17 +1,7 @@
|
||||
<script setup>
|
||||
// [TODO] This componet is too big and bulky to be in the same file, we can consider splitting this into multiple
|
||||
// composables and components, useVirtualChatList, useChatlistFilters
|
||||
import {
|
||||
ref,
|
||||
unref,
|
||||
provide,
|
||||
computed,
|
||||
watch,
|
||||
onMounted,
|
||||
defineEmits,
|
||||
} from 'vue';
|
||||
import { ref, provide, computed, watch, onMounted, defineEmits } from 'vue';
|
||||
import { useStore } from 'vuex';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { useRouter } from 'vue-router';
|
||||
import {
|
||||
useMapGetter,
|
||||
useFunctionGetter,
|
||||
@@ -19,48 +9,24 @@ import {
|
||||
|
||||
import ChatListHeader from './ChatListHeader.vue';
|
||||
import ConversationList from './ConversationList.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';
|
||||
|
||||
import DeleteCustomViews from 'dashboard/routes/dashboard/customviews/DeleteCustomViews.vue';
|
||||
import ConversationBulkActions from './widgets/conversation/conversationBulkActions/Index.vue';
|
||||
|
||||
import TeleportWithDirection from 'dashboard/components-next/TeleportWithDirection.vue';
|
||||
import ChatListFilters from './ChatListFilters.vue';
|
||||
import ConversationActiveFiltersPreview from './widgets/conversation/ConversationActiveFiltersPreview.vue';
|
||||
|
||||
import { useUISettings } from 'dashboard/composables/useUISettings';
|
||||
import { useAlert } from 'dashboard/composables';
|
||||
|
||||
import { useBulkActions } from 'dashboard/composables/chatlist/useBulkActions';
|
||||
import { useFilter } from 'shared/composables/useFilter';
|
||||
import { useTrack } from 'dashboard/composables';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import {
|
||||
useCamelCase,
|
||||
useSnakeCase,
|
||||
} from 'dashboard/composables/useTransformKeys';
|
||||
import { useSnakeCase } from 'dashboard/composables/useTransformKeys';
|
||||
import { useEmitter } from 'dashboard/composables/emitter';
|
||||
|
||||
import { useBulkActions } from 'dashboard/composables/chatlist/useBulkActions';
|
||||
import { emitter } from 'shared/helpers/mitt';
|
||||
|
||||
import wootConstants from 'dashboard/constants/globals';
|
||||
import advancedFilterOptions from './widgets/conversation/advancedFilterItems';
|
||||
import filterQueryGenerator from '../helper/filterQueryGenerator.js';
|
||||
import languages from 'dashboard/components/widgets/conversation/advancedFilterItems/languages';
|
||||
import countries from 'shared/constants/countries';
|
||||
import { generateValuesForEditCustomViews } from 'dashboard/helper/customViewsHelper';
|
||||
import { conversationListPageURL } from '../helper/URLHelper';
|
||||
import {
|
||||
isOnMentionsView,
|
||||
isOnUnattendedView,
|
||||
} from '../store/modules/conversations/helpers/actionHelpers';
|
||||
import {
|
||||
getUserPermissions,
|
||||
filterItemsByPermission,
|
||||
} from 'dashboard/helper/permissionsHelper.js';
|
||||
import { matchesFilters } from '../store/modules/conversations/helpers/filterHelpers';
|
||||
import { CONVERSATION_EVENTS } from '../helper/AnalyticsHelper/events';
|
||||
import { ASSIGNEE_TYPE_TAB_PERMISSIONS } from 'dashboard/constants/permissions.js';
|
||||
|
||||
const props = defineProps({
|
||||
@@ -76,34 +42,20 @@ const props = defineProps({
|
||||
const emit = defineEmits(['conversationLoad']);
|
||||
const { uiSettings } = useUISettings();
|
||||
const { t } = useI18n();
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const store = useStore();
|
||||
const router = useRouter();
|
||||
|
||||
const activeAssigneeTab = ref(wootConstants.ASSIGNEE_TYPE.ME);
|
||||
const activeStatus = ref(wootConstants.STATUS_TYPE.OPEN);
|
||||
const activeSortBy = ref(wootConstants.SORT_BY_TYPE.LAST_ACTIVITY_AT_DESC);
|
||||
const chatsOnView = ref([]);
|
||||
const isContextMenuOpen = ref(false);
|
||||
const chatListFiltersRef = ref(null);
|
||||
|
||||
// Provide trigger for label recalculation (parent → child pattern)
|
||||
const recalculateLabelsKey = ref(0);
|
||||
provide('recalculateLabelsKey', recalculateLabelsKey);
|
||||
|
||||
const showAdvancedFilters = ref(false);
|
||||
// chatsOnView is to store the chats that are currently visible on the screen,
|
||||
// which mirrors the conversationList.
|
||||
const chatsOnView = ref([]);
|
||||
const foldersQuery = ref({});
|
||||
const showAddFoldersModal = ref(false);
|
||||
const showDeleteFoldersModal = ref(false);
|
||||
const isContextMenuOpen = ref(false);
|
||||
const appliedFilter = ref([]);
|
||||
const advancedFilterTypes = ref(
|
||||
advancedFilterOptions.map(filter => ({
|
||||
...filter,
|
||||
attributeName: t(`FILTER.ATTRIBUTES.${filter.attributeI18nKey}`),
|
||||
}))
|
||||
);
|
||||
|
||||
const currentUser = useMapGetter('getCurrentUser');
|
||||
const chatLists = useMapGetter('getFilteredConversations');
|
||||
const mineChatsList = useMapGetter('getMineChats');
|
||||
@@ -113,15 +65,9 @@ const chatListLoading = useMapGetter('getChatListLoadingStatus');
|
||||
const activeInbox = useMapGetter('getSelectedInbox');
|
||||
const conversationStats = useMapGetter('conversationStats/getStats');
|
||||
const appliedFilters = useMapGetter('getAppliedConversationFiltersV2');
|
||||
const folders = useMapGetter('customViews/getConversationCustomViews');
|
||||
const agentList = useMapGetter('agents/getAgents');
|
||||
const teamsList = useMapGetter('teams/getTeams');
|
||||
const inboxesList = useMapGetter('inboxes/getInboxes');
|
||||
const campaigns = useMapGetter('campaigns/getAllCampaigns');
|
||||
const labels = useMapGetter('labels/getLabels');
|
||||
const currentAccountId = useMapGetter('getCurrentAccountId');
|
||||
// We can't useFunctionGetter here since it needs to be called on setup?
|
||||
const getTeamFn = useMapGetter('teams/getTeam');
|
||||
const folders = useMapGetter('customViews/getConversationCustomViews');
|
||||
|
||||
const {
|
||||
selectedConversations,
|
||||
@@ -131,55 +77,24 @@ const {
|
||||
selectAllConversations,
|
||||
resetBulkActions,
|
||||
isConversationSelected,
|
||||
onAssignAgent,
|
||||
onAssignLabels,
|
||||
onAssignTeamsForBulk,
|
||||
onUpdateConversations,
|
||||
} = useBulkActions();
|
||||
|
||||
const {
|
||||
initializeStatusAndAssigneeFilterToModal,
|
||||
initializeInboxTeamAndLabelFilterToModal,
|
||||
} = useFilter({
|
||||
filteri18nKey: 'FILTER',
|
||||
attributeModel: 'conversation_attribute',
|
||||
});
|
||||
|
||||
// computed
|
||||
// computed
|
||||
|
||||
const hasAppliedFilters = computed(() => {
|
||||
return appliedFilters.value.length !== 0;
|
||||
});
|
||||
|
||||
const activeFolder = computed(() => {
|
||||
if (props.foldersId) {
|
||||
const activeView = folders.value.filter(
|
||||
view => view.id === Number(props.foldersId)
|
||||
);
|
||||
const [firstValue] = activeView;
|
||||
return firstValue;
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
|
||||
const activeFolderName = computed(() => {
|
||||
return activeFolder.value?.name;
|
||||
});
|
||||
|
||||
const hasActiveFolders = computed(() => {
|
||||
return Boolean(activeFolder.value && props.foldersId !== 0);
|
||||
return Boolean(props.foldersId !== 0);
|
||||
});
|
||||
|
||||
const activeFolder = computed(() => {
|
||||
return chatListFiltersRef.value?.activeFolder;
|
||||
});
|
||||
|
||||
const hasAppliedFiltersOrActiveFolders = computed(() => {
|
||||
return hasAppliedFilters.value || hasActiveFolders.value;
|
||||
});
|
||||
|
||||
const currentUserDetails = computed(() => {
|
||||
const { id, name } = currentUser.value;
|
||||
return { id, name };
|
||||
});
|
||||
|
||||
const userPermissions = computed(() => {
|
||||
return getUserPermissions(currentUser.value, currentAccountId.value);
|
||||
});
|
||||
@@ -223,11 +138,6 @@ const hasCurrentPageEndReached = useFunctionGetter(
|
||||
currentPageFilterKey
|
||||
);
|
||||
|
||||
const conversationCustomAttributes = useFunctionGetter(
|
||||
'attributes/getAttributesByModel',
|
||||
'conversation_attribute'
|
||||
);
|
||||
|
||||
const activeAssigneeTabCount = computed(() => {
|
||||
const count = assigneeTabItems.value.find(
|
||||
item => item.key === activeAssigneeTab.value
|
||||
@@ -297,7 +207,7 @@ const pageTitle = computed(() => {
|
||||
if (props.conversationType === 'unattended') {
|
||||
return t('CHAT_LIST.UNATTENDED_HEADING');
|
||||
}
|
||||
if (hasActiveFolders.value) {
|
||||
if (hasActiveFolders.value && activeFolder.value?.name) {
|
||||
return activeFolder.value.name;
|
||||
}
|
||||
return t('CHAT_LIST.TAB_HEADING');
|
||||
@@ -319,7 +229,7 @@ const conversationList = computed(() => {
|
||||
localConversationList = [...chatLists.value];
|
||||
}
|
||||
|
||||
if (activeFolder.value) {
|
||||
if (activeFolder.value?.query?.payload) {
|
||||
const { payload } = activeFolder.value.query;
|
||||
localConversationList = localConversationList.filter(conversation => {
|
||||
return matchesFilters(conversation, payload);
|
||||
@@ -364,205 +274,71 @@ function setFiltersFromUISettings() {
|
||||
|
||||
function emitConversationLoaded() {
|
||||
emit('conversationLoad');
|
||||
// [VITE] removing this since the library has changed
|
||||
// nextTick(() => {
|
||||
// // Addressing a known issue in the virtual list library where dynamically added items
|
||||
// // might not render correctly. This workaround involves a slight manual adjustment
|
||||
// // to the scroll position, triggering the list to refresh its rendering.
|
||||
// const virtualList = conversationListRef.value;
|
||||
// const scrollToOffset = virtualList?.scrollToOffset;
|
||||
// const currentOffset = virtualList?.getOffset() || 0;
|
||||
// if (scrollToOffset) {
|
||||
// scrollToOffset(currentOffset + 1);
|
||||
// }
|
||||
// });
|
||||
}
|
||||
|
||||
function fetchFilteredConversations(payload) {
|
||||
payload = useSnakeCase(payload);
|
||||
const transformedPayload = useSnakeCase(payload);
|
||||
let page = currentFiltersPage.value + 1;
|
||||
store
|
||||
.dispatch('fetchFilteredConversations', {
|
||||
queryData: filterQueryGenerator(payload),
|
||||
queryData: filterQueryGenerator(transformedPayload),
|
||||
page,
|
||||
})
|
||||
.then(emitConversationLoaded);
|
||||
|
||||
showAdvancedFilters.value = false;
|
||||
}
|
||||
|
||||
function fetchSavedFilteredConversations(payload) {
|
||||
payload = useSnakeCase(payload);
|
||||
const transformedPayload = useSnakeCase(payload);
|
||||
let page = currentFiltersPage.value + 1;
|
||||
store
|
||||
.dispatch('fetchFilteredConversations', {
|
||||
queryData: payload,
|
||||
queryData: transformedPayload,
|
||||
page,
|
||||
})
|
||||
.then(emitConversationLoaded);
|
||||
}
|
||||
|
||||
function onApplyFilter(payload) {
|
||||
payload = useSnakeCase(payload);
|
||||
resetBulkActions();
|
||||
foldersQuery.value = filterQueryGenerator(payload);
|
||||
store.dispatch('conversationPage/reset');
|
||||
store.dispatch('emptyAllConversations');
|
||||
fetchFilteredConversations(payload);
|
||||
}
|
||||
|
||||
function closeAdvanceFiltersModal() {
|
||||
showAdvancedFilters.value = false;
|
||||
appliedFilter.value = [];
|
||||
}
|
||||
|
||||
function onUpdateSavedFilter(payload, folderName) {
|
||||
const transformedPayload = useSnakeCase(payload);
|
||||
const payloadData = {
|
||||
...unref(activeFolder),
|
||||
name: unref(folderName),
|
||||
query: filterQueryGenerator(transformedPayload),
|
||||
};
|
||||
store.dispatch('customViews/update', payloadData);
|
||||
closeAdvanceFiltersModal();
|
||||
}
|
||||
|
||||
function onClickOpenAddFoldersModal() {
|
||||
showAddFoldersModal.value = true;
|
||||
}
|
||||
|
||||
function onCloseAddFoldersModal() {
|
||||
showAddFoldersModal.value = false;
|
||||
}
|
||||
|
||||
function onClickOpenDeleteFoldersModal() {
|
||||
showDeleteFoldersModal.value = true;
|
||||
}
|
||||
|
||||
function onCloseDeleteFoldersModal() {
|
||||
showDeleteFoldersModal.value = false;
|
||||
}
|
||||
|
||||
function setParamsForEditFolderModal() {
|
||||
// Here we are setting the params for edit folder modal to show the existing values.
|
||||
|
||||
// For agent, team, inboxes,and campaigns we get only the id's from the query.
|
||||
// So we are mapping the id's to the actual values.
|
||||
|
||||
// For labels we get the name of the label from the query.
|
||||
// If we delete the label from the label list then we will not be able to show the label name.
|
||||
|
||||
// For custom attributes we get only attribute key.
|
||||
// So we are mapping it to find the input type of the attribute to show in the edit folder modal.
|
||||
return {
|
||||
agents: agentList.value,
|
||||
teams: teamsList.value,
|
||||
inboxes: inboxesList.value,
|
||||
labels: labels.value,
|
||||
campaigns: campaigns.value,
|
||||
languages: languages,
|
||||
countries: countries,
|
||||
priority: [
|
||||
{ id: 'low', name: t('CONVERSATION.PRIORITY.OPTIONS.LOW') },
|
||||
{ id: 'medium', name: t('CONVERSATION.PRIORITY.OPTIONS.MEDIUM') },
|
||||
{ id: 'high', name: t('CONVERSATION.PRIORITY.OPTIONS.HIGH') },
|
||||
{ id: 'urgent', name: t('CONVERSATION.PRIORITY.OPTIONS.URGENT') },
|
||||
],
|
||||
filterTypes: advancedFilterTypes.value,
|
||||
allCustomAttributes: conversationCustomAttributes.value,
|
||||
};
|
||||
}
|
||||
|
||||
function initializeExistingFilterToModal() {
|
||||
const statusFilter = initializeStatusAndAssigneeFilterToModal(
|
||||
activeStatus.value,
|
||||
currentUserDetails.value,
|
||||
activeAssigneeTab.value
|
||||
);
|
||||
// TODO: Remove the usage of useCamelCase after migrating useFilter to camelcase
|
||||
if (statusFilter) {
|
||||
appliedFilter.value = [...appliedFilter.value, useCamelCase(statusFilter)];
|
||||
}
|
||||
|
||||
// TODO: Remove the usage of useCamelCase after migrating useFilter to camelcase
|
||||
const otherFilters = initializeInboxTeamAndLabelFilterToModal(
|
||||
props.conversationInbox,
|
||||
inbox.value,
|
||||
props.teamId,
|
||||
activeTeam.value,
|
||||
props.label
|
||||
).map(useCamelCase);
|
||||
|
||||
appliedFilter.value = [...appliedFilter.value, ...otherFilters];
|
||||
}
|
||||
|
||||
function initializeFolderToFilterModal(newActiveFolder) {
|
||||
// Here we are setting the params for edit folder modal.
|
||||
// To show the existing values. when we click on edit folder button.
|
||||
|
||||
// Here we get the query from the active folder.
|
||||
// And we are mapping the query to the actual values.
|
||||
// To show in the edit folder modal by the help of generateValuesForEditCustomViews helper.
|
||||
const query = unref(newActiveFolder)?.query?.payload;
|
||||
if (!Array.isArray(query)) return;
|
||||
|
||||
const newFilters = query.map(filter => {
|
||||
const transformed = useCamelCase(filter);
|
||||
const values = Array.isArray(transformed.values)
|
||||
? generateValuesForEditCustomViews(
|
||||
useSnakeCase(filter),
|
||||
setParamsForEditFolderModal()
|
||||
)
|
||||
: [];
|
||||
|
||||
return {
|
||||
attributeKey: transformed.attributeKey,
|
||||
attributeModel: transformed.attributeModel,
|
||||
customAttributeType: transformed.customAttributeType,
|
||||
filterOperator: transformed.filterOperator,
|
||||
queryOperator: transformed.queryOperator ?? 'and',
|
||||
values,
|
||||
};
|
||||
});
|
||||
|
||||
appliedFilter.value = [...appliedFilter.value, ...newFilters];
|
||||
}
|
||||
|
||||
function initalizeAppliedFiltersToModal() {
|
||||
appliedFilter.value = [...appliedFilters.value];
|
||||
}
|
||||
|
||||
function onToggleAdvanceFiltersModal() {
|
||||
if (showAdvancedFilters.value === true) {
|
||||
closeAdvanceFiltersModal();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hasAppliedFilters.value && !hasActiveFolders.value) {
|
||||
initializeExistingFilterToModal();
|
||||
}
|
||||
if (hasActiveFolders.value) {
|
||||
initializeFolderToFilterModal(activeFolder.value);
|
||||
}
|
||||
if (hasAppliedFilters.value) {
|
||||
initalizeAppliedFiltersToModal();
|
||||
}
|
||||
|
||||
showAdvancedFilters.value = true;
|
||||
}
|
||||
|
||||
function fetchConversations() {
|
||||
store.dispatch('updateChatListFilters', conversationFilters.value);
|
||||
store.dispatch('fetchAllConversations').then(emitConversationLoaded);
|
||||
}
|
||||
|
||||
function onApplyFilter(payload) {
|
||||
resetBulkActions();
|
||||
store.dispatch('conversationPage/reset');
|
||||
store.dispatch('emptyAllConversations');
|
||||
fetchFilteredConversations(payload);
|
||||
}
|
||||
|
||||
function onOpenAddFolders() {
|
||||
const lastItemOfFolder = folders.value[folders.value.length - 1];
|
||||
const lastItemId = lastItemOfFolder.id;
|
||||
router.push({
|
||||
name: 'folder_conversations',
|
||||
params: { id: lastItemId },
|
||||
});
|
||||
}
|
||||
|
||||
function onOpenDeleteFolders() {
|
||||
if (folders.value.length > 0) {
|
||||
const lastItemOfFolder = folders.value[folders.value.length - 1];
|
||||
const lastItemId = lastItemOfFolder.id;
|
||||
router.push({
|
||||
name: 'folder_conversations',
|
||||
params: { id: lastItemId },
|
||||
});
|
||||
} else {
|
||||
router.push({ name: 'home' });
|
||||
fetchConversations();
|
||||
}
|
||||
}
|
||||
|
||||
function resetAndFetchData() {
|
||||
appliedFilter.value = [];
|
||||
resetBulkActions();
|
||||
store.dispatch('conversationPage/reset');
|
||||
store.dispatch('emptyAllConversations');
|
||||
store.dispatch('clearConversationFilters');
|
||||
if (hasActiveFolders.value) {
|
||||
if (hasActiveFolders.value && activeFolder.value?.query) {
|
||||
const payload = activeFolder.value.query;
|
||||
fetchSavedFilteredConversations(payload);
|
||||
}
|
||||
@@ -579,7 +355,7 @@ function loadMoreConversations() {
|
||||
|
||||
if (!hasAppliedFiltersOrActiveFolders.value) {
|
||||
fetchConversations();
|
||||
} else if (hasActiveFolders.value) {
|
||||
} else if (hasActiveFolders.value && activeFolder.value?.query) {
|
||||
const payload = activeFolder.value.query;
|
||||
fetchSavedFilteredConversations(payload);
|
||||
} else if (hasAppliedFilters.value) {
|
||||
@@ -609,116 +385,6 @@ function onBasicFilterChange(value, type) {
|
||||
resetAndFetchData();
|
||||
}
|
||||
|
||||
function openLastSavedItemInFolder() {
|
||||
const lastItemOfFolder = folders.value[folders.value.length - 1];
|
||||
const lastItemId = lastItemOfFolder.id;
|
||||
router.push({
|
||||
name: 'folder_conversations',
|
||||
params: { id: lastItemId },
|
||||
});
|
||||
}
|
||||
|
||||
function openLastItemAfterDeleteInFolder() {
|
||||
if (folders.value.length > 0) {
|
||||
openLastSavedItemInFolder();
|
||||
} else {
|
||||
router.push({ name: 'home' });
|
||||
fetchConversations();
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
conversationId,
|
||||
});
|
||||
store.dispatch('assignPriority', { conversationId, priority }).then(() => {
|
||||
useTrack(CONVERSATION_EVENTS.CHANGE_PRIORITY, {
|
||||
newValue: priority,
|
||||
from: 'Context menu',
|
||||
});
|
||||
useAlert(
|
||||
t('CONVERSATION.PRIORITY.CHANGE_PRIORITY.SUCCESSFUL', {
|
||||
priority,
|
||||
conversationId,
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
async function markAsUnread(conversationId) {
|
||||
try {
|
||||
await store.dispatch('markMessagesUnread', {
|
||||
id: conversationId,
|
||||
});
|
||||
redirectToConversationList();
|
||||
} catch (error) {
|
||||
// Ignore error
|
||||
}
|
||||
}
|
||||
async function markAsRead(conversationId) {
|
||||
try {
|
||||
await store.dispatch('markMessagesRead', {
|
||||
id: conversationId,
|
||||
});
|
||||
} catch (error) {
|
||||
// Ignore error
|
||||
}
|
||||
}
|
||||
|
||||
async function onAssignTeam(team, conversationId = null) {
|
||||
try {
|
||||
await store.dispatch('assignTeam', {
|
||||
conversationId,
|
||||
teamId: team.id,
|
||||
});
|
||||
useAlert(
|
||||
t('CONVERSATION.CARD_CONTEXT_MENU.API.TEAM_ASSIGNMENT.SUCCESFUL', {
|
||||
team: team.name,
|
||||
conversationId,
|
||||
})
|
||||
);
|
||||
} catch (error) {
|
||||
useAlert(t('CONVERSATION.CARD_CONTEXT_MENU.API.TEAM_ASSIGNMENT.FAILED'));
|
||||
}
|
||||
}
|
||||
|
||||
function toggleConversationStatus(conversationId, status, snoozedUntil) {
|
||||
store
|
||||
.dispatch('toggleStatus', {
|
||||
conversationId,
|
||||
status,
|
||||
snoozedUntil,
|
||||
})
|
||||
.then(() => {
|
||||
useAlert(t('CONVERSATION.CHANGE_STATUS'));
|
||||
});
|
||||
}
|
||||
|
||||
function allSelectedConversationsStatus(status) {
|
||||
if (!selectedConversations.value.length) return false;
|
||||
return selectedConversations.value.every(item => {
|
||||
@@ -750,45 +416,19 @@ 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);
|
||||
provide('assignTeam', onAssignTeam);
|
||||
provide('assignLabels', onAssignLabels);
|
||||
provide('updateConversationStatus', toggleConversationStatus);
|
||||
provide('toggleContextMenu', onContextMenuToggle);
|
||||
provide('markAsUnread', markAsUnread);
|
||||
provide('markAsRead', markAsRead);
|
||||
provide('assignPriority', assignPriority);
|
||||
provide('isConversationSelected', isConversationSelected);
|
||||
provide('deleteConversation', handleDelete);
|
||||
|
||||
watch(activeTeam, () => resetAndFetchData());
|
||||
|
||||
watch(
|
||||
computed(() => props.conversationInbox),
|
||||
() => resetAndFetchData()
|
||||
);
|
||||
watch(
|
||||
computed(() => props.teamId),
|
||||
() => resetAndFetchData()
|
||||
);
|
||||
watch(
|
||||
computed(() => props.label),
|
||||
() => resetAndFetchData()
|
||||
@@ -797,6 +437,10 @@ watch(
|
||||
computed(() => props.conversationType),
|
||||
() => resetAndFetchData()
|
||||
);
|
||||
watch(
|
||||
computed(() => props.foldersId),
|
||||
() => resetAndFetchData()
|
||||
);
|
||||
|
||||
watch(activeFolder, (newVal, oldVal) => {
|
||||
if (newVal !== oldVal) {
|
||||
@@ -833,32 +477,24 @@ watch(conversationFilters, (newVal, oldVal) => {
|
||||
:is-on-expanded-layout="isOnExpandedLayout"
|
||||
:conversation-stats="conversationStats"
|
||||
:is-list-loading="chatListLoading && !conversationList.length"
|
||||
@add-folders="onClickOpenAddFoldersModal"
|
||||
@delete-folders="onClickOpenDeleteFoldersModal"
|
||||
@filters-modal="onToggleAdvanceFiltersModal"
|
||||
@reset-filters="resetAndFetchData"
|
||||
@filters-modal="chatListFiltersRef?.onToggleAdvanceFiltersModal"
|
||||
@basic-filter-change="onBasicFilterChange"
|
||||
/>
|
||||
|
||||
<TeleportWithDirection
|
||||
v-if="showAddFoldersModal"
|
||||
to="#saveFilterTeleportTarget"
|
||||
>
|
||||
<SaveCustomView
|
||||
v-model="appliedFilter"
|
||||
:custom-views-query="foldersQuery"
|
||||
:open-last-saved-item="openLastSavedItemInFolder"
|
||||
@close="onCloseAddFoldersModal"
|
||||
/>
|
||||
</TeleportWithDirection>
|
||||
|
||||
<DeleteCustomViews
|
||||
v-if="showDeleteFoldersModal"
|
||||
v-model:show="showDeleteFoldersModal"
|
||||
:active-custom-view="activeFolder"
|
||||
:custom-views-id="foldersId"
|
||||
:open-last-item-after-delete="openLastItemAfterDeleteInFolder"
|
||||
@close="onCloseDeleteFoldersModal"
|
||||
<ChatListFilters
|
||||
ref="chatListFiltersRef"
|
||||
:conversation-inbox="conversationInbox"
|
||||
:team-id="teamId"
|
||||
:label="label"
|
||||
:folders-id="foldersId"
|
||||
:active-status="activeStatus"
|
||||
:active-assignee-tab="activeAssigneeTab"
|
||||
:current-user-details="{ id: currentUser.id, name: currentUser.name }"
|
||||
:has-active-folders="hasActiveFolders"
|
||||
:has-applied-filters="hasAppliedFilters"
|
||||
@apply-filter="onApplyFilter"
|
||||
@open-add-folders="onOpenAddFolders"
|
||||
@open-delete-folders="onOpenDeleteFolders"
|
||||
/>
|
||||
|
||||
<ChatTypeTabs
|
||||
@@ -868,13 +504,15 @@ watch(conversationFilters, (newVal, oldVal) => {
|
||||
is-compact
|
||||
@chat-tab-change="updateAssigneeTab"
|
||||
/>
|
||||
|
||||
<p
|
||||
v-if="!chatListLoading && !conversationList.length"
|
||||
class="flex items-center justify-center p-4 overflow-auto"
|
||||
>
|
||||
{{ $t('CHAT_LIST.LIST.404') }}
|
||||
</p>
|
||||
<ConversationActiveFiltersPreview
|
||||
:active-folder="activeFolder"
|
||||
:has-active-folders="hasActiveFolders"
|
||||
:is-on-expanded-layout="isOnExpandedLayout"
|
||||
@clear-filters="resetAndFetchData"
|
||||
@open-filter="chatListFiltersRef?.onToggleAdvanceFiltersModal"
|
||||
@save-folder="chatListFiltersRef?.onClickOpenAddFoldersModal"
|
||||
@delete-folder="chatListFiltersRef?.onClickOpenDeleteFoldersModal"
|
||||
/>
|
||||
<ConversationBulkActions
|
||||
v-if="selectedConversations.length"
|
||||
:conversations="selectedConversations"
|
||||
@@ -884,11 +522,15 @@ watch(conversationFilters, (newVal, oldVal) => {
|
||||
:show-resolved-action="allSelectedConversationsStatus('resolved')"
|
||||
:show-snoozed-action="allSelectedConversationsStatus('snoozed')"
|
||||
@select-all-conversations="toggleSelectAll"
|
||||
@assign-agent="onAssignAgent"
|
||||
@update-conversations="onUpdateConversations"
|
||||
@assign-labels="onAssignLabels"
|
||||
@assign-team="onAssignTeamsForBulk"
|
||||
/>
|
||||
|
||||
<p
|
||||
v-if="!chatListLoading && !conversationList.length"
|
||||
class="flex items-center justify-center p-4 overflow-auto"
|
||||
>
|
||||
{{ $t('CHAT_LIST.LIST.404') }}
|
||||
</p>
|
||||
|
||||
<ConversationList
|
||||
:items="conversationList"
|
||||
:is-loading="chatListLoading"
|
||||
@@ -902,32 +544,5 @@ watch(conversationFilters, (newVal, oldVal) => {
|
||||
:is-expanded-layout="isOnExpandedLayout"
|
||||
@load-more="loadMoreConversations"
|
||||
/>
|
||||
|
||||
<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"
|
||||
>
|
||||
<ConversationFilter
|
||||
v-model="appliedFilter"
|
||||
:folder-name="activeFolderName"
|
||||
:is-folder-view="hasActiveFolders"
|
||||
@apply-filter="onApplyFilter"
|
||||
@update-folder="onUpdateSavedFilter"
|
||||
@close="closeAdvanceFiltersModal"
|
||||
/>
|
||||
</TeleportWithDirection>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user