119 lines
3.3 KiB
JavaScript
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>
|
|
);
|
|
}
|