"use client"; import { AnimatePresence, motion, useInView, useReducedMotion } from "motion/react"; import { useEffect, useRef, useState } from "react"; import { cn } from "@/lib/utils"; export type ServerRackHue = "sky" | "emerald" | "amber" | "violet" | "rose" | "orange" | "indigo" | "teal"; const hues: Record = { sky: "[--hue:var(--color-sky-500)]", emerald: "[--hue:var(--color-emerald-500)]", amber: "[--hue:var(--color-amber-500)]", violet: "[--hue:var(--color-violet-500)]", rose: "[--hue:var(--color-rose-500)]", orange: "[--hue:var(--color-orange-500)]", indigo: "[--hue:var(--color-indigo-500)]", teal: "[--hue:var(--color-teal-500)]", }; export interface ServerRackUnit { name: string; kind?: "switch" | "server" | "storage"; drives?: number; detail?: string; temp?: number; status?: "healthy" | "degraded"; failing?: number; } export interface ServerRackProps { units?: ServerRackUnit[]; label?: string; hue?: ServerRackHue; autoPeek?: boolean; className?: string; } const defaultUnits: ServerRackUnit[] = [ { name: "edge-sw-01", kind: "switch" }, { name: "app-01", kind: "server", drives: 4, detail: "4 × 1.9 TB NVMe", temp: 34 }, { name: "app-02", kind: "server", drives: 4, detail: "4 × 1.9 TB NVMe", temp: 36 }, { name: "vault-01", kind: "storage", drives: 12, detail: "12 × 16 TB", temp: 39 }, { name: "vault-02", kind: "storage", drives: 12, detail: "12 × 16 TB", temp: 41, status: "degraded", failing: 6 }, ]; const pull = { type: "spring", stiffness: 380, damping: 32 } as const; function blink(seed: number) { const d = 1.1 + ((seed * 37) % 17) / 10; return { animate: { opacity: [1, 0.25, 1, 1, 0.35, 1] }, transition: { duration: d, delay: ((seed * 53) % 11) / 10, ease: "linear" as const, repeat: Number.POSITIVE_INFINITY, times: [0, 0.12, 0.24, 0.6, 0.7, 0.8], }, }; } function Led({ className, seed, still }: { className: string; seed?: number; still?: boolean }) { const anim = seed === undefined || still ? null : blink(seed); return ( ); } function Drives({ count, failing, seed, still }: { count: number; failing?: number; seed: number; still: boolean }) { const cols = 4; const rows = Math.ceil(count / cols); return (
{Array.from({ length: count }, (_, i) => (
))}
); } function Faceplate({ unit, index, still }: { unit: ServerRackUnit; index: number; still: boolean }) { if (unit.kind === "switch") { return (
{unit.name}
{Array.from({ length: 12 }, (_, i) => (
{i % 5 !== 3 && ( )}
))}
); } return (
{unit.name}
); } function useCalmMotion() { const prefers = useReducedMotion(); const [mounted, setMounted] = useState(false); useEffect(() => setMounted(true), []); return mounted && !!prefers; } export function ServerRack({ units = defaultUnits, label = "rack-a3", hue = "sky", autoPeek = true, className, }: ServerRackProps) { const reduce = useCalmMotion(); const root = useRef(null); const inView = useInView(root); const [hovered, setHovered] = useState(null); const [pinned, setPinned] = useState(null); const [peek, setPeek] = useState(null); const [inside, setInside] = useState(false); const openable = units.map((u, i) => (u.kind === "switch" ? -1 : i)).filter((i) => i >= 0); const interacting = inside || hovered !== null || pinned !== null; useEffect(() => { if (!autoPeek || !inView || interacting || openable.length === 0) { setPeek(null); return; } let step = 0; let timer: ReturnType; const tick = (open: boolean) => { if (open) { setPeek(openable[(openable.length - 1 + step) % openable.length]); step += 2; timer = setTimeout(() => tick(false), 2600); } else { setPeek(null); timer = setTimeout(() => tick(true), 2200); } }; timer = setTimeout(() => tick(true), 1400); return () => clearTimeout(timer); }, [autoPeek, inView, interacting, openable.length]); const open = hovered ?? pinned ?? peek; const active = open === null ? null : units[open]; const totalDrives = units.reduce((n, u) => n + (u.kind === "switch" ? 0 : (u.drives ?? 0)), 0); const degraded = units.filter((u) => u.status === "degraded").length; return (
e.pointerType === "mouse" && setInside(true)} onPointerLeave={() => { setInside(false); setHovered(null); }} > {["left-1.5", "right-1.5"].map((side) => (
))}
    {units.map((unit, i) => { const canOpen = unit.kind !== "switch"; const isOpen = open === i; const tall = unit.kind === "storage"; const still = !!reduce || !inView; const body = (
    {canOpen && ( )}
    ); return ( {canOpen ? ( ) : (
    {body}
    )}
    ); })}
{active?.name ?? label} {active ? [active.detail, active.temp !== undefined ? `${active.temp} °C` : null].filter(Boolean).join(" · ") : `${units.length} units · ${totalDrives} drives`} 0) ? "text-warning-foreground" : "text-success-foreground", )} > 0) ? "bg-warning" : "bg-success", )} /> {active ? active.status === "degraded" ? active.failing !== undefined ? "Rebuilding" : "Degraded" : "Healthy" : degraded > 0 ? `${degraded} degraded` : "All healthy"}
); }