gitscope
← marzneshin / v0.5.0 / dashboard/src/features/hosts/dialogs/mutation/index.tsx
https://dev.kronoz.win/r/marzneshin.git
dashboard/src/features/hosts/dialogs/mutation/index.tsx · TypeScript
Blame Raw
  1import { type FC, useEffect } from "react";
  2import {
  3    DialogTitle,
  4    DialogContent,
  5    Dialog,
  6    DialogHeader,
  7    Form,
  8    Button,
  9    Accordion,
 10    AccordionContent,
 11    AccordionItem,
 12    AccordionTrigger,
 13    ScrollArea,
 14    HStack,
 15} from "@marzneshin/components";
 16import { useForm } from "react-hook-form";
 17import { zodResolver } from "@hookform/resolvers/zod";
 18import { useTranslation } from "react-i18next";
 19import {
 20    HostSchema,
 21    getDefaultValues,
 22    useHostsCreationMutation,
 23    useHostsUpdateMutation,
 24    type HostType,
 25} from "@marzneshin/features/hosts";
 26import {
 27    RemarkField,
 28    AddressField,
 29    PortField,
 30    HostField,
 31    MuxField,
 32    FragmentField,
 33    PathField,
 34    WeightField,
 35    SecurityFields,
 36} from "./fields";
 37import { useDialog, MutationDialogProps } from "@marzneshin/hooks";
 38
 39interface HostMutationDialogProps extends MutationDialogProps<HostType> {
 40    inboundId?: number;
 41}
 42
 43const transformFormValue = (values: HostType) => {
 44    const port = values.port === "" ? null : values.port;
 45    const alpn = values.alpn === "none" ? "" : values.alpn;
 46    const fingerprint = values.fingerprint === "none" ? "" : values.fingerprint;
 47    return { ...values, alpn, fingerprint, port };
 48};
 49
 50export const HostsMutationDialog: FC<HostMutationDialogProps> = ({
 51    inboundId,
 52    entity,
 53    onClose,
 54}) => {
 55    const [open, onOpenChange] = useDialog(true);
 56    const form = useForm({
 57        defaultValues: entity ? entity : getDefaultValues(),
 58        resolver: zodResolver(HostSchema),
 59    });
 60    const updateMutation = useHostsUpdateMutation();
 61    const createMutation = useHostsCreationMutation();
 62    const { t } = useTranslation();
 63
 64    const submit = (values: HostType) => {
 65        const host = transformFormValue(values);
 66        if (entity && entity.id !== undefined) {
 67            updateMutation.mutate({ hostId: entity.id, host });
 68            onOpenChange(false);
 69        } else if (inboundId !== undefined) {
 70            createMutation.mutate({ inboundId, host });
 71            onOpenChange(false);
 72        }
 73    };
 74
 75    useEffect(() => {
 76        if (!open) onClose();
 77    }, [open, onClose]);
 78
 79    useEffect(() => {
 80        if (entity) form.reset(entity);
 81        else form.reset(getDefaultValues());
 82    }, [entity, form, open]);
 83
 84    return (
 85        <Dialog open={open} onOpenChange={onOpenChange} defaultOpen={true}>
 86            <DialogContent className="min-w-full h-full md:h-auto md:min-w-[32rem]">
 87                <ScrollArea className="flex flex-col h-full p-0">
 88                    <DialogHeader className="mb-3">
 89                        <DialogTitle className="text-primary">
 90                            {entity
 91                                ? t("page.hosts.dialogs.edition.title")
 92                                : t("page.hosts.dialogs.creation.title")}
 93                        </DialogTitle>
 94                    </DialogHeader>
 95                    <Form {...form}>
 96                        <form
 97                            onSubmit={form.handleSubmit(submit)}
 98                            className="h-full flex flex-col justify-between"
 99                        >
100                            <RemarkField />
101                            <HStack className="gap-2 items-start">
102                                <AddressField />
103                                <PortField />
104                            </HStack>
105                            <Accordion type="single" collapsible>
106                                <AccordionItem value="item-1">
107                                    <AccordionTrigger>{t("advanced-options")}</AccordionTrigger>
108                                    <AccordionContent>
109                                        <div className="flex flex-row gap-2 items-start">
110                                            <HostField />
111                                            <PathField />
112                                            <WeightField />
113                                        </div>
114                                        <FragmentField />
115                                        <MuxField />
116                                        <SecurityFields />
117                                    </AccordionContent>
118                                </AccordionItem>
119                            </Accordion>
120                            <Button
121                                className="mt-3 w-full font-semibold"
122                                type="submit"
123                                disabled={form.formState.isSubmitting}
124                            >
125                                {t("submit")}
126                            </Button>
127                        </form>
128                    </Form>
129                </ScrollArea>
130            </DialogContent>
131        </Dialog>
132    );
133};