diff --git a/src/components/documents/folder-actions-menu.tsx b/src/components/documents/folder-actions-menu.tsx
index 643f4805..d9f37633 100644
--- a/src/components/documents/folder-actions-menu.tsx
+++ b/src/components/documents/folder-actions-menu.tsx
@@ -21,14 +21,30 @@ import {
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { ConfirmationDialog } from '@/components/shared/confirmation-dialog';
+import {
+ Tooltip,
+ TooltipContent,
+ TooltipProvider,
+ TooltipTrigger,
+} from '@/components/ui/tooltip';
import { toastError } from '@/lib/api/toast-error';
import {
useCreateFolder,
useDeleteFolder,
useRenameFolder,
useDocumentFolders,
+ type FolderNode,
} from '@/hooks/use-document-folders';
+function findInTree(nodes: FolderNode[], id: string): FolderNode | null {
+ for (const node of nodes) {
+ if (node.id === id) return node;
+ const found = findInTree(node.children, id);
+ if (found) return found;
+ }
+ return null;
+}
+
interface FolderActionsMenuProps {
/** The folder these actions apply to. `null` means root → only the
* Create-new-folder action is available. */
@@ -61,6 +77,9 @@ export function FolderActionsMenu({ selectedFolderId, onAfterDelete }: FolderAct
return find(tree) ?? '';
})();
+ const selectedNode = isFolderSelected ? findInTree(tree, selectedFolderId as string) : null;
+ const isSystem = selectedNode?.systemManaged ?? false;
+
return (
<>
@@ -81,41 +100,62 @@ export function FolderActionsMenu({ selectedFolderId, onAfterDelete }: FolderAct
New folder {isFolderSelected ? 'inside this' : 'at root'}
{isFolderSelected ? (
- <>
- {
- setName(currentName);
- setRenameOpen(true);
- }}
- >
-
- Rename
-
- e.preventDefault()}
- className="text-destructive"
- >
-
- Delete
-
- }
- title="Delete folder?"
- description="Subfolders and documents inside will move up to the parent. The folder itself is removed."
- confirmLabel="Delete folder"
- loading={deleteMutation.isPending}
- onConfirm={async () => {
- try {
- await deleteMutation.mutateAsync(selectedFolderId as string);
- toast.success('Folder deleted; contents moved to parent.');
- onAfterDelete?.();
- } catch (err) {
- toastError(err);
- }
- }}
- />
- >
+
+
+
+
+ {
+ if (isSystem) return;
+ setName(currentName);
+ setRenameOpen(true);
+ }}
+ >
+
+ Rename
+
+
+
+ {isSystem ? (
+ System folders can't be renamed.
+ ) : null}
+
+
+
+
+ e.preventDefault()}
+ className="text-destructive"
+ >
+
+ Delete
+
+ }
+ title="Delete folder?"
+ description="Subfolders and documents inside will move up to the parent. The folder itself is removed."
+ confirmLabel="Delete folder"
+ loading={deleteMutation.isPending}
+ onConfirm={async () => {
+ try {
+ await deleteMutation.mutateAsync(selectedFolderId as string);
+ toast.success('Folder deleted; contents moved to parent.');
+ onAfterDelete?.();
+ } catch (err) {
+ toastError(err);
+ }
+ }}
+ />
+
+
+ {isSystem ? (
+ System folders can't be deleted.
+ ) : null}
+
+
) : null}
diff --git a/src/components/documents/folder-tree-sidebar.tsx b/src/components/documents/folder-tree-sidebar.tsx
index 663cb33e..327a623c 100644
--- a/src/components/documents/folder-tree-sidebar.tsx
+++ b/src/components/documents/folder-tree-sidebar.tsx
@@ -1,7 +1,7 @@
'use client';
import { useState } from 'react';
-import { ChevronRight, Folder, FolderOpen, Inbox } from 'lucide-react';
+import { ChevronRight, Folder, FolderOpen, Inbox, Lock } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { cn } from '@/lib/utils';
@@ -135,7 +135,10 @@ function FolderRow({
{open