feat(lobby): F8-04b hierarchical sidebar + paid-tier gate for create-game
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:
@@ -13,6 +13,8 @@ import {
|
||||
import {
|
||||
ApplicationSubmitResponse,
|
||||
ApplicationSummary,
|
||||
ErrorBody,
|
||||
ErrorResponse,
|
||||
GameCreateResponse,
|
||||
GameSummary,
|
||||
InviteDeclineResponse,
|
||||
@@ -218,17 +220,36 @@ export interface AccountFixture {
|
||||
preferredLanguage?: string;
|
||||
timeZone?: string;
|
||||
declaredCountry?: string;
|
||||
isPaid?: boolean;
|
||||
}
|
||||
|
||||
// buildLobbyErrorPayload builds a `lobby.ErrorResponse` FBS payload
|
||||
// the Playwright suite returns on non-`ok` result codes. The TS lobby
|
||||
// client decodes the same payload via `decodeLobbyError`, surfacing
|
||||
// `code` / `message` to the UI for inline rendering.
|
||||
export function buildLobbyErrorPayload(code: string, message: string): Uint8Array {
|
||||
const builder = new Builder(128);
|
||||
const codeOff = builder.createString(code);
|
||||
const messageOff = builder.createString(message);
|
||||
ErrorBody.startErrorBody(builder);
|
||||
ErrorBody.addCode(builder, codeOff);
|
||||
ErrorBody.addMessage(builder, messageOff);
|
||||
const bodyOff = ErrorBody.endErrorBody(builder);
|
||||
ErrorResponse.startErrorResponse(builder);
|
||||
ErrorResponse.addError(builder, bodyOff);
|
||||
builder.finish(ErrorResponse.endErrorResponse(builder));
|
||||
return builder.asUint8Array();
|
||||
}
|
||||
|
||||
export function buildAccountResponsePayload(account: AccountFixture): Uint8Array {
|
||||
const builder = new Builder(256);
|
||||
|
||||
const planCode = builder.createString("free");
|
||||
const planCode = builder.createString(account.isPaid === true ? "permanent" : "free");
|
||||
const source = builder.createString("internal");
|
||||
const reasonCode = builder.createString("");
|
||||
EntitlementSnapshot.startEntitlementSnapshot(builder);
|
||||
EntitlementSnapshot.addPlanCode(builder, planCode);
|
||||
EntitlementSnapshot.addIsPaid(builder, false);
|
||||
EntitlementSnapshot.addIsPaid(builder, account.isPaid === true);
|
||||
EntitlementSnapshot.addSource(builder, source);
|
||||
EntitlementSnapshot.addReasonCode(builder, reasonCode);
|
||||
EntitlementSnapshot.addStartsAtMs(builder, 0n);
|
||||
|
||||
Reference in New Issue
Block a user