Add login functionality

This commit is contained in:
Ategon 2025-01-15 22:42:11 -05:00
parent e61dd263c5
commit 51d92b2fc4
5 changed files with 222 additions and 10 deletions

83
src/app/login/page.tsx Normal file
View file

@ -0,0 +1,83 @@
"use client";
import { Button, Form, Input } from "@nextui-org/react";
import { redirect } from "next/navigation";
import { useState } from "react";
export default function UserPage() {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = 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"
validationBehavior="native"
onReset={() => {
setUsername("");
setPassword("");
}}
onSubmit={async (e) => {
e.preventDefault();
const response = await fetch(
process.env.MODE === "PROD"
? "https://d2jam.com/api/v1/login"
: "http://localhost:3005/api/v1/login",
{
body: JSON.stringify({ username: username, password: password }),
method: "POST",
headers: { "Content-Type": "application/json" },
}
);
if (response.status == 401) {
setError("Invalid username or password");
setPassword("");
return;
}
const token = await response.json();
document.cookie = `token=${token}`;
redirect("/");
}}
>
<Input
isRequired
errorMessage="Please enter a valid username"
label="Username"
labelPlacement="outside"
name="username"
placeholder="Enter your username"
type="text"
value={username}
onValueChange={setUsername}
/>
<Input
isRequired
errorMessage="Please enter a valid password"
label="Password"
labelPlacement="outside"
name="password"
placeholder="Enter your password"
type="password"
value={password}
onValueChange={setPassword}
/>
<div className="flex gap-2">
<Button color="primary" type="submit">
Submit
</Button>
<Button type="reset" variant="flat">
Reset
</Button>
</div>
<p>Sign up is being worked on currently</p>
{error && <p className="text-red-500">{error}</p>}
{}
</Form>
</div>
);
}

18
src/app/logout/page.tsx Normal file
View file

@ -0,0 +1,18 @@
"use client";
import { redirect } from "next/navigation";
import React, { useEffect } from "react";
export default function UserPage() {
useEffect(() => {
document.cookie = "token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
redirect("/");
});
return (
<div className="absolute flex items-center justify-center top-0 left-0 w-screen h-screen">
<p>Logging out...</p>
</div>
);
}

0
src/app/signup/page.tsx Normal file
View file

View file

@ -1,3 +1,5 @@
"use client";
import {
Navbar as NavbarBase,
NavbarBrand,
@ -6,10 +8,51 @@ import {
} from "@nextui-org/navbar";
import { Link } from "@nextui-org/link";
import { Divider } from "@nextui-org/divider";
import { Image, Tooltip } from "@nextui-org/react";
import {
Avatar,
Button,
Dropdown,
DropdownItem,
DropdownMenu,
DropdownTrigger,
Image,
Tooltip,
} from "@nextui-org/react";
import { SiDiscord, SiForgejo, SiGithub } from "@icons-pack/react-simple-icons";
import { LogInIcon } from "lucide-react";
import { useEffect, useState } from "react";
import { hasCookie, getCookies } from "@/helpers/cookie";
import { usePathname } from "next/navigation";
export default function Navbar() {
const [user, setUser] = useState("");
const pathname = usePathname();
useEffect(() => {
loadUser();
async function loadUser() {
if (!hasCookie()) {
setUser("");
return;
}
const response = await fetch(
process.env.MODE === "PROD"
? "https://d2jam.com/api/v1/self"
: "http://localhost:3005/api/v1/self",
{
headers: { authorization: `Bearer ${getCookies().token}` },
}
);
if ((await response.text()) == "ok") {
setUser("ok");
} else {
setUser("");
}
}
}, [pathname]);
return (
<NavbarBase
shouldHideOnScroll
@ -83,15 +126,63 @@ export default function Navbar() {
</Link>
</NavbarItem>
<Divider orientation="vertical" className="h-1/2" />
{/* <NavbarItem>
<Button
endContent={<LogInIcon />}
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"
>
Log In
</Button>
</NavbarItem> */}
{!user ? (
<div className="flex gap-3 items-center">
<NavbarItem>
<Link href="/login">
<Button
endContent={<LogInIcon />}
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"
>
Log In
</Button>
</Link>
</NavbarItem>
{/* <NavbarItem>
<Link href="/signup">
<Button
endContent={<NotebookPen />}
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"
>
Sign up
</Button>
</Link>
</NavbarItem> */}
</div>
) : (
<Dropdown>
<DropdownTrigger>
<Avatar />
</DropdownTrigger>
<DropdownMenu>
{/* <DropdownItem
key="profile"
className="text-black"
href="/profile"
>
Profile
</DropdownItem>
<DropdownItem
showDivider
key="settings"
className="text-black"
href="/settings"
>
Settings
</DropdownItem> */}
<DropdownItem
key="logout"
color="danger"
className="text-danger"
href="/logout"
>
Logout
</DropdownItem>
</DropdownMenu>
</Dropdown>
)}
</NavbarContent>
</NavbarBase>
);

20
src/helpers/cookie.ts Normal file
View file

@ -0,0 +1,20 @@
export function getCookies() {
const pairs = document.cookie.split(";");
const cookies: Record<string, string> = {};
for (let i = 0; i < pairs.length; i++) {
const pair = pairs[i].split("=");
cookies[(pair[0] + "").trim()] = unescape(pair.slice(1).join("="));
}
return cookies;
}
export function hasCookie() {
const pairs = document.cookie.split(";");
for (let i = 0; i < pairs.length; i++) {
const pair = pairs[i].split("=");
if (pair[0] == "token") {
return true;
}
}
return false;
}