240 lines
12 KiB
TypeScript
240 lines
12 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 RegisterPage() {
|
||
const { lang } = useLang()
|
||
const { register } = useAuth()
|
||
const navigate = useNavigate()
|
||
const [name, setName] = useState('')
|
||
const [email, setEmail] = useState('')
|
||
const [password, setPassword] = useState('')
|
||
const [showPass, setShowPass] = useState(false)
|
||
const [error, setError] = useState('')
|
||
const [loading, setLoading] = useState(false)
|
||
|
||
const isRtl = lang === 'fa'
|
||
|
||
async function handleSubmit(e: React.FormEvent) {
|
||
e.preventDefault()
|
||
setError('')
|
||
setLoading(true)
|
||
try {
|
||
await register(name, email, password)
|
||
navigate('/profile')
|
||
} catch (err: unknown) {
|
||
setError(err instanceof Error ? err.message : 'خطا در ثبتنام')
|
||
} finally {
|
||
setLoading(false)
|
||
}
|
||
}
|
||
|
||
return (
|
||
<div style={{
|
||
position: 'fixed', inset: 0, zIndex: 100,
|
||
display: 'flex', flexDirection: isRtl ? 'row-reverse' : 'row',
|
||
background: '#fff',
|
||
}}>
|
||
{/* ── Brand Panel ── */}
|
||
<div style={{
|
||
flex: '0 0 48%', position: 'relative', overflow: 'hidden',
|
||
background: `linear-gradient(145deg, ${NAVY} 0%, #043560 55%, #021828 100%)`,
|
||
display: 'flex', flexDirection: 'column', justifyContent: 'space-between',
|
||
padding: '3rem',
|
||
}}
|
||
className="max-lg:hidden"
|
||
>
|
||
<svg style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', opacity: 0.05 }} preserveAspectRatio="xMidYMid slice">
|
||
<defs>
|
||
<pattern id="grid2" width="40" height="40" patternUnits="userSpaceOnUse">
|
||
<path d="M 40 0 L 0 0 0 40" fill="none" stroke="#CD9E53" strokeWidth="0.8"/>
|
||
</pattern>
|
||
</defs>
|
||
<rect width="100%" height="100%" fill="url(#grid2)" />
|
||
</svg>
|
||
|
||
<svg style={{ position: 'absolute', top: -60, left: isRtl ? -60 : 'auto', right: isRtl ? 'auto' : -60, width: 420, height: 420, opacity: 0.06 }} viewBox="0 0 420 420">
|
||
{[0,1,2,3,4].map(i => (
|
||
<polygon key={i} points="210,10 390,110 390,310 210,410 30,310 30,110"
|
||
fill="none" stroke={GOLD} strokeWidth="1.5"
|
||
transform={`scale(${0.3 + i*0.15}) translate(${(1-0.3-i*0.15)*280}, ${(1-0.3-i*0.15)*280})`}
|
||
/>
|
||
))}
|
||
</svg>
|
||
|
||
<div style={{ position: 'relative', zIndex: 1 }}>
|
||
<Link to="/" style={{ display: 'flex', alignItems: 'center', gap: 12, textDecoration: 'none' }}>
|
||
<img src="/Frame%202095585206%20(2).png" alt="" style={{ height: 44, filter: 'brightness(0) invert(1)' }} />
|
||
<div>
|
||
<div style={{ color: '#fff', fontWeight: 800, fontSize: '1.1rem', lineHeight: 1.3 }}>اندیشکده فولاد آینده</div>
|
||
<div style={{ color: 'rgba(255,255,255,0.45)', fontSize: '0.65rem', letterSpacing: '2px', textTransform: 'uppercase' }}>The Steel Futures Institute</div>
|
||
</div>
|
||
</Link>
|
||
</div>
|
||
|
||
<div style={{ position: 'relative', zIndex: 1, direction: 'rtl' }}>
|
||
<div style={{
|
||
display: 'inline-block', background: `${GOLD}22`, border: `1px solid ${GOLD}44`,
|
||
color: GOLD, padding: '0.35rem 0.9rem', borderRadius: 4, fontSize: '0.75rem',
|
||
fontWeight: 600, marginBottom: '1.25rem', letterSpacing: '0.5px',
|
||
}}>
|
||
عضویت رایگان
|
||
</div>
|
||
<h1 style={{ color: '#fff', fontSize: 'clamp(1.6rem, 2.5vw, 2.4rem)', fontWeight: 800, lineHeight: 1.4, margin: '0 0 1rem' }}>
|
||
عضو اندیشکده شوید<br/>
|
||
<span style={{ color: GOLD }}>و به تحلیلها دسترسی داشته باشید</span>
|
||
</h1>
|
||
<p style={{ color: 'rgba(255,255,255,0.55)', fontSize: '0.95rem', lineHeight: 1.8, margin: 0, maxWidth: 340 }}>
|
||
با ثبتنام رایگان به آرشیو گزارشهای تخصصی صنعت فولاد دسترسی پیدا کنید
|
||
</p>
|
||
</div>
|
||
|
||
<div style={{ position: 'relative', zIndex: 1, direction: 'rtl' }}>
|
||
{['دسترسی به گزارشهای تخصصی', 'رصد بازار و قیمتها', 'شبکه خبرگان صنعت'].map((item, i) => (
|
||
<div key={i} style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: i < 2 ? '0.75rem' : 0, color: 'rgba(255,255,255,0.7)', fontSize: '0.875rem' }}>
|
||
<div style={{ width: 20, height: 20, borderRadius: '50%', background: `${GOLD}33`, border: `1px solid ${GOLD}66`, display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
|
||
<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke={GOLD} strokeWidth="3" strokeLinecap="round" strokeLinejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
|
||
</div>
|
||
{item}
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
{/* ── Form Panel ── */}
|
||
<div style={{
|
||
flex: 1, display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center',
|
||
padding: '2rem', background: '#fafaf8', overflowY: 'auto',
|
||
}}>
|
||
<div style={{ width: '100%', maxWidth: 400 }}>
|
||
<div style={{ marginBottom: '2.5rem', direction: 'rtl' }}>
|
||
<h2 style={{ color: NAVY, fontSize: '1.75rem', fontWeight: 800, margin: '0 0 0.5rem' }}>ثبتنام</h2>
|
||
<p style={{ color: '#8a8a8a', margin: 0, fontSize: '0.9rem' }}>یک حساب کاربری جدید بسازید</p>
|
||
</div>
|
||
|
||
<form onSubmit={handleSubmit} dir="rtl" style={{ display: 'flex', flexDirection: 'column', gap: '1.1rem' }}>
|
||
<div>
|
||
<label style={labelStyle}>نام و نام خانوادگی</label>
|
||
<InputWithIcon
|
||
type="text" required value={name}
|
||
onChange={e => setName(e.target.value)}
|
||
placeholder="علی محمدی"
|
||
icon={
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#333" strokeWidth="2" 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>
|
||
|
||
<div>
|
||
<label style={labelStyle}>ایمیل</label>
|
||
<InputWithIcon
|
||
type="email" required value={email} dir="ltr"
|
||
onChange={e => setEmail(e.target.value)}
|
||
placeholder="example@email.com"
|
||
icon={
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#333" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||
<path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/><polyline points="22,6 12,13 2,6"/>
|
||
</svg>
|
||
}
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<label style={labelStyle}>رمز عبور</label>
|
||
<div style={{ position: 'relative' }}>
|
||
<input
|
||
type={showPass ? 'text' : 'password'} required minLength={6}
|
||
value={password} onChange={e => setPassword(e.target.value)}
|
||
dir="ltr"
|
||
style={inputStyle}
|
||
onFocus={e => { e.target.style.borderColor = GOLD }}
|
||
onBlur={e => { e.target.style.borderColor = '#e0e0e0' }}
|
||
/>
|
||
<svg style={{ position: 'absolute', left: 12, top: '50%', transform: 'translateY(-50%)', opacity: 0.35, pointerEvents: 'none' }} width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#333" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||
<rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/>
|
||
</svg>
|
||
<button type="button" onClick={() => setShowPass(s => !s)} style={{ position: 'absolute', right: 10, top: '50%', transform: 'translateY(-50%)', background: 'none', border: 'none', cursor: 'pointer', padding: 4, opacity: 0.45, display: 'flex' }}>
|
||
{showPass
|
||
? <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#333" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94"/><path d="M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19"/><line x1="1" y1="1" x2="23" y2="23"/></svg>
|
||
: <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#333" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
|
||
}
|
||
</button>
|
||
</div>
|
||
<p style={{ color: '#aaa', fontSize: '0.75rem', margin: '4px 0 0', textAlign: 'right' }}>حداقل ۶ کاراکتر</p>
|
||
</div>
|
||
|
||
{error && (
|
||
<div style={{ background: '#fff0f0', border: '1px solid #ffcccc', borderRadius: 8, padding: '0.65rem 1rem', color: '#c0392b', fontSize: '0.85rem' }}>
|
||
{error}
|
||
</div>
|
||
)}
|
||
|
||
<button type="submit" disabled={loading} style={{
|
||
width: '100%', padding: '0.95rem', borderRadius: 10, border: 'none',
|
||
background: loading ? '#d4b97a' : GOLD,
|
||
color: NAVY, fontWeight: 800, fontSize: '1rem',
|
||
cursor: loading ? 'not-allowed' : 'pointer',
|
||
transition: 'all 0.2s', marginTop: '0.4rem',
|
||
boxShadow: loading ? 'none' : `0 4px 20px ${GOLD}55`,
|
||
fontFamily: 'inherit',
|
||
}}
|
||
onMouseEnter={e => { if (!loading) e.currentTarget.style.transform = 'translateY(-1px)' }}
|
||
onMouseLeave={e => { e.currentTarget.style.transform = 'translateY(0)' }}
|
||
>
|
||
{loading ? 'در حال ثبتنام...' : 'ساخت حساب کاربری'}
|
||
</button>
|
||
</form>
|
||
|
||
<div style={{ marginTop: '2rem', textAlign: 'center', direction: 'rtl' }}>
|
||
<span style={{ color: '#888', fontSize: '0.875rem' }}>حساب دارید؟ </span>
|
||
<Link to="/login" style={{ color: GOLD, fontWeight: 700, textDecoration: 'none', fontSize: '0.875rem' }}>وارد شوید</Link>
|
||
</div>
|
||
|
||
<div style={{ marginTop: '2rem', paddingTop: '1.5rem', borderTop: '1px solid #eee', textAlign: 'center' }}>
|
||
<Link to="/" style={{ color: '#aaa', fontSize: '0.8rem', textDecoration: 'none', display: 'inline-flex', alignItems: 'center', gap: 4, direction: 'rtl' }}>
|
||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
|
||
<line x1="19" y1="12" x2="5" y2="12"/><polyline points="12 19 5 12 12 5"/>
|
||
</svg>
|
||
بازگشت به سایت
|
||
</Link>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
/* helpers */
|
||
const labelStyle: React.CSSProperties = {
|
||
display: 'block', color: '#444', fontSize: '0.82rem', fontWeight: 600, marginBottom: 6,
|
||
}
|
||
|
||
const inputStyle: React.CSSProperties = {
|
||
width: '100%', padding: '0.85rem 1rem 0.85rem 2.75rem', borderRadius: 10,
|
||
border: '1.5px solid #e0e0e0', background: '#fff',
|
||
color: '#1a1a1a', fontSize: '0.95rem', outline: 'none',
|
||
boxSizing: 'border-box', transition: 'border-color 0.2s', fontFamily: 'inherit',
|
||
}
|
||
|
||
function InputWithIcon({ icon, ...props }: React.InputHTMLAttributes<HTMLInputElement> & { icon: React.ReactNode }) {
|
||
return (
|
||
<div style={{ position: 'relative' }}>
|
||
<input
|
||
{...props}
|
||
style={inputStyle}
|
||
onFocus={e => { e.target.style.borderColor = GOLD }}
|
||
onBlur={e => { e.target.style.borderColor = '#e0e0e0' }}
|
||
/>
|
||
<span style={{ position: 'absolute', left: 12, top: '50%', transform: 'translateY(-50%)', opacity: 0.35, pointerEvents: 'none', display: 'flex' }}>
|
||
{icon}
|
||
</span>
|
||
</div>
|
||
)
|
||
}
|