Merge remote-tracking branch 'upstream/dev' into feat/onedrive-connector

This commit is contained in:
Anish Sarkar 2026-03-29 11:55:06 +05:30
commit 5a3eece397
70 changed files with 8288 additions and 5698 deletions

View file

@ -10,6 +10,8 @@ import type { DocumentTypeEnum } from "@/contracts/types/document.types";
import { DocumentNode, type DocumentNodeDoc } from "./DocumentNode";
import { type FolderDisplay, FolderNode } from "./FolderNode";
export type FolderSelectionState = "all" | "some" | "none";
interface FolderTreeViewProps {
folders: FolderDisplay[];
documents: DocumentNodeDoc[];
@ -20,6 +22,7 @@ interface FolderTreeViewProps {
doc: { id: number; title: string; document_type: string },
isMentioned: boolean
) => void;
onToggleFolderSelect: (folderId: number, selectAll: boolean) => void;
onRenameFolder: (folder: FolderDisplay, newName: string) => void;
onDeleteFolder: (folder: FolderDisplay) => void;
onMoveFolder: (folder: FolderDisplay) => void;
@ -30,6 +33,7 @@ interface FolderTreeViewProps {
onMoveDocument: (doc: DocumentNodeDoc) => void;
onExportDocument?: (doc: DocumentNodeDoc, format: string) => void;
activeTypes: DocumentTypeEnum[];
searchQuery?: string;
onDropIntoFolder?: (
itemType: "folder" | "document",
itemId: number,
@ -55,6 +59,7 @@ export function FolderTreeView({
onToggleExpand,
mentionedDocIds,
onToggleChatMention,
onToggleFolderSelect,
onRenameFolder,
onDeleteFolder,
onMoveFolder,
@ -65,6 +70,7 @@ export function FolderTreeView({
onMoveDocument,
onExportDocument,
activeTypes,
searchQuery,
onDropIntoFolder,
onReorderFolder,
}: FolderTreeViewProps) {
@ -93,13 +99,13 @@ export function FolderTreeView({
const handleCancelRename = useCallback(() => setRenamingFolderId(null), [setRenamingFolderId]);
const hasDescendantMatch = useMemo(() => {
if (activeTypes.length === 0) return null;
if (activeTypes.length === 0 && !searchQuery) return null;
const match: Record<number, boolean> = {};
function check(folderId: number): boolean {
if (match[folderId] !== undefined) return match[folderId];
const childDocs = (docsByFolder[folderId] ?? []).some((d) =>
activeTypes.includes(d.document_type as DocumentTypeEnum)
const childDocs = (docsByFolder[folderId] ?? []).some(
(d) => activeTypes.length === 0 || activeTypes.includes(d.document_type as DocumentTypeEnum)
);
if (childDocs) {
match[folderId] = true;
@ -120,7 +126,37 @@ export function FolderTreeView({
check(f.id);
}
return match;
}, [folders, docsByFolder, foldersByParent, activeTypes]);
}, [folders, docsByFolder, foldersByParent, activeTypes, searchQuery]);
const folderSelectionStates = useMemo(() => {
const states: Record<number, FolderSelectionState> = {};
const isSelectable = (d: DocumentNodeDoc) =>
d.status?.state !== "pending" && d.status?.state !== "processing";
function compute(folderId: number): { selected: number; total: number } {
const directDocs = (docsByFolder[folderId] ?? []).filter(isSelectable);
let selected = directDocs.filter((d) => mentionedDocIds.has(d.id)).length;
let total = directDocs.length;
for (const child of foldersByParent[folderId] ?? []) {
const sub = compute(child.id);
selected += sub.selected;
total += sub.total;
}
if (total === 0) states[folderId] = "none";
else if (selected === total) states[folderId] = "all";
else if (selected > 0) states[folderId] = "some";
else states[folderId] = "none";
return { selected, total };
}
for (const f of folders) {
if (states[f.id] === undefined) compute(f.id);
}
return states;
}, [folders, docsByFolder, foldersByParent, mentionedDocIds]);
function renderLevel(parentId: number | null, depth: number): React.ReactNode[] {
const key = parentId ?? "root";
@ -143,14 +179,19 @@ export function FolderTreeView({
after: i < visibleFolders.length - 1 ? visibleFolders[i + 1].position : null,
};
const isAutoExpanded = !!searchQuery && !!hasDescendantMatch?.[f.id];
const isExpanded = expandedIds.has(f.id) || isAutoExpanded;
nodes.push(
<FolderNode
key={`folder-${f.id}`}
folder={f}
depth={depth}
isExpanded={expandedIds.has(f.id)}
isExpanded={isExpanded}
isRenaming={renamingFolderId === f.id}
childCount={folderChildCounts[f.id] ?? 0}
selectionState={folderSelectionStates[f.id] ?? "none"}
onToggleSelect={onToggleFolderSelect}
onToggleExpand={onToggleExpand}
onRename={onRenameFolder}
onStartRename={handleStartRename}
@ -166,7 +207,7 @@ export function FolderTreeView({
/>
);
if (expandedIds.has(f.id)) {
if (isExpanded) {
nodes.push(...renderLevel(f.id, depth + 1));
}
}
@ -204,7 +245,7 @@ export function FolderTreeView({
);
}
if (treeNodes.length === 0 && activeTypes.length > 0) {
if (treeNodes.length === 0 && (activeTypes.length > 0 || searchQuery)) {
return (
<div className="flex flex-1 flex-col items-center justify-center gap-3 px-4 py-12 text-muted-foreground">
<CirclePlus className="h-10 w-10 rotate-45" />