From ca626873a4cf19925229851b0da2a4ff55c2073f Mon Sep 17 00:00:00 2001 From: DavidQ Date: Sat, 20 Jun 2026 14:20:49 -0400 Subject: [PATCH] PR_26171_041 polish idea board production behavior --- assets/theme-v2/css/tables.css | 70 ++- assets/theme-v2/js/tool-display-mode.js | 2 +- .../dev/reports/codex_changed_files.txt | 89 ++-- docs_build/dev/reports/codex_review.diff | 412 +----------------- .../reports/coverage_changed_js_guardrail.txt | 5 +- .../reports/playwright_v8_coverage_report.txt | 11 +- src/shared/toolbox/tool-metadata-inventory.js | 6 +- .../tools/IdeaBoardTableNotes.spec.mjs | 71 ++- .../tools/ToolboxRoutePages.spec.mjs | 26 +- toolbox/idea-board/index.html | 31 +- toolbox/idea-board/index.js | 191 +++++++- 11 files changed, 431 insertions(+), 483 deletions(-) diff --git a/assets/theme-v2/css/tables.css b/assets/theme-v2/css/tables.css index a8c75090a..c39ef19f0 100644 --- a/assets/theme-v2/css/tables.css +++ b/assets/theme-v2/css/tables.css @@ -38,6 +38,67 @@ td { color: var(--text) } +.idea-board-table-caption { + position: relative; + z-index: var(--z-index-sm); + display: flex; + align-items: center; + justify-content: space-between; + gap: var(--space-10); + padding: var(--space-10); + font-weight: var(--font-weight-heavy) +} + +.idea-board-show-filter { + position: relative; + z-index: var(--z-index-md); + color: var(--text); + font-size: var(--font-size-xs); + font-weight: var(--font-weight-bold) +} + +.idea-board-show-filter summary { + display: inline-flex; + align-items: center; + min-height: var(--space-28); + padding: var(--space-3) var(--space-10); + border: var(--border-standard); + border-radius: var(--radius-md); + background: var(--panel-soft); + cursor: pointer; + list-style: none +} + +.idea-board-show-filter summary::-webkit-details-marker { + display: none +} + +.idea-board-show-filter__menu { + position: static; + min-width: 12rem; + margin-top: var(--space-6); + padding: var(--space-10); + border: var(--border-standard); + border-radius: var(--radius-md); + background: var(--panel); + box-shadow: var(--shadow-md) +} + +.idea-board-show-filter__options { + display: grid; + gap: var(--space-6); + margin-top: var(--space-10) +} + +.idea-board-show-filter__option { + display: flex; + align-items: center; + gap: var(--space-8); + color: var(--text); + font-weight: var(--font-weight-regular); + white-space: nowrap +} + .data-table th { color: var(--gold); font-size: var(--font-size-sm); @@ -60,8 +121,13 @@ td { gap: .35em; color: inherit; font: inherit; - line-height: inherit; - vertical-align: baseline + line-height: var(--line-height-single); + vertical-align: baseline; + white-space: nowrap +} + +.idea-board-idea-label__text { + line-height: var(--line-height-single) } .idea-board-idea-chevron { diff --git a/assets/theme-v2/js/tool-display-mode.js b/assets/theme-v2/js/tool-display-mode.js index 9c23ba67e..e77b7a9df 100644 --- a/assets/theme-v2/js/tool-display-mode.js +++ b/assets/theme-v2/js/tool-display-mode.js @@ -157,7 +157,7 @@ const diagnostic = document.createElement("p"); diagnostic.className = "status"; diagnostic.setAttribute("role", "status"); - diagnostic.textContent = "Tool navigation could not load from the server API. Start the local server API and refresh."; + diagnostic.textContent = "Tool navigation is temporarily unavailable. Refresh the page or try again shortly."; body.appendChild(diagnostic); } } diff --git a/docs_build/dev/reports/codex_changed_files.txt b/docs_build/dev/reports/codex_changed_files.txt index 8249c3ff9..3cab64e3e 100644 --- a/docs_build/dev/reports/codex_changed_files.txt +++ b/docs_build/dev/reports/codex_changed_files.txt @@ -1,60 +1,83 @@ -# Codex Changed Files - PR_26171_040-idea-board-production-copy-cleanup +# Codex Changed Files - PR_26171_041-idea-board-production-polish ## Git Workflow -- Current branch: `codex/pr-26171-040-idea-board-production-copy-cleanup`. +- Current branch: `codex/pr-26171-041-idea-board-production-polish`. - Expected starting branch: `main` (PASS before branch creation). -- Created branch: `codex/pr-26171-040-idea-board-production-copy-cleanup`. -- Commit before conflict resolution: `acbc5b113e9e90a4051e810897415ea222040ddd`. -- Initial push result: PASS, branch pushed to `origin/codex/pr-26171-040-idea-board-production-copy-cleanup`. -- PR URL: `https://github.com/ToolboxAid/HTML-JavaScript-Gaming/pull/15`. -- Conflict resolution: merged `origin/main` at `7393b650228c84d266701cc3f4ce3696029dd47e`; conflicts were limited to `codex_changed_files.txt` and `codex_review.diff`. -- Conflict resolution push result: pending until after merge-validation commit. +- Created branch: `codex/pr-26171-041-idea-board-production-polish`. +- Push result: pending until after commit. +- PR URL: pending until after push. - Merge result: pending until after PR validation/merge. - Final main sync: pending until after merge and final pull. -## Changed Files -- docs_build/dev/reports/codex_changed_files.txt -- docs_build/dev/reports/codex_review.diff -- docs_build/dev/reports/coverage_changed_js_guardrail.txt -- docs_build/dev/reports/playwright_v8_coverage_report.txt -- src/dev-runtime/server/local-api-router.mjs -- src/shared/toolbox/tool-metadata-inventory.js -- tests/playwright/tools/IdeaBoardTableNotes.spec.mjs -- tests/playwright/tools/ToolboxRoutePages.spec.mjs -- toolbox/idea-board/index.html -- toolbox/idea-board/index.js +## Git Status +```text + M assets/theme-v2/css/tables.css + M assets/theme-v2/js/tool-display-mode.js + M docs_build/dev/reports/coverage_changed_js_guardrail.txt + M docs_build/dev/reports/playwright_v8_coverage_report.txt + M src/shared/toolbox/tool-metadata-inventory.js + M tests/playwright/tools/IdeaBoardTableNotes.spec.mjs + M tests/playwright/tools/ToolboxRoutePages.spec.mjs + M toolbox/idea-board/index.html + M toolbox/idea-board/index.js +``` + +## Diff Stat +```text + assets/theme-v2/css/tables.css | 70 +++++++- + assets/theme-v2/js/tool-display-mode.js | 2 +- + .../dev/reports/coverage_changed_js_guardrail.txt | 5 +- + .../dev/reports/playwright_v8_coverage_report.txt | 11 +- + src/shared/toolbox/tool-metadata-inventory.js | 6 +- + .../playwright/tools/IdeaBoardTableNotes.spec.mjs | 71 +++++++- + tests/playwright/tools/ToolboxRoutePages.spec.mjs | 26 ++- + toolbox/idea-board/index.html | 31 ++-- + toolbox/idea-board/index.js | 191 ++++++++++++++++++++- + 9 files changed, 368 insertions(+), 45 deletions(-) +``` ## Requirement Evidence -- PASS: Creator-visible Idea Board page copy no longer uses DB-shaped, in-page data model, ID/flag/metadata/seed/debug/mock/test/internal wording. -- PASS: Shared Toolbox metadata for Idea Board now uses simple creator-facing language for short description, description, and progress checklist text. -- PASS: Existing persisted Toolbox metadata rows now source-sync Idea Board copy so the browser-visible registry summary updates from source-controlled text. -- PASS: Idea Board table workflow, Add Idea, Add Note, Edit/Delete, Save/Cancel, Status dropdown, accordion behavior, notes under idea rows, and existing in-page data structure were preserved. -- PASS: Targeted Playwright production-copy checks verify the Idea Board main surface does not expose the prohibited creator-visible wording. -- PASS: Copied/adapted files verified as not applicable for this copy-only PR; no Tool Template V2 file copy was required. +- PASS: Removed creator-visible Create Project from the right-side Next Steps area; no disabled `data-idea-board-create-project` panel remains. +- PASS: Create Project is shown only in a Ready idea row's Actions column. +- PASS: Clicking Create Project changes the idea status from Ready to Project and replaces Create Project/Delete with Open Project and Archive. +- PASS: Project rows show Open Project and Archive and do not show Delete. +- PASS: Added Archived status; archived rows are hidden by default. +- PASS: Archived rows show Restore and Delete. +- PASS: Restore returns archived ideas to their previous non-archived status, with Refining fallback in runtime. +- PASS: Delete is guarded so Project ideas cannot be deleted unless archived first. +- PASS: Status dropdown uses New, Exploring, Refining, Ready, Project, and Archived. +- PASS: Added compact Show checkbox dropdown beside the visible table description line, with default statuses New, Exploring, Refining, Ready, and Project selected and Archived unselected. +- PASS: Show dropdown supports any checkbox combination plus Select All and Clear All. +- PASS: Updated creator-facing copy to "Capture, compare, and shape game ideas." and "Scan, compare, and update early ideas." +- PASS: Replaced creator-visible Tool Display Mode navigation error with safe copy that does not mention server/API/local server/port/implementation details. +- PASS: Chevron renders to the left of the Idea text, inline, same size, same color, and on the same text line; the whole Idea cell remains the expansion target. +- PASS: Notes count remains informational only. +- PASS: Table-first structure, inline Add Idea/Add Note, row-level Save/Cancel, status dropdown edit mode, notes indentation, and single-open accordion behavior remain covered. ## Validation - PASS: `node --check toolbox/idea-board/index.js`. -- PASS: `node --check src/dev-runtime/server/local-api-router.mjs`. +- PASS: `node --check assets/theme-v2/js/tool-display-mode.js`. - PASS: `node --check src/shared/toolbox/tool-metadata-inventory.js`. - PASS: `node --check tests/playwright/tools/IdeaBoardTableNotes.spec.mjs`. - PASS: `node --check tests/playwright/tools/ToolboxRoutePages.spec.mjs`. -- PASS: `npx playwright test tests/playwright/tools/IdeaBoardTableNotes.spec.mjs --project=playwright --workers=1 --reporter=line`. -- PASS: `npx playwright test tests/playwright/tools/ToolboxRoutePages.spec.mjs --project=playwright --workers=1 --reporter=line -g "Idea Board launches"`. +- PASS: `npx playwright test tests/playwright/tools/IdeaBoardTableNotes.spec.mjs --project=playwright --workers=1 --reporter=line --timeout=90000`. +- PASS: `npx playwright test tests/playwright/tools/ToolboxRoutePages.spec.mjs --project=playwright --workers=1 --reporter=line -g "Idea Board launches" --timeout=90000`. +- PASS: `npm run test:workspace-v2` (workspace-contract lane, 5 passed). - PASS: `git diff --check`. -- PASS: Post-conflict rerun completed after merging `origin/main` at `7393b650228c84d266701cc3f4ce3696029dd47e`. - PASS: Playwright V8 coverage report produced because runtime JavaScript changed. -- WARN: Coverage report marks `src/dev-runtime/server/local-api-router.mjs` and `src/shared/toolbox/tool-metadata-inventory.js` as not collected by browser V8 coverage; advisory only per project instructions. +- WARN: Coverage report marks `src/shared/toolbox/tool-metadata-inventory.js` as not collected by browser V8 coverage; advisory only per project instructions. - SKIPPED: Full samples smoke was not run per request. ## ZIP -- Path: `tmp/PR_26171_040-idea-board-production-copy-cleanup_delta.zip`. -- Size: final size reported in the delivery summary after conflict-resolution ZIP refresh. +- Path: `tmp/PR_26171_041-idea-board-production-polish_delta.zip`. +- Size: `50677` bytes before final report refresh; final size reported in the delivery summary. - Contents: + - assets/theme-v2/css/tables.css + - assets/theme-v2/js/tool-display-mode.js - docs_build/dev/reports/codex_changed_files.txt - docs_build/dev/reports/codex_review.diff - docs_build/dev/reports/coverage_changed_js_guardrail.txt - docs_build/dev/reports/playwright_v8_coverage_report.txt - - src/dev-runtime/server/local-api-router.mjs - src/shared/toolbox/tool-metadata-inventory.js - tests/playwright/tools/IdeaBoardTableNotes.spec.mjs - tests/playwright/tools/ToolboxRoutePages.spec.mjs diff --git a/docs_build/dev/reports/codex_review.diff b/docs_build/dev/reports/codex_review.diff index 0e5fe9981..aa580069d 100644 --- a/docs_build/dev/reports/codex_review.diff +++ b/docs_build/dev/reports/codex_review.diff @@ -1,420 +1,22 @@ diff --git a/docs_build/dev/reports/codex_changed_files.txt b/docs_build/dev/reports/codex_changed_files.txt -index cd256bd46..8249c3ff9 100644 +index f45918591..3cab64e3e 100644 --- a/docs_build/dev/reports/codex_changed_files.txt +++ b/docs_build/dev/reports/codex_changed_files.txt -@@ -1,39 +1,62 @@ --# Codex Changed Files - PR_26171_059-rollback-restore-plan -+# Codex Changed Files - PR_26171_040-idea-board-production-copy-cleanup - - ## Git Workflow --- Verified starting branch: `main`. --- Verified repo scope: clean after removing leftover local-only PR_26171_057 report artifacts. --- Pulled latest `origin/main`: `20fd280c608917b960b3080484a5d28c51990ccb`. --- Created branch: `pr/26171-059-rollback-restore-plan`. --- Push result: pending until after commit. --- PR URL: pending until after push. -+- Current branch: `codex/pr-26171-040-idea-board-production-copy-cleanup`. -+- Expected starting branch: `main` (PASS before branch creation). -+- Created branch: `codex/pr-26171-040-idea-board-production-copy-cleanup`. -+- Commit before conflict resolution: `acbc5b113e9e90a4051e810897415ea222040ddd`. -+- Initial push result: PASS, branch pushed to `origin/codex/pr-26171-040-idea-board-production-copy-cleanup`. -+- PR URL: `https://github.com/ToolboxAid/HTML-JavaScript-Gaming/pull/15`. -+- Conflict resolution: merged `origin/main` at `7393b650228c84d266701cc3f4ce3696029dd47e`; conflicts were limited to `codex_changed_files.txt` and `codex_review.diff`. -+- Conflict resolution push result: pending until after merge-validation commit. - - Merge result: pending until after PR validation/merge. - - Final main sync: pending until after merge and final pull. - - ## Changed Files --- docs_build/dev/reports/PR_26171_059-rollback-restore-plan.md --- docs_build/dev/reports/PR_26171_059-validation.md --- docs_build/dev/reports/PR_26171_059-manual-validation-notes.md --- docs_build/dev/reports/codex_review.diff - - docs_build/dev/reports/codex_changed_files.txt -+- docs_build/dev/reports/codex_review.diff -+- docs_build/dev/reports/coverage_changed_js_guardrail.txt -+- docs_build/dev/reports/playwright_v8_coverage_report.txt -+- src/dev-runtime/server/local-api-router.mjs -+- src/shared/toolbox/tool-metadata-inventory.js -+- tests/playwright/tools/IdeaBoardTableNotes.spec.mjs -+- tests/playwright/tools/ToolboxRoutePages.spec.mjs -+- toolbox/idea-board/index.html -+- toolbox/idea-board/index.js - - ## Requirement Evidence --- PASS: Documented Local API sign-in recovery requirements. --- PASS: Documented env diagnostics and runtime port requirements. --- PASS: Documented toolbox image restoration requirements. --- PASS: Documented Text To Speech engine/audio rebuild requirements. --- PASS: Documented Game Journey table correction requirements. --- PASS: Documented Game Journey post-rollback verification requirements. --- PASS: Documented reapply rules requiring clean `main`, scoped branches, and no disconnected branch reuse. --- PASS: Documented discarded contaminated work list. --- PASS: No implementation reapply in this PR. -+- PASS: Creator-visible Idea Board page copy no longer uses DB-shaped, in-page data model, ID/flag/metadata/seed/debug/mock/test/internal wording. -+- PASS: Shared Toolbox metadata for Idea Board now uses simple creator-facing language for short description, description, and progress checklist text. -+- PASS: Existing persisted Toolbox metadata rows now source-sync Idea Board copy so the browser-visible registry summary updates from source-controlled text. -+- PASS: Idea Board table workflow, Add Idea, Add Note, Edit/Delete, Save/Cancel, Status dropdown, accordion behavior, notes under idea rows, and existing in-page data structure were preserved. -+- PASS: Targeted Playwright production-copy checks verify the Idea Board main surface does not expose the prohibited creator-visible wording. -+- PASS: Copied/adapted files verified as not applicable for this copy-only PR; no Tool Template V2 file copy was required. - - ## Validation --- PASS: `npm run test:playwright:static`. --- PASS: Restored unrelated generated validation report churn after static validation. -+- PASS: `node --check toolbox/idea-board/index.js`. -+- PASS: `node --check src/dev-runtime/server/local-api-router.mjs`. -+- PASS: `node --check src/shared/toolbox/tool-metadata-inventory.js`. -+- PASS: `node --check tests/playwright/tools/IdeaBoardTableNotes.spec.mjs`. -+- PASS: `node --check tests/playwright/tools/ToolboxRoutePages.spec.mjs`. -+- PASS: `npx playwright test tests/playwright/tools/IdeaBoardTableNotes.spec.mjs --project=playwright --workers=1 --reporter=line`. -+- PASS: `npx playwright test tests/playwright/tools/ToolboxRoutePages.spec.mjs --project=playwright --workers=1 --reporter=line -g "Idea Board launches"`. - - PASS: `git diff --check`. --- NOT RUN: `npm run dev:local-api` by design for docs/static-only scope. --- NOT RUN: `npm run test:workspace-v2` by design for docs/static-only scope. -+- PASS: Post-conflict rerun completed after merging `origin/main` at `7393b650228c84d266701cc3f4ce3696029dd47e`. -+- PASS: Playwright V8 coverage report produced because runtime JavaScript changed. -+- WARN: Coverage report marks `src/dev-runtime/server/local-api-router.mjs` and `src/shared/toolbox/tool-metadata-inventory.js` as not collected by browser V8 coverage; advisory only per project instructions. -+- SKIPPED: Full samples smoke was not run per request. +@@ -70,4 +70,16 @@ ## ZIP --- Path: `tmp/PR_26171_059-rollback-restore-plan_delta.zip`. -\ No newline at end of file -+- Path: `tmp/PR_26171_040-idea-board-production-copy-cleanup_delta.zip`. -+- Size: final size reported in the delivery summary after conflict-resolution ZIP refresh. + - Path: `tmp/PR_26171_041-idea-board-production-polish_delta.zip`. +-- Size and contents: reported after final ZIP generation. ++- Size: `50677` bytes before final report refresh; final size reported in the delivery summary. +- Contents: ++ - assets/theme-v2/css/tables.css ++ - assets/theme-v2/js/tool-display-mode.js + - docs_build/dev/reports/codex_changed_files.txt + - docs_build/dev/reports/codex_review.diff + - docs_build/dev/reports/coverage_changed_js_guardrail.txt + - docs_build/dev/reports/playwright_v8_coverage_report.txt -+ - src/dev-runtime/server/local-api-router.mjs + - src/shared/toolbox/tool-metadata-inventory.js + - tests/playwright/tools/IdeaBoardTableNotes.spec.mjs + - tests/playwright/tools/ToolboxRoutePages.spec.mjs + - toolbox/idea-board/index.html + - toolbox/idea-board/index.js -diff --git a/docs_build/dev/reports/coverage_changed_js_guardrail.txt b/docs_build/dev/reports/coverage_changed_js_guardrail.txt -index 24989fe63..076d27690 100644 ---- a/docs_build/dev/reports/coverage_changed_js_guardrail.txt -+++ b/docs_build/dev/reports/coverage_changed_js_guardrail.txt -@@ -6,7 +6,10 @@ Missing changed runtime JS files are WARN, not FAIL. - Source: Playwright/Chromium built-in V8 coverage from the active Playwright run. - - Changed runtime JS files considered: --(70%) toolbox/idea-board/index.js - executed lines 481/481; executed functions 23/33 -+(0%) src/dev-runtime/server/local-api-router.mjs - WARNING: changed runtime JS file was not collected by Playwright V8 coverage; advisory only -+(0%) src/shared/toolbox/tool-metadata-inventory.js - WARNING: changed runtime JS file was not collected by Playwright V8 coverage; advisory only -+(70%) toolbox/idea-board/index.js - executed lines 480/480; executed functions 23/33 - - Guardrail warnings: --(100%) none - no changed runtime JS coverage warnings -+(0%) src/dev-runtime/server/local-api-router.mjs - WARNING: changed runtime JS file missing from coverage; advisory only -+(0%) src/shared/toolbox/tool-metadata-inventory.js - WARNING: changed runtime JS file missing from coverage; advisory only -diff --git a/docs_build/dev/reports/playwright_v8_coverage_report.txt b/docs_build/dev/reports/playwright_v8_coverage_report.txt -index e9decee67..cb35b4d50 100644 ---- a/docs_build/dev/reports/playwright_v8_coverage_report.txt -+++ b/docs_build/dev/reports/playwright_v8_coverage_report.txt -@@ -17,7 +17,9 @@ Exercised tool entry points detected: - (63%) Theme V2 Shared JS - exercised 2 runtime JS files - - Changed runtime JS files covered: --(70%) toolbox/idea-board/index.js - executed lines 481/481; executed functions 23/33 -+(0%) src/dev-runtime/server/local-api-router.mjs - WARNING: changed runtime JS file was not collected by Playwright V8 coverage; advisory only -+(0%) src/shared/toolbox/tool-metadata-inventory.js - WARNING: changed runtime JS file was not collected by Playwright V8 coverage; advisory only -+(70%) toolbox/idea-board/index.js - executed lines 480/480; executed functions 23/33 - - Files with executed line/function counts where available: - (25%) src/api/session-api-client.js - executed lines 68/68; executed functions 3/12 -@@ -28,14 +30,17 @@ Files with executed line/function counts where available: - (65%) src/api/public-config-client.js - executed lines 209/209; executed functions 17/26 - (67%) src/api/game-journey-completion-api-client.js - executed lines 15/15; executed functions 2/3 - (67%) toolbox/game-workspace/game-workspace-api-client.js - executed lines 20/20; executed functions 2/3 --(70%) toolbox/idea-board/index.js - executed lines 481/481; executed functions 23/33 -+(70%) toolbox/idea-board/index.js - executed lines 480/480; executed functions 23/33 - (78%) toolbox/tools-page-accordions.js - executed lines 1156/1156; executed functions 87/111 - (86%) toolbox/tool-registry-api-client.js - executed lines 155/155; executed functions 25/29 - - Uncovered or low-coverage changed JS files: --(100%) none - no low-coverage changed runtime JS files -+(0%) src/dev-runtime/server/local-api-router.mjs - WARNING: uncovered changed runtime JS file; advisory only -+(0%) src/shared/toolbox/tool-metadata-inventory.js - WARNING: uncovered changed runtime JS file; advisory only - - Changed JS files considered: -+(0%) src/dev-runtime/server/local-api-router.mjs - changed JS file not collected as browser runtime coverage -+(0%) src/shared/toolbox/tool-metadata-inventory.js - changed JS file not collected as browser runtime coverage - (0%) tests/playwright/tools/IdeaBoardTableNotes.spec.mjs - changed JS file not collected as browser runtime coverage - (0%) tests/playwright/tools/ToolboxRoutePages.spec.mjs - changed JS file not collected as browser runtime coverage - (70%) toolbox/idea-board/index.js - changed JS file with browser V8 coverage -diff --git a/src/dev-runtime/server/local-api-router.mjs b/src/dev-runtime/server/local-api-router.mjs -index f272c970b..9b98fca14 100644 ---- a/src/dev-runtime/server/local-api-router.mjs -+++ b/src/dev-runtime/server/local-api-router.mjs -@@ -1427,7 +1427,7 @@ function normalizedToolKey(row) { - return String(row?.toolKey || row?.toolId || row?.id || "").trim(); - } - --const SOURCE_CONTROLLED_TOOLBOX_TOOL_IDS = new Set(["game-workspace", "messages", "tags", "text-to-speech", "users"]); -+const SOURCE_CONTROLLED_TOOLBOX_TOOL_IDS = new Set(["game-workspace", "idea-board", "messages", "tags", "text-to-speech", "users"]); - const SOURCE_CONTROLLED_TOOLBOX_METADATA_FIELDS = Object.freeze([ - "active", - "adminOnly", -diff --git a/src/shared/toolbox/tool-metadata-inventory.js b/src/shared/toolbox/tool-metadata-inventory.js -index 130f70814..db04eac5b 100644 ---- a/src/shared/toolbox/tool-metadata-inventory.js -+++ b/src/shared/toolbox/tool-metadata-inventory.js -@@ -72,14 +72,14 @@ export const TOOL_REGISTRY = Object.freeze([ - "id": "idea-board", - "name": "Idea Board", - "displayName": "Idea Board", -- "shortDescription": "Capture creator notebook cards before a project exists.", -+ "shortDescription": "Capture, compare, and shape game ideas in a table.", - "shortLabel": "Idea Board", - "path": "idea-board", - "folderName": "idea-board", - "entryPoint": "idea-board/index.html", - "badge": "/assets/theme-v2/images/badges/game-design.png", - "tool": "/assets/theme-v2/images/tools/game-design.png", -- "description": "Capture creator notebook cards, board views, lists, notes, tags, and placeholder project creation before a project exists.", -+ "description": "Capture, compare, and shape game ideas with notes under each idea row.", - "category": "Idea", - "colorGroup": "tool-group-idea", - "active": true, -@@ -91,9 +91,9 @@ export const TOOL_REGISTRY = Object.freeze([ - "status": "Wireframe", - "releaseChannel": "wireframe", - "progressChecklist": [ -- "Wireframe notebook sections visible", -- "Create Project placeholder remains disabled", -- "No persistence or project record creation" -+ "Idea table workflow visible", -+ "Add Idea and Add Note actions remain inline", -+ "Create Project remains unavailable until an idea is ready" - ], - "deferred": false, - "hidden": false, -diff --git a/tests/playwright/tools/IdeaBoardTableNotes.spec.mjs b/tests/playwright/tools/IdeaBoardTableNotes.spec.mjs -index b115c4191..f5544ca88 100644 ---- a/tests/playwright/tools/IdeaBoardTableNotes.spec.mjs -+++ b/tests/playwright/tools/IdeaBoardTableNotes.spec.mjs -@@ -90,7 +90,13 @@ async function expectExpandedNotesChildIndentation(page, ideaId, expectedInputRo - } - } - --test("Idea Board uses DB-shaped accordion table ideas and notes", async ({ page }) => { -+async function expectProductionCopy(page) { -+ await expect(page.locator("main")).not.toContainText( -+ /\bDB-shaped\b|\bin-page data model\b|\buserId\b|\bideaId\b|\bnoteId\b|\bsystem flag\b|\bmetadata\b|\bseed\b|\bdebug\b|\bselected context\b|\bmock\b|\btest\b|\binternal implementation\b|\bplaceholder\b|\bproject records\b|\bmutating API\b|\bauth\b|\bAI\b|\bdatabase behavior\b/i, -+ ); -+} -+ -+test("Idea Board uses accordion table ideas and notes", async ({ page }) => { - const server = await startRepoServer(); - const previousApiUrl = process.env.GAMEFOUNDRY_API_URL; - const previousSiteUrl = process.env.GAMEFOUNDRY_SITE_URL; -@@ -121,6 +127,7 @@ test("Idea Board uses DB-shaped accordion table ideas and notes", async ({ page - try { - await page.goto(`${server.baseUrl}/toolbox/idea-board/index.html`, { waitUntil: "networkidle" }); - await expect(page.getByRole("heading", { level: 1, name: "Idea Board" })).toBeVisible(); -+ await expectProductionCopy(page); - await expect(page.locator("[data-idea-board-table] > thead th[scope='col']")).toHaveText([ - "Idea", - "Pitch", -@@ -160,6 +167,7 @@ test("Idea Board uses DB-shaped accordion table ideas and notes", async ({ page - await expect(page.locator("[data-idea-board-expanded-row]")).toHaveCount(0); - await page.locator("[data-idea-board-idea-cell='top-thoughts']").click(); - await expect(page.locator("[data-idea-board-expanded-row='top-thoughts']")).toBeVisible(); -+ await expectProductionCopy(page); - await expectIdeaChevron(page, "top-thoughts", "gfs-chevron-up.svg"); - await expect(page.locator("[data-idea-board-idea-row='top-thoughts'] + [data-idea-board-expanded-row='top-thoughts']")).toHaveCount(1); - await expect(page.locator("[data-idea-board-expanded-row='top-thoughts'] [data-idea-board-notes-header]")).toHaveCount(0); -@@ -179,9 +187,9 @@ test("Idea Board uses DB-shaped accordion table ideas and notes", async ({ page - await systemNote.locator("[data-idea-board-note-action='edit']").click(); - await expect(page.locator("[data-idea-board-note-input-row] [data-idea-board-note-action]")).toHaveText(["Save", "Cancel"]); - await expectExpandedNotesChildIndentation(page, "top-thoughts", 1); -- await page.locator("[data-idea-board-note-input]").fill("System note can be edited in-place."); -+ await page.locator("[data-idea-board-note-input]").fill("Starter note can be edited in place."); - await page.locator("[data-idea-board-note-action='save']").click(); -- await expect(page.locator("[data-idea-board-notes-table='top-thoughts']")).toContainText("System note can be edited in-place."); -+ await expect(page.locator("[data-idea-board-notes-table='top-thoughts']")).toContainText("Starter note can be edited in place."); - await expect(page.locator("[data-idea-board-system-note] [data-idea-board-note-action='delete']")).toHaveCount(0); - - await page.locator("[data-idea-board-add-note='top-thoughts']").click(); -diff --git a/tests/playwright/tools/ToolboxRoutePages.spec.mjs b/tests/playwright/tools/ToolboxRoutePages.spec.mjs -index 6a1f1ebe3..b3033e10d 100644 ---- a/tests/playwright/tools/ToolboxRoutePages.spec.mjs -+++ b/tests/playwright/tools/ToolboxRoutePages.spec.mjs -@@ -192,6 +192,12 @@ async function expectExpandedNotesChildIndentation(page, ideaId, expectedInputRo - } - } - -+async function expectIdeaBoardProductionCopy(page) { -+ await expect(page.locator("main")).not.toContainText( -+ /\bDB-shaped\b|\bin-page data model\b|\buserId\b|\bideaId\b|\bnoteId\b|\bsystem flag\b|\bmetadata\b|\bseed\b|\bdebug\b|\bselected context\b|\bmock\b|\btest\b|\binternal implementation\b|\bplaceholder\b|\bproject records\b|\bmutating API\b|\bauth\b|\bAI\b|\bdatabase behavior\b/i, -+ ); -+} -+ - test("tools route aliases render toolbox tool pages", async ({ page }) => { - const server = await startRepoServer(); - const failedRequests = []; -@@ -288,6 +294,7 @@ test("Idea Board launches from Toolbox with accordion table notes model", async - await page.waitForURL(/\/toolbox\/idea-board\/index\.html$/); - await page.waitForLoadState("networkidle"); - await expect(page.getByRole("heading", { level: 1, name: "Idea Board" })).toBeVisible(); -+ await expectIdeaBoardProductionCopy(page); - const ideaBoardSections = await page.locator("[data-idea-board-section]").evaluateAll((sections) => ( - sections.map((section) => section.getAttribute("data-idea-board-section")) - )); -@@ -310,11 +317,12 @@ test("Idea Board launches from Toolbox with accordion table notes model", async - await expect(page.locator("[data-idea-board-notes-count='sky-orchard']")).toHaveText("3 Notes"); - await expect(page.locator("[data-idea-board-notes-count='clockwork-courier']")).toHaveText("0 Notes"); - await expectIdeaChevron(page, "top-thoughts", "gfs-chevron-down.svg"); -- await expect(page.locator("[data-idea-board-status]")).toHaveText("Idea Board table edits are in-page only. No project records, auth, AI, or database behavior is connected."); -+ await expect(page.locator("[data-idea-board-status]")).toHaveText("Ready to shape ideas and notes."); - await page.locator("[data-idea-board-notes-count='top-thoughts']").click(); - await expect(page.locator("[data-idea-board-expanded-row]")).toHaveCount(0); - await page.locator("[data-idea-board-idea-cell='top-thoughts']").click(); - await expect(page.locator("[data-idea-board-expanded-row='top-thoughts']")).toBeVisible(); -+ await expectIdeaBoardProductionCopy(page); - await expectIdeaChevron(page, "top-thoughts", "gfs-chevron-up.svg"); - await expect(page.locator("[data-idea-board-idea-row='top-thoughts'] + [data-idea-board-expanded-row='top-thoughts']")).toHaveCount(1); - await expect(page.locator("[data-idea-board-expanded-row='top-thoughts'] [data-idea-board-notes-header]")).toHaveCount(0); -diff --git a/toolbox/idea-board/index.html b/toolbox/idea-board/index.html -index a74e747be..214a303fb 100644 ---- a/toolbox/idea-board/index.html -+++ b/toolbox/idea-board/index.html -@@ -6,7 +6,7 @@ - - - Idea Board - GameFoundryStudio -- -+ - - - -@@ -18,7 +18,7 @@ -
-
Toolbox
-

