"use client"; /** * V2 공정 작업기준 설정 패널 (간소화) */ import React, { useState, useEffect } from "react"; import { Input } from "@/components/ui/input"; import { Switch } from "@/components/ui/switch"; import { Button } from "@/components/ui/button"; import { Collapsible, CollapsibleContent, CollapsibleTrigger, } from "@/components/ui/collapsible"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command"; import { Badge } from "@/components/ui/badge"; import { Settings, ChevronDown, ChevronRight, Plus, Trash2, Check, ChevronsUpDown, Database, Layers, List, } from "lucide-react"; import { cn } from "@/lib/utils"; import type { ProcessWorkStandardConfig, WorkPhaseDefinition, DetailTypeDefinition, } from "@/lib/registry/components/v2-process-work-standard/types"; import { defaultConfig } from "@/lib/registry/components/v2-process-work-standard/config"; interface TableInfo { tableName: string; displayName?: string; } function TableCombobox({ value, onChange, tables, loading, label }: { value: string; onChange: (v: string) => void; tables: TableInfo[]; loading: boolean; label: string; }) { const [open, setOpen] = useState(false); const selected = tables.find((t) => t.tableName === value); return (
{label} 테이블을 찾을 수 없습니다. {tables.map((t) => ( { onChange(t.tableName); setOpen(false); }} className="text-xs">
{t.displayName || t.tableName} {t.displayName && {t.tableName}}
))}
); } interface V2ProcessWorkStandardConfigPanelProps { config: Partial; onChange: (config: Partial) => void; } export const V2ProcessWorkStandardConfigPanel: React.FC< V2ProcessWorkStandardConfigPanelProps > = ({ config: configProp, onChange }) => { const [phasesOpen, setPhasesOpen] = useState(false); const [detailTypesOpen, setDetailTypesOpen] = useState(false); const [layoutOpen, setLayoutOpen] = useState(false); const [dataSourceOpen, setDataSourceOpen] = useState(false); const [tables, setTables] = useState([]); const [loadingTables, setLoadingTables] = useState(false); useEffect(() => { const loadTables = async () => { setLoadingTables(true); try { const { tableManagementApi } = await import("@/lib/api/tableManagement"); const res = await tableManagementApi.getTableList(); if (res.success && res.data) { setTables(res.data.map((t: any) => ({ tableName: t.tableName, displayName: t.displayName || t.tableName }))); } } catch { /* ignore */ } finally { setLoadingTables(false); } }; loadTables(); }, []); const config: ProcessWorkStandardConfig = { ...defaultConfig, ...configProp, dataSource: { ...defaultConfig.dataSource, ...configProp?.dataSource }, phases: configProp?.phases?.length ? configProp.phases : defaultConfig.phases, detailTypes: configProp?.detailTypes?.length ? configProp.detailTypes : defaultConfig.detailTypes, }; const update = (partial: Partial) => { onChange({ ...configProp, ...partial }); }; const updateDataSource = (field: string, value: string) => { update({ dataSource: { ...config.dataSource, [field]: value } }); }; const addPhase = () => { const nextOrder = config.phases.length + 1; update({ phases: [ ...config.phases, { key: `PHASE_${nextOrder}`, label: `단계 ${nextOrder}`, sortOrder: nextOrder, }, ], }); }; const removePhase = (idx: number) => { update({ phases: config.phases.filter((_, i) => i !== idx) }); }; const updatePhase = ( idx: number, field: keyof WorkPhaseDefinition, value: string | number, ) => { const next = [...config.phases]; next[idx] = { ...next[idx], [field]: value }; update({ phases: next }); }; const addDetailType = () => { update({ detailTypes: [ ...config.detailTypes, { value: `TYPE_${config.detailTypes.length + 1}`, label: "신규 유형", }, ], }); }; const removeDetailType = (idx: number) => { update({ detailTypes: config.detailTypes.filter((_, i) => i !== idx) }); }; const updateDetailType = ( idx: number, field: keyof DetailTypeDefinition, value: string, ) => { const next = [...config.detailTypes]; next[idx] = { ...next[idx], [field]: value }; update({ detailTypes: next }); }; return (
{/* 품목 목록 모드 */}
품목 목록 모드
{config.itemListMode === "registered" && (

품목별 라우팅 탭에서 등록한 품목만 표시됩니다.

)}
{/* 작업 단계 */}

공정별 작업 단계를 정의

{config.phases.map((phase, idx) => (
updatePhase(idx, "key", e.target.value) } className="h-7 text-xs" />
표시명 updatePhase(idx, "label", e.target.value) } className="h-7 text-xs" />
순서 updatePhase( idx, "sortOrder", parseInt(e.target.value) || 1, ) } className="h-7 text-center text-xs" />
))}
{/* 상세 유형 */}

작업 항목의 상세 유형 옵션

{config.detailTypes.map((dt, idx) => (
updateDetailType(idx, "value", e.target.value) } className="h-7 text-xs" />
표시명 updateDetailType(idx, "label", e.target.value) } className="h-7 text-xs" />
))}
{/* 데이터 소스 (테이블만) */}

테이블만 선택하면 컬럼 정보는 엔티티 설정에서 자동으로 가져옵니다.

updateDataSource("itemTable", v)} tables={tables} loading={loadingTables} /> updateDataSource("routingVersionTable", v)} tables={tables} loading={loadingTables} /> updateDataSource("routingDetailTable", v)} tables={tables} loading={loadingTables} /> updateDataSource("processTable", v)} tables={tables} loading={loadingTables} />
{/* 레이아웃 & 기타 */}
좌측 패널 비율 (%)

품목/공정 선택 패널의 너비

update({ splitRatio: parseInt(e.target.value) || 30 }) } className="h-7 w-[80px] text-xs" />
좌측 패널 제목 update({ leftPanelTitle: e.target.value })} placeholder="품목 및 공정 선택" className="h-7 w-[140px] text-xs" />

읽기 전용

수정/삭제 버튼을 숨겨요

update({ readonly: checked })} />
); }; V2ProcessWorkStandardConfigPanel.displayName = "V2ProcessWorkStandardConfigPanel"; export default V2ProcessWorkStandardConfigPanel;