"use client"; import React, { useState, useEffect } from "react"; import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; import { X, Save, Loader2 } from "lucide-react"; import { toast } from "sonner"; import { DynamicComponentRenderer } from "@/lib/registry/DynamicComponentRenderer"; import { InteractiveScreenViewer } from "./InteractiveScreenViewer"; import { screenApi } from "@/lib/api/screen"; import { dynamicFormApi, DynamicFormData } from "@/lib/api/dynamicForm"; import { ComponentData } from "@/lib/types/screen"; import { useAuth } from "@/hooks/useAuth"; interface SaveModalProps { isOpen: boolean; onClose: () => void; screenId?: number; modalSize?: "sm" | "md" | "lg" | "xl" | "full"; initialData?: any; // 수정 모드일 때 기존 데이터 onSaveSuccess?: () => void; // 저장 성공 시 콜백 (테이블 새로고침용) } /** * 저장 전용 모달 컴포넌트 * - 저장 성공 시: 메시지 표시 → 모달 닫기 → 테이블 새로고침 * - 저장 실패 시: 에러 메시지 표시, 모달 유지 */ export const SaveModal: React.FC = ({ isOpen, onClose, screenId, modalSize = "lg", initialData, onSaveSuccess, }) => { const { user, userName } = useAuth(); // 현재 사용자 정보 가져오기 const [formData, setFormData] = useState>(initialData || {}); const [originalData, setOriginalData] = useState>(initialData || {}); const [screenData, setScreenData] = useState(null); const [components, setComponents] = useState([]); const [loading, setLoading] = useState(true); const [isSaving, setIsSaving] = useState(false); // 모달 크기 설정 const modalSizeClasses = { sm: "max-w-md", md: "max-w-2xl", lg: "max-w-4xl", xl: "max-w-6xl", full: "max-w-[95vw]", }; // 화면 데이터 로드 useEffect(() => { const loadScreenData = async () => { if (!screenId || !isOpen) return; try { setLoading(true); // 화면 정보 로드 const screen = await screenApi.getScreen(screenId); setScreenData(screen); // 레이아웃 로드 const layout = await screenApi.getLayout(screenId); setComponents(layout.components || []); // initialData가 있으면 폼에 채우기 if (initialData) { setFormData(initialData); setOriginalData(initialData); } } catch (error) { console.error("화면 로드 실패:", error); toast.error("화면을 불러오는데 실패했습니다."); } finally { setLoading(false); } }; loadScreenData(); }, [screenId, isOpen, initialData]); // closeSaveModal 이벤트 리스너 useEffect(() => { const handleCloseSaveModal = () => { console.log("🚪 SaveModal 닫기 이벤트 수신"); onClose(); }; if (typeof window !== "undefined") { window.addEventListener("closeSaveModal", handleCloseSaveModal); } return () => { if (typeof window !== "undefined") { window.removeEventListener("closeSaveModal", handleCloseSaveModal); } }; }, [onClose]); // 저장 핸들러 const handleSave = async () => { if (!screenData || !screenId) return; try { setIsSaving(true); // 변경된 데이터만 추출 (수정 모드일 때) const changedData: Record = {}; if (initialData) { // 수정 모드: 변경된 필드만 전송 Object.keys(formData).forEach((key) => { if (formData[key] !== originalData[key]) { changedData[key] = formData[key]; } }); // 변경사항이 없으면 저장하지 않음 if (Object.keys(changedData).length === 0) { toast.info("변경된 내용이 없습니다."); setIsSaving(false); return; } } // 저장할 데이터 준비 const dataToSave = initialData ? changedData : formData; // 🆕 자동으로 작성자 정보 추가 const writerValue = user?.userId || userName || "unknown"; console.log("👤 현재 사용자 정보:", { userId: user?.userId, userName: userName, writerValue: writerValue, }); const dataWithUserInfo = { ...dataToSave, writer: writerValue, // 테이블 생성 시 자동 생성되는 컬럼 created_by: writerValue, updated_by: writerValue, }; // 테이블명 결정 const tableName = screenData.tableName || components.find((c) => c.columnName)?.tableName || "dynamic_form_data"; const saveData: DynamicFormData = { screenId: screenId, tableName: tableName, data: dataWithUserInfo, }; console.log("💾 저장 요청 데이터:", saveData); // API 호출 const result = await dynamicFormApi.saveFormData(saveData); if (result.success) { // ✅ 저장 성공 toast.success(initialData ? "수정되었습니다!" : "저장되었습니다!"); // 모달 닫기 onClose(); // 테이블 새로고침 콜백 호출 if (onSaveSuccess) { setTimeout(() => { onSaveSuccess(); }, 300); // 모달 닫힘 애니메이션 후 실행 } } else { throw new Error(result.message || "저장에 실패했습니다."); } } catch (error: any) { // ❌ 저장 실패 - 모달은 닫히지 않음 console.error("저장 실패:", error); toast.error(`저장 중 오류가 발생했습니다: ${error.message || "알 수 없는 오류"}`); } finally { setIsSaving(false); } }; // 동적 크기 계산 (컴포넌트들의 위치 기반) const calculateDynamicSize = () => { if (!components.length) return { width: 800, height: 600 }; const maxX = Math.max(...components.map((c) => (c.position?.x || 0) + (c.size?.width || 200))); const maxY = Math.max(...components.map((c) => (c.position?.y || 0) + (c.size?.height || 40))); const padding = 40; return { width: Math.max(maxX + padding, 400), height: Math.max(maxY + padding, 300), }; }; const dynamicSize = calculateDynamicSize(); return ( !isSaving && !open && onClose()}>
{initialData ? "데이터 수정" : "데이터 등록"}
{loading ? (
) : screenData && components.length > 0 ? (
{components.map((component, index) => (
{component.type === "widget" ? ( { setFormData((prev) => ({ ...prev, [fieldName]: value, })); }} hideLabel={false} /> ) : ( { setFormData((prev) => ({ ...prev, [fieldName]: value, })); }} mode={initialData ? "edit" : "create"} isInModal={true} isInteractive={true} /> )}
))}
) : (
화면에 컴포넌트가 없습니다.
)}
); };