gitscope
← marzneshin / v0.7.3 / dashboard/src/modules/hosts/dialogs/mutation/fields/noise.tsx
https://dev.kronoz.win/r/marzneshin.git
dashboard/src/modules/hosts/dialogs/mutation/fields/noise.tsx · TypeScript
Blame Raw
  1import {
  2    Button,
  3    FormControl,
  4    FormField,
  5    FormItem,
  6    FormLabel,
  7    Input,
  8    Popover,
  9    PopoverContent,
 10    PopoverTrigger,
 11    Select,
 12    SelectContent,
 13    SelectItem,
 14    SelectTrigger,
 15    SelectValue,
 16} from "@marzneshin/common/components";
 17import type { FieldError, FieldErrors } from "react-hook-form";
 18import { useFormContext } from "react-hook-form";
 19import { useTranslation } from "react-i18next";
 20import { ListPlus, MailWarning, TrashIcon } from "lucide-react";
 21import type { FC } from "react";
 22import { useState } from "react";
 23import { noiseTypes } from "../profiles";
 24
 25interface NoiseForm {
 26    noise: Array<{
 27        type: string;
 28        packet: string;
 29        delay: string;
 30    } | null>;
 31}
 32
 33const NoiseErrorPopover: FC<{
 34    errors?: { packet?: FieldError; type?: FieldError; delay?: FieldError };
 35}> = ({ errors }) => {
 36    const { t } = useTranslation();
 37
 38    if (!errors || (!errors.packet && !errors.type && !errors.delay))
 39        return null;
 40
 41    return (
 42        <Popover>
 43            <PopoverTrigger>
 44                <MailWarning className="p-0 size-5 text-destructive bg-primary-background" />
 45            </PopoverTrigger>
 46            <PopoverContent className="bg-destructive-accent text-sm">
 47                <ul className="my-6 ml-3 list-disc [&>li]:mt-1 mt-0">
 48                    {errors.type?.message && (
 49                        <li>
 50                            <b>Type:</b> {t(errors.type.message as string)}
 51                        </li>
 52                    )}
 53                    {errors.packet?.message && (
 54                        <li>
 55                            <b>Packet:</b> {t(errors.packet.message as string)}
 56                        </li>
 57                    )}
 58                    {errors.delay?.message && (
 59                        <li>
 60                            <b>Interval:</b> {t(errors.delay.message as string)}
 61                        </li>
 62                    )}
 63                </ul>
 64            </PopoverContent>
 65        </Popover>
 66    );
 67};
 68
 69export const NoiseField = () => {
 70    const { t } = useTranslation();
 71    const form = useFormContext();
 72    const [forms, setForms] = useState<NoiseForm["noise"][number][]>(
 73        form.getValues("noise") || [],
 74    );
 75
 76    const errors = form.formState.errors as FieldErrors<NoiseForm>;
 77
 78    const addForm = () => {
 79        if (forms.length < 5) {
 80            const newForms = [
 81                ...forms,
 82                { type: "rand", packet: "", delay: "" },
 83            ];
 84            setForms(newForms);
 85            form.setValue("noise", newForms, { shouldValidate: true });
 86        }
 87    };
 88
 89    const removeForm = (index: number) => {
 90        const updatedForms = forms.filter((_, i) => i !== index);
 91        setForms(updatedForms);
 92        form.setValue("noise", updatedForms, { shouldValidate: true });
 93    };
 94
 95    const enableForm = (index: number) => {
 96        const updatedForms = [...forms];
 97        updatedForms[index] = { type: "", packet: "", delay: "" };
 98        setForms(updatedForms);
 99        form.setValue("noise", updatedForms, { shouldValidate: true });
100    };
101
102    return (
103        <div>
104            {forms.length > 0 ? (
105                forms.map((formData, index) => (
106                    <FormItem key={index} className="my-2 w-full">
107                        {index === 0 && (
108                            <FormLabel className="flex flex-row justify-between items-center">
109                                {t("Noise")}
110                                <div className="flex flex-row items-center gap-2">
111                                    {errors.noise?.[index] && (
112                                        <NoiseErrorPopover
113                                        // errors={errors.noise[index]}
114                                        />
115                                    )}
116                                    {forms.length < 5 && (
117                                        <Button
118                                            className="p-0 size-5 bg-primary-background"
119                                            variant="secondary"
120                                            onClick={(e) => {
121                                                e.preventDefault();
122                                                addForm();
123                                            }}
124                                        >
125                                            <ListPlus />
126                                        </Button>
127                                    )}
128                                </div>
129                            </FormLabel>
130                        )}
131                        <FormControl>
132                            <div className="flex flex-row w-full items-center gap-2">
133                                {formData ? (
134                                    <>
135                                        <FormField
136                                            name={`noise.${index}.type`}
137                                            render={({ field }) => (
138                                                <Select
139                                                    onValueChange={
140                                                        field.onChange
141                                                    }
142                                                    defaultValue={field.value}
143                                                >
144                                                    <FormControl>
145                                                        <SelectTrigger className="w-2/5">
146                                                            <SelectValue placeholder="Noise Type" />
147                                                        </SelectTrigger>
148                                                    </FormControl>
149                                                    <SelectContent>
150                                                        {noiseTypes.map(
151                                                            (option) => (
152                                                                <SelectItem
153                                                                    value={
154                                                                        option
155                                                                    }
156                                                                >
157                                                                    {option}
158                                                                </SelectItem>
159                                                            ),
160                                                        )}
161                                                    </SelectContent>
162                                                </Select>
163                                            )}
164                                        />
165                                        <div className="w-full hstack">
166                                            <FormField
167                                                name={`noise.${index}.packet`}
168                                                render={({ field }) => (
169                                                    <Input
170                                                        className="border rounded-none rounded-s-lg p-2 w-full"
171                                                        placeholder="Packet"
172                                                        {...field}
173                                                    />
174                                                )}
175                                            />
176                                            <FormField
177                                                name={`noise.${index}.delay`}
178                                                render={({ field }) => (
179                                                    <Input
180                                                        placeholder="Delay"
181                                                        className="border w-full rounded-none rounded-e-lg p-2"
182                                                        {...field}
183                                                    />
184                                                )}
185                                            />
186                                        </div>
187                                        <Button
188                                            variant="destructive"
189                                            className="p-0 size-5 bg-primary-background"
190                                            onClick={(e) => {
191                                                e.preventDefault();
192                                                removeForm(index);
193                                            }}
194                                        >
195                                            <TrashIcon className="text-red-400" />
196                                        </Button>
197                                    </>
198                                ) : (
199                                    <Button
200                                        className="border w-full"
201                                        variant="ghost"
202                                        onClick={(e) => {
203                                            e.preventDefault();
204                                            enableForm(index);
205                                        }}
206                                    >
207                                        {t("Enable Form")}
208                                    </Button>
209                                )}
210                            </div>
211                        </FormControl>
212                    </FormItem>
213                ))
214            ) : (
215                <FormItem className="my-2 w-full">
216                    <FormLabel className="flex flex-row justify-between items-center">
217                        {t("Noise")}
218                    </FormLabel>
219
220                    <Button
221                        className="border w-full"
222                        variant="ghost"
223                        onClick={(e) => {
224                            e.preventDefault();
225                            addForm();
226                        }}
227                    >
228                        {t("disabled")}
229                    </Button>
230                </FormItem>
231            )}
232        </div>
233    );
234};