"use client"; /** * V2Select 설정 패널 * 통합 선택 컴포넌트의 세부 설정을 관리합니다. */ 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 { Separator } from "@/components/ui/separator"; import { Checkbox } from "@/components/ui/checkbox"; import { Button } from "@/components/ui/button"; import { Plus, Trash2, Loader2 } from "lucide-react"; import { apiClient } from "@/lib/api/client"; interface ColumnOption { columnName: string; columnLabel: string; } interface CategoryValueOption { valueCode: string; valueLabel: string; } interface V2SelectConfigPanelProps { config: Record; onChange: (config: Record) => void; /** 컬럼의 inputType (entity/category 타입 확인용) */ inputType?: string; /** 현재 테이블명 (카테고리 값 조회용) */ tableName?: string; /** 현재 컬럼명 (카테고리 값 조회용) */ columnName?: string; } export const V2SelectConfigPanel: React.FC = ({ config, onChange, inputType, tableName, columnName, }) => { const isEntityType = inputType === "entity"; const isCategoryType = inputType === "category"; const [entityColumns, setEntityColumns] = useState([]); const [loadingColumns, setLoadingColumns] = useState(false); // 카테고리 값 목록 const [categoryValues, setCategoryValues] = useState([]); const [loadingCategoryValues, setLoadingCategoryValues] = useState(false); const updateConfig = (field: string, value: any) => { onChange({ ...config, [field]: value }); }; // 카테고리 타입이면 source를 자동으로 category로 설정 useEffect(() => { if (isCategoryType && config.source !== "category") { onChange({ ...config, source: "category" }); } }, [isCategoryType]); // 카테고리 값 로드 const loadCategoryValues = useCallback(async (catTable: string, catColumn: string) => { if (!catTable || !catColumn) { setCategoryValues([]); return; } setLoadingCategoryValues(true); try { const response = await apiClient.get(`/table-categories/${catTable}/${catColumn}/values`); const data = response.data; if (data.success && data.data) { const flattenTree = (items: any[], depth: number = 0): CategoryValueOption[] => { const result: CategoryValueOption[] = []; for (const item of items) { result.push({ valueCode: item.valueCode, valueLabel: depth > 0 ? `${" ".repeat(depth)}${item.valueLabel}` : item.valueLabel, }); if (item.children && item.children.length > 0) { result.push(...flattenTree(item.children, depth + 1)); } } return result; }; setCategoryValues(flattenTree(data.data)); } } catch (error) { console.error("카테고리 값 조회 실패:", error); setCategoryValues([]); } finally { setLoadingCategoryValues(false); } }, []); // 카테고리 소스일 때 값 로드 useEffect(() => { if (config.source === "category") { const catTable = config.categoryTable || tableName; const catColumn = config.categoryColumn || columnName; if (catTable && catColumn) { loadCategoryValues(catTable, catColumn); } } }, [config.source, config.categoryTable, config.categoryColumn, tableName, columnName, loadCategoryValues]); // 엔티티 테이블 변경 시 컬럼 목록 조회 const loadEntityColumns = useCallback(async (tblName: string) => { if (!tblName) { setEntityColumns([]); return; } setLoadingColumns(true); try { const response = await apiClient.get(`/table-management/tables/${tblName}/columns?size=500`); const data = response.data.data || response.data; const columns = data.columns || data || []; const columnOptions: ColumnOption[] = columns.map((col: any) => { const name = col.columnName || col.column_name || col.name; const label = col.displayName || col.display_name || col.columnLabel || col.column_label || name; return { columnName: name, columnLabel: label, }; }); setEntityColumns(columnOptions); } catch (error) { console.error("컬럼 목록 조회 실패:", error); setEntityColumns([]); } finally { setLoadingColumns(false); } }, []); useEffect(() => { if (config.source === "entity" && config.entityTable) { loadEntityColumns(config.entityTable); } }, [config.source, config.entityTable, loadEntityColumns]); // 정적 옵션 관리 const options = config.options || []; const addOption = () => { const newOptions = [...options, { value: "", label: "" }]; updateConfig("options", newOptions); }; const updateOptionValue = (index: number, value: string) => { const newOptions = [...options]; newOptions[index] = { ...newOptions[index], value, label: value }; updateConfig("options", newOptions); }; const removeOption = (index: number) => { const newOptions = options.filter((_: any, i: number) => i !== index); updateConfig("options", newOptions); }; // 현재 source 결정 (카테고리 타입이면 강제 category) const effectiveSource = isCategoryType ? "category" : config.source || "static"; return (
{/* 선택 모드 */}
{/* 데이터 소스 */}
{isCategoryType ? (
카테고리 (자동 설정)
) : ( )}
{/* 카테고리 설정 */} {effectiveSource === "category" && (

테이블

{config.categoryTable || tableName || "-"}

컬럼

{config.categoryColumn || columnName || "-"}

{/* 카테고리 값 로딩 중 */} {loadingCategoryValues && (
카테고리 값 로딩 중...
)} {/* 카테고리 값 목록 표시 */} {categoryValues.length > 0 && (
{categoryValues.map((cv) => (
{cv.valueCode} {cv.valueLabel}
))}
)} {/* 기본값 설정 */} {categoryValues.length > 0 && (

화면 로드 시 자동 선택될 카테고리 값

)} {/* 카테고리 값 없음 안내 */} {!loadingCategoryValues && categoryValues.length === 0 && (

카테고리 값이 없습니다. 테이블 카테고리 관리에서 값을 추가해주세요.

)}
)} {/* 정적 옵션 관리 */} {effectiveSource === "static" && (
{options.map((option: any, index: number) => (
updateOptionValue(index, e.target.value)} placeholder={`옵션 ${index + 1}`} className="h-7 flex-1 text-xs" />
))} {options.length === 0 && (

옵션을 추가해주세요

)}
{/* 기본값 설정 */} {options.length > 0 && (

화면 로드 시 자동 선택될 값

)}
)} {/* 공통 코드 설정 */} {effectiveSource === "code" && (
{config.codeGroup ? (

{config.codeGroup}

) : (

테이블 타입 관리에서 코드 그룹을 설정해주세요

)}
)} {/* 엔티티(참조 테이블) 설정 */} {effectiveSource === "entity" && (

조인할 테이블명 (테이블 타입 관리에서 설정된 경우 자동 입력됨)

{loadingColumns && (
컬럼 목록 로딩 중...
)}
{entityColumns.length > 0 ? ( ) : ( updateConfig("entityValueColumn", e.target.value)} placeholder="id" className="h-8 text-xs" /> )}

저장될 값

{entityColumns.length > 0 ? ( ) : ( updateConfig("entityLabelColumn", e.target.value)} placeholder="name" className="h-8 text-xs" /> )}

화면에 표시될 값

{config.entityTable && !loadingColumns && entityColumns.length === 0 && (

테이블 컬럼을 조회할 수 없습니다. 테이블 타입 관리에서 참조 테이블을 설정해주세요.

)} {config.entityTable && entityColumns.length > 0 && (

같은 폼에 참조 테이블({config.entityTable})의 컬럼이 배치되어 있으면, 엔티티 선택 시 해당 필드가 자동으로 채워집니다.

)}
)} {/* 추가 옵션 */}
updateConfig("multiple", checked)} />
updateConfig("searchable", checked)} />
updateConfig("allowClear", checked)} />
{/* 다중 선택 시 최대 개수 */} {config.multiple && (
updateConfig("maxSelect", e.target.value ? Number(e.target.value) : undefined)} placeholder="제한 없음" min="1" className="h-8 text-xs" />
)}
); }; V2SelectConfigPanel.displayName = "V2SelectConfigPanel"; export default V2SelectConfigPanel;