"use client"; import React, { useState, useEffect } from "react"; import { Plus, Trash2, GripVertical, Check, ChevronsUpDown } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Switch } from "@/components/ui/switch"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command"; import { cn } from "@/lib/utils"; import { ProcessWorkStandardConfig, WorkPhaseDefinition, DetailTypeDefinition } from "./types"; import { defaultConfig } from "./config"; interface TableInfo { tableName: string; displayName?: string; } interface ColumnInfo { columnName: string; displayName?: string; dataType?: string; } function TableCombobox({ value, onChange, tables, loading }: { value: string; onChange: (v: string) => void; tables: TableInfo[]; loading: boolean; }) { const [open, setOpen] = useState(false); const selected = tables.find((t) => t.tableName === value); return ( {loading ? "로딩 중..." : selected ? selected.displayName || selected.tableName : "테이블 선택"} 테이블을 찾을 수 없습니다. {tables.map((t) => ( { onChange(t.tableName); setOpen(false); }} className="text-xs"> {t.displayName || t.tableName} {t.displayName && {t.tableName}} ))} ); } function ColumnCombobox({ value, onChange, tableName, placeholder }: { value: string; onChange: (v: string) => void; tableName: string; placeholder?: string; }) { const [open, setOpen] = useState(false); const [columns, setColumns] = useState([]); const [loading, setLoading] = useState(false); useEffect(() => { if (!tableName) { setColumns([]); return; } const load = async () => { setLoading(true); try { const { tableManagementApi } = await import("@/lib/api/tableManagement"); const res = await tableManagementApi.getColumnList(tableName); if (res.success && res.data?.columns) setColumns(res.data.columns); } catch { /* ignore */ } finally { setLoading(false); } }; load(); }, [tableName]); const selected = columns.find((c) => c.columnName === value); return ( {loading ? "로딩..." : !tableName ? "테이블 먼저 선택" : selected ? selected.displayName || selected.columnName : placeholder || "컬럼 선택"} 컬럼을 찾을 수 없습니다. {columns.map((c) => ( { onChange(c.columnName); setOpen(false); }} className="text-xs"> {c.displayName || c.columnName} {c.displayName && {c.columnName}} ))} ); } interface ConfigPanelProps { config: Partial; onChange: (config: Partial) => void; } export function ProcessWorkStandardConfigPanel({ config: configProp, onChange, }: ConfigPanelProps) { const [tables, setTables] = useState([]); const [loadingTables, setLoadingTables] = useState(false); 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, }; 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 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 ( 공정 작업기준 설정 {/* 품목 목록 모드 */} 품목 목록 모드 update({ itemListMode: v as "all" | "registered" })} > 전체 품목 등록 품목만 {config.itemListMode === "registered" ? "품목별 라우팅 탭에서 등록한 품목만 표시됩니다. screenCode는 화면 ID 기준으로 자동 설정됩니다." : "모든 품목을 표시합니다."} {/* 데이터 소스 설정 */} 데이터 소스 설정 품목 테이블 updateDataSource("itemTable", v)} tables={tables} loading={loadingTables} /> 품목명 컬럼 updateDataSource("itemNameColumn", v)} tableName={config.dataSource.itemTable} placeholder="품목명" /> 품목코드 컬럼 updateDataSource("itemCodeColumn", v)} tableName={config.dataSource.itemTable} placeholder="품목코드" /> 라우팅 버전 테이블 updateDataSource("routingVersionTable", v)} tables={tables} loading={loadingTables} /> 품목 연결 FK 컬럼 updateDataSource("routingFkColumn", v)} tableName={config.dataSource.routingVersionTable} placeholder="FK 컬럼" /> 공정 마스터 테이블 updateDataSource("processTable", v)} tables={tables} loading={loadingTables} /> 공정명 컬럼 updateDataSource("processNameColumn", v)} tableName={config.dataSource.processTable} placeholder="공정명" /> 공정코드 컬럼 updateDataSource("processCodeColumn", v)} tableName={config.dataSource.processTable} placeholder="공정코드" /> {/* 작업 단계 설정 */} 작업 단계 설정 단계 추가 {config.phases.map((phase, idx) => ( updatePhase(idx, "key", e.target.value)} className="h-7 w-20 text-[10px]" placeholder="키" /> updatePhase(idx, "label", e.target.value)} className="h-7 flex-1 text-[10px]" placeholder="표시명" /> removePhase(idx)} disabled={config.phases.length <= 1} > ))} {/* 상세 유형 옵션 */} 상세 유형 옵션 유형 추가 {config.detailTypes.map((dt, idx) => ( updateDetailType(idx, "value", e.target.value)} className="h-7 w-24 text-[10px]" placeholder="값" /> updateDetailType(idx, "label", e.target.value)} className="h-7 flex-1 text-[10px]" placeholder="표시명" /> removeDetailType(idx)} disabled={config.detailTypes.length <= 1} > ))} {/* UI 설정 */} UI 설정 좌우 분할 비율 (%) update({ splitRatio: Number(e.target.value) })} min={15} max={50} className="mt-1 h-8 w-20 text-xs" /> 좌측 패널 제목 update({ leftPanelTitle: e.target.value })} className="mt-1 h-8 text-xs" /> update({ readonly: v })} /> 읽기 전용 모드 ); }
품목 목록 모드
{config.itemListMode === "registered" ? "품목별 라우팅 탭에서 등록한 품목만 표시됩니다. screenCode는 화면 ID 기준으로 자동 설정됩니다." : "모든 품목을 표시합니다."}
데이터 소스 설정
작업 단계 설정
상세 유형 옵션
UI 설정