feat(dashboard): shift+click trigger + Improvements section in Todo

improvement-modal.js:
- Replace contextmenu handler with click+shiftKey check — browser
  context menu is no longer intercepted
- Add keydown/keyup/blur listeners: holding Shift applies
  .impr-shift-mode to <body>, switching cursor to crosshair
  across the entire page as a visual affordance
- Update hint text to "Ctrl + Enter to submit · Escape to cancel"

todo.md:
- New "Improvements" section shows open dashboard-improvement TD items
  with a "review →" link to the UI Feedback page
- KPI sidebar row added for open improvement count (indigo when > 0)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-17 23:56:34 +01:00
parent b558610de6
commit 46f4b0c25d
2 changed files with 67 additions and 13 deletions

View File

@@ -1,5 +1,5 @@
/**
* improvement-modal — right-click any dashboard widget to suggest an improvement.
* improvement-modal — Shift+click any dashboard widget to suggest an improvement.
*
* Usage (once per page, usually via _footer.md):
* import {initImprovementModal} from "./components/improvement-modal.js";
@@ -10,6 +10,10 @@
*
* Otherwise the component walks the DOM to infer the nearest section heading.
* Submissions are stored as technical-debt items with debt_type="dashboard-improvement".
*
* Interaction:
* - Hold Shift → cursor changes to crosshair across the entire page
* - Shift+click any element (except form controls) → opens suggestion modal
*/
const _STYLE_ID = "improvement-modal-styles";
@@ -136,8 +140,9 @@ function _ensureStyles() {
@keyframes _im-tin { from { opacity:0; transform:translateX(-50%) translateY(6px) } to { opacity:1; transform:translateX(-50%) translateY(0) } }
@keyframes _im-tout { from { opacity:1 } to { opacity:0 } }
/* ── Right-click hint cursor on interactive elements ────────────────────── */
.impr-hint-cursor { cursor: context-menu; }
/* ── Shift-held cursor override (applied to <body>) ─────────────────────── */
.impr-shift-mode,
.impr-shift-mode * { cursor: crosshair !important; }
`;
document.head.append(s);
}
@@ -204,8 +209,19 @@ export function initImprovementModal({ apiBase = "http://127.0.0.1:8000", domain
_initialized = true;
_ensureStyles();
document.addEventListener("contextmenu", (e) => {
// Don't intercept native right-clicks on form controls or links
// Shift-held cursor indicator
document.addEventListener("keydown", (e) => {
if (e.key === "Shift") document.body.classList.add("impr-shift-mode");
});
document.addEventListener("keyup", (e) => {
if (e.key === "Shift") document.body.classList.remove("impr-shift-mode");
});
// Remove shift-mode if window loses focus while Shift is held
window.addEventListener("blur", () => document.body.classList.remove("impr-shift-mode"));
document.addEventListener("click", (e) => {
if (!e.shiftKey) return;
// Don't intercept shift-clicks on form controls or links
if (e.target.matches("input, textarea, select, a, button")) return;
e.preventDefault();
@@ -255,7 +271,7 @@ export function initImprovementModal({ apiBase = "http://127.0.0.1:8000", domain
const hint = Object.assign(document.createElement("div"), {
className: "impr-hint",
textContent: "Ctrl + Enter to submit",
textContent: "Ctrl + Enter to submit · Escape to cancel",
});
body.append(ctxLabel, chip, sugLabel, textarea, hint);