feat: add computed battery estimate to ReviewStep via hardware wattHours + tdpWatts (Tasks 4.1-4.3)

This commit is contained in:
2026-05-10 05:11:46 +08:00
parent 6d906f0bbf
commit c704f35f51
3 changed files with 35 additions and 4 deletions
+18 -4
View File
@@ -52,6 +52,8 @@ export function GameEntryWizard({ gameId, gameVersions, defaultVersionId, editEn
// Step 0: Setup — Hardware // Step 0: Setup — Hardware
const [hardwareSlug, setHardwareSlug] = useState(editEntry?.hardwareSlug ?? "") const [hardwareSlug, setHardwareSlug] = useState(editEntry?.hardwareSlug ?? "")
const [hardwareName, setHardwareName] = useState("") const [hardwareName, setHardwareName] = useState("")
const [hardwareWattHours, setHardwareWattHours] = useState<number | null>(null)
const [hardwareDeviceType, setHardwareDeviceType] = useState<string | null>(null)
// Step 0: Setup — Game Version // Step 0: Setup — Game Version
const [selectedVersionId, setSelectedVersionId] = useState(defaultVersionId) const [selectedVersionId, setSelectedVersionId] = useState(defaultVersionId)
@@ -134,14 +136,20 @@ export function GameEntryWizard({ gameId, gameVersions, defaultVersionId, editEn
setHardwareSlug(slug) setHardwareSlug(slug)
if (!slug) { if (!slug) {
setHardwareName("") setHardwareName("")
setHardwareWattHours(null)
setHardwareDeviceType(null)
return return
} }
try { try {
const res = await fetch("/api/performance/hardware") const res = await fetch("/api/performance/hardware")
if (res.ok) { 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) 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 { } catch {
// ignore // ignore
@@ -156,9 +164,13 @@ export function GameEntryWizard({ gameId, gameVersions, defaultVersionId, editEn
try { try {
const res = await fetch("/api/performance/hardware") const res = await fetch("/api/performance/hardware")
if (res.ok && !cancelled) { 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) 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 { } catch {
// ignore // ignore
@@ -402,6 +414,8 @@ export function GameEntryWizard({ gameId, gameVersions, defaultVersionId, editEn
data={{ data={{
hardwareSlug, hardwareSlug,
hardwareName, hardwareName,
hardwareWattHours,
hardwareDeviceType,
gameVersionLabel: getVersionLabel(), gameVersionLabel: getVersionLabel(),
antiCheat, antiCheat,
performance, performance,
+15
View File
@@ -12,6 +12,8 @@ import { UPSCALER_TYPE_OPTIONS, FRAME_GEN_OPTIONS } from "./environment-step"
export interface ReviewData { export interface ReviewData {
hardwareSlug: string hardwareSlug: string
hardwareName: string hardwareName: string
hardwareWattHours: number | null
hardwareDeviceType: string | null
gameVersionLabel: string gameVersionLabel: string
antiCheat: { antiCheat: {
antiCheatRelevant: boolean antiCheatRelevant: boolean
@@ -144,6 +146,19 @@ export function ReviewStep({
<SummaryRow label="Load Time SSD" value={formatNumber(performance.loadTimeSsd)} /> <SummaryRow label="Load Time SSD" value={formatNumber(performance.loadTimeSsd)} />
<SummaryRow label="Load Time SD" value={formatNumber(performance.loadTimeSd)} /> <SummaryRow label="Load Time SD" value={formatNumber(performance.loadTimeSd)} />
<SummaryRow label="TDP (Watts)" value={formatNumber(performance.tdpWatts)} /> <SummaryRow label="TDP (Watts)" value={formatNumber(performance.tdpWatts)} />
<SummaryRow
label="Est. Battery"
value={(() => {
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"
})()}
/>
</div> </div>
{/* Environment */} {/* Environment */}
+2
View File
@@ -51,6 +51,8 @@ export const performanceSubmitRoutes = new Elysia({ prefix: "/performance" })
slug: hardware.slug, slug: hardware.slug,
name: hardware.name, name: hardware.name,
deviceType: hardware.deviceType, deviceType: hardware.deviceType,
wattHours: hardware.wattHours,
tdpMax: hardware.tdpMax,
}) })
.from(hardware) .from(hardware)
.orderBy(hardware.sortOrder) .orderBy(hardware.sortOrder)