Skip to content

Commit 71db0af

Browse files
committed
feat: staff init
1 parent dba7eba commit 71db0af

File tree

7 files changed

+214
-44
lines changed

7 files changed

+214
-44
lines changed

app/books/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import {
2525
import { getListBooks } from "@/lib/api/book";
2626
import Link from "next/link";
2727

28-
export default async function Borrows({
28+
export default async function Books({
2929
searchParams,
3030
}: {
3131
searchParams: Promise<{

app/globals.css

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ body {
1414
--card-foreground: 240 10% 3.9%;
1515
--popover: 0 0% 100%;
1616
--popover-foreground: 240 10% 3.9%;
17-
--primary: 240 5.9% 10%;
18-
--primary-foreground: 0 0% 98%;
17+
--primary: 142.1 76.2% 36.3%;
18+
--primary-foreground: 355.7 100% 97.3%;
1919
--secondary: 240 4.8% 95.9%;
2020
--secondary-foreground: 240 5.9% 10%;
2121
--muted: 240 4.8% 95.9%;
@@ -26,34 +26,35 @@ body {
2626
--destructive-foreground: 0 0% 98%;
2727
--border: 240 5.9% 90%;
2828
--input: 240 5.9% 90%;
29-
--ring: 240 10% 3.9%;
29+
--ring: 142.1 76.2% 36.3%;
30+
--radius: 0.5rem;
3031
--chart-1: 12 76% 61%;
3132
--chart-2: 173 58% 39%;
3233
--chart-3: 197 37% 24%;
3334
--chart-4: 43 74% 66%;
3435
--chart-5: 27 87% 67%;
35-
--radius: 0.5rem;
3636
}
37+
3738
.dark {
38-
--background: 240 10% 3.9%;
39-
--foreground: 0 0% 98%;
40-
--card: 240 10% 3.9%;
41-
--card-foreground: 0 0% 98%;
42-
--popover: 240 10% 3.9%;
43-
--popover-foreground: 0 0% 98%;
44-
--primary: 0 0% 98%;
45-
--primary-foreground: 240 5.9% 10%;
39+
--background: 20 14.3% 4.1%;
40+
--foreground: 0 0% 95%;
41+
--card: 24 9.8% 10%;
42+
--card-foreground: 0 0% 95%;
43+
--popover: 0 0% 9%;
44+
--popover-foreground: 0 0% 95%;
45+
--primary: 142.1 70.6% 45.3%;
46+
--primary-foreground: 144.9 80.4% 10%;
4647
--secondary: 240 3.7% 15.9%;
4748
--secondary-foreground: 0 0% 98%;
48-
--muted: 240 3.7% 15.9%;
49+
--muted: 0 0% 15%;
4950
--muted-foreground: 240 5% 64.9%;
50-
--accent: 240 3.7% 15.9%;
51+
--accent: 12 6.5% 15.1%;
5152
--accent-foreground: 0 0% 98%;
5253
--destructive: 0 62.8% 30.6%;
53-
--destructive-foreground: 0 0% 98%;
54+
--destructive-foreground: 0 85.7% 97.3%;
5455
--border: 240 3.7% 15.9%;
5556
--input: 240 3.7% 15.9%;
56-
--ring: 240 4.9% 83.9%;
57+
--ring: 142.4 71.8% 29.2%;
5758
--chart-1: 220 70% 50%;
5859
--chart-2: 160 60% 45%;
5960
--chart-3: 30 80% 55%;
@@ -66,7 +67,8 @@ body {
6667
* {
6768
@apply border-border;
6869
}
70+
6971
body {
7072
@apply bg-background text-foreground;
7173
}
72-
}
74+
}

app/staffs/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/staffs/new/page.tsx

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { Button } from "@/components/ui/button";
2+
import { Input } from "@/components/ui/input";
3+
import { createBook } from "@/lib/api/book";
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 NewBook() {
17+
async function create(formData: FormData) {
18+
"use server";
19+
20+
const title = formData.get("title") as string;
21+
const author = formData.get("author") as string;
22+
const year = formData.get("year") as string;
23+
const code = formData.get("code") as string;
24+
const library_id = formData.get("library_id") as string;
25+
26+
await createBook({
27+
title,
28+
author,
29+
year: Number(year),
30+
code,
31+
library_id,
32+
});
33+
34+
redirect("/books");
35+
}
36+
37+
return (
38+
<div className="space-y-4">
39+
<h1 className="text-2xl font-semibold">Books</h1>
40+
<Breadcrumb>
41+
<BreadcrumbList>
42+
<BreadcrumbItem>
43+
<Link href="/" passHref legacyBehavior>
44+
<BreadcrumbLink>Home</BreadcrumbLink>
45+
</Link>
46+
</BreadcrumbItem>
47+
<BreadcrumbSeparator />
48+
<BreadcrumbItem>
49+
<Link href="/books" passHref legacyBehavior>
50+
<BreadcrumbLink>books</BreadcrumbLink>
51+
</Link>
52+
</BreadcrumbItem>
53+
<BreadcrumbSeparator />
54+
<BreadcrumbItem>
55+
<BreadcrumbPage>Register a book</BreadcrumbPage>
56+
</BreadcrumbItem>
57+
</BreadcrumbList>
58+
</Breadcrumb>
59+
60+
<form action={create} className="space-y-4 md:max-w-[40%]">
61+
<Input name="title" placeholder="Title" required />
62+
<Input name="author" placeholder="Author" required />
63+
<Input name="year" type="number" placeholder="Year" required />
64+
<Input name="code" placeholder="Code" required />
65+
<Input name="library_id" placeholder="Library ID" required />
66+
<Button type="submit">Create</Button>
67+
</form>
68+
</div>
69+
);
70+
}

app/staffs/page.tsx

Lines changed: 90 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,46 +6,111 @@ import {
66
BreadcrumbPage,
77
BreadcrumbSeparator,
88
} 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";
925
import { getListStaffs } from "@/lib/api/staff";
1026
import Link from "next/link";
1127

12-
export default async function Staffs() {
28+
export default async function Staffs({
29+
searchParams,
30+
}: {
31+
searchParams: Promise<{
32+
skip?: number;
33+
limit?: number;
34+
library_id?: string;
35+
}>;
36+
}) {
37+
const sp = await searchParams;
38+
const skip = Number(sp?.skip ?? 0);
39+
const limit = Number(sp?.limit ?? 20);
40+
const library_id = sp?.library_id;
1341
const res = await getListStaffs({
1442
sort_by: "created_at",
1543
sort_in: "desc",
16-
limit: 20,
44+
limit: limit,
45+
skip: skip,
46+
...(library_id ? { library_id } : {}),
1747
});
1848

1949
if ("error" in res) {
2050
console.log(res);
2151
return <div>{JSON.stringify(res.message)}</div>;
2252
}
2353

54+
const prevSkip = skip - limit > 0 ? skip - limit : 0;
55+
56+
const nextURL = `/staffs?skip=${skip + limit}&limit=${limit}`;
57+
const prevURL = `/staffs?skip=${prevSkip}&limit=${limit}`;
58+
2459
return (
2560
<div>
26-
<h1 className="text-2xl font-semibold">Libraries</h1>
27-
<Breadcrumb>
28-
<BreadcrumbList>
29-
<BreadcrumbItem>
30-
<Link href="/" passHref legacyBehavior>
31-
<BreadcrumbLink>Home</BreadcrumbLink>
32-
</Link>
33-
</BreadcrumbItem>
34-
<BreadcrumbSeparator />
35-
36-
<BreadcrumbItem>
37-
<BreadcrumbPage>Staffs</BreadcrumbPage>
38-
</BreadcrumbItem>
39-
</BreadcrumbList>
40-
</Breadcrumb>
41-
42-
{res.data.map((staff) => (
43-
<div key={staff.id}>
44-
<Link href={`staffs/${staff.id}`} legacyBehavior>
45-
{staff.name}
46-
</Link>
47-
</div>
48-
))}
61+
<h1 className="text-2xl font-semibold">Books</h1>
62+
<div className="flex justify-between items-center">
63+
<Breadcrumb>
64+
<BreadcrumbList>
65+
<BreadcrumbItem>
66+
<Link href="/" passHref legacyBehavior>
67+
<BreadcrumbLink>Home</BreadcrumbLink>
68+
</Link>
69+
</BreadcrumbItem>
70+
<BreadcrumbSeparator />
71+
72+
<BreadcrumbItem>
73+
<BreadcrumbPage>Staffs</BreadcrumbPage>
74+
</BreadcrumbItem>
75+
</BreadcrumbList>
76+
</Breadcrumb>
77+
<Button asChild>
78+
<Link href="/staffs/new">Register a Staff</Link>
79+
</Button>
80+
</div>
81+
82+
<Table>
83+
{/* <TableCaption>List of books available in the library.</TableCaption> */}
84+
<TableHeader>
85+
<TableRow>
86+
<TableHead>Name</TableHead>
87+
<TableHead>User</TableHead>
88+
<TableHead>Library</TableHead>
89+
<TableHead>Registered At</TableHead>
90+
</TableRow>
91+
</TableHeader>
92+
<TableBody>
93+
{res.data.map((s) => (
94+
<TableRow key={s.id}>
95+
<TableCell>{s.name}</TableCell>
96+
<TableCell>{s.user?.name}</TableCell>
97+
<TableCell>{s.library?.name}</TableCell>
98+
<TableCell>{s.created_at}</TableCell>
99+
</TableRow>
100+
))}
101+
</TableBody>
102+
</Table>
103+
104+
<Pagination>
105+
<PaginationContent>
106+
<PaginationItem>
107+
<PaginationPrevious href={prevURL} />
108+
</PaginationItem>
109+
<PaginationItem>
110+
<PaginationNext href={nextURL} />
111+
</PaginationItem>
112+
</PaginationContent>
113+
</Pagination>
49114
</div>
50115
);
51116
}

lib/api/common.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export const BASE_URL = "http://localhost:8080/api/v1";
1+
export const BASE_URL = `${process.env.API_URL}/api/v1`;

next.config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ import type { NextConfig } from "next";
22

33
const nextConfig: NextConfig = {
44
/* config options here */
5+
env: {
6+
API_URL: process.env.API_URL,
7+
},
58
};
69

710
export default nextConfig;

0 commit comments

Comments
 (0)