From 417e1d297b52176aa7060af6bb22194deef71a05 Mon Sep 17 00:00:00 2001 From: kjs Date: Wed, 31 Dec 2025 13:53:30 +0900 Subject: [PATCH 1/2] =?UTF-8?q?=ED=8F=BC=20=EC=A1=B0=EA=B1=B4=EB=B3=84=20?= =?UTF-8?q?=EA=B3=84=EC=82=B0=EC=8B=9D=20=EC=84=A4=EC=A0=95=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TableSectionRenderer.tsx | 58 ++- .../modals/TableSectionSettingsModal.tsx | 474 ++++++++++++++++-- .../components/universal-form-modal/types.ts | 24 + 3 files changed, 505 insertions(+), 51 deletions(-) diff --git a/frontend/lib/registry/components/universal-form-modal/TableSectionRenderer.tsx b/frontend/lib/registry/components/universal-form-modal/TableSectionRenderer.tsx index a1c0bd76..1242e1d2 100644 --- a/frontend/lib/registry/components/universal-form-modal/TableSectionRenderer.tsx +++ b/frontend/lib/registry/components/universal-form-modal/TableSectionRenderer.tsx @@ -16,7 +16,13 @@ import { ItemSelectionModal } from "../modal-repeater-table/ItemSelectionModal"; import { RepeaterColumnConfig, CalculationRule } from "../modal-repeater-table/types"; // 타입 정의 -import { TableSectionConfig, TableColumnConfig, TableJoinCondition, FormDataState } from "./types"; +import { + TableSectionConfig, + TableColumnConfig, + TableJoinCondition, + FormDataState, + TableCalculationRule, +} from "./types"; interface TableSectionRendererProps { sectionId: string; @@ -811,39 +817,69 @@ export function TableSectionRenderer({ }); }, [tableConfig.columns, dynamicSelectOptionsMap]); - // 계산 규칙 변환 - const calculationRules: CalculationRule[] = (tableConfig.calculations || []).map(convertToCalculationRule); + // 원본 계산 규칙 (조건부 계산 포함) + const originalCalculationRules: TableCalculationRule[] = useMemo( + () => tableConfig.calculations || [], + [tableConfig.calculations], + ); - // 계산 로직 + // 기본 계산 규칙 변환 (RepeaterTable용 - 조건부 계산이 없는 경우에 사용) + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const calculationRules: CalculationRule[] = originalCalculationRules.map(convertToCalculationRule); + + // 조건부 계산 로직: 행의 조건 필드 값에 따라 적절한 계산식 선택 + const getFormulaForRow = useCallback((rule: TableCalculationRule, row: Record): string => { + // 조건부 계산이 활성화된 경우 + if (rule.conditionalCalculation?.enabled && rule.conditionalCalculation.conditionField) { + const conditionValue = row[rule.conditionalCalculation.conditionField]; + // 조건값과 일치하는 규칙 찾기 + const matchedRule = rule.conditionalCalculation.rules?.find((r) => r.conditionValue === conditionValue); + if (matchedRule) { + return matchedRule.formula; + } + // 일치하는 규칙이 없으면 기본 계산식 사용 + if (rule.conditionalCalculation.defaultFormula) { + return rule.conditionalCalculation.defaultFormula; + } + } + // 조건부 계산이 비활성화되었거나 기본값이 없으면 원래 계산식 사용 + return rule.formula; + }, []); + + // 계산 로직 (조건부 계산 지원) const calculateRow = useCallback( (row: any): any => { - if (calculationRules.length === 0) return row; + if (originalCalculationRules.length === 0) return row; const updatedRow = { ...row }; - for (const rule of calculationRules) { + for (const rule of originalCalculationRules) { try { - let formula = rule.formula; + // 조건부 계산에 따라 적절한 계산식 선택 + let formula = getFormulaForRow(rule, row); + + if (!formula) continue; + const fieldMatches = formula.match(/[a-zA-Z_][a-zA-Z0-9_]*/g) || []; const dependencies = rule.dependencies.length > 0 ? rule.dependencies : fieldMatches; for (const dep of dependencies) { - if (dep === rule.result) continue; + if (dep === rule.resultField) continue; const value = parseFloat(row[dep]) || 0; formula = formula.replace(new RegExp(`\\b${dep}\\b`, "g"), value.toString()); } const result = new Function(`return ${formula}`)(); - updatedRow[rule.result] = result; + updatedRow[rule.resultField] = result; } catch (error) { console.error(`계산 오류 (${rule.formula}):`, error); - updatedRow[rule.result] = 0; + updatedRow[rule.resultField] = 0; } } return updatedRow; }, - [calculationRules], + [originalCalculationRules, getFormulaForRow], ); const calculateAll = useCallback( diff --git a/frontend/lib/registry/components/universal-form-modal/modals/TableSectionSettingsModal.tsx b/frontend/lib/registry/components/universal-form-modal/modals/TableSectionSettingsModal.tsx index d82db59b..037707ca 100644 --- a/frontend/lib/registry/components/universal-form-modal/modals/TableSectionSettingsModal.tsx +++ b/frontend/lib/registry/components/universal-form-modal/modals/TableSectionSettingsModal.tsx @@ -24,6 +24,8 @@ import { TablePreFilter, TableModalFilter, TableCalculationRule, + ConditionalCalculationRule, + ConditionalCalculationConfig, LookupOption, LookupCondition, ConditionalTableOption, @@ -52,6 +54,429 @@ const HelpText = ({ children }: { children: React.ReactNode }) => (

