feat: add status basic page
This commit is contained in:
parent
3c195b1245
commit
63145c1e4b
@ -18,6 +18,7 @@ import { useInjectWebsiteScript } from './hooks/useInjectWebsiteScript';
|
|||||||
import { ConfigProvider, theme } from 'antd';
|
import { ConfigProvider, theme } from 'antd';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useSettingsStore } from './store/settings';
|
import { useSettingsStore } from './store/settings';
|
||||||
|
import { StatusPage } from './pages/Status';
|
||||||
|
|
||||||
export const AppRoutes: React.FC = React.memo(() => {
|
export const AppRoutes: React.FC = React.memo(() => {
|
||||||
const { info } = useUserStore();
|
const { info } = useUserStore();
|
||||||
@ -42,6 +43,8 @@ export const AppRoutes: React.FC = React.memo(() => {
|
|||||||
</Route>
|
</Route>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<Route path="/status/:slug" element={<StatusPage />} />
|
||||||
|
|
||||||
<Route
|
<Route
|
||||||
path="*"
|
path="*"
|
||||||
element={
|
element={
|
||||||
|
@ -1,11 +1,15 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { MonitorHealthBar } from '../../monitor/MonitorHealthBar';
|
import { MonitorHealthBar } from '../../monitor/MonitorHealthBar';
|
||||||
|
import { useCurrentWorkspaceId } from '../../../store/user';
|
||||||
|
|
||||||
export const MonitorHealthBarItem: React.FC<{
|
export const MonitorHealthBarItem: React.FC<{
|
||||||
monitorId: string;
|
monitorId: string;
|
||||||
}> = React.memo((props) => {
|
}> = React.memo((props) => {
|
||||||
|
const workspaceId = useCurrentWorkspaceId();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MonitorHealthBar
|
<MonitorHealthBar
|
||||||
|
workspaceId={workspaceId}
|
||||||
monitorId={props.monitorId}
|
monitorId={props.monitorId}
|
||||||
count={40}
|
count={40}
|
||||||
size="large"
|
size="large"
|
||||||
|
@ -8,6 +8,7 @@ import { useCurrentWorkspaceId } from '../../store/user';
|
|||||||
import { useWatch } from '../../hooks/useWatch';
|
import { useWatch } from '../../hooks/useWatch';
|
||||||
|
|
||||||
interface MonitorHealthBarProps {
|
interface MonitorHealthBarProps {
|
||||||
|
workspaceId: string;
|
||||||
monitorId: string;
|
monitorId: string;
|
||||||
count?: number;
|
count?: number;
|
||||||
size?: HealthBarProps['size'];
|
size?: HealthBarProps['size'];
|
||||||
@ -18,8 +19,13 @@ interface MonitorHealthBarProps {
|
|||||||
}
|
}
|
||||||
export const MonitorHealthBar: React.FC<MonitorHealthBarProps> = React.memo(
|
export const MonitorHealthBar: React.FC<MonitorHealthBarProps> = React.memo(
|
||||||
(props) => {
|
(props) => {
|
||||||
const { monitorId, size, count = 20, showCurrentStatus = false } = props;
|
const {
|
||||||
const workspaceId = useCurrentWorkspaceId();
|
workspaceId,
|
||||||
|
monitorId,
|
||||||
|
size,
|
||||||
|
count = 20,
|
||||||
|
showCurrentStatus = false,
|
||||||
|
} = props;
|
||||||
const { data: recent = [] } = trpc.monitor.recentData.useQuery({
|
const { data: recent = [] } = trpc.monitor.recentData.useQuery({
|
||||||
workspaceId,
|
workspaceId,
|
||||||
monitorId,
|
monitorId,
|
||||||
|
@ -166,6 +166,7 @@ export const MonitorInfo: React.FC<MonitorInfoProps> = React.memo((props) => {
|
|||||||
|
|
||||||
<Card>
|
<Card>
|
||||||
<MonitorHealthBar
|
<MonitorHealthBar
|
||||||
|
workspaceId={workspaceId}
|
||||||
monitorId={monitorId}
|
monitorId={monitorId}
|
||||||
count={40}
|
count={40}
|
||||||
size="large"
|
size="large"
|
||||||
|
@ -65,6 +65,7 @@ export const MonitorListItem: React.FC<{
|
|||||||
}> = React.memo((props) => {
|
}> = React.memo((props) => {
|
||||||
const { monitor, selectedMonitorId, setSelectedMonitorId } = props;
|
const { monitor, selectedMonitorId, setSelectedMonitorId } = props;
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const workspaceId = useCurrentWorkspaceId();
|
||||||
const [beats, setBeats] = useState<
|
const [beats, setBeats] = useState<
|
||||||
({
|
({
|
||||||
value: number;
|
value: number;
|
||||||
@ -126,7 +127,11 @@ export const MonitorListItem: React.FC<{
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<MonitorHealthBar monitorId={monitor.id} onBeatsItemUpdate={setBeats} />
|
<MonitorHealthBar
|
||||||
|
workspaceId={workspaceId}
|
||||||
|
monitorId={monitor.id}
|
||||||
|
onBeatsItemUpdate={setBeats}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -91,7 +91,10 @@ export const WebsiteOverview: React.FC<{
|
|||||||
className="cursor-pointer"
|
className="cursor-pointer"
|
||||||
onClick={() => navigate(`/monitor/${website.monitorId}`)}
|
onClick={() => navigate(`/monitor/${website.monitorId}`)}
|
||||||
>
|
>
|
||||||
<MonitorHealthBar monitorId={website.monitorId} />
|
<MonitorHealthBar
|
||||||
|
workspaceId={website.workspaceId}
|
||||||
|
monitorId={website.monitorId}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
@ -4,4 +4,4 @@ import { Dashboard } from '../components/dashboard/Dashboard';
|
|||||||
export const DashboardPage: React.FC = React.memo(() => {
|
export const DashboardPage: React.FC = React.memo(() => {
|
||||||
return <Dashboard />;
|
return <Dashboard />;
|
||||||
});
|
});
|
||||||
Dashboard.displayName = 'Dashboard';
|
DashboardPage.displayName = 'DashboardPage';
|
||||||
|
46
src/client/pages/Status.tsx
Normal file
46
src/client/pages/Status.tsx
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { useParams } from 'react-router';
|
||||||
|
import { trpc } from '../api/trpc';
|
||||||
|
import { Empty } from 'antd';
|
||||||
|
import { MonitorHealthBar } from '../components/monitor/MonitorHealthBar';
|
||||||
|
import { useUserStore } from '../store/user';
|
||||||
|
|
||||||
|
export const StatusPage: React.FC = React.memo(() => {
|
||||||
|
const { slug } = useParams<{ slug: string }>();
|
||||||
|
useUserStore();
|
||||||
|
|
||||||
|
const { data: info } = trpc.monitor.getPageInfo.useQuery({
|
||||||
|
slug: slug!,
|
||||||
|
});
|
||||||
|
|
||||||
|
const monitorList = info?.monitorList ?? [];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-4/5 mx-auto py-8">
|
||||||
|
<div className="text-2xl mb-4">{info?.title}</div>
|
||||||
|
|
||||||
|
<div className="text-lg mb-2">Services</div>
|
||||||
|
|
||||||
|
{info && (
|
||||||
|
<div className="shadow-2xl p-2.5">
|
||||||
|
{monitorList.length > 0 ? (
|
||||||
|
monitorList.map((item) => (
|
||||||
|
<div key={item.id} className="hover:bg-black hover:bg-opacity-20">
|
||||||
|
<MonitorHealthBar
|
||||||
|
workspaceId={info.workspaceId}
|
||||||
|
monitorId={item.id}
|
||||||
|
count={40}
|
||||||
|
size="large"
|
||||||
|
showCurrentStatus={true}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<Empty description="No any monitor has been set" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
StatusPage.displayName = 'StatusPage';
|
@ -15,7 +15,10 @@ export const useUserStore = create<UserState>(() => ({
|
|||||||
export function setUserInfo(info: UserLoginInfo) {
|
export function setUserInfo(info: UserLoginInfo) {
|
||||||
if (!info.currentWorkspace && info.workspaces[0]) {
|
if (!info.currentWorkspace && info.workspaces[0]) {
|
||||||
// Make sure currentWorkspace existed
|
// Make sure currentWorkspace existed
|
||||||
info.currentWorkspace = info.workspaces[0].workspace;
|
info.currentWorkspace = {
|
||||||
|
...info.workspaces[0].workspace,
|
||||||
|
dashboardLayout: null,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
useUserStore.setState({
|
useUserStore.setState({
|
||||||
@ -28,6 +31,10 @@ export function setUserInfo(info: UserLoginInfo) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function useUserInfo(): UserLoginInfo | null {
|
||||||
|
return useUserStore((state) => state.info);
|
||||||
|
}
|
||||||
|
|
||||||
export function useCurrentWorkspace() {
|
export function useCurrentWorkspace() {
|
||||||
const currentWorkspace = useUserStore(
|
const currentWorkspace = useUserStore(
|
||||||
(state) => state.info?.currentWorkspace
|
(state) => state.info?.currentWorkspace
|
||||||
|
@ -28,8 +28,7 @@ export const userInfoSchema = z.object({
|
|||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date(),
|
updatedAt: z.date(),
|
||||||
deletedAt: z.date().nullable(),
|
deletedAt: z.date().nullable(),
|
||||||
currentWorkspace: z.intersection(
|
currentWorkspace: workspaceSchema.merge(
|
||||||
workspaceSchema,
|
|
||||||
z.object({
|
z.object({
|
||||||
dashboardLayout: workspaceDashboardLayoutSchema.nullable(),
|
dashboardLayout: workspaceDashboardLayoutSchema.nullable(),
|
||||||
})
|
})
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
OpenApiMetaInfo,
|
OpenApiMetaInfo,
|
||||||
|
publicProcedure,
|
||||||
router,
|
router,
|
||||||
workspaceOwnerProcedure,
|
workspaceOwnerProcedure,
|
||||||
workspaceProcedure,
|
workspaceProcedure,
|
||||||
@ -435,6 +436,29 @@ export const monitorRouter = router({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
|
getPageInfo: publicProcedure
|
||||||
|
.meta({
|
||||||
|
openapi: {
|
||||||
|
tags: [OPENAPI_TAG.MONITOR],
|
||||||
|
method: 'GET',
|
||||||
|
path: '/monitor/getPageInfo',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
slug: z.string(),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.output(MonitorStatusPageModelSchema.nullable())
|
||||||
|
.query(({ input }) => {
|
||||||
|
const { slug } = input;
|
||||||
|
|
||||||
|
return prisma.monitorStatusPage.findUnique({
|
||||||
|
where: {
|
||||||
|
slug,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}),
|
||||||
createPage: workspaceOwnerProcedure
|
createPage: workspaceOwnerProcedure
|
||||||
.meta(
|
.meta(
|
||||||
buildMonitorOpenapi({
|
buildMonitorOpenapi({
|
||||||
|
Loading…
Reference in New Issue
Block a user