PURPOSE
Dev-only performance analytics dashboard mounted at /dev/analytics. Visualizes telemetry pulled from Supabase to expose FPS trends, render-pass breakdowns, weapon/hull performance impact, spike frequency, and device/session health, segmented by version and by platform (mobile vs desktop). Uses the Supabase service_role key to bypass RLS and read every player’s telemetry; production builds and any environment without VITE_SUPABASE_SERVICE_KEY render a stub instead, so the inner component (and the key reference) is tree-shaken out of the production bundle.
OWNS
- Default export
PerfDashboardScreen— guard wrapper that gates onimport.meta.env.DEV && SERVICE_KEY. - Internal component
_PerfDashboardScreenInner— owns the dashboard’s React state:rangeDays,platform,allRuns,allSessions,allRenderPerfRuns,runsState,sessionsState,error. - Section components
FpsOverviewSection,BottleneckSection,GameplayImpactSection,DeviceHealthSection. - Inline presentational components
ChartCardandMetricCard, plus theTOOLTIP_STYLE/TOOLTIP_LABELconstants. - Local type declarations:
TelemetryRun,RenderPerfRun,TelemetrySession,LoadState,Platform. - Pure helpers:
dateGte,fetchSupabase,fetchRuns,fetchRenderPerf,fetchSessions,avg,round1,sortVersions,groupBy,classifyDevice,dayOf,fpsColor,filterRunsByPlatform,filterRenderPerfByPlatform,filterSessionsByPlatform. - Constants
SUPABASE_URL,SERVICE_KEY,DATE_RANGES,FPS_BUCKET_COLORS.
READS FROM
import.meta.env.VITE_SUPABASE_SERVICE_KEY— Supabase service-role key, read once at module load intoSERVICE_KEY. Empty string fallback when unset.import.meta.env.DEV— Vite static constant used by the guard wrapper.- Hardcoded
SUPABASE_URLconstant pointing at the project Supabase instance. - Supabase REST endpoints via
fetch:telemetry_runs(filtered:cause_of_death=neq.heartbeat,started_at=gte.<range>,order=started_at.desc,limit=500) — full run columns.telemetry_runsagain withrender_perf=not.is.nullandlimit=200— render-perf-only column subset, fetched on demand.telemetry_sessions(filtered:started_at=gte.<range>,order=started_at.desc,limit=500).
rechartsprimitivesBarChart,Bar,LineChart,Line,ScatterChart,Scatter,XAxis,YAxis,CartesianGrid,Tooltip,Legend,Cell,ResponsiveContainer.- React hooks
useState,useEffect,useMemo, and theReactNodetype.
PUSHES TO
- Component-local React state via
setRangeDays,setPlatform,setAllRuns,setAllSessions,setAllRenderPerfRuns,setRunsState,setSessionsState,setError. - The DOM: rendered chart cards, metric cards, platform/date toggles, loading/error views, and the
Back to Devlink (href="/dev"). console.errorchannel[perf-dashboard]when the on-demand render-perf fetch fails.- No writes to Zustand stores, no calls into the game engine, no Supabase writes.
DOES NOT
- Does not mutate any telemetry row — reads only, no
POST/PATCH/DELETE. - Does not authenticate users; relies on the service-role key being present in the local
.env.local. - Does not render anything sensitive in production: when
import.meta.env.DEVis false orSERVICE_KEYis empty it returns a static “Analytics only available in local dev” stub, and_PerfDashboardScreenInneris tree-shaken. - Does not import from the game engine, the metagame stores, the bridge layer, or the Supabase JS client. The fetch path is raw REST with
apikey+Authorization: Bearerheaders, matching the telemetry sender pattern. - Does not depend on routing context — the back link is a plain anchor, not a router navigation call.
- Does not persist UI state across reloads (range, platform, loaded-render-perf flag are component-local).
- Does not paginate beyond the hard
limit=500/limit=200ceilings on each query.
Signals
LoadStateunion ('idle' | 'loading' | 'ready' | 'error') tracks the runs and sessions fetches independently.- Per-section memoization signals:
versions,byVersion,avgByVersion,histogramData,dailyData,spikeData,causeData,passData,weaponData,hullData,durationScatter,killScatter,screenData— all derived from filteredruns/sessions/renderPerfRunsviauseMemo. - Platform filter signal:
perf_flags.isMobiledecides mobile vs desktop for run and render-perf records;classifyDevice(device_ua)(Mobile/Tablet/Desktop) decides for sessions, with Mobile and Tablet both counted as mobile. - FPS classification thresholds for color: green at >=50, yellow at >=30, red below 30 (
fpsColor). - FPS histogram bucket palette:
0-15red,15-30orange,30-45yellow,45-60light-green,60+green (FPS_BUCKET_COLORS). - Date-range options: 1d / 7d / 30d / all-time (
DATE_RANGES);dateGte(0)returns1970-01-01T00:00:00Z. - Aggregation min-sample gate: weapon and hull groupings require >=3 runs before they appear.
- Render-pass display gate: passes with
avgMs <= 0.1are filtered out. - Survived-vs-died bucketing: any
cause_of_deathother than'survived'is treated as'died'.
Entry points
- Default export
PerfDashboardScreenis the only consumer-facing entry. - Wired into the metagame routing layer at the
/dev/analyticspath (the screen header includes a← Back to Devanchor to/dev). - Mount sequence: guard wrapper checks
import.meta.env.DEV && SERVICE_KEY→_PerfDashboardScreenInnermounts →useEffectkeyed onrangeDayskicks offPromise.all([fetchRuns, fetchSessions])→ sections render oncerunsState === 'ready'. - The “Load Render Perf Data” button inside
BottleneckSectioninvokesloadRenderPerf, which firesfetchRenderPerf(rangeDays)and stores the result intoallRenderPerfRuns. - Changing the platform toggle or the date-range buttons updates state; range changes trigger a refetch (and clear
allRenderPerfRuns), platform changes only recompute the memoized filtered views.
Pattern notes
- Two-component guard pattern:
PerfDashboardScreen(default export) renders the stub or the inner component. All hooks live in_PerfDashboardScreenInnerso React never sees a hook count mismatch between the two branches. - Production safety relies on Vite tree-shaking the inner component when
import.meta.env.DEVis the staticfalseconstant andSERVICE_KEYis''. The comments in the file call this out explicitly because shippingservice_roleto the browser would be catastrophic. - Inline styling: every visual is a literal
style={{ ... }}object — no CSS modules, no styled-components, no shared theme tokens beyond the localTOOLTIP_STYLE/TOOLTIP_LABELconstants and theFPS_BUCKET_COLORStable. - All data shaping happens in
useMemoblocks colocated with the section that consumes them; raw fetched arrays live at the top level and are filtered by platform once before being passed down. - Charts use
rechartsResponsiveContainerwith fixed heights (mostly 280, orMath.max(280, rows * 28)for vertical bar charts) so they reflow to the parent grid columns. - Fetch helpers use a single shared
fetchSupabase<T>that throws on non-OK responses with the status code and body text — surfaced to the user via the inline error banner with a Retry button. - The
useEffectfetch uses acancelledflag to avoid setting state after unmount or after a stale range change. - Memo dependency hygiene: every memoized derivation lists its inputs explicitly, and the platform-filtered
runs,sessions,renderPerfRunsare themselves memoized to keep section memos stable when only unrelated state changes. - Display rules: numbers shown in the UI are rounded to one decimal via
round1; days are sliced toMM-DDfor the daily-FPS line chart x-axis. - Aesthetic palette: dark navy background (
#0a0e18), card surfaces#111128with#2a2a4aborders, accent#44ffccfor primary headings and active controls, mobile platform colored orange#ff8844and desktop colored blue#4488ff.