Files
Rasadyar_Inspection_System/src/partials/auth/Login.tsx
2026-02-01 15:09:31 +03:30

237 lines
7.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
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 { motion, Variants } from "framer-motion";
import { useNavigate } from "@tanstack/react-router";
import { useApiMutation } from "../../utils/useApiRequest";
import {
useUserStore,
useUserProfileStore,
} from "../../context/zustand-store/userStore";
import { useToast } from "../../hooks/useToast";
import Textfield from "../../components/Textfeild/Textfeild";
import Button from "../../components/Button/Button";
import Typography from "../../components/Typography/Typography";
import Captcha from "../../components/Captcha/Captcha";
import logo from "../../assets/images/logo.png";
const containerVariants: Variants = {
hidden: {},
show: {
transition: {
staggerChildren: 0.1,
},
},
};
const fadeInUp: Variants = {
hidden: { opacity: 0, y: 10 },
show: { opacity: 1, y: 0, transition: { duration: 0.4, ease: "easeOut" } },
};
const Login: React.FC = () => {
const navigate = useNavigate();
const { setUser } = useUserStore();
const { setUserProfile } = useUserProfileStore();
const showToast = useToast();
const [phoneNumber, setPhoneNumber] = useState("");
const [password, setPassword] = useState("");
const [isValidCaptcha, setIsValidCaptcha] = useState(false);
const [captchaImage, setCaptchaImage] = useState<string>("");
const [captchaKey, setCaptchaKey] = useState<string>("");
const [captchaCode, setCaptchaCode] = useState<string>("");
const mutationCaptcha = useApiMutation({
api: "auth/",
method: "post",
disableBackdrop: true,
});
const mutationLogin = useApiMutation({
api: "login",
method: "post",
disableBackdrop: false,
});
const handleGetCaptcha = async () => {
try {
const data = await mutationCaptcha.mutateAsync({
mobile: phoneNumber || "",
state: "login",
});
if (data?.captcha_image) {
setCaptchaImage(data.captcha_image);
setCaptchaKey(data.captcha_key || "");
}
} catch (error) {
console.error("Error getting captcha:", error);
}
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!phoneNumber || !password) {
showToast("لطفا شماره همراه و کلمه عبور را وارد کنید", "error");
return;
}
if (captchaImage && captchaKey && !captchaCode) {
showToast("لطفا کد امنیتی را وارد کنید", "error");
return;
}
if (!captchaImage && !isValidCaptcha) {
showToast("لطفا کد امنیتی را به درستی وارد کنید", "error");
return;
}
try {
const loginData: any = {
mobile: phoneNumber,
password: password,
};
if (captchaImage && captchaKey && captchaCode) {
loginData.captcha_code = captchaCode;
loginData.captcha_key = `rest_captcha_${captchaKey}.0`;
}
const response = await mutationLogin.mutateAsync(loginData);
if (response?.token || response?.access) {
const token = response.token || response.access;
setUser({ auth: token });
if (response?.user) {
setUserProfile(response.user);
}
navigate({ to: "/" });
} else {
showToast("نام کاربری یا کلمه عبور صحیح نیست!", "error");
handleGetCaptcha();
}
} catch (error: any) {
console.error("Login error:", error);
showToast("نام کاربری یا کلمه عبور صحیح نیست!", "error");
handleGetCaptcha();
}
};
return (
<motion.form
onSubmit={handleSubmit}
variants={containerVariants}
initial="hidden"
animate="show"
className="w-full p-6 sm:p-10 bg-white rounded-xl flex flex-col max-w-md space-y-4 items-center justify-center dark:bg-dark-800 shadow-lg"
>
<motion.img
variants={fadeInUp}
src={logo}
alt="Logo"
className="mx-auto w-20 select-none mb-4 dark:brightness-100 dark:opacity-100"
/>
<motion.div variants={fadeInUp}>
<Typography className="mb-4 select-none">
سامانه بازرسی رصدبان
</Typography>
</motion.div>
<motion.div variants={fadeInUp} className="w-full">
<Textfield
fullWidth
placeholder="شماره همراه"
value={phoneNumber}
onChange={(e) => setPhoneNumber(e.target.value)}
/>
</motion.div>
<motion.div variants={fadeInUp} className="w-full">
<Textfield
fullWidth
placeholder="کلمه عبور"
password
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
</motion.div>
<motion.div variants={fadeInUp} className="w-full">
<div className="space-y-2">
{captchaImage ? (
<div className="flex items-center gap-2">
<Textfield
fullWidth
placeholder="کد امنیتی"
value={captchaCode}
onChange={(e) => {
setCaptchaCode(e.target.value);
setIsValidCaptcha(e.target.value.length > 0);
}}
isNumber
/>
<button
type="button"
onClick={handleGetCaptcha}
className="p-2 rounded-lg hover:bg-gray-200 dark:hover:bg-dark-600 transition shrink-0"
>
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-5 w-5 text-gray-600 dark:text-gray-300"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"
/>
</svg>
</button>
<div className="h-10 w-[120px] overflow-hidden rounded-lg border border-gray-300 dark:border-dark-600 shrink-0">
<img
className="w-full h-full object-cover"
src={
captchaImage.startsWith("data:")
? captchaImage
: `data:image/png;base64,${captchaImage}`
}
alt="captcha"
/>
</div>
</div>
) : (
<Captcha
onChange={(status) => setIsValidCaptcha(status)}
captchaImage={captchaImage}
captchaKey={captchaKey}
onRefresh={handleGetCaptcha}
/>
)}
</div>
</motion.div>
<motion.div variants={fadeInUp} className="w-full">
<Button
fullWidth
type="submit"
disabled={
!phoneNumber ||
!password ||
(captchaImage && !captchaCode) ||
(!captchaImage && !isValidCaptcha)
}
>
ورود به سامانه
</Button>
</motion.div>
</motion.form>
);
};
export default Login;