Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";

import BottomNavBar from "@/components/bottomNavbar";
const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
Expand All @@ -28,6 +28,9 @@ export default function RootLayout({
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
{children}

<BottomNavBar />

</body>
</html>
);
Expand Down
180 changes: 145 additions & 35 deletions app/login/page.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,148 @@
"use client";

import { useState } from 'react';
import { signInWithGoogle } from '@/lib/firebase/auth';

export default function LoginPage() {
const [loading, setLoading] = useState(false);
const [error, setError] = useState('');

const handleLoginWithGoogle = async () => {
setLoading(true);
setError('');
try {
await signInWithGoogle();
} catch (err: any) {
if (err.code === 'auth/cancelled-popup-request' || err.code === 'auth/popup-closed-by-user') {
setError('Login was cancelled. Please try again.');
} else {
setError('Login failed. Please try again.');
}
} finally {
setLoading(false);
}
};

return (
<div className="flex flex-col items-center justify-center min-h-screen">
<button
onClick={handleLoginWithGoogle}
disabled={loading}
className="bg-blue-600 text-white px-6 py-2 rounded hover:bg-blue-700 disabled:opacity-50"
>
{loading ? 'Logging in...' : 'Login with Google'}
</button>
{error && <div className="text-red-600 mt-4">{error}</div>}
import { useState } from "react";
import { signInWithGoogle } from "@/lib/firebase/auth";
import { useRouter } from "next/navigation";

export default function AuthPage() {
const [error, setError] = useState<string | null>(null);
const [loading, setLoading] = useState(false);
const router = useRouter();

const handleLoginWithGoogle = async () => {
setLoading(true);
setError(null);
try {
const result = await signInWithGoogle();
const user = result?.user;

if (user) {
const idToken = await user.getIdToken();
document.cookie = `__session=${idToken}; path=/; `;
console.log("User logged in:", user);
console.log("token stored in cookie:", idToken);
}
if (result?.isAdmin) {
router.push("/admin");
} else {
router.push("/");
}
} catch (err) {
console.error("Failed to log in with Google:", err);
setError(
err instanceof Error ? err.message : "Failed to log in with Google"
);
} finally {
setLoading(false);
}
};

return (
<div className="min-h-screen bg-gradient-to-br from-blue-50 via-white to-blue-100 flex items-center justify-center p-4 relative overflow-hidden">
{/* Subtle background decorations */}
<div className="absolute top-20 left-10 w-32 h-32 bg-blue-200 rounded-full opacity-10 blur-3xl"></div>
<div className="absolute bottom-20 right-10 w-40 h-40 bg-blue-300 rounded-full opacity-15 blur-3xl"></div>
<div className="absolute top-1/2 left-1/4 w-24 h-24 bg-blue-400 rounded-full opacity-10 blur-2xl"></div>

<div className="relative z-10 w-full max-w-md">
{/* Main card */}
<div className="bg-white/80 backdrop-blur-sm rounded-3xl shadow-xl border border-blue-100 p-8 transform hover:scale-105 transition-all duration-300">
{/* Header */}
<div className="text-center mb-8">
<div className="w-16 h-16 bg-gradient-to-br from-blue-500 to-blue-700 rounded-2xl flex items-center justify-center mx-auto mb-4 transform rotate-12">
<svg
className="w-8 h-8 text-white"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"
/>
</svg>
</div>
<h2 className="text-3xl font-bold bg-gradient-to-r from-blue-600 to-blue-800 bg-clip-text text-transparent">
Welcome
</h2>
<p className="text-blue-600 mt-2 font-medium">
Sign in to continue your journey
</p>
</div>

{/* Google Login Button */}
<button
onClick={handleLoginWithGoogle}
disabled={loading}
aria-label="Login with Google"
type="button"
className="group relative w-full p-2 bg-white border-2 border-blue-200 rounded-2xl shadow-lg hover:shadow-xl transform hover:scale-105 transition-all duration-300 focus:outline-none focus:ring-4 focus:ring-blue-300 focus:ring-opacity-50 disabled:opacity-50 disabled:cursor-not-allowed disabled:transform-none"
>
<div className="flex items-center justify-center space-x-3">
{loading ? (
<>
<div className="w-6 h-6 border-2 border-blue-300 border-t-blue-600 rounded-full animate-spin"></div>
<span className="text-lg font-semibold text-blue-700">
Signing you in...
</span>
</>
) : (
<>
<svg
xmlns="http://www.w3.org/2000/svg"
x="0px"
y="0px"
width="30"
height="30"
viewBox="0 0 48 48"
>
<path
fill="#fbc02d"
d="M43.611,20.083H42V20H24v8h11.303c-1.649,4.657-6.08,8-11.303,8c-6.627,0-12-5.373-12-12 s5.373-12,12-12c3.059,0,5.842,1.154,7.961,3.039l5.657-5.657C34.046,6.053,29.268,4,24,4C12.955,4,4,12.955,4,24s8.955,20,20,20 s20-8.955,20-20C44,22.659,43.862,21.35,43.611,20.083z"
></path>
<path
fill="#e53935"
d="M6.306,14.691l6.571,4.819C14.655,15.108,18.961,12,24,12c3.059,0,5.842,1.154,7.961,3.039 l5.657-5.657C34.046,6.053,29.268,4,24,4C16.318,4,9.656,8.337,6.306,14.691z"
></path>
<path
fill="#4caf50"
d="M24,44c5.166,0,9.86-1.977,13.409-5.192l-6.19-5.238C29.211,35.091,26.715,36,24,36 c-5.202,0-9.619-3.317-11.283-7.946l-6.522,5.025C9.505,39.556,16.227,44,24,44z"
></path>
<path
fill="#1565c0"
d="M43.611,20.083L43.595,20L42,20H24v8h11.303c-0.792,2.237-2.231,4.166-4.087,5.571 c0.001-0.001,0.002-0.001,0.003-0.002l6.19,5.238C36.971,39.205,44,34,44,24C44,22.659,43.862,21.35,43.611,20.083z"
></path>
</svg>

<span className="text-lg font-semibold text-gray-700 group-hover:text-blue-700 transition-colors">
Continue with Google
</span>
</>
)}
</div>

{/* Hover effect overlay */}
<div className="absolute inset-0 bg-gradient-to-r from-blue-50 to-blue-100 rounded-2xl opacity-0 group-hover:opacity-100 transition-opacity duration-300 -z-10"></div>
</button>

{/* Error message */}
{error && (
<div className="mt-6 p-4 bg-red-50 border border-red-200 rounded-xl">
<p className="text-red-600 text-sm text-center font-medium">
{error}
</p>
</div>
)}
</div>
);
}

{/* Footer text */}
<p className="text-center text-blue-500 text-sm mt-6 font-medium">
Secure authentication powered by Google
</p>

</div>
</div>
);
}
2 changes: 1 addition & 1 deletion app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export default function Home() {
<div className="flex flex-col md:flex-row p-4 md:p-0">
<FilterSection onFilterSubmit={setFilters} onClearFilters={handleClearFilters} />
<div className="flex-1 max-w-7xl px-4 py-6 space-y-8 w-full">
<TabSection activeTab={activeTab} onTabChange={setActiveTab} />
{/* <TabSection activeTab={activeTab} onTabChange={setActiveTab} /> */}
<HostelGrid activeTab={activeTab} filters={filters} refreshTrigger={refreshTrigger} />
</div>
</div>
Expand Down
15 changes: 13 additions & 2 deletions components/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Link from "next/link";
import { onAuthStateChanged } from "@/lib/firebase/auth";
import { signOut, getAuth, User } from "firebase/auth"; // Adjust the import path based on your Firebase setup
import { useRouter } from "next/navigation";
import { ArrowLeftIcon} from "lucide-react";

const Navbar = () => {
const [user, setUser] = useState<User | null>(null);
Expand All @@ -31,9 +32,17 @@ const Navbar = () => {
return (
<nav className="bg-white relative z-50 border-b-2 border-black px-4">
<div className="max-w-screen-xl flex items-center justify-between mx-auto py-5">
{/* Logo Section */}
<div className="flex justify-start items-center mb-6">
<button
className="border text-black border-gray-400 rounded-full p-2"
onClick={() => router.push("https://gecian-hub.netlify.app/")}
>
<ArrowLeftIcon className="w-5 h-5" />
</button>
</div>

<div className="flex flex-row font-poppins font-bold text-[26px] z-10 items-center relative text-black">
Project Archive
NearBy Hostel
</div>

{/* Authentication Button */}
Expand All @@ -57,3 +66,5 @@ const Navbar = () => {
};

export default Navbar;


52 changes: 52 additions & 0 deletions components/bottomNavbar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
"use client";

import Link from "next/link";
import { useState } from "react";
import { bottomTabs } from "@/data/nav";

const BottomNavBar = () => {
const [activeTab, setActiveTab] = useState("home");
const [hoveredTab, setHoveredTab] = useState<string | null>(null);

return (
<nav className="fixed left-6 right-6 bottom-8 border-t border-gray-300 bg-[#17c6fa] max-w-[800px] mx-auto text-[#171717] rounded-2xl">
<ul className="flex items-center justify-around py-2">
{bottomTabs.map(({ name, icon: Icon, href, label }) => (
<li key={name} className="flex flex-col items-center">
<button
type="button"
className="focus:outline-none"
onClick={() => setActiveTab(name)}
onMouseEnter={() => setHoveredTab(name)}
onMouseLeave={() => setHoveredTab(null)}
>
<Link href={href}>
<div
className={`${
activeTab === name
? "bg-black w-full flex text-blue-400 text-center items-center gap-2 p-[4px] px-2 font-bold rounded-xl transition-all duration-300"
: "overflow-hidden transition-all duration-300 hover:bg-black hover:text-blue-400 text-center items-center gap-2 p-[4px] px-2 font-bold rounded-xl"
}`}
>
<Icon
color={
activeTab === name || hoveredTab === name
? "#17c6fa"
: "black"
}
size="32"
/>
<span className="hidden md:inline">
{activeTab === name ? label : ""}
</span>
</div>
</Link>
</button>
</li>
))}
</ul>
</nav>
);
};

export default BottomNavBar;
2 changes: 1 addition & 1 deletion components/loadingScrenn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const LoadingScreen = () => {

return (
<div className="fixed inset-0 flex items-center justify-center bg-gray-100">
<h1 className="text-black text-7xl font-bold">Project Archive</h1>
<h1 className="text-black text-7xl font-bold">Nearby Hostel</h1>
</div>
);
};
Expand Down
9 changes: 9 additions & 0 deletions data/nav.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Book, Calendar2, Calendar, Home2, Money } from "iconsax-react";

export const bottomTabs = [
{ name: "home", icon: Home2, href: "https://gecian-hub.netlify.app/", label: "Home" },
{ name: "studymaterial", icon: Book, href: "https://www.ktunotes.in/", label: "Study" },
{ name: "attendance", icon: Calendar2, href: "https://gecian-hub.netlify.app/attendance/calendar", label: "Attendance" },
{ name: "finance", icon: Money, href: "https://gecian-hub.netlify.app/expense", label: "Finance" },
{ name: "event", icon: Calendar, href: "https://gecian-hub.netlify.app/events", label: "Event" },
];
15 changes: 4 additions & 11 deletions lib/firebase/auth.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Import User and other necessary types/functions from Firebase Auth
import { signInWithPopup, GoogleAuthProvider, User, UserCredential } from 'firebase/auth';
import { signInWithPopup, GoogleAuthProvider, User, UserCredential , getAuth } from 'firebase/auth';
import { doc, getDoc } from 'firebase/firestore';
import { auth, firestore } from './config'; // Ensure you are correctly importing from your Firebase config

Expand All @@ -9,7 +9,7 @@ export function onAuthStateChanged(callback: (authUser: User | null) => void) {
}

// Function for Google sign-in and role check
export async function signInWithGoogle(): Promise<{ isAdmin: boolean }> {
export async function signInWithGoogle(): Promise<{ user:User; isAdmin: boolean }> {
const provider = new GoogleAuthProvider();
provider.setCustomParameters({ display: "popup" }); // Force popup

Expand All @@ -22,14 +22,7 @@ export async function signInWithGoogle(): Promise<{ isAdmin: boolean }> {
throw new Error('Google sign-in failed');
}

// Restrict login to only emails from "gecskp.ac.in"
// Restrict login to only emails from "gecskp.ac.in", except for a specific admin email
const allowedEmailPattern = /^[a-zA-Z0-9]+@gecskp\.ac\.in$/;
const adminOverrideEmail = "codecompass2024@gmail.com";

if (user.email !== adminOverrideEmail && !allowedEmailPattern.test(user.email)) {
throw new Error('Only GEC SKP emails are allowed');
}



Expand All @@ -38,7 +31,7 @@ if (user.email !== adminOverrideEmail && !allowedEmailPattern.test(user.email))

const isAdmin = userDoc.exists() && userDoc.data()?.role === 'admin';

return { isAdmin };
return {user, isAdmin };
} catch (error) {
console.error('Error signing in with Google:', error);
throw error;
Expand All @@ -54,4 +47,4 @@ export async function signOutWithGoogle(): Promise<void> {
console.error('Error signing out with Google:', error);
throw error;
}
}
}
Loading