Add per-site READMEs describing what each site serves
Covers pages, data sources, config knobs, files and deploy target for 40k-rankings, aos-rankings, events, kingmaker and scouting. Also lists events in the root README layout and points to the per-site docs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -15,12 +15,16 @@ shared/
|
|||||||
scripts/
|
scripts/
|
||||||
render-header.mjs # shared/header.template.html + sites/<site>/nav.html -> sites/<site>/public/header.html
|
render-header.mjs # shared/header.template.html + sites/<site>/nav.html -> sites/<site>/public/header.html
|
||||||
sites/
|
sites/
|
||||||
40k-rankings/
|
40k-rankings/ # 40K team leaderboard (ITC, LMC, Stat Check ELO)
|
||||||
aos-rankings/
|
aos-rankings/ # Age of Sigmar team leaderboard (ITC)
|
||||||
kingmaker/
|
events/ # upcoming events across followed BCP circuits
|
||||||
scouting/
|
kingmaker/ # Kingmaker league hub (Google Forms/Sheets)
|
||||||
|
scouting/ # BCP event roster scout + matchup matrix generator
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Each site has its own `README.md` describing what it serves, where its data
|
||||||
|
comes from and what to edit.
|
||||||
|
|
||||||
Each `sites/<name>/` is a full Vite project (own `package.json`, `vite.config.js`,
|
Each `sites/<name>/` is a full Vite project (own `package.json`, `vite.config.js`,
|
||||||
`src/`, `index.html`). Two things are *not* duplicated per site:
|
`src/`, `index.html`). Two things are *not* duplicated per site:
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
# 40k-rankings.gateway-gamers.net
|
||||||
|
|
||||||
|
A Warhammer 40,000 leaderboard for Gateway Gamers team members, ranked by ITC
|
||||||
|
points in the Warhammer Global Rankings 2026 league on Best Coast Pairings (BCP).
|
||||||
|
|
||||||
|
## What it serves
|
||||||
|
|
||||||
|
A single page (`index.html`) with:
|
||||||
|
|
||||||
|
- **Team Leader card** showing the top-ranked member's name, ITC points, win
|
||||||
|
rate and global ITC rank.
|
||||||
|
- **Stat strip** with players placed, top ITC points, combined team win % and
|
||||||
|
the best league rank held by any member.
|
||||||
|
- **Sortable leaderboard**, one row per team member who has placed in the
|
||||||
|
league. Each row shows:
|
||||||
|
- ITC points and league rank
|
||||||
|
- Lord Marshal Conference (LMC) points and rank
|
||||||
|
- W–L–T record and win % for the league, with career totals underneath
|
||||||
|
- Stat Check ELO rating
|
||||||
|
|
||||||
|
The page reads from BCP in the viewer's browser on every load. It caches the
|
||||||
|
last result in `localStorage` (`gg_board_v3`) and shows that straight away
|
||||||
|
while the live data arrives. The **Refresh** button fetches again.
|
||||||
|
|
||||||
|
## Data sources
|
||||||
|
|
||||||
|
| Data | Source |
|
||||||
|
| --- | --- |
|
||||||
|
| Team roster | BCP `teams/{teamId}` |
|
||||||
|
| ITC points, rank, record | BCP `placings` for `leagueId`, filtered to `regionId` |
|
||||||
|
| LMC points and rank | BCP `placings` for `lmcLeagueId` (2026 Lord Marshal Conference) |
|
||||||
|
| Career W–L–T | BCP `placings?userId=…` for each member |
|
||||||
|
| ELO | `public/elo-data.json`, served from this site |
|
||||||
|
|
||||||
|
All BCP calls go to `https://newprod-api.bestcoastpairings.com/v1` without
|
||||||
|
authentication.
|
||||||
|
|
||||||
|
`elo-data.json` is built from Stat Check's public ELO spreadsheet
|
||||||
|
(https://www.stat-check.com/elo) by the scraper in
|
||||||
|
[`statcheck-elo-scraper/`](statcheck-elo-scraper/README.md). The root
|
||||||
|
`refresh-elo.yml` workflow runs it weekly and commits the result, and that
|
||||||
|
commit triggers a redeploy.
|
||||||
|
|
||||||
|
ELO is matched by player name. When several Stat Check players share a name,
|
||||||
|
the page picks the one whose "Last Event" appears in that member's BCP event
|
||||||
|
history. If it can't narrow it down to one, it shows "—" rather than guess.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Everything you'd normally change is in the `CONFIG` block at the top of
|
||||||
|
`src/main.js`:
|
||||||
|
|
||||||
|
- `teamId`: the BCP team whose members are shown
|
||||||
|
- `leagueId` / `regionId`: which ITC league and region to rank against
|
||||||
|
- `lmcLeagueId`: the Lord Marshal Conference league used for the LMC columns
|
||||||
|
- `blacklistNames`: players to hide from the board (full name or nickname,
|
||||||
|
case-insensitive)
|
||||||
|
- `memberIds`: fallback roster, used if the team endpoint ever stops returning
|
||||||
|
members without auth
|
||||||
|
- `useSample`: set to `true` to render placeholder rows with no network calls
|
||||||
|
|
||||||
|
## Files
|
||||||
|
|
||||||
|
- `index.html`, `src/main.js`, `src/style.css`: the page
|
||||||
|
- `nav.html`: this site's header nav entries (see the root README)
|
||||||
|
- `public/elo-data.json`: generated ELO data. Don't edit it by hand; the
|
||||||
|
weekly sync overwrites it.
|
||||||
|
- `statcheck-elo-scraper/`: builds `elo-data.json`
|
||||||
|
- `40k-rankings.gateway-gamers.net.conf`: reference nginx config for the live
|
||||||
|
server block
|
||||||
|
- `gateway_gamers_leaderboard.html`: an older standalone version of the page
|
||||||
|
from before the Vite rewrite. It isn't part of the build.
|
||||||
|
- `.github/workflows/refresh-elo.yml`: the old GitHub Actions ELO job from
|
||||||
|
before this site moved into the monorepo. It doesn't run (Gitea only reads
|
||||||
|
workflows from the repo root); the live job is `.gitea/workflows/refresh-elo.yml`.
|
||||||
|
|
||||||
|
## Deploy
|
||||||
|
|
||||||
|
`.gitea/workflows/deploy-40k-rankings.yml` builds the site and rsyncs `dist/`
|
||||||
|
to `/var/www/domains/gateway-gamers.net/40k-rankings/` on every push to `main`
|
||||||
|
that touches `sites/40k-rankings/` or `shared/`. See the root README for local
|
||||||
|
dev commands and the shared header, footer and images.
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
# aos-rankings.gateway-gamers.net
|
||||||
|
|
||||||
|
A Warhammer Age of Sigmar leaderboard for Gateway Gamers team members, ranked
|
||||||
|
by ITC points in an AoS league on Best Coast Pairings (BCP). It's a slimmer
|
||||||
|
copy of [40k-rankings](../40k-rankings/README.md), without the Lord Marshal
|
||||||
|
Conference and ELO columns.
|
||||||
|
|
||||||
|
## What it serves
|
||||||
|
|
||||||
|
A single page (`index.html`) with:
|
||||||
|
|
||||||
|
- **Team Leader card** showing the top-ranked member's name, ITC points, win
|
||||||
|
rate and global ITC rank.
|
||||||
|
- **Stat strip** with players placed, top ITC points, combined team win % and
|
||||||
|
the best league rank held by any member.
|
||||||
|
- **Sortable leaderboard**, one row per team member who has placed in the
|
||||||
|
league. Each row shows ITC points, W–L–T record and win % (with career
|
||||||
|
totals underneath) and ITC rank.
|
||||||
|
|
||||||
|
The page reads from BCP in the viewer's browser on every load. It caches the
|
||||||
|
last result in `localStorage` (`gg_aos_board_v1`) and shows that straight away
|
||||||
|
while the live data arrives. The **Refresh** button fetches again.
|
||||||
|
|
||||||
|
## Data sources
|
||||||
|
|
||||||
|
| Data | Source |
|
||||||
|
| --- | --- |
|
||||||
|
| Team roster | BCP `teams/{teamId}` |
|
||||||
|
| ITC points, rank, record | BCP `placings` for `leagueId`, filtered to `regionId` |
|
||||||
|
| Career W–L–T | BCP `placings?userId=…` for each member |
|
||||||
|
|
||||||
|
All calls go to `https://newprod-api.bestcoastpairings.com/v1` without
|
||||||
|
authentication. Nothing is stored on the server.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Everything you'd normally change is in the `CONFIG` block at the top of
|
||||||
|
`src/main.js`:
|
||||||
|
|
||||||
|
- `teamId`: the BCP team whose members are shown (the same team as 40k-rankings)
|
||||||
|
- `leagueId` / `regionId`: which AoS league and region to rank against
|
||||||
|
- `memberIds`: fallback roster, used if the team endpoint ever stops returning
|
||||||
|
members without auth
|
||||||
|
- `useSample`: set to `true` to render placeholder rows with no network calls
|
||||||
|
|
||||||
|
## Files
|
||||||
|
|
||||||
|
- `index.html`, `src/main.js`, `src/style.css`: the page
|
||||||
|
- `nav.html`: this site's header nav entries (see the root README)
|
||||||
|
- `aos-rankings.gateway-gamers.net.conf`: reference nginx config for the live
|
||||||
|
server block
|
||||||
|
|
||||||
|
## Deploy
|
||||||
|
|
||||||
|
`.gitea/workflows/deploy-aos-rankings.yml` builds the site and rsyncs `dist/`
|
||||||
|
to `/var/www/domains/gateway-gamers.net/aos-rankings/` on every push to `main`
|
||||||
|
that touches `sites/aos-rankings/` or `shared/`. See the root README for local
|
||||||
|
dev commands and the shared header, footer and images.
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
# events.gateway-gamers.net
|
||||||
|
|
||||||
|
A single feed of upcoming Warhammer events from the Best Coast Pairings (BCP)
|
||||||
|
circuits Gateway Gamers follows, soonest first.
|
||||||
|
|
||||||
|
## What it serves
|
||||||
|
|
||||||
|
A single page (`index.html`) listing every upcoming event across the configured
|
||||||
|
circuits. Each event card shows:
|
||||||
|
|
||||||
|
- event name and venue, plus distance from St. Louis ("Local" if under 30 miles)
|
||||||
|
- date range, shown in the event's own timezone
|
||||||
|
- badges for:
|
||||||
|
- format: Singles or Team Event
|
||||||
|
- scope: RTT or GT
|
||||||
|
- every followed circuit the event belongs to
|
||||||
|
- game system
|
||||||
|
- checked-in count against tickets
|
||||||
|
|
||||||
|
Each card links to the event on bestcoastpairings.com.
|
||||||
|
|
||||||
|
Filter buttons at the top toggle **Format**, **Scope** and **Circuit**. Within
|
||||||
|
a group they combine with OR, and across groups with AND.
|
||||||
|
|
||||||
|
Some of the rules behind the badges and filters:
|
||||||
|
|
||||||
|
- **Scope** is worked out from duration: an event that starts and ends on the
|
||||||
|
same local calendar day is an RTT, anything longer is a GT. BCP has no
|
||||||
|
reliable field for this. Team events get no scope badge.
|
||||||
|
- **Duplicate events** are merged. An event that belongs to several followed
|
||||||
|
circuits appears once, with a badge for each circuit, including circuits
|
||||||
|
listed only in the event's own `leagues` data.
|
||||||
|
- **Past events** are skipped. Paging stops as soon as it reaches events that
|
||||||
|
have already ended, so a circuit's full history is never downloaded.
|
||||||
|
|
||||||
|
## Data sources
|
||||||
|
|
||||||
|
BCP `events?leagueId=…` for each configured circuit, at
|
||||||
|
`https://newprod-api.bestcoastpairings.com/v1`, without authentication. All
|
||||||
|
requests come from the viewer's browser; nothing is stored on the server.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Edit `CONFIG.circuits` at the top of `src/main.js`. Each entry takes:
|
||||||
|
|
||||||
|
- `id`: the circuit's leagueId, from its BCP calendar URL
|
||||||
|
(`bestcoastpairings.com/circuit/<id>/calendar`)
|
||||||
|
- `name`: the label used on the filter button and badges
|
||||||
|
- `near` (optional): `{ lat, lon, miles }` to include only events within that
|
||||||
|
radius. Events without coordinates are left out.
|
||||||
|
|
||||||
|
The circuit filter buttons are generated from this list. Currently followed:
|
||||||
|
|
||||||
|
- Away Games 2025/26 ITC Season
|
||||||
|
- National Tabletop League (NTL) 8-Player Circuit
|
||||||
|
- TEO 2026 Circuit
|
||||||
|
- 2026 Lord Marshal Conference
|
||||||
|
- 2027 Lord Marshal Conference
|
||||||
|
- Warhammer Global Rankings 2026, limited to events within 250 miles of
|
||||||
|
St. Louis
|
||||||
|
|
||||||
|
## Files
|
||||||
|
|
||||||
|
- `index.html`, `src/main.js`, `src/style.css`: the page
|
||||||
|
- `nav.html`: this site's header nav entries (see the root README)
|
||||||
|
- `events.gateway-gamers.net.conf`: reference nginx config for the live server
|
||||||
|
block. It sets a Content-Security-Policy, so calling a new external API from
|
||||||
|
the page means adding that host to `connect-src` there too.
|
||||||
|
|
||||||
|
## Deploy
|
||||||
|
|
||||||
|
`.gitea/workflows/deploy-events.yml` builds the site and rsyncs `dist/` to
|
||||||
|
`/var/www/domains/gateway-gamers.net/events/` on every push to `main` that
|
||||||
|
touches `sites/events/` or `shared/`. See the root README for local dev
|
||||||
|
commands and the shared header, footer and images.
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
# kingmaker.gateway-gamers.net
|
||||||
|
|
||||||
|
The hub for Kingmaker, a local Warhammer 40k league campaign split into
|
||||||
|
divisions by skill level. The site is a thin shell around Google Forms and a
|
||||||
|
Google Sheet; it has no data or logic of its own.
|
||||||
|
|
||||||
|
## What it serves
|
||||||
|
|
||||||
|
| Page | Contents |
|
||||||
|
| --- | --- |
|
||||||
|
| `index.html` | Campaign home: current season and dates, sign-up status, how divisions work, quick links |
|
||||||
|
| `signups.html` | Embedded Google Form for joining, plus a note about paying the entry fee at Game Nite |
|
||||||
|
| `reporting.html` | Embedded Google Form for reporting game results |
|
||||||
|
| `standings.html` | Embedded published Google Sheet with campaign standings |
|
||||||
|
|
||||||
|
Each page is a separate Vite entry point, listed under `build.rollupOptions.input`
|
||||||
|
in `vite.config.js`.
|
||||||
|
|
||||||
|
## Data sources
|
||||||
|
|
||||||
|
Everything is in Google, owned by the league organizer:
|
||||||
|
|
||||||
|
- Sign-ups and game reports go into their Google Forms' response sheets.
|
||||||
|
- Standings are a published-to-web tab of a Google Sheet.
|
||||||
|
|
||||||
|
To change a form or the standings, edit it in Google. To point the site at a
|
||||||
|
different form or sheet, replace the iframe `src` in the relevant HTML file.
|
||||||
|
|
||||||
|
## Updating each season
|
||||||
|
|
||||||
|
Season name, dates and sign-up status are hardcoded in `index.html`. The entry
|
||||||
|
fee note is in `signups.html`. There's also a commented-out PayPal block there
|
||||||
|
that can be re-enabled if fees move back to PayPal.
|
||||||
|
|
||||||
|
## Files
|
||||||
|
|
||||||
|
- `index.html`, `signups.html`, `reporting.html`, `standings.html`: the pages
|
||||||
|
- `src/main.js`: loads the shared header and footer only
|
||||||
|
- `src/style.css`: Tailwind and daisyUI entry
|
||||||
|
- `nav.html`: this site's header nav entries (see the root README)
|
||||||
|
- `kingmaker.gateway-gamers.net.conf`: reference nginx config for the live
|
||||||
|
server block. It sets a Content-Security-Policy, so embedding anything from a
|
||||||
|
new host means adding it to `frame-src` there too.
|
||||||
|
- `CLAUDE.md`: notes for Claude Code on the Vite, Tailwind and daisyUI setup
|
||||||
|
|
||||||
|
## Deploy
|
||||||
|
|
||||||
|
`.gitea/workflows/deploy-kingmaker.yml` builds the site and rsyncs `dist/` to
|
||||||
|
`/var/www/domains/gateway-gamers.net/kingmaker/` on every push to `main` that
|
||||||
|
touches `sites/kingmaker/` or `shared/`. See the root README for local dev
|
||||||
|
commands and the shared header, footer and images.
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
# scouting.gateway-gamers.net
|
||||||
|
|
||||||
|
**BCP Roster Scout**: paste a Best Coast Pairings (BCP) event link and get
|
||||||
|
instant roster stats for that event. For team events, it can also generate a
|
||||||
|
pre-filled Google Sheets matchup matrix for each opposing team.
|
||||||
|
|
||||||
|
## What it serves
|
||||||
|
|
||||||
|
A single page (`index.html`). Enter an event URL or ID (it's also read from
|
||||||
|
`?event=<id>`, so scouted events can be linked) and the page shows:
|
||||||
|
|
||||||
|
- **Summary**: event name and dates, player and team counts, checked-in rate,
|
||||||
|
lists submitted.
|
||||||
|
- **Insights and faction distribution**: faction spread, with each faction's
|
||||||
|
most-used dispositions (Purge the Foe, Take and Hold and so on).
|
||||||
|
- **Roster**, with ITC rank (Warhammer Global Rankings 2026) and career win %
|
||||||
|
for every player:
|
||||||
|
- team events: grouped by team, with a search box
|
||||||
|
- singles events: a flat player table
|
||||||
|
- **Generate Matrix** (team events): pick your team and the page:
|
||||||
|
1. copies the team's Google Sheets matchup-matrix template once per opposing
|
||||||
|
team
|
||||||
|
2. fills in each opponent's name, faction and career win %, plus their army
|
||||||
|
list cleaned up by the vendored 40k Compactor
|
||||||
|
3. links you to the new spreadsheet
|
||||||
|
|
||||||
|
## Data sources
|
||||||
|
|
||||||
|
| Data | Source |
|
||||||
|
| --- | --- |
|
||||||
|
| Event, roster, team players | BCP `events/{id}` and related endpoints |
|
||||||
|
| ITC rank | BCP `placings` for the ITC league and region |
|
||||||
|
| Career W–L–T | BCP `placings?userId=…` for each player |
|
||||||
|
| Army lists | BCP `armylists/{id}` (needs a subscriber token) |
|
||||||
|
| Matrix | Google Drive and Sheets APIs, as the signed-in viewer |
|
||||||
|
|
||||||
|
All requests come from the viewer's browser. There's no backend and nothing is
|
||||||
|
stored on the server.
|
||||||
|
|
||||||
|
### Optional BCP token
|
||||||
|
|
||||||
|
BCP often leaves faction or disposition blank on the roster. A viewer with a
|
||||||
|
BCP subscription can paste their BCP auth token; the page then fetches each
|
||||||
|
submitted list and pulls the missing faction and disposition out of it.
|
||||||
|
Disposition names are fuzzy-matched so typos don't create duplicate entries.
|
||||||
|
The same token is used to fetch lists for the matrix.
|
||||||
|
|
||||||
|
The token is kept only in that browser's `localStorage` (`bcp_auth_token`) and
|
||||||
|
only sent to BCP's own API. The page includes step-by-step instructions for
|
||||||
|
finding it. BCP tokens expire after about an hour.
|
||||||
|
|
||||||
|
### Google sign-in (matrix only)
|
||||||
|
|
||||||
|
Google Identity Services opens an OAuth popup and the viewer signs in with
|
||||||
|
their own account. It requests these scopes:
|
||||||
|
|
||||||
|
- `drive.readonly`: read the template so it can be copied
|
||||||
|
- `drive.file`: create the copies
|
||||||
|
- `spreadsheets`: write into the copies
|
||||||
|
|
||||||
|
The OAuth client ID and template file ID are constants at the top of
|
||||||
|
`src/matrix.js`.
|
||||||
|
|
||||||
|
## Files
|
||||||
|
|
||||||
|
- `index.html`, `src/main.js`, `src/style.css`: the page, BCP data loading,
|
||||||
|
stats and rendering
|
||||||
|
- `src/matrix.js`: Google sign-in, template copying and list cleaning for
|
||||||
|
**Generate Matrix**
|
||||||
|
- `src/vendor/40k-compactor/`: vendored copy of Desjani's 40k Compactor
|
||||||
|
(MIT), used to reformat army lists. It isn't on npm. See its `NOTICE.md`
|
||||||
|
for where it came from, the license and how to update it.
|
||||||
|
- `nav.html`: this site's header nav entries (see the root README)
|
||||||
|
- `scouting.gateway-gamers.net.conf`: reference nginx config for the live
|
||||||
|
server block. Its Content-Security-Policy allows BCP's API and Google's auth,
|
||||||
|
Drive and Sheets hosts. Calling a new external host from the page means
|
||||||
|
adding it there too.
|
||||||
|
|
||||||
|
## Deploy
|
||||||
|
|
||||||
|
`.gitea/workflows/deploy-scouting.yml` builds the site and rsyncs `dist/` to
|
||||||
|
`/var/www/domains/gateway-gamers.net/scouting/` on every push to `main` that
|
||||||
|
touches `sites/scouting/` or `shared/`. See the root README for local dev
|
||||||
|
commands and the shared header, footer and images.
|
||||||
Reference in New Issue
Block a user