https://dev.kronoz.win/r/marzneshin.git
1import {
2 FormControl,
3 FormField,
4 FormItem,
5 FormLabel,
6 Input,
7 Button,
8 Popover,
9 PopoverContent,
10 PopoverTrigger,
11} from "@marzneshin/components";
12import type { FieldErrors, FieldError } from "react-hook-form";
13import { useWatch, useFormContext } from "react-hook-form";
14import { useTranslation } from "react-i18next";
15import { MailWarning, TrashIcon } from "lucide-react";
16import type { FC } from "react";
17
18interface FragmentForm {
19 fragment: {
20 packets: FieldError;
21 length: FieldError;
22 interval: FieldError;
23 };
24}
25
26const FragmentErrorPopover: FC = () => {
27 const form = useFormContext();
28 const { t } = useTranslation();
29 const errors = form.formState.errors as FieldErrors<FragmentForm>;
30 return (
31 <Popover>
32 <PopoverTrigger>
33 <MailWarning className="p-0 size-5 text-destructive bg-primary-background" />
34 </PopoverTrigger>
35 <PopoverContent className="bg-destructive-accent text-sm">
36 <ul className="my-6 ml-3 list-disc [&>li]:mt-1 mt-0">
37 {errors.fragment?.length?.message && (
38 <li>
39 <b>Length:</b> {t(errors.fragment.length.message as string)}
40 </li>
41 )}
42 {errors.fragment?.packets?.message && (
43 <li>
44 <b>Packets:</b> {t(errors.fragment.packets.message as string)}
45 </li>
46 )}
47 {errors.fragment?.interval?.message && (
48 <li>
49 <b>Interval:</b> {t(errors.fragment.interval.message as string)}
50 </li>
51 )}
52 </ul>
53 </PopoverContent>
54 </Popover>
55 );
56};
57
58export const FragmentField = () => {
59 const { t } = useTranslation();
60 const form = useFormContext();
61 const errors = form.formState.errors as FieldErrors<FragmentForm>;
62 const fragment = useWatch({ name: "fragment" });
63 const disabled = fragment === null || fragment === undefined;
64
65 const enable = () => {
66 form.setValue(
67 "fragment",
68 { length: "", packets: "", interval: "" },
69 {
70 shouldTouch: true,
71 shouldDirty: true,
72 },
73 );
74 };
75
76 const disable = () => {
77 form.setValue("fragment.length", "");
78 form.setValue("fragment.packets", "");
79 form.setValue("fragment.interval", "");
80 form.setValue("fragment", null, {
81 shouldValidate: true,
82 shouldTouch: false,
83 shouldDirty: false,
84 });
85 };
86
87 const hasFragmentErrors = (errors: FieldErrors<FragmentForm>) =>
88 errors.fragment?.packets ||
89 errors.fragment?.length ||
90 errors.fragment?.interval;
91
92 return (
93 <FormField
94 control={form.control}
95 name="fragment"
96 render={() => (
97 <FormItem className="my-2 w-full">
98 <FormLabel className="flex flex-row justify-between items-center">
99 {t("fragment")}
100 <div className="flex flex-row items-center gap-2">
101 {hasFragmentErrors(errors) && <FragmentErrorPopover />}
102 {!disabled && (
103 <Button
104 variant="destructive"
105 className="p-0 size-5 bg-primary-background"
106 onClick={(e) => {
107 e.preventDefault();
108 disable();
109 }}
110 >
111 <TrashIcon className="text-red-400" />
112 </Button>
113 )}
114 </div>
115 </FormLabel>
116
117 <FormControl>
118 <div className="flex flex-row w-full items-center">
119 {!disabled ? (
120 <>
121 <FormField
122 name="fragment.packets"
123 render={({ field }) => (
124 <div className="flex flex-col">
125 <FormItem>
126 <FormLabel className="font-medium">
127 {t("page.hosts.fragment.packets")}
128 </FormLabel>
129 <FormControl>
130 <Input
131 className="border rounded-none rounded-s-lg p-2 w-full"
132 {...field}
133 />
134 </FormControl>
135 </FormItem>
136 </div>
137 )}
138 />
139 <FormField
140 name="fragment.length"
141 render={({ field }) => (
142 <div className="flex flex-col">
143 <FormItem>
144 <FormLabel className="font-medium">
145 {t("page.hosts.fragment.length")}
146 </FormLabel>
147 <FormControl>
148 <Input
149 className="rounded-none w-full p-2"
150 {...field}
151 />
152 </FormControl>
153 </FormItem>
154 </div>
155 )}
156 />
157 <FormField
158 name="fragment.interval"
159 render={({ field }) => (
160 <div className="flex flex-col">
161 <FormItem>
162 <FormLabel className="font-medium">
163 {t("page.hosts.fragment.interval")}
164 </FormLabel>
165 <FormControl>
166 <Input
167 className="border w-full rounded-none rounded-e-lg p-2"
168 {...field}
169 />
170 </FormControl>
171 </FormItem>
172 </div>
173 )}
174 />
175 </>
176 ) : (
177 <Button
178 className="border w-full"
179 variant="ghost"
180 onClick={(e) => {
181 e.preventDefault();
182 enable();
183 }}
184 >
185 {t("disabled")}
186 </Button>
187 )}
188 </div>
189 </FormControl>
190 </FormItem>
191 )}
192 />
193 );
194};