wfs-frontend

The website (and a barely started phone app) for World Fantasy Soccer. It is what players see: pick a team, make transfers, check leaderboards and mini-leagues, make predictions. Moderators and admins also use it to set up seasons and fix data.

Code: /home/rob/code/wfs-frontend ยท Last checked against commit 216b3e1 on 2026-10-14

WFS is its own game and is not related to Football Manager. Related pages: wfs-backend (the API this site calls), wfs-beta-frontend (an older copy).

Current status

The newest commits ("Close site", 29 July 2026) change the site so every page shows a footer banner saying "WFS has closed", with a link to a closing message at /closing-message. The message explains that WFS is shutting down after the World Cup, because running it takes too much of the owner's time. The rest of the app is still intact. Page code for the banner is in apps/web-vue/src/App.vue; to reopen, remove that footer and link.

What it does

A single-page website built with Vue. It has no data of its own: every list, score and button talks to the wfs-backend API. For the meaning of terms like gameweek, entry, chip and mini-league, see the "What it does" section of the backend page. In this repo:

Pages

Address patterns. A "season" is addressed by a short readable name (its slug) rather than a number.

AddressWhat it shows
/Home: all fantasy seasons, with tabs and search
/authLog in, register and lost password on one page
/settingsAccount, your teams, email subscription, mini-leagues
/reset-password/:tokenPassword reset link target
/predictions, /predictions/me, /predictions/me/logThe predictions side game
/competition/:id, /team/:id, /player/:id, /searchReal-world information pages
/faq, /terms, /privacy, /closing-messageStatic pages
/s/:slugA season: deadline countdown, fixtures, players, leaderboard
/s/:slug/pick-teamChoose your squad, captain and chips
/s/:slug/rules, /leaderboard, /entries, /gameweeks, /fixtures, /players, /teams, /price-changes, /predictions, /mini-leagues, /settingsSeason sub-pages (add after /s/:slug)
/s/:slug/entry/:entry and /s/:slug/entry-gameweek/:entry/:gameweekSomeone's team across the season, or in one gameweek
/s/:slug/mod/...Moderator tools for one season (players, teams, fixtures, setup, sources)
/admin/...Admin area: seasons, competitions, real-world teams/players/fixtures, gameweeks, chips, users, price changes, transfers

Old /season/:id/... addresses from the previous version of the site are redirected to the new /s/:slug/... ones (apps/web-vue/src/router/legacyRedirects.ts). The full screen-by-screen description, with a done/partial status for each, is in agent/routes.md.

Running it

You need Node 22 or newer and pnpm 10 (turned on with corepack enable). Run everything from the repo root, not from apps/web-vue.

Your values
cd {{APP_DIR}}
corepack enable
pnpm install

Without a backend (fake data). The default. Opens at http://localhost:5173.

cd {{APP_DIR}}
pnpm dev:web

Against a real backend. Copy the example settings file and set the API address in it:

Your values
cd {{APP_DIR}}
cp apps/web-vue/.env.example apps/web-vue/.env
# then edit apps/web-vue/.env so that it contains:
# VITE_API_BASE_URL={{API_URL}}

There is also apps/web-vue/.env.development, which points at /api; the dev server forwards /api to a local backend with a self-signed certificate (see apps/web-vue/vite.config.ts). The phone app starts with pnpm dev:mobile.

Checks

cd {{APP_DIR}}
pnpm lint
pnpm typecheck
pnpm test

pnpm lint also runs a colour check (scripts/check-css-tokens.mjs) that complains about hard-coded colours that should come from the shared design tokens.

Deploying

The result of a build is a folder of plain files that nginx serves. There is no Node process running on the server. The first-time server setup (nginx block, HTTPS with certbot, permissions) is written out step by step in deploy.md, with ready-made nginx, Caddy, Vercel and Netlify snippets in apps/web-vue/deploy/.

Your values

Production build and update:

cd {{DEPLOY_DIR}}
git pull --ff-only
pnpm install --frozen-lockfile
pnpm build:web:prod
sudo chmod -R a+rX apps/web-vue/dist

Beta build instead (use pnpm build:web:beta in the same steps). The files to serve appear in apps/web-vue/dist; point nginx's root at that folder. A new build is picked up with no nginx reload, because the front page file is never cached.

Which API each build talks to is fixed at build time from a settings file:

(The deploy guide deploy.md still lists api2026.worldfantasysoccer.com as the production API; trust the settings files, which are what the build uses.)

Configuration

A production build that has no API address, or has mock, refuses to start and shows "Refusing to boot with the mock API client". That is on purpose so fake data can never reach real users.

Logging in uses a token the backend hands out, kept in the browser's local storage (the phone app uses its secure store). If the API is on a different address from the website, the backend must allow that website address, or every request fails.

Whatever serves the files must send unknown addresses to index.html, otherwise reloading a page like /s/some-season/pick-team shows a 404.

How it works

Gotchas

Changelog