"use client"; import React, { useEffect, useState } from "react"; import { Check, ChevronsUpDown, Loader2 } from "lucide-react"; import { Label } from "@/components/ui/label"; import { Input } from "@/components/ui/input"; import { Switch } from "@/components/ui/switch"; import { Button } from "@/components/ui/button"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@/components/ui/accordion"; import { Checkbox } from "@/components/ui/checkbox"; 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 { tableTypeApi } from "@/lib/api/screen"; import { TableGroupedConfig, ColumnConfig, LinkedFilterConfig } from "./types"; import { groupHeaderStyleOptions, checkboxModeOptions, sortDirectionOptions, } from "./config"; import { Trash2, Plus } from "lucide-react"; interface TableGroupedConfigPanelProps { config: TableGroupedConfig; onConfigChange: (newConfig: TableGroupedConfig) => void; } /** * v2-table-grouped 설정 패널 */ // 테이블 정보 타입 interface TableInfo { tableName: string; displayName: string; } export function TableGroupedConfigPanel({ config, onConfigChange, }: TableGroupedConfigPanelProps) { // 테이블 목록 (라벨명 포함) const [tables, setTables] = useState([]); const [tableColumns, setTableColumns] = useState([]); const [loadingTables, setLoadingTables] = useState(false); const [loadingColumns, setLoadingColumns] = useState(false); const [tableSelectOpen, setTableSelectOpen] = useState(false); // 테이블 목록 로드 useEffect(() => { const loadTables = async () => { setLoadingTables(true); try { const tableList = await tableTypeApi.getTables(); if (tableList && Array.isArray(tableList)) { setTables( tableList.map((t: any) => ({ tableName: t.tableName || t.table_name, displayName: t.displayName || t.display_name || t.tableName || t.table_name, })) ); } } catch (err) { console.error("테이블 목록 로드 실패:", err); } finally { setLoadingTables(false); } }; loadTables(); }, []); // 선택된 테이블의 컬럼 로드 useEffect(() => { const tableName = config.useCustomTable ? config.customTableName : config.selectedTable; if (!tableName) { setTableColumns([]); return; } const loadColumns = async () => { setLoadingColumns(true); try { const columns = await tableTypeApi.getColumns(tableName); if (columns && Array.isArray(columns)) { const cols: ColumnConfig[] = columns.map( (col: any, idx: number) => ({ columnName: col.column_name || col.columnName, displayName: col.display_name || col.displayName || col.column_name || col.columnName, visible: true, sortable: true, searchable: false, align: "left" as const, order: idx, }) ); setTableColumns(cols); // 컬럼 설정이 없으면 자동 설정 if (!config.columns || config.columns.length === 0) { onConfigChange({ ...config, columns: cols }); } } } catch (err) { console.error("컬럼 로드 실패:", err); } finally { setLoadingColumns(false); } }; loadColumns(); }, [config.selectedTable, config.customTableName, config.useCustomTable]); // 설정 업데이트 헬퍼 const updateConfig = (updates: Partial) => { onConfigChange({ ...config, ...updates }); }; // 그룹 설정 업데이트 헬퍼 const updateGroupConfig = ( updates: Partial ) => { onConfigChange({ ...config, groupConfig: { ...config.groupConfig, ...updates }, }); }; // 컬럼 가시성 토글 const toggleColumnVisibility = (columnName: string) => { const updatedColumns = (config.columns || []).map((col) => col.columnName === columnName ? { ...col, visible: !col.visible } : col ); updateConfig({ columns: updatedColumns }); }; // 합계 컬럼 토글 const toggleSumColumn = (columnName: string) => { const currentSumCols = config.groupConfig?.summary?.sumColumns || []; const newSumCols = currentSumCols.includes(columnName) ? currentSumCols.filter((c) => c !== columnName) : [...currentSumCols, columnName]; updateGroupConfig({ summary: { ...config.groupConfig?.summary, sumColumns: newSumCols, }, }); }; // 연결 필터 추가 const addLinkedFilter = () => { const newFilter: LinkedFilterConfig = { sourceComponentId: "", sourceField: "value", targetColumn: "", enabled: true, }; updateConfig({ linkedFilters: [...(config.linkedFilters || []), newFilter], }); }; // 연결 필터 제거 const removeLinkedFilter = (index: number) => { const filters = [...(config.linkedFilters || [])]; filters.splice(index, 1); updateConfig({ linkedFilters: filters }); }; // 연결 필터 업데이트 const updateLinkedFilter = ( index: number, updates: Partial ) => { const filters = [...(config.linkedFilters || [])]; filters[index] = { ...filters[index], ...updates }; updateConfig({ linkedFilters: filters }); }; return (
{/* 테이블 설정 */} 테이블 설정 {/* 커스텀 테이블 사용 */}
updateConfig({ useCustomTable: checked }) } />
{/* 테이블 선택 */} {config.useCustomTable ? (
updateConfig({ customTableName: e.target.value }) } placeholder="테이블명 입력" className="h-8 text-xs" />
) : (
{ // 테이블명 또는 라벨명에 검색어가 포함되면 1, 아니면 0 const lowerSearch = search.toLowerCase(); if (value.toLowerCase().includes(lowerSearch)) { return 1; } return 0; }} > 테이블을 찾을 수 없습니다. {tables.map((table) => ( { updateConfig({ selectedTable: table.tableName }); setTableSelectOpen(false); }} className="text-xs" >
{table.displayName} {table.tableName}
))}
)}
{/* 그룹화 설정 */} 그룹화 설정 {/* 그룹화 기준 컬럼 */}
{/* 그룹 라벨 형식 */}
updateGroupConfig({ groupLabelFormat: e.target.value }) } placeholder="{value} ({컬럼명})" className="h-8 text-xs" />

