ERP-node/frontend/components/dataflow/node-editor/nodes/NodeWithValidation.tsx

139 lines
4.6 KiB
TypeScript

"use client";
/**
* 검증 기능이 포함된 노드 래퍼
*
* 모든 노드에 경고/에러 아이콘을 표시하는 공통 래퍼
*/
import { memo, ReactNode } from "react";
import { AlertTriangle, AlertCircle, Info } from "lucide-react";
import type { FlowValidation } from "@/lib/utils/flowValidation";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
interface NodeWithValidationProps {
nodeId: string;
validations: FlowValidation[];
children: ReactNode;
onClick?: () => void;
}
export const NodeWithValidation = memo(
({ nodeId, validations, children, onClick }: NodeWithValidationProps) => {
// 이 노드와 관련된 검증 결과 필터링
const nodeValidations = validations.filter(
(v) => v.nodeId === nodeId || v.affectedNodes?.includes(nodeId)
);
// 가장 높은 심각도 결정
const hasError = nodeValidations.some((v) => v.severity === "error");
const hasWarning = nodeValidations.some((v) => v.severity === "warning");
const hasInfo = nodeValidations.some((v) => v.severity === "info");
if (nodeValidations.length === 0) {
return <>{children}</>;
}
// 심각도별 아이콘 및 색상
const getIconAndColor = () => {
if (hasError) {
return {
Icon: AlertCircle,
bgColor: "bg-red-500",
textColor: "text-red-700",
borderColor: "border-red-500",
hoverBgColor: "hover:bg-red-600",
};
}
if (hasWarning) {
return {
Icon: AlertTriangle,
bgColor: "bg-yellow-500",
textColor: "text-yellow-700",
borderColor: "border-yellow-500",
hoverBgColor: "hover:bg-yellow-600",
};
}
return {
Icon: Info,
bgColor: "bg-blue-500",
textColor: "text-blue-700",
borderColor: "border-blue-500",
hoverBgColor: "hover:bg-blue-600",
};
};
const { Icon, bgColor, textColor, borderColor, hoverBgColor } =
getIconAndColor();
return (
<div className="relative" onClick={onClick}>
{children}
{/* 경고 배지 */}
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<div
className={`absolute -right-2 -top-2 flex h-6 w-6 cursor-pointer items-center justify-center rounded-full ${bgColor} ${hoverBgColor} shadow-lg transition-all hover:scale-110`}
>
<Icon className="h-3.5 w-3.5 text-white" />
{nodeValidations.length > 1 && (
<span className="absolute -right-1 -top-1 flex h-4 w-4 items-center justify-center rounded-full bg-white text-[10px] font-bold shadow-sm">
{nodeValidations.length}
</span>
)}
</div>
</TooltipTrigger>
<TooltipContent
side="right"
className="max-w-xs border-0 p-0"
>
<div className={`rounded-lg border-2 ${borderColor} bg-white p-3 shadow-lg`}>
<div className="mb-2 flex items-center gap-2">
<Icon className={`h-4 w-4 ${textColor}`} />
<span className="font-semibold text-gray-900">
{hasError
? "오류"
: hasWarning
? "경고"
: "정보"} ({nodeValidations.length})
</span>
</div>
<div className="space-y-2">
{nodeValidations.map((validation, index) => (
<div
key={index}
className="rounded border-l-2 border-gray-300 bg-gray-50 p-2"
>
<div className="mb-1 text-xs font-medium text-gray-500">
{validation.type}
</div>
<div className="text-sm text-gray-700">
{validation.message}
</div>
{validation.affectedNodes && validation.affectedNodes.length > 1 && (
<div className="mt-1 text-xs text-gray-500">
: {validation.affectedNodes.length}
</div>
)}
</div>
))}
</div>
</div>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
);
}
);
NodeWithValidation.displayName = "NodeWithValidation";