gitscope
← marzneshin / v0.3.0-alpha1 / dashboard/src/features/services/dialogs/mutation.dialog.tsx
https://dev.kronoz.win/r/marzneshin.git
dashboard/src/features/services/dialogs/mutation.dialog.tsx · TypeScript
Blame Raw
  1
  2
  3import {
  4    DialogTitle,
  5    DialogContent,
  6    Dialog,
  7    DialogHeader,
  8    Form,
  9    FormItem,
 10    FormControl,
 11    FormMessage,
 12    FormLabel,
 13    Input,
 14    FormField,
 15    Button,
 16    Checkbox,
 17    Badge
 18} from '@marzneshin/components'
 19import { FC } from 'react'
 20import {
 21    ServiceType,
 22    useServicesCreationMutation,
 23    useServicesUpdateMutation,
 24} from '@marzneshin/features/services'
 25import { useInboundsQuery } from '@marzneshin/features/inbounds'
 26import { useTranslation } from 'react-i18next'
 27import { z } from 'zod'
 28import { cn } from '@marzneshin/utils'
 29import { Box, GlobeLock } from 'lucide-react'
 30import { useMutationDialog } from '@marzneshin/hooks'
 31
 32export const ServiceSchema = z.object({
 33    id: z.number().optional(),
 34    inbounds: z.array(z.number()).refine((value) => value.some((item) => item), {
 35        message: "You have to select at least one inbound.",
 36    }),
 37    name: z.string().trim().min(1)
 38})
 39type ServiceCreateType = z.infer<typeof ServiceSchema>
 40
 41interface MutationDialogProps {
 42    entity: ServiceType | null
 43    open: boolean
 44    onOpenChange: (state: boolean) => void
 45}
 46
 47const getDefaultValues = (): ServiceCreateType => ({
 48    name: '',
 49    inbounds: [],
 50})
 51
 52export const MutationDialog: FC<MutationDialogProps> = ({ entity, open, onOpenChange }) => {
 53    const isEditing = entity !== null
 54    const updateMutation = useServicesUpdateMutation();
 55    const createMutation = useServicesCreationMutation();
 56    const { data: inbounds } = useInboundsQuery()
 57    const { form, handleSubmit } = useMutationDialog({
 58        entity,
 59        updateMutation,
 60        createMutation,
 61        getDefaultValue: getDefaultValues,
 62        schema: ServiceSchema,
 63        onOpenChange,
 64    })
 65    const { t } = useTranslation()
 66    return (
 67        <Dialog open={open} onOpenChange={onOpenChange} defaultOpen={true}>
 68            <DialogContent>
 69                <DialogHeader>
 70                    <DialogTitle className="text-primary">
 71                        {isEditing ?
 72                            t('page.services.dialogs.edition.title') :
 73                            t('page.services.dialogs.creation.title')}
 74                    </DialogTitle>
 75                </DialogHeader>
 76                <Form {...form} >
 77                    <form onSubmit={handleSubmit} >
 78                        <FormField
 79                            control={form.control}
 80                            name="name"
 81                            render={({ field }) => (
 82                                <FormItem>
 83                                    <FormLabel>{t('name')}</FormLabel>
 84                                    <FormControl>
 85                                        <Input {...field} />
 86                                    </FormControl>
 87                                    <FormMessage />
 88                                </FormItem>
 89                            )}
 90                        />
 91                        <FormField
 92                            control={form.control}
 93                            name="inbounds"
 94                            render={() => (
 95                                <FormItem>
 96                                    <FormLabel>{t('inbounds')}</FormLabel>
 97                                    {inbounds.map((inbound) => (
 98                                        <FormField
 99                                            key={inbound.id}
100                                            control={form.control}
101                                            name="inbounds"
102                                            render={({ field }) => (
103                                                <FormItem key={inbound.id} >
104                                                    <FormControl>
105                                                        <div
106                                                            className={cn("flex flex-row items-center p-3 space-y-0 space-x-3 rounded-md border", { "bg-secondary": field.value?.includes(inbound.id) })}>
107                                                            <Checkbox
108                                                                checked={field.value?.includes(inbound.id)}
109                                                                onCheckedChange={(checked) => {
110                                                                    return checked
111                                                                        ? field.onChange([...field.value, inbound.id])
112                                                                        : field.onChange(
113                                                                            field.value?.filter(
114                                                                                (value: number) => value !== inbound.id
115                                                                            )
116                                                                        )
117                                                                }}
118                                                            />
119                                                            <FormLabel className="flex flex-row justify-between items-center w-full">
120                                                                {inbound.tag}
121                                                                <div className="flex gap-2">
122                                                                    <Badge className="py-1 px-2"> <GlobeLock className="mr-1 w-4" /> {inbound.protocol} </Badge>
123                                                                    <Badge className="py-1 px-2"> <Box className="mr-1 w-4" /> {inbound.node.name} </Badge>
124                                                                </div>
125                                                            </FormLabel>
126                                                        </div>
127                                                    </FormControl>
128                                                </FormItem>
129                                            )}
130                                        />
131                                    ))}
132                                    <FormMessage />
133                                </FormItem>
134                            )}
135                        />
136                        <Button
137                            className="mt-3 w-full font-semibold"
138                            type="submit"
139                            disabled={form.formState.isSubmitting}>
140                            {t('submit')}
141                        </Button>
142                    </form>
143                </Form>
144            </DialogContent>
145        </Dialog >
146    )
147}