From a211193277ecd8e37653db6c73a026685ef4ebe5 Mon Sep 17 00:00:00 2001 From: Usman Baig Date: Sun, 8 Feb 2026 14:21:38 +0100 Subject: [PATCH] feat: enhance welcome page with organization selection and workspace switching functionality --- app/welcome/page.tsx | 134 ++++++++++++++++++++++++++++++++-------- lib/welcomeAnalytics.ts | 5 ++ 2 files changed, 113 insertions(+), 26 deletions(-) diff --git a/app/welcome/page.tsx b/app/welcome/page.tsx index dc93780..f182db3 100644 --- a/app/welcome/page.tsx +++ b/app/welcome/page.tsx @@ -14,6 +14,7 @@ import { getUserOrganizations, switchContext, type Organization, + type OrganizationMember, } from '@/lib/api/organization' import { createCheckoutSession } from '@/lib/api/billing' import { createSite, type Site } from '@/lib/api/sites' @@ -22,6 +23,7 @@ import { useAuth } from '@/lib/auth/context' import { getAuthErrorMessage } from '@/lib/utils/authErrors' import { trackWelcomeStepView, + trackWelcomeWorkspaceSelected, trackWelcomeWorkspaceCreated, trackWelcomePlanContinue, trackWelcomePlanSkip, @@ -38,6 +40,7 @@ import { BarChartIcon, GlobeIcon, ZapIcon, + PlusIcon, } from '@ciphera-net/ui' import Link from 'next/link' @@ -97,6 +100,10 @@ function WelcomeContent() { const [hadPendingCheckout, setHadPendingCheckout] = useState(null) const [dismissedPendingCheckout, setDismissedPendingCheckout] = useState(false) + const [organizations, setOrganizations] = useState(null) + const [orgsLoading, setOrgsLoading] = useState(false) + const [switchingOrgId, setSwitchingOrgId] = useState(null) + const setStep = useCallback( (next: number) => { const s = Math.min(Math.max(1, next), TOTAL_STEPS) @@ -113,22 +120,45 @@ function WelcomeContent() { if (stepFromUrl !== step) setStepState(stepFromUrl) }, [stepParam, step]) - // * If user already has orgs and no pending checkout, send to dashboard (avoid re-doing wizard) + // * Fetch organizations when on step 1 so we can show "Choose workspace" when user has orgs useEffect(() => { if (!user || step !== 1) return let cancelled = false + setOrgsLoading(true) getUserOrganizations() .then((orgs) => { - if (cancelled || orgs.length === 0) return - if (!localStorage.getItem('pulse_pending_checkout')) { - router.replace('/') - } + if (!cancelled) setOrganizations(orgs || []) + }) + .catch(() => { + if (!cancelled) setOrganizations([]) + }) + .finally(() => { + if (!cancelled) setOrgsLoading(false) }) - .catch(() => {}) return () => { cancelled = true } - }, [user, step, router]) + }, [user, step]) + + const handleSelectWorkspace = async (org: OrganizationMember) => { + setSwitchingOrgId(org.organization_id) + try { + const { access_token } = await switchContext(org.organization_id) + const result = await setSessionAction(access_token) + if (result.success && result.user) { + login(result.user) + router.refresh() + trackWelcomeWorkspaceSelected() + setStep(3) + } + } catch (err) { + toast.error(getAuthErrorMessage(err) || 'Failed to switch workspace') + } finally { + setSwitchingOrgId(null) + } + } + + const handleCreateNewWorkspace = () => setStep(2) const handleNameChange = (e: React.ChangeEvent) => { const val = e.target.value @@ -272,6 +302,10 @@ function WelcomeContent() { return } + if (switchingOrgId) { + return + } + if (redirectingCheckout || (planLoading && step === 3)) { return ( -
-
- + {orgsLoading ? ( +
+

Loading your workspaces...

-

- Welcome to Pulse -

-

- Privacy-first analytics in a few steps. No credit card required to start. -

- -
+ ) : organizations && organizations.length > 0 ? ( + <> +
+
+ +
+

+ Choose your workspace +

+

+ Continue with an existing workspace or create a new one. +

+
+
+ {organizations.map((org) => ( + + ))} +
+ + + ) : ( +
+
+ +
+

+ Welcome to Pulse +

+

+ Privacy-first analytics in a few steps. No credit card required to start. +

+ +
+ )} )} diff --git a/lib/welcomeAnalytics.ts b/lib/welcomeAnalytics.ts index d0887bf..9f1ee71 100644 --- a/lib/welcomeAnalytics.ts +++ b/lib/welcomeAnalytics.ts @@ -6,6 +6,7 @@ export type WelcomeEventName = | 'welcome_step_view' + | 'welcome_workspace_selected' | 'welcome_workspace_created' | 'welcome_plan_continue' | 'welcome_plan_skip' @@ -47,6 +48,10 @@ export function trackWelcomeStepView(step: number) { emit('welcome_step_view', { step }) } +export function trackWelcomeWorkspaceSelected() { + emit('welcome_workspace_selected') +} + export function trackWelcomeWorkspaceCreated(hadPendingCheckout: boolean) { emit('welcome_workspace_created', { had_pending_checkout: hadPendingCheckout }) }