Skip to content

Commit 97552fe

Browse files
committed
implemented authentication and worked on messaging
1 parent fcfa6bc commit 97552fe

60 files changed

Lines changed: 1111 additions & 91 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

client/package-lock.json

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

client/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,9 @@
2323
"class-variance-authority": "^0.7.1",
2424
"clsx": "^2.1.1",
2525
"is-inside-container": "^1.0.0",
26+
"jwt-decode": "^4.0.0",
2627
"lucide-react": "^0.516.0",
27-
"next": "15.4.7",
28+
"next": "^15.5.9",
2829
"react": "19.1.0",
2930
"react-dom": "19.1.0",
3031
"tailwind-merge": "^3.3.1",

client/src/components/Layout.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import Navbar from "@/components/ui/Navbar";
2+
3+
type LayoutProps = { children: React.ReactNode };
4+
5+
export default function Layout({ children }: LayoutProps) {
6+
return (
7+
<div className="flex min-h-screen flex-col">
8+
<Navbar />
9+
<main className="flex-1 p-4">{children}</main>
10+
<footer className="bg-gray-200 p-4 text-center">© 2026 My App</footer>
11+
</div>
12+
);
13+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import Link from "next/link";
2+
3+
import { useAuth } from "@/context/AuthContext";
4+
5+
const Navbar = () => {
6+
const { isLoggedIn, logout } = useAuth();
7+
8+
return (
9+
<nav className="flex justify-between bg-gray-200 p-4">
10+
<div className="flex gap-4">
11+
<Link href="/">Home</Link>
12+
<Link href="/chat">Chat</Link>
13+
</div>
14+
15+
<div className="flex gap-4">
16+
{isLoggedIn ? (
17+
<>
18+
<button onClick={logout} className="text-red-500">
19+
Logout
20+
</button>
21+
</>
22+
) : (
23+
<>
24+
<Link href="/login">Login</Link>
25+
<Link href="/signup">Signup</Link>
26+
</>
27+
)}
28+
</div>
29+
</nav>
30+
);
31+
};
32+
33+
export default Navbar;

client/src/context/AuthContext.tsx

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { useRouter } from "next/router";
2+
import {
3+
createContext,
4+
ReactNode,
5+
useContext,
6+
useEffect,
7+
useState,
8+
} from "react";
9+
10+
type AuthContextType = {
11+
accessToken: string | null;
12+
refreshToken: string | null;
13+
login: (access: string, refresh: string) => void;
14+
logout: () => void;
15+
isLoggedIn: boolean;
16+
};
17+
18+
const AuthContext = createContext<AuthContextType | undefined>(undefined);
19+
20+
export const AuthProvider = ({ children }: { children: ReactNode }) => {
21+
const [accessToken, setAccessToken] = useState<string | null>(null);
22+
const [refreshToken, setRefreshToken] = useState<string | null>(null);
23+
const router = useRouter();
24+
25+
useEffect(() => {
26+
const storedAccess = localStorage.getItem("accessToken");
27+
const storedRefresh = localStorage.getItem("refreshToken");
28+
if (storedAccess) setAccessToken(storedAccess);
29+
if (storedRefresh) setRefreshToken(storedRefresh);
30+
}, []);
31+
32+
const login = (access: string, refresh: string) => {
33+
setAccessToken(access);
34+
setRefreshToken(refresh);
35+
localStorage.setItem("accessToken", access);
36+
localStorage.setItem("refreshToken", refresh);
37+
};
38+
39+
const logout = () => {
40+
setAccessToken(null);
41+
setRefreshToken(null);
42+
localStorage.removeItem("accessToken");
43+
localStorage.removeItem("refreshToken");
44+
router.push("/login");
45+
};
46+
47+
const isLoggedIn = !!accessToken;
48+
49+
return (
50+
<AuthContext.Provider
51+
value={{ accessToken, refreshToken, login, logout, isLoggedIn }}
52+
>
53+
{children}
54+
</AuthContext.Provider>
55+
);
56+
};
57+
58+
export const useAuth = () => {
59+
const context = useContext(AuthContext);
60+
if (!context) throw new Error("useAuth must be used within AuthProvider");
61+
return context;
62+
};

client/src/pages/_app.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,17 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
44
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
55
import type { AppProps } from "next/app";
66

7+
import { AuthProvider } from "@/context/AuthContext";
8+
79
const queryClient = new QueryClient();
810

911
export default function App({ Component, pageProps }: AppProps) {
1012
return (
11-
<QueryClientProvider client={queryClient}>
12-
<ReactQueryDevtools initialIsOpen={false} />
13-
<Component {...pageProps} />
14-
</QueryClientProvider>
13+
<AuthProvider>
14+
<QueryClientProvider client={queryClient}>
15+
<ReactQueryDevtools initialIsOpen={false} />
16+
<Component {...pageProps} />
17+
</QueryClientProvider>
18+
</AuthProvider>
1519
);
1620
}

0 commit comments

Comments
 (0)