Build, Test, and Push CFDM Docker Image / test (push) Successful in 4m5s
Build, Test, and Push CFDM Docker Image / build-and-push (push) Successful in 2m26s
Build, Test, and Push CFDM Docker Image / update-wiki (push) Successful in 8s
Build, Test, and Push CFDM Docker Image / create-release (push) Has been skipped
156 lines
4.6 KiB
TypeScript
156 lines
4.6 KiB
TypeScript
import { useEffect } from 'react'
|
|
import { useForm, Controller } from 'react-hook-form'
|
|
import { zodResolver } from '@hookform/resolvers/zod'
|
|
import {
|
|
createServiceGroupSchema,
|
|
type CreateServiceGroupInput,
|
|
type ServiceGroup,
|
|
} from '@/lib/schemas'
|
|
import type { z } from 'zod'
|
|
import { FormSheet } from '@/components/form-sheet'
|
|
import { FormFieldSimple } from '@/components/form-field'
|
|
import { LoadingButton } from '@/components/loading-button'
|
|
import { FieldGroup } from '@cfdm/ui/components/field'
|
|
import { Input } from '@cfdm/ui/components/input'
|
|
import {
|
|
Select,
|
|
SelectContent,
|
|
SelectItem,
|
|
SelectTrigger,
|
|
SelectValue,
|
|
} from '@cfdm/ui/components/select'
|
|
|
|
const groupTypes = [
|
|
{ value: 'vpn', label: 'VPN' },
|
|
{ value: 'network', label: 'Сеть' },
|
|
{ value: 'internet', label: 'Интернет' },
|
|
{ value: 'bgp', label: 'BGP' },
|
|
{ value: 'custom', label: 'Другое' },
|
|
] as const
|
|
|
|
type ServiceGroupFormValues = z.input<typeof createServiceGroupSchema>
|
|
|
|
interface ServiceGroupEditSheetProps {
|
|
mode: 'create' | 'edit'
|
|
group: ServiceGroup | null
|
|
open: boolean
|
|
isSaving: boolean
|
|
onOpenChange: (open: boolean) => void
|
|
onCreate?: (body: CreateServiceGroupInput) => void
|
|
onSave?: (id: number, body: CreateServiceGroupInput) => void
|
|
}
|
|
|
|
export function ServiceGroupEditSheet({
|
|
mode,
|
|
group,
|
|
open,
|
|
isSaving,
|
|
onOpenChange,
|
|
onCreate,
|
|
onSave,
|
|
}: ServiceGroupEditSheetProps) {
|
|
const form = useForm<ServiceGroupFormValues>({
|
|
resolver: zodResolver(createServiceGroupSchema),
|
|
defaultValues: { name: '', type: 'custom', domain: null },
|
|
})
|
|
|
|
useEffect(() => {
|
|
if (!open) return
|
|
if (mode === 'edit' && group) {
|
|
form.reset({
|
|
name: group.name,
|
|
type: group.type,
|
|
domain: group.domain ?? null,
|
|
})
|
|
} else {
|
|
form.reset({ name: '', type: 'custom', domain: null })
|
|
}
|
|
}, [open, mode, group, form])
|
|
|
|
function handleSubmit(values: ServiceGroupFormValues) {
|
|
const body: CreateServiceGroupInput = {
|
|
name: values.name,
|
|
type: values.type ?? 'custom',
|
|
icon: values.icon,
|
|
domain: values.domain?.trim() || null,
|
|
}
|
|
if (mode === 'create') {
|
|
onCreate?.(body)
|
|
} else if (group) {
|
|
onSave?.(group.id, body)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<FormSheet<ServiceGroupFormValues>
|
|
open={open}
|
|
onOpenChange={onOpenChange}
|
|
title={mode === 'create' ? 'Новая группа сервисов' : 'Редактировать группу'}
|
|
description="Домен группы необязателен. Если указан FQDN (gr.ivx.su, domain.new.ivx.su), он публикуется в Cloudflare отдельно от привязок сервисов."
|
|
form={form}
|
|
onSubmit={handleSubmit}
|
|
contentClassName="gap-6"
|
|
footer={
|
|
<LoadingButton
|
|
type="submit"
|
|
className="w-full"
|
|
isLoading={isSaving}
|
|
loadingLabel="Сохранение…"
|
|
>
|
|
{mode === 'create' ? 'Создать' : 'Сохранить'}
|
|
</LoadingButton>
|
|
}
|
|
>
|
|
<FieldGroup>
|
|
<FormFieldSimple
|
|
label="Название"
|
|
htmlFor="group-name"
|
|
error={form.formState.errors.name}
|
|
>
|
|
<Input
|
|
id="group-name"
|
|
placeholder="VPN"
|
|
{...form.register('name')}
|
|
aria-invalid={!!form.formState.errors.name}
|
|
/>
|
|
</FormFieldSimple>
|
|
<FormFieldSimple label="Тип" htmlFor="group-type">
|
|
<Controller
|
|
control={form.control}
|
|
name="type"
|
|
render={({ field }) => (
|
|
<Select
|
|
value={field.value}
|
|
onValueChange={(value) =>
|
|
field.onChange(value as CreateServiceGroupInput['type'])
|
|
}
|
|
>
|
|
<SelectTrigger id="group-type">
|
|
<SelectValue placeholder="Выберите тип" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
{groupTypes.map((item) => (
|
|
<SelectItem key={item.value} value={item.value}>
|
|
{item.label}
|
|
</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
)}
|
|
/>
|
|
</FormFieldSimple>
|
|
<FormFieldSimple
|
|
label="Домен группы (FQDN, необязательно)"
|
|
htmlFor="group-domain"
|
|
>
|
|
<Input
|
|
id="group-domain"
|
|
placeholder="domain.new.ivx.su"
|
|
{...form.register('domain')}
|
|
/>
|
|
</FormFieldSimple>
|
|
</FieldGroup>
|
|
</FormSheet>
|
|
)
|
|
}
|