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 { 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 />} />
|
||||||
|
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 navigate = useNavigate();
|
||||||
|
|
||||||
const handleEdit = useEvent((websiteId) => {
|
const handleEdit = useEvent((websiteId) => {
|
||||||
console.log(`/settings/website/${websiteId}`);
|
|
||||||
navigate(`/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;
|
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,19 +56,35 @@ export const monitorRouter = router({
|
|||||||
payload,
|
payload,
|
||||||
} = input;
|
} = input;
|
||||||
|
|
||||||
const monitor = await prisma.monitor.create({
|
if (id) {
|
||||||
data: {
|
return prisma.monitor.update({
|
||||||
workspaceId,
|
data: {
|
||||||
name,
|
name,
|
||||||
type,
|
type,
|
||||||
active,
|
active,
|
||||||
interval,
|
interval,
|
||||||
maxRetry,
|
maxRetry,
|
||||||
retryInterval,
|
retryInterval,
|
||||||
payload,
|
payload,
|
||||||
},
|
},
|
||||||
});
|
where: {
|
||||||
|
id,
|
||||||
return monitor;
|
workspaceId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return prisma.monitor.create({
|
||||||
|
data: {
|
||||||
|
workspaceId,
|
||||||
|
name,
|
||||||
|
type,
|
||||||
|
active,
|
||||||
|
interval,
|
||||||
|
maxRetry,
|
||||||
|
retryInterval,
|
||||||
|
payload,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user