Skip to content

Commit 0512e40

Browse files
committed
Convert Admin Tool Vote to template tool - PR_26160_067-admin-tool-vote-template-conversion
1 parent c7db31d commit 0512e40

6 files changed

Lines changed: 269 additions & 191 deletions

File tree

admin/tool-votes.html

Lines changed: 53 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -22,47 +22,40 @@ <h1>Tool Votes</h1>
2222
</div>
2323
</section>
2424
<section class="section">
25-
<div class="container account-panel" data-toolbox-votes-layout>
26-
<aside class="side-menu" aria-label="Admin pages">
27-
<a href="/admin/analytics.html">Analytics</a>
28-
<a href="/admin/branding.html">Branding</a>
29-
<a href="/admin/controls.html">Controls</a>
30-
<a href="/admin/environments.html">Environments</a>
31-
<a href="/admin/game-migration.html">Game Migration</a>
32-
<a href="/admin/moderation.html">Moderation</a>
33-
<a href="/admin/platform-settings.html">Platform Settings</a>
34-
<a href="/admin/ratings.html">Ratings</a>
35-
<a href="/admin/roles.html">Roles</a>
36-
<a href="/admin/site-settings.html">Site Settings</a>
37-
<a href="/admin/themes.html">Themes</a>
38-
<a class="active" aria-current="page" href="/admin/tool-votes.html">Tool Votes</a>
39-
<a href="/admin/users.html">Users</a>
40-
</aside>
41-
<div class="card">
42-
<div class="card-body content-stack">
25+
<div class="container container--tool-wide">
26+
<div class="tool-workspace tool-workspace--wide tool-workspace--fullscreen-chrome tool-workspace--table-scroll-focus" data-toolbox-votes-layout>
27+
<aside class="tool-column tool-group-platform">
28+
<div class="tool-column-header">
29+
<h2>Admin</h2>
30+
</div>
31+
<div class="accordion-stack">
32+
<details class="vertical-accordion" open>
33+
<summary>Admin Tools</summary>
34+
<nav class="accordion-body content-stack" aria-label="Admin tool pages" data-admin-tool-menu>
35+
<a class="btn btn--compact primary" aria-current="page" href="/admin/tool-votes.html">Tool Votes</a>
36+
<a class="btn btn--compact" href="/admin/environments.html">Environments</a>
37+
<a class="btn btn--compact" href="/admin/users.html">Users</a>
38+
<a class="btn btn--compact" href="/admin/game-migration.html">Game Migration</a>
39+
<a class="btn btn--compact" href="/admin/platform-settings.html">Platform Settings</a>
40+
</nav>
41+
</details>
42+
<details class="vertical-accordion" open>
43+
<summary>Review Scope</summary>
44+
<div class="accordion-body content-stack">
45+
<p>Rows, votes, ordering, sorting, and state edits use the same server API data source as Toolbox Build Path.</p>
46+
<p>Drag/drop order changes are available while the table is sorted by Order.</p>
47+
</div>
48+
</details>
49+
</div>
50+
</aside>
51+
<div data-tool-display-mode data-asset-root="assets/theme-v2/images" data-tool-slug="admin-tool-votes" data-tool-icon-src="assets/theme-v2/images/image-missing.svg" data-tool-character-src="assets/theme-v2/images/image-missing.svg"></div>
52+
<section class="tool-center-panel content-stack" aria-labelledby="admin-tool-votes-title" data-toolbox-votes-panel>
4353
<div>
4454
<div class="kicker">Shared Toolbox Data</div>
45-
<h2>Vote Review</h2>
55+
<h2 id="admin-tool-votes-title">Vote Review</h2>
4656
</div>
4757
<p>Vote totals and order values are read from the server API data source used by Toolbox tiles.</p>
48-
<p class="status" role="status" data-toolbox-votes-status>Loading Toolbox vote totals.</p>
49-
<p class="status" role="status" data-toolbox-votes-drag-status>Drag/drop is available when sorted by Order.</p>
50-
<div class="content-cluster">
51-
<button class="btn btn--compact" type="button" data-toolbox-votes-width-toggle aria-expanded="false">Expand Table Width</button>
52-
<span class="status" role="status" data-toolbox-votes-width-status>Standard table width.</span>
53-
</div>
54-
<div class="content-cluster" aria-label="Selected Toolbox vote item">
55-
<span class="pill">Selected Order: <span data-toolbox-votes-selected-order>None</span></span>
56-
<span class="pill">Selected Group: <span data-toolbox-votes-selected-group>None</span></span>
57-
<span class="pill">Selected Path: <span data-toolbox-votes-selected-path>None</span></span>
58-
</div>
59-
<div class="content-cluster" aria-label="Selected Toolbox metadata editor">
60-
<label>Group <select data-toolbox-votes-group-edit disabled></select></label>
61-
<label>Path <input type="text" data-toolbox-votes-path-edit disabled></label>
62-
<label>Status <select data-toolbox-votes-status-edit disabled></select></label>
63-
<button class="btn btn--compact" type="button" data-toolbox-votes-metadata-save disabled>Update Metadata</button>
64-
</div>
65-
<div class="table-wrapper">
58+
<div class="table-wrapper tool-table-scroll-region" data-toolbox-votes-scroll-region>
6659
<table class="data-table" aria-label="Toolbox vote totals">
6760
<caption>Toolbox Vote Totals</caption>
6861
<thead>
@@ -83,13 +76,35 @@ <h2>Vote Review</h2>
8376
<tbody data-toolbox-votes-body></tbody>
8477
</table>
8578
</div>
86-
</div>
79+
</section>
80+
<aside class="tool-column tool-group-platform">
81+
<div class="tool-column-header">
82+
<h2>Vote Inspector</h2>
83+
</div>
84+
<div class="accordion-stack">
85+
<details class="vertical-accordion" open>
86+
<summary>Status</summary>
87+
<div class="accordion-body content-stack">
88+
<p class="status" role="status" data-toolbox-votes-status>Loading Toolbox vote totals.</p>
89+
<p class="status" role="status" data-toolbox-votes-drag-status>Drag/drop is available when sorted by Order.</p>
90+
</div>
91+
</details>
92+
<details class="vertical-accordion" open>
93+
<summary>Diagnostics</summary>
94+
<div class="accordion-body content-stack">
95+
<p>State edits are limited to planned, wireframe, beta, and complete.</p>
96+
<p>Tool links open the current Toolbox route stored in shared metadata.</p>
97+
</div>
98+
</details>
99+
</div>
100+
</aside>
87101
</div>
88102
</div>
89103
</section>
90104
</main>
91105
<div data-partial="footer"></div>
92106
<script src="assets/theme-v2/js/gamefoundry-partials.js" defer></script>
107+
<script src="assets/theme-v2/js/tool-display-mode.js" defer></script>
93108
<script type="module" src="admin/tool-votes.js"></script>
94109
</body>
95110

