import { SegmentQualifier } from "@/components/auth/segment-qualifier"; import { useAuth } from "@/hooks/use-auth"; import { capture, setPeopleProperties } from "@/lib/posthog"; import { Button } from "@sase/ui"; import { useLocation } from "@tanstack/react-router"; import { useEffect, useState } from "react"; /** * Universal post-auth segment gate. * * The register page gates segment selection behind the form, but that only * covers email + register-page-OAuth signups. A user who starts Google OAuth * from the LOGIN page (login.tsx) gets an account created server-side and lands * on the dashboard with no segment ever chosen — this is the bulk of the ~41% * of signups with a null segment. Segment isn't persisted server-side (only * localStorage + a PostHog person property), so we enforce it here at the one * chokepoint every authenticated user passes through: the dashboard. * * Behaviour (product decision 2026-07-27): * - New users (account created in the last few minutes → the login-OAuth gap): * MANDATORY, non-dismissible modal. * - Existing segment-less users (the ~538 backfill): SOFT, dismissible with a * cooldown so we re-ask later without nagging or blocking active/paying users. * * This is a baseline fix shipped to everyone — NOT the `funnel-bucket` A/B * experiment (that gates the tailored value/upgrade experience separately). */ const SEGMENT_KEY = "sase-b2b-segment"; const DISMISS_UNTIL_KEY = "sase-b2b-segment-prompt-until"; // Account younger than this ⇒ treat as a fresh signup (mandatory). Generous so // the signup → first dashboard load always counts, even with a slow OAuth hop. const NEW_USER_WINDOW_MS = 15 * 60 * 1000; // How long a "Daha sonra" dismissal silences the soft prompt for existing users. const DISMISS_COOLDOWN_MS = 7 * 24 * 60 * 60 * 1000; function readSegment(): string | null { try { return localStorage.getItem(SEGMENT_KEY); } catch { return null; } } export function SegmentGate() { const { user, isLoading } = useAuth(); const location = useLocation(); const [open, setOpen] = useState(false); const [mandatory, setMandatory] = useState(false); useEffect(() => { // Only inside the app, only once the session has resolved to a real user. if (isLoading || !user) { setOpen(false); return; } if (!location.pathname.startsWith("/dashboard")) { setOpen(false); return; } // Already segmented — nothing to do. (Register writes this pre-redirect, so // email + register-OAuth signups never see the gate.) if (readSegment()) { setOpen(false); return; } const createdAt = user.createdAt ? new Date(user.createdAt).getTime() : 0; const isNew = createdAt > 0 && Date.now() - createdAt < NEW_USER_WINDOW_MS; if (isNew) { setMandatory(true); setOpen(true); return; } // Existing segment-less user: honour the soft-dismiss cooldown. let dismissedUntil = 0; try { dismissedUntil = Number(localStorage.getItem(DISMISS_UNTIL_KEY)) || 0; } catch { dismissedUntil = 0; } if (Date.now() < dismissedUntil) { setOpen(false); return; } setMandatory(false); setOpen(true); }, [user, isLoading, location.pathname]); if (!open) return null; function handleSelect(seg: string) { try { localStorage.setItem(SEGMENT_KEY, seg); } catch { // localStorage unavailable — still fire analytics below. } // `source` distinguishes the login-OAuth gap fill from the backfill so we can // measure each in PostHog; mirrors register.tsx's signup_segment_selected. capture("signup_segment_selected", { segment: seg, source: mandatory ? "post_signup_gate" : "backfill_gate", }); setPeopleProperties({ b2b_segment: seg }); setOpen(false); } function handleDismiss() { try { localStorage.setItem(DISMISS_UNTIL_KEY, String(Date.now() + DISMISS_COOLDOWN_MS)); } catch { // best-effort } capture("signup_segment_prompt_dismissed"); setOpen(false); } return (
İşletmeni seç — sana uygun sınırsız şase sorgulama erişimini açalım.