Skip to content

Commit 3a808e5

Browse files
committed
❇️ Finish up project
1 parent 970ffeb commit 3a808e5

14 files changed

+372
-39
lines changed

components/Navbar.tsx

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import Link from "next/link";
2+
import QuilPen from "remixicon-react/QuillPenFillIcon"
3+
import Home from "remixicon-react/Home2FillIcon"
4+
import Sun from "remixicon-react/SunFillIcon"
5+
import LogOut from "remixicon-react/LogoutCircleLineIcon"
6+
import {destroyCookie} from "nookies";
7+
import { useRouter } from 'next/navigation'
8+
9+
export default function Navbar() {
10+
const router = useRouter()
11+
const handleLogout = async () => {
12+
destroyCookie(null, 'token')
13+
router.push("/signIn")
14+
}
15+
return (
16+
<div className={"w-full h-[90px] border-b-[1px] border-white flex justify-between items-center p-4 lg:p-8"}>
17+
<div>
18+
<h1 className={"text-4xl font-bold"}>Y</h1>
19+
</div>
20+
<div className={"flex gap-10"}>
21+
<Link
22+
href={"/"}>
23+
<Home/>
24+
</Link>
25+
<Link
26+
href={"/post"}>
27+
<QuilPen/>
28+
</Link>
29+
<Link
30+
href={"/lightMode"}>
31+
<Sun/>
32+
</Link>
33+
<div className={"cursor-pointer hover:text-red-700 duration-300"} onClick={handleLogout}>
34+
<LogOut/>
35+
</div>
36+
</div>
37+
</div>
38+
)
39+
}

package-lock.json

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

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"postcss": "8.4.27",
2323
"react": "18.2.0",
2424
"react-dom": "18.2.0",
25+
"remixicon-react": "^1.0.0",
2526
"tailwindcss": "3.3.3",
2627
"typescript": "5.1.6"
2728
},

pages/api/createPosts.ts

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import {NextApiRequest, NextApiResponse} from "next";
2+
import Prisma from "@/utils/prisma";
3+
4+
export default async function handler(
5+
req: NextApiRequest,
6+
res: NextApiResponse
7+
) {
8+
try {
9+
const {authorization: userId} = req.headers;
10+
if (!userId) {
11+
return res.status(401).send({error: "Must be logged in to create a post"});
12+
}
13+
14+
const {title: _title, content: _content} = req.body;
15+
if (!_title || !_content || !_title.trim() || !_content.trim()) {
16+
return res
17+
.status(400)
18+
.send({error: "Provide both title and content for the post"});
19+
}
20+
21+
// Assuming Prisma.getInstance() returns a valid Prisma client instance
22+
const prisma = Prisma.getInstance();
23+
await prisma.post.create({
24+
data: {
25+
title: _title,
26+
content: _content,
27+
authorId: String(userId),
28+
},
29+
});
30+
31+
res.status(200).send({message: "Post created successfully!"});
32+
} catch (e) {
33+
console.error("Error creating post:", e);
34+
res.status(500).json({error: "Failed to create the post"});
35+
}
36+
}

pages/api/getPosts.ts

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import {NextApiRequest, NextApiResponse} from "next";
2+
import Prisma from "@/utils/prisma";
3+
4+
export default async function handler(
5+
req: NextApiRequest,
6+
res: NextApiResponse
7+
) {
8+
try {
9+
const response = await Prisma.getInstance().post.findMany({
10+
select: {
11+
id: true,
12+
title: true,
13+
content: true,
14+
author: {
15+
select: {
16+
id: true,
17+
displayName: true,
18+
userName: true,
19+
}
20+
}
21+
}
22+
});
23+
const posts = await response;
24+
res.status(200).json(posts);
25+
} catch (e) {
26+
console.log(e);
27+
res.status(500).json({error: e})
28+
}
29+
}

pages/api/getUserData.ts

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import {NextApiRequest, NextApiResponse} from "next";
2+
import Prisma from "@/utils/prisma";
3+
4+
export default async function handler(
5+
req: NextApiRequest,
6+
res: NextApiResponse
7+
) {
8+
const {userId} = req.headers;
9+
try {
10+
const response = await Prisma.getInstance().user.findFirst({
11+
where: {
12+
id: userId as string
13+
},
14+
select: {
15+
id: true,
16+
email: true,
17+
displayName: true,
18+
userName: true,
19+
}
20+
});
21+
res.status(200).json(response);
22+
} catch (e) {
23+
console.log(e);
24+
res.status(500).json({error: e})
25+
}
26+
27+
}

pages/index.tsx

+51-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,40 @@
11
import {GetServerSideProps} from "next";
22
import nookies from 'nookies'
3+
import Navbar from "@/components/Navbar";
34

