Files
prosappweb/lib/models/service.dart
T
Lizandro GuarnizoandClaude Sonnet 4.6 498481cab0 fix(scheduling): fix schedule save endpoint, time format, and calendar load
Critical fixes for the scheduling flow:

- ScheduleEntity: add toScheduleDto() with snake_case keys, day_of_week,
  and HH:MM zero-padded format required by the backend DTO @Matches validator
- Schedules: add toSchedulesArray() converting the day-keyed object to the
  array format expected by PATCH /professionals/me/schedules
- ProfessionalFormProvider: updateProfesionalProfileScheduleInfo now calls
  the correct endpoint (/professionals/me/schedules) instead of /professionals/me
  which was stripping the schedules field via ValidationPipe whitelist
- Service.formatTimeOfDay: zero-pad hours and minutes so POST /services passes
  the @Matches(/^\d{2}:\d{2}$/) DTO validation
- ProfessionalProvider: add getProfessionalById() calling /professionals/:id
  (public endpoint) to load any professional's data, not the viewer's own
- CalendarServicesProvider: add getPublicServicesForProfessional() calling
  /services/public-calendar/:id so the user calendar shows the target
  professional's booked slots, not the viewer's own services
- CalendarView: use getProfessionalById + getPublicServicesForProfessional
  so the calendar correctly reflects the selected professional's schedule

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-12 17:52:02 -05:00

127 lines
4.0 KiB
Dart

import 'package:flutter/material.dart';
import 'package:prosapp_web_app/models/service_location_preferences.dart';
import 'package:prosapp_web_app/models/service_status.dart';
class Service {
final String? id;
final String professionalId;
final bool professionalScored;
final String userId;
final bool userScored;
final String address;
final String aditionalAddress;
final double latitude;
final double longitude;
final String day;
final String createdAt;
final String description;
final TimeOfDay range1Hour1;
final TimeOfDay range1Hour2;
final String rate;
final ServiceStatus status;
final ServiceLocationPreferences location;
Service({
required this.id,
required this.professionalId,
required this.professionalScored,
required this.userId,
required this.userScored,
required this.address,
required this.aditionalAddress,
required this.latitude,
required this.longitude,
required this.day,
required this.createdAt,
required this.description,
required this.range1Hour1,
required this.range1Hour2,
required this.rate,
required this.status,
required this.location,
});
static const _statusMap = {
'pending': 0, 'accepted': 1, 'denied': 2, 'active': 3,
'cancelled': 4, 'completed': 5, 'self_booked': 6,
};
static Service fromJson(Map<String, dynamic> doc, String id) {
final rawStatus = doc['status'];
final statusInt = rawStatus is int ? rawStatus
: rawStatus is String ? (_statusMap[rawStatus] ?? 0)
: (rawStatus as num?)?.toInt() ?? 0;
final rawLoc = doc['location_preference'] ?? doc['location'];
final locInt = rawLoc == 'delivery' ? 1
: rawLoc == 'office' ? 0
: (rawLoc as num?)?.toInt() ?? 0;
return Service(
id: id,
professionalId: doc['professional_id'] as String,
professionalScored: doc['professional_scored'] as bool? ?? false,
userId: doc['user_id'] as String,
userScored: doc['user_scored'] as bool? ?? false,
address: doc['address'] as String? ?? '',
aditionalAddress: (doc['additional_address'] ?? doc['aditional_address']) as String? ?? '',
latitude: (doc['latitude'] as num?)?.toDouble() ?? 0.0,
longitude: (doc['longitude'] as num?)?.toDouble() ?? 0.0,
day: doc['day']?.toString() ?? '',
createdAt: doc['created_at']?.toString() ?? '',
description: doc['description'] as String? ?? '',
range1Hour1: _parseTime(doc['range1_hour1']?.toString() ?? '0:0'),
range1Hour2: _parseTime(doc['range1_hour2']?.toString() ?? '0:0'),
status: intToEnumService(statusInt),
rate: doc['rate']?.toString() ?? '',
location: intToEnum(locInt),
);
}
static TimeOfDay _parseTime(String t) {
// Handle ISO8601 (e.g. "1970-01-01T08:30:00.000Z") or "H:M"
final s = t.contains('T') ? t.split('T')[1] : t;
final parts = s.split(':');
return TimeOfDay(
hour: int.tryParse(parts[0]) ?? 0,
minute: int.tryParse(parts.length > 1 ? parts[1] : '0') ?? 0,
);
}
Map<String, dynamic> toDocument() {
return {
'id': id,
'professional_id': professionalId,
'professional_scored': professionalScored,
'user_id': userId,
'user_scored': userScored,
'address': address,
'aditional_address': aditionalAddress,
'latitude': latitude,
'longitude': longitude,
'day': day,
'created_at': createdAt,
'description': description,
'range1_hour1': formatTimeOfDay(range1Hour1),
'range1_hour2': formatTimeOfDay(range1Hour2),
'rate': rate,
'status': enumToIntService(status),
'location': enumToInt(location),
};
}
static TimeOfDay parseTimeOfDay(String timeString) => _parseTime(timeString);
String? formatTimeOfDay(TimeOfDay? time) {
if (time == null) return null;
final h = time.hour.toString().padLeft(2, '0');
final m = time.minute.toString().padLeft(2, '0');
return '$h:$m';
}
@override
String toString() {
return 'Service{professionalId: $professionalId, userId: $userId, day: $day, status: $status}';
}
}