{children}

); +// 계산 규칙 편집 컴포넌트 (조건부 계산 지원) +interface CalculationRuleEditorProps { + calc: TableCalculationRule; + index: number; + columns: TableColumnConfig[]; + sourceTableName?: string; // 소스 테이블명 추가 + onUpdate: (updates: Partial) => void; + onRemove: () => void; +} + +const CalculationRuleEditor: React.FC = ({ + calc, + index, + columns, + sourceTableName, + onUpdate, + onRemove, +}) => { + const [categoryOptions, setCategoryOptions] = useState<{ value: string; label: string }[]>([]); + const [loadingOptions, setLoadingOptions] = useState(false); + const [categoryColumns, setCategoryColumns] = useState>({}); + + // 조건부 계산 활성화 여부 + const isConditionalEnabled = calc.conditionalCalculation?.enabled ?? false; + + // 소스 테이블의 카테고리 컬럼 정보 로드 + useEffect(() => { + const loadCategoryColumns = async () => { + console.log("[CalculationRuleEditor] sourceTableName:", sourceTableName); + + if (!sourceTableName) { + setCategoryColumns({}); + return; + } + + 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 = {}; + result.data.forEach((col: any) => { + // API 응답은 camelCase (columnName) + const colName = col.columnName || col.column_name; + if (colName) { + categoryMap[colName] = true; + } + }); + console.log("[CalculationRuleEditor] categoryMap:", categoryMap); + setCategoryColumns(categoryMap); + } + } catch (error) { + console.error("카테고리 컬럼 조회 실패:", error); + } + }; + + loadCategoryColumns(); + }, [sourceTableName]); + + // 조건 필드가 선택되었을 때 옵션 로드 (테이블 타입 관리의 카테고리 기준) + useEffect(() => { + const loadConditionOptions = async () => { + if (!isConditionalEnabled || !calc.conditionalCalculation?.conditionField) { + setCategoryOptions([]); + return; + } + + const conditionField = calc.conditionalCalculation.conditionField; + + // 소스 필드(sourceField)가 있으면 해당 필드명 사용, 없으면 field명 사용 + 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([]); + } + } catch (error) { + console.error("카테고리 값 로드 실패:", error); + setCategoryOptions([]); + } finally { + setLoadingOptions(false); + } + return; + } + + // 카테고리 키가 직접 설정된 경우 (저장된 값) + const categoryKey = calc.conditionalCalculation?.conditionFieldCategoryKey; + if (categoryKey) { + try { + setLoadingOptions(true); + const [tableName, columnName] = categoryKey.split("."); + if (tableName && columnName) { + const { getCategoryValues } = await import("@/lib/api/tableCategoryValue"); + const result = await getCategoryValues(tableName, columnName, false); + if (result && result.success && Array.isArray(result.data)) { + setCategoryOptions( + 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, + })) + ); + } + } + } catch (error) { + console.error("카테고리 옵션 로드 실패:", error); + } finally { + setLoadingOptions(false); + } + return; + } + + // 그 외 타입은 옵션 없음 (직접 입력) + setCategoryOptions([]); + }; + + loadConditionOptions(); + }, [isConditionalEnabled, calc.conditionalCalculation?.conditionField, calc.conditionalCalculation?.conditionFieldCategoryKey, columns, sourceTableName, categoryColumns]); + + // 조건부 계산 토글 + const toggleConditionalCalculation = (enabled: boolean) => { + onUpdate({ + conditionalCalculation: enabled + ? { + enabled: true, + conditionField: "", + conditionFieldType: "static", + rules: [], + defaultFormula: calc.formula || "", + } + : undefined, + }); + }; + + // 조건 필드 변경 + const updateConditionField = (field: string) => { + const selectedColumn = columns.find((col) => col.field === field); + const actualFieldName = selectedColumn?.sourceField || field; + + // 컬럼의 타입과 옵션 확인 (테이블 타입 관리의 카테고리 기준) + let conditionFieldType: "static" | "code" | "table" = "static"; + let conditionFieldCategoryKey = ""; + + // 소스 테이블에서 해당 컬럼이 카테고리 타입인지 확인 + if (sourceTableName && categoryColumns[actualFieldName]) { + conditionFieldType = "code"; + conditionFieldCategoryKey = `${sourceTableName}.${actualFieldName}`; + } + + onUpdate({ + conditionalCalculation: { + ...calc.conditionalCalculation!, + conditionField: field, + conditionFieldType, + conditionFieldCategoryKey, + rules: [], // 필드 변경 시 규칙 초기화 + }, + }); + }; + + // 조건 규칙 추가 + const addConditionRule = () => { + const newRule: ConditionalCalculationRule = { + conditionValue: "", + formula: calc.formula || "", + }; + onUpdate({ + conditionalCalculation: { + ...calc.conditionalCalculation!, + rules: [...(calc.conditionalCalculation?.rules || []), newRule], + }, + }); + }; + + // 조건 규칙 업데이트 + const updateConditionRule = (ruleIndex: number, updates: Partial) => { + const newRules = [...(calc.conditionalCalculation?.rules || [])]; + newRules[ruleIndex] = { ...newRules[ruleIndex], ...updates }; + onUpdate({ + conditionalCalculation: { + ...calc.conditionalCalculation!, + rules: newRules, + }, + }); + }; + + // 조건 규칙 삭제 + const removeConditionRule = (ruleIndex: number) => { + onUpdate({ + conditionalCalculation: { + ...calc.conditionalCalculation!, + rules: (calc.conditionalCalculation?.rules || []).filter((_, i) => i !== ruleIndex), + }, + }); + }; + + // 기본 계산식 업데이트 + const updateDefaultFormula = (formula: string) => { + onUpdate({ + conditionalCalculation: { + ...calc.conditionalCalculation!, + defaultFormula: formula, + }, + }); + }; + + // 조건 필드로 사용 가능한 컬럼 (모든 컬럼) + const availableColumns = columns.filter((col) => col.field); + + return ( +
+ {/* 기본 계산 규칙 */} +
+ + = + onUpdate({ formula: e.target.value })} + placeholder="수식 (예: qty * unit_price)" + className="h-8 text-xs flex-1" + disabled={isConditionalEnabled} + /> + +
+ + {/* 조건부 계산 토글 */} +
+ + + {availableColumns.length === 0 && !isConditionalEnabled && ( + + (컬럼 설정에서 먼저 컬럼을 추가하세요) + + )} +
+ + {/* 조건부 계산 설정 */} + {isConditionalEnabled && ( +
+ {/* 조건 필드 선택 */} +
+ + +
+ + {/* 조건별 계산식 목록 */} + {calc.conditionalCalculation?.conditionField && ( +
+
+ + +
+ + {(calc.conditionalCalculation?.rules || []).map((rule, ruleIndex) => ( +
+ {/* 조건값 선택 */} + {categoryOptions.length > 0 ? ( + + ) : ( + + updateConditionRule(ruleIndex, { conditionValue: e.target.value }) + } + placeholder="조건값" + className="h-7 text-xs w-[120px]" + /> + )} + + + updateConditionRule(ruleIndex, { formula: e.target.value }) + } + placeholder="계산식" + className="h-7 text-xs flex-1" + /> + +
+ ))} + + {/* 기본 계산식 */} +
+ + (기본값) + + + updateDefaultFormula(e.target.value)} + placeholder="기본 계산식 (조건 미해당 시)" + className="h-7 text-xs flex-1" + /> +
+
+ )} + + {loadingOptions && ( +

옵션 로딩 중...

+ )} +
+ )} +
+ ); +}; + // 옵션 소스 설정 컴포넌트 (검색 가능한 Combobox) interface OptionSourceConfigProps { optionSource: { @@ -3034,46 +3459,15 @@ export function TableSectionSettingsModal({ {(tableConfig.calculations || []).map((calc, index) => ( -
- - = - updateCalculation(index, { formula: e.target.value })} - placeholder="수식 (예: quantity * unit_price)" - className="h-8 text-xs flex-1" - /> - -
+ updateCalculation(index, updates)} + onRemove={() => removeCalculation(index)} + /> ))} diff --git a/frontend/lib/registry/components/universal-form-modal/types.ts b/frontend/lib/registry/components/universal-form-modal/types.ts index a07feed6..3d8cac6c 100644 --- a/frontend/lib/registry/components/universal-form-modal/types.ts +++ b/frontend/lib/registry/components/universal-form-modal/types.ts @@ -604,6 +604,27 @@ export interface ColumnModeConfig { valueMapping: ValueMappingConfig; // 이 모드의 값 매핑 } +/** + * 조건부 계산 규칙 + * 특정 필드 값에 따라 다른 계산식 적용 + */ +export interface ConditionalCalculationRule { + conditionValue: string; // 조건 값 (예: "국내", "해외") + formula: string; // 해당 조건일 때 사용할 계산식 +} + +/** + * 조건부 계산 설정 + */ +export interface ConditionalCalculationConfig { + enabled: boolean; // 조건부 계산 활성화 여부 + conditionField: string; // 조건 기준 필드 (예: "sales_type") + conditionFieldType?: "static" | "code" | "table"; // 조건 필드의 옵션 타입 + conditionFieldCategoryKey?: string; // 카테고리 키 (예: "sales_order_mng.sales_type") + rules: ConditionalCalculationRule[]; // 조건별 계산 규칙 + defaultFormula?: string; // 조건에 해당하지 않을 때 기본 계산식 +} + /** * 테이블 계산 규칙 * 다른 컬럼 값을 기반으로 자동 계산 @@ -612,6 +633,9 @@ export interface TableCalculationRule { resultField: string; // 결과를 저장할 필드 formula: string; // 계산 공식 (예: "quantity * unit_price") dependencies: string[]; // 의존하는 필드들 + + // 조건부 계산 (선택사항) + conditionalCalculation?: ConditionalCalculationConfig; } // 다중 행 저장 설정 -- 2.43.0 From eb868965df4372656276edcf5dfdc7d18966b6b4 Mon Sep 17 00:00:00 2001 From: kjs Date: Wed, 31 Dec 2025 14:17:39 +0900 Subject: [PATCH 2/2] =?UTF-8?q?=EC=A1=B0=EA=B1=B4=EB=B6=80=20=EA=B3=84?= =?UTF-8?q?=EC=82=B0=EC=8B=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../modal-repeater-table/RepeaterTable.tsx | 41 ++++++++++--------- .../components/modal-repeater-table/types.ts | 1 + .../TableSectionRenderer.tsx | 1 + .../modals/TableSectionSettingsModal.tsx | 23 ++++------- .../components/universal-form-modal/types.ts | 1 + 5 files changed, 33 insertions(+), 34 deletions(-) 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({ /> 필수 +