fix: 수주등록 항목추가 시 정보출력 가능하게 수정
문제: - 조건부 컨테이너 내부의 modal-repeater-table 컴포넌트가 데이터 업데이트 불가 - ConditionalSectionViewer가 RealtimePreview에 formData/onFormDataChange 미전달 해결: - ConditionalSectionViewer.tsx: RealtimePreview에 formData, onFormDataChange props 추가 - DynamicComponentRenderer.tsx: 디버깅 로그 정리 - ScreenModal.tsx: 디버깅 로그 정리 영향: - 수주 등록 화면 품목 추가 기능 정상 작동 - 조건부 컨테이너 내부 모든 폼 컴포넌트 데이터 바인딩 정상화 Refs: #수주관리 #modal-repeater-table #ConditionalContainer
This commit is contained in:
@@ -42,7 +42,6 @@ export function ItemSelectionModal({
|
||||
// 모달 열릴 때 초기 검색
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
console.log("🚪 모달 열림 - uniqueField:", uniqueField, "multiSelect:", multiSelect);
|
||||
search("", 1); // 빈 검색어로 전체 목록 조회
|
||||
setSelectedItems([]);
|
||||
} else {
|
||||
@@ -59,14 +58,6 @@ export function ItemSelectionModal({
|
||||
const handleToggleItem = (item: any) => {
|
||||
const itemValue = uniqueField ? item[uniqueField] : undefined;
|
||||
|
||||
console.log("🖱️ 행 클릭:", {
|
||||
item,
|
||||
uniqueField,
|
||||
itemValue,
|
||||
currentSelected: selectedItems.length,
|
||||
selectedValues: uniqueField ? selectedItems.map(s => s[uniqueField]) : []
|
||||
});
|
||||
|
||||
if (!multiSelect) {
|
||||
setSelectedItems([item]);
|
||||
return;
|
||||
@@ -77,14 +68,10 @@ export function ItemSelectionModal({
|
||||
console.warn(`⚠️ uniqueField "${uniqueField}"의 값이 undefined입니다. 객체 참조로 비교합니다.`);
|
||||
const itemIsSelected = selectedItems.includes(item);
|
||||
|
||||
console.log("📊 선택 상태 (객체 참조):", itemIsSelected);
|
||||
|
||||
if (itemIsSelected) {
|
||||
const newSelected = selectedItems.filter((selected) => selected !== item);
|
||||
console.log("➖ 제거 후:", newSelected.length);
|
||||
setSelectedItems(newSelected);
|
||||
} else {
|
||||
console.log("➕ 추가");
|
||||
setSelectedItems([...selectedItems, item]);
|
||||
}
|
||||
return;
|
||||
@@ -101,8 +88,6 @@ export function ItemSelectionModal({
|
||||
return selectedValue === itemValue;
|
||||
});
|
||||
|
||||
console.log("📊 선택 상태:", itemIsSelected);
|
||||
|
||||
if (itemIsSelected) {
|
||||
const newSelected = selectedItems.filter((selected) => {
|
||||
if (!uniqueField) {
|
||||
@@ -114,15 +99,15 @@ export function ItemSelectionModal({
|
||||
}
|
||||
return selectedValue !== itemValue;
|
||||
});
|
||||
console.log("➖ 제거 후:", newSelected.length);
|
||||
setSelectedItems(newSelected);
|
||||
} else {
|
||||
console.log("➕ 추가");
|
||||
setSelectedItems([...selectedItems, item]);
|
||||
}
|
||||
};
|
||||
|
||||
const handleConfirm = () => {
|
||||
console.log("✅ ItemSelectionModal 추가:", selectedItems.length, "개 항목");
|
||||
|
||||
onSelect(selectedItems);
|
||||
onOpenChange(false);
|
||||
};
|
||||
@@ -307,7 +292,7 @@ export function ItemSelectionModal({
|
||||
}}
|
||||
>
|
||||
<div className="pointer-events-none">
|
||||
<Checkbox checked={selected} readOnly />
|
||||
<Checkbox checked={selected} />
|
||||
</div>
|
||||
</td>
|
||||
)}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Button } from "@/components/ui/button";
|
||||
import { Plus } from "lucide-react";
|
||||
import { ItemSelectionModal } from "./ItemSelectionModal";
|
||||
import { RepeaterTable } from "./RepeaterTable";
|
||||
import { ModalRepeaterTableProps } from "./types";
|
||||
import { ModalRepeaterTableProps, RepeaterColumnConfig } from "./types";
|
||||
import { useCalculation } from "./useCalculation";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
@@ -32,19 +32,87 @@ export function ModalRepeaterTableComponent({
|
||||
}: ModalRepeaterTableComponentProps) {
|
||||
// config prop 우선, 없으면 개별 prop 사용
|
||||
const sourceTable = config?.sourceTable || propSourceTable || "";
|
||||
const sourceColumns = config?.sourceColumns || propSourceColumns || [];
|
||||
|
||||
// sourceColumns에서 빈 문자열 필터링
|
||||
const rawSourceColumns = config?.sourceColumns || propSourceColumns || [];
|
||||
const sourceColumns = rawSourceColumns.filter((col) => col && col.trim() !== "");
|
||||
|
||||
const sourceSearchFields = config?.sourceSearchFields || propSourceSearchFields || [];
|
||||
const modalTitle = config?.modalTitle || propModalTitle || "항목 검색";
|
||||
const modalButtonText = config?.modalButtonText || propModalButtonText || "품목 검색";
|
||||
const multiSelect = config?.multiSelect ?? propMultiSelect ?? true;
|
||||
const columns = config?.columns || propColumns || [];
|
||||
const calculationRules = config?.calculationRules || propCalculationRules || [];
|
||||
const value = config?.value || propValue || [];
|
||||
const onChange = config?.onChange || propOnChange || (() => {});
|
||||
const uniqueField = config?.uniqueField || propUniqueField;
|
||||
|
||||
// uniqueField 자동 보정: order_no는 item_info 테이블에 없으므로 item_number로 변경
|
||||
const rawUniqueField = config?.uniqueField || propUniqueField;
|
||||
const uniqueField = rawUniqueField === "order_no" && sourceTable === "item_info"
|
||||
? "item_number"
|
||||
: rawUniqueField;
|
||||
|
||||
const filterCondition = config?.filterCondition || propFilterCondition || {};
|
||||
const companyCode = config?.companyCode || propCompanyCode;
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
|
||||
// columns가 비어있으면 sourceColumns로부터 자동 생성
|
||||
const columns = React.useMemo((): RepeaterColumnConfig[] => {
|
||||
const configuredColumns = config?.columns || propColumns || [];
|
||||
|
||||
if (configuredColumns.length > 0) {
|
||||
console.log("✅ 설정된 columns 사용:", configuredColumns);
|
||||
return configuredColumns;
|
||||
}
|
||||
|
||||
// columns가 비어있으면 sourceColumns로부터 자동 생성
|
||||
if (sourceColumns.length > 0) {
|
||||
console.log("🔄 sourceColumns로부터 자동 생성:", sourceColumns);
|
||||
const autoColumns: RepeaterColumnConfig[] = sourceColumns.map((field) => ({
|
||||
field: field,
|
||||
label: field, // 필드명을 라벨로 사용 (나중에 설정에서 변경 가능)
|
||||
editable: false, // 기본적으로 읽기 전용
|
||||
type: "text" as const,
|
||||
width: "150px",
|
||||
}));
|
||||
console.log("📋 자동 생성된 columns:", autoColumns);
|
||||
return autoColumns;
|
||||
}
|
||||
|
||||
console.warn("⚠️ columns와 sourceColumns 모두 비어있음!");
|
||||
return [];
|
||||
}, [config?.columns, propColumns, sourceColumns]);
|
||||
|
||||
// 초기 props 로깅
|
||||
useEffect(() => {
|
||||
if (rawSourceColumns.length !== sourceColumns.length) {
|
||||
console.warn(`⚠️ sourceColumns 필터링: ${rawSourceColumns.length}개 → ${sourceColumns.length}개 (빈 문자열 제거)`);
|
||||
}
|
||||
|
||||
if (rawUniqueField !== uniqueField) {
|
||||
console.warn(`⚠️ uniqueField 자동 보정: "${rawUniqueField}" → "${uniqueField}"`);
|
||||
}
|
||||
|
||||
console.log("🎬 ModalRepeaterTableComponent 마운트:", {
|
||||
columnsLength: columns.length,
|
||||
sourceTable,
|
||||
sourceColumns,
|
||||
uniqueField,
|
||||
});
|
||||
|
||||
if (columns.length === 0) {
|
||||
console.error("❌ columns가 비어있습니다! sourceColumns:", sourceColumns);
|
||||
} else {
|
||||
console.log("✅ columns 설정 완료:", columns.map(c => c.label || c.field).join(", "));
|
||||
}
|
||||
}, []);
|
||||
|
||||
// value 변경 감지
|
||||
useEffect(() => {
|
||||
console.log("📦 ModalRepeaterTableComponent value 변경:", {
|
||||
valueLength: value.length,
|
||||
});
|
||||
}, [value]);
|
||||
|
||||
const { calculateRow, calculateAll } = useCalculation(calculationRules);
|
||||
|
||||
// 초기 데이터에 계산 필드 적용
|
||||
@@ -59,6 +127,8 @@ export function ModalRepeaterTableComponent({
|
||||
}, []);
|
||||
|
||||
const handleAddItems = (items: any[]) => {
|
||||
console.log("➕ handleAddItems 호출:", items.length, "개 항목");
|
||||
|
||||
// 기본값 적용
|
||||
const itemsWithDefaults = items.map((item) => {
|
||||
const newItem = { ...item };
|
||||
@@ -74,7 +144,10 @@ export function ModalRepeaterTableComponent({
|
||||
const calculatedItems = calculateAll(itemsWithDefaults);
|
||||
|
||||
// 기존 데이터에 추가
|
||||
onChange([...value, ...calculatedItems]);
|
||||
const newData = [...value, ...calculatedItems];
|
||||
console.log("✅ 최종 데이터:", newData.length, "개 항목");
|
||||
|
||||
onChange(newData);
|
||||
};
|
||||
|
||||
const handleRowChange = (index: number, newRow: any) => {
|
||||
|
||||
@@ -13,12 +13,31 @@ export class ModalRepeaterTableRenderer extends AutoRegisteringComponentRenderer
|
||||
static componentDefinition = ModalRepeaterTableDefinition;
|
||||
|
||||
render(): React.ReactElement {
|
||||
return <ModalRepeaterTableComponent {...this.props} renderer={this} />;
|
||||
// onChange 콜백을 명시적으로 전달
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const handleChange = (newValue: any[]) => {
|
||||
console.log("🔄 ModalRepeaterTableRenderer onChange:", newValue.length, "개 항목");
|
||||
|
||||
// 컴포넌트 업데이트
|
||||
this.updateComponent({ value: newValue });
|
||||
|
||||
// 원본 onChange 콜백도 호출 (있다면)
|
||||
if (this.props.onChange) {
|
||||
this.props.onChange(newValue);
|
||||
}
|
||||
};
|
||||
|
||||
// renderer prop 제거 (불필요)
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { onChange, ...restProps } = this.props;
|
||||
|
||||
return <ModalRepeaterTableComponent {...restProps} onChange={handleChange} />;
|
||||
}
|
||||
|
||||
/**
|
||||
* 값 변경 처리
|
||||
* 값 변경 처리 (레거시 메서드 - 호환성 유지)
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
protected handleValueChange = (value: any) => {
|
||||
this.updateComponent({ value });
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import React, { useState } from "react";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { Button } from "@/components/ui/button";
|
||||
@@ -28,6 +28,11 @@ export function RepeaterTable({
|
||||
field: string;
|
||||
} | null>(null);
|
||||
|
||||
// 데이터 변경 감지 (필요시 활성화)
|
||||
// useEffect(() => {
|
||||
// console.log("📊 RepeaterTable 데이터 업데이트:", data.length, "개 행");
|
||||
// }, [data]);
|
||||
|
||||
const handleCellEdit = (rowIndex: number, field: string, value: any) => {
|
||||
const newRow = { ...data[rowIndex], [field]: value };
|
||||
onRowChange(rowIndex, newRow);
|
||||
|
||||
Reference in New Issue
Block a user