diff --git a/frontend/lib/registry/components/modal-repeater-table/RepeaterTable.tsx b/frontend/lib/registry/components/modal-repeater-table/RepeaterTable.tsx index 70b15e7d..995ebccb 100644 --- a/frontend/lib/registry/components/modal-repeater-table/RepeaterTable.tsx +++ b/frontend/lib/registry/components/modal-repeater-table/RepeaterTable.tsx @@ -1,6 +1,6 @@ "use client"; -import React, { useState, useEffect, useRef } from "react"; +import React, { useState, useEffect, useRef, useMemo } from "react"; import { Input } from "@/components/ui/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; @@ -84,6 +84,9 @@ export function RepeaterTable({ onSelectionChange, equalizeWidthsTrigger, }: RepeaterTableProps) { + // 히든 컬럼을 제외한 표시 가능한 컬럼만 필터링 + const visibleColumns = useMemo(() => columns.filter((col) => !col.hidden), [columns]); + // 컨테이너 ref - 실제 너비 측정용 const containerRef = useRef(null); @@ -145,7 +148,7 @@ export function RepeaterTable({ // 컬럼 너비 상태 관리 const [columnWidths, setColumnWidths] = useState>(() => { const widths: Record = {}; - columns.forEach((col) => { + columns.filter((col) => !col.hidden).forEach((col) => { widths[col.field] = col.width ? parseInt(col.width) : 120; }); return widths; @@ -154,11 +157,11 @@ export function RepeaterTable({ // 기본 너비 저장 (리셋용) const defaultWidths = React.useMemo(() => { const widths: Record = {}; - columns.forEach((col) => { + visibleColumns.forEach((col) => { widths[col.field] = col.width ? parseInt(col.width) : 120; }); return widths; - }, [columns]); + }, [visibleColumns]); // 리사이즈 상태 const [resizing, setResizing] = useState<{ field: string; startX: number; startWidth: number } | null>(null); @@ -206,7 +209,7 @@ export function RepeaterTable({ // 해당 컬럼의 가장 긴 글자 너비 계산 // equalWidth: 균등 분배 시 너비 (값이 없는 컬럼의 최소값으로 사용) const calculateColumnContentWidth = (field: string, equalWidth: number): number => { - const column = columns.find((col) => col.field === field); + const column = visibleColumns.find((col) => col.field === field); if (!column) return equalWidth; // 날짜 필드는 110px (yyyy-MM-dd) @@ -257,7 +260,7 @@ export function RepeaterTable({ // 헤더 더블클릭: 해당 컬럼만 글자 너비에 맞춤 const handleDoubleClick = (field: string) => { const availableWidth = getAvailableWidth(); - const equalWidth = Math.max(60, Math.floor(availableWidth / columns.length)); + const equalWidth = Math.max(60, Math.floor(availableWidth / visibleColumns.length)); const contentWidth = calculateColumnContentWidth(field, equalWidth); setColumnWidths((prev) => ({ ...prev, @@ -268,10 +271,10 @@ export function RepeaterTable({ // 균등 분배: 컬럼 수로 테이블 너비를 균등 분배 const applyEqualizeWidths = () => { const availableWidth = getAvailableWidth(); - const equalWidth = Math.max(60, Math.floor(availableWidth / columns.length)); + const equalWidth = Math.max(60, Math.floor(availableWidth / visibleColumns.length)); const newWidths: Record = {}; - columns.forEach((col) => { + visibleColumns.forEach((col) => { newWidths[col.field] = equalWidth; }); @@ -280,15 +283,15 @@ export function RepeaterTable({ // 자동 맞춤: 각 컬럼을 글자 너비에 맞추고, 컨테이너보다 작으면 남는 공간 분배 const applyAutoFitWidths = () => { - if (columns.length === 0) return; + if (visibleColumns.length === 0) return; // 균등 분배 너비 계산 (값이 없는 컬럼의 최소값) const availableWidth = getAvailableWidth(); - const equalWidth = Math.max(60, Math.floor(availableWidth / columns.length)); + const equalWidth = Math.max(60, Math.floor(availableWidth / visibleColumns.length)); // 1. 각 컬럼의 글자 너비 계산 (값이 없으면 균등 분배 너비 사용) const newWidths: Record = {}; - columns.forEach((col) => { + visibleColumns.forEach((col) => { newWidths[col.field] = calculateColumnContentWidth(col.field, equalWidth); }); @@ -298,8 +301,8 @@ export function RepeaterTable({ // 3. 컨테이너보다 작으면 남는 공간을 균등 분배 (테이블 꽉 참 유지) if (totalContentWidth < availableWidth) { const extraSpace = availableWidth - totalContentWidth; - const extraPerColumn = Math.floor(extraSpace / columns.length); - columns.forEach((col) => { + const extraPerColumn = Math.floor(extraSpace / visibleColumns.length); + visibleColumns.forEach((col) => { newWidths[col.field] += extraPerColumn; }); } @@ -311,7 +314,7 @@ export function RepeaterTable({ // 초기 마운트 시 균등 분배 적용 useEffect(() => { if (initializedRef.current) return; - if (!containerRef.current || columns.length === 0) return; + if (!containerRef.current || visibleColumns.length === 0) return; const timer = setTimeout(() => { applyEqualizeWidths(); @@ -319,7 +322,7 @@ export function RepeaterTable({ }, 100); return () => clearTimeout(timer); - }, [columns]); + }, [visibleColumns]); // 트리거 감지: 1=균등분배, 2=자동맞춤 useEffect(() => { @@ -357,7 +360,7 @@ export function RepeaterTable({ document.removeEventListener("mousemove", handleMouseMove); document.removeEventListener("mouseup", handleMouseUp); }; - }, [resizing, columns, data]); + }, [resizing, visibleColumns, data]); // 데이터 변경 감지 (필요시 활성화) // useEffect(() => { @@ -531,7 +534,7 @@ export function RepeaterTable({ className={cn("border-gray-400", isIndeterminate && "data-[state=checked]:bg-primary")} /> - {columns.map((col) => { + {visibleColumns.map((col) => { const hasDynamicSource = col.dynamicDataSource?.enabled && col.dynamicDataSource.options.length > 0; const activeOptionId = activeDataSources[col.field] || col.dynamicDataSource?.defaultOptionId; const activeOption = hasDynamicSource @@ -631,7 +634,7 @@ export function RepeaterTable({ {data.length === 0 ? ( 추가된 항목이 없습니다 @@ -672,7 +675,7 @@ export function RepeaterTable({ /> {/* 데이터 컬럼들 */} - {columns.map((col) => ( + {visibleColumns.map((col) => ( = ({ // 소스 테이블의 카테고리 컬럼 정보 로드 useEffect(() => { const loadCategoryColumns = async () => { - console.log("[CalculationRuleEditor] sourceTableName:", sourceTableName); - if (!sourceTableName) { setCategoryColumns({}); return; @@ -92,7 +90,6 @@ const CalculationRuleEditor: React.FC = ({ try { const { getCategoryColumns } = await import("@/lib/api/tableCategoryValue"); const result = await getCategoryColumns(sourceTableName); - console.log("[CalculationRuleEditor] getCategoryColumns 결과:", result); if (result && result.success && Array.isArray(result.data)) { const categoryMap: Record = {}; @@ -103,7 +100,6 @@ const CalculationRuleEditor: React.FC = ({ categoryMap[colName] = true; } }); - console.log("[CalculationRuleEditor] categoryMap:", categoryMap); setCategoryColumns(categoryMap); } } catch (error) { @@ -128,29 +124,18 @@ const CalculationRuleEditor: React.FC = ({ const selectedColumn = columns.find((col) => col.field === conditionField); const actualFieldName = selectedColumn?.sourceField || conditionField; - console.log("[loadConditionOptions] 조건 필드:", { - conditionField, - actualFieldName, - sourceTableName, - categoryColumnsKeys: Object.keys(categoryColumns), - isCategoryColumn: categoryColumns[actualFieldName], - }); - // 소스 테이블에서 해당 컬럼이 카테고리 타입인지 확인 if (sourceTableName && categoryColumns[actualFieldName]) { try { setLoadingOptions(true); const { getCategoryValues } = await import("@/lib/api/tableCategoryValue"); - console.log("[loadConditionOptions] getCategoryValues 호출:", sourceTableName, actualFieldName); const result = await getCategoryValues(sourceTableName, actualFieldName, false); - console.log("[loadConditionOptions] getCategoryValues 결과:", result); if (result && result.success && Array.isArray(result.data)) { const options = result.data.map((item: any) => ({ // API 응답은 camelCase (valueCode, valueLabel) value: item.valueCode || item.value_code || item.value, label: item.valueLabel || item.displayLabel || item.display_label || item.label || item.valueCode || item.value_code || item.value, })); - console.log("[loadConditionOptions] 매핑된 옵션:", options); setCategoryOptions(options); } else { setCategoryOptions([]); @@ -1094,6 +1079,14 @@ function ColumnSettingItem({ /> 필수 +