Added routes

This commit is contained in:
Jose Selesan
2026-09-25 10:05:56 -03:00
parent 336e1af523
commit 94c353f4f2
100 changed files with 6126 additions and 183 deletions

View File

@@ -0,0 +1,171 @@
import { useState } from 'react'
import { useMutation } from '@tanstack/react-query'
import type { ConnectPaymentResult, PaymentProvider } from '@gruperly/shared'
import {
ArrowLeft,
ArrowRight,
BadgeCheck,
Check,
CheckCircle2,
CreditCard,
Loader2,
ShieldCheck,
} from 'lucide-react'
import { connectPayment } from '../../lib/api'
import { cn } from '../../lib/utils'
import { Badge, Button } from '../../components/ui'
import { PAYMENT_PROVIDERS } from './constants'
type PaymentStepProps = {
initialResult: ConnectPaymentResult | null
onConnected: (result: ConnectPaymentResult) => void
onBack: () => void
}
export function PaymentStep({ initialResult, onConnected, onBack }: PaymentStepProps) {
const [selected, setSelected] = useState<PaymentProvider>('MERCADO_PAGO')
const [connected, setConnected] = useState<ConnectPaymentResult | null>(initialResult)
const connect = useMutation({
mutationFn: () => connectPayment({ provider: selected, sandbox: true }),
onSuccess: (result) => {
setConnected(result)
onConnected(result)
},
})
if (connected) {
return (
<section className="space-y-6">
<header className="space-y-1">
<h2 className="text-2xl font-bold text-primary">Tu cobro está conectado</h2>
<p className="text-sm text-foreground/60">
Ya podés dejar listo tu primer grupo para cobrar con{' '}
{PAYMENT_PROVIDERS.find((p) => p.value === connected.provider)?.name ?? ''}.
</p>
</header>
<div className="rounded-xl border border-success/30 bg-success-soft p-5 text-center">
<span className="mx-auto flex size-12 items-center justify-center rounded-full bg-surface">
<CheckCircle2 className="size-6 text-success" />
</span>
<p className="mt-3 text-sm font-semibold text-success">Cuenta conectada</p>
<div className="mt-1 flex items-center justify-center gap-2">
<span className="text-sm font-medium text-primary">
{PAYMENT_PROVIDERS.find((p) => p.value === connected.provider)?.name}
</span>
<Badge variant="success">Modo prueba</Badge>
</div>
</div>
<div className="flex items-center gap-3">
<Button variant="outline" onClick={() => setConnected(null)}>
<ArrowLeft className="size-4" />
Cambiar
</Button>
<Button
variant="primary"
className="flex-1"
onClick={() => onConnected(connected)}
>
Continuar
<ArrowRight className="size-4" />
</Button>
</div>
</section>
)
}
return (
<section className="space-y-5">
<header className="space-y-1">
<h2 className="text-2xl font-bold text-primary">Elegí tu procesador de cobro</h2>
<p className="text-sm text-foreground/60">
Los pagos de tus miembros van a llegar por esta plataforma.
</p>
</header>
<div role="radiogroup" aria-label="Procesador de pago" className="space-y-3">
{PAYMENT_PROVIDERS.map((provider) => {
const isSelected = selected === provider.value
return (
<button
key={provider.value}
type="button"
role="radio"
aria-checked={isSelected}
onClick={() => setSelected(provider.value)}
className={cn(
'flex w-full items-center gap-3 rounded-xl border bg-surface p-4 text-left transition-colors',
isSelected
? 'border-accent ring-2 ring-accent/20'
: 'border-border hover:bg-primary-soft',
)}
>
<span
className={cn(
'rounded-lg p-2',
isSelected ? 'bg-accent-soft' : 'bg-primary-soft',
)}
>
<CreditCard
className={cn('size-5', isSelected ? 'text-accent' : 'text-primary/40')}
/>
</span>
<span className="min-w-0 flex-1">
<span className="block text-sm font-semibold text-primary">
{provider.name}
</span>
<span className="block text-xs text-foreground/60">
{provider.description}
</span>
</span>
<span
className={cn(
'flex size-5 shrink-0 items-center justify-center rounded-full border transition-colors',
isSelected ? 'border-accent bg-accent text-on-accent' : 'border-border',
)}
>
{isSelected ? <Check className="size-3" strokeWidth={3} /> : null}
</span>
</button>
)
})}
</div>
<div className="flex items-start gap-2 rounded-xl bg-warning-soft px-4 py-3">
<ShieldCheck className="mt-0.5 size-4 shrink-0 text-warning" />
<p className="text-xs text-warning">
Por ahora la conexión se hace en modo prueba (sandbox). Más adelante vas a poder
vincular tu cuenta real de {selected === 'STRIPE' ? 'Stripe' : 'Mercado Pago'}.
</p>
</div>
{connect.isError ? (
<p className="rounded-xl bg-danger-soft px-4 py-3 text-sm text-danger">
No pudimos conectar la cuenta. Intentalo de nuevo.
</p>
) : null}
<div className="flex items-center gap-3">
<Button variant="outline" onClick={onBack}>
<ArrowLeft className="size-4" />
Volver
</Button>
<Button
variant="primary"
className="flex-1"
disabled={connect.isPending}
onClick={() => connect.mutate()}
>
{connect.isPending ? (
<Loader2 className="size-4 animate-spin" />
) : (
<BadgeCheck className="size-4" />
)}
{connect.isPending ? 'Conectando…' : 'Conectar cuenta'}
</Button>
</div>
</section>
)
}