"use client"; import React, { useState, useEffect } from "react"; import { useTableOptions } from "@/contexts/TableOptionsContext"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; import { Checkbox } from "@/components/ui/checkbox"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { ScrollArea } from "@/components/ui/scroll-area"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Switch } from "@/components/ui/switch"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { GripVertical, Eye, EyeOff, Lock, ArrowRight, X, Settings, Filter, Layers } from "lucide-react"; import { ColumnVisibility, TableFilter, GroupSumConfig } from "@/types/table-options"; interface Props { isOpen: boolean; onClose: () => void; onFiltersApplied?: (filters: TableFilter[]) => void; screenId?: number; } // 컬럼 필터 설정 인터페이스 interface ColumnFilterConfig { columnName: string; columnLabel: string; inputType: string; enabled: boolean; filterType: "text" | "number" | "date" | "select"; width?: number; selectOptions?: Array<{ label: string; value: string }>; } export const TableSettingsModal: React.FC = ({ isOpen, onClose, onFiltersApplied, screenId }) => { const { getTable, selectedTableId } = useTableOptions(); const table = selectedTableId ? getTable(selectedTableId) : undefined; const [activeTab, setActiveTab] = useState("columns"); // 컬럼 가시성 상태 const [localColumns, setLocalColumns] = useState([]); const [draggedColumnIndex, setDraggedColumnIndex] = useState(null); const [frozenColumnCount, setFrozenColumnCount] = useState(0); // 필터 상태 const [columnFilters, setColumnFilters] = useState([]); const [selectAllFilters, setSelectAllFilters] = useState(false); const [groupSumEnabled, setGroupSumEnabled] = useState(false); const [groupByColumn, setGroupByColumn] = useState(""); // 그룹화 상태 const [selectedGroupColumns, setSelectedGroupColumns] = useState([]); const [draggedGroupIndex, setDraggedGroupIndex] = useState(null); // 테이블 정보 로드 - 컬럼 가시성 useEffect(() => { if (table) { setLocalColumns( table.columns.map((col) => ({ columnName: col.columnName, visible: col.visible, width: col.width, order: 0, })) ); setFrozenColumnCount(table.frozenColumnCount ?? 0); } }, [table]); // 테이블 정보 로드 - 필터 useEffect(() => { if (table?.columns && table?.tableName) { const storageKey = screenId ? `table_filters_${table.tableName}_screen_${screenId}` : `table_filters_${table.tableName}`; const savedFilters = localStorage.getItem(storageKey); const groupSumKey = screenId ? `table_groupsum_${table.tableName}_screen_${screenId}` : `table_groupsum_${table.tableName}`; const savedGroupSum = localStorage.getItem(groupSumKey); if (savedGroupSum) { try { const parsed = JSON.parse(savedGroupSum) as GroupSumConfig; setGroupSumEnabled(parsed.enabled); setGroupByColumn(parsed.groupByColumn || ""); } catch { setGroupSumEnabled(false); setGroupByColumn(""); } } if (savedFilters) { try { const parsed = JSON.parse(savedFilters); setColumnFilters(parsed); setSelectAllFilters(parsed.every((f: ColumnFilterConfig) => f.enabled)); } catch { initializeFilters(); } } else { initializeFilters(); } } }, [table?.columns, table?.tableName, screenId]); const initializeFilters = () => { if (!table?.columns) return; const filters: ColumnFilterConfig[] = table.columns .filter((col) => col.columnName !== "__checkbox__") .map((col) => { let filterType: "text" | "number" | "date" | "select" = "text"; const inputType = col.inputType || ""; if (["number", "decimal", "currency", "integer"].includes(inputType)) { filterType = "number"; } else if (["date", "datetime", "time"].includes(inputType)) { filterType = "date"; } else if (["select", "dropdown", "code", "category", "entity"].includes(inputType)) { filterType = "select"; } return { columnName: col.columnName, columnLabel: col.columnLabel, inputType, enabled: false, filterType, width: 200, }; }); setColumnFilters(filters); setSelectAllFilters(false); }; // 컬럼 가시성 핸들러 const handleVisibilityChange = (columnName: string, visible: boolean) => { setLocalColumns((prev) => prev.map((col) => (col.columnName === columnName ? { ...col, visible } : col)) ); }; const handleWidthChange = (columnName: string, width: number) => { setLocalColumns((prev) => prev.map((col) => (col.columnName === columnName ? { ...col, width } : col)) ); }; const moveColumn = (fromIndex: number, toIndex: number) => { const newColumns = [...localColumns]; const [movedItem] = newColumns.splice(fromIndex, 1); newColumns.splice(toIndex, 0, movedItem); setLocalColumns(newColumns); }; // 필터 핸들러 const handleFilterEnabledChange = (columnName: string, enabled: boolean) => { setColumnFilters((prev) => prev.map((f) => (f.columnName === columnName ? { ...f, enabled } : f)) ); }; const handleFilterTypeChange = (columnName: string, filterType: "text" | "number" | "date" | "select") => { setColumnFilters((prev) => prev.map((f) => (f.columnName === columnName ? { ...f, filterType } : f)) ); }; const handleFilterWidthChange = (columnName: string, width: number) => { setColumnFilters((prev) => prev.map((f) => (f.columnName === columnName ? { ...f, width } : f)) ); }; const handleSelectAll = (checked: boolean) => { setSelectAllFilters(checked); setColumnFilters((prev) => prev.map((f) => ({ ...f, enabled: checked }))); }; // 그룹화 핸들러 const toggleGroupColumn = (columnName: string) => { if (selectedGroupColumns.includes(columnName)) { setSelectedGroupColumns(selectedGroupColumns.filter((c) => c !== columnName)); } else { setSelectedGroupColumns([...selectedGroupColumns, columnName]); } }; const removeGroupColumn = (columnName: string) => { setSelectedGroupColumns(selectedGroupColumns.filter((c) => c !== columnName)); }; const moveGroupColumn = (fromIndex: number, toIndex: number) => { const newColumns = [...selectedGroupColumns]; const [movedItem] = newColumns.splice(fromIndex, 1); newColumns.splice(toIndex, 0, movedItem); setSelectedGroupColumns(newColumns); }; const clearGrouping = () => { setSelectedGroupColumns([]); table?.onGroupChange([]); }; // 틀고정 컬럼 수 변경 핸들러 const handleFrozenColumnCountChange = (value: string) => { const count = parseInt(value) || 0; // 최대값은 표시 가능한 컬럼 수 const maxCount = localColumns.filter((col) => col.visible).length; setFrozenColumnCount(Math.min(Math.max(0, count), maxCount)); }; const visibleCount = localColumns.filter((col) => col.visible).length; // 저장 const handleSave = () => { if (!table) return; // 1. 컬럼 가시성 저장 table.onColumnVisibilityChange(localColumns); // 2. 컬럼 순서 변경 콜백 호출 if (table.onColumnOrderChange) { const newOrder = localColumns .map((col) => col.columnName) .filter((name) => name !== "__checkbox__"); table.onColumnOrderChange(newOrder); } // 3. 틀고정 컬럼 수 변경 콜백 호출 (현재 컬럼 상태도 함께 전달) if (table.onFrozenColumnCountChange) { const updatedColumns = localColumns.map((col) => ({ columnName: col.columnName, visible: col.visible, })); table.onFrozenColumnCountChange(frozenColumnCount, updatedColumns); } // 2. 필터 설정 저장 const storageKey = screenId ? `table_filters_${table.tableName}_screen_${screenId}` : `table_filters_${table.tableName}`; localStorage.setItem(storageKey, JSON.stringify(columnFilters)); // 그룹별 합산 설정 저장 const groupSumKey = screenId ? `table_groupsum_${table.tableName}_screen_${screenId}` : `table_groupsum_${table.tableName}`; const groupSumConfig: GroupSumConfig = { enabled: groupSumEnabled, groupByColumn: groupByColumn || undefined, }; localStorage.setItem(groupSumKey, JSON.stringify(groupSumConfig)); // 활성화된 필터만 콜백 const activeFilters: TableFilter[] = columnFilters .filter((f) => f.enabled) .map((f) => ({ columnName: f.columnName, operator: "contains", value: "", filterType: f.filterType, width: f.width || 200, })); onFiltersApplied?.(activeFilters); // 3. 그룹화 저장 table.onGroupChange(selectedGroupColumns); onClose(); }; return ( 테이블 설정 테이블의 컬럼, 필터, 그룹화를 설정합니다 컬럼 설정 필터 설정 그룹 설정 {/* 컬럼 설정 탭 */}
{/* 상태 표시 및 틀고정 설정 */}
{visibleCount}/{localColumns.length}개 컬럼 표시 중
{/* 틀고정 설정 */}
handleFrozenColumnCountChange(e.target.value)} className="h-7 w-16 text-xs sm:h-8 sm:w-20 sm:text-sm" min={0} max={visibleCount} placeholder="0" /> 개 컬럼
{/* 컬럼 목록 */}
{localColumns.map((col, index) => { const originalCol = table?.columns.find((c) => c.columnName === col.columnName); if (!originalCol) return null; // 표시 가능한 컬럼 중 몇 번째인지 계산 (틀고정 표시용) const visibleIndex = localColumns .slice(0, index + 1) .filter((c) => c.visible).length; const isFrozen = col.visible && visibleIndex <= frozenColumnCount; return (
setDraggedColumnIndex(index)} onDragOver={(e) => { e.preventDefault(); if (draggedColumnIndex !== null && draggedColumnIndex !== index) { moveColumn(draggedColumnIndex, index); setDraggedColumnIndex(index); } }} onDragEnd={() => setDraggedColumnIndex(null)} className={`flex cursor-move items-center gap-3 rounded-lg border p-3 transition-colors ${ isFrozen ? "border-blue-200 bg-blue-50 dark:border-blue-800 dark:bg-blue-950/30" : "bg-background hover:bg-muted/50" }`} > {/* 드래그 핸들 */} {/* 체크박스 */} handleVisibilityChange(col.columnName, checked as boolean) } /> {/* 가시성/틀고정 아이콘 */} {isFrozen ? ( ) : col.visible ? ( ) : ( )} {/* 컬럼명 */}
{originalCol.columnLabel} {isFrozen && ( (고정) )}
{col.columnName}
{/* 너비 설정 */}
handleWidthChange(col.columnName, parseInt(e.target.value) || 150)} className="h-7 w-16 text-xs sm:h-8 sm:w-20 sm:text-sm" min={50} max={500} />
); })}
{/* 필터 설정 탭 */}
{/* 전체 선택 */}
handleSelectAll(checked as boolean)} />
{/* 필터 목록 */}
{columnFilters.map((filter) => (
handleFilterEnabledChange(filter.columnName, checked as boolean) } />
{filter.columnLabel}
handleFilterWidthChange(filter.columnName, parseInt(e.target.value) || 200) } className="h-7 w-16 text-center text-xs" /> px
))}
{/* 그룹별 합산 설정 */}
그룹별 합산
같은 값끼리 그룹핑하여 합산
{groupSumEnabled && (
)}
{/* 그룹 설정 탭 */}
{/* 선택된 그룹화 컬럼 */} {selectedGroupColumns.length > 0 && (
그룹화 순서 ({selectedGroupColumns.length}개)
{selectedGroupColumns.map((colName, index) => { const col = table?.columns.find((c) => c.columnName === colName); if (!col) return null; return (
setDraggedGroupIndex(index)} onDragOver={(e) => { e.preventDefault(); if (draggedGroupIndex !== null && draggedGroupIndex !== index) { moveGroupColumn(draggedGroupIndex, index); setDraggedGroupIndex(index); } }} onDragEnd={() => setDraggedGroupIndex(null)} className="hover:bg-primary/10 bg-primary/5 flex cursor-move items-center gap-2 rounded-lg border p-2 transition-colors" >
{index + 1}
{col.columnLabel}
); })}
{/* 그룹화 순서 미리보기 */}
{selectedGroupColumns.map((colName, index) => { const col = table?.columns.find((c) => c.columnName === colName); return ( {col?.columnLabel} {index < selectedGroupColumns.length - 1 && ( )} ); })}
)} {/* 사용 가능한 컬럼 */}
사용 가능한 컬럼
0 ? "h-[200px]" : "h-[320px]"}>
{table?.columns .filter((col) => !selectedGroupColumns.includes(col.columnName)) .map((col) => (
toggleGroupColumn(col.columnName)} > toggleGroupColumn(col.columnName)} className="flex-shrink-0" />
{col.columnLabel}
{col.columnName}
))}
); };