import React, { useState, useEffect } from 'react'; import { motion, useMotionValue, useSpring } from 'framer-motion'; // --- mock data --- const PROJECTS = [ { id: 1, title: "TaskFlow API", category: "Backend", description: "A high-throughput microservice handling real-time project syncing and background workers.", metrics: { stars: 142, uptime: "99.98%", commits: "412" }, tags: ["Node.js", "TypeScript", "Redis", "Docker"], repo: "https://github.com/jeanlucponsard/taskflow", demo: "https://jeanlucponsard.dev/taskflow" }, { id: 2, title: "DevMetrics Dashboard", category: "Frontend", description: "An interactive telemetry UI rendering real-time system performance and database health.", metrics: { stars: 89, uptime: "100%", commits: "185" }, tags: ["Next.js", "Tailwind", "Framer Motion", "Recharts"], repo: "https://github.com/jeanlucponsard/devmetrics", demo: "https://jeanlucponsard.dev/metrics" }, { id: 3, title: "Sentinel Auth", category: "Security", description: "A lightweight, zero-dependency OAuth2 provider featuring biometric passkey support.", metrics: { stars: 310, uptime: "99.99%", commits: "290" }, tags: ["Go", "WebAuthn", "SQLite", "Crypto"], repo: "https://github.com/jeanlucponsard/sentinel", demo: "https://jeanlucponsard.dev/sentinel" } ]; export default function ProjectPanel() { const [filter, setFilter] = useState("All"); const [hoveredCard, setHoveredCard] = useState(null); const [cursorText, setCursorText] = useState(""); // Custom Cursor Logic const cursorX = useMotionValue(-100); const cursorY = useMotionValue(-100); const springConfig = { damping: 25, stiffness: 250 }; const cursorXSpring = useSpring(cursorX, springConfig); const cursorYSpring = useSpring(cursorY, springConfig); useEffect(() => { const moveCursor = (e) => { cursorX.set(e.clientX - 16); cursorY.set(e.clientY - 16); }; window.addEventListener("mousemove", moveCursor); return () => window.removeEventListener("mousemove", moveCursor); }, [cursorX, cursorY]); const filteredProjects = filter === "All" ? PROJECTS : PROJECTS.filter(p => p.category === filter); return (
{/* 1. Custom Interactive Cursor */} {hoveredCard && VIEW}
{/* Header Widget */}

// PORTFOLIO.PROJECTS

Jean-Luc Ponsard

{/* Filter Widget */}
{["All", "Frontend", "Backend", "Security"].map((cat) => ( ))}
{/* 2. Project Cards Grid with Scroll & Hover Animations */}
{filteredProjects.map((project, index) => ( setHoveredCard(project.id)} onMouseLeave={() => setHoveredCard(null)} className="group relative bg-slate-900/40 border border-slate-800/60 rounded-2xl p-6 backdrop-blur-xl hover:border-slate-700/80 transition-colors duration-300 flex flex-col justify-between overflow-hidden" > {/* Decorative background glow */}
{/* Card Header Widget */}
{project.category}
LIVE

{project.title}

{project.description}

{/* 3. Metrics Widget Panel */}

Stars

{project.metrics.stars}

Uptime

{project.metrics.uptime}

Commits

{project.metrics.commits}

{/* Card Footer */}
{project.tags.map(tag => ( {tag} ))}
))}
); }