feat(ms-ai-architect): renderer A.5 conformity adopt page-header + kanban-board
- parseConformityChecklist utvidet med buckets {passed, conditional, failed} via bucketOf-mapping
- Status-mapping stotter bade engelsk (met/partial/missing) og norsk (bestatt/betinget/avvist) for backward-compat
- renderConformity: erstatter findings-listen med E1 kanban-board (3 kolonner: Bestatt, Med betingelser, Ikke bestatt)
- aiact-timeline beholdt for deadlines (under kanban som sekundaer report-meta-blokk)
- Wrapped med renderPageShell (eyebrow SAMSVAR)
- Fixture conformity.md oppdatert til norske status-markorer for tydeligere bucket-mapping (5 bestatt, 3 betinget, 4 avvist)
This commit is contained in:
parent
ead1697ff0
commit
3a1dd8a70f
2 changed files with 65 additions and 44 deletions
|
|
@ -2508,13 +2508,25 @@
|
|||
const reqKey = checklistTbl.headers.find(function (h) { return /krav|requirement/i.test(h); }) || checklistTbl.headers[0];
|
||||
const statusKey = checklistTbl.headers.find(function (h) { return /status/i.test(h); }) || checklistTbl.headers[1];
|
||||
const evidKey = checklistTbl.headers.find(function (h) { return /bevis|evidence/i.test(h); }) || checklistTbl.headers[2];
|
||||
// Bucket-klassifisering — støtter bade engelske og norske status-markører.
|
||||
const bucketOf = function (status) {
|
||||
const s = (status || '').toLowerCase().trim();
|
||||
if (/^(pass|met|ok|bestått|bestatt|godkjent|approved|done)$/.test(s)) return 'passed';
|
||||
if (/^(partial|conditional|betinget|delvis|in-progress|active)$/.test(s)) return 'conditional';
|
||||
if (/^(missing|failed|avvist|underkjent|fail|rejected|blocked)$/.test(s)) return 'failed';
|
||||
return 'conditional';
|
||||
};
|
||||
const checklist = checklistTbl.rows.map(function (row) {
|
||||
const status = (row[statusKey] || '').toLowerCase().trim();
|
||||
return {
|
||||
requirement: row[reqKey] || '',
|
||||
status: (row[statusKey] || '').toLowerCase().trim(),
|
||||
status: status,
|
||||
bucket: bucketOf(status),
|
||||
evidence: row[evidKey] || ''
|
||||
};
|
||||
});
|
||||
const buckets = { passed: [], conditional: [], failed: [] };
|
||||
checklist.forEach(function (it) { buckets[it.bucket].push(it); });
|
||||
const deadlinesTbl = parseTable(md, /##\s*Frister/i);
|
||||
const deadlines = deadlinesTbl ? deadlinesTbl.rows.map(function (row) {
|
||||
const dateKey = deadlinesTbl.headers.find(function (h) { return /dato|date/i.test(h); }) || deadlinesTbl.headers[0];
|
||||
|
|
@ -2526,7 +2538,7 @@
|
|||
status: (row[stKey] || '').toLowerCase().trim()
|
||||
};
|
||||
}) : [];
|
||||
return { ok: true, data: { checklist: checklist, deadlines: deadlines } };
|
||||
return { ok: true, data: { checklist: checklist, buckets: buckets, deadlines: deadlines } };
|
||||
}
|
||||
|
||||
function parseMatrixRisk(md) {
|
||||
|
|
@ -3162,6 +3174,29 @@
|
|||
}
|
||||
|
||||
function renderConformity(data, slot) {
|
||||
const buckets = data.buckets || { passed: [], conditional: [], failed: [] };
|
||||
const cardFor = function (bucket, label) {
|
||||
const items = buckets[bucket] || [];
|
||||
const cards = items.length ? items.map(function (it, idx) {
|
||||
return '<div class="kanban-card">' +
|
||||
'<div class="kanban-card__name">C-' + String(idx + 1).padStart(2, '0') + ' — ' + escapeHtml(it.requirement) + '</div>' +
|
||||
'<div class="kanban-card__meta">Bevis: ' + escapeHtml(it.evidence || '—') + '</div>' +
|
||||
'</div>';
|
||||
}).join('') : '<div class="kanban-col__empty">Ingen krav</div>';
|
||||
return '<div class="kanban-col" data-bucket="' + escapeAttr(bucket) + '">' +
|
||||
'<div class="kanban-col__head">' +
|
||||
'<span class="kanban-col__title">' + escapeHtml(label) + '</span>' +
|
||||
'<span class="kanban-col__count">' + items.length + '</span>' +
|
||||
'</div>' +
|
||||
cards +
|
||||
'</div>';
|
||||
};
|
||||
const kanbanHtml = '<div class="kanban-board">' +
|
||||
cardFor('passed', 'Bestått') +
|
||||
cardFor('conditional', 'Med betingelser') +
|
||||
cardFor('failed', 'Ikke bestått') +
|
||||
'</div>';
|
||||
|
||||
const stateOf = function (status) {
|
||||
const s = (status || '').toLowerCase();
|
||||
if (s === 'passed' || s === 'met' || s === 'done') return 'passed';
|
||||
|
|
@ -3182,38 +3217,24 @@
|
|||
'</div>';
|
||||
}).join('');
|
||||
timelineHtml =
|
||||
'<div class="aiact-timeline">' +
|
||||
'<div class="aiact-timeline__track">' +
|
||||
'<div class="aiact-timeline__progress" style="width: 0%"></div>' +
|
||||
milestones +
|
||||
'<section class="report-meta"><h4>Frister</h4>' +
|
||||
'<div class="aiact-timeline">' +
|
||||
'<div class="aiact-timeline__track">' +
|
||||
'<div class="aiact-timeline__progress" style="width: 0%"></div>' +
|
||||
milestones +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
'</section>';
|
||||
}
|
||||
const sevForStatus = function (status) {
|
||||
const s = (status || '').toLowerCase();
|
||||
if (s === 'met') return 'low';
|
||||
if (s === 'partial') return 'medium';
|
||||
if (s === 'missing') return 'critical';
|
||||
return 'info';
|
||||
};
|
||||
const items = (data.checklist || []).map(function (it, idx) {
|
||||
return '<li class="findings__item" data-status="' + escapeAttr(it.status) + '">' +
|
||||
'<span class="findings__item-severity-dot" data-severity="' + escapeAttr(sevForStatus(it.status)) + '"></span>' +
|
||||
'<span class="findings__item-id">C-' + String(idx + 1).padStart(2, '0') + '</span>' +
|
||||
'<span class="findings__item-title">' + escapeHtml(it.requirement) + '</span>' +
|
||||
'<span class="findings__item-meta">Bevis: ' + escapeHtml(it.evidence || '—') + ' · ' + escapeHtml(it.status || '—') + '</span>' +
|
||||
'</li>';
|
||||
}).join('');
|
||||
const findingsHtml =
|
||||
'<div class="findings">' +
|
||||
'<div class="findings__list">' +
|
||||
'<div class="findings__group">' +
|
||||
'<div class="findings__group-header"><span>Sjekkliste</span><span>' + (data.checklist || []).length + '</span></div>' +
|
||||
'<ul class="findings__items">' + items + '</ul>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
slot.innerHTML = timelineHtml + findingsHtml;
|
||||
|
||||
const body = kanbanHtml + timelineHtml;
|
||||
slot.innerHTML = renderPageShell({
|
||||
eyebrow: 'SAMSVAR',
|
||||
title: data.title || 'Samsvarsvurdering (Art. 43)',
|
||||
lede: data.lede || 'Annex IV-sjekkliste fordelt på Bestått / Med betingelser / Ikke bestått.',
|
||||
verdict: data.verdict || inferVerdict(data, 'conformity-checklist'),
|
||||
keyStats: data.keyStats || inferKeyStats(data, 'conformity-checklist')
|
||||
}, body);
|
||||
}
|
||||
|
||||
function renderDpia(data, slot) {
|
||||
|
|
|
|||
|
|
@ -7,18 +7,18 @@ Vurderingsprosedyre: Annex VI (intern kontroll)
|
|||
|
||||
| Krav | Status | Bevis |
|
||||
|------|--------|-------|
|
||||
| Risk Management System dokumentert | met | RMS-rapport v2.1 (2026-04-15) |
|
||||
| Treningsdata-governance med kvalitetskriterier | met | Data-governance handbook §4.2 |
|
||||
| Teknisk dokumentasjon Annex IV komplett | partial | Mangler ytelsesmål per stratum |
|
||||
| Logging av hendelser implementert | met | OpenTelemetry-spans i Azure Monitor |
|
||||
| Transparens-instruksjoner skrevet | missing | Skal leveres innen 2026-09-01 |
|
||||
| Menneskelig oversikt på saksbehandler | met | Workflow-design godkjent av juridisk |
|
||||
| Nøyaktighetsmål dokumentert | partial | 96.3% overall, men ikke per objekt-ID-region |
|
||||
| Robusthet under adversarielle forhold | partial | Test-suite mangler skitne plater og natt-scenarier |
|
||||
| Cybersikkerhetstiltak per Art. 15 | met | NSM Grunnprinsipper-vurdering bestått |
|
||||
| Conformity assessment underskrevet | missing | Avhengig av FRIA-resultat |
|
||||
| EU declaration of conformity utstedt | missing | Avhenger av Art. 47 |
|
||||
| CE-merking påført | missing | Markedsplassering ikke aktuell (intern bruk) — vurder om Art. 48 gjelder |
|
||||
| Risk Management System dokumentert | bestått | RMS-rapport v2.1 (2026-04-15) |
|
||||
| Treningsdata-governance med kvalitetskriterier | bestått | Data-governance handbook §4.2 |
|
||||
| Teknisk dokumentasjon Annex IV komplett | betinget | Mangler ytelsesmål per stratum |
|
||||
| Logging av hendelser implementert | bestått | OpenTelemetry-spans i Azure Monitor |
|
||||
| Transparens-instruksjoner skrevet | avvist | Skal leveres innen 2026-09-01 |
|
||||
| Menneskelig oversikt på saksbehandler | bestått | Workflow-design godkjent av juridisk |
|
||||
| Nøyaktighetsmål dokumentert | betinget | 96.3% overall, men ikke per objekt-ID-region |
|
||||
| Robusthet under adversarielle forhold | betinget | Test-suite mangler skitne plater og natt-scenarier |
|
||||
| Cybersikkerhetstiltak per Art. 15 | bestått | NSM Grunnprinsipper-vurdering bestått |
|
||||
| Conformity assessment underskrevet | avvist | Avhengig av FRIA-resultat |
|
||||
| EU declaration of conformity utstedt | avvist | Avhenger av Art. 47 |
|
||||
| CE-merking påført | avvist | Markedsplassering ikke aktuell (intern bruk) — vurder om Art. 48 gjelder |
|
||||
|
||||
## Frister
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue