Skip to content

Commit 803cec9

Browse files
committed
middleware setup complete
1 parent dbc2848 commit 803cec9

7 files changed

Lines changed: 216 additions & 73 deletions

File tree

public/images/FingerprintImg.png

222 KB
Loading

src/app/auth/page.tsx

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
//
2+
"use client";
3+
import { useState } from "react";
4+
import { signInWithGoogle } from "@/lib/firebase/auth";
5+
import { useRouter } from "next/navigation";
6+
import Image from "next/image";
7+
import Link from "next/link";
8+
9+
export default function AuthPage() {
10+
const [error, setError] = useState<string | null>(null);
11+
const [loading, setLoading] = useState(false);
12+
const [agreed, setAgreed] = useState(false);
13+
const router = useRouter();
14+
15+
const handleLoginWithGoogle = async () => {
16+
if (!agreed) {
17+
setError("Please agree to the terms and services.");
18+
return;
19+
}
20+
21+
setLoading(true);
22+
setError(null);
23+
try {
24+
const result = await signInWithGoogle();
25+
if (result?.isAdmin) {
26+
router.push("/admin");
27+
} else {
28+
router.push("/");
29+
}
30+
} catch (err) {
31+
console.error("Failed to log in with Google:", err);
32+
setError(err instanceof Error ? err.message : "Failed to log in with Google");
33+
} finally {
34+
setLoading(false);
35+
}
36+
};
37+
38+
return (
39+
<div className="flex flex-col items-center justify-center min-h-screen bg-white dark:bg-black px-6 py-8 relative">
40+
{/* Back Arrow Button */}
41+
<Link
42+
href="/"
43+
className="absolute top-6 left-6 p-2 rounded-full hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors"
44+
aria-label="Go back"
45+
>
46+
<svg
47+
xmlns="http://www.w3.org/2000/svg"
48+
className="w-6 h-6"
49+
viewBox="0 0 24 24"
50+
fill="none"
51+
stroke="currentColor"
52+
strokeWidth="2"
53+
strokeLinecap="round"
54+
strokeLinejoin="round"
55+
>
56+
<line x1="19" y1="12" x2="5" y2="12"></line>
57+
<polyline points="12 19 5 12 12 5"></polyline>
58+
</svg>
59+
</Link>
60+
61+
<div className="w-full max-w-md text-center space-y-6">
62+
{/* Title */}
63+
<h1 className="text-2xl md:text-3xl font-semibold text-black dark:text-white">
64+
Create your account
65+
</h1>
66+
67+
{/* Illustration */}
68+
<div className="flex justify-center">
69+
<Image
70+
src={"/images/FingerprintImg.png"}
71+
alt="Fingerprint illustration"
72+
width={240}
73+
height={240}
74+
className="rounded-xl w-[180px] h-[180px] md:w-[220px] md:h-[220px]"
75+
priority
76+
/>
77+
</div>
78+
79+
{/* Google Login Button */}
80+
<button
81+
onClick={handleLoginWithGoogle}
82+
disabled={loading}
83+
aria-label="Continue with Google"
84+
type="button"
85+
className="flex items-center justify-center w-full py-3 px-6 rounded-full shadow-md
86+
bg-white border border-gray-300 hover:bg-gray-100
87+
transition-all disabled:opacity-60 disabled:cursor-not-allowed"
88+
>
89+
{loading ? (
90+
<span className="text-base font-medium text-gray-600">Logging in...</span>
91+
) : (
92+
<>
93+
<Image
94+
src="https://www.gstatic.com/firebasejs/ui/2.0.0/images/auth/google.svg"
95+
alt="Google logo"
96+
width={24}
97+
height={24}
98+
className="w-5 h-5 md:w-6 md:h-6"
99+
/>
100+
<span className="ml-3 text-base font-medium text-gray-700">
101+
Continue with Google
102+
</span>
103+
</>
104+
)}
105+
</button>
106+
107+
{/* Terms and Services */}
108+
<div className="flex items-center justify-center space-x-3 text-sm md:text-base">
109+
<input
110+
type="checkbox"
111+
id="terms"
112+
checked={agreed}
113+
onChange={() => setAgreed(!agreed)}
114+
className="form-checkbox text-blue-600 w-4 h-4 md:w-5 md:h-5"
115+
/>
116+
<label htmlFor="terms" className="text-gray-600 dark:text-gray-400">
117+
I agree to my <span className="font-semibold underline">terms and services</span>
118+
</label>
119+
</div>
120+
121+
{/* Error Message */}
122+
{error && (
123+
<p className="text-red-500 text-sm md:text-base">
124+
{error}
125+
</p>
126+
)}
127+
</div>
128+
</div>
129+
);
130+
}

src/app/login/page.tsx

Lines changed: 0 additions & 66 deletions
This file was deleted.

src/components/home/ResponsiveDashboard.tsx

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,33 @@
11
"use client";
2-
import TopNavBar from '../topNavbar';
2+
import { useEffect, useState } from "react";
3+
import TopNavBar from "../topNavbar";
34
import { QuickActions } from "./QuickActions";
45
import { TimeTableBlock } from "./TimeTable";
6+
import { onAuthStateChanged } from "@/lib/firebase/auth"; // Update with correct path
7+
import { User } from "firebase/auth";
58

69
const ResponsiveDashboard = () => {
10+
const [userName, setUserName] = useState<string>("");
11+
const [userPhotoURL, setUserPhotoURL] = useState<string | null>(null);
12+
13+
useEffect(() => {
14+
const unsubscribe = onAuthStateChanged((authUser: User | null) => {
15+
if (authUser) {
16+
setUserName(authUser.displayName || "Student");
17+
setUserPhotoURL(authUser.photoURL || null);
18+
} else {
19+
setUserName("Guest");
20+
setUserPhotoURL(null);
21+
}
22+
});
23+
24+
return () => unsubscribe();
25+
}, []);
26+
27+
728
return (
829
<div className="min-h-screen">
9-
<TopNavBar userName="John Doe" semester={4} department="CSE" />
30+
<TopNavBar userName={userName} userPhotoURL={userPhotoURL} />
1031
<div className="mx-auto max-w-[var(--max-screen-size)] pt-4">
1132
<main className="px-3 pb-24">
1233
<div className="md:flex lg:h-[calc(100vh-12rem)] md:gap-6 lg:gap-8">

src/components/topNavbar.tsx

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import { User } from "iconsax-react";
44
import React from "react";
5+
import { useRouter } from "next/navigation";
56

67
function isValidSemester(semester: number) {
78
return semester >= 1 && semester <= 8 && Number.isInteger(semester);
@@ -11,11 +12,16 @@ const Header: React.FC<{
1112
userName?: string | null;
1213
semester?: number | null;
1314
department?: string | null;
14-
}> = ({ userName, semester, department }) => {
15+
userPhotoURL?: string | null;
16+
}> = ({ userName, semester, department, userPhotoURL }) => {
17+
const router = useRouter();
1518
const displaySemester =
1619
semester !== undefined && semester !== null && isValidSemester(semester)
1720
? `S${semester}, `
1821
: "";
22+
const handleUsrProfileClick = () => {
23+
router.push("/login");
24+
};
1925

2026
return (
2127
<div className="flex items-center justify-between px-4 py-7 bg-gradient-to-b from-transparent to-20% to-[var(--main)] shadow-[0_8px_20px_10px_var(--main)]">
@@ -31,15 +37,24 @@ const Header: React.FC<{
3137
</p>
3238
</div>
3339

34-
{/* User Icon */}
40+
{/* User Image or Icon */}
3541
<button
3642
type="button"
37-
className="flex size-10 items-center justify-center rounded-full bg-gray-600 transition-colors hover:bg-purple-300 dark:bg-purple-800 dark:hover:bg-purple-700"
43+
className="flex size-10 items-center justify-center rounded-full overflow-hidden bg-gray-600 transition-colors hover:bg-purple-300 dark:bg-purple-800 dark:hover:bg-purple-700"
3844
title="User Profile"
39-
onClick={() => console.warn("Profile Clicked")} // Changed to warn
4045
aria-label="Open user profile"
46+
onClick={() => handleUsrProfileClick()}
4147
>
42-
<User size="28" color="white" variant="Linear" />
48+
{userPhotoURL ? (
49+
<img
50+
src={userPhotoURL}
51+
alt="User Profile"
52+
className="object-cover w-full h-full"
53+
/>
54+
) : (
55+
<User size="28" color="white" variant="Linear" />
56+
)}
57+
4358
</button>
4459
</div>
4560
</div>

src/middleware.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// middleware.ts
2+
import { NextResponse } from 'next/server'
3+
import type { NextRequest } from 'next/server'
4+
5+
export function middleware(request: NextRequest) {
6+
const { pathname } = request.nextUrl
7+
const token = request.cookies.get('authToken')?.value
8+
9+
// Define public routes that don't need authentication
10+
const publicRoutes = [ '/auth']
11+
12+
// Check if the current path is a public route
13+
const isPublicRoute = publicRoutes.includes(pathname) ||
14+
publicRoutes.some(route => pathname.startsWith(route + '/'))
15+
16+
// If user has no token and trying to access protected route
17+
if (!token && !isPublicRoute) {
18+
console.log(`Redirecting ${pathname} to /auth - no token`)
19+
return NextResponse.redirect(new URL('/auth', request.url))
20+
}
21+
22+
// If user has token and trying to access auth page, redirect to home
23+
if (token && pathname === '/auth') {
24+
console.log(`Redirecting from /auth to / - user has token`)
25+
return NextResponse.redirect(new URL('/', request.url))
26+
}
27+
28+
return NextResponse.next()
29+
}
30+
31+
export const config = {
32+
matcher: [
33+
/*
34+
* Match all request paths except for the ones starting with:
35+
* - api (API routes)
36+
* - _next/static (static files)
37+
* - _next/image (image optimization files)
38+
* - favicon.ico (favicon file)
39+
* - public files (images, etc.)
40+
*/
41+
'/((?!api|_next/static|_next/image|favicon.ico|images|.*\\..*).*)',
42+
],
43+
}

0 commit comments

Comments
 (0)