desktop: five sections by job (Release, Store page, Revenue, Players, Health), Game services view, sidebar by attention

- app view: 12 tabs become 5 sections, each with a header (what it is for, the one next step from state
  already read) and its sub views in a segmented control; 1-5 jump, Ctrl+Shift+[ ] cycle, Ctrl+K and the
  context menu list sections and sub views; old tab ids, section and section/sub links all resolve
- Players > Game services (games only): both stores, one row per leaderboard / achievement with its
  languages and each store's state, Game Center locale map, Play API-disabled state with the console plan
- games status --json: items, localeMap, playApiDisabled; inspect summary: games
- sidebar: apps grouped by attention / in progress / live / not on a store, one status line each, studio
  filter, workspace as a footer icon row
- boot check opens places like a person (section, then sub view)
This commit is contained in:
CRTkafa 2026-10-24 13:01:50 +08:00
parent 98cd1d3c92
commit 0ef27cf2e4
22 changed files with 918 additions and 115 deletions

View file

@ -8,7 +8,7 @@ import { capabilitySpecs, planCapabilities } from '../cli/lib/signing';
import { gamesPrivacyFindings, gcFindings, gcLocaleMap, liveGcLocales, suggestLocaleMap, pgsFindings, planGameCenter, playAchievementBody, playLeaderboardBody, readPgsProject, validateGames } from '../cli/lib/game-services';
import { consolePlan } from '../cli/lib/console-plan';
import { verifyText } from '../cli/lib/console-verify';
import { gameServicesFindings, type GameServicesStatus } from '../cli/games-status';
import { gameItemRows, gameServicesFindings, type GameServicesStatus } from '../cli/games-status';
import { run as gameCenter } from '../cli/apple/game-center';
import { run as playGames } from '../cli/play/games';
import { run as appleSubmit } from '../cli/apple/submit';
@ -298,3 +298,15 @@ describe('apple submit with a Game Center build (live 409 STATE_ERROR.BUILD_INDI
expect(s.asc.all('gameCenterAppVersions')).toHaveLength(1);
});
});
describe('games status rows (desktop Players section)', () => {
test('one row per item, store states from the plans, null when a store was not read', () => {
const g = { leaderboards: [{ id: 'com.example.lb', locales: { en: { name: 'Best' } } }], achievements: [{ id: 'com.example.a1', points: 10, hidden: true, platforms: ['android' as const], locales: { en: { name: 'First', description: 'd' } } }] };
const plan = [{ kind: 'leaderboard' as const, id: 'com.example.lb', action: 'update' as const, attributes: {}, changed: ['scoreSortType'], locales: [{ locale: 'en-US', action: 'create' as const, attributes: {}, changed: [] }] }];
const rows = gameItemRows(g, plan, null);
expect(rows.map(r => [r.kind, r.id, r.name, r.ios, r.android])).toEqual([['leaderboard', 'com.example.lb', 'Best', true, true], ['achievement', 'com.example.a1', 'First', false, true]]);
expect(rows[0]!.apple).toEqual({ action: 'update', changed: ['scoreSortType'], locales: [{ locale: 'en-US', action: 'create' }] });
expect(rows[0]!.play).toBeNull();
expect(rows[1]).toMatchObject({ points: 10, hidden: true, apple: null });
});
});