모달에서 저장 안되는 문제 수정
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import React, { useState } from "react";
|
||||
import React, { useState, useCallback } from "react";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { Button } from "@/components/ui/button";
|
||||
@@ -12,6 +12,7 @@ import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/u
|
||||
import { CalendarIcon } from "lucide-react";
|
||||
import { format } from "date-fns";
|
||||
import { ko } from "date-fns/locale";
|
||||
import { useAuth } from "@/hooks/useAuth";
|
||||
import {
|
||||
ComponentData,
|
||||
WidgetComponent,
|
||||
@@ -53,6 +54,7 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
|
||||
hideLabel = false,
|
||||
screenInfo,
|
||||
}) => {
|
||||
const { userName } = useAuth(); // 현재 로그인한 사용자명 가져오기
|
||||
const [localFormData, setLocalFormData] = useState<Record<string, any>>({});
|
||||
const [dateValues, setDateValues] = useState<Record<string, Date | undefined>>({});
|
||||
|
||||
@@ -67,6 +69,32 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
|
||||
const [popupLayout, setPopupLayout] = useState<ComponentData[]>([]);
|
||||
const [popupLoading, setPopupLoading] = useState(false);
|
||||
const [popupScreenResolution, setPopupScreenResolution] = useState<{ width: number; height: number } | null>(null);
|
||||
const [popupScreenInfo, setPopupScreenInfo] = useState<{ id: number; tableName?: string } | null>(null);
|
||||
|
||||
// 팝업 전용 formData 상태
|
||||
const [popupFormData, setPopupFormData] = useState<Record<string, any>>({});
|
||||
|
||||
// 자동값 생성 함수
|
||||
const generateAutoValue = useCallback((autoValueType: string): string => {
|
||||
const now = new Date();
|
||||
switch (autoValueType) {
|
||||
case "current_datetime":
|
||||
return now.toISOString().slice(0, 19).replace("T", " "); // YYYY-MM-DD HH:mm:ss
|
||||
case "current_date":
|
||||
return now.toISOString().slice(0, 10); // YYYY-MM-DD
|
||||
case "current_time":
|
||||
return now.toTimeString().slice(0, 8); // HH:mm:ss
|
||||
case "current_user":
|
||||
// 실제 접속중인 사용자명 사용
|
||||
return userName || "사용자"; // 사용자명이 없으면 기본값
|
||||
case "uuid":
|
||||
return crypto.randomUUID();
|
||||
case "sequence":
|
||||
return `SEQ_${Date.now()}`;
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}, [userName]); // userName 의존성 추가
|
||||
|
||||
// 팝업 화면 레이아웃 로드
|
||||
React.useEffect(() => {
|
||||
@@ -74,29 +102,36 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
|
||||
const loadPopupLayout = async () => {
|
||||
try {
|
||||
setPopupLoading(true);
|
||||
console.log("🔍 팝업 화면 로드 시작:", {
|
||||
screenId: popupScreen.screenId,
|
||||
title: popupScreen.title,
|
||||
size: popupScreen.size
|
||||
});
|
||||
console.log("🔍 팝업 화면 로드 시작:", popupScreen);
|
||||
|
||||
const layout = await screenApi.getLayout(popupScreen.screenId);
|
||||
console.log("📊 팝업 화면 레이아웃 로드 완료:", {
|
||||
// 화면 레이아웃과 화면 정보를 병렬로 가져오기
|
||||
const [layout, screen] = await Promise.all([
|
||||
screenApi.getLayout(popupScreen.screenId),
|
||||
screenApi.getScreen(popupScreen.screenId)
|
||||
]);
|
||||
|
||||
console.log("📊 팝업 화면 로드 완료:", {
|
||||
componentsCount: layout.components?.length || 0,
|
||||
gridSettings: layout.gridSettings,
|
||||
screenResolution: layout.screenResolution,
|
||||
components: layout.components?.map(c => ({
|
||||
id: c.id,
|
||||
type: c.type,
|
||||
title: (c as any).title
|
||||
}))
|
||||
screenInfo: {
|
||||
screenId: screen.screenId,
|
||||
tableName: screen.tableName
|
||||
},
|
||||
popupFormData: {}
|
||||
});
|
||||
|
||||
setPopupLayout(layout.components || []);
|
||||
setPopupScreenResolution(layout.screenResolution || null);
|
||||
setPopupScreenInfo({
|
||||
id: popupScreen.screenId,
|
||||
tableName: screen.tableName
|
||||
});
|
||||
|
||||
// 팝업 formData 초기화
|
||||
setPopupFormData({});
|
||||
} catch (error) {
|
||||
console.error("❌ 팝업 화면 레이아웃 로드 실패:", error);
|
||||
console.error("❌ 팝업 화면 로드 실패:", error);
|
||||
setPopupLayout([]);
|
||||
setPopupScreenInfo(null);
|
||||
} finally {
|
||||
setPopupLoading(false);
|
||||
}
|
||||
@@ -106,23 +141,86 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
|
||||
}
|
||||
}, [popupScreen]);
|
||||
|
||||
// 실제 사용할 폼 데이터 (외부에서 제공된 경우 우선 사용)
|
||||
const formData = externalFormData || localFormData;
|
||||
// 실제 사용할 폼 데이터 (외부와 로컬 데이터 병합)
|
||||
const formData = { ...localFormData, ...externalFormData };
|
||||
console.log("🔄 formData 구성:", {
|
||||
external: externalFormData,
|
||||
local: localFormData,
|
||||
merged: formData,
|
||||
hasExternalCallback: !!onFormDataChange
|
||||
});
|
||||
|
||||
// 폼 데이터 업데이트
|
||||
const updateFormData = (fieldName: string, value: any) => {
|
||||
console.log(`🔄 updateFormData: ${fieldName} = "${value}" (외부콜백: ${!!onFormDataChange})`);
|
||||
|
||||
// 항상 로컬 상태도 업데이트
|
||||
setLocalFormData((prev) => ({
|
||||
...prev,
|
||||
[fieldName]: value,
|
||||
}));
|
||||
console.log(`💾 로컬 상태 업데이트: ${fieldName} = "${value}"`);
|
||||
|
||||
// 외부 콜백이 있는 경우에도 전달
|
||||
if (onFormDataChange) {
|
||||
// 외부 콜백이 있는 경우 사용
|
||||
onFormDataChange(fieldName, value);
|
||||
} else {
|
||||
// 로컬 상태 업데이트
|
||||
setLocalFormData((prev) => ({
|
||||
...prev,
|
||||
[fieldName]: value,
|
||||
}));
|
||||
// 개별 필드를 객체로 변환해서 전달
|
||||
const dataToSend = { [fieldName]: value };
|
||||
onFormDataChange(dataToSend);
|
||||
console.log(`📤 외부 콜백으로 전달: ${fieldName} = "${value}" (객체: ${JSON.stringify(dataToSend)})`);
|
||||
}
|
||||
};
|
||||
|
||||
// 자동입력 필드들의 값을 formData에 초기 설정
|
||||
React.useEffect(() => {
|
||||
console.log("🚀 자동입력 초기화 useEffect 실행 - allComponents 개수:", allComponents.length);
|
||||
const initAutoInputFields = () => {
|
||||
console.log("🔧 initAutoInputFields 실행 시작");
|
||||
allComponents.forEach(comp => {
|
||||
if (comp.type === 'widget') {
|
||||
const widget = comp as WidgetComponent;
|
||||
const fieldName = widget.columnName || widget.id;
|
||||
|
||||
// 텍스트 타입 위젯의 자동입력 처리
|
||||
if ((widget.widgetType === 'text' || widget.widgetType === 'email' || widget.widgetType === 'tel') &&
|
||||
widget.webTypeConfig) {
|
||||
const config = widget.webTypeConfig as TextTypeConfig;
|
||||
const isAutoInput = config?.autoInput || false;
|
||||
|
||||
if (isAutoInput && config?.autoValueType) {
|
||||
// 이미 값이 있으면 덮어쓰지 않음
|
||||
const currentValue = formData[fieldName];
|
||||
console.log(`🔍 자동입력 필드 체크: ${fieldName}`, {
|
||||
currentValue,
|
||||
isEmpty: currentValue === undefined || currentValue === '',
|
||||
isAutoInput,
|
||||
autoValueType: config.autoValueType
|
||||
});
|
||||
|
||||
if (currentValue === undefined || currentValue === '') {
|
||||
const autoValue = config.autoValueType === "custom"
|
||||
? config.customValue || ""
|
||||
: generateAutoValue(config.autoValueType);
|
||||
|
||||
console.log("🔄 자동입력 필드 초기화:", {
|
||||
fieldName,
|
||||
autoValueType: config.autoValueType,
|
||||
autoValue
|
||||
});
|
||||
|
||||
updateFormData(fieldName, autoValue);
|
||||
} else {
|
||||
console.log(`⏭️ 자동입력 건너뜀 (값 있음): ${fieldName} = "${currentValue}"`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 초기 로드 시 자동입력 필드들 설정
|
||||
initAutoInputFields();
|
||||
}, [allComponents, generateAutoValue]); // formData는 의존성에서 제외 (무한 루프 방지)
|
||||
|
||||
// 날짜 값 업데이트
|
||||
const updateDateValue = (fieldName: string, date: Date | undefined) => {
|
||||
setDateValues((prev) => ({
|
||||
@@ -177,6 +275,17 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
|
||||
const widget = comp as WidgetComponent;
|
||||
const config = widget.webTypeConfig as TextTypeConfig | undefined;
|
||||
|
||||
// 자동입력 관련 처리
|
||||
const isAutoInput = config?.autoInput || false;
|
||||
const autoValue = isAutoInput && config?.autoValueType
|
||||
? config.autoValueType === "custom"
|
||||
? config.customValue || ""
|
||||
: generateAutoValue(config.autoValueType)
|
||||
: "";
|
||||
|
||||
// 기본값 또는 자동값 설정
|
||||
const displayValue = isAutoInput ? autoValue : currentValue || config?.defaultValue || "";
|
||||
|
||||
console.log("📝 InteractiveScreenViewer - Text 위젯:", {
|
||||
componentId: widget.id,
|
||||
widgetType: widget.widgetType,
|
||||
@@ -187,6 +296,11 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
|
||||
maxLength: config?.maxLength,
|
||||
pattern: config?.pattern,
|
||||
placeholder: config?.placeholder,
|
||||
defaultValue: config?.defaultValue,
|
||||
autoInput: isAutoInput,
|
||||
autoValueType: config?.autoValueType,
|
||||
autoValue,
|
||||
displayValue,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -215,6 +329,7 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
|
||||
// 입력 검증 함수
|
||||
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const value = e.target.value;
|
||||
console.log(`📝 입력 변경: ${fieldName} = "${value}"`);
|
||||
|
||||
// 형식별 실시간 검증
|
||||
if (config?.format && config.format !== "none") {
|
||||
@@ -222,6 +337,7 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
|
||||
if (pattern) {
|
||||
const regex = new RegExp(`^${pattern}$`);
|
||||
if (value && !regex.test(value)) {
|
||||
console.log(`❌ 형식 검증 실패: ${fieldName} = "${value}"`);
|
||||
return; // 유효하지 않은 입력 차단
|
||||
}
|
||||
}
|
||||
@@ -229,9 +345,11 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
|
||||
|
||||
// 길이 제한 검증
|
||||
if (config?.maxLength && value.length > config.maxLength) {
|
||||
console.log(`❌ 길이 제한 초과: ${fieldName} = "${value}" (최대: ${config.maxLength})`);
|
||||
return; // 최대 길이 초과 차단
|
||||
}
|
||||
|
||||
console.log(`✅ updateFormData 호출: ${fieldName} = "${value}"`);
|
||||
updateFormData(fieldName, value);
|
||||
};
|
||||
|
||||
@@ -241,15 +359,16 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
|
||||
return applyStyles(
|
||||
<Input
|
||||
type={inputType}
|
||||
placeholder={finalPlaceholder}
|
||||
value={currentValue}
|
||||
onChange={handleInputChange}
|
||||
disabled={readonly}
|
||||
placeholder={isAutoInput ? `자동입력: ${config?.autoValueType}` : finalPlaceholder}
|
||||
value={displayValue}
|
||||
onChange={isAutoInput ? undefined : handleInputChange}
|
||||
disabled={readonly || isAutoInput}
|
||||
readOnly={isAutoInput}
|
||||
required={required}
|
||||
minLength={config?.minLength}
|
||||
maxLength={config?.maxLength}
|
||||
pattern={getPatternByFormat(config?.format || "none")}
|
||||
className="w-full"
|
||||
className={`w-full ${isAutoInput ? "bg-gray-50 text-gray-700" : ""}`}
|
||||
style={{
|
||||
height: "100%",
|
||||
minHeight: "100%",
|
||||
@@ -750,9 +869,6 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
|
||||
case "save":
|
||||
await handleSaveAction();
|
||||
break;
|
||||
case "cancel":
|
||||
handleCancelAction();
|
||||
break;
|
||||
case "delete":
|
||||
await handleDeleteAction();
|
||||
break;
|
||||
@@ -794,8 +910,23 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
|
||||
|
||||
// 저장 액션
|
||||
const handleSaveAction = async () => {
|
||||
if (!formData || Object.keys(formData).length === 0) {
|
||||
alert("저장할 데이터가 없습니다.");
|
||||
// 저장 시점에서 최신 formData 구성
|
||||
const currentFormData = { ...localFormData, ...externalFormData };
|
||||
console.log("💾 저장 시작 - currentFormData:", currentFormData);
|
||||
console.log("💾 저장 시점 formData 상세:", {
|
||||
local: localFormData,
|
||||
external: externalFormData,
|
||||
merged: currentFormData
|
||||
});
|
||||
console.log("💾 currentFormData 키-값 상세:");
|
||||
Object.entries(currentFormData).forEach(([key, value]) => {
|
||||
console.log(` ${key}: "${value}" (타입: ${typeof value})`);
|
||||
});
|
||||
|
||||
// formData 유효성 체크를 완화 (빈 객체라도 위젯이 있으면 저장 진행)
|
||||
const hasWidgets = allComponents.some(comp => comp.type === 'widget');
|
||||
if (!hasWidgets) {
|
||||
alert("저장할 입력 컴포넌트가 없습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -803,7 +934,7 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
|
||||
const requiredFields = allComponents.filter(c => c.required && (c.columnName || c.id));
|
||||
const missingFields = requiredFields.filter(field => {
|
||||
const fieldName = field.columnName || field.id;
|
||||
const value = formData[fieldName];
|
||||
const value = currentFormData[fieldName];
|
||||
return !value || value.toString().trim() === "";
|
||||
});
|
||||
|
||||
@@ -822,27 +953,93 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
|
||||
// 컬럼명 기반으로 데이터 매핑
|
||||
const mappedData: Record<string, any> = {};
|
||||
|
||||
// 컴포넌트에서 컬럼명이 있는 것들만 매핑
|
||||
// 입력 가능한 컴포넌트에서 데이터 수집
|
||||
allComponents.forEach(comp => {
|
||||
if (comp.columnName) {
|
||||
const fieldName = comp.columnName;
|
||||
const componentId = comp.id;
|
||||
// 위젯 컴포넌트이고 입력 가능한 타입인 경우
|
||||
if (comp.type === 'widget') {
|
||||
const widget = comp as WidgetComponent;
|
||||
const fieldName = widget.columnName || widget.id;
|
||||
let value = currentFormData[fieldName];
|
||||
|
||||
// formData에서 해당 값 찾기 (컬럼명 우선, 없으면 컴포넌트 ID)
|
||||
const value = formData[fieldName] || formData[componentId];
|
||||
console.log(`🔍 컴포넌트 처리: ${fieldName}`, {
|
||||
widgetType: widget.widgetType,
|
||||
formDataValue: value,
|
||||
hasWebTypeConfig: !!widget.webTypeConfig,
|
||||
config: widget.webTypeConfig
|
||||
});
|
||||
|
||||
if (value !== undefined && value !== "") {
|
||||
mappedData[fieldName] = value;
|
||||
// 자동입력 필드인 경우에만 값이 없을 때 생성
|
||||
if ((widget.widgetType === 'text' || widget.widgetType === 'email' || widget.widgetType === 'tel') &&
|
||||
widget.webTypeConfig) {
|
||||
const config = widget.webTypeConfig as TextTypeConfig;
|
||||
const isAutoInput = config?.autoInput || false;
|
||||
|
||||
console.log(`📋 ${fieldName} 자동입력 체크:`, {
|
||||
isAutoInput,
|
||||
autoValueType: config?.autoValueType,
|
||||
hasValue: !!value,
|
||||
value
|
||||
});
|
||||
|
||||
if (isAutoInput && config?.autoValueType && (!value || value === '')) {
|
||||
// 자동입력이고 값이 없을 때만 생성
|
||||
value = config.autoValueType === "custom"
|
||||
? config.customValue || ""
|
||||
: generateAutoValue(config.autoValueType);
|
||||
|
||||
console.log("💾 자동입력 값 저장 (값이 없어서 생성):", {
|
||||
fieldName,
|
||||
autoValueType: config.autoValueType,
|
||||
generatedValue: value
|
||||
});
|
||||
} else if (isAutoInput && value) {
|
||||
console.log("💾 자동입력 필드지만 기존 값 유지:", {
|
||||
fieldName,
|
||||
existingValue: value
|
||||
});
|
||||
} else if (!isAutoInput) {
|
||||
console.log(`📝 일반 입력 필드: ${fieldName} = "${value}"`);
|
||||
}
|
||||
}
|
||||
|
||||
// 값이 있는 경우만 매핑 (빈 문자열도 포함하되, undefined는 제외)
|
||||
if (value !== undefined && value !== null && value !== "undefined") {
|
||||
// columnName이 있으면 columnName을 키로, 없으면 컴포넌트 ID를 키로 사용
|
||||
const saveKey = widget.columnName || `comp_${widget.id}`;
|
||||
mappedData[saveKey] = value;
|
||||
} else if (widget.columnName) {
|
||||
// 값이 없지만 columnName이 있는 경우, 빈 문자열로 저장
|
||||
console.log(`⚠️ ${widget.columnName} 필드에 값이 없어 빈 문자열로 저장`);
|
||||
mappedData[widget.columnName] = "";
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
console.log("💾 저장할 데이터 매핑:", {
|
||||
원본데이터: formData,
|
||||
원본데이터: currentFormData,
|
||||
매핑된데이터: mappedData,
|
||||
화면정보: screenInfo,
|
||||
전체컴포넌트수: allComponents.length,
|
||||
위젯컴포넌트수: allComponents.filter(c => c.type === 'widget').length,
|
||||
});
|
||||
|
||||
// 각 컴포넌트의 상세 정보 로그
|
||||
console.log("🔍 컴포넌트별 데이터 수집 상세:");
|
||||
allComponents.forEach(comp => {
|
||||
if (comp.type === 'widget') {
|
||||
const widget = comp as WidgetComponent;
|
||||
const fieldName = widget.columnName || widget.id;
|
||||
const value = currentFormData[fieldName];
|
||||
const hasValue = value !== undefined && value !== null && value !== '';
|
||||
console.log(` - ${fieldName} (${widget.widgetType}): "${value}" (값있음: ${hasValue}, 컬럼명: ${widget.columnName})`);
|
||||
}
|
||||
});
|
||||
|
||||
// 매핑된 데이터가 비어있으면 경고
|
||||
if (Object.keys(mappedData).length === 0) {
|
||||
console.warn("⚠️ 매핑된 데이터가 없습니다. 빈 데이터로 저장됩니다.");
|
||||
}
|
||||
|
||||
// 테이블명 결정 (화면 정보에서 가져오거나 첫 번째 컴포넌트의 테이블명 사용)
|
||||
const tableName = screenInfo.tableName ||
|
||||
allComponents.find(c => c.columnName)?.tableName ||
|
||||
@@ -864,9 +1061,11 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
|
||||
|
||||
// 저장 후 데이터 초기화 (선택사항)
|
||||
if (onFormDataChange) {
|
||||
const resetData: Record<string, any> = {};
|
||||
Object.keys(formData).forEach(key => {
|
||||
onFormDataChange(key, "");
|
||||
resetData[key] = "";
|
||||
});
|
||||
onFormDataChange(resetData);
|
||||
}
|
||||
} else {
|
||||
throw new Error(result.message || "저장에 실패했습니다.");
|
||||
@@ -877,19 +1076,6 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
|
||||
}
|
||||
};
|
||||
|
||||
// 취소 액션
|
||||
const handleCancelAction = () => {
|
||||
if (confirm("변경사항을 취소하시겠습니까?")) {
|
||||
// 폼 초기화 또는 이전 페이지로 이동
|
||||
if (onFormDataChange) {
|
||||
// 모든 폼 데이터 초기화
|
||||
Object.keys(formData).forEach(key => {
|
||||
onFormDataChange(key, "");
|
||||
});
|
||||
}
|
||||
console.log("❌ 작업이 취소되었습니다.");
|
||||
}
|
||||
};
|
||||
|
||||
// 삭제 액션
|
||||
const handleDeleteAction = async () => {
|
||||
@@ -928,9 +1114,11 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
|
||||
|
||||
// 삭제 후 폼 초기화
|
||||
if (onFormDataChange) {
|
||||
const resetData: Record<string, any> = {};
|
||||
Object.keys(formData).forEach(key => {
|
||||
onFormDataChange(key, "");
|
||||
resetData[key] = "";
|
||||
});
|
||||
onFormDataChange(resetData);
|
||||
}
|
||||
} else {
|
||||
throw new Error(result.message || "삭제에 실패했습니다.");
|
||||
@@ -971,9 +1159,11 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
|
||||
const handleResetAction = () => {
|
||||
if (confirm("모든 입력을 초기화하시겠습니까?")) {
|
||||
if (onFormDataChange) {
|
||||
const resetData: Record<string, any> = {};
|
||||
Object.keys(formData).forEach(key => {
|
||||
onFormDataChange(key, "");
|
||||
resetData[key] = "";
|
||||
});
|
||||
onFormDataChange(resetData);
|
||||
}
|
||||
console.log("🔄 폼 초기화 완료");
|
||||
alert("입력이 초기화되었습니다.");
|
||||
@@ -989,42 +1179,92 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
|
||||
|
||||
// 닫기 액션
|
||||
const handleCloseAction = () => {
|
||||
console.log("❌ 창 닫기");
|
||||
// 창 닫기 또는 모달 닫기
|
||||
if (window.opener) {
|
||||
console.log("❌ 닫기 액션 실행");
|
||||
|
||||
// 모달 내부에서 실행되는지 확인
|
||||
const isInModal = document.querySelector('[role="dialog"]') !== null;
|
||||
const isInPopup = window.opener !== null;
|
||||
|
||||
if (isInModal) {
|
||||
// 모달 내부인 경우: 모달의 닫기 버튼 클릭하거나 모달 닫기 이벤트 발생
|
||||
console.log("🔄 모달 내부에서 닫기 - 모달 닫기 시도");
|
||||
|
||||
// 모달의 닫기 버튼을 찾아서 클릭
|
||||
const modalCloseButton = document.querySelector('[role="dialog"] button[aria-label*="Close"], [role="dialog"] button[data-dismiss="modal"], [role="dialog"] .dialog-close');
|
||||
if (modalCloseButton) {
|
||||
(modalCloseButton as HTMLElement).click();
|
||||
} else {
|
||||
// ESC 키 이벤트 발생시키기
|
||||
const escEvent = new KeyboardEvent('keydown', { key: 'Escape', keyCode: 27, which: 27 });
|
||||
document.dispatchEvent(escEvent);
|
||||
}
|
||||
} else if (isInPopup) {
|
||||
// 팝업 창인 경우
|
||||
console.log("🔄 팝업 창 닫기");
|
||||
window.close();
|
||||
} else {
|
||||
history.back();
|
||||
// 일반 페이지인 경우 - 이전 페이지로 이동하지 않고 아무것도 하지 않음
|
||||
console.log("🔄 일반 페이지에서 닫기 - 아무 동작 하지 않음");
|
||||
alert("닫기 버튼이 클릭되었습니다.");
|
||||
}
|
||||
};
|
||||
|
||||
// 팝업 액션
|
||||
const handlePopupAction = () => {
|
||||
console.log("🎯 팝업 액션 실행:", { popupScreenId: config?.popupScreenId });
|
||||
|
||||
if (config?.popupScreenId) {
|
||||
// 화면 팝업 열기
|
||||
// 화면 모달 열기
|
||||
setPopupScreen({
|
||||
screenId: config.popupScreenId,
|
||||
title: config.popupTitle || "상세 정보",
|
||||
size: config.popupSize || "md",
|
||||
size: "lg",
|
||||
});
|
||||
} else if (config?.popupTitle && config?.popupContent) {
|
||||
// 텍스트 팝업 표시
|
||||
// 텍스트 모달 표시
|
||||
alert(`${config.popupTitle}\n\n${config.popupContent}`);
|
||||
} else {
|
||||
alert("팝업을 표시합니다.");
|
||||
alert("모달을 표시합니다.");
|
||||
}
|
||||
};
|
||||
|
||||
// 네비게이션 액션
|
||||
const handleNavigateAction = () => {
|
||||
if (config?.navigateUrl) {
|
||||
const navigateType = config?.navigateType || "url";
|
||||
|
||||
if (navigateType === "screen" && config?.navigateScreenId) {
|
||||
// 화면으로 이동
|
||||
const screenPath = `/screens/${config.navigateScreenId}`;
|
||||
|
||||
console.log("🎯 화면으로 이동:", {
|
||||
screenId: config.navigateScreenId,
|
||||
target: config.navigateTarget || "_self",
|
||||
path: screenPath
|
||||
});
|
||||
|
||||
if (config.navigateTarget === "_blank") {
|
||||
window.open(screenPath, "_blank");
|
||||
} else {
|
||||
window.location.href = screenPath;
|
||||
}
|
||||
} else if (navigateType === "url" && config?.navigateUrl) {
|
||||
// URL로 이동
|
||||
console.log("🔗 URL로 이동:", {
|
||||
url: config.navigateUrl,
|
||||
target: config.navigateTarget || "_self"
|
||||
});
|
||||
|
||||
if (config.navigateTarget === "_blank") {
|
||||
window.open(config.navigateUrl, "_blank");
|
||||
} else {
|
||||
window.location.href = config.navigateUrl;
|
||||
}
|
||||
} else {
|
||||
console.log("🔗 네비게이션 URL이 설정되지 않았습니다.");
|
||||
console.log("🔗 네비게이션 정보가 설정되지 않았습니다:", {
|
||||
navigateType,
|
||||
hasUrl: !!config?.navigateUrl,
|
||||
hasScreenId: !!config?.navigateScreenId
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1050,7 +1290,7 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
|
||||
<Button
|
||||
onClick={handleButtonClick}
|
||||
disabled={readonly}
|
||||
size={config?.size || "sm"}
|
||||
size="sm"
|
||||
variant={config?.variant || "default"}
|
||||
className="w-full"
|
||||
style={{ height: "100%" }}
|
||||
@@ -1142,16 +1382,6 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
|
||||
marginBottom: component.style?.labelMarginBottom || "4px",
|
||||
};
|
||||
|
||||
// 팝업 크기 설정
|
||||
const getPopupMaxWidth = (size: string) => {
|
||||
switch (size) {
|
||||
case "sm": return "max-w-md";
|
||||
case "md": return "max-w-2xl";
|
||||
case "lg": return "max-w-4xl";
|
||||
case "xl": return "max-w-6xl";
|
||||
default: return "max-w-2xl";
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -1168,9 +1398,12 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
|
||||
<div className="h-full w-full">{renderInteractiveWidget(component)}</div>
|
||||
</div>
|
||||
|
||||
{/* 팝업 화면 모달 */}
|
||||
<Dialog open={!!popupScreen} onOpenChange={() => setPopupScreen(null)}>
|
||||
<DialogContent className={`${getPopupMaxWidth(popupScreen?.size || "md")} max-h-[80vh] overflow-hidden`}>
|
||||
{/* 모달 화면 */}
|
||||
<Dialog open={!!popupScreen} onOpenChange={() => {
|
||||
setPopupScreen(null);
|
||||
setPopupFormData({}); // 팝업 닫을 때 formData도 초기화
|
||||
}}>
|
||||
<DialogContent className="max-w-4xl max-h-[80vh] overflow-hidden">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{popupScreen?.title || "상세 정보"}</DialogTitle>
|
||||
</DialogHeader>
|
||||
@@ -1201,10 +1434,31 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
|
||||
zIndex: popupComponent.position.z || 1,
|
||||
}}
|
||||
>
|
||||
{/* 🎯 핵심 수정: 팝업 전용 formData 사용 */}
|
||||
<InteractiveScreenViewer
|
||||
component={popupComponent}
|
||||
allComponents={popupLayout}
|
||||
hideLabel={false}
|
||||
screenInfo={popupScreenInfo || undefined}
|
||||
formData={popupFormData}
|
||||
onFormDataChange={(newData) => {
|
||||
console.log("💾 팝업 formData 업데이트:", {
|
||||
newData,
|
||||
newDataType: typeof newData,
|
||||
newDataKeys: Object.keys(newData || {}),
|
||||
prevFormData: popupFormData
|
||||
});
|
||||
|
||||
// 잘못된 데이터 타입 체크
|
||||
if (typeof newData === 'string') {
|
||||
console.error("❌ 문자열이 formData로 전달됨:", newData);
|
||||
return;
|
||||
}
|
||||
|
||||
if (newData && typeof newData === 'object') {
|
||||
setPopupFormData(prev => ({ ...prev, ...newData }));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
|
||||
Reference in New Issue
Block a user