Skip to content

Commit d85b2a1

Browse files
committed
gga
1 parent e4d48d2 commit d85b2a1

File tree

14 files changed

+825
-30
lines changed

14 files changed

+825
-30
lines changed

Semester_04/mpp/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
"@trpc/react-query": "^11.0.0-rc.446",
3535
"@trpc/server": "^11.0.0-rc.446",
3636
"@uploadthing/react": "^7.3.0",
37+
"bcryptjs": "^3.0.2",
3738
"class-variance-authority": "^0.7.1",
3839
"clsx": "^2.1.1",
3940
"geist": "^1.3.0",
@@ -57,6 +58,7 @@
5758
"@testing-library/jest-dom": "^6.6.3",
5859
"@testing-library/react": "^16.2.0",
5960
"@testing-library/user-event": "^14.6.1",
61+
"@types/bcryptjs": "^3.0.0",
6062
"@types/eslint": "^8.56.10",
6163
"@types/jest": "^29.5.14",
6264
"@types/node": "^20.14.10",

Semester_04/mpp/pnpm-lock.yaml

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Semester_04/mpp/prisma/schema.prisma

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,14 @@ model Exercise {
3636
duration Int
3737
createdAt DateTime @default(now())
3838
updatedAt DateTime @updatedAt
39+
40+
// Add relation to User
41+
user User? @relation(fields: [userId], references: [id])
42+
userId String?
3943
4044
@@index([form])
4145
@@index([name])
46+
@@index([userId])
4247
}
4348

4449
// Necessary for Next auth
@@ -78,6 +83,12 @@ model User {
7883
accounts Account[]
7984
sessions Session[]
8085
posts Post[]
86+
87+
// Add password field for credentials login
88+
password String?
89+
90+
// Add exercises relation
91+
exercises Exercise[]
8192
}
8293

