diff --git a/README.md b/README.md index d1e1f31..c2c5dca 100644 --- a/README.md +++ b/README.md @@ -15,12 +15,16 @@ shared/ scripts/ render-header.mjs # shared/header.template.html + sites//nav.html -> sites//public/header.html sites/ - 40k-rankings/ - aos-rankings/ - kingmaker/ - scouting/ + 40k-rankings/ # 40K team leaderboard (ITC, LMC, Stat Check ELO) + aos-rankings/ # Age of Sigmar team leaderboard (ITC) + events/ # upcoming events across followed BCP circuits + 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//` is a full Vite project (own `package.json`, `vite.config.js`, `src/`, `index.html`). Two things are *not* duplicated per site: diff --git a/sites/40k-rankings/README.md b/sites/40k-rankings/README.md index e69de29..1c588c2 100644 --- a/sites/40k-rankings/README.md +++ b/sites/40k-rankings/README.md @@ -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. diff --git a/sites/aos-rankings/README.md b/sites/aos-rankings/README.md new file mode 100644 index 0000000..0aba988 --- /dev/null +++ b/sites/aos-rankings/README.md @@ -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. diff --git a/sites/events/README.md b/sites/events/README.md new file mode 100644 index 0000000..2df6ff2 --- /dev/null +++ b/sites/events/README.md @@ -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//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. diff --git a/sites/kingmaker/README.md b/sites/kingmaker/README.md new file mode 100644 index 0000000..ec03f27 --- /dev/null +++ b/sites/kingmaker/README.md @@ -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. diff --git a/sites/scouting/README.md b/sites/scouting/README.md new file mode 100644 index 0000000..8c06e83 --- /dev/null +++ b/sites/scouting/README.md @@ -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=`, 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.