"use client"; import React, { useState, useEffect, useCallback } from "react"; import { Label } from "@/components/ui/label"; import { Input } from "@/components/ui/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Switch } from "@/components/ui/switch"; import { Button } from "@/components/ui/button"; import { Card } from "@/components/ui/card"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command"; import { Check, ChevronsUpDown, Plus, X, Search } from "lucide-react"; import { cn } from "@/lib/utils"; import { ComponentData } from "@/types/screen"; import { QuickInsertConfig, QuickInsertColumnMapping } from "@/types/screen-management"; import { apiClient } from "@/lib/api/client"; interface QuickInsertConfigSectionProps { component: ComponentData; onUpdateProperty: (path: string, value: any) => void; allComponents?: ComponentData[]; currentTableName?: string; } interface TableOption { name: string; label: string; } interface ColumnOption { name: string; label: string; } export const QuickInsertConfigSection: React.FC = ({ component, onUpdateProperty, allComponents = [], currentTableName, }) => { // 현재 설정 가져오기 const config: QuickInsertConfig = component.componentConfig?.action?.quickInsertConfig || { targetTable: "", columnMappings: [], afterInsert: { refreshData: true, clearComponents: [], showSuccessMessage: true, successMessage: "저장되었습니다.", }, duplicateCheck: { enabled: false, columns: [], errorMessage: "이미 존재하는 데이터입니다.", }, }; // 테이블 목록 상태 const [tables, setTables] = useState([]); const [tablesLoading, setTablesLoading] = useState(false); const [tablePopoverOpen, setTablePopoverOpen] = useState(false); const [tableSearch, setTableSearch] = useState(""); // 대상 테이블 컬럼 목록 상태 const [targetColumns, setTargetColumns] = useState([]); const [targetColumnsLoading, setTargetColumnsLoading] = useState(false); // 매핑별 Popover 상태 const [targetColumnPopoverOpen, setTargetColumnPopoverOpen] = useState>({}); const [targetColumnSearch, setTargetColumnSearch] = useState>({}); const [sourceComponentPopoverOpen, setSourceComponentPopoverOpen] = useState>({}); const [sourceComponentSearch, setSourceComponentSearch] = useState>({}); // 테이블 목록 로드 useEffect(() => { const loadTables = async () => { setTablesLoading(true); try { const response = await apiClient.get("/table-management/tables"); if (response.data?.success && response.data?.data) { setTables( response.data.data.map((t: any) => ({ name: t.tableName, label: t.displayName || t.tableName, })) ); } } catch (error) { console.error("테이블 목록 로드 실패:", error); } finally { setTablesLoading(false); } }; loadTables(); }, []); // 대상 테이블 선택 시 컬럼 로드 useEffect(() => { const loadTargetColumns = async () => { if (!config.targetTable) { setTargetColumns([]); return; } setTargetColumnsLoading(true); try { const response = await apiClient.get(`/table-management/tables/${config.targetTable}/columns`); if (response.data?.success && response.data?.data) { // columns가 배열인지 확인 (data.columns 또는 data 직접) const columns = response.data.data.columns || response.data.data; setTargetColumns( (Array.isArray(columns) ? columns : []).map((col: any) => ({ name: col.columnName || col.column_name, label: col.displayName || col.columnLabel || col.column_label || col.columnName || col.column_name, })) ); } } catch (error) { console.error("컬럼 목록 로드 실패:", error); setTargetColumns([]); } finally { setTargetColumnsLoading(false); } }; loadTargetColumns(); }, [config.targetTable]); // 설정 업데이트 헬퍼 const updateConfig = useCallback( (updates: Partial) => { const newConfig = { ...config, ...updates }; onUpdateProperty("componentConfig.action.quickInsertConfig", newConfig); }, [config, onUpdateProperty] ); // 컬럼 매핑 추가 const addMapping = () => { const newMapping: QuickInsertColumnMapping = { targetColumn: "", sourceType: "component", sourceComponentId: "", }; updateConfig({ columnMappings: [...(config.columnMappings || []), newMapping], }); }; // 컬럼 매핑 삭제 const removeMapping = (index: number) => { const newMappings = [...(config.columnMappings || [])]; newMappings.splice(index, 1); updateConfig({ columnMappings: newMappings }); }; // 컬럼 매핑 업데이트 const updateMapping = (index: number, updates: Partial) => { const newMappings = [...(config.columnMappings || [])]; newMappings[index] = { ...newMappings[index], ...updates }; updateConfig({ columnMappings: newMappings }); }; // 필터링된 테이블 목록 const filteredTables = tables.filter( (t) => t.name.toLowerCase().includes(tableSearch.toLowerCase()) || t.label.toLowerCase().includes(tableSearch.toLowerCase()) ); // 컴포넌트 목록 (entity 타입 우선) const availableComponents = allComponents.filter((comp: any) => { // entity 타입 또는 select 타입 컴포넌트 필터링 const widgetType = comp.widgetType || comp.componentType || ""; return widgetType === "entity" || widgetType === "select" || widgetType === "text"; }); return (

즉시 저장 설정

화면에서 선택한 데이터를 버튼 클릭 시 특정 테이블에 즉시 저장합니다.

{/* 대상 테이블 선택 */}
테이블을 찾을 수 없습니다. {filteredTables.map((table) => ( { updateConfig({ targetTable: table.name, columnMappings: [] }); setTablePopoverOpen(false); setTableSearch(""); }} className="text-xs" >
{table.label} {table.name}
))}
{/* 컬럼 매핑 */} {config.targetTable && (
{(config.columnMappings || []).length === 0 ? (
컬럼 매핑을 추가하세요
) : (
{(config.columnMappings || []).map((mapping, index) => (
매핑 #{index + 1}
{/* 대상 컬럼 */}
setTargetColumnPopoverOpen((prev) => ({ ...prev, [index]: open }))} > setTargetColumnSearch((prev) => ({ ...prev, [index]: v }))} className="text-xs" /> 컬럼을 찾을 수 없습니다. {targetColumns .filter( (c) => c.name.toLowerCase().includes((targetColumnSearch[index] || "").toLowerCase()) || c.label.toLowerCase().includes((targetColumnSearch[index] || "").toLowerCase()) ) .map((col) => ( { updateMapping(index, { targetColumn: col.name }); setTargetColumnPopoverOpen((prev) => ({ ...prev, [index]: false })); setTargetColumnSearch((prev) => ({ ...prev, [index]: "" })); }} className="text-xs" >
{col.label} {col.name}
))}
{/* 값 소스 타입 */}
{/* 소스 타입별 추가 설정 */} {mapping.sourceType === "component" && (
setSourceComponentPopoverOpen((prev) => ({ ...prev, [index]: open }))} > setSourceComponentSearch((prev) => ({ ...prev, [index]: v }))} className="text-xs" /> 컴포넌트를 찾을 수 없습니다. {availableComponents .filter((comp: any) => { const search = (sourceComponentSearch[index] || "").toLowerCase(); const label = (comp.label || "").toLowerCase(); const colName = (comp.columnName || "").toLowerCase(); return label.includes(search) || colName.includes(search); }) .map((comp: any) => ( { // sourceComponentId와 함께 sourceColumnName도 저장 (formData 접근용) updateMapping(index, { sourceComponentId: comp.id, sourceColumnName: comp.columnName || undefined, }); setSourceComponentPopoverOpen((prev) => ({ ...prev, [index]: false })); setSourceComponentSearch((prev) => ({ ...prev, [index]: "" })); }} className="text-xs" >
{comp.label || comp.columnName || comp.id} {comp.widgetType || comp.componentType}
))}
)} {mapping.sourceType === "leftPanel" && (
updateMapping(index, { sourceColumn: e.target.value })} className="h-7 text-xs" />

분할 패널 좌측에서 선택된 데이터의 컬럼명을 입력하세요

)} {mapping.sourceType === "fixed" && (
updateMapping(index, { fixedValue: e.target.value })} className="h-7 text-xs" />
)} {mapping.sourceType === "currentUser" && (
)}
))}
)}
)} {/* 저장 후 동작 설정 */} {config.targetTable && (
{ updateConfig({ afterInsert: { ...config.afterInsert, refreshData: checked }, }); }} />

테이블리스트, 카드 디스플레이 컴포넌트를 새로고침합니다

{ updateConfig({ afterInsert: { ...config.afterInsert, showSuccessMessage: checked }, }); }} />
{config.afterInsert?.showSuccessMessage && (
{ updateConfig({ afterInsert: { ...config.afterInsert, successMessage: e.target.value }, }); }} className="h-7 text-xs" />
)}
)} {/* 중복 체크 설정 */} {config.targetTable && (
{ updateConfig({ duplicateCheck: { ...config.duplicateCheck, enabled: checked }, }); }} />
{config.duplicateCheck?.enabled && ( <>
{targetColumns.length === 0 ? (

컬럼을 불러오는 중...

) : (
{targetColumns.map((col) => { const isChecked = (config.duplicateCheck?.columns || []).includes(col.name); return (
{ const currentColumns = config.duplicateCheck?.columns || []; const newColumns = isChecked ? currentColumns.filter((c) => c !== col.name) : [...currentColumns, col.name]; updateConfig({ duplicateCheck: { ...config.duplicateCheck, columns: newColumns }, }); }} > {}} className="h-3 w-3 flex-shrink-0" /> {col.label}{col.label !== col.name && ` (${col.name})`}
); })}
)}

선택한 컬럼들의 조합으로 중복 여부를 체크합니다

{ updateConfig({ duplicateCheck: { ...config.duplicateCheck, errorMessage: e.target.value }, }); }} className="h-7 text-xs" />
)}
)} {/* 사용 안내 */}

사용 방법:
1. 저장할 대상 테이블을 선택합니다
2. 컬럼 매핑을 추가하여 각 컬럼에 어떤 값을 저장할지 설정합니다
3. 버튼 클릭 시 설정된 값들이 대상 테이블에 즉시 저장됩니다

); }; export default QuickInsertConfigSection;