Skip to content

Commit a1d4e4f

Browse files
author
UnknownCoder404
authored
Merge pull request #90 from UnknownCoder404/better-auth
Use context/state for storing user info locally
2 parents c97fab0 + e478afc commit a1d4e4f

File tree

12 files changed

+177
-112
lines changed

12 files changed

+177
-112
lines changed

pnpm-lock.yaml

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

src/app/Login/LoginPage.tsx

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,30 @@
11
"use client";
2+
23
import styles from "./Login.module.css";
34
import { url } from "@/globals";
45
import { Dispatch, SetStateAction, useState } from "react";
5-
import { isAdmin } from "../utils/credentials";
66
import { Loader } from "../components/Loader/Loader";
77
import { useRouter } from "next/navigation";
88
import { AppRouterInstance } from "next/dist/shared/lib/app-router-context.shared-runtime";
99
import { clsx } from "clsx";
10+
import { useAuth } from "@/app/context/AuthContext"; // Import useAuth
11+
import { Role } from "../utils/credentials";
1012

1113
async function handleSubmit(
1214
username: string,
1315
password: string,
1416
setMsg: Dispatch<SetStateAction<string>>,
1517
router: AppRouterInstance,
18+
login: (
19+
token: string,
20+
username: string,
21+
role: Role,
22+
userId: string,
23+
) => void,
24+
setLoading: (isLoading: boolean) => void,
1625
) {
26+
setLoading(true); // Set loading to true when the submission starts.
27+
1728
try {
1829
const loginUrl = new URL(url);
1930
loginUrl.pathname = "/login";
@@ -51,30 +62,34 @@ async function handleSubmit(
5162
}
5263

5364
const { id, token, username: responseUsername, role } = data.info;
54-
localStorage.setItem("id", id);
55-
localStorage.setItem("token", token);
56-
localStorage.setItem("username", responseUsername);
57-
localStorage.setItem("role", role);
5865

59-
window.dispatchEvent(new Event("storage"));
66+
// Use context's login function instead of localStorage
67+
login(token, responseUsername, role, id);
6068

61-
if (isAdmin(role)) router.push("/Dashboard");
62-
else router.push("/");
69+
// Redirect based on role
70+
setTimeout(
71+
() => router.push(role === "admin" ? "/Dashboard" : "/"),
72+
1000,
73+
);
6374
} catch (error) {
6475
setMsg(
6576
`Greška prilikom prijave: ${
6677
error instanceof Error ? error.message : "Nepoznata greška"
6778
}`,
6879
);
6980
console.error(error);
81+
} finally {
82+
setLoading(false); // Ensure loading is set back to false.
7083
}
7184
}
7285

