171 lines
5.1 KiB
TypeScript

'use client'
import { Book, Genre, Repository } from '@/payload-types'
import { Combobox, ComboboxLabel, ComboboxOption, ComboboxDescription } from '@/components/combobox'
import { Field, Label } from '@/components/fieldset'
import type { PaginatedDocs } from 'payload'
import { Button } from '@/components/ui/button'
import { Badge } from '@/components/ui/badge'
import { Loader2 } from 'lucide-react'
import { RichText } from '@/components/RichText'
import { useMemo, useState } from 'react'
import requestHold from '../../serverCalls/requestHold'
type DropDownProps = {
currentRepository: Repository
repositories: Repository[]
isDisabled: boolean
isRequesting: boolean
onClickRequest: () => void
onChange: (repo: Repository | null) => void
}
function RepoDropdown({
currentRepository,
repositories,
isRequesting,
isDisabled,
onClickRequest,
onChange,
}: DropDownProps) {
return (
<Field className="">
<Label htmlFor="repositories">From Repository</Label>
<div className="flex gap-2">
<Combobox
name="repositories"
options={repositories}
displayValue={(repo) => repo?.name}
defaultValue={currentRepository}
className=""
onChange={(repo) => onChange(repo)}
>
{(repo) => (
<ComboboxOption value={repo}>
<ComboboxLabel>{repo.abbreviation}</ComboboxLabel>
<ComboboxDescription>{repo.name}</ComboboxDescription>
</ComboboxOption>
)}
</Combobox>
<Button
disabled={isDisabled}
onClick={onClickRequest}
className="hover:scale-105 bg-emerald-500 text-foreground hover:text-black cursor-pointer"
>
{isRequesting && <Loader2 className="animate-spin" />}
<span>Request Copy</span>
</Button>
</div>
</Field>
)
}
type Props = {
book: Book
repositories: PaginatedDocs<Repository>
}
export default function BookByIdPageClient(props: Props) {
const { book, repositories } = props
const repos = repositories.docs
const [isRequestingCopy, setIsRequestingCopy] = useState(false)
const [selectedRepository, setSelectedRepository] = useState<Repository | null>(
repos.length ? repos[0] : null,
)
const isRequestDisabled = useMemo(() => {
return isRequestingCopy
}, [isRequestingCopy])
const onClickRequest = async () => {
if (isRequestingCopy || !selectedRepository || !book) return
setIsRequestingCopy(true)
const response = await requestHold({
repositoryId: selectedRepository.id,
bookId: book.id,
})
console.log(response)
setIsRequestingCopy(false)
}
return (
<div className="mx-auto px-4 py-16">
{/* Book */}
<div className="md:grid md:grid-cols-7 md:gap-8">
{/* Book Cover */}
<div className="md:col-span-3">
<img
alt="book cover art"
src={
book.isbn
? `https://covers.openlibrary.org/b/isbn/${book.isbn}-L.jpg`
: '/images/book-48.svg'
}
className="w-full rounded-lg bg-gray-100 object-cover"
/>
</div>
{/* Book details */}
<div className="mt-14 sm:mt-16 md:col-span-4 md:mt-0">
<div className="">
<div className="mt-4">
<h1 className="text-2xl font-bold tracking-tight text-foreground sm:text-3xl">
{book.title}
</h1>
<h2 id="information-heading" className="sr-only">
Book information
</h2>
<p className="mt-2 text-sm text-muted-foreground">
{book.publication && (
<span>
Published:&nbsp;
<time dateTime={book.publication}>{book.publication}</time>
</span>
)}
</p>
</div>
</div>
<p className="my-6 text-accent-foreground">{book.summary}</p>
<RepoDropdown
currentRepository={repos[0]}
repositories={repos}
isRequesting={isRequestingCopy}
isDisabled={isRequestDisabled}
onClickRequest={onClickRequest}
onChange={(repo) => setSelectedRepository(repo)}
/>
<div className="mt-10 border-t border-gray-200 pt-10 ">
<h3 className="text-sm font-medium text-foreground">Genres</h3>
<div className="mt-4">
<ul role="list" className="text-foreground flex gap-2 flex-wrap">
{(book.genre as Genre[])?.map((g) => (
<Badge key={g.id} className="inline-block cursor-pointer">
{g.name}
</Badge>
))}
</ul>
</div>
</div>
{book.description && (
<div className="mt-10 border-t border-gray-200 pt-10">
<h3 className="text-sm font-medium text-muted-foreground">Description</h3>
<RichText data={book.description} />
</div>
)}
</div>
</div>
</div>
)
}