frontend
This commit is contained in:
143
src/pages/LoginPage.jsx
Normal file
143
src/pages/LoginPage.jsx
Normal file
@@ -0,0 +1,143 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useAuth } from '../context/AuthContext';
|
||||
import { ShieldCheck, UserCheck, Lock, User, ArrowRight, Sparkles } from 'lucide-react';
|
||||
|
||||
export function LoginPage() {
|
||||
const navigate = useNavigate();
|
||||
const { login } = useAuth();
|
||||
|
||||
const [selectedRole, setSelectedRole] = useState('admin');
|
||||
const [username, setUsername] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const [error, setError] = useState('');
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const handleRoleSelect = (role) => {
|
||||
setSelectedRole(role);
|
||||
setError('');
|
||||
};
|
||||
|
||||
const handleFormSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
setError('');
|
||||
if (!username.trim() || !password.trim()) {
|
||||
setError('Please enter both username and password.');
|
||||
return;
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
|
||||
try {
|
||||
const user = await login(username, password, selectedRole);
|
||||
setLoading(false);
|
||||
if (user.role === 'admin') navigate('/admin');
|
||||
else navigate('/user');
|
||||
} catch (err) {
|
||||
setLoading(false);
|
||||
setError(`Login failed. Invalid username or password for ${selectedRole.toUpperCase()} account.`);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen w-full bg-slate-950 text-slate-100 flex items-center justify-center p-4 relative overflow-hidden font-sans">
|
||||
{/* Background Glow Accents */}
|
||||
<div className="absolute -top-40 -left-40 h-96 w-96 rounded-full bg-amber-500/10 blur-3xl pointer-events-none" />
|
||||
<div className="absolute -bottom-40 -right-40 h-96 w-96 rounded-full bg-emerald-500/10 blur-3xl pointer-events-none" />
|
||||
|
||||
<div className="w-full max-w-md relative z-10">
|
||||
{/* Header Branding */}
|
||||
<div className="text-center mb-8">
|
||||
<div className="inline-flex items-center gap-2 px-3.5 py-1 rounded-full bg-amber-500/10 border border-amber-500/30 text-amber-400 text-xs font-semibold mb-3">
|
||||
<Sparkles className="h-3.5 w-3.5" /> Brand Catalog & Store Intelligence
|
||||
</div>
|
||||
<h1 className="text-3xl font-bold tracking-tight text-white font-display">System Authentication</h1>
|
||||
<p className="text-sm text-slate-400 mt-1">Please enter your username and password to sign in</p>
|
||||
</div>
|
||||
|
||||
{/* Main Card */}
|
||||
<div className="rounded-2xl border border-slate-800 bg-slate-900/80 p-6 backdrop-blur-xl shadow-2xl">
|
||||
{/* Role Selection Tabs: Admin and User */}
|
||||
<div className="grid grid-cols-2 gap-2 p-1.5 rounded-xl bg-slate-950/80 border border-slate-800/80 mb-6">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleRoleSelect('admin')}
|
||||
className={`flex items-center justify-center gap-2 py-3 px-4 rounded-lg text-xs font-bold transition-all ${
|
||||
selectedRole === 'admin'
|
||||
? 'bg-amber-500 text-slate-950 shadow-md scale-[1.02]'
|
||||
: 'text-slate-400 hover:text-white hover:bg-slate-800/50'
|
||||
}`}
|
||||
>
|
||||
<ShieldCheck className="h-4 w-4" /> Admin Account
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleRoleSelect('user')}
|
||||
className={`flex items-center justify-center gap-2 py-3 px-4 rounded-lg text-xs font-bold transition-all ${
|
||||
selectedRole === 'user'
|
||||
? 'bg-blue-500 text-white shadow-md scale-[1.02]'
|
||||
: 'text-slate-400 hover:text-white hover:bg-slate-800/50'
|
||||
}`}
|
||||
>
|
||||
<UserCheck className="h-4 w-4" /> User Account
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<div className="mb-4 rounded-lg bg-rose-500/10 border border-rose-500/30 p-3 text-xs text-rose-400">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Login Form */}
|
||||
<form onSubmit={handleFormSubmit} className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-xs font-medium text-slate-300 mb-1.5">Username</label>
|
||||
<div className="relative">
|
||||
<User className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-slate-500" />
|
||||
<input
|
||||
type="text"
|
||||
required
|
||||
value={username}
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
placeholder={`Enter ${selectedRole} username`}
|
||||
className="w-full rounded-xl border border-slate-700 bg-slate-950/80 py-2.5 pl-9 pr-4 text-sm text-white placeholder-slate-500 focus:border-amber-500 focus:outline-none focus:ring-1 focus:ring-amber-500"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-xs font-medium text-slate-300 mb-1.5">Password</label>
|
||||
<div className="relative">
|
||||
<Lock className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-slate-500" />
|
||||
<input
|
||||
type="password"
|
||||
required
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
placeholder="Enter password"
|
||||
className="w-full rounded-xl border border-slate-700 bg-slate-950/80 py-2.5 pl-9 pr-4 text-sm text-white placeholder-slate-500 focus:border-amber-500 focus:outline-none focus:ring-1 focus:ring-amber-500"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
className="w-full flex items-center justify-center gap-2 rounded-xl bg-gradient-to-r from-amber-500 to-amber-600 hover:from-amber-600 hover:to-amber-700 text-slate-950 py-3 text-sm font-bold shadow-lg shadow-amber-500/20 transition-all duration-200 active:scale-98 disabled:opacity-50"
|
||||
>
|
||||
{loading ? 'Authenticating...' : `Sign In as ${selectedRole.toUpperCase()}`}
|
||||
<ArrowRight className="h-4 w-4" />
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{/* Footer info */}
|
||||
<p className="text-center text-xs text-slate-500 mt-6">
|
||||
Powered by pgvector RAG Model & Multi-Store Nutrition Intelligence Engine
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user