From c704f35f51d9510a2b5f3e70c93a1e0d24da114d Mon Sep 17 00:00:00 2001 From: Adrian Bonpin Date: Sun, 10 May 2026 05:11:46 +0800 Subject: [PATCH] feat: add computed battery estimate to ReviewStep via hardware wattHours + tdpWatts (Tasks 4.1-4.3) --- components/wizard/game-entry-wizard.tsx | 22 ++++++++++++++++++---- components/wizard/steps/review-step.tsx | 15 +++++++++++++++ lib/api/performance-submit.ts | 2 ++ 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/components/wizard/game-entry-wizard.tsx b/components/wizard/game-entry-wizard.tsx index 66e32d7..a1a0d89 100644 --- a/components/wizard/game-entry-wizard.tsx +++ b/components/wizard/game-entry-wizard.tsx @@ -52,6 +52,8 @@ export function GameEntryWizard({ gameId, gameVersions, defaultVersionId, editEn // Step 0: Setup — Hardware const [hardwareSlug, setHardwareSlug] = useState(editEntry?.hardwareSlug ?? "") const [hardwareName, setHardwareName] = useState("") + const [hardwareWattHours, setHardwareWattHours] = useState(null) + const [hardwareDeviceType, setHardwareDeviceType] = useState(null) // Step 0: Setup — Game Version const [selectedVersionId, setSelectedVersionId] = useState(defaultVersionId) @@ -134,14 +136,20 @@ export function GameEntryWizard({ gameId, gameVersions, defaultVersionId, editEn setHardwareSlug(slug) if (!slug) { setHardwareName("") + setHardwareWattHours(null) + setHardwareDeviceType(null) return } try { const res = await fetch("/api/performance/hardware") if (res.ok) { - const data = await res.json() as { data: Array<{ slug: string; name: string }> } + const data = await res.json() as { data: Array<{ slug: string; name: string; deviceType: string; wattHours: number | null; tdpMax: number | null }> } const device = data.data.find((d) => d.slug === slug) - if (device) setHardwareName(device.name) + if (device) { + setHardwareName(device.name) + setHardwareWattHours(device.wattHours ?? null) + setHardwareDeviceType(device.deviceType ?? null) + } } } catch { // ignore @@ -156,9 +164,13 @@ export function GameEntryWizard({ gameId, gameVersions, defaultVersionId, editEn try { const res = await fetch("/api/performance/hardware") if (res.ok && !cancelled) { - const data = await res.json() as { data: Array<{ slug: string; name: string }> } + const data = await res.json() as { data: Array<{ slug: string; name: string; deviceType: string; wattHours: number | null; tdpMax: number | null }> } const device = data.data.find((d) => d.slug === hardwareSlug) - if (device && !cancelled) setHardwareName(device.name) + if (device && !cancelled) { + setHardwareName(device.name) + setHardwareWattHours(device.wattHours ?? null) + setHardwareDeviceType(device.deviceType ?? null) + } } } catch { // ignore @@ -402,6 +414,8 @@ export function GameEntryWizard({ gameId, gameVersions, defaultVersionId, editEn data={{ hardwareSlug, hardwareName, + hardwareWattHours, + hardwareDeviceType, gameVersionLabel: getVersionLabel(), antiCheat, performance, diff --git a/components/wizard/steps/review-step.tsx b/components/wizard/steps/review-step.tsx index 121bee1..77753ee 100644 --- a/components/wizard/steps/review-step.tsx +++ b/components/wizard/steps/review-step.tsx @@ -12,6 +12,8 @@ import { UPSCALER_TYPE_OPTIONS, FRAME_GEN_OPTIONS } from "./environment-step" export interface ReviewData { hardwareSlug: string hardwareName: string + hardwareWattHours: number | null + hardwareDeviceType: string | null gameVersionLabel: string antiCheat: { antiCheatRelevant: boolean @@ -144,6 +146,19 @@ export function ReviewStep({ + { + const wh = data.hardwareWattHours + const tdp = performance.tdpWatts + if (wh && tdp && tdp > 0 && data.hardwareDeviceType === "handheld") { + const hours = wh / tdp + const mins = Math.round(hours * 60) + return `~${hours.toFixed(1)}h (${mins} min)` + } + return "Not available — requires TDP and a handheld device" + })()} + /> {/* Environment */} diff --git a/lib/api/performance-submit.ts b/lib/api/performance-submit.ts index b1a952c..6e55571 100644 --- a/lib/api/performance-submit.ts +++ b/lib/api/performance-submit.ts @@ -51,6 +51,8 @@ export const performanceSubmitRoutes = new Elysia({ prefix: "/performance" }) slug: hardware.slug, name: hardware.name, deviceType: hardware.deviceType, + wattHours: hardware.wattHours, + tdpMax: hardware.tdpMax, }) .from(hardware) .orderBy(hardware.sortOrder)