Skip to content

Commit c4a3db1

Browse files
committed
PR_26174_ALFA_011-game-hub-readiness-output-child-table
1 parent 8194907 commit c4a3db1

9 files changed

Lines changed: 226 additions & 90 deletions
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Branch Validation: PASS
2+
3+
PASS - Current branch: pr/26174-ALFA-011-game-hub-readiness-output-child-table.
4+
PASS - Stack base: pr/26174-ALFA-010-game-hub-source-idea-child-table-polish.
5+
PASS - Changes are scoped to Readiness Output child table rendering, targeted tests, and required reports.
6+
PASS - No merge to main performed.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Manual Validation Notes: PASS
2+
3+
PASS - Confirmed expanded Demo Game row renders three separate child tables.
4+
PASS - Confirmed Readiness Output caption and columns are separate from Source Idea.
5+
PASS - Confirmed readiness rows use existing Game Status, Game Progress, Launch Progress, Current Focus, Recommended Next Tool, and checklist data.
6+
PASS - Confirmed no Game Journey bucket order files/constants were modified.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
Requirement Checklist: PASS
2+
3+
PASS - Readiness Output appears as its own child table under the same game parent row.
4+
PASS - Readiness Output stays separate from Source Idea.
5+
PASS - Displays existing readiness/progress output only.
6+
PASS - No new readiness scoring/math was added.
7+
PASS - Existing Game Journey bucket order was preserved; no Game Journey bucket constants were changed.
8+
PASS - Existing API/service contract is preserved.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Validation Lane: PASS
2+
3+
PASS - npx playwright test tests/playwright/tools/GameHubMockRepository.spec.mjs -g "Game Hub creates, opens, and deletes mock games"
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# PR_26174_ALFA_011-game-hub-readiness-output-child-table
2+
3+
## Purpose
4+
5+
Make Readiness Output a separate child table under the expanded game row.
6+
7+
## Summary
8+
9+
- Added a separate Readiness Output child table to expanded Game Hub parent rows.
10+
- Kept Readiness Output separate from Source Idea and Game Summary child tables.
11+
- Displayed existing progress/readiness output fields and checklist rows only.
12+
- Preserved the existing Game Hub API/service contract and did not change Game Journey bucket ordering.
13+
14+
## Validation
15+
16+
PASS - `npx playwright test tests/playwright/tools/GameHubMockRepository.spec.mjs -g "Game Hub creates, opens, and deletes mock games"`
Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
toolbox/game-hub/game-hub.js
2-
tests/playwright/tools/IdeaBoardTableNotes.spec.mjs
2+
tests/playwright/tools/GameHubMockRepository.spec.mjs
33
docs_build/dev/reports/codex_review.diff
44
docs_build/dev/reports/codex_changed_files.txt
5-
docs_build/dev/reports/PR_26174_ALFA_010-game-hub-source-idea-child-table-polish.md
6-
docs_build/dev/reports/PR_26174_ALFA_010-game-hub-source-idea-child-table-polish-branch-validation.txt
7-
docs_build/dev/reports/PR_26174_ALFA_010-game-hub-source-idea-child-table-polish-requirement-checklist.txt
8-
docs_build/dev/reports/PR_26174_ALFA_010-game-hub-source-idea-child-table-polish-validation-lane.txt
9-
docs_build/dev/reports/PR_26174_ALFA_010-game-hub-source-idea-child-table-polish-manual-validation-notes.txt
5+
docs_build/dev/reports/PR_26174_ALFA_011-game-hub-readiness-output-child-table.md
6+
docs_build/dev/reports/PR_26174_ALFA_011-game-hub-readiness-output-child-table-branch-validation.txt
7+
docs_build/dev/reports/PR_26174_ALFA_011-game-hub-readiness-output-child-table-requirement-checklist.txt
8+
docs_build/dev/reports/PR_26174_ALFA_011-game-hub-readiness-output-child-table-validation-lane.txt
9+
docs_build/dev/reports/PR_26174_ALFA_011-game-hub-readiness-output-child-table-manual-validation-notes.txt
Lines changed: 109 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,76 @@
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+
2428
diff --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);

tests/playwright/tools/GameHubMockRepository.spec.mjs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,22 @@ test("Game Hub creates, opens, and deletes mock games", async ({ page }) => {
288288
"StatusUnder Construction",
289289
"OwnerUser 1",
290290
]);
291+
await expect(page.locator("[data-game-expanded-row='demo-game'] [data-game-child-table]")).toHaveCount(3);
292+
await expect(page.locator("[data-game-expanded-row='demo-game'] [data-game-child-table='source-idea'] caption")).toHaveText("Source Idea");
293+
const readinessOutputTable = page.locator("[data-game-expanded-row='demo-game'] [data-game-child-table='readiness-output']");
294+
await expect(readinessOutputTable.locator("caption")).toHaveText("Readiness Output");
295+
await expect(readinessOutputTable.locator("thead th")).toHaveText(["Output", "Status"]);
296+
await expect(readinessOutputTable.locator("tbody tr")).toHaveText([
297+
"Game StatusUnder Construction",
298+
"Game ProgressDemo Game identity ready",
299+
"Launch ProgressPublish blocked until configuration and required assets are ready",
300+
"Current FocusComplete Game Configuration",
301+
"Recommended Next ToolGame Configuration",
302+
"Game identityComplete",
303+
"Game configurationUnder Construction",
304+
"Playable buildPlanned",
305+
"Publishing reviewPlanned",
306+
]);
291307
await demoGameRow.locator("[data-game-toggle='demo-game']").click();
292308
await expect(page.locator("[data-game-expanded-row='demo-game']")).toHaveCount(0);
293309

0 commit comments

Comments
 (0)