Skip to content

Commit 70c4e07

Browse files
committed
Limit Toolbox voting to users and improve Tool Votes review - PR_26160_063-toolbox-user-voting-and-admin-width
1 parent e187922 commit 70c4e07

9 files changed

Lines changed: 194 additions & 35 deletions

File tree

admin/tool-votes.html

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ <h1>Tool Votes</h1>
2222
</div>
2323
</section>
2424
<section class="section">
25-
<div class="container account-panel">
25+
<div class="container account-panel" data-toolbox-votes-layout>
2626
<aside class="side-menu" aria-label="Admin pages">
2727
<a href="admin/analytics.html">Analytics</a>
2828
<a href="admin/branding.html">Branding</a>
@@ -45,6 +45,10 @@ <h2>Vote Review</h2>
4545
<p>Vote totals and order values are read from the server API data source used by Toolbox tiles.</p>
4646
<p class="status" role="status" data-toolbox-votes-status>Loading Toolbox vote totals.</p>
4747
<p class="status" role="status" data-toolbox-votes-drag-status>Drag/drop is available when sorted by Order.</p>
48+
<div class="content-cluster">
49+
<button class="btn btn--compact" type="button" data-toolbox-votes-width-toggle aria-expanded="false">Expand Table Width</button>
50+
<span class="status" role="status" data-toolbox-votes-width-status>Standard table width.</span>
51+
</div>
4852
<div class="content-cluster" aria-label="Selected Toolbox vote item">
4953
<span class="pill">Selected Order: <span data-toolbox-votes-selected-order>None</span></span>
5054
<span class="pill">Selected Group: <span data-toolbox-votes-selected-group>None</span></span>
@@ -62,6 +66,9 @@ <h2>Vote Review</h2>
6266
<th scope="col" data-toolbox-votes-sort-header="releaseChannelLabel"><button class="btn btn--compact" type="button" data-toolbox-votes-sort="releaseChannelLabel">State</button></th>
6367
<th scope="col" data-toolbox-votes-sort-header="up"><button class="btn btn--compact" type="button" data-toolbox-votes-sort="up">Votes Up</button></th>
6468
<th scope="col" data-toolbox-votes-sort-header="down"><button class="btn btn--compact" type="button" data-toolbox-votes-sort="down">Votes Down</button></th>
69+
<th scope="col" data-toolbox-votes-sort-header="totalVotes"><button class="btn btn--compact" type="button" data-toolbox-votes-sort="totalVotes">Total Votes</button></th>
70+
<th scope="col" data-toolbox-votes-sort-header="upPercent"><button class="btn btn--compact" type="button" data-toolbox-votes-sort="upPercent">Up %</button></th>
71+
<th scope="col" data-toolbox-votes-sort-header="downPercent"><button class="btn btn--compact" type="button" data-toolbox-votes-sort="downPercent">Down %</button></th>
6572
<th scope="col" data-toolbox-votes-sort-header="currentUserVote"><button class="btn btn--compact" type="button" data-toolbox-votes-sort="currentUserVote">Current User Vote</button></th>
6673
</tr>
6774
</thead>

