Add post creation

This commit is contained in:
Ategon 2025-01-16 01:42:36 -05:00
parent 2178675f2a
commit 047d630b14
6 changed files with 160 additions and 15 deletions

View file

@ -0,0 +1,108 @@
"use client";
import { getCookies, hasCookie } from "@/helpers/cookie";
import { Button, Form, Input, Textarea } from "@nextui-org/react";
import { redirect } from "next/navigation";
import { useState } from "react";
import { toast } from "react-toastify";
export default function CreatePostPage() {
const [title, setTitle] = useState("");
const [content, setContent] = useState("");
const [errors, setErrors] = useState({});
return (
<div className="absolute flex items-center justify-center top-0 left-0 w-screen h-screen">
<Form
className="w-full max-w-xs flex flex-col gap-4"
validationErrors={errors}
onReset={() => {
setTitle("");
setContent("");
}}
onSubmit={async (e) => {
e.preventDefault();
if (!title && !content) {
setErrors({
title: "Please enter a valid title",
content: "Please enter valid content",
});
return;
}
if (!title) {
setErrors({ title: "Please enter a valid title" });
return;
}
if (!content) {
setErrors({ content: "Please enter valid content" });
return;
}
if (!hasCookie()) {
setErrors({ content: "You are not logged in" });
return;
}
const response = await fetch(
process.env.NEXT_PUBLIC_MODE === "PROD"
? "https://d2jam.com/api/v1/post"
: "http://localhost:3005/api/v1/post",
{
body: JSON.stringify({
title: title,
content: content,
username: getCookies().user,
}),
method: "POST",
headers: {
"Content-Type": "application/json",
authorization: `Bearer ${getCookies().token}`,
},
}
);
if (response.status == 401) {
setErrors({ content: "Invalid user" });
return;
}
toast.success("Successfully created post");
redirect("/app");
}}
>
<Input
isRequired
label="Title"
labelPlacement="outside"
name="title"
placeholder="Enter a title"
type="text"
value={title}
onValueChange={setTitle}
/>
<Textarea
isRequired
label="Content"
labelPlacement="outside"
name="content"
placeholder="Enter the post body"
value={content}
onValueChange={setContent}
/>
<div className="flex gap-2">
<Button color="primary" type="submit">
Create
</Button>
<Button type="reset" variant="flat">
Reset
</Button>
</div>
</Form>
</div>
);
}

View file

@ -57,8 +57,10 @@ export default function UserPage() {
return;
}
const token = await response.json();
const { token, user } = await response.json();
document.cookie = `token=${token}`;
document.cookie = `user=${user.slug}`;
toast.success("Successfully logged in");

View file

@ -48,6 +48,12 @@ export default function UserPage() {
return;
}
if (username.length > 32) {
setPassword2("");
setErrors({ password: "Usernames can be maximum 32 characters" });
return;
}
if (password.length < 8) {
setPassword2("");
setErrors({ password: "Password must be minimum 8 characters" });
@ -71,8 +77,15 @@ export default function UserPage() {
}
);
const token = await response.json();
if (response.status == 409) {
setErrors({ username: "User already exists" });
setPassword2("");
return;
}
const { token, user } = await response.json();
document.cookie = `token=${token}`;
document.cookie = `user=${user.slug}`;
toast.success("Successfully signed up");

View file

@ -16,39 +16,41 @@ import {
DropdownMenu,
DropdownTrigger,
Image,
Spacer,
Tooltip,
} from "@nextui-org/react";
import { SiDiscord, SiForgejo, SiGithub } from "@icons-pack/react-simple-icons";
import { LogInIcon, NotebookPen } from "lucide-react";
import { LogInIcon, NotebookPen, SquarePen } from "lucide-react";
import { useEffect, useState } from "react";
import { hasCookie, getCookies } from "@/helpers/cookie";
import { usePathname } from "next/navigation";
import { UserType } from "@/types/UserType";
export default function Navbar() {
const [user, setUser] = useState("");
const [user, setUser] = useState<UserType>();
const pathname = usePathname();
useEffect(() => {
loadUser();
async function loadUser() {
if (!hasCookie()) {
setUser("");
setUser(undefined);
return;
}
const response = await fetch(
process.env.NEXT_PUBLIC_MODE === "PROD"
? "https://d2jam.com/api/v1/self"
: "http://localhost:3005/api/v1/self",
? `https://d2jam.com/api/v1/self?username=${getCookies().user}`
: `http://localhost:3005/api/v1/self?username=${getCookies().user}`,
{
headers: { authorization: `Bearer ${getCookies().token}` },
}
);
if ((await response.text()) == "ok") {
setUser("ok");
if (response.status == 200) {
setUser(await response.json());
} else {
setUser("");
setUser(undefined);
}
}
}, [pathname]);
@ -78,6 +80,20 @@ export default function Navbar() {
</NavbarItem>
</NavbarContent>
<NavbarContent justify="end">
{user && (
<NavbarItem>
<Link href="/create-post">
<Button
endContent={<SquarePen />}
className="text-white border-white/50 hover:border-green-100/50 hover:text-green-100 hover:scale-110 transition-all transform duration-500 ease-in-out"
variant="bordered"
>
Create Post
</Button>
</Link>
<Spacer x={32} />
</NavbarItem>
)}
<NavbarItem>
<Tooltip
delay={1000}
@ -154,7 +170,7 @@ export default function Navbar() {
) : (
<Dropdown>
<DropdownTrigger>
<Avatar />
<Avatar src={user.profilePicture} />
</DropdownTrigger>
<DropdownMenu>
{/* <DropdownItem

View file

@ -1,5 +1,4 @@
import { Avatar, Button, Card, CardBody, Spacer } from "@nextui-org/react";
import { Heart, MessageCircle } from "lucide-react";
import { Avatar, Card, CardBody, Spacer } from "@nextui-org/react";
import { formatDistance } from "date-fns";
import Link from "next/link";
import { PostType } from "@/types/PostType";
@ -36,14 +35,14 @@ export default function PostCard({ post }: { post: PostType }) {
<Spacer y={4} />
<div className="flex gap-3">
{/* <div className="flex gap-3">
<Button size="sm">
<Heart size={16} /> {post.likers.length}
</Button>
<Button size="sm">
<MessageCircle size={16} /> {0}
</Button>
</div>
</div> */}
</CardBody>
</Card>
);

7
src/types/UserType.ts Normal file
View file

@ -0,0 +1,7 @@
export interface UserType {
id: number;
slug: string;
name: string;
profilePicture: string;
createdAt: Date;
}