feat: add move functionality to DocumentList and FAQList for improved document management

This commit is contained in:
mlogclub
2026-06-02 16:21:05 +08:00
parent 44ec2921d4
commit ae62f7ecce
2 changed files with 62 additions and 12 deletions
@@ -152,6 +152,7 @@ export function DocumentList({ knowledgeBaseId, onActionStateChange }: DocumentL
const [moving, setMoving] = useState(false); const [moving, setMoving] = useState(false);
const [bulkMoveOpen, setBulkMoveOpen] = useState(false); const [bulkMoveOpen, setBulkMoveOpen] = useState(false);
const [selectedIds, setSelectedIds] = useState<number[]>([]); const [selectedIds, setSelectedIds] = useState<number[]>([]);
const [moveTargetIds, setMoveTargetIds] = useState<number[]>([]);
const [contextMenuDocumentId, setContextMenuDocumentId] = useState<number | null>(null); const [contextMenuDocumentId, setContextMenuDocumentId] = useState<number | null>(null);
const [selectedDirectoryId, setSelectedDirectoryId] = useState<number | null>(null); const [selectedDirectoryId, setSelectedDirectoryId] = useState<number | null>(null);
const [actionLoadingMap, setActionLoadingMap] = useState<Record<number, { rebuildIndex: boolean; delete: boolean }>>({}); const [actionLoadingMap, setActionLoadingMap] = useState<Record<number, { rebuildIndex: boolean; delete: boolean }>>({});
@@ -272,6 +273,11 @@ export function DocumentList({ knowledgeBaseId, onActionStateChange }: DocumentL
setSelectedIds(checked ? currentPageIds : []); setSelectedIds(checked ? currentPageIds : []);
} }
function openMoveDialog(ids: number[]) {
setMoveTargetIds(ids);
setBulkMoveOpen(true);
}
const openCreateDialog = useCallback(() => { const openCreateDialog = useCallback(() => {
setEditingItem(null); setEditingItem(null);
setDialogOpen(true); setDialogOpen(true);
@@ -373,7 +379,8 @@ export function DocumentList({ knowledgeBaseId, onActionStateChange }: DocumentL
} }
async function handleBatchMove(directoryId: number) { async function handleBatchMove(directoryId: number) {
if (!knowledgeBaseId || selectedIds.length === 0) { const ids = moveTargetIds.length > 0 ? moveTargetIds : selectedIds;
if (!knowledgeBaseId || ids.length === 0) {
return; return;
} }
setMoving(true); setMoving(true);
@@ -381,10 +388,11 @@ export function DocumentList({ knowledgeBaseId, onActionStateChange }: DocumentL
await batchMoveKnowledgeDocuments({ await batchMoveKnowledgeDocuments({
knowledgeBaseId, knowledgeBaseId,
directoryId, directoryId,
ids: selectedIds, ids,
}); });
toast.success(t("knowledge.batchMoved", { count: selectedIds.length })); toast.success(t("knowledge.batchMoved", { count: ids.length }));
setBulkMoveOpen(false); setBulkMoveOpen(false);
setMoveTargetIds([]);
setSelectedIds([]); setSelectedIds([]);
await loadData(); await loadData();
} catch (error) { } catch (error) {
@@ -540,6 +548,10 @@ export function DocumentList({ knowledgeBaseId, onActionStateChange }: DocumentL
<PencilIcon className="mr-2 size-3.5" /> <PencilIcon className="mr-2 size-3.5" />
{t("knowledge.edit")} {t("knowledge.edit")}
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem onClick={() => openMoveDialog([item.id])}>
<FolderInputIcon className="mr-2 size-3.5" />
{t("knowledge.move")}
</DropdownMenuItem>
<DropdownMenuItem onClick={() => void handleBuildIndex(item)}> <DropdownMenuItem onClick={() => void handleBuildIndex(item)}>
<WrenchIcon className="mr-2 size-3.5" /> <WrenchIcon className="mr-2 size-3.5" />
{actionLoadingMap[item.id]?.rebuildIndex ? t("knowledge.running") : t("knowledge.rebuildIndex")} {actionLoadingMap[item.id]?.rebuildIndex ? t("knowledge.running") : t("knowledge.rebuildIndex")}
@@ -561,6 +573,10 @@ export function DocumentList({ knowledgeBaseId, onActionStateChange }: DocumentL
<PencilIcon className="mr-2 size-3.5" /> <PencilIcon className="mr-2 size-3.5" />
{t("knowledge.edit")} {t("knowledge.edit")}
</ContextMenuItem> </ContextMenuItem>
<ContextMenuItem onClick={() => openMoveDialog([item.id])}>
<FolderInputIcon className="mr-2 size-3.5" />
{t("knowledge.move")}
</ContextMenuItem>
<ContextMenuItem onClick={() => void handleBuildIndex(item)} disabled={actionLoadingMap[item.id]?.rebuildIndex}> <ContextMenuItem onClick={() => void handleBuildIndex(item)} disabled={actionLoadingMap[item.id]?.rebuildIndex}>
<WrenchIcon className="mr-2 size-3.5" /> <WrenchIcon className="mr-2 size-3.5" />
{actionLoadingMap[item.id]?.rebuildIndex ? t("knowledge.running") : t("knowledge.rebuildIndex")} {actionLoadingMap[item.id]?.rebuildIndex ? t("knowledge.running") : t("knowledge.rebuildIndex")}
@@ -626,6 +642,10 @@ export function DocumentList({ knowledgeBaseId, onActionStateChange }: DocumentL
<PencilIcon className="mr-2 size-3.5" /> <PencilIcon className="mr-2 size-3.5" />
{t("knowledge.edit")} {t("knowledge.edit")}
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem onClick={() => openMoveDialog([item.id])}>
<FolderInputIcon className="mr-2 size-3.5" />
{t("knowledge.move")}
</DropdownMenuItem>
<DropdownMenuItem onClick={() => void handleBuildIndex(item)}> <DropdownMenuItem onClick={() => void handleBuildIndex(item)}>
<WrenchIcon className="mr-2 size-3.5" /> <WrenchIcon className="mr-2 size-3.5" />
{actionLoadingMap[item.id]?.rebuildIndex ? t("knowledge.running") : t("knowledge.rebuildIndex")} {actionLoadingMap[item.id]?.rebuildIndex ? t("knowledge.running") : t("knowledge.rebuildIndex")}
@@ -646,6 +666,10 @@ export function DocumentList({ knowledgeBaseId, onActionStateChange }: DocumentL
<PencilIcon className="mr-2 size-3.5" /> <PencilIcon className="mr-2 size-3.5" />
{t("knowledge.edit")} {t("knowledge.edit")}
</ContextMenuItem> </ContextMenuItem>
<ContextMenuItem onClick={() => openMoveDialog([item.id])}>
<FolderInputIcon className="mr-2 size-3.5" />
{t("knowledge.move")}
</ContextMenuItem>
<ContextMenuItem onClick={() => void handleBuildIndex(item)} disabled={actionLoadingMap[item.id]?.rebuildIndex}> <ContextMenuItem onClick={() => void handleBuildIndex(item)} disabled={actionLoadingMap[item.id]?.rebuildIndex}>
<WrenchIcon className="mr-2 size-3.5" /> <WrenchIcon className="mr-2 size-3.5" />
{actionLoadingMap[item.id]?.rebuildIndex ? t("knowledge.running") : t("knowledge.rebuildIndex")} {actionLoadingMap[item.id]?.rebuildIndex ? t("knowledge.running") : t("knowledge.rebuildIndex")}
@@ -680,7 +704,7 @@ export function DocumentList({ knowledgeBaseId, onActionStateChange }: DocumentL
variant="outline" variant="outline"
size="sm" size="sm"
className="h-7" className="h-7"
onClick={() => setBulkMoveOpen(true)} onClick={() => openMoveDialog(selectedIds)}
disabled={saving || moving} disabled={saving || moving}
> >
<FolderInputIcon className="size-3.5" /> <FolderInputIcon className="size-3.5" />
@@ -737,8 +761,13 @@ export function DocumentList({ knowledgeBaseId, onActionStateChange }: DocumentL
open={bulkMoveOpen} open={bulkMoveOpen}
knowledgeBaseId={knowledgeBaseId} knowledgeBaseId={knowledgeBaseId}
moving={moving} moving={moving}
selectedCount={selectedIds.length} selectedCount={moveTargetIds.length || selectedIds.length}
onOpenChange={setBulkMoveOpen} onOpenChange={(open) => {
setBulkMoveOpen(open);
if (!open) {
setMoveTargetIds([]);
}
}}
onSubmit={(directoryId) => void handleBatchMove(directoryId)} onSubmit={(directoryId) => void handleBatchMove(directoryId)}
/> />
</> </>
@@ -141,6 +141,7 @@ export function FAQList({
const [moving, setMoving] = useState(false); const [moving, setMoving] = useState(false);
const [bulkMoveOpen, setBulkMoveOpen] = useState(false); const [bulkMoveOpen, setBulkMoveOpen] = useState(false);
const [selectedIds, setSelectedIds] = useState<number[]>([]); const [selectedIds, setSelectedIds] = useState<number[]>([]);
const [moveTargetIds, setMoveTargetIds] = useState<number[]>([]);
const [contextMenuFAQId, setContextMenuFAQId] = useState<number | null>(null); const [contextMenuFAQId, setContextMenuFAQId] = useState<number | null>(null);
const [importing, setImporting] = useState(false); const [importing, setImporting] = useState(false);
const [importDialogOpen, setImportDialogOpen] = useState(false); const [importDialogOpen, setImportDialogOpen] = useState(false);
@@ -252,6 +253,11 @@ export function FAQList({
setSelectedIds(checked ? currentPageIds : []); setSelectedIds(checked ? currentPageIds : []);
} }
function openMoveDialog(ids: number[]) {
setMoveTargetIds(ids);
setBulkMoveOpen(true);
}
function openEditDialog(item: KnowledgeFAQ) { function openEditDialog(item: KnowledgeFAQ) {
setEditingItem(item); setEditingItem(item);
setDialogOpen(true); setDialogOpen(true);
@@ -330,7 +336,8 @@ export function FAQList({
} }
async function handleBatchMove(directoryId: number) { async function handleBatchMove(directoryId: number) {
if (!knowledgeBaseId || selectedIds.length === 0) { const ids = moveTargetIds.length > 0 ? moveTargetIds : selectedIds;
if (!knowledgeBaseId || ids.length === 0) {
return; return;
} }
setMoving(true); setMoving(true);
@@ -338,10 +345,11 @@ export function FAQList({
await batchMoveKnowledgeFAQs({ await batchMoveKnowledgeFAQs({
knowledgeBaseId, knowledgeBaseId,
directoryId, directoryId,
ids: selectedIds, ids,
}); });
toast.success(t("knowledge.batchMoved", { count: selectedIds.length })); toast.success(t("knowledge.batchMoved", { count: ids.length }));
setBulkMoveOpen(false); setBulkMoveOpen(false);
setMoveTargetIds([]);
setSelectedIds([]); setSelectedIds([]);
await loadData(); await loadData();
} catch (error) { } catch (error) {
@@ -471,6 +479,10 @@ export function FAQList({
<PencilIcon className="mr-2 size-3.5" /> <PencilIcon className="mr-2 size-3.5" />
{t("knowledge.edit")} {t("knowledge.edit")}
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem onClick={() => openMoveDialog([item.id])}>
<FolderInputIcon className="mr-2 size-3.5" />
{t("knowledge.move")}
</DropdownMenuItem>
<DropdownMenuItem onClick={() => void handleBuildIndex(item)}> <DropdownMenuItem onClick={() => void handleBuildIndex(item)}>
<WrenchIcon className="mr-2 size-3.5" /> <WrenchIcon className="mr-2 size-3.5" />
{actionLoadingMap[item.id]?.rebuildIndex ? t("knowledge.running") : t("knowledge.rebuildIndex")} {actionLoadingMap[item.id]?.rebuildIndex ? t("knowledge.running") : t("knowledge.rebuildIndex")}
@@ -491,6 +503,10 @@ export function FAQList({
<PencilIcon className="mr-2 size-3.5" /> <PencilIcon className="mr-2 size-3.5" />
{t("knowledge.edit")} {t("knowledge.edit")}
</ContextMenuItem> </ContextMenuItem>
<ContextMenuItem onClick={() => openMoveDialog([item.id])}>
<FolderInputIcon className="mr-2 size-3.5" />
{t("knowledge.move")}
</ContextMenuItem>
<ContextMenuItem onClick={() => void handleBuildIndex(item)} disabled={actionLoadingMap[item.id]?.rebuildIndex}> <ContextMenuItem onClick={() => void handleBuildIndex(item)} disabled={actionLoadingMap[item.id]?.rebuildIndex}>
<WrenchIcon className="mr-2 size-3.5" /> <WrenchIcon className="mr-2 size-3.5" />
{actionLoadingMap[item.id]?.rebuildIndex ? t("knowledge.running") : t("knowledge.rebuildIndex")} {actionLoadingMap[item.id]?.rebuildIndex ? t("knowledge.running") : t("knowledge.rebuildIndex")}
@@ -524,7 +540,7 @@ export function FAQList({
variant="outline" variant="outline"
size="sm" size="sm"
className="h-7" className="h-7"
onClick={() => setBulkMoveOpen(true)} onClick={() => openMoveDialog(selectedIds)}
disabled={saving || moving} disabled={saving || moving}
> >
<FolderInputIcon className="size-3.5" /> <FolderInputIcon className="size-3.5" />
@@ -593,8 +609,13 @@ export function FAQList({
open={bulkMoveOpen} open={bulkMoveOpen}
knowledgeBaseId={knowledgeBaseId} knowledgeBaseId={knowledgeBaseId}
moving={moving} moving={moving}
selectedCount={selectedIds.length} selectedCount={moveTargetIds.length || selectedIds.length}
onOpenChange={setBulkMoveOpen} onOpenChange={(open) => {
setBulkMoveOpen(open);
if (!open) {
setMoveTargetIds([]);
}
}}
onSubmit={(directoryId) => void handleBatchMove(directoryId)} onSubmit={(directoryId) => void handleBatchMove(directoryId)}
/> />
</> </>