|
1 | 1 | import { |
2 | 2 | GAME_JOURNEY_KEYS, |
| 3 | + GAME_JOURNEY_RECOMMENDED_TARGETS, |
3 | 4 | GAME_JOURNEY_STATUS_BY_ID, |
4 | 5 | GAME_JOURNEY_STATUSES, |
5 | 6 | GAME_JOURNEY_SUGGESTED_TOOLS, |
@@ -51,6 +52,8 @@ const diagnostics = document.querySelector("[data-journey-diagnostics]"); |
51 | 52 | const searchInput = document.querySelector("[data-journey-search-input]"); |
52 | 53 | const searchStatus = document.querySelector("[data-journey-search-status]"); |
53 | 54 | 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]"); |
54 | 57 |
|
55 | 58 | const statTargets = { |
56 | 59 | open: document.querySelector("[data-journey-stat-open]"), |
@@ -78,6 +81,9 @@ let summarySort = { |
78 | 81 | }; |
79 | 82 | let completionMetricsSnapshot = null; |
80 | 83 | let completionMetricsDiagnostic = ""; |
| 84 | +const recommendedTargetValues = new Map( |
| 85 | + GAME_JOURNEY_RECOMMENDED_TARGETS.map((target) => [target.key, target.suggestedCount]), |
| 86 | +); |
81 | 87 |
|
82 | 88 | function refreshCompletionMetricsSnapshot() { |
83 | 89 | try { |
@@ -819,6 +825,58 @@ function renderCompletionMetrics() { |
819 | 825 | completionMetrics.append(dashboard); |
820 | 826 | } |
821 | 827 |
|
| 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 | + |
822 | 880 | function renderSearchStatus(query, notes) { |
823 | 881 | if (!searchStatus) { |
824 | 882 | return; |
@@ -1089,6 +1147,7 @@ function render() { |
1089 | 1147 | renderStatScope(selectedStatsNote, notes); |
1090 | 1148 | renderStats(statCounts); |
1091 | 1149 | renderCompletionMetrics(); |
| 1150 | + renderRecommendedTargets(); |
1092 | 1151 | renderSuggestedTools(displayNote); |
1093 | 1152 | renderRecentActivity(); |
1094 | 1153 | renderDiagnostics(activeGame, displayNote, notes); |
@@ -1256,6 +1315,23 @@ searchInput?.addEventListener("input", () => { |
1256 | 1315 | render(); |
1257 | 1316 | }); |
1258 | 1317 |
|
| 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 | + |
1259 | 1335 | addNoteButton.addEventListener("click", () => { |
1260 | 1336 | if (redirectGuestWriteAction(noteStatus)) { |
1261 | 1337 | return; |
|
0 commit comments