From e27a93d051f5692c4a0f78397f897ce69d293ce7 Mon Sep 17 00:00:00 2001 From: tusharmagar Date: Wed, 11 Mar 2026 10:00:36 +0530 Subject: [PATCH] new onboarding flow --- .../src/components/onboarding-modal.tsx | 221 +++++++++++------- 1 file changed, 142 insertions(+), 79 deletions(-) diff --git a/apps/x/apps/renderer/src/components/onboarding-modal.tsx b/apps/x/apps/renderer/src/components/onboarding-modal.tsx index 3528d1ee..2080c52b 100644 --- a/apps/x/apps/renderer/src/components/onboarding-modal.tsx +++ b/apps/x/apps/renderer/src/components/onboarding-modal.tsx @@ -2,7 +2,8 @@ import * as React from "react" import { useState, useEffect, useCallback } from "react" -import { Loader2, Mic, Mail, CheckCircle2, MessageSquare } from "lucide-react" +import { Loader2, Mic, Mail, CheckCircle2, ArrowLeft, MessageSquare } from "lucide-react" +// import { MessageSquare } from "lucide-react" import { Dialog, @@ -38,7 +39,7 @@ interface OnboardingModalProps { onComplete: () => void } -type Step = 0 | 1 | 2 | 3 +type Step = 0 | 1 | 2 | 3 | 4 type OnboardingPath = 'rowboat' | 'byok' | null @@ -367,11 +368,29 @@ export function OnboardingModal({ open, onComplete }: OnboardingModalProps) { }, []) const handleNext = () => { - if (currentStep < 3) { + if (currentStep < 4) { setCurrentStep((prev) => (prev + 1) as Step) } } + const handleBack = () => { + if (currentStep === 1) { + // BYOK upsell → back to sign-in page + setOnboardingPath(null) + setCurrentStep(0 as Step) + } else if (currentStep === 2) { + // LLM setup → back to BYOK upsell + setCurrentStep(1 as Step) + } else if (currentStep === 3) { + // Connect accounts → back depends on path + if (onboardingPath === 'rowboat') { + setCurrentStep(0 as Step) + } else { + setCurrentStep(2 as Step) + } + } + } + const handleComplete = () => { onComplete() } @@ -486,11 +505,11 @@ export function OnboardingModal({ open, onComplete }: OnboardingModalProps) { // Auto-advance from Rowboat sign-in step when OAuth completes useEffect(() => { - if (onboardingPath !== 'rowboat' || currentStep !== 1) return + if (onboardingPath !== 'rowboat' || currentStep !== 0) return const cleanup = window.ipc.on('oauth:didConnect', (event) => { if (event.provider === 'rowboat' && event.success) { - setCurrentStep(2 as Step) + setCurrentStep(3 as Step) } }) @@ -568,20 +587,30 @@ export function OnboardingModal({ open, onComplete }: OnboardingModalProps) { startConnect('google', clientId) }, [startConnect]) - // Step indicator - const renderStepIndicator = () => ( -
- {[0, 1, 2, 3].map((step) => ( -
= step ? "bg-primary" : "bg-muted" - )} - /> - ))} -
- ) + // Step indicator - dynamic based on path + const renderStepIndicator = () => { + // Rowboat path: Sign In (0), Connect (3), Done (4) = 3 dots + // BYOK path: Sign In (0), Upsell (1), Model (2), Connect (3), Done (4) = 5 dots + // Before path is chosen: show 3 dots (minimal) + const rowboatSteps = [0, 3, 4] + const byokSteps = [0, 1, 2, 3, 4] + const steps = onboardingPath === 'byok' ? byokSteps : rowboatSteps + const currentIndex = steps.indexOf(currentStep) + + return ( +
+ {steps.map((_, i) => ( +
= i ? "bg-primary" : "bg-muted" + )} + /> + ))} +
+ ) + } // Helper to render an OAuth provider row const renderOAuthProvider = (provider: string, displayName: string, icon: React.ReactNode, description: string) => { @@ -788,55 +817,15 @@ export function OnboardingModal({ open, onComplete }: OnboardingModalProps) {
) - // Step 0: Path Choice (Rowboat vs BYOK) - const renderPathChoiceStep = () => ( -
-
- Your AI coworker, with memory -
- - Get Started - - Choose how you'd like to set up Rowboat - - - -
- - - -
-
- ) - - // Step 1 (Rowboat path): Sign in to Rowboat - const renderRowboatSignInStep = () => { + // Step 0: Sign in to Rowboat (with BYOK option) + const renderSignInStep = () => { const rowboatState = providerStates['rowboat'] || { isConnected: false, isLoading: false, isConnecting: false } return (
+
+ Your AI coworker, with memory +
Sign in to Rowboat @@ -850,14 +839,17 @@ export function OnboardingModal({ open, onComplete }: OnboardingModalProps) { Connected to Rowboat
-
) : (
)} + +
+ +
) } - // Step 1 (BYOK path): LLM Setup + // Step 1: BYOK upsell — explain benefits of Rowboat before continuing with BYOK + const renderByokUpsellStep = () => ( +
+ + Before you continue + + With a Rowboat account, you get: + + + +
+
+ +
+
Instant access to all models
+
GPT, Claude, Gemini, and more — no separate API keys needed
+
+
+
+ +
+
Simplified billing
+
One account for everything — no juggling multiple provider subscriptions
+
+
+
+ +
+
Automatic updates
+
New models are available as soon as they launch, with no configuration changes
+
+
+
+ +

+ By continuing, you'll set up your own API keys instead of using Rowboat's managed gateway. +

+ +
+ + +
+
+ ) + + // Step 2 (BYOK path): LLM Setup const renderLlmSetupStep = () => { const primaryProviders: Array<{ id: LlmProviderFlavor; name: string; description: string }> = [ { id: "openai", name: "OpenAI", description: "Use your OpenAI API key" }, @@ -1055,10 +1109,13 @@ export function OnboardingModal({ open, onComplete }: OnboardingModalProps) { )} -
+
+ - +
+ + +
) - // Step 2: Completion + // Step 4: Completion const renderCompletionStep = () => { const hasConnections = connectedProviders.length > 0 || granolaEnabled || slackEnabled || gmailConnected @@ -1224,11 +1287,11 @@ export function OnboardingModal({ open, onComplete }: OnboardingModalProps) { onEscapeKeyDown={(e) => e.preventDefault()} > {renderStepIndicator()} - {currentStep === 0 && renderPathChoiceStep()} - {currentStep === 1 && onboardingPath === 'rowboat' && renderRowboatSignInStep()} - {currentStep === 1 && onboardingPath === 'byok' && renderLlmSetupStep()} - {currentStep === 2 && renderAccountConnectionStep()} - {currentStep === 3 && renderCompletionStep()} + {currentStep === 0 && renderSignInStep()} + {currentStep === 1 && renderByokUpsellStep()} + {currentStep === 2 && renderLlmSetupStep()} + {currentStep === 3 && renderAccountConnectionStep()} + {currentStep === 4 && renderCompletionStep()}