/services/public-calendar also returns the professional's current opening
hours; the client threw them away and drew the day from the snapshot the
search list carried, which can be days old. It now uses the fresh ones.
Between picking an hour and pressing "Pedir cita" another patient can
take the slot. The confirm step asks the agenda again and, if the hour is
gone, says so and clears the selection instead of creating a service the
professional would have to deny. A failed re-check lets the booking
through: a network hiccup should not block a patient.
Also:
- tapping a busy slot with no id explains itself instead of doing nothing
- the profile refuses to save an empty rate with the rate switch on,
which was reaching the backend as rate: ''
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The two calendars each built their own list of hours. The patient's
enforced a 3-hour booking lead time; the professional's did not, so at
15:00 an 08:00-18:00 agenda reported "3 libres" (15, 16, 17) that no
patient could actually take.
Both now go through SlotGenerator. The professional still sees the
near-term hours (blocking the next hour is legitimate) but they are
labelled "Sin reserva" and excluded from the "libres" count, so the
number on his agenda means what the patient sees.
Also in this pass:
- endOf() clamps the slot end at 23:59; a 120-minute slot booked at
23:00 was sending "25:00" to the backend.
- ScheduleEntity.copyWith can set an hour back to null, so returning a
day to jornada continua no longer keeps the split hours around.
- Removed the Firebase-era schedule map ('habilitado', 'range1Hour1')
together with the dead ProfessionalEntity.fromDocument that fed it.
- Settings loads guard on mounted and swallow failures instead of
calling setState after dispose.
- "Pedir cita" says what is missing instead of doing nothing.
Tests: 9 passing, including the clamp and the empty/inverted ranges.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Saving opening hours from the app never did anything. The payload went to
PATCH /professionals/me as a map keyed by day name ({"monday": {"habilitado":
true, "range1Hour1": "8:0"}}), and that endpoint silently drops a `schedules`
key — it answers 200 with the record unchanged. So the professional edited
their hours, got "Información actualizada correctamente", and the agenda kept
whatever it had.
The real endpoint is PATCH /professionals/me/schedules and it takes an array
of {day_of_week, enabled, continuous_day, range*_hour*} with zero-padded
times, which is what the web has been sending all along.
Verified against the backend: the previous payload returns 200 and changes
nothing; the new one returns 200 and the rows come back updated.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>