Also removes the legacy home mode selector from the settings form because the affected JSX lives inside the same settings client refactor.
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-xl border bg-background">
|
||
<div className="border-b bg-muted/20 px-5 py-4">
|
||
<div className="flex flex-col gap-3 lg:flex-row lg:items-start lg:justify-between">
|
||
<div className="space-y-1.5">
|
||
<div className="font-semibold text-muted-foreground text-xs">
|
||
SYSTEM INTEGRATIONS
|
||
</div>
|
||
<h2 className="font-semibold text-2xl tracking-normal">
|
||
高级能力与外部服务
|
||
</h2>
|
||
<p className="max-w-3xl text-muted-foreground text-sm leading-6">
|
||
把商品策划、图像处理、3D/CAD、实时画布和开放 API
|
||
按运维任务重新归类。折叠状态会保留,常用模块可以保持展开。
|
||
</p>
|
||
</div>
|
||
<div className="flex flex-wrap gap-2">
|
||
<SettingStatusBadge tone="neutral">
|
||
已展开 {openSectionCount} 个模块
|
||
</SettingStatusBadge>
|
||
<SettingStatusBadge tone="enabled">
|
||
供应商 {providerCount}
|
||
</SettingStatusBadge>
|
||
<SettingStatusBadge tone="neutral">
|
||
模型 {modelCount}
|
||
</SettingStatusBadge>
|
||
<Button
|
||
className="h-7 rounded-md px-2 text-xs"
|
||
onClick={() => onSetAllExpanded(true)}
|
||
size="sm"
|
||
type="button"
|
||
variant="outline"
|
||
>
|
||
全部展开
|
||
</Button>
|
||
<Button
|
||
className="h-7 rounded-md px-2 text-xs"
|
||
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-4 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-8 w-8 shrink-0 items-center justify-center rounded-md border bg-muted/30">
|
||
<Icon className="h-4 w-4 text-muted-foreground" />
|
||
</div>
|
||
<div className="min-w-0">
|
||
<div className="truncate font-semibold text-sm">
|
||
{item.title}
|
||
</div>
|
||
<div className="mt-0.5 truncate text-muted-foreground text-xs">
|
||
{item.description}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<SettingStatusBadge tone={item.tone}>
|
||
{item.status}
|
||
</SettingStatusBadge>
|
||
</div>
|
||
</button>
|
||
);
|
||
})}
|
||
</div>
|
||
<div className="border-t bg-muted/10 px-5 py-4">
|
||
<div className="grid gap-4 lg:grid-cols-4">
|
||
{LOCAL_AI_QUICK_LINK_GROUPS.map((group) => (
|
||
<div className="space-y-2" key={group}>
|
||
<div className="font-semibold text-muted-foreground text-xs">
|
||
{group}
|
||
</div>
|
||
<div className="flex flex-wrap gap-2">
|
||
{quickLinks
|
||
.filter((item) => item.group === group)
|
||
.map((item) => (
|
||
<button
|
||
className={cn(
|
||
"inline-flex h-7 items-center gap-1.5 rounded-md border px-2 text-xs 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>
|
||
);
|
||
}
|