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
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:
@@ -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)} />}
|
||||
</>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user