feat(dashboard): add entity detail modal and fixed-layout tables

Replace Inputs.table() with buildEntityTable() across workstreams and
tasks pages. Add click-to-detail modal (openEntityModal) on all entity
list views: workstreams, tasks, extension points, and technical debt.

- New component: src/components/entity-modal.js
  - openEntityModal(entity, type) — full-detail overlay (Esc/click-outside to close)
  - buildEntityTable(rows, cols, onRowClick) — table-layout:fixed, overflow-safe wrapper
  - CSS injected lazily; no separate stylesheet required

- Tables: table-layout:fixed keeps content within the content column;
  title col 32%, workstream col 14%, all cells ellipsis + title tooltip
- Cards (EP, TD): onclick → modal; workstream name span gets title tooltip
- Blocked task cards also wired to modal

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-27 18:28:44 +01:00
parent bd6e16394a
commit 0546a1bb2a
5 changed files with 479 additions and 36 deletions

View File

@@ -86,8 +86,9 @@ const filtered = data.filter(t =>
# Technical Debt
```js
import {injectTocTop} from "./components/toc-sidebar.js";
import {withDocHelp} from "./components/doc-overlay.js";
import {injectTocTop} from "./components/toc-sidebar.js";
import {withDocHelp} from "./components/doc-overlay.js";
import {openEntityModal} from "./components/entity-modal.js";
// ── KPI sidebar ───────────────────────────────────────────────────────────────
const _open = data.filter(t => t.status === "open" || t.status === "in_progress");
@@ -191,16 +192,18 @@ if (_urgent.length === 0) {
display(html`<p class="dim">No critical or high severity open items in current filter. ✓</p>`);
} else {
display(html`<div class="td-list">${_urgent.map(t => html`
<div class="td-item td-sev-${t.severity}">
<div class="td-item td-sev-${t.severity} entity-row"
onclick=${() => openEntityModal(t, "td")}
title="Click to view full details">
<div class="td-item-header">
${t.td_id ? html`<span class="td-ref">${t.td_id}</span>` : ""}
<span class="td-sev-badge td-sev-${t.severity}">${t.severity}</span>
<span class="td-type-badge">${t.debt_type}</span>
<span class="td-domain">${t.domain}</span>
${t.workstream_title ? html`<span class="td-ws">${t.workstream_title}</span>` : ""}
${t.workstream_title ? html`<span class="td-ws" title=${t.workstream_title}>${t.workstream_title}</span>` : ""}
</div>
<div class="td-title">${t.title}</div>
${t.description ? html`<div class="td-desc">${t.description.slice(0, 240)}${t.description.length > 240 ? "…" : ""}</div>` : ""}
${t.description ? html`<div class="td-desc">${t.description.slice(0, 220)}${t.description.length > 220 ? " …" : ""}</div>` : ""}
${t.location ? html`<div class="td-location"><code>${t.location}</code></div>` : ""}
</div>
`)}</div>`);
@@ -214,17 +217,19 @@ display(_filtersForm);
display(html`<p><strong>${filtered.length}</strong> items shown.</p>`);
display(html`<div class="td-list">${filtered.map(t => html`
<div class="td-item td-status-${t.status}">
<div class="td-item td-status-${t.status} entity-row"
onclick=${() => openEntityModal(t, "td")}
title="Click to view full details">
<div class="td-item-header">
${t.td_id ? html`<span class="td-ref">${t.td_id}</span>` : ""}
<span class="td-sev-badge td-sev-${t.severity}">${t.severity}</span>
<span class="td-type-badge">${t.debt_type}</span>
<span class="td-badge td-badge-${t.status}">${t.status.replace("_", " ")}</span>
<span class="td-domain">${t.domain}</span>
${t.workstream_title ? html`<span class="td-ws">${t.workstream_title}</span>` : ""}
${t.workstream_title ? html`<span class="td-ws" title=${t.workstream_title}>${t.workstream_title}</span>` : ""}
</div>
<div class="td-title">${t.title}</div>
${t.description ? html`<div class="td-desc">${t.description.slice(0, 240)}${t.description.length > 240 ? "…" : ""}</div>` : ""}
${t.description ? html`<div class="td-desc">${t.description.slice(0, 220)}${t.description.length > 220 ? " …" : ""}</div>` : ""}
${t.location ? html`<div class="td-location"><code>${t.location}</code></div>` : ""}
</div>
`)}
@@ -254,6 +259,8 @@ display(html`<div class="td-list">${filtered.map(t => html`
/* ── TD list ──────────────────────────────────────────────────────────────── */
.td-list { display: flex; flex-direction: column; gap: 0.5rem; }
.td-item { border-left: 3px solid #94a3b8; border-radius: 0 6px 6px 0; background: var(--theme-background-alt); padding: 0.65rem 0.9rem; }
.td-item.entity-row { cursor: pointer; transition: filter 0.1s; }
.td-item.entity-row:hover { filter: brightness(0.97); }
.td-sev-critical { border-left-color: #dc2626; }
.td-sev-high { border-left-color: #ea580c; }
.td-sev-medium { border-left-color: #3b82f6; }