Files
prosappweb/lib/providers/professional_form_provider.dart
T
Lizandro GuarnizoandClaude Sonnet 4.6 706b78b737 fix: safe parsing of Prisma Decimal fields to prevent data loss on save
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>
2026-07-06 22:00:29 -05:00

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!;
}
}