diff --git a/apps/dashboard/src/components/settings/Account/Billing/ApplyCouponCard.stories.tsx b/apps/dashboard/src/components/settings/Account/Billing/ApplyCouponCard.stories.tsx index 9bf79a07d61..4b5fdb9dd3d 100644 --- a/apps/dashboard/src/components/settings/Account/Billing/ApplyCouponCard.stories.tsx +++ b/apps/dashboard/src/components/settings/Account/Billing/ApplyCouponCard.stories.tsx @@ -59,6 +59,19 @@ function Story() { submit={statusStub(200)} onCouponApplied={undefined} prefillPromoCode="XYZ" + isPaymentSetup={true} + onAddPayment={() => {}} + /> + + + + { + console.log("show payment modal"); + }} /> @@ -66,6 +79,8 @@ function Story() { {}} /> @@ -73,6 +88,8 @@ function Story() { {}} /> @@ -80,6 +97,8 @@ function Story() { {}} /> @@ -87,6 +106,8 @@ function Story() { {}} /> @@ -94,6 +115,8 @@ function Story() { {}} /> @@ -101,6 +124,8 @@ function Story() { {}} /> diff --git a/apps/dashboard/src/components/settings/Account/Billing/CouponCard.tsx b/apps/dashboard/src/components/settings/Account/Billing/CouponCard.tsx index a1f718171b9..44fb4f82a74 100644 --- a/apps/dashboard/src/components/settings/Account/Billing/CouponCard.tsx +++ b/apps/dashboard/src/components/settings/Account/Billing/CouponCard.tsx @@ -37,6 +37,8 @@ export type ActiveCouponResponse = { function ApplyCouponCard(props: { teamId: string | undefined; onCouponApplied: (data: ActiveCouponResponse) => void; + isPaymentSetup: boolean; + onAddPayment: () => void; }) { const searchParams = useSearchParams(); const couponCode = searchParams?.get("coupon"); @@ -45,6 +47,8 @@ function ApplyCouponCard(props: { onCouponApplied={props.onCouponApplied} prefillPromoCode={couponCode || undefined} scrollIntoView={!!couponCode} + isPaymentSetup={props.isPaymentSetup} + onAddPayment={props.onAddPayment} submit={async (promoCode: string) => { const res = await fetch("/api/server-proxy/api/v1/coupons/redeem", { method: "POST", @@ -86,6 +90,8 @@ export function ApplyCouponCardUI(props: { onCouponApplied: ((data: ActiveCouponResponse) => void) | undefined; prefillPromoCode?: string; scrollIntoView?: boolean; + isPaymentSetup: boolean; + onAddPayment: () => void; }) { const containerRef = useRef(null); const form = useForm>({ @@ -113,6 +119,11 @@ export function ApplyCouponCardUI(props: { }); async function onSubmit(values: z.infer) { + if (!props.isPaymentSetup) { + props.onAddPayment(); + return; + } + try { const res = await applyCoupon.mutateAsync(values.promoCode); switch (res.status) { @@ -161,7 +172,11 @@ export function ApplyCouponCardUI(props: { description: "Enter your coupon code to apply discounts or free trials on thirdweb products", }} - bottomText="" + bottomText={ + props.isPaymentSetup + ? "" + : "A valid payment method must be added to apply a coupon" + } saveButton={{ variant: "default", disabled: false, @@ -238,7 +253,11 @@ export function CouponDetailsCardUI(props: { ); } -export function CouponSection(props: { teamId: string | undefined }) { +export function CouponSection(props: { + teamId: string | undefined; + isPaymentSetup: boolean; + onAddPayment: () => void; +}) { const loggedInUser = useLoggedInUser(); const [optimisticCouponData, setOptimisticCouponData] = useState< | { @@ -325,6 +344,8 @@ export function CouponSection(props: { teamId: string | undefined }) { setOptimisticCouponData(undefined); }); }} + isPaymentSetup={props.isPaymentSetup} + onAddPayment={props.onAddPayment} /> ); diff --git a/apps/dashboard/src/components/settings/Account/Billing/index.tsx b/apps/dashboard/src/components/settings/Account/Billing/index.tsx index e9012561520..a0f0240d2fb 100644 --- a/apps/dashboard/src/components/settings/Account/Billing/index.tsx +++ b/apps/dashboard/src/components/settings/Account/Billing/index.tsx @@ -5,7 +5,7 @@ import { AccountStatus, useUpdateAccountPlan, } from "@3rdweb-sdk/react/hooks/useApi"; -import { Flex, Icon, useDisclosure } from "@chakra-ui/react"; +import { Flex, Icon } from "@chakra-ui/react"; import { StepsCard } from "components/dashboard/StepsCard"; import { OnboardingModal } from "components/onboarding/Modal"; import { AccountForm } from "components/settings/Account/AccountForm"; @@ -32,11 +32,7 @@ export const Billing: React.FC = ({ account, teamId }) => { const updatePlanMutation = useUpdateAccountPlan( account?.plan === AccountPlan.Free, ); - const { - isOpen: isPaymentMethodOpen, - onOpen: onPaymentMethodOpen, - onClose: onPaymentMethodClose, - } = useDisclosure(); + const [isPaymentModalOpen, setIsPaymentModalOpen] = useState(false); const [paymentMethodSaving, setPaymentMethodSaving] = useState(false); const [selectedPlan, setSelectedPlan] = useState(); const trackEvent = useTrack(); @@ -114,7 +110,7 @@ export const Billing: React.FC = ({ account, teamId }) => { if (!validPayment) { setSelectedPlan(plan); - onPaymentMethodOpen(); + setIsPaymentModalOpen(true); return; } // downgrade from Growth to Free @@ -127,7 +123,7 @@ export const Billing: React.FC = ({ account, teamId }) => { const handlePaymentAdded = () => { setPaymentMethodSaving(true); - onPaymentMethodClose(); + setIsPaymentModalOpen(false); }; const handleDowngradeAlertClose = () => { @@ -186,12 +182,12 @@ export const Billing: React.FC = ({ account, teamId }) => { account={account} loading={paymentMethodSaving} loadingText="Verifying payment method" - onClick={onPaymentMethodOpen} + onClick={() => setIsPaymentModalOpen(true)} /> ), }, ]; - }, [account, onPaymentMethodOpen, paymentMethodSaving, stepsCompleted]); + }, [account, paymentMethodSaving, stepsCompleted]); // FIXME: this entire flow needs to be re-worked // eslint-disable-next-line no-restricted-syntax @@ -245,16 +241,16 @@ export const Billing: React.FC = ({ account, teamId }) => { return ( + + setIsPaymentModalOpen(false)} + /> + + {showSteps ? ( <> - - - - ) : ( <> @@ -303,7 +299,13 @@ export const Billing: React.FC = ({ account, teamId }) => { /> )} - + { + setIsPaymentModalOpen(true); + }} + isPaymentSetup={validPayment} + /> ); };