Archived
Reviewed and merged by Reid (Hermes Reviewer) Co-authored-by: crisleo-hermes <hermes@codescripters.org> Co-committed-by: crisleo-hermes <hermes@codescripters.org>
61 lines
4.5 KiB
TypeScript
61 lines
4.5 KiB
TypeScript
import React, { useCallback, useEffect, useState } from "react";
|
|
import { useNavigate } from "react-router-dom";
|
|
import { booksApi } from "../api/books";
|
|
import type { EBookListItem } from "../types/book";
|
|
import { useAuth } from "../context/AuthContext";
|
|
|
|
export function LibraryPage() {
|
|
const [books, setBooks] = useState<EBookListItem[]>([]);
|
|
const [loading, setLoading] = useState(true);
|
|
const [error, setError] = useState<string | null>(null);
|
|
const { logout } = useAuth();
|
|
const navigate = useNavigate();
|
|
|
|
const loadBooks = useCallback(async () => {
|
|
setLoading(true);
|
|
setError(null);
|
|
try {
|
|
const data = await booksApi.getEBooks();
|
|
setBooks(data);
|
|
} catch (err) {
|
|
setError(err instanceof Error ? err.message : "Failed to load library");
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}, []);
|
|
|
|
useEffect(() => { void loadBooks(); }, [loadBooks]);
|
|
|
|
return (
|
|
<div style={{ maxWidth: 800, margin: "0 auto", padding: 16, minHeight: "100vh", background: "#f8f9fa" }}>
|
|
<header style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 24, padding: "16px 0", borderBottom: "1px solid #eee", flexWrap: "wrap", gap: 8 }}>
|
|
<h1 style={{ fontSize: 24, fontWeight: 700, color: "#1a1a2e" }}>My Library</h1>
|
|
<div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
|
|
<button onClick={() => navigate("/add")} style={{ padding: "10px 20px", borderRadius: 8, border: "none", background: "#1a1a2e", color: "#fff", fontSize: 14, fontWeight: 600, cursor: "pointer" }}>+ Add Book</button>
|
|
<button onClick={() => navigate("/settings")} style={{ padding: "10px 20px", borderRadius: 8, border: "1px solid #ddd", background: "#fff", color: "#333", fontSize: 14, cursor: "pointer" }}>Settings</button>
|
|
<button onClick={() => navigate("/bookmarks-notes")} style={{ padding: "10px 20px", borderRadius: 8, border: "1px solid #ddd", background: "#fff", color: "#333", fontSize: 14, cursor: "pointer" }}>Bookmarks</button>
|
|
<button onClick={logout} style={{ padding: "10px 20px", borderRadius: 8, border: "1px solid #e74c3c", background: "#fff", color: "#e74c3c", fontSize: 14, cursor: "pointer" }}>Logout</button>
|
|
</div>
|
|
</header>
|
|
|
|
{error && <div style={{ background: "#fde8e8", padding: 16, borderRadius: 8, marginBottom: 16, textAlign: "center" }}><p>{error}</p><button onClick={loadBooks} style={{ marginTop: 8, padding: "8px 16px", border: "none", borderRadius: 6, background: "#e74c3c", color: "#fff", cursor: "pointer" }}>Retry</button></div>}
|
|
{loading && books.length === 0 && <div style={{ textAlign: "center", padding: "60px 20px" }}><p>Loading your library...</p></div>}
|
|
{!loading && !error && books.length === 0 && <div style={{ textAlign: "center", padding: "60px 20px" }}><p style={{ fontSize: 20, color: "#666", marginBottom: 8 }}>Your library is empty</p><p style={{ color: "#999", marginBottom: 20 }}>Add a book to get started</p><button onClick={() => navigate("/add")} style={{ padding: "10px 20px", borderRadius: 8, border: "none", background: "#1a1a2e", color: "#fff", fontSize: 14, fontWeight: 600, cursor: "pointer" }}>Add Your First Book</button></div>}
|
|
|
|
<div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(200px, 1fr))", gap: 16 }}>
|
|
{books.map((book) => (
|
|
<div key={book.id} onClick={() => navigate(`/reader/${book.id}`)} style={{ background: "#fff", borderRadius: 12, overflow: "hidden", boxShadow: "0 2px 8px rgba(0,0,0,0.06)", cursor: "pointer" }}>
|
|
<div style={{ height: 180, background: "#f0f0f0", display: "flex", alignItems: "center", justifyContent: "center" }}>
|
|
{book.cover_image ? <img src={book.cover_image} alt={book.title} style={{ width: "100%", height: "100%", objectFit: "cover" }} /> : <span style={{ fontSize: 48 }}>📖</span>}
|
|
</div>
|
|
<div style={{ padding: 12 }}>
|
|
<h3 style={{ fontSize: 14, fontWeight: 600, color: "#1a1a2e", marginBottom: 4, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{book.title}</h3>
|
|
<p style={{ fontSize: 12, color: "#888", marginBottom: 8 }}>{book.author || "Unknown Author"}</p>
|
|
{book.progress !== null && <div style={{ width: "100%", height: 4, background: "#eee", borderRadius: 2, overflow: "hidden" }}><div style={{ height: "100%", background: "#1a1a2e", borderRadius: 2, width: `${Math.min(book.progress, 100)}%` }} /></div>}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
} |