{"{value}"} = 그룹값, {"{컬럼명}"} = 해당 컬럼 값

{/* 기본 펼침 상태 */}
updateGroupConfig({ defaultExpanded: checked }) } />
{/* 그룹 정렬 */}
{/* 개수 표시 */}
updateGroupConfig({ summary: { ...config.groupConfig?.summary, showCount: checked, }, }) } />
{/* 합계 컬럼 */}
{tableColumns.map((col) => (
toggleSumColumn(col.columnName)} />
))}
{/* 표시 설정 */} 표시 설정 {/* 체크박스 표시 */}
updateConfig({ showCheckbox: checked }) } />
{/* 체크박스 모드 */} {config.showCheckbox && (
)} {/* 그룹 헤더 스타일 */}
{/* 전체 펼치기/접기 버튼 */}
updateConfig({ showExpandAllButton: checked }) } />
{/* 행 클릭 가능 */}
updateConfig({ rowClickable: checked }) } />
{/* 최대 높이 */}
updateConfig({ maxHeight: parseInt(e.target.value) || 600 }) } className="h-8 text-xs" />
{/* 빈 데이터 메시지 */}
updateConfig({ emptyMessage: e.target.value }) } placeholder="데이터가 없습니다." className="h-8 text-xs" />
{/* 컬럼 설정 */} 컬럼 설정
{(config.columns || tableColumns).map((col) => (
toggleColumnVisibility(col.columnName) } />
))}
{/* 연동 설정 */} 연동 설정
{(config.linkedFilters || []).length === 0 ? (

연결된 필터가 없습니다.

) : (
{(config.linkedFilters || []).map((filter, idx) => (
필터 #{idx + 1}
updateLinkedFilter(idx, { enabled: checked }) } />
updateLinkedFilter(idx, { sourceComponentId: e.target.value, }) } placeholder="예: search-filter-1" className="h-7 text-xs" />
updateLinkedFilter(idx, { sourceField: e.target.value, }) } placeholder="value" className="h-7 text-xs" />
))}
)}

다른 컴포넌트(검색필터 등)의 선택 값으로 이 테이블을 필터링합니다.

); } export default TableGroupedConfigPanel;