"use client"; import React, { useState, useEffect } from "react"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, } from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Textarea } from "@/components/ui/textarea"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Loader2, Copy, Link as LinkIcon, Trash2, AlertCircle } from "lucide-react"; import { ScreenDefinition } from "@/types/screen"; import { screenApi } from "@/lib/api/screen"; import { apiClient } from "@/lib/api/client"; import { toast } from "sonner"; import { useAuth } from "@/hooks/useAuth"; import { Alert, AlertDescription } from "@/components/ui/alert"; interface LinkedModalScreen { screenId: number; screenName: string; screenCode: string; newScreenName?: string; newScreenCode?: string; } interface CompanyInfo { companyCode: string; companyName: string; } interface CopyScreenModalProps { isOpen: boolean; onClose: () => void; sourceScreen: ScreenDefinition | null; onCopySuccess: () => void; } export default function CopyScreenModal({ isOpen, onClose, sourceScreen, onCopySuccess, }: CopyScreenModalProps) { const { user } = useAuth(); // 최고 관리자 판별: userType이 "SUPER_ADMIN" 또는 companyCode가 "*" const isSuperAdmin = user?.userType === "SUPER_ADMIN" || user?.companyCode === "*"; // 디버깅: 사용자 정보 확인 useEffect(() => { console.log("🔍 CopyScreenModal - User Info:", { user, isSuperAdmin, userType: user?.userType, companyCode: user?.companyCode, 조건1: user?.userType === "SUPER_ADMIN", 조건2: user?.companyCode === "*", 최종판별: user?.userType === "SUPER_ADMIN" || user?.companyCode === "*", }); }, [user, isSuperAdmin]); // 메인 화면 복사 정보 const [screenName, setScreenName] = useState(""); const [screenCode, setScreenCode] = useState(""); const [description, setDescription] = useState(""); // 대상 회사 선택 (최고 관리자 전용) const [targetCompanyCode, setTargetCompanyCode] = useState(""); const [companies, setCompanies] = useState([]); const [loadingCompanies, setLoadingCompanies] = useState(false); // 연결된 모달 화면들 const [linkedScreens, setLinkedScreens] = useState([]); const [loadingLinkedScreens, setLoadingLinkedScreens] = useState(false); // 화면명 일괄 수정 기능 const [useBulkRename, setUseBulkRename] = useState(false); const [removeText, setRemoveText] = useState(""); const [addPrefix, setAddPrefix] = useState(""); // 복사 중 상태 const [isCopying, setIsCopying] = useState(false); // 최고 관리자인 경우 회사 목록 조회 useEffect(() => { console.log("🔍 회사 목록 조회 체크:", { isSuperAdmin, isOpen }); if (isSuperAdmin && isOpen) { console.log("✅ 회사 목록 조회 시작"); loadCompanies(); } }, [isSuperAdmin, isOpen]); // 모달이 열릴 때 초기값 설정 및 연결된 화면 감지 useEffect(() => { console.log("🔍 모달 초기화:", { isOpen, sourceScreen, isSuperAdmin }); if (isOpen && sourceScreen) { // 메인 화면 정보 설정 setScreenName(`${sourceScreen.screenName} (복사본)`); setDescription(sourceScreen.description || ""); // 대상 회사 코드 설정 if (isSuperAdmin) { setTargetCompanyCode(sourceScreen.companyCode); // 기본값: 원본과 같은 회사 } else { setTargetCompanyCode(sourceScreen.companyCode); } // 연결된 모달 화면 감지 console.log("✅ 연결된 모달 화면 감지 시작"); detectLinkedModals(); } }, [isOpen, sourceScreen, isSuperAdmin]); // 일괄 변경 설정이 변경될 때 화면명 자동 업데이트 useEffect(() => { if (!sourceScreen) return; if (useBulkRename) { // 일괄 수정 사용 시: (복사본) 텍스트 제거 const newMainName = applyBulkRename(sourceScreen.screenName); setScreenName(newMainName); // 모달 화면명 업데이트 setLinkedScreens((prev) => prev.map((screen) => ({ ...screen, newScreenName: applyBulkRename(screen.screenName), })) ); } else { // 일괄 수정 미사용 시: (복사본) 텍스트 추가 setScreenName(`${sourceScreen.screenName} (복사본)`); setLinkedScreens((prev) => prev.map((screen) => ({ ...screen, newScreenName: screen.screenName, })) ); } }, [useBulkRename, removeText, addPrefix]); // 대상 회사 변경 시 기존 코드 초기화 useEffect(() => { if (targetCompanyCode) { console.log("🔄 회사 변경 → 기존 코드 초기화:", targetCompanyCode); setScreenCode(""); // 모달 화면들의 코드도 초기화 setLinkedScreens((prev) => prev.map((screen) => ({ ...screen, newScreenCode: undefined })) ); } }, [targetCompanyCode]); // linkedScreens 로딩이 완료되면 화면 코드 생성 useEffect(() => { // 모달 화면들의 코드가 모두 설정되었는지 확인 const allModalCodesSet = linkedScreens.length === 0 || linkedScreens.every(screen => screen.newScreenCode); console.log("🔍 코드 생성 조건 체크:", { targetCompanyCode, loadingLinkedScreens, screenCode, linkedScreensCount: linkedScreens.length, allModalCodesSet, }); // 조건: 회사 코드가 있고, 로딩이 완료되고, (메인 코드가 없거나 모달 코드가 없을 때) const needsCodeGeneration = targetCompanyCode && !loadingLinkedScreens && (!screenCode || (linkedScreens.length > 0 && !allModalCodesSet)); if (needsCodeGeneration) { console.log("✅ 화면 코드 생성 시작 (linkedScreens 개수:", linkedScreens.length, ")"); generateScreenCodes(); } }, [targetCompanyCode, loadingLinkedScreens, screenCode, linkedScreens]); // 회사 목록 조회 const loadCompanies = async () => { try { setLoadingCompanies(true); const response = await apiClient.get("/admin/companies"); const data = response.data.data || response.data || []; setCompanies(data.map((c: any) => ({ companyCode: c.company_code || c.companyCode, companyName: c.company_name || c.companyName, }))); } catch (error) { console.error("회사 목록 조회 실패:", error); toast.error("회사 목록을 불러오는데 실패했습니다."); } finally { setLoadingCompanies(false); } }; // 연결된 모달 화면 감지 const detectLinkedModals = async () => { if (!sourceScreen) return; try { setLoadingLinkedScreens(true); console.log("📡 API 호출: detectLinkedModals", sourceScreen.screenId); const linked = await screenApi.detectLinkedModals(sourceScreen.screenId); console.log("✅ 연결된 모달 화면 감지 결과:", linked); // 초기 newScreenName 설정 setLinkedScreens( linked.map((screen) => ({ ...screen, newScreenName: `${screen.screenName} (복사본)`, })) ); if (linked.length > 0) { toast.info(`${linked.length}개의 연결된 모달 화면을 감지했습니다.`); console.log("🎉 감지된 화면들:", linked); } else { console.log("ℹ️ 연결된 모달 화면이 없습니다."); } } catch (error) { console.error("❌ 연결된 화면 감지 실패:", error); // 에러가 나도 진행 가능하도록 무시 } finally { setLoadingLinkedScreens(false); } }; // 화면 코드 자동 생성 (메인 + 모달 화면들) - 일괄 생성으로 중복 방지 const generateScreenCodes = async () => { if (!targetCompanyCode) { console.log("❌ targetCompanyCode가 없어서 화면 코드 생성 중단"); return; } try { // 메인 화면 1개 + 연결된 모달 화면들 = 총 개수 const totalCount = 1 + linkedScreens.length; console.log(`📡 화면 코드 일괄 생성 API 호출: ${targetCompanyCode}, 개수: ${totalCount}`); // 한 번에 모든 코드 생성 (중복 방지) const generatedCodes = await screenApi.generateMultipleScreenCodes( targetCompanyCode, totalCount ); console.log("✅ 생성된 화면 코드들:", generatedCodes); // 첫 번째 코드는 메인 화면용 setScreenCode(generatedCodes[0]); console.log("✅ 메인 화면 코드:", generatedCodes[0]); // 나머지 코드들은 모달 화면들에 순서대로 할당 if (linkedScreens.length > 0) { const updatedLinkedScreens = linkedScreens.map((screen, index) => ({ ...screen, newScreenCode: generatedCodes[index + 1], // 1번째부터 시작 })); setLinkedScreens(updatedLinkedScreens); console.log("✅ 모달 화면 코드 할당 완료:", updatedLinkedScreens.map(s => ({ name: s.screenName, code: s.newScreenCode }))); } } catch (error) { console.error("❌ 화면 코드 일괄 생성 실패:", error); toast.error("화면 코드 생성에 실패했습니다."); } }; // 연결된 화면 이름 변경 const updateLinkedScreenName = (screenId: number, newName: string) => { setLinkedScreens((prev) => prev.map((screen) => screen.screenId === screenId ? { ...screen, newScreenName: newName } : screen ) ); }; // 연결된 화면 제거 (복사하지 않음) const removeLinkedScreen = (screenId: number) => { setLinkedScreens((prev) => prev.filter((screen) => screen.screenId !== screenId)); }; // 화면명 일괄 변경 적용 const applyBulkRename = (originalName: string): string => { if (!useBulkRename) return originalName; let newName = originalName; // 1. 제거할 텍스트 제거 if (removeText.trim()) { newName = newName.replace(new RegExp(removeText.trim(), "g"), ""); newName = newName.trim(); // 앞뒤 공백 제거 } // 2. 접두사 추가 if (addPrefix.trim()) { newName = addPrefix.trim() + " " + newName; } return newName; }; // 미리보기: 변경될 화면명들 const getPreviewNames = () => { if (!sourceScreen || !useBulkRename) return null; return { main: { original: sourceScreen.screenName, preview: applyBulkRename(sourceScreen.screenName), // (복사본) 없음 }, modals: linkedScreens.map((screen) => ({ original: screen.screenName, preview: applyBulkRename(screen.screenName), })), }; }; // 화면 복사 실행 const handleCopy = async () => { if (!sourceScreen) return; // 입력값 검증 if (!screenName.trim()) { toast.error("화면명을 입력해주세요."); return; } if (!screenCode.trim()) { toast.error("화면 코드 생성에 실패했습니다. 잠시 후 다시 시도해주세요."); return; } // 연결된 화면들의 이름 검증 for (const linked of linkedScreens) { if (!linked.newScreenName?.trim()) { toast.error(`"${linked.screenName}" 모달 화면의 새 이름을 입력해주세요.`); return; } if (!linked.newScreenCode?.trim()) { toast.error(`"${linked.screenName}" 모달 화면의 코드가 생성되지 않았습니다.`); return; } } try { setIsCopying(true); // 화면명 중복 체크 const companyCode = targetCompanyCode || sourceScreen.companyCode; // 메인 화면명 중복 체크 const isMainDuplicate = await screenApi.checkDuplicateScreenName( companyCode, screenName.trim() ); if (isMainDuplicate) { toast.error(`"${screenName}" 화면명이 이미 존재합니다. 다른 이름을 입력해주세요.`); setIsCopying(false); return; } // 모달 화면명 중복 체크 for (const linked of linkedScreens) { const isModalDuplicate = await screenApi.checkDuplicateScreenName( companyCode, linked.newScreenName!.trim() ); if (isModalDuplicate) { toast.error( `"${linked.newScreenName}" 화면명이 이미 존재합니다. 모달 화면의 이름을 변경해주세요.` ); setIsCopying(false); return; } } // 메인 화면 + 모달 화면들 일괄 복사 const result = await screenApi.copyScreenWithModals(sourceScreen.screenId, { targetCompanyCode: targetCompanyCode || undefined, // 최고 관리자: 대상 회사 전달 mainScreen: { screenName: screenName.trim(), screenCode: screenCode.trim(), description: description.trim(), }, modalScreens: linkedScreens.map((screen) => ({ sourceScreenId: screen.screenId, screenName: screen.newScreenName!.trim(), screenCode: screen.newScreenCode!.trim(), })), }); console.log("✅ 복사 완료:", result); toast.success( `화면 복사가 완료되었습니다! (메인 1개 + 모달 ${result.modalScreens.length}개)` ); onCopySuccess(); handleClose(); } catch (error: any) { console.error("화면 복사 실패:", error); const errorMessage = error.response?.data?.message || "화면 복사에 실패했습니다."; toast.error(errorMessage); } finally { setIsCopying(false); } }; // 모달 닫기 const handleClose = () => { setScreenName(""); setScreenCode(""); setDescription(""); setTargetCompanyCode(""); setLinkedScreens([]); onClose(); }; return ( 화면 복사 {linkedScreens.length > 0 && ( ({linkedScreens.length}개의 모달 화면 포함) )} {sourceScreen?.screenName} 화면을 복사합니다. 화면 구성과 연결된 모달 화면도 함께 복사됩니다.
{/* 원본 화면 정보 */}

원본 화면 정보

화면명: {sourceScreen?.screenName}
화면코드: {sourceScreen?.screenCode}
회사코드: {sourceScreen?.companyCode}
{/* 최고 관리자: 대상 회사 선택 */} {isSuperAdmin && (

선택한 회사로 화면이 복사됩니다. 원본과 다른 회사를 선택하면 회사 간 화면 복사가 가능합니다.

)} {/* 화면명 일괄 수정 */}
setUseBulkRename(e.target.checked)} className="h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-2 focus:ring-blue-500" />
{useBulkRename && (
setRemoveText(e.target.value)} placeholder="예: 탑씰" className="mt-1 bg-white" />
setAddPrefix(e.target.value)} placeholder="예: 대진산업" className="mt-1 bg-white" />
{/* 미리보기 */} {(removeText || addPrefix) && getPreviewNames() && (

미리보기

{/* 메인 화면 */}

메인: {getPreviewNames()?.main.original}

→ {getPreviewNames()?.main.preview}

{/* 모달 화면들 */} {getPreviewNames()?.modals.map((modal, idx) => (

모달: {modal.original}

→ {modal.preview}

))}
)}

💡 모든 화면명에서 "제거할 텍스트"를 삭제하고 "추가할 접두사"를 앞에 붙입니다.

)}
{/* 메인 화면 정보 입력 */}

메인 화면 정보

setScreenName(e.target.value)} placeholder="복사될 화면의 이름을 입력하세요" className="mt-1" />