Bläddra i källkod

refactor(client): remove layoutv2 abstraction

Nicolas Meienberger 2 år sedan
förälder
incheckning
a47606b472

+ 0 - 98
packages/dashboard/src/client/components/Layout/LayoutV2.tsx

@@ -1,98 +0,0 @@
-import Head from 'next/head';
-import Link from 'next/link';
-import React, { useEffect } from 'react';
-import clsx from 'clsx';
-import ReactTooltip from 'react-tooltip';
-import semver from 'semver';
-import { Header } from '../ui/Header';
-import styles from './Layout.module.scss';
-import { ErrorPage } from '../ui/ErrorPage';
-import { trpc } from '../../utils/trpc';
-
-interface IProps {
-  loading?: boolean;
-  loadingComponent?: React.ReactNode;
-  error?: string;
-  breadcrumbs?: { name: string; href: string; current?: boolean }[];
-  children: React.ReactNode;
-  title?: string;
-  actions?: React.ReactNode;
-  data: unknown;
-}
-
-export const Layout: React.FC<IProps> = ({ children, breadcrumbs, title, actions, loading, error, loadingComponent, data }) => {
-  const refreshToken = trpc.auth.refreshToken.useMutation({
-    onSuccess: (d) => {
-      if (d?.token) localStorage.setItem('token', d.token);
-    },
-  });
-
-  useEffect(() => {
-    refreshToken.mutate();
-  }, []);
-
-  const { data: dataVersion } = trpc.system.getVersion.useQuery(undefined, { networkMode: 'online' });
-
-  const defaultVersion = '0.0.0';
-  const isLatest = semver.gte(dataVersion?.current || defaultVersion, dataVersion?.latest || defaultVersion);
-
-  const renderBreadcrumbs = () => {
-    if (!breadcrumbs) {
-      return null;
-    }
-
-    return (
-      <ol className="breadcrumb" aria-label="breadcrumbs">
-        {breadcrumbs.map((breadcrumb) => (
-          <li key={breadcrumb.name} data-testid="breadcrumb-item" className={clsx('breadcrumb-item', { active: breadcrumb.current })}>
-            <Link data-testid="breadcrumb-link" href={breadcrumb.href}>
-              {breadcrumb.name}
-            </Link>
-          </li>
-        ))}
-      </ol>
-    );
-  };
-
-  const renderContent = () => {
-    if (loading) {
-      return loadingComponent;
-    }
-
-    if (error) {
-      return <ErrorPage error={error} />;
-    }
-
-    if (data) {
-      return children;
-    }
-
-    return null;
-  };
-
-  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" />
-      <Header isUpdateAvailable={!isLatest} />
-      <div className="page-wrapper">
-        <div className="page-header d-print-none">
-          <div className="container-xl">
-            <div className={clsx('align-items-stretch align-items-md-center d-flex flex-column flex-md-row ', styles.topActions)}>
-              <div className="me-3 text-white">
-                <div className="page-pretitle">{renderBreadcrumbs()}</div>
-                <h2 className="page-title">{title}</h2>
-              </div>
-              <div className="flex-fill">{actions}</div>
-            </div>
-          </div>
-        </div>
-        <div className="page-body">
-          <div className="container-xl">{renderContent()}</div>
-        </div>
-      </div>
-    </div>
-  );
-};

+ 2 - 10
packages/dashboard/src/client/modules/Dashboard/containers/DashboardContainer.tsx

@@ -1,13 +1,11 @@
 import { IconCircuitResistor, IconCpu, IconDatabase } from '@tabler/icons';
 import React from 'react';
-import { Layout } from '../../../components/Layout/LayoutV2';
 import { SystemRouterOutput } from '../../../../server/routers/system/system.router';
 import SystemStat from '../components/SystemStat';
-import { ContainerProps } from '../../../types/layout-helpers';
 
-type IProps = { data?: SystemRouterOutput['systemInfo'] };
+type IProps = { data: SystemRouterOutput['systemInfo'] };
 
