feat: rip queue page, library stats/queue links and on-loan chip
This commit is contained in:
61
web/src/pages/QueuePage.tsx
Normal file
61
web/src/pages/QueuePage.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { api } from '../api.js'
|
||||
import type { Item } from '../types.js'
|
||||
import Cover from '../components/Cover.js'
|
||||
|
||||
export default function QueuePage() {
|
||||
const [items, setItems] = useState<Item[] | null>(null)
|
||||
const [error, setError] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
void api
|
||||
.listCollection({ ripped: 'not_ripped' })
|
||||
.then((res) => setItems([...res.items].reverse()))
|
||||
.catch(() => setError(true))
|
||||
}, [])
|
||||
|
||||
function markRipped(id: number) {
|
||||
void api
|
||||
.setRip(id, true)
|
||||
.then(() => setItems((list) => (list ?? []).filter((i) => i.id !== id)))
|
||||
.catch(() => setError(true))
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
<p className="text-sm text-neutral-400">Rip queue — oldest additions first.</p>
|
||||
{error && <p className="text-sm text-red-400">Could not load the queue.</p>}
|
||||
{items && items.length === 0 && (
|
||||
<div className="py-12 text-center">
|
||||
<p className="text-neutral-400">Nothing waiting to be ripped.</p>
|
||||
<Link to="/scan" className="mt-2 inline-block text-sm text-emerald-400">
|
||||
Scan something →
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
{items &&
|
||||
items.map((item) => (
|
||||
<div key={item.id} className="flex items-center gap-3 rounded-xl border border-neutral-800 bg-neutral-900 p-3">
|
||||
<Cover src={item.artworkUrl} alt="" className="size-12 shrink-0" />
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="truncate text-sm font-medium">{item.title}</p>
|
||||
<p className="truncate text-xs text-neutral-400">
|
||||
{item.artist} · added {new Date(item.dateAdded + 'Z').toLocaleDateString()}
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => markRipped(item.id)}
|
||||
className="shrink-0 rounded-lg bg-emerald-500 px-3 py-1.5 text-sm font-medium text-neutral-950"
|
||||
>
|
||||
Mark ripped
|
||||
</button>
|
||||
<Link to={`/item/${item.id}`} className="shrink-0 text-xs text-neutral-400">
|
||||
details
|
||||
</Link>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user