From b3588b33781514df92e9eb4e4e2a7c73e3ccd628 Mon Sep 17 00:00:00 2001 From: UUBulb <35923940+uubulb@users.noreply.github.com> Date: Wed, 20 Nov 2024 21:37:03 +0800 Subject: [PATCH] implement notification page (#8) --- src/api/alert-rule.ts | 14 ++ src/api/notification-group.ts | 6 +- src/api/notification.ts | 18 ++ src/api/terminal.ts | 2 +- src/components/alert-rule.tsx | 281 ++++++++++++++++++++++++++ src/components/ddns.tsx | 2 +- src/components/group-tab.tsx | 4 +- src/components/header.tsx | 5 + src/components/notification-group.tsx | 30 +-- src/components/notification-tab.tsx | 23 +++ src/components/notifier.tsx | 263 ++++++++++++++++++++++++ src/components/server-group.tsx | 2 +- src/hooks/useNotfication.tsx | 52 +++++ src/hooks/useNotificationStore.ts | 18 ++ src/main.tsx | 13 +- src/routes/alert-rule.tsx | 186 +++++++++++++++++ src/routes/notification-group.tsx | 6 +- src/routes/notification.tsx | 184 +++++++++++++++++ src/routes/server-group.tsx | 6 +- src/routes/server.tsx | 2 +- src/routes/service.tsx | 2 +- src/types/alert-rule.tsx | 4 + src/types/api.ts | 19 +- src/types/index.ts | 4 + src/types/notification.ts | 9 + src/types/notificationContext.ts | 6 + src/types/notificationStore.ts | 8 + 27 files changed, 1133 insertions(+), 36 deletions(-) create mode 100644 src/api/alert-rule.ts create mode 100644 src/api/notification.ts create mode 100644 src/components/alert-rule.tsx create mode 100644 src/components/notification-tab.tsx create mode 100644 src/components/notifier.tsx create mode 100644 src/hooks/useNotfication.tsx create mode 100644 src/hooks/useNotificationStore.ts create mode 100644 src/routes/alert-rule.tsx create mode 100644 src/routes/notification.tsx create mode 100644 src/types/alert-rule.tsx create mode 100644 src/types/notification.ts create mode 100644 src/types/notificationContext.ts create mode 100644 src/types/notificationStore.ts diff --git a/src/api/alert-rule.ts b/src/api/alert-rule.ts new file mode 100644 index 0000000..893c621 --- /dev/null +++ b/src/api/alert-rule.ts @@ -0,0 +1,14 @@ +import { ModelAlertRuleForm } from "@/types" +import { fetcher, FetcherMethod } from "./api" + +export const createAlertRule = async (data: ModelAlertRuleForm): Promise => { + return fetcher(FetcherMethod.POST, '/api/v1/alert-rule', data); +} + +export const updateAlertRule = async (id: number, data: ModelAlertRuleForm): Promise => { + return fetcher(FetcherMethod.PATCH, `/api/v1/alert-rule/${id}`, data); +} + +export const deleteAlertRules = async (id: number[]): Promise => { + return fetcher(FetcherMethod.POST, '/api/v1/batch-delete/alert-rule', id); +} diff --git a/src/api/notification-group.ts b/src/api/notification-group.ts index 8ffc501..6a73b18 100644 --- a/src/api/notification-group.ts +++ b/src/api/notification-group.ts @@ -1,4 +1,4 @@ -import { ModelNotificationGroupForm } from "@/types" +import { ModelNotificationGroupForm, ModelNotificationGroupResponseItem } from "@/types" import { fetcher, FetcherMethod } from "./api" export const createNotificationGroup = async (data: ModelNotificationGroupForm): Promise => { @@ -12,3 +12,7 @@ export const updateNotificationGroup = async (id: number, data: ModelNotificatio export const deleteNotificationGroups = async (id: number[]): Promise => { return fetcher(FetcherMethod.POST, `/api/v1/batch-delete/notification-group`, id); } + +export const getNotificationGroups = async (): Promise => { + return fetcher(FetcherMethod.GET, '/api/v1/notification-group', null); +} diff --git a/src/api/notification.ts b/src/api/notification.ts new file mode 100644 index 0000000..b5fdb57 --- /dev/null +++ b/src/api/notification.ts @@ -0,0 +1,18 @@ +import { ModelNotificationForm, ModelNotification } from "@/types" +import { fetcher, FetcherMethod } from "./api" + +export const createNotification = async (data: ModelNotificationForm): Promise => { + return fetcher(FetcherMethod.POST, '/api/v1/notification', data); +} + +export const updateNotification = async (id: number, data: ModelNotificationForm): Promise => { + return fetcher(FetcherMethod.PATCH, `/api/v1/notification/${id}`, data); +} + +export const deleteNotification = async (id: number[]): Promise => { + return fetcher(FetcherMethod.POST, '/api/v1/batch-delete/notification', id); +} + +export const getNotification = async (): Promise => { + return fetcher(FetcherMethod.GET, '/api/v1/notification', null); +} diff --git a/src/api/terminal.ts b/src/api/terminal.ts index 0e21c05..9aa5203 100644 --- a/src/api/terminal.ts +++ b/src/api/terminal.ts @@ -1,4 +1,4 @@ -import { ModelTerminalForm, ModelCreateTerminalResponse } from "@/types"; +import { ModelCreateTerminalResponse } from "@/types"; import { fetcher, FetcherMethod } from "./api" export const createTerminal = async (id: number): Promise => { diff --git a/src/components/alert-rule.tsx b/src/components/alert-rule.tsx new file mode 100644 index 0000000..7d03812 --- /dev/null +++ b/src/components/alert-rule.tsx @@ -0,0 +1,281 @@ +import { Button } from "@/components/ui/button" +import { + Dialog, + DialogClose, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog" +import { Input } from "@/components/ui/input" +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select" +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form" +import { ScrollArea } from "@/components/ui/scroll-area" +import { useForm } from "react-hook-form" +import { z } from "zod" +import { zodResolver } from "@hookform/resolvers/zod" +import { ModelAlertRule } from "@/types" +import { createAlertRule, updateAlertRule } from "@/api/alert-rule" +import { Checkbox } from "@/components/ui/checkbox" +import { Label } from "@/components/ui/label" +import { conv } from "@/lib/utils" +import { useState } from "react" +import { KeyedMutator } from "swr" +import { asOptionalField } from "@/lib/utils" +import { IconButton } from "@/components/xui/icon-button" +import { triggerModes } from "@/types" +import { Textarea } from "./ui/textarea" + +interface AlertRuleCardProps { + data?: ModelAlertRule; + mutate: KeyedMutator; +} + +const ruleSchema = z.object({ + type: z.string(), + min: asOptionalField(z.number()), + max: asOptionalField(z.number()), + cycle_start: asOptionalField(z.string()), + cycle_interval: asOptionalField(z.number()), + cycle_unit: asOptionalField(z.enum(['hour', 'day', 'week', 'month', 'year'])), + duration: asOptionalField(z.number()), + cover: z.number().int().min(0), + ignore: asOptionalField(z.record(z.boolean())), + next_transfer_at: asOptionalField(z.record(z.string())), + last_cycle_status: asOptionalField((z.boolean())), +}); + +const alertRuleFormSchema = z.object({ + name: z.string().min(1), + rules_raw: z.string().refine((val) => { + try { + JSON.parse(val); + return true; + } catch (e) { + return false; + } + }, { + message: 'Invalid JSON string', + }), + rules: z.array(ruleSchema), + fail_trigger_tasks: z.array(z.string()).transform((v => { + return v.filter(Boolean).map(Number); + })), + recover_trigger_tasks: z.array(z.string()).transform((v => { + return v.filter(Boolean).map(Number); + })), + notification_group_id: z.coerce.number().int(), + trigger_mode: z.coerce.number().int().min(0), + enable: asOptionalField(z.boolean()), +}); + +export const AlertRuleCard: React.FC = ({ data, mutate }) => { + const form = useForm>({ + resolver: zodResolver(alertRuleFormSchema), + defaultValues: data ? { + ...data, + rules_raw: JSON.stringify(data.rules), + } : { + name: "", + rules_raw: "", + rules: [], + fail_trigger_tasks: [], + recover_trigger_tasks: [], + notification_group_id: 0, + trigger_mode: 0, + }, + resetOptions: { + keepDefaultValues: false, + } + }) + + const [open, setOpen] = useState(false); + + const onSubmit = async (values: z.infer) => { + values.rules = JSON.parse(values.rules_raw); + data?.id ? await updateAlertRule(data.id, values) : await createAlertRule(values); + setOpen(false); + await mutate(); + form.reset(); + } + + return ( + + + {data + ? + + : + + } + + + +
+ + New Alert Rule + + +
+ + ( + + Name + + + + + + )} + /> + ( + + Rules + +