feat: add status list and add route

This commit is contained in:
moonrailgun 2023-12-13 19:46:32 +08:00
parent 669b2ed8b8
commit 0cf934c9e3
5 changed files with 170 additions and 4 deletions

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

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

View File

@ -5,6 +5,9 @@ import { MonitorAdd } from './Add';
import { MonitorDetail } from './Detail'; import { MonitorDetail } from './Detail';
import { MonitorEdit } from './Edit'; import { MonitorEdit } from './Edit';
import { MonitorOverview } from './Overview'; import { MonitorOverview } from './Overview';
import { Button } from 'antd';
import { MonitorPageList } from './PageList';
import { MonitorPageAdd } from './PageAdd';
export const MonitorPage: React.FC = React.memo(() => { export const MonitorPage: React.FC = React.memo(() => {
const navigate = useNavigate(); const navigate = useNavigate();
@ -12,13 +15,21 @@ export const MonitorPage: React.FC = React.memo(() => {
return ( return (
<div className="h-full flex flex-col"> <div className="h-full flex flex-col">
<div> <div>
<div className="px-4 pt-4"> <div className="px-4 pt-4 flex gap-4">
<div <Button
className="px-3 py-2 rounded-full bg-green-400 hover:bg-green-500 text-white dark:text-gray-700 inline-block cursor-pointer" type="primary"
size="large"
onClick={() => navigate('/monitor/add')} onClick={() => navigate('/monitor/add')}
> >
Add new Montior Add new Montior
</div> </Button>
<Button
type="default"
size="large"
onClick={() => navigate('/monitor/pages')}
>
Pages
</Button>
</div> </div>
</div> </div>
<div className="py-5 flex flex-1 overflow-hidden"> <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" element={<MonitorDetail />} />
<Route path="/:monitorId/edit" element={<MonitorEdit />} /> <Route path="/:monitorId/edit" element={<MonitorEdit />} />
<Route path="/add" element={<MonitorAdd />} /> <Route path="/add" element={<MonitorAdd />} />
<Route path="/pages" element={<MonitorPageList />} />
<Route path="/pages/add" element={<MonitorPageAdd />} />
</Routes> </Routes>
</div> </div>
</div> </div>

View File

@ -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 createPage: workspaceOwnerProcedure
.meta( .meta(
buildMonitorOpenapi({ buildMonitorOpenapi({

View File

@ -1,2 +1,4 @@
export const hostnameRegex = 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])$/; /^(([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]+)*$/;