수정 모달
This commit is contained in:
parent
556354219a
commit
4d9e783c57
|
|
@ -2,276 +2,293 @@
|
|||
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from "@/components/ui/dialog";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { X, Save, RotateCcw } from "lucide-react";
|
||||
import { toast } from "sonner";
|
||||
import { DynamicComponentRenderer } from "@/lib/registry/DynamicComponentRenderer";
|
||||
import { InteractiveScreenViewer } from "./InteractiveScreenViewer";
|
||||
import { InteractiveScreenViewerDynamic } from "@/components/screen/InteractiveScreenViewerDynamic";
|
||||
import { screenApi } from "@/lib/api/screen";
|
||||
import { ComponentData } from "@/lib/types/screen";
|
||||
import { ComponentData } from "@/types/screen";
|
||||
import { toast } from "sonner";
|
||||
import { dynamicFormApi } from "@/lib/api/dynamicForm";
|
||||
|
||||
interface EditModalProps {
|
||||
interface EditModalState {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
screenId?: number;
|
||||
modalSize?: "sm" | "md" | "lg" | "xl" | "full";
|
||||
editData?: any;
|
||||
screenId: number | null;
|
||||
title: string;
|
||||
description?: string;
|
||||
modalSize: "sm" | "md" | "lg" | "xl";
|
||||
editData: Record<string, any>;
|
||||
onSave?: () => void;
|
||||
onDataChange?: (formData: Record<string, any>) => void; // 폼 데이터 변경 콜백 추가
|
||||
modalTitle?: string; // 모달 제목
|
||||
modalDescription?: string; // 모달 설명
|
||||
}
|
||||
|
||||
/**
|
||||
* 편집 모달 컴포넌트
|
||||
* 선택된 데이터를 폼 화면에 로드하여 편집할 수 있게 해주는 모달
|
||||
*/
|
||||
export const EditModal: React.FC<EditModalProps> = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
screenId,
|
||||
modalSize = "lg",
|
||||
editData,
|
||||
onSave,
|
||||
onDataChange,
|
||||
modalTitle,
|
||||
modalDescription,
|
||||
}) => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [formData, setFormData] = useState<any>({});
|
||||
const [originalData, setOriginalData] = useState<any>({}); // 부분 업데이트용 원본 데이터
|
||||
const [screenData, setScreenData] = useState<any>(null);
|
||||
const [components, setComponents] = useState<ComponentData[]>([]);
|
||||
interface EditModalProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
// 컴포넌트 기반 동적 크기 계산
|
||||
const calculateModalSize = () => {
|
||||
export const EditModal: React.FC<EditModalProps> = ({ className }) => {
|
||||
const [modalState, setModalState] = useState<EditModalState>({
|
||||
isOpen: false,
|
||||
screenId: null,
|
||||
title: "",
|
||||
description: "",
|
||||
modalSize: "md",
|
||||
editData: {},
|
||||
onSave: undefined,
|
||||
});
|
||||
|
||||
const [screenData, setScreenData] = useState<{
|
||||
components: ComponentData[];
|
||||
screenInfo: any;
|
||||
} | null>(null);
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [screenDimensions, setScreenDimensions] = useState<{
|
||||
width: number;
|
||||
height: number;
|
||||
offsetX?: number;
|
||||
offsetY?: number;
|
||||
} | null>(null);
|
||||
|
||||
// 폼 데이터 상태 (편집 데이터로 초기화됨)
|
||||
const [formData, setFormData] = useState<Record<string, any>>({});
|
||||
const [originalData, setOriginalData] = useState<Record<string, any>>({});
|
||||
|
||||
// 화면의 실제 크기 계산 함수 (ScreenModal과 동일)
|
||||
const calculateScreenDimensions = (components: ComponentData[]) => {
|
||||
if (components.length === 0) {
|
||||
return { width: 600, height: 400 }; // 기본 크기
|
||||
return {
|
||||
width: 400,
|
||||
height: 300,
|
||||
offsetX: 0,
|
||||
offsetY: 0,
|
||||
};
|
||||
}
|
||||
|
||||
const maxWidth = Math.max(...components.map((c) => (c.position?.x || 0) + (c.size?.width || 200)), 500) + 100; // 더 넉넉한 여백
|
||||
// 모든 컴포넌트의 경계 찾기
|
||||
let minX = Infinity;
|
||||
let minY = Infinity;
|
||||
let maxX = -Infinity;
|
||||
let maxY = -Infinity;
|
||||
|
||||
const contentHeight = Math.max(...components.map((c) => (c.position?.y || 0) + (c.size?.height || 40)), 400) + 20; // 컨텐츠 높이
|
||||
components.forEach((component) => {
|
||||
const x = parseFloat(component.position?.x?.toString() || "0");
|
||||
const y = parseFloat(component.position?.y?.toString() || "0");
|
||||
const width = parseFloat(component.size?.width?.toString() || "100");
|
||||
const height = parseFloat(component.size?.height?.toString() || "40");
|
||||
|
||||
// 헤더 높이 추가 (ScreenModal과 동일)
|
||||
const headerHeight = 60; // DialogHeader 높이 (타이틀 + 패딩)
|
||||
const maxHeight = contentHeight + headerHeight;
|
||||
minX = Math.min(minX, x);
|
||||
minY = Math.min(minY, y);
|
||||
maxX = Math.max(maxX, x + width);
|
||||
maxY = Math.max(maxY, y + height);
|
||||
});
|
||||
|
||||
console.log(
|
||||
`🎯 계산된 모달 크기: ${maxWidth}px x ${maxHeight}px (컨텐츠: ${contentHeight}px + 헤더: ${headerHeight}px)`,
|
||||
);
|
||||
console.log(
|
||||
"📍 컴포넌트 위치들:",
|
||||
components.map((c) => ({ x: c.position?.x, y: c.position?.y, w: c.size?.width, h: c.size?.height })),
|
||||
);
|
||||
return { width: maxWidth, height: maxHeight };
|
||||
// 실제 컨텐츠 크기 계산
|
||||
const contentWidth = maxX - minX;
|
||||
const contentHeight = maxY - minY;
|
||||
|
||||
// 적절한 여백 추가
|
||||
const paddingX = 40;
|
||||
const paddingY = 40;
|
||||
|
||||
const finalWidth = Math.max(contentWidth + paddingX, 400);
|
||||
const finalHeight = Math.max(contentHeight + paddingY, 300);
|
||||
|
||||
return {
|
||||
width: Math.min(finalWidth, window.innerWidth * 0.95),
|
||||
height: Math.min(finalHeight, window.innerHeight * 0.9),
|
||||
offsetX: Math.max(0, minX - paddingX / 2),
|
||||
offsetY: Math.max(0, minY - paddingY / 2),
|
||||
};
|
||||
};
|
||||
|
||||
const dynamicSize = calculateModalSize();
|
||||
|
||||
// EditModal 전용 닫기 이벤트 리스너
|
||||
// 전역 모달 이벤트 리스너
|
||||
useEffect(() => {
|
||||
const handleCloseEditModal = () => {
|
||||
console.log("🚪 EditModal: closeEditModal 이벤트 수신");
|
||||
onClose();
|
||||
const handleOpenEditModal = (event: CustomEvent) => {
|
||||
const { screenId, title, description, modalSize, editData, onSave } = event.detail;
|
||||
console.log("🚀 EditModal 열기 이벤트 수신:", {
|
||||
screenId,
|
||||
title,
|
||||
description,
|
||||
modalSize,
|
||||
editData,
|
||||
});
|
||||
|
||||
setModalState({
|
||||
isOpen: true,
|
||||
screenId,
|
||||
title,
|
||||
description: description || "",
|
||||
modalSize: modalSize || "lg",
|
||||
editData: editData || {},
|
||||
onSave,
|
||||
});
|
||||
|
||||
// 편집 데이터로 폼 데이터 초기화
|
||||
setFormData(editData || {});
|
||||
setOriginalData(editData || {});
|
||||
};
|
||||
|
||||
const handleCloseEditModal = () => {
|
||||
console.log("🚪 EditModal 닫기 이벤트 수신");
|
||||
handleClose();
|
||||
};
|
||||
|
||||
window.addEventListener("openEditModal", handleOpenEditModal as EventListener);
|
||||
window.addEventListener("closeEditModal", handleCloseEditModal);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("openEditModal", handleOpenEditModal as EventListener);
|
||||
window.removeEventListener("closeEditModal", handleCloseEditModal);
|
||||
};
|
||||
}, [onClose]);
|
||||
}, []);
|
||||
|
||||
// DialogContent 크기 강제 적용
|
||||
// 화면 데이터 로딩
|
||||
useEffect(() => {
|
||||
if (isOpen && dynamicSize) {
|
||||
// 모달이 렌더링된 후 DOM 직접 조작으로 크기 강제 적용
|
||||
setTimeout(() => {
|
||||
const dialogContent = document.querySelector('[role="dialog"] > div');
|
||||
const modalContent = document.querySelector('[role="dialog"] [class*="overflow-auto"]');
|
||||
|
||||
if (dialogContent) {
|
||||
const targetWidth = dynamicSize.width;
|
||||
const targetHeight = dynamicSize.height;
|
||||
|
||||
console.log(`🔧 DialogContent 크기 강제 적용: ${targetWidth}px x ${targetHeight}px`);
|
||||
|
||||
dialogContent.style.width = `${targetWidth}px`;
|
||||
dialogContent.style.height = `${targetHeight}px`;
|
||||
dialogContent.style.minWidth = `${targetWidth}px`;
|
||||
dialogContent.style.minHeight = `${targetHeight}px`;
|
||||
dialogContent.style.maxWidth = "95vw";
|
||||
dialogContent.style.maxHeight = "95vh";
|
||||
dialogContent.style.padding = "0";
|
||||
}
|
||||
|
||||
// 스크롤 완전 제거
|
||||
if (modalContent) {
|
||||
modalContent.style.overflow = "hidden";
|
||||
console.log("🚫 스크롤 완전 비활성화");
|
||||
}
|
||||
}, 100); // 100ms 지연으로 렌더링 완료 후 실행
|
||||
if (modalState.isOpen && modalState.screenId) {
|
||||
loadScreenData(modalState.screenId);
|
||||
}
|
||||
}, [isOpen, dynamicSize]);
|
||||
}, [modalState.isOpen, modalState.screenId]);
|
||||
|
||||
// 편집 데이터가 변경되면 폼 데이터 및 원본 데이터 초기화
|
||||
useEffect(() => {
|
||||
if (editData) {
|
||||
console.log("📋 편집 데이터 로드:", editData);
|
||||
console.log("📋 편집 데이터 키들:", Object.keys(editData));
|
||||
|
||||
// 원본 데이터와 현재 폼 데이터 모두 설정
|
||||
const dataClone = { ...editData };
|
||||
setOriginalData(dataClone); // 원본 데이터 저장 (부분 업데이트용)
|
||||
setFormData(dataClone); // 편집용 폼 데이터 설정
|
||||
|
||||
console.log("📋 originalData 설정 완료:", dataClone);
|
||||
console.log("📋 formData 설정 완료:", dataClone);
|
||||
} else {
|
||||
setOriginalData({});
|
||||
setFormData({});
|
||||
}
|
||||
}, [editData]);
|
||||
|
||||
// formData 변경 시 로그
|
||||
useEffect(() => {}, [formData]);
|
||||
|
||||
// 화면 데이터 로드
|
||||
useEffect(() => {
|
||||
const fetchScreenData = async () => {
|
||||
if (!screenId || !isOpen) return;
|
||||
|
||||
try {
|
||||
setLoading(true);
|
||||
console.log("🔄 화면 데이터 로드 시작:", screenId);
|
||||
|
||||
// 화면 정보와 레이아웃 데이터를 동시에 로드
|
||||
const [screenInfo, layoutData] = await Promise.all([
|
||||
screenApi.getScreen(screenId),
|
||||
screenApi.getLayout(screenId),
|
||||
]);
|
||||
|
||||
console.log("📋 화면 정보:", screenInfo);
|
||||
console.log("🎨 레이아웃 데이터:", layoutData);
|
||||
|
||||
setScreenData(screenInfo);
|
||||
|
||||
if (layoutData && layoutData.components) {
|
||||
setComponents(layoutData.components);
|
||||
console.log("✅ 화면 컴포넌트 로드 완료:", layoutData.components);
|
||||
|
||||
// 컴포넌트와 formData 매칭 정보 출력
|
||||
console.log("🔍 컴포넌트-formData 매칭 분석:");
|
||||
layoutData.components.forEach((comp) => {
|
||||
if (comp.columnName) {
|
||||
const formValue = formData[comp.columnName];
|
||||
console.log(
|
||||
` - ${comp.columnName}: "${formValue}" (타입: ${comp.type}, 웹타입: ${(comp as any).widgetType})`,
|
||||
);
|
||||
|
||||
// 코드 타입인 경우 특별히 로깅
|
||||
if ((comp as any).widgetType === "code") {
|
||||
console.log(" 🔍 코드 타입 세부정보:", {
|
||||
columnName: comp.columnName,
|
||||
componentId: comp.id,
|
||||
formValue,
|
||||
webTypeConfig: (comp as any).webTypeConfig,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.log("⚠️ 레이아웃 데이터가 없습니다:", layoutData);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("❌ 화면 데이터 로드 실패:", error);
|
||||
toast.error("화면을 불러오는데 실패했습니다.");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchScreenData();
|
||||
}, [screenId, isOpen]);
|
||||
|
||||
// 저장 처리
|
||||
const handleSave = async () => {
|
||||
const loadScreenData = async (screenId: number) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
console.log("💾 편집 데이터 저장:", formData);
|
||||
|
||||
// TODO: 실제 저장 API 호출
|
||||
// const result = await DynamicFormApi.updateFormData({
|
||||
// screenId,
|
||||
// data: formData,
|
||||
// });
|
||||
console.log("화면 데이터 로딩 시작:", screenId);
|
||||
|
||||
// 임시: 저장 성공 시뮬레이션
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
// 화면 정보와 레이아웃 데이터 로딩
|
||||
const [screenInfo, layoutData] = await Promise.all([
|
||||
screenApi.getScreen(screenId),
|
||||
screenApi.getLayout(screenId),
|
||||
]);
|
||||
|
||||
toast.success("수정이 완료되었습니다.");
|
||||
onSave?.();
|
||||
onClose();
|
||||
console.log("API 응답:", { screenInfo, layoutData });
|
||||
|
||||
if (screenInfo && layoutData) {
|
||||
const components = layoutData.components || [];
|
||||
|
||||
// 화면의 실제 크기 계산
|
||||
const dimensions = calculateScreenDimensions(components);
|
||||
setScreenDimensions(dimensions);
|
||||
|
||||
setScreenData({
|
||||
components,
|
||||
screenInfo: screenInfo,
|
||||
});
|
||||
console.log("화면 데이터 설정 완료:", {
|
||||
componentsCount: components.length,
|
||||
dimensions,
|
||||
screenInfo,
|
||||
});
|
||||
} else {
|
||||
throw new Error("화면 데이터가 없습니다");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("❌ 저장 실패:", error);
|
||||
toast.error("저장 중 오류가 발생했습니다.");
|
||||
console.error("화면 데이터 로딩 오류:", error);
|
||||
toast.error("화면을 불러오는 중 오류가 발생했습니다.");
|
||||
handleClose();
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 초기화 처리
|
||||
const handleReset = () => {
|
||||
if (editData) {
|
||||
setFormData({ ...editData });
|
||||
toast.info("초기값으로 되돌렸습니다.");
|
||||
const handleClose = () => {
|
||||
setModalState({
|
||||
isOpen: false,
|
||||
screenId: null,
|
||||
title: "",
|
||||
description: "",
|
||||
modalSize: "md",
|
||||
editData: {},
|
||||
onSave: undefined,
|
||||
});
|
||||
setScreenData(null);
|
||||
setFormData({});
|
||||
setOriginalData({});
|
||||
};
|
||||
|
||||
// 저장 버튼 클릭 시 - UPDATE 액션 실행
|
||||
const handleSave = async () => {
|
||||
if (!screenData?.screenInfo?.tableName) {
|
||||
toast.error("테이블 정보가 없습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
console.log("💾 수정 저장 시작:", {
|
||||
tableName: screenData.screenInfo.tableName,
|
||||
formData,
|
||||
originalData,
|
||||
});
|
||||
|
||||
// 변경된 필드만 추출
|
||||
const changedData: Record<string, any> = {};
|
||||
Object.keys(formData).forEach((key) => {
|
||||
if (formData[key] !== originalData[key]) {
|
||||
changedData[key] = formData[key];
|
||||
}
|
||||
});
|
||||
|
||||
console.log("📝 변경된 필드:", changedData);
|
||||
|
||||
if (Object.keys(changedData).length === 0) {
|
||||
toast.info("변경된 내용이 없습니다.");
|
||||
handleClose();
|
||||
return;
|
||||
}
|
||||
|
||||
// UPDATE 액션 실행
|
||||
const response = await dynamicFormApi.updateData(screenData.screenInfo.tableName, {
|
||||
...originalData, // 원본 데이터 (WHERE 조건용)
|
||||
...changedData, // 변경된 데이터만
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
toast.success("데이터가 수정되었습니다.");
|
||||
|
||||
// 부모 컴포넌트의 onSave 콜백 실행
|
||||
if (modalState.onSave) {
|
||||
modalState.onSave();
|
||||
}
|
||||
|
||||
handleClose();
|
||||
} else {
|
||||
throw new Error(response.message || "수정에 실패했습니다.");
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error("❌ 수정 실패:", error);
|
||||
toast.error(error.message || "데이터 수정 중 오류가 발생했습니다.");
|
||||
}
|
||||
};
|
||||
|
||||
// 모달 크기 클래스 매핑
|
||||
const getModalSizeClass = () => {
|
||||
switch (modalSize) {
|
||||
case "sm":
|
||||
return "max-w-md";
|
||||
case "md":
|
||||
return "max-w-lg";
|
||||
case "lg":
|
||||
return "max-w-4xl";
|
||||
case "xl":
|
||||
return "max-w-6xl";
|
||||
case "full":
|
||||
return "max-w-[95vw] max-h-[95vh]";
|
||||
default:
|
||||
return "max-w-4xl";
|
||||
// 모달 크기 설정 - ScreenModal과 동일
|
||||
const getModalStyle = () => {
|
||||
if (!screenDimensions) {
|
||||
return {
|
||||
className: "w-fit min-w-[400px] max-w-4xl max-h-[90vh] overflow-hidden p-0",
|
||||
style: {},
|
||||
};
|
||||
}
|
||||
|
||||
const headerHeight = 60;
|
||||
const totalHeight = screenDimensions.height + headerHeight;
|
||||
|
||||
return {
|
||||
className: "overflow-hidden p-0",
|
||||
style: {
|
||||
width: `${Math.min(screenDimensions.width, window.innerWidth * 0.98)}px`,
|
||||
height: `${Math.min(totalHeight, window.innerHeight * 0.95)}px`,
|
||||
maxWidth: "98vw",
|
||||
maxHeight: "95vh",
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
if (!screenId) {
|
||||
return null;
|
||||
}
|
||||
const modalStyle = getModalStyle();
|
||||
|
||||
return (
|
||||
<Dialog open={isOpen} onOpenChange={onClose}>
|
||||
<DialogContent
|
||||
className="overflow-hidden p-0"
|
||||
style={{
|
||||
// 실제 컨텐츠 크기 그대로 적용 (패딩/여백 제거)
|
||||
width: dynamicSize.width,
|
||||
height: dynamicSize.height,
|
||||
minWidth: dynamicSize.width,
|
||||
minHeight: dynamicSize.height,
|
||||
maxWidth: "95vw",
|
||||
maxHeight: "95vh",
|
||||
zIndex: 9999, // 모든 컴포넌트보다 위에 표시
|
||||
}}
|
||||
data-radix-portal="true"
|
||||
>
|
||||
{/* 모달 헤더 - 항상 표시 (ScreenModal과 동일 구조) */}
|
||||
<Dialog open={modalState.isOpen} onOpenChange={handleClose}>
|
||||
<DialogContent className={`${modalStyle.className} ${className || ""}`} style={modalStyle.style}>
|
||||
<DialogHeader className="shrink-0 border-b px-4 py-3">
|
||||
<DialogTitle className="text-base">{modalTitle || "데이터 수정"}</DialogTitle>
|
||||
{modalDescription && !loading && (
|
||||
<DialogDescription className="text-muted-foreground text-xs">{modalDescription}</DialogDescription>
|
||||
<DialogTitle className="text-base">{modalState.title || "데이터 수정"}</DialogTitle>
|
||||
{modalState.description && !loading && (
|
||||
<DialogDescription className="text-muted-foreground text-xs">{modalState.description}</DialogDescription>
|
||||
)}
|
||||
{loading && (
|
||||
<DialogDescription className="text-xs">{loading ? "화면을 불러오는 중입니다..." : ""}</DialogDescription>
|
||||
|
|
@ -286,96 +303,61 @@ export const EditModal: React.FC<EditModalProps> = ({
|
|||
<p className="text-muted-foreground">화면을 불러오는 중...</p>
|
||||
</div>
|
||||
</div>
|
||||
) : screenData && components.length > 0 ? (
|
||||
// 원본 화면과 동일한 레이아웃으로 렌더링
|
||||
) : screenData ? (
|
||||
<div
|
||||
className="relative bg-white"
|
||||
style={{
|
||||
// 실제 컨텐츠 크기 그대로 적용 (여백 제거)
|
||||
width: dynamicSize.width,
|
||||
height: dynamicSize.height,
|
||||
overflow: "hidden",
|
||||
width: screenDimensions?.width || 800,
|
||||
height: screenDimensions?.height || 600,
|
||||
transformOrigin: "center center",
|
||||
maxWidth: "100%",
|
||||
maxHeight: "100%",
|
||||
}}
|
||||
>
|
||||
{/* 화면 컴포넌트들 원본 레이아웃 유지하여 렌더링 */}
|
||||
<div className="relative" style={{ minHeight: "300px" }}>
|
||||
{components.map((component, index) => (
|
||||
<div
|
||||
{screenData.components.map((component) => {
|
||||
// 컴포넌트 위치를 offset만큼 조정
|
||||
const offsetX = screenDimensions?.offsetX || 0;
|
||||
const offsetY = screenDimensions?.offsetY || 0;
|
||||
|
||||
const adjustedComponent = {
|
||||
...component,
|
||||
position: {
|
||||
...component.position,
|
||||
x: parseFloat(component.position?.x?.toString() || "0") - offsetX,
|
||||
y: parseFloat(component.position?.y?.toString() || "0") - offsetY,
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<InteractiveScreenViewerDynamic
|
||||
key={component.id}
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: component.position?.y || 0,
|
||||
left: component.position?.x || 0,
|
||||
width: component.size?.width || 200,
|
||||
height: component.size?.height || 40,
|
||||
zIndex: component.position?.z || 1000 + index, // 모달 내부에서 충분히 높은 z-index
|
||||
component={adjustedComponent}
|
||||
allComponents={screenData.components}
|
||||
formData={formData}
|
||||
onFormDataChange={(fieldName, value) => {
|
||||
console.log(`🎯 EditModal onFormDataChange 호출: ${fieldName} = "${value}"`);
|
||||
console.log("📋 현재 formData:", formData);
|
||||
setFormData((prev) => {
|
||||
const newFormData = {
|
||||
...prev,
|
||||
[fieldName]: value,
|
||||
};
|
||||
console.log("📝 EditModal 업데이트된 formData:", newFormData);
|
||||
return newFormData;
|
||||
});
|
||||
}}
|
||||
>
|
||||
{/* 위젯 컴포넌트는 InteractiveScreenViewer 사용 (라벨 표시) */}
|
||||
{component.type === "widget" ? (
|
||||
<InteractiveScreenViewer
|
||||
component={component}
|
||||
allComponents={components}
|
||||
hideLabel={false} // ✅ 라벨 표시
|
||||
formData={formData}
|
||||
onFormDataChange={(fieldName, value) => {
|
||||
console.log("📝 폼 데이터 변경:", fieldName, value);
|
||||
const newFormData = { ...formData, [fieldName]: value };
|
||||
setFormData(newFormData);
|
||||
|
||||
// 변경된 데이터를 즉시 부모로 전달
|
||||
if (onDataChange) {
|
||||
console.log("📤 EditModal -> 부모로 데이터 전달:", newFormData);
|
||||
onDataChange(newFormData);
|
||||
}
|
||||
}}
|
||||
screenInfo={{
|
||||
id: screenId || 0,
|
||||
tableName: screenData.tableName,
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<DynamicComponentRenderer
|
||||
component={{
|
||||
...component,
|
||||
style: {
|
||||
...component.style,
|
||||
labelDisplay: true, // ✅ 라벨 표시
|
||||
},
|
||||
}}
|
||||
screenId={screenId}
|
||||
tableName={screenData.tableName}
|
||||
formData={formData}
|
||||
originalData={originalData} // 부분 업데이트용 원본 데이터 전달
|
||||
onFormDataChange={(fieldName, value) => {
|
||||
console.log("📝 폼 데이터 변경:", fieldName, value);
|
||||
const newFormData = { ...formData, [fieldName]: value };
|
||||
setFormData(newFormData);
|
||||
|
||||
// 변경된 데이터를 즉시 부모로 전달
|
||||
if (onDataChange) {
|
||||
console.log("📤 EditModal -> 부모로 데이터 전달:", newFormData);
|
||||
onDataChange(newFormData);
|
||||
}
|
||||
}}
|
||||
// 편집 모드로 설정
|
||||
mode="edit"
|
||||
// 모달 내에서 렌더링되고 있음을 표시
|
||||
isInModal={true}
|
||||
// 인터랙티브 모드 활성화 (formData 사용을 위해 필수)
|
||||
isInteractive={true}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
screenInfo={{
|
||||
id: modalState.screenId!,
|
||||
tableName: screenData.screenInfo?.tableName,
|
||||
}}
|
||||
onSave={handleSave}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex h-full items-center justify-center">
|
||||
<div className="text-center">
|
||||
<p className="text-gray-500">화면을 불러올 수 없습니다.</p>
|
||||
<p className="mt-1 text-sm text-gray-400">화면 ID: {screenId}</p>
|
||||
</div>
|
||||
<p className="text-muted-foreground">화면 데이터가 없습니다.</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -383,3 +365,5 @@ export const EditModal: React.FC<EditModalProps> = ({
|
|||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default EditModal;
|
||||
|
|
|
|||
Loading…
Reference in New Issue