feat: add status list and add route
This commit is contained in:
parent
669b2ed8b8
commit
0cf934c9e3
94
src/client/pages/Monitor/PageAdd.tsx
Normal file
94
src/client/pages/Monitor/PageAdd.tsx
Normal file
@ -0,0 +1,94 @@
|
||||
import React from 'react';
|
||||
import { useNavigate } from 'react-router';
|
||||
import { useCurrentWorkspaceId } from '../../store/user';
|
||||
import { trpc } from '../../api/trpc';
|
||||
import { Button, Form, Input, Typography } from 'antd';
|
||||
import { useEvent } from '../../hooks/useEvent';
|
||||
import { z } from 'zod';
|
||||
import { slugRegex } from '../../../shared';
|
||||
|
||||
const { Text, Paragraph } = Typography;
|
||||
|
||||
interface Values {
|
||||
title: string;
|
||||
slug: string;
|
||||
}
|
||||
|
||||
export const MonitorPageAdd: React.FC = React.memo(() => {
|
||||
const workspaceId = useCurrentWorkspaceId()!;
|
||||
const navigate = useNavigate();
|
||||
|
||||
const createPageMutation = trpc.monitor.createPage.useMutation();
|
||||
const trpcUtils = trpc.useContext();
|
||||
|
||||
const handleFinish = useEvent(async (values: Values) => {
|
||||
await createPageMutation.mutateAsync({
|
||||
workspaceId,
|
||||
title: values.title,
|
||||
slug: values.slug,
|
||||
});
|
||||
|
||||
trpcUtils.monitor.getAllPages.refetch();
|
||||
|
||||
navigate('/monitor/pages');
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="px-8 py-4">
|
||||
<Form<Values> layout="vertical" onFinish={handleFinish}>
|
||||
<Form.Item
|
||||
label="Title"
|
||||
name="title"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="Slug"
|
||||
name="slug"
|
||||
extra={
|
||||
<div className="pt-2">
|
||||
<div>
|
||||
Accept characters: <Text code>a-z</Text> <Text code>0-9</Text>{' '}
|
||||
<Text code>-</Text>
|
||||
</div>
|
||||
<div>
|
||||
No consecutive dashes <Text code>--</Text>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
validator(rule, value, callback) {
|
||||
try {
|
||||
z.string().regex(slugRegex).parse(value);
|
||||
callback();
|
||||
} catch (err) {
|
||||
callback('Not valid slug');
|
||||
}
|
||||
},
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input addonBefore={`${window.origin}/status/`} />
|
||||
</Form.Item>
|
||||
<Button
|
||||
type="primary"
|
||||
htmlType="submit"
|
||||
loading={createPageMutation.isLoading}
|
||||
>
|
||||
Next
|
||||
</Button>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
MonitorPageAdd.displayName = 'MonitorPageAdd';
|
40
src/client/pages/Monitor/PageList.tsx
Normal file
40
src/client/pages/Monitor/PageList.tsx
Normal file
@ -0,0 +1,40 @@
|
||||
import React from 'react';
|
||||
import { useNavigate } from 'react-router';
|
||||
import { useCurrentWorkspaceId } from '../../store/user';
|
||||
import { trpc } from '../../api/trpc';
|
||||
import { Button, Card } from 'antd';
|
||||
import { EditOutlined, EyeOutlined } from '@ant-design/icons';
|
||||
|
||||
export const MonitorPageList: React.FC = React.memo(() => {
|
||||
const workspaceId = useCurrentWorkspaceId()!;
|
||||
const { data: pages = [] } = trpc.monitor.getAllPages.useQuery({
|
||||
workspaceId,
|
||||
});
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<div className="px-8 py-4">
|
||||
<Button type="primary" onClick={() => navigate('/monitor/pages/add')}>
|
||||
New page
|
||||
</Button>
|
||||
|
||||
<div className="mt-4 flex flex-col gap-2">
|
||||
{pages.map((p) => (
|
||||
<Card bodyStyle={{ padding: 12 }}>
|
||||
<div className="flex">
|
||||
<div className="flex-1">{p.title}</div>
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
icon={<EyeOutlined />}
|
||||
onClick={() => navigate(`/status/${p.slug}`)}
|
||||
/>
|
||||
<Button icon={<EditOutlined />} />
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
MonitorPageList.displayName = 'MonitorPageList';
|
@ -5,6 +5,9 @@ import { MonitorAdd } from './Add';
|
||||
import { MonitorDetail } from './Detail';
|
||||
import { MonitorEdit } from './Edit';
|
||||
import { MonitorOverview } from './Overview';
|
||||
import { Button } from 'antd';
|
||||
import { MonitorPageList } from './PageList';
|
||||
import { MonitorPageAdd } from './PageAdd';
|
||||
|
||||
export const MonitorPage: React.FC = React.memo(() => {
|
||||
const navigate = useNavigate();
|
||||
@ -12,13 +15,21 @@ export const MonitorPage: React.FC = React.memo(() => {
|
||||
return (
|
||||
<div className="h-full flex flex-col">
|
||||
<div>
|
||||
<div className="px-4 pt-4">
|
||||
<div
|
||||
className="px-3 py-2 rounded-full bg-green-400 hover:bg-green-500 text-white dark:text-gray-700 inline-block cursor-pointer"
|
||||
<div className="px-4 pt-4 flex gap-4">
|
||||
<Button
|
||||
type="primary"
|
||||
size="large"
|
||||
onClick={() => navigate('/monitor/add')}
|
||||
>
|
||||
Add new Montior
|
||||
</div>
|
||||
</Button>
|
||||
<Button
|
||||
type="default"
|
||||
size="large"
|
||||
onClick={() => navigate('/monitor/pages')}
|
||||
>
|
||||
Pages
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="py-5 flex flex-1 overflow-hidden">
|
||||
@ -31,6 +42,8 @@ export const MonitorPage: React.FC = React.memo(() => {
|
||||
<Route path="/:monitorId" element={<MonitorDetail />} />
|
||||
<Route path="/:monitorId/edit" element={<MonitorEdit />} />
|
||||
<Route path="/add" element={<MonitorAdd />} />
|
||||
<Route path="/pages" element={<MonitorPageList />} />
|
||||
<Route path="/pages/add" element={<MonitorPageAdd />} />
|
||||
</Routes>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -418,6 +418,23 @@ export const monitorRouter = router({
|
||||
},
|
||||
});
|
||||
}),
|
||||
getAllPages: workspaceProcedure
|
||||
.meta(
|
||||
buildMonitorOpenapi({
|
||||
method: 'GET',
|
||||
path: '/getAllPages',
|
||||
})
|
||||
)
|
||||
.output(z.array(MonitorStatusPageModelSchema))
|
||||
.query(({ input }) => {
|
||||
const { workspaceId } = input;
|
||||
|
||||
return prisma.monitorStatusPage.findMany({
|
||||
where: {
|
||||
workspaceId,
|
||||
},
|
||||
});
|
||||
}),
|
||||
createPage: workspaceOwnerProcedure
|
||||
.meta(
|
||||
buildMonitorOpenapi({
|
||||
|
@ -1,2 +1,4 @@
|
||||
export const hostnameRegex =
|
||||
/^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]).)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9-]*[A-Za-z0-9])$/;
|
||||
|
||||
export const slugRegex = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
||||
|
Loading…
Reference in New Issue
Block a user