From 900e6146e762448576d4acd0102ccc65dd67ff6c Mon Sep 17 00:00:00 2001
From: Jose Selesan
Date: Tue, 19 May 2026 09:32:26 -0300
Subject: [PATCH] Stepper improvement
---
.../public-booking/public-booking-page.tsx | 110 +++++++++++++-----
1 file changed, 83 insertions(+), 27 deletions(-)
diff --git a/apps/frontend/src/features/public-booking/public-booking-page.tsx b/apps/frontend/src/features/public-booking/public-booking-page.tsx
index 8250aad..131ef1a 100644
--- a/apps/frontend/src/features/public-booking/public-booking-page.tsx
+++ b/apps/frontend/src/features/public-booking/public-booking-page.tsx
@@ -49,7 +49,7 @@ import {
Users,
Wrench,
} from 'lucide-react';
-import { useEffect, useMemo, useState } from 'react';
+import { useEffect, useMemo, useRef, useState } from 'react';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
@@ -190,6 +190,15 @@ function getStep(selectedSlot: SelectedSlot | null, isValid: boolean) {
return 4;
}
+function getCompletedSteps(selectedSlot: SelectedSlot | null, isValid: boolean) {
+ return {
+ court: Boolean(selectedSlot),
+ time: Boolean(selectedSlot),
+ details: Boolean(selectedSlot && isValid),
+ confirm: false,
+ };
+}
+
function getSelectedCourt(
courts: PublicAvailabilityCourt[],
selectedCourtId: string | undefined,
@@ -273,7 +282,15 @@ function PlayzerBrand({ compact = false }: { compact?: boolean }) {
);
}
-function ProgressSteps({ currentStep, mobile = false }: { currentStep: number; mobile?: boolean }) {
+function ProgressSteps({
+ currentStep,
+ completedSteps,
+ mobile = false,
+}: {
+ currentStep: number;
+ completedSteps: Record;
+ mobile?: boolean;
+}) {
const steps = [
{ value: 'court', label: 'Elegi cancha' },
{ value: 'time', label: 'Selecciona horario' },
@@ -289,26 +306,47 @@ function ProgressSteps({ currentStep, mobile = false }: { currentStep: number; m
className={mobile ? 'mx-auto w-full max-w-[240px]' : 'max-w-[760px]'}
>
- {steps.map((step, index) => (
-
-
-
- {index + 1}
-
- {!mobile && (
-
- {step.label}
-
- )}
-
-
-
- ))}
+ {steps.map((step, index) => {
+ const isCompleted = completedSteps[step.value];
+ const isActive = step.value === activeStep;
+
+ return (
+
+
+
+ {index + 1}
+
+ {!mobile && (
+
+ {step.label}
+
+ )}
+
+
+
+ );
+ })}
);
@@ -340,10 +378,12 @@ function CourtIcon({ className = 'size-5' }: { className?: string }) {
function PublicBookingPageChrome({
children,
currentStep,
+ completedSteps,
mobile = false,
}: {
children: React.ReactNode;
currentStep: number;
+ completedSteps: Record;
mobile?: boolean;
}) {
const { resolvedTheme } = useTheme();
@@ -373,7 +413,7 @@ function PublicBookingPageChrome({
{children}
@@ -415,7 +455,7 @@ function PublicBookingPageChrome({
Elegi tu cancha, selecciona el horario y completa tu reserva.
@@ -467,10 +507,11 @@ function PublicBookingDesktop(props: BookingShellProps) {
} = props;
const selectedCourt = getSelectedCourt(courts, selectedCourtId, selectedSlot);
const currentStep = getStep(selectedSlot, props.form.formState.isValid);
+ const completedSteps = getCompletedSteps(selectedSlot, props.form.formState.isValid);
const selectedDay = dayOptions.find((option) => option.value === selectedDate);
return (
-
+