feat: implement login modal and altcha captcha for registration
- Added a login modal to the home page with mobile and password fields. - Integrated altcha captcha for the registration process to enhance security. - Updated the registration submission logic to include altcha payload. - Improved error handling and user feedback for both login and registration processes.
This commit is contained in:
@@ -4,6 +4,7 @@ import { toast } from 'sonner';
|
||||
import { EyeIcon, EyeSlashIcon } from '@heroicons/react/24/outline';
|
||||
import { useAuthStore } from '../stores/authStore';
|
||||
import PwaLoginCard from '../components/ui/PwaLoginCard';
|
||||
import Altcha from '../components/ui/Altcha';
|
||||
import { sanitizeMobileInput } from '../lib/utils';
|
||||
|
||||
type Mode = 'password' | 'sms' | 'forgot';
|
||||
@@ -37,6 +38,8 @@ export default function LoginPage() {
|
||||
const [forgotLoading, setForgotLoading] = useState(false);
|
||||
|
||||
const [cooldown, setCooldown] = useState(0);
|
||||
// آخرین payload حلشدهی ALTCHA برای مرحلهی جاری (یکبارمصرف؛ بین مراحل ریست میشود).
|
||||
const [altcha, setAltcha] = useState('');
|
||||
const timerRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
||||
|
||||
useEffect(() => () => { if (timerRef.current) clearInterval(timerRef.current); }, []);
|
||||
@@ -57,6 +60,7 @@ export default function LoginPage() {
|
||||
setForgotStep(1); setForgotMobile(''); setForgotCode(''); setForgotUuid('');
|
||||
setForgotPass(''); setForgotPass2('');
|
||||
setCooldown(0);
|
||||
setAltcha('');
|
||||
if (timerRef.current) { clearInterval(timerRef.current); }
|
||||
};
|
||||
|
||||
@@ -84,10 +88,11 @@ export default function LoginPage() {
|
||||
const res = await fetch('/api/v1/user/send-code', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ mobile }),
|
||||
body: JSON.stringify({ mobile, altcha }),
|
||||
});
|
||||
const json = await res.json();
|
||||
if (!res.ok) { toast.error(json?.errors?.[0]?.message ?? 'خطا در ارسال کد'); return; }
|
||||
setAltcha('');
|
||||
onSuccess(json.uuid);
|
||||
startCooldown();
|
||||
toast.success('کد تأیید ارسال شد');
|
||||
@@ -117,7 +122,7 @@ export default function LoginPage() {
|
||||
const lRes = await fetch('/api/v1/user/otp-login', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ grant }),
|
||||
body: JSON.stringify({ grant, altcha }),
|
||||
});
|
||||
const lJson = await lRes.json();
|
||||
if (!lRes.ok) { toast.error(lJson?.errors?.[0]?.message ?? 'کاربری با این شماره یافت نشد'); return; }
|
||||
@@ -157,7 +162,7 @@ export default function LoginPage() {
|
||||
const res = await fetch('/api/v1/user/reset-password', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ grant: forgotGrant, new_password: forgotPass }),
|
||||
body: JSON.stringify({ grant: forgotGrant, new_password: forgotPass, altcha }),
|
||||
});
|
||||
const json = await res.json();
|
||||
if (!res.ok) { toast.error(json?.errors?.[0]?.message ?? 'خطا در تغییر رمز'); return; }
|
||||
@@ -267,6 +272,7 @@ export default function LoginPage() {
|
||||
value={smsMobile} onChange={(e) => setSmsMobile(sanitizeMobileInput(e.target.value))}
|
||||
onKeyDown={(e) => e.key === 'Enter' && handleSmsSend()} />
|
||||
</div>
|
||||
<div style={{ marginBottom: 12 }}><Altcha key="sms-send" onVerified={setAltcha} /></div>
|
||||
<button className="btn primary block" disabled={smsLoading} onClick={handleSmsSend}
|
||||
style={{ height: 46, fontSize: 15 }}>
|
||||
{smsLoading ? 'در حال ارسال...' : 'ارسال کد تأیید'}
|
||||
@@ -285,6 +291,7 @@ export default function LoginPage() {
|
||||
value={smsCode} onChange={(e) => setSmsCode(e.target.value.replace(/\D/g, ''))}
|
||||
onKeyDown={(e) => e.key === 'Enter' && handleSmsVerify()} autoFocus />
|
||||
</div>
|
||||
<div style={{ marginBottom: 12 }}><Altcha key="sms-login" onVerified={setAltcha} /></div>
|
||||
<button className="btn primary block" disabled={smsLoading} onClick={handleSmsVerify}
|
||||
style={{ height: 46, fontSize: 15 }}>
|
||||
{smsLoading ? 'در حال تأیید...' : 'تأیید و ورود'}
|
||||
@@ -315,6 +322,7 @@ export default function LoginPage() {
|
||||
value={forgotMobile} onChange={(e) => setForgotMobile(sanitizeMobileInput(e.target.value))}
|
||||
onKeyDown={(e) => e.key === 'Enter' && handleForgotSend()} />
|
||||
</div>
|
||||
<div style={{ marginBottom: 12 }}><Altcha key="forgot-send" onVerified={setAltcha} /></div>
|
||||
<button className="btn primary block" disabled={forgotLoading} onClick={handleForgotSend}
|
||||
style={{ height: 46, fontSize: 15 }}>
|
||||
{forgotLoading ? 'در حال ارسال...' : 'ارسال کد تأیید'}
|
||||
@@ -368,6 +376,7 @@ export default function LoginPage() {
|
||||
<div className="err-text">رمزهای عبور یکسان نیستند</div>
|
||||
)}
|
||||
</div>
|
||||
<div style={{ marginBottom: 12 }}><Altcha key="forgot-reset" onVerified={setAltcha} /></div>
|
||||
<button className="btn primary block" disabled={forgotLoading} onClick={handleForgotReset}
|
||||
style={{ height: 46, fontSize: 15 }}>
|
||||
{forgotLoading ? 'در حال ذخیره...' : 'تغییر رمز عبور'}
|
||||
|
||||
Reference in New Issue
Block a user