From 83f915d725497974dfadb308e6958aae2709309a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vi=CC=81ctor=20Falco=CC=81n?= Date: Sat, 18 Jul 2026 08:35:22 +0200 Subject: [PATCH] Add a support button to the paywall when it can't be skipped When the user has no free-plan escape (canUseFreePlan is false) the paywall previously offered no way out at all. Add a subtle help/support affordance in the same slots the free-plan escape uses: bottom button on desktop, top-right corner on mobile. It fades in slightly later than the free-plan escape (7s vs 5s) and is deliberately low-key (muted ghost, no pill) so it doesn't compete with the subscribe CTA. It opens the existing SupportDialog (join the community / email support), the same one behind the user-menu "Support" entry. The two escapes are mutually exclusive per page load, so the separate free-button timer collapses into one escapeVisible timer whose delay depends on canUseFreePlan. --- resources/js/pages/subscription/paywall.tsx | 60 +++++++++++++++++---- 1 file changed, 50 insertions(+), 10 deletions(-) diff --git a/resources/js/pages/subscription/paywall.tsx b/resources/js/pages/subscription/paywall.tsx index 92effc54..3e9d8496 100644 --- a/resources/js/pages/subscription/paywall.tsx +++ b/resources/js/pages/subscription/paywall.tsx @@ -1,3 +1,4 @@ +import { SupportDialog } from '@/components/support-dialog'; import { Button } from '@/components/ui/button'; import { Card, CardContent } from '@/components/ui/card'; import { useCountUp } from '@/hooks/use-count-up'; @@ -15,6 +16,7 @@ import { CheckIcon, FolderIcon, LandmarkIcon, + LifeBuoy, LockIcon, PiggyBankIcon, ReceiptIcon, @@ -363,8 +365,10 @@ function PricingSection({ canManageConnectionsForFreePlan: boolean; offer: ExperimentOffer; }) { + const { auth } = usePage().props; const [selectedPlan, setSelectedPlan] = useState(defaultPlan); - const [freeButtonVisible, setFreeButtonVisible] = useState(false); + const [escapeVisible, setEscapeVisible] = useState(false); + const [supportOpen, setSupportOpen] = useState(false); const locale = useLocale(); const selectedPlanData = planEntries.find( @@ -376,25 +380,23 @@ function PricingSection({ : ''; useEffect(() => { - if (!canUseFreePlan) { - return; - } - const timer = setTimeout(() => setFreeButtonVisible(true), 5000); + const delay = canUseFreePlan ? 5000 : 7000; + const timer = setTimeout(() => setEscapeVisible(true), delay); return () => clearTimeout(timer); }, [canUseFreePlan]); const continueForFree = () => router.visit(dashboard().url); - const freeRevealClasses = freeButtonVisible + const revealClasses = escapeVisible ? 'opacity-100' : 'pointer-events-none opacity-0'; return (
- {canUseFreePlan && ( + {canUseFreePlan ? (
+ ) : ( +
+ +
)} {selectedPlanData && ( @@ -459,11 +476,11 @@ function PricingSection({
)} - {canUseFreePlan && ( + {canUseFreePlan ? (
+ ) : ( +
+ +
)} + + ); }