"use client"; /** * V2 결재 단계 설정 패널 * 토스식 단계별 UX: 데이터 소스(Combobox) -> 레코드 식별(Combobox) -> 표시 설정(Switch+설명, Collapsible) */ import React, { useState, useEffect } from "react"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; 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 { Check, ChevronsUpDown, Database, ChevronDown, Settings } from "lucide-react"; import { cn } from "@/lib/utils"; import { tableTypeApi } from "@/lib/api/screen"; import { tableManagementApi } from "@/lib/api/tableManagement"; import type { ApprovalStepConfig } from "@/lib/registry/components/v2-approval-step/types"; interface V2ApprovalStepConfigPanelProps { config: ApprovalStepConfig; onChange: (config: Partial) => void; screenTableName?: string; } export const V2ApprovalStepConfigPanel: React.FC = ({ config, onChange, screenTableName, }) => { const [availableTables, setAvailableTables] = useState>([]); const [loadingTables, setLoadingTables] = useState(false); const [tableOpen, setTableOpen] = useState(false); const [availableColumns, setAvailableColumns] = useState>([]); const [loadingColumns, setLoadingColumns] = useState(false); const [columnOpen, setColumnOpen] = useState(false); const [displayOpen, setDisplayOpen] = useState(false); const targetTableName = config.targetTable || screenTableName; const handleChange = (key: keyof ApprovalStepConfig, value: any) => { onChange({ [key]: value }); }; useEffect(() => { const fetchTables = async () => { setLoadingTables(true); try { const response = await tableTypeApi.getTables(); setAvailableTables( response.map((table: any) => ({ tableName: table.tableName, displayName: table.displayName || table.tableName, })) ); } catch { /* ignore */ } finally { setLoadingTables(false); } }; fetchTables(); }, []); useEffect(() => { if (!targetTableName) { setAvailableColumns([]); return; } const fetchColumns = async () => { setLoadingColumns(true); try { const result = await tableManagementApi.getColumnList(targetTableName); if (result.success && result.data) { const columns = Array.isArray(result.data) ? result.data : result.data.columns; if (columns && Array.isArray(columns)) { setAvailableColumns( columns.map((col: any) => ({ columnName: col.columnName || col.column_name || col.name, label: col.displayName || col.columnLabel || col.column_label || col.columnName || col.column_name || col.name, })) ); } } } catch { setAvailableColumns([]); } finally { setLoadingColumns(false); } }; fetchColumns(); }, [targetTableName]); const handleTableChange = (newTableName: string) => { if (newTableName === targetTableName) return; handleChange("targetTable", newTableName); handleChange("targetRecordIdField", ""); setTableOpen(false); }; return (
{/* ─── 1단계: 데이터 소스 ─── */}

데이터 소스

결재 상태를 조회할 대상 테이블을 설정해요

대상 테이블 테이블을 찾을 수 없습니다. {availableTables.map((table) => ( handleTableChange(table.tableName)} className="text-xs" >
{table.displayName} {table.displayName !== table.tableName && ( {table.tableName} )}
))}
{screenTableName && targetTableName !== screenTableName && (
화면 기본 테이블({screenTableName})과 다른 테이블 사용 중
)}
{/* 레코드 ID 필드 */}
레코드 ID 필드 {targetTableName ? ( 컬럼을 찾을 수 없습니다. {availableColumns.map((col) => ( { handleChange("targetRecordIdField", col.columnName); setColumnOpen(false); }} className="text-xs" >
{col.label} {col.label !== col.columnName && ( {col.columnName} )}
))}
) : (

대상 테이블을 먼저 선택하세요

)}

결재 대상 레코드를 식별할 PK 컬럼

{/* ─── 2단계: 표시 모드 ─── */}

표시 모드

결재 단계의 방향을 설정해요

{/* ─── 3단계: 표시 옵션 (Collapsible) ─── */}

부서/직급 표시

결재자의 부서와 직급을 보여줘요

handleChange("showDept", checked)} />

결재 코멘트

결재자가 남긴 의견을 표시해요

handleChange("showComment", checked)} />

처리 시각

각 단계의 처리 일시를 보여줘요

handleChange("showTimestamp", checked)} />

콤팩트 모드

좁은 공간에 맞게 작게 표시해요

handleChange("compact", checked)} />
); }; V2ApprovalStepConfigPanel.displayName = "V2ApprovalStepConfigPanel"; export default V2ApprovalStepConfigPanel;