admin/tool-votes.js

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,21 @@ const selectedGroup = document.querySelector("[data-toolbox-votes-selected-group
1111
const selectedPath = document.querySelector("[data-toolbox-votes-selected-path]");
1212
const sortButtons = Array.from(document.querySelectorAll("[data-toolbox-votes-sort]"));
1313
const sortHeaders = Array.from(document.querySelectorAll("[data-toolbox-votes-sort-header]"));
14+
const widthLayout = document.querySelector("[data-toolbox-votes-layout]");
15+
const widthToggle = document.querySelector("[data-toolbox-votes-width-toggle]");
16+
const widthStatus = document.querySelector("[data-toolbox-votes-width-status]");
1417
const sortButtonLabels = new Map(sortButtons.map((button) => [
1518
button.dataset.toolboxVotesSort,
1619
button.textContent.trim(),
1720
]));
1821

1922
const SORT_TYPES = Object.freeze({
2023
down: "number",
24+
downPercent: "number",
2125
order: "number",
26+
totalVotes: "number",
2227
up: "number",
28+
upPercent: "number",
2329
});
2430

2531
let selectedToolId = "";
@@ -55,6 +61,11 @@ function tableCell(value) {
5561
return cell;
5662
}
5763

64+
function percentCell(value) {
65+
const percent = Number(value);
66+
return tableCell(`${Number.isFinite(percent) ? percent : 0}%`);
67+
}
68+
5869
function toolNameCell(voteRow) {
5970
const cell = document.createElement("td");
6071
const link = document.createElement("a");
@@ -211,7 +222,7 @@ function renderRows(rows) {
211222
if (!displayRows.length) {
212223
const row = document.createElement("tr");
213224
const cell = document.createElement("td");
214-
cell.colSpan = 8;
225+
cell.colSpan = 11;
215226
cell.textContent = "No Toolbox vote rows are available.";
216227
row.append(cell);
217228
body.replaceChildren(row);
@@ -247,6 +258,9 @@ function renderRows(rows) {
247258
tableCell(voteRow.releaseChannelLabel),
248259
tableCell(voteRow.up),
249260
tableCell(voteRow.down),
261+
tableCell(voteRow.totalVotes),
262+
percentCell(voteRow.upPercent),
263+
percentCell(voteRow.downPercent),
250264
tableCell(voteRow.currentUserVote || "None"),
251265
);
252266
return row;
@@ -292,4 +306,18 @@ sortButtons.forEach((button) => {
292306
});
293307
});
294308

309+
widthToggle?.addEventListener("click", () => {
310+
const expanded = widthToggle.getAttribute("aria-expanded") !== "true";
311+
widthToggle.setAttribute("aria-expanded", String(expanded));
312+
widthToggle.textContent = expanded ? "Collapse Table Width" : "Expand Table Width";
313+
if (widthLayout) {
314+
widthLayout.dataset.toolboxVotesExpanded = String(expanded);
315+
}
316+
if (widthStatus) {
317+
widthStatus.textContent = expanded
318+
? "Expanded table width; Admin side menu is collapsed."
319+
: "Standard table width.";
320+
}
321+
});
322+
295323
renderToolboxVotes();

assets/theme-v2/css/layout.css

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,14 @@ body.tool-focus-mode .tool-column:last-of-type {
283283
gap: var(--space-18)
284284
}
285285

286+
.account-panel[data-toolbox-votes-expanded="true"] {
287+
grid-template-columns: 1fr
288+
}
289+
290+
.account-panel[data-toolbox-votes-expanded="true"] > .side-menu {
291+
display: none
292+
}
293+
286294
.admin-page-stack {
287295
display: grid;
288296
gap: var(--space-18);

docs_build/dev/reports/coverage_changed_js_guardrail.txt

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,8 @@ Source: Playwright/Chromium built-in V8 coverage from the active Playwright run.
77

88
Changed runtime JS files considered:
99
(0%) src/dev-runtime/server/mock-api-router.mjs - WARNING: changed runtime JS file was not collected by Playwright V8 coverage; advisory only
10-
(0%) toolbox/colors/colors.js - WARNING: changed runtime JS file was not collected by Playwright V8 coverage; advisory only
11-
(0%) toolbox/toolRegistry.js - WARNING: changed runtime JS file was not collected by Playwright V8 coverage; advisory only
12-
(40%) src/engine/api/toolbox-votes-api-client.js - executed lines 37/37; executed functions 2/5
13-
(73%) admin/tool-votes.js - executed lines 268/268; executed functions 24/33
14-
(75%) toolbox/tools-page-accordions.js - executed lines 1034/1034; executed functions 77/103
10+
(96%) toolbox/tools-page-accordions.js - executed lines 1048/1048; executed functions 109/114
11+
(100%) admin/tool-votes.js - executed lines 294/294; executed functions 37/37
1512

1613
Guardrail warnings:
1714
(0%) src/dev-runtime/server/mock-api-router.mjs - WARNING: changed runtime JS file missing from coverage; advisory only
18-
(0%) toolbox/colors/colors.js - WARNING: changed runtime JS file missing from coverage; advisory only
19-
(0%) toolbox/toolRegistry.js - WARNING: changed runtime JS file missing from coverage; advisory only
20-
(40%) src/engine/api/toolbox-votes-api-client.js - WARNING: advisory low coverage below 50%; executed lines 37/37; executed functions 2/5

docs_build/dev/reports/playwright_v8_coverage_report.txt

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,39 +12,37 @@ Note: entry percentages use function coverage when available, otherwise line cov
1212
Note: coverage entries are aggregated across every page/tool where coverageReporter.start(page) and coverageReporter.stop(page) ran.
1313

1414
Exercised tool entry points detected:
15-
(70%) Toolbox Index - exercised 3 runtime JS files
15+
(69%) Toolbox Index - exercised 9 runtime JS files
1616
(0%) Tool Template V2 - not exercised by this Playwright run
17-
(77%) Theme V2 Shared JS - exercised 1 runtime JS files
17+
(74%) Theme V2 Shared JS - exercised 2 runtime JS files
1818

1919
Changed runtime JS files covered:
2020
(0%) src/dev-runtime/server/mock-api-router.mjs - WARNING: changed runtime JS file was not collected by Playwright V8 coverage; advisory only
21-
(0%) toolbox/colors/colors.js - WARNING: changed runtime JS file was not collected by Playwright V8 coverage; advisory only
22-
(0%) toolbox/toolRegistry.js - WARNING: changed runtime JS file was not collected by Playwright V8 coverage; advisory only
23-
(40%) src/engine/api/toolbox-votes-api-client.js - executed lines 37/37; executed functions 2/5
24-
(73%) admin/tool-votes.js - executed lines 268/268; executed functions 24/33
25-
(75%) toolbox/tools-page-accordions.js - executed lines 1034/1034; executed functions 77/103
21+
(96%) toolbox/tools-page-accordions.js - executed lines 1048/1048; executed functions 109/114
22+
(100%) admin/tool-votes.js - executed lines 294/294; executed functions 37/37
2623

2724
Files with executed line/function counts where available:
2825
(38%) src/engine/api/session-api-client.js - executed lines 34/34; executed functions 3/8
29-
(40%) src/engine/api/toolbox-votes-api-client.js - executed lines 37/37; executed functions 2/5
30-
(43%) toolbox/tool-registry-api-client.js - executed lines 148/148; executed functions 9/21
31-
(53%) src/engine/api/server-api-client.js - executed lines 159/159; executed functions 10/19
32-
(73%) admin/tool-votes.js - executed lines 268/268; executed functions 24/33
33-
(75%) toolbox/tools-page-accordions.js - executed lines 1034/1034; executed functions 77/103
26+
(55%) toolbox/project-journey/project-journey.js - executed lines 1003/1003; executed functions 54/99
27+
(57%) toolbox/colors/colors.js - executed lines 2289/2289; executed functions 123/216
28+
(58%) src/engine/api/server-api-client.js - executed lines 159/159; executed functions 11/19
29+
(64%) assets/theme-v2/js/tool-display-mode.js - executed lines 209/209; executed functions 9/14
30+
(71%) toolbox/assets/assets.js - executed lines 519/519; executed functions 42/59
3431
(77%) assets/theme-v2/js/gamefoundry-partials.js - executed lines 547/547; executed functions 37/48
32+
(80%) src/engine/api/toolbox-votes-api-client.js - executed lines 37/37; executed functions 4/5
33+
(85%) toolbox/tool-registry-api-client.js - executed lines 148/148; executed functions 23/27
34+
(96%) toolbox/tools-page-accordions.js - executed lines 1048/1048; executed functions 109/114
35+
(100%) admin/tool-votes.js - executed lines 294/294; executed functions 37/37
36+
(100%) toolbox/assets/assets-api-client.js - executed lines 17/17; executed functions 3/3
37+
(100%) toolbox/colors/palette-api-client.js - executed lines 19/19; executed functions 4/4
38+
(100%) toolbox/project-journey/project-journey-api-client.js - executed lines 12/12; executed functions 2/2
3539
(100%) toolbox/project-workspace/project-workspace-api-client.js - executed lines 12/12; executed functions 2/2
3640

3741
Uncovered or low-coverage changed JS files:
3842
(0%) src/dev-runtime/server/mock-api-router.mjs - WARNING: uncovered changed runtime JS file; advisory only
39-
(0%) toolbox/colors/colors.js - WARNING: uncovered changed runtime JS file; advisory only
40-
(0%) toolbox/toolRegistry.js - WARNING: uncovered changed runtime JS file; advisory only
41-
(40%) src/engine/api/toolbox-votes-api-client.js - WARNING: advisory low coverage; executed lines 37/37
4243

4344
Changed JS files considered:
4445
(0%) src/dev-runtime/server/mock-api-router.mjs - changed JS file not collected as browser runtime coverage
4546
(0%) tests/playwright/tools/ToolboxRoutePages.spec.mjs - changed JS file not collected as browser runtime coverage
46-
(0%) toolbox/colors/colors.js - changed JS file not collected as browser runtime coverage
47-
(0%) toolbox/toolRegistry.js - changed JS file not collected as browser runtime coverage
48-
(40%) src/engine/api/toolbox-votes-api-client.js - changed JS file with browser V8 coverage
49-
(73%) admin/tool-votes.js - changed JS file with browser V8 coverage
50-
(75%) toolbox/tools-page-accordions.js - changed JS file with browser V8 coverage
47+
(96%) toolbox/tools-page-accordions.js - changed JS file with browser V8 coverage
48+
(100%) admin/tool-votes.js - changed JS file with browser V8 coverage
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# PR_26160_063-toolbox-user-voting-and-admin-width
2+
3+
## Branch Validation
4+
5+
- Current branch: `main`
6+
- Expected branch: `main`
7+
- Local branches found: `main`
8+
- Branch validation: PASS
9+
10+
## Summary
11+
12+
- Toolbox vote buttons now remain visible with counts for guests, but guest sessions cannot vote.
13+
- Authenticated selected votes now receive the same `primary` selected button treatment used by other Toolbox controls.
14+
- Admin Tool Votes now receives `Total Votes`, `Up %`, and `Down %` from the server-backed vote snapshot.
15+
- Admin Tool Votes now has an expand/collapse width control that collapses the side menu and gives the table more horizontal room.
16+
- Tool group assignments, tool order rules, and tool status rules were not changed.
17+
18+
## Requirement Checklist
19+
20+
| Requirement | Status | Evidence |
21+
| --- | --- | --- |
22+
| Limit Toolbox voting to authenticated users only | PASS | `toolbox/tools-page-accordions.js` disables vote buttons when `session.authenticated`/`session.userKey` is absent; `src/dev-runtime/server/mock-api-router.mjs` still rejects unauthenticated vote writes. |
23+
| Guests may view Up/Down vote counts but not vote | PASS | Vote controls still render `Up n` and `Down n`; guest buttons are disabled and covered by Playwright guest assertions in `tests/playwright/tools/ToolboxRoutePages.spec.mjs`. |
24+
| Show visible login/sign-in required state for guests | PASS | `data-toolbox-vote-login-required` displays `Login required to vote.` for guest sessions. |
25+
| Keep Up/Down buttons with vote counts | PASS | Existing button rendering is preserved; only disabled state and message were added. |
26+
| Visually identify current user's selected vote with selected Toolbox treatment | PASS | Selected vote buttons toggle `primary` and `aria-pressed`; Playwright validates `primary` on selected Up/Down states. |
27+
| Preserve one-vote-per-user-per-tool behavior | PASS | Existing server row update by `(toolId, userKey)` remains unchanged; Playwright verifies User 1 and User 2 vote counts and switching behavior. |
28+
| Add Total Votes, Up %, Down % columns to Admin > Tool Votes | PASS | `admin/tool-votes.html`, `admin/tool-votes.js`, and server snapshot fields `totalVotes`, `upPercent`, `downPercent` added; Playwright validates values. |
29+
| Add horizontal accordion/equivalent expand/collapse control for more table visibility | PASS | `data-toolbox-votes-width-toggle` toggles `data-toolbox-votes-expanded` on the account panel and collapses the side menu; Playwright validates expanded/collapsed behavior. |
30+
| Do not change tool group assignments, order rules, or status rules | PASS | No registry/status/order assignment files were changed; only vote rendering/snapshot fields and admin table display changed. |
31+
| Do not use inline script/style/event handlers | PASS | Static `rg` checks found no runtime/page inline handlers/styles; only existing Playwright source assertions matched. |
32+
33+
## Validation
34+
35+
- PASS: `node --check admin/tool-votes.js`
36+
- PASS: `node --check toolbox/tools-page-accordions.js`
37+
- PASS: `node --check src/dev-runtime/server/mock-api-router.mjs`
38+
- PASS: `node --check tests/playwright/tools/ToolboxRoutePages.spec.mjs`
39+
- PASS: `git diff --check` (line-ending warnings only, no whitespace errors)
40+
- PASS: Runtime/page inline check: no inline handlers/styles/scripts in changed active runtime/page files.
41+
- PASS: `npx playwright test tests/playwright/tools/ToolboxRoutePages.spec.mjs --project=playwright` (8 passed)
42+
- SKIPPED/invalid command: `npx playwright test tests/playwright/tools/ToolboxRoutePages.spec.mjs --project=chromium`; repo config exposes `ui` and `playwright`, not `chromium`.
43+
44+
## Impacted Lane
45+
46+
- Targeted Toolbox/Admin Tool Votes Playwright lane.
47+
48+
## Skipped Lanes
49+
50+
- Full samples validation: skipped because this PR only changes Toolbox vote controls, Admin Tool Votes table display, and server vote snapshot fields.
51+
- Full repo smoke: skipped per request to run targeted Toolbox/Admin Tool Votes validation only.
52+
53+
## Manual Test Notes
54+
55+
- Guest `Build Game` vote buttons show counts, are disabled, and show `Login required to vote.`
56+
- Authenticated Up/Down selection shows `primary` selected styling and persisted vote counts.
57+
- Admin Tool Votes shows new `Total Votes`, `Up %`, and `Down %` columns.
58+
- Admin Tool Votes width toggle expands the table area by collapsing the Admin side menu, then restores it.

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

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,15 @@ function serverGeneratedUlid(source) {
307307
return `01K2GFSJ0Y${String(9_000_000_000 + hash).padStart(16, "0")}`;
308308
}
309309

310+
function votePercent(count, total) {
311+
const resolvedCount = Number(count);
312+
const resolvedTotal = Number(total);
313+
if (!Number.isFinite(resolvedCount) || !Number.isFinite(resolvedTotal) || resolvedTotal <= 0) {
314+
return 0;
315+
}
316+
return Math.round((resolvedCount / resolvedTotal) * 100);
317+
}
318+
310319
function snapshotAuditFields(index = 0, userKey = MOCK_DB_KEYS.users.forgeBot) {
311320
return createMockDbAuditFields(index, userKey);
312321
}
@@ -830,17 +839,23 @@ class LocalDevMockDataSource {
830839
.map((tool, index) => {
831840
const releaseChannel = getToolReleaseChannel(tool);
832841
const toolVotes = votes.filter((row) => row.toolId === tool.id);
842+
const up = toolVotes.filter((row) => row.direction === "up").length;
843+
const down = toolVotes.filter((row) => row.direction === "down").length;
844+
const totalVotes = up + down;
833845
return {
834846
currentUserVote: toolVotes.find((row) => row.userKey === voterKey)?.direction || "",
835-
down: toolVotes.filter((row) => row.direction === "down").length,
847+
down,
848+
downPercent: votePercent(down, totalVotes),
836849
group: tool.category || "",
837850
order: this.toolboxVoteOrder(tool.id, tool.order ?? index + 1),
838851
path: getToolRoute(tool) || "",
839852
releaseChannel,
840853
releaseChannelLabel: getToolReleaseChannelLabel(releaseChannel),
841854
toolId: tool.id,
842855
toolName: tool.displayName || tool.name || tool.id,
843-
up: toolVotes.filter((row) => row.direction === "up").length,
856+
totalVotes,
857+
up,
858+
upPercent: votePercent(up, totalVotes),
844859
};
845860
})
846861
.sort((left, right) => left.order - right.order || left.toolName.localeCompare(right.toolName)),

0 commit comments

Comments
 (0)