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
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
79 lines
2.8 KiB
TypeScript
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>
|
|
);
|
|
}
|