@@ -2,19 +2,8 @@ import { conform, useForm } from "@conform-to/react";
22import { parse } from "@conform-to/zod" ;
33import { EnvelopeIcon , NoSymbolIcon , UserPlusIcon } from "@heroicons/react/20/solid" ;
44import { DialogClose } from "@radix-ui/react-dialog" ;
5- import {
6- Form ,
7- type MetaFunction ,
8- useActionData ,
9- useNavigation ,
10- useSearchParams ,
11- } from "@remix-run/react" ;
12- import {
13- type ActionFunctionArgs ,
14- type ActionFunction ,
15- type LoaderFunctionArgs ,
16- json ,
17- } from "@remix-run/server-runtime" ;
5+ import { Form , type MetaFunction , useActionData , useFetcher , useNavigation } from "@remix-run/react" ;
6+ import { type ActionFunctionArgs , type LoaderFunctionArgs , json } from "@remix-run/server-runtime" ;
187import { tryCatch } from "@trigger.dev/core" ;
198import { useEffect , useRef , useState } from "react" ;
209import { type UseDataFunctionReturn , typedjson , useTypedLoaderData } from "remix-typedjson" ;
@@ -129,11 +118,7 @@ export const action = async ({ request, params }: ActionFunctionArgs) => {
129118 const formType = formData . get ( "_formType" ) ;
130119
131120 if ( formType === "purchase-seats" ) {
132- const redirectTo = formData . get ( "redirectTo" ) ;
133- const redirectPath =
134- typeof redirectTo === "string" && redirectTo
135- ? redirectTo
136- : organizationTeamPath ( { slug : organizationSlug } ) ;
121+ const redirectPath = organizationTeamPath ( { slug : organizationSlug } ) ;
137122
138123 const org = await $replica . organization . findFirst ( {
139124 where : { slug : organizationSlug } ,
@@ -171,7 +156,7 @@ export const action = async ({ request, params }: ActionFunctionArgs) => {
171156 }
172157
173158 return redirectWithSuccessMessage (
174- ` ${ redirectPath } ?purchaseSuccess=true` ,
159+ redirectPath ,
175160 request ,
176161 submission . value . action === "purchase"
177162 ? "Seats updated successfully"
@@ -512,11 +497,9 @@ function ResendButton({ invite }: { invite: Invite }) {
512497 prevSubmitting . current = isSubmitting ;
513498 } , [ isSubmitting ] ) ;
514499
500+ const cooldownActive = cooldown > 0 ;
515501 useEffect ( ( ) => {
516- if ( cooldown <= 0 ) {
517- clearInterval ( intervalRef . current ) ;
518- return ;
519- }
502+ if ( ! cooldownActive ) return ;
520503
521504 intervalRef . current = setInterval ( ( ) => {
522505 setCooldown ( ( c ) => {
@@ -529,7 +512,7 @@ function ResendButton({ invite }: { invite: Invite }) {
529512 } , 1000 ) ;
530513
531514 return ( ) => clearInterval ( intervalRef . current ) ;
532- } , [ cooldown > 0 ] ) ; // only re-run when transitioning between active/inactive
515+ } , [ cooldownActive ] ) ;
533516
534517 const isDisabled = isSubmitting || cooldown > 0 ;
535518
@@ -580,7 +563,6 @@ export function PurchaseSeatsModal({
580563 maxQuota,
581564 planSeatLimit,
582565 triggerButton,
583- redirectTo,
584566} : {
585567 seatPricing : {
586568 stepSize : number ;
@@ -591,35 +573,29 @@ export function PurchaseSeatsModal({
591573 maxQuota : number ;
592574 planSeatLimit : number ;
593575 triggerButton ?: React . ReactNode ;
594- redirectTo ?: string ;
595576} ) {
596- const lastSubmission = useActionData ( ) ;
577+ const fetcher = useFetcher ( ) ;
597578 const organization = useOrganization ( ) ;
598579 const [ form , { amount } ] = useForm ( {
599580 id : "purchase-seats" ,
600- lastSubmission : lastSubmission as any ,
581+ lastSubmission : fetcher . data as any ,
601582 onValidate ( { formData } ) {
602583 return parse ( formData , { schema : PurchaseSchema } ) ;
603584 } ,
604585 shouldRevalidate : "onSubmit" ,
605586 } ) ;
606587
607588 const [ amountValue , setAmountValue ] = useState ( extraSeats ) ;
608- const navigation = useNavigation ( ) ;
609- const isLoading = navigation . state !== "idle" && navigation . formMethod === "POST" ;
589+ const isLoading = fetcher . state !== "idle" ;
610590
611- const [ searchParams , setSearchParams ] = useSearchParams ( ) ;
612591 const [ open , setOpen ] = useState ( false ) ;
592+ const prevFetcherState = useRef ( fetcher . state ) ;
613593 useEffect ( ( ) => {
614- const success = searchParams . get ( "purchaseSuccess" ) ;
615- if ( success ) {
594+ if ( prevFetcherState . current !== "idle" && fetcher . state === "idle" && ! fetcher . data ) {
616595 setOpen ( false ) ;
617- setSearchParams ( ( s ) => {
618- s . delete ( "purchaseSuccess" ) ;
619- return s ;
620- } ) ;
621596 }
622- } , [ searchParams . get ( "purchaseSuccess" ) ] ) ;
597+ prevFetcherState . current = fetcher . state ;
598+ } , [ fetcher . state , fetcher . data ] ) ;
623599
624600 const state = updateSeatState ( {
625601 value : amountValue ,
@@ -645,9 +621,8 @@ export function PurchaseSeatsModal({
645621 </ DialogTrigger >
646622 < DialogContent >
647623 < DialogHeader > { title } </ DialogHeader >
648- < Form method = "post" action = { organizationTeamPath ( organization ) } { ...form . props } >
624+ < fetcher . Form method = "post" action = { organizationTeamPath ( organization ) } { ...form . props } >
649625 < input type = "hidden" name = "_formType" value = "purchase-seats" />
650- { redirectTo && < input type = "hidden" name = "redirectTo" value = { redirectTo } /> }
651626 < div className = "flex flex-col gap-4 pt-2" >
652627 < div className = "flex flex-col gap-1" >
653628 < Paragraph variant = "small/bright" >
@@ -803,7 +778,7 @@ export function PurchaseSeatsModal({
803778 </ DialogClose >
804779 }
805780 />
806- </ Form >
781+ </ fetcher . Form >
807782 </ DialogContent >
808783 </ Dialog >
809784 ) ;
0 commit comments