150 lines
5.6 KiB
TypeScript
150 lines
5.6 KiB
TypeScript
"use client";
|
|
|
|
import { memo, useMemo } from "react";
|
|
import { Handle, Position, NodeProps } from "@xyflow/react";
|
|
import { Badge } from "@/components/ui/badge";
|
|
|
|
export interface PageNodeData {
|
|
title: string;
|
|
status: "draft" | "ready" | "review";
|
|
content?: string;
|
|
notes?: string;
|
|
hasAttachments?: boolean;
|
|
hasLinks?: boolean;
|
|
[key: string]: unknown;
|
|
}
|
|
|
|
const statusConfig = {
|
|
draft: {
|
|
label: "Szkic",
|
|
className: "bg-yellow-500/20 text-yellow-400 border-yellow-500/30",
|
|
},
|
|
ready: {
|
|
label: "Gotowe",
|
|
className: "bg-green-500/20 text-green-400 border-green-500/30",
|
|
},
|
|
review: {
|
|
label: "Do poprawki",
|
|
className: "bg-red-500/20 text-red-400 border-red-500/30",
|
|
},
|
|
};
|
|
|
|
// Different styles for node types
|
|
const nodeTypeConfig = {
|
|
page: {
|
|
bgClass: "bg-slate-800/90",
|
|
borderClass: "border-slate-600",
|
|
selectedBorderClass: "border-blue-500",
|
|
accentColor: "blue",
|
|
icon: (
|
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
|
</svg>
|
|
),
|
|
label: "Strona",
|
|
},
|
|
note: {
|
|
bgClass: "bg-amber-900/40",
|
|
borderClass: "border-amber-600/50",
|
|
selectedBorderClass: "border-amber-400",
|
|
accentColor: "amber",
|
|
icon: (
|
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
|
|
</svg>
|
|
),
|
|
label: "Notatka",
|
|
},
|
|
section: {
|
|
bgClass: "bg-purple-900/40",
|
|
borderClass: "border-purple-600/50",
|
|
selectedBorderClass: "border-purple-400",
|
|
accentColor: "purple",
|
|
icon: (
|
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
|
|
</svg>
|
|
),
|
|
label: "Sekcja",
|
|
},
|
|
};
|
|
|
|
function CustomNode({ data, selected, type }: NodeProps) {
|
|
const nodeData = data as PageNodeData;
|
|
const status = statusConfig[nodeData.status] || statusConfig.draft;
|
|
const nodeType = nodeTypeConfig[type as keyof typeof nodeTypeConfig] || nodeTypeConfig.page;
|
|
|
|
const hasContent = useMemo(() => {
|
|
return nodeData.hasAttachments || nodeData.hasLinks;
|
|
}, [nodeData.hasAttachments, nodeData.hasLinks]);
|
|
|
|
const handleColorClass = nodeType.accentColor === "amber"
|
|
? "!bg-amber-500"
|
|
: nodeType.accentColor === "purple"
|
|
? "!bg-purple-500"
|
|
: "!bg-blue-500";
|
|
|
|
return (
|
|
<div
|
|
className={`
|
|
${nodeType.bgClass} backdrop-blur-sm border-2 rounded-xl shadow-xl
|
|
min-w-[180px] max-w-[240px] transition-all duration-200
|
|
${selected ? `${nodeType.selectedBorderClass} shadow-${nodeType.accentColor}-500/25` : `${nodeType.borderClass} hover:border-opacity-80`}
|
|
`}
|
|
>
|
|
{/* Top Handle */}
|
|
<Handle
|
|
type="target"
|
|
position={Position.Top}
|
|
className={`!w-3 !h-3 ${handleColorClass} !border-2 !border-slate-800`}
|
|
/>
|
|
|
|
{/* Type indicator */}
|
|
<div className={`px-3 py-1.5 border-b border-white/10 flex items-center gap-2 text-${nodeType.accentColor}-400`}>
|
|
{nodeType.icon}
|
|
<span className="text-xs font-medium opacity-70">{nodeType.label}</span>
|
|
</div>
|
|
|
|
{/* Content */}
|
|
<div className="p-4">
|
|
<div className="flex items-start justify-between gap-2 mb-2">
|
|
<h3 className="font-semibold text-white text-sm leading-tight line-clamp-2">
|
|
{nodeData.title || "Nowa strona"}
|
|
</h3>
|
|
{hasContent && (
|
|
<div className="flex-shrink-0">
|
|
<svg
|
|
className={`w-4 h-4 text-${nodeType.accentColor}-400`}
|
|
fill="none"
|
|
stroke="currentColor"
|
|
viewBox="0 0 24 24"
|
|
>
|
|
<path
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
strokeWidth={2}
|
|
d="M15.172 7l-6.586 6.586a2 2 0 102.828 2.828l6.414-6.586a4 4 0 00-5.656-5.656l-6.415 6.585a6 6 0 108.486 8.486L20.5 13"
|
|
/>
|
|
</svg>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
<Badge variant="outline" className={`text-xs ${status.className}`}>
|
|
{status.label}
|
|
</Badge>
|
|
</div>
|
|
|
|
{/* Bottom Handle */}
|
|
<Handle
|
|
type="source"
|
|
position={Position.Bottom}
|
|
className={`!w-3 !h-3 ${handleColorClass} !border-2 !border-slate-800`}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default memo(CustomNode);
|
|
|