feat(lobby): F8-04b hierarchical sidebar + paid-tier gate for create-game
Tests · Go / test (push) Successful in 2m17s
Tests · UI / test (push) Waiting to run

Reshape the lobby UI from a single Overview into a two-level sidebar
(games · profile · DEV synthetic-reports) with four games sub-panels
(active-past · recruitment · invitations · private-games). Move the
`create new game` button into the private-games panel, merge the
applications section into recruitment cards as status chips, and add
DEV-only synthetic-report loader as a top-level screen.

Add a paid-tier gate at backend `lobby.game.create`: free callers get
`403 forbidden` before the lobby service is invoked. The UI hides the
private-games sub-panel + create button on free tier (DEV affordances
flag overrides). Update every integration test that creates a game to
use a new `testenv.PromoteToPaid` helper; add a new
`TestLobbyFlow_FreeUserCreateGameForbidden`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ilia Denisov
2026-05-26 23:53:53 +02:00
parent 98d1fe6cae
commit 009ea560f9
44 changed files with 2486 additions and 1118 deletions
+20 -3
View File
@@ -21,6 +21,11 @@
import LobbyCreateScreen from "$lib/screens/lobby-create-screen.svelte";
import ProfileScreen from "$lib/screens/profile-screen.svelte";
import GameShell from "$lib/game/game-shell.svelte";
import GamesActivePastScreen from "$lib/screens/games-active-past-screen.svelte";
import GamesRecruitmentScreen from "$lib/screens/games-recruitment-screen.svelte";
import GamesInvitationsScreen from "$lib/screens/games-invitations-screen.svelte";
import GamesPrivateGamesScreen from "$lib/screens/games-private-games-screen.svelte";
import SyntheticReportsScreen from "$lib/screens/synthetic-reports-screen.svelte";
import { pushState } from "$app/navigation";
import { page } from "$app/state";
@@ -90,11 +95,23 @@
<ProfileScreen />
{:else if appScreen.screen === "game" && appScreen.gameId !== null}
<GameShell />
{:else if appScreen.screen === "games-active-past"}
<GamesActivePastScreen />
{:else if appScreen.screen === "games-recruitment"}
<GamesRecruitmentScreen />
{:else if appScreen.screen === "games-invitations"}
<GamesInvitationsScreen />
{:else if appScreen.screen === "games-private-games"}
<GamesPrivateGamesScreen />
{:else if appScreen.screen === "synthetic-reports"}
<SyntheticReportsScreen />
{:else}
<!--
Default authenticated screen. Covers `lobby`, a stale `login`
screen restored from a previous anonymous session, and a `game`
screen with no active game id (a snapshot that lost its id).
Default authenticated screen. Covers the historical `lobby`
alias and any restored snapshot that lost its game id. The
`LobbyScreen` resolver navigates to `games-recruitment` on
mount; the shell then re-routes to a more appropriate
sub-page if visibility rules allow.
-->
<LobbyScreen />
{/if}