refactor(onboarding): restructure onboarding routes and components
- Removed obsolete onboarding routes: complete, create-complex, index, verify-email, and verify. - Introduced new setup stepper page for onboarding process. - Created a multi-step setup component to handle complex creation with validation. - Added new schemas for complex creation and plan features. - Implemented detailed steps for complex name, location, plan selection, and court setup. - Enhanced error handling and user feedback during the onboarding process.
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
import { cn } from '@/lib/utils';
|
||||
import type { PlanWithFeatures } from '@repo/api-contract';
|
||||
import { Check, X } from 'lucide-react';
|
||||
import type { UseFormReturn } from 'react-hook-form';
|
||||
|
||||
type PlanSelectStepProps = {
|
||||
form: UseFormReturn<any>;
|
||||
plans: PlanWithFeatures[];
|
||||
};
|
||||
|
||||
const FEATURE_LABELS: Record<string, string> = {
|
||||
publicBookingPage: 'Página de booking pública',
|
||||
onlinePayments: 'Pagos online',
|
||||
advancedReports: 'Reportes avanzados',
|
||||
whatsappReminders: 'Recordatorios WhatsApp',
|
||||
};
|
||||
|
||||
export function PlanSelectStep({ form, plans }: PlanSelectStepProps) {
|
||||
const selectedCode = form.watch('planCode') as string | undefined;
|
||||
const errors = form.formState.errors;
|
||||
|
||||
const featuresList = [
|
||||
'publicBookingPage',
|
||||
'onlinePayments',
|
||||
'advancedReports',
|
||||
'whatsappReminders',
|
||||
] as const;
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold">Elegí tu plan</h2>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Seleccioná el plan que mejor se adapte a tu complejo.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-4 md:grid-cols-3">
|
||||
{plans.map((plan) => {
|
||||
const isSelected = selectedCode === plan.code;
|
||||
return (
|
||||
<button
|
||||
key={plan.code}
|
||||
type="button"
|
||||
onClick={() => form.setValue('planCode', plan.code, { shouldValidate: true })}
|
||||
className={cn(
|
||||
'relative flex flex-col rounded-xl border-2 p-5 text-left transition-all',
|
||||
'hover:border-primary/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
|
||||
isSelected ? 'border-primary bg-primary/5 shadow-sm' : 'border-border bg-card'
|
||||
)}
|
||||
>
|
||||
{isSelected && (
|
||||
<div className="absolute right-3 top-3 flex size-6 items-center justify-center rounded-full bg-primary text-primary-foreground">
|
||||
<Check className="size-4" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="mb-3">
|
||||
<h3 className="text-xl font-bold">{plan.name}</h3>
|
||||
<div className="mt-1">
|
||||
<span className="text-3xl font-bold">${plan.price}</span>
|
||||
<span className="text-sm text-muted-foreground">/mes</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mb-4 space-y-1.5 border-t pt-3 text-xs text-muted-foreground">
|
||||
<p className="font-medium text-foreground">Incluye:</p>
|
||||
<p>Hasta {plan.limits.maxCourts} canchas</p>
|
||||
<p>Hasta {plan.limits.maxBookingsPerDay} reservas/día</p>
|
||||
{plan.limits.maxActiveUsers && <p>Hasta {plan.limits.maxActiveUsers} usuarios</p>}
|
||||
</div>
|
||||
|
||||
<div className="space-y-1.5 border-t pt-3">
|
||||
{featuresList.map((feature) => {
|
||||
const enabled = plan.features[feature as keyof typeof plan.features];
|
||||
return (
|
||||
<div key={feature} className="flex items-center gap-2 text-xs">
|
||||
{enabled ? (
|
||||
<Check className="size-3.5 shrink-0 text-green-600" />
|
||||
) : (
|
||||
<X className="size-3.5 shrink-0 text-muted-foreground" />
|
||||
)}
|
||||
<span className={enabled ? 'text-foreground' : 'text-muted-foreground'}>
|
||||
{FEATURE_LABELS[feature]}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
{errors.planCode && (
|
||||
<p className="text-sm text-destructive">{String(errors.planCode.message ?? '')}</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user