feat: allow display current response value in monitor list
useful for monitor page to display current value
This commit is contained in:
parent
e24d82224c
commit
ec591f0c54
@ -1,6 +1,6 @@
|
||||
{
|
||||
"verbose": true,
|
||||
"watch": ["./src/server"],
|
||||
"watch": ["./src/server", "./prisma"],
|
||||
"ext": "ts",
|
||||
"delay": 1000,
|
||||
"exec": "ts-node --transpileOnly ./src/server/main.ts"
|
||||
|
@ -3,5 +3,6 @@ import { z } from 'zod';
|
||||
export const MonitorStatusPageListSchema = z.array(
|
||||
z.object({
|
||||
id: z.string(),
|
||||
showCurrent: z.boolean().default(false).optional(),
|
||||
})
|
||||
);
|
||||
|
@ -6,7 +6,7 @@ import React, { useState, useMemo } from 'react';
|
||||
import { useSocketSubscribeList } from '../../api/socketio';
|
||||
import { trpc } from '../../api/trpc';
|
||||
import { useCurrentWorkspaceId } from '../../store/user';
|
||||
import { getMonitorProvider } from './provider';
|
||||
import { getMonitorProvider, getProviderDisplay } from './provider';
|
||||
|
||||
export const MonitorDataChart: React.FC<{ monitorId: string }> = React.memo(
|
||||
(props) => {
|
||||
@ -122,16 +122,14 @@ export const MonitorDataChart: React.FC<{ monitorId: string }> = React.memo(
|
||||
return dayjs(datum.time).format('YYYY-MM-DD HH:mm');
|
||||
},
|
||||
formatter(datum) {
|
||||
const name = providerInfo?.valueLabel
|
||||
? providerInfo?.valueLabel
|
||||
: 'usage';
|
||||
const formatterFn = providerInfo?.valueFormatter
|
||||
? providerInfo?.valueFormatter
|
||||
: (value: number) => `${value}ms`;
|
||||
const { name, text } = getProviderDisplay(
|
||||
datum.value,
|
||||
providerInfo
|
||||
);
|
||||
|
||||
return {
|
||||
name,
|
||||
value: datum.value ? formatterFn(datum.value) : 'null',
|
||||
value: datum.value ? text : 'null',
|
||||
};
|
||||
},
|
||||
},
|
||||
|
@ -4,7 +4,6 @@ import { takeRight, last } from 'lodash-es';
|
||||
import { HealthBar, HealthBarBeat, HealthBarProps } from '../HealthBar';
|
||||
import dayjs from 'dayjs';
|
||||
import { trpc } from '../../api/trpc';
|
||||
import { useCurrentWorkspaceId } from '../../store/user';
|
||||
import { useWatch } from '../../hooks/useWatch';
|
||||
|
||||
interface MonitorHealthBarProps {
|
||||
|
@ -44,6 +44,7 @@ export const MonitorList: React.FC = React.memo(() => {
|
||||
workspaceId={workspaceId}
|
||||
monitorId={monitor.id}
|
||||
monitorName={monitor.name}
|
||||
monitorType={monitor.type}
|
||||
onClick={() => {
|
||||
navigate(`/monitor/${monitor.id}`);
|
||||
}}
|
||||
|
@ -1,15 +1,28 @@
|
||||
import clsx from 'clsx';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { MonitorHealthBar } from './MonitorHealthBar';
|
||||
import { last } from 'lodash-es';
|
||||
import { getMonitorProvider, getProviderDisplay } from './provider';
|
||||
import { Tooltip } from 'antd';
|
||||
|
||||
export const MonitorListItem: React.FC<{
|
||||
className?: string;
|
||||
workspaceId: string;
|
||||
monitorId: string;
|
||||
monitorName: string;
|
||||
monitorType: string;
|
||||
showCurrentResponse?: boolean;
|
||||
onClick?: () => void;
|
||||
}> = React.memo((props) => {
|
||||
const { className, workspaceId, monitorId, monitorName, onClick } = props;
|
||||
const {
|
||||
className,
|
||||
workspaceId,
|
||||
monitorId,
|
||||
monitorName,
|
||||
monitorType,
|
||||
showCurrentResponse = false,
|
||||
onClick,
|
||||
} = props;
|
||||
|
||||
const [beats, setBeats] = useState<
|
||||
({
|
||||
@ -33,11 +46,29 @@ export const MonitorListItem: React.FC<{
|
||||
return parseFloat(((up / beats.length) * 100).toFixed(1));
|
||||
}, [beats]);
|
||||
|
||||
const provider = getMonitorProvider(monitorType);
|
||||
|
||||
const latestResponse = useMemo((): string | false => {
|
||||
const val = last(beats)?.value;
|
||||
|
||||
if (!val) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!provider) {
|
||||
return String(val);
|
||||
}
|
||||
|
||||
const { text } = getProviderDisplay(val, provider);
|
||||
|
||||
return text;
|
||||
}, [beats, provider]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
className,
|
||||
'flex rounded-lg py-3 px-4 mb-1 bg-green-500 bg-opacity-0 hover:bg-opacity-10',
|
||||
'flex rounded-lg py-3 px-4 mb-1 bg-green-500 bg-opacity-0 hover:bg-opacity-10 items-center',
|
||||
onClick && 'cursor-pointer'
|
||||
)}
|
||||
onClick={onClick}
|
||||
@ -66,6 +97,14 @@ export const MonitorListItem: React.FC<{
|
||||
</div> */}
|
||||
</div>
|
||||
|
||||
{showCurrentResponse && latestResponse && (
|
||||
<Tooltip title="Current">
|
||||
<div className="px-2 text-sm text-gray-800 dark:text-gray-400">
|
||||
{latestResponse}
|
||||
</div>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
<div className="flex items-center">
|
||||
<MonitorHealthBar
|
||||
workspaceId={workspaceId}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Button, Form, Input, Typography } from 'antd';
|
||||
import { Button, Divider, Form, Input, Switch, Typography } from 'antd';
|
||||
import React from 'react';
|
||||
import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
import { MonitorPicker } from '../MonitorPicker';
|
||||
@ -72,27 +72,50 @@ export const MonitorStatusPageEditForm: React.FC<MonitorStatusPageEditFormProps>
|
||||
return (
|
||||
<>
|
||||
<Form.Item label="Monitors">
|
||||
<div className="flex flex-col gap-2 mb-2">
|
||||
{fields.map((field, index) => (
|
||||
<div key={field.key} className="flex gap-2 items-start">
|
||||
// monitor item
|
||||
<>
|
||||
{index !== 0 && <Divider className="my-0.5" />}
|
||||
|
||||
<div key={field.key} className="flex flex-col gap-1">
|
||||
<Form.Item
|
||||
name={[field.name, 'id']}
|
||||
className="flex-1"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: 'Please select monitor',
|
||||
},
|
||||
]}
|
||||
noStyle={true}
|
||||
>
|
||||
<MonitorPicker />
|
||||
</Form.Item>
|
||||
|
||||
<div className="flex item-center">
|
||||
<div className="flex-1">
|
||||
<Form.Item
|
||||
name={[field.name, 'showCurrent']}
|
||||
valuePropName="checked"
|
||||
noStyle={true}
|
||||
>
|
||||
<Switch size="small" />
|
||||
</Form.Item>
|
||||
|
||||
<span className="text-sm align-middle ml-1">
|
||||
Show Current Response
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<MinusCircleOutlined
|
||||
className="text-lg mt-1.5"
|
||||
onClick={() => remove(field.name)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<Button
|
||||
type="dashed"
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { Empty } from 'antd';
|
||||
import React from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import { trpc } from '../../../api/trpc';
|
||||
import { Loading } from '../../Loading';
|
||||
import { MonitorListItem } from '../MonitorListItem';
|
||||
import { keyBy } from 'lodash-es';
|
||||
|
||||
interface StatusPageServicesProps {
|
||||
workspaceId: string;
|
||||
@ -16,12 +17,14 @@ export const StatusPageServices: React.FC<StatusPageServicesProps> = React.memo(
|
||||
monitorIds: monitorList.map((item) => item.id),
|
||||
});
|
||||
|
||||
const monitorProps = useMemo(() => keyBy(monitorList, 'id'), [monitorList]);
|
||||
|
||||
if (isLoading) {
|
||||
return <Loading />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="shadow-2xl p-2.5 flex flex-col gap-4">
|
||||
<div className="p-2.5 flex flex-col gap-4 rounded-md border border-gray-200 dark:border-gray-700">
|
||||
{list.length > 0 ? (
|
||||
list.map((item) => (
|
||||
<MonitorListItem
|
||||
@ -29,6 +32,8 @@ export const StatusPageServices: React.FC<StatusPageServicesProps> = React.memo(
|
||||
workspaceId={workspaceId}
|
||||
monitorId={item.id}
|
||||
monitorName={item.name}
|
||||
monitorType={item.type}
|
||||
showCurrentResponse={monitorProps[item.id].showCurrent ?? false}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
|
@ -32,3 +32,21 @@ export function getMonitorLink(info: MonitorInfo): React.ReactNode {
|
||||
|
||||
return provider.link(info);
|
||||
}
|
||||
|
||||
export function getProviderDisplay(
|
||||
value: number,
|
||||
provider:
|
||||
| Pick<MonitorProvider, 'valueFormatter' | 'valueLabel'>
|
||||
| undefined
|
||||
| null
|
||||
) {
|
||||
const name = provider?.valueLabel ? provider?.valueLabel : 'usage';
|
||||
const formatterFn = provider?.valueFormatter
|
||||
? provider?.valueFormatter
|
||||
: (value: number) => `${value}ms`;
|
||||
|
||||
return {
|
||||
name,
|
||||
text: formatterFn(value),
|
||||
};
|
||||
}
|
||||
|
@ -1,3 +1,6 @@
|
||||
import { z } from 'zod';
|
||||
import { MonitorStatusPageListSchema } from '../../prisma/zod/schemas';
|
||||
|
||||
export * as schemas from '../../prisma/zod/index';
|
||||
export * from './server';
|
||||
export * from './monitor';
|
||||
@ -10,8 +13,6 @@ declare global {
|
||||
layouts: Record<string, any[]>;
|
||||
items: any[];
|
||||
} | null;
|
||||
type MonitorStatusPageList = {
|
||||
id: string;
|
||||
}[];
|
||||
type MonitorStatusPageList = z.infer<typeof MonitorStatusPageListSchema>;
|
||||
}
|
||||
}
|
||||
|
@ -16,4 +16,5 @@ export type MonitorInfoWithNotificationIds = MonitorInfo & {
|
||||
export const MonitorPublicInfoSchema = schemas.MonitorModelSchema.pick({
|
||||
id: true,
|
||||
name: true,
|
||||
type: true,
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user