fix(schedules): actually persist the professional's opening hours

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>
This commit is contained in:
Lizandro Guarnizo
2026-08-25 12:52:34 -05:00
co-authored by Claude Opus 5
parent 29ff91a790
commit 0bf96a935d
3 changed files with 47 additions and 6 deletions
@@ -90,11 +90,28 @@ class ScheduleEntity extends Equatable {
}; };
} }
String? formatTimeOfDay(TimeOfDay? time) { String? formatTimeOfDay(TimeOfDay? time) => formatTimePadded(time);
if (time != null) {
return "${time.hour.toString()}:${time.minute.toString()}"; /// "08:05", never "8:5" — the backend matches /^\d{2}:\d{2}$/.
} static String? formatTimePadded(TimeOfDay? time) {
return null; if (time == null) return null;
final h = time.hour.toString().padLeft(2, '0');
final m = time.minute.toString().padLeft(2, '0');
return '$h:$m';
}
/// The row shape PATCH /professionals/me/schedules expects.
/// [dayOfWeek] is 0 = Monday … 6 = Sunday, the backend's own convention.
Map<String, dynamic> toScheduleDto(int dayOfWeek) {
return {
'day_of_week': dayOfWeek,
'enabled': enabled,
'continuous_day': continuousDay,
'range1_hour1': formatTimePadded(range1Hour1),
'range1_hour2': formatTimePadded(range1Hour2),
'range2_hour1': formatTimePadded(range2Hour1),
'range2_hour2': formatTimePadded(range2Hour2),
};
} }
static String? getFormatTime(TimeOfDay? time) { static String? getFormatTime(TimeOfDay? time) {
@@ -63,6 +63,24 @@ class Schedules extends Equatable {
); );
} }
/// The array PATCH /professionals/me/schedules expects, ordered
/// 0 = Monday … 6 = Sunday.
///
/// The map form below is NOT accepted by the backend: sending it to
/// /professionals/me returns 200 and silently discards the schedules, which
/// is why editing opening hours appeared to work and never persisted.
List<Map<String, dynamic>> toSchedulesArray() {
return [
monday.toScheduleDto(0),
tuesday.toScheduleDto(1),
wednesday.toScheduleDto(2),
thursday.toScheduleDto(3),
friday.toScheduleDto(4),
saturday.toScheduleDto(5),
sunday.toScheduleDto(6),
];
}
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
return { return {
'monday': monday.toJson(), 'monday': monday.toJson(),
@@ -234,6 +234,9 @@ class ApiProfessionalRepository {
PaymentMethodEntity paymentMethods, { PaymentMethodEntity paymentMethods, {
int slotDurationMinutes = 120, int slotDurationMinutes = 120,
}) async { }) async {
// Two calls on purpose: /professionals/me ignores a `schedules` key
// (returns 200 and drops it), and the schedules endpoint takes the array
// form. Sending them together is what made saving opening hours a no-op.
await _patch('/professionals/me', { await _patch('/professionals/me', {
'address': address, 'address': address,
'aditional_address': aditionalAddress, 'aditional_address': aditionalAddress,
@@ -242,11 +245,14 @@ class ApiProfessionalRepository {
'location_preferences': enumToInt(locationPreferences), 'location_preferences': enumToInt(locationPreferences),
'latitude': latitude, 'latitude': latitude,
'longitude': longitude, 'longitude': longitude,
'schedules': schedules.toJson(),
'payment_methods': paymentMethods.toDocument(), 'payment_methods': paymentMethods.toDocument(),
'slot_duration_minutes': slotDurationMinutes, 'slot_duration_minutes': slotDurationMinutes,
}); });
await _patch('/professionals/me/schedules', {
'schedules': schedules.toSchedulesArray(),
});
if (_proInfo != null) { if (_proInfo != null) {
await updateFromFirebase(userId: _proInfo!.id); await updateFromFirebase(userId: _proInfo!.id);
} }