Skip to content

Commit

Permalink
Playground/shadcn (#17)
Browse files Browse the repository at this point in the history
* [frontend] ♻️ Move Nav to RootLayout

* [frontend] Add user list and signup page to Nav

- [frontend] Applied Card component to signup page

* [frontend] Fix value to defaultValue in user page

* [frontend] Add UserCard component

* [frontend] Update title and description

* [frontend] Add toast on signup success/failure

* [frontend] Redirect to user list page after signup

* [frontend] Implement update and delete user

* [frontend] `npx prettier --write frontend/src/**/*.tsx`

* [frontend] No cache for user page, redirect to user list after delete

* [backend] `npx prettier --write backend/src/**/*.ts`

* [Makefile] Add fmt target
  • Loading branch information
usatie authored Oct 28, 2023
1 parent e23c125 commit 2ddddb1
Show file tree
Hide file tree
Showing 21 changed files with 749 additions and 180 deletions.
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,7 @@ dev: build
.PHONY: prod
prod: rebuild
docker compose -f compose.yml -f compose.prod.yml up -d

.PHONY: fmt
fmt:
npx prettier --write frontend/src backend/src
5 changes: 4 additions & 1 deletion backend/src/user/user.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ export class UserController {
}

@Patch(':id')
update(@Param('id') id: string, @Body() userData: { name?: string; email?: string }) {
update(
@Param('id') id: string,
@Body() userData: { name?: string; email?: string },
) {
return this.userService.update(+id, userData);
}

Expand Down
18 changes: 9 additions & 9 deletions backend/src/user/user.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,23 @@ export class UserService {
}

findAll() {
return this.prisma.user.findMany();
return this.prisma.user.findMany();
}

findOne(id: number) {
return this.prisma.user.findFirst( { where: { id: id } });
return this.prisma.user.findFirst({ where: { id: id } });
}

update(id: number, data: Prisma.UserUpdateInput) {
return this.prisma.user.update({
data,
where: { id: id },
});
return this.prisma.user.update({
data,
where: { id: id },
});
}

remove(id: number) {
return this.prisma.user.delete({
where: { id: id },
});
return this.prisma.user.delete({
where: { id: id },
});
}
}
58 changes: 58 additions & 0 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"@radix-ui/react-dropdown-menu": "^2.0.6",
"@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-toast": "^1.1.5",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"lucide-react": "^0.288.0",
Expand Down
14 changes: 11 additions & 3 deletions frontend/src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";

// components
import { ThemeProvider } from "@/components/theme-provider";
import Nav from "@/components/Nav";
import { Toaster } from "@/components/ui/toaster";

const inter = Inter({ subsets: ["latin"] });

export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
title: "Pong",
description: "Classic Pong game",
};

