Files
ToBePlayed-Frontend/README.md
T
lobo d9a39dda22 feat(auth): use the cookie session and refresh on 401
The token is no longer stored; a tbp_session flag remembers the
login. apiFetch renews the session once on a 401 and sends the
request again. Parallel requests and other tabs share one refresh,
because Auth revokes the session when a refresh token is used twice.
Only a 401 from the refresh ends the session; a 502 or no network
shows the normal error.
2026-09-25 13:45:08 +02:00

3.9 KiB
Raw Blame History

To Be Played – Frontend

Web app of To Be Played, a Steam backlog tracker: import your Steam library, decide what to play, and track what you finished. The app is shown as "Game Log" in the UI, in German and English, light and dark.

Library

Game detail Public profile Mobile
Game detail Public profile Mobile library

To Be Played is split into three repositories:

Repository Role
ToBePlayed-Auth Users, login, settings, signs JWTs
ToBePlayed-Core Steam library, backlog, wishlist, stats; verifies JWTs
ToBePlayed-Frontend (this one) React single-page app

Features

  • Dashboard: most played games, games in progress, "Pile of Shame" with backlog hours and a random suggestion, Steam avatar and a banner from the most played game of the last two weeks, and an achievement card: unlocked total, perfect games, average completion and the 5 games closest to 100 %.
  • What to play next (/play-next): three backlog picks for a time budget (up to 5 / 15 / 40 h or any), high priority first, reroll without repeats, and "Start playing" in one click.
  • Library: status filters, "short games" filter (up to 10 h), search, sorting by playtime, recently seen, newest in library, rating or priority, paging. Each row shows the unlocked achievements.
  • Game detail: status, priority, 1–5 star rating, notes, achievements, time to beat (story / extras / 100 %), a hint when Steam shows recent playtime for a game still in the backlog, and one to set the status to 100 % when every achievement is unlocked.
  • Wishlist with store price and discount.
  • Public profile at /u/<display name>, opt-in in the settings, with perfect games and average completion.
  • Appearance: accent color, corner style, wide layout, page background and banner from your own game art. Saved in Auth, so it follows you to every device.
  • German and English, dark mode, mobile layout, session-expired message instead of a silent logout.
  • Session: the tokens are HttpOnly cookies that no script can read. apiFetch renews an expired access token once and sends the request again; parallel requests and other tabs share one refresh.

Tech stack

  • React 19, React Router 7, Vite 8
  • Tailwind CSS 4, Radix UI primitives (shadcn/ui style components), Lucide icons
  • react-hook-form + zod for forms, i18next for translations (public/i18n)
  • Vitest + React Testing Library + jsdom
  • Production image: nginx (Alpine), SPA fallback and long-term caching for hashed assets
  • CI: Forgejo Actions (npm ci, npm test, npm run build, Docker image)

Project structure

src/
├── pages/        # One component per route (Dashboard, GameLibrary, GameDetail, ...)
├── components/   # Shared components; ui/ holds the base components
├── hooks/        # Data hooks (library sync, Steam profile, appearance, session expiry)
├── context/      # PreferencesContext: theme, language, appearance
├── utils/        # Validation schemas and helpers
└── test/         # Test setup (i18n mock)

Tests sit next to the code they test (Component.test.jsx).

Run locally

Needs Node 24 and running Auth and Core services.

npm install
npm run dev
Variable Purpose
VITE_API_URL Base URL of the Auth service
VITE_CORE_API_URL Base URL of the Core service

Values for development and production live in .env.development and .env.production. They end up in the browser bundle, so they must never hold a secret.

Tests and build

npm test
npm run build

Roadmap

  • Mood filter for "What to play next".
  • A Content Security Policy in nginx.conf.
  • Installable PWA.

License

All rights reserved. The source is public for reading only.