From 67c7552b97e0eb431ac5541caabf526b0332d1af Mon Sep 17 00:00:00 2001 From: Can Tezel Date: Sat, 28 Dec 2024 15:54:15 +0000 Subject: [PATCH 1/7] Added a add payment method to when initially adding a subscription --- bun.lockb | Bin 329018 -> 329010 bytes .../(dashboard)/add-payment-method-form.tsx | 74 +++ .../(dashboard)/add-subscription-dialog.tsx | 472 ++++++++++-------- 3 files changed, 334 insertions(+), 212 deletions(-) create mode 100644 src/app/(dashboard)/add-payment-method-form.tsx diff --git a/bun.lockb b/bun.lockb index 02479d788ea1de86b8b5eba0a903fc379e77f399..d26fccf2f99b59d73a6b8d3d399cb6c4f5732daf 100755 GIT binary patch delta 3498 zcmXxm2~<>79LMoDTo{@{5gA|x7&H)35EQr6L{xGil||eI%sio%%Q>Akvz#o=@?@q~ z*DXC_X11A>VVjv7TCUZxY(LehR8Y}UaqatufA{b?bMNoocjxi$oBQ5J3%ykfy)P8F zD(4o?FLcc>4B1*}iak-sLmbEDI8Gd-9JgCMCOYQ_jSW#kV~6AXNXDNNibTzR)^TC9 zTWR)-b=}Mqzv}RC;#BSWO^4q^J6*HiHS^H!pxGa`Sf=<>yKW)Q)~>%ai=f?Ev%fWK zM!SnzroK~}wV*jb&YV)KSrqkL&FaYH0<=UQ&CY5TO*Rx#&g-#W z>cbin$N8rz3pz0bMH>FA17m5A)$D?1vH(LgyQrCuc7}fOCC&U~iJCQ#$!f&oic~1q zbeybOLujxubFOPB%eGqw-q0*SR->8xyK*-qVS{EaGKMxT?_$l2j%!UmQ?pRbZY4|C zEX-y{u#Rr+N};O{bIDSNlgS#kL9S-eIxdwgLcWvYwR4#w7^7WjL{@GoN~~t>s2^i( zWsT!BYft@zuD4G!x#K?2OXt@tgRDa5i`PuUTWbFcE za>^YMrC-uY$8{nr)=Sb_Gr6COG`m$Zxt|L)OV&*8=Ys#&F-60!gmQOEX`@*;>ZN2U ze5R5~cXy1{EKSGVMpmp@JI#8Kjn=HaW@2vQUw{>Q?q>P??Xy2GX6K`PHdHc(p$&L&oxe$ zI!80v6BbdI(nmAd6Lw)Ba*boxN1CXdzS>zxSPl7tewxXC;n#`#YgSCQn7WiaTOVnn z+YZpK5+X}oh78oKs72G*X#qPYV3O@3j|s0XX<;&5!M0qbRP5!->_-juU>|nkbL_@0 z9E5C}2QZy8x(8!14Kq-IdvPa*q70)j6?b6@3Q&Y%ObT;?&SWaXQH~N!zzE!pk(h}g zn20eLhw&&yfAoXw+k+r`bzjK-dr`=DSV$upOUa2R_G6)Zj}Tz(G4J#;|vxsd_ERJw(ePY3>BD+`%wn&2&WYHVH%cTDPH7f^+Swl zHsA}c_CBO=UdfRC-Ggwr5r(7k1LR9(PnSKs0GG)ca21zqS*%IR&SsiU=!k$!i6r<~ zl{7xvqYX~eu0@^Q8f#_-|K)Vfp#c|g5$ACUr%{W4a0X{lhk6{x5gf%ae2Xg1<}JL7 z%~*+5SdA6P#}LR?k_R^FVB^M~MMEA{Z6S{_dGg36FONBSGRYH0p3}0)d$F1|S;D1R zil_MExp;;8r+n7)KW^glb<8H4g-7u=9>T*o&3d20N%x?pN5Kvn+fj{e_z)lAV|;@5 z@d0Kq={HPp6kp+M^x$Ng*&LrqafebZb=z{Ei5>Y6LlJPOiIyB5EmLvM zl~|8A5Wy06=CcdBq8qy7HhaxyM*A;w=oPqG#%p}ao0Y+JJKb+G;+HYwJksrWn)*99 z%gLW{+c*4XPUh{LWe#%DA9)yze8giaV;|<&{a9to;?1h=bD3fe9>N2c2*C79%pyH%#6iDiO&gUd%T6n2z*o^s@ zgqLl7qDk{qaj1_&ciHTKiL*lk#^;v@?MwKWKCxV0{3yQa#{*`4l4r2I9Z;UBZr|H< wcZa*mduDaY%C4^HZ_;;oz6;@=p)9Xn5fU2i^6WEB?Uf5Qnb7J@CTxl8KUqSOC;$Ke delta 3506 zcmXZe2~bs49LMqd-g!VI`@X9nq97>lsffFxCE%J0xaB&jGmUGOPb<`%$}K&bW{H($ zmO?pbi(|Q$yNzYbWYcPjTke4N{lotbpLzfLJLjEyx#zxfZ^g2R&z42380gxzD8Dq{ zRhsYKm2V3DL(jP#$K^OqG-Nwx%l%Dw&JP+JqWHQ7$2m{NpW{J-WxW*0Q`XTODJzu7_=;&*LoK%A{je`prKerwJC)GUzwHk$oq zH#3CyZ*2-DKIL2A_K#*E>~@tiXS+xy|7<97HM^`?7+G)4uIhKg$p%2SYnnw+KVFwO z&h`4t>qHVfq2Uef7{z{pX8&m>6EH%vo0`S2pP>i7rCBUlf@ZhLWH#b(PYSmC+D~S! z+T*RuoN5ha+74*P8qE^P4r^AcSrXa1nmr)nuWovmYbKXU?wUrJp_xmw#$@T5xovg; z^Jugwg{H2IB~$GolR0dHT+KqXUn*IE9Fr~7E@B98m^P&mS-GXy!Zm9~eU7=6IgZe* zIrT*C7pa-tafkHMMQN5nc2Vz(XwBptwRC1hU0x$1LilbqT)`&04Y_ zss~BXtQFZny(Ed6$^D$KS(0XQKNo1$P&2uo3q5vNpb7G}rLNNJ(pcNuNjq5zpUIlF zryi_Xie??ihHKVDvyNmVG)vX&F|s1fnvzjEolxv{dej}HnRe_<*ppV7z~-8Dp_YMU zOV=!iT4qYN3_CW6`=Nz4#ptYSxXqN+%>sv+iVXK_;f9EoX?#R@&5)=(OC5 zY}uOiqQ0s_v?lY|>p`ZCSK^NB%74@k6_Bixel3ssQ^?j?`}L>Z1?iWgnf&adb%t~G zyYjPtm0GrLn#r$zKl-4%aqO006PnXQ8}kV(AqVKGnJfvhI&d$|3dxpJ%huao4>sYc zeYI&Yk)@U{{WL3(pAg&3%H|9=1!OfNlKltNO zG=Qvbfe1n{LJ*2Dgd+lxh{8SQuNpO|#REKqk%k{UIFs$pZZeO}nrg zdr-wizJjZ`=CQXUO|rL??n@wR@-ocBb9e?5F$u-sws4B@4OjOFk{K@rKJbMf{2}YO ztlP3q%eq{M)BM2h;vVj!2J#H&nQbRUnZ)dt?6g8QQjmm1q@g)tn3pDeUgon3SM07R zGsAnC^QpoWT*Wn9#|`|4o4AGBxP!a6hx0gx?{NyJQNcNF#}4eodc1)R*a%s0`XCPj zzudvS#Xhzo)+}fj$0?8F z)TW>_bq;dT6aA0}d5&GfQNFhUb8K;(+0b+cLp_fLScDglLAS-oLne~_Y*xHU_T9x8?CBKd+dslQ0?k>3RSs@D7Jr z&DUSXe9S@#4s&giXm5x{Xo3I)*}MeiRNhwxLEce@U?_$`-cRIxWF$sG-a~lrsC&&w z;IkRla|+uzL@?L7jKjLj;HVkiJ{78JlneJFwR6ggK=Y3os4SG2YfD zn6%h(zAJA9pFp1aF^sdH&(CaTqKS6xw%rp=eB2tYi#&Hfr$xLiuXJpUy4ds(lU%tj z(JV~zFX&RAuBdF>#kBX82Y_sqB|S{~KK~1D?kKjmD_6QbelGu0rhdO{$!6oJ+-!VS Gy8Z{)Z;)&N diff --git a/src/app/(dashboard)/add-payment-method-form.tsx b/src/app/(dashboard)/add-payment-method-form.tsx new file mode 100644 index 0000000..8548b37 --- /dev/null +++ b/src/app/(dashboard)/add-payment-method-form.tsx @@ -0,0 +1,74 @@ +"use client"; + +import { zodResolver } from "@hookform/resolvers/zod"; +import { useForm } from "react-hook-form"; +import { z } from "zod"; +import { Button } from "@/components/ui/button"; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; +import { api } from "@/trpc/react"; + +const paymentMethodSchema = z.object({ + name: z.string().min(1, "Name is required"), + // Add other fields as needed +}); + +interface AddPaymentMethodFormProps { + onSuccess: (paymentMethodId: string) => void; + onCancel: () => void; +} + +export function AddPaymentMethodForm({ onSuccess, onCancel }: AddPaymentMethodFormProps) { + const form = useForm>({ + resolver: zodResolver(paymentMethodSchema), + defaultValues: { + name: "", + }, + }); + + const createPaymentMethod = api.paymentMethod.create.useMutation({ + onSuccess: (data) => { + onSuccess(data.id); + }, + }); + + const onSubmit = (values: z.infer) => { + createPaymentMethod.mutate(values); + }; + + return ( +
+ + ( + + Payment Method Name + + + + + + )} + /> + + {/* Add other payment method fields as needed */} + +
+ + +
+ + + ); +} \ No newline at end of file diff --git a/src/app/(dashboard)/add-subscription-dialog.tsx b/src/app/(dashboard)/add-subscription-dialog.tsx index f82b313..496ba43 100644 --- a/src/app/(dashboard)/add-subscription-dialog.tsx +++ b/src/app/(dashboard)/add-subscription-dialog.tsx @@ -42,6 +42,18 @@ import { format } from "date-fns"; import { CalendarIcon } from "lucide-react"; import { addDays } from "date-fns"; import { api } from "@/trpc/react"; +import { AddPaymentMethodForm } from "./add-payment-method-form"; +import { Form } from "@/components/ui/form"; +import { useForm } from "react-hook-form"; + +interface SubscriptionFormData { + name: string; + price: number; + billingCycle: BillingCycle; + startDate: Date; + paymentMethodId: string; + isTrial: boolean; +} type AddSubscriptionDialogProps = { isOpen: boolean; @@ -142,10 +154,23 @@ export function AddSubscriptionDialog({ const [userInput, setUserInput] = useState(""); const [isPopoverOpen, setIsPopoverOpen] = useState(false); const [isCustomService, setIsCustomService] = useState(false); + const [showPaymentMethodForm, setShowPaymentMethodForm] = useState(true); + const [formData, setFormData] = useState>({}); const { data: paymentMethods } = api.paymentMethod.getAll.useQuery(); const { data: user } = api.user.getCurrent.useQuery(); + const form = useForm({ + defaultValues: { + name: "", + price: 0, + billingCycle: "Monthly", + startDate: new Date(), + paymentMethodId: "", + isTrial: false, + } + }); + useEffect(() => { if (initialData && isOpen) { setNewSubscription({ @@ -255,191 +280,129 @@ export function AddSubscriptionDialog({ } }; + const handlePaymentMethodAdded = (paymentMethodId: string) => { + setShowPaymentMethodForm(false); + setFormData(prev => ({ + ...prev, + paymentMethodId + })); + }; + return ( - + - {initialData ? "Edit Subscription" : "Add New Subscription"} + {showPaymentMethodForm + ? "Add Payment Method" + : initialData ? "Edit Subscription" : "Add Subscription"} - - {initialData - ? "Enter the details of your subscription. This will not affect historical data - only the most recent period will be updated." - : "Enter the details of your new subscription"} - -
-
-
- -
- {isCustomService ? ( - - setNewSubscription({ - ...newSubscription, - name: e.target.value, - }) - } - className="w-full" - placeholder="Enter custom service name" - /> - ) : ( - - - - - - - - - {isSearching && ( - -
- -
-
- )} - No results found. - - {searchResults.map((service) => ( - handleServiceSelect(service)} - > - {service.name} + + {showPaymentMethodForm ? ( + setShowPaymentMethodForm(false)} + /> + ) : ( + +
+
+ +
+ {isCustomService ? ( + + setNewSubscription({ + ...newSubscription, + name: e.target.value, + }) + } + className="w-full" + placeholder="Enter custom service name" + /> + ) : ( + + + + + + + + + {isSearching && ( + +
+ +
+
+ )} + No results found. + + {searchResults.map((service) => ( + handleServiceSelect(service)} + > + {service.name} + + ))} + + + + + Add custom service - ))} - - - - - Add custom service - - -
-
-
-
- )} + + + + + + )} +
-
-
- - - setNewSubscription({ - ...newSubscription, - price: parseFloat(e.target.value), - }) - } - className="col-span-4" - required - /> -
-
- - - - - - - - setNewSubscription({ - ...newSubscription, - startDate: date ?? new Date(), - }) - } - initialFocus - /> - - -
-
- - -
-
- -
- handleTrialToggle(e.target.checked)} - className="mr-2" +
+ + + setNewSubscription({ + ...newSubscription, + price: parseFloat(e.target.value), + }) + } + className="col-span-4" + required /> -
-
- - {newSubscription.isTrial && (
-
- )} -
- - + setNewSubscription({ + ...newSubscription, + billingCycle: value as BillingCycle, + }) + } + > + + + + + Weekly + Biweekly + Monthly + Yearly + + +
+
+ +
+ handleTrialToggle(e.target.checked)} + className="mr-2" + /> + +
+
+ + {newSubscription.isTrial && ( +
+ + + + + + + + setTrialEndDate(date ?? addDays(new Date(), 30)) + } + initialFocus + /> + + +
+ )} +
+ + + {paymentMethods?.map((method) => ( + + {method.name} {method.id === user?.defaultPaymentMethodId ? "(Default)" : ""} + + ))} + + +
-
- - - - + + + + + )}
); From 9d6239e4445fda934d6a07e8bcf95eded8b7dd40 Mon Sep 17 00:00:00 2001 From: Can Tezel Date: Sat, 28 Dec 2024 16:04:25 +0000 Subject: [PATCH 2/7] Adding payment method UI is now the same as the payment methods screen --- src/app/(dashboard)/add-payment-method-form.tsx | 9 ++++++++- src/app/(dashboard)/add-subscription-dialog.tsx | 4 +--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/app/(dashboard)/add-payment-method-form.tsx b/src/app/(dashboard)/add-payment-method-form.tsx index 8548b37..81ea926 100644 --- a/src/app/(dashboard)/add-payment-method-form.tsx +++ b/src/app/(dashboard)/add-payment-method-form.tsx @@ -14,6 +14,7 @@ import { } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { api } from "@/trpc/react"; +import { toast } from "sonner"; const paymentMethodSchema = z.object({ name: z.string().min(1, "Name is required"), @@ -37,10 +38,16 @@ export function AddPaymentMethodForm({ onSuccess, onCancel }: AddPaymentMethodFo onSuccess: (data) => { onSuccess(data.id); }, + onError: (error) => { + toast.error(error.message); + }, }); const onSubmit = (values: z.infer) => { - createPaymentMethod.mutate(values); + createPaymentMethod.mutate({ + type: "card", + name: values.name, + }); }; return ( diff --git a/src/app/(dashboard)/add-subscription-dialog.tsx b/src/app/(dashboard)/add-subscription-dialog.tsx index 496ba43..9f62cc2 100644 --- a/src/app/(dashboard)/add-subscription-dialog.tsx +++ b/src/app/(dashboard)/add-subscription-dialog.tsx @@ -42,7 +42,7 @@ import { format } from "date-fns"; import { CalendarIcon } from "lucide-react"; import { addDays } from "date-fns"; import { api } from "@/trpc/react"; -import { AddPaymentMethodForm } from "./add-payment-method-form"; +import { AddPaymentMethodForm } from "@/components/add-payment-method-form"; import { Form } from "@/components/ui/form"; import { useForm } from "react-hook-form"; @@ -301,8 +301,6 @@ export function AddSubscriptionDialog({ {showPaymentMethodForm ? ( setShowPaymentMethodForm(false)} /> ) : (
From 9b7dd310e44ba950f1f4fb6602d6df426a751e80 Mon Sep 17 00:00:00 2001 From: Can Tezel Date: Sat, 28 Dec 2024 16:26:29 +0000 Subject: [PATCH 3/7] Fixed up payment method sub modal staying open --- .../(dashboard)/add-payment-method-form.tsx | 81 ------------------- .../(dashboard)/add-subscription-dialog.tsx | 21 +++-- src/components/add-payment-method-form.tsx | 3 +- 3 files changed, 17 insertions(+), 88 deletions(-) delete mode 100644 src/app/(dashboard)/add-payment-method-form.tsx diff --git a/src/app/(dashboard)/add-payment-method-form.tsx b/src/app/(dashboard)/add-payment-method-form.tsx deleted file mode 100644 index 81ea926..0000000 --- a/src/app/(dashboard)/add-payment-method-form.tsx +++ /dev/null @@ -1,81 +0,0 @@ -"use client"; - -import { zodResolver } from "@hookform/resolvers/zod"; -import { useForm } from "react-hook-form"; -import { z } from "zod"; -import { Button } from "@/components/ui/button"; -import { - Form, - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, -} from "@/components/ui/form"; -import { Input } from "@/components/ui/input"; -import { api } from "@/trpc/react"; -import { toast } from "sonner"; - -const paymentMethodSchema = z.object({ - name: z.string().min(1, "Name is required"), - // Add other fields as needed -}); - -interface AddPaymentMethodFormProps { - onSuccess: (paymentMethodId: string) => void; - onCancel: () => void; -} - -export function AddPaymentMethodForm({ onSuccess, onCancel }: AddPaymentMethodFormProps) { - const form = useForm>({ - resolver: zodResolver(paymentMethodSchema), - defaultValues: { - name: "", - }, - }); - - const createPaymentMethod = api.paymentMethod.create.useMutation({ - onSuccess: (data) => { - onSuccess(data.id); - }, - onError: (error) => { - toast.error(error.message); - }, - }); - - const onSubmit = (values: z.infer) => { - createPaymentMethod.mutate({ - type: "card", - name: values.name, - }); - }; - - return ( - - - ( - - Payment Method Name - - - - - - )} - /> - - {/* Add other payment method fields as needed */} - -
- - -
- - - ); -} \ No newline at end of file diff --git a/src/app/(dashboard)/add-subscription-dialog.tsx b/src/app/(dashboard)/add-subscription-dialog.tsx index 9f62cc2..25e4cff 100644 --- a/src/app/(dashboard)/add-subscription-dialog.tsx +++ b/src/app/(dashboard)/add-subscription-dialog.tsx @@ -154,7 +154,7 @@ export function AddSubscriptionDialog({ const [userInput, setUserInput] = useState(""); const [isPopoverOpen, setIsPopoverOpen] = useState(false); const [isCustomService, setIsCustomService] = useState(false); - const [showPaymentMethodForm, setShowPaymentMethodForm] = useState(true); + const [showPaymentMethodForm, setShowPaymentMethodForm] = useState(false); const [formData, setFormData] = useState>({}); const { data: paymentMethods } = api.paymentMethod.getAll.useQuery(); @@ -282,14 +282,19 @@ export function AddSubscriptionDialog({ const handlePaymentMethodAdded = (paymentMethodId: string) => { setShowPaymentMethodForm(false); - setFormData(prev => ({ + setNewSubscription(prev => ({ ...prev, paymentMethodId })); }; + const closeDialog = () => { + onClose(); + setTimeout(() => setShowPaymentMethodForm(false), 1000); + }; + return ( - + @@ -298,10 +303,14 @@ export function AddSubscriptionDialog({ : initialData ? "Edit Subscription" : "Add Subscription"} - + {showPaymentMethodForm ? ( - + <> + + + ) : (
diff --git a/src/components/add-payment-method-form.tsx b/src/components/add-payment-method-form.tsx index 2f62a24..490ce20 100644 --- a/src/components/add-payment-method-form.tsx +++ b/src/components/add-payment-method-form.tsx @@ -21,7 +21,7 @@ import { } from "@/lib/schema/paymentMethod"; import { api } from "@/trpc/react"; -export function AddPaymentMethodForm() { +export function AddPaymentMethodForm({ onSuccess }: { onSuccess?: (paymentMethodId: string) => void }) { const utils = api.useUtils(); const { mutate: createPaymentMethod, isPending } = @@ -33,6 +33,7 @@ export function AddPaymentMethodForm() { form.reset(); void utils.paymentMethod.getAll.invalidate(); void utils.user.getCurrent.invalidate(); + onSuccess?.(data.id); }, onError: (error) => { toast.error( From 88632fbb8dab75e3db8e4124e918c339cdc907b4 Mon Sep 17 00:00:00 2001 From: Can Tezel Date: Sat, 28 Dec 2024 16:36:51 +0000 Subject: [PATCH 4/7] Added back button to add payment method modal --- .../(dashboard)/add-subscription-dialog.tsx | 34 ++++++++++++------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/src/app/(dashboard)/add-subscription-dialog.tsx b/src/app/(dashboard)/add-subscription-dialog.tsx index 25e4cff..5d65a39 100644 --- a/src/app/(dashboard)/add-subscription-dialog.tsx +++ b/src/app/(dashboard)/add-subscription-dialog.tsx @@ -296,21 +296,31 @@ export function AddSubscriptionDialog({ return ( - - - {showPaymentMethodForm - ? "Add Payment Method" - : initialData ? "Edit Subscription" : "Add Subscription"} - + +
+ {showPaymentMethodForm && ( + + )} + + {showPaymentMethodForm + ? "Add Payment Method" + : initialData ? "Edit Subscription" : "Add Subscription"} + +
{showPaymentMethodForm ? ( - <> - - - + ) : (
From 96db703ae9648b266be96e053454c844d134ca68 Mon Sep 17 00:00:00 2001 From: Jack Dewinter Date: Sat, 28 Dec 2024 16:53:31 +0000 Subject: [PATCH 5/7] utilise react hook for more --- .../(dashboard)/add-subscription-dialog.tsx | 615 ++++++++++-------- 1 file changed, 330 insertions(+), 285 deletions(-) diff --git a/src/app/(dashboard)/add-subscription-dialog.tsx b/src/app/(dashboard)/add-subscription-dialog.tsx index 5d65a39..5bc18b2 100644 --- a/src/app/(dashboard)/add-subscription-dialog.tsx +++ b/src/app/(dashboard)/add-subscription-dialog.tsx @@ -43,15 +43,26 @@ import { CalendarIcon } from "lucide-react"; import { addDays } from "date-fns"; import { api } from "@/trpc/react"; import { AddPaymentMethodForm } from "@/components/add-payment-method-form"; -import { Form } from "@/components/ui/form"; +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; import { useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import * as z from "zod"; +import { Checkbox } from "@/components/ui/checkbox"; interface SubscriptionFormData { name: string; price: number; billingCycle: BillingCycle; startDate: Date; - paymentMethodId: string; + paymentMethodId: string | null; isTrial: boolean; } @@ -126,6 +137,19 @@ const mockServices = [ }, ]; +const formSchema = z.object({ + name: z.string().min(2, { + message: "Name must be at least 2 characters.", + }), + price: z.number().min(0, { + message: "Price must be a positive number.", + }), + billingCycle: z.enum(["Weekly", "Biweekly", "Monthly", "Yearly"]), + startDate: z.date(), + paymentMethodId: z.string().nullable(), + isTrial: z.boolean(), +}); + export function AddSubscriptionDialog({ isOpen, onClose, @@ -160,15 +184,16 @@ export function AddSubscriptionDialog({ const { data: paymentMethods } = api.paymentMethod.getAll.useQuery(); const { data: user } = api.user.getCurrent.useQuery(); - const form = useForm({ + const form = useForm>({ + resolver: zodResolver(formSchema), defaultValues: { - name: "", - price: 0, - billingCycle: "Monthly", - startDate: new Date(), - paymentMethodId: "", - isTrial: false, - } + name: initialData?.name ?? "", + price: initialData ? initialData.price / 100 : 0, + billingCycle: initialData?.billingCycle ?? "Monthly", + startDate: initialData?.startDate ?? new Date(), + paymentMethodId: initialData?.paymentMethodId ?? null, + isTrial: initialData?.isTrial ?? false, + }, }); useEffect(() => { @@ -183,7 +208,7 @@ export function AddSubscriptionDialog({ setTrialEndDate(initialData.endDate); } } else if (isOpen && user?.defaultPaymentMethodId) { - setNewSubscription(prev => ({ + setNewSubscription((prev) => ({ ...prev, paymentMethodId: user.defaultPaymentMethodId, })); @@ -208,35 +233,32 @@ export function AddSubscriptionDialog({ } }, [userInput]); - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault(); - if (newSubscription.name && newSubscription.price > 0) { - const subscriptionData = { - ...newSubscription, - price: newSubscription.price * 100, - ...(newSubscription.isTrial && { endDate: trialEndDate }), - }; + useEffect(() => { + if (isOpen && user?.defaultPaymentMethodId) { + form.setValue("paymentMethodId", user.defaultPaymentMethodId); + } + }, [isOpen, user?.defaultPaymentMethodId, form]); - if (initialData?.id) { - onUpdateSubscription?.({ ...subscriptionData, id: initialData.id }); - } else { - onAddSubscription(subscriptionData); - } + const onSubmit = (data: z.infer) => { + const subscriptionData = { + ...data, + price: data.price * 100, + autoRenew: true, + ...(data.isTrial && { endDate: trialEndDate }), + paymentMethodId: + data.paymentMethodId === "none" ? null : data.paymentMethodId, + }; - setNewSubscription({ - name: "", - price: 0, - billingCycle: "Monthly", - isTrial: false as const, - startDate: new Date(), - paymentMethodId: null, - }); - setTrialEndDate(addDays(new Date(), 30)); - setUserInput(""); - setIsCustomService(false); + if (initialData?.id) { + onUpdateSubscription?.({ ...subscriptionData, id: initialData.id }); } else { - toast.error("Please fill in all fields correctly."); + onAddSubscription(subscriptionData); } + + form.reset(); + setTrialEndDate(addDays(new Date(), 30)); + setUserInput(""); + setIsCustomService(false); }; const handleServiceSelect = (service: (typeof mockServices)[0]) => { @@ -282,10 +304,15 @@ export function AddSubscriptionDialog({ const handlePaymentMethodAdded = (paymentMethodId: string) => { setShowPaymentMethodForm(false); - setNewSubscription(prev => ({ - ...prev, - paymentMethodId - })); + form.setValue("paymentMethodId", paymentMethodId); + }; + + const handlePaymentMethodChange = (value: string) => { + if (value === "add_new") { + setShowPaymentMethodForm(true); + } else { + form.setValue("paymentMethodId", value); + } }; const closeDialog = () => { @@ -297,10 +324,10 @@ export function AddSubscriptionDialog({ -
+
{showPaymentMethodForm && ( - )} - {showPaymentMethodForm - ? "Add Payment Method" - : initialData ? "Edit Subscription" : "Add Subscription"} + {showPaymentMethodForm + ? "Add Payment Method" + : initialData + ? "Edit Subscription" + : "Add Subscription"}
- + {showPaymentMethodForm ? ( - + ) : ( -
-
- -
- {isCustomService ? ( - - setNewSubscription({ - ...newSubscription, - name: e.target.value, - }) - } - className="w-full" - placeholder="Enter custom service name" - /> - ) : ( - - - - - - - - - {isSearching && ( - -
- -
-
+ + + + + + + + {isSearching && ( + +
+ +
+
+ )} + No results found. + + {searchResults.map((service) => ( + + handleServiceSelect(service) + } + > + {service.name} + + ))} + + + + + Add custom service + + +
+
+
+
+ )} + + + + )} + /> + ( + + Cost + + field.onChange(Number(e.target.value))} + /> + + + + )} + /> + ( + + Start Date + + + +
-
-
- - - setNewSubscription({ - ...newSubscription, - price: parseFloat(e.target.value), - }) - } - className="col-span-4" - required - /> -
-
- - - -
-
- -
- handleTrialToggle(e.target.checked)} - className="mr-2" - /> - -
-
- - {newSubscription.isTrial && ( -
- - - - - - - - setTrialEndDate(date ?? addDays(new Date(), 30)) - } - initialFocus + + + + + + + Weekly + Biweekly + Monthly + Yearly + + + + + )} + /> + ( + + + - - -
+ +
+ Trial Period + + This is a trial subscription + +
+ + )} + /> + {form.watch("isTrial") && ( + ( + + Trial End Date + + + + + + + + + + + + + )} + /> )} -
- - -
-
- - - + ( + + Payment Method + + + + )} + /> + + + + )} From 3b4b501fbc9c8dfe6870d2c434ed004f3d3ba859 Mon Sep 17 00:00:00 2001 From: Jack Dewinter Date: Mon, 30 Dec 2024 14:25:06 +0000 Subject: [PATCH 6/7] set default payment method if no payment method added --- src/app/(dashboard)/add-subscription-dialog.tsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/app/(dashboard)/add-subscription-dialog.tsx b/src/app/(dashboard)/add-subscription-dialog.tsx index 5bc18b2..3378d65 100644 --- a/src/app/(dashboard)/add-subscription-dialog.tsx +++ b/src/app/(dashboard)/add-subscription-dialog.tsx @@ -5,13 +5,11 @@ import { Button } from "@/components/ui/button"; import { Dialog, DialogContent, - DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; import { Select, SelectContent, @@ -19,7 +17,6 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select"; -import { toast } from "sonner"; import type { InputType } from "@/server/api/root"; import type { BillingCycle } from "@prisma/client"; import { @@ -320,6 +317,13 @@ export function AddSubscriptionDialog({ setTimeout(() => setShowPaymentMethodForm(false), 1000); }; + const closeAddPaymentMethodDialog = () => { + setShowPaymentMethodForm(false); + if (user?.defaultPaymentMethodId) { + form.setValue("paymentMethodId", user.defaultPaymentMethodId); + } + }; + return ( @@ -329,7 +333,7 @@ export function AddSubscriptionDialog({