From 316b95467d49b3ebe93d03006d4b90f9ca482262 Mon Sep 17 00:00:00 2001 From: moonrailgun Date: Sun, 13 Oct 2024 23:19:40 +0800 Subject: [PATCH] feat: add MonitorLatestResponse and up status summary --- src/client/components/HealthBar.tsx | 10 +- .../components/monitor/StatusPage/Body.tsx | 124 ++++++++++++------ src/client/utils/health.spec.ts | 45 +++++++ src/client/utils/health.ts | 36 +++++ 4 files changed, 168 insertions(+), 47 deletions(-) create mode 100644 src/client/utils/health.spec.ts create mode 100644 src/client/utils/health.ts diff --git a/src/client/components/HealthBar.tsx b/src/client/components/HealthBar.tsx index 8a4dbff..795320d 100644 --- a/src/client/components/HealthBar.tsx +++ b/src/client/components/HealthBar.tsx @@ -1,10 +1,9 @@ import { useResizeObserver } from '@/hooks/useResizeObserver'; +import { getStatusBgColorClassName, HealthStatus } from '@/utils/health'; import { cn } from '@/utils/style'; import clsx from 'clsx'; import React from 'react'; -export type HealthStatus = 'health' | 'error' | 'warning' | 'none'; - export interface HealthBarBeat { title?: string; status: HealthStatus; @@ -52,12 +51,7 @@ export const HealthBar: React.FC = React.memo((props) => { 'h-4 w-[5px]': size === 'small', 'h-8 w-2': size === 'large', }, - { - 'bg-green-500': beat.status === 'health', - 'bg-red-600': beat.status === 'error', - 'bg-yellow-400': beat.status === 'warning', - 'bg-gray-400': beat.status === 'none', - } + getStatusBgColorClassName(beat.status) )} /> ))} diff --git a/src/client/components/monitor/StatusPage/Body.tsx b/src/client/components/monitor/StatusPage/Body.tsx index 61ec953..8c1276b 100644 --- a/src/client/components/monitor/StatusPage/Body.tsx +++ b/src/client/components/monitor/StatusPage/Body.tsx @@ -3,11 +3,18 @@ import React, { useMemo } from 'react'; import { bodySchema } from './schema'; import { Empty } from 'antd'; import { useTranslation } from '@i18next-toolkit/react'; -import { MonitorListItem } from '../MonitorListItem'; import { cn } from '@/utils/style'; -import { Tooltip } from '@/components/ui/tooltip'; -import { MonitorHealthBar } from '../MonitorHealthBar'; -import { HealthBar, HealthStatus } from '@/components/HealthBar'; +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from '@/components/ui/tooltip'; +import { HealthBar } from '@/components/HealthBar'; +import { getMonitorProvider, getProviderDisplay } from '../provider'; +import { + getStatusBgColorClassName, + parseHealthStatusByPercent, +} from '@/utils/health'; interface StatusPageBodyProps { workspaceId: string; @@ -66,7 +73,6 @@ export const StatusItemMonitor: React.FC<{ showCurrent: boolean; workspaceId: string; }> = React.memo((props) => { - const { t } = useTranslation(); const { data: info } = trpc.monitor.getPublicInfo.useQuery( { monitorIds: [props.id], @@ -81,6 +87,22 @@ export const StatusItemMonitor: React.FC<{ monitorId: props.id, }); + const { summaryStatus, summaryPercent } = useMemo(() => { + let upCount = 0; + let totalCount = 0; + list.forEach((item) => { + upCount += item.upCount; + totalCount += item.totalCount; + }); + + const percent = Number(((upCount / totalCount) * 100).toFixed(1)); + + return { + summaryPercent: percent, + summaryStatus: parseHealthStatusByPercent(percent, totalCount), + }; + }, [list]); + if (isLoading) { return null; } @@ -91,45 +113,38 @@ export const StatusItemMonitor: React.FC<{ 'mb-1 flex items-center overflow-hidden rounded-lg bg-green-500 bg-opacity-0 px-4 py-3 hover:bg-opacity-10' )} > - {/*
+
- {upPercent}% + {summaryPercent}% -
*/} +
{info?.name}
- {/* {props.showCurrent && latestResponse && ( - -
- {latestResponse} -
-
- )} */} + {props.showCurrent && info && ( + + )} -
+
{ - let status: HealthStatus = 'none'; - - if (item.upRate === 1) { - status = 'health'; - } else if (item.upRate === 0 && item.totalCount === 0) { - status = 'none'; - } else if (item.upCount === 0 && item.totalCount !== 0) { - status = 'error'; - } else { - status = 'warning'; - } + const status = parseHealthStatusByPercent( + item.upRate, + item.totalCount + ); return { status, @@ -140,16 +155,47 @@ export const StatusItemMonitor: React.FC<{
); - - // return ( - // - // ); }); StatusItemMonitor.displayName = 'StatusItemMonitor'; + +const MonitorLatestResponse: React.FC<{ + workspaceId: string; + monitorId: string; + monitorType: string; +}> = React.memo((props) => { + const { t } = useTranslation(); + const { data: recentText } = trpc.monitor.recentData.useQuery( + { + workspaceId: props.workspaceId, + monitorId: props.monitorId, + take: 1, + }, + { + select: (data) => { + const provider = getMonitorProvider(props.monitorType); + + const value = data[0].value; + + if (!value) { + return ''; + } + + const { text } = getProviderDisplay(value, provider); + + return text; + }, + } + ); + + return ( + + +
+ {recentText} +
+
+ {t('Current')} +
+ ); +}); +MonitorLatestResponse.displayName = 'MonitorLatestResponse'; diff --git a/src/client/utils/health.spec.ts b/src/client/utils/health.spec.ts new file mode 100644 index 0000000..4f55315 --- /dev/null +++ b/src/client/utils/health.spec.ts @@ -0,0 +1,45 @@ +import { describe, test, expect } from 'vitest'; +import { + parseHealthStatusByPercent, + getStatusBgColorClassName, +} from './health'; + +describe('parseHealthStatusByPercent', () => { + test('should return "health" when percent is 100', () => { + expect(parseHealthStatusByPercent(100, 0)).toEqual('health'); + }); + + test('should return "none" when percent is 0 and count is 0', () => { + expect(parseHealthStatusByPercent(0, 0)).toEqual('none'); + }); + + test('should return "error" when percent is 0 and count is not 0', () => { + expect(parseHealthStatusByPercent(0, 1)).toEqual('error'); + }); + + test('should return "warning" for other cases', () => { + expect(parseHealthStatusByPercent(50, 1)).toEqual('warning'); + }); +}); + +describe('getStatusBgColorClassName', () => { + test('should return bg-green-500 for health status', () => { + expect(getStatusBgColorClassName('health')).toEqual('bg-green-500'); + }); + + test('should return bg-red-600 for error status', () => { + expect(getStatusBgColorClassName('error')).toEqual('bg-red-600'); + }); + + test('should return bg-yellow-400 for warning status', () => { + expect(getStatusBgColorClassName('warning')).toEqual('bg-yellow-400'); + }); + + test('should return bg-gray-400 for none status', () => { + expect(getStatusBgColorClassName('none')).toEqual('bg-gray-400'); + }); + + test('should return empty string for other status', () => { + expect(getStatusBgColorClassName('other' as any)).toEqual(''); + }); +}); diff --git a/src/client/utils/health.ts b/src/client/utils/health.ts new file mode 100644 index 0000000..ab1cae5 --- /dev/null +++ b/src/client/utils/health.ts @@ -0,0 +1,36 @@ +export type HealthStatus = 'health' | 'error' | 'warning' | 'none'; + +/** + * + * @param percent 0 - 100 + * @param count + * @returns + */ +export function parseHealthStatusByPercent( + percent: number, + count: number +): HealthStatus { + if (percent === 100) { + return 'health'; + } else if (percent === 0 && count === 0) { + return 'none'; + } else if (percent === 0 && count !== 0) { + return 'error'; + } else { + return 'warning'; + } +} + +export function getStatusBgColorClassName(status: HealthStatus): string { + if (status === 'health') { + return 'bg-green-500'; + } else if (status === 'error') { + return 'bg-red-600'; + } else if (status === 'warning') { + return 'bg-yellow-400'; + } else if (status === 'none') { + return 'bg-gray-400'; + } else { + return ''; + } +}