feat(calendar): add block/unblock slots for professionals

- ServicesProvider: add blockSlot() calling POST /services/block and
  unblockSlot() calling PATCH /services/:id/status with cancelled
- ProfessionalCalendarView:
  - Fix _services always-null bug: getServicesForProfessional() returns
    void; now reads from sp.services after it resolves
  - Differentiate self_booked (blocked) vs regular occupied slots
  - Show amber 'Bloqueado' state with lock icon for self_booked services
  - Show lock button on available slots → confirm dialog → blockSlot()
  - Show unlock button on blocked slots → confirm dialog → unblockSlot()
  - Reload calendar after block/unblock so UI reflects new state
  - Header stats now show separate counts: ocupadas / bloqueadas / libres
  - _ActionButton widget for reusable tap-target icon buttons

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Lizandro Guarnizo
2026-07-12 18:26:45 -05:00
co-authored by Claude Sonnet 4.6
parent 498481cab0
commit 52845c59fd
2 changed files with 163 additions and 24 deletions
+12
View File
@@ -51,6 +51,18 @@ class ServicesProvider extends ChangeNotifier {
notifyListeners();
}
// Blocks a slot on the professional's calendar by creating a self_booked service.
Future<void> blockSlot(String day, TimeOfDay time) async {
final h = time.hour.toString().padLeft(2, '0');
final m = time.minute.toString().padLeft(2, '0');
await _api.post('/services/block', {'day': day, 'range1_hour1': '$h:$m'});
}
// Unblocks a previously blocked slot by cancelling the self_booked service.
Future<void> unblockSlot(String serviceId) async {
await _api.patch('/services/$serviceId/status', {'status': 'cancelled'});
}
getServiceForUser(String serviceId) async {
try {
isLoading = true;