Skip to content

Commit 4ecc5a8

Browse files
committed
feat: borrow details ui revamp
1 parent 8edcfcb commit 4ecc5a8

File tree

6 files changed

+223
-23
lines changed

6 files changed

+223
-23
lines changed

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

Lines changed: 145 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,22 @@ import Link from 'next/link'
1010
import { Verify } from '@/lib/firebase/firebase'
1111
import { getBorrow } from '@/lib/api/borrow'
1212
import { Badge } from '@/components/ui/badge'
13-
import { getBorrowStatus } from '@/lib/utils'
14-
import { CardBorrow } from '@/components/borrows/CardBorrow'
15-
import { CardBook } from '@/components/books/CardBook'
16-
import { Carduser } from '@/components/users/CardUser'
17-
import { Cardstaff } from '@/components/staffs/CardStaff'
18-
import { Cardsubscription } from '@/components/subscriptions/CardSubscription'
19-
import { CardMembership } from '@/components/memberships/CardMembership'
13+
import { formatDate, getBorrowStatus, isBorrowDue } from '@/lib/utils'
2014
import { BtnReturnBook } from '@/components/borrows/BtnReturnBorrow'
2115
import Image from 'next/image'
16+
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
17+
import {
18+
Calendar,
19+
CalendarCheck,
20+
CalendarClock,
21+
CalendarX,
22+
Gavel,
23+
Library,
24+
User,
25+
UserCog,
26+
} from 'lucide-react'
27+
import clsx from 'clsx'
28+
import { differenceInDays } from 'date-fns'
2229

