diff --git a/apps/web/src/components/trial-urgency-banner.tsx b/apps/web/src/components/trial-urgency-banner.tsx index 5aa4680..bc32cd8 100644 --- a/apps/web/src/components/trial-urgency-banner.tsx +++ b/apps/web/src/components/trial-urgency-banner.tsx @@ -36,71 +36,60 @@ export function TrialUrgencyBanner() { }); const subscription = subData?.subscription; + const endDate = subscription?.endDate; + const days = + subscription?.status === "trial" && endDate + ? Math.max( + 0, + Math.ceil((new Date(endDate).getTime() - Date.now()) / (1000 * 60 * 60 * 24)), + ) + : null; + const visible = !isLoading && days !== null && days <= 7; + const dismissKey = endDate ? getDismissKey(endDate) : null; - // Hydrate dismissal state from localStorage on mount useEffect(() => { setMounted(true); }, []); - // Guard: not loaded yet, or no subscription - if (isLoading || !subscription) return null; - // Guard: not trial or no end date - if (subscription.status !== "trial" || !subscription.endDate) return null; - - const days = Math.max( - 0, - Math.ceil((new Date(subscription.endDate).getTime() - Date.now()) / (1000 * 60 * 60 * 24)), - ); - - // Only show when 7 or fewer days remain - if (days > 7) return null; - - const dismissKey = getDismissKey(subscription.endDate); - - // Check localStorage on mount and when endDate changes useEffect(() => { - if (!mounted) return; + if (!mounted || !dismissKey) return; const stored = localStorage.getItem(dismissKey); if (stored === "true") { setDismissed(true); } }, [mounted, dismissKey]); - // Fire trial_urgency_banner_viewed once when the banner becomes visible useEffect(() => { - if (!subscription || subscription.status !== "trial" || !subscription.endDate) return; - if (days > 7) return; - if (dismissed) return; - if (viewedRef.current) return; - + if (!visible || dismissed || viewedRef.current || days === null) return; viewedRef.current = true; capture("trial_urgency_banner_viewed", { days_remaining: days, - plan_name: subscription.plan?.name, - billing_period: subscription.billingPeriod, + plan_name: subscription?.plan?.name, + billing_period: subscription?.billingPeriod, }); - }, [subscription, days, dismissed]); + }, [visible, dismissed, days, subscription?.plan?.name, subscription?.billingPeriod]); - if (dismissed) return null; + const handleCTAClick = useCallback(() => { + if (days === null) return; + capture("trial_urgency_banner_cta_clicked", { + days_remaining: days, + plan_name: subscription?.plan?.name, + billing_period: subscription?.billingPeriod, + }); + }, [days, subscription?.plan?.name, subscription?.billingPeriod]); + + if (!visible || dismissed || days === null || !dismissKey) return null; const handleDismiss = () => { capture("trial_urgency_banner_dismissed", { days_remaining: days, - plan_name: subscription.plan?.name, - billing_period: subscription.billingPeriod, + plan_name: subscription?.plan?.name, + billing_period: subscription?.billingPeriod, }); localStorage.setItem(dismissKey, "true"); setDismissed(true); }; - const handleCTAClick = useCallback(() => { - capture("trial_urgency_banner_cta_clicked", { - days_remaining: days, - plan_name: subscription.plan?.name, - billing_period: subscription.billingPeriod, - }); - }, [days, subscription.plan?.name, subscription.billingPeriod]); - return (