source: chapterx-frontend/src/pages/auth/LoginPage.tsx@ 0b502c2

main
Last change on this file since 0b502c2 was 0b502c2, checked in by kikisrbinoska <srbinoskakristina07@…>, 12 days ago

Fixed user profile and reading lists

  • Property mode set to 100644
File size: 4.2 KB
Line 
1import React, { useState } from 'react'
2import { useNavigate, Link } from 'react-router-dom'
3import { Eye, EyeOff } from 'lucide-react'
4import logo from '../../assets/chapterX-removebg-preview.png'
5import { useAuthStore } from '../../store/authStore'
6import { useUIStore } from '../../store/uiStore'
7import { Button } from '../../components/ui/Button'
8
9export const LoginPage: React.FC = () => {
10 const navigate = useNavigate()
11 const { login } = useAuthStore()
12 const { addToast } = useUIStore()
13 const [email, setEmail] = useState('')
14 const [password, setPassword] = useState('')
15 const [showPw, setShowPw] = useState(false)
16 const [loading, setLoading] = useState(false)
17 const [errors, setErrors] = useState<{ email?: string; password?: string }>({})
18
19 const validate = () => {
20 const e: typeof errors = {}
21 if (!email.trim()) e.email = 'Email or username is required'
22 if (!password.trim()) e.password = 'Password is required'
23 setErrors(e)
24 return Object.keys(e).length === 0
25 }
26
27 const handleSubmit = async (ev: React.FormEvent) => {
28 ev.preventDefault()
29 if (!validate()) return
30 setLoading(true)
31 try {
32 await login(email, password)
33 addToast('Welcome back!')
34 navigate('/')
35 } catch (err: any) {
36 addToast(err.message || 'Login failed', 'error')
37 } finally {
38 setLoading(false)
39 }
40 }
41
42 return (
43 <div className="min-h-[80vh] flex items-center justify-center px-4 py-12">
44 <div className="w-full max-w-md">
45 {/* Logo */}
46 <div className="text-center mb-8">
47 <Link to="/" className="inline-flex items-center gap-2 mb-6">
48 <img src={logo} alt="ChapterX" className="h-20 w-20 object-contain" />
49 </Link>
50 <h1 className="font-serif text-2xl font-bold text-white">Welcome back</h1>
51 <p className="text-slate-400 text-sm mt-2">Sign in to your account</p>
52 </div>
53
54 {/* Form */}
55 <form onSubmit={handleSubmit} className="space-y-4">
56 <div>
57 <label className="block text-sm text-slate-400 mb-1.5">Email or Username</label>
58 <input
59 type="text"
60 value={email}
61 onChange={e => { setEmail(e.target.value); setErrors(p => ({ ...p, email: '' })) }}
62 placeholder="you@example.com"
63 className={`w-full px-4 py-3 bg-slate-800 border rounded-xl text-white placeholder-slate-500 focus:outline-none focus:border-indigo-500 transition-colors ${
64 errors.email ? 'border-rose-500' : 'border-slate-700'
65 }`}
66 />
67 {errors.email && <p className="text-rose-400 text-xs mt-1">{errors.email}</p>}
68 </div>
69 <div>
70 <label className="block text-sm text-slate-400 mb-1.5">Password</label>
71 <div className="relative">
72 <input
73 type={showPw ? 'text' : 'password'}
74 value={password}
75 onChange={e => { setPassword(e.target.value); setErrors(p => ({ ...p, password: '' })) }}
76 placeholder="••••••••"
77 className={`w-full px-4 py-3 pr-12 bg-slate-800 border rounded-xl text-white placeholder-slate-500 focus:outline-none focus:border-indigo-500 transition-colors ${
78 errors.password ? 'border-rose-500' : 'border-slate-700'
79 }`}
80 />
81 <button
82 type="button"
83 onClick={() => setShowPw(!showPw)}
84 className="absolute right-3 top-1/2 -translate-y-1/2 text-slate-500 hover:text-slate-300"
85 >
86 {showPw ? <EyeOff size={16} /> : <Eye size={16} />}
87 </button>
88 </div>
89 {errors.password && <p className="text-rose-400 text-xs mt-1">{errors.password}</p>}
90 </div>
91
92 <Button type="submit" className="w-full" size="lg" loading={loading}>
93 Sign In
94 </Button>
95 </form>
96
97 <p className="text-center text-slate-500 text-sm mt-6">
98 Don't have an account?{' '}
99 <Link to="/register" className="text-indigo-400 hover:text-indigo-300">
100 Create one
101 </Link>
102 </p>
103 </div>
104 </div>
105 )
106}
Note: See TracBrowser for help on using the repository browser.