refactor: refactor monitor layout
This commit is contained in:
parent
a4dfc54eb2
commit
6ddc344b3a
@ -2,7 +2,6 @@ import { BrowserRouter, Route, Routes, Navigate } from 'react-router-dom';
|
||||
import { Layout } from './pages/Layout';
|
||||
import { Dashboard } from './pages/Dashboard';
|
||||
import { Login } from './pages/Login';
|
||||
import { Monitor } from './pages/Monitor';
|
||||
import { Website } from './pages/Website';
|
||||
import { SettingsPage } from './pages/Settings';
|
||||
import { Servers } from './pages/Servers';
|
||||
@ -13,6 +12,7 @@ import { queryClient } from './api/cache';
|
||||
import { TokenLoginContainer } from './components/TokenLoginContainer';
|
||||
import React from 'react';
|
||||
import { trpc, trpcClient } from './api/trpc';
|
||||
import { MonitorPage } from './pages/Monitor';
|
||||
|
||||
export const AppRoutes: React.FC = React.memo(() => {
|
||||
const { info } = useUserStore();
|
||||
@ -22,7 +22,7 @@ export const AppRoutes: React.FC = React.memo(() => {
|
||||
{info ? (
|
||||
<Route element={<Layout />}>
|
||||
<Route path="/dashboard" element={<Dashboard />} />
|
||||
<Route path="/monitor" element={<Monitor />} />
|
||||
<Route path="/monitor/*" element={<MonitorPage />} />
|
||||
<Route path="/website" element={<Website />} />
|
||||
<Route path="/servers" element={<Servers />} />
|
||||
<Route path="/settings/*" element={<SettingsPage />} />
|
||||
|
17
src/client/components/MonitorInfoEditor.tsx
Normal file
17
src/client/components/MonitorInfoEditor.tsx
Normal file
@ -0,0 +1,17 @@
|
||||
import React from 'react';
|
||||
import type { Monitor } from '@prisma/client';
|
||||
|
||||
type MonitorInfoEditorValues = Omit<Monitor, 'id'> & {
|
||||
id?: string;
|
||||
};
|
||||
|
||||
interface MonitorInfoEditorProps {
|
||||
initValue?: MonitorInfoEditorValues;
|
||||
onSave: (value: MonitorInfoEditorValues) => void;
|
||||
}
|
||||
export const MonitorInfoEditor: React.FC<MonitorInfoEditorProps> = React.memo(
|
||||
(props) => {
|
||||
return <div>MonitorInfoEditor</div>;
|
||||
}
|
||||
);
|
||||
MonitorInfoEditor.displayName = 'MonitorInfoEditor';
|
71
src/client/components/MonitorList.tsx
Normal file
71
src/client/components/MonitorList.tsx
Normal file
@ -0,0 +1,71 @@
|
||||
import clsx from 'clsx';
|
||||
import React, { useState } from 'react';
|
||||
import { trpc } from '../api/trpc';
|
||||
import { useCurrentWorkspaceId } from '../store/user';
|
||||
import { HealthBar } from './HealthBar';
|
||||
import { NoWorkspaceTip } from './NoWorkspaceTip';
|
||||
|
||||
export const MonitorList: React.FC = React.memo(() => {
|
||||
const currentWorkspaceId = useCurrentWorkspaceId()!;
|
||||
const { data: monitors = [] } = trpc.monitor.all.useQuery({
|
||||
workspaceId: currentWorkspaceId,
|
||||
});
|
||||
|
||||
const [selectedMonitorId, setSelectedMonitorId] = useState<string | null>(
|
||||
null
|
||||
);
|
||||
|
||||
if (!currentWorkspaceId) {
|
||||
return <NoWorkspaceTip />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{monitors.map((monitor) => (
|
||||
<div
|
||||
key={monitor.name}
|
||||
className={clsx(
|
||||
'flex rounded-lg py-3 px-4 cursor-pointer',
|
||||
selectedMonitorId === monitor.id
|
||||
? 'bg-green-500 bg-opacity-20'
|
||||
: 'bg-green-500 bg-opacity-0 hover:bg-opacity-10'
|
||||
)}
|
||||
onClick={() => setSelectedMonitorId(monitor.id)}
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
className={
|
||||
'bg-green-400 min-w-[62px] p-0.5 rounded-full text-white inline-block text-center'
|
||||
}
|
||||
>
|
||||
{/* {monitor.monthOnlineRate * 100}% */}
|
||||
80%
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex-1 pl-2">
|
||||
<div className="text-base mb-2">{monitor.name}</div>
|
||||
{/* <div>
|
||||
{monitor.tags.map((tag) => (
|
||||
<span
|
||||
className="py-0.5 px-1 rounded-full text-white text-sm"
|
||||
style={{ backgroundColor: tag.color }}
|
||||
>
|
||||
{tag.label}
|
||||
</span>
|
||||
))}
|
||||
</div> */}
|
||||
</div>
|
||||
|
||||
<div className="flex items-center">
|
||||
<HealthBar
|
||||
beats={Array.from({ length: 13 }).map(() => ({
|
||||
status: 'health',
|
||||
}))}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
MonitorList.displayName = 'MonitorList';
|
@ -95,7 +95,6 @@ const WebsiteListTable: React.FC<{ workspaceId: string }> = React.memo(
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleEdit = useEvent((websiteId) => {
|
||||
console.log(`/settings/website/${websiteId}`);
|
||||
navigate(`/settings/website/${websiteId}`);
|
||||
});
|
||||
|
||||
|
@ -1,117 +0,0 @@
|
||||
import clsx from 'clsx';
|
||||
import React from 'react';
|
||||
import { RouterOutput, trpc } from '../api/trpc';
|
||||
import { HealthBar } from '../components/HealthBar';
|
||||
import { useEvent } from '../hooks/useEvent';
|
||||
import { useCurrentWorkspaceId } from '../store/user';
|
||||
|
||||
interface MonitorHeartbeatInfo {
|
||||
name: string;
|
||||
dayOnlineRate: number;
|
||||
monthOnlineRate: number;
|
||||
certificateExpiredTo: string;
|
||||
detectFrequency: number; // second
|
||||
response: number;
|
||||
avgResponse: number; // 24hour
|
||||
tags: { label: string; color: string }[];
|
||||
}
|
||||
|
||||
const demoMonitors = [
|
||||
{
|
||||
name: 'Tianji',
|
||||
dayOnlineRate: 1,
|
||||
monthOnlineRate: 0.8,
|
||||
certificateExpiredTo: '2023-9-2 15:49:56',
|
||||
detectFrequency: 300,
|
||||
response: 592,
|
||||
avgResponse: 846,
|
||||
tags: [{ label: 'Core', color: 'red' }],
|
||||
},
|
||||
];
|
||||
|
||||
export const Monitor: React.FC = React.memo(() => {
|
||||
const currentWorkspaceId = useCurrentWorkspaceId()!;
|
||||
const { data: monitors = [] } = trpc.monitor.all.useQuery({
|
||||
workspaceId: currentWorkspaceId,
|
||||
});
|
||||
|
||||
const handleCreateMonitor = useEvent(() => {
|
||||
// TODO
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="h-full flex flex-col">
|
||||
<div className="flex-1">
|
||||
<div className="p-4">
|
||||
<div
|
||||
className="px-3 py-2 rounded-full bg-green-400 hover:bg-green-500 text-white inline-block cursor-pointer"
|
||||
onClick={handleCreateMonitor}
|
||||
>
|
||||
Add new Montior
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="py-5 flex h-full">
|
||||
<div className="w-5/12 bg-gray-50">
|
||||
<MonitorList monitors={monitors} />
|
||||
</div>
|
||||
<div className="w-7/12">Info</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
Monitor.displayName = 'Monitor';
|
||||
|
||||
const MonitorList: React.FC<{
|
||||
monitors: RouterOutput['monitor']['all'];
|
||||
}> = React.memo((props) => {
|
||||
const selectedMonitorName = 'Tianji1';
|
||||
|
||||
return (
|
||||
<div>
|
||||
{props.monitors.map((monitor) => (
|
||||
<div
|
||||
key={monitor.name}
|
||||
className={clsx('flex rounded-lg py-3 px-4 cursor-pointer', {
|
||||
'bg-green-500 bg-opacity-20': selectedMonitorName === monitor.name,
|
||||
'bg-green-500 bg-opacity-0 hover:bg-opacity-10':
|
||||
selectedMonitorName !== monitor.name,
|
||||
})}
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
className={
|
||||
'bg-green-400 min-w-[62px] p-0.5 rounded-full text-white inline-block text-center'
|
||||
}
|
||||
>
|
||||
{/* {monitor.monthOnlineRate * 100}% */}
|
||||
80%
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex-1 pl-2">
|
||||
<div className="text-base mb-2">{monitor.name}</div>
|
||||
{/* <div>
|
||||
{monitor.tags.map((tag) => (
|
||||
<span
|
||||
className="py-0.5 px-1 rounded-full text-white text-sm"
|
||||
style={{ backgroundColor: tag.color }}
|
||||
>
|
||||
{tag.label}
|
||||
</span>
|
||||
))}
|
||||
</div> */}
|
||||
</div>
|
||||
|
||||
<div className="flex items-center">
|
||||
<HealthBar
|
||||
beats={Array.from({ length: 13 }).map(() => ({
|
||||
status: 'health',
|
||||
}))}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
MonitorList.displayName = 'MonitorList';
|
18
src/client/pages/Monitor/Add.tsx
Normal file
18
src/client/pages/Monitor/Add.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
import React from 'react';
|
||||
import { MonitorInfoEditor } from '../../components/MonitorInfoEditor';
|
||||
import { useCurrentWorkspaceId } from '../../store/user';
|
||||
|
||||
export const MonitorAdd: React.FC = React.memo(() => {
|
||||
const currentWorkspaceId = useCurrentWorkspaceId();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<MonitorInfoEditor
|
||||
onSave={(value) => {
|
||||
// console.log(value);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
MonitorAdd.displayName = 'MonitorAdd';
|
6
src/client/pages/Monitor/Detail.tsx
Normal file
6
src/client/pages/Monitor/Detail.tsx
Normal file
@ -0,0 +1,6 @@
|
||||
import React from 'react';
|
||||
|
||||
export const MonitorDetail: React.FC = React.memo(() => {
|
||||
return <div>Detail</div>;
|
||||
});
|
||||
MonitorDetail.displayName = 'MonitorDetail';
|
36
src/client/pages/Monitor/Edit.tsx
Normal file
36
src/client/pages/Monitor/Edit.tsx
Normal file
@ -0,0 +1,36 @@
|
||||
import React from 'react';
|
||||
import { useParams } from 'react-router';
|
||||
import { trpc } from '../../api/trpc';
|
||||
import { ErrorTip } from '../../components/ErrorTip';
|
||||
import { Loading } from '../../components/Loading';
|
||||
import { MonitorInfoEditor } from '../../components/MonitorInfoEditor';
|
||||
import { useCurrentWorkspaceId } from '../../store/user';
|
||||
|
||||
export const MonitorEdit: React.FC = React.memo(() => {
|
||||
const { monitorId } = useParams<{ monitorId: string }>();
|
||||
const currentWorkspaceId = useCurrentWorkspaceId();
|
||||
const { data: monitor, isLoading } = trpc.monitor.get.useQuery({
|
||||
id: monitorId!,
|
||||
workspaceId: currentWorkspaceId!,
|
||||
});
|
||||
|
||||
if (isLoading) {
|
||||
return <Loading />;
|
||||
}
|
||||
|
||||
if (!monitor) {
|
||||
return <ErrorTip />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<MonitorInfoEditor
|
||||
initValue={monitor}
|
||||
onSave={(value) => {
|
||||
console.log(value);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
MonitorEdit.displayName = 'MonitorEdit';
|
6
src/client/pages/Monitor/Overview.tsx
Normal file
6
src/client/pages/Monitor/Overview.tsx
Normal file
@ -0,0 +1,6 @@
|
||||
import React from 'react';
|
||||
|
||||
export const MonitorOverview: React.FC = React.memo(() => {
|
||||
return <div>Overview</div>;
|
||||
});
|
||||
MonitorOverview.displayName = 'MonitorOverview';
|
40
src/client/pages/Monitor/index.tsx
Normal file
40
src/client/pages/Monitor/index.tsx
Normal file
@ -0,0 +1,40 @@
|
||||
import React from 'react';
|
||||
import { Route, Routes, useNavigate } from 'react-router';
|
||||
import { MonitorList } from '../../components/MonitorList';
|
||||
import { MonitorAdd } from './Add';
|
||||
import { MonitorDetail } from './Detail';
|
||||
import { MonitorEdit } from './Edit';
|
||||
import { MonitorOverview } from './Overview';
|
||||
|
||||
export const MonitorPage: React.FC = React.memo(() => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<div className="h-full flex flex-col">
|
||||
<div className="flex-1">
|
||||
<div className="p-4">
|
||||
<div
|
||||
className="px-3 py-2 rounded-full bg-green-400 hover:bg-green-500 text-white inline-block cursor-pointer"
|
||||
onClick={() => navigate('/monitor/add')}
|
||||
>
|
||||
Add new Montior
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="py-5 flex h-full">
|
||||
<div className="w-5/12 bg-gray-50">
|
||||
<MonitorList />
|
||||
</div>
|
||||
<div className="w-7/12">
|
||||
<Routes>
|
||||
<Route path="/" element={<MonitorOverview />} />
|
||||
<Route path="/:monitorId" element={<MonitorDetail />} />
|
||||
<Route path="/:monitorId/edit" element={<MonitorEdit />} />
|
||||
<Route path="/add" element={<MonitorAdd />} />
|
||||
</Routes>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
MonitorPage.displayName = 'MonitorPage';
|
@ -13,9 +13,27 @@ export const monitorRouter = router({
|
||||
|
||||
return monitors;
|
||||
}),
|
||||
create: workspaceOwnerProcedure
|
||||
get: workspaceProcedure
|
||||
.input(
|
||||
z.object({
|
||||
id: z.string(),
|
||||
})
|
||||
)
|
||||
.query(async ({ input }) => {
|
||||
const { id, workspaceId } = input;
|
||||
const monitor = await prisma.monitor.findUnique({
|
||||
where: {
|
||||
id,
|
||||
workspaceId,
|
||||
},
|
||||
});
|
||||
|
||||
return monitor;
|
||||
}),
|
||||
upsert: workspaceOwnerProcedure
|
||||
.input(
|
||||
z.object({
|
||||
id: z.string().optional(),
|
||||
name: z.string(),
|
||||
type: z.string(),
|
||||
active: z.boolean().default(true),
|
||||
@ -27,6 +45,7 @@ export const monitorRouter = router({
|
||||
)
|
||||
.mutation(async ({ input }) => {
|
||||
const {
|
||||
id,
|
||||
workspaceId,
|
||||
name,
|
||||
type,
|
||||
@ -37,19 +56,35 @@ export const monitorRouter = router({
|
||||
payload,
|
||||
} = input;
|
||||
|
||||
const monitor = await prisma.monitor.create({
|
||||
data: {
|
||||
workspaceId,
|
||||
name,
|
||||
type,
|
||||
active,
|
||||
interval,
|
||||
maxRetry,
|
||||
retryInterval,
|
||||
payload,
|
||||
},
|
||||
});
|
||||
|
||||
return monitor;
|
||||
if (id) {
|
||||
return prisma.monitor.update({
|
||||
data: {
|
||||
name,
|
||||
type,
|
||||
active,
|
||||
interval,
|
||||
maxRetry,
|
||||
retryInterval,
|
||||
payload,
|
||||
},
|
||||
where: {
|
||||
id,
|
||||
workspaceId,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
return prisma.monitor.create({
|
||||
data: {
|
||||
workspaceId,
|
||||
name,
|
||||
type,
|
||||
active,
|
||||
interval,
|
||||
maxRetry,
|
||||
retryInterval,
|
||||
payload,
|
||||
},
|
||||
});
|
||||
}
|
||||
}),
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user