7386
function ErrorMessage({ message }: { message: string }) {
7487
if (!message) return null;
7588
return (
7689
<div className={styles["message-container"]}>
77-
<p className={styles["message"]}>{message}</p>
90+
<p className={clsx(styles["message"], styles["error"])}>
91+
{message}
92+
</p>
7893
</div>
7994
);
8095
}
@@ -108,6 +123,7 @@ function LoginButton({
108123

109124
function LoginForm() {
110125
const router = useRouter();
126+
const { login } = useAuth(); // Get login function from context
111127
const [message, setMessage] = useState("");
112128
const [isLoading, setLoading] = useState(false);
113129
const [username, setUsername] = useState("");
@@ -118,13 +134,15 @@ function LoginForm() {
118134

119135
const handleFormSubmit = async (event: React.FormEvent) => {
120136
event.preventDefault();
121-
setMessage("");
122-
setLoading(true);
123-
try {
124-
await handleSubmit(username, password, setMessage, router);
125-
} finally {
126-
setLoading(false);
127-
}
137+
setMessage(""); // Clear previous message on new submission attempt.
138+
await handleSubmit(
139+
username,
140+
password,
141+
setMessage,
142+
router,
143+
login,
144+
setLoading,
145+
);
128146
};
129147

130148
return (

src/app/Login/page.tsx

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { Metadata } from "next";
22
import LoginPage from "./LoginPage";
3-
import ProtectedRoute from "../components/Common/ProtectedRoute";
43

54
export const dynamic = "error";
65

@@ -11,9 +10,5 @@ export const metadata: Metadata = {
1110
};
1211

1312
export default function Login() {
14-
return (
15-
<ProtectedRoute require="loggedout" redirectTo="/" validateToken>
16-
<LoginPage />
17-
</ProtectedRoute>
18-
);
13+
return <LoginPage />;
1914
}

src/app/Types/solve.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { Role } from "../utils/credentials";
2+
13
export type AllowedEvents = "3x3" | "4x4" | "3x3oh" | "2x2";
24

35
export type UserEvent = {
@@ -10,8 +12,6 @@ export type UserComp = {
1012
events: UserEvent[];
1113
};
1214

13-
export type Role = "admin" | "user";
14-
1515
export type User = {
1616
_id: string;
1717
username: string;
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
"use client";
22
import { VercelToolbar } from "@vercel/toolbar/next";
3-
import { getRole, isAdmin } from "../utils/credentials";
3+
import { isAdmin } from "../utils/credentials";
4+
import { useAuth } from "../context/AuthContext";
45

56
// Show the Vercel toolbar only for admin users
67
export function AdminToolbar() {
7-
const role = getRole();
8+
const { role } = useAuth();
89
const isUserAdmin = role && isAdmin(role);
910
return isUserAdmin ? <VercelToolbar /> : null;
1011
}

src/app/components/Common/ProtectedRoute.tsx

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,9 @@
11
"use client";
22

3-
import {
4-
getRole,
5-
isAdmin,
6-
loggedIn,
7-
logOut,
8-
tokenValid,
9-
} from "@/app/utils/credentials";
103
import { useRouter } from "next/navigation";
114
import { useEffect, useState } from "react";
5+
import { useAuth } from "@/app/context/AuthContext";
6+
import { tokenValid } from "@/app/utils/credentials";
127

138
type Props = {
149
redirectTo?: string; // Optional redirection URL
@@ -25,25 +20,25 @@ export default function ProtectedRoute({
2520
}: Props) {
2621
const router = useRouter();
2722
const [isAuthorized, setIsAuthorized] = useState<boolean | null>(null); // null indicates "loading"
28-
const isLoggedIn = loggedIn();
23+
const { role, token, logOut } = useAuth();
24+
const isLoggedIn = !!token; //Much better way to check if logged in
2925

3026
useEffect(() => {
3127
const validateAccess = async () => {
3228
try {
33-
const role = getRole();
34-
3529
// If token needs validation, check if it's valid
3630
if (validateToken) {
37-
const tokenIsValid = await tokenValid();
31+
const isTokenValid = await tokenValid();
3832
// If token is invalid and user does not need to be logged out, redirect
39-
if (!tokenIsValid && require !== "loggedout") {
33+
if (!isTokenValid && require !== "loggedout") {
34+
logOut(); // Log out so the old token is not valid anymore
4035
handleRedirect();
4136
return;
4237
}
4338
// If user has to be logged out, and user is logged in, log them out
4439
if (
4540
require === "loggedout" &&
46-
!tokenIsValid &&
41+
!isTokenValid &&
4742
isLoggedIn
4843
) {
4944
// User has invalid token, but is logged in locally
@@ -55,8 +50,8 @@ export default function ProtectedRoute({
5550
// Check for other cases
5651
if (
5752
(require === "loggedin" && !isLoggedIn) ||
58-
(require === "admin" && (!role || !isAdmin(role))) ||
59-
(require === "loggedout" && role)
53+
(require === "admin" && role !== "admin") || // Simplified admin check
54+
(require === "loggedout" && isLoggedIn) // Simplified loggedout
6055
) {
6156
handleRedirect();
6257
return;
@@ -78,10 +73,14 @@ export default function ProtectedRoute({
7873
};
7974

8075
validateAccess();
81-
}, [require, validateToken, redirectTo, router, isLoggedIn]);
76+
}, [require, validateToken, redirectTo, router, isLoggedIn, role, logOut]);
77+
78+
if (isAuthorized === null) {
79+
return null; // Hide content while loading
80+
}
8281

8382
if (!isAuthorized) {
84-
return null; // Hide content for unauthorized users or loading state
83+
return null; // Hide content for unauthorized users.
8584
}
8685

8786
return <>{children}</>; // Render protected content

src/app/components/Dashboard/UserButtons.tsx

Lines changed: 12 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,23 @@
11
import { clsx } from "clsx";
2-
import { getId, isAdmin, Role } from "@/app/utils/credentials";
2+
import { isAdmin, Role } from "@/app/utils/credentials";
33
import dashboardStyles from "@/app/Dashboard/Dashboard.module.css";
44
import { assignAdminToUser, deleteUserById } from "@/app/utils/users";
55
import { useRouter } from "next/navigation";
6-
import { AppRouterInstance } from "next/dist/shared/lib/app-router-context.shared-runtime";
76
import { User } from "@/app/Types/solve";
7+
import { useAuth } from "@/app/context/AuthContext";
8+
9+
function DeleteUserButton({ id }: { id: string }) {
10+
const { userId } = useAuth();
11+
const router = useRouter();
812

9-
function DeleteUserButton({
10-
id,
11-
router,
12-
}: {
13-
id: string;
14-
router: AppRouterInstance;
15-
}) {
1613
return (
1714
<button
1815
className={clsx(
1916
dashboardStyles["user-btn"],
2017
dashboardStyles["remove-btn"],
2118
)}
2219
onClick={async () => {
23-
if (id === getId()) {
20+
if (id === userId) {
2421
return alert("Ne možete izbrisati vlastiti računa.");
2522
}
2623
const userDeletion = await deleteUserById({ id });
@@ -38,15 +35,9 @@ function DeleteUserButton({
3835
);
3936
}
4037

41-
function AdminButton({
42-
role,
43-
id,
44-
router,
45-
}: {
46-
role: Role;
47-
id: string;
48-
router: AppRouterInstance;
49-
}) {
38+
function AdminButton({ role, id }: { role: Role; id: string }) {
39+
const router = useRouter();
40+
5041
return (
5142
<button
5243
className={clsx(dashboardStyles["user-btn"], {
@@ -93,11 +84,10 @@ type Props = {
9384
};
9485

9586
export default function UserButtons({ user, toggleCompVisibility }: Props) {
96-
const router = useRouter();
9787
return (
9888
<div className={dashboardStyles["user-btns"]}>
99-
<DeleteUserButton id={user._id} router={router} />
100-
<AdminButton role={user.role} id={user._id} router={router} />
89+
<DeleteUserButton id={user._id} />
90+
<AdminButton role={user.role} id={user._id} />
10191
<CompButton toggleCompVisibility={toggleCompVisibility} />
10292
</div>
10393
);

src/app/components/Header/ClientLoginStatus.tsx

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,36 +2,16 @@
22

33
import Link from "next/link";
44
import headerStyles from "./Header.module.css";
5-
import { getUsername, logOut } from "@/app/utils/credentials";
6-
import { useEffect, useState } from "react";
5+
import { useAuth } from "@/app/context/AuthContext";
76
import { useRouter } from "next/navigation";
87
import { clsx } from "clsx";
98
import AccountCircleSvg from "../Svg/account_circle";
109

1110
function ClientLoginStatus() {
1211
const router = useRouter();
13-
const [username, setUsername] = useState<string | null>(null);
14-
const [loaded, setLoaded] = useState<boolean>(false);
12+
const { username, logOut } = useAuth(); // Use useAuth hook
1513
const loggedIn = !!username;
1614

17-
useEffect(() => {
18-
const syncUsername = () => {
19-
setUsername(getUsername());
20-
};
21-
22-
setUsername(getUsername());
23-
setLoaded(true);
24-
25-
window.addEventListener("storage", syncUsername); // Listen for changes in storage
26-
return () => {
27-
window.removeEventListener("storage", syncUsername);
28-
};
29-
}, []);
30-
31-
if (!loaded) {
32-
return <div className={headerStyles["account-container"]}></div>;
33-
}
34-
3515
return (
3616
<header className={headerStyles["account-container"]}>
3717
<h2 className={headerStyles["log-in"]}>
@@ -47,7 +27,6 @@ function ClientLoginStatus() {
4727
onClick={() => {
4828
if (loggedIn) {
4929
logOut();
50-
setUsername(null);
5130
router.refresh();
5231
}
5332
}}

0 commit comments

Comments
 (0)