"use client"; import React, { useState, useEffect, useCallback } from "react"; import { Plus, Trash2, 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 { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, } from "@/components/ui/command"; import { Popover, PopoverContent, PopoverTrigger, } from "@/components/ui/popover"; import { cn } from "@/lib/utils"; import { ItemRoutingConfig, ProcessColumnDef } from "./types"; import { defaultConfig } from "./config"; interface TableInfo { tableName: string; displayName?: string; } interface ColumnInfo { columnName: string; displayName?: string; dataType?: string; } interface ScreenInfo { screenId: number; screenName: string; screenCode: string; } // 테이블 셀렉터 Combobox function TableSelector({ 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 ( 테이블을 찾을 수 없습니다. {tables.map((t) => ( { onChange(t.tableName); setOpen(false); }} className="text-xs" >
{t.displayName || t.tableName} {t.displayName && ( {t.tableName} )}
))}
); } // 컬럼 셀렉터 Combobox function ColumnSelector({ value, onChange, tableName, label, }: { value: string; onChange: (v: string) => void; tableName: string; label?: 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 ( 컬럼을 찾을 수 없습니다. {columns.map((c) => ( { onChange(c.columnName); setOpen(false); }} className="text-xs" >
{c.displayName || c.columnName} {c.displayName && ( {c.columnName} )}
))}
); } // 화면 셀렉터 Combobox function ScreenSelector({ value, onChange, }: { value?: number; onChange: (v?: number) => void; }) { const [open, setOpen] = useState(false); const [screens, setScreens] = useState([]); const [loading, setLoading] = useState(false); useEffect(() => { const load = async () => { setLoading(true); try { const { screenApi } = await import("@/lib/api/screen"); const res = await screenApi.getScreens({ page: 1, size: 1000 }); setScreens( res.data.map((s: any) => ({ screenId: s.screenId, screenName: s.screenName, screenCode: s.screenCode, })) ); } catch { /* ignore */ } finally { setLoading(false); } }; load(); }, []); const selected = screens.find((s) => s.screenId === value); return ( 화면을 찾을 수 없습니다. {screens.map((s) => ( { onChange(s.screenId === value ? undefined : s.screenId); setOpen(false); }} className="text-xs" >
{s.screenName} {s.screenCode} (ID: {s.screenId})
))}
); } // 공정 테이블 컬럼 셀렉터 (routingDetailTable의 컬럼 목록에서 선택) function ProcessColumnSelector({ value, onChange, tableName, processTable, }: { value: string; onChange: (v: string) => void; tableName: string; processTable: string; }) { const [open, setOpen] = useState(false); const [columns, setColumns] = useState([]); const [loading, setLoading] = useState(false); useEffect(() => { const loadAll = async () => { if (!tableName) return; setLoading(true); try { const { tableManagementApi } = await import( "@/lib/api/tableManagement" ); const res = await tableManagementApi.getColumnList(tableName); const cols: ColumnInfo[] = []; if (res.success && res.data?.columns) { cols.push(...res.data.columns); } if (processTable && processTable !== tableName) { const res2 = await tableManagementApi.getColumnList(processTable); if (res2.success && res2.data?.columns) { cols.push( ...res2.data.columns.map((c: any) => ({ ...c, columnName: c.columnName, displayName: `[${processTable}] ${c.displayName || c.columnName}`, })) ); } } setColumns(cols); } catch { /* ignore */ } finally { setLoading(false); } }; loadAll(); }, [tableName, processTable]); const selected = columns.find((c) => c.columnName === value); return ( 없음 {columns.map((c) => ( { onChange(c.columnName); setOpen(false); }} className="text-xs" > {c.displayName || c.columnName} ))} ); } interface ConfigPanelProps { config: Partial; onChange: (config: Partial) => void; } export function ItemRoutingConfigPanel({ config: configProp, onChange, }: ConfigPanelProps) { const config: ItemRoutingConfig = { ...defaultConfig, ...configProp, dataSource: { ...defaultConfig.dataSource, ...configProp?.dataSource }, modals: { ...defaultConfig.modals, ...configProp?.modals }, processColumns: configProp?.processColumns?.length ? configProp.processColumns : defaultConfig.processColumns, }; const [allTables, setAllTables] = useState([]); const [tablesLoading, setTablesLoading] = useState(false); useEffect(() => { const load = async () => { setTablesLoading(true); try { const { tableManagementApi } = await import( "@/lib/api/tableManagement" ); const res = await tableManagementApi.getTableList(); if (res.success && res.data) { setAllTables(res.data); } } catch { /* ignore */ } finally { setTablesLoading(false); } }; load(); }, []); const update = (partial: Partial) => { onChange({ ...configProp, ...partial }); }; const updateDataSource = (field: string, value: string) => { update({ dataSource: { ...config.dataSource, [field]: value } }); }; const updateModals = (field: string, value: number | undefined) => { update({ modals: { ...config.modals, [field]: value } }); }; // 컬럼 관리 const addColumn = () => { update({ processColumns: [ ...config.processColumns, { name: "", label: "새 컬럼", width: 100 }, ], }); }; const removeColumn = (idx: number) => { update({ processColumns: config.processColumns.filter((_, i) => i !== idx), }); }; const updateColumn = ( idx: number, field: keyof ProcessColumnDef, value: any ) => { const next = [...config.processColumns]; next[idx] = { ...next[idx], [field]: value }; update({ processColumns: next }); }; return (