2330
export default async function BorrowDetailsPage({
2431
params,
@@ -36,6 +43,10 @@ export default async function BorrowDetailsPage({
3643
return <div>{JSON.stringify(borrowRes.message)}</div>
3744
}
3845

46+
const isDue = isBorrowDue(borrowRes.data)
47+
48+
// const progressPercent = getBorrowProgressPercent(borrowRes.data)
49+
3950
// const cookieStore = await cookies()
4051
// const sessionName = process.env.SESSION_COOKIE_NAME as string
4152
// const session = cookieStore.get(sessionName)
@@ -78,23 +89,134 @@ export default async function BorrowDetailsPage({
7889
</Badge>
7990
</div>
8091

81-
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
82-
{borrowRes.data.book?.cover && (
83-
<Image
84-
src={borrowRes.data.book.cover}
85-
alt={borrowRes.data.book.title + "'s cover"}
86-
width={256}
87-
height={256}
88-
className="rounded-lg w-56 h-auto place-self-center row-span-2"
89-
/>
90-
)}
91-
<CardBorrow borrow={borrowRes.data} />
92-
<CardBook book={borrowRes.data.book} />
93-
<Carduser user={borrowRes.data.subscription.user} />
94-
<Cardstaff staff={borrowRes.data.staff} />
95-
<Cardsubscription subscription={borrowRes.data.subscription} />
96-
<CardMembership membership={borrowRes.data.subscription.membership} />
92+
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
93+
<Card className="md:row-span-2">
94+
<CardHeader>
95+
<CardTitle>Book Information</CardTitle>
96+
</CardHeader>
97+
<CardContent className="grid md:grid-cols-2 gap-4">
98+
<Image
99+
src={borrowRes.data.book?.cover ?? '/book-placeholder.svg'}
100+
alt={borrowRes.data.book.title + "'s cover"}
101+
width={256}
102+
height={256}
103+
className="shadow-md rounded-lg w-56 h-auto place-self-center row-span-2"
104+
/>
105+
<div>
106+
<h2 className="text-xl font-semibold">
107+
{borrowRes.data.book.title}
108+
</h2>
109+
<p className="text-gray-600">{borrowRes.data.book.author}</p>
110+
<p className="text-sm text-gray-500">
111+
{borrowRes.data.book.code}
112+
</p>
113+
</div>
114+
</CardContent>
115+
</Card>
116+
117+
<Card>
118+
<CardHeader>
119+
<CardTitle>User Information</CardTitle>
120+
</CardHeader>
121+
<CardContent className="grid gap-2 grid-cols-[max-content,1fr] items-center">
122+
<User className="h-4 w-4" />
123+
<p>
124+
<span className="font-medium">Name:&nbsp;</span>
125+
{borrowRes.data.subscription.user.name}
126+
</p>
127+
<Library className="h-4 w-4" />
128+
<p>
129+
<span className="font-medium">Library:&nbsp;</span>
130+
{borrowRes.data.subscription.membership.library.name}
131+
</p>
132+
{/* <CreditCard className="h-4 w-4" />
133+
<p>
134+
<span className="font-medium">Membership:&nbsp;</span>
135+
{borrowRes.data.subscription.membership.name}
136+
</p>
137+
<Clock className="h-4 w-4" />
138+
<p>
139+
<span className="font-medium">Expires:&nbsp;</span>
140+
{formatDate(borrowRes.data.subscription.expires_at)}
141+
</p> */}
142+
</CardContent>
143+
</Card>
144+
145+
<Card>
146+
<CardHeader>
147+
<CardTitle>Borrow Details</CardTitle>
148+
</CardHeader>
149+
<CardContent className="grid gap-2 grid-cols-[max-content,1fr] items-center">
150+
<UserCog className="h-4 w-4 text-muted-foreground" />
151+
<p>
152+
<span className="font-medium">Staff:&nbsp;</span>
153+
{borrowRes.data.staff.name}
154+
&nbsp;
155+
{borrowRes.data.returning
156+
? '/ ' + borrowRes.data.returning.staff.name
157+
: null}
158+
</p>
159+
160+
<Calendar className="h-4 w-4 text-muted-foreground" />
161+
<p>
162+
<span className="font-medium">Borrowed:&nbsp;</span>
163+
{formatDate(borrowRes.data.borrowed_at)}
164+
</p>
165+
{isDue ? (
166+
<>
167+
<Gavel className="h-4 w-4 text-muted-foreground" />
168+
<p>
169+
<span className="font-medium">Fine Expected:&nbsp;</span>
170+
{differenceInDays(
171+
new Date(),
172+
new Date(borrowRes.data.due_at)
173+
) +
174+
' x ' +
175+
borrowRes.data.subscription.fine_per_day +
176+
' = ' +
177+
differenceInDays(
178+
new Date(),
179+
new Date(borrowRes.data.due_at)
180+
) *
181+
borrowRes.data.subscription.fine_per_day +
182+
' Pts'}
183+
</p>
184+
<CalendarX className="h-4 w-4 text-destructive" />
185+
</>
186+
) : (
187+
<CalendarClock className="h-4 w-4 text-muted-foreground" />
188+
)}
189+
<p className={clsx({ 'text-destructive': isDue })}>
190+
<span className="font-medium">Due:&nbsp;</span>
191+
{formatDate(borrowRes.data.due_at)}
192+
</p>
193+
{borrowRes.data.returning ? (
194+
<>
195+
<CalendarCheck className="h-4 w-4 text-muted-foreground" />
196+
<p>
197+
<span className="font-medium">Returned:&nbsp;</span>
198+
{formatDate(borrowRes.data.returning.returned_at)}
199+
</p>
200+
<Gavel className="h-4 w-4 text-muted-foreground" />
201+
<p>
202+
<span className="font-medium">Fine Received:&nbsp;</span>
203+
{borrowRes.data.returning.fine ?? '-'} Pts
204+
</p>
205+
</>
206+
) : null}
207+
</CardContent>
208+
</Card>
97209
</div>
210+
211+
{/* <Card>
212+
<CardHeader>
213+
<CardTitle>Progress</CardTitle>
214+
</CardHeader>
215+
<CardContent>
216+
<Progress value={progressPercent} />
217+
</CardContent>
218+
</Card> */}
219+
98220
{!borrowRes.data.returning && (
99221
<div className="bottom-0 sticky py-2">
100222
<BtnReturnBook

app/globals.css

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,4 +90,13 @@ body {
9090
--chart-4: 280 65% 60%;
9191
--chart-5: 340 75% 55%;
9292
}
93+
}
94+
95+
@layer base {
96+
* {
97+
@apply border-border outline-ring/50;
98+
}
99+
body {
100+
@apply bg-background text-foreground;
101+
}
93102
}

components/ui/progress.tsx

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
'use client'
2+
3+
import * as React from 'react'
4+
import * as ProgressPrimitive from '@radix-ui/react-progress'
5+
6+
import { cn } from '@/lib/utils'
7+
8+
const Progress = React.forwardRef<
9+
React.ElementRef<typeof ProgressPrimitive.Root>,
10+
React.ComponentPropsWithoutRef<typeof ProgressPrimitive.Root>
11+
>(({ className, value, ...props }, ref) => (
12+
<ProgressPrimitive.Root
13+
ref={ref}
14+
className={cn(
15+
'relative h-4 w-full overflow-hidden rounded-full bg-secondary',
16+
className
17+
)}
18+
{...props}
19+
>
20+
<ProgressPrimitive.Indicator
21+
className="h-full w-full flex-1 bg-primary transition-all"
22+
style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
23+
/>
24+
</ProgressPrimitive.Root>
25+
))
26+
Progress.displayName = ProgressPrimitive.Root.displayName
27+
28+
export { Progress }

lib/utils.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,21 @@ export const isSubscriptionActive = (subscription: Subscription) => {
3939
export const getSubscriptionStatus = (subscription: Subscription) => {
4040
return isSubscriptionActive(subscription) ? 'active' : 'expired'
4141
}
42+
43+
export const getBorrowProgressPercent = (borrow: Borrow): number => {
44+
if (isBorrowDue(borrow)) return 100
45+
46+
const start = new Date(borrow.borrowed_at).getTime()
47+
48+
const end = borrow.returning
49+
? new Date(borrow.returning.returned_at).getTime()
50+
: new Date().getTime()
51+
52+
return Math.min(
53+
100,
54+
Math.max(
55+
0,
56+
((end - start) / (new Date(borrow.due_at).getTime() - start)) * 100
57+
)
58+
)
59+
}

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"@radix-ui/react-dropdown-menu": "^2.1.3",
1717
"@radix-ui/react-label": "^2.1.1",
1818
"@radix-ui/react-popover": "^1.1.3",
19+
"@radix-ui/react-progress": "^1.1.2",
1920
"@radix-ui/react-select": "^2.1.4",
2021
"@radix-ui/react-separator": "^1.1.1",
2122
"@radix-ui/react-slot": "^1.1.1",

yarn.lock

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1242,6 +1242,21 @@
12421242
dependencies:
12431243
"@radix-ui/react-slot" "1.1.1"
12441244

1245+
"@radix-ui/react-primitive@2.0.2":
1246+
version "2.0.2"
1247+
resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-2.0.2.tgz#ac8b7854d87b0d7af388d058268d9a7eb64ca8ef"
1248+
integrity sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==
1249+
dependencies:
1250+
"@radix-ui/react-slot" "1.1.2"
1251+
1252+
"@radix-ui/react-progress@^1.1.2":
1253+
version "1.1.2"
1254+
resolved "https://registry.yarnpkg.com/@radix-ui/react-progress/-/react-progress-1.1.2.tgz#3584c346d47f2a6f86076ce5af56ab00c66ded2b"
1255+
integrity sha512-u1IgJFQ4zNAUTjGdDL5dcl/U8ntOR6jsnhxKb5RKp5Ozwl88xKR9EqRZOe/Mk8tnx0x5tNUe2F+MzsyjqMg0MA==
1256+
dependencies:
1257+
"@radix-ui/react-context" "1.1.1"
1258+
"@radix-ui/react-primitive" "2.0.2"
1259+
12451260
"@radix-ui/react-roving-focus@1.1.1":
12461261
version "1.1.1"
12471262
resolved "https://registry.yarnpkg.com/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.1.tgz#3b3abb1e03646937f28d9ab25e96343667ca6520"
@@ -1306,6 +1321,13 @@
13061321
dependencies:
13071322
"@radix-ui/react-compose-refs" "1.1.1"
13081323

1324+
"@radix-ui/react-slot@1.1.2":
1325+
version "1.1.2"
1326+
resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.1.2.tgz#daffff7b2bfe99ade63b5168407680b93c00e1c6"
1327+
integrity sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==
1328+
dependencies:
1329+
"@radix-ui/react-compose-refs" "1.1.1"
1330+
13091331
"@radix-ui/react-toast@^1.2.3":
13101332
version "1.2.3"
13111333
resolved "https://registry.yarnpkg.com/@radix-ui/react-toast/-/react-toast-1.2.3.tgz#459979e05d287f92cd907c6c0a00d390d5c0cdd7"

0 commit comments

Comments
 (0)