feat: knowledge directory
This commit is contained in:
@@ -32,7 +32,11 @@
|
||||
updateFileFromKnowledgeById,
|
||||
updateKnowledgeById,
|
||||
updateKnowledgeAccessGrants,
|
||||
searchKnowledgeFilesById
|
||||
searchKnowledgeFilesById,
|
||||
createKnowledgeDirectory,
|
||||
updateKnowledgeDirectory,
|
||||
deleteKnowledgeDirectory,
|
||||
moveFileInKnowledge
|
||||
} from '$lib/apis/knowledge';
|
||||
import { processWeb, processYoutubeVideo } from '$lib/apis/retrieval';
|
||||
|
||||
@@ -44,8 +48,11 @@
|
||||
|
||||
import AddContentMenu from './KnowledgeBase/AddContentMenu.svelte';
|
||||
import AddTextContentModal from './KnowledgeBase/AddTextContentModal.svelte';
|
||||
import NewDirectoryModal from './KnowledgeBase/NewDirectoryModal.svelte';
|
||||
import KnowledgeBreadcrumbs from './KnowledgeBase/KnowledgeBreadcrumbs.svelte';
|
||||
|
||||
import SyncConfirmDialog from '../../common/ConfirmDialog.svelte';
|
||||
import ConfirmDialog from '../../common/ConfirmDialog.svelte';
|
||||
import Drawer from '$lib/components/common/Drawer.svelte';
|
||||
import ChevronLeft from '$lib/components/icons/ChevronLeft.svelte';
|
||||
import LockClosed from '$lib/components/icons/LockClosed.svelte';
|
||||
@@ -63,6 +70,7 @@
|
||||
|
||||
let showAddWebpageModal = false;
|
||||
let showAddTextContentModal = false;
|
||||
let showNewDirectoryModal = false;
|
||||
|
||||
let showSyncConfirmModal = false;
|
||||
let showAccessControlModal = false;
|
||||
@@ -101,6 +109,15 @@
|
||||
let fileItems = null;
|
||||
let fileItemsTotal = null;
|
||||
|
||||
// Directory state
|
||||
let currentDirectoryId: string | null = null;
|
||||
let directoryItems = [];
|
||||
let breadcrumbs = [];
|
||||
|
||||
let showDeleteDirectoryConfirm = false;
|
||||
let pendingDeleteDirectoryId: string | null = null;
|
||||
let deleteDirectoryContents = true;
|
||||
|
||||
const reset = () => {
|
||||
currentPage = 1;
|
||||
};
|
||||
@@ -156,7 +173,8 @@
|
||||
viewOption,
|
||||
sortKey,
|
||||
direction,
|
||||
currentPage
|
||||
currentPage,
|
||||
currentDirectoryId
|
||||
).catch(() => {
|
||||
return null;
|
||||
});
|
||||
@@ -164,6 +182,8 @@
|
||||
if (res) {
|
||||
fileItems = res.items;
|
||||
fileItemsTotal = res.total;
|
||||
directoryItems = res.directories ?? [];
|
||||
breadcrumbs = res.breadcrumbs ?? [];
|
||||
}
|
||||
return res;
|
||||
};
|
||||
@@ -531,7 +551,12 @@
|
||||
};
|
||||
|
||||
const addFileHandler = async (fileId) => {
|
||||
const res = await addFileToKnowledgeById(localStorage.token, id, fileId).catch((e) => {
|
||||
const res = await addFileToKnowledgeById(
|
||||
localStorage.token,
|
||||
id,
|
||||
fileId,
|
||||
currentDirectoryId
|
||||
).catch((e) => {
|
||||
toast.error(`${e}`);
|
||||
return null;
|
||||
});
|
||||
@@ -545,6 +570,88 @@
|
||||
}
|
||||
};
|
||||
|
||||
// Directory handlers
|
||||
const navigateToDirectory = (directoryId: string | null) => {
|
||||
currentDirectoryId = directoryId;
|
||||
currentPage = 1;
|
||||
selectedFileId = null;
|
||||
selectedFile = null;
|
||||
getItemsPage();
|
||||
};
|
||||
|
||||
const createDirectoryHandler = async (name: string) => {
|
||||
const res = await createKnowledgeDirectory(
|
||||
localStorage.token,
|
||||
knowledge.id,
|
||||
name,
|
||||
currentDirectoryId
|
||||
).catch((e) => {
|
||||
toast.error(`${e}`);
|
||||
return null;
|
||||
});
|
||||
|
||||
if (res) {
|
||||
toast.success($i18n.t('Directory created.'));
|
||||
getItemsPage();
|
||||
}
|
||||
};
|
||||
|
||||
const renameDirectoryHandler = async (dirId: string, name: string) => {
|
||||
const res = await updateKnowledgeDirectory(localStorage.token, knowledge.id, dirId, {
|
||||
name
|
||||
}).catch((e) => {
|
||||
toast.error(`${e}`);
|
||||
return null;
|
||||
});
|
||||
|
||||
if (res) {
|
||||
toast.success($i18n.t('Directory renamed.'));
|
||||
getItemsPage();
|
||||
}
|
||||
};
|
||||
|
||||
const confirmDeleteDirectory = (dirId: string) => {
|
||||
pendingDeleteDirectoryId = dirId;
|
||||
showDeleteDirectoryConfirm = true;
|
||||
};
|
||||
|
||||
const deleteDirectoryHandler = async (moveFiles: boolean) => {
|
||||
if (!pendingDeleteDirectoryId) return;
|
||||
|
||||
const res = await deleteKnowledgeDirectory(
|
||||
localStorage.token,
|
||||
knowledge.id,
|
||||
pendingDeleteDirectoryId,
|
||||
moveFiles
|
||||
).catch((e) => {
|
||||
toast.error(`${e}`);
|
||||
return null;
|
||||
});
|
||||
|
||||
if (res) {
|
||||
toast.success($i18n.t('Directory deleted.'));
|
||||
getItemsPage();
|
||||
}
|
||||
pendingDeleteDirectoryId = null;
|
||||
};
|
||||
|
||||
const moveFileToDirectoryHandler = async (fileId: string, directoryId: string | null) => {
|
||||
const res = await moveFileInKnowledge(
|
||||
localStorage.token,
|
||||
knowledge.id,
|
||||
fileId,
|
||||
directoryId
|
||||
).catch((e) => {
|
||||
toast.error(`${e}`);
|
||||
return null;
|
||||
});
|
||||
|
||||
if (res) {
|
||||
toast.success($i18n.t('File moved.'));
|
||||
getItemsPage();
|
||||
}
|
||||
};
|
||||
|
||||
const deleteFileHandler = async (fileId) => {
|
||||
try {
|
||||
console.log('Starting file deletion process for:', fileId);
|
||||
@@ -806,6 +913,13 @@
|
||||
}}
|
||||
/>
|
||||
|
||||
<NewDirectoryModal
|
||||
bind:show={showNewDirectoryModal}
|
||||
on:submit={(e) => {
|
||||
createDirectoryHandler(e.detail.name);
|
||||
}}
|
||||
/>
|
||||
|
||||
<input
|
||||
id="files-input"
|
||||
bind:files={inputFiles}
|
||||
@@ -942,6 +1056,8 @@
|
||||
onUpload={(data) => {
|
||||
if (data.type === 'directory') {
|
||||
uploadDirectoryHandler();
|
||||
} else if (data.type === 'new_directory') {
|
||||
showNewDirectoryModal = true;
|
||||
} else if (data.type === 'web') {
|
||||
showAddWebpageModal = true;
|
||||
} else if (data.type === 'text') {
|
||||
@@ -959,6 +1075,12 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if currentDirectoryId !== null}
|
||||
<div class="px-5 -mt-1 pb-2">
|
||||
<KnowledgeBreadcrumbs rootLabel={knowledge.name} {breadcrumbs} onNavigate={(dirId) => navigateToDirectory(dirId)} />
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="px-3 flex justify-between">
|
||||
<div
|
||||
class="flex w-full bg-transparent overflow-x-auto scrollbar-none"
|
||||
@@ -1020,10 +1142,11 @@
|
||||
<div class="flex-1 flex">
|
||||
<div class=" flex flex-col w-full space-x-2 rounded-lg h-full">
|
||||
<div class="w-full h-full flex flex-col min-h-full">
|
||||
{#if fileItems.length > 0}
|
||||
{#if fileItems.length > 0 || directoryItems.length > 0}
|
||||
<div class=" flex overflow-y-auto h-full w-full scrollbar-hidden text-xs">
|
||||
<Files
|
||||
files={fileItems}
|
||||
directories={directoryItems}
|
||||
{knowledge}
|
||||
{selectedFileId}
|
||||
onClick={(fileId) => {
|
||||
@@ -1044,6 +1167,11 @@
|
||||
|
||||
deleteFileHandler(fileId);
|
||||
}}
|
||||
onNavigateDirectory={(dirId) => navigateToDirectory(dirId)}
|
||||
onRenameDirectory={(id, name) => renameDirectoryHandler(id, name)}
|
||||
onDeleteDirectory={(id) => confirmDeleteDirectory(id)}
|
||||
onMoveFileToDirectory={(fileId, dirId) =>
|
||||
moveFileToDirectoryHandler(fileId, dirId)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1133,3 +1261,26 @@
|
||||
<Spinner className="size-5" />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<ConfirmDialog
|
||||
bind:show={showDeleteDirectoryConfirm}
|
||||
title={$i18n.t('Delete directory?')}
|
||||
on:confirm={() => {
|
||||
deleteDirectoryHandler(!deleteDirectoryContents);
|
||||
}}
|
||||
on:cancel={() => {
|
||||
pendingDeleteDirectoryId = null;
|
||||
}}
|
||||
>
|
||||
<div class="text-sm text-gray-700 dark:text-gray-300 flex-1 line-clamp-3 mb-2">
|
||||
{$i18n.t(`Are you sure you want to delete this directory?`)}
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-1.5">
|
||||
<input type="checkbox" bind:checked={deleteDirectoryContents} />
|
||||
|
||||
<div class="text-xs text-gray-500">
|
||||
{$i18n.t('Delete all contents inside this directory')}
|
||||
</div>
|
||||
</div>
|
||||
</ConfirmDialog>
|
||||
|
||||
Reference in New Issue
Block a user