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.
All authenticated calls now use apiFetch, and only utils/auth.js
touches the stored token. The cookie login can then change these two
files instead of every page.
/u/<name> shows a public profile without login. Settings get the switch and
the link; every settings save now sends the other saved values, and removing
the display name also makes the profile private.
The dashboard shows the playtime of the last 30 days and Steam's recently
played games. The detail page offers to set a backlog game to Playing when
Steam reports recent playtime; unsaved edits in the backlog card stay.
Every 401 from Auth or Core now goes through useSessionExpired: the login page
says the session expired and returns to the last page after login. App no
longer removes a token that only the browser clock calls expired, so a backend
401 is the one signal. A 403 on the profile now shows an error instead.
First tests cover the library search debounce (fails on the old looping
version), the Pile of Shame states and the backlog save body. jsdom is
enabled per test file, so the plain util tests stay in the node environment.
Pile of Shame shows the backlog count and share, a status bar and a random
backlog pick. Both tiles show the first-sync text while the initial sync runs;
a failed summary load shows a retry button.
Settings: new Profil card with display name and Steam ID. Registration: optional display name. The dashboard falls back to the email prefix when no display name is set.
Add shadcn/ui (Radix + Tailwind 4) components, zinc/violet theme tokens and self-hosted Inter. New header with user menu, Steam cover art, skeletons and toasts. Replace @mdi/font with lucide-react. Fix: top-owned list on the landing page only showed with top-played entries; numbers use the app language.
Remove the placeholder tiles and the settings tile, fix the mobile header width. iconUrl/formatPlaytime move to utils/games.js (599 min now shows 10h, not 10.0h), and the sync button logic to the useForceSync hook shared with the library.
i18n/*.json and index.html keep their names across deploys, so browsers kept old files and showed raw keys like NAV.DASHBOARD. Hashed /assets/ now get a one-year immutable cache.
Header links to Dashboard, Library and Settings. The logo, the login page and
the landing page lead logged-in users to the dashboard. Expired tokens are
removed, and a 403 on the dashboard logs out. Remove the dev dummy token.
Number("76561198005679788") is 76561198005679790 in JavaScript, so the wrong
Steam ID was saved. The Steam ID now stays a 17-digit string. Theme and
language saves no longer send steamId 0. After a save the new token from Auth
replaces the old one.
Add Vitest and run it in CI.
Add a multi-stage Dockerfile to build the React app (node:24-alpine, npm ci, npm run build) and serve the compiled /app/dist via nginx:alpine. Include nginx.conf that routes unknown paths to /index.html so React Router works in a SPA. Add a CI workflow (.forgejo/workflows/docker-publish.yml) to build and push the Docker image to Codeberg Container Registry on pushes to main (requires CODEBERG_TOKEN repository secret and tags the image as codeberg.org/${{ github.repository }}:latest).
Introduce LandingPage, LoginPage and ImprintPage components and replace the previous inline placeholders in App.jsx. Update App layout and header (branding, language toggle, dark mode toggle, route wiring) and use new i18n keys (IMPRINT and LOGIN) in en/de locales. Add client-side validation and UI for the login form. Remove unused assets/icons (public/icons.svg, src/assets/hero.png, vite.svg, vue.svg). Small logging/message tweaks and various Tailwind styling refinements.
Introduce internationalization and basic routing: add i18n initialization (src/i18n.js), translation files (public/i18n/en.json, de.json), and wire i18n into the app (import in main.jsx). Update App.jsx to use react-router (BrowserRouter, Routes) and react-i18next (useTranslation), provide simple Landing/Login/Imprint pages, a language toggle and a dark-mode toggle. Add dependencies in package.json (i18next, i18next-http-backend, react-i18next, react-router-dom) and small Tailwind style tweak (custom-variant for dark) in src/style.css.