"use client"; /** * 플로우 검증 결과 알림 (우측 상단 플로팅) */ import { memo, useState } from "react"; import { AlertTriangle, AlertCircle, Info, X, ChevronDown, ChevronUp } from "lucide-react"; import type { FlowValidation } from "@/lib/utils/flowValidation"; import { summarizeValidations } from "@/lib/utils/flowValidation"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { cn } from "@/lib/utils"; interface ValidationNotificationProps { validations: FlowValidation[]; onNodeClick?: (nodeId: string) => void; onClose?: () => void; } export const ValidationNotification = memo(({ validations, onNodeClick, onClose }: ValidationNotificationProps) => { const [isExpanded, setIsExpanded] = useState(false); const summary = summarizeValidations(validations); if (validations.length === 0) { return null; } const getTypeLabel = (type: string): string => { const labels: Record = { "disconnected-node": "연결되지 않은 노드", "parallel-conflict": "병렬 실행 충돌", "missing-where": "WHERE 조건 누락", "circular-reference": "순환 참조", "data-source-mismatch": "데이터 소스 불일치", "parallel-table-access": "병렬 테이블 접근", }; return labels[type] || type; }; // 타입별로 그룹화 const groupedValidations = validations.reduce( (acc, validation) => { if (!acc[validation.type]) { acc[validation.type] = []; } acc[validation.type].push(validation); return acc; }, {} as Record, ); return (
0 ? "border-warning" : "border-primary", )} > {/* 헤더 */}
0 ? "bg-warning/10" : "bg-primary/10", )} onClick={() => setIsExpanded(!isExpanded)} >
{summary.hasBlockingIssues ? ( ) : summary.warningCount > 0 ? ( ) : ( )} 플로우 검증
{summary.errorCount > 0 && ( {summary.errorCount} )} {summary.warningCount > 0 && ( {summary.warningCount} )} {summary.infoCount > 0 && ( {summary.infoCount} )}
{isExpanded ? ( ) : ( )} {onClose && ( )}
{/* 확장된 내용 */} {isExpanded && (
{Object.entries(groupedValidations).map(([type, typeValidations]) => { const firstValidation = typeValidations[0]; const Icon = firstValidation.severity === "error" ? AlertCircle : firstValidation.severity === "warning" ? AlertTriangle : Info; return (
{/* 타입 헤더 */}
{getTypeLabel(type)} {typeValidations.length}개
{/* 검증 항목들 */}
{typeValidations.map((validation, index) => (
onNodeClick?.(validation.nodeId)} >

{validation.message}

{validation.affectedNodes && validation.affectedNodes.length > 1 && (
영향받는 노드: {validation.affectedNodes.length}개
)}
클릭하여 노드 보기 →
))}
); })}
)} {/* 요약 메시지 (닫혀있을 때) */} {!isExpanded && (

{summary.hasBlockingIssues ? "⛔ 오류를 해결해야 저장할 수 있습니다" : summary.warningCount > 0 ? "⚠️ 경고 사항을 확인하세요" : "ℹ️ 정보를 확인하세요"}

)}
); }); ValidationNotification.displayName = "ValidationNotification";