- Add Profesional.empty() factory for users with no professional record - initState: catch error from getProfessional and show empty form instead of infinite spinner - Backend upsert: create professional record if none exists (no longer throws 404) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
245 lines
8.8 KiB
Dart
245 lines
8.8 KiB
Dart
import 'package:prosapp_web_app/models/location_preferences.dart';
|
|
import 'package:prosapp_web_app/models/payment_method_entity.dart';
|
|
import 'package:prosapp_web_app/models/schedules.dart';
|
|
import 'package:prosapp_web_app/models/schedules_entity.dart';
|
|
|
|
class Profesional {
|
|
final String id;
|
|
final String identification;
|
|
final String rethusCode;
|
|
final bool rethusValidated;
|
|
final String address;
|
|
final String aditionalAddress;
|
|
final String profession;
|
|
final bool ratePreferences;
|
|
final String rate;
|
|
final LocationPreferences locationPreferences;
|
|
final String bannerPicture;
|
|
final String identificationPicture;
|
|
final String certificatePicture;
|
|
final double latitude;
|
|
final double longitude;
|
|
final List<String> specializations;
|
|
final List<String> specializationsPictures;
|
|
final Schedules schedules;
|
|
final PaymentMethodEntity paymentMethods;
|
|
|
|
const Profesional({
|
|
required this.id,
|
|
required this.identification,
|
|
required this.rethusCode,
|
|
required this.rethusValidated,
|
|
required this.address,
|
|
required this.aditionalAddress,
|
|
required this.profession,
|
|
required this.ratePreferences,
|
|
required this.rate,
|
|
required this.locationPreferences,
|
|
required this.bannerPicture,
|
|
required this.identificationPicture,
|
|
required this.certificatePicture,
|
|
required this.latitude,
|
|
required this.longitude,
|
|
required this.specializations,
|
|
required this.specializationsPictures,
|
|
required this.schedules,
|
|
required this.paymentMethods,
|
|
});
|
|
|
|
static Profesional empty() => Profesional(
|
|
id: '',
|
|
identification: '',
|
|
rethusCode: '',
|
|
rethusValidated: false,
|
|
address: '',
|
|
aditionalAddress: '',
|
|
profession: '',
|
|
ratePreferences: false,
|
|
rate: '',
|
|
locationPreferences: LocationPreferences.office,
|
|
bannerPicture: '',
|
|
identificationPicture: '',
|
|
certificatePicture: '',
|
|
latitude: 0.0,
|
|
longitude: 0.0,
|
|
specializations: [],
|
|
specializationsPictures: [],
|
|
schedules: Schedules.empty,
|
|
paymentMethods: PaymentMethodEntity.empty,
|
|
);
|
|
|
|
static Profesional fromDocument(Map<String, dynamic> doc) {
|
|
// Backend returns schedules as array [{day_of_week, enabled, ...}]; convert to Schedules
|
|
Schedules parsedSchedules = Schedules.empty;
|
|
final rawSchedules = doc['schedules'];
|
|
if (rawSchedules is List && rawSchedules.isNotEmpty) {
|
|
final days = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'];
|
|
final map = <int, Map<String, dynamic>>{};
|
|
for (final s in rawSchedules) {
|
|
map[(s['day_of_week'] as int)] = s as Map<String, dynamic>;
|
|
}
|
|
ScheduleEntity _se(int day) {
|
|
final s = map[day];
|
|
if (s == null) return ScheduleEntity.empty;
|
|
return ScheduleEntity(
|
|
enabled: (s['enabled'] as bool?) ?? false,
|
|
continuousDay: (s['continuous_day'] as bool?) ?? false,
|
|
range1Hour1: ScheduleEntity.parseTime(s['range1_hour1']?.toString()),
|
|
range1Hour2: ScheduleEntity.parseTime(s['range1_hour2']?.toString()),
|
|
range2Hour1: ScheduleEntity.parseTime(s['range2_hour1']?.toString()),
|
|
range2Hour2: ScheduleEntity.parseTime(s['range2_hour2']?.toString()),
|
|
);
|
|
}
|
|
parsedSchedules = Schedules(
|
|
monday: _se(0), tuesday: _se(1), wednesday: _se(2), thursday: _se(3),
|
|
friday: _se(4), saturday: _se(5), sunday: _se(6),
|
|
);
|
|
} else if (rawSchedules is Map) {
|
|
parsedSchedules = Schedules.fromDocument(rawSchedules as Map<String, dynamic>);
|
|
}
|
|
|
|
// Specializations: array of objects [{name, picture}] or list of strings
|
|
List<String> specs = [];
|
|
List<String> specPics = [];
|
|
final rawSpecs = doc['specializations'];
|
|
if (rawSpecs is List) {
|
|
for (final s in rawSpecs) {
|
|
if (s is Map) {
|
|
specs.add((s['name'] as String?) ?? '');
|
|
specPics.add((s['picture'] as String?) ?? '');
|
|
} else {
|
|
specs.add(s.toString());
|
|
}
|
|
}
|
|
}
|
|
final rawSpecPics = doc['specializations_pictures'];
|
|
if (specPics.isEmpty && rawSpecPics is List) {
|
|
specPics = List<String>.from(rawSpecPics);
|
|
}
|
|
|
|
// payment_methods: object {nequi, datafono, transferencia} or array with one element or null
|
|
PaymentMethodEntity pm = PaymentMethodEntity.empty;
|
|
final rawPm = doc['payment_methods'];
|
|
if (rawPm is Map<String, dynamic>) {
|
|
pm = PaymentMethodEntity.fromDocument(rawPm);
|
|
} else if (rawPm is List && rawPm.isNotEmpty) {
|
|
pm = PaymentMethodEntity.fromDocument(rawPm.first as Map<String, dynamic>);
|
|
}
|
|
|
|
return Profesional(
|
|
id: doc['id'] as String,
|
|
identification: (doc['identification'] as String?) ?? '',
|
|
rethusCode: (doc['rethus_code'] as String?) ?? '',
|
|
rethusValidated: (doc['rethus_validated'] as bool?) ?? false,
|
|
address: (doc['address'] as String?) ?? '',
|
|
aditionalAddress: (doc['aditional_address'] as String?) ?? '',
|
|
profession: (doc['profession'] as String?) ?? '',
|
|
ratePreferences: (doc['rate_preferences'] as bool?) ?? false,
|
|
rate: (doc['rate'] as num?)?.toString() ?? '',
|
|
locationPreferences: locationPrefsFromValue(doc['location_preferences']),
|
|
bannerPicture: (doc['banner_picture'] as String?) ?? '',
|
|
identificationPicture: (doc['identification_picture'] as String?) ?? '',
|
|
certificatePicture: (doc['certificate_picture'] as String?) ?? '',
|
|
latitude: (doc['latitude'] as num?)?.toDouble() ?? 0.0,
|
|
longitude: (doc['longitude'] as num?)?.toDouble() ?? 0.0,
|
|
specializations: specs,
|
|
specializationsPictures: specPics,
|
|
schedules: parsedSchedules,
|
|
paymentMethods: pm,
|
|
);
|
|
}
|
|
|
|
Profesional copyWith({
|
|
String? id,
|
|
String? identification,
|
|
String? rethusCode,
|
|
bool? rethusValidated,
|
|
String? address,
|
|
String? aditionalAddress,
|
|
String? profession,
|
|
bool ratePreferences = false,
|
|
String? rate,
|
|
LocationPreferences? locationPreferences,
|
|
String? bannerPicture,
|
|
String? identificationPicture,
|
|
String? certificatePicture,
|
|
double? latitude,
|
|
double? longitude,
|
|
List<String>? specializations,
|
|
List<String>? specializationsPictures,
|
|
Schedules? schedules,
|
|
PaymentMethodEntity? paymentMethods,
|
|
}) {
|
|
return Profesional(
|
|
id: id ?? this.id,
|
|
identification: identification ?? this.identification,
|
|
rethusCode: rethusCode ?? this.rethusCode,
|
|
rethusValidated: rethusValidated ?? this.rethusValidated,
|
|
address: address ?? this.address,
|
|
aditionalAddress: aditionalAddress ?? this.aditionalAddress,
|
|
profession: profession ?? this.profession,
|
|
ratePreferences: ratePreferences,
|
|
rate: rate ?? this.rate,
|
|
locationPreferences: locationPreferences ?? this.locationPreferences,
|
|
bannerPicture: bannerPicture ?? this.bannerPicture,
|
|
identificationPicture:
|
|
identificationPicture ?? this.identificationPicture,
|
|
certificatePicture: certificatePicture ?? this.certificatePicture,
|
|
latitude: latitude ?? this.latitude,
|
|
longitude: longitude ?? this.longitude,
|
|
specializations: specializations ?? this.specializations,
|
|
specializationsPictures:
|
|
specializationsPictures ?? this.specializationsPictures,
|
|
schedules: schedules ?? this.schedules,
|
|
paymentMethods: paymentMethods ?? this.paymentMethods,
|
|
);
|
|
}
|
|
|
|
Map<String, dynamic> toDocument() {
|
|
return {
|
|
'id': id,
|
|
'identification': identification,
|
|
'rethus_code': rethusCode,
|
|
'rethus_validated': rethusValidated,
|
|
'address': address,
|
|
'aditional_address': aditionalAddress,
|
|
'profession': profession,
|
|
'rate_preferences': ratePreferences,
|
|
'rate': double.tryParse(rate) ?? 0.0,
|
|
'location_preferences': locationPrefsToString(locationPreferences),
|
|
'banner_picture': bannerPicture,
|
|
'identification_picture': identificationPicture,
|
|
'certificate_picture': certificatePicture,
|
|
'latitude': latitude,
|
|
'longitude': longitude,
|
|
'specializations': specializations,
|
|
'specializations_pictures': specializationsPictures,
|
|
'schedules': schedules.toJson(),
|
|
'payment_methods': paymentMethods.toDocument(),
|
|
};
|
|
}
|
|
|
|
@override
|
|
String toString() {
|
|
return '''Profesional{ {
|
|
id: $id,
|
|
identification: $identification,
|
|
address: $address,
|
|
aditional_address: $aditionalAddress,
|
|
profession: $profession,
|
|
rate_preferences: $ratePreferences,
|
|
rate: $rate,
|
|
location_preferences: $locationPreferences,
|
|
bannerPicture: $bannerPicture,
|
|
identificationPicture: $identificationPicture,
|
|
certificatePicture: $certificatePicture,
|
|
latitude: $latitude,
|
|
longitude: $longitude,
|
|
specializations: $specializations,
|
|
specializationsPictures: $specializationsPictures,
|
|
schedule: $schedules,
|
|
paymentMethods: $paymentMethods
|
|
}''';
|
|
}
|
|
}
|