feat: basic onboarding flow\
This commit is contained in:
131
apps/web/src/routes/onboarding.tsx
Normal file
131
apps/web/src/routes/onboarding.tsx
Normal file
@@ -0,0 +1,131 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { useNavigate } from '@tanstack/react-router'
|
||||
import { Loader2 } from 'lucide-react'
|
||||
import type { ConnectPaymentResult, OnboardingGroupDto } from '@gruperly/shared'
|
||||
import { Logo, LogoIcon } from '../components/brand'
|
||||
import {
|
||||
ConfirmationStep,
|
||||
FirstGroupStep,
|
||||
PaymentStep,
|
||||
Stepper,
|
||||
WelcomeStep,
|
||||
} from '../components/onboarding'
|
||||
import { PAYMENT_PROVIDERS } from '../components/onboarding/constants'
|
||||
import { Button } from '../components/ui'
|
||||
import { useAuth } from '../context/AuthProvider'
|
||||
import { getOnboardingStatus } from '../lib/api'
|
||||
|
||||
const STEPS = [
|
||||
{ label: 'Bienvenida' },
|
||||
{ label: 'Cobros' },
|
||||
{ label: 'Tu grupo' },
|
||||
{ label: 'Listo' },
|
||||
]
|
||||
|
||||
export function OnboardingView() {
|
||||
const { user, isPending: authPending } = useAuth()
|
||||
const navigate = useNavigate()
|
||||
|
||||
const [step, setStep] = useState(0)
|
||||
const [paymentResult, setPaymentResult] = useState<ConnectPaymentResult | null>(null)
|
||||
const [createdGroup, setCreatedGroup] = useState<OnboardingGroupDto | null>(null)
|
||||
|
||||
const statusQuery = useQuery({
|
||||
queryKey: ['onboarding', 'status'],
|
||||
queryFn: getOnboardingStatus,
|
||||
enabled: !!user,
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
const status = statusQuery.data
|
||||
if (!status) return
|
||||
if (status.completed) {
|
||||
void navigate({ to: '/' })
|
||||
return
|
||||
}
|
||||
// Si interrumpió después de conectar el cobro, retomamos en el paso del grupo.
|
||||
if (status.step === 'PAYMENT_CONNECTED') {
|
||||
setStep((current) => (current === 0 ? 2 : current))
|
||||
}
|
||||
}, [statusQuery.data, navigate])
|
||||
|
||||
if (authPending) {
|
||||
return (
|
||||
<div className="flex min-h-dvh items-center justify-center">
|
||||
<Loader2 className="size-6 animate-spin text-foreground/40" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (!user) {
|
||||
return (
|
||||
<div className="flex min-h-dvh items-center justify-center px-4">
|
||||
<div className="w-full max-w-sm space-y-4 text-center">
|
||||
<span className="mx-auto flex size-14 items-center justify-center rounded-2xl bg-accent-soft">
|
||||
<LogoIcon className="size-8 text-accent" />
|
||||
</span>
|
||||
<h1 className="text-2xl font-bold text-primary">Tu cuenta, lista</h1>
|
||||
<p className="text-sm text-foreground/60">
|
||||
Iniciá sesión para configurar tus cobros y crear tu primer grupo.
|
||||
</p>
|
||||
<Button variant="primary" className="w-full" onClick={() => void navigate({ to: '/login' })}>
|
||||
Iniciar sesión
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (statusQuery.isPending) {
|
||||
return (
|
||||
<div className="flex min-h-dvh items-center justify-center">
|
||||
<Loader2 className="size-6 animate-spin text-foreground/40" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const firstName = user.name?.trim().split(/\s+/)[0] ?? 'profesor/a'
|
||||
const providerName = paymentResult
|
||||
? PAYMENT_PROVIDERS.find((p) => p.value === paymentResult.provider)?.name
|
||||
: undefined
|
||||
|
||||
return (
|
||||
<main className="mx-auto flex min-h-dvh w-full max-w-md flex-col px-4 pb-12 pt-8 sm:pt-12">
|
||||
<header className="mb-6 flex items-center justify-center gap-2">
|
||||
<LogoIcon className="size-7" />
|
||||
<Logo className="text-xl" />
|
||||
</header>
|
||||
|
||||
<Stepper steps={STEPS} current={step} />
|
||||
|
||||
<div key={step} className="mt-8 animate-step-enter">
|
||||
{step === 0 ? (
|
||||
<WelcomeStep name={firstName} onNext={() => setStep(1)} />
|
||||
) : null}
|
||||
{step === 1 ? (
|
||||
<PaymentStep
|
||||
initialResult={paymentResult}
|
||||
onConnected={(result) => {
|
||||
setPaymentResult(result)
|
||||
setStep(2)
|
||||
}}
|
||||
onBack={() => setStep(0)}
|
||||
/>
|
||||
) : null}
|
||||
{step === 2 ? (
|
||||
<FirstGroupStep
|
||||
onBack={() => setStep(1)}
|
||||
onCompleted={(group) => {
|
||||
setCreatedGroup(group)
|
||||
setStep(3)
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
{step === 3 && createdGroup ? (
|
||||
<ConfirmationStep group={createdGroup} providerName={providerName} />
|
||||
) : null}
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user