feat(ms-ai-architect): v1.11.0 Sesjon 2 — design-system 100%-adoption (Fase 4-6) [skip-docs]
Migrerer alle 6 PARALLEL CSS-navn i playground HTML til DS-konvensjon:
- .topbar* -> .app-header* (DS components.css)
- .residual-pair* -> .pair-before-after* (DS components-tier3.css; data-severity -> BEM modifier)
- .command-card* -> .card + .card__* (DS base.css + tier3-supplement; outer + 4 sub-elementer)
- .catalog-card* -> .card + .card__* (samme; outer + 7 sub-elementer)
- .screen-tabs/.screen-tab/.screen
-> .tab-list/.tab/.tab-panel (DS tier3-supplement; data-active="..." -> [hidden]-attr)
- .pyramide-desc* -> .stack-sm + .pyramide-tier-detail*
(DS tier3-supplement section 22+23)
Trimmer plugin-local <style>-blokk fra 202 -> 127 linjer (37% reduksjon):
- Sletter inline duplikater av DS v0.3 sections 14-15 (.page__*, .key-stats, .key-stat--{level})
- Sletter inline duplikater av sections 18-19 (.top-risks, .recommendation-card)
- Refaktorerer renderPageShell + renderKeyStatsGrid til DS markup-pattern
(.page__header-main + .page__header-aside + .page__title h1; .key-stat--{level} BEM)
Beholdt eksplisitt plugin-local (med dokumentasjon i CSS-kommentarer):
- .verdict-pill (domain-semantikk go/block — distinkt fra DS .verdict-pill-lg severity-band)
- .scenario-card[data-status="met/partial/missing"] (DS har kun "winner")
- .read-more-block + .suppressed-panel (native <details>; DS bruker JS-toggled aria-expanded)
- .onboarding-*, .home-*, .project-*, .modal*, .command-form*, .catalog-cards (plugin-spesifikk layout)
Visuell oppgradering (Fase 6):
- Eyebrow-label "PROSJEKTER · X av X" over home-projects seksjon
- .card--severity-{positive/medium/critical} venstre-border på rapport-cards basert på
parsed.verdict (go/approved/allow=positive, go-with-conditions/warning=medium,
block/failed=critical) — visuell signal for rapport-status i project surface
- AI Act-pyramide bredde min-width: 480px + tier-label font-size: var(--font-size-md)
for å fjerne tekstklipping ("Uakseptabe...", "klassifisert"). Responsive @media for <560px.
- App-header-restruktur: brand + breadcrumb + spacer + actions (DS pattern), ikke flex-between
- .stack-lg vertikalt rytme-utility på home/project/catalog body i renderPageShell
Tests oppdatert for nye DS-navn:
- Step 10: residual-pair -> pair-before-after assert
- Step 12: screen-tabs -> tab-list assert (class="tab-list" eksplisitt)
Verification: 201 + 70 + 7 = 278/278 PASS, 0 FAIL.
6 intentional plugin-local residuals (1 .catalog-cards container + 4 .read-more-block + 1 .suppressed-panel)
— alle dokumenterte i inline <style>.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
f58b892436
commit
31aed40308
3 changed files with 168 additions and 216 deletions
|
|
@ -52,13 +52,7 @@
|
|||
|
||||
/* .app-shell + .app-shell--wide hentet fra vendored DS v0.3 (tier3-supplement section 25) */
|
||||
|
||||
/* Topbar — vises på alle surfaces unntatt onboarding (uten projekt-kontekst) */
|
||||
.topbar { display: flex; align-items: center; justify-content: space-between; padding: var(--space-3) var(--space-5); border-bottom: 1px solid var(--color-border-subtle); background: var(--color-surface); position: sticky; top: 0; z-index: 10; }
|
||||
.topbar__brand { display: flex; align-items: center; gap: var(--space-2); font-weight: var(--font-weight-semibold); }
|
||||
.topbar__brand-mark { width: 28px; height: 28px; border-radius: var(--radius-sm); background: var(--color-primary-500); color: var(--color-text-on-primary); display: inline-flex; align-items: center; justify-content: center; font-family: var(--font-family-mono); font-weight: var(--font-weight-bold); font-size: 13px; }
|
||||
.topbar__nav { display: flex; gap: var(--space-2); align-items: center; }
|
||||
.topbar__crumb { font-size: var(--font-size-sm); color: var(--color-text-secondary); }
|
||||
.topbar__crumb a { cursor: pointer; }
|
||||
/* App-header (.app-header*) hentet fra vendored DS (components.css). */
|
||||
|
||||
/* Onboarding-layout: sidebar + main */
|
||||
.onboarding-layout { display: grid; grid-template-columns: 280px 1fr; gap: var(--space-6); align-items: start; }
|
||||
|
|
@ -95,11 +89,7 @@
|
|||
.project-tab[aria-current="true"] { color: var(--color-text-primary); border-bottom-color: var(--color-primary-500); }
|
||||
.project-tab__count { display: inline-block; margin-left: 6px; padding: 1px 6px; background: var(--color-bg-soft); border-radius: 10px; font-size: 11px; color: var(--color-text-tertiary); }
|
||||
.command-cards { display: flex; flex-direction: column; gap: var(--space-4); }
|
||||
.command-card { background: var(--color-surface); border: 1px solid var(--color-border-subtle); border-radius: var(--radius-md); padding: var(--space-4); display: flex; flex-direction: column; gap: var(--space-3); }
|
||||
.command-card__head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-3); }
|
||||
.command-card__title { font-size: var(--font-size-md); font-weight: var(--font-weight-semibold); margin: 0; }
|
||||
.command-card__desc { font-size: var(--font-size-sm); color: var(--color-text-secondary); margin: 4px 0 0; }
|
||||
.command-card__id { font-family: var(--font-family-mono); font-size: var(--font-size-xs); color: var(--color-text-tertiary); }
|
||||
/* .card + .card__* hentet fra vendored DS (base.css + tier3-supplement). */
|
||||
.sub-zone { border-top: 1px solid var(--color-border-subtle); padding-top: var(--space-3); }
|
||||
.sub-zone__heading { font-size: var(--font-size-xs); font-weight: var(--font-weight-semibold); text-transform: uppercase; letter-spacing: 0.06em; color: var(--color-text-tertiary); margin: 0 0 var(--space-2); }
|
||||
.paste-import-row { display: flex; flex-direction: column; gap: var(--space-2); }
|
||||
|
|
@ -136,25 +126,16 @@
|
|||
.catalog-toolbar__count { font-size: var(--font-size-sm); color: var(--color-text-tertiary); }
|
||||
.catalog-groups { display: flex; flex-direction: column; gap: var(--space-3); }
|
||||
.catalog-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: var(--space-3); padding: var(--space-2) 0; }
|
||||
.catalog-card { background: var(--color-surface); border: 1px solid var(--color-border-subtle); border-radius: var(--radius-md); padding: var(--space-4); display: flex; flex-direction: column; gap: var(--space-2); }
|
||||
.catalog-card__head { display: flex; justify-content: space-between; align-items: flex-start; gap: var(--space-2); }
|
||||
.catalog-card__title { font-size: var(--font-size-md); margin: 0; font-weight: var(--font-weight-semibold); }
|
||||
.catalog-card__desc { font-size: var(--font-size-sm); color: var(--color-text-secondary); margin: 4px 0 0; }
|
||||
.catalog-card__pill { padding: 2px 8px; background: var(--color-bg-soft); color: var(--color-text-secondary); border-radius: var(--radius-sm); font-size: 10px; font-weight: var(--font-weight-medium); flex-shrink: 0; text-transform: uppercase; letter-spacing: 0.04em; }
|
||||
.catalog-card__meta { display: flex; gap: var(--space-2); flex-wrap: wrap; align-items: center; }
|
||||
.catalog-card__hint { font-size: var(--font-size-xs); color: var(--color-text-tertiary); font-family: var(--font-family-mono); }
|
||||
.catalog-card__actions { display: flex; gap: var(--space-2); margin-top: auto; padding-top: var(--space-2); }
|
||||
/* .card + .card__* hentet fra vendored DS (base.css + tier3-supplement). */
|
||||
.catalog-tool-notice { padding: var(--space-2) var(--space-3); background: var(--color-bg-soft); border-left: 3px solid var(--color-primary-500); border-radius: var(--radius-sm); font-size: var(--font-size-xs); color: var(--color-text-secondary); }
|
||||
|
||||
/* Foundation page-shell helpers (v1.10.0 Sesjon 1).
|
||||
Felles header + verdict-pille + key-stats-grid for alle 17 renderers.
|
||||
Kandidater for hoisting til shared/playground-design-system/ i en
|
||||
senere iterasjon (Sesjon 6 visual QA). */
|
||||
.page__header { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-6); margin: 0 0 var(--space-6) 0; }
|
||||
.page__title { flex: 1 1 auto; min-width: 0; }
|
||||
.page__eyebrow { display: block; font-size: var(--font-size-xs); font-weight: var(--font-weight-semibold); text-transform: uppercase; letter-spacing: 0.08em; color: var(--color-scope-architect); margin-bottom: var(--space-2); }
|
||||
.page__title h1 { margin: 0 0 var(--space-3) 0; font-size: var(--font-size-3xl); line-height: var(--line-height-tight); color: var(--color-text-primary); }
|
||||
.page__lede { margin: 0; max-width: var(--measure); font-size: var(--font-size-lg); color: var(--color-text-secondary); line-height: var(--line-height-snug); }
|
||||
/* Foundation .page__*, .key-stats, .key-stat--{level} hentet fra vendored DS (tier3-supplement section 14-15).
|
||||
.top-risks*, .top-risk*, .recommendation-card* hentet fra DS section 18-19.
|
||||
.pair-before-after*, .pyramide-tier-detail*, .tab-list/.tab/.tab-panel også fra DS. */
|
||||
|
||||
/* .verdict-pill: plugin-domain semantikk (go/block/approved/allow/warning/n-a) — distinkt fra
|
||||
DS .verdict-pill-lg (severity-band: critical/high/medium/low/positive). Domain-pillen brukes
|
||||
av architect-rapporter for GO/BLOCK-beslutninger; severity-pillen brukes for risiko-band. */
|
||||
.verdict-pill { display: inline-flex; align-items: center; padding: var(--space-2) var(--space-4); border-radius: var(--radius-pill); font-size: var(--font-size-sm); font-weight: var(--font-weight-semibold); text-transform: uppercase; letter-spacing: 0.06em; white-space: nowrap; flex-shrink: 0; }
|
||||
.verdict-pill[data-verdict="go"],
|
||||
.verdict-pill[data-verdict="approved"],
|
||||
|
|
@ -164,85 +145,29 @@
|
|||
.verdict-pill[data-verdict="block"],
|
||||
.verdict-pill[data-verdict="failed"] { background: var(--color-severity-critical); color: var(--color-severity-critical-on); }
|
||||
.verdict-pill[data-verdict="n-a"] { background: var(--color-bg-soft); color: var(--color-text-secondary); border: 1px solid var(--color-border-subtle); }
|
||||
.key-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: var(--space-4); margin: 0 0 var(--space-6) 0; padding: var(--space-4); background: var(--color-bg-soft); border: 1px solid var(--color-border-subtle); border-radius: var(--radius-md); }
|
||||
.key-stat { display: flex; flex-direction: column; gap: 2px; }
|
||||
.key-stat__label { font-size: var(--font-size-xs); font-weight: var(--font-weight-semibold); color: var(--color-text-secondary); text-transform: uppercase; letter-spacing: 0.06em; }
|
||||
.key-stat__value { font-size: var(--font-size-2xl); font-weight: var(--font-weight-bold); color: var(--color-text-primary); font-variant-numeric: tabular-nums; line-height: var(--line-height-tight); }
|
||||
.key-stat__hint { font-size: var(--font-size-xs); color: var(--color-text-tertiary); }
|
||||
.key-stat[data-modifier="critical"] .key-stat__value { color: var(--color-severity-critical); }
|
||||
.key-stat[data-modifier="high"] .key-stat__value { color: var(--color-severity-high); }
|
||||
.key-stat[data-modifier="medium"] .key-stat__value { color: var(--color-severity-medium); }
|
||||
.key-stat[data-modifier="low"] .key-stat__value { color: var(--color-severity-low); }
|
||||
|
||||
/* Renderer-batch A inlines (v1.10.0 Sesjon 3). Klasser ikke i vendor —
|
||||
inline her per scope-regel (plugin standalone). Kandidat for hoisting
|
||||
til shared/playground-design-system/ i Sesjon 6 visual QA. */
|
||||
.pyramide-desc { margin: var(--space-4) 0 0 0; display: flex; flex-direction: column; gap: var(--space-2); }
|
||||
.pyramide-desc__item { background: var(--color-bg-soft); border: 1px solid var(--color-border-subtle); border-radius: var(--radius-sm); padding: var(--space-2) var(--space-3); }
|
||||
.pyramide-desc__item summary { cursor: pointer; font-weight: var(--font-weight-semibold); color: var(--color-text-primary); }
|
||||
.pyramide-desc__item[open] summary { margin-bottom: var(--space-2); }
|
||||
.pyramide-desc__item p { margin: 0; font-size: var(--font-size-sm); color: var(--color-text-secondary); }
|
||||
|
||||
.scenario-card-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: var(--space-3); margin: 0 0 var(--space-4) 0; }
|
||||
.scenario-card { background: var(--color-surface); border: 1px solid var(--color-border-subtle); border-radius: var(--radius-md); padding: var(--space-3) var(--space-4); display: flex; flex-direction: column; gap: var(--space-2); }
|
||||
/* .scenario-card[data-status="met/partial/missing"]: plugin-spesifikke 3-stadie-status —
|
||||
DS har kun "winner". Beholdt for AI Act / cost-distribution / capability-matrix-renderers. */
|
||||
.scenario-card[data-status="met"] { border-left: 4px solid var(--color-state-success); }
|
||||
.scenario-card[data-status="partial"] { border-left: 4px solid var(--color-severity-medium); }
|
||||
.scenario-card[data-status="missing"] { border-left: 4px solid var(--color-severity-critical); }
|
||||
.scenario-card__head { display: flex; justify-content: space-between; align-items: baseline; gap: var(--space-2); }
|
||||
.scenario-card__source { font-family: var(--font-family-mono); font-size: var(--font-size-xs); color: var(--color-text-tertiary); }
|
||||
.scenario-card__count { font-family: var(--font-family-mono); font-size: 11px; padding: 2px 8px; background: var(--color-bg-soft); border-radius: var(--radius-pill); color: var(--color-text-secondary); border: 1px solid var(--color-border-subtle); }
|
||||
.scenario-card__title { font-size: var(--font-size-md); font-weight: var(--font-weight-semibold); color: var(--color-text-primary); margin: 0; }
|
||||
|
||||
.residual-pair { display: grid; grid-template-columns: 1fr auto 1fr; gap: var(--space-4); align-items: center; margin: var(--space-4) 0; padding: var(--space-3); background: var(--color-bg-soft); border: 1px solid var(--color-border-subtle); border-radius: var(--radius-md); }
|
||||
.residual-pair__cell { background: var(--color-surface); border: 1px solid var(--color-border-subtle); border-radius: var(--radius-sm); padding: var(--space-3); display: flex; flex-direction: column; gap: 4px; }
|
||||
.residual-pair__cell-label { font-size: var(--font-size-xs); text-transform: uppercase; letter-spacing: 0.06em; color: var(--color-text-tertiary); font-weight: var(--font-weight-semibold); }
|
||||
.residual-pair__cell-value { font-size: var(--font-size-2xl); font-weight: var(--font-weight-bold); font-variant-numeric: tabular-nums; line-height: 1; color: var(--color-text-primary); }
|
||||
.residual-pair__cell-meta { font-size: var(--font-size-xs); color: var(--color-text-secondary); }
|
||||
.residual-pair__arrow { font-size: var(--font-size-xl); color: var(--color-text-tertiary); justify-self: center; }
|
||||
.residual-pair__cell[data-severity="low"] { border-left: 4px solid var(--color-severity-low); }
|
||||
.residual-pair__cell[data-severity="medium"] { border-left: 4px solid var(--color-severity-medium); }
|
||||
.residual-pair__cell[data-severity="high"] { border-left: 4px solid var(--color-severity-high); }
|
||||
.residual-pair__cell[data-severity="critical"] { border-left: 4px solid var(--color-severity-critical); }
|
||||
/* AI Act-pyramide-overrides: bumpe label-font så tier-tekst ikke klippes,
|
||||
sikre tilstrekkelig parent-bredde (DS-default har ingen min-width). */
|
||||
.pyramide { min-width: 480px; max-width: 100%; }
|
||||
.pyramide__tier { font-size: var(--font-size-md); padding: var(--space-3) var(--space-4); }
|
||||
@media (max-width: 560px) { .pyramide { min-width: 0; } .pyramide__tier { font-size: var(--font-size-sm); padding: 8px 12px; } }
|
||||
|
||||
/* .read-more-block + .suppressed-panel: native <details>-baserte mønstre — distinkte fra
|
||||
DS .read-more og .suppressed (som bruker JS-toggled aria-expanded). */
|
||||
.read-more-block { margin: var(--space-2) 0; }
|
||||
.read-more-block summary { cursor: pointer; color: var(--color-text-link); font-weight: var(--font-weight-medium); }
|
||||
|
||||
/* Renderer-batch B inlines (v1.10.0 Sesjon 4). Klasser ikke i vendor —
|
||||
inline her per scope-regel (plugin standalone). Kandidat for hoisting
|
||||
til shared/playground-design-system/ i Sesjon 6 visual QA. */
|
||||
.top-risks { margin: var(--space-4) 0; display: flex; flex-direction: column; gap: var(--space-2); }
|
||||
.top-risks__heading { margin: 0 0 var(--space-2) 0; font-size: var(--font-size-md); color: var(--color-text-secondary); text-transform: uppercase; letter-spacing: 0.06em; font-weight: var(--font-weight-semibold); }
|
||||
.top-risk { display: grid; grid-template-columns: 28px 1fr auto; gap: var(--space-3); align-items: center; padding: var(--space-2) var(--space-3); background: var(--color-surface); border: 1px solid var(--color-border-subtle); border-radius: var(--radius-sm); }
|
||||
.top-risk__rank { font-family: var(--font-family-mono); font-size: var(--font-size-sm); color: var(--color-text-tertiary); font-weight: var(--font-weight-bold); }
|
||||
.top-risk__desc { font-size: var(--font-size-sm); color: var(--color-text-primary); }
|
||||
.top-risk__score { font-family: var(--font-family-mono); font-size: var(--font-size-md); font-weight: var(--font-weight-bold); padding: 2px 10px; border-radius: var(--radius-pill); background: var(--color-bg-soft); color: var(--color-text-primary); }
|
||||
.top-risk[data-severity="critical"] { border-left: 4px solid var(--color-severity-critical); }
|
||||
.top-risk[data-severity="critical"] .top-risk__score { background: var(--color-severity-critical); color: var(--color-severity-critical-on); }
|
||||
.top-risk[data-severity="high"] { border-left: 4px solid var(--color-severity-high); }
|
||||
.top-risk[data-severity="high"] .top-risk__score { background: var(--color-severity-high-soft); color: var(--color-severity-high-on); }
|
||||
.top-risk[data-severity="medium"] { border-left: 4px solid var(--color-severity-medium); }
|
||||
.top-risk[data-severity="medium"] .top-risk__score { background: var(--color-severity-medium-soft); color: var(--color-severity-medium-on); }
|
||||
.top-risk[data-severity="low"] { border-left: 4px solid var(--color-severity-low); }
|
||||
|
||||
.recommendation-card { margin: var(--space-4) 0 0 0; padding: var(--space-4); background: var(--color-bg-soft); border: 1px solid var(--color-border-subtle); border-radius: var(--radius-md); border-left: 4px solid var(--color-scope-architect); display: flex; flex-direction: column; gap: var(--space-2); }
|
||||
.recommendation-card__label { font-size: var(--font-size-xs); font-weight: var(--font-weight-semibold); text-transform: uppercase; letter-spacing: 0.06em; color: var(--color-text-tertiary); }
|
||||
.recommendation-card__body { font-size: var(--font-size-md); color: var(--color-text-primary); line-height: var(--line-height-snug); }
|
||||
|
||||
.suppressed-panel { margin: var(--space-4) 0 0 0; padding: var(--space-3) var(--space-4); background: var(--color-bg-soft); border: 1px dashed var(--color-border-subtle); border-radius: var(--radius-md); opacity: 0.85; }
|
||||
.suppressed-panel summary { cursor: pointer; color: var(--color-text-secondary); font-weight: var(--font-weight-medium); font-size: var(--font-size-sm); }
|
||||
.suppressed-panel[open] summary { margin-bottom: var(--space-2); }
|
||||
.suppressed-panel__list { display: flex; flex-direction: column; gap: var(--space-2); margin: var(--space-2) 0 0 0; }
|
||||
.suppressed-panel__item { padding: var(--space-2) var(--space-3); background: var(--color-surface); border: 1px solid var(--color-border-subtle); border-radius: var(--radius-sm); font-size: var(--font-size-sm); color: var(--color-text-secondary); display: flex; gap: var(--space-3); align-items: baseline; }
|
||||
.suppressed-panel__id { font-family: var(--font-family-mono); font-size: var(--font-size-xs); color: var(--color-text-tertiary); }
|
||||
|
||||
/* Tier 3 A4 — Screen-tabs (segmented). Inline her per scope-regel
|
||||
(plugin standalone). Kandidat for hoisting til shared/ i Sesjon 6. */
|
||||
.screen-tabs { display: flex; gap: var(--space-1); padding: 4px; background: var(--color-bg-soft); border-radius: var(--radius-md); width: fit-content; margin: 0 0 var(--space-4) 0; }
|
||||
.screen-tab { padding: var(--space-2) var(--space-3); font-size: var(--font-size-sm); font-weight: var(--font-weight-medium); background: transparent; border: none; border-radius: var(--radius-sm); cursor: pointer; color: var(--color-text-secondary); font-family: inherit; }
|
||||
.screen-tab:hover { color: var(--color-text-primary); }
|
||||
.screen-tab[aria-current="true"] { background: var(--color-surface); color: var(--color-text-primary); box-shadow: var(--shadow-sm); }
|
||||
.screen { display: none; }
|
||||
.screen[data-active="true"] { display: block; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
|
@ -1716,25 +1641,27 @@
|
|||
scheduleRender();
|
||||
}
|
||||
|
||||
// Topbar — gjenbrukes på home, catalog, project. Onboarding viser ingen topbar
|
||||
// App-header — gjenbrukes på home, catalog, project. Onboarding viser ingen header
|
||||
// (full-fokus førstegangs-flyt). Eksport/import-knapper wires opp til
|
||||
// __exportState/__importState fra Step 3.
|
||||
function renderTopbar(crumb) {
|
||||
const orgName = (store.state.shared.organization && store.state.shared.organization.name) || '';
|
||||
const crumbHtml = (orgName || crumb)
|
||||
? '<span class="topbar__crumb">' + (orgName ? escapeHtml(orgName) : '') + (orgName && crumb ? ' · ' : '') + (crumb || '') + '</span>'
|
||||
const breadcrumbInner = (orgName ? escapeHtml(orgName) : '') + (orgName && crumb ? ' · ' : '') + (crumb || '');
|
||||
const breadcrumbHtml = breadcrumbInner
|
||||
? '<nav class="app-header__breadcrumb" aria-label="Brødsmuler">' + breadcrumbInner + '</nav>'
|
||||
: '';
|
||||
const currentTheme = document.documentElement.getAttribute('data-theme') === 'light' ? 'light' : 'dark';
|
||||
const themeLabel = currentTheme === 'light' ? 'Lys' : 'Mørk';
|
||||
const themeNext = currentTheme === 'light' ? 'mørk' : 'lys';
|
||||
return (
|
||||
'<header class="topbar">' +
|
||||
'<div class="topbar__brand">' +
|
||||
'<span class="topbar__brand-mark" aria-hidden="true">M</span>' +
|
||||
'<header class="app-header">' +
|
||||
'<div class="app-header__brand">' +
|
||||
'<span class="app-header__brand-mark" aria-hidden="true">M</span>' +
|
||||
'<span>ms-ai-architect</span>' +
|
||||
crumbHtml +
|
||||
'</div>' +
|
||||
'<nav class="topbar__nav" aria-label="Hovednavigasjon">' +
|
||||
breadcrumbHtml +
|
||||
'<div class="app-header__spacer"></div>' +
|
||||
'<div class="app-header__actions" role="group" aria-label="Hovednavigasjon">' +
|
||||
'<button type="button" class="btn btn--ghost btn--sm" data-action="goto-home">Hjem</button>' +
|
||||
'<button type="button" class="btn btn--ghost btn--sm" data-action="goto-catalog">Katalog</button>' +
|
||||
'<button type="button" class="btn btn--ghost btn--sm" data-action="goto-onboarding">Re-onboard</button>' +
|
||||
|
|
@ -1744,7 +1671,7 @@
|
|||
'<button type="button" class="theme-toggle" data-action="toggle-theme" aria-label="Bytt til ' + themeNext + ' modus">' +
|
||||
'<span data-theme-label>' + themeLabel + '</span>' +
|
||||
'</button>' +
|
||||
'</nav>' +
|
||||
'</div>' +
|
||||
'</header>'
|
||||
);
|
||||
}
|
||||
|
|
@ -1901,15 +1828,18 @@
|
|||
{ label: 'AKTIVE RAPPORTER', value: activeReportCount }
|
||||
]
|
||||
},
|
||||
tracksHtml +
|
||||
'<section class="home-projects">' +
|
||||
'<div class="home-section-head">' +
|
||||
'<h2>Mine prosjekter</h2>' +
|
||||
'<span class="home-section-meta">' + projects.length + ' prosjekt' + (projects.length === 1 ? '' : 'er') + ' · maks ' + reportTotal + ' rapporter per prosjekt</span>' +
|
||||
'</div>' +
|
||||
projectListHtml +
|
||||
(projects.length > 0 ? '<div class="onboarding-actions" style="margin-top: var(--space-4);"><button type="button" class="btn btn--primary" data-action="new-project">Nytt prosjekt</button></div>' : '') +
|
||||
'</section>'
|
||||
'<div class="stack-lg">' +
|
||||
tracksHtml +
|
||||
'<section class="home-projects">' +
|
||||
'<span class="eyebrow">PROSJEKTER · ' + projects.length + ' av ' + projects.length + '</span>' +
|
||||
'<div class="home-section-head">' +
|
||||
'<h2>Mine prosjekter</h2>' +
|
||||
'<span class="home-section-meta">' + projects.length + ' prosjekt' + (projects.length === 1 ? '' : 'er') + ' · maks ' + reportTotal + ' rapporter per prosjekt</span>' +
|
||||
'</div>' +
|
||||
projectListHtml +
|
||||
(projects.length > 0 ? '<div class="onboarding-actions" style="margin-top: var(--space-4);"><button type="button" class="btn btn--primary" data-action="new-project">Nytt prosjekt</button></div>' : '') +
|
||||
'</section>' +
|
||||
'</div>'
|
||||
);
|
||||
|
||||
root.innerHTML = (
|
||||
|
|
@ -2093,13 +2023,27 @@
|
|||
// ---- Sub-card rendering ----
|
||||
|
||||
function renderCommandSubCard(cmd, projectId) {
|
||||
// Sev-modifier: hvis rapporten er parsed, mappe verdict til DS card--severity-{level}.
|
||||
// Plugin-domain-verdicts (go/block/approved/...) → severity-band (positive/critical/medium).
|
||||
const project = findProject(projectId);
|
||||
const report = project && project.reports && project.reports[cmd.id];
|
||||
const verdict = report && report.parsed && report.parsed.verdict
|
||||
? String(report.parsed.verdict).toLowerCase() : '';
|
||||
const sevMap = {
|
||||
'go': 'positive', 'approved': 'positive', 'allow': 'positive',
|
||||
'go-with-conditions': 'medium', 'warning': 'medium',
|
||||
'block': 'critical', 'failed': 'critical'
|
||||
};
|
||||
const sevModifier = sevMap[verdict] || '';
|
||||
const sevClass = sevModifier ? ' card--severity-' + sevModifier : '';
|
||||
|
||||
const titleHtml = (
|
||||
'<div class="command-card__head">' +
|
||||
'<div class="card__head">' +
|
||||
'<div>' +
|
||||
'<h3 class="command-card__title">' + escapeHtml(cmd.label) + '</h3>' +
|
||||
'<p class="command-card__desc">' + escapeHtml(cmd.description) + '</p>' +
|
||||
'<h3 class="card__title">' + escapeHtml(cmd.label) + '</h3>' +
|
||||
'<p class="card__desc">' + escapeHtml(cmd.description) + '</p>' +
|
||||
'</div>' +
|
||||
'<span class="command-card__id">/architect:' + escapeHtml(cmd.id) + '</span>' +
|
||||
'<span class="card__id">/architect:' + escapeHtml(cmd.id) + '</span>' +
|
||||
'</div>'
|
||||
);
|
||||
|
||||
|
|
@ -2126,7 +2070,7 @@
|
|||
'</div>'
|
||||
);
|
||||
return (
|
||||
'<article class="command-card" data-command-card data-command-id="' + escapeAttr(cmd.id) + '">' +
|
||||
'<article class="card' + sevClass + '" data-command-card data-command-id="' + escapeAttr(cmd.id) + '">' +
|
||||
titleHtml +
|
||||
formZone +
|
||||
toolNotice +
|
||||
|
|
@ -2156,7 +2100,7 @@
|
|||
);
|
||||
|
||||
return (
|
||||
'<article class="command-card" data-command-card data-command-id="' + escapeAttr(cmd.id) + '">' +
|
||||
'<article class="card' + sevClass + '" data-command-card data-command-id="' + escapeAttr(cmd.id) + '">' +
|
||||
titleHtml +
|
||||
formZone +
|
||||
pasteZone +
|
||||
|
|
@ -2188,7 +2132,7 @@
|
|||
'</div>'
|
||||
);
|
||||
|
||||
// Screen-tabs (A4 Tier 3): Oversikt / Rapporter / Kontekst / Eksport.
|
||||
// Tab-list (DS): Oversikt / Rapporter / Kontekst / Eksport.
|
||||
// Sesjon 2: Rapporter er primær; andre er stub-skjermer som fylles ut
|
||||
// i Sesjon 3-6.
|
||||
const SCREENS = [
|
||||
|
|
@ -2197,10 +2141,10 @@
|
|||
{ id: 'kontekst', label: 'Kontekst' },
|
||||
{ id: 'eksport', label: 'Eksport' }
|
||||
];
|
||||
const screenTabsHtml = '<nav class="screen-tabs" role="tablist" aria-label="Prosjekt-skjermer">' + SCREENS.map(function (s) {
|
||||
const screenTabsHtml = '<nav class="tab-list" role="tablist" aria-label="Prosjekt-skjermer">' + SCREENS.map(function (s) {
|
||||
const isActive = currentProjectScreen === s.id;
|
||||
return (
|
||||
'<button type="button" class="screen-tab" role="tab"' +
|
||||
'<button type="button" class="tab" role="tab"' +
|
||||
' aria-current="' + (isActive ? 'true' : 'false') + '"' +
|
||||
' data-action="project-screen" data-screen="' + escapeAttr(s.id) + '">' +
|
||||
escapeHtml(s.label) +
|
||||
|
|
@ -2242,7 +2186,7 @@
|
|||
}).join('');
|
||||
|
||||
const oversiktHtml = (
|
||||
'<div class="screen" data-active="' + (currentProjectScreen === 'oversikt' ? 'true' : 'false') + '" data-screen-id="oversikt">' +
|
||||
'<div class="tab-panel" data-screen-id="oversikt"' + (currentProjectScreen === 'oversikt' ? '' : ' hidden') + '>' +
|
||||
'<div class="guide-panel guide-panel--info">' +
|
||||
'<div class="guide-panel__icon" aria-hidden="true">i</div>' +
|
||||
'<div class="guide-panel__body">' +
|
||||
|
|
@ -2256,14 +2200,14 @@
|
|||
);
|
||||
|
||||
const rapporterHtml = (
|
||||
'<div class="screen" data-active="' + (currentProjectScreen === 'rapporter' ? 'true' : 'false') + '" data-screen-id="rapporter">' +
|
||||
'<div class="tab-panel" data-screen-id="rapporter"' + (currentProjectScreen === 'rapporter' ? '' : ' hidden') + '>' +
|
||||
tabsHtml +
|
||||
panelsHtml +
|
||||
'</div>'
|
||||
);
|
||||
|
||||
const kontekstHtml = (
|
||||
'<div class="screen" data-active="' + (currentProjectScreen === 'kontekst' ? 'true' : 'false') + '" data-screen-id="kontekst">' +
|
||||
'<div class="tab-panel" data-screen-id="kontekst"' + (currentProjectScreen === 'kontekst' ? '' : ' hidden') + '>' +
|
||||
'<div class="guide-panel guide-panel--info">' +
|
||||
'<div class="guide-panel__icon" aria-hidden="true">i</div>' +
|
||||
'<div class="guide-panel__body">' +
|
||||
|
|
@ -2276,12 +2220,12 @@
|
|||
);
|
||||
|
||||
const eksportHtml = (
|
||||
'<div class="screen" data-active="' + (currentProjectScreen === 'eksport' ? 'true' : 'false') + '" data-screen-id="eksport">' +
|
||||
'<div class="tab-panel" data-screen-id="eksport"' + (currentProjectScreen === 'eksport' ? '' : ' hidden') + '>' +
|
||||
'<div class="guide-panel guide-panel--info">' +
|
||||
'<div class="guide-panel__icon" aria-hidden="true">i</div>' +
|
||||
'<div class="guide-panel__body">' +
|
||||
'<h3 class="guide-panel__title">Eksport</h3>' +
|
||||
'<p class="guide-panel__text">Bruk <strong>Eksporter</strong> i topbar for hele state. Per-prosjekt eksport (PDF/Markdown) kommer i Sesjon 6.</p>' +
|
||||
'<p class="guide-panel__text">Bruk <strong>Eksporter</strong> i toppmenyen for hele state. Per-prosjekt eksport (PDF/Markdown) kommer i Sesjon 6.</p>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>'
|
||||
|
|
@ -2297,12 +2241,14 @@
|
|||
{ label: 'SIST OPPDATERT', value: inferProjectLastUpdated(project) }
|
||||
]
|
||||
},
|
||||
actionBar +
|
||||
screenTabsHtml +
|
||||
oversiktHtml +
|
||||
rapporterHtml +
|
||||
kontekstHtml +
|
||||
eksportHtml
|
||||
'<div class="stack-lg">' +
|
||||
actionBar +
|
||||
screenTabsHtml +
|
||||
oversiktHtml +
|
||||
rapporterHtml +
|
||||
kontekstHtml +
|
||||
eksportHtml +
|
||||
'</div>'
|
||||
);
|
||||
|
||||
root.innerHTML = (
|
||||
|
|
@ -2350,29 +2296,29 @@
|
|||
function renderCatalogCardHtml(cmd) {
|
||||
const isVerktoy = !cmd.produces_report;
|
||||
const pill = isVerktoy
|
||||
? '<span class="catalog-card__pill">Verktøy</span>'
|
||||
: '<span class="catalog-card__pill">Rapport</span>';
|
||||
? '<span class="card__pill">Verktøy</span>'
|
||||
: '<span class="card__pill">Rapport</span>';
|
||||
const hintHtml = cmd.argument_hint
|
||||
? '<span class="catalog-card__hint">' + escapeHtml(cmd.argument_hint) + '</span>'
|
||||
? '<span class="card__hint">' + escapeHtml(cmd.argument_hint) + '</span>'
|
||||
: '';
|
||||
const verktoyNotice = isVerktoy
|
||||
? '<div class="catalog-tool-notice">Verktøy — ingen rapport-import. Skjema bygger pipeline-streng som kjøres i terminalen.</div>'
|
||||
: '';
|
||||
return (
|
||||
'<article class="catalog-card" data-command-card data-command-id="' + escapeAttr(cmd.id) + '">' +
|
||||
'<div class="catalog-card__head">' +
|
||||
'<article class="card" data-command-card data-command-id="' + escapeAttr(cmd.id) + '">' +
|
||||
'<div class="card__head">' +
|
||||
'<div>' +
|
||||
'<h3 class="catalog-card__title">' + escapeHtml(cmd.label) + '</h3>' +
|
||||
'<p class="catalog-card__desc">' + escapeHtml(cmd.description) + '</p>' +
|
||||
'<h3 class="card__title">' + escapeHtml(cmd.label) + '</h3>' +
|
||||
'<p class="card__desc">' + escapeHtml(cmd.description) + '</p>' +
|
||||
'</div>' +
|
||||
pill +
|
||||
'</div>' +
|
||||
'<div class="catalog-card__meta">' +
|
||||
'<span class="command-card__id">/architect:' + escapeHtml(cmd.id) + '</span>' +
|
||||
'<div class="card__meta">' +
|
||||
'<span class="card__id">/architect:' + escapeHtml(cmd.id) + '</span>' +
|
||||
hintHtml +
|
||||
'</div>' +
|
||||
verktoyNotice +
|
||||
'<div class="catalog-card__actions">' +
|
||||
'<div class="card__actions">' +
|
||||
'<button type="button" class="btn btn--secondary btn--sm" data-action="open-catalog-form" data-command="' + escapeAttr(cmd.id) + '">Åpne skjema</button>' +
|
||||
'</div>' +
|
||||
'</article>'
|
||||
|
|
@ -2428,11 +2374,13 @@
|
|||
{ label: 'SKILLS', value: 5 }
|
||||
]
|
||||
},
|
||||
'<div class="catalog-toolbar">' +
|
||||
'<input type="search" class="input" placeholder="Søk på navn, beskrivelse eller argument-hint…" value="' + escapeAttr(catalogSearchQuery) + '" data-catalog-search aria-label="Søk i katalog">' +
|
||||
'<span class="catalog-toolbar__count" data-catalog-count>' + countText + '</span>' +
|
||||
'</div>' +
|
||||
'<div class="catalog-groups" data-catalog-groups>' + renderCatalogGroupsHtml() + '</div>'
|
||||
'<div class="stack-lg">' +
|
||||
'<div class="catalog-toolbar">' +
|
||||
'<input type="search" class="input" placeholder="Søk på navn, beskrivelse eller argument-hint…" value="' + escapeAttr(catalogSearchQuery) + '" data-catalog-search aria-label="Søk i katalog">' +
|
||||
'<span class="catalog-toolbar__count" data-catalog-count>' + countText + '</span>' +
|
||||
'</div>' +
|
||||
'<div class="catalog-groups" data-catalog-groups>' + renderCatalogGroupsHtml() + '</div>' +
|
||||
'</div>'
|
||||
);
|
||||
root.innerHTML = (
|
||||
renderTopbar('Katalog') +
|
||||
|
|
@ -2473,8 +2421,8 @@
|
|||
'<div class="modal modal--wide">' +
|
||||
'<div>' +
|
||||
'<h2 class="modal__title" id="cf-modal-title">' + escapeHtml(cmd.label) + '</h2>' +
|
||||
'<p class="catalog-card__desc" style="margin-top: var(--space-2);">' + escapeHtml(cmd.description) + '</p>' +
|
||||
'<span class="command-card__id">/architect:' + escapeHtml(cmd.id) + '</span>' +
|
||||
'<p class="card__desc" style="margin-top: var(--space-2);">' + escapeHtml(cmd.description) + '</p>' +
|
||||
'<span class="card__id">/architect:' + escapeHtml(cmd.id) + '</span>' +
|
||||
'</div>' +
|
||||
verktoyBanner +
|
||||
'<div>' + formHtml + '</div>' +
|
||||
|
|
@ -3420,11 +3368,11 @@
|
|||
'</div>';
|
||||
}).join('');
|
||||
|
||||
const tierDescsHtml = '<div class="pyramide-desc">' + tiers.map(function (t) {
|
||||
const tierDescsHtml = '<div class="stack-sm" style="margin-top: var(--space-4);">' + tiers.map(function (t) {
|
||||
const open = (t.id === activeTier) ? ' open' : '';
|
||||
return '<details class="pyramide-desc__item" data-tier="' + escapeAttr(t.id) + '"' + open + '>' +
|
||||
return '<details class="pyramide-tier-detail" data-tier="' + escapeAttr(t.id) + '"' + open + '>' +
|
||||
'<summary>' + escapeHtml(t.label) + '</summary>' +
|
||||
'<p>' + escapeHtml(t.desc) + '</p>' +
|
||||
'<div class="pyramide-tier-detail__body">' + escapeHtml(t.desc) + '</div>' +
|
||||
'</details>';
|
||||
}).join('') + '</div>';
|
||||
|
||||
|
|
@ -3652,19 +3600,19 @@
|
|||
};
|
||||
const sevBefore = rp.before.score != null ? sevFor(rp.before.score) : '';
|
||||
const sevAfter = rp.after.score != null ? sevFor(rp.after.score) : '';
|
||||
const attrBefore = sevBefore ? ' data-severity="' + sevBefore + '"' : '';
|
||||
const attrAfter = sevAfter ? ' data-severity="' + sevAfter + '"' : '';
|
||||
residualHtml = '<div class="residual-pair">' +
|
||||
'<div class="residual-pair__cell"' + attrBefore + '>' +
|
||||
'<span class="residual-pair__cell-label">FØR TILTAK</span>' +
|
||||
'<span class="residual-pair__cell-value">' + escapeHtml(labelOf(rp.before)) + '</span>' +
|
||||
'<span class="residual-pair__cell-meta">Sannsynlighet × konsekvens</span>' +
|
||||
const cellBefore = 'pair-before-after__cell' + (sevBefore ? ' pair-before-after__cell--severity-' + sevBefore : '');
|
||||
const cellAfter = 'pair-before-after__cell' + (sevAfter ? ' pair-before-after__cell--severity-' + sevAfter : '');
|
||||
residualHtml = '<div class="pair-before-after">' +
|
||||
'<div class="' + cellBefore + '">' +
|
||||
'<span class="pair-before-after__cell-label">FØR TILTAK</span>' +
|
||||
'<span class="pair-before-after__cell-value">' + escapeHtml(labelOf(rp.before)) + '</span>' +
|
||||
'<span class="pair-before-after__cell-meta">Sannsynlighet × konsekvens</span>' +
|
||||
'</div>' +
|
||||
'<div class="residual-pair__arrow" aria-hidden="true">→</div>' +
|
||||
'<div class="residual-pair__cell"' + attrAfter + '>' +
|
||||
'<span class="residual-pair__cell-label">ETTER TILTAK</span>' +
|
||||
'<span class="residual-pair__cell-value">' + escapeHtml(labelOf(rp.after)) + '</span>' +
|
||||
'<span class="residual-pair__cell-meta">Restrisiko</span>' +
|
||||
'<div class="pair-before-after__arrow" aria-hidden="true"></div>' +
|
||||
'<div class="' + cellAfter + '">' +
|
||||
'<span class="pair-before-after__cell-label">ETTER TILTAK</span>' +
|
||||
'<span class="pair-before-after__cell-value">' + escapeHtml(labelOf(rp.after)) + '</span>' +
|
||||
'<span class="pair-before-after__cell-meta">Restrisiko</span>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
}
|
||||
|
|
@ -3736,19 +3684,19 @@
|
|||
};
|
||||
const sevBefore = rp.before.score != null ? sevForScore(rp.before.score) : '';
|
||||
const sevAfter = rp.after.score != null ? sevForScore(rp.after.score) : '';
|
||||
const attrBefore = sevBefore ? ' data-severity="' + sevBefore + '"' : '';
|
||||
const attrAfter = sevAfter ? ' data-severity="' + sevAfter + '"' : '';
|
||||
residualHtml = '<div class="residual-pair">' +
|
||||
'<div class="residual-pair__cell"' + attrBefore + '>' +
|
||||
'<span class="residual-pair__cell-label">FØR TILTAK</span>' +
|
||||
'<span class="residual-pair__cell-value">' + escapeHtml(labelOf(rp.before)) + '</span>' +
|
||||
'<span class="residual-pair__cell-meta">Sannsynlighet × konsekvens</span>' +
|
||||
const cellBefore = 'pair-before-after__cell' + (sevBefore ? ' pair-before-after__cell--severity-' + sevBefore : '');
|
||||
const cellAfter = 'pair-before-after__cell' + (sevAfter ? ' pair-before-after__cell--severity-' + sevAfter : '');
|
||||
residualHtml = '<div class="pair-before-after">' +
|
||||
'<div class="' + cellBefore + '">' +
|
||||
'<span class="pair-before-after__cell-label">FØR TILTAK</span>' +
|
||||
'<span class="pair-before-after__cell-value">' + escapeHtml(labelOf(rp.before)) + '</span>' +
|
||||
'<span class="pair-before-after__cell-meta">Sannsynlighet × konsekvens</span>' +
|
||||
'</div>' +
|
||||
'<div class="residual-pair__arrow" aria-hidden="true">→</div>' +
|
||||
'<div class="residual-pair__cell"' + attrAfter + '>' +
|
||||
'<span class="residual-pair__cell-label">ETTER TILTAK</span>' +
|
||||
'<span class="residual-pair__cell-value">' + escapeHtml(labelOf(rp.after)) + '</span>' +
|
||||
'<span class="residual-pair__cell-meta">Restrisiko</span>' +
|
||||
'<div class="pair-before-after__arrow" aria-hidden="true"></div>' +
|
||||
'<div class="' + cellAfter + '">' +
|
||||
'<span class="pair-before-after__cell-label">ETTER TILTAK</span>' +
|
||||
'<span class="pair-before-after__cell-value">' + escapeHtml(labelOf(rp.after)) + '</span>' +
|
||||
'<span class="pair-before-after__cell-meta">Restrisiko</span>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
}
|
||||
|
|
@ -3806,19 +3754,19 @@
|
|||
};
|
||||
const sevBefore = rp.before.score != null ? sevForScore(rp.before.score) : '';
|
||||
const sevAfter = rp.after.score != null ? sevForScore(rp.after.score) : '';
|
||||
const attrBefore = sevBefore ? ' data-severity="' + sevBefore + '"' : '';
|
||||
const attrAfter = sevAfter ? ' data-severity="' + sevAfter + '"' : '';
|
||||
residualHtml = '<div class="residual-pair">' +
|
||||
'<div class="residual-pair__cell"' + attrBefore + '>' +
|
||||
'<span class="residual-pair__cell-label">FØR TILTAK</span>' +
|
||||
'<span class="residual-pair__cell-value">' + escapeHtml(labelOf(rp.before)) + '</span>' +
|
||||
'<span class="residual-pair__cell-meta">Sannsynlighet × konsekvens</span>' +
|
||||
const cellBefore = 'pair-before-after__cell' + (sevBefore ? ' pair-before-after__cell--severity-' + sevBefore : '');
|
||||
const cellAfter = 'pair-before-after__cell' + (sevAfter ? ' pair-before-after__cell--severity-' + sevAfter : '');
|
||||
residualHtml = '<div class="pair-before-after">' +
|
||||
'<div class="' + cellBefore + '">' +
|
||||
'<span class="pair-before-after__cell-label">FØR TILTAK</span>' +
|
||||
'<span class="pair-before-after__cell-value">' + escapeHtml(labelOf(rp.before)) + '</span>' +
|
||||
'<span class="pair-before-after__cell-meta">Sannsynlighet × konsekvens</span>' +
|
||||
'</div>' +
|
||||
'<div class="residual-pair__arrow" aria-hidden="true">→</div>' +
|
||||
'<div class="residual-pair__cell"' + attrAfter + '>' +
|
||||
'<span class="residual-pair__cell-label">ETTER TILTAK</span>' +
|
||||
'<span class="residual-pair__cell-value">' + escapeHtml(labelOf(rp.after)) + '</span>' +
|
||||
'<span class="residual-pair__cell-meta">Restrisiko</span>' +
|
||||
'<div class="pair-before-after__arrow" aria-hidden="true"></div>' +
|
||||
'<div class="' + cellAfter + '">' +
|
||||
'<span class="pair-before-after__cell-label">ETTER TILTAK</span>' +
|
||||
'<span class="pair-before-after__cell-value">' + escapeHtml(labelOf(rp.after)) + '</span>' +
|
||||
'<span class="pair-before-after__cell-meta">Restrisiko</span>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
}
|
||||
|
|
@ -4354,8 +4302,8 @@
|
|||
}
|
||||
return '<div>' + escapeHtml(txt).replace(/\n/g, '<br>') + '</div>';
|
||||
};
|
||||
const tabsNavHtml = tabs.length ? '<nav class="screen-tabs" role="tablist" aria-label="Utredning">' + tabs.map(function (t, i) {
|
||||
return '<a class="screen-tab" role="tab" aria-current="' + (i === 0 ? 'true' : 'false') + '" href="#utr-' + escapeAttr(t.id) + '">' + escapeHtml(t.label) + '</a>';
|
||||
const tabsNavHtml = tabs.length ? '<nav class="tab-list" role="tablist" aria-label="Utredning">' + tabs.map(function (t, i) {
|
||||
return '<a class="tab" role="tab" aria-current="' + (i === 0 ? 'true' : 'false') + '" href="#utr-' + escapeAttr(t.id) + '">' + escapeHtml(t.label) + '</a>';
|
||||
}).join('') + '</nav>' : '';
|
||||
const tabsBodyHtml = tabs.map(function (t) {
|
||||
return '<section id="utr-' + escapeAttr(t.id) + '" class="utr-panel">' +
|
||||
|
|
@ -4726,9 +4674,9 @@
|
|||
function renderKeyStatsGrid(stats) {
|
||||
if (!stats || !stats.length) return '';
|
||||
const items = stats.map(function (s) {
|
||||
const mod = s.modifier ? ' data-modifier="' + escapeAttr(s.modifier) + '"' : '';
|
||||
const cls = 'key-stat' + (s.modifier ? ' key-stat--' + escapeAttr(s.modifier) : '');
|
||||
const hint = s.hint ? '<span class="key-stat__hint">' + escapeHtml(s.hint) + '</span>' : '';
|
||||
return '<div class="key-stat"' + mod + '>' +
|
||||
return '<div class="' + cls + '">' +
|
||||
'<span class="key-stat__label">' + escapeHtml(s.label || '') + '</span>' +
|
||||
'<span class="key-stat__value">' + escapeHtml(String(s.value)) + '</span>' +
|
||||
hint +
|
||||
|
|
@ -4740,14 +4688,15 @@
|
|||
function renderPageShell(opts, bodyHtml) {
|
||||
opts = opts || {};
|
||||
const eyebrow = opts.eyebrow ? '<span class="page__eyebrow">' + escapeHtml(opts.eyebrow) + '</span>' : '';
|
||||
const title = '<h1>' + escapeHtml(opts.title || '') + '</h1>';
|
||||
const title = '<h1 class="page__title">' + escapeHtml(opts.title || '') + '</h1>';
|
||||
const lede = opts.lede ? '<p class="page__lede">' + escapeHtml(opts.lede) + '</p>' : '';
|
||||
const verdict = (opts.verdict && opts.verdict !== 'n-a') ? renderVerdictPill(opts.verdict) : '';
|
||||
const aside = verdict ? '<div class="page__header-aside">' + verdict + '</div>' : '';
|
||||
const stats = renderKeyStatsGrid(opts.keyStats);
|
||||
return (
|
||||
'<header class="page__header">' +
|
||||
'<div class="page__title">' + eyebrow + title + lede + '</div>' +
|
||||
verdict +
|
||||
'<div class="page__header-main">' + eyebrow + title + lede + '</div>' +
|
||||
aside +
|
||||
'</header>' +
|
||||
stats +
|
||||
(bodyHtml || '')
|
||||
|
|
@ -5452,17 +5401,18 @@
|
|||
const screen = el.dataset.screen;
|
||||
if (!screen) return;
|
||||
currentProjectScreen = screen;
|
||||
// Toggle aria-current på screen-tabs + data-active på .screen-paneler
|
||||
// Toggle aria-current på .tab-list-knappene + [hidden] på .tab-panel-paneler
|
||||
// uten full re-render (bevarer evt textarea-input i panels).
|
||||
const root = getSurfaceEl('project');
|
||||
if (!root) return;
|
||||
const tabs = root.querySelectorAll('.screen-tab');
|
||||
const tabs = root.querySelectorAll('.tab-list .tab');
|
||||
tabs.forEach(function (t) {
|
||||
t.setAttribute('aria-current', t.dataset.screen === screen ? 'true' : 'false');
|
||||
});
|
||||
const screens = root.querySelectorAll('.screen[data-screen-id]');
|
||||
const screens = root.querySelectorAll('.tab-panel[data-screen-id]');
|
||||
screens.forEach(function (s) {
|
||||
s.setAttribute('data-active', s.dataset.screenId === screen ? 'true' : 'false');
|
||||
if (s.dataset.screenId === screen) s.removeAttribute('hidden');
|
||||
else s.setAttribute('hidden', '');
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -5553,9 +5503,9 @@
|
|||
};
|
||||
|
||||
ACTIONS['import-state'] = function (ev, el) {
|
||||
const topbar = el.closest('.topbar');
|
||||
if (!topbar) return;
|
||||
const input = topbar.querySelector('[data-import-input]');
|
||||
const header = el.closest('.app-header');
|
||||
if (!header) return;
|
||||
const input = header.querySelector('[data-import-input]');
|
||||
if (!input) return;
|
||||
input.value = ''; // tillat samme fil to ganger
|
||||
input.click();
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
"generated_by": "scripts/sync-design-system.mjs",
|
||||
"do_not_edit": true,
|
||||
"source": "shared/playground-design-system/",
|
||||
"source_commit": "e3378e9b9c8df259ba12f8146c44e5c18b40ff31",
|
||||
"sync_date": "2026-05-04T07:56:18.490Z",
|
||||
"source_commit": "f58b89243653b8207f006460e75d4a39bc8348a1",
|
||||
"sync_date": "2026-05-04T14:45:29.457Z",
|
||||
"file_count": 26,
|
||||
"files": {
|
||||
"CHANGELOG.md": "e293a911701e0ae8e95f8d30e2b583d1c578d0c2af4fd2abfbee3a7d65d5f7ba",
|
||||
|
|
|
|||
|
|
@ -407,7 +407,8 @@ for fn in $SC8_RENDERERS_A; do
|
|||
done
|
||||
|
||||
# -------------------------------------------------------
|
||||
# 25b. Step 10 must_contain — kanban-board + residual-pair
|
||||
# 25b. Step 10 must_contain — kanban-board + pair-before-after
|
||||
# (v1.11.0: residual-pair migrert til DS-navn pair-before-after)
|
||||
# -------------------------------------------------------
|
||||
kanban_count=$( { grep -cE "kanban-board|kanban-col" "$HTML_FILE" || true; } | tr -d ' ')
|
||||
if [ "${kanban_count:-0}" -ge 1 ]; then
|
||||
|
|
@ -416,11 +417,11 @@ else
|
|||
fail "kanban-board markup mangler (Step 10 must_contain krever >=1)"
|
||||
fi
|
||||
|
||||
residual_count=$( { grep -cE "residual-pair" "$HTML_FILE" || true; } | tr -d ' ')
|
||||
if [ "${residual_count:-0}" -ge 1 ]; then
|
||||
pass "residual-pair markup til stede ($residual_count treff, Step 10 must_contain)"
|
||||
pair_count=$( { grep -cE "pair-before-after" "$HTML_FILE" || true; } | tr -d ' ')
|
||||
if [ "${pair_count:-0}" -ge 1 ]; then
|
||||
pass "pair-before-after markup til stede ($pair_count treff, Step 10 must_contain)"
|
||||
else
|
||||
fail "residual-pair markup mangler (Step 10 must_contain krever >=1)"
|
||||
fail "pair-before-after markup mangler (Step 10 must_contain krever >=1)"
|
||||
fi
|
||||
|
||||
# -------------------------------------------------------
|
||||
|
|
@ -473,7 +474,8 @@ for fn in $SC8_RENDERERS_C; do
|
|||
done
|
||||
|
||||
# -------------------------------------------------------
|
||||
# 25f. Step 12 must_contain — mat-ladder + screen-tabs + _consumer
|
||||
# 25f. Step 12 must_contain — mat-ladder + tab-list + _consumer
|
||||
# (v1.11.0: screen-tabs migrert til DS-navn tab-list)
|
||||
# -------------------------------------------------------
|
||||
matladder_count=$( { grep -cE "mat-ladder" "$HTML_FILE" || true; } | tr -d ' ')
|
||||
if [ "${matladder_count:-0}" -ge 1 ]; then
|
||||
|
|
@ -482,11 +484,11 @@ else
|
|||
fail "mat-ladder markup mangler (Step 12 must_contain krever >=1)"
|
||||
fi
|
||||
|
||||
screentabs_count=$( { grep -cE "screen-tabs" "$HTML_FILE" || true; } | tr -d ' ')
|
||||
if [ "${screentabs_count:-0}" -ge 1 ]; then
|
||||
pass "screen-tabs markup til stede ($screentabs_count treff, Step 12 must_contain)"
|
||||
tablist_count=$( { grep -cE 'class="tab-list"' "$HTML_FILE" || true; } | tr -d ' ')
|
||||
if [ "${tablist_count:-0}" -ge 1 ]; then
|
||||
pass "tab-list markup til stede ($tablist_count treff, Step 12 must_contain)"
|
||||
else
|
||||
fail "screen-tabs markup mangler (Step 12 must_contain krever >=1)"
|
||||
fail "tab-list markup mangler (Step 12 must_contain krever >=1)"
|
||||
fi
|
||||
|
||||
consumer_count=$( { grep -cE "_consumer" "$HTML_FILE" || true; } | tr -d ' ')
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue