Skip to content

Commit f123a4a

Browse files
authored
Merge branch 'main' into main
2 parents e43b087 + 782bafb commit f123a4a

33 files changed

+1621
-27
lines changed

package-lock.json

+675-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+7-5
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,14 @@
1313
"@auth/prisma-adapter": "^1.0.0",
1414
"@next-auth/prisma-adapter": "^1.0.7",
1515
"@prisma/client": "^4.16.2",
16+
"@radix-ui/react-dropdown-menu": "^2.0.5",
1617
"@radix-ui/react-slot": "^1.0.2",
1718
"class-variance-authority": "^0.6.1",
1819
"clsx": "^1.2.1",
1920
"lucide-react": "^0.259.0",
2021
"next": "13.4.9",
2122
"next-auth": "^4.22.1",
23+
"next-themes": "^0.2.1",
2224
"postcss": "8.4.25",
2325
"react": "18.2.0",
2426
"react-dom": "18.2.0",
@@ -29,15 +31,15 @@
2931
"typescript": "5.1.6"
3032
},
3133
"devDependencies": {
32-
"@typescript-eslint/eslint-plugin": "^5.61.0",
33-
"@typescript-eslint/parser": "^5.61.0",
34-
"eslint-plugin-jsx-a11y": "^6.7.1",
35-
"prisma": "^4.16.2",
3634
"@types/node": "20.4.1",
3735
"@types/react": "18.2.14",
3836
"@types/react-dom": "18.2.6",
37+
"@typescript-eslint/eslint-plugin": "^5.61.0",
38+
"@typescript-eslint/parser": "^5.61.0",
39+
"autoprefixer": "10.4.14",
3940
"eslint": "8.44.0",
4041
"eslint-config-next": "13.4.9",
41-
"autoprefixer": "10.4.14"
42+
"eslint-plugin-jsx-a11y": "^6.7.1",
43+
"prisma": "^4.16.2"
4244
}
4345
}

public/placeholder-image.jpg

744 KB
Loading

src/app/api/auth/[...nextauth]/route.ts

+18-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
11
import { prisma } from "@/lib/prisma";
22
import { PrismaAdapter } from "@next-auth/prisma-adapter";
3-
import NextAuth, { AuthOptions } from "next-auth";
3+
import NextAuth, { AuthOptions, DefaultSession } from "next-auth";
44
import GithubProvider from "next-auth/providers/github";
55

6+
declare module "next-auth" {
7+
interface Session extends DefaultSession {
8+
user: {
9+
id: string;
10+
} & DefaultSession["user"];
11+
}
12+
}
13+
614
export const nextAuthOptions = {
715
adapter: PrismaAdapter(prisma),
816
providers: [
@@ -11,6 +19,15 @@ export const nextAuthOptions = {
1119
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
1220
}),
1321
],
22+
callbacks: {
23+
session({ session, user }) {
24+
if (session.user) {
25+
session.user.id = user.id;
26+
}
27+
28+
return session;
29+
}
30+
}
1431
} satisfies AuthOptions;
1532

1633
const handler = NextAuth(nextAuthOptions);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { prisma } from "@/lib/prisma";
2+
3+
const handler = async (request: Request) => {
4+
const object = await request.json();
5+
const uid = request.headers.get("Authorization")?.split("userId ")[1];
6+
const newName = object.newName;
7+
console.log(newName)
8+
if (!newName) {
9+
return new Response(
10+
"name-is-empty",
11+
{ status: 400 }
12+
);
13+
};
14+
15+
prisma.$connect;
16+
17+
await prisma.$executeRaw`UPDATE "User" SET name = ${newName} WHERE id = ${uid}`;
18+
19+
prisma.$disconnect;
20+
21+
return new Response("success!", { status: 200 });
22+
};
23+
24+
export {
25+
handler as POST
26+
};

src/app/globals.css

+4
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
--ring: 215 20.2% 65.1%;
3535

3636
--radius: 0.5rem;
37+
38+
--monochrome-color: 0 0% 0%;
3739
}
3840

3941
.dark {
@@ -65,6 +67,8 @@
6567
--destructive-foreground: 0 85.7% 97.3%;
6668

6769
--ring: 217.2 32.6% 17.5%;
70+
71+
--monochrome-color: 0 0% 100%;
6872
}
6973
}
7074

src/app/layout.tsx

+9-3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ import "./globals.css";
33
import type { Metadata } from "next";
44
import { Inter } from "next/font/google";
55

6+
import { Footer } from "@/components/footer";
7+
import { ThemeProvider } from "@/components/theme-provider"
8+
69
const inter = Inter({ subsets: ["latin"] });
710

