refactor: refactor monitor layout

This commit is contained in:
moonrailgun 2023-09-29 17:42:36 +08:00
parent a4dfc54eb2
commit 6ddc344b3a
11 changed files with 246 additions and 135 deletions

View File

@ -2,7 +2,6 @@ import { BrowserRouter, Route, Routes, Navigate } from 'react-router-dom';
import { Layout } from './pages/Layout'; import { Layout } from './pages/Layout';
import { Dashboard } from './pages/Dashboard'; import { Dashboard } from './pages/Dashboard';
import { Login } from './pages/Login'; import { Login } from './pages/Login';
import { Monitor } from './pages/Monitor';
import { Website } from './pages/Website'; import { Website } from './pages/Website';
import { SettingsPage } from './pages/Settings'; import { SettingsPage } from './pages/Settings';
import { Servers } from './pages/Servers'; import { Servers } from './pages/Servers';
@ -13,6 +12,7 @@ import { queryClient } from './api/cache';
import { TokenLoginContainer } from './components/TokenLoginContainer'; import { TokenLoginContainer } from './components/TokenLoginContainer';
import React from 'react'; import React from 'react';
import { trpc, trpcClient } from './api/trpc'; import { trpc, trpcClient } from './api/trpc';
import { MonitorPage } from './pages/Monitor';
export const AppRoutes: React.FC = React.memo(() => { export const AppRoutes: React.FC = React.memo(() => {
const { info } = useUserStore(); const { info } = useUserStore();
@ -22,7 +22,7 @@ export const AppRoutes: React.FC = React.memo(() => {
{info ? ( {info ? (
<Route element={<Layout />}> <Route element={<Layout />}>
<Route path="/dashboard" element={<Dashboard />} /> <Route path="/dashboard" element={<Dashboard />} />
<Route path="/monitor" element={<Monitor />} /> <Route path="/monitor/*" element={<MonitorPage />} />
<Route path="/website" element={<Website />} /> <Route path="/website" element={<Website />} />
<Route path="/servers" element={<Servers />} /> <Route path="/servers" element={<Servers />} />
<Route path="/settings/*" element={<SettingsPage />} /> <Route path="/settings/*" element={<SettingsPage />} />

View 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';

View 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';

View File

@ -95,7 +95,6 @@ const WebsiteListTable: React.FC<{ workspaceId: string }> = React.memo(
const navigate = useNavigate(); const navigate = useNavigate();
const handleEdit = useEvent((websiteId) => { const handleEdit = useEvent((websiteId) => {
console.log(`/settings/website/${websiteId}`);
navigate(`/settings/website/${websiteId}`); navigate(`/settings/website/${websiteId}`);
}); });

View File

@ -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';

View 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';

View File

@ -0,0 +1,6 @@
import React from 'react';
export const MonitorDetail: React.FC = React.memo(() => {
return <div>Detail</div>;
});
MonitorDetail.displayName = 'MonitorDetail';

View 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';

View File

@ -0,0 +1,6 @@
import React from 'react';
export const MonitorOverview: React.FC = React.memo(() => {
return <div>Overview</div>;
});
MonitorOverview.displayName = 'MonitorOverview';

View 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';

View File

@ -13,9 +13,27 @@ export const monitorRouter = router({
return monitors; return monitors;
}), }),
create: workspaceOwnerProcedure get: workspaceProcedure
.input( .input(
z.object({ 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(), name: z.string(),
type: z.string(), type: z.string(),
active: z.boolean().default(true), active: z.boolean().default(true),
@ -27,6 +45,7 @@ export const monitorRouter = router({
) )
.mutation(async ({ input }) => { .mutation(async ({ input }) => {
const { const {
id,
workspaceId, workspaceId,
name, name,
type, type,
@ -37,7 +56,24 @@ export const monitorRouter = router({
payload, payload,
} = input; } = input;
const monitor = await prisma.monitor.create({ if (id) {
return prisma.monitor.update({
data: {
name,
type,
active,
interval,
maxRetry,
retryInterval,
payload,
},
where: {
id,
workspaceId,
},
});
} else {
return prisma.monitor.create({
data: { data: {
workspaceId, workspaceId,
name, name,
@ -49,7 +85,6 @@ export const monitorRouter = router({
payload, payload,
}, },
}); });
}
return monitor;
}), }),
}); });