CI / Web-Barrierefreiheitspruefung (axe, Tastatur, Reflow, Textabstaende) (push) Canceled after 0s
CI / Web-Barrierefreiheitspruefung (axe, Tastatur, Reflow, Textabstaende) (pull_request) Canceled after 0s
CI / Core-Modul (Build/Vet/Test) (push) Canceled after 0s
CI / DMS Security-Test-Gate (AC5, Merge-Gate) (push) Canceled after 0s
CI / Connect Security-Test-Gate (QA-01 AC4, Merge-Gate) (push) Canceled after 0s
CI / DMS-Modul Supply-Chain-Scan (govulncheck) (push) Canceled after 0s
CI / Web (dms-browser) (push) Canceled after 0s
CI / Web (pdf-viewer) (push) Canceled after 0s
CI / Web (shl) (push) Canceled after 0s
Supply-Chain-Scan (npm) / npm-audit (push) Canceled after 0s
CI / Core-Modul (Build/Vet/Test) (pull_request) Canceled after 0s
CI / DMS Security-Test-Gate (AC5, Merge-Gate) (pull_request) Canceled after 0s
CI / Connect Security-Test-Gate (QA-01 AC4, Merge-Gate) (pull_request) Canceled after 0s
CI / DMS-Modul Supply-Chain-Scan (govulncheck) (pull_request) Canceled after 0s
CI / Web (dms-browser) (pull_request) Canceled after 0s
CI / Web (pdf-viewer) (pull_request) Canceled after 0s
CI / Web (shl) (pull_request) Canceled after 0s
Core-Schnittstellen-Vertragstests / contract-tests (pull_request) Canceled after 0s
Supply-Chain-Scan (Go) / govulncheck (pull_request) Canceled after 0s
Mail-Pflichttest-Gate / pflichttest-gate (pull_request) Canceled after 0s
Supply-Chain-Scan (npm) / npm-audit (pull_request) Canceled after 0s
Core-Pflichttest-Gate / pflichttest-gate (pull_request) Canceled after 0s
RBAC-Rollenhierarchie-Pflichtgate / rbac-policy-invariant (pull_request) Canceled after 0s
CI / DMS-Modul (Lint/Build/Test/Coverage/Artefakt) (push) Canceled after 0s
CI / DMS-Modul (Lint/Build/Test/Coverage/Artefakt) (pull_request) Canceled after 0s
Frontend (web/mail-search): Anhang-Vorschau fuer PDF (Canvas-Rendering via pdfjs-dist, gleiches Muster wie DOC-05s web/pdf-viewer/PdfViewer.tsx -- kein aequivalentes PdfCanvasPreview im Repo gefunden, daher PdfViewer als Vorlage uebernommen statt neu erfunden) und Bilder (<img> auf einem authentifiziert geladenen Blob). Die Vorschauart haengt ausschliesslich vom serverseitig per Magic-Bytes bestimmten contentType ab, nie von einem Dateinamen (den es an der Schnittstelle gar nicht gibt); nicht unterstuetzte Typen werden nur als Hinweistext angezeigt, nie eingebettet. Kein iframe/<object>/dangerouslySetInnerHTML -- durch automatisierten Sicherheitsreview-Test abgesichert. Neue lib/http.ts (authentifizierter Zugriff ueber @nexarch/shl, identisch zu web/pdf-viewer/lib/http.ts) und lib/attachments.ts (Anhangsliste/ -inhalt der Mail-API). Pruefprotokoll in mail/docs/UX-03-PRUEFPROTOKOLL.md. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
51 lines
1.4 KiB
TypeScript
51 lines
1.4 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useState } from "react";
|
|
import { fetchAttachmentList, type AttachmentListResult } from "../../../lib/attachments";
|
|
import { AttachmentPreview } from "./AttachmentPreview";
|
|
|
|
export interface AttachmentListProps {
|
|
messageId: string;
|
|
}
|
|
|
|
// UX-03 Akzeptanzkriterium 1: laedt die Anhangsliste und zeigt je Anhang
|
|
// eine Vorschau. Ein Ausfall der Mail-API blockiert die uebrige
|
|
// Mailanzeige nicht (gleiche Konvention wie CaseContextPanel).
|
|
export function AttachmentList({ messageId }: AttachmentListProps) {
|
|
const [result, setResult] = useState<AttachmentListResult | null>(null);
|
|
|
|
useEffect(() => {
|
|
let cancelled = false;
|
|
setResult(null);
|
|
fetchAttachmentList(messageId).then((r) => {
|
|
if (!cancelled) setResult(r);
|
|
});
|
|
return () => {
|
|
cancelled = true;
|
|
};
|
|
}, [messageId]);
|
|
|
|
if (result === null) {
|
|
return null;
|
|
}
|
|
if (result.status === "unavailable") {
|
|
return (
|
|
<section aria-label="Anhänge" style={{ marginTop: 24 }}>
|
|
<p>Anhänge konnten nicht geladen werden.</p>
|
|
</section>
|
|
);
|
|
}
|
|
if (result.attachments.length === 0) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<section aria-label="Anhänge" style={{ marginTop: 24 }}>
|
|
<h2>Anhänge</h2>
|
|
{result.attachments.map((attachment) => (
|
|
<AttachmentPreview key={attachment.index} messageId={messageId} attachment={attachment} />
|
|
))}
|
|
</section>
|
|
);
|
|
}
|