206 lines
6.6 KiB
TypeScript
206 lines
6.6 KiB
TypeScript
"use client";
|
|
|
|
import React, { useMemo, useState } from "react";
|
|
import { useRouter } from "next/navigation";
|
|
import { useMutation } from "@tanstack/react-query";
|
|
import { ShieldPlus, User } from "lucide-react";
|
|
import { useSelector } from "react-redux";
|
|
|
|
import { RootState, store } from "@/redux/store";
|
|
import {
|
|
setDataLoginStorage,
|
|
setStateLogin,
|
|
setToken,
|
|
} from "@/redux/reducer/auth";
|
|
import { setRememberPassword } from "@/redux/reducer/site";
|
|
import { setInfoUser } from "@/redux/reducer/user";
|
|
import authServices, { LoginResponse } from "@/services/authServices";
|
|
import { httpRequest } from "@/services";
|
|
import { PATH } from "@/constant/config";
|
|
|
|
import FormCustom from "@/components/utils/FormCustom";
|
|
import InputForm from "@/components/utils/FormCustom/components/InputForm";
|
|
import CustomLoading from "@/components/customs/custom-loading";
|
|
import CustomButton from "@/components/customs/custom-button";
|
|
import { ContextFormCustom } from "@/components/utils/FormCustom/contexts";
|
|
|
|
export default function MainLogin() {
|
|
const router = useRouter();
|
|
|
|
const { dataLoginStorage } = useSelector((state: RootState) => state.auth);
|
|
const { isRememberPassword } = useSelector((state: RootState) => state.site);
|
|
|
|
// Khởi tạo state form từ data lưu trữ
|
|
const [form, setForm] = useState(() => ({
|
|
username: isRememberPassword ? dataLoginStorage?.usernameStorage || "" : "",
|
|
password: isRememberPassword ? dataLoginStorage?.passwordStorage || "" : "",
|
|
}));
|
|
|
|
const loginMutation = useMutation<LoginResponse>({
|
|
mutationFn: async () => {
|
|
return (await httpRequest({
|
|
http: authServices.login({
|
|
username: form.username,
|
|
password: form.password,
|
|
deviceId: "Browser-Chrome-Windowns",
|
|
}),
|
|
})) as LoginResponse;
|
|
},
|
|
onSuccess(data) {
|
|
if (data) {
|
|
store.dispatch(setToken(data?.token));
|
|
store.dispatch(
|
|
setInfoUser({
|
|
accessToken: data?.token || "",
|
|
refreshToken: data?.refreshToken || "",
|
|
userId: data?.userId || "",
|
|
}),
|
|
);
|
|
store.dispatch(setStateLogin(true));
|
|
|
|
if (isRememberPassword) {
|
|
store.dispatch(
|
|
setDataLoginStorage({
|
|
usernameStorage: form.username,
|
|
passwordStorage: form.password,
|
|
}),
|
|
);
|
|
} else {
|
|
store.dispatch(setDataLoginStorage(null));
|
|
}
|
|
|
|
router.replace(PATH.HOME, undefined);
|
|
}
|
|
},
|
|
});
|
|
|
|
const handleLogin = () => {
|
|
// Kiểm tra nhanh điều kiện dữ liệu trước khi trigger API
|
|
if (!form.username || !form.password) return;
|
|
|
|
if (isRememberPassword) {
|
|
store.dispatch(
|
|
setDataLoginStorage({
|
|
usernameStorage: form.username,
|
|
passwordStorage: form.password,
|
|
}),
|
|
);
|
|
} else {
|
|
store.dispatch(setDataLoginStorage(null));
|
|
}
|
|
|
|
loginMutation.mutate();
|
|
};
|
|
|
|
return (
|
|
<div className="w-full">
|
|
<CustomLoading loading={loginMutation.isPending} />
|
|
|
|
<FormCustom form={form} setForm={setForm} onSubmit={handleLogin}>
|
|
{/* TITLE */}
|
|
<h3 className="text-[34px] font-semibold text-[#1A1B2D]">Đăng nhập</h3>
|
|
<p className="mt-1 text-[14px] font-medium text-[#6F767E]">
|
|
Chào mừng bạn đến với hệ thống quản lý
|
|
</p>
|
|
|
|
{/* FORM */}
|
|
<div className="mt-6">
|
|
{/* USERNAME */}
|
|
<InputForm
|
|
label={
|
|
<span>
|
|
Tài khoản<span className="text-red-500"> *</span>
|
|
</span>
|
|
}
|
|
placeholder="Tài khoản"
|
|
type="text"
|
|
name="username"
|
|
onClean
|
|
isRequired
|
|
isBlur
|
|
showDone
|
|
icon={<User size={22} />}
|
|
/>
|
|
|
|
{/* PASSWORD */}
|
|
<div className="mt-5">
|
|
<InputForm
|
|
label={
|
|
<span>
|
|
Mật khẩu<span className="text-red-500"> *</span>
|
|
</span>
|
|
}
|
|
placeholder="Mật khẩu"
|
|
type="password"
|
|
name="password"
|
|
onClean
|
|
isRequired
|
|
isBlur
|
|
showDone
|
|
icon={<ShieldPlus size={22} />}
|
|
/>
|
|
</div>
|
|
|
|
{/* REMEMBER PASSWORD */}
|
|
<div className="mt-[14px] flex items-center gap-2 cursor-pointer">
|
|
<input
|
|
id="rememberPassword"
|
|
type="checkbox"
|
|
checked={isRememberPassword}
|
|
onChange={() =>
|
|
store.dispatch(setRememberPassword(!isRememberPassword))
|
|
}
|
|
className="h-5 w-5 cursor-pointer accent-[#0011AB]"
|
|
/>
|
|
<label
|
|
htmlFor="rememberPassword"
|
|
className="cursor-pointer select-none text-[14px] font-medium text-[#171717]"
|
|
>
|
|
Nhớ mật khẩu
|
|
</label>
|
|
</div>
|
|
|
|
{/* BUTTONS */}
|
|
<div className="mt-6">
|
|
<div className="flex gap-2">
|
|
{/* LOGIN BUTTON */}
|
|
<ContextFormCustom.Consumer>
|
|
{({ isDone }) => {
|
|
// Tự kiểm tra xem form hiện tại đã có sẵn dữ liệu hợp lệ chưa
|
|
const hasRememberedData = !!(form.username && form.password);
|
|
|
|
// Sáng nút nếu FormCustom báo Done HOẶC form đã có sẵn dữ liệu từ Redux
|
|
const canSubmit = isDone || hasRememberedData;
|
|
|
|
return (
|
|
<CustomButton
|
|
variant="midnightBlue"
|
|
disabled={!canSubmit}
|
|
rounded="full"
|
|
type="button" // Chuyển từ "submit" sang "button" để tránh bị FormCustom chặn
|
|
onClick={handleLogin} // Gọi trực tiếp hàm submit khi click
|
|
className="font-bold text-2xl disabled:cursor-not-allowed disabled:opacity-50 h-[52px]"
|
|
>
|
|
Đăng nhập
|
|
</CustomButton>
|
|
);
|
|
}}
|
|
</ContextFormCustom.Consumer>
|
|
|
|
{/* REGISTER BUTTON */}
|
|
<CustomButton
|
|
variant="default"
|
|
rounded="full"
|
|
className="font-bold text-2xl disabled:cursor-not-allowed disabled:opacity-50 h-[52px]"
|
|
onClick={() => router.push(PATH.REGISTER)}
|
|
>
|
|
Đăng ký
|
|
</CustomButton>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</FormCustom>
|
|
</div>
|
|
);
|
|
}
|