"use client"; import React, { useState, useEffect, useCallback } from "react"; import { CardContent, CardHeader, CardTitle } from "@/components/ui/card"; 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 { Badge } from "@/components/ui/badge"; import { ArrowRight, Database, Globe, Loader2, AlertTriangle, CheckCircle } from "lucide-react"; import { toast } from "sonner"; // API import import { getActiveConnections, ConnectionInfo } from "@/lib/api/multiConnection"; import { checkRelationshipNameDuplicate } from "@/lib/api/dataflowSave"; // νƒ€μž… import import { Connection } from "@/lib/types/multiConnection"; interface ConnectionStepProps { connectionType: "data_save" | "external_call"; fromConnection?: Connection; toConnection?: Connection; relationshipName?: string; description?: string; diagramId?: number; // πŸ”§ μˆ˜μ • λͺ¨λ“œ κ°μ§€μš© onSelectConnection: (type: "from" | "to", connection: Connection) => void; onSetRelationshipName: (name: string) => void; onSetDescription: (description: string) => void; onNext: () => void; } /** * πŸ”— 1단계: μ—°κ²° 선택 * - FROM/TO λ°μ΄ν„°λ² μ΄μŠ€ μ—°κ²° 선택 * - μ—°κ²° μƒνƒœ ν‘œμ‹œ * - μ§€μ—°μ‹œκ°„ 정보 */ const ConnectionStep: React.FC = React.memo( ({ connectionType, fromConnection, toConnection, relationshipName, description, diagramId, onSelectConnection, onSetRelationshipName, onSetDescription, onNext, }) => { const [connections, setConnections] = useState([]); const [isLoading, setIsLoading] = useState(true); const [nameCheckStatus, setNameCheckStatus] = useState<"idle" | "checking" | "valid" | "duplicate">("idle"); // API 응닡을 Connection νƒ€μž…μœΌλ‘œ λ³€ν™˜ const convertToConnection = (connectionInfo: ConnectionInfo): Connection => ({ id: connectionInfo.id, name: connectionInfo.connection_name, type: connectionInfo.db_type, host: connectionInfo.host, port: connectionInfo.port, database: connectionInfo.database_name, username: connectionInfo.username, isActive: connectionInfo.is_active === "Y", companyCode: connectionInfo.company_code, createdDate: connectionInfo.created_date, updatedDate: connectionInfo.updated_date, }); // πŸ” 관계λͺ… 쀑볡 체크 (λ””λ°”μš΄μŠ€ 적용) const checkNameDuplicate = useCallback( async (name: string) => { if (!name.trim()) { setNameCheckStatus("idle"); return; } setNameCheckStatus("checking"); try { const result = await checkRelationshipNameDuplicate(name, diagramId); setNameCheckStatus(result.isDuplicate ? "duplicate" : "valid"); if (result.isDuplicate) { toast.warning(`"${name}" 이름이 이미 μ‚¬μš© μ€‘μž…λ‹ˆλ‹€. (${result.duplicateCount}개 발견)`); } } catch (error) { console.error("쀑볡 체크 μ‹€νŒ¨:", error); setNameCheckStatus("idle"); } }, [diagramId], ); // 관계λͺ… λ³€κ²½ μ‹œ 쀑볡 체크 (λ””λ°”μš΄μŠ€) useEffect(() => { if (!relationshipName) { setNameCheckStatus("idle"); return; } const timeoutId = setTimeout(() => { checkNameDuplicate(relationshipName); }, 500); // 500ms λ””λ°”μš΄μŠ€ return () => clearTimeout(timeoutId); }, [relationshipName, checkNameDuplicate]); // μ—°κ²° λͺ©λ‘ λ‘œλ“œ useEffect(() => { const loadConnections = async () => { try { setIsLoading(true); const data = await getActiveConnections(); // 메인 DB μ—°κ²° μΆ”κ°€ const mainConnection: Connection = { id: 0, name: "메인 λ°μ΄ν„°λ² μ΄μŠ€", type: "postgresql", host: "localhost", port: 5432, database: "main", username: "main_user", isActive: true, }; // API 응닡을 Connection νƒ€μž…μœΌλ‘œ λ³€ν™˜ const convertedConnections = data.map(convertToConnection); // 쀑볡 λ°©μ§€: 기쑴에 메인 연결이 μ—†λŠ” κ²½μš°μ—λ§Œ μΆ”κ°€ const hasMainConnection = convertedConnections.some((conn) => conn.id === 0); const preliminaryConnections = hasMainConnection ? convertedConnections : [mainConnection, ...convertedConnections]; // ID 쀑볡 제거 (Set μ‚¬μš©) const uniqueConnections = preliminaryConnections.filter( (conn, index, arr) => arr.findIndex((c) => c.id === conn.id) === index, ); console.log("πŸ”— μ—°κ²° λͺ©λ‘ λ‘œλ“œ μ™„λ£Œ:", uniqueConnections); setConnections(uniqueConnections); } catch (error) { console.error("❌ μ—°κ²° λͺ©λ‘ λ‘œλ“œ μ‹€νŒ¨:", error); toast.error("μ—°κ²° λͺ©λ‘μ„ λΆˆλŸ¬μ˜€λŠ”λ° μ‹€νŒ¨ν–ˆμŠ΅λ‹ˆλ‹€."); // μ—λŸ¬ μ‹œμ—λ„ 메인 연결은 제곡 const mainConnection: Connection = { id: 0, name: "메인 λ°μ΄ν„°λ² μ΄μŠ€", type: "postgresql", host: "localhost", port: 5432, database: "main", username: "main_user", isActive: true, }; setConnections([mainConnection]); } finally { setIsLoading(false); } }; loadConnections(); }, []); const handleConnectionSelect = (type: "from" | "to", connectionId: string) => { const connection = connections.find((c) => c.id.toString() === connectionId); if (connection) { onSelectConnection(type, connection); } }; const canProceed = fromConnection && toConnection; const getConnectionIcon = (connection: Connection) => { return connection.id === 0 ? : ; }; const getConnectionBadge = (connection: Connection) => { if (connection.id === 0) { return ( 메인 DB ); } return ( {connection.type?.toUpperCase()} ); }; return ( <> 1단계: μ—°κ²° 선택

{connectionType === "data_save" ? "데이터λ₯Ό μ €μž₯ν•  μ†ŒμŠ€μ™€ λŒ€μƒ λ°μ΄ν„°λ² μ΄μŠ€λ₯Ό μ„ νƒν•˜μ„Έμš”." : "μ™ΈλΆ€ ν˜ΈμΆœμ„ μœ„ν•œ μ†ŒμŠ€μ™€ λŒ€μƒ 연결을 μ„ νƒν•˜μ„Έμš”."}

{/* 관계 정보 μž…λ ₯ */}

관계 정보

onSetRelationshipName(e.target.value)} className={`pr-10 ${ nameCheckStatus === "duplicate" ? "border-red-500 focus:border-red-500" : nameCheckStatus === "valid" ? "border-green-500 focus:border-green-500" : "" }`} />
{nameCheckStatus === "checking" && ( )} {nameCheckStatus === "valid" && } {nameCheckStatus === "duplicate" && }
{nameCheckStatus === "duplicate" &&

이미 μ‚¬μš© 쀑인 μ΄λ¦„μž…λ‹ˆλ‹€.

} {nameCheckStatus === "valid" &&

μ‚¬μš© κ°€λŠ₯ν•œ μ΄λ¦„μž…λ‹ˆλ‹€.

}