Files
admin-frontend-domain/src/components/service.tsx
T
胡说丷刂 ff231da753 service 增加权重 (#150)
* 服务增加权重

* 避免显示成 undefined

---------

Co-authored-by: huYang <306061454@qq.com>
2026-02-14 10:10:24 +08:00

474 lines
24 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { createService, updateService } from "@/api/service"
import { Button } from "@/components/ui/button"
import { Checkbox } from "@/components/ui/checkbox"
import {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog"
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { ScrollArea } from "@/components/ui/scroll-area"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
import { IconButton } from "@/components/xui/icon-button"
import { useNotification } from "@/hooks/useNotfication"
import { useServer } from "@/hooks/useServer"
import { conv } from "@/lib/utils"
import { asOptionalField } from "@/lib/utils"
import { ModelService } from "@/types"
import { serviceCoverageTypes, serviceTypes } from "@/types"
import { zodResolver } from "@hookform/resolvers/zod"
import { useState } from "react"
import { useForm } from "react-hook-form"
import { useTranslation } from "react-i18next"
import { toast } from "sonner"
import { KeyedMutator } from "swr"
import { z } from "zod"
import { Combobox } from "./ui/combobox"
import { MultiSelect } from "./xui/multi-select"
interface ServiceCardProps {
data?: ModelService
mutate: KeyedMutator<ModelService[]>
}
const serviceFormSchema = z.object({
cover: z.coerce.number().int().min(0),
display_index: z.coerce.number().int(),
duration: z.coerce.number().int().min(30),
enable_show_in_service: asOptionalField(z.boolean()),
enable_trigger_task: asOptionalField(z.boolean()),
fail_trigger_tasks: z.array(z.number()),
fail_trigger_tasks_raw: z.string(),
latency_notify: asOptionalField(z.boolean()),
max_latency: z.coerce.number().int().min(0),
min_latency: z.coerce.number().int().min(0),
name: z.string().min(1),
notification_group_id: z.coerce.number().int(),
notify: asOptionalField(z.boolean()),
recover_trigger_tasks: z.array(z.number()),
recover_trigger_tasks_raw: z.string(),
skip_servers: z.record(z.string(), z.boolean()),
skip_servers_raw: z.array(z.string()),
target: z.string(),
type: z.coerce.number().int().min(0),
})
export const ServiceCard: React.FC<ServiceCardProps> = ({ data, mutate }) => {
const { t } = useTranslation()
const form = useForm({
resolver: zodResolver(serviceFormSchema) as any,
defaultValues: data
? {
...data,
fail_trigger_tasks_raw: conv.arrToStr(data.fail_trigger_tasks),
recover_trigger_tasks_raw: conv.arrToStr(data.recover_trigger_tasks),
skip_servers_raw: conv.recordToStrArr(data.skip_servers ? data.skip_servers : {}),
}
: {
type: 1,
cover: 0,
display_index: 0,
name: "",
target: "",
max_latency: 0.0,
min_latency: 0.0,
duration: 30,
notification_group_id: 0,
fail_trigger_tasks: [],
fail_trigger_tasks_raw: "",
recover_trigger_tasks: [],
recover_trigger_tasks_raw: "",
skip_servers: {},
skip_servers_raw: [],
},
resetOptions: {
keepDefaultValues: false,
},
})
const [open, setOpen] = useState(false)
const onSubmit = async (values: any) => {
values.skip_servers = conv.arrToRecord(values.skip_servers_raw)
values.fail_trigger_tasks = conv.strToArr(values.fail_trigger_tasks_raw).map(Number)
values.recover_trigger_tasks = conv.strToArr(values.recover_trigger_tasks_raw).map(Number)
const { skip_servers_raw, ...requiredFields } = values
try {
data?.id
? await updateService(data.id, requiredFields)
: await createService(requiredFields)
} catch (e) {
console.error(e)
toast(t("Error"), {
description: t("Results.UnExpectedError"),
})
return
}
setOpen(false)
await mutate()
form.reset()
}
const { servers } = useServer()
const serverList = servers?.map((s) => ({
value: `${s.id}`,
label: s.name,
})) || [{ value: "", label: "" }]
const { notifierGroup } = useNotification()
const ngroupList = notifierGroup?.map((ng) => ({
value: `${ng.group.id}`,
label: ng.group.name,
})) || [{ value: "", label: "" }]
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
{data ? <IconButton variant="outline" icon="edit" /> : <IconButton icon="plus" />}
</DialogTrigger>
<DialogContent className="sm:max-w-xl">
<ScrollArea className="max-h-[calc(100dvh-5rem)] p-3">
<div className="items-center mx-1">
<DialogHeader>
<DialogTitle>
{data ? t("EditService") : t("CreateService")}
</DialogTitle>
<DialogDescription />
</DialogHeader>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-2 my-2">
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>{t("Name")}</FormLabel>
<FormControl>
<Input
placeholder="My Service Monitor"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="display_index"
render={({ field }) => (
<FormItem>
<FormLabel>{t("Weight")}</FormLabel>
<FormControl>
<Input type="number" placeholder="0" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="target"
render={({ field }) => (
<FormItem>
<FormLabel>{t("Target")}</FormLabel>
<FormControl>
<Input
placeholder="HTTP (https://t.tt)Ping (t.tt)TCP (t.tt:80)"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="type"
render={({ field }) => (
<FormItem>
<FormLabel>{t("Type")}</FormLabel>
<Select
onValueChange={field.onChange}
defaultValue={`${field.value}`}
>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Select service type" />
</SelectTrigger>
</FormControl>
<SelectContent>
{Object.entries(serviceTypes).map(([k, v]) => (
<SelectItem key={k} value={k}>
{v}
</SelectItem>
))}
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="enable_show_in_service"
render={({ field }) => (
<FormItem className="flex items-center space-x-2">
<FormControl>
<div className="flex items-center gap-2">
<Checkbox
checked={field.value}
onCheckedChange={field.onChange}
/>
<Label className="text-sm">
{t("ShowInService")}
</Label>
</div>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="duration"
render={({ field }) => (
<FormItem>
<FormLabel>{t("Interval")} (s)</FormLabel>
<FormControl>
<Input type="number" placeholder="30" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="cover"
render={({ field }) => (
<FormItem>
<FormLabel>{t("Coverage")}</FormLabel>
<Select
onValueChange={field.onChange}
defaultValue={`${field.value}`}
>
<FormControl>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
</FormControl>
<SelectContent>
{Object.entries(serviceCoverageTypes).map(
([k, v]) => (
<SelectItem key={k} value={k}>
{v}
</SelectItem>
),
)}
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="skip_servers_raw"
render={({ field }) => (
<FormItem>
<FormLabel>{t("SpecificServers")}</FormLabel>
<FormControl>
<MultiSelect
options={serverList}
onValueChange={field.onChange}
defaultValue={field.value}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="notification_group_id"
render={({ field }) => (
<FormItem>
<FormLabel>{t("NotifierGroupID")}</FormLabel>
<FormControl>
<Combobox
placeholder="Search..."
options={ngroupList}
onValueChange={field.onChange}
defaultValue={field.value.toString()}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="notify"
render={({ field }) => (
<FormItem className="flex items-center space-x-2">
<FormControl>
<div className="flex items-center gap-2">
<Checkbox
checked={field.value}
onCheckedChange={field.onChange}
/>
<Label className="text-sm">
{t("EnableFailureNotification")}
</Label>
</div>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="max_latency"
render={({ field }) => (
<FormItem>
<FormLabel>{t("MaximumLatency")}</FormLabel>
<FormControl>
<Input
type="number"
placeholder="100.88"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="min_latency"
render={({ field }) => (
<FormItem>
<FormLabel>{t("MinimumLatency")}</FormLabel>
<FormControl>
<Input
type="number"
placeholder="100.88"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="latency_notify"
render={({ field }) => (
<FormItem className="flex items-center space-x-2">
<FormControl>
<div className="flex items-center gap-2">
<Checkbox
checked={field.value}
onCheckedChange={field.onChange}
/>
<Label className="text-sm">
{t("EnableLatencyNotification")}
</Label>
</div>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="enable_trigger_task"
render={({ field }) => (
<FormItem className="flex items-center space-x-2">
<FormControl>
<div className="flex items-center gap-2">
<Checkbox
checked={field.value}
onCheckedChange={field.onChange}
/>
<Label className="text-sm">
{t("EnableTriggerTask")}
</Label>
</div>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="fail_trigger_tasks_raw"
render={({ field }) => (
<FormItem>
<FormLabel>
{t("TasksToTriggerOnAlert") +
t("SeparateWithComma")}
</FormLabel>
<FormControl>
<Input placeholder="1,2,3" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="recover_trigger_tasks_raw"
render={({ field }) => (
<FormItem>
<FormLabel>
{t("TasksToTriggerAfterRecovery") +
t("SeparateWithComma")}
</FormLabel>
<FormControl>
<Input placeholder="1,2,3" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<DialogFooter className="justify-end">
<DialogClose asChild>
<Button type="button" className="my-2" variant="secondary">
{t("Close")}
</Button>
</DialogClose>
<Button type="submit" className="my-2">
{t("Submit")}
</Button>
</DialogFooter>
</form>
</Form>
</div>
</ScrollArea>
</DialogContent>
</Dialog>
)
}