Fix trial upsell modal logic

This commit is contained in:
Arthur Belleville 2026-03-08 21:59:38 +01:00
parent 76f497d2c8
commit 8fc463313d
No known key found for this signature in database
2 changed files with 89 additions and 6 deletions

View file

@ -0,0 +1,59 @@
import { describe, expect, it } from "vitest";
import { shouldShowTrialUpsell } from "./TrialUpsellModal";
describe("shouldShowTrialUpsell", () => {
it("does not show when trial has more than 7 days remaining", () => {
expect(
shouldShowTrialUpsell({
isTrialExpired: false,
activeSubscriptionPlan: null,
isTemporaryUser: false,
daysRemaining: 14,
})
).toBe(false);
});
it("shows when trial is within reminder window and user has no subscription", () => {
expect(
shouldShowTrialUpsell({
isTrialExpired: false,
activeSubscriptionPlan: null,
isTemporaryUser: false,
daysRemaining: 3,
})
).toBe(true);
});
it("does not show when trial is expired", () => {
expect(
shouldShowTrialUpsell({
isTrialExpired: true,
activeSubscriptionPlan: null,
isTemporaryUser: false,
daysRemaining: null,
})
).toBe(false);
});
it("does not show when organization already has a paid subscription", () => {
expect(
shouldShowTrialUpsell({
isTrialExpired: false,
activeSubscriptionPlan: "team",
isTemporaryUser: false,
daysRemaining: 2,
})
).toBe(false);
});
it("does not show for temporary users", () => {
expect(
shouldShowTrialUpsell({
isTrialExpired: false,
activeSubscriptionPlan: null,
isTemporaryUser: true,
daysRemaining: 2,
})
).toBe(false);
});
});

View file

@ -15,9 +15,29 @@ import { useMaybeUser } from "../providers/UserStoreProvider";
const MODAL_INTERVAL_MS = 15 * 60 * 1000; // 15 minutes
const LAST_SHOWN_KEY = "trial-upsell-modal-last-shown";
const TRIAL_UPSELL_REMINDER_DAYS = 7;
export const shouldShowTrialUpsell = (input: {
isTrialExpired: boolean;
activeSubscriptionPlan: "solo" | "team" | "annual" | null;
isTemporaryUser: boolean;
daysRemaining: number | null;
}) => {
const { isTrialExpired, activeSubscriptionPlan, isTemporaryUser, daysRemaining } = input;
if (isTrialExpired || activeSubscriptionPlan || isTemporaryUser) {
return false;
}
if (daysRemaining === null) {
return false;
}
return daysRemaining > 0 && daysRemaining <= TRIAL_UPSELL_REMINDER_DAYS;
};
/**
* Auto-opening modal that shows every 15 minutes to remind users about trial expiration.
* Auto-opening modal that reminds users near trial expiration.
*/
export function TrialUpsellModal() {
const [isOpen, setIsOpen] = useState(false);
@ -26,11 +46,15 @@ export function TrialUpsellModal() {
const user = useMaybeUser();
const { mutate: createCheckout, isPending: checkoutPending } = useCreateCheckoutSession();
const shouldShowModal =
!!organizationData &&
!organizationData.is_trial_expired &&
!organizationData.active_subscription_plan &&
!user?.is_temporary;
const shouldShowModal = Boolean(
organizationData &&
shouldShowTrialUpsell({
isTrialExpired: organizationData.is_trial_expired,
activeSubscriptionPlan: organizationData.active_subscription_plan,
isTemporaryUser: Boolean(user?.is_temporary),
daysRemaining,
})
);
const requiredPlan = organizationData?.required_plan ?? "solo";
const checkoutPlan = requiredPlan === "team" ? "team" : "solo";