-const DashboardWithData: React.FC<Required<IProps>> = ({ data }) => {
+export const DashboardContainer: React.FC<IProps> = ({ data }) => {
   const { disk, memory, cpu } = data;
   // Convert bytes to GB
   const diskFree = Math.round(disk.available / 1024 / 1024 / 1024);
@@ -27,9 +25,3 @@ const DashboardWithData: React.FC<Required<IProps>> = ({ data }) => {
     </div>
   );
 };
-
-export const DashboardContainer: React.FC<ContainerProps<IProps>> = ({ data, loading, error }) => (
-  <Layout data={data} loading={loading} error={error} title="Dashboard">
-    <DashboardWithData data={data!} />
-  </Layout>
-);

+ 9 - 2
packages/dashboard/src/client/modules/Dashboard/pages/DashboardPage/DashboardPage.tsx

@@ -2,9 +2,16 @@ import React from 'react';
 import type { NextPage } from 'next';
 import { DashboardContainer } from '../../containers/DashboardContainer';
 import { trpc } from '../../../../utils/trpc';
+import { Layout } from '../../../../components/Layout';
+import { ErrorPage } from '../../../../components/ui/ErrorPage';
 
 export const DashboardPage: NextPage = () => {
-  const { data, isLoading, error } = trpc.system.systemInfo.useQuery();
+  const { data, error } = trpc.system.systemInfo.useQuery();
 
-  return <DashboardContainer data={data} loading={isLoading} error={error?.message} />;
+  return (
+    <Layout title="Dashboard">
+      {data && <DashboardContainer data={data} />}
+      {error && <ErrorPage error={error.message} />}
+    </Layout>
+  );
 };

+ 0 - 9
packages/dashboard/src/client/modules/Settings/containers/SettingsContainer/SettingsContainer.test.tsx

@@ -39,15 +39,6 @@ describe('Test: SettingsContainer', () => {
       expect(screen.getByText(`Update to ${latest}`)).toBeInTheDocument();
       expect(screen.getByText(`Update to ${latest}`)).not.toBeDisabled();
     });
-
-    it('should display error page if error is present', () => {
-      const current = faker.system.semver();
-      const error = faker.lorem.sentence();
-
-      render(<SettingsContainer data={{ current }} error={error} />);
-
-      expect(screen.getByText(error)).toBeInTheDocument();
-    });
   });
 
   describe('Update', () => {

+ 3 - 11
packages/dashboard/src/client/modules/Settings/containers/SettingsContainer/SettingsContainer.tsx

@@ -6,13 +6,11 @@ import { SystemRouterOutput } from '../../../../../server/routers/system/system.
 import { useToastStore } from '../../../../state/toastStore';
 import { RestartModal } from '../../components/RestartModal';
 import { UpdateModal } from '../../components/UpdateModal/UpdateModal';
-import { Layout } from '../../../../components/Layout/LayoutV2';
-import { ContainerProps } from '../../../../types/layout-helpers';
 import { trpc } from '../../../../utils/trpc';
 
-type IProps = { data?: SystemRouterOutput['getVersion'] };
+type IProps = { data: SystemRouterOutput['getVersion'] };
 
-const SettingsContainerWithData: React.FC<Required<IProps>> = ({ data }) => {
+export const SettingsContainer: React.FC<IProps> = ({ data }) => {
   const [loading, setLoading] = React.useState(false);
   const { current, latest } = data;
   const { addToast } = useToastStore();
@@ -81,7 +79,7 @@ const SettingsContainerWithData: React.FC<Required<IProps>> = ({ data }) => {
         <div className="col d-flex flex-column">
           <div className="card-body">
             <h2 className="mb-4">Actions</h2>
-            <h3 className="card-title mt-4">Version</h3>
+            <h3 className="card-title mt-4">Version {current}</h3>
             <p className="card-subtitle">Stay up to date with the latest version of Tipi</p>
             {renderUpdate()}
             <h3 className="card-title mt-4">Maintenance</h3>
@@ -97,9 +95,3 @@ const SettingsContainerWithData: React.FC<Required<IProps>> = ({ data }) => {
     </div>
   );
 };
-
-export const SettingsContainer: React.FC<ContainerProps<IProps>> = ({ data, loading, error }) => (
-  <Layout title="Settings" data={data} loading={loading} error={error}>
-    <SettingsContainerWithData data={data!} />
-  </Layout>
-);

+ 14 - 0
packages/dashboard/src/client/modules/Settings/pages/SettingsPage/SettingsPage.test.tsx

@@ -1,5 +1,8 @@
+import { faker } from '@faker-js/faker';
 import React from 'react';
 import { render, screen, waitFor } from '../../../../../../tests/test-utils';
+import { getTRPCMockError } from '../../../../mocks/getTrpcMock';
+import { server } from '../../../../mocks/server';
 import { SettingsPage } from './SettingsPage';
 
 describe('Test: SettingsPage', () => {
@@ -8,4 +11,15 @@ describe('Test: SettingsPage', () => {
 
     await waitFor(() => expect(screen.getByTestId('settings-layout')).toBeInTheDocument());
   });
+
+  it('should display error page if error is present', async () => {
+    const error = faker.lorem.sentence();
+    server.use(getTRPCMockError({ path: ['system', 'getVersion'], message: error }));
+
+    render(<SettingsPage />);
+
+    await waitFor(() => {
+      expect(screen.getByText(error)).toBeInTheDocument();
+    });
+  });
 });

+ 10 - 2
packages/dashboard/src/client/modules/Settings/pages/SettingsPage/SettingsPage.tsx

@@ -2,9 +2,17 @@ import React from 'react';
 import type { NextPage } from 'next';
 import { SettingsContainer } from '../../containers/SettingsContainer/SettingsContainer';
 import { trpc } from '../../../../utils/trpc';
+import { Layout } from '../../../../components/Layout';
+import { ErrorPage } from '../../../../components/ui/ErrorPage';
 
 export const SettingsPage: NextPage = () => {
-  const { data, isLoading, error } = trpc.system.getVersion.useQuery(undefined, { staleTime: 0 });
+  const { data, error } = trpc.system.getVersion.useQuery(undefined, { staleTime: 0 });
 
-  return <SettingsContainer data={data} loading={isLoading} error={error?.message} />;
+  // TODO: add loading state
+  return (
+    <Layout title="Settings">
+      {data && <SettingsContainer data={data} />}
+      {error && <ErrorPage error={error.message} />}
+    </Layout>
+  );
 };