Prisma serializes Decimal fields (rate, latitude, longitude) as JSON strings, causing 'as num?' casts to throw in fromDocument(). The catch block in getProfessional() then returned an empty Profesional, so saving overwrote all existing data with empty strings. Also added try-catch in all patch API calls to surface errors to user instead of silently failing with a stuck loading button. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
143 lines
5.1 KiB
Dart
143 lines
5.1 KiB
Dart
import 'dart:typed_data';
|
|
import 'package:flutter/material.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/profesional.dart';
|
|
import 'package:prosapp_web_app/models/schedules.dart';
|
|
import 'package:prosapp_web_app/services/api_service.dart';
|
|
import 'package:prosapp_web_app/services/notifications_service.dart';
|
|
|
|
class ProfessionalFormProvider with ChangeNotifier {
|
|
Profesional? profesional;
|
|
GlobalKey<FormState> formKey = GlobalKey<FormState>();
|
|
GlobalKey<FormState> profileFormKey = GlobalKey<FormState>();
|
|
final _api = ApiService.instance;
|
|
|
|
copyProfesionalWith({
|
|
String? id,
|
|
String? identification,
|
|
String? rethusCode,
|
|
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,
|
|
}) {
|
|
profesional = Profesional(
|
|
id: id ?? profesional!.id,
|
|
identification: identification ?? profesional!.identification,
|
|
rethusCode: rethusCode ?? profesional!.rethusCode,
|
|
rethusValidated: profesional!.rethusValidated,
|
|
address: address ?? profesional!.address,
|
|
aditionalAddress: aditionalAddress ?? profesional!.aditionalAddress,
|
|
profession: profession ?? profesional!.profession,
|
|
ratePreferences: ratePreferences,
|
|
rate: rate ?? profesional!.rate,
|
|
locationPreferences: locationPreferences ?? profesional!.locationPreferences,
|
|
bannerPicture: bannerPicture ?? profesional!.bannerPicture,
|
|
identificationPicture: identificationPicture ?? profesional!.identificationPicture,
|
|
certificatePicture: certificatePicture ?? profesional!.certificatePicture,
|
|
latitude: latitude ?? profesional!.latitude,
|
|
longitude: longitude ?? profesional!.longitude,
|
|
specializations: specializations ?? profesional!.specializations,
|
|
specializationsPictures: specializationsPictures ?? profesional!.specializationsPictures,
|
|
schedules: schedules ?? profesional!.schedules,
|
|
paymentMethods: paymentMethods ?? profesional!.paymentMethods,
|
|
);
|
|
notifyListeners();
|
|
}
|
|
|
|
bool _validForm() => formKey.currentState!.validate();
|
|
bool _validProfileForm() => profileFormKey.currentState!.validate();
|
|
|
|
void clear() {
|
|
profesional = null;
|
|
notifyListeners();
|
|
}
|
|
|
|
setProfesional(Profesional p) {
|
|
profesional = p;
|
|
notifyListeners();
|
|
}
|
|
|
|
Future<bool> updateProfesionalInfo(String userId) async {
|
|
if (!_validForm()) return false;
|
|
try {
|
|
await _api.patch('/professionals/me', profesional!.toDocument());
|
|
} catch (e) {
|
|
NotificationsService.showSnackbar('Error al guardar: $e');
|
|
return false;
|
|
}
|
|
NotificationsService.showSnackbar('Información actualizada');
|
|
return true;
|
|
}
|
|
|
|
Future<void> submitForReview() async {
|
|
await _api.patch('/professionals/me', profesional!.toDocument());
|
|
NotificationsService.showSnackbar('Solicitud enviada a revisión');
|
|
}
|
|
|
|
Future<bool> updateProfesionalProfileInfo(String userId) async {
|
|
if (!_validProfileForm()) return false;
|
|
try {
|
|
await _api.patch('/professionals/me', profesional!.toDocument());
|
|
} catch (e) {
|
|
NotificationsService.showSnackbar('Error al guardar: $e');
|
|
return false;
|
|
}
|
|
try {
|
|
final data = await _api.get('/professionals/me');
|
|
profesional = Profesional.fromDocument(data as Map<String, dynamic>);
|
|
notifyListeners();
|
|
} catch (_) {}
|
|
NotificationsService.showSnackbar('Información actualizada');
|
|
return true;
|
|
}
|
|
|
|
Future<bool> updateProfesionalProfileScheduleInfo(String userId) async {
|
|
try {
|
|
await _api.patch('/professionals/me', profesional!.toDocument());
|
|
} catch (e) {
|
|
NotificationsService.showSnackbar('Error al guardar: $e');
|
|
return false;
|
|
}
|
|
NotificationsService.showSnackbar('Información actualizada');
|
|
return true;
|
|
}
|
|
|
|
Future<Profesional> uploadPdfIdentification(Uint8List fileBytes, String userId) async {
|
|
final url = await _api.upload(fileBytes, '${userId}_cedula.pdf');
|
|
if (url != null) copyProfesionalWith(identificationPicture: url);
|
|
notifyListeners();
|
|
return profesional!;
|
|
}
|
|
|
|
Future<Profesional> uploadPdfCertificate(Uint8List fileBytes, String userId) async {
|
|
final url = await _api.upload(fileBytes, '${userId}_certificado.pdf');
|
|
if (url != null) copyProfesionalWith(certificatePicture: url);
|
|
notifyListeners();
|
|
return profesional!;
|
|
}
|
|
|
|
Future<Profesional> uploadPdfSpecializations(List<Uint8List> filesBytes, String userId) async {
|
|
List<String> urls = [];
|
|
for (int i = 0; i < filesBytes.length; i++) {
|
|
final url = await _api.upload(filesBytes[i], '${userId}_especializacion_$i.pdf');
|
|
if (url != null) urls.add(url);
|
|
}
|
|
copyProfesionalWith(specializationsPictures: urls);
|
|
notifyListeners();
|
|
return profesional!;
|
|
}
|
|
}
|