1- import {
2- Breadcrumb ,
3- BreadcrumbItem ,
4- BreadcrumbLink ,
5- BreadcrumbList ,
6- BreadcrumbPage ,
7- BreadcrumbSeparator ,
8- } from '@/components/ui/breadcrumb'
1+ import { Calendar , Hash , Library , Star } from 'lucide-react'
2+ import { Card , CardContent , CardHeader , CardTitle } from '@/components/ui/card'
3+ import { Badge } from '@/components/ui/badge'
4+ import { formatDate , getBookStatus } from '@/lib/utils'
95import { getBook } from '@/lib/api/book'
10- import { Button } from '@/components/ui/button'
11- import { Pen } from 'lucide-react'
126import Link from 'next/link'
13- import { DetailBook } from '@/components/books/DetailBook'
14- import { colorsToCssVars } from '@/lib/utils/color-utils'
7+ import { getListReviews } from '@/lib/api/review'
8+ import { getListBorrows } from '@/lib/api/borrow'
9+ import { Avatar , AvatarFallback } from '@/components/ui/avatar'
10+ import { Button } from '@/components/ui/button'
11+ import { Verify } from '@/lib/firebase/firebase'
1512
1613export default async function BookDetailsPage ( {
1714 params,
@@ -20,42 +17,155 @@ export default async function BookDetailsPage({
2017} ) {
2118 const { id } = await params
2219
23- const [ bookRes ] = await Promise . all ( [ getBook ( { id, include_stats : 'true' } ) ] )
20+ const headers = await Verify ( {
21+ from : `/admin/books/${ id } ` ,
22+ } )
23+
24+ const [ bookRes , reviewsRes , borrowsRes ] = await Promise . all ( [
25+ getBook ( { id, include_stats : 'true' } ) ,
26+ getListReviews ( { book_id : id , limit : 3 } , { headers } ) ,
27+ getListBorrows ( { book_id : id , limit : 3 } , { headers } ) ,
28+ ] )
2429
2530 if ( 'error' in bookRes ) {
2631 console . log ( { libRes : bookRes } )
2732 return < div > { JSON . stringify ( bookRes . message ) } </ div >
2833 }
34+ if ( 'error' in reviewsRes ) {
35+ console . log ( { reviewsRes } )
36+ return < div > { JSON . stringify ( reviewsRes . message ) } </ div >
37+ }
38+ if ( 'error' in borrowsRes ) {
39+ console . log ( { borrowsRes } )
40+ return < div > { JSON . stringify ( borrowsRes . message ) } </ div >
41+ }
2942
30- const cssVars = colorsToCssVars ( bookRes . data . colors )
43+ const status = getBookStatus ( bookRes . data . stats )
44+ const isAvailable = status === 'available'
3145
3246 return (
3347 < div className = "space-y-4" >
34- < h1 className = "text-2xl font-semibold" > { bookRes . data . title } </ h1 >
35- < Breadcrumb >
36- < BreadcrumbList >
37- < BreadcrumbItem >
38- < BreadcrumbLink href = "/admin" > Home</ BreadcrumbLink >
39- </ BreadcrumbItem >
40- < BreadcrumbSeparator />
41- < BreadcrumbItem >
42- < BreadcrumbLink href = "/admin/books" > Books</ BreadcrumbLink >
43- </ BreadcrumbItem >
44- < BreadcrumbSeparator />
45- < BreadcrumbItem >
46- < BreadcrumbPage > { bookRes . data . title } </ BreadcrumbPage >
47- </ BreadcrumbItem >
48- </ BreadcrumbList >
49- </ Breadcrumb >
50-
51- < DetailBook book = { bookRes . data } style = { cssVars } >
52- < Button className = "w-full" asChild >
53- < Link href = { `/admin/books/${ bookRes . data . id } /edit` } >
54- < Pen />
55- Edit
56- </ Link >
57- </ Button >
58- </ DetailBook >
48+ < div >
49+ < h1 className = "text-3xl font-bold mb-2" > { bookRes . data . title } </ h1 >
50+ < p className = "text-xl text-muted-foreground mb-4" >
51+ { bookRes . data . author }
52+ </ p >
53+ < div className = "flex flex-wrap gap-2 mb-4" >
54+ < Badge
55+ className = "uppercase"
56+ variant = { isAvailable ? 'default' : 'secondary' }
57+ >
58+ { status }
59+ </ Badge >
60+ { /* <Badge variant="outline">bookRes.data.genre</Badge> */ }
61+ </ div >
62+ </ div >
63+
64+ < Card >
65+ < CardHeader >
66+ < CardTitle > Book Information</ CardTitle >
67+ </ CardHeader >
68+ < CardContent className = "grid gap-2 grid-cols-[max-content_1fr] md:grid-cols-[max-content_1fr_max-content_1fr] items-center" >
69+ < Hash className = "size-4" />
70+ < p >
71+ < span className = "font-medium" > Code: </ span >
72+ { bookRes . data . code }
73+ </ p >
74+ < Calendar className = "size-4" />
75+ < p >
76+ < span className = "font-medium" > Year: </ span >
77+ { bookRes . data . year }
78+ </ p >
79+ < Library className = "size-4" />
80+ < p >
81+ < span className = "font-medium" > Library: </ span >
82+ < Link
83+ href = { `/libraries/${ bookRes . data . library . id } ` }
84+ className = "link"
85+ >
86+ { bookRes . data . library . name }
87+ </ Link >
88+ </ p >
89+ </ CardContent >
90+ </ Card >
91+
92+ < Card >
93+ < CardHeader >
94+ < CardTitle > Description</ CardTitle >
95+ </ CardHeader >
96+ < CardContent >
97+ < p className = "text-sm leading-relaxed" > { bookRes . data . description } </ p >
98+ </ CardContent >
99+ </ Card >
100+
101+ < Card >
102+ < CardHeader >
103+ < div className = "flex items-center justify-between" >
104+ < CardTitle > Reviews</ CardTitle >
105+ < div className = "flex items-center gap-2" >
106+ < div className = "flex items-center gap-1" >
107+ < Star className = "h-5 w-5 fill-(--color-vibrant,var(--color-yellow-400)) text-(--color-vibrant,var(--color-yellow-400))" />
108+ < span className = "font-semibold" >
109+ { bookRes . data . stats ?. rating ?. toFixed ( 1 ) }
110+ </ span >
111+ </ div >
112+ < span className = "text-sm text-muted-foreground" >
113+ ({ reviewsRes . meta . total } { ' ' }
114+ { reviewsRes . meta . total === 1 ? 'review' : 'reviews' } )
115+ </ span >
116+ </ div >
117+ </ div >
118+ </ CardHeader >
119+ < CardContent >
120+ < div className = "space-y-6" >
121+ { reviewsRes . data . map ( ( review ) => (
122+ < div
123+ key = { review . id }
124+ className = "border-b last:border-b-0 pb-6 last:pb-0"
125+ >
126+ < div className = "flex items-start justify-between mb-2" >
127+ < div className = "flex items-center gap-2" >
128+ < Avatar className = "h-8 w-8" >
129+ < AvatarFallback className = "text-xs bg-primary/10" >
130+ { review . user ?. name ?. slice ( 0 , 2 ) }
131+ </ AvatarFallback >
132+ </ Avatar >
133+ < div >
134+ { review . user ?. name }
135+ < div className = "flex items-center gap-1 text-xs text-muted-foreground" >
136+ < Calendar className = "h-3 w-3" />
137+ { formatDate ( review . created_at ) }
138+ </ div >
139+ </ div >
140+ </ div >
141+ < div className = "flex items-center gap-1" >
142+ { [ 1 , 2 , 3 , 4 , 5 ] . map ( ( star ) => (
143+ < Star
144+ key = { star }
145+ className = { `h-4 w-4 ${
146+ star <= review . rating
147+ ? 'fill-(--color-vibrant,var(--color-yellow-400)) text-(--color-vibrant,var(--color-yellow-400))'
148+ : 'text-gray-300'
149+ } `}
150+ />
151+ ) ) }
152+ </ div >
153+ </ div >
154+ < p className = "text-sm leading-relaxed text-foreground" >
155+ { review . comment }
156+ </ p >
157+ </ div >
158+ ) ) }
159+ </ div >
160+ < div className = "mt-6 pt-6 border-t" >
161+ { /* <Link href="/reviews"> */ }
162+ < Button variant = "outline" className = "w-full bg-transparent" >
163+ View All Reviews
164+ </ Button >
165+ { /* </Link> */ }
166+ </ div >
167+ </ CardContent >
168+ </ Card >
59169 </ div >
60170 )
61171}
0 commit comments