chore(documents): remove legacy /documents/files route + folder tree
The /documents/files page rendered a storagePath-prefix folder tree disconnected from document_folders. Replaced by the unified hub (Task 15). 301 redirect catches stray bookmarks. file-browser-store repurposed to hold the document_folders.id selection. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,139 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { ChevronDown, ChevronRight, Folder, FolderOpen } from 'lucide-react';
|
||||
|
||||
import { cn } from '@/lib/utils';
|
||||
import type { FileRow } from '@/components/files/file-grid';
|
||||
|
||||
interface FolderNode {
|
||||
name: string;
|
||||
fullPath: string;
|
||||
children: Record<string, FolderNode>;
|
||||
}
|
||||
|
||||
function buildFolderTree(files: FileRow[]): FolderNode {
|
||||
const root: FolderNode = { name: '', fullPath: '', children: {} };
|
||||
|
||||
for (const file of files) {
|
||||
const parts = file.storagePath ? file.storagePath.split('/').slice(0, -1) : [];
|
||||
if (parts.length <= 1) continue; // skip files directly in root/port folder
|
||||
|
||||
let node = root;
|
||||
let accumulated = '';
|
||||
for (const part of parts.slice(1)) { // skip portSlug prefix
|
||||
accumulated = accumulated ? `${accumulated}/${part}` : part;
|
||||
if (!node.children[part]) {
|
||||
node.children[part] = { name: part, fullPath: accumulated, children: {} };
|
||||
}
|
||||
node = node.children[part]!;
|
||||
}
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
interface FolderNodeComponentProps {
|
||||
node: FolderNode;
|
||||
currentFolder: string;
|
||||
onFolderSelect: (path: string) => void;
|
||||
depth?: number;
|
||||
}
|
||||
|
||||
function FolderNodeComponent({
|
||||
node,
|
||||
currentFolder,
|
||||
onFolderSelect,
|
||||
depth = 0,
|
||||
}: FolderNodeComponentProps) {
|
||||
const [expanded, setExpanded] = useState(true);
|
||||
const hasChildren = Object.keys(node.children).length > 0;
|
||||
const isSelected = currentFolder === node.fullPath;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
onFolderSelect(node.fullPath);
|
||||
if (hasChildren) setExpanded((v) => !v);
|
||||
}}
|
||||
className={cn(
|
||||
'flex w-full items-center gap-1.5 rounded px-2 py-1 text-left text-sm hover:bg-muted/60 transition-colors',
|
||||
isSelected && 'bg-muted font-medium',
|
||||
)}
|
||||
style={{ paddingLeft: `${depth * 12 + 8}px` }}
|
||||
>
|
||||
{hasChildren ? (
|
||||
expanded ? (
|
||||
<ChevronDown className="h-3.5 w-3.5 shrink-0 text-muted-foreground" />
|
||||
) : (
|
||||
<ChevronRight className="h-3.5 w-3.5 shrink-0 text-muted-foreground" />
|
||||
)
|
||||
) : (
|
||||
<span className="w-3.5" />
|
||||
)}
|
||||
{isSelected ? (
|
||||
<FolderOpen className="h-4 w-4 shrink-0 text-primary" />
|
||||
) : (
|
||||
<Folder className="h-4 w-4 shrink-0 text-muted-foreground" />
|
||||
)}
|
||||
<span className="truncate">{node.name}</span>
|
||||
</button>
|
||||
|
||||
{hasChildren && expanded && (
|
||||
<div>
|
||||
{Object.values(node.children).map((child) => (
|
||||
<FolderNodeComponent
|
||||
key={child.fullPath}
|
||||
node={child}
|
||||
currentFolder={currentFolder}
|
||||
onFolderSelect={onFolderSelect}
|
||||
depth={depth + 1}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface FolderTreeProps {
|
||||
files: (FileRow & { storagePath: string })[];
|
||||
currentFolder: string;
|
||||
onFolderSelect: (path: string) => void;
|
||||
}
|
||||
|
||||
export function FolderTree({ files, currentFolder, onFolderSelect }: FolderTreeProps) {
|
||||
const tree = buildFolderTree(files);
|
||||
|
||||
return (
|
||||
<div className="space-y-0.5">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onFolderSelect('')}
|
||||
className={cn(
|
||||
'flex w-full items-center gap-1.5 rounded px-2 py-1 text-left text-sm hover:bg-muted/60 transition-colors',
|
||||
currentFolder === '' && 'bg-muted font-medium',
|
||||
)}
|
||||
>
|
||||
<span className="w-3.5" />
|
||||
{currentFolder === '' ? (
|
||||
<FolderOpen className="h-4 w-4 shrink-0 text-primary" />
|
||||
) : (
|
||||
<Folder className="h-4 w-4 shrink-0 text-muted-foreground" />
|
||||
)}
|
||||
<span>All Files</span>
|
||||
</button>
|
||||
|
||||
{Object.values(tree.children).map((child) => (
|
||||
<FolderNodeComponent
|
||||
key={child.fullPath}
|
||||
node={child}
|
||||
currentFolder={currentFolder}
|
||||
onFolderSelect={onFolderSelect}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -13,7 +13,6 @@ import {
|
||||
Building2,
|
||||
Receipt,
|
||||
FileText,
|
||||
FolderOpen,
|
||||
Bell,
|
||||
Camera,
|
||||
Globe,
|
||||
@@ -116,10 +115,7 @@ function buildNavSections(portSlug: string | undefined): NavSection[] {
|
||||
{
|
||||
title: 'Documents',
|
||||
marinaRequired: true,
|
||||
items: [
|
||||
{ href: `${base}/documents`, label: 'Documents', icon: FileText },
|
||||
{ href: `${base}/documents/files`, label: 'Files', icon: FolderOpen },
|
||||
],
|
||||
items: [{ href: `${base}/documents`, label: 'Documents', icon: FileText }],
|
||||
},
|
||||
{
|
||||
title: 'Financial',
|
||||
|
||||
@@ -1010,7 +1010,7 @@ function buildFlatRows(args: BuildFlatRowsArgs): FlatRow[] {
|
||||
icon: Folder,
|
||||
label: f.filename,
|
||||
sub: f.ownerLabel,
|
||||
href: `/${portSlug}/documents/files`,
|
||||
href: `/${portSlug}/documents`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user