Skip to content

Commit b82823c

Browse files
committed
feat: user list and create
1 parent a2dbc9a commit b82823c

File tree

5 files changed

+225
-2
lines changed

5 files changed

+225
-2
lines changed

app/books/new/page.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,12 @@ export default function NewBook() {
4747
<BreadcrumbSeparator />
4848
<BreadcrumbItem>
4949
<Link href="/books" passHref legacyBehavior>
50-
<BreadcrumbLink>books</BreadcrumbLink>
50+
<BreadcrumbLink>Books</BreadcrumbLink>
5151
</Link>
5252
</BreadcrumbItem>
5353
<BreadcrumbSeparator />
5454
<BreadcrumbItem>
55-
<BreadcrumbPage>Register a book</BreadcrumbPage>
55+
<BreadcrumbPage>Register Book</BreadcrumbPage>
5656
</BreadcrumbItem>
5757
</BreadcrumbList>
5858
</Breadcrumb>

app/users/new/error.tsx

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
'use client' // Error boundaries must be Client Components
2+
3+
import { useEffect } from 'react'
4+
5+
export default function Error({
6+
error,
7+
reset,
8+
}: {
9+
error: Error & { digest?: string }
10+
reset: () => void
11+
}) {
12+
useEffect(() => {
13+
// Log the error to an error reporting service
14+
console.error(error)
15+
}, [error])
16+
17+
return (
18+
<div>
19+
<h2>Something went wrong!</h2>
20+
<button
21+
onClick={
22+
// Attempt to recover by trying to re-render the segment
23+
() => reset()
24+
}
25+
>
26+
Try again
27+
</button>
28+
</div>
29+
)
30+
}

app/users/new/page.tsx

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { Button } from '@/components/ui/button'
2+
import { Input } from '@/components/ui/input'
3+
import { createUser } from '@/lib/api/user'
4+
5+
import {
6+
Breadcrumb,
7+
BreadcrumbItem,
8+
BreadcrumbLink,
9+
BreadcrumbList,
10+
BreadcrumbPage,
11+
BreadcrumbSeparator,
12+
} from '@/components/ui/breadcrumb'
13+
import Link from 'next/link'
14+
import { redirect } from 'next/navigation'
15+
16+
export default function NewUser() {
17+
async function create(formData: FormData) {
18+
'use server'
19+
20+
const name = formData.get('name') as string
21+
const email = formData.get('email') as string
22+
23+
await createUser({
24+
name,
25+
email,
26+
})
27+
28+
redirect('/users')
29+
}
30+
31+
return (
32+
<div className="space-y-4">
33+
<h1 className="text-2xl font-semibold">Add User</h1>
34+
<Breadcrumb>
35+
<BreadcrumbList>
36+
<BreadcrumbItem>
37+
<Link href="/" passHref legacyBehavior>
38+
<BreadcrumbLink>Home</BreadcrumbLink>
39+
</Link>
40+
</BreadcrumbItem>
41+
<BreadcrumbSeparator />
42+
<BreadcrumbItem>
43+
<Link href="/users" passHref legacyBehavior>
44+
<BreadcrumbLink>Users</BreadcrumbLink>
45+
</Link>
46+
</BreadcrumbItem>
47+
<BreadcrumbSeparator />
48+
<BreadcrumbItem>
49+
<BreadcrumbPage>Add a User</BreadcrumbPage>
50+
</BreadcrumbItem>
51+
</BreadcrumbList>
52+
</Breadcrumb>
53+
54+
<form action={create} className="space-y-4 md:max-w-[40%]">
55+
<Input name="name" placeholder="Name" required />
56+
<Input name="email" placeholder="Email" type="email" required />
57+
<Button type="submit">Create</Button>
58+
</form>
59+
</div>
60+
)
61+
}

app/users/page.tsx

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import {
2+
Breadcrumb,
3+
BreadcrumbItem,
4+
BreadcrumbLink,
5+
BreadcrumbList,
6+
BreadcrumbPage,
7+
BreadcrumbSeparator,
8+
} from '@/components/ui/breadcrumb'
9+
import { Button } from '@/components/ui/button'
10+
import {
11+
Pagination,
12+
PaginationContent,
13+
PaginationItem,
14+
PaginationNext,
15+
PaginationPrevious,
16+
} from '@/components/ui/pagination'
17+
import {
18+
Table,
19+
TableBody,
20+
TableCell,
21+
TableHead,
22+
TableHeader,
23+
TableRow,
24+
} from '@/components/ui/table'
25+
import { getListUsers } from '@/lib/api/user'
26+
import Link from 'next/link'
27+
28+
export default async function Users({
29+
searchParams,
30+
}: {
31+
searchParams: Promise<{
32+
skip?: number
33+
limit?: number
34+
}>
35+
}) {
36+
const sp = await searchParams
37+
const skip = Number(sp?.skip ?? 0)
38+
const limit = Number(sp?.limit ?? 20)
39+
const res = await getListUsers({
40+
sort_by: 'created_at',
41+
sort_in: 'desc',
42+
limit: limit,
43+
skip: skip,
44+
})
45+
46+
if ('error' in res) {
47+
console.log(res)
48+
return <div>{JSON.stringify(res.message)}</div>
49+
}
50+
51+
const prevSkip = skip - limit > 0 ? skip - limit : 0
52+
53+
const nextURL = `/users?skip=${skip + limit}&limit=${limit}`
54+
const prevURL = `/users?skip=${prevSkip}&limit=${limit}`
55+
56+
return (
57+
<div>
58+
<h1 className="text-2xl font-semibold">Users</h1>
59+
<div className="flex justify-between items-center">
60+
<Breadcrumb>
61+
<BreadcrumbList>
62+
<BreadcrumbItem>
63+
<Link href="/" passHref legacyBehavior>
64+
<BreadcrumbLink>Home</BreadcrumbLink>
65+
</Link>
66+
</BreadcrumbItem>
67+
<BreadcrumbSeparator />
68+
69+
<BreadcrumbItem>
70+
<BreadcrumbPage>Users</BreadcrumbPage>
71+
</BreadcrumbItem>
72+
</BreadcrumbList>
73+
</Breadcrumb>
74+
<Button asChild>
75+
<Link href="/users/new">Add User</Link>
76+
</Button>
77+
</div>
78+
79+
<Table>
80+
{/* <TableCaption>List of books available in the library.</TableCaption> */}
81+
<TableHeader>
82+
<TableRow>
83+
<TableHead>ID</TableHead>
84+
<TableHead>Name</TableHead>
85+
<TableHead>Email</TableHead>
86+
<TableHead>Created At</TableHead>
87+
</TableRow>
88+
</TableHeader>
89+
<TableBody>
90+
{res.data.map((u) => (
91+
<TableRow key={u.id}>
92+
<TableCell>{u.id}</TableCell>
93+
<TableCell>{u.name}</TableCell>
94+
<TableCell>{u.email}</TableCell>
95+
<TableCell>{u.created_at}</TableCell>
96+
</TableRow>
97+
))}
98+
</TableBody>
99+
</Table>
100+
101+
<Pagination>
102+
<PaginationContent>
103+
<PaginationItem>
104+
<PaginationPrevious href={prevURL} />
105+
</PaginationItem>
106+
<PaginationItem>
107+
<PaginationNext href={nextURL} />
108+
</PaginationItem>
109+
</PaginationContent>
110+
</Pagination>
111+
</div>
112+
)
113+
}

lib/api/user.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,22 @@ export const getUser = async (query: GetUserQuery): GetUserResponse => {
3535
const response = await fetch(url.toString())
3636
return response.json()
3737
}
38+
39+
type CreateUserQuery = Pick<User, 'name' | 'email'>
40+
type CreateBookResponse = Promise<ResSingle<Pick<User, 'id'>>>
41+
export const createUser = async (
42+
query: CreateUserQuery
43+
): CreateBookResponse => {
44+
const response = await fetch(USERS_URL, {
45+
method: 'POST',
46+
headers: {
47+
'Content-Type': 'application/json',
48+
},
49+
body: JSON.stringify(query),
50+
})
51+
if (!response.ok) {
52+
const e = await response.json()
53+
throw e
54+
}
55+
return response.json()
56+
}

0 commit comments

Comments
 (0)