Public Access
444b03acee87f212276005b09a00eb448b745c70
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.
| Game detail | Public profile | Mobile |
|---|---|---|
![]() |
![]() |
![]() |
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.
apiFetchrenews 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.
Languages
JavaScript
96.7%
CSS
3.2%



