static: public Reverse Watch dashboard#71
Conversation
Single self-contained static/index.html (no build step) plus the icons / favicons / chart-event data it consumes. Layout - three KPI cards (Traders indexed, Traders flagged, Traders flagged 24h) - volume chart with period picker (7d / 30d / 3m / 6m / 1y), powered by uPlot from the unpkg CDN - recent-reversals table with client-side paging - single-Steam-ID search with avatar + display name + Steam/CSFloat profile link result chip - CS2 event annotation chips on the chart, data driven by static/cs2-events.json — edit that file to add or update events - responsive mobile layout (single-column, condensed KPI row) Dependencies - Material Symbols (Google Fonts CDN) - uPlot 1.6.x (unpkg CDN) - No bundler, no NPM dependencies, no build step. This PR depends on #1 (in-memory static serving) and #2 (public stats / recent endpoints) being merged for the dashboard to function, but is reviewable in isolation against master. Co-authored-by: Cursor <cursoragent@cursor.com>
|
If you want to make this code easier to maintain: there are lots of Static Site Generators (SSGs) to keep the build step super minimal. I can recommend:
Probably max. a 2-prompt addition to this PR. All of these can be configured to generate fully static HTMLs at build-time. |
There was a problem hiding this comment.
We can inline this as a const in the script block in our html file.
There was a problem hiding this comment.
Done — inlined as a CS2_EVENTS const in the script block; the JSON file is removed.
| // Dev placeholder: deterministic fake Steam display names derived | ||
| // from steam_id. Remove when real names ship (PRD §14 D-open-4). | ||
| const TRADER_ADJECTIVES = [ | ||
| 'Shadow', 'Golden', 'Crimson', 'Frozen', 'Stealth', 'Iron', 'Silent', | ||
| 'Quick', 'Rogue', 'Mighty', 'Phantom', 'Toxic', 'Wild', 'Lone', 'Lucky', | ||
| 'Cosmic', 'Royal', 'Savage', 'Mystic', 'Dark', 'Vivid', 'Solar', 'Lunar', | ||
| 'Electric', 'Neon', 'Cyber', 'Mecha', 'Ghost', 'Steel', 'Radiant', | ||
| ]; | ||
| const TRADER_NOUNS = [ | ||
| 'Wolf', 'Hawk', 'Tiger', 'Fox', 'Bear', 'Dragon', 'Phoenix', 'Viper', | ||
| 'Hunter', 'Sniper', 'Ranger', 'Knight', 'Reaper', 'Warden', 'Mage', | ||
| 'Trader', 'Ace', 'Bandit', 'Ninja', 'Samurai', 'Pirate', 'Demon', | ||
| 'Spirit', 'Storm', 'Blade', 'Shadow', 'Wraith', 'Beast', 'Falcon', 'Specter', | ||
| ]; | ||
| const TRADER_SUFFIXES = [ | ||
| '', '', '', '_HD', '47', '88', '99', '_TR', 'X', 'TV', '_GG', '_RU', 'Z', '03', '13', | ||
| ]; |
There was a problem hiding this comment.
Please remove all placeholder data. In general, this should not be committed.
There was a problem hiding this comment.
Done — removed the fake display-name generator entirely. The Trader column + chip now show the real Steam ID, and avatars are a neutral person glyph. Real display names are pending a source decision (the open Steam-name-source question). Done in 8a04e6b.
| # [reverse.watch](https://reverse.watch) | ||
|
|
||
| Community-driven open trade reversal tracking database for Steam. Participating entities can report trade reverals to the open database. | ||
| Community-driven open trade reversal tracking database for Steam. Participating entities can report trade reversals to the open database, and anyone can browse activity at [reverse.watch](https://reverse.watch) — a public dashboard served from [`static/index.html`](static/index.html) at `/`. |
There was a problem hiding this comment.
Please remove all changes, except for the spelling fix.
There was a problem hiding this comment.
Done — the README diff vs master is now just the reverals to reversals spelling fix.
This looks like a great idea. Missed this comment in my original review. We can use Astro, it's the lighter-weight option. We can use a FileServer which exposes certain directories This will require a Node build stage in the Dockerfile. fs := http.FileServer(http.Dir("dist"))
r.Handle("/static/*", fs)
r.Handle("/_astro/*", fs)
r.Get("/", func(w, r) { http.ServeFile(w, r, "dist/index.html") })Here's a rough outline of the structure: |
- Anchor the chart fill gradient to the plot area (u.bbox.top .. u.bbox.top + u.bbox.height) so the fade aligns with the plotted line instead of the canvas origin. - Keep Steam IDs as strings end-to-end: they are uint64 and exceed Number.MAX_SAFE_INTEGER, so coercing them through Number would corrupt the profile/stall links and table rows. The API already marshals steam_id as a JSON string; explicitly String() it at the chip links and recent-table rows so it is never parsed as a number. - Inline the CS2 event annotations as a CS2_EVENTS const in the script block, drop the runtime fetch of /static/cs2-events.json, and delete the now-unused static/cs2-events.json file. Co-authored-by: Cursor <cursoragent@cursor.com>
Replace the single self-contained static/index.html with an Astro project in web/ that builds to web/dist (gitignored). uPlot is now a pinned npm dependency bundled into the hashed _astro output instead of loaded from a CDN; Material Symbols stays on its existing web-font link. Markup is split into Hero/Search/Kpis/ReversalChart/RecentReversals/ SiteFooter components, the inline <style> moves to src/styles/global.css, and the inline <script> (including the CS2_EVENTS const and string-only steam_id handling) moves to src/scripts/app.ts. Go now serves web/dist via http.FileServer for /static/* and /_astro/* with / -> web/dist/index.html, replacing the old static/index.html serving. The Dockerfile gains a node build stage (npm ci && npm run build) whose web/dist is copied into the runtime image. The three PNG assets are relocated to web/public/static so their /static/* URLs are preserved. Co-authored-by: Cursor <cursoragent@cursor.com>
The summary endpoint now returns steam_ids_searched (backed by the new search_counts table) instead of traders_indexed. Co-authored-by: Cursor <cursoragent@cursor.com>
- Replace fabricated Steam display names and per-user avatar gradients with the real Steam ID; use a neutral person glyph avatar everywhere - Base64-inline the CSFloat logo and Steam/CSFloat icons and delete the static PNGs - Reduce README diff against master to only the "reversals" spelling fix - Offset chart event chips and hover tooltip by the plot-area inset so overlays align to the data across all period ranges Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 8a04e6b. Configure here.
| r.Handle("/_astro/*", fs) | ||
| r.Get("/", func(w http.ResponseWriter, r *http.Request) { | ||
| http.ServeFile(w, r, "static/index.html") | ||
| http.ServeFile(w, r, "web/dist/index.html") |
There was a problem hiding this comment.
Homepage missing without web build
High Severity
The root route now serves web/dist/index.html, but web/dist/ is a gitignored build output. This means the dashboard at / will be missing for local development and deployments that don't include the web build step.
Reviewed by Cursor Bugbot for commit 8a04e6b. Configure here.
|
Context for re-review: since the original round of comments, the frontend moved from a single |


Summary
Single self-contained `static/index.html` (inline CSS + JS, no build step) plus the icons / favicons / chart-event data it consumes.
Layout
Dependencies
Heads-up for review
Test plan
Made with Cursor
Note
Low Risk
Static HTML/JSON and CDN assets only; no server auth or data-model changes in this PR. Main follow-up is replacing placeholder Steam display names before production.
Overview
Replaces the minimal landing search page with a self-contained public dashboard in
static/index.html(inline CSS/JS, no build). The page now loads KPI stats, a uPlot reversal volume chart with a 7d–1y period picker, a recent reversals table with client-side “load more,” and an upgraded Steam ID lookup that shows a compact result chip (verdict, placeholder avatar/name, Steam/CSFloat links). CS2 timeline annotations come from newstatic/cs2-events.json, rendered as stacked chips on the chart for the selected window.README.mdfixes a typo and documents that the dashboard is served at/fromstatic/index.html. Trader display names are deterministic placeholders derived from Steam ID until real profile data exists; chart/table/search call the existing public/api/v1/...endpoints and Material Symbols + uPlot via CDN.Reviewed by Cursor Bugbot for commit 0865861. Bugbot is set up for automated code reviews on this repo. Configure here.