From 6f2899b77d37ee249d2116e19a7d941fcc32112e Mon Sep 17 00:00:00 2001 From: Adrian Bonpin Date: Sun, 26 Apr 2026 11:23:24 -0500 Subject: [PATCH] feat: add devices grid page with device cards --- app/devices/page-client.tsx | 167 ++++++++++++++++++++++++++++++++++++ app/devices/page.tsx | 10 +++ 2 files changed, 177 insertions(+) create mode 100644 app/devices/page-client.tsx create mode 100644 app/devices/page.tsx diff --git a/app/devices/page-client.tsx b/app/devices/page-client.tsx new file mode 100644 index 0000000..039aea0 --- /dev/null +++ b/app/devices/page-client.tsx @@ -0,0 +1,167 @@ +"use client" + +import { useEffect, useState } from "react" +import Link from "next/link" +import { motion } from "motion/react" +import { Gamepad2Icon, TrendingUpIcon, DatabaseIcon, ArrowRightIcon, Loader2 } from "lucide-react" + +interface DeviceStats { + slug: string + name: string + deviceType: string + sortOrder: number + totalBenchmarks: number + avgFps: number | null + gameCount: number + verifiedCount: number + bestGame: { + id: string + title: string + headerImage: string | null + fpsAvg: number + } | null +} + +const deviceTypeLabel: Record = { + handheld: "Handheld", + console: "Console", +} + +export function DevicesPageClient() { + const [devices, setDevices] = useState([]) + const [loading, setLoading] = useState(true) + const [error, setError] = useState(null) + + useEffect(() => { + async function fetchDevices() { + try { + const res = await fetch("/api/hardware/stats") + if (!res.ok) throw new Error(`HTTP ${res.status}`) + const data = await res.json() + setDevices(data) + } catch (err) { + setError("Failed to load devices") + console.error(err) + } finally { + setLoading(false) + } + } + fetchDevices() + }, []) + + if (loading) { + return ( +
+ +
+ ) + } + + if (error) { + return ( +
+
+ +

{error}

+
+
+ ) + } + + if (devices.length === 0) { + return ( +
+
+ +

No devices found

+

Benchmark data will appear as devices are added

+
+
+ ) + } + + return ( +
+ {/* Header */} + +
+

Devices

+

+ Browse benchmark data for handheld and console devices +

+
+
+ + {/* Device Grid */} + +
+ {devices.map((device, i) => ( + + + {/* Header */} +
+
+

+ {device.name} +

+ + + {deviceTypeLabel[device.deviceType] || device.deviceType} + +
+ +
+ + {/* Stats */} +
+
+ + {device.totalBenchmarks} + Benchmarks +
+
+ + + {device.avgFps !== null ? device.avgFps : "—"} + + Avg FPS +
+
+ + {device.gameCount} + Games +
+
+ + {/* Best game */} + {device.bestGame && ( +
+ Top: {device.bestGame.title} · {device.bestGame.fpsAvg} FPS +
+ )} + +
+ ))} +
+
+
+ ) +} diff --git a/app/devices/page.tsx b/app/devices/page.tsx new file mode 100644 index 0000000..f99b99c --- /dev/null +++ b/app/devices/page.tsx @@ -0,0 +1,10 @@ +import { DevicesPageClient } from "./page-client" + +export const metadata = { + title: "Devices — DeckyVault", + description: "Browse handheld and console devices with benchmark data on DeckyVault", +} + +export default function DevicesPage() { + return +}