1+ import { zodResolver } from '@hookform/resolvers/zod'
12import {
23 Accordion ,
34 AccordionItem ,
45 getKeyValue ,
6+ Input ,
57 ModalBody ,
68 ModalContent ,
79 ModalHeader ,
@@ -15,11 +17,18 @@ import {
1517} from '@nextui-org/react'
1618import { IconPlus , IconRefresh , IconTrash } from '@tabler/icons-react'
1719import dayjs from 'dayjs'
18- import { FC , Fragment , useMemo } from 'react'
20+ import { FC , Fragment , useEffect , useMemo } from 'react'
21+ import { Controller , FormProvider , useFieldArray , useForm } from 'react-hook-form'
1922import { useTranslation } from 'react-i18next'
20- import { useRemoveSubscriptionsMutation , useUpdateSubscriptionsMutation } from '~/apis/mutation'
23+ import { z } from 'zod'
24+ import {
25+ useImportSubscriptionsMutation ,
26+ useRemoveSubscriptionsMutation ,
27+ useUpdateSubscriptionsMutation
28+ } from '~/apis/mutation'
2129import { Button } from '~/components/Button'
22- import { Modal , ModalConfirmFormFooter } from '~/components/Modal'
30+ import { Modal , ModalConfirmFormFooter , ModalSubmitFormFooter } from '~/components/Modal'
31+ import { subscriptionFormDefault , subscriptionFormSchema } from '~/schemas/subscription'
2332import { Node , Subscription } from './typings'
2433
2534const SubscriptionNodeTable : FC < {
@@ -52,30 +61,124 @@ const SubscriptionNodeTable: FC<{
5261 )
5362}
5463
55- export const SubscriptionSection : FC < { subscriptions : Subscription [ ] ; refetch : ( ) => Promise < unknown > } > = ( {
56- subscriptions,
57- refetch
58- } ) => {
64+ const ImportSubscriptionInputList : FC < { name : string } > = ( { name } ) => {
65+ const { t } = useTranslation ( )
66+ const { fields, append, remove } = useFieldArray ( { name } )
67+
68+ return (
69+ < div className = "flex flex-col gap-2" >
70+ { fields . map ( ( item , index ) => (
71+ < div key = { item . id } className = "flex items-start gap-2" >
72+ < Controller
73+ name = { `${ name } .${ index } .tag` }
74+ render = { ( { field, fieldState } ) => (
75+ < Input
76+ className = "w-1/3"
77+ label = { t ( 'form.fields.tag' ) }
78+ placeholder = { t ( 'form.fields.tag' ) }
79+ errorMessage = { fieldState . error ?. message }
80+ isRequired
81+ { ...field }
82+ />
83+ ) }
84+ />
85+
86+ < Controller
87+ name = { `${ name } .${ index } .link` }
88+ render = { ( { field, fieldState } ) => (
89+ < Input
90+ label = { t ( 'form.fields.link' ) }
91+ placeholder = { t ( 'form.fields.link' ) }
92+ errorMessage = { fieldState . error ?. message }
93+ isRequired
94+ { ...field }
95+ />
96+ ) }
97+ />
98+
99+ < Button color = "danger" isIconOnly onPress = { ( ) => remove ( index ) } >
100+ < IconTrash />
101+ </ Button >
102+ </ div >
103+ ) ) }
104+
105+ < div className = "self-end" >
106+ < Button color = "primary" onPress = { ( ) => append ( { tag : '' , link : '' } ) } isIconOnly >
107+ < IconPlus />
108+ </ Button >
109+ </ div >
110+ </ div >
111+ )
112+ }
113+
114+ export const SubscriptionSection : FC < { subscriptions : Subscription [ ] } > = ( { subscriptions } ) => {
59115 const { t } = useTranslation ( )
60116
117+ const {
118+ isOpen : isImportSubscriptionOpen ,
119+ onOpen : onImportSubscriptionOpen ,
120+ onClose : onImportSubscriptionClose ,
121+ onOpenChange : onImportSubscriptionOpenChange
122+ } = useDisclosure ( )
123+
124+ const importSubscriptionForm = useForm < z . infer < typeof subscriptionFormSchema > > ( {
125+ shouldFocusError : true ,
126+ resolver : zodResolver ( subscriptionFormSchema ) ,
127+ defaultValues : subscriptionFormDefault
128+ } )
129+
61130 const {
62131 isOpen : isRemoveSubscriptionOpen ,
63132 onOpen : onRemoveSubscriptionOpen ,
64133 onClose : onRemoveSubscriptionClose ,
65134 onOpenChange : onRemoveSubscriptionOpenChange
66135 } = useDisclosure ( )
67136
137+ const importSubscriptionsMutation = useImportSubscriptionsMutation ( )
68138 const removeSubscriptionsMutation = useRemoveSubscriptionsMutation ( )
69139 const updateSubscriptionsMutation = useUpdateSubscriptionsMutation ( )
70140
141+ useEffect ( ( ) => {
142+ const timer = setTimeout ( ( ) => {
143+ if ( ! isImportSubscriptionOpen ) importSubscriptionForm . reset ( )
144+ } , 150 )
145+
146+ return ( ) => timer && clearTimeout ( timer )
147+ } , [ importSubscriptionForm , isImportSubscriptionOpen ] )
148+
71149 return (
72150 < div className = "flex flex-col gap-4" >
73151 < div className = "flex items-center justify-between" >
74152 < h3 className = "text-xl font-bold" > { t ( 'primitives.subscription' ) } </ h3 >
75153
76- < Button color = "primary" isIconOnly >
154+ < Button color = "primary" isIconOnly onPress = { onImportSubscriptionOpen } >
77155 < IconPlus />
78156 </ Button >
157+
158+ < Modal isOpen = { isImportSubscriptionOpen } onOpenChange = { onImportSubscriptionOpenChange } >
159+ < FormProvider { ...importSubscriptionForm } >
160+ < form
161+ onSubmit = { importSubscriptionForm . handleSubmit ( async ( values ) => {
162+ await importSubscriptionsMutation . mutateAsync ( values . subscriptions )
163+
164+ onImportSubscriptionClose ( )
165+ } ) }
166+ >
167+ < ModalContent >
168+ < ModalHeader > { t ( 'primitives.subscription' ) } </ ModalHeader >
169+
170+ < ModalBody >
171+ < ImportSubscriptionInputList name = "subscriptions" />
172+ </ ModalBody >
173+
174+ < ModalSubmitFormFooter
175+ reset = { importSubscriptionForm . reset }
176+ isSubmitting = { importSubscriptionsMutation . isPending }
177+ />
178+ </ ModalContent >
179+ </ form >
180+ </ FormProvider >
181+ </ Modal >
79182 </ div >
80183
81184 < Accordion selectionMode = "multiple" variant = "shadow" isCompact >
@@ -91,11 +194,7 @@ export const SubscriptionSection: FC<{ subscriptions: Subscription[]; refetch: (
91194 as = "div"
92195 isIconOnly
93196 isLoading = { updateSubscriptionsMutation . isPending }
94- onPress = { ( ) =>
95- updateSubscriptionsMutation . mutate ( {
96- subscriptionIDs : [ subscription . id ]
97- } )
98- }
197+ onPress = { ( ) => updateSubscriptionsMutation . mutate ( { subscriptionIDs : [ subscription . id ] } ) }
99198 >
100199 < IconRefresh />
101200 </ Button >
@@ -117,7 +216,6 @@ export const SubscriptionSection: FC<{ subscriptions: Subscription[]; refetch: (
117216 isSubmitting = { removeSubscriptionsMutation . isPending }
118217 onConfirm = { async ( ) => {
119218 await removeSubscriptionsMutation . mutateAsync ( { subscriptionIDs : [ subscription . id ] } )
120- await refetch ( )
121219
122220 onRemoveSubscriptionClose ( )
123221 } }
0 commit comments