113 lines
4.4 KiB
TypeScript
113 lines
4.4 KiB
TypeScript
import React, { useState } from 'react'
|
|
import { useNavigate, Link } from 'react-router-dom'
|
|
import { useAuth } from '@/context/AuthContext'
|
|
import { useLang } from '@/context/LangContext'
|
|
|
|
const GOLD = '#CD9E53'
|
|
const NAVY = '#032340'
|
|
|
|
export default function LoginPage() {
|
|
const { lang } = useLang()
|
|
const { login } = useAuth()
|
|
const navigate = useNavigate()
|
|
const [email, setEmail] = useState('')
|
|
const [password, setPassword] = useState('')
|
|
const [error, setError] = useState('')
|
|
const [loading, setLoading] = useState(false)
|
|
|
|
const T = {
|
|
title: lang === 'fa' ? 'ورود به حساب' : 'Sign In',
|
|
email: lang === 'fa' ? 'ایمیل' : 'Email',
|
|
pass: lang === 'fa' ? 'رمز عبور' : 'Password',
|
|
submit: lang === 'fa' ? 'ورود' : 'Login',
|
|
noAcc: lang === 'fa' ? 'حساب ندارید؟' : "Don't have an account?",
|
|
register: lang === 'fa' ? 'ثبتنام کنید' : 'Register',
|
|
}
|
|
|
|
async function handleSubmit(e: React.FormEvent) {
|
|
e.preventDefault()
|
|
setError('')
|
|
setLoading(true)
|
|
try {
|
|
await login(email, password)
|
|
navigate('/profile')
|
|
} catch (err: unknown) {
|
|
setError(err instanceof Error ? err.message : 'خطا در ورود')
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div style={{
|
|
minHeight: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center',
|
|
background: 'var(--paper)', padding: '2rem',
|
|
}}>
|
|
<div style={{
|
|
width: '100%', maxWidth: 420,
|
|
background: 'var(--paper-2)', borderRadius: 16,
|
|
padding: '2.5rem 2rem', boxShadow: '0 4px 40px rgba(0,0,0,0.08)',
|
|
border: `1px solid var(--rule-thin)`,
|
|
}}>
|
|
<div style={{ textAlign: 'center', marginBottom: '2rem' }}>
|
|
<div style={{ width: 48, height: 48, borderRadius: '50%', background: GOLD, margin: '0 auto 1rem', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
|
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke={NAVY} strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
|
|
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/>
|
|
</svg>
|
|
</div>
|
|
<h1 style={{ color: 'var(--ink)', fontSize: '1.5rem', fontWeight: 700, margin: 0 }}>{T.title}</h1>
|
|
</div>
|
|
|
|
<form onSubmit={handleSubmit} dir={lang === 'fa' ? 'rtl' : 'ltr'} style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
|
|
<div>
|
|
<label style={{ display: 'block', color: 'var(--ink-3)', fontSize: '0.85rem', marginBottom: 6 }}>{T.email}</label>
|
|
<input
|
|
type="email" required value={email}
|
|
onChange={e => setEmail(e.target.value)}
|
|
style={inputStyle}
|
|
placeholder="example@email.com"
|
|
dir="ltr"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label style={{ display: 'block', color: 'var(--ink-3)', fontSize: '0.85rem', marginBottom: 6 }}>{T.pass}</label>
|
|
<input
|
|
type="password" required value={password}
|
|
onChange={e => setPassword(e.target.value)}
|
|
style={inputStyle}
|
|
dir="ltr"
|
|
/>
|
|
</div>
|
|
|
|
{error && (
|
|
<p style={{ color: 'var(--red)', fontSize: '0.85rem', margin: 0, textAlign: 'center' }}>{error}</p>
|
|
)}
|
|
|
|
<button type="submit" disabled={loading} style={btnStyle(loading)}>
|
|
{loading ? '...' : T.submit}
|
|
</button>
|
|
</form>
|
|
|
|
<p style={{ textAlign: 'center', marginTop: '1.5rem', color: 'var(--ink-3)', fontSize: '0.875rem' }}>
|
|
{T.noAcc}{' '}
|
|
<Link to="/register" style={{ color: GOLD, fontWeight: 600, textDecoration: 'none' }}>{T.register}</Link>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
const inputStyle: React.CSSProperties = {
|
|
width: '100%', padding: '0.75rem 1rem', borderRadius: 8,
|
|
border: '1px solid var(--rule-thin)', background: 'var(--paper)',
|
|
color: 'var(--ink)', fontSize: '0.95rem', outline: 'none', boxSizing: 'border-box',
|
|
}
|
|
|
|
const btnStyle = (loading: boolean): React.CSSProperties => ({
|
|
width: '100%', padding: '0.85rem', borderRadius: 8,
|
|
background: loading ? 'var(--ink-5)' : GOLD,
|
|
color: NAVY, fontWeight: 700, fontSize: '1rem',
|
|
border: 'none', cursor: loading ? 'not-allowed' : 'pointer',
|
|
transition: 'opacity 0.2s',
|
|
})
|