Files
files/nextjs/app/manage/_components/upload-progress-form.js
2026-03-27 20:10:51 +01:00

119 lines
3.3 KiB
JavaScript

'use client';
import { useState } from 'react';
function parseErrorMessage(xhr) {
const response = xhr.response;
if (response && typeof response === 'object' && response.error) {
return String(response.error);
}
try {
const parsed = JSON.parse(xhr.responseText || '{}');
if (parsed && typeof parsed.error === 'string') {
return parsed.error;
}
} catch {
}
return `Upload fehlgeschlagen (HTTP ${xhr.status}).`;
}
export function UploadProgressForm({ csrfToken }) {
const [uploading, setUploading] = useState(false);
const [progress, setProgress] = useState(0);
const [localError, setLocalError] = useState('');
function handleSubmit(event) {
event.preventDefault();
if (uploading) {
return;
}
const form = event.currentTarget;
const formData = new FormData(form);
const uploadedFile = formData.get('file');
if (!uploadedFile || typeof uploadedFile === 'string' || !uploadedFile.size) {
setLocalError('Bitte zuerst eine Datei auswählen.');
return;
}
setUploading(true);
setProgress(0);
setLocalError('');
const xhr = new XMLHttpRequest();
xhr.open('POST', '/manage/api/upload', true);
xhr.responseType = 'json';
xhr.setRequestHeader('x-csrf-token', csrfToken);
xhr.upload.onprogress = (uploadEvent) => {
if (!uploadEvent.lengthComputable || uploadEvent.total <= 0) {
return;
}
const nextValue = Math.round((uploadEvent.loaded / uploadEvent.total) * 100);
setProgress(Math.max(0, Math.min(100, nextValue)));
};
xhr.onerror = () => {
setUploading(false);
setLocalError('Netzwerkfehler beim Upload.');
};
xhr.onload = () => {
if (xhr.status >= 200 && xhr.status < 300) {
const redirectPath = xhr.response?.redirect || '/manage/dashboard?success=Upload%20abgeschlossen.';
window.location.assign(redirectPath);
return;
}
setUploading(false);
setProgress(0);
setLocalError(parseErrorMessage(xhr));
};
xhr.send(formData);
}
return (
<form
className="form-grid"
onSubmit={handleSubmit}
action="/manage/api/upload"
method="post"
encType="multipart/form-data"
>
<input type="hidden" name="csrfToken" value={csrfToken} />
<label className="field">
Datei
<input className="input" name="file" type="file" required disabled={uploading} />
</label>
<label className="field">
Aufbewahrung in Stunden
<input className="input" name="retentionHours" placeholder="168" disabled={uploading} />
</label>
{uploading ? (
<div className="upload-progress" role="status" aria-live="polite">
<div className="upload-progress-row">
<span className="muted">Upload läuft </span>
<strong>{progress}%</strong>
</div>
<div className="upload-progress-track" aria-hidden="true">
<div className="upload-progress-fill" style={{ width: `${progress}%` }} />
</div>
</div>
) : null}
{localError ? <div className="status error">{localError}</div> : null}
<button className="btn" type="submit" disabled={uploading}>
{uploading ? 'Wird hochgeladen …' : 'Hochladen'}
</button>
</form>
);
}