ai-chatbot/app/(admin)/settings/_components/local-ai-command-center.tsx

149 lines
5.6 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 { 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>
);
}