"use client"; import React, { useState, useEffect } from "react"; import { ResizableDialog, ResizableDialogContent, ResizableDialogHeader, ResizableDialogTitle, ResizableDialogDescription, ResizableDialogFooter, } from "@/components/ui/resizable-dialog"; import { InteractiveScreenViewerDynamic } from "@/components/screen/InteractiveScreenViewerDynamic"; import { screenApi } from "@/lib/api/screen"; import { ComponentData } from "@/types/screen"; import { toast } from "sonner"; import { dynamicFormApi } from "@/lib/api/dynamicForm"; import { useAuth } from "@/hooks/useAuth"; interface EditModalState { isOpen: boolean; screenId: number | null; title: string; description?: string; modalSize: "sm" | "md" | "lg" | "xl"; editData: Record; onSave?: () => void; } interface EditModalProps { className?: string; } export const EditModal: React.FC = ({ className }) => { const { user } = useAuth(); const [modalState, setModalState] = useState({ 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>({}); const [originalData, setOriginalData] = useState>({}); // 화면의 실제 크기 계산 함수 (ScreenModal과 동일) const calculateScreenDimensions = (components: ComponentData[]) => { if (components.length === 0) { return { width: 400, height: 300, offsetX: 0, offsetY: 0, }; } // 모든 컴포넌트의 경계 찾기 let minX = Infinity; let minY = Infinity; let maxX = -Infinity; let maxY = -Infinity; 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"); minX = Math.min(minX, x); minY = Math.min(minY, y); maxX = Math.max(maxX, x + width); maxY = Math.max(maxY, y + height); }); // 실제 컨텐츠 크기 계산 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), }; }; // 전역 모달 이벤트 리스너 useEffect(() => { const handleOpenEditModal = (event: CustomEvent) => { const { screenId, title, description, modalSize, editData, onSave } = event.detail; setModalState({ isOpen: true, screenId, title, description: description || "", modalSize: modalSize || "lg", editData: editData || {}, onSave, }); // 편집 데이터로 폼 데이터 초기화 setFormData(editData || {}); setOriginalData(editData || {}); }; const handleCloseEditModal = () => { // 부모 컴포넌트의 onSave 콜백 실행 (테이블 새로고침) if (modalState.onSave) { try { modalState.onSave(); } catch (callbackError) { console.error("⚠️ onSave 콜백 에러:", callbackError); } } // 모달 닫기 handleClose(); }; window.addEventListener("openEditModal", handleOpenEditModal as EventListener); window.addEventListener("closeEditModal", handleCloseEditModal); return () => { window.removeEventListener("openEditModal", handleOpenEditModal as EventListener); window.removeEventListener("closeEditModal", handleCloseEditModal); }; }, [modalState.onSave]); // modalState.onSave를 의존성에 추가하여 최신 콜백 참조 // 화면 데이터 로딩 useEffect(() => { if (modalState.isOpen && modalState.screenId) { loadScreenData(modalState.screenId); } }, [modalState.isOpen, modalState.screenId]); const loadScreenData = async (screenId: number) => { try { setLoading(true); console.log("화면 데이터 로딩 시작:", screenId); // 화면 정보와 레이아웃 데이터 로딩 const [screenInfo, layoutData] = await Promise.all([ screenApi.getScreen(screenId), screenApi.getLayout(screenId), ]); 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("화면을 불러오는 중 오류가 발생했습니다."); handleClose(); } finally { setLoading(false); } }; 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 { // 변경된 필드만 추출 const changedData: Record = {}; Object.keys(formData).forEach((key) => { if (formData[key] !== originalData[key]) { changedData[key] = formData[key]; } }); if (Object.keys(changedData).length === 0) { toast.info("변경된 내용이 없습니다."); handleClose(); return; } // 기본키 확인 (id 또는 첫 번째 키) const recordId = originalData.id || Object.values(originalData)[0]; // UPDATE 액션 실행 const response = await dynamicFormApi.updateFormDataPartial( recordId, originalData, changedData, screenData.screenInfo.tableName, ); if (response.success) { toast.success("데이터가 수정되었습니다."); // 부모 컴포넌트의 onSave 콜백 실행 (테이블 새로고침) if (modalState.onSave) { try { modalState.onSave(); } catch (callbackError) { console.error("⚠️ onSave 콜백 에러:", callbackError); } } handleClose(); } else { throw new Error(response.message || "수정에 실패했습니다."); } } catch (error: any) { console.error("❌ 수정 실패:", error); toast.error(error.message || "데이터 수정 중 오류가 발생했습니다."); } }; // 모달 크기 설정 - 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", }, }; }; const modalStyle = getModalStyle(); return (
{modalState.title || "데이터 수정"} {modalState.description && !loading && ( {modalState.description} )} {loading && ( {loading ? "화면을 불러오는 중입니다..." : ""} )}
{loading ? (

화면을 불러오는 중...

) : screenData ? (
{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 ( { setFormData((prev) => ({ ...prev, [fieldName]: value, })); }} screenInfo={{ id: modalState.screenId!, tableName: screenData.screenInfo?.tableName, }} onSave={handleSave} /> ); })}
) : (

화면 데이터가 없습니다.

)}
); }; export default EditModal;