@@ -47,6 +47,7 @@ import { type VercelOnboardingData } from "~/presenters/v3/VercelSettingsPresent
4747import { vercelAppInstallPath , v3ProjectSettingsPath , githubAppInstallPath , vercelResourcePath } from "~/utils/pathBuilder" ;
4848import type { loader } from "~/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel" ;
4949import { useEffect , useState , useCallback , useRef } from "react" ;
50+ import { usePostHogTracking } from "~/hooks/usePostHog" ;
5051
5152function safeRedirectUrl ( url : string ) : string | null {
5253 try {
@@ -114,6 +115,7 @@ export function VercelOnboardingModal({
114115 nextUrl ?: string ;
115116 onDataReload ?: ( vercelStagingEnvironment ?: string ) => void ;
116117} ) {
118+ const { capture, startSessionRecording } = usePostHogTracking ( ) ;
117119 const navigation = useNavigation ( ) ;
118120 const fetcher = useTypedFetcher < typeof loader > ( ) ;
119121 const envMappingFetcher = useFetcher ( ) ;
@@ -172,6 +174,31 @@ export function VercelOnboardingModal({
172174 prevIsOpenRef . current = isOpen ;
173175 } , [ isOpen , state , computeInitialState ] ) ;
174176
177+ const trackOnboarding = useCallback (
178+ ( eventName : string , extraProperties ?: Record < string , unknown > ) => {
179+ capture ( eventName , {
180+ origin : fromMarketplaceContext ? "marketplace" : "dashboard" ,
181+ step : state ,
182+ organization_slug : organizationSlug ,
183+ project_slug : projectSlug ,
184+ ...extraProperties ,
185+ } ) ;
186+ } ,
187+ [ capture , fromMarketplaceContext , state , organizationSlug , projectSlug ]
188+ ) ;
189+
190+ const hasTrackedStartRef = useRef ( false ) ;
191+ useEffect ( ( ) => {
192+ if ( isOpen && state === "project-selection" && ! hasTrackedStartRef . current ) {
193+ hasTrackedStartRef . current = true ;
194+ startSessionRecording ( ) ;
195+ trackOnboarding ( "vercel onboarding started" ) ;
196+ }
197+ if ( ! isOpen ) {
198+ hasTrackedStartRef . current = false ;
199+ }
200+ } , [ isOpen , state , trackOnboarding , startSessionRecording ] ) ;
201+
175202 const [ selectedVercelProject , setSelectedVercelProject ] = useState < {
176203 id : string ;
177204 name : string ;
@@ -337,14 +364,17 @@ export function VercelOnboardingModal({
337364
338365 useEffect ( ( ) => {
339366 if ( state === "project-selection" && fetcher . data && "success" in fetcher . data && fetcher . data . success && fetcher . state === "idle" ) {
367+ trackOnboarding ( "vercel onboarding project selected" , {
368+ vercel_project_name : selectedVercelProject ?. name ,
369+ } ) ;
340370 setState ( "loading-env-mapping" ) ;
341371 if ( onDataReload ) {
342372 onDataReload ( ) ;
343373 }
344374 } else if ( fetcher . data && "error" in fetcher . data && typeof fetcher . data . error === "string" ) {
345375 setProjectSelectionError ( fetcher . data . error ) ;
346376 }
347- } , [ state , fetcher . data , fetcher . state , onDataReload ] ) ;
377+ } , [ state , fetcher . data , fetcher . state , onDataReload , trackOnboarding , selectedVercelProject ?. name ] ) ;
348378
349379 // For marketplace origin, skip env-mapping step
350380 useEffect ( ( ) => {
@@ -437,6 +467,7 @@ export function VercelOnboardingModal({
437467 } , [ selectedVercelProject , fetcher , actionUrl ] ) ;
438468
439469 const handleSkipOnboarding = useCallback ( ( ) => {
470+ trackOnboarding ( "vercel onboarding abandoned" ) ;
440471 onClose ( ) ;
441472
442473 if ( fromMarketplaceContext ) {
@@ -449,14 +480,23 @@ export function VercelOnboardingModal({
449480 method : "post" ,
450481 action : actionUrl ,
451482 } ) ;
452- } , [ actionUrl , fetcher , onClose , nextUrl , fromMarketplaceContext ] ) ;
483+ } , [ actionUrl , fetcher , onClose , nextUrl , fromMarketplaceContext , trackOnboarding ] ) ;
453484
454485 const handleSkipEnvMapping = useCallback ( ( ) => {
486+ trackOnboarding ( "vercel onboarding env mapping completed" , {
487+ skipped : true ,
488+ staging_environment : null ,
489+ } ) ;
455490 setVercelStagingEnvironment ( null ) ;
456491 setState ( "loading-env-vars" ) ;
457- } , [ ] ) ;
492+ } , [ trackOnboarding ] ) ;
458493
459494 const handleUpdateEnvMapping = useCallback ( ( ) => {
495+ trackOnboarding ( "vercel onboarding env mapping completed" , {
496+ skipped : false ,
497+ staging_environment : vercelStagingEnvironment ?. displayName ?? null ,
498+ } ) ;
499+
460500 if ( ! vercelStagingEnvironment ) {
461501 setState ( "loading-env-vars" ) ;
462502 return ;
@@ -471,9 +511,11 @@ export function VercelOnboardingModal({
471511 action : actionUrl ,
472512 } ) ;
473513
474- } , [ vercelStagingEnvironment , envMappingFetcher , actionUrl ] ) ;
514+ } , [ vercelStagingEnvironment , envMappingFetcher , actionUrl , trackOnboarding ] ) ;
475515
476516 const handleBuildSettingsNext = useCallback ( ( ) => {
517+ trackOnboarding ( "vercel onboarding build settings completed" ) ;
518+
477519 if ( nextUrl && fromMarketplaceContext && isGitHubConnectedForOnboarding ) {
478520 setIsRedirecting ( true ) ;
479521 }
@@ -501,7 +543,7 @@ export function VercelOnboardingModal({
501543 if ( ! isGitHubConnectedForOnboarding ) {
502544 setState ( "github-connection" ) ;
503545 }
504- } , [ vercelStagingEnvironment , pullEnvVarsBeforeBuild , atomicBuilds , discoverEnvVars , syncEnvVarsMapping , nextUrl , fromMarketplaceContext , isGitHubConnectedForOnboarding , completeOnboardingFetcher , actionUrl ] ) ;
546+ } , [ vercelStagingEnvironment , pullEnvVarsBeforeBuild , atomicBuilds , discoverEnvVars , syncEnvVarsMapping , nextUrl , fromMarketplaceContext , isGitHubConnectedForOnboarding , completeOnboardingFetcher , actionUrl , trackOnboarding ] ) ;
505547
506548 const handleFinishOnboarding = useCallback ( ( e : React . FormEvent < HTMLFormElement > ) => {
507549 e . preventDefault ( ) ;
@@ -531,9 +573,12 @@ export function VercelOnboardingModal({
531573
532574 useEffect ( ( ) => {
533575 if ( state === "completed" ) {
576+ trackOnboarding ( "vercel onboarding completed" , {
577+ github_connected : isGitHubConnectedForOnboarding ,
578+ } ) ;
534579 onClose ( ) ;
535580 }
536- } , [ state , onClose ] ) ;
581+ } , [ state , onClose , trackOnboarding , isGitHubConnectedForOnboarding ] ) ;
537582
538583 useEffect ( ( ) => {
539584 if ( state === "installing" ) {
@@ -584,7 +629,14 @@ export function VercelOnboardingModal({
584629
585630 if ( isLoadingState ) {
586631 return (
587- < Dialog open = { isOpen } onOpenChange = { ( open ) => ! open && ! fromMarketplaceContext && onClose ( ) } >
632+ < Dialog open = { isOpen } onOpenChange = { ( open ) => {
633+ if ( ! open && ! fromMarketplaceContext ) {
634+ if ( state as string !== "completed" ) {
635+ trackOnboarding ( "vercel onboarding abandoned" ) ;
636+ }
637+ onClose ( ) ;
638+ }
639+ } } >
588640 < DialogContent className = "max-w-lg" >
589641 < DialogHeader >
590642 < div className = "flex items-center gap-2" >
@@ -607,7 +659,14 @@ export function VercelOnboardingModal({
607659 const showGitHubConnection = state === "github-connection" ;
608660
609661 return (
610- < Dialog open = { isOpen } onOpenChange = { ( open ) => ! open && ! fromMarketplaceContext && onClose ( ) } >
662+ < Dialog open = { isOpen } onOpenChange = { ( open ) => {
663+ if ( ! open && ! fromMarketplaceContext ) {
664+ if ( state !== "completed" ) {
665+ trackOnboarding ( "vercel onboarding abandoned" ) ;
666+ }
667+ onClose ( ) ;
668+ }
669+ } } >
611670 < DialogContent className = "max-w-lg" >
612671 < DialogHeader >
613672 < div className = "flex items-center gap-2" >
@@ -902,6 +961,10 @@ export function VercelOnboardingModal({
902961 < Button
903962 variant = "primary/medium"
904963 onClick = { ( ) => {
964+ trackOnboarding ( "vercel onboarding env vars configured" , {
965+ env_vars_enabled : enabledEnvVars . length ,
966+ env_vars_total : syncableEnvVars . length ,
967+ } ) ;
905968 if ( fromMarketplaceContext ) {
906969 handleBuildSettingsNext ( ) ;
907970 } else {
0 commit comments