"use client"
import { useCallback, useState, useEffect, useMemo, useRef } from "react"
import { useRouter, useSearchParams } from "next/navigation"
import Image from "next/image"
import {
Gamepad2Icon,
MessageSquareIcon,
SettingsIcon,
TrendingUpIcon,
ExternalLinkIcon,
ClockIcon,
SparklesIcon,
DatabaseIcon,
Plus,
ThumbsUpIcon,
ThumbsDownIcon,
GaugeIcon,
ChevronDownIcon,
PencilIcon,
} from "lucide-react"
import Link from "next/link"
import { useSession } from "@/lib/auth-client"
import { FaSteam } from "react-icons/fa"
import { motion, AnimatePresence } from "motion/react"
import type { GameSettingCategory } from "@/lib/db/schema/performanceEntries"
import { PresetDetailModal } from "./preset-detail-modal"
import { BookmarkButton } from "@/components/saved-games/bookmark-button"
import { WindowsIcon, MacIcon, LinuxIcon } from "@/app/components/PlatformIcons"
import { AntiCheatBadge } from "@/components/anti-cheat-badge"
import { PlayabilityBadge } from "@/components/playability-badge"
import { SteamReviews } from "@/components/steam-reviews"
import { CommunitySuggestionForm } from "@/components/community-suggestion-form"
// Chart imports
import { HistoricalAreaChart } from "@/components/charts/HistoricalAreaChart"
import { UpscalerBarChart } from "@/components/charts/UpscalerBarChart"
import { FpsBoxplot } from "@/components/charts/FpsBoxplot"
import { FpsRangeChart } from "@/components/charts/FpsRangeChart"
import { DeviceDonut } from "@/components/charts/DeviceDonut"
import { PerformanceTierChart } from "@/components/charts/PerformanceTierChart"
import { StabilityScatterChart } from "@/components/charts/StabilityScatterChart"
import { BatteryLifeChart } from "@/components/charts/BatteryLifeChart"
// Comments
import { CommentSection } from "@/components/comments/comment-section"
// Types
interface Game {
id: string
steamAppId: number | null
title: string
description: string | null
developer: string | null
publisher: string | null
genres: string[] | null
headerImage: string | null
capsuleImage: string | null
storeUrl: string | null
source: string
lastSync: string | null
syncStatus: string | null
createdAt: string
metascore?: number | null
onlineMultiplayerStatus?: string | null
systemRequirements: { minimum: string | null; recommended: string | null } | null
metacriticScore: number | null
metacriticUrl: string | null
recommendationsTotal: number | null
priceCurrent: number | null
priceInitial: number | null
priceCurrency: string | null
isFree: boolean
releaseDate: string | null
categories: string[] | null
platforms: { windows: boolean; mac: boolean; linux: boolean } | null
playabilityStatus?: "great" | "playable" | "needs_tweaks" | "unplayable" | "unknown" | null
steamReviewScore?: number | null
steamReviewSentiment?: string | null
steamReviewCount?: number | null
}
interface Counts {
benchmarks: number
presets: number
comments: number
}
interface PlatformSupport {
id: string
gameId: string
hardwareSlug: string
isSupported: boolean
protonStatus: string
playabilityStatus: "great" | "playable" | "needs_tweaks" | "unplayable" | "unknown" | null
antiCheatRelevant: boolean
antiCheatStatus: "none" | "supported" | "unsupported" | "unknown" | null
antiCheatName: string | null
}
interface Preset {
id: string
gameId?: string
hardwareSlug: string
hardwareName: string
upvotes: number
downvotes: number
settingsJson: GameSettingCategory[] | null
settingsCount: number
fpsAvg: number | null
fpsLow: number | null
fpsHigh: number | null
fpsOnePercentLow: number | null
upscalerType: string | null
upscalerVersion: string | null
frameGenMethod: string | null
protonVersion: string | null
osVersion: string | null
launchOptions: string | null
loadTimeSsd: number | null
loadTimeSd: number | null
estimatedBatteryMin: number | null
tdpWatts: number | null
youtubeVideoId: string | null
screenshots: Array<{ id: string; url: string; width: number; height: number; orderIndex: number }> | null
customSystem: boolean
userNotes: string | null
userId: string
userName: string | null
userImage: string | null
verifiedAt: string | null
isPinned: boolean
pinnedAt: string | null
createdAt: string
hardwareWattHours: number | null
hardwareDeviceType: string | null
}
interface StatsResponse {
summary: {
totalEntries: number
avgFps: number
bestDevice: string
verifiedCount: number
versionCount: number
avgStability: number | null
bestOnePercentLow: number | null
}
isRawPerformer: boolean
isPoorPerformance: boolean
boxplot: Array<{
hardwareSlug: string
hardwareName: string
min: number
q1: number
median: number
onePercentLow?: number
q3: number
max: number
}>
historical: Array<{
period: string
entries: Array<{
hardwareSlug: string
avgFps: number
count: number
}>
}>
upscalerStats: Array<{
upscalerType: string
upscalerVersion?: string
frameGenMethod: string
hardwareSlug: string
avgFps: number
count: number
}>
fpsRange: Array<{
id: string
hardwareSlug: string
fpsLow: number
fpsAvg: number
fpsHigh: number
fpsOnePercentLow: number | null
isRawPerformer: boolean
}>
deviceBreakdown: Array<{
hardwareSlug: string
hardwareName: string
count: number
wattHours: number | null
tdpMax: number | null
deviceType: string | null
}>
performanceTiers: Array<{
hardwareSlug: string
unplayable: number
playable: number
smooth: number
excellent: number
}>
stabilityScatter: Array<{
id: string
hardwareSlug: string
fpsAvg: number
fpsOnePercentLow: number
stabilityRatio: number
}>
batteryLife: Array<{
id: string
hardwareSlug: string
tdpWatts: number
estimatedBatteryMin: number
estimatedBatteryHours: number
wattHours: number | null
tdpMax: number | null
estimatedAtMaxTdpMin: number | null
}>
filterOptions: {
protonVersions: string[]
osVersions: string[]
}
}
interface Props {
game: Game
counts: Counts
platformSupport: PlatformSupport[]
presets: Preset[]
gameId: string
}
const UPSCALER_OPTIONS = ["Any", "None", "FSR", "DLSS", "XeSS", "LSFG", "Other"]
const FRAMEGEN_OPTIONS = ["Any", "None", "FSR FG", "DLSS FG", "LSFG", "Other"]
function formatDate(value: string | null): string {
if (!value) return "—"
return new Date(value).toLocaleDateString()
}
function isRawPerformerPreset(preset: Preset): boolean {
return (
preset.fpsAvg !== null &&
preset.fpsAvg >= 60 &&
preset.upscalerType === "none" &&
preset.frameGenMethod === "none"
)
}
function isPoorPerformancePreset(preset: Preset): boolean {
return preset.fpsAvg !== null && preset.fpsAvg < 30
}
function getFpsColor(preset: Preset): string {
if (preset.fpsAvg === null) return "text-text/60"
if (isRawPerformerPreset(preset)) return "text-green-400"
if (preset.frameGenMethod && preset.frameGenMethod !== "none") {
if (preset.frameGenMethod === "dlss_fg") return "text-blue-400"
return "text-orange-400"
}
if (preset.upscalerType && preset.upscalerType !== "none")
return "text-orange-400"
return "text-text/60"
}
function generatePresetName(preset: Preset): string {
const parts = [preset.hardwareName]
if (preset.fpsAvg !== null) parts.push(`${Math.round(preset.fpsAvg)}fps`)
return parts.join(" · ")
}
function HeroInfo({
game,
stats,
session,
counts,
coverImage,
imgError,
handleImgError,
platformSupport,
protonDbUrl,
steamDbUrl,
gameId,
formatDate,
Badge,
}: {
game: Game
stats: StatsResponse | null
session: { user?: { id?: string } } | null
counts: Counts
coverImage: string | null
imgError: boolean
handleImgError: () => void
platformSupport: PlatformSupport[]
protonDbUrl: string | null
steamDbUrl: string | null
gameId: string
formatDate: (value: string | null) => string
Badge: React.ComponentType<{ icon: React.ElementType; value: number; label: string }>
}) {
return (
<>
{/* Title row */}
{game.title}
{stats?.isRawPerformer && (
Raw Performer
)}
{stats?.isPoorPerformance && (
⚠ Poor Performance
)}
{/* Playability badge */}
{game.playabilityStatus && (
)}
{/* Anti-cheat badge */}
{platformSupport?.some((p) => p.antiCheatRelevant) && (
p.antiCheatRelevant)?.antiCheatStatus ?? "unknown"
}
antiCheatName={
platformSupport.find((p) => p.antiCheatRelevant)?.antiCheatName
}
/>
)}
{/* Steam review score */}
{game.steamReviewScore != null && (
{game.steamReviewScore}% Positive
{game.steamReviewSentiment && (
({game.steamReviewSentiment.replace(/_/g, ' ')})
)}
)}
{/* Subtitle */}
{game.developer && {game.developer}}
{game.developer && game.publisher && (
·
)}
{game.publisher && {game.publisher}}
{game.genres && game.genres.length > 0 && (
<>
·
{game.genres.slice(0, 3).join(", ")}
>
)}
{/* Stats badges */}
{game.metascore !== undefined &&
game.metascore !== null && (
★ {game.metascore}
)}
{/* Metadata pills */}
{game.releaseDate && (
{game.releaseDate}
)}
{game.isFree && (
Free to Play
)}
{game.priceCurrent != null && !game.isFree && game.priceCurrency && (
{new Intl.NumberFormat("en-US", { style: "currency", currency: game.priceCurrency }).format(game.priceCurrent / 100)}
)}
{game.priceCurrent != null && !game.isFree && game.priceInitial != null && game.priceInitial > game.priceCurrent && game.priceCurrency && (
{new Intl.NumberFormat("en-US", { style: "currency", currency: game.priceCurrency }).format(game.priceInitial / 100)}
)}
{game.metacriticScore != null && (
★ {game.metacriticScore}/100
)}
{game.onlineMultiplayerStatus && (
{game.onlineMultiplayerStatus}
)}
{game.platforms && (
{game.platforms.windows && }
{game.platforms.mac && }
{game.platforms.linux && }
)}
{game.steamAppId !== null && (
AppID {game.steamAppId}
)}
{/* External links */}
{/* DB stats row */}
{stats && (
{stats.summary.versionCount} versions
·
{stats.summary.verifiedCount} verified
{game.lastSync && (
<>
·
Last sync{" "}
{formatDate(game.lastSync)}
>
)}
)}
{stats && (
{session?.user && game.source !== "steam" && (
Edit Game
)}
{session && (
Add Benchmark
)}
{game.source !== "steam" && (
)}
)}
>
)
}
export function GamePageClient({
game,
counts,
platformSupport,
presets,
gameId,
}: Props) {
const router = useRouter()
const searchParams = useSearchParams()
const { data: session } = useSession()
const [imgError, setImgError] = useState(false)
const [stats, setStats] = useState(null)
const [selectedDevices, setSelectedDevices] = useState([])
const [filters, setFilters] = useState({
proton: "all",
os: "all",
upscaler: "all",
frameGen: "all",
})
const [loading, setLoading] = useState(true)
const [selectedPresetId, setSelectedPresetId] = useState(null)
const [showSystemReq, setShowSystemReq] = useState(true)
const [reportedPresets, setReportedPresets] = useState>(new Set())
const presetsRef = useRef(null)
// On mount, auto-open preset from URL
useEffect(() => {
const presetId = searchParams.get("preset")
queueMicrotask(() => {
if (presetId) {
setSelectedPresetId(presetId)
} else {
setSelectedPresetId(null)
}
})
}, [searchParams])
const handlePresetOpen = (presetId: string) => {
setSelectedPresetId(presetId)
const url = new URL(window.location.href)
url.searchParams.set("preset", presetId)
router.replace(url.toString(), { scroll: false })
}
const handlePresetClose = () => {
setSelectedPresetId(null)
const url = new URL(window.location.href)
url.searchParams.delete("preset")
router.replace(url.toString(), { scroll: false })
}
const handleDeletePreset = async (presetId: string) => {
try {
const res = await fetch(`/api/performance/${presetId}/user-delete`, {
method: "DELETE",
headers: { "Content-Type": "application/json" },
})
if (res.ok) {
setSelectedPresetId(null)
router.refresh()
}
} catch (err) {
console.error("Failed to delete preset:", err)
}
}
const handleReportPreset = async (presetId: string, reason: "inaccurate" | "spam" | "inappropriate" | "other", details?: string) => {
try {
const res = await fetch(`/api/performance/${presetId}/report`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ reason, details }),
})
if (res.ok) {
setReportedPresets((prev) => new Set(prev).add(presetId))
}
} catch (err) {
console.error("Failed to report preset:", err)
}
}
const handleImgError = useCallback(() => setImgError(true), [])
// Fetch stats on mount
useEffect(() => {
let cancelled = false
async function fetchStats() {
setLoading(true)
try {
const res = await fetch(`/api/games/${gameId}/stats`)
if (!res.ok) throw new Error(`HTTP ${res.status}`)
const data = await res.json()
if (!cancelled) setStats(data)
} catch (err) {
console.error("Failed to fetch stats:", err)
} finally {
if (!cancelled) setLoading(false)
}
}
fetchStats()
return () => {
cancelled = true
}
}, [gameId])
// Initialize selected devices when stats load
const didInitDevices = useRef(false)
useEffect(() => {
if (stats && !didInitDevices.current) {
didInitDevices.current = true
const slugs = stats.deviceBreakdown.map((d) => d.hardwareSlug)
queueMicrotask(() => setSelectedDevices(slugs))
}
}, [stats])
// Filtered stats
const filteredStats = useMemo(() => {
if (!stats) return null
const deviceSet = new Set(selectedDevices)
const filterByDevice = (arr: T[] | undefined) =>
arr?.filter((item) => deviceSet.has(item.hardwareSlug)) ?? []
const filterUpscaler = (arr: StatsResponse["upscalerStats"] | undefined) =>
(arr ?? []).filter((item) => {
if (!deviceSet.has(item.hardwareSlug)) return false
if (filters.upscaler !== "all" && item.upscalerType !== filters.upscaler)
return false
if (
filters.frameGen !== "all" &&
item.frameGenMethod !== filters.frameGen
)
return false
return true
})
return {
...stats,
boxplot: filterByDevice(stats.boxplot),
historical: (stats.historical ?? [])
.map((h) => ({
...h,
entries: h.entries.filter((e) =>
deviceSet.has(e.hardwareSlug),
),
}))
.filter((h) => h.entries.length > 0),
upscalerStats: filterUpscaler(stats.upscalerStats),
fpsRange: filterByDevice(stats.fpsRange),
deviceBreakdown: filterByDevice(stats.deviceBreakdown),
performanceTiers: filterByDevice(stats.performanceTiers),
stabilityScatter: filterByDevice(stats.stabilityScatter),
batteryLife: filterByDevice(stats.batteryLife),
}
}, [stats, selectedDevices, filters])
// Filtered presets
const filteredPresets = useMemo(() => {
return presets.filter((p) => {
if (
selectedDevices.length > 0 &&
!selectedDevices.includes(p.hardwareSlug)
)
return false
if (filters.proton !== "all" && p.protonVersion !== filters.proton)
return false
if (filters.os !== "all" && p.osVersion !== filters.os) return false
if (filters.upscaler !== "all" && p.upscalerType !== filters.upscaler)
return false
if (
filters.frameGen !== "all" &&
p.frameGenMethod !== filters.frameGen
)
return false
return true
})
}, [presets, selectedDevices, filters])
const pinnedPresets = useMemo(() => filteredPresets.filter((p) => p.isPinned), [filteredPresets])
const regularPresets = useMemo(() => filteredPresets.filter((p) => !p.isPinned), [filteredPresets])
const coverImage = game.capsuleImage || game.headerImage
const protonDbUrl = game.steamAppId
? `https://www.protondb.com/app/${game.steamAppId}`
: null
const steamDbUrl = game.steamAppId
? `https://steamdb.info/app/${game.steamAppId}`
: null
const toggleDevice = useCallback((slug: string) => {
setSelectedDevices((prev) =>
prev.includes(slug)
? prev.filter((s) => s !== slug)
: [...prev, slug],
)
}, [])
return (
<>
{/* Section 1: Hero Header */}
{/* Mobile hero — full-bleed background */}
{coverImage && !imgError ? (
) : (
)}
{/* Desktop hero — side-by-side layout */}
{/* Cover image */}
{coverImage && !imgError ? (
) : (
)}
{/* Info */}
{/* Section 2: Overview */}
{/* Description */}
About
{game.description ? (
{game.description}
) : (
No description available.
)}
{game.systemRequirements && (game.systemRequirements.minimum || game.systemRequirements.recommended) && (
{showSystemReq && (
{game.systemRequirements.minimum && (
)}
{game.systemRequirements.recommended && (
)}
)}
)}
{/* Platforms */}
{game.platforms && (
)}
{platformSupport.length > 0 && (
Platform Support
{platformSupport.map((ps) => (
{ps.hardwareSlug.replace(
/-/g,
" ",
)}
{ps.isSupported
? "Supported"
: "Unsupported"}
{ps.protonStatus}
{/* Per-device playability */}
{ps.playabilityStatus && (
)}
{/* Per-device anti-cheat */}
{ps.antiCheatRelevant && (
)}
))}
)}
{/* Section 3: Device Selector + Filters */}
{/* Device ribbon */}
{stats && stats.deviceBreakdown.length > 0 && (
{stats.deviceBreakdown.map((device) => {
const active = selectedDevices.includes(
device.hardwareSlug,
)
return (
)
})}
)}
{/* Fine filters */}
setFilters((f) => ({ ...f, proton: v }))
}
/>
setFilters((f) => ({ ...f, os: v }))
}
/>
o.toLowerCase())}
onChange={(v) =>
setFilters((f) => ({ ...f, upscaler: v }))
}
/>
o.toLowerCase().replace(" ", "_"),
)}
onChange={(v) =>
setFilters((f) => ({ ...f, frameGen: v }))
}
/>
{/* Section 4a: Pinned Presets */}
{pinnedPresets.length > 0 && (
📌 Pinned Presets
{pinnedPresets.length} pinned
{pinnedPresets.map((preset) => {
const raw = isRawPerformerPreset(preset)
const fpsColor = getFpsColor(preset)
return (
handlePresetOpen(preset.id)}
className={`flex flex-col gap-3 p-4 rounded-xl border transition-colors cursor-pointer hover:border-primary/30 ${
raw
? "border-green-500/30 bg-green-500/5"
: "border-yellow-500/30 bg-yellow-500/5"
}`}
data-gamepad-focusable
>
{/* Header */}
{generatePresetName(preset)}
{raw && (
Raw
)}
{isPoorPerformancePreset(preset) && preset.fpsAvg !== null && (
⚠ Slow
)}
{preset.hardwareName}
{preset.youtubeVideoId && (
)}
{preset.upvotes}
{preset.downvotes}
{/* Settings count */}
{preset.settingsCount} settings
{/* FPS */}
{preset.fpsAvg !== null && (
{preset.fpsAvg}
{" "}
avg
{preset.fpsOnePercentLow !== null && (
{" "}
· {preset.fpsOnePercentLow} 1% low
)}
{preset.fpsLow !== null && preset.fpsHigh !== null && !preset.fpsOnePercentLow && (
{" "}
({preset.fpsLow}–{preset.fpsHigh})
)}
)}
{/* Power / Battery quick-look */}
{(() => {
const dev = stats?.deviceBreakdown.find((d) => d.hardwareSlug === preset.hardwareSlug)
const isHandheld = dev?.deviceType === "handheld"
const wh = dev?.wattHours ?? null
const tdp = preset.tdpWatts ?? null
const estHours = wh && tdp && tdp > 0 ? wh / tdp : null
if (!isHandheld || (!tdp && !wh)) return null
return (
{tdp && ⚡ {Math.round(tdp)}W}
{wh && 🔋 {Math.round(wh)}Wh}
{estHours !== null && (
⏱ ~{estHours.toFixed(1)}h
)}
)
})()}
{/* Technology tags */}
{preset.upscalerType &&
preset.upscalerType !==
"none" && (
{preset.upscalerType.toUpperCase()}
{preset.upscalerVersion ? ` ${preset.upscalerVersion}` : ""}
)}
{preset.frameGenMethod &&
preset.frameGenMethod !==
"none" && (
{preset.frameGenMethod ===
"fsr_fg"
? "FSR FG"
: preset.frameGenMethod ===
"dlss_fg"
? "DLSS FG"
: preset.frameGenMethod}
)}
{/* Proton + OS */}
{preset.protonVersion && (
Proton{" "}
{preset.protonVersion}
)}
{preset.osVersion && (
{preset.osVersion}
)}
)
})}
)}
{/* Section 4b: Community Presets */}
Community Presets
{regularPresets.length} preset
{regularPresets.length !== 1 ? "s" : ""}
{regularPresets.length === 0 ? (
No presets match the selected filters
) : (
{regularPresets.map((preset) => {
const raw = isRawPerformerPreset(preset)
const fpsColor = getFpsColor(preset)
return (
handlePresetOpen(preset.id)}
className={`flex flex-col gap-3 p-4 rounded-xl border transition-colors cursor-pointer hover:border-primary/30 ${
raw
? "border-green-500/30 bg-green-500/5"
: "border-border bg-text/3"
}`}
data-gamepad-focusable
>
{/* Header */}
{generatePresetName(preset)}
{raw && (
Raw
)}
{isPoorPerformancePreset(preset) && preset.fpsAvg !== null && (
⚠ Slow
)}
{preset.hardwareName}
{preset.youtubeVideoId && (
)}
{preset.upvotes}
{preset.downvotes}
{/* Settings count */}
{preset.settingsCount} settings
{/* FPS */}
{preset.fpsAvg !== null && (
{preset.fpsAvg}
{" "}
avg
{preset.fpsOnePercentLow !== null && (
{" "}
· {preset.fpsOnePercentLow} 1% low
)}
{preset.fpsLow !== null && preset.fpsHigh !== null && !preset.fpsOnePercentLow && (
{" "}
({preset.fpsLow}–{preset.fpsHigh})
)}
)}
{/* Power / Battery quick-look */}
{(() => {
const dev = stats?.deviceBreakdown.find((d) => d.hardwareSlug === preset.hardwareSlug)
const isHandheld = dev?.deviceType === "handheld"
const wh = dev?.wattHours ?? null
const tdp = preset.tdpWatts ?? null
const estHours = wh && tdp && tdp > 0 ? wh / tdp : null
if (!isHandheld || (!tdp && !wh)) return null
return (
{tdp && ⚡ {Math.round(tdp)}W}
{wh && 🔋 {Math.round(wh)}Wh}
{estHours !== null && (
⏱ ~{estHours.toFixed(1)}h
)}
)
})()}
{/* Technology tags */}
{preset.upscalerType &&
preset.upscalerType !==
"none" && (
{preset.upscalerType.toUpperCase()}
{preset.upscalerVersion ? ` ${preset.upscalerVersion}` : ""}
)}
{preset.frameGenMethod &&
preset.frameGenMethod !==
"none" && (
{preset.frameGenMethod ===
"fsr_fg"
? "FSR FG"
: preset.frameGenMethod ===
"dlss_fg"
? "DLSS FG"
: preset.frameGenMethod}
)}
{/* Proton + OS */}
{preset.protonVersion && (
Proton{" "}
{preset.protonVersion}
)}
{preset.osVersion && (
{preset.osVersion}
)}
)
})}
)}
{/* Section 5: Statistics Dashboard */}
{/* Row 1 — Featured */}
{filteredStats && (
Historical Performance
{filteredStats.historical.length > 0 ? (
) : (
No historical data
)}
{filteredStats.summary.avgStability !== null && (
)}
)}
{/* Loading state */}
{loading && (
Loading statistics...
)}
{/* Row 2 — Upscaler Bar */}
{filteredStats &&
filteredStats.upscalerStats.length > 0 && (
Upscaler Performance
)}
{/* Row 3 — 3-column grid */}
{filteredStats && (
{filteredStats.boxplot.length > 0 && (
FPS Distribution
)}
{filteredStats.fpsRange.length > 0 && (
FPS Range
)}
{filteredStats.deviceBreakdown.length > 0 && (
Device Breakdown
)}
)}
{/* Row 4 — Performance Tiers + Stability Scatter */}
{filteredStats && (
{filteredStats.performanceTiers.length > 0 && (
)}
{filteredStats.stabilityScatter.length > 0 && (
Avg FPS vs 1% Low (Stability)
[d.hardwareSlug, d.hardwareName]))}
/>
)}
)}
{/* Battery Life Estimates */}
{filteredStats && filteredStats.batteryLife && filteredStats.batteryLife.length > 0 && (
Battery Life Estimates
[d.hardwareSlug, d.hardwareName]))}
/>
)}
{/* Steam Reviews */}
{game.steamAppId && (
)}
{/* Section 6: Comments */}
{/* Mobile FAB for Add Benchmark */}
{session && (
)}
{selectedPresetId && (() => {
const preset = filteredPresets.find((p) => p.id === selectedPresetId)
if (!preset) return null
return (
)
})()}
>
)
}
function Badge({
icon: Icon,
value,
label,
}: {
icon: React.ElementType
value: number
label: string
}) {
return (
{value} {label}
)
}
function StatCard({
label,
value,
icon: Icon,
}: {
label: string
value: string
icon: React.ElementType
}) {
return (
)
}
function FilterSelect({
label,
value,
options,
onChange,
}: {
label: string
value: string
options: string[]
onChange: (value: string) => void
}) {
return (
)
}