- Add Dashboard as first sidebar nav item under Overview section
- Update /manage page to render DashboardOverview instead of redirecting
- Remove redirect to /manage/users
- DashboardOverview: stat cards, top contributors, playability distribution,
and Steam sync health sections
- DashboardCharts: placeholder for future ECharts integration
Add /api/dashboard/stats route that provides admin dashboard statistics:
- Total counts (games, benchmarks, users)
- Pending moderation items (reports, suggestions)
- 30-day activity metrics (recent benchmarks, recent games)
- Top 10 contributors by entry count
- Sync health breakdown by status
- Games by source distribution
- Playability distribution
Auth: requireContributorOrAdmin guard
Route registered in lib/api/index.ts
- Add new filter state variables (min/max FPS, FSR, Proton/Native, anti-cheat,
playability, Steam review score, free-to-play, multiplayer)
- Expand sort options to include performance, popularity, release_date, steam_reviews
- Add Performance, Compatibility, and Other filter UI sections with inputs
- Integrate SavedFilters component for saving/loading filter presets
- Add PlayabilityBadge and AntiCheatBadge to game cards
- Update API listing to return antiCheatRelevant/antiCheatStatus per game
- Update server-side games page to include new fields in initialGames
- Update clear-filters logic across filter panel and empty state
Add 8 new filter parameters to GET /api/games/listing:
- minFps/maxFps: filter games with benchmarks in FPS range
- fsrSupport: filter games that have FSR entries
- protonNative: filter by proton status (proton/native/both)
- antiCheatStatus: filter by anti-cheat support status
- playabilityStatus: filter by playability (great/playable/needs_tweaks/unplayable)
- steamReviewScore: minimum Steam review score threshold
- isFree: filter free-to-play games
- hasMultiplayer: filter games with multiplayer support
Add 4 new sort options:
- performance: sort by average FPS (subquery)
- popularity: sort by recommendationsTotal
- release_date: sort by releaseDate
- steam_reviews: sort by steamReviewScore
Also returns additional fields in response: isFree, releaseDate,
steamReviewScore, recommendationsTotal, playabilityStatus,
onlineMultiplayerStatus.
- Create lib/api/saved-filters.ts with GET, POST, PATCH, DELETE endpoints
- All endpoints require authentication via requireAuth guard
- Users can only access their own saved filters
- Register savedFilterRoutes in lib/api/index.ts
- POST /api/community-suggestions - submit suggestion (auth required)
- GET /api/community-suggestions/game/:gameId - list suggestions for a game
- GET /api/community-suggestions/pending - moderation view (contributor/admin)
- PATCH /api/community-suggestions/:suggestionId/review - approve/reject
- Discord webhook notification on new suggestions
- Route registered in API index and app router
- Create lib/api/steam-reviews.ts with in-memory cache (1hr TTL)
- Proxy endpoint for fetching Steam reviews by game ID
- Supports offset/limit/language query params
- Registers route in API index and app router
Task 10: Device filter now combines games with platform support entries
AND games with performance entries for the selected hardware, instead of
only checking gamePlatformSupport. This ensures games that have benchmarks
but no explicit platform support entry still appear in filtered results.
Task 11: The benchmark count query (bCounts) in unified search now filters
out soft-deleted performance entries using isRemoved = false. The pCounts
and perfStats queries already had this filter; only bCounts was missing it.
- Wrap table in overflow-x-auto container with min-w-[800px] to prevent overflow on smaller screens
- Add hard delete (purge) button to actions column for each benchmark entry
- Add confirmation dialog for permanent deletion with warning
- Add handleHardDelete handler that calls DELETE /api/admin/performance/:id/hard-delete
- Add DELETE endpoint for hard-deleting performance entries from the database
- Expand confirmAction state type to include 'hardDelete'
- Return null when price is missing instead of showing 'Free'
- Show green 'Free' badge only when initial === 0 && final === 0
- Show emerald 'Free*' badge with tooltip for promotional free (final === 0, initial > 0)
- Fixes incorrect 'Free' label for games like Days Gone during free weekends
- Remove layoutId from preset card in game-page-client.tsx and modal in preset-detail-modal.tsx to eliminate text smearing during morph animation.
- Replace modal scale/opacity spring animation with bottom sheet y-slide spring (y: 100% → 0).
- Wrap settings table content in AnimatePresence mode='wait' with keyed motion.div for crossfade when switching categories.
- Add Management and Moderation sections with labeled groups
- Section labels: muted uppercase micro text
- Section dividers: horizontal on desktop, vertical on mobile
- Active state: bg-primary/10 text-primary rounded-lg
- Widen desktop sidebar from md:w-48 to md:w-56
- Add desktop-only Admin Panel header with LayoutDashboardIcon
- Expand nav to cover all 6 admin pages (users, hardware, games,
reports, benchmarks, comments)
- Update admin layout description to 'Manage your platform'
- GET /admin/comments: list all comments with pagination, removed filter, and author search
- PATCH /admin/comments/:id/remove: admin-only soft delete
- PATCH /admin/comments/:id/restore: admin-only restore
- GET /admin/reports with pagination, status filtering, joins, and batch author name lookup
- PATCH /admin/reports/:id/status to review (soft-delete entry) or dismiss
- Register route in lib/api/index.ts and app/api/[[...slugs]]/route.ts
- Device list fetched from /api/hardware/stats
- Create/edit devices via modal form
- Delete with confirmation dialog
- Uses existing hardware CRUD API endpoints
- User list with search by name/email
- Change role (user/contributor/admin) via dropdown
- Ban/unban users with status indicators
- Uses better-auth admin plugin client methods
- Admin layout with server-side auth guard (redirects non-admins)
- Reusable sidebar component with active state detection
- Navbar profile dropdown shows admin link for admin users
- Admin routes are noindexed (robots: false)
- /admin redirects to /admin/users