Files
chpark 37cac72085 refactor: Pipeline 네이밍 통일 및 AI 에이전트/장비 연결 기능 추가
- Docker/K8s 배포 설정을 pipeline-backend/pipeline-front로 통일
- 네임스페이스, 서비스, PVC 등 k8s 리소스명 pipeline-* 로 변경
- AI 에이전트 관리 기능 추가 (에이전트, 그룹, 프로바이더, 대화, API 키, 지식베이스)
- 장비 연결 관리 기능 추가 (PLC/Modbus/OPC-UA/MQTT)
- 배치 스케줄러에 AI agent/device collection/crawling 타입 추가
- 배치 편집 UI 개선 (6가지 실행 방식 지원)
- 회사별 페이지(COMPANY_*) 제거 및 AdminPageRenderer 최적화
- 메뉴 재구성: 장비 연결 관리 시스템관리로 이동, 에이전트 오케스트레이션으로 개명
- ai-assistant 디렉토리 제거 (backend-node로 통합)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 12:14:50 +09:00

67 lines
2.5 KiB
TypeScript

"use client";
import { useEffect, useState, useRef } from "react";
import { Palette, Check } from "lucide-react";
import { useColorTheme, type ColorTheme } from "@/components/providers/ThemeProvider";
const THEMES: { id: ColorTheme; label: string; color: string }[] = [
{ id: "blue", label: "Blue", color: "#3b82f6" },
{ id: "teal", label: "Teal", color: "#14b8a6" },
{ id: "green", label: "Green", color: "#22c55e" },
{ id: "purple", label: "Purple", color: "#8b5cf6" },
{ id: "red", label: "Red", color: "#ef4444" },
{ id: "dark", label: "Dark", color: "#1e293b" },
];
export function ThemeToggle({ collapsed = false }: { collapsed?: boolean }) {
const { colorTheme, setColorTheme } = useColorTheme();
const [open, setOpen] = useState(false);
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
const handleClick = (e: MouseEvent) => {
if (ref.current && !ref.current.contains(e.target as Node)) {
setOpen(false);
}
};
document.addEventListener("mousedown", handleClick);
return () => document.removeEventListener("mousedown", handleClick);
}, []);
const current = THEMES.find((t) => t.id === colorTheme) || THEMES[0];
return (
<div ref={ref} className="relative">
<button
onClick={() => setOpen(!open)}
className="flex h-6 items-center gap-1 rounded-full border border-border/50 px-1.5 py-0.5 text-[10px] font-medium transition-colors hover:bg-accent"
>
<span className="h-2.5 w-2.5 rounded-full" style={{ backgroundColor: current.color }} />
<span className="text-muted-foreground">{current.label}</span>
</button>
{open && (
<div className="absolute right-0 top-full mt-1 z-[9999] min-w-[140px] rounded-xl border border-border bg-popover p-1.5 shadow-lg">
{THEMES.map((theme) => (
<button
key={theme.id}
onClick={(e) => {
setColorTheme(theme.id, e);
setOpen(false);
}}
className="flex w-full items-center gap-2.5 rounded-lg px-2.5 py-2 text-sm transition-colors hover:bg-accent"
>
<span
className="h-3.5 w-3.5 rounded-full ring-1 ring-border/30"
style={{ backgroundColor: theme.color }}
/>
<span className="flex-1 text-left text-[13px] font-medium">{theme.label}</span>
{colorTheme === theme.id && <Check className="h-3.5 w-3.5 text-primary" />}
</button>
))}
</div>
)}
</div>
);
}