From 25967e4f19310b175b6530a929c5613766bfebcc Mon Sep 17 00:00:00 2001 From: Nate Kelley Date: Wed, 24 Sep 2025 17:47:56 -0600 Subject: [PATCH] update healthcheck styling --- .../buster_rest/healthcheck/queryRequests.ts | 1 + apps/web/src/routeTree.gen.ts | 21 - apps/web/src/routes/app/_app/healthcheck.tsx | 375 ------------- apps/web/src/routes/healthcheck.tsx | 517 ++++++++++++------ 4 files changed, 359 insertions(+), 555 deletions(-) delete mode 100644 apps/web/src/routes/app/_app/healthcheck.tsx diff --git a/apps/web/src/api/buster_rest/healthcheck/queryRequests.ts b/apps/web/src/api/buster_rest/healthcheck/queryRequests.ts index 6476214d9..92d494f5e 100644 --- a/apps/web/src/api/buster_rest/healthcheck/queryRequests.ts +++ b/apps/web/src/api/buster_rest/healthcheck/queryRequests.ts @@ -5,5 +5,6 @@ export const useHealthcheck = () => { return useQuery({ queryKey: ['healthcheck'], queryFn: getHealthcheck, + refetchInterval: 1000 * 30, // 30 seconds }); }; diff --git a/apps/web/src/routeTree.gen.ts b/apps/web/src/routeTree.gen.ts index 1f30b7bb9..eef2ccee3 100644 --- a/apps/web/src/routeTree.gen.ts +++ b/apps/web/src/routeTree.gen.ts @@ -32,7 +32,6 @@ import { Route as EmbedDashboardDashboardIdRouteImport } from './routes/embed/da import { Route as AppSettingsRestricted_layoutRouteImport } from './routes/app/_settings/_restricted_layout' import { Route as AppSettingsPermissionsRouteImport } from './routes/app/_settings/_permissions' import { Route as AppAppHomeRouteImport } from './routes/app/_app/home' -import { Route as AppAppHealthcheckRouteImport } from './routes/app/_app/healthcheck' import { Route as AppAppAssetRouteImport } from './routes/app/_app/_asset' import { Route as AppSettingsSettingsIndexRouteImport } from './routes/app/_settings/settings.index' import { Route as AppAppReportsIndexRouteImport } from './routes/app/_app/reports.index' @@ -263,11 +262,6 @@ const AppAppHomeRoute = AppAppHomeRouteImport.update({ path: '/home', getParentRoute: () => AppAppRoute, } as any) -const AppAppHealthcheckRoute = AppAppHealthcheckRouteImport.update({ - id: '/healthcheck', - path: '/healthcheck', - getParentRoute: () => AppAppRoute, -} as any) const AppAppAssetRoute = AppAppAssetRouteImport.update({ id: '/_asset', getParentRoute: () => AppAppRoute, @@ -964,7 +958,6 @@ export interface FileRoutesByFullPath { '/auth/reset-password': typeof AuthResetPasswordRoute '/info/getting-started': typeof InfoGettingStartedRoute '/app/': typeof AppIndexRoute - '/app/healthcheck': typeof AppAppHealthcheckRoute '/app/home': typeof AppAppHomeRoute '/embed/dashboard/$dashboardId': typeof EmbedDashboardDashboardIdRoute '/embed/metric/$metricId': typeof EmbedMetricMetricIdRoute @@ -1074,7 +1067,6 @@ export interface FileRoutesByTo { '/auth/logout': typeof AuthLogoutRoute '/auth/reset-password': typeof AuthResetPasswordRoute '/info/getting-started': typeof InfoGettingStartedRoute - '/app/healthcheck': typeof AppAppHealthcheckRoute '/app/home': typeof AppAppHomeRoute '/embed/dashboard/$dashboardId': typeof EmbedDashboardDashboardIdRoute '/embed/metric/$metricId': typeof EmbedMetricMetricIdRoute @@ -1172,7 +1164,6 @@ export interface FileRoutesById { '/info/getting-started': typeof InfoGettingStartedRoute '/app/': typeof AppIndexRoute '/app/_app/_asset': typeof AppAppAssetRouteWithChildren - '/app/_app/healthcheck': typeof AppAppHealthcheckRoute '/app/_app/home': typeof AppAppHomeRoute '/app/_settings/_permissions': typeof AppSettingsPermissionsRouteWithChildren '/app/_settings/_restricted_layout': typeof AppSettingsRestricted_layoutRouteWithChildren @@ -1298,7 +1289,6 @@ export interface FileRouteTypes { | '/auth/reset-password' | '/info/getting-started' | '/app/' - | '/app/healthcheck' | '/app/home' | '/embed/dashboard/$dashboardId' | '/embed/metric/$metricId' @@ -1408,7 +1398,6 @@ export interface FileRouteTypes { | '/auth/logout' | '/auth/reset-password' | '/info/getting-started' - | '/app/healthcheck' | '/app/home' | '/embed/dashboard/$dashboardId' | '/embed/metric/$metricId' @@ -1505,7 +1494,6 @@ export interface FileRouteTypes { | '/info/getting-started' | '/app/' | '/app/_app/_asset' - | '/app/_app/healthcheck' | '/app/_app/home' | '/app/_settings/_permissions' | '/app/_settings/_restricted_layout' @@ -1793,13 +1781,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof AppAppHomeRouteImport parentRoute: typeof AppAppRoute } - '/app/_app/healthcheck': { - id: '/app/_app/healthcheck' - path: '/healthcheck' - fullPath: '/app/healthcheck' - preLoaderRoute: typeof AppAppHealthcheckRouteImport - parentRoute: typeof AppAppRoute - } '/app/_app/_asset': { id: '/app/_app/_asset' path: '' @@ -3017,7 +2998,6 @@ const AppAppDatasetsDatasetIdRouteWithChildren = interface AppAppRouteChildren { AppAppAssetRoute: typeof AppAppAssetRouteWithChildren - AppAppHealthcheckRoute: typeof AppAppHealthcheckRoute AppAppHomeRoute: typeof AppAppHomeRoute AppAppDatasetsDatasetIdRoute: typeof AppAppDatasetsDatasetIdRouteWithChildren AppAppChatsIndexRoute: typeof AppAppChatsIndexRoute @@ -3031,7 +3011,6 @@ interface AppAppRouteChildren { const AppAppRouteChildren: AppAppRouteChildren = { AppAppAssetRoute: AppAppAssetRouteWithChildren, - AppAppHealthcheckRoute: AppAppHealthcheckRoute, AppAppHomeRoute: AppAppHomeRoute, AppAppDatasetsDatasetIdRoute: AppAppDatasetsDatasetIdRouteWithChildren, AppAppChatsIndexRoute: AppAppChatsIndexRoute, diff --git a/apps/web/src/routes/app/_app/healthcheck.tsx b/apps/web/src/routes/app/_app/healthcheck.tsx deleted file mode 100644 index 9c3d38e54..000000000 --- a/apps/web/src/routes/app/_app/healthcheck.tsx +++ /dev/null @@ -1,375 +0,0 @@ -import type { HealthCheckResponse } from '@buster/server-shared/healthcheck'; -import { createFileRoute } from '@tanstack/react-router'; -import { useHealthcheck } from '@/api/buster_rest/healthcheck/queryRequests'; -import { useGetUserBasicInfo } from '@/api/buster_rest/users/useGetUserInfo'; -import type { RustApiError } from '@/api/errors'; -import { useGetSupabaseUser } from '@/context/Supabase'; - -export const Route = createFileRoute('/app/_app/healthcheck')({ - component: RouteComponent, -}); - -function RouteComponent() { - const { data, isLoading, error } = useHealthcheck(); - const supabaseUser = useGetSupabaseUser(); - const user = useGetUserBasicInfo(); - - if (isLoading) { - return ; - } - - if (error) { - return ; - } - - if (!data) { - return ; - } - - return ; -} - -function LoadingState() { - return ( -
-
-
-

Checking system health...

-
-
- ); -} - -function ErrorState({ error }: { error: Error | RustApiError }) { - return ( -
-
-
-
- - - -
-
-

Health Check Failed

-

Unable to retrieve system status

-
-
-
-

{error.message}

-
-
-
- ); -} - -function HealthcheckDashboard({ - data, - supabaseUser, - user, -}: { - data: HealthCheckResponse; - supabaseUser: ReturnType; - user: ReturnType; -}) { - const getStatusColor = (status: string) => { - switch (status) { - case 'healthy': - case 'pass': - return 'text-green-600 bg-green-100'; - case 'degraded': - case 'warn': - return 'text-yellow-600 bg-yellow-100'; - case 'unhealthy': - case 'fail': - return 'text-red-600 bg-red-100'; - default: - return 'text-gray-600 bg-gray-100'; - } - }; - - const getStatusIcon = (status: string) => { - switch (status) { - case 'healthy': - case 'pass': - return ( - - - - ); - case 'degraded': - case 'warn': - return ( - - - - ); - case 'unhealthy': - case 'fail': - return ( - - - - ); - default: - return ( - - - - ); - } - }; - - const formatUptime = (seconds: number) => { - const days = Math.floor(seconds / 86400); - const hours = Math.floor((seconds % 86400) / 3600); - const minutes = Math.floor((seconds % 3600) / 60); - - if (days > 0) { - return `${days}d ${hours}h ${minutes}m`; - } - if (hours > 0) { - return `${hours}h ${minutes}m`; - } - return `${minutes}m`; - }; - - const formatTimestamp = (timestamp: string) => { - return new Date(timestamp).toLocaleString(); - }; - - return ( -
-
- {/* Header */} -
-

System Health Dashboard

-

Real-time monitoring of system components

-
- - {/* User Data Section */} -
- {/* Supabase User Data */} -
-

-
- - - -
- Supabase User -

-
-
-                {JSON.stringify(supabaseUser, null, 2)}
-              
-
-
- - {/* User Basic Info */} -
-

-
- - - -
- User Basic Info -

-
-
-                {JSON.stringify(user, null, 2)}
-              
-
-
-
- - {/* Overall Status Card */} -
-
-
-
- {getStatusIcon(data.status)} -
-
-

{data.status}

-

Overall System Status

-
-
-
-

Last checked

-

{formatTimestamp(data.timestamp)}

-
-
-
- - {/* Metrics Grid */} -
-
-
-
- - - -
-
-

Uptime

-

{formatUptime(data.uptime)}

-
-
-
- -
-
-
- - - -
-
-

Version

-

{data.version}

-
-
-
- -
-
-
- - - -
-
-

Environment

-

{data.environment}

-
-
-
-
- - {/* Component Checks */} -
-

Component Health Checks

-
- {Object.entries(data.checks).map(([componentName, check]) => ( -
-
-
-
- {getStatusIcon(check.status)} -
-
-

{componentName}

- {check.message &&

{check.message}

} -
-
-
- - {check.status} - - {check.responseTime && ( -

{check.responseTime}ms

- )} -
-
-
- ))} -
-
- - {/* Footer */} -
-

System health is monitored continuously. Data refreshes automatically.

-
-
-
- ); -} diff --git a/apps/web/src/routes/healthcheck.tsx b/apps/web/src/routes/healthcheck.tsx index 308c1315d..5816b9a3a 100644 --- a/apps/web/src/routes/healthcheck.tsx +++ b/apps/web/src/routes/healthcheck.tsx @@ -1,14 +1,41 @@ import type { HealthCheckResponse } from '@buster/server-shared/healthcheck'; +import type { User } from '@supabase/supabase-js'; import { createFileRoute } from '@tanstack/react-router'; import { useHealthcheck } from '@/api/buster_rest/healthcheck/queryRequests'; -import type { RustApiError } from '../api/errors'; +import { prefetchGetMyUserInfo } from '@/api/buster_rest/users'; +import { useGetUserBasicInfo } from '@/api/buster_rest/users/useGetUserInfo'; +import type { RustApiError } from '@/api/errors'; +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from '@/components/ui/card/CardBase'; +import { Text } from '@/components/ui/typography/Text'; +import { useAppVersionMeta } from '@/context/AppVersion/useAppVersion'; +import { getSupabaseUser } from '@/integrations/supabase/getSupabaseUserClient'; +import { formatDate } from '@/lib/date'; export const Route = createFileRoute('/healthcheck')({ component: RouteComponent, + beforeLoad: async () => { + const supabaseUser = await getSupabaseUser(); + return { supabaseUser }; + }, + loader: async ({ context }) => { + await prefetchGetMyUserInfo(context.queryClient); + const { supabaseUser } = context; + return { supabaseUser }; + }, }); function RouteComponent() { const { data, isLoading, error } = useHealthcheck(); + const supabaseUser = Route.useLoaderData().supabaseUser; + const user = useGetUserBasicInfo(); + const appVersionData = useAppVersionMeta(); if (isLoading) { return ; @@ -22,65 +49,92 @@ function RouteComponent() { return ; } - return ; + return ( + + ); } function LoadingState() { return (
-
-
-

Checking system health...

-
+ + +
+ + Checking system health... + +
+
); } function ErrorState({ error }: { error: Error | RustApiError }) { return ( -
-
-
-
- - - +
+ + +
+
+ + + +
+
+ Health Check Failed + Unable to retrieve system status +
-
-

Health Check Failed

-

Unable to retrieve system status

+ + +
+ + {error.message} +
-
-
-

{error.message}

-
-
+ +
); } -function HealthcheckDashboard({ data }: { data: HealthCheckResponse }) { +function HealthcheckDashboard({ + data, + supabaseUser, + user, + appVersionData, +}: { + data: HealthCheckResponse; + supabaseUser: Pick; + user: ReturnType; + appVersionData: ReturnType; +}) { const getStatusColor = (status: string) => { switch (status) { case 'healthy': case 'pass': - return 'text-green-600 bg-green-100'; + return 'text-gray-700 bg-gray-200'; case 'degraded': case 'warn': - return 'text-yellow-600 bg-yellow-100'; + return 'text-gray-600 bg-gray-100'; case 'unhealthy': case 'fail': - return 'text-red-600 bg-red-100'; + return 'text-gray-800 bg-gray-300'; default: return 'text-gray-600 bg-gray-100'; } @@ -148,157 +202,302 @@ function HealthcheckDashboard({ data }: { data: HealthCheckResponse }) { }; const formatTimestamp = (timestamp: string) => { - return new Date(timestamp).toLocaleString(); + return formatDate({ date: timestamp, format: 'LLL' }); }; return ( -
-
+
+
{/* Header */} -
-

System Health Dashboard

-

Real-time monitoring of system components

+
+ + System Health Dashboard + + + Real-time monitoring of system components + +
+ + {/* User Data Section */} +
+ {/* Supabase User Data */} + + + +
+ + + +
+ Supabase User +
+
+ +
+
+                  {JSON.stringify(supabaseUser, null, 2)}
+                
+
+
+
+ + {/* User Basic Info */} + + + +
+ + + +
+ User Basic Info +
+
+ +
+
+                  {JSON.stringify(user, null, 2)}
+                
+
+
+
{/* Overall Status Card */} -
-
-
-
- {getStatusIcon(data.status)} + + +
+
+
+ {getStatusIcon(data.status)} +
+
+ + {data.status} + + + Overall System Status + +
-
-

{data.status}

-

Overall System Status

+
+ + Last checked + + + {formatTimestamp(data.timestamp)} +
-
-

Last checked

-

{formatTimestamp(data.timestamp)}

-
-
-
+ + {/* Metrics Grid */} -
-
-
-
- - - +
+ + +
+
+ + + +
+
+ + Uptime + + + {formatUptime(data.uptime)} + +
-
-

Uptime

-

{formatUptime(data.uptime)}

-
-
-
+ + -
-
-
- - - + + +
+
+ + + +
+
+ + Server Version + + + {data.version} + +
-
-

Version

-

{data.version}

-
-
-
+ + -
-
-
- - - + + +
+
+ + + +
+
+ + Environment + + + {data.environment} + +
-
-

Environment

-

{data.environment}

+ + + + + +
+
+ + + +
+
+ + App Builds + + + Browser: {appVersionData.browserBuild || 'N/A'} + + + + Server: {appVersionData.buildId} + +
-
-
+ +
{/* Component Checks */} -
-

Component Health Checks

-
- {Object.entries(data.checks).map(([componentName, check]) => ( -
-
-
-
- {getStatusIcon(check.status)} + + + Component Health Checks + + +
+ {Object.entries(data.checks).map(([componentName, check]) => ( +
+
+
+
+ {getStatusIcon(check.status)} +
+
+ + {componentName} + + {check.message && ( + + {check.message} + + )} +
-
-

{componentName}

- {check.message &&

{check.message}

} +
+ + {check.status} + + {check.responseTime && ( + + {check.responseTime}ms + + )}
-
- - {check.status} - - {check.responseTime && ( -

{check.responseTime}ms

- )} -
-
- ))} -
-
+ ))} +
+ + {/* Footer */} -
-

System health is monitored continuously. Data refreshes automatically.

+
+ + System health is monitored continuously. Data refreshes automatically. +