/** * GetStartedView - Main view for the get-started flow * * Supports multiple handoff scenarios from eligibility check: * * 1. Verified handoff (?verified=true): * - User already completed OTP on eligibility page * - SessionStorage has: sessionToken, accountStatus, prefill, email * - Skip directly to complete-account step * * 2. Unverified handoff (?handoff=true): * - User came from eligibility check success page or SF email * - Email is pre-filled but NOT verified yet * - User must complete OTP verification * - SessionStorage may have: handoff-token for prefill data */ "use client"; import { useState, useCallback, useEffect } from "react"; import { useSearchParams } from "next/navigation"; import { AuthLayout } from "@/components/templates/AuthLayout"; import { GetStartedForm } from "../components"; import { useGetStartedStore, type GetStartedStep, type GetStartedAddress, } from "../stores/get-started.store"; import type { AccountStatus, VerifyCodeResponse } from "@customer-portal/domain/get-started"; // Session data staleness threshold (5 minutes) const SESSION_STALE_THRESHOLD_MS = 5 * 60 * 1000; export function GetStartedView() { const searchParams = useSearchParams(); const { updateFormData, goToStep, setHandoffToken, setSessionToken, setAccountStatus, setPrefill, } = useGetStartedStore(); const [meta, setMeta] = useState({ title: "Get Started", subtitle: "Enter your email to begin", }); const [initialized, setInitialized] = useState(false); // Helper to clear all get-started sessionStorage items const clearGetStartedSessionStorage = () => { sessionStorage.removeItem("get-started-session-token"); sessionStorage.removeItem("get-started-account-status"); sessionStorage.removeItem("get-started-prefill"); sessionStorage.removeItem("get-started-email"); sessionStorage.removeItem("get-started-timestamp"); sessionStorage.removeItem("get-started-handoff-token"); }; // Check for handoff from eligibility check on mount useEffect(() => { if (initialized) return; // Check for verified handoff (user already completed OTP on eligibility page) const verifiedParam = searchParams.get("verified"); if (verifiedParam === "true") { // Read all session data at once const storedSessionToken = sessionStorage.getItem("get-started-session-token"); const storedAccountStatus = sessionStorage.getItem("get-started-account-status"); const storedPrefillRaw = sessionStorage.getItem("get-started-prefill"); const storedEmail = sessionStorage.getItem("get-started-email"); const storedTimestamp = sessionStorage.getItem("get-started-timestamp"); // Validate timestamp to prevent stale data const isStale = !storedTimestamp || Date.now() - Number.parseInt(storedTimestamp, 10) > SESSION_STALE_THRESHOLD_MS; // Clear sessionStorage immediately after reading clearGetStartedSessionStorage(); if (storedSessionToken && !isStale) { // Parse prefill data let prefill: VerifyCodeResponse["prefill"] | null = null; if (storedPrefillRaw) { try { prefill = JSON.parse(storedPrefillRaw); } catch { // Ignore parse errors } } // Set session data in store setSessionToken(storedSessionToken); if (storedAccountStatus) { setAccountStatus(storedAccountStatus as AccountStatus); } if (prefill) { setPrefill(prefill); // Also update form data with prefill updateFormData({ email: storedEmail || prefill.email || "", firstName: prefill.firstName || "", lastName: prefill.lastName || "", phone: prefill.phone || "", address: (prefill.address as GetStartedAddress) || {}, }); } else if (storedEmail) { updateFormData({ email: storedEmail }); } // Skip directly to complete-account (email already verified) goToStep("complete-account"); setInitialized(true); return; } // If stale or no token, fall through to normal flow } // Check for unverified handoff (from success page CTA or SF email) const emailParam = searchParams.get("email"); const handoffParam = searchParams.get("handoff"); const storedHandoffToken = sessionStorage.getItem("get-started-handoff-token"); const storedEmail = sessionStorage.getItem("get-started-email"); // Clear handoff-related sessionStorage after reading sessionStorage.removeItem("get-started-handoff-token"); sessionStorage.removeItem("get-started-email"); const email = emailParam || storedEmail; const isHandoff = handoffParam === "true" || !!storedHandoffToken; if (email && isHandoff) { // User came from eligibility check - email is NOT verified yet // Pre-fill email and let user verify via OTP updateFormData({ email }); // Store handoff token if available - will be used during OTP verification if (storedHandoffToken) { setHandoffToken(storedHandoffToken); } // Stay at email step - user needs to verify their email // Don't call goToStep - let the form start at its default step // The email is pre-filled so user just clicks "Send Code" } else if (email) { // Just email param (from SF email link without handoff) updateFormData({ email }); } setInitialized(true); }, [ initialized, searchParams, updateFormData, setHandoffToken, goToStep, setSessionToken, setAccountStatus, setPrefill, ]); const handleStepChange = useCallback( (_step: GetStartedStep, stepMeta: { title: string; subtitle: string }) => { setMeta(stepMeta); }, [] ); return ( ); } export default GetStartedView;