Idea Board

--

Table-first creator notebook for ideas before a project exists.

-+

Capture, compare, and shape game ideas in one table.

-
- -
-@@ -32,21 +32,21 @@ -
- Workflow -
--

Review ideas in the work table, then expand an idea row to manage its notes in place.

-+

Review ideas in the table, then expand an idea row to manage its notes.

-
-
-
- Status -
--

Statuses remain governance labels only: New, Exploring, Parked, Ready to Shape.

-+

Use status to show where each idea stands: New, Exploring, Parked, or Ready to Shape.

-
-
- - -
-
--

Idea Work Surface

--

The primary surface is a table so creators can scan, compare, and govern early ideas without a form-first workflow.

-+

Ideas

-+

Scan, compare, and update early ideas without leaving the table.

-
- - -@@ -66,27 +66,27 @@ - -
Idea Board table with expandable notes rows
- +
Idea Board table with expandable notes rows
@@ -69,14 +81,7 @@

Ideas

Next Steps

-
- Create Project -
-

Create Project will be available after an idea is ready.

- -
-
-
+
Notes

Add Note opens a new note row under the expanded idea. Notes can be edited, and removable notes can be deleted.

diff --git a/toolbox/idea-board/index.js b/toolbox/idea-board/index.js index 8f588c3c3..2b74e797d 100644 --- a/toolbox/idea-board/index.js +++ b/toolbox/idea-board/index.js @@ -1,4 +1,5 @@ -const statusOptions = Object.freeze(["New", "Exploring", "Parked", "Ready to Shape"]); +const statusOptions = Object.freeze(["New", "Exploring", "Refining", "Ready", "Project", "Archived"]); +const defaultVisibleStatuses = Object.freeze(["New", "Exploring", "Refining", "Ready", "Project"]); const userId = "user-1"; const ideaTable = [ @@ -79,6 +80,7 @@ const state = { editingNoteId: null, addingIdea: false, addingNoteIdeaId: null, + visibleStatuses: new Set(defaultVisibleStatuses), }; function today() { @@ -98,6 +100,34 @@ function noteCountLabel(ideaId) { return `${count} ${count === 1 ? "Note" : "Notes"}`; } +function isArchived(record) { + return record.status === "Archived"; +} + +function isProject(record) { + return record.status === "Project"; +} + +function visibleIdeas() { + return ideaTable.filter((record) => state.visibleStatuses.has(record.status)); +} + +function previousStatusForRestore(record) { + return statusOptions.includes(record.previousStatus) && record.previousStatus !== "Archived" + ? record.previousStatus + : "Refining"; +} + +function rememberPreviousStatus(record) { + if (record.status !== "Archived") { + record.previousStatus = record.status; + } +} + +function canDeleteIdea(record) { + return !isProject(record) || isArchived(record); +} + function cell(text) { const td = document.createElement("td"); td.textContent = text; @@ -113,6 +143,25 @@ function actionButton(label, action, datasetName, variant = "") { return control; } +function renderStatusFilter(root) { + const options = root.querySelector("[data-idea-board-status-options]"); + if (!options) return; + options.replaceChildren(); + for (const status of statusOptions) { + const label = document.createElement("label"); + label.className = "idea-board-show-filter__option"; + const input = document.createElement("input"); + input.type = "checkbox"; + input.value = status; + input.checked = state.visibleStatuses.has(status); + input.dataset.ideaBoardStatusFilterOption = status; + const text = document.createElement("span"); + text.textContent = status; + label.append(input, text); + options.append(label); + } +} + function textInput(label, value = "") { const input = document.createElement("input"); input.type = "text"; @@ -199,7 +248,7 @@ function renderIdeaRow(tbody, record) { chevron.setAttribute("aria-hidden", "true"); chevron.dataset.ideaBoardChevron = record.ideaId; chevron.dataset.ideaBoardChevronIcon = chevronIcon; - ideaLabel.append(ideaText, chevron); + ideaLabel.append(chevron, ideaText); idea.append(ideaLabel); row.append(idea); row.append(cell(record.pitch)); @@ -214,11 +263,28 @@ function renderIdeaRow(tbody, record) { row.append(notes); const actions = document.createElement("td"); - actions.append( - actionButton("Edit", "edit", "ideaBoardIdeaAction"), - " ", - actionButton("Delete", "delete", "ideaBoardIdeaAction"), - ); + if (isArchived(record)) { + actions.append( + actionButton("Restore", "restore", "ideaBoardIdeaAction"), + " ", + actionButton("Delete", "delete", "ideaBoardIdeaAction"), + ); + } else { + actions.append(actionButton("Edit", "edit", "ideaBoardIdeaAction")); + if (record.status === "Ready") { + actions.append(" ", actionButton("Create Project", "create-project", "ideaBoardIdeaAction", "primary")); + } + if (isProject(record)) { + actions.append( + " ", + actionButton("Open Project", "open-project", "ideaBoardIdeaAction", "primary"), + " ", + actionButton("Archive", "archive", "ideaBoardIdeaAction"), + ); + } else { + actions.append(" ", actionButton("Delete", "delete", "ideaBoardIdeaAction")); + } + } row.append(actions); tbody.append(row); } @@ -322,8 +388,9 @@ function renderAddIdeaRow(tbody) { function render(root) { const tbody = root.querySelector("[data-idea-board-ideas-body]"); if (!tbody) return; + renderStatusFilter(root); tbody.replaceChildren(); - for (const record of ideaTable) { + for (const record of visibleIdeas()) { if (state.editingIdeaId === record.ideaId) { renderIdeaInputRow(tbody, record); } else { @@ -361,7 +428,11 @@ function saveIdeaRow(root, row) { } record.idea = idea; record.pitch = pitch; + if (status === "Archived" && record.status !== "Archived") { + record.previousStatus = record.status; + } record.status = status; + rememberPreviousStatus(record); record.updated = today(); state.editingIdeaId = null; updateStatus(root, `Updated ${record.idea}.`); @@ -427,6 +498,10 @@ function deleteIdea(root, ideaId) { updateStatus(root, "Idea Board could not delete that idea."); return; } + if (!canDeleteIdea(ideaTable[index])) { + updateStatus(root, "Archive this project before deleting it."); + return; + } const [removed] = ideaTable.splice(index, 1); for (let noteIndex = noteTable.length - 1; noteIndex >= 0; noteIndex -= 1) { if (noteTable[noteIndex].ideaId === ideaId) noteTable.splice(noteIndex, 1); @@ -438,6 +513,61 @@ function deleteIdea(root, ideaId) { render(root); } +function createProject(root, ideaId) { + const record = ideaRecord(ideaId); + if (!record) { + updateStatus(root, "Idea Board could not update that idea."); + return; + } + if (record.status !== "Ready") { + updateStatus(root, "Set this idea to Ready before creating a project."); + return; + } + record.status = "Project"; + record.previousStatus = "Project"; + record.updated = today(); + updateStatus(root, `${record.idea} is now a project.`); + render(root); +} + +function archiveIdea(root, ideaId) { + const record = ideaRecord(ideaId); + if (!record) { + updateStatus(root, "Idea Board could not archive that idea."); + return; + } + if (record.status !== "Archived") record.previousStatus = record.status; + record.status = "Archived"; + record.updated = today(); + if (!state.visibleStatuses.has("Archived") && state.expandedIdeaId === ideaId) { + state.expandedIdeaId = null; + } + updateStatus(root, `Archived ${record.idea}.`); + render(root); +} + +function restoreIdea(root, ideaId) { + const record = ideaRecord(ideaId); + if (!record) { + updateStatus(root, "Idea Board could not restore that idea."); + return; + } + record.status = previousStatusForRestore(record); + record.previousStatus = record.status; + record.updated = today(); + updateStatus(root, `Restored ${record.idea}.`); + render(root); +} + +function openProject(root, ideaId) { + const record = ideaRecord(ideaId); + if (!record) { + updateStatus(root, "Idea Board could not open that project."); + return; + } + updateStatus(root, `Opening ${record.idea}.`); +} + function handleIdeaAction(root, actionControl) { const action = actionControl.dataset.ideaBoardIdeaAction; const row = actionControl.closest("tr"); @@ -454,6 +584,14 @@ function handleIdeaAction(root, actionControl) { render(root); } else if (action === "delete") { deleteIdea(root, ideaId); + } else if (action === "create-project") { + createProject(root, ideaId); + } else if (action === "open-project") { + openProject(root, ideaId); + } else if (action === "archive") { + archiveIdea(root, ideaId); + } else if (action === "restore") { + restoreIdea(root, ideaId); } else if (action === "cancel") { state.editingIdeaId = null; state.addingIdea = false; @@ -497,7 +635,38 @@ function handleNoteAction(root, actionControl) { } } +function handleFilterAction(root, actionControl) { + if (actionControl.matches("[data-idea-board-filter-select-all]")) { + state.visibleStatuses = new Set(statusOptions); + updateStatus(root, "Showing all statuses."); + } else if (actionControl.matches("[data-idea-board-filter-clear-all]")) { + state.visibleStatuses = new Set(); + state.expandedIdeaId = null; + updateStatus(root, "Status filters cleared."); + } + render(root); +} + +function handleFilterChange(root, input) { + if (input.checked) { + state.visibleStatuses.add(input.value); + } else { + state.visibleStatuses.delete(input.value); + if (state.expandedIdeaId && ideaRecord(state.expandedIdeaId)?.status === input.value) { + state.expandedIdeaId = null; + } + } + updateStatus(root, "Updated visible statuses."); + render(root); +} + function handleClick(root, event) { + const filterAction = event.target.closest("[data-idea-board-filter-select-all], [data-idea-board-filter-clear-all]"); + if (filterAction) { + handleFilterAction(root, filterAction); + return; + } + const toggle = event.target.closest("[data-idea-board-toggle-notes]"); if (toggle) { toggleNotes(root, toggle.dataset.ideaBoardToggleNotes); @@ -514,6 +683,11 @@ function handleClick(root, event) { if (noteAction) handleNoteAction(root, noteAction); } +function handleChange(root, event) { + const statusFilter = event.target.closest("[data-idea-board-status-filter-option]"); + if (statusFilter) handleFilterChange(root, statusFilter); +} + function handleKeydown(root, event) { const toggle = event.target.closest("[data-idea-board-toggle-notes]"); if (!toggle || (event.key !== "Enter" && event.key !== " ")) return; @@ -526,5 +700,6 @@ document.addEventListener("DOMContentLoaded", () => { if (!root) return; render(root); root.addEventListener("click", (event) => handleClick(root, event)); + root.addEventListener("change", (event) => handleChange(root, event)); root.addEventListener("keydown", (event) => handleKeydown(root, event)); });
Idea