1import { type FC, useEffect } from "react";
2import {
3 DialogTitle,
4 DialogContent,
5 Dialog,
6 DialogHeader,
7 Form,
8 Button,
9 ScrollArea,
10} from "@marzneshin/common/components";
11import { useForm } from "react-hook-form";
12import { zodResolver } from "@hookform/resolvers/zod";
13import { useTranslation } from "react-i18next";
14import {
15 useHostsCreationMutation,
16 useHostsUpdateMutation,
17 type HostWithProfileType,
18 type HostWithProfileSchemaType
19} from "@marzneshin/modules/hosts";
20import { useDialog, type MutationDialogProps } from "@marzneshin/common/hooks";
21import { ProtocolType } from "@marzneshin/modules/inbounds";
22import { useProfileStrategy } from "./profiles";
23
24interface HostMutationDialogProps extends MutationDialogProps<HostWithProfileType> {
25 inboundId?: number;
26 protocol?: ProtocolType;
27}
28
29const transformFormValue = (values: any) => {
30 if (!(values.alpn && values.fingerprint))
31 return values;
32 const port = values.port === "" ? null : values.port;
33 const alpn = values.alpn === "none" ? "" : values.alpn;
34 const fingerprint = values.fingerprint === "none" ? "" : values.fingerprint;
35 return { ...values, alpn, fingerprint, port };
36};
37
38export const HostsMutationDialog: FC<HostMutationDialogProps> = ({
39 entity,
40 inboundId,
41 onClose,
42 protocol,
43}) => {
44 const [open, onOpenChange] = useDialog(true);
45 const [Schema, ProfileFields, defaultValue] = useProfileStrategy(protocol)
46 const form = useForm<HostWithProfileSchemaType>({
47 defaultValues: entity ? entity : defaultValue,
48 resolver: zodResolver(Schema),
49 });
50 const updateMutation = useHostsUpdateMutation();
51 const createMutation = useHostsCreationMutation();
52 const { t } = useTranslation();
53
54 const submit = (values: HostWithProfileSchemaType) => {
55 const host = transformFormValue(values);
56 if (entity && entity.id !== undefined) {
57 updateMutation.mutate({ hostId: entity.id, host });
58 onOpenChange(false);
59 } else if (inboundId !== undefined) {
60 createMutation.mutate({ inboundId, host });
61 onOpenChange(false);
62 }
63 };
64
65 useEffect(() => {
66 if (!open) onClose();
67 }, [open, onClose]);
68
69 useEffect(() => {
70 if (entity) form.reset(entity);
71 else form.reset(defaultValue);
72 }, [entity, form, open, defaultValue]);
73
74 return (
75 <Dialog open={open} onOpenChange={onOpenChange} defaultOpen={true}>
76 <DialogContent className="min-w-full h-full md:h-auto md:min-w-[32rem]">
77 <ScrollArea className="flex flex-col h-full p-0">
78 <DialogHeader className="mb-3">
79 <DialogTitle className="text-primary">
80 {entity
81 ? t("page.hosts.dialogs.edition.title")
82 : t("page.hosts.dialogs.creation.title")}
83 </DialogTitle>
84 </DialogHeader>
85 <Form {...form}>
86 <form
87 onSubmit={form.handleSubmit(submit)}
88 className="h-full flex flex-col justify-between"
89 >
90 <ProfileFields />
91 <Button
92 className="mt-3 w-full font-semibold"
93 type="submit"
94 disabled={form.formState.isSubmitting}
95 >
96 {t("submit")}
97 </Button>
98 </form>
99 </Form>
100 </ScrollArea>
101 </DialogContent>
102 </Dialog>
103 );
104};