# Devices Redesign & Admin Panel — Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Fix critical device page bugs, redesign devices list/detail pages with proper SEO, remove the broken Community Trust graph, and add an admin management panel with nested routes for Users, Hardware, and Games. **Architecture:** Server components fetch data and pass as props (true RSC pattern). Admin panel uses a `(admin)` route group with shared sidebar layout and nested routes. All devices code fixes the `"handled"` → `"handheld"` enum typo and the `upscalerBreakdown`/`fsrBreakdown` property mismatch. **Tech Stack:** Next.js 16 (App Router), React 19, Tailwind CSS v4, Drizzle ORM (PostgreSQL), better-auth (admin plugin), Elysia API, Framer Motion v12, ECharts, Lucide React icons --- ### Task 1: DB Migration — Rename Enum & Add Image Column **Files:** - Create: `drizzle/0011_rename-handled-to-handheld-add-image.sql` - Modify: `lib/db/schema/hardware.ts` - Modify: `lib/db/seed.ts` - [ ] **Step 1: Create the SQL migration file** Create `drizzle/0011_rename-handled-to-handheld-add-image.sql`: ```sql -- Rename pg_enum value from 'handled' to 'handheld' ALTER TYPE device_type RENAME VALUE 'handled' TO 'handheld'; -- Add nullable image column to hardware table ALTER TABLE hardware ADD COLUMN image text; ``` - [ ] **Step 2: Update the Drizzle schema to match** In `lib/db/schema/hardware.ts`, change the enum and add the image column: ```ts import { integer, pgEnum, pgTable, text, timestamp } from "drizzle-orm/pg-core" export const deviceTypeEnum = pgEnum("device_type", ["handheld", "console"]) export const hardware = pgTable("hardware", { slug: text("slug").primaryKey(), name: text("name").notNull(), deviceType: deviceTypeEnum("device_type").notNull(), image: text("image"), sortOrder: integer("sort_order").default(0).notNull(), createdAt: timestamp("created_at").defaultNow().notNull(), }) ``` - [ ] **Step 3: Update the seed file** In `lib/db/seed.ts`, change all `"handled" as const` to `"handheld" as const`: ```ts const devices = [ { slug: "steamdeck-oled", name: "Steam Deck OLED", deviceType: "handheld" as const, sortOrder: 0, }, { slug: "steamdeck-lcd", name: "Steam Deck LCD", deviceType: "handheld" as const, sortOrder: 1, }, { slug: "steam-machine", name: "Steam Machine", deviceType: "console" as const, sortOrder: 2, }, ] ``` - [ ] **Step 4: Run the migration** Run: `cd /Users/adrianbonpin/Documents/Code/personal/deckyvault && bunx drizzle-kit push` Expected: Migration applies — enum value renamed, image column added. - [ ] **Step 5: Run the seed to verify** Run: `cd /Users/adrianbonpin/Documents/Code/personal/deckyvault && bun run lib/db/seed.ts` Expected: No conflict errors, devices seeded with `"handheld"` type. - [ ] **Step 6: Commit** ```bash git add drizzle/0011_rename-handled-to-handheld-add-image.sql lib/db/schema/hardware.ts lib/db/seed.ts git commit -m "fix: rename device_type enum 'handled' to 'handheld', add image column" ``` --- ### Task 2: Fix API Bug Fixes — avgFps Nulls & upscalerBreakdown Consistency **Files:** - Modify: `lib/api/hardware-stats.ts` - [ ] **Step 1: Fix avgFps calculation to exclude null FPS entries** In `lib/api/hardware-stats.ts`, locate the `/:slug/stats` handler and replace the `avgFps` calculation: **Old code (around line ~90):** ```ts const avgFps = Math.round( (entries.reduce((s, e) => s + (e.fpsAvg ?? 0), 0) / totalBenchmarks) * 10 ) / 10 ``` **New code:** ```ts // Filter out null FPS entries before computing average (SQL avg() ignores nulls) const fpsEntries = entries.filter((e) => e.fpsAvg !== null) const avgFps = fpsEntries.length > 0 ? Math.round( (fpsEntries.reduce((s, e) => s + e.fpsAvg!, 0) / fpsEntries.length) * 10 ) / 10 : null ``` - [ ] **Step 2: Verify the API response shape uses `upscalerBreakdown` consistently** The API already returns `upscalerBreakdown` (this is the correct name). Confirm there is no `fsrBreakdown` in the API file. No changes needed here — the fix happens in the client component (Task 5). - [ ] **Step 3: Run the dev server and test the endpoint** Run: `cd /Users/adrianbonpin/Documents/Code/personal/deckyvault && curl -s http://localhost:3000/api/hardware/steamdeck-oled/stats | head -c 200` Expected: JSON response with `avgFps` reflecting only non-null entries, and `upscalerBreakdown` (not `fsrBreakdown`). - [ ] **Step 4: Commit** ```bash git add lib/api/hardware-stats.ts git commit -m "fix: exclude null FPS entries from avgFps calculation in device stats API" ``` --- ### Task 3: Remove Community Trust Chart **Files:** - Modify: `app/game/[id]/game-page-client.tsx` - Modify: `lib/api/game-stats.ts` - Delete: `components/charts/TrustBar.tsx` - [ ] **Step 1: Remove TrustBar from game-page-client.tsx** In `app/game/[id]/game-page-client.tsx`: 1. **Remove the import** (line ~34): Delete: `import { TrustBar } from "@/components/charts/TrustBar"` 2. **Remove trust from the stats interface** (around line ~143): Delete the entire `trust` array from the interface: ```ts // DELETE these lines: trust: Array<{ id: string hardwareSlug: string upvotes: number downvotes: number verifiedAt: string | null userNotes: string | null createdAt: string }> ``` 3. **Remove trust from filteredStats** (around line ~326): Delete the line: `trust: filterByDevice(stats.trust),` 4. **Remove the Community Trust render section** (around line ~995): Delete the entire block: ```tsx {/* Row 4 — Trust Bar */} {filteredStats && filteredStats.trust.length > 0 && (

Community Trust

)} ``` - [ ] **Step 2: Remove trust data from game-stats API** In `lib/api/game-stats.ts`: 1. **Remove Section 9** (trust data mapping, around line ~242): Delete: ```ts // ── 9. Community trust ──────────────────────────────────────── const trust = entries.map((e) => ({ id: e.id, hardwareSlug: e.hardwareSlug, upvotes: e.upvotes, downvotes: e.downvotes, verifiedAt: e.verifiedAt ? e.verifiedAt.toISOString() : null, userNotes: e.userNotes, createdAt: e.createdAt.toISOString(), })) ``` 2. **Remove `trust` from the empty return** (around line ~77): Change: `trust: [],` → remove this line 3. **Remove `trust` from the final return object** (around line ~276): Delete the line: `trust,` - [ ] **Step 3: Delete the TrustBar component file** Delete: `components/charts/TrustBar.tsx` - [ ] **Step 4: Verify no other imports of TrustBar exist** Run: `cd /Users/adrianbonpin/Documents/Code/personal/deckyvault && grep -r "TrustBar" --include="*.tsx" --include="*.ts" app/ components/ lib/` Expected: No results (TrustBar fully removed from codebase). - [ ] **Step 5: Commit** ```bash git add -A git commit -m "refactor: remove broken Community Trust chart (TrustBar) - Remove TrustBar component and chart - Remove trust data from game-stats API response - Remove trust section from game detail page - DB columns and vote endpoints preserved for future voting system" ```