- Rebrand all user-facing text to AgendaMax - Fix sidebar scrolling: card container no longer overflows html (flex h-full min-h-0) - Fix calendar toolbar hover: active buttons keep readable contrast - Sticky calendar toolbar (month/week/day always visible on scroll) - Seed guarantees 2-4 appointments per day for current week (Mon-Sat)
5.8 KiB
Task 12 — Wizard 3/4 pasos + Confirmation con reasons + anclar main ancho
File changed
src/pages/public/BookingPage.tsx(only file touched)
Steps applied (1–9)
- Replaced static
STEPSwithFULL_STEPSconst +stepsFor(autoAssign)helper that drops the "Especialista" step when auto-assign is on (returns{n, label, original}items). - Added
maxBookableDate()helper next totodayStr(today + 3 months, ISOYYYY-MM-DD). - In
BookingPage, computedautoAssign,steps(useMemo on autoAssign),stepCount, andcurrentOriginal(find byn === step, read.original) beforeslotsQueryso the closure can see it. slotsQuery.enablednow usescurrentOriginal === 3(wasstep >= 3).goNext/goBack/pickSlotusestepCount(no hard-coded4).canContinue,primaryLabel,onPrimaryswitch oncurrentOriginal.handleBooksendsemployee_id: autoAssign ? undefined : selectedSlot.employee_id.- Rewrote the main
return (...)to render sections bycurrentOriginal;mainwidens tomax-w-5xlonly whencurrentOriginal === 3, otherwisemax-w-2xl;DateTimePickergetsmax={maxBookableDate()}; the Especialista row in Datos is hidden whenautoAssign. - Updated
TopBar+ActionBarsignatures to acceptstepCount+steps; both widened frommax-w-3xl/max-w-2xl→max-w-5xl. TopBar now reads "Paso X de {stepCount}" and iterates the dynamicstepsarray (including the mobileX / {stepCount}chip). Confirmationrendersappointment.reasonsasbrandbadges (with Sparkles icon) guarded byreasons && reasons.length > 0, placed under the Especialista row.
Also updated the two secondary TopBar call sites (disabled-booking screen and Confirmation) to pass stepCount={4} steps={[]} — step={0} hides the indicator block, so an empty array is safe there.
Verification
typecheck
> tsc -b --noEmit
(no output — 0 errors)
First pass surfaced one error: ActionBar's stepCount param was unused (TS6133). Fixed by surfacing it in the empty-state label: Paso {step} de {stepCount} · Continúa para reservar.
build
> tsc -b && vite build
✓ 2461 modules transformed.
✓ built in 5.16s
lint
> eslint . --ext ts,tsx
ESLint couldn't find an eslint.config.(js|mjs|cjs) file.
Pre-existing env issue (ESLint 9 with no flat config in the repo). Not introduced by this task; no react-hooks/exhaustive-deps warnings would be reachable until the config is repaired. The useMemo(() => stepsFor(autoAssign), [autoAssign]) dep array is correct.
Runtime smoke (server already running on :5173)
- Slug:
lumiere-estetica-spa. Defaultauto_assign_specialist=0. - OFF flow: GET
/api/public/<slug>→auto_assign_specialist=0. Slots for service 6 tomorrow returned 10 slots. POST/bookwithoutemployee_id→ 201,emp_id=2 Mateo Herrera,reasons=["Disponible"],client_created=true. (Server still auto-ranked because the client didn't pick one — that's the unchanged 4-step path.) - ON flow: PATCH
/api/settings {auto_assign_specialist:1}. GET public →auto_assign_specialist=1(correctly exposed). POST/bookwithoutemployee_idat a free slot → 201,emp_id=2 Mateo Herrera,reasons=["Disponible"]. - Double-booking guard: re-POSTing the first booked slot → 409
Esa hora acaba de ocuparse, elige otra.✓ - Restored
auto_assign_specialist=0afterwards.
Visual confirmation of step count / layout width was done by code inspection (no headless browser available in this subagent): stepsFor(false) yields 4 items; stepsFor(true) yields 3 items (Especialista filtered). The TopBar "Paso X de {stepCount}" and the mobile "X / {stepCount}" both read from the dynamic count, so 4 vs 3 follows automatically. main's width is selected by currentOriginal === 3 ? "max-w-5xl" : "max-w-2xl", which is true only on the Horario section regardless of the 3- or 4-step mapping.
Deviations
ActionBar's previously-unusedstepCountprop (the spec required ActionBar to accept it, but the plan's literal snippet didn't read it) is now surfaced in the empty-state copy. This both satisfies TS6133 and improves UX consistency.- No commit performed (per task constraints).
Self-review
- OFF = 4 steps? Yes —
stepsFor(false)maps all 4FULL_STEPSto{1:Servicio, 2:Especialista, 3:Horario, 4:Datos}withoriginalmatchingn.currentOriginaldrives section rendering, so the 4-step Servicio→Especialista→Horario→Datos flow is preserved exactly. The "Cualquiera" path still hits the server ranking (server-side, unchanged here). - ON = 3 steps? Yes —
stepsFor(true)filters out Especialista, renumbering to{1:Servicio (orig 1), 2:Horario (orig 3), 3:Datos (orig 4)}. ThecurrentOriginal === 2branch (Especialista) never matches because no step maps to original 2. - currentOriginal before slotsQuery? Yes — declared at line 83, slotsQuery at line 90 references it in
enabledat line 93. - handleBook sends undefined employee_id when autoAssign? Yes — line 198
employee_id: autoAssign ? undefined : selectedSlot.employee_id.BookPayload.employee_idis optional, so this typechecks. - Confirmation shows reasons badges? Yes — guarded by
appointment.reasons && appointment.reasons.length > 0, renders<span>chips with Sparkles icon under the Especialista row. - main widened only on Horario? Yes —
cn("…", currentOriginal === 3 ? "max-w-5xl" : "max-w-2xl"). Horario is always original 3 in both flows. - Disabled-booking screen, loading/error states, reset() preserved? Yes — only the TopBar call in the disabled screen was updated to pass the new required props; the rest of that branch is untouched. Loading/error branches and
reset()are unchanged. - No git commit. Confirmed — only file edits + npm runs.