admin/tool-votes.js

Lines changed: 50 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,8 @@ import {
77
const status = document.querySelector("[data-toolbox-votes-status]");
88
const dragStatus = document.querySelector("[data-toolbox-votes-drag-status]");
99
const body = document.querySelector("[data-toolbox-votes-body]");
10-
const selectedOrder = document.querySelector("[data-toolbox-votes-selected-order]");
11-
const selectedGroup = document.querySelector("[data-toolbox-votes-selected-group]");
12-
const selectedPath = document.querySelector("[data-toolbox-votes-selected-path]");
1310
const sortButtons = Array.from(document.querySelectorAll("[data-toolbox-votes-sort]"));
1411
const sortHeaders = Array.from(document.querySelectorAll("[data-toolbox-votes-sort-header]"));
15-
const widthLayout = document.querySelector("[data-toolbox-votes-layout]");
16-
const widthToggle = document.querySelector("[data-toolbox-votes-width-toggle]");
17-
const widthStatus = document.querySelector("[data-toolbox-votes-width-status]");
18-
const groupEdit = document.querySelector("[data-toolbox-votes-group-edit]");
19-
const pathEdit = document.querySelector("[data-toolbox-votes-path-edit]");
20-
const statusEdit = document.querySelector("[data-toolbox-votes-status-edit]");
21-
const metadataSave = document.querySelector("[data-toolbox-votes-metadata-save]");
2212
const sortButtonLabels = new Map(sortButtons.map((button) => [
2313
button.dataset.toolboxVotesSort,
2414
button.textContent.trim(),
@@ -30,6 +20,7 @@ const RELEASE_CHANNEL_OPTIONS = Object.freeze([
3020
["beta", "Beta"],
3121
["complete", "Complete"],
3222
]);
23+
const RELEASE_CHANNEL_LABELS = new Map(RELEASE_CHANNEL_OPTIONS);
3324

3425
const SORT_TYPES = Object.freeze({
3526
down: "number",
@@ -97,6 +88,53 @@ function orderCell(voteRow) {
9788
return cell;
9889
}
9990

91+
function releaseChannelLabel(value) {
92+
return RELEASE_CHANNEL_LABELS.get(value) || RELEASE_CHANNEL_LABELS.get("planned");
93+
}
94+
95+
function stateCell(voteRow) {
96+
const cell = document.createElement("td");
97+
const select = document.createElement("select");
98+
const currentState = RELEASE_CHANNEL_LABELS.has(voteRow.releaseChannel) ? voteRow.releaseChannel : "planned";
99+
select.dataset.toolboxVotesState = voteRow.toolId;
100+
select.setAttribute("aria-label", `State for ${voteRow.toolName}`);
101+
RELEASE_CHANNEL_OPTIONS.forEach(([value, label]) => {
102+
const option = document.createElement("option");
103+
option.value = value;
104+
option.textContent = label;
105+
select.append(option);
106+
});
107+
select.value = currentState;
108+
select.addEventListener("click", (event) => {
109+
event.stopPropagation();
110+
});
111+
select.addEventListener("change", () => {
112+
handleStateChange(voteRow, select);
113+
});
114+
cell.append(select);
115+
return cell;
116+
}
117+
118+
function handleStateChange(voteRow, select) {
119+
const nextState = select.value;
120+
if (nextState === voteRow.releaseChannel) {
121+
return;
122+
}
123+
try {
124+
const snapshot = updateToolboxVoteMetadata(voteRow.toolId, {
125+
group: voteRow.group,
126+
path: voteRow.path,
127+
releaseChannel: nextState,
128+
});
129+
renderSnapshot(snapshot, `${voteRow.toolName} state updated to ${releaseChannelLabel(nextState)}. Toolbox Build Path uses the same metadata.`);
130+
selectRow(voteRow.toolId);
131+
} catch (error) {
132+
select.value = RELEASE_CHANNEL_LABELS.has(voteRow.releaseChannel) ? voteRow.releaseChannel : "planned";
133+
const message = error instanceof Error ? error.message : String(error || "Toolbox state update unavailable.");
134+
setStatus(`${voteRow.toolName} state could not be updated. ${message}`);
135+
}
136+
}
137+
100138
function sortValue(voteRow, key) {
101139
if (key === "currentUserVote") {
102140
return voteRow.currentUserVote || "None";
@@ -145,74 +183,8 @@ function updateDragStatus() {
145183
dragStatus.textContent = `Drag/drop row ordering is disabled while sorted by ${sortLabel(sortState.key)}. Sort by Order to reorder.`;
146184
}
147185

148-
function setSelectedDetails(voteRow) {
149-
if (selectedOrder) {
150-
selectedOrder.textContent = voteRow ? String(wholeNumberOrder(voteRow.order)) : "None";
151-
}
152-
if (selectedGroup) {
153-
selectedGroup.textContent = voteRow?.group || "None";
154-
}
155-
if (selectedPath) {
156-
selectedPath.textContent = voteRow?.path || "None";
157-
}
158-
setMetadataEditor(voteRow);
159-
}
160-
161-
function option(value, label = value) {
162-
const node = document.createElement("option");
163-
node.value = value;
164-
node.textContent = label;
165-
return node;
166-
}
167-
168-
function populateGroupOptions(rows) {
169-
if (!groupEdit) {
170-
return;
171-
}
172-
const selectedValue = groupEdit.value;
173-
const groups = [...new Set(rows.map((row) => row.group).filter(Boolean))].sort((left, right) => left.localeCompare(right));
174-
groupEdit.replaceChildren(...groups.map((group) => option(group)));
175-
if (groups.includes(selectedValue)) {
176-
groupEdit.value = selectedValue;
177-
}
178-
}
179-
180-
function populateStatusOptions() {
181-
if (!statusEdit || statusEdit.childElementCount) {
182-
return;
183-
}
184-
statusEdit.replaceChildren(...RELEASE_CHANNEL_OPTIONS.map(([value, label]) => option(value, label)));
185-
}
186-
187-
function setMetadataEditor(voteRow) {
188-
populateStatusOptions();
189-
const disabled = !voteRow;
190-
[groupEdit, pathEdit, statusEdit, metadataSave].forEach((control) => {
191-
if (control) {
192-
control.disabled = disabled;
193-
}
194-
});
195-
if (!voteRow) {
196-
if (groupEdit) groupEdit.value = "";
197-
if (pathEdit) pathEdit.value = "";
198-
if (statusEdit) statusEdit.value = "planned";
199-
return;
200-
}
201-
if (groupEdit) {
202-
groupEdit.value = voteRow.group || "";
203-
}
204-
if (pathEdit) {
205-
pathEdit.value = voteRow.path || "";
206-
}
207-
if (statusEdit) {
208-
statusEdit.value = voteRow.releaseChannel || "planned";
209-
}
210-
}
211-
212186
function selectRow(toolId) {
213187
selectedToolId = String(toolId || "");
214-
const selectedRow = snapshotRows.find((row) => row.toolId === selectedToolId);
215-
setSelectedDetails(selectedRow);
216188
body?.querySelectorAll("tr[data-toolbox-votes-tool-id]").forEach((row) => {
217189
row.setAttribute("aria-selected", String(row.dataset.toolboxVotesToolId === selectedToolId));
218190
});
@@ -280,7 +252,6 @@ function renderRows(rows) {
280252
return;
281253
}
282254
snapshotRows = rows;
283-
populateGroupOptions(snapshotRows);
284255
updateSortHeaders();
285256
updateDragStatus();
286257
const displayRows = sortedRows();
@@ -291,7 +262,7 @@ function renderRows(rows) {
291262
cell.textContent = "No Toolbox vote rows are available.";
292263
row.append(cell);
293264
body.replaceChildren(row);
294-
setSelectedDetails(null);
265+
selectedToolId = "";
295266
return;
296267
}
297268

@@ -320,7 +291,7 @@ function renderRows(rows) {
320291
orderCell(voteRow),
321292
tableCell(voteRow.group),
322293
tableCell(voteRow.path),
323-
tableCell(voteRow.releaseChannelLabel),
294+
stateCell(voteRow),
324295
tableCell(voteRow.up),
325296
tableCell(voteRow.down),
326297
tableCell(voteRow.totalVotes),
@@ -371,37 +342,4 @@ sortButtons.forEach((button) => {
371342
});
372343
});
373344

374-
widthToggle?.addEventListener("click", () => {
375-
const expanded = widthToggle.getAttribute("aria-expanded") !== "true";
376-
widthToggle.setAttribute("aria-expanded", String(expanded));
377-
widthToggle.textContent = expanded ? "Collapse Table Width" : "Expand Table Width";
378-
if (widthLayout) {
379-
widthLayout.dataset.toolboxVotesExpanded = String(expanded);
380-
}
381-
if (widthStatus) {
382-
widthStatus.textContent = expanded
383-
? "Expanded table width; Admin side menu is collapsed."
384-
: "Standard table width.";
385-
}
386-
});
387-
388-
metadataSave?.addEventListener("click", () => {
389-
if (!selectedToolId) {
390-
setStatus("Select a Toolbox row before updating metadata.");
391-
return;
392-
}
393-
try {
394-
const snapshot = updateToolboxVoteMetadata(selectedToolId, {
395-
group: groupEdit?.value || "",
396-
path: pathEdit?.value || "",
397-
releaseChannel: statusEdit?.value || "planned",
398-
});
399-
renderSnapshot(snapshot, "Toolbox metadata updated. Reload Toolbox Build Path to see the SSoT values.");
400-
selectRow(selectedToolId);
401-
} catch (error) {
402-
const message = error instanceof Error ? error.message : String(error || "Toolbox metadata update unavailable.");
403-
setStatus(`Toolbox metadata could not be updated. ${message}`);
404-
}
405-
});
406-
407345
renderToolboxVotes();

0 commit comments

Comments
 (0)