+
+
+
+
+
-
-
-
setConfirm(e.target.value)} />
+
لینک بازیابی قدیمی غیرفعال شده است
+
+ مسیرهای مبتنی بر ایمیل دیگر برای بازیابی حساب استفاده نمیشوند.
+
+
+
+
+ {isLoading ? (
+
+
+ در حال دریافت راهنمای بازیابی...
+
+ ) : (
+ message
+ )}
-
-
-
-
+
+
+
+
+
+
راه جایگزین
+
+ از صفحه بازیابی با موبایل استفاده کنید. اگر موبایل ثبتشده را هم در دسترس ندارید، ورود با گوگل و همان ایمیل قبلی بهترین مسیر بازیابی است.
+
+
+
+
+
+
+
+
+
+
+
+
);
}
-
diff --git a/src/views/ResetPasswordRequest.tsx b/src/views/ResetPasswordRequest.tsx
index 0975da8..971aee8 100644
--- a/src/views/ResetPasswordRequest.tsx
+++ b/src/views/ResetPasswordRequest.tsx
@@ -1,35 +1,103 @@
"use client";
-import { useState } from 'react';
-import { useToast } from '@/hooks/use-toast';
-import { api } from '@/lib/api';
-import { resolveErrorMessage } from '@/lib/utils';
-import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
-import { Label } from '@/components/ui/label';
-import { Input } from '@/components/ui/input';
-import { Button } from '@/components/ui/button';
+import { useEffect, useState } from "react";
+import { AlertTriangle, Loader2, ShieldCheck } from "lucide-react";
+import OtpCodeField from "@/components/OtpCodeField";
+import { Button } from "@/components/ui/button";
+import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
+import { Input } from "@/components/ui/input";
+import { Label } from "@/components/ui/label";
+import { useToast } from "@/hooks/use-toast";
+import { api } from "@/lib/api";
+import { Link } from "@/lib/router";
+import { resolveErrorMessage } from "@/lib/utils";
+
+const normalizeDigits = (value: string) =>
+ value
+ .replace(/[\u06F0-\u06F9]/g, (digit) => String(digit.charCodeAt(0) - 0x06f0))
+ .replace(/[\u0660-\u0669]/g, (digit) => String(digit.charCodeAt(0) - 0x0660));
+const sanitizeMobile = (value: string) => normalizeDigits(value).replace(/[^\d]/g, "");
export default function ResetPasswordRequest() {
const { toast } = useToast();
- const [email, setEmail] = useState('');
+ const [mobile, setMobile] = useState("");
+ const [code, setCode] = useState("");
+ const [newPassword, setNewPassword] = useState("");
+ const [confirmPassword, setConfirmPassword] = useState("");
const [loading, setLoading] = useState(false);
+ const [cooldown, setCooldown] = useState(0);
- const onSubmit = async (e: React.FormEvent) => {
- e.preventDefault();
+ useEffect(() => {
+ if (cooldown <= 0) {
+ return;
+ }
+ const timer = window.setTimeout(() => setCooldown((current) => current - 1), 1000);
+ return () => window.clearTimeout(timer);
+ }, [cooldown]);
+
+ const handleSendOtp = async () => {
try {
setLoading(true);
- await api.requestPasswordReset(email);
+ const response = await api.sendOtp({
+ mobile: sanitizeMobile(mobile),
+ mode: "reset_password",
+ });
+ setCooldown(Math.min(response.expires_in_seconds, 120));
toast({
- title: 'اگر ایمیلی ثبت شده باشد، لینک بازیابی ارسال شد',
- description: 'ایمیل خود را بررسی کنید.',
- variant: 'success'
+ title: "کد بازیابی ارسال شد",
+ description: response.message,
+ variant: "success",
});
} catch (error: unknown) {
- // بکاند 200 میدهد حتی اگر ایمیل نباشد؛ اما اگر اروری بیاید، نشان بده
toast({
- title: 'خطا',
- description: resolveErrorMessage(error, 'مشکلی رخ داد'),
- variant: 'destructive',
+ title: "ارسال کد انجام نشد",
+ description: resolveErrorMessage(error, "امکان ارسال پیامک بازیابی وجود ندارد."),
+ variant: "destructive",
+ });
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const handleResetPassword = async (event: React.FormEvent) => {
+ event.preventDefault();
+ if (newPassword.length < 8) {
+ toast({
+ title: "رمز عبور کوتاه است",
+ description: "رمز جدید باید حداقل ۸ کاراکتر داشته باشد.",
+ variant: "destructive",
+ });
+ return;
+ }
+ if (newPassword !== confirmPassword) {
+ toast({
+ title: "عدم تطابق رمزها",
+ description: "تکرار رمز عبور با رمز جدید یکسان نیست.",
+ variant: "destructive",
+ });
+ return;
+ }
+
+ try {
+ setLoading(true);
+ await api.resetPassword({
+ mobile: sanitizeMobile(mobile),
+ code: normalizeDigits(code),
+ new_password: newPassword,
+ });
+ toast({
+ title: "رمز عبور تغییر کرد",
+ description: "اکنون میتوانید با رمز جدید وارد شوید.",
+ variant: "success",
+ });
+ setCode("");
+ setNewPassword("");
+ setConfirmPassword("");
+ } catch (error: unknown) {
+ toast({
+ title: "بازیابی ناموفق بود",
+ description: resolveErrorMessage(error, "کد تأیید یا رمز جدید قابل پذیرش نیست."),
+ variant: "destructive",
});
} finally {
setLoading(false);
@@ -37,24 +105,131 @@ export default function ResetPasswordRequest() {
};
return (
-
-
-
- بازیابی رمز عبور
- ایمیلتان را وارد کنید تا لینک بازیابی برای شما ارسال شود
-
-
-