Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -59,48 +59,73 @@ function Story() {
submit={statusStub(200)}
onCouponApplied={undefined}
prefillPromoCode="XYZ"
isPaymentSetup={true}
onAddPayment={() => {}}
/>
</BadgeContainer>

<BadgeContainer label="No Valid Payment setup">
<ApplyCouponCardUI
submit={statusStub(200)}
onCouponApplied={undefined}
isPaymentSetup={false}
onAddPayment={() => {
console.log("show payment modal");
}}
/>
</BadgeContainer>

<BadgeContainer label="Success - 200">
<ApplyCouponCardUI
submit={statusStub(200)}
onCouponApplied={undefined}
isPaymentSetup={true}
onAddPayment={() => {}}
/>
</BadgeContainer>

<BadgeContainer label="Invalid - 400">
<ApplyCouponCardUI
submit={statusStub(400)}
onCouponApplied={undefined}
isPaymentSetup={true}
onAddPayment={() => {}}
/>
</BadgeContainer>

<BadgeContainer label="Not Authorized - 401">
<ApplyCouponCardUI
submit={statusStub(401)}
onCouponApplied={undefined}
isPaymentSetup={true}
onAddPayment={() => {}}
/>
</BadgeContainer>

<BadgeContainer label="Already applied - 409">
<ApplyCouponCardUI
submit={statusStub(409)}
onCouponApplied={undefined}
isPaymentSetup={true}
onAddPayment={() => {}}
/>
</BadgeContainer>

<BadgeContainer label="Rate Limited - 429">
<ApplyCouponCardUI
submit={statusStub(429)}
onCouponApplied={undefined}
isPaymentSetup={true}
onAddPayment={() => {}}
/>
</BadgeContainer>

<BadgeContainer label="Other - 500">
<ApplyCouponCardUI
submit={statusStub(500)}
onCouponApplied={undefined}
isPaymentSetup={true}
onAddPayment={() => {}}
/>
</BadgeContainer>
<Toaster richColors />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand All @@ -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",
Expand Down Expand Up @@ -86,6 +90,8 @@ export function ApplyCouponCardUI(props: {
onCouponApplied: ((data: ActiveCouponResponse) => void) | undefined;
prefillPromoCode?: string;
scrollIntoView?: boolean;
isPaymentSetup: boolean;
onAddPayment: () => void;
}) {
const containerRef = useRef<HTMLFormElement | null>(null);
const form = useForm<z.infer<typeof couponFormSchema>>({
Expand Down Expand Up @@ -113,6 +119,11 @@ export function ApplyCouponCardUI(props: {
});

async function onSubmit(values: z.infer<typeof couponFormSchema>) {
if (!props.isPaymentSetup) {
props.onAddPayment();
return;
}

try {
const res = await applyCoupon.mutateAsync(values.promoCode);
switch (res.status) {
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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<
| {
Expand Down Expand Up @@ -325,6 +344,8 @@ export function CouponSection(props: { teamId: string | undefined }) {
setOptimisticCouponData(undefined);
});
}}
isPaymentSetup={props.isPaymentSetup}
onAddPayment={props.onAddPayment}
/>
</Suspense>
);
Expand Down
38 changes: 20 additions & 18 deletions apps/dashboard/src/components/settings/Account/Billing/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -32,11 +32,7 @@ export const Billing: React.FC<BillingProps> = ({ 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<AccountPlan | undefined>();
const trackEvent = useTrack();
Expand Down Expand Up @@ -114,7 +110,7 @@ export const Billing: React.FC<BillingProps> = ({ account, teamId }) => {

if (!validPayment) {
setSelectedPlan(plan);
onPaymentMethodOpen();
setIsPaymentModalOpen(true);
return;
}
// downgrade from Growth to Free
Expand All @@ -127,7 +123,7 @@ export const Billing: React.FC<BillingProps> = ({ account, teamId }) => {

const handlePaymentAdded = () => {
setPaymentMethodSaving(true);
onPaymentMethodClose();
setIsPaymentModalOpen(false);
};

const handleDowngradeAlertClose = () => {
Expand Down Expand Up @@ -186,12 +182,12 @@ export const Billing: React.FC<BillingProps> = ({ 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
Expand Down Expand Up @@ -245,16 +241,16 @@ export const Billing: React.FC<BillingProps> = ({ account, teamId }) => {

return (
<Flex flexDir="column" gap={8}>
<OnboardingModal isOpen={isPaymentModalOpen}>
<LazyOnboardingBilling
onSave={handlePaymentAdded}
onCancel={() => setIsPaymentModalOpen(false)}
/>
</OnboardingModal>

{showSteps ? (
<>
<StepsCard title="Get started with billing" steps={steps} />

<OnboardingModal isOpen={isPaymentMethodOpen}>
<LazyOnboardingBilling
onSave={handlePaymentAdded}
onCancel={onPaymentMethodClose}
/>
</OnboardingModal>
</>
) : (
<>
Expand Down Expand Up @@ -303,7 +299,13 @@ export const Billing: React.FC<BillingProps> = ({ account, teamId }) => {
/>
)}

<CouponSection teamId={teamId} />
<CouponSection
teamId={teamId}
onAddPayment={() => {
setIsPaymentModalOpen(true);
}}
isPaymentSetup={validPayment}
/>
</Flex>
);
};
Loading