import { getNodeGroupList, getNodeList } from '@/services/admin/server'; import { getSubscribeGroupList } from '@/services/admin/subscribe'; import { zodResolver } from '@hookform/resolvers/zod'; import { Icon } from '@iconify/react'; import { useQuery } from '@tanstack/react-query'; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from '@workspace/ui/components/accordion'; import { Button } from '@workspace/ui/components/button'; import { Checkbox } from '@workspace/ui/components/checkbox'; import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, } from '@workspace/ui/components/form'; import { Label } from '@workspace/ui/components/label'; 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/custom-components/combobox'; import { ArrayInput } from '@workspace/ui/custom-components/dynamic-Inputs'; import { JSONEditor } from '@workspace/ui/custom-components/editor'; import { EnhancedInput } from '@workspace/ui/custom-components/enhanced-input'; import { evaluateWithPrecision, unitConversion } from '@workspace/ui/utils'; import { useTranslations } from 'next-intl'; import { assign, shake } from 'radash'; import { useEffect, useState } from 'react'; import { useForm } from 'react-hook-form'; import { z } from 'zod'; interface SubscribeFormProps { onSubmit: (data: T) => Promise | boolean; initialValues?: T; loading?: boolean; trigger: string; title: string; } const defaultValues = { inventory: -1, speed_limit: 0, device_limit: 0, traffic: 0, quota: 0, discount: [], server_group: [], server: [], unit_time: 'Month', }; export default function SubscribeForm>({ onSubmit, initialValues, loading, trigger, title, }: SubscribeFormProps) { const t = useTranslations('subscribe'); const [open, setOpen] = useState(false); const formSchema = z.object({ name: z.string(), description: z.string().optional(), unit_price: z.number(), unit_time: z.string().default('Month'), replacement: z.number().optional(), discount: z .array( z.object({ quantity: z.number(), discount: z.number(), }), ) .optional(), inventory: z.number().optional(), speed_limit: z.number().optional(), device_limit: z.number().optional(), traffic: z.number().optional(), quota: z.number().optional(), group_id: z.number().optional().nullish(), server_group: z.array(z.number()).optional(), server: z.array(z.number()).optional(), }); const form = useForm({ resolver: zodResolver(formSchema), defaultValues: assign( defaultValues, shake(initialValues, (value) => value === null) as Record, ), }); useEffect(() => { form?.reset( assign(defaultValues, shake(initialValues, (value) => value === null) as Record), ); }, [form, initialValues]); async function handleSubmit(data: { [x: string]: any }) { const bool = await onSubmit(data as T); if (bool) setOpen(false); } const { data: group } = useQuery({ queryKey: ['getSubscribeGroupList'], queryFn: async () => { const { data } = await getSubscribeGroupList(); return data.data?.list as API.SubscribeGroup[]; }, }); const { data: server } = useQuery({ queryKey: ['getNodeList', 'all'], queryFn: async () => { const { data } = await getNodeList({ page: 1, size: 9999, }); return data.data?.list; }, }); const { data: server_groups } = useQuery({ queryKey: ['getNodeGroupList'], queryFn: async () => { const { data } = await getNodeGroupList(); return (data.data?.list || []) as API.ServerGroup[]; }, }); const unit_time = form.watch('unit_time'); return ( {title}
( {t('form.name')} { form.setValue(field.name, value); }} /> )} /> ( { form.setValue(field.name, JSON.stringify(value)); }} placeholder={{ description: 'description', features: [ { type: 'default', icon: '', label: 'label', }, ], }} schema={{ type: 'object', properties: { description: { type: 'string', description: 'A brief description of the item.', }, features: { type: 'array', items: { type: 'object', properties: { icon: { type: 'string', description: "Enter an Iconify icon identifier (e.g., 'mdi:account').", pattern: '^[a-z0-9]+:[a-z0-9-]+$', examples: [ 'uil:shield-check', 'uil:shield-exclamation', 'uil:database', 'uil:server', 'Visit https://icon-sets.iconify.design to browse available icons.', ], }, label: { type: 'string', description: 'The label describing the feature.', }, type: { type: 'string', enum: ['default', 'success', 'destructive'], description: 'The type of feature, limited to specific values.', }, }, }, description: 'A list of feature objects.', }, }, required: ['description', 'features'], additionalProperties: false, }} /> )} />
( {t('form.unitPrice')} unitConversion('centsToDollars', value)} formatOutput={(value) => unitConversion('dollarsToCents', value)} onValueChange={(value) => { form.setValue(field.name, value); }} /> )} /> ( {t('form.unitTime')} { if (value) { form.setValue(field.name, value); } }} options={[ { label: t('form.Year'), value: 'Year' }, { label: t('form.Month'), value: 'Month' }, { label: t('form.Day'), value: 'Day' }, { label: t('form.Hour'), value: 'Hour' }, { label: t('form.Minute'), value: 'Minute' }, ]} /> )} /> ( {t('form.replacement')} unitConversion('centsToDollars', value)} formatOutput={(value) => unitConversion('dollarsToCents', value)} onValueChange={(value) => { form.setValue(field.name, value); }} /> )} /> ( {t('form.traffic')} unitConversion('bytesToGb', value)} formatOutput={(value) => unitConversion('gbToBytes', value)} suffix='GB' onValueChange={(value) => { form.setValue(field.name, value); }} /> )} /> ( {t('form.speedLimit')} unitConversion('bitsToMb', value)} formatOutput={(value) => unitConversion('mbToBits', value)} suffix='MB' onValueChange={(value) => { form.setValue(field.name, value); }} /> )} /> ( {t('form.deviceLimit')} { form.setValue(field.name, value); }} /> )} /> ( {t('form.inventory')} { form.setValue(field.name, value); }} /> )} /> ( {t('form.quota')} { form.setValue(field.name, value); }} /> )} /> ( {t('form.subscribeGroup')} placeholder={t('form.selectSubscribeGroup')} {...field} onChange={(value) => { form.setValue(field.name, value); }} options={group?.map((item) => ({ label: item.name, value: item.id, }))} /> )} />
( {t('form.discount')} fields={[ { name: 'quantity', type: 'number', min: 1, suffix: unit_time && t(`form.${unit_time}`), }, { name: 'discount', type: 'number', min: 1, max: 100, placeholder: t('form.discountPercent'), suffix: '%', calculateValue: function (data) { const { unit_price } = form.getValues(); return { ...data, price: evaluateWithPrecision( `${unit_price} * ${data.quantity} * ${data.discount} / 100`, ), }; }, }, { name: 'price', placeholder: t('form.discount_price'), type: 'number', min: 0, formatInput: (value) => unitConversion('centsToDollars', value), formatOutput: (value) => unitConversion('dollarsToCents', value), internal: true, calculateValue: function (data) { const { unit_price } = form.getValues(); return { ...data, discount: evaluateWithPrecision( `${data.price} / ${data.quantity} / ${unit_price} * 100`, ), }; }, }, ]} value={field.value} onChange={(value) => { form.setValue(field.name, value); }} /> {t('form.discountDescription')} )} />
( {t('form.serverGroup')} {server_groups?.map((group: API.ServerGroup) => { const value = field.value || []; return (
{ return checked ? form.setValue(field.name, [...value, group.id]) : form.setValue( field.name, value.filter((value: number) => value !== group.id), ); }} />
    {server ?.filter((server: API.Server) => server.group_id === group.id) ?.map((node: API.Server) => { return (
  • {node.name} {node.server_addr} {node.protocol}
  • ); })}
); })}
)} /> ( {t('form.server')}
{server ?.filter((item: API.Server) => !item.group_id) ?.map((item: API.Server) => { const value = field.value || []; return (
{ return checked ? form.setValue(field.name, [...value, item.id]) : form.setValue( field.name, value.filter((value: number) => value !== item.id), ); }} />
); })}
)} />
); }