Browse Source

feat: add loading state to dashboard page

Nicolas Meienberger 2 years ago
parent
commit
2cd61c03b9

+ 1 - 1
src/client/components/ui/EmptyPage/EmptyPage.tsx

@@ -22,7 +22,7 @@ export const EmptyPage: React.FC<IProps> = ({ title, subtitle, onAction, actionL
       className={clsx(styles.emptyImage, 'mb-3')}
       style={{
         maxWidth: '100%',
-        height: 'auto',
+        height: '80px',
       }}
     />
     <p className="empty-title">{title}</p>

+ 6 - 4
src/client/modules/Dashboard/components/SystemStat.tsx

@@ -1,4 +1,5 @@
 import { Icon } from '@tabler/icons-react';
+import clsx from 'clsx';
 import React from 'react';
 
 interface IProps {
@@ -7,18 +8,19 @@ interface IProps {
   title: string;
   subtitle: string;
   metric: string;
+  isLoading?: boolean;
 }
 
-const SystemStat: React.FC<IProps> = ({ icon: IconComponent, progress, title, subtitle, metric }) => (
+const SystemStat: React.FC<IProps> = ({ icon: IconComponent, progress, title, subtitle, metric, isLoading }) => (
   <div className="col-sm-6 col-lg-4">
     <div className="card">
       <div className="card-body">
         <div className="d-flex justify-content-between align-items-start">
-          <div className="h2 mb-3 font-weight-bold">{title}</div>
+          <div className={clsx('h2 mb-3 font-weight-bold', { placeholder: isLoading })}>{title}</div>
           <IconComponent />
         </div>
-        <div className="h2">{metric}</div>
-        <div className="mb-3 text-muted">{subtitle}</div>
+        <div className={clsx('h2', { 'placeholder col-3': isLoading })}>{metric}</div>
+        <div className={clsx('mb-3 text-muted', { 'placeholder col-11': isLoading })}>{subtitle}</div>
         <div className="progress progress-sm">
           <div
             className="progress-bar bg-primary"

+ 11 - 5
src/client/modules/Dashboard/containers/DashboardContainer.tsx

@@ -4,9 +4,15 @@ import { useTranslations } from 'next-intl';
 import { SystemRouterOutput } from '../../../../server/routers/system/system.router';
 import SystemStat from '../components/SystemStat';
 
-type IProps = { data: SystemRouterOutput['systemInfo'] };
+type IProps = { data?: SystemRouterOutput['systemInfo']; isLoading: boolean };
 
-export const DashboardContainer: React.FC<IProps> = ({ data }) => {
+const defaultData: SystemRouterOutput['systemInfo'] = {
+  cpu: { load: 0 },
+  disk: { available: 0, total: 0, used: 0 },
+  memory: { available: 0, total: 0, used: 0 },
+};
+
+export const DashboardContainer: React.FC<IProps> = ({ data = defaultData, isLoading }) => {
   const { disk, memory, cpu } = data;
   const t = useTranslations('dashboard');
   // Convert bytes to GB
@@ -21,9 +27,9 @@ export const DashboardContainer: React.FC<IProps> = ({ data }) => {
 
   return (
     <div className="row row-deck row-cards">
-      <SystemStat title={t('cards.disk.title')} metric={`${diskUsed} GB`} subtitle={t('cards.disk.subtitle', { total: diskSize })} icon={IconDatabase} progress={percentUsed} />
-      <SystemStat title={t('cards.cpu.title')} metric={`${cpu.load.toFixed(2)}%`} subtitle={t('cards.cpu.subtitle')} icon={IconCpu} progress={cpu.load} />
-      <SystemStat title={t('cards.memory.title')} metric={`${percentUsedMemory || 0}%`} subtitle={`${memoryTotal} GB`} icon={IconCircuitResistor} progress={percentUsedMemory} />
+      <SystemStat isLoading={isLoading} title={t('cards.disk.title')} metric={`${diskUsed} GB`} subtitle={t('cards.disk.subtitle', { total: diskSize })} icon={IconDatabase} progress={percentUsed} />
+      <SystemStat isLoading={isLoading} title={t('cards.cpu.title')} metric={`${cpu.load.toFixed(2)}%`} subtitle={t('cards.cpu.subtitle')} icon={IconCpu} progress={cpu.load} />
+      <SystemStat isLoading={isLoading} title={t('cards.memory.title')} metric={`${percentUsedMemory || 0}%`} subtitle={`${memoryTotal} GB`} icon={IconCircuitResistor} progress={percentUsedMemory} />
     </div>
   );
 };

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

@@ -9,11 +9,11 @@ import { ErrorPage } from '../../../../components/ui/ErrorPage';
 
 export const DashboardPage: NextPage = () => {
   const t = useTranslations();
-  const { data, error } = trpc.system.systemInfo.useQuery();
+  const { data, error, isLoading } = trpc.system.systemInfo.useQuery();
 
   return (
     <Layout title={t('dashboard.title')}>
-      {data && <DashboardContainer data={data} />}
+      <DashboardContainer data={data} isLoading={isLoading} />
       {error && <ErrorPage error={t(error.data?.tError.message as MessageKey, { ...error.data?.tError.variables })} />}
     </Layout>
   );