223 lines
6.7 KiB
TypeScript
223 lines
6.7 KiB
TypeScript
import React, { useState } from "react";
|
||
import { useAuth } from "~/contexts/auth-context";
|
||
import toast from "react-hot-toast";
|
||
import { Button } from "~/components/ui/button";
|
||
import {
|
||
TextField,
|
||
PasswordField,
|
||
CheckboxField,
|
||
FieldGroup,
|
||
} from "~/components/ui/form-field";
|
||
import { ErrorAlert, ConnectionError } from "~/components/ui/error-alert";
|
||
import { InogenLogo } from "~/components/ui/brand-logo";
|
||
import {
|
||
LoginLayout,
|
||
LoginContent,
|
||
LoginSidebar,
|
||
LoginHeader,
|
||
LoginBranding,
|
||
LoginFormContainer,
|
||
} from "./login-layout";
|
||
import { Loader2, User, Lock } from "lucide-react";
|
||
|
||
interface LoginFormProps {
|
||
onSuccess?: () => void;
|
||
}
|
||
|
||
interface FormData {
|
||
username: string;
|
||
password: string;
|
||
rememberMe: boolean;
|
||
}
|
||
|
||
interface ValidationErrors {
|
||
username?: string;
|
||
password?: string;
|
||
general?: string;
|
||
}
|
||
|
||
export function LoginForm({ onSuccess }: LoginFormProps) {
|
||
const [formData, setFormData] = useState<FormData>({
|
||
username: "",
|
||
password: "",
|
||
rememberMe: false,
|
||
});
|
||
|
||
const [errors, setErrors] = useState<ValidationErrors>({});
|
||
const [isConnectionError, setIsConnectionError] = useState(false);
|
||
const { login, isLoading } = useAuth();
|
||
|
||
const updateField = (field: keyof FormData, value: string | boolean) => {
|
||
setFormData((prev) => ({ ...prev, [field]: value }));
|
||
|
||
// Clear field-specific errors when user starts typing
|
||
if (errors[field as keyof ValidationErrors]) {
|
||
setErrors((prev) => ({ ...prev, [field]: undefined }));
|
||
}
|
||
};
|
||
|
||
const validateForm = (): ValidationErrors => {
|
||
const newErrors: ValidationErrors = {};
|
||
|
||
if (!formData.username.trim()) {
|
||
newErrors.username = "نام کاربری الزامی است";
|
||
} else if (formData.username.length < 3) {
|
||
newErrors.username = "نام کاربری باید حداقل ۳ کاراکتر باشد";
|
||
}
|
||
|
||
if (!formData.password) {
|
||
newErrors.password = "کلمه عبور الزامی است";
|
||
} else if (formData.password.length < 4) {
|
||
newErrors.password = "کلمه عبور باید حداقل ۴ کاراکتر باشد";
|
||
}
|
||
|
||
return newErrors;
|
||
};
|
||
|
||
const handleSubmit = async (e: React.FormEvent) => {
|
||
e.preventDefault();
|
||
|
||
// Clear previous errors
|
||
setErrors({});
|
||
setIsConnectionError(false);
|
||
|
||
// Validate form
|
||
const validationErrors = validateForm();
|
||
if (Object.keys(validationErrors).length > 0) {
|
||
setErrors(validationErrors);
|
||
const firstError = Object.values(validationErrors)[0];
|
||
toast.error(firstError);
|
||
return;
|
||
}
|
||
|
||
try {
|
||
const success = await login(formData.username, formData.password);
|
||
|
||
if (success) {
|
||
toast.success("ورود موفقیتآمیز بود!");
|
||
onSuccess?.();
|
||
} else {
|
||
const errorMessage = "نام کاربری یا رمز عبور اشتباه است";
|
||
setErrors({ general: errorMessage });
|
||
toast.error(errorMessage);
|
||
}
|
||
} catch (err: any) {
|
||
console.error("Login error:", err);
|
||
|
||
// Check if it's a connection error
|
||
if (err?.code === "NETWORK_ERROR" || err?.message?.includes("fetch")) {
|
||
setIsConnectionError(true);
|
||
} else {
|
||
const errorMessage = err?.message || "خطا در برقراری ارتباط با سرور";
|
||
setErrors({ general: errorMessage });
|
||
toast.error(errorMessage);
|
||
}
|
||
}
|
||
};
|
||
|
||
const handleRetry = () => {
|
||
setIsConnectionError(false);
|
||
setErrors({});
|
||
};
|
||
|
||
return (
|
||
<LoginLayout>
|
||
{/* Left Side - Login Form */}
|
||
<LoginContent>
|
||
<LoginHeader
|
||
title="ورود"
|
||
subtitle="داشبورد مدیریت فناوری و نوآوری"
|
||
description="لطفا نام کاربری و پسورد خود را در قسمت خواسته شده وارد فرمایید"
|
||
/>
|
||
|
||
<LoginFormContainer onSubmit={handleSubmit}>
|
||
<FieldGroup>
|
||
{/* Connection Error */}
|
||
{isConnectionError && <ConnectionError onRetry={handleRetry} />}
|
||
|
||
{/* General Error */}
|
||
{errors.general && (
|
||
<ErrorAlert
|
||
message={errors.general}
|
||
variant="error"
|
||
onRetry={() => setErrors({ general: undefined })}
|
||
dismissible
|
||
onDismiss={() => setErrors({ general: undefined })}
|
||
/>
|
||
)}
|
||
|
||
{/* Username Field */}
|
||
<TextField
|
||
id="username"
|
||
label=""
|
||
type="text"
|
||
value={formData.username}
|
||
onChange={(value) => updateField("username", value)}
|
||
error={errors.username}
|
||
placeholder="نام کاربری"
|
||
disabled={isLoading}
|
||
autoComplete="username"
|
||
required
|
||
leftIcon={<User className="h-4 w-4" />}
|
||
/>
|
||
|
||
{/* Password Field */}
|
||
<PasswordField
|
||
id="password"
|
||
label=""
|
||
value={formData.password}
|
||
onChange={(value) => updateField("password", value)}
|
||
error={errors.password}
|
||
placeholder="کلمه عبور"
|
||
disabled={isLoading}
|
||
autoComplete="current-password"
|
||
required
|
||
minLength={4}
|
||
/>
|
||
|
||
{/* Remember Me Checkbox */}
|
||
<div className="flex justify-end">
|
||
<CheckboxField
|
||
id="remember"
|
||
label="همیشه متصل بمانم"
|
||
checked={formData.rememberMe}
|
||
onChange={(checked) => updateField("rememberMe", checked)}
|
||
disabled={isLoading}
|
||
size="md"
|
||
/>
|
||
</div>
|
||
|
||
{/* Login Button */}
|
||
<Button
|
||
type="submit"
|
||
disabled={isLoading || isConnectionError}
|
||
size="lg"
|
||
className="w-full font-persian bg-[var(--color-login-primary)] hover:bg-[var(--color-login-primary)]/90 text-slate-800 font-bold"
|
||
>
|
||
{isLoading ? (
|
||
<>
|
||
<Loader2 className="h-4 w-4 animate-spin ml-2" />
|
||
در حال ورود...
|
||
</>
|
||
) : (
|
||
"ورود"
|
||
)}
|
||
</Button>
|
||
|
||
{/* Additional Links */}
|
||
</FieldGroup>
|
||
</LoginFormContainer>
|
||
</LoginContent>
|
||
|
||
{/* Right Side - Branding */}
|
||
<LoginSidebar>
|
||
<LoginBranding
|
||
brandName="پتروشیمی بندر امام"
|
||
companyName="توسعهیافته توسط شرکت رهپویان دانش و فناوری فرا"
|
||
logo={<img src="/brand2.svg"/>}
|
||
/>
|
||
</LoginSidebar>
|
||
</LoginLayout>
|
||
);
|
||
}
|