diff --git a/README.md b/README.md index 3aa80d0..59528e4 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # 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 UI is in German and English, with light and dark mode. +Web app of **To Be Played**, a backlog tracker: import your Steam and PlayStation libraries or add games by hand, +decide what to play, and track what you finished. The UI is in German and English, with light and dark mode. ![Library](docs/screenshots/library-desktop-dark.png) @@ -9,13 +9,14 @@ you finished. The UI is in German and English, with light and dark mode. |---|---|---|---| | ![Dashboard](docs/screenshots/dashboard-desktop-dark.png) | ![Game detail](docs/screenshots/detail-desktop-dark.png) | ![Public profile](docs/screenshots/profile-desktop-dark.png) | ![Mobile library](docs/screenshots/library-mobile-dark.png) | -To Be Played is split into three repositories: +To Be Played is split into four 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 | +| ToBePlayed-Deployment | Docker Compose stack, env template, ROADMAP.md (history), plans | ## Features @@ -41,6 +42,8 @@ To Be Played is split into three repositories: - **Your stats** (`/stats`): totals and statuses; the pile in hours, in weeks at your pace of the last 90 days and in years of full-time work, and its oldest unplayed game; most played and most loyal games; top genres and moods; trips to the moon, the longest gaming day, games started but under one hour; finished games per year; games per platform. + The user picks a chart form per group (bars, pie, donut, line or list, `src/utils/statCharts.js`). Auth saves it + (`PUT /api/users/settings/stat-chart`). - **Click a number** (stats page, dashboard, public profile, landing page, Pile Points): a number, bar, row or year opens the games behind it in a dialog (a bottom sheet on phones), 40 per page with "Load more". In your own views the status counts and the pile link to the library with the filter instead. Not clickable: sums (playtime), @@ -216,9 +219,11 @@ flowchart LR - react-hook-form + zod for forms, i18next for translations (`public/i18n`) - Vitest + React Testing Library + jsdom - Production image: nginx (Alpine), SPA fallback, long-term caching for hashed assets, Content Security Policy - and security headers + and security headers. nginx reads `AUTH_HOST` and `API_HOST` from the container env at start (`nginx.conf` is an + nginx template) and puts them into the CSP `connect-src`. - ESLint with the React hooks rules (`npm run lint`) -- CI: Forgejo Actions (`npm ci`, `npm run lint`, `npm test`, `npm run build`, Docker image) +- CI: Gitea Actions (`.gitea/workflows/docker-publish.yml`: `npm ci`, `npm run lint`, `npm test`, `npm run build`, then the + Docker image tagged `latest`, ``, `-`) ## Architecture @@ -259,6 +264,8 @@ flowchart LR - The pages that need a login sit under the `RequireSession` layout route in `App.jsx`. Without a session it opens the login page: after a `401` with the "session expired" message and back to the same page, after a logout without a way back. A public page stays open when the session ends there. +- Public routes: `/`, `/login`, `/register`, `/confirm-email`, `/reset-password`, `/u/:name` and `/impressum` + (`ImprintPage`, linked from the footer). Every other route sits under `RequireSession`. - `CurrentUserProvider` holds `GET /api/users/me` for the whole app (`useCurrentUserContext`, `useCurrentUser`). Dashboard and settings load it again when they open. - Tests render through `src/test/renderWithProviders.jsx` (a data router like `main.jsx`) and answer requests @@ -340,13 +347,16 @@ flowchart LR ```text src/ +├── App.jsx, main.jsx, i18n.js # Routes, entry point, i18next setup +├── style.css, skins.css, effects.css # Base styles, the skins, the background effects ├── pages/ # One component per route (Dashboard, GameLibrary, GameDetail, ...): loads data, places cards -├── components/ # Cards and shared components; ui/ holds the base components, import/ and stats/ the parts -│ # of one page that share page-only code +├── components/ # Cards and shared components; ui/ holds the base components, import/, stats/ and people/ the +│ # parts of one page that share page-only code ├── hooks/ # Data hooks (game, dashboard, library sync, Steam profile, appearance, session) -├── context/ # PreferencesContext: theme, language, appearance +├── context/ # CurrentUserContext, PreferencesContext +├── lib/ # utils.js (class name helper) ├── utils/ # Validation schemas and helpers -└── test/ # Test setup (i18n mock) +└── test/ # renderWithProviders.jsx, fetchRoutes.js, i18n mock, WalletLoader, fixtures/import ``` Tests sit next to the code they test (`Component.test.jsx`). @@ -388,6 +398,16 @@ npm run dev 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. +Production image (container env, read by nginx at start): + +| Variable | Purpose | +|---|---| +| `AUTH_HOST` | Auth host name for the CSP `connect-src` | +| `API_HOST` | Core host name, same | + +Both default to empty in the image; `docker-compose.yml` of the Deployment repo passes them. `FRONTEND_HOST` is not +passed to the frontend image; compose uses it for the Traefik rule and as `APP_FRONTEND_URL` of Auth and Core (Deployment `docker-compose.yml`:68,152). + ## Tests and build ```bash