refactor(dashboard): change layout and page of auth to be url based instead of state based

This commit is contained in:
Nicolas Meienberger 2023-02-21 22:59:06 +01:00
parent 5df9adf3f3
commit 372a76f485
11 changed files with 25 additions and 59 deletions

View file

@ -31,6 +31,7 @@ module.exports = {
'react/button-has-type': 0,
'import/no-extraneous-dependencies': ['error', { devDependencies: ['esbuild.js', '**/*.test.{ts,tsx}', '**/*.spec.{ts,tsx}', '**/*.factory.{ts,tsx}', '**/mocks/**', 'tests/**'] }],
'no-underscore-dangle': 0,
'arrow-body-style': 0,
},
globals: {
JSX: true,

View file

@ -4,6 +4,7 @@ import React, { useEffect } from 'react';
import clsx from 'clsx';
import ReactTooltip from 'react-tooltip';
import semver from 'semver';
import { useRouter } from 'next/router';
import { Header } from '../ui/Header';
import styles from './Layout.module.scss';
import { useSystemStore } from '../../state/systemStore';
@ -18,10 +19,15 @@ interface IProps {
}
export const Layout: React.FC<IProps> = ({ children, breadcrumbs, title, actions }) => {
const router = useRouter();
const refreshToken = trpc.auth.refreshToken.useMutation({
onSuccess: (data) => {
if (data?.token) localStorage.setItem('token', data.token);
},
onError: () => {
localStorage.removeItem('token');
router.push('/login');
},
});
useEffect(() => {
@ -52,10 +58,8 @@ export const Layout: React.FC<IProps> = ({ children, breadcrumbs, title, actions
return (
<div data-testid={`${title?.toLowerCase().split(' ').join('-')}-layout`} className="page">
<Head>
<title>{title} - Tipi</title>
</Head>
<ReactTooltip offset={{ right: 1 }} effect="solid" place="bottom" />
<Head>{/* <title>{title} - Tipi</title> */}</Head>
{/* <ReactTooltip offset={{ right: 1 }} effect="solid" place="bottom" /> */}
<Header isUpdateAvailable={!isLatest} />
<div className="page-wrapper">
<div className="page-header d-print-none">

View file

@ -3,6 +3,7 @@ import { IconBrandGithub, IconHeart, IconLogout, IconMoon, IconSun } from '@tabl
import Image from 'next/image';
import clsx from 'clsx';
import Link from 'next/link';
import { useRouter } from 'next/router';
import { getUrl } from '../../../core/helpers/url-helpers';
import { useUIStore } from '../../../state/uiStore';
import { NavBar } from '../NavBar';
@ -13,12 +14,14 @@ interface IProps {
}
export const Header: React.FC<IProps> = ({ isUpdateAvailable }) => {
const router = useRouter();
const { setDarkMode } = useUIStore();
const utils = trpc.useContext();
const logout = trpc.auth.logout.useMutation({
onSuccess: () => {
localStorage.removeItem('token');
utils.auth.me.invalidate();
router.push('/login');
},
});

View file

@ -1,28 +0,0 @@
import { useEffect, useState, useCallback } from 'react';
interface IReturnProps {
isLoadingComplete?: boolean;
}
export default function useCachedResources(): IReturnProps {
const [isLoadingComplete, setLoadingComplete] = useState(false);
const loadResourcesAndDataAsync = useCallback(() => {
try {
// Load any resources or data that we need prior to rendering the app
} catch (error) {
// We might want to provide this error information to an error reporting service
console.error(error);
}
}, []);
useEffect(() => {
loadResourcesAndDataAsync();
}, [loadResourcesAndDataAsync]);
useEffect(() => {
setLoadingComplete(true);
}, []);
return { isLoadingComplete };
}

View file

@ -2,6 +2,7 @@ import React from 'react';
import { useForm } from 'react-hook-form';
import z from 'zod';
import { zodResolver } from '@hookform/resolvers/zod';
import Link from 'next/link';
import { Button } from '../../../../components/ui/Button';
import { Input } from '../../../../components/ui/Input';
@ -36,6 +37,9 @@ export const LoginForm: React.FC<IProps> = ({ loading, onSubmit }) => {
<form className="flex flex-col" onSubmit={handleSubmit(onSubmit)}>
<h2 className="h2 text-center mb-3">Login to your account</h2>
<Input {...register('email')} label="Email address" error={errors.email?.message} disabled={loading} type="email" className="mb-3" placeholder="you@example.com" />
<span className="form-label-description">
<Link href="/reset-password">Forgot password?</Link>
</span>
<Input {...register('password')} label="Password" error={errors.password?.message} disabled={loading} type="password" className="mb-3" placeholder="Your password" />
<Button disabled={isDisabled} loading={loading} type="submit" className="btn btn-primary w-100">
Login

View file

@ -1,3 +1,4 @@
import { useRouter } from 'next/router';
import React from 'react';
import { useToastStore } from '../../../../state/toastStore';
import { trpc } from '../../../../utils/trpc';
@ -7,6 +8,7 @@ import { LoginForm } from '../../components/LoginForm';
type FormValues = { email: string; password: string };
export const LoginContainer: React.FC = () => {
const router = useRouter();
const { addToast } = useToastStore();
const utils = trpc.useContext();
const login = trpc.auth.login.useMutation({
@ -17,6 +19,7 @@ export const LoginContainer: React.FC = () => {
onSuccess: (data) => {
localStorage.setItem('token', data.token);
utils.auth.me.invalidate();
router.push('/');
},
});

View file

@ -1,3 +1,4 @@
import { useRouter } from 'next/router';
import React from 'react';
import { useToastStore } from '../../../../state/toastStore';
import { trpc } from '../../../../utils/trpc';
@ -8,6 +9,7 @@ type FormValues = { email: string; password: string };
export const RegisterContainer: React.FC = () => {
const { addToast } = useToastStore();
const router = useRouter();
const utils = trpc.useContext();
const register = trpc.auth.register.useMutation({
onError: (e) => {
@ -17,6 +19,7 @@ export const RegisterContainer: React.FC = () => {
onSuccess: (data) => {
localStorage.setItem('token', data.token);
utils.auth.me.invalidate();
router.push('/');
},
});

View file

@ -1,16 +0,0 @@
import React from 'react';
import { ResetPasswordContainer } from '../containers/ResetPasswordContainer/ResetPasswordContainer';
import { trpc } from '../../../utils/trpc';
import { ErrorPage } from '../../../components/ui/ErrorPage';
export const ResetPasswordPage = () => {
const { data, error } = trpc.auth.checkPasswordChangeRequest.useQuery();
// TODO: Add loading state
return (
<>
{error && <ErrorPage error={error.message} />}
<ResetPasswordContainer isRequested={Boolean(data)} />
</>
);
};

View file

@ -1,14 +1,11 @@
import React, { useEffect } from 'react';
import type { AppProps } from 'next/app';
import Head from 'next/head';
import useCachedResources from '../client/hooks/useCachedRessources';
import '../client/styles/global.css';
import '../client/styles/global.scss';
import { useUIStore } from '../client/state/uiStore';
import { ToastProvider } from '../client/components/hoc/ToastProvider';
import { StatusProvider } from '../client/components/hoc/StatusProvider';
import { AuthProvider } from '../client/components/hoc/AuthProvider';
import { StatusScreen } from '../client/components/StatusScreen';
import { trpc } from '../client/utils/trpc';
import { SystemStatus, useSystemStore } from '../client/state/systemStore';
@ -39,11 +36,6 @@ function MyApp({ Component, pageProps }: AppProps) {
themeCheck();
}, [setDarkMode]);
const { isLoadingComplete } = useCachedResources();
if (!isLoadingComplete) {
return <StatusScreen title="" subtitle="" />;
}
return (
<main className="h-100">
<Head>
@ -51,9 +43,7 @@ function MyApp({ Component, pageProps }: AppProps) {
</Head>
<ToastProvider>
<StatusProvider>
<AuthProvider>
<Component {...pageProps} />
</AuthProvider>
<Component {...pageProps} />
</StatusProvider>
</ToastProvider>
</main>

1
src/pages/login.tsx Normal file
View file

@ -0,0 +1 @@
export { LoginPage as default } from '../client/modules/Auth/pages/LoginPage';

1
src/pages/register.tsx Normal file
View file

@ -0,0 +1 @@
export { RegisterPage as default } from '../client/modules/Auth/pages/RegisterPage';