149 lines
5.6 KiB
TypeScript
149 lines
5.6 KiB
TypeScript
"use client";
|
||
|
||
import { Button } from "@/components/ui/button";
|
||
import { cn } from "@/lib/utils";
|
||
import { LOCAL_AI_QUICK_LINK_GROUPS } from "../_lib/local-ai-sections";
|
||
import type { LocalAiOverviewItem, LocalAiQuickLink } from "../_lib/types";
|
||
import { SettingStatusBadge } from "./setting-status-badge";
|
||
|
||
export function LocalAiCommandCenter({
|
||
openSectionCount,
|
||
providerCount,
|
||
modelCount,
|
||
overviewItems,
|
||
quickLinks,
|
||
isSectionExpanded,
|
||
onFocusSection,
|
||
onSetAllExpanded,
|
||
}: {
|
||
openSectionCount: number;
|
||
providerCount: number;
|
||
modelCount: number;
|
||
overviewItems: LocalAiOverviewItem[];
|
||
quickLinks: LocalAiQuickLink[];
|
||
isSectionExpanded: (sectionKey: string) => boolean;
|
||
onFocusSection: (sectionKey: string) => void;
|
||
onSetAllExpanded: (expanded: boolean) => void;
|
||
}) {
|
||
return (
|
||
<div className="overflow-hidden rounded-lg border bg-background">
|
||
<div className="border-b bg-muted/20 px-4 py-3">
|
||
<div className="flex flex-col gap-2.5 lg:flex-row lg:items-start lg:justify-between">
|
||
<div className="space-y-1">
|
||
<div className="font-semibold text-[10px] text-muted-foreground">
|
||
SYSTEM INTEGRATIONS
|
||
</div>
|
||
<h2 className="font-semibold text-lg">
|
||
高级能力与外部服务
|
||
</h2>
|
||
<p className="max-w-3xl text-muted-foreground text-xs leading-5">
|
||
把商品策划、图像处理、3D/CAD、实时画布和开放 API
|
||
按运维任务重新归类。折叠状态会保留,常用模块可以保持展开。
|
||
</p>
|
||
</div>
|
||
<div className="flex flex-wrap gap-1.5">
|
||
<SettingStatusBadge tone="neutral">
|
||
已展开 {openSectionCount} 个模块
|
||
</SettingStatusBadge>
|
||
<SettingStatusBadge tone="enabled">
|
||
供应商 {providerCount}
|
||
</SettingStatusBadge>
|
||
<SettingStatusBadge tone="neutral">
|
||
模型 {modelCount}
|
||
</SettingStatusBadge>
|
||
<Button
|
||
className="h-6 rounded-md px-2 text-[11px]"
|
||
onClick={() => onSetAllExpanded(true)}
|
||
size="sm"
|
||
type="button"
|
||
variant="outline"
|
||
>
|
||
全部展开
|
||
</Button>
|
||
<Button
|
||
className="h-6 rounded-md px-2 text-[11px]"
|
||
onClick={() => onSetAllExpanded(false)}
|
||
size="sm"
|
||
type="button"
|
||
variant="outline"
|
||
>
|
||
全部收起
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div className="grid gap-px bg-border md:grid-cols-2 xl:grid-cols-4">
|
||
{overviewItems.map((item) => {
|
||
const Icon = item.icon;
|
||
return (
|
||
<button
|
||
className="group bg-background p-3 text-left transition-colors hover:bg-muted/30"
|
||
key={item.key}
|
||
onClick={() => onFocusSection(item.key)}
|
||
type="button"
|
||
>
|
||
<div className="flex items-start justify-between gap-3">
|
||
<div className="flex min-w-0 items-center gap-2">
|
||
<div className="flex h-7 w-7 shrink-0 items-center justify-center rounded-md border bg-muted/30">
|
||
<Icon className="h-3.5 w-3.5 text-muted-foreground" />
|
||
</div>
|
||
<div className="min-w-0">
|
||
<div className="truncate font-semibold text-xs">
|
||
{item.title}
|
||
</div>
|
||
<div className="mt-0.5 truncate text-[11px] text-muted-foreground">
|
||
{item.description}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<SettingStatusBadge tone={item.tone}>
|
||
{item.status}
|
||
</SettingStatusBadge>
|
||
</div>
|
||
</button>
|
||
);
|
||
})}
|
||
</div>
|
||
<div className="border-t bg-muted/10 px-4 py-3">
|
||
<div className="grid gap-3 lg:grid-cols-4">
|
||
{LOCAL_AI_QUICK_LINK_GROUPS.map((group) => (
|
||
<div className="space-y-1.5" key={group}>
|
||
<div className="font-semibold text-[11px] text-muted-foreground">
|
||
{group}
|
||
</div>
|
||
<div className="flex flex-wrap gap-1.5">
|
||
{quickLinks
|
||
.filter((item) => item.group === group)
|
||
.map((item) => (
|
||
<button
|
||
className={cn(
|
||
"inline-flex h-6 items-center gap-1 rounded-md border px-2 text-[11px] transition-colors hover:bg-background",
|
||
isSectionExpanded(item.key)
|
||
? "border-foreground/20 bg-background text-foreground"
|
||
: "border-border bg-transparent text-muted-foreground"
|
||
)}
|
||
key={item.key}
|
||
onClick={() => onFocusSection(item.key)}
|
||
type="button"
|
||
>
|
||
<span
|
||
className={cn(
|
||
"h-1.5 w-1.5 rounded-full",
|
||
item.tone === "enabled" && "bg-emerald-500",
|
||
item.tone === "disabled" && "bg-zinc-300",
|
||
item.tone === "warning" && "bg-amber-500",
|
||
item.tone === "neutral" && "bg-sky-500"
|
||
)}
|
||
/>
|
||
{item.label}
|
||
</button>
|
||
))}
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|