From 4d39cb5ef4d95626e600289a1e949e78ccd7906f Mon Sep 17 00:00:00 2001 From: moonrailgun Date: Tue, 17 Sep 2024 19:08:58 +0800 Subject: [PATCH] feat: add group feature in backend --- .../components/monitor/StatusPage/Body.tsx | 90 +++++++++++++++++++ .../monitor/StatusPage/EditForm.tsx | 9 +- .../monitor/StatusPage/ServiceList.tsx | 23 ++--- .../monitor/StatusPage/Services.tsx | 3 + .../components/monitor/StatusPage/index.tsx | 21 +++-- .../components/monitor/StatusPage/schema.ts | 20 +++++ .../migration.sql | 2 + src/server/prisma/schema.prisma | 5 +- src/server/prisma/zod/monitorstatuspage.ts | 4 + src/server/trpc/routers/monitor.ts | 27 +++++- 10 files changed, 171 insertions(+), 33 deletions(-) create mode 100644 src/client/components/monitor/StatusPage/Body.tsx create mode 100644 src/client/components/monitor/StatusPage/schema.ts create mode 100644 src/server/prisma/migrations/20240916135644_add_monitor_status_page_body/migration.sql diff --git a/src/client/components/monitor/StatusPage/Body.tsx b/src/client/components/monitor/StatusPage/Body.tsx new file mode 100644 index 0000000..3a911d3 --- /dev/null +++ b/src/client/components/monitor/StatusPage/Body.tsx @@ -0,0 +1,90 @@ +import { AppRouterOutput, trpc } from '@/api/trpc'; +import React, { useMemo } from 'react'; +import { bodySchema } from './schema'; +import { Empty } from 'antd'; +import { useTranslation } from '@i18next-toolkit/react'; +import { MonitorListItem } from '../MonitorListItem'; + +interface StatusPageBodyProps { + workspaceId: string; + info: NonNullable; +} +export const StatusPageBody: React.FC = React.memo( + (props) => { + const { info } = props; + const { t } = useTranslation(); + + const body = useMemo(() => { + const res = bodySchema.safeParse(info.body); + if (res.success) { + return res.data; + } else { + return { groups: [] }; + } + }, [info.body]); + + return ( +
+ {body.groups.map((group) => ( +
+
{group.title}
+ +
+ {group.children.length === 0 && ( + + )} + + {group.children.map((item) => { + if (item.type === 'monitor') { + return ( + + ); + } + + return null; + })} +
+
+ ))} +
+ ); + } +); +StatusPageBody.displayName = 'StatusPageBody'; + +export const StatusItemMonitor: React.FC<{ + id: string; + showCurrent: boolean; + workspaceId: string; +}> = React.memo((props) => { + const { data: list = [], isLoading } = trpc.monitor.getPublicInfo.useQuery({ + monitorIds: [props.id], + }); + + if (isLoading) { + return null; + } + + const item = list[0]; + + if (!item) { + return null; + } + + return ( + + ); +}); +StatusItemMonitor.displayName = 'StatusItemMonitor'; diff --git a/src/client/components/monitor/StatusPage/EditForm.tsx b/src/client/components/monitor/StatusPage/EditForm.tsx index 1236b02..77412e8 100644 --- a/src/client/components/monitor/StatusPage/EditForm.tsx +++ b/src/client/components/monitor/StatusPage/EditForm.tsx @@ -27,7 +27,8 @@ import { Collapsible, CollapsibleContent } from '@/components/ui/collapsible'; import { CollapsibleTrigger } from '@radix-ui/react-collapsible'; import { CaretSortIcon } from '@radix-ui/react-icons'; import { DeprecatedBadge } from '@/components/DeprecatedBadge'; -import { groupItemSchema, MonitorStatusPageServiceList } from './ServiceList'; +import { MonitorStatusPageServiceList } from './ServiceList'; +import { bodySchema } from './schema'; const Text = Typography.Text; @@ -40,11 +41,7 @@ const editFormSchema = z.object({ .regex(domainRegex, 'Invalid domain') .or(z.literal('')) .optional(), - body: z - .object({ - groups: z.array(groupItemSchema), - }) - .default({ groups: [] }), + body: bodySchema, /** * @deprecated diff --git a/src/client/components/monitor/StatusPage/ServiceList.tsx b/src/client/components/monitor/StatusPage/ServiceList.tsx index 6aa6c12..f7ff16e 100644 --- a/src/client/components/monitor/StatusPage/ServiceList.tsx +++ b/src/client/components/monitor/StatusPage/ServiceList.tsx @@ -13,19 +13,7 @@ import { MonitorPicker } from '../MonitorPicker'; import { Switch } from '@/components/ui/switch'; import { set } from 'lodash-es'; import { EditableText } from '@/components/EditableText'; - -export const leafItemSchema = z.object({ - key: z.string(), - id: z.string(), - type: z.enum(['monitor']), - showCurrent: z.boolean().default(false).optional(), -}); - -export const groupItemSchema = z.object({ - key: z.string(), - title: z.string(), - children: z.array(leafItemSchema), -}); +import { groupItemSchema, leafItemSchema } from './schema'; type GroupItemProps = Omit, 'key' | 'children'>; type LeafItemProps = Omit, 'key'>; @@ -167,7 +155,7 @@ export const MonitorStatusPageServiceList: React.FC 0 && (
handleChangeGroupTitle(group.key, text)} /> @@ -193,7 +181,10 @@ export const MonitorStatusPageServiceList: React.FC 0 && 'border-l-4 border-gray-600 p-2')} + className={cn( + level > 0 && + 'border-l-4 border-gray-300 p-2 dark:border-gray-600' + )} > {children}
@@ -203,7 +194,7 @@ export const MonitorStatusPageServiceList: React.FC - {i !== 0 && } + {i !== 0 && }
= React.memo( (props) => { const { t } = useTranslation(); diff --git a/src/client/components/monitor/StatusPage/index.tsx b/src/client/components/monitor/StatusPage/index.tsx index 5682727..f0cf848 100644 --- a/src/client/components/monitor/StatusPage/index.tsx +++ b/src/client/components/monitor/StatusPage/index.tsx @@ -22,6 +22,7 @@ import { SheetTrigger, } from '@/components/ui/sheet'; import { cn } from '@/utils/style'; +import { StatusPageBody } from './Body'; interface MonitorStatusPageProps { slug: string; @@ -150,13 +151,21 @@ export const MonitorStatusPage: React.FC = React.memo(
-
{t('Services')}
- + {/* Body */} {info && ( - + + )} + + {/* deprecated monitor list */} + {info && Array.isArray(monitorList) && monitorList.length > 0 && ( + <> +
{t('Services')}
+ + + )} diff --git a/src/client/components/monitor/StatusPage/schema.ts b/src/client/components/monitor/StatusPage/schema.ts new file mode 100644 index 0000000..cef8029 --- /dev/null +++ b/src/client/components/monitor/StatusPage/schema.ts @@ -0,0 +1,20 @@ +import { z } from 'zod'; + +export const leafItemSchema = z.object({ + key: z.string(), + id: z.string(), + type: z.enum(['monitor']), + showCurrent: z.boolean().default(false).optional(), +}); + +export const groupItemSchema = z.object({ + key: z.string(), + title: z.string(), + children: z.array(leafItemSchema), +}); + +export const bodySchema = z + .object({ + groups: z.array(groupItemSchema), + }) + .default({ groups: [] }); diff --git a/src/server/prisma/migrations/20240916135644_add_monitor_status_page_body/migration.sql b/src/server/prisma/migrations/20240916135644_add_monitor_status_page_body/migration.sql new file mode 100644 index 0000000..e791f96 --- /dev/null +++ b/src/server/prisma/migrations/20240916135644_add_monitor_status_page_body/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "MonitorStatusPage" ADD COLUMN "body" JSONB NOT NULL DEFAULT '{}'; diff --git a/src/server/prisma/schema.prisma b/src/server/prisma/schema.prisma index 1616f04..226e7a6 100644 --- a/src/server/prisma/schema.prisma +++ b/src/server/prisma/schema.prisma @@ -421,9 +421,12 @@ model MonitorStatusPage { slug String @unique // url slug title String @db.VarChar(100) description String @default("") @db.VarChar(1000) + /// [CommonPayload] + /// @zod.custom(imports.CommonPayloadSchema) + body Json @default("{}") /// [MonitorStatusPageList] /// @zod.custom(imports.MonitorStatusPageListSchema) - monitorList Json @default("[]") // monitor list + monitorList Json @default("[]") // monitor list @deprecated domain String? // custom domain which can add cname record createdAt DateTime @default(now()) @db.Timestamptz(6) updatedAt DateTime @updatedAt @db.Timestamptz(6) diff --git a/src/server/prisma/zod/monitorstatuspage.ts b/src/server/prisma/zod/monitorstatuspage.ts index 966f207..1abe765 100644 --- a/src/server/prisma/zod/monitorstatuspage.ts +++ b/src/server/prisma/zod/monitorstatuspage.ts @@ -14,6 +14,10 @@ export const MonitorStatusPageModelSchema = z.object({ slug: z.string(), title: z.string(), description: z.string(), + /** + * [CommonPayload] + */ + body: imports.CommonPayloadSchema, /** * [MonitorStatusPageList] */ diff --git a/src/server/trpc/routers/monitor.ts b/src/server/trpc/routers/monitor.ts index a4fb064..9bc4614 100644 --- a/src/server/trpc/routers/monitor.ts +++ b/src/server/trpc/routers/monitor.ts @@ -601,6 +601,7 @@ export const monitorRouter = router({ .merge( MonitorStatusPageModelSchema.pick({ description: true, + body: true, monitorList: true, domain: true, }).partial() @@ -608,8 +609,15 @@ export const monitorRouter = router({ ) .output(MonitorStatusPageModelSchema) .mutation(async ({ input }) => { - const { workspaceId, slug, title, description, monitorList, domain } = - input; + const { + workspaceId, + slug, + title, + description, + body, + monitorList, + domain, + } = input; const existSlugCount = await prisma.monitorStatusPage.count({ where: { @@ -631,6 +639,7 @@ export const monitorRouter = router({ slug, title, description, + body, monitorList, domain: domain || null, // make sure not '' }, @@ -661,6 +670,7 @@ export const monitorRouter = router({ slug: true, title: true, description: true, + body: true, monitorList: true, domain: true, }).partial() @@ -668,8 +678,16 @@ export const monitorRouter = router({ ) .output(MonitorStatusPageModelSchema) .mutation(async ({ input }) => { - const { id, workspaceId, slug, title, description, monitorList, domain } = - input; + const { + id, + workspaceId, + slug, + title, + description, + body, + monitorList, + domain, + } = input; if (slug) { const existSlugCount = await prisma.monitorStatusPage.count({ @@ -699,6 +717,7 @@ export const monitorRouter = router({ slug, title, description, + body, monitorList, domain: domain || null, },