import Footer from '@/components/Footer'; import LangSwitches from '@/components/Lang/LanguageSwitcherAuth'; import ThemeSwitcherAuth from '@/components/Theme/ThemeSwitcherAuth'; import { THEME_KEY } from '@/constants'; import { ROUTER_HOME } from '@/constants/routes'; import { apiForgotPassword, apiLogin, apiQueryProfile, } from '@/services/master/AuthController'; import { checkRefreshTokenExpired } from '@/utils/jwt'; import { getDomainTitle, getLogoImage } from '@/utils/logo'; import { getBrowserId, getRefreshToken, removeAccessToken, removeRefreshToken, setAccessToken, setRefreshToken, } from '@/utils/storage'; import { LockOutlined, UserOutlined } from '@ant-design/icons'; import { LoginFormPage, ProFormText } from '@ant-design/pro-components'; import { FormattedMessage, history, useIntl, useModel } from '@umijs/max'; import { Button, ConfigProvider, Flex, Image, message, theme } from 'antd'; import { CSSProperties, useEffect, useState } from 'react'; import { flushSync } from 'react-dom'; import mobifontLogo from '../../../public/mobifont-logo.png'; type LoginType = 'login' | 'forgot'; // Form wrapper with animation const FormWrapper = ({ children, key, }: { children: React.ReactNode; key: string; }) => { const style: CSSProperties = { animation: 'fadeInSlide 0.4s ease-out forwards', }; return (
{children}
); }; const LoginPage = () => { const [isDark, setIsDark] = useState( (localStorage.getItem(THEME_KEY) as 'light' | 'dark') === 'dark', ); const { token } = theme.useToken(); const [messageApi, contextHolder] = message.useMessage(); const urlParams = new URL(window.location.href).searchParams; const redirect = urlParams.get('redirect'); const intl = useIntl(); const { setInitialState } = useModel('@@initialState'); const [loginType, setLoginType] = useState('login'); // Listen for theme changes from ThemeSwitcherAuth useEffect(() => { const handleThemeChange = (e: Event) => { const customEvent = e as CustomEvent<{ theme: 'light' | 'dark' }>; setIsDark(customEvent.detail.theme === 'dark'); }; window.addEventListener('theme-change', handleThemeChange as EventListener); return () => { window.removeEventListener( 'theme-change', handleThemeChange as EventListener, ); }; }, []); const checkLogin = async () => { const refreshToken = getRefreshToken(); if (!refreshToken) { return; } const isRefreshTokenExpired = checkRefreshTokenExpired(refreshToken); if (isRefreshTokenExpired) { removeAccessToken(); removeRefreshToken(); return; } else { const userInfo = await apiQueryProfile(); if (userInfo) { flushSync(() => { setInitialState((s: any) => ({ ...s, currentUserProfile: userInfo, })); }); } if (redirect) { history.push(redirect); } else { history.push(ROUTER_HOME); } } }; useEffect(() => { checkLogin(); }, []); const handleLogin = async (values: MasterModel.LoginRequestBody) => { const { email, password } = values; if (loginType === 'login') { try { const resp = await apiLogin({ guid: getBrowserId(), email, password, }); if (resp?.token) { setAccessToken(resp.token); setRefreshToken(resp.refresh_token); const userInfo = await apiQueryProfile(); if (userInfo) { flushSync(() => { setInitialState((s: any) => ({ ...s, currentUserProfile: userInfo, })); }); } if (redirect) { history.push(redirect); } else { history.push(ROUTER_HOME); } } } catch (error) { console.error('Login error:', error); } } else { try { const host = window.location.origin; const body: MasterModel.ForgotPasswordRequestBody = { email: email, host: host, }; const resp = await apiForgotPassword(body); if (!resp.error) { messageApi.success( intl.formatMessage({ id: 'master.auth.forgot.message.success', defaultMessage: 'Request sent successfully, please check your email!', }), ); } } catch (error) { console.error('Error when send reset password: ', error); messageApi.error( intl.formatMessage({ id: 'master.auth.forgot.message.fail', defaultMessage: 'Request failed, please try again later!', }), ); } } }; return (
{contextHolder} {intl.formatMessage({ id: getDomainTitle(), defaultMessage: 'Smatec', })} } containerStyle={{ backgroundColor: 'rgba(0, 0, 0,0.65)', backdropFilter: 'blur(4px)', }} subTitle={} submitter={{ searchConfig: { submitText: loginType === 'login' ? intl.formatMessage({ id: 'master.auth.login.title', defaultMessage: 'Đăng nhập', }) : intl.formatMessage({ id: 'master.auth.forgot.button.title', defaultMessage: 'Đăng nhập', }), }, }} onFinish={async (values: MasterModel.LoginRequestBody) => handleLogin(values) } > {loginType === 'login' && ( <> ), }} placeholder={intl.formatMessage({ id: 'master.auth.login.email', defaultMessage: 'Email', })} rules={[ { required: true, message: ( ), }, { type: 'email', message: ( ), }, ]} /> , }} placeholder={intl.formatMessage({ id: 'master.auth.password', defaultMessage: 'Mật khẩu', })} rules={[ { required: true, message: intl.formatMessage({ id: 'master.auth.validation.password', defaultMessage: 'Mật khẩu không được để trống!', }), }, ]} /> )} {loginType === 'forgot' && ( <> , }} placeholder={intl.formatMessage({ id: 'master.auth.login.email', defaultMessage: 'Email', })} rules={[ { required: true, message: ( ), }, { type: 'email', message: ( ), }, ]} /> )}
); }; export default LoginPage;