Skip to content

Commit 163d79c

Browse files
committed
feat: borrow edit page
1 parent 73c6945 commit 163d79c

File tree

10 files changed

+260
-50
lines changed

10 files changed

+260
-50
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { ModalEditBorrow } from '@/components/borrows/ModalEditBorrow'
2+
import { getBorrow } from '@/lib/api/borrow'
3+
import { Verify } from '@/lib/firebase/firebase'
4+
5+
// This is a server component use to pass data to the modal
6+
export default async function BorrowDetailsPage({
7+
params,
8+
}: {
9+
params: Promise<{ id: string }>
10+
}) {
11+
const { id } = await params
12+
13+
await Verify({ from: `/borrows/${id}` })
14+
15+
const [borrowRes] = await Promise.all([getBorrow({ id })])
16+
if ('error' in borrowRes) {
17+
return <div>{JSON.stringify(borrowRes.message)}</div>
18+
}
19+
return <ModalEditBorrow borrow={borrowRes.data} />
20+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function Default() {
2+
return null
3+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { Verify } from '@/lib/firebase/firebase'
2+
import { getBorrow } from '@/lib/api/borrow'
3+
import { FormEditBorrow } from '@/components/borrows/FormEditBorrow'
4+
5+
export default async function BorrowDetailsPage({
6+
params,
7+
}: {
8+
params: Promise<{ id: string }>
9+
}) {
10+
const { id } = await params
11+
12+
await Verify({ from: `/borrows/${id}` })
13+
14+
const [borrowRes] = await Promise.all([getBorrow({ id })])
15+
16+
if ('error' in borrowRes) {
17+
console.log({ libRes: borrowRes })
18+
return <div>{JSON.stringify(borrowRes.message)}</div>
19+
}
20+
21+
return <FormEditBorrow borrow={borrowRes.data} />
22+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { Badge } from '@/components/ui/badge'
2+
import {
3+
Breadcrumb,
4+
BreadcrumbItem,
5+
BreadcrumbLink,
6+
BreadcrumbList,
7+
BreadcrumbSeparator,
8+
} from '@/components/ui/breadcrumb'
9+
import { getBorrow } from '@/lib/api/borrow'
10+
import { Verify } from '@/lib/firebase/firebase'
11+
import { getBorrowStatus } from '@/lib/utils'
12+
import Link from 'next/link'
13+
14+
export default async function BorrowDetailsLayout({
15+
children,
16+
edit,
17+
params,
18+
}: Readonly<{
19+
children: React.ReactNode
20+
edit: React.ReactNode
21+
params: Promise<{ id: string }>
22+
}>) {
23+
const { id } = await params
24+
25+
await Verify({ from: `/borrows/${id}` })
26+
27+
const [borrowRes] = await Promise.all([getBorrow({ id })])
28+
29+
if ('error' in borrowRes) {
30+
console.log({ libRes: borrowRes })
31+
return <div>{JSON.stringify(borrowRes.message)}</div>
32+
}
33+
return (
34+
<div className="space-y-4">
35+
<h1 className="text-2xl font-semibold">{borrowRes.data.book.title}</h1>
36+
<div className="flex justify-between items-center">
37+
<Breadcrumb>
38+
<BreadcrumbList>
39+
<BreadcrumbItem>
40+
<Link href="/" passHref legacyBehavior>
41+
<BreadcrumbLink>Home</BreadcrumbLink>
42+
</Link>
43+
</BreadcrumbItem>
44+
<BreadcrumbSeparator />
45+
<BreadcrumbItem>
46+
<Link href="/borrows" passHref legacyBehavior>
47+
<BreadcrumbLink>Borrows</BreadcrumbLink>
48+
</Link>
49+
</BreadcrumbItem>
50+
<BreadcrumbSeparator />
51+
<BreadcrumbItem>
52+
<Link href={`/borrows/${id}`} passHref legacyBehavior>
53+
<BreadcrumbLink>{borrowRes.data.book.title}</BreadcrumbLink>
54+
</Link>
55+
</BreadcrumbItem>
56+
</BreadcrumbList>
57+
</Breadcrumb>
58+
59+
<Badge
60+
variant={
61+
getBorrowStatus(borrowRes.data) === 'overdue'
62+
? 'destructive'
63+
: getBorrowStatus(borrowRes.data) === 'returned'
64+
? 'secondary'
65+
: 'default'
66+
}
67+
className="uppercase h-8 min-w-24 justify-center"
68+
>
69+
{getBorrowStatus(borrowRes.data)}
70+
</Badge>
71+
</div>
72+
{edit}
73+
{children}
74+
</div>
75+
)
76+
}

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

Lines changed: 3 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,9 @@
1-
import {
2-
Breadcrumb,
3-
BreadcrumbItem,
4-
BreadcrumbLink,
5-
BreadcrumbList,
6-
BreadcrumbPage,
7-
BreadcrumbSeparator,
8-
} from '@/components/ui/breadcrumb'
91
import Link from 'next/link'
102
import { Verify } from '@/lib/firebase/firebase'
113
import { getBorrow, getListBorrows } from '@/lib/api/borrow'
124
import { Badge } from '@/components/ui/badge'
135
import {
146
formatDate,
15-
getBorrowStatus,
167
getSubscriptionStatus,
178
isBorrowDue,
189
isSubscriptionActive,
@@ -85,43 +76,7 @@ export default async function BorrowDetailsPage({
8576
}
8677

8778
return (
88-
<div className="space-y-4">
89-
<h1 className="text-2xl font-semibold">{borrowRes.data.book.title}</h1>
90-
<div className="flex justify-between items-center">
91-
<Breadcrumb>
92-
<BreadcrumbList>
93-
<BreadcrumbItem>
94-
<Link href="/" passHref legacyBehavior>
95-
<BreadcrumbLink>Home</BreadcrumbLink>
96-
</Link>
97-
</BreadcrumbItem>
98-
<BreadcrumbSeparator />
99-
<BreadcrumbItem>
100-
<Link href="/borrows" passHref legacyBehavior>
101-
<BreadcrumbLink>Borrows</BreadcrumbLink>
102-
</Link>
103-
</BreadcrumbItem>
104-
<BreadcrumbSeparator />
105-
<BreadcrumbItem>
106-
<BreadcrumbPage>{borrowRes.data.book.title}</BreadcrumbPage>
107-
</BreadcrumbItem>
108-
</BreadcrumbList>
109-
</Breadcrumb>
110-
111-
<Badge
112-
variant={
113-
getBorrowStatus(borrowRes.data) === 'overdue'
114-
? 'destructive'
115-
: getBorrowStatus(borrowRes.data) === 'returned'
116-
? 'secondary'
117-
: 'default'
118-
}
119-
className="uppercase h-8 min-w-24 justify-center"
120-
>
121-
{getBorrowStatus(borrowRes.data)}
122-
</Badge>
123-
</div>
124-
79+
<>
12580
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
12681
<Card className="md:row-span-2">
12782
<CardHeader>
@@ -331,7 +286,7 @@ export default async function BorrowDetailsPage({
331286
<CardHeader>
332287
<CardTitle>Previous Borrows</CardTitle>
333288
</CardHeader>
334-
<CardContent className="flex items-end overflow-x-scroll p-6">
289+
<CardContent className="flex items-end overflow-x-scroll p-6 isolate">
335290
{prevBorrows.map((b) => (
336291
<Link
337292
href={`/borrows/${b.id}`}
@@ -375,6 +330,6 @@ export default async function BorrowDetailsPage({
375330
</Link>
376331
</Button>
377332
</div>
378-
</div>
333+
</>
379334
)
380335
}

components/borrows/BtnReturnBorrow.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export const BtnReturnBook: React.FC<
2727
const onClick = () => {
2828
startTransition(async () => {
2929
clearTimeout(confirmTimeout)
30+
setConfirmTimeout(undefined)
3031
const res = await actionReturnBorrow(borrow.id)
3132
if ('error' in res) {
3233
toast({
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
'use client'
2+
3+
import { Borrow } from '@/lib/types/borrow'
4+
import { zodResolver } from '@hookform/resolvers/zod'
5+
import { useForm } from 'react-hook-form'
6+
import { z } from 'zod'
7+
import {
8+
Form,
9+
FormControl,
10+
FormDescription,
11+
FormField,
12+
FormItem,
13+
FormLabel,
14+
FormMessage,
15+
} from '../ui/form'
16+
import { useToast } from '../hooks/use-toast'
17+
import { Popover, PopoverContent, PopoverTrigger } from '../ui/popover'
18+
import { Button } from '../ui/button'
19+
import { cn, formatDate } from '@/lib/utils'
20+
import { CalendarIcon } from 'lucide-react'
21+
import { Calendar } from '../ui/calendar'
22+
23+
const FormSchema = z.object({
24+
id: z.string({
25+
required_error: 'Please select a borrow.',
26+
}),
27+
borrowed_at: z.string(),
28+
// due_at: z.coerce.string().datetime(),
29+
// staff_id: z.string({
30+
// required_error: 'Please select a staff.',
31+
// }),
32+
})
33+
34+
export const FormEditBorrow: React.FC<{ borrow: Borrow }> = ({ borrow }) => {
35+
const form = useForm<z.infer<typeof FormSchema>>({
36+
resolver: zodResolver(FormSchema),
37+
defaultValues: borrow,
38+
})
39+
const { toast } = useToast()
40+
41+
function onSubmit(data: z.infer<typeof FormSchema>) {
42+
toast({
43+
title: 'You submitted the following values:',
44+
description: (
45+
<pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
46+
<code className="text-white">{JSON.stringify(data, null, 2)}</code>
47+
</pre>
48+
),
49+
})
50+
}
51+
52+
return (
53+
<Form {...form}>
54+
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
55+
<FormField
56+
control={form.control}
57+
name="borrowed_at"
58+
render={({ field }) => (
59+
<FormItem className="flex flex-col">
60+
<FormLabel>Borrowed At</FormLabel>
61+
<Popover>
62+
<PopoverTrigger asChild>
63+
<FormControl>
64+
<Button
65+
variant={'outline'}
66+
className={cn(
67+
'w-[240px] pl-3 text-left font-normal',
68+
!field.value && 'text-muted-foreground'
69+
)}
70+
>
71+
{field.value ? (
72+
formatDate(field.value)
73+
) : (
74+
<span>Pick a date</span>
75+
)}
76+
<CalendarIcon className="ml-auto h-4 w-4 opacity-50" />
77+
</Button>
78+
</FormControl>
79+
</PopoverTrigger>
80+
<PopoverContent className="w-auto p-0" align="start">
81+
<Calendar
82+
mode="single"
83+
selected={new Date(field.value)}
84+
onSelect={(v) => field.onChange(v?.toISOString())}
85+
disabled={(date) =>
86+
date > new Date() || date < new Date('1900-01-01')
87+
}
88+
initialFocus
89+
/>
90+
</PopoverContent>
91+
</Popover>
92+
<FormDescription>
93+
Change the date when the book was borrowed.
94+
</FormDescription>
95+
<FormMessage />
96+
</FormItem>
97+
)}
98+
/>
99+
<Button type="submit">Submit</Button>
100+
</form>
101+
</Form>
102+
)
103+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
'use client'
2+
3+
import { FormEditBorrow } from '@/components/borrows/FormEditBorrow'
4+
import {
5+
Dialog,
6+
DialogContent,
7+
DialogDescription,
8+
DialogHeader,
9+
DialogTitle,
10+
} from '@/components/ui/dialog'
11+
import { Borrow } from '@/lib/types/borrow'
12+
import { useRouter } from 'next/navigation'
13+
14+
export const ModalEditBorrow: React.FC<{ borrow: Borrow }> = ({ borrow }) => {
15+
const router = useRouter()
16+
return (
17+
<Dialog open={true} onOpenChange={router.back}>
18+
<DialogContent>
19+
<DialogHeader>
20+
<DialogTitle>{borrow.book.title}</DialogTitle>
21+
<DialogDescription>
22+
Editing {borrow.subscription.user.name}&apos;s Borrow.
23+
</DialogDescription>
24+
25+
<FormEditBorrow borrow={borrow} />
26+
</DialogHeader>
27+
</DialogContent>
28+
</Dialog>
29+
)
30+
}

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"dependencies": {
1313
"@hookform/resolvers": "^3.9.1",
1414
"@radix-ui/react-checkbox": "^1.1.3",
15-
"@radix-ui/react-dialog": "^1.1.3",
15+
"@radix-ui/react-dialog": "^1.1.6",
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",

yarn.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -948,7 +948,7 @@
948948
resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.1.1.tgz#82074aa83a472353bb22e86f11bcbd1c61c4c71a"
949949
integrity sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==
950950

951-
"@radix-ui/react-dialog@^1.1.2", "@radix-ui/react-dialog@^1.1.3":
951+
"@radix-ui/react-dialog@^1.1.2", "@radix-ui/react-dialog@^1.1.6":
952952
version "1.1.6"
953953
resolved "https://registry.yarnpkg.com/@radix-ui/react-dialog/-/react-dialog-1.1.6.tgz#65b4465e99ad900f28a98eed9a94bb21ec644bf7"
954954
integrity sha512-/IVhJV5AceX620DUJ4uYVMymzsipdKBzo3edo+omeskCKGm9FRHM0ebIdbPnlQVJqyuHbuBltQUOG2mOTq2IYw==

0 commit comments

Comments
 (0)