'use client';
import { useState, useEffect, useRef } from 'react';
interface NodeConfig {
id: string;
label: string;
description: string;
icon: string;
color: string;
tech: string[];
x: number;
y: number;
connections: string[];
}
const nodes: NodeConfig[] = [
{
id: 'whymyrating',
label: 'whymyrating',
description: 'Web + Mobile Apps',
icon: '📱',
color: '#8b5cf6',
tech: ['React', 'Next.js', 'React Native', 'TypeScript'],
x: 50,
y: 5,
connections: ['whymyrating-engine', 'whymyrating-brand', 'whymyrating-templates'],
},
{
id: 'whymyrating-engine',
label: 'whymyrating-engine',
description: 'AI Pipelines & Backend API',
icon: '⚙️',
color: '#ef4444',
tech: ['Python', 'FastAPI', 'PostgreSQL', 'Docker'],
x: 10,
y: 40,
connections: ['nuc-server'],
},
{
id: 'whymyrating-brand',
label: 'whymyrating-brand',
description: 'Brand Assets & Guidelines',
icon: '🎨',
color: '#f59e0b',
tech: ['SVG', 'Figma', 'CSS'],
x: 50,
y: 40,
connections: [],
},
{
id: 'whymyrating-templates',
label: 'whymyrating-templates',
description: 'Email & Doc Templates',
icon: '📄',
color: '#10b981',
tech: ['Next.js', 'React Email', 'TypeScript'],
x: 90,
y: 40,
connections: [],
},
{
id: 'whyrating-hub',
label: 'whyrating-hub',
description: 'Internal Dashboard (You are here)',
icon: '🏠',
color: '#3b82f6',
tech: ['Next.js', 'Tailwind', 'TypeScript'],
x: 50,
y: 75,
connections: [],
},
{
id: 'nuc-server',
label: 'NUC Server',
description: 'Self-hosted Infrastructure',
icon: '🖥️',
color: '#6366f1',
tech: ['Docker', 'PostgreSQL', 'Nginx', 'Gitea'],
x: 10,
y: 75,
connections: ['whyrating-hub'],
},
];
const DiagramNode = ({
node,
index,
onHover,
isHighlighted,
}: {
node: NodeConfig;
index: number;
onHover: (id: string | null) => void;
isHighlighted: boolean;
}) => {
const [isHovered, setIsHovered] = useState(false);
const [isVisible, setIsVisible] = useState(false);
useEffect(() => {
const timer = setTimeout(() => setIsVisible(true), index * 100);
return () => clearTimeout(timer);
}, [index]);
return (
{
setIsHovered(true);
onHover(node.id);
}}
onMouseLeave={() => {
setIsHovered(false);
onHover(null);
}}
>
{/* Pulse animation for hovered node */}
{isHovered && (
)}
{/* Icon */}
{node.icon}
{/* Label */}
{node.label}
{/* Description */}
{node.description}
{/* Tech stack - shown on hover */}
{node.tech.map((t, i) => (
{t}
))}
);
};
const ConnectionLine = ({
from,
to,
isHighlighted,
delay,
}: {
from: NodeConfig;
to: NodeConfig;
isHighlighted: boolean;
delay: number;
}) => {
const [isVisible, setIsVisible] = useState(false);
useEffect(() => {
const timer = setTimeout(() => setIsVisible(true), delay);
return () => clearTimeout(timer);
}, [delay]);
// Calculate line positions (center of nodes)
const x1 = from.x;
const y1 = from.y + 8; // Offset for node height
const x2 = to.x;
const y2 = to.y;
// Calculate control points for curved line
const midY = (y1 + y2) / 2;
return (
);
};
export default function InfrastructureDiagram() {
const [hoveredNode, setHoveredNode] = useState(null);
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
}, []);
if (!mounted) {
return (
);
}
// Get all connections
const connections: { from: NodeConfig; to: NodeConfig }[] = [];
nodes.forEach((node) => {
node.connections.forEach((targetId) => {
const target = nodes.find((n) => n.id === targetId);
if (target) {
connections.push({ from: node, to: target });
}
});
});
// Determine which nodes/connections are highlighted
const getHighlightedNodes = () => {
if (!hoveredNode) return null;
const node = nodes.find((n) => n.id === hoveredNode);
if (!node) return null;
return new Set([hoveredNode, ...node.connections]);
};
const highlightedSet = getHighlightedNodes();
return (
{/* Background grid */}
{/* Diagram container */}
{/* Connection lines */}
{connections.map(({ from, to }, i) => (
))}
{/* Nodes */}
{nodes.map((node, i) => (
))}
{/* Legend */}
{[
{ color: '#8b5cf6', label: 'Main Apps' },
{ color: '#ef4444', label: 'Backend/AI' },
{ color: '#f59e0b', label: 'Brand' },
{ color: '#10b981', label: 'Templates' },
{ color: '#3b82f6', label: 'Dashboard' },
{ color: '#6366f1', label: 'Infrastructure' },
].map((item) => (
))}
{/* Global styles for animations */}
);
}