"use client";
import { FormEvent, useCallback, useEffect, useRef, useState } from "react";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { storeTokens, consumePostLoginRedirect, fetchLoginUrl } from "@/lib/auth";
import { identityApi } from "@/lib/identity";
import {
isValidMobile,
normalizeOtpCode,
normalizePhone,
persianToEnglish,
} from "@/lib/phone";
import { useAuth } from "@/hooks/useAuth";
import { Button } from "@/components/ui";
type Step = "phone" | "code";
export interface AuthPanelProps {
/** true = نمایش فیلدهای ثبتنام از ابتدا */
initialSignup?: boolean;
/** مسیر بعد از ورود؛ اگر نباشد از sessionStorage خوانده میشود */
redirectTo?: string;
}
function ProfileFields({
displayName,
setDisplayName,
email,
setEmail,
username,
setUsername,
password,
setPassword,
disabled,
}: {
displayName: string;
setDisplayName: (v: string) => void;
email: string;
setEmail: (v: string) => void;
username: string;
setUsername: (v: string) => void;
password: string;
setPassword: (v: string) => void;
disabled?: boolean;
}) {
return (
);
}
export function AuthPanel({ initialSignup = false, redirectTo }: AuthPanelProps) {
const router = useRouter();
const { isAuthenticated, reload } = useAuth();
const [passwordLoading, setPasswordLoading] = useState(false);
const [step, setStep] = useState("phone");
const [signupMode, setSignupMode] = useState(initialSignup);
const [intent, setIntent] = useState<"login" | "register">("login");
const [mobile, setMobile] = useState("");
const [displayName, setDisplayName] = useState("");
const [email, setEmail] = useState("");
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [code, setCode] = useState("");
const [countdown, setCountdown] = useState(0);
const [loading, setLoading] = useState(false);
const [error, setError] = useState("");
const verifyingRef = useRef(false);
const showProfileFields =
signupMode || (step === "code" && intent === "register");
const profileComplete =
displayName.trim().length > 0 &&
email.trim().length > 0 &&
username.trim().length >= 3 &&
password.length >= 8;
useEffect(() => {
const params = new URLSearchParams(window.location.search);
if (params.get("signup") === "1") setSignupMode(true);
const prefilled = params.get("mobile");
if (prefilled) setMobile(prefilled);
}, []);
useEffect(() => {
if (countdown <= 0) return;
const timer = setTimeout(() => setCountdown((c) => c - 1), 1000);
return () => clearTimeout(timer);
}, [countdown]);
const finishSession = useCallback(
async (tokens: {
access_token: string;
refresh_token?: string;
expires_in: number;
token_type: string;
}) => {
storeTokens(tokens);
await reload();
router.replace(redirectTo ?? consumePostLoginRedirect("/dashboard"));
},
[redirectTo, reload, router]
);
const validateProfile = useCallback(() => {
if (!profileComplete) {
setError("نام، ایمیل، نام کاربری (حداقل ۳ کاراکتر) و رمز (حداقل ۸) الزامی است");
return false;
}
return true;
}, [profileComplete]);
const handleRequestOtp = async (e?: FormEvent) => {
e?.preventDefault();
setError("");
const normalized = normalizePhone(mobile);
if (!isValidMobile(normalized)) {
setError("شماره موبایل صحیح نیست");
return;
}
if (signupMode && !validateProfile()) return;
setMobile(normalized);
setLoading(true);
try {
const res = await identityApi.mobileStart(normalized);
setIntent(res.intent);
if (res.intent === "login" && signupMode) {
setSignupMode(false);
setError("این شماره قبلاً ثبت شده — کد ورود ارسال شد");
}
setCountdown(res.expires_in);
setStep("code");
} catch (err) {
setError(err instanceof Error ? err.message : "ارسال کد ناموفق بود");
} finally {
setLoading(false);
}
};
const handleVerify = useCallback(
async (codeOverride?: string) => {
if (verifyingRef.current) return;
const normalizedCode = normalizeOtpCode(codeOverride ?? code);
if (normalizedCode.length !== 4) {
setError("کد تأیید باید ۴ رقم باشد");
return;
}
if (intent === "register" && !validateProfile()) return;
verifyingRef.current = true;
setLoading(true);
let keepLoading = false;
try {
const session = await identityApi.mobileComplete({
mobile,
code: normalizedCode,
display_name: displayName.trim() || undefined,
email: showProfileFields && profileComplete ? email.trim() : undefined,
username: showProfileFields && profileComplete ? username.trim() : undefined,
password: showProfileFields && profileComplete ? password : undefined,
});
keepLoading = true;
await finishSession(session);
} catch (err) {
setError(err instanceof Error ? err.message : "تأیید کد ناموفق بود");
} finally {
if (!keepLoading) {
setLoading(false);
verifyingRef.current = false;
}
}
},
[
code,
displayName,
email,
finishSession,
intent,
mobile,
password,
profileComplete,
showProfileFields,
username,
validateProfile,
]
);
const handleCodeChange = (value: string) => {
const next = normalizeOtpCode(value);
setCode(next);
const canAutoVerify =
intent === "login" || (intent === "register" && profileComplete);
if (next.length === 4 && canAutoVerify && !verifyingRef.current && !loading) {
void handleVerify(next);
}
};
if (isAuthenticated) {
return (
);
}
return (
{signupMode ? "ثبتنام" : "ورود"}
{step === "phone"
? "شماره موبایل خود را وارد کنید"
: intent === "login"
? "کد ورود را وارد کنید"
: "کد تأیید و اطلاعات حساب را تکمیل کنید"}
{error && (
{error}
)}
{step === "phone" ? (
) : (
کد به{" "}
{mobile}
{" "}
ارسال شد
{countdown > 0 && (
ارسال مجدد تا {countdown} ثانیه
)}
handleCodeChange(e.target.value)}
className="w-full rounded-xl border border-gray-200 px-4 py-3 text-center font-mono text-lg tracking-[0.4em]"
placeholder="••••"
/>
{intent === "register" && !signupMode && (
)}
)}
بازگشت به صفحه اصلی
);
}