feat(iace): DSMS-CID-Badge im Tech-File-Export + aggregierter Bulk-Diff
CI / detect-changes (push) Successful in 8s
CI / branch-name (push) Has been skipped
CI / build-sha-integrity (push) Failing after 4s
CI / validate-canonical-controls (push) Successful in 10s
CI / loc-budget (push) Successful in 14s
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / nodejs-build (push) Successful in 2m21s
CI / test-go (push) Failing after 37s
CI / iace-gt-coverage (push) Successful in 23s
CI / test-python-backend (push) Has been skipped
CI / guardrail-integrity (push) Has been skipped
CI / secret-scan (push) Has been skipped
CI / dep-audit (push) Has been skipped
CI / sbom-scan (push) Has been skipped
CI / test-python-document-crawler (push) Has been skipped
CI / test-python-dsms-gateway (push) Successful in 17s

Punkt 1 — UI-CID-Badge nach erfolgreichem Tech-File-Export:
- archiveTechFile setzt X-DSMS-CID / X-DSMS-Filename / X-DSMS-Size response
  headers + Access-Control-Expose-Headers, sobald DSMS-Archive durchlief
- Split iace_handler_techfile.go (war ueber 500 LOC) → archiveTechFile lebt
  jetzt in iace_handler_techfile_archive.go, setDSMSResponseHeaders als
  pure Helper mit 3 unit tests
- Next.js IACE-Proxy forwarded die X-DSMS-* Header und erkennt jetzt auch
  XLSX/DOCX/MD als Binary-Response (vorher nur PDF/ZIP/octet-stream)
- ExportCIDBadge.tsx zeigt CID, Filename, Groesse + Kopieren-Button +
  "Verlauf anzeigen" (oeffnet CIDHistoryModal)

Punkt 2 — Bulk-Diff Report V1 → V_latest:
- Neuer Endpoint GET /api/v1/documents/{cid}/bulk-diff im dsms-gateway:
  laeuft parent_cid-Kette ab, berechnet chronologische Step-Diffs,
  aggregiert Totals (added/removed lines, metadata_fields_changed,
  binary_steps). Edge-Cases: einzelne Version, binaere Steps, abgebrochene
  Kette
- BulkDiffPanel.tsx zeigt 4-Stat-Header + Step-Tabelle
- CIDHistoryModal bekommt Toggle-Button "Bulk-Diff V1 → V_latest anzeigen"
  neben dem Versions-Counter; damit auch vom IACE-Export-Badge erreichbar

Tests: 3 neue Go-Tests, 4 neue pytest-Tests, alle gruen

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-06-09 09:07:20 +02:00
parent d3ac33d53a
commit 216c7b8eca
10 changed files with 684 additions and 42 deletions
@@ -0,0 +1,95 @@
'use client'
import { useState } from 'react'
import CIDHistoryModal from '@/app/sdk/audit-timeline/_components/CIDHistoryModal'
export interface LastExport {
cid: string
filename: string
size: number
format: string
}
interface Props {
lastExport: LastExport | null
onDismiss: () => void
}
function formatBytes(n: number): string {
if (n < 1024) return `${n} B`
if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`
return `${(n / 1024 / 1024).toFixed(2)} MB`
}
export function ExportCIDBadge({ lastExport, onDismiss }: Props) {
const [showHistory, setShowHistory] = useState(false)
const [copied, setCopied] = useState(false)
if (!lastExport) return null
async function copyToClipboard() {
if (!lastExport) return
try {
await navigator.clipboard.writeText(lastExport.cid)
setCopied(true)
setTimeout(() => setCopied(false), 1500)
} catch {
// clipboard not available — silent
}
}
return (
<>
<div className="rounded-xl border border-emerald-200 bg-emerald-50 dark:border-emerald-800 dark:bg-emerald-900/20 p-4">
<div className="flex items-start gap-3">
<div className="rounded-full bg-emerald-500 p-1 flex-shrink-0 mt-0.5">
<svg className="w-4 h-4 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={3} d="M5 13l4 4L19 7" />
</svg>
</div>
<div className="flex-1 min-w-0">
<div className="text-sm font-semibold text-emerald-900 dark:text-emerald-200">
CE-Akte exportiert und in DSMS archiviert
</div>
<div className="mt-1 text-xs text-emerald-800 dark:text-emerald-300">
{lastExport.filename} · {formatBytes(lastExport.size)} · Format {lastExport.format.toUpperCase()}
</div>
<div className="mt-2 flex items-center gap-2 flex-wrap">
<span className="text-[10px] uppercase tracking-wide text-emerald-700 dark:text-emerald-400 font-semibold">
CID
</span>
<code className="font-mono text-xs text-emerald-900 dark:text-emerald-100 bg-white/60 dark:bg-black/20 px-2 py-0.5 rounded select-all break-all">
{lastExport.cid}
</code>
<button
onClick={copyToClipboard}
className="text-[11px] text-emerald-700 hover:text-emerald-900 dark:text-emerald-400 dark:hover:text-emerald-200 underline"
title="CID in Zwischenablage kopieren"
>
{copied ? '✓ Kopiert' : 'Kopieren'}
</button>
<button
onClick={() => setShowHistory(true)}
className="text-[11px] text-emerald-700 hover:text-emerald-900 dark:text-emerald-400 dark:hover:text-emerald-200 underline"
title="DSMS-Versionsverlauf und Diffs anzeigen"
>
Verlauf anzeigen
</button>
</div>
</div>
<button
onClick={onDismiss}
className="text-emerald-600 hover:text-emerald-800 dark:text-emerald-400 dark:hover:text-emerald-200 p-1 flex-shrink-0"
title="Hinweis schliessen"
aria-label="Hinweis schliessen"
>
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
</div>
{showHistory && <CIDHistoryModal cid={lastExport.cid} onClose={() => setShowHistory(false)} />}
</>
)
}