62 lines
2.7 KiB
TypeScript
62 lines
2.7 KiB
TypeScript
import { useRef, useState } from "react";
|
|
import { FileText, Loader2, Trash2, Upload } from "lucide-react";
|
|
import { adminApi, AdminApiError } from "../../api/adminApi";
|
|
|
|
interface PublicationFilePickerProps {
|
|
format: "pdf" | "epub";
|
|
value: string;
|
|
onChange: (url: string) => void;
|
|
}
|
|
|
|
export default function PublicationFilePicker({ format, value, onChange }: PublicationFilePickerProps) {
|
|
const inputRef = useRef<HTMLInputElement>(null);
|
|
const [loading, setLoading] = useState(false);
|
|
const [error, setError] = useState("");
|
|
|
|
const upload = (file?: File) => {
|
|
if (!file) return;
|
|
if (!file.name.toLowerCase().endsWith(`.${format}`)) {
|
|
setError(`Bitte eine ${format.toUpperCase()}-Datei auswählen.`);
|
|
return;
|
|
}
|
|
if (file.size > 10 * 1024 * 1024) {
|
|
setError("Die Datei darf maximal 10 MB groß sein.");
|
|
return;
|
|
}
|
|
setLoading(true);
|
|
setError("");
|
|
const reader = new FileReader();
|
|
reader.onload = async () => {
|
|
try {
|
|
const result = await adminApi.uploadPublication(file.name, String(reader.result), format);
|
|
onChange(result.url);
|
|
} catch (uploadError) {
|
|
setError(uploadError instanceof AdminApiError ? uploadError.message : "Upload fehlgeschlagen.");
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
reader.onerror = () => {
|
|
setError("Die Datei konnte nicht gelesen werden.");
|
|
setLoading(false);
|
|
};
|
|
reader.readAsDataURL(file);
|
|
};
|
|
|
|
return (
|
|
<div className="space-y-2 rounded-xl border border-slate-800 bg-slate-950/50 p-3">
|
|
<input ref={inputRef} type="file" accept={format === "pdf" ? "application/pdf,.pdf" : "application/epub+zip,.epub"} className="hidden" onChange={(event) => upload(event.target.files?.[0])} />
|
|
{value ? (
|
|
<div className="flex items-center justify-between gap-2">
|
|
<span className="min-w-0 truncate text-xs text-slate-300"><FileText className="mr-1 inline h-4 w-4" />{value.split("/").pop()}</span>
|
|
<button type="button" onClick={() => onChange("")} className="p-1.5 text-slate-400 hover:text-rose-400" aria-label={`${format.toUpperCase()} entfernen`}><Trash2 className="h-4 w-4" /></button>
|
|
</div>
|
|
) : (
|
|
<button type="button" disabled={loading} onClick={() => inputRef.current?.click()} className="flex w-full items-center justify-center gap-2 rounded-lg bg-indigo-600 px-3 py-2 text-xs font-semibold text-white hover:bg-indigo-500 disabled:opacity-60">
|
|
{loading ? <Loader2 className="h-4 w-4 animate-spin" /> : <Upload className="h-4 w-4" />}<span>{format.toUpperCase()} hochladen</span>
|
|
</button>
|
|
)}
|
|
{error && <p role="alert" className="text-xs text-rose-400">{error}</p>}
|
|
</div>
|
|
);
|
|
}
|