품목별 라우팅 설정

{/* 데이터 소스 설정 */}

데이터 소스

updateDataSource("itemTable", v)} tables={allTables} loading={tablesLoading} />
updateDataSource("itemNameColumn", v)} tableName={config.dataSource.itemTable} label="품목명" />
updateDataSource("itemCodeColumn", v)} tableName={config.dataSource.itemTable} label="품목코드" />
updateDataSource("routingVersionTable", v)} tables={allTables} loading={tablesLoading} />
updateDataSource("routingVersionFkColumn", v)} tableName={config.dataSource.routingVersionTable} label="FK 컬럼" />
updateDataSource("routingVersionNameColumn", v) } tableName={config.dataSource.routingVersionTable} label="버전명" />
updateDataSource("routingDetailTable", v)} tables={allTables} loading={tablesLoading} />
updateDataSource("routingDetailFkColumn", v)} tableName={config.dataSource.routingDetailTable} label="FK 컬럼" />
updateDataSource("processTable", v)} tables={allTables} loading={tablesLoading} />
updateDataSource("processNameColumn", v)} tableName={config.dataSource.processTable} label="공정명" />
updateDataSource("processCodeColumn", v)} tableName={config.dataSource.processTable} label="공정코드" />
{/* 모달 설정 */}

모달 연동

updateModals("versionAddScreenId", v)} />
updateModals("processAddScreenId", v)} />
updateModals("processEditScreenId", v)} />
{/* 공정 테이블 컬럼 설정 */}

공정 테이블 컬럼

{config.processColumns.map((col, idx) => (
updateColumn(idx, "name", v)} tableName={config.dataSource.routingDetailTable} processTable={config.dataSource.processTable} /> updateColumn(idx, "label", e.target.value)} className="h-7 flex-1 text-[10px]" placeholder="표시명" /> updateColumn( idx, "width", e.target.value ? Number(e.target.value) : undefined ) } className="h-7 w-14 text-[10px]" placeholder="너비" />
))}
{/* UI 설정 */}

UI 설정

update({ splitRatio: Number(e.target.value) })} min={20} max={60} className="mt-1 h-8 w-20 text-xs" />
update({ leftPanelTitle: e.target.value })} className="mt-1 h-8 text-xs" />
update({ rightPanelTitle: e.target.value })} className="mt-1 h-8 text-xs" />
update({ versionAddButtonText: e.target.value })} className="mt-1 h-8 text-xs" />
update({ processAddButtonText: e.target.value })} className="mt-1 h-8 text-xs" />
update({ autoSelectFirstVersion: v })} />
update({ readonly: v })} />
); }