Files
InkFlow/frontend/src/Library.jsx

81 lines
3.0 KiB
JavaScript

import React, { useEffect, useRef, useState } from "react";
import { api } from "./api.js";
import { Spinner } from "./ui.jsx";
export default function Library({ onOpen }) {
const [books, setBooks] = useState(null);
const [uploading, setUploading] = useState(false);
const [error, setError] = useState(null);
const fileRef = useRef();
const refresh = () => api.listBooks().then(setBooks).catch((e) => setError(String(e)));
useEffect(() => { refresh(); }, []);
const upload = async (file) => {
if (!file) return;
setUploading(true);
setError(null);
try {
const { slug } = await api.uploadBook(file);
await refresh();
onOpen(slug);
} catch (e) {
setError("Échec de l'import : " + e);
} finally {
setUploading(false);
}
};
return (
<div className="space-y-8">
<section
onDragOver={(e) => e.preventDefault()}
onDrop={(e) => { e.preventDefault(); upload(e.dataTransfer.files[0]); }}
className="card flex flex-col items-center justify-center gap-3 border-dashed py-12 text-center"
>
<div className="text-4xl">📖</div>
<p className="font-serif text-lg">Déposez un fichier EPUB</p>
<p className="text-sm text-ink-muted">ou</p>
<button className="btn-primary" disabled={uploading}
onClick={() => fileRef.current?.click()}>
{uploading ? <Spinner /> : null}
{uploading ? "Import en cours…" : "Choisir un fichier"}
</button>
<input ref={fileRef} type="file" accept=".epub" className="hidden"
onChange={(e) => upload(e.target.files[0])} />
</section>
{error && <p className="text-sm text-red-400">{error}</p>}
<section>
<h2 className="mb-3 font-serif text-lg text-ink-muted">Bibliothèque</h2>
{books === null ? (
<p className="text-ink-muted"><Spinner /> chargement</p>
) : books.length === 0 ? (
<p className="text-ink-muted">Aucun livre pour l'instant.</p>
) : (
<div className="grid grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-4">
{books.map((b) => (
<button key={b.slug} onClick={() => onOpen(b.slug)}
className="card group overflow-hidden text-left transition-transform hover:-translate-y-1">
<div className="aspect-[2/3] w-full bg-ink-edge">
{b.cover && (
<img src={b.cover} alt="" className="h-full w-full object-cover" />
)}
</div>
<div className="p-3">
<p className="line-clamp-2 font-serif text-sm">{b.title}</p>
<p className="mt-1 text-xs text-ink-muted">{b.author}</p>
<p className="mt-2 text-xs text-ink-accent">
{b.rendered}/{b.chapters} chapitres rendus
</p>
</div>
</button>
))}
</div>
)}
</section>
</div>
);
}