Skip to content

Commit b474043

Browse files
feat: updated initial login and registration pages.
1 parent d3f4309 commit b474043

3 files changed

Lines changed: 395 additions & 157 deletions

File tree

apps/web/app/login/page.tsx

Lines changed: 172 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,30 @@
33
import Link from 'next/link';
44
import { useRouter, useSearchParams } from 'next/navigation';
55
import { FormEvent, useState } from 'react';
6-
import { ArrowRight } from 'lucide-react';
6+
import { motion, AnimatePresence } from 'framer-motion';
7+
import {
8+
Mail,
9+
Lock,
10+
ShieldCheck,
11+
Loader2,
12+
AlertCircle,
13+
Eye,
14+
EyeOff,
15+
ArrowRight
16+
} from 'lucide-react';
717
import { useAuth } from '../../src/components/auth/AuthProvider';
18+
import { GridBackground } from '../../components/home';
19+
import { fadeUp, staggerContainer } from '../../components/home/motion-variants';
20+
import { GlassCard } from '../../components/ui/GlassCard';
21+
import { AccentButton } from '../../components/ui/AccentButton';
822

923
export default function LoginPage() {
1024
const [email, setEmail] = useState('');
1125
const [password, setPassword] = useState('');
26+
const [showPassword, setShowPassword] = useState(false);
1227
const [error, setError] = useState('');
1328
const [loading, setLoading] = useState(false);
29+
1430
const router = useRouter();
1531
const searchParams = useSearchParams();
1632
const { login } = useAuth();
@@ -25,87 +41,170 @@ export default function LoginPage() {
2541
const next = searchParams.get('next') || '/dashboard';
2642
router.push(next);
2743
} catch (err) {
28-
setError((err as Error).message || 'Login failed');
44+
setError((err as Error).message || 'Authentication failed. Please check your credentials.');
2945
} finally {
3046
setLoading(false);
3147
}
3248
};
3349