export default function RootLayout({
Expand All @@ -24,7 +28,11 @@ export default function RootLayout({
enableSystem
disableTransitionOnChange
>
{children}
<main className="flex flex-col gap-8 p-16">
<Nav />
{children}
</main>
<Toaster />
</ThemeProvider>
</body>
</html>
Expand Down
6 changes: 2 additions & 4 deletions frontend/src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import Nav from "../components/Nav";
import { Button } from "@/components/ui/button";

export default function Home() {
return (
<main className="flex flex-col gap-8 p-24">
<Nav />
<>
<div className="text-muted-foreground">Hello</div>
<div className="flex gap-8">
<Button variant={"secondary"}>Learn More</Button>
<Button>Enroll</Button>
</div>
</main>
</>
);
}
12 changes: 6 additions & 6 deletions frontend/src/app/user/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import UserCard from "@/components/UserCard";

async function getUser(id: number) {
const res = await fetch(`${process.env.API_URL}/user/${id}`);
const res = await fetch(`${process.env.API_URL}/user/${id}`, {
cache: "no-cache",
});
const user = await res.json();
return user;
}
Expand All @@ -10,9 +14,5 @@ export default async function FindUser({
params: { id: number };
}) {
const user = await getUser(id);
return (
<div>
{user.name}({user.id}) : {user.email}
</div>
);
return <UserCard user={user} />;
}
46 changes: 6 additions & 40 deletions frontend/src/app/user/page.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,4 @@
import Nav from "@/components/Nav";
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Button } from "@/components/ui/button";
import UserCard from "@/components/UserCard";

export type User = { id: number; name?: string; email?: string };

Expand All @@ -24,33 +13,10 @@ async function getUsers(): Promise<User[]> {
export default async function UserListPage() {
const users = await getUsers();
return (
<main className="flex flex-col gap-8 p-24">
<Nav />
<div className="flex flex-col gap-3">
{users.map((user, index) => (
<Card className="w-[350px]" key={index}>
<CardHeader></CardHeader>
<CardContent>
<form>
<div className="grid w-full items-center gap-4">
<div className="flex flex-col space-y-1.5">
<Label htmlFor="name">Name</Label>
<Input id="name" value={user.name} />
</div>
<div className="flex flex-col space-y-1.5">
<Label htmlFor="email">Email</Label>
<Input id="email" value={user.email} />
</div>
</div>
</form>
</CardContent>
<CardFooter className="flex justify-between">
<Button variant="outline">Delete</Button>
<Button>Update</Button>
</CardFooter>
</Card>
))}
</div>
</main>
<div className="flex flex-wrap gap-8">
{users.map((user, index) => (
<UserCard user={user} key={index} />
))}
</div>
);
}
90 changes: 67 additions & 23 deletions frontend/src/app/user/signup/page.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,74 @@
"use client";

async function submit(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault();
const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/user`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(Object.fromEntries(new FormData(event.currentTarget))),
});
const user = await res.json();
console.log(user);
}
import { useRouter } from "next/navigation";

// components
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Button } from "@/components/ui/button";
import { useToast } from "@/components/ui/use-toast";

export default function SignUp() {
const router = useRouter();
const { toast } = useToast();
async function createUser(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault();
const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/user`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(
Object.fromEntries(new FormData(event.currentTarget)),
),
});
const data = await res.json();
if (!res.ok) {
toast({
title: res.status + " " + res.statusText,
description: data.message,
});
} else {
toast({
title: "Success",
description: "User created successfully.",
});
router.push("/user");
router.refresh();
}
}

return (
<form className="flex flex-col gap-4" onSubmit={submit}>
<label>
<span>Name</span>
<input type="text" name="name" />
</label>
<label>
<span>Email</span>
<input type="email" name="email" />
</label>
<button type="submit">Sign Up</button>
</form>
<Card className="w-[300px]">
<CardHeader>Create Account</CardHeader>
<CardContent>
<form onSubmit={createUser}>
<div className="grid w-full items-center gap-8">
<div className="flex flex-col space-y-1.5">
<Label htmlFor="name">Name</Label>
<Input id="name" type="text" name="name" placeholder="nop" />
</div>
<div className="flex flex-col space-y-1.5">
<Label htmlFor="email">Email</Label>
<Input
id="email"
type="email"
name="email"
placeholder="[email protected]"
/>
</div>
<Button type="submit">Sign Up</Button>
</div>
</form>
</CardContent>
</Card>
);
}
11 changes: 10 additions & 1 deletion frontend/src/components/Nav.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Image from "next/image";
import { ModeToggle } from "./toggle-mode";
import Link from "next/link";

export default function Nav() {
return (
Expand All @@ -16,7 +17,15 @@ export default function Nav() {
priority
/>
</li>
<li>
<li className="flex gap-8 items-center">
<Link href="/" className="">
Home
</Link>
<Link href="/user">User List</Link>
<Link href="/user/signup">Sign Up</Link>
<Link href="/playground/pong.html" target="_blank">
Pong
</Link>
<ModeToggle></ModeToggle>
</li>
</ul>
Expand Down
Loading

0 comments on commit 2ddddb1

Please sign in to comment.