import { type PlanId, plans } from '@shared/pricing'; import { IconArrowRight as ArrowRight, IconBuilding as Building2, IconCheck as Check, IconLoader2 as Loader2, IconUsers as Users, } from '@tabler/icons-react'; import { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router'; import { useAuth } from '@/components/auth/AuthProvider'; import { SEO } from '@/components/SEO'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { useOrganizations } from '@/hooks/useOrganizations'; import { supabase } from '@/lib/supabase'; import { cn } from '@/lib/utils'; const STEPS = [ 'onboarding.steps.profile', 'onboarding.steps.organization', 'onboarding.steps.plan', ] as const; export default function Onboarding() { const { t } = useTranslation(); const { user } = useAuth(); const navigate = useNavigate(); const [step, setStep] = useState(0); async function completeOnboarding() { await supabase.auth.updateUser({ data: { onboarding_completed: true }, }); navigate('/dashboard', { replace: true }); } return (
{/* Progress */}
{STEPS.map((label, i) => (
{i < step ? : i + 1}
{t(label)} {i < STEPS.length - 1 &&
}
))}
{/* Steps */}
{step === 0 && ( setStep(1)} /> )} {step === 1 && setStep(2)} onSkip={() => setStep(2)} />} {step === 2 && }
); } function ProfileStep({ defaultName, onNext }: { defaultName: string; onNext: () => void }) { const { t } = useTranslation(); const [name, setName] = useState(defaultName); const [saving, setSaving] = useState(false); async function handleSubmit(e: React.FormEvent) { e.preventDefault(); setSaving(true); await supabase.auth.updateUser({ data: { full_name: name.trim() } }); setSaving(false); onNext(); } return ( {t('onboarding.profile.title')} {t('onboarding.profile.description')}
setName(e.target.value)} placeholder={t('onboarding.profile.placeholder')} autoFocus />
); } function OrgStep({ onNext, onSkip }: { onNext: () => void; onSkip: () => void }) { const { t } = useTranslation(); const { createOrganization } = useOrganizations(); const [orgName, setOrgName] = useState(''); const [saving, setSaving] = useState(false); const [error, setError] = useState(''); async function handleSubmit(e: React.FormEvent) { e.preventDefault(); if (!orgName.trim()) return; setSaving(true); setError(''); try { await createOrganization(orgName.trim()); onNext(); } catch (err) { setError(err instanceof Error ? err.message : t('onboarding.org.error')); } finally { setSaving(false); } } return ( {t('onboarding.org.title')} {t('onboarding.org.description')}
setOrgName(e.target.value)} placeholder={t('onboarding.org.placeholder')} autoFocus /> {error &&

{error}

}
); } function PlanStep({ onNext }: { onNext: () => void }) { const { t } = useTranslation(); const [selected, setSelected] = useState('free'); function handleContinue() { if (selected !== 'free') { // Redirect to billing to complete checkout, then they'll come back window.location.href = '/settings/billing'; return; } onNext(); } return ( {t('onboarding.plan.title')} {t('onboarding.plan.description')} {plans.map((plan) => ( ))} ); }