Files
pragativeer-v2/src/app/login/page.tsx

147 lines
6.3 KiB
TypeScript

"use client"
import { fetchWithAuth } from "@/lib/apiClient";
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { ArrowLeft, Eye, EyeOff } from "lucide-react"
import Link from "next/link"
import { useState } from "react"
import { useRouter } from "next/navigation"
import { Logo } from "@/components/Logo"
export default function LoginPage() {
const [showPassword, setShowPassword] = useState(false)
const [email, setEmail] = useState("")
const [password, setPassword] = useState("")
const [error, setError] = useState("")
const router = useRouter()
const handleLogin = async (e: React.FormEvent) => {
e.preventDefault()
setError("")
try {
const res = await fetch("/api/auth/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email, password })
})
const data = await res.json()
if (data.success) {
if (typeof window !== "undefined") {
// Set auth_token cookie required by Next.js 16 proxy.ts middleware
const cookieVal = data.role === "admin" ? "admin" : (data.token || "authenticated");
document.cookie = `auth_token=${cookieVal}; path=/; max-age=2592000; SameSite=Lax`;
localStorage.setItem("activeUserId", data.user_id || data.token);
// Store the JWT as the active session token
const sessionToken = data.token;
localStorage.setItem("active_session_token", sessionToken);
if (data.token && data.token !== "admin") {
try {
await fetchWithAuth(`/api/users/${data.user_id}/session`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ session_token: sessionToken })
});
} catch (e) {
console.warn("Session update skipped", e);
}
}
if (data.role === "admin") {
window.location.href = "/admin";
} else {
window.location.href = "/dashboard";
}
}
} else {
setError(data.error || "Invalid email or password.")
}
} catch (err) {
setError("An error occurred. Please try again.")
}
}
return (
<div className="min-h-screen bg-background flex flex-col items-center justify-center p-6 selection:bg-[var(--cyan-ai-20)]">
<Link href="/" className="absolute top-8 left-8 text-muted-foreground hover:text-foreground flex items-center gap-2 transition-colors font-medium">
<ArrowLeft size={16} /> Back to Home
</Link>
<div className="w-full max-w-md space-y-8 animate-in fade-in slide-in-from-bottom-8 duration-700">
<div className="flex justify-center mb-8">
<Logo href="/" className="w-72" imageClassName="object-contain" height={80} />
</div>
<Card className="border-border bg-secondary/10 backdrop-blur-xl shadow-2xl shadow-[var(--cyan-ai-10)] rounded-[2rem] overflow-hidden">
<CardHeader className="text-center pb-2">
<CardTitle className="text-2xl font-bold tracking-tight">Welcome back</CardTitle>
<CardDescription className="text-muted-foreground">Enter your credentials to access your dashboard.</CardDescription>
</CardHeader>
<CardContent className="space-y-6 pt-4">
<form onSubmit={handleLogin} className="space-y-6">
<div className="space-y-4">
<div className="space-y-2">
<label className="text-sm font-bold text-foreground">Email</label>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="student@example.com"
suppressHydrationWarning
className="w-full bg-background border border-border rounded-xl px-4 py-3 text-sm focus:outline-none focus:ring-2 focus:ring-[var(--cyan-ai)] focus:border-transparent transition-all"
/>
</div>
<div className="space-y-2">
<div className="flex items-center justify-between">
<label className="text-sm font-bold text-foreground">
Password
</label>
<Link href="/forgot-password" className="text-xs font-bold text-[var(--cyan-ai)] hover:text-[var(--cyan-ai-80)] transition-colors">Forgot password?</Link>
</div>
<div className="relative">
<input
type={showPassword ? "text" : "password"}
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="••••••••"
suppressHydrationWarning
className="w-full bg-background border border-border rounded-xl px-4 py-3 text-sm focus:outline-none focus:ring-2 focus:ring-[var(--cyan-ai)] focus:border-transparent transition-all pr-10"
/>
<button
type="button"
onClick={() => setShowPassword(!showPassword)}
className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground transition-colors"
>
{showPassword ? <EyeOff size={16} /> : <Eye size={16} />}
</button>
</div>
</div>
</div>
{error && (
<div className="p-3 bg-red-500/10 border border-red-500/20 rounded-xl text-red-500 text-sm font-medium text-center">
{error}
</div>
)}
<Button type="submit" className="w-full rounded-xl h-12 font-bold text-base bg-foreground text-background hover:bg-foreground/90 transition-all shadow-[0_0_15px_-5px_var(--cyan-ai)]">
Sign In
</Button>
</form>
</CardContent>
</Card>
<p className="text-center text-sm text-muted-foreground font-medium">
Don't have an account? <Link href="/signup" className="font-bold text-foreground hover:text-primary transition-colors">Sign up</Link>
</p>
</div>
</div>
)
}