Files
rootandClaude Sonnet 5 0097c8cf2b
CI / Web-Barrierefreiheitspruefung (axe, Tastatur, Reflow, Textabstaende) (pull_request) Canceled after 0s
CI / Web-Barrierefreiheitspruefung (axe, Tastatur, Reflow, Textabstaende) (push) 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 / 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 (Go) / govulncheck (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
Supply-Chain-Scan (Go) / govulncheck (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 (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
INT-19: mobile erfassung mobilfreundliche weboberflaeche
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-26 12:06:19 +00:00

79 lines
2.8 KiB
TypeScript

"use client";
import { useCallback, useReducer, useRef, useState } from "react";
import { useToast } from "@nexarch/shl";
import { CameraCapture } from "./CameraCapture";
import { UploadList } from "./UploadList";
import { applyUploadQueueAction, createQueuedItem, initialQueueState } from "../lib/uploadQueue";
import { uploadFileInChunks } from "../lib/chunkUpload";
import { pageToFile, type CapturedPage } from "../lib/mobileCapture";
// INT-19: gleiche Warteschlangen-Logik wie UploadManager (UX-10/DOC-03),
// bewusst dupliziert statt UploadManager umzubauen ("kein Umbau angrenzender
// Bereiche") — einzige Aenderung ist die Dateiquelle (Kamera statt Dropzone).
export function CameraCaptureManager() {
const [state, dispatch] = useReducer(applyUploadQueueAction, undefined, initialQueueState);
const [submitting, setSubmitting] = useState(false);
const { push } = useToast();
const filesById = useRef(new Map<string, File>());
const runUpload = useCallback(
async (id: string, file: File, resumeSessionId?: string) => {
dispatch({ type: "uploadStarted", id });
try {
const result = await uploadFileInChunks(
file,
{
onProgress: (bytesUploaded) => dispatch({ type: "progress", id, bytesUploaded }),
onSessionAssigned: (sessionId) => dispatch({ type: "sessionAssigned", id, sessionId }),
},
resumeSessionId,
);
dispatch({ type: "succeeded", id, documentId: result.documentId });
push(`${file.name} erfolgreich eingereicht.`, "success");
} catch (err) {
const message = err instanceof Error ? err.message : "Unbekannter Fehler beim Einreichen.";
dispatch({ type: "failed", id, message });
push(`${file.name}: ${message}`, "danger");
}
},
[push],
);
const handleSubmit = useCallback(
async (pages: CapturedPage[]) => {
setSubmitting(true);
try {
const files = pages.map(pageToFile);
const items = files.map(createQueuedItem);
items.forEach((item) => filesById.current.set(item.id, item.file));
dispatch({ type: "filesAdded", items });
for (const item of items) {
await runUpload(item.id, item.file);
}
} finally {
setSubmitting(false);
}
},
[runUpload],
);
const handleRetry = useCallback(
(id: string) => {
const item = state.items.find((entry) => entry.id === id);
const file = filesById.current.get(id);
if (!item || !file) return;
dispatch({ type: "retry", id });
void runUpload(id, file, item.sessionId);
},
[state.items, runUpload],
);
return (
<div>
<CameraCapture onSubmit={handleSubmit} submitting={submitting} />
<UploadList items={state.items} onRetry={handleRetry} />
</div>
);
}