/** * Onboarding Flow (S-7.7) * * First-time user wizard: * Step 1: Create organization * Step 2: Generate first API key * Step 3: Show SDK install snippet * Step 4: Verify first event received */ import React, { useState, useCallback, useEffect, useRef } from 'react'; import { useOrg } from './OrgContext'; import { createApiKey, getOnboardingStatus, verifyFirstEvent, type OnboardingStatus, type CreateApiKeyResponse, } from './api'; type Step = 'create-org' | 'generate-key' | 'install-sdk' | 'verify-event' | 'complete'; const SDK_SNIPPET = `# Install npm install @agentlens/sdk # Initialize in your code import { AgentLens } from '@agentlens/sdk'; const lens = new AgentLens({ apiKey: 'YOUR_API_KEY', endpoint: 'https://cloud.agentlens.dev', }); // Track an event await lens.track({ type: 'agent.action', sessionId: 'my-session', data: { action: 'hello-world' }, });`; function stepIndex(step: Step): number { const order: Step[] = ['create-org', 'generate-key', 'install-sdk', 'verify-event', 'complete']; return order.indexOf(step); } export function OnboardingFlow(): React.ReactElement { const { currentOrg, createOrg } = useOrg(); const [step, setStep] = useState('create-org'); const [orgName, setOrgName] = useState(''); const [creating, setCreating] = useState(false); const [error, setError] = useState(null); const [keyResponse, setKeyResponse] = useState(null); const [copied, setCopied] = useState(false); const [verifying, setVerifying] = useState(false); const [verified, setVerified] = useState(false); const pollRef = useRef | null>(null); // Check initial status useEffect(() => { getOnboardingStatus() .then((status: OnboardingStatus) => { if (status.has_first_event) setStep('complete'); else if (status.has_api_key) setStep('install-sdk'); else if (status.has_org) setStep('generate-key'); else setStep('create-org'); }) .catch(() => setStep('create-org')); }, []); // If org already exists, skip step 1 useEffect(() => { if (currentOrg && step === 'create-org') { setStep('generate-key'); } }, [currentOrg, step]); const handleCreateOrg = useCallback(async () => { if (!orgName.trim()) return; setCreating(true); setError(null); try { await createOrg(orgName.trim()); setStep('generate-key'); } catch (err) { setError(err instanceof Error ? err.message : 'Failed to create org'); } finally { setCreating(false); } }, [orgName, createOrg]); const handleGenerateKey = useCallback(async () => { if (!currentOrg) return; setCreating(true); setError(null); try { const result = await createApiKey(currentOrg.id, 'onboarding-key', 'production'); setKeyResponse(result); setStep('install-sdk'); } catch (err) { setError(err instanceof Error ? err.message : 'Failed to generate key'); } finally { setCreating(false); } }, [currentOrg]); const handleCopyKey = useCallback(() => { if (keyResponse) { navigator.clipboard.writeText(keyResponse.fullKey).then(() => { setCopied(true); setTimeout(() => setCopied(false), 2000); }); } }, [keyResponse]); const handleCopySnippet = useCallback(() => { const snippet = keyResponse ? SDK_SNIPPET.replace('YOUR_API_KEY', keyResponse.fullKey) : SDK_SNIPPET; navigator.clipboard.writeText(snippet); }, [keyResponse]); const handleVerify = useCallback(async () => { if (!currentOrg) return; setVerifying(true); setError(null); // Poll every 2 seconds for up to 30 seconds let attempts = 0; const maxAttempts = 15; const poll = async () => { try { const result = await verifyFirstEvent(currentOrg.id); if (result.received) { setVerified(true); setStep('complete'); if (pollRef.current) clearInterval(pollRef.current); setVerifying(false); return true; } } catch { // ignore poll errors } attempts++; if (attempts >= maxAttempts) { setVerifying(false); setError('No events received yet. Make sure your SDK is configured and sending events.'); if (pollRef.current) clearInterval(pollRef.current); } return false; }; // Immediate check const found = await poll(); if (!found) { pollRef.current = setInterval(poll, 2000); } }, [currentOrg]); // Cleanup polling on unmount useEffect(() => { return () => { if (pollRef.current) clearInterval(pollRef.current); }; }, []); const handleSkipVerify = useCallback(() => { setStep('complete'); if (pollRef.current) clearInterval(pollRef.current); }, []); const currentStep = stepIndex(step); return (

Welcome to AgentLens Cloud! 🚀

{/* Progress indicator */}
{['Create Org', 'Generate Key', 'Install SDK', 'Verify'].map((label, idx) => ( {idx < currentStep ? '✅' : `${idx + 1}.`} {label} ))}
{error &&

{error}

} {/* Step 1: Create Org */} {step === 'create-org' && (

Step 1: Create Your Organization

An organization is where your team's data lives. You can invite teammates later.

setOrgName(e.target.value)} data-testid="org-name-input" aria-label="Organization name" />
)} {/* Step 2: Generate Key */} {step === 'generate-key' && (

Step 2: Generate Your First API Key

You'll need an API key to send events from your application.

)} {/* Step 3: Install SDK */} {step === 'install-sdk' && (

Step 3: Install the SDK

{keyResponse && (

Your API Key:

{keyResponse.fullKey}

⚠️ Save this key now — you won't see it again!

)}
{keyResponse ? SDK_SNIPPET.replace('YOUR_API_KEY', keyResponse.fullKey) : SDK_SNIPPET}
)} {/* Step 4: Verify Event */} {step === 'verify-event' && (

Step 4: Verify Your First Event

Run your application and send a test event. We'll detect it automatically.

)} {/* Complete */} {step === 'complete' && (

🎉 You're all set!

{verified ? 'We received your first event! Your AgentLens Cloud is ready.' : 'Your org and API key are set up. Send your first event when ready.'}

Next steps:

  • Explore the Sessions page to see your agent activity
  • Invite your team from Team Management
  • Set up Guardrails for safety monitoring
)}
); } export default OnboardingFlow;