|
1 | | -import { BtnReturnBook } from '@/components/borrows/BtnReturnBorrow' |
2 | | -import { Badge } from '@/components/ui/badge' |
3 | | -import { Borrow } from '@/lib/types/borrow' |
4 | | -import { formatDate } from '@/lib/utils' |
5 | | -import { |
6 | | - Calendar, |
7 | | - CalendarClock, |
8 | | - CalendarX, |
9 | | - LibraryIcon, |
10 | | - User, |
11 | | -} from 'lucide-react' |
| 1 | +import { BorrowDetail } from '@/lib/types/borrow' |
| 2 | +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' |
| 3 | +import { formatDate, isBorrowDue } from '@/lib/utils' |
| 4 | +import { differenceInDays, formatDistanceToNowStrict } from 'date-fns' |
12 | 5 |
|
13 | | -import { |
14 | | - Card, |
15 | | - CardContent, |
16 | | - CardDescription, |
17 | | - CardFooter, |
18 | | - CardHeader, |
19 | | - CardTitle, |
20 | | -} from '@/components/ui/card' |
21 | | -import clsx from 'clsx' |
22 | | - |
23 | | -const checkIsDue = (borrow: Borrow) => { |
24 | | - const now = new Date() |
25 | | - const due = new Date(borrow.due_at) |
26 | | - return now > due && !borrow.returning |
27 | | -} |
28 | | - |
29 | | -const getBorrowStatus = (borrow: Borrow) => { |
30 | | - if (borrow.returning?.returned_at) return 'returned' |
31 | | - |
32 | | - return checkIsDue(borrow) ? 'overdue' : 'active' |
33 | | -} |
| 6 | +export const CardBorrow: React.FC<{ borrow: BorrowDetail }> = ({ borrow }) => { |
| 7 | + const overduedDays = differenceInDays( |
| 8 | + borrow.returning ? new Date(borrow.returning.returned_at) : new Date(), |
| 9 | + new Date(borrow.due_at) |
| 10 | + ) |
| 11 | + const finePerDay = borrow.subscription.membership.fine_per_day ?? 0 |
34 | 12 |
|
35 | | -export const CardBorrow: React.FC<{ borrow: Borrow }> = ({ borrow }) => { |
36 | | - const isDue = checkIsDue(borrow) |
| 13 | + const fine = overduedDays * finePerDay |
37 | 14 |
|
38 | 15 | return ( |
39 | | - <Card |
40 | | - key={borrow.id} |
41 | | - className={clsx('relative', { |
42 | | - 'bg-destructive/5': isDue, |
43 | | - })} |
44 | | - > |
| 16 | + <Card> |
45 | 17 | <CardHeader> |
46 | | - <div className="flex justify-between items-start min-h-20"> |
47 | | - <div> |
48 | | - <CardTitle className="text-lg line-clamp-2"> |
49 | | - <abbr title={borrow.book.title} className="no-underline"> |
50 | | - {borrow.book.title} |
51 | | - </abbr> |
52 | | - </CardTitle> |
53 | | - <CardDescription>{borrow.book.code}</CardDescription> |
54 | | - </div> |
55 | | - <Badge |
56 | | - variant={ |
57 | | - getBorrowStatus(borrow) === 'overdue' |
58 | | - ? 'destructive' |
59 | | - : getBorrowStatus(borrow) === 'returned' |
60 | | - ? 'secondary' |
61 | | - : 'default' |
62 | | - } |
63 | | - className="capitalize" |
64 | | - > |
65 | | - {getBorrowStatus(borrow)} |
66 | | - </Badge> |
67 | | - </div> |
| 18 | + <CardTitle className="text-lg line-clamp-2">Borrow</CardTitle> |
68 | 19 | </CardHeader> |
69 | | - <CardContent className="space-y-3"> |
70 | | - <div className="flex items-center gap-2 text-sm"> |
71 | | - <User className="h-4 w-4 text-muted-foreground" /> |
72 | | - <span>{borrow.subscription.user.name}</span> |
73 | | - </div> |
74 | | - <div className="flex items-center gap-2 text-sm"> |
75 | | - <LibraryIcon className="h-4 w-4 text-muted-foreground" /> |
76 | | - <span>{borrow.subscription.membership.library.name}</span> |
77 | | - </div> |
78 | | - <div className="flex items-center gap-2 text-sm"> |
79 | | - <Calendar className="h-4 w-4 text-muted-foreground" /> |
80 | | - <span>Borrowed: {formatDate(borrow.borrowed_at)}</span> |
81 | | - </div> |
82 | | - <div className="flex items-center gap-2 text-sm"> |
83 | | - {isDue ? ( |
84 | | - <CalendarX className="h-4 w-4 text-destructive" /> |
85 | | - ) : ( |
86 | | - <CalendarClock className="h-4 w-4 text-muted-foreground" /> |
| 20 | + <CardContent> |
| 21 | + <dl className="grid gap-2"> |
| 22 | + <div className="grid grid-cols-3"> |
| 23 | + <dt className="font-medium">Borrowed At:</dt> |
| 24 | + <dd className="col-span-2"> |
| 25 | + {formatDate(borrow.borrowed_at)} |
| 26 | + {!borrow.returning && |
| 27 | + ` (${formatDistanceToNowStrict(new Date(borrow.borrowed_at), { addSuffix: true })})`} |
| 28 | + </dd> |
| 29 | + </div> |
| 30 | + <div className="grid grid-cols-3"> |
| 31 | + <dt className="font-medium">Due At:</dt> |
| 32 | + <dd className="col-span-2"> |
| 33 | + {formatDate(borrow.due_at)} |
| 34 | + {!borrow.returning && |
| 35 | + ` (${formatDistanceToNowStrict(new Date(borrow.due_at), { addSuffix: true })})`} |
| 36 | + </dd> |
| 37 | + </div> |
| 38 | + |
| 39 | + {borrow.returning && ( |
| 40 | + <> |
| 41 | + <div className="grid grid-cols-3"> |
| 42 | + <dt className="font-medium">Returned At:</dt> |
| 43 | + <dd className="col-span-2"> |
| 44 | + {formatDate(borrow.returning.returned_at)} |
| 45 | + </dd> |
| 46 | + </div> |
| 47 | + <div className="grid grid-cols-3"> |
| 48 | + <dt className="font-medium">Fine Received:</dt> |
| 49 | + <dd className="col-span-2"> |
| 50 | + {borrow.returning.fine ?? '-'} Pts |
| 51 | + </dd> |
| 52 | + </div> |
| 53 | + </> |
| 54 | + )} |
| 55 | + {isBorrowDue(borrow) && ( |
| 56 | + <div className="grid grid-cols-3"> |
| 57 | + <dt className="font-medium">Fine:</dt> |
| 58 | + <dd className="col-span-2">{fine ?? '-'} Pts</dd> |
| 59 | + </div> |
87 | 60 | )} |
88 | | - <span className={`${isDue ? 'text-destructive' : ''}`}> |
89 | | - Due: {formatDate(borrow.due_at)} |
90 | | - </span> |
91 | | - </div> |
| 61 | + </dl> |
92 | 62 | </CardContent> |
93 | | - <CardFooter> |
94 | | - <BtnReturnBook variant="outline" className="w-full" borrow={borrow}> |
95 | | - Return Book |
96 | | - </BtnReturnBook> |
97 | | - </CardFooter> |
98 | 63 | </Card> |
99 | 64 | ) |
100 | 65 | } |
0 commit comments