Skip to content

Commit c14c357

Browse files
committed
PR_26171_011 Game Journey recommended targets
1 parent f301d41 commit c14c357

6 files changed

Lines changed: 133 additions & 0 deletions

File tree

src/dev-runtime/persistence/tool-repositories/game-journey-mock-repository.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,33 @@ export const GAME_JOURNEY_TOOL_OWNERSHIP_AREAS = Object.freeze([
243243
}),
244244
]);
245245

246+
export const GAME_JOURNEY_RECOMMENDED_TARGETS = Object.freeze([
247+
Object.freeze({
248+
key: "heroes",
249+
label: "Heroes",
250+
sectionName: "Objects",
251+
suggestedCount: 1,
252+
}),
253+
Object.freeze({
254+
key: "enemies",
255+
label: "Enemies",
256+
sectionName: "Objects",
257+
suggestedCount: 3,
258+
}),
259+
Object.freeze({
260+
key: "levels",
261+
label: "Levels",
262+
sectionName: "Worlds",
263+
suggestedCount: 5,
264+
}),
265+
Object.freeze({
266+
key: "audio",
267+
label: "Audio",
268+
sectionName: "Audio",
269+
suggestedCount: 6,
270+
}),
271+
]);
272+
246273
function clone(value) {
247274
return JSON.parse(JSON.stringify(value));
248275
}

src/dev-runtime/server/local-api-router.mjs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ import {
6868
GAME_JOURNEY_KEYS,
6969
GAME_JOURNEY_STATUS_BY_ID,
7070
GAME_JOURNEY_STATUSES,
71+
GAME_JOURNEY_RECOMMENDED_TARGETS,
7172
GAME_JOURNEY_SUGGESTED_TOOLS,
7273
GAME_JOURNEY_TOOL_OWNERSHIP_AREAS,
7374
createGameJourneyMockRepository,
@@ -5115,6 +5116,7 @@ LIMIT 1;
51155116
GAME_JOURNEY_KEYS,
51165117
GAME_JOURNEY_STATUS_BY_ID,
51175118
GAME_JOURNEY_STATUSES,
5119+
GAME_JOURNEY_RECOMMENDED_TARGETS,
51185120
GAME_JOURNEY_SUGGESTED_TOOLS,
51195121
GAME_JOURNEY_TOOL_OWNERSHIP_AREAS,
51205122
};

