diff --git a/backend-node/src/services/tableManagementService.ts b/backend-node/src/services/tableManagementService.ts index 5b598422..8fc854c5 100644 --- a/backend-node/src/services/tableManagementService.ts +++ b/backend-node/src/services/tableManagementService.ts @@ -1465,6 +1465,31 @@ export class TableManagementService { const webType = columnInfo.webType; + // ๐Ÿ”ง ๋‹ค์ค‘์„ ํƒ ์ฒ˜๋ฆฌ: actualValue๊ฐ€ ํŒŒ์ดํ”„(|)๋ฅผ ํฌํ•จํ•˜๊ณ  ๋‚ ์งœ ํƒ€์ž…์ด ์•„๋‹Œ ๊ฒฝ์šฐ + if ( + typeof actualValue === "string" && + actualValue.includes("|") && + webType !== "date" && + webType !== "datetime" + ) { + const multiValues = actualValue + .split("|") + .filter((v: string) => v.trim() !== ""); + if (multiValues.length > 0) { + const placeholders = multiValues + .map((_: string, idx: number) => `$${paramIndex + idx}`) + .join(", "); + logger.info( + `๐Ÿ” ๋‹ค์ค‘์„ ํƒ ํ•„ํ„ฐ ์ ์šฉ (๊ฐ์ฒด): ${columnName} IN (${multiValues.join(", ")})` + ); + return { + whereClause: `${columnName}::text IN (${placeholders})`, + values: multiValues, + paramCount: multiValues.length, + }; + } + } + // ์›นํƒ€์ž…๋ณ„ ๊ฒ€์ƒ‰ ์กฐ๊ฑด ๊ตฌ์„ฑ switch (webType) { case "date": diff --git a/frontend/app/registry-provider.tsx b/frontend/app/registry-provider.tsx index d2bd3e32..1595bbe2 100644 --- a/frontend/app/registry-provider.tsx +++ b/frontend/app/registry-provider.tsx @@ -22,7 +22,7 @@ export function RegistryProvider({ children }: RegistryProviderProps) { // V2 Core ์ดˆ๊ธฐํ™” (๋А์Šจํ•œ ๊ฒฐํ•ฉ ์•„ํ‚คํ…์ฒ˜) initV2Core({ - debug: process.env.NODE_ENV === "development", + debug: false, legacyBridge: { legacyToV2: true, v2ToLegacy: true, diff --git a/frontend/components/screen/table-options/TableSettingsModal.tsx b/frontend/components/screen/table-options/TableSettingsModal.tsx new file mode 100644 index 00000000..ef07e017 --- /dev/null +++ b/frontend/components/screen/table-options/TableSettingsModal.tsx @@ -0,0 +1,668 @@ +"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} +
+
+
+ ))} +
+
+
+
+
+
+ + + + + +
+
+ ); +}; + diff --git a/frontend/components/table-category/CategoryValueManagerTree.tsx b/frontend/components/table-category/CategoryValueManagerTree.tsx index b65c2247..d4da04fc 100644 --- a/frontend/components/table-category/CategoryValueManagerTree.tsx +++ b/frontend/components/table-category/CategoryValueManagerTree.tsx @@ -3,9 +3,10 @@ /** * ์นดํ…Œ๊ณ ๋ฆฌ ๊ฐ’ ๊ด€๋ฆฌ - ํŠธ๋ฆฌ ๊ตฌ์กฐ ๋ฒ„์ „ * - 3๋‹จ๊ณ„ ํŠธ๋ฆฌ ๊ตฌ์กฐ ์ง€์› (๋Œ€๋ถ„๋ฅ˜/์ค‘๋ถ„๋ฅ˜/์†Œ๋ถ„๋ฅ˜) + * - ์ฒดํฌ๋ฐ•์Šค๋ฅผ ํ†ตํ•œ ๋‹ค์ค‘ ์„ ํƒ ๋ฐ ์ผ๊ด„ ์‚ญ์ œ ์ง€์› */ -import React, { useState, useEffect, useCallback } from "react"; +import React, { useState, useEffect, useCallback, useMemo } from "react"; import { ChevronRight, ChevronDown, @@ -20,6 +21,7 @@ import { } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; +import { Checkbox } from "@/components/ui/checkbox"; import { cn } from "@/lib/utils"; import { CategoryValue, @@ -65,11 +67,13 @@ interface TreeNodeProps { expandedNodes: Set; selectedValueId?: number; searchQuery: string; + checkedIds: Set; onToggle: (valueId: number) => void; onSelect: (value: CategoryValue) => void; onAdd: (parentValue: CategoryValue | null) => void; onEdit: (value: CategoryValue) => void; onDelete: (value: CategoryValue) => void; + onCheck: (valueId: number, checked: boolean) => void; } // ๊ฒ€์ƒ‰์–ด๊ฐ€ ๋…ธ๋“œ ๋˜๋Š” ํ•˜์œ„์— ๋งค์นญ๋˜๋Š”์ง€ ํ™•์ธ @@ -90,15 +94,18 @@ const TreeNode: React.FC = ({ expandedNodes, selectedValueId, searchQuery, + checkedIds, onToggle, onSelect, onAdd, onEdit, onDelete, + onCheck, }) => { const hasChildren = node.children && node.children.length > 0; const isExpanded = expandedNodes.has(node.valueId); const isSelected = selectedValueId === node.valueId; + const isChecked = checkedIds.has(node.valueId); const canAddChild = node.depth < 3; // ๊ฒ€์ƒ‰ ํ•„ํ„ฐ๋ง @@ -138,11 +145,22 @@ const TreeNode: React.FC = ({ className={cn( "group flex items-center gap-1 rounded-md px-2 py-2 transition-colors", isSelected ? "border-primary bg-primary/10 border-l-2" : "hover:bg-muted/50", + isChecked && "bg-primary/5", "cursor-pointer", )} style={{ paddingLeft: `${level * 20 + 8}px` }} onClick={() => onSelect(node)} > + {/* ์ฒดํฌ๋ฐ•์Šค */} + { + onCheck(node.valueId, checked as boolean); + }} + onClick={(e) => e.stopPropagation()} + className="mr-1" + /> + {/* ํ™•์žฅ ํ† ๊ธ€ */} +
+

{columnLabel} ์นดํ…Œ๊ณ ๋ฆฌ

+ {checkedIds.size > 0 && ( + + {checkedIds.size}๊ฐœ ์„ ํƒ + + )} +
+
+ {checkedIds.size > 0 && ( + <> + + + + )} + +
{/* ํˆด๋ฐ” */}
{/* ๊ฒ€์ƒ‰ */}
- + =
+ -
@@ -568,18 +744,19 @@ export const CategoryValueManagerTree: React.FC = expandedNodes={expandedNodes} selectedValueId={selectedValue?.valueId} searchQuery={searchQuery} + checkedIds={checkedIds} onToggle={handleToggle} onSelect={setSelectedValue} onAdd={handleOpenAddModal} onEdit={handleOpenEditModal} onDelete={handleOpenDeleteDialog} + onCheck={handleCheck} /> ))}
)} - {/* ์ถ”๊ฐ€ ๋ชจ๋‹ฌ */} @@ -588,7 +765,9 @@ export const CategoryValueManagerTree: React.FC = {parentValue ? `"${parentValue.valueLabel}" ํ•˜์œ„ ์ถ”๊ฐ€` : "๋Œ€๋ถ„๋ฅ˜ ์ถ”๊ฐ€"} - {parentValue ? `${parentValue.depth + 1}๋‹จ๊ณ„ ์นดํ…Œ๊ณ ๋ฆฌ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค` : "1๋‹จ๊ณ„ ๋Œ€๋ถ„๋ฅ˜ ์นดํ…Œ๊ณ ๋ฆฌ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค"} + {parentValue + ? `${parentValue.depth + 1}๋‹จ๊ณ„ ์นดํ…Œ๊ณ ๋ฆฌ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค` + : "1๋‹จ๊ณ„ ๋Œ€๋ถ„๋ฅ˜ ์นดํ…Œ๊ณ ๋ฆฌ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค"} @@ -631,7 +810,11 @@ export const CategoryValueManagerTree: React.FC = - - - - - - + )} - {/* ํŒจ๋„๋“ค */} - setColumnVisibilityOpen(false)} /> - setFilterOpen(false)} + {/* ํ†ตํ•ฉ ์„ค์ • ๋ชจ๋‹ฌ */} + setSettingsOpen(false)} onFiltersApplied={(filters) => setActiveFilters(filters)} screenId={screenId} /> - setGroupingOpen(false)} /> ); } diff --git a/frontend/lib/utils/buttonActions.ts b/frontend/lib/utils/buttonActions.ts index cb5bdfcc..4e6d2ac4 100644 --- a/frontend/lib/utils/buttonActions.ts +++ b/frontend/lib/utils/buttonActions.ts @@ -529,8 +529,6 @@ export class ButtonActionExecutor { // ์•ฝ๊ฐ„์˜ ๋Œ€๊ธฐ ์‹œ๊ฐ„์„ ์ฃผ์–ด ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ formData๋ฅผ ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•จ await new Promise((resolve) => setTimeout(resolve, 100)); - console.log("๐Ÿ“ฆ [handleSave] beforeFormSave ์ด๋ฒคํŠธ ํ›„ formData keys:", Object.keys(context.formData || {})); - // ๊ฒ€์ฆ ์‹คํŒจ ์‹œ ์ €์žฅ ์ค‘๋‹จ if (beforeSaveEventDetail.validationFailed) { console.log("โŒ [handleSave] ๊ฒ€์ฆ ์‹คํŒจ๋กœ ์ €์žฅ ์ค‘๋‹จ:", beforeSaveEventDetail.validationErrors); @@ -549,13 +547,13 @@ export class ButtonActionExecutor { ); if (hasTableSectionData) { - console.log("๐Ÿ“‹ [handleSave] _tableSection_ ๋ฐ์ดํ„ฐ ๊ฐ์ง€ - onSave ์ฝœ๋ฐฑ ๊ฑด๋„ˆ๋›ฐ๊ณ  ํ…Œ์ด๋ธ” ์„น์…˜ ์ €์žฅ ๋กœ์ง ์‚ฌ์šฉ"); + } // ๐Ÿ†• EditModal ๋“ฑ์—์„œ ์ „๋‹ฌ๋œ onSave ์ฝœ๋ฐฑ์ด ์žˆ์œผ๋ฉด ์šฐ์„  ์‚ฌ์šฉ // ๋‹จ, _tableSection_ ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ์œผ๋ฉด ๊ฑด๋„ˆ๋›ฐ๊ธฐ (handleUniversalFormModalTableSectionSave๊ฐ€ ์ฒ˜๋ฆฌ) if (onSave && !hasTableSectionData) { - console.log("โœ… [handleSave] onSave ์ฝœ๋ฐฑ ๋ฐœ๊ฒฌ - ์ฝœ๋ฐฑ ์‹คํ–‰ (ํ…Œ์ด๋ธ” ์„น์…˜ ๋ฐ์ดํ„ฐ ์—†์Œ)"); + try { await onSave(); return true; @@ -2214,14 +2212,6 @@ export class ButtonActionExecutor { // ์„น์…˜๋ณ„ ์›๋ณธ ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ์œผ๋ฉด ์‚ฌ์šฉ, ์—†์œผ๋ฉด ์ „์—ญ originalGroupedData ์‚ฌ์šฉ const originalDataForDelete = sectionOriginalData.length > 0 ? sectionOriginalData : originalGroupedData; - console.log(`๐Ÿ” [DELETE ๋น„๊ต] ์„น์…˜ ${sectionId}:`, { - sectionOriginalKey, - sectionOriginalCount: sectionOriginalData.length, - globalOriginalCount: originalGroupedData.length, - usingData: sectionOriginalData.length > 0 ? "์„น์…˜๋ณ„ ์›๋ณธ" : "์ „์—ญ ์›๋ณธ", - currentCount: currentItems.length, - }); - // โš ๏ธ id ํƒ€์ž… ํ†ต์ผ: ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ๋น„๊ต (์ˆซ์ž vs ๋ฌธ์ž์—ด ๋ถˆ์ผ์น˜ ๋ฐฉ์ง€) const currentIds = new Set(currentItems.map((item) => String(item.id)).filter(Boolean)); const deletedItems = originalDataForDelete.filter((orig) => orig.id && !currentIds.has(String(orig.id))); diff --git a/frontend/lib/v2-core/adapters/LegacyEventAdapter.ts b/frontend/lib/v2-core/adapters/LegacyEventAdapter.ts index b1779b5f..f37aa726 100644 --- a/frontend/lib/v2-core/adapters/LegacyEventAdapter.ts +++ b/frontend/lib/v2-core/adapters/LegacyEventAdapter.ts @@ -319,8 +319,6 @@ class LegacyEventAdapter { this.config = { ...this.config, ...options }; } - console.log("[LegacyEventAdapter] ์ดˆ๊ธฐํ™” ์‹œ์ž‘", this.config); - EVENT_MAPPINGS.forEach((mapping) => { // ๋ ˆ๊ฑฐ์‹œ โ†’ V2 ๋ธŒ๋ฆฟ์ง€ if (this.config.legacyToV2) { @@ -334,9 +332,6 @@ class LegacyEventAdapter { }); this.isActive = true; - console.log( - `[LegacyEventAdapter] ์ดˆ๊ธฐํ™” ์™„๋ฃŒ (${EVENT_MAPPINGS.length}๊ฐœ ๋งคํ•‘)` - ); } private setupLegacyToV2Bridge(mapping: EventMapping): void { @@ -411,8 +406,6 @@ class LegacyEventAdapter { this.bridgedEvents.clear(); this.isActive = false; - - console.log("[LegacyEventAdapter] ์ •๋ฆฌ ์™„๋ฃŒ"); } /** diff --git a/frontend/lib/v2-core/init.ts b/frontend/lib/v2-core/init.ts index 93eabb80..8ec5dc11 100644 --- a/frontend/lib/v2-core/init.ts +++ b/frontend/lib/v2-core/init.ts @@ -55,8 +55,6 @@ export function initV2Core(options?: V2CoreOptions): void { legacyBridge = { legacyToV2: true, v2ToLegacy: true }, } = options ?? {}; - console.log("[V2Core] ์ดˆ๊ธฐํ™” ์‹œ์ž‘..."); - // ๋””๋ฒ„๊ทธ ๋ชจ๋“œ ์„ค์ • v2EventBus.debug = debug; @@ -64,11 +62,6 @@ export function initV2Core(options?: V2CoreOptions): void { legacyEventAdapter.init(legacyBridge); isInitialized = true; - - console.log("[V2Core] ์ดˆ๊ธฐํ™” ์™„๋ฃŒ", { - debug, - legacyBridge: legacyEventAdapter.active, - }); } /**