import { zodResolver } from "@hookform/resolvers/zod"; import { Button } from "@workspace/ui/components/button"; import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, } from "@workspace/ui/components/form"; import { ScrollArea } from "@workspace/ui/components/scroll-area"; import { Sheet, SheetContent, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, } from "@workspace/ui/components/sheet"; import { Combobox } from "@workspace/ui/composed/combobox"; import { DatePicker } from "@workspace/ui/composed/date-picker"; import { ArrayInput } from "@workspace/ui/composed/dynamic-inputs"; import { EnhancedInput } from "@workspace/ui/composed/enhanced-input"; import { Icon } from "@workspace/ui/composed/icon"; import { unitConversion } from "@workspace/ui/utils/unit-conversions"; import { type KeyboardEvent, type ReactNode, useState } from "react"; import { useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { z } from "zod"; import { useSubscribe } from "@/stores/subscribe"; interface Props { trigger: ReactNode; title: string; loading?: boolean; initialData?: API.UserSubscribe; onSubmit: (values: SubscriptionFormValues) => Promise; } type SubscriptionTrafficLimitRule = API.UserSubscribeTrafficLimitRule; const defaultLimitRule: SubscriptionTrafficLimitRule = { stat_type: "day", stat_value: 1, traffic_usage: 0, speed_limit: 0, }; const formSchema = z.object({ subscribe_id: z.number().optional(), traffic: z.number().min(0).optional(), speed_limit: z.number().min(0).optional(), device_limit: z.number().min(0).optional(), expired_at: z.number().nullish().optional(), upload: z.number().min(0).optional(), download: z.number().min(0).optional(), id: z.number().optional(), traffic_limit: z .array( z.object({ stat_type: z.string(), stat_value: z.number().int().min(1), traffic_usage: z.number().min(0), speed_limit: z.number().min(0), }) ) .optional(), }); type SubscriptionFormValues = z.infer; export type { SubscriptionFormValues }; const normalizeLimitRule = ( item: SubscriptionTrafficLimitRule ): SubscriptionTrafficLimitRule => ({ stat_type: item.stat_type || "day", stat_value: Math.max(1, Math.floor(Number(item.stat_value) || 1)), traffic_usage: Math.max(0, Number(item.traffic_usage) || 0), speed_limit: Math.max(0, Number(item.speed_limit) || 0), }); export function SubscriptionForm({ trigger, title, loading, initialData, onSubmit, }: Props) { const { t } = useTranslation("user"); const [open, setOpen] = useState(false); const form = useForm({ resolver: zodResolver(formSchema), defaultValues: { subscribe_id: initialData?.subscribe_id || 0, traffic: initialData?.traffic || 0, speed_limit: initialData?.speed_limit || 0, device_limit: initialData?.device_limit || 0, traffic_limit: initialData?.traffic_limit || [], upload: initialData?.upload || 0, download: initialData?.download || 0, expired_at: initialData?.expire_time || 0, ...(initialData && { id: initialData.id }), }, }); const handleSubmit = async (values: SubscriptionFormValues) => { const success = await onSubmit(values); if (success) { setOpen(false); form.reset(); } }; const { subscribes } = useSubscribe(); return ( {title}
( {t("subscription", "Subscription")} onChange={(value) => { form.setValue(field.name, value); }} options={subscribes?.map((item) => ({ value: item.id!, label: item.name!, }))} placeholder="Select Subscription" value={field.value} /> )} /> ( {t("trafficLimit", "Traffic Limit")} unitConversion("bytesToGb", value) } formatOutput={(value) => unitConversion("gbToBytes", value) } onValueChange={(value) => { form.setValue(field.name, value as number); }} suffix="GB" /> )} /> ( {t("speedLimit", "Speed Limit")} { form.setValue(field.name, value as number); }} suffix="Mbps" /> {t( "userSpeedLimitDescription", "Set to 0 to use the subscription default speed limit." )} )} /> ( {t("deviceLimit", "Device Limit")} { form.setValue(field.name, value as number); }} /> )} /> ( {t("uploadTraffic", "Upload Traffic")} unitConversion("bytesToGb", value) } formatOutput={(value) => unitConversion("gbToBytes", value) } onValueChange={(value) => { form.setValue(field.name, value as number); }} suffix="GB" /> )} /> ( {t("downloadTraffic", "Download Traffic")} unitConversion("bytesToGb", value) } formatOutput={(value) => unitConversion("gbToBytes", value) } onValueChange={(value) => { form.setValue(field.name, value as number); }} suffix="GB" /> )} /> ( {t("trafficLimitRules", "Traffic Limit Rules")} className="grid grid-cols-2 gap-3 lg:grid-cols-4" fields={[ { name: "stat_type", type: "select", placeholder: t("statType", "Statistics Type"), value: "day", options: [ { label: t("statTypeHour", "Hour"), value: "hour", }, { label: t("statTypeDay", "Day"), value: "day", }, ], }, { name: "stat_value", type: "number", placeholder: t("statValue", "Time Value"), min: 1, step: 1, onKeyDown: ( e: KeyboardEvent ) => { if (e.key === "." || e.key === ",") { e.preventDefault(); } }, formatOutput: (value: string | number) => { const num = Number(value); return String( Number.isNaN(num) ? 1 : Math.floor(num) ); }, }, { name: "traffic_usage", type: "number", placeholder: t( "trafficUsage", "Traffic Usage (GB)" ), min: 0, onKeyDown: ( e: KeyboardEvent ) => { if (e.key === "." || e.key === ",") { e.preventDefault(); } }, formatOutput: (value: string | number) => { const num = Number(value); return String( Number.isNaN(num) ? 0 : Math.floor(num) ); }, }, { name: "speed_limit", type: "number", placeholder: t( "speedLimitMbps", "Speed Limit (Mbps)" ), min: 0, onKeyDown: ( e: KeyboardEvent ) => { if (e.key === "." || e.key === ",") { e.preventDefault(); } }, formatOutput: (value: string | number) => { const num = Number(value); return String( Number.isNaN(num) ? 0 : Math.floor(num) ); }, }, ]} onChange={(items) => { field.onChange(items.map(normalizeLimitRule)); }} value={ field.value && field.value.length > 0 ? field.value : [defaultLimitRule] } /> {t( "trafficLimitDescription", "Limit speed when usage reaches the configured amount in the selected time window." )} )} /> ( {t("expiredAt", "Expired At")} { form.setValue(field.name, value || 0); }} placeholder={t("permanent", "Permanent")} value={ field.value && field.value > 0 ? field.value : undefined } /> )} />
); }