3450
return (
35-
<main className="relative min-h-screen overflow-hidden bg-[#030303] text-white flex flex-col justify-center px-6">
36-
{/* Background Ambience */}
37-
<div className="pointer-events-none absolute inset-0 grid-bg opacity-10" />
38-
<div className="pointer-events-none absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 h-[600px] w-full max-w-4xl bg-purple-600/5 blur-[120px]" />
39-
40-
<div className="relative z-10 mx-auto w-full max-w-lg">
41-
<div className="text-center mb-10">
42-
<Link href="/" className="inline-flex h-16 w-16 items-center justify-center rounded-2xl bg-gradient-to-br from-purple-500 to-purple-700 shadow-xl shadow-purple-500/20 mb-6 group transition-transform hover:scale-105 active:scale-95">
43-
<span className="text-white font-black text-3xl">FS</span>
44-
</Link>
45-
<h1 className="text-4xl font-extrabold tracking-tight text-white mb-3">
46-
Welcome Back
47-
</h1>
48-
<p className="text-zinc-400 font-medium tracking-wide uppercase text-[10px] tracking-[0.2em]">
49-
Sign in to your FairShare account
50-
</p>
51-
</div>
51+
<main className="relative min-h-screen overflow-hidden bg-[#030303] text-white selection:bg-purple-500/30 font-sans">
52+
<GridBackground />
53+
54+
{/* Subtle Background Glows */}
55+
<div className="absolute top-0 left-0 h-full w-full pointer-events-none overflow-hidden">
56+
<div className="absolute top-[-10%] left-[-10%] h-[500px] w-[500px] bg-purple-600/10 blur-[120px] rounded-full" />
57+
<div className="absolute bottom-[-10%] right-[-10%] h-[600px] w-[600px] bg-indigo-600/5 blur-[150px] rounded-full" />
58+
</div>
59+
60+
<div className="relative z-10 flex min-h-screen pt-10 md:pt-5 flex-col lg:flex-row max-w-[1600px] mx-auto">
61+
{/* Left Side: Brand & Value Prop */}
62+
<div className="hidden md:flex flex-1 flex-col justify-center px-8 py-0 -mt-32 lg:px-20 xl:px-32">
63+
<motion.div
64+
variants={staggerContainer}
65+
initial="hidden"
66+
animate="show"
67+
className="max-w-xl"
68+
>
5269

53-
<div className="relative overflow-hidden rounded-[2.5rem] border border-white/5 bg-white/[0.03] backdrop-blur-2xl p-8 sm:p-12 shadow-2xl">
54-
{/* Internal Glow */}
55-
<div className="absolute inset-0 bg-gradient-to-br from-purple-500/5 via-transparent to-transparent pointer-events-none" />
56-
57-
<form className="relative z-10 space-y-6" onSubmit={onSubmit}>
58-
<div className="space-y-2">
59-
<label className="text-[10px] font-bold uppercase tracking-[0.2em] text-zinc-500 ml-1">Email Address</label>
60-
<input
61-
className="w-full bg-white/5 border border-white/10 rounded-2xl p-4 font-medium text-white outline-none focus:border-purple-500/50 focus:bg-white/[0.08] transition-all"
62-
placeholder="name@company.com"
63-
type="email"
64-
value={email}
65-
onChange={(event) => setEmail(event.target.value)}
66-
required
67-
/>
68-
</div>
69-
70-
<div className="space-y-2">
71-
<label className="text-[10px] font-bold uppercase tracking-[0.2em] text-zinc-500 ml-1">Password</label>
72-
<input
73-
className="w-full bg-white/5 border border-white/10 rounded-2xl p-4 font-medium text-white outline-none focus:border-purple-500/50 focus:bg-white/[0.08] transition-all"
74-
placeholder="••••••••"
75-
type="password"
76-
value={password}
77-
onChange={(event) => setPassword(event.target.value)}
78-
required
79-
/>
80-
</div>
81-
82-
{error ? (
83-
<div className="bg-red-500/10 border border-red-500/20 p-4 rounded-xl text-red-400 font-bold text-sm text-center">
84-
{error}
85-
</div>
86-
) : null}
8770

88-
<button
89-
className="btn-royal w-full group py-4 h-14"
90-
disabled={loading}
91-
type="submit"
71+
<motion.h1
72+
variants={fadeUp}
73+
className="text-5xl font-bold tracking-tight md:text-6xl lg:text-7xl leading-[1.1] text-white mb-8"
9274
>
93-
<span className="flex items-center justify-center gap-2">
94-
{loading ? 'Authenticating...' : 'Sign In'}
95-
{!loading && <ArrowRight size={18} className="transition-transform group-hover:translate-x-1" />}
96-
</span>
97-
</button>
98-
99-
<p className="text-center text-sm font-medium text-zinc-500">
100-
New to FairShare?{' '}
101-
<Link className="text-purple-400 hover:text-purple-300 transition-colors font-bold underline decoration-purple-500/30 underline-offset-8 decoration-2" href="/register">
102-
Create account
103-
</Link>
104-
</p>
105-
</form>
106-
107-
{/* Top light leak */}
108-
<div className="absolute top-0 left-0 right-0 h-px bg-gradient-to-r from-transparent via-white/15 to-transparent" />
75+
Access the <br />
76+
<span className="text-zinc-600">financial engine.</span>
77+
</motion.h1>
78+
79+
<motion.p
80+
variants={fadeUp}
81+
className="mb-12 text-lg text-zinc-400 leading-relaxed max-w-lg"
82+
>
83+
The unified protocol for expense management and collaborative finance. Sign in to resume your operations.
84+
</motion.p>
85+
86+
<motion.div variants={fadeUp} className="space-y-6">
87+
{[
88+
{ title: 'Secure Vault', sub: 'End-to-end encrypted session keys.', icon: Lock },
89+
{ title: 'Verified Nodes', sub: 'Institutional-grade access control.', icon: ShieldCheck }
90+
].map((item, idx) => (
91+
<div key={idx} className="flex items-center gap-5 group">
92+
<div className="h-10 w-10 flex items-center justify-center rounded-xl border border-white/5 bg-white/[0.03] group-hover:border-purple-500/30 group-hover:bg-purple-500/5 transition-all">
93+
<item.icon size={18} className="text-zinc-500 group-hover:text-purple-400" />
94+
</div>
95+
<div>
96+
<h4 className="text-sm font-bold text-white uppercase tracking-wider">{item.title}</h4>
97+
<p className="text-xs text-zinc-500 font-medium">{item.sub}</p>
98+
</div>
99+
</div>
100+
))}
101+
</motion.div>
102+
</motion.div>
103+
</div>
104+
105+
{/* Right Side: Identity Form */}
106+
<div className="relative flex flex-1 items-center justify-center px-6 py-20 lg:px-12 xl:px-24">
107+
<motion.div
108+
initial={{ opacity: 0, y: 20 }}
109+
animate={{ opacity: 1, y: 0 }}
110+
transition={{ duration: 0.8, ease: "easeOut" }}
111+
className="w-full max-w-[480px]"
112+
>
113+
<GlassCard className="relative p-8 md:p-12 border-white/5 bg-white/[0.01] overflow-hidden">
114+
<div className="relative z-10">
115+
<div className="mb-10">
116+
<h2 className="text-3xl font-bold tracking-tight text-white mb-2">Welcome Back</h2>
117+
<p className="text-sm text-zinc-500">Resume your collaborative financial operations.</p>
118+
</div>
119+
120+
<form onSubmit={onSubmit} className="space-y-5">
121+
<AnimatePresence mode="wait">
122+
{error && (
123+
<motion.div
124+
initial={{ opacity: 0, y: -10 }}
125+
animate={{ opacity: 1, y: 0 }}
126+
exit={{ opacity: 0, scale: 0.95 }}
127+
className="flex items-center gap-3 rounded-xl border border-red-500/10 bg-red-500/5 p-4 text-xs font-medium text-red-500"
128+
>
129+
<AlertCircle size={14} />
130+
<span>{error}</span>
131+
</motion.div>
132+
)}
133+
</AnimatePresence>
134+
135+
<div className="space-y-1.5">
136+
<label className="text-xs font-bold text-zinc-500 ml-1">Email Address</label>
137+
<div className="relative group">
138+
<Mail size={16} className="absolute left-4 top-1/2 -translate-y-1/2 text-zinc-600 group-focus-within:text-purple-500 transition-colors" />
139+
<input
140+
required
141+
type="email"
142+
value={email}
143+
onChange={(e) => setEmail(e.target.value)}
144+
placeholder="name@fairshare.com"
145+
disabled={loading}
146+
className="w-full bg-white/[0.03] border border-white/5 rounded-xl py-4 pl-12 pr-4 text-sm font-medium text-white placeholder-zinc-700 outline-none focus:border-purple-500/30 focus:bg-white/[0.05] transition-all"
147+
/>
148+
</div>
149+
</div>
150+
151+
<div className="space-y-1.5">
152+
<div className="flex items-center justify-between px-1">
153+
<label className="text-xs font-bold text-zinc-500">Password</label>
154+
<Link href="/forgot-password" className="text-[10px] font-bold text-zinc-500 hover:text-white transition-colors uppercase tracking-widest">
155+
Forgot?
156+
</Link>
157+
</div>
158+
<div className="relative group">
159+
<Lock size={16} className="absolute left-4 top-1/2 -translate-y-1/2 text-zinc-600 group-focus-within:text-purple-500 transition-colors" />
160+
<input
161+
required
162+
type={showPassword ? 'text' : 'password'}
163+
value={password}
164+
onChange={(e) => setPassword(e.target.value)}
165+
placeholder="••••••••"
166+
disabled={loading}
167+
className="w-full bg-white/[0.03] border border-white/5 rounded-xl py-4 pl-12 pr-12 text-sm font-medium text-white placeholder-zinc-700 outline-none focus:border-purple-500/30 focus:bg-white/[0.05] transition-all"
168+
/>
169+
<button
170+
type="button"
171+
onClick={() => setShowPassword(!showPassword)}
172+
className="absolute right-4 top-1/2 -translate-y-1/2 text-zinc-600 hover:text-white transition-colors"
173+
>
174+
{showPassword ? <EyeOff size={16} /> : <Eye size={16} />}
175+
</button>
176+
</div>
177+
</div>
178+
179+
<div className="pt-4">
180+
<AccentButton
181+
type="submit"
182+
disabled={loading}
183+
className="w-full h-14 text-base font-bold"
184+
variant="primary"
185+
>
186+
{loading ? (
187+
<span className="flex items-center gap-2">
188+
<Loader2 size={18} className="animate-spin" /> Verifying...
189+
</span>
190+
) : (
191+
<span className="flex items-center gap-2">
192+
Sign In <ArrowRight size={18} />
193+
</span>
194+
)}
195+
</AccentButton>
196+
</div>
197+
198+
<p className="pt-8 text-center text-xs font-medium text-zinc-500">
199+
New to the ecosystem?{' '}
200+
<Link className="text-white hover:text-purple-400 font-bold transition-all" href="/register">
201+
Create an account
202+
</Link>
203+
</p>
204+
</form>
205+
</div>
206+
</GlassCard>
207+
</motion.div>
109208
</div>
110209
</div>
111210
</main>

0 commit comments

Comments
 (0)