Skip to content

Commit 0b3bc6a

Browse files
committed
feat: book stats
1 parent 2c20938 commit 0b3bc6a

File tree

9 files changed

+49
-40
lines changed

9 files changed

+49
-40
lines changed

app/(protected)/admin/books/[id]/page.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export default async function BookDetailsPage({
2121
}) {
2222
const { id } = await params
2323

24-
const [bookRes] = await Promise.all([getBook({ id })])
24+
const [bookRes] = await Promise.all([getBook({ id, include_stats: 'true' })])
2525

2626
if ('error' in bookRes) {
2727
console.log({ libRes: bookRes })
@@ -74,8 +74,12 @@ export default async function BookDetailsPage({
7474
{bookRes.data.author}
7575
</p>
7676
<div className="flex flex-wrap gap-2 mb-4">
77-
<Badge variant={true ? 'default' : 'destructive'}>
78-
{true ? 'Available' : 'Borrowed'}
77+
<Badge
78+
variant={
79+
bookRes.data.stats?.is_available ? 'default' : 'secondary'
80+
}
81+
>
82+
{bookRes.data.stats?.is_available ? 'Available' : 'Borrowed'}
7983
</Badge>
8084
<Badge variant="outline">bookRes.data.genre</Badge>
8185
</div>

app/(protected)/admin/books/page.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ export default async function Books({
5555
skip: skip,
5656
title: sp?.title,
5757
library_id: libID,
58+
include_stats: 'true',
5859
})
5960

6061
if ('error' in res) {

app/(protected)/books/[id]/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export default async function BookDetailsPage({
1818
}) {
1919
const { id } = await params
2020

21-
const [bookRes] = await Promise.all([getBook({ id })])
21+
const [bookRes] = await Promise.all([getBook({ id, include_stats: 'true' })])
2222

2323
if ('error' in bookRes) {
2424
console.log({ libRes: bookRes })

app/(protected)/books/page.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ export default async function UserBooks({
4848
limit: limit,
4949
skip: skip,
5050
title: sp?.title,
51+
include_stats: 'true',
5152
...(library_id ? { library_id } : {}),
5253
} as const
5354

components/books/DetailBook.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ export const DetailBook: React.FC<
2222
<h1 className="text-3xl font-bold mb-2">{book.title}</h1>
2323
<p className="text-xl text-muted-foreground mb-4">{book.author}</p>
2424
<div className="flex flex-wrap gap-2 mb-4">
25-
<Badge variant={true ? 'default' : 'destructive'}>
26-
{true ? 'Available' : 'Borrowed'}
25+
<Badge variant={book.stats?.is_available ? 'default' : 'secondary'}>
26+
{book.stats?.is_available ? 'Available' : 'Borrowed'}
2727
</Badge>
2828
<Badge variant="outline">book.genre</Badge>
2929
</div>

components/books/ListBook.tsx

Lines changed: 24 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Book } from '@/lib/types/book'
22
import Image from 'next/image'
3-
import { Calendar, Hash } from 'lucide-react'
3+
import { BadgeCheck, BadgeMinus, Calendar, Hash } from 'lucide-react'
44
import {
55
Card,
66
CardContent,
@@ -12,17 +12,18 @@ import clsx from 'clsx'
1212

1313
export const ListBook: React.FC<{ book: Book }> = ({ book }) => {
1414
return (
15-
<Card className="group cursor-pointer transition-all duration-200 hover:shadow-lg hover:-translate-y-1">
15+
<Card
16+
className={clsx(
17+
'group cursor-pointer transition-all duration-200 hover:shadow-lg hover:-translate-y-1',
18+
book.stats?.is_available && ''
19+
)}
20+
>
1621
<CardHeader className="pb-3">
1722
<div className="grid place-items-center">
1823
{/* 3D Book Effect */}
1924
<div className="flex my-12">
2025
<div className="bg-accent [transform:perspective(400px)_rotateY(314deg)] -mr-1 w-4">
21-
<span className="inline-block text-nowrap text-[0.5rem] font-bold text-accent-foreground/50 [transform:rotate(90deg)_translateY(-16px)] origin-top-left">
22-
{/* {book.title.length > 15
23-
? book.title.substring(0, 25) + '...'
24-
: book.title} */}
25-
</span>
26+
<span className="inline-block text-nowrap text-[0.5rem] font-bold text-accent-foreground/50 [transform:rotate(90deg)_translateY(-16px)] origin-top-left"></span>
2627
</div>
2728
<Image
2829
src={book?.cover ?? '/book-placeholder.svg'}
@@ -31,28 +32,12 @@ export const ListBook: React.FC<{ book: Book }> = ({ book }) => {
3132
height={192}
3233
className={clsx(
3334
'shadow-xl rounded-r-md w-32 h-48 object-cover',
34-
'[transform:perspective(800px)_rotateY(14deg)]'
35+
'[transform:perspective(800px)_rotateY(14deg)]',
36+
!book.stats?.is_available && 'grayscale'
3537
)}
3638
priority
3739
/>
3840
</div>
39-
{/* <div className="bg-accent [transform:perspective(400px)_rotateY(314deg)] -mr-1 w-3 h-48">
40-
<span className="my-2 inline-block text-nowrap text-xs font-bold text-accent-foreground/50 [transform:rotate(90deg)_translateY(-20px)] origin-top-left">
41-
{book.title.length > 15
42-
? book.title.substring(0, 15) + '...'
43-
: book.title}
44-
</span>
45-
</div>
46-
<Image
47-
src={
48-
book.cover ??
49-
'/placeholder.svg?height=192&width=128&text=No+Cover'
50-
}
51-
alt={`${book.title} cover`}
52-
width={128}
53-
height={192}
54-
className="shadow-xl rounded-r-md w-32 h-48 object-cover [transform:perspective(800px)_rotateY(14deg)] group-hover:[transform:perspective(800px)_rotateY(8deg)] transition-transform duration-300"
55-
/> */}
5641
</div>
5742
<CardTitle className="text-lg line-clamp-1">{book.title}</CardTitle>
5843
<CardDescription className="line-clamp-1">
@@ -74,12 +59,20 @@ export const ListBook: React.FC<{ book: Book }> = ({ book }) => {
7459
{book.genre}
7560
</Badge>
7661
)} */}
77-
{/* <div className="flex justify-between items-center mt-3">
78-
<Badge variant={book.available ? "default" : "destructive"}>
79-
{book.available ? "Available" : "Borrowed"}
80-
</Badge>
81-
<span className="text-xs text-muted-foreground">{book.library}</span>
82-
</div> */}
62+
63+
<div className="flex items-center gap-2 text-sm text-muted-foreground">
64+
{book.stats?.is_available ? (
65+
<>
66+
<BadgeCheck className="h-3 w-3 text-primary" />
67+
<span>Available</span>
68+
</>
69+
) : (
70+
<>
71+
<BadgeMinus className="h-3 w-3" />
72+
<span>Borrowed</span>
73+
</>
74+
)}
75+
</div>
8376
</div>
8477
</CardContent>
8578
</Card>

lib/api/book.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@ import { BASE_URL } from './common'
44

55
const BOOKS_URL = `${BASE_URL}/books`
66

7-
type GetListBooksQuery = QueryParams<Book>
7+
type GetListBooksQuery = QueryParams<
8+
Pick<Book, 'id' | 'title' | 'library_id'>
9+
> & {
10+
include_stats?: 'true'
11+
}
812
type GetListBooksResponse = Promise<ResList<Book>>
913

1014
export const getListBooks = async (
@@ -21,7 +25,7 @@ export const getListBooks = async (
2125
return response.json()
2226
}
2327

24-
type GetBookQuery = Pick<Book, 'id'>
28+
type GetBookQuery = Pick<Book, 'id'> & { include_stats?: 'true' }
2529
type GetBookResponse = Promise<ResSingle<BookDetail>>
2630
export const getBook = async (query: GetBookQuery): GetBookResponse => {
2731
const url = new URL(`${BOOKS_URL}/${query.id}`)

lib/types/book.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import { WithCommon } from './common'
22
import { Library } from './library'
33

4+
type BookStats = {
5+
borrow_count: number
6+
is_available: boolean
7+
}
8+
49
export type Book = WithCommon<{
510
title: string
611
author: string
@@ -9,6 +14,7 @@ export type Book = WithCommon<{
914
cover?: string
1015
library_id: string
1116
library?: Pick<Library, 'id' | 'name'>
17+
stats?: BookStats
1218
}>
1319

1420
export type BookDetail = Omit<Book, 'library'> & {

lib/types/common.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ type WithDates<T> = T & { created_at: string; updated_at: string }
2828
export type WithCommon<T> = WithID<WithDates<T>>
2929

3030
export type QueryParams<T> = {
31-
sort_by?: keyof T
31+
sort_by?: 'created_at' | 'updated_at' | keyof T
3232
sort_in?: 'asc' | 'desc'
3333
limit?: number
3434
skip?: number

0 commit comments

Comments
 (0)