4-
export default function Home() {
5+
export default function Home({user, posts}: any) {
6+
console.log(posts);
57
return (
68
// TODO: Add home page
7-
<div>
8-
9+
<div className={"w-full min-h-screen h-fit flex flex-col"}>
10+
<div>
11+
<Navbar/>
12+
</div>
13+
<div className={"p-8 flex flex-col gap-5"}>
14+
<div className={"flex flex-col gap-2"}>
15+
<h1 className={"text-5xl font-bold"}>Hello {user.displayName}</h1>
16+
<h1 className={"text-[#909099]"}>Here are some dank tYeets</h1>
17+
</div>
18+
<div>
19+
{posts ? posts.reverse().map((post: any) => {
20+
return (
21+
<div key={post.id}
22+
className={"p-4 border-b border-[#2f3336] my-2 flex flex-col gap-2"}>
23+
<div className={"flex flex-col"}>
24+
<h1 className={"text-xl font-bold"}>{post.author.displayName}</h1>
25+
<h1 className={"text-xs text-[#71767b]"}>@{post.author.userName}</h1>
26+
</div>
27+
<h1 className={"text-xl font-bold"}>{post.title}</h1>
28+
<p>{post.content}</p>
29+
</div>
30+
)
31+
}) : (
32+
<div className={"w-full h-full flex items-center justify-center"}>
33+
<h1 className={"text-xl text-[#909099] font-bold"}>No posts found :(</h1>
34+
</div>
35+
)}
36+
</div>
37+
</div>
938
</div>
1039
)
1140
}
@@ -22,7 +51,25 @@ export const getServerSideProps: GetServerSideProps = async (ctx) => {
2251
}
2352
}
2453
}
54+
// Fetch user data from /api/getUserData
55+
const userResponse = await fetch("http://localhost:3000/api/getUserData", {
56+
method: "POST",
57+
headers: {
58+
"Content-Type": "application/json",
59+
"userId": cookies.token
60+
}
61+
}
62+
)
63+
64+
const postsResponse = await fetch("http://localhost:3000/api/getPosts", {
65+
method: "POST",
66+
})
67+
2568
return {
26-
props: {}
69+
props: {
70+
user: await userResponse.json(),
71+
posts: await postsResponse.json()
72+
}
2773
}
74+
2875
}

pages/lightMode.tsx

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import Image from "next/image";
2+
3+
export default function LightMode() {
4+
return (
5+
<div className={"w-full min-h-screen h-fit overflow-hidden"}>
6+
<Image className={"w-full"} src={"/meme.jpg"} alt={"NO LIGHT MODE"}
7+
width={100} height={100}/>
8+
</div>
9+
)
10+
}

pages/post.tsx

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import {useRouter} from "next/router";
2+
import {useState} from "react";
3+
import {parseCookies} from "nookies";
4+
import Navbar from "@/components/Navbar";
5+
6+
export default function Post() {
7+
const router = useRouter();
8+
const [title, setTitle] = useState("");
9+
const [content, setContent] = useState("");
10+
11+
const handleKeyDown = (e: any) => {
12+
// Reset field height
13+
e.target.style.height = 'inherit';
14+
15+
// Get the computed styles for the element
16+
const computed = window.getComputedStyle(e.target);
17+
18+
// Calculate the height
19+
const height = parseInt(computed.getPropertyValue('border-top-width'), 10)
20+
+ parseInt(computed.getPropertyValue('padding-top'), 10)
21+
+ e.target.scrollHeight
22+
+ parseInt(computed.getPropertyValue('padding-bottom'), 10)
23+
+ parseInt(computed.getPropertyValue('border-bottom-width'), 10);
24+
25+
e.target.style.height = `${height}px`;
26+
}
27+
28+
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
29+
e.preventDefault();
30+
const cookies = parseCookies()
31+
32+
const response = await fetch("/api/createPosts", {
33+
method: "POST",
34+
headers: {
35+
"Content-Type": "application/json",
36+
"Authorization": cookies.token
37+
},
38+
body: JSON.stringify({title, content}),
39+
});
40+
if (response.ok) {
41+
router.push("/")
42+
}
43+
}
44+
return (
45+
<div className={"w-full min-h-screen h-fit flex flex-col"}>
46+
<div>
47+
<Navbar/>
48+
</div>
49+
<div className={"w-full h-full p-8 flex flex-col gap-10"}>
50+
<div className={"flex flex-col gap-2"}>
51+
<h1 className={"text-4xl md:text-7xl font-semibold"}>Create a new post</h1>
52+
<h1 className={"text-[#909099] md:ml-1"}>Say something dank to the world</h1>
53+
</div>
54+
<form className={"w-full md:w-1/2 flex flex-col gap-5"} onSubmit={handleSubmit}>
55+
<div className={"flex flex-col gap-2"}>
56+
<label htmlFor="title">Title</label>
57+
<input
58+
className={"text-white px-4 py-2 outline-0 bg-transparent"}
59+
required={true}
60+
placeholder={"Why did the chicken cross the road?"}
61+
type="text" id="title" value={title}
62+
onChange={(e) => setTitle(e.target.value)}/>
63+
</div>
64+
<div className={"flex flex-col gap-2"}>
65+
<label htmlFor="content">Content</label>
66+
<textarea
67+
className={"text-white px-4 py-2 outline-0 bg-transparent w-full h-auto resize-none"}
68+
required={true}
69+
placeholder={"To get to the other side"}
70+
id="content" value={content}
71+
onChange={(e) => setContent(e.target.value)}
72+
onKeyDown={handleKeyDown}
73+
/>
74+
</div>
75+
<div className={"flex flex-col gap-2 absolute bottom-2 left-2 right-2"}>
76+
<button className={"px-4 py-2 rounded-lg bg-white text-black font-semibold"}>Create
77+
Post
78+
</button>
79+
</div>
80+
</form>
81+
</div>
82+
</div>
83+
)
84+
}

0 commit comments

Comments
 (0)