steelforesight/src/pages/Auth/RegisterPage.tsx

240 lines
12 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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>
)
}