1- diff --git a/tests/playwright/tools/IdeaBoardTableNotes.spec.mjs b/tests/playwright/tools/IdeaBoardTableNotes.spec.mjs
2- index 722595ae9..f861f1f44 100644
3- --- a/tests/playwright/tools/IdeaBoardTableNotes.spec.mjs
4- +++ b/tests/playwright/tools/IdeaBoardTableNotes.spec.mjs
5- @@ -392,6 +392,18 @@ test("Idea Board uses accordion table ideas and notes", async ({ page }) => {
6- await expect(page.locator("[data-source-idea-notes]")).toContainText("Use dusk tide changes as the first Game Hub planning note.");
7- await expect(page.locator("[data-source-idea-section] :is(input, textarea, select, button)")).toHaveCount(0);
8- await expect(page.getByRole("button", { name: "Delete Open Game" })).toHaveCount(0);
9- + const activeGameRow = page.locator("[data-game-row][data-game-active='true']");
10- + await expect(activeGameRow).toContainText("Lantern Reef");
11- + await activeGameRow.locator("[data-game-toggle]").click();
12- + const sourceIdeaChildTable = page.locator("[data-game-expanded-row] [data-game-child-table='source-idea']");
13- + await expect(sourceIdeaChildTable.locator("caption")).toHaveText("Source Idea");
14- + await expect(sourceIdeaChildTable.locator("thead th")).toHaveText(["Context", "Details"]);
15- + await expect(sourceIdeaChildTable.locator("tbody tr")).toHaveText([
16- + "IdeaLantern Reef",
17- + "PitchGuide light through a reef that rearranges at dusk.",
18- + "Note 1Use dusk tide changes as the first Game Hub planning note.",
1+ diff --git a/tests/playwright/tools/GameHubMockRepository.spec.mjs b/tests/playwright/tools/GameHubMockRepository.spec.mjs
2+ index 695b0a609..dac65c676 100644
3+ --- a/tests/playwright/tools/GameHubMockRepository.spec.mjs
4+ +++ b/tests/playwright/tools/GameHubMockRepository.spec.mjs
5+ @@ -288,6 +288,22 @@ test("Game Hub creates, opens, and deletes mock games", async ({ page }) => {
6+ "StatusUnder Construction",
7+ "OwnerUser 1",
8+ ]);
9+ + await expect(page.locator("[data-game-expanded-row='demo-game'] [data-game-child-table]")).toHaveCount(3);
10+ + await expect(page.locator("[data-game-expanded-row='demo-game'] [data-game-child-table='source-idea'] caption")).toHaveText("Source Idea");
11+ + const readinessOutputTable = page.locator("[data-game-expanded-row='demo-game'] [data-game-child-table='readiness-output']");
12+ + await expect(readinessOutputTable.locator("caption")).toHaveText("Readiness Output");
13+ + await expect(readinessOutputTable.locator("thead th")).toHaveText(["Output", "Status"]);
14+ + await expect(readinessOutputTable.locator("tbody tr")).toHaveText([
15+ + "Game StatusUnder Construction",
16+ + "Game ProgressDemo Game identity ready",
17+ + "Launch ProgressPublish blocked until configuration and required assets are ready",
18+ + "Current FocusComplete Game Configuration",
19+ + "Recommended Next ToolGame Configuration",
20+ + "Game identityComplete",
21+ + "Game configurationUnder Construction",
22+ + "Playable buildPlanned",
23+ + "Publishing reviewPlanned",
1924+ ]);
20- + await expect(sourceIdeaChildTable.locator(":is(input, textarea, select, button)")).toHaveCount(0);
21- await page.reload({ waitUntil: "networkidle" });
22- await expect(page.locator("[data-active-game-name]")).toHaveText("Lantern Reef");
23- await expect(page.locator("[data-game-list]")).toContainText("Lantern Reef");
25+ await demoGameRow.locator("[data-game-toggle='demo-game']").click();
26+ await expect(page.locator("[data-game-expanded-row='demo-game']")).toHaveCount(0);
27+
2428diff --git a/toolbox/game-hub/game-hub.js b/toolbox/game-hub/game-hub.js
25- index 121e82562..67f56ca41 100644
29+ index 67f56ca41..4b21814d7 100644
2630--- a/toolbox/game-hub/game-hub.js
2731+++ b/toolbox/game-hub/game-hub.js
28- @@ -271,6 +271,54 @@ function renderGameSummaryChildTable (parent, game) {
32+ @@ -319,7 +319,53 @@ function renderSourceIdeaChildTable (parent, game) {
2933 parent.append(wrapper);
3034 }
3135
32- + function gameSourceIdeaDetails(game) {
33- + const sourceIdea = isRecord(game?.sourceIdea) ? game.sourceIdea : null;
34- + const name = String(sourceIdea?.idea || "").trim();
35- + const pitch = String(sourceIdea?.pitch || "").trim();
36- + const notes = Array.isArray(sourceIdea?.notes)
37- + ? sourceIdea.notes.map((note) => String(note || "").trim()).filter(Boolean)
38- + : [];
39- + return {
40- + name,
41- + notes,
42- + pitch,
43- + };
44- + }
45- +
46- + function renderSourceIdeaChildTable(parent, game) {
47- + const sourceIdea = gameSourceIdeaDetails(game);
36+ - function renderExpandedGameRow(tbody, game) {
37+ + function renderReadinessOutputChildTable(parent, game, progress, active) {
38+ + const readiness = active && isRecord(progress)
39+ + ? progress
40+ + : {
41+ + currentFocus: "Open this game to review readiness",
42+ + gameProgress: `${game.name} identity ready`,
43+ + gameStatus: game.status || "No status",
44+ + publishingProgress: "Open this game to review launch progress",
45+ + recommendedNextTool: "Game Hub",
46+ + progressChecklist: [],
47+ + };
4848+ const wrapper = document.createElement("div");
4949+ wrapper.className = "table-wrapper";
5050+ const table = document.createElement("table");
5151+ table.className = "data-table data-table--fixed";
52- + table.dataset.gameChildTable = "source-idea ";
53- + table.setAttribute("aria-label", `${game.name} source idea `);
54- + table.innerHTML = "<caption>Source Idea </caption><thead><tr><th scope=\"col\">Context </th><th scope=\"col\">Details </th></tr></thead>";
52+ + table.dataset.gameChildTable = "readiness-output ";
53+ + table.setAttribute("aria-label", `${game.name} readiness output `);
54+ + table.innerHTML = "<caption>Readiness Output </caption><thead><tr><th scope=\"col\">Output </th><th scope=\"col\">Status </th></tr></thead>";
5555+ const body = document.createElement("tbody");
5656+ [
57- + ["Idea", sourceIdea.name || "No source idea yet"],
58- + ["Pitch", sourceIdea.pitch || "Create a project from Idea Board to see source details."],
57+ + ["Game Status", readiness.gameStatus],
58+ + ["Game Progress", readiness.gameProgress],
59+ + ["Launch Progress", readiness.publishingProgress],
60+ + ["Current Focus", readiness.currentFocus],
61+ + ["Recommended Next Tool", readiness.recommendedNextTool],
5962+ ].forEach(([label, value]) => {
6063+ const row = document.createElement("tr");
61- + row.append(createCell(label, "th"), createCell(value));
64+ + row.append(createCell(label, "th"), createCell(value || "Not available" ));
6265+ row.firstElementChild.scope = "row";
6366+ body.append(row);
6467+ });
6568+
66- + const notes = sourceIdea.notes.length ? sourceIdea.notes : ["No source notes." ];
67- + notes .forEach((note, index ) => {
69+ + const checklist = Array.isArray(readiness.progressChecklist) ? readiness.progressChecklist : [];
70+ + checklist .forEach((item ) => {
6871+ const row = document.createElement("tr");
69- + row.dataset.sourceIdeaNoteRow = String(index + 1) ;
70- + row.append(createCell(`Note ${index + 1}` , "th"), createCell(note ));
72+ + row.dataset.readinessChecklistRow = item.label || "Checklist" ;
73+ + row.append(createCell(item.label || "Checklist" , "th"), createCell(item.status || "Not available" ));
7174+ row.firstElementChild.scope = "row";
7275+ body.append(row);
7376+ });
@@ -77,40 +80,70 @@ index 121e82562..67f56ca41 100644
7780+ parent.append(wrapper);
7881+ }
7982+
80- function renderExpandedGameRow(tbody, game) {
83+ + function renderExpandedGameRow(tbody, game, progress, active ) {
8184 const row = document.createElement("tr");
8285 row.dataset.gameExpandedRow = game.id;
83- @@ -280,6 +328,7 @@ function renderExpandedGameRow(tbody, game) {
84- const stack = document.createElement("div");
86+ row.id = ` game-child-${game.id}`;
87+ @@ -329,16 +375,18 @@ function renderExpandedGameRow(tbody, game) {
8588 stack.className = "content-stack content-stack--compact";
8689 renderGameSummaryChildTable(stack, game);
87- + renderSourceIdeaChildTable(stack, game);
90+ renderSourceIdeaChildTable(stack, game);
91+ + renderReadinessOutputChildTable(stack, game, progress, active);
8892 content.append(stack);
8993 row.append(content);
9094 tbody.append(row);
91- @@ -443,20 +492,15 @@ function renderTableCounts() {
9295 }
9396
94- function renderSourceIdea(activeGame) {
95- - const sourceIdea = isRecord(activeGame?.sourceIdea) ? activeGame.sourceIdea : null;
96- - const name = String(sourceIdea?.idea || "").trim();
97- - const pitch = String(sourceIdea?.pitch || "").trim();
98- - const notes = Array.isArray(sourceIdea?.notes)
99- - ? sourceIdea.notes.map((note) => String(note || "").trim()).filter(Boolean)
100- - : [];
101- + const sourceIdea = gameSourceIdeaDetails(activeGame);
97+ - function renderGameParentRow(tbody, game, activeGame) {
98+ + function renderGameParentRow(tbody, game, activeGame, progress) {
99+ const expanded = state.expandedGameId === game.id;
100+ + const active = activeGame?.id === game.id;
101+ const row = document.createElement("tr");
102+ row.dataset.gameRow = game.id;
103+ - if (activeGame?.id === game.id) {
104+ + if (active) {
105+ row.dataset.gameActive = "true";
106+ }
107+
108+ @@ -353,12 +401,12 @@ function renderGameParentRow(tbody, game, activeGame) {
109+ );
110+
111+ const actions = document.createElement("td");
112+ - actions.append(createGameButton(game, activeGame?.id === game.id));
113+ + actions.append(createGameButton(game, active));
114+ row.append(actions);
115+ tbody.append(row);
116+
117+ if (expanded) {
118+ - renderExpandedGameRow(tbody, game);
119+ + renderExpandedGameRow(tbody, game, progress, active);
120+ }
121+ }
122+
123+ @@ -389,7 +437,7 @@ function renderProjectInformation(activeGame, currentMember, progress) {
124+ : "Project Information loaded. Sign in to save changes.");
125+ }
102126
103- - setText(elements.sourceIdeaName, name || "No source idea yet");
104- - setText(elements.sourceIdeaDisplay, name || "No source idea yet");
105- - setText(elements.sourceIdeaPitch, pitch || "Create a project from Idea Board to see source details.");
106- + setText(elements.sourceIdeaName, sourceIdea.name || "No source idea yet");
107- + setText(elements.sourceIdeaDisplay, sourceIdea.name || "No source idea yet");
108- + setText(elements.sourceIdeaPitch, sourceIdea.pitch || "Create a project from Idea Board to see source details.");
127+ - function renderGameList() {
128+ + function renderGameList(progress) {
129+ if (!elements.gameList) {
130+ return;
131+ }
132+ @@ -421,7 +469,7 @@ function renderGameList() {
133+ table.setAttribute("aria-label", "Open Games");
134+ table.innerHTML = "<caption>Open Games</caption><thead><tr><th scope=\"col\">Game</th><th scope=\"col\">Purpose</th><th scope=\"col\">Status</th><th scope=\"col\">Owner</th><th scope=\"col\">Actions</th></tr></thead>";
135+ const body = document.createElement("tbody");
136+ - listResult.forEach((game) => renderGameParentRow(body, game, activeGame));
137+ + listResult.forEach((game) => renderGameParentRow(body, game, activeGame, progress));
138+ table.append(body);
139+ wrapper.append(table);
140+ elements.gameList.append(wrapper);
141+ @@ -558,7 +606,7 @@ function renderWorkspace() {
142+ }
143+ }
109144
110- if (elements.sourceIdeaNotes) {
111- elements.sourceIdeaNotes.replaceChildren();
112- - const visibleNotes = notes.length ? notes : ["No source notes."];
113- + const visibleNotes = sourceIdea.notes.length ? sourceIdea.notes : ["No source notes."];
114- visibleNotes.forEach((note) => {
115- const item = document.createElement("li");
116- item.textContent = note;
145+ - renderGameList();
146+ + renderGameList(progress);
147+ renderMembersTable(activeGame);
148+ renderTableCounts();
149+ renderChecklist(progress);
0 commit comments