'use client'; import { ProTable, ProTableActions } from '@/components/pro-table'; import { createApplication, deleteApplication, getApplication, getSubscribeType, updateApplication, } from '@/services/admin/system'; import { zodResolver } from '@hookform/resolvers/zod'; import { Icon } from '@iconify/react'; import { useQuery } from '@tanstack/react-query'; import { Button } from '@workspace/ui/components/button'; import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, } from '@workspace/ui/components/form'; import { ScrollArea } from '@workspace/ui/components/scroll-area'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@workspace/ui/components/select'; import { Sheet, SheetContent, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, } from '@workspace/ui/components/sheet'; import { ConfirmButton } from '@workspace/ui/custom-components/confirm-button'; import { EnhancedInput } from '@workspace/ui/custom-components/enhanced-input'; import { useTranslations } from 'next-intl'; import Image from 'next/legacy/image'; import { assign, shake } from 'radash'; import { useEffect, useRef, useState } from 'react'; import { useForm } from 'react-hook-form'; import { toast } from 'sonner'; import { z } from 'zod'; const defaultValues = { platform: 'windows', subscribe_type: 'Clash', name: '', icon: '', url: '', }; interface FormProps { trigger: React.ReactNode | string; title: string; initialValues?: Partial; onSubmit: (values: T) => Promise; loading?: boolean; } function SubscribeAppForm({ trigger, title, loading, initialValues, onSubmit, }: FormProps) { const t = useTranslations('subscribe.app'); const [open, setOpen] = useState(false); const formSchema = z.object({ platform: z.enum(['windows', 'macos', 'linux', 'android', 'ios']), name: z.string(), subscribe_type: z.string(), icon: z.string(), url: z.string(), }); type FormSchema = z.infer; const form = useForm({ resolver: zodResolver(formSchema), defaultValues: assign( defaultValues, shake(initialValues, (value) => value === null), ), }); useEffect(() => { form.reset( assign( defaultValues, shake(initialValues, (value) => value === null), ), ); }, [form, initialValues]); const { data: subscribe_types } = useQuery({ queryKey: ['getSubscribeType'], queryFn: async () => { const { data } = await getSubscribeType(); return data.data?.subscribe_types || []; }, }); return ( {typeof trigger === 'string' ? : trigger} {title}
( {t('platform')} )} /> ( {t('subscriptionProtocol')} )} /> ( {t('appName')} )} /> ( {t('appIcon')} )} /> ( {t('appDownloadURL')} )} />
); } export default function SubscribeApp() { const t = useTranslations('subscribe.app'); const [loading, setLoading] = useState(false); const ref = useRef(null); return ( action={ref} header={{ toolbar: ( trigger={t('add')} title={t('createApp')} loading={loading} onSubmit={async (values) => { setLoading(true); try { await createApplication(values); toast.success(t('createSuccess')); ref.current?.refresh(); setLoading(false); return true; } catch (error) { setLoading(false); return false; } }} /> ), }} params={[ { key: 'platform', placeholder: t('platform'), options: [ { label: 'Windows', value: 'windows' }, { label: 'MacOS', value: 'mac' }, { label: 'Linux', value: 'linux' }, { label: 'Android', value: 'android' }, { label: 'iOS', value: 'ios' }, ], }, ]} request={async (_pagination, filters) => { const { data } = await getApplication(); const flatApps = Object.entries(data.data || {}).flatMap(([platform, apps]) => (apps as API.Application[]).map((app) => ({ ...app, platform, })), ); return { list: filters.platform ? flatApps.filter((app) => app.platform === filters.platform) : flatApps, total: 0, }; }} columns={[ { accessorKey: 'platform', header: t('platform'), cell: ({ row }) => row.getValue('platform'), }, { accessorKey: 'subscribe_type', header: t('subscriptionProtocol'), cell: ({ row }) => row.getValue('subscribe_type'), }, { accessorKey: 'name', header: t('appName'), }, { accessorKey: 'icon', header: t('appIcon'), cell: ({ row }) => ( {row.getValue('name')} ), }, { accessorKey: 'url', header: t('appDownloadURL'), }, ]} actions={{ render: (row) => [ key='edit' trigger={} title={t('editApp')} loading={loading} initialValues={{ ...row, }} onSubmit={async (values) => { setLoading(true); try { await updateApplication({ ...values, id: row.id, }); toast.success(t('updateSuccess')); ref.current?.refresh(); setLoading(false); return true; } catch (error) { setLoading(false); return false; } }} />, {t('delete')}} title={t('confirmDelete')} description={t('deleteWarning')} onConfirm={async () => { await deleteApplication({ id: row.id! }); toast.success(t('deleteSuccess')); ref.current?.refresh(); }} cancelText={t('cancel')} confirmText={t('confirm')} />, ], batchRender: (rows) => [ {t('batchDelete')}} title={t('confirmDelete')} description={t('deleteWarning')} onConfirm={async () => { await Promise.all(rows.map((row) => deleteApplication({ id: row.id! }))); toast.success(t('deleteSuccess')); ref.current?.reset(); }} cancelText={t('cancel')} confirmText={t('confirm')} />, ], }} /> ); }