♻️ refactor: Login and Registration component Improvement (#2716)

* ♻️ refactor: Login form improvement

* display error message when API is down
* add loading animation to Login form while fetching data
* optimize startupConfig to fetch data only on initial render to prevent unnecessary API calls

* 🚑 fix: clear authentication error messages on successful login

* ♻️ refactor: componentize duplicate codes on registration and login screens

* chore: update types

* refactor: layout rendering order

* refactor: startup title fix

* refactor: reset/request-reset-password under new AuthLayout

* ci: fix Login.spec.ts

* ci: fix registration.spec.tsx

---------

Co-authored-by: Danny Avila <danny@librechat.ai>
This commit is contained in:
Yuichi Oneda 2024-05-28 05:25:07 -07:00 committed by GitHub
parent 2b7a973a33
commit 9f2538fcd9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 775 additions and 750 deletions

View file

@ -0,0 +1,7 @@
import { useAuthContext } from '~/hooks/AuthContext';
import StartupLayout from './Startup';
export default function LoginLayout() {
const { isAuthenticated } = useAuthContext();
return <StartupLayout isAuthenticated={isAuthenticated} />;
}

View file

@ -0,0 +1,70 @@
import { useEffect, useState } from 'react';
import { Outlet, useNavigate, useLocation } from 'react-router-dom';
import { useGetStartupConfig } from 'librechat-data-provider/react-query';
import type { TStartupConfig } from 'librechat-data-provider';
import AuthLayout from '~/components/Auth/AuthLayout';
import { useLocalize } from '~/hooks';
const headerMap = {
'/login': 'com_auth_welcome_back',
'/register': 'com_auth_create_account',
'/forgot-password': 'com_auth_reset_password',
'/reset-password': 'com_auth_reset_password',
};
export default function StartupLayout({ isAuthenticated }: { isAuthenticated?: boolean }) {
const [error, setError] = useState<string | null>(null);
const [headerText, setHeaderText] = useState<string | null>(null);
const [startupConfig, setStartupConfig] = useState<TStartupConfig | null>(null);
const {
data,
isFetching,
error: startupConfigError,
} = useGetStartupConfig({
enabled: isAuthenticated ? startupConfig === null : true,
});
const localize = useLocalize();
const navigate = useNavigate();
const location = useLocation();
useEffect(() => {
if (isAuthenticated) {
navigate('/c/new', { replace: true });
}
if (data) {
setStartupConfig(data);
}
}, [isAuthenticated, navigate, data]);
useEffect(() => {
document.title = startupConfig?.appTitle || 'LibreChat';
}, [startupConfig?.appTitle]);
useEffect(() => {
setError(null);
setHeaderText(null);
}, [location.pathname]);
const contextValue = {
error,
setError,
headerText,
setHeaderText,
startupConfigError,
startupConfig,
isFetching,
};
return (
<AuthLayout
header={headerText ? localize(headerText) : localize(headerMap[location.pathname])}
isFetching={isFetching}
startupConfig={startupConfig}
startupConfigError={startupConfigError}
pathname={location.pathname}
error={error}
>
<Outlet context={contextValue} />
</AuthLayout>
);
}

View file

@ -7,6 +7,8 @@ import {
ApiErrorWatcher,
} from '~/components/Auth';
import { AuthContextProvider } from '~/hooks/AuthContext';
import StartupLayout from './Layouts/Startup';
import LoginLayout from './Layouts/Login';
import ShareRoute from './ShareRoute';
import ChatRoute from './ChatRoute';
import Search from './Search';
@ -20,28 +22,40 @@ const AuthLayout = () => (
);
export const router = createBrowserRouter([
{
path: 'register',
element: <Registration />,
},
{
path: 'forgot-password',
element: <RequestPasswordReset />,
},
{
path: 'reset-password',
element: <ResetPassword />,
},
{
path: 'share/:shareId',
element: <ShareRoute />,
},
{
path: '/',
element: <StartupLayout />,
children: [
{
path: 'register',
element: <Registration />,
},
{
path: 'forgot-password',
element: <RequestPasswordReset />,
},
{
path: 'reset-password',
element: <ResetPassword />,
},
],
},
{
element: <AuthLayout />,
children: [
{
path: 'login',
element: <Login />,
path: '/',
element: <LoginLayout />,
children: [
{
path: 'login',
element: <Login />,
},
],
},
{
path: '/',