feat: support bulk select and delete for documents (#13907)
This commit is contained in:
@@ -4,9 +4,13 @@ import { useMapGetter, useStore } from 'dashboard/composables/store';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { FEATURE_FLAGS } from 'dashboard/featureFlags';
|
||||
import { useAccount } from 'dashboard/composables/useAccount';
|
||||
import { usePolicy } from 'dashboard/composables/usePolicy';
|
||||
|
||||
import DeleteDialog from 'dashboard/components-next/captain/pageComponents/DeleteDialog.vue';
|
||||
import DocumentCard from 'dashboard/components-next/captain/assistant/DocumentCard.vue';
|
||||
import BulkSelectBar from 'dashboard/components-next/captain/assistant/BulkSelectBar.vue';
|
||||
import BulkDeleteDialog from 'dashboard/components-next/captain/pageComponents/BulkDeleteDialog.vue';
|
||||
import Policy from 'dashboard/components/policy.vue';
|
||||
import PageLayout from 'dashboard/components-next/captain/PageLayout.vue';
|
||||
import CaptainPaywall from 'dashboard/components-next/captain/pageComponents/Paywall.vue';
|
||||
import RelatedResponses from 'dashboard/components-next/captain/pageComponents/document/RelatedResponses.vue';
|
||||
@@ -14,9 +18,12 @@ import CreateDocumentDialog from 'dashboard/components-next/captain/pageComponen
|
||||
import DocumentPageEmptyState from 'dashboard/components-next/captain/pageComponents/emptyStates/DocumentPageEmptyState.vue';
|
||||
import FeatureSpotlightPopover from 'dashboard/components-next/feature-spotlight/FeatureSpotlightPopover.vue';
|
||||
import LimitBanner from 'dashboard/components-next/captain/pageComponents/document/LimitBanner.vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const route = useRoute();
|
||||
const store = useStore();
|
||||
const { t } = useI18n();
|
||||
const { checkPermissions } = usePolicy();
|
||||
|
||||
const { isOnChatwootCloud } = useAccount();
|
||||
const uiFlags = useMapGetter('captainDocuments/getUIFlags');
|
||||
@@ -25,9 +32,13 @@ const isFetching = computed(() => uiFlags.value.fetchingList);
|
||||
const documentsMeta = useMapGetter('captainDocuments/getMeta');
|
||||
|
||||
const selectedAssistantId = computed(() => Number(route.params.assistantId));
|
||||
const canManageDocuments = computed(() => checkPermissions(['administrator']));
|
||||
|
||||
const selectedDocument = ref(null);
|
||||
const deleteDocumentDialog = ref(null);
|
||||
const bulkDeleteDialog = ref(null);
|
||||
const bulkSelectedIds = ref(new Set());
|
||||
const hoveredCard = ref(null);
|
||||
|
||||
const handleDelete = () => {
|
||||
deleteDocumentDialog.value.dialogRef.open();
|
||||
@@ -78,7 +89,14 @@ const fetchDocuments = (page = 1) => {
|
||||
store.dispatch('captainDocuments/get', filterParams);
|
||||
};
|
||||
|
||||
const onPageChange = page => fetchDocuments(page);
|
||||
const onPageChange = page => {
|
||||
const hadSelection = bulkSelectedIds.value.size > 0;
|
||||
fetchDocuments(page);
|
||||
|
||||
if (hadSelection) {
|
||||
bulkSelectedIds.value = new Set();
|
||||
}
|
||||
};
|
||||
|
||||
const onDeleteSuccess = () => {
|
||||
if (documents.value?.length === 0 && documentsMeta.value?.page > 1) {
|
||||
@@ -86,6 +104,58 @@ const onDeleteSuccess = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const buildSelectedCountLabel = computed(() => {
|
||||
const count = documents.value?.length || 0;
|
||||
const isAllSelected = bulkSelectedIds.value.size === count && count > 0;
|
||||
return isAllSelected
|
||||
? t('CAPTAIN.DOCUMENTS.UNSELECT_ALL', { count })
|
||||
: t('CAPTAIN.DOCUMENTS.SELECT_ALL', { count });
|
||||
});
|
||||
|
||||
const selectedCountLabel = computed(() => {
|
||||
return t('CAPTAIN.DOCUMENTS.SELECTED', {
|
||||
count: bulkSelectedIds.value.size,
|
||||
});
|
||||
});
|
||||
|
||||
const hasBulkSelection = computed(() => bulkSelectedIds.value.size > 0);
|
||||
|
||||
const shouldShowSelectionControl = docId => {
|
||||
return (
|
||||
canManageDocuments.value &&
|
||||
(hoveredCard.value === docId || hasBulkSelection.value)
|
||||
);
|
||||
};
|
||||
|
||||
const handleCardHover = (isHovered, id) => {
|
||||
hoveredCard.value = isHovered ? id : null;
|
||||
};
|
||||
|
||||
const handleCardSelect = id => {
|
||||
if (!canManageDocuments.value) return;
|
||||
const selected = new Set(bulkSelectedIds.value);
|
||||
selected[selected.has(id) ? 'delete' : 'add'](id);
|
||||
bulkSelectedIds.value = selected;
|
||||
};
|
||||
|
||||
const fetchDocumentsAfterBulkAction = () => {
|
||||
const hasNoDocumentsLeft = documents.value?.length === 0;
|
||||
const currentPage = documentsMeta.value?.page;
|
||||
|
||||
if (hasNoDocumentsLeft) {
|
||||
const pageToFetch = currentPage > 1 ? currentPage - 1 : currentPage;
|
||||
fetchDocuments(pageToFetch);
|
||||
} else {
|
||||
fetchDocuments(currentPage);
|
||||
}
|
||||
|
||||
bulkSelectedIds.value = new Set();
|
||||
};
|
||||
|
||||
const onBulkDeleteSuccess = () => {
|
||||
fetchDocumentsAfterBulkAction();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
fetchDocuments();
|
||||
});
|
||||
@@ -106,6 +176,21 @@ onMounted(() => {
|
||||
@update:current-page="onPageChange"
|
||||
@click="handleCreateDocument"
|
||||
>
|
||||
<template #subHeader>
|
||||
<Policy :permissions="['administrator']">
|
||||
<BulkSelectBar
|
||||
v-model="bulkSelectedIds"
|
||||
:all-items="documents"
|
||||
:select-all-label="buildSelectedCountLabel"
|
||||
:selected-count-label="selectedCountLabel"
|
||||
:delete-label="$t('CAPTAIN.DOCUMENTS.BULK_DELETE_BUTTON')"
|
||||
class="w-fit"
|
||||
:class="{ 'mb-2': bulkSelectedIds.size > 0 }"
|
||||
@bulk-delete="bulkDeleteDialog.dialogRef.open()"
|
||||
/>
|
||||
</Policy>
|
||||
</template>
|
||||
|
||||
<template #knowMore>
|
||||
<FeatureSpotlightPopover
|
||||
:button-label="$t('CAPTAIN.HEADER_KNOW_MORE')"
|
||||
@@ -138,7 +223,13 @@ onMounted(() => {
|
||||
:external-link="doc.external_link"
|
||||
:assistant="doc.assistant"
|
||||
:created-at="doc.created_at"
|
||||
:is-selected="canManageDocuments && bulkSelectedIds.has(doc.id)"
|
||||
:selectable="canManageDocuments"
|
||||
:show-selection-control="shouldShowSelectionControl(doc.id)"
|
||||
:show-menu="!bulkSelectedIds.has(doc.id)"
|
||||
@action="handleAction"
|
||||
@select="handleCardSelect"
|
||||
@hover="isHovered => handleCardHover(isHovered, doc.id)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -162,5 +253,12 @@ onMounted(() => {
|
||||
type="Documents"
|
||||
@delete-success="onDeleteSuccess"
|
||||
/>
|
||||
<BulkDeleteDialog
|
||||
v-if="bulkSelectedIds"
|
||||
ref="bulkDeleteDialog"
|
||||
:bulk-ids="bulkSelectedIds"
|
||||
type="AssistantDocument"
|
||||
@delete-success="onBulkDeleteSuccess"
|
||||
/>
|
||||
</PageLayout>
|
||||
</template>
|
||||
|
||||
@@ -316,7 +316,7 @@ onMounted(() => {
|
||||
v-if="bulkSelectedIds"
|
||||
ref="bulkDeleteDialog"
|
||||
:bulk-ids="bulkSelectedIds"
|
||||
type="Responses"
|
||||
type="AssistantResponse"
|
||||
@delete-success="onBulkDeleteSuccess"
|
||||
/>
|
||||
|
||||
|
||||
@@ -361,7 +361,7 @@ onMounted(() => {
|
||||
v-if="bulkSelectedIds"
|
||||
ref="bulkDeleteDialog"
|
||||
:bulk-ids="bulkSelectedIds"
|
||||
type="Responses"
|
||||
type="AssistantResponse"
|
||||
@delete-success="onBulkDeleteSuccess"
|
||||
/>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user