8394
model VerificationToken {

Semester_04/mpp/prisma/seed.ts

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { PrismaClient } from "@prisma/client";
2+
import bcrypt from "bcryptjs";
23

34
const prisma = new PrismaClient();
45

@@ -78,18 +79,66 @@ const exercises = [
7879
async function main() {
7980
console.log("Starting seed...");
8081

81-
// Delete existing data
82+
// Create a test user
83+
const hashedPassword = await bcrypt.hash("password123", 10);
84+
85+
// Check if test user already exists
86+
const existingUser = await prisma.user.findUnique({
87+
where: { email: "[email protected]" },
88+
});
89+
90+
let userId;
91+
92+
if (existingUser) {
93+
userId = existingUser.id;
94+
console.log("Test user already exists, using existing user");
95+
} else {
96+
const newUser = await prisma.user.create({
97+
data: {
98+
name: "Test User",
99+
100+
password: hashedPassword,
101+
},
102+
});
103+
userId = newUser.id;
104+
console.log("Created test user:", newUser.email);
105+
}
106+
107+
// Delete existing exercises
82108
await prisma.exercise.deleteMany();
83109
console.log("Cleared existing exercises");
84110

85-
// Insert seed data
111+
// Insert seed data and associate with the test user
86112
for (const exercise of exercises) {
87113
await prisma.exercise.create({
88-
data: exercise,
114+
data: {
115+
...exercise,
116+
userId: userId,
117+
},
118+
});
119+
}
120+
121+
// Also create some exercises without a user (for users who aren't logged in)
122+
for (let i = 0; i < 5; i++) {
123+
await prisma.exercise.create({
124+
data: {
125+
name: `Public Exercise ${i + 1}`,
126+
videoUrl: `https://example.com/public-exercise-${i + 1}.mp4`,
127+
form: ["bad", "medium", "good"][Math.floor(Math.random() * 3)] as
128+
| "bad"
129+
| "medium"
130+
| "good",
131+
date: new Date(
132+
Date.now() - Math.floor(Math.random() * 30 * 24 * 60 * 60 * 1000),
133+
),
134+
duration: Math.floor(Math.random() * 600) + 60,
135+
},
89136
});
90137
}
91138

92-
console.log(`Seeded ${exercises.length} exercises`);
139+
console.log(
140+
`Seeded ${exercises.length} user exercises and 5 public exercises`,
141+
);
93142
}
94143

95144
main()
735 KB
Binary file not shown.
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
"use client";
2+
3+
import { useEffect, useState } from "react";
4+
import Link from "next/link";
5+
import { signOut, useSession } from "next-auth/react";
6+
import { usePathname } from "next/navigation";
7+
8+
export default function Navbar() {
9+
const { data: session, status } = useSession();
10+
const pathname = usePathname();
11+
const [isMenuOpen, setIsMenuOpen] = useState(false);
12+
13+
// Close mobile menu when changing routes
14+
useEffect(() => {
15+
setIsMenuOpen(false);
16+
}, [pathname]);
17+
18+
return (
19+
<nav className="border-b border-gray-800 bg-black">
20+
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
21+
<div className="flex h-16 justify-between">
22+
<div className="flex">
23+
<div className="flex flex-shrink-0 items-center">
24+
<Link href="/" className="text-xl font-bold text-white">
25+
Optima
26+
</Link>
27+
</div>
28+
<div className="hidden sm:ml-6 sm:flex sm:items-center sm:space-x-4">
29+
<Link
30+
href="/"
31+
className={`rounded-md px-3 py-2 text-sm font-medium ${
32+
pathname === "/"
33+
? "bg-gray-900 text-white"
34+
: "text-gray-300 hover:bg-gray-700 hover:text-white"
35+
}`}
36+
>
37+
Home
38+
</Link>
39+
<Link
40+
href="/gallery"
41+
className={`rounded-md px-3 py-2 text-sm font-medium ${
42+
pathname === "/gallery"
43+
? "bg-gray-900 text-white"
44+
: "text-gray-300 hover:bg-gray-700 hover:text-white"
45+
}`}
46+
>
47+
Gallery
48+
</Link>
49+
{session && (
50+
<Link
51+
href="/settings"
52+
className={`rounded-md px-3 py-2 text-sm font-medium ${
53+
pathname === "/settings"
54+
? "bg-gray-900 text-white"
55+
: "text-gray-300 hover:bg-gray-700 hover:text-white"
56+
}`}
57+
>
58+
Settings
59+
</Link>
60+
)}
61+
</div>
62+
</div>
63+
<div className="hidden sm:ml-6 sm:flex sm:items-center">
64+
{status === "loading" ? (
65+
<div className="h-5 w-16 animate-pulse rounded bg-gray-700"></div>
66+
) : session ? (
67+
<div className="flex items-center space-x-4">
68+
<span className="text-sm text-gray-300">
69+
{session.user?.name || session.user?.email}
70+
</span>
71+
<button
72+
onClick={() => signOut({ callbackUrl: "/" })}
73+
className="rounded-md bg-red-600 px-3 py-2 text-sm font-medium text-white hover:bg-red-500"
74+
>
75+
Sign out
76+
</button>
77+
</div>
78+
) : (
79+
<div className="flex items-center space-x-4">
80+
<Link
81+
href="/auth/signin"
82+
className="rounded-md bg-gray-800 px-3 py-2 text-sm font-medium text-white hover:bg-gray-700"
83+
>
84+
Sign in
85+
</Link>
86+
<Link
87+
href="/auth/signup"
88+
className="rounded-md bg-blue-600 px-3 py-2 text-sm font-medium text-white hover:bg-blue-500"
89+
>
90+
Sign up
91+
</Link>
92+
</div>
93+
)}
94+
</div>
95+
96+
{/* Mobile menu button */}
97+
<div className="-mr-2 flex items-center sm:hidden">
98+
<button
99+
onClick={() => setIsMenuOpen(!isMenuOpen)}
100+
className="inline-flex items-center justify-center rounded-md p-2 text-gray-400 hover:bg-gray-700 hover:text-white focus:outline-none focus:ring-2 focus:ring-inset focus:ring-white"
101+
>
102+
<span className="sr-only">
103+
{isMenuOpen ? "Close main menu" : "Open main menu"}
104+
</span>
105+
<svg
106+
className={`h-6 w-6 ${isMenuOpen ? "hidden" : "block"}`}
107+
fill="none"
108+
viewBox="0 0 24 24"
109+
strokeWidth="1.5"
110+
stroke="currentColor"
111+
>
112+
<path
113+
strokeLinecap="round"
114+
strokeLinejoin="round"
115+
d="M4 6h16M4 12h16M4 18h16"
116+
/>
117+
</svg>
118+
<svg
119+
className={`h-6 w-6 ${isMenuOpen ? "block" : "hidden"}`}
120+
fill="none"
121+
viewBox="0 0 24 24"
122+
strokeWidth="1.5"
123+
stroke="currentColor"
124+
>
125+
<path
126+
strokeLinecap="round"
127+
strokeLinejoin="round"
128+
d="M6 18L18 6M6 6l12 12"
129+
/>
130+
</svg>
131+
</button>
132+
</div>
133+
</div>
134+
</div>
135+
136+
{/* Mobile menu */}
137+
<div className={`${isMenuOpen ? "block" : "hidden"} sm:hidden`}>
138+
<div className="space-y-1 px-2 pb-3 pt-2">
139+
<Link
140+
href="/"
141+
className={`block rounded-md px-3 py-2 text-base font-medium ${
142+
pathname === "/"
143+
? "bg-gray-900 text-white"
144+
: "text-gray-300 hover:bg-gray-700 hover:text-white"
145+
}`}
146+
>
147+
Home
148+
</Link>
149+
<Link
150+
href="/gallery"
151+
className={`block rounded-md px-3 py-2 text-base font-medium ${
152+
pathname === "/gallery"
153+
? "bg-gray-900 text-white"
154+
: "text-gray-300 hover:bg-gray-700 hover:text-white"
155+
}`}
156+
>
157+
Gallery
158+
</Link>
159+
{session && (
160+
<Link
161+
href="/settings"
162+
className={`block rounded-md px-3 py-2 text-base font-medium ${
163+
pathname === "/settings"
164+
? "bg-gray-900 text-white"
165+
: "text-gray-300 hover:bg-gray-700 hover:text-white"
166+
}`}
167+
>
168+
Settings
169+
</Link>
170+
)}
171+
</div>
172+
<div className="border-t border-gray-700 pb-3 pt-4">
173+
{status === "loading" ? (
174+
<div className="mx-4 h-5 w-16 animate-pulse rounded bg-gray-700"></div>
175+
) : session ? (
176+
<div className="space-y-3 px-4">
177+
<div className="text-base font-medium text-white">
178+
{session.user?.name || session.user?.email}
179+
</div>
180+
<button
181+
onClick={() => signOut({ callbackUrl: "/" })}
182+
className="block w-full rounded-md bg-red-600 px-3 py-2 text-center text-base font-medium text-white hover:bg-red-500"
183+
>
184+
Sign out
185+
</button>
186+
</div>
187+
) : (
188+
<div className="flex flex-col space-y-2 px-4">
189+
<Link
190+
href="/auth/signin"
191+
className="block w-full rounded-md bg-gray-800 px-3 py-2 text-center text-base font-medium text-white hover:bg-gray-700"
192+
>
193+
Sign in
194+
</Link>
195+
<Link
196+
href="/auth/signup"
197+
className="block w-full rounded-md bg-blue-600 px-3 py-2 text-center text-base font-medium text-white hover:bg-blue-500"
198+
>
199+
Sign up
200+
</Link>
201+
</div>
202+
)}
203+
</div>
204+
</div>
205+
</nav>
206+
);
207+
}

0 commit comments

Comments
 (0)