Files
AiTool/frontend/components/app-sidebar.tsx
2026-03-15 16:38:59 +08:00

163 lines
5.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { useEffect, useState, useCallback } from "react";
import {
FileText,
FolderArchive,
Settings,
Globe,
FileStack,
Settings2,
} from "lucide-react";
import { cn } from "@/lib/utils";
import { Separator } from "@/components/ui/separator";
import { Button } from "@/components/ui/button";
import { HistoricalReferences } from "@/components/historical-references";
import { cloudDocsApi, portalLinksApi, type CloudDocLinkRead, type PortalLinkRead } from "@/lib/api/client";
export function AppSidebar() {
const pathname = usePathname();
const [cloudDocs, setCloudDocs] = useState<CloudDocLinkRead[]>([]);
const [portalLinks, setPortalLinks] = useState<PortalLinkRead[]>([]);
const loadCloudDocs = useCallback(async () => {
try {
const list = await cloudDocsApi.list();
setCloudDocs(list);
} catch {
setCloudDocs([]);
}
}, []);
useEffect(() => {
loadCloudDocs();
}, [loadCloudDocs]);
useEffect(() => {
const onCloudDocsChanged = () => loadCloudDocs();
window.addEventListener("cloud-docs-changed", onCloudDocsChanged);
return () => window.removeEventListener("cloud-docs-changed", onCloudDocsChanged);
}, [loadCloudDocs]);
const loadPortalLinks = useCallback(async () => {
try {
const list = await portalLinksApi.list();
setPortalLinks(list);
} catch {
setPortalLinks([]);
}
}, []);
useEffect(() => {
loadPortalLinks();
}, [loadPortalLinks]);
useEffect(() => {
const onPortalLinksChanged = () => loadPortalLinks();
window.addEventListener("portal-links-changed", onPortalLinksChanged);
return () => window.removeEventListener("portal-links-changed", onPortalLinksChanged);
}, [loadPortalLinks]);
const nav = [
{ href: "/workspace", label: "需求与方案", icon: FileText },
{ href: "/finance", label: "财务归档", icon: FolderArchive },
{ href: "/settings", label: "设置", icon: Settings },
];
return (
<aside className="flex w-56 flex-col border-r bg-card text-card-foreground">
<div className="p-4">
<Link href="/" className="font-semibold text-foreground">
Ops-Core
</Link>
<p className="text-xs text-muted-foreground mt-0.5"></p>
</div>
<Separator />
<nav className="flex flex-1 flex-col gap-1 p-2">
{nav.map((item) => (
<Link key={item.href} href={item.href}>
<Button
variant={pathname === item.href ? "secondary" : "ghost"}
size="sm"
className="w-full justify-start"
>
<item.icon className="mr-2 h-4 w-4" />
{item.label}
</Button>
</Link>
))}
</nav>
<Separator />
<div className="flex-1 min-h-0 overflow-y-auto flex flex-col">
<div className="p-2 shrink-0">
<div className="flex items-center justify-between px-2 mb-2">
<p className="text-xs font-medium text-muted-foreground"></p>
<Link
href="/settings/cloud-docs"
className="text-xs text-muted-foreground hover:text-foreground"
title="管理云文档入口"
>
<Settings2 className="h-3.5 w-3.5" />
</Link>
</div>
<div className="flex flex-col gap-1">
{cloudDocs.length === 0 ? (
<p className="text-xs text-muted-foreground px-2"></p>
) : (
cloudDocs.map((doc) => (
<a
key={doc.id}
href={doc.url}
target="_blank"
rel="noopener noreferrer"
className={cn(
"flex items-center gap-2 rounded-md px-2 py-1.5 text-sm text-muted-foreground hover:bg-accent hover:text-accent-foreground"
)}
>
<FileStack className="h-4 w-4 shrink-0" />
<span className="truncate">{doc.name}</span>
</a>
))
)}
</div>
</div>
<div className="p-2 shrink-0">
<div className="flex items-center justify-between px-2 mb-2">
<p className="text-xs font-medium text-muted-foreground"></p>
<Link
href="/settings/portal-links"
className="text-xs text-muted-foreground hover:text-foreground"
title="管理快捷门户"
>
<Settings2 className="h-3.5 w-3.5" />
</Link>
</div>
<div className="flex flex-col gap-1">
{portalLinks.length === 0 ? (
<p className="text-xs text-muted-foreground px-2"></p>
) : (
portalLinks.map((link) => (
<a
key={link.id}
href={link.url}
target="_blank"
rel="noopener noreferrer"
className={cn(
"flex items-center gap-2 rounded-md px-2 py-1.5 text-sm text-muted-foreground hover:bg-accent hover:text-accent-foreground"
)}
>
<Globe className="h-4 w-4 shrink-0" />
<span className="truncate">{link.name}</span>
</a>
))
)}
</div>
</div>
</div>
{pathname === "/workspace" && <HistoricalReferences />}
</aside>
);
}