tests/playwright/tools/GameJourneyTool.spec.mjs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { clearPlaywrightStorage, installPlaywrightStorageIsolation } from "../..
77
import { workspaceV2CoverageReporter } from "../../helpers/workspaceV2CoverageReporter.mjs";
88
import {
99
GAME_JOURNEY_KEYS,
10+
GAME_JOURNEY_RECOMMENDED_TARGETS,
1011
GAME_JOURNEY_STATUS_BY_ID,
1112
GAME_JOURNEY_STATUSES,
1213
GAME_JOURNEY_TOOL_OWNERSHIP_AREAS,
@@ -195,11 +196,23 @@ async function expectFilteredSummaryRows(page, statusId, expectedRows) {
195196

196197
test("Game Journey exposes static tool ownership areas without automatic counts", async () => {
197198
expectStaticToolOwnershipAreas(GAME_JOURNEY_TOOL_OWNERSHIP_AREAS);
199+
expect(GAME_JOURNEY_RECOMMENDED_TARGETS.map((target) => target.label)).toEqual([
200+
"Heroes",
201+
"Enemies",
202+
"Levels",
203+
"Audio",
204+
]);
198205

199206
const server = await startRepoServer();
200207
try {
201208
const constants = await fetchApiData(server, "/api/toolbox/game-journey/constants");
202209
expectStaticToolOwnershipAreas(constants.GAME_JOURNEY_TOOL_OWNERSHIP_AREAS);
210+
expect(constants.GAME_JOURNEY_RECOMMENDED_TARGETS.map((target) => target.label)).toEqual([
211+
"Heroes",
212+
"Enemies",
213+
"Levels",
214+
"Audio",
215+
]);
203216
} finally {
204217
await server.close();
205218
}
@@ -269,6 +282,13 @@ test("Game Journey progress dashboard summarizes completion metrics", async ({ p
269282
await expect(page.locator("[data-journey-most-complete-areas] li").first()).toHaveText("Idea: 0% complete (0 of 4)");
270283
await expect(page.locator("[data-journey-least-complete-areas] li")).toHaveCount(3);
271284
await expect(page.locator("[data-journey-least-complete-areas] li").first()).toHaveText("Idea: 0% complete (0 of 4)");
285+
await expect(page.locator("[data-journey-recommended-target]")).toHaveCount(4);
286+
await expect(page.locator("[data-journey-recommended-target='heroes'] td").nth(0)).toHaveText("Heroes");
287+
await expect(page.locator("[data-journey-recommended-target='heroes'] td").nth(1)).toHaveText("Objects");
288+
await expect(page.locator("[data-journey-target-input='heroes']")).toHaveValue("1");
289+
await page.locator("[data-journey-target-input='heroes']").fill("2");
290+
await expect(page.locator("[data-journey-target-status]")).toHaveText("Heroes suggested target set to 2.");
291+
await expect(page.locator("[data-journey-target-input='heroes']")).toHaveValue("2");
272292
await expect(page.locator("style, [style], script:not([src])")).toHaveCount(0);
273293

274294
expect(failedRequests).toEqual([]);

toolbox/game-journey/game-journey-api-client.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export const GAME_JOURNEY_STATUS_BY_ID = Object.freeze(requireServerConstant(con
1515
export const GAME_JOURNEY_STATUSES = Object.freeze(requireServerConstant(constants, "GAME_JOURNEY_STATUSES", "game-journey"));
1616
export const GAME_JOURNEY_SUGGESTED_TOOLS = Object.freeze(requireServerConstant(constants, "GAME_JOURNEY_SUGGESTED_TOOLS", "game-journey"));
1717
export const GAME_JOURNEY_TOOL_OWNERSHIP_AREAS = Object.freeze(requireServerConstant(constants, "GAME_JOURNEY_TOOL_OWNERSHIP_AREAS", "game-journey"));
18+
export const GAME_JOURNEY_RECOMMENDED_TARGETS = Object.freeze(requireServerConstant(constants, "GAME_JOURNEY_RECOMMENDED_TARGETS", "game-journey"));
1819

1920
export function createGameJourneyApiRepository(options = {}) {
2021
return createServerRepositoryClient("game-journey", options);

toolbox/game-journey/game-journey.js

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {
22
GAME_JOURNEY_KEYS,
3+
GAME_JOURNEY_RECOMMENDED_TARGETS,
34
GAME_JOURNEY_STATUS_BY_ID,
45
GAME_JOURNEY_STATUSES,
56
GAME_JOURNEY_SUGGESTED_TOOLS,
@@ -51,6 +52,8 @@ const diagnostics = document.querySelector("[data-journey-diagnostics]");
5152
const searchInput = document.querySelector("[data-journey-search-input]");
5253
const searchStatus = document.querySelector("[data-journey-search-status]");
5354
const completionMetrics = document.querySelector("[data-journey-completion-metrics]");
55+
const recommendedTargets = document.querySelector("[data-journey-recommended-targets]");
56+
const recommendedTargetStatus = document.querySelector("[data-journey-target-status]");
5457

5558
const statTargets = {
5659
open: document.querySelector("[data-journey-stat-open]"),
@@ -78,6 +81,9 @@ let summarySort = {
7881
};
7982
let completionMetricsSnapshot = null;
8083
let completionMetricsDiagnostic = "";
84+
const recommendedTargetValues = new Map(
85+
GAME_JOURNEY_RECOMMENDED_TARGETS.map((target) => [target.key, target.suggestedCount]),
86+
);
8187

8288
function refreshCompletionMetricsSnapshot() {
8389
try {
@@ -819,6 +825,58 @@ function renderCompletionMetrics() {
819825
completionMetrics.append(dashboard);
820826
}
821827

828+
function normalizeTargetCount(value) {
829+
const parsed = Number(value);
830+
if (!Number.isFinite(parsed)) {
831+
return 0;
832+
}
833+
return Math.max(0, Math.trunc(parsed));
834+
}
835+
836+
function renderRecommendedTargets() {
837+
if (!recommendedTargets) {
838+
return;
839+
}
840+
recommendedTargets.innerHTML = "";
841+
if (!GAME_JOURNEY_RECOMMENDED_TARGETS.length) {
842+
recommendedTargets.append(createElement("p", { text: "No recommended planning targets are available." }));
843+
return;
844+
}
845+
846+
const wrapper = createElement("div", { className: "table-wrapper" });
847+
const table = createElement("table", { className: "data-table data-table--fixed" });
848+
table.setAttribute("aria-label", "Game Journey recommended planning targets");
849+
const head = createElement("thead");
850+
const headRow = createElement("tr");
851+
["Target", "Section", "Suggested"].forEach((heading) => {
852+
const cell = createElement("th", { text: heading });
853+
cell.scope = "col";
854+
headRow.append(cell);
855+
});
856+
head.append(headRow);
857+
const body = createElement("tbody");
858+
GAME_JOURNEY_RECOMMENDED_TARGETS.forEach((target) => {
859+
const row = createElement("tr");
860+
row.dataset.journeyRecommendedTarget = target.key;
861+
const labelCell = createElement("td", { text: target.label });
862+
const sectionCell = createElement("td", { text: target.sectionName });
863+
const input = document.createElement("input");
864+
input.type = "number";
865+
input.min = "0";
866+
input.step = "1";
867+
input.value = String(recommendedTargetValues.get(target.key) ?? target.suggestedCount);
868+
input.setAttribute("aria-label", `${target.label} suggested target`);
869+
input.dataset.journeyTargetInput = target.key;
870+
const inputCell = createElement("td");
871+
inputCell.append(input);
872+
row.append(labelCell, sectionCell, inputCell);
873+
body.append(row);
874+
});
875+
table.append(head, body);
876+
wrapper.append(table);
877+
recommendedTargets.append(wrapper);
878+
}
879+
822880
function renderSearchStatus(query, notes) {
823881
if (!searchStatus) {
824882
return;
@@ -1089,6 +1147,7 @@ function render() {
10891147
renderStatScope(selectedStatsNote, notes);
10901148
renderStats(statCounts);
10911149
renderCompletionMetrics();
1150+
renderRecommendedTargets();
10921151
renderSuggestedTools(displayNote);
10931152
renderRecentActivity();
10941153
renderDiagnostics(activeGame, displayNote, notes);
@@ -1256,6 +1315,23 @@ searchInput?.addEventListener("input", () => {
12561315
render();
12571316
});
12581317

1318+
recommendedTargets?.addEventListener("input", (event) => {
1319+
const input = event.target.closest("[data-journey-target-input]");
1320+
if (!input) {
1321+
return;
1322+
}
1323+
const target = GAME_JOURNEY_RECOMMENDED_TARGETS.find((item) => item.key === input.dataset.journeyTargetInput);
1324+
if (!target) {
1325+
return;
1326+
}
1327+
const value = normalizeTargetCount(input.value);
1328+
recommendedTargetValues.set(target.key, value);
1329+
input.value = String(value);
1330+
if (recommendedTargetStatus) {
1331+
recommendedTargetStatus.textContent = `${target.label} suggested target set to ${value}.`;
1332+
}
1333+
});
1334+
12591335
addNoteButton.addEventListener("click", () => {
12601336
if (redirectGuestWriteAction(noteStatus)) {
12611337
return;

toolbox/game-journey/index.html

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,13 @@ <h2>Journey Inspector</h2>
180180
<div class="content-stack content-stack--compact" data-journey-suggested-tools></div>
181181
</div>
182182
</details>
183+
<details class="vertical-accordion" open>
184+
<summary>Recommended Targets</summary>
185+
<div class="accordion-body content-stack">
186+
<div class="content-stack content-stack--compact" data-journey-recommended-targets></div>
187+
<div class="status" role="status" data-journey-target-status>Suggested planning targets are ready to edit.</div>
188+
</div>
189+
</details>
183190
<details class="vertical-accordion" open>
184191
<summary>Recent Activity</summary>
185192
<div class="accordion-body">

0 commit comments

Comments
 (0)