811
export const metadata: Metadata = {
@@ -11,15 +14,18 @@ export const metadata: Metadata = {
1114
};
1215

1316
export default function RootLayout({
14-
children,
17+
children
1518
}: {
1619
children: React.ReactNode;
1720
}) {
1821
return (
1922
<html lang="en">
2023
<body className={inter.className}>
21-
<Header />
22-
{children}
24+
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
25+
<Header />
26+
{children}
27+
<Footer />
28+
</ThemeProvider>
2329
</body>
2430
</html>
2531
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
"use client";
2+
3+
import { Button } from "@/components/ui/button"
4+
import { getSession } from "next-auth/react";
5+
import { useRouter } from "next/navigation";
6+
import { useState } from "react";
7+
8+
async function handleSubmit({
9+
typedName,
10+
}: {
11+
typedName: string;
12+
}) {
13+
const session = await getSession();
14+
const uid = session?.user.id;
15+
const displayName = session?.user.name;
16+
17+
if (displayName === typedName) {
18+
console.error("The current username and the new one is still the same!");
19+
throw new Error("The current username and the new one is still the same!")
20+
}
21+
22+
const response = await fetch("/api/user-actions/edit-username", {
23+
headers: {
24+
Authorization: `userId ${uid}`
25+
},
26+
method: "POST",
27+
body: JSON.stringify({ newName: typedName })
28+
});
29+
30+
if (response.status === 200) {
31+
return "succes";
32+
} else {
33+
return "fail";
34+
}
35+
}
36+
37+
export default function ChangeNameForm() {
38+
const [typedName, setTypedName] = useState("");
39+
const router = useRouter();
40+
return (
41+
<>
42+
<form onSubmit={ async (e) => {
43+
e.preventDefault();
44+
const result = await handleSubmit({ typedName });
45+
if (result === "fail") {
46+
/** show some UI on every error here.
47+
* This will be polished soon.
48+
*/
49+
console.error("something went wrong!");
50+
} else {
51+
router.refresh();
52+
}
53+
}} className="z-10 w-[90%] p-6 h-48 max-w-[30rem] rounded-md shadow-monochrome shadow-lg bg-secondary flex flex-col justify-center gap-4">
54+
<div title="Type in new username">
55+
<label htmlFor="username-input" className="text-lg lg:text-xl">Type in your new username</label>
56+
<div className="block cursor-text rounded-sm relative w-full h-12 p-3 mt-2 border-[1px] border-solid border-primary">
57+
<input
58+
type="text"
59+
name="username-input"
60+
placeholder={"your new username"}
61+
id="username-input"
62+
onChange={(e) => setTypedName(e.target.value)}
63+
className="outline-none bg-transparent h-full w-full z-10 relative"
64+
required
65+
value={typedName}
66+
minLength={3}
67+
/>
68+
</div>
69+
</div>
70+
<div>
71+
<Button
72+
type="submit"
73+
variant={"destructive"}
74+
className="py-6 w-full text-lg"
75+
>
76+
Save
77+
</Button>
78+
</div>
79+
</form>
80+
</>
81+
);
82+
};

src/app/profile/_components/index.ts

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import ProfileNavigation from "./profile-navigation";
2+
import ProfileImageButton from "./profile-image-button";
3+
import SearchParamProfilePicture from "./search-param-pfp";
4+
import ToggleChangeName from "./toggle-change-name";
5+
export {
6+
ProfileNavigation,
7+
ProfileImageButton,
8+
SearchParamProfilePicture,
9+
ToggleChangeName
10+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import Link from "next/link";
2+
3+
export default function ProfileImageButton({
4+
children,
5+
photoURL
6+
}: {
7+
children: React.ReactNode,
8+
photoURL?: string | null;
9+
}) {
10+
11+
return (
12+
<Link
13+
title="View Profile Picture"
14+
className="w-full h-full relative before:content-[''] before:w-full before:h-full before:absolute hover:before:bg-monochrome-low-opacity before:inset-0"
15+
href={`/profile/view-photo?photoURL=${photoURL ?? "/placeholder.png"}`}
16+
prefetch
17+
>
18+
{children}
19+
</Link>
20+
);
21+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
"use client";
2+
import { Button } from "@/components/ui/button";
3+
import {
4+
User2,
5+
History
6+
} from "lucide-react";
7+
import {
8+
usePathname,
9+
useRouter
10+
} from "next/navigation";
11+
12+
const profileNavigationLinks = [
13+
{
14+
text: "User Profile",
15+
id: "user-profile-navigation",
16+
title: "Navigate to User Profile",
17+
href: "/profile",
18+
icon: User2
19+
},
20+
{
21+
text: "Race History",
22+
id: "user-race-history-navigation",
23+
title: "View Race History",
24+
href: "/profile/race-history",
25+
icon: History
26+
}
27+
28+
];
29+
30+
const styles = {
31+
ul: "flex md:flex-col gap-x-2 gap-y-4",
32+
listText: "hidden xl:inline-block flex-[0.8]"
33+
};
34+
35+
export default function ProfileNavigation() {
36+
const router = useRouter();
37+
const pathname = usePathname();
38+
39+
return (
40+
<ul className={styles.ul}>
41+
{
42+
profileNavigationLinks.map(link => {
43+
const isLinkActive = pathname === link.href;
44+
return (
45+
<li key={link.id}>
46+
<Button
47+
type="button"
48+
title={link.title}
49+
id={link.id}
50+
name="profile-navigation-buttons"
51+
variant={"ghost"}
52+
className={`w-full gap-2${isLinkActive ? " bg-accent" : ""}`}
53+
aria-current={isLinkActive ? "page" : undefined}
54+
onMouseEnter={() => router.prefetch(link.href)}
55+
onClick={() => router.replace(link.href)}
56+
>
57+
<span>
58+
<link.icon
59+
width={28}
60+
height={28}
61+
/>
62+
</span>
63+
<span className={styles.listText}>
64+
{link.text}
65+
</span>
66+
</Button>
67+
</li>
68+
);
69+
})
70+
}
71+
</ul>
72+
);
73+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
"use client";
2+
import Image from "next/image";
3+
import { useSearchParams } from "next/navigation";
4+
5+
export default function SearchParamProfilePicture() {
6+
const searchParam = useSearchParams();
7+
8+
if (!searchParam) {
9+
return (
10+
<h1 className="text-center">Please provide an image link to view it on full screen!</h1>
11+
);
12+
}
13+
return (
14+
<Image
15+
src={searchParam.get("photoURL") as string}
16+
alt="Profile Picture"
17+
width={1044}
18+
height={861}
19+
className="w-full h-full object-contain"
20+
priority
21+
fetchPriority="high"
22+
loading="eager"
23+
/>
24+
);
25+
};

0 commit comments

Comments
 (0)