90 lines
3.0 KiB
TypeScript
90 lines
3.0 KiB
TypeScript
import { useEffect, useMemo, useState } from 'react'
|
|
import type { Book } from '../../types'
|
|
import { fetchBooks, updateBook } from '../../api/books'
|
|
import MetadataForm from '../../components/MetadataForm/MetadataForm'
|
|
import s from './Metadata.module.css'
|
|
|
|
export default function Metadata() {
|
|
const [books, setBooks] = useState<Book[]>([])
|
|
const [selected, setSelected] = useState<Book | null>(null)
|
|
const [query, setQuery] = useState('')
|
|
|
|
useEffect(() => {
|
|
fetchBooks().then(list => {
|
|
setBooks(list)
|
|
if (list.length > 0) setSelected(list[0])
|
|
})
|
|
}, [])
|
|
|
|
const filtered = useMemo(() =>
|
|
books.filter(b =>
|
|
!query ||
|
|
b.title.toLowerCase().includes(query.toLowerCase()) ||
|
|
b.authors.some(a => a.name.toLowerCase().includes(query.toLowerCase()))
|
|
), [books, query])
|
|
|
|
function handleSave(patch: Partial<Book>) {
|
|
if (!selected) return
|
|
updateBook(selected.id, patch).then(updated => {
|
|
setBooks(bs => bs.map(b => b.id === updated.id ? updated : b))
|
|
setSelected(updated)
|
|
})
|
|
}
|
|
|
|
return (
|
|
<div className={s.layout}>
|
|
<aside className={s.list}>
|
|
<div className={s.listHeader}>
|
|
<div className={s.search}>
|
|
<span className={`material-symbols-outlined ${s.searchIcon}`}>search</span>
|
|
<input
|
|
className={s.searchInput}
|
|
type="search"
|
|
placeholder="Filter books…"
|
|
value={query}
|
|
onChange={e => setQuery(e.target.value)}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<ul className={s.bookList}>
|
|
{filtered.map(book => (
|
|
<li
|
|
key={book.id}
|
|
className={`${s.bookItem} ${selected?.id === book.id ? s.bookItemActive : ''}`}
|
|
onClick={() => setSelected(book)}
|
|
>
|
|
<div className={s.thumb} style={{ background: book.color }}>
|
|
{book.title[0]}
|
|
</div>
|
|
<div className={s.bookMeta}>
|
|
<span className={s.bookTitle}>{book.title}</span>
|
|
<span className={s.bookAuthor}>{book.authors.map(a => a.name).join(', ')}</span>
|
|
</div>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</aside>
|
|
|
|
<main className={s.editor}>
|
|
{selected ? (
|
|
<>
|
|
<div className={s.editorHeader}>
|
|
<div className={s.editorCover} style={{ background: selected.color }}>
|
|
{selected.title[0]}
|
|
</div>
|
|
<div>
|
|
<p className={s.editorTitle}>{selected.title}</p>
|
|
<p className={s.editorAuthor}>{selected.authors.map(a => a.name).join(', ')}</p>
|
|
</div>
|
|
</div>
|
|
<MetadataForm key={selected.id} book={selected} onSave={handleSave} />
|
|
</>
|
|
) : (
|
|
<div className={s.empty}>Select a book to edit metadata</div>
|
|
)}
|
|
</main>
|
|
</div>
|
|
)
|
|
}
|