From e37f30d33502671a123f2c3771a273628d6e3bcb Mon Sep 17 00:00:00 2001 From: UUBulb <35923940+uubulb@users.noreply.github.com> Date: Wed, 20 Nov 2024 00:19:40 +0800 Subject: [PATCH] implement cron page (#7) --- src/api/cron.ts | 18 ++ src/api/ddns.ts | 8 +- src/api/nat.ts | 6 +- src/api/notification-group.ts | 2 +- src/api/server-group.ts | 4 +- src/api/server.ts | 6 +- src/api/service.ts | 6 +- src/api/user.ts | 4 +- src/components/cron.tsx | 249 ++++++++++++++++++++++++++ src/components/header.tsx | 7 +- src/components/notification-group.tsx | 4 +- src/components/service.tsx | 4 +- src/components/xui/icon-button.tsx | 7 +- src/main.tsx | 5 + src/routes/cron.tsx | 237 ++++++++++++++++++++++++ src/routes/notification-group.tsx | 2 +- src/routes/service.tsx | 2 +- src/types/api.ts | 1 - src/types/cron.ts | 10 ++ src/types/index.ts | 1 + 20 files changed, 555 insertions(+), 28 deletions(-) create mode 100644 src/api/cron.ts create mode 100644 src/components/cron.tsx create mode 100644 src/routes/cron.tsx create mode 100644 src/types/cron.ts diff --git a/src/api/cron.ts b/src/api/cron.ts new file mode 100644 index 0000000..2eb8146 --- /dev/null +++ b/src/api/cron.ts @@ -0,0 +1,18 @@ +import { ModelCronForm } from "@/types" +import { fetcher, FetcherMethod } from "./api" + +export const createCron = async (data: ModelCronForm): Promise => { + return fetcher(FetcherMethod.POST, '/api/v1/cron', data); +} + +export const updateCron = async (id: number, data: ModelCronForm): Promise => { + return fetcher(FetcherMethod.PATCH, `/api/v1/cron/${id}`, data); +} + +export const deleteCron = async (id: number[]): Promise => { + return fetcher(FetcherMethod.POST, '/api/v1/batch-delete/cron', id); +} + +export const runCron = async (id: number): Promise => { + return fetcher(FetcherMethod.GET, `/api/v1/cron/${id}/manual`, null); +} diff --git a/src/api/ddns.ts b/src/api/ddns.ts index 0b74c2d..d2accd1 100644 --- a/src/api/ddns.ts +++ b/src/api/ddns.ts @@ -2,17 +2,17 @@ import { ModelDDNSForm } from "@/types" import { fetcher, FetcherMethod } from "./api" export const createDDNSProfile = async (data: ModelDDNSForm): Promise => { - return fetcher(FetcherMethod.POST, '/api/v1/ddns', data) + return fetcher(FetcherMethod.POST, '/api/v1/ddns', data); } export const updateDDNSProfile = async (id: number, data: ModelDDNSForm): Promise => { - return fetcher(FetcherMethod.PATCH, `/api/v1/ddns/${id}`, data) + return fetcher(FetcherMethod.PATCH, `/api/v1/ddns/${id}`, data); } export const deleteDDNSProfiles = async (id: number[]): Promise => { - return fetcher(FetcherMethod.POST, '/api/v1/batch-delete/ddns', id) + return fetcher(FetcherMethod.POST, '/api/v1/batch-delete/ddns', id); } export const getDDNSProviders = async (): Promise => { - return fetcher(FetcherMethod.GET, '/api/v1/ddns/providers', null) + return fetcher(FetcherMethod.GET, '/api/v1/ddns/providers', null); } diff --git a/src/api/nat.ts b/src/api/nat.ts index 524a02e..81d7809 100644 --- a/src/api/nat.ts +++ b/src/api/nat.ts @@ -2,13 +2,13 @@ import { ModelNATForm } from "@/types" import { fetcher, FetcherMethod } from "./api" export const createNAT = async (data: ModelNATForm): Promise => { - return fetcher(FetcherMethod.POST, '/api/v1/nat', data) + return fetcher(FetcherMethod.POST, '/api/v1/nat', data); } export const updateNAT = async (id: number, data: ModelNATForm): Promise => { - return fetcher(FetcherMethod.PATCH, `/api/v1/nat/${id}`, data) + return fetcher(FetcherMethod.PATCH, `/api/v1/nat/${id}`, data); } export const deleteNAT = async (id: number[]): Promise => { - return fetcher(FetcherMethod.POST, '/api/v1/batch-delete/nat', id) + return fetcher(FetcherMethod.POST, '/api/v1/batch-delete/nat', id); } diff --git a/src/api/notification-group.ts b/src/api/notification-group.ts index 23c102a..8ffc501 100644 --- a/src/api/notification-group.ts +++ b/src/api/notification-group.ts @@ -10,5 +10,5 @@ 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) + return fetcher(FetcherMethod.POST, `/api/v1/batch-delete/notification-group`, id); } diff --git a/src/api/server-group.ts b/src/api/server-group.ts index e38418c..b482711 100644 --- a/src/api/server-group.ts +++ b/src/api/server-group.ts @@ -10,9 +10,9 @@ export const updateServerGroup = async (id: number, data: ModelServerGroupForm): } export const deleteServerGroups = async (id: number[]): Promise => { - return fetcher(FetcherMethod.POST, `/api/v1/batch-delete/server-group`, id) + return fetcher(FetcherMethod.POST, `/api/v1/batch-delete/server-group`, id); } export const getServerGroups = async (): Promise => { - return fetcher(FetcherMethod.GET, '/api/v1/server-group', null) + return fetcher(FetcherMethod.GET, '/api/v1/server-group', null); } diff --git a/src/api/server.ts b/src/api/server.ts index 470f00d..d87381b 100644 --- a/src/api/server.ts +++ b/src/api/server.ts @@ -2,13 +2,13 @@ import { ModelServer, ModelServerForm } from "@/types" import { fetcher, FetcherMethod } from "./api" export const updateServer = async (id: number, data: ModelServerForm): Promise => { - return fetcher(FetcherMethod.PATCH, `/api/v1/server/${id}`, data) + return fetcher(FetcherMethod.PATCH, `/api/v1/server/${id}`, data); } export const deleteServer = async (id: number[]): Promise => { - return fetcher(FetcherMethod.POST, '/api/v1/batch-delete/server', id) + return fetcher(FetcherMethod.POST, '/api/v1/batch-delete/server', id); } export const getServers = async (): Promise => { - return fetcher(FetcherMethod.GET, '/api/v1/server', null) + return fetcher(FetcherMethod.GET, '/api/v1/server', null); } diff --git a/src/api/service.ts b/src/api/service.ts index e96280f..1b7efe3 100644 --- a/src/api/service.ts +++ b/src/api/service.ts @@ -2,13 +2,13 @@ import { ModelServiceForm } from "@/types" import { fetcher, FetcherMethod } from "./api" export const createService = async (data: ModelServiceForm): Promise => { - return fetcher(FetcherMethod.POST, '/api/v1/service', data) + return fetcher(FetcherMethod.POST, '/api/v1/service', data); } export const updateService = async (id: number, data: ModelServiceForm): Promise => { - return fetcher(FetcherMethod.PATCH, `/api/v1/service/${id}`, data) + return fetcher(FetcherMethod.PATCH, `/api/v1/service/${id}`, data); } export const deleteService = async (id: number[]): Promise => { - return fetcher(FetcherMethod.POST, '/api/v1/batch-delete/service', id) + return fetcher(FetcherMethod.POST, '/api/v1/batch-delete/service', id); } diff --git a/src/api/user.ts b/src/api/user.ts index e6c0409..6c031cf 100644 --- a/src/api/user.ts +++ b/src/api/user.ts @@ -2,9 +2,9 @@ import { ModelUser } from "@/types" import { fetcher, FetcherMethod } from "./api" export const getProfile = async (): Promise => { - return fetcher(FetcherMethod.GET, '/api/v1/profile', null) + return fetcher(FetcherMethod.GET, '/api/v1/profile', null); } export const login = async (username: string, password: string): Promise => { - return fetcher(FetcherMethod.POST, '/api/v1/login', { username, password }) + return fetcher(FetcherMethod.POST, '/api/v1/login', { username, password }); } diff --git a/src/components/cron.tsx b/src/components/cron.tsx new file mode 100644 index 0000000..be4cf57 --- /dev/null +++ b/src/components/cron.tsx @@ -0,0 +1,249 @@ +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 { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form" +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select" +import { ScrollArea } from "@/components/ui/scroll-area" +import { useForm } from "react-hook-form" +import { z } from "zod" +import { zodResolver } from "@hookform/resolvers/zod" +import { ModelCron } from "@/types" +import { useState } from "react" +import { KeyedMutator } from "swr" +import { IconButton } from "@/components/xui/icon-button" +import { createCron, updateCron } from "@/api/cron" +import { asOptionalField } from "@/lib/utils" +import { cronTypes, cronCoverageTypes } from "@/types" +import { Textarea } from "./ui/textarea" +import { conv } from "@/lib/utils" + +interface CronCardProps { + data?: ModelCron; + mutate: KeyedMutator; +} + +const cronFormSchema = z.object({ + task_type: z.coerce.number(), + name: z.string().min(1), + scheduler: z.string(), + command: asOptionalField(z.string()), + servers: z.array(z.string()).transform((v => { + return v.filter(Boolean).map(Number); + })), + cover: z.coerce.number().int(), + push_successful: asOptionalField(z.boolean()), + notification_group_id: z.coerce.number().int(), +}); + +export const CronCard: React.FC = ({ data, mutate }) => { + const form = useForm>({ + resolver: zodResolver(cronFormSchema), + defaultValues: data ? data : { + name: "", + task_type: 0, + scheduler: "", + servers: [], + cover: 0, + notification_group_id: 0, + }, + resetOptions: { + keepDefaultValues: false, + } + }) + + const [open, setOpen] = useState(false); + + const onSubmit = async (values: z.infer) => { + data?.id ? await updateCron(data.id, values) : await createCron(values); + setOpen(false); + await mutate(); + form.reset(); + } + + return ( + + + {data + ? + + : + + } + + + +
+ + New Task + + +
+ + ( + + Name + + + + + + )} + /> + ( + + Task Type + + + + )} + /> + ( + + Cron expression + + + + + + )} + /> + ( + + Command + +