gitscope
← marzneshin / v0.6.5 / dashboard/src/modules/hosts/dialogs/mutation/fields/fragment.tsx
https://dev.kronoz.win/r/marzneshin.git
dashboard/src/modules/hosts/dialogs/mutation/fields/fragment.tsx · TypeScript
Blame Raw
  1import {
  2    FormControl,
  3    FormField,
  4    FormItem,
  5    FormLabel,
  6    Input,
  7    Button,
  8    Popover,
  9    PopoverContent,
 10    PopoverTrigger,
 11} from "@marzneshin/common/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};