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')}
);
}
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')}
);
}
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) => (
))}
);
}