diff --git a/lib/blocs/professional_bloc/professional_bloc.dart b/lib/blocs/professional_bloc/professional_bloc.dart index bbf97b4..3ca3d26 100644 --- a/lib/blocs/professional_bloc/professional_bloc.dart +++ b/lib/blocs/professional_bloc/professional_bloc.dart @@ -39,25 +39,43 @@ class ProfessionalBloc extends Bloc { on((event, emit) async { final myUser = await _userRepository.lastUser(); if (myUser == null) return; - await _professionalRepository.saveProfessionalInfo(ProfessionalEntity( - id: event.id, - identification: event.identification, - identificationPicture: event.identificationPicture, - address: '', - profession: event.profession, - specializations: event.specializations, - specializationsPictures: event.specializationsPictures, - certificatePicture: event.certificatePicture, - latitude: '', - longitude: '', - rate: '', - location: '', - schedules: Schedules.empty, - paymentMethods: PaymentMethodEntity.empty, - )); + try { + final identificationPdfUrl = await _professionalRepository + .uploadPdfCedula(event.identificationPicture, myUser.id); - _userRepository - .updateUserInfo(myUser.copyWith(proState: ProState.pending)); + final certificatePdfUrl = await _professionalRepository + .uploadPdfCertificado(event.certificatePicture, myUser.id); + + List specializationsPdfUrls = []; + if (event.specializationsPictures.isNotEmpty) { + specializationsPdfUrls = + await _professionalRepository.uploadPdfsEspecializaciones( + event.specializationsPictures, myUser.id); + } else { + specializationsPdfUrls = []; + } + + await _professionalRepository.saveProfessionalInfo(ProfessionalEntity( + id: event.id, + identification: event.identification, + identificationPicture: identificationPdfUrl, + address: '', + profession: event.profession, + specializations: event.specializations, + specializationsPictures: specializationsPdfUrls, + certificatePicture: certificatePdfUrl, + latitude: '', + longitude: '', + rate: '', + location: '', + schedules: Schedules.empty, + paymentMethods: PaymentMethodEntity.empty, + )); + } catch (e) { + log('error acceso a pro ${e.toString()}'); + } + // _userRepository + // .updateUserInfo(myUser.copyWith(proState: ProState.pending)); }); } } diff --git a/lib/blocs/profile_bloc/profile_bloc.dart b/lib/blocs/profile_bloc/profile_bloc.dart index a1b2e96..2bc74e5 100644 --- a/lib/blocs/profile_bloc/profile_bloc.dart +++ b/lib/blocs/profile_bloc/profile_bloc.dart @@ -7,19 +7,15 @@ part 'profile_state.dart'; class ProfileBloc extends Bloc { final UserRepository _userRepository; - // final AuthBloc _authBloc; // lo nuevo ProfileBloc({ required UserRepository userRepository, - // required AuthBloc authBloc }) : _userRepository = userRepository, - // _authBloc = authBloc, super(UpdateUserInfoInitial()) { on(_onUpdateUserInfo); } - void _onUpdateUserInfo( - UpdateUserInfo event, Emitter emit) async { + void _onUpdateUserInfo(UpdateUserInfo event, Emitter emit) async { emit(UpdateUserInfoLoading()); try { final userImageUrl = event.filePicture != null diff --git a/lib/components/general_drawer_header.dart b/lib/components/general_drawer_header.dart index af92f37..070fb0b 100644 --- a/lib/components/general_drawer_header.dart +++ b/lib/components/general_drawer_header.dart @@ -20,15 +20,16 @@ class GeneralDrawerHeader extends StatelessWidget { CupertinoPageRoute( builder: (BuildContext context) { return const ProfileScreen(); - // return const CityScreen(); }, ), ); }, title: Text(user.name ?? '', + overflow: TextOverflow.ellipsis, style: const TextStyle(fontWeight: FontWeight.bold)), - subtitle: - Text(user.drawerLabel, style: const TextStyle(fontSize: 12)), + subtitle: Text(user.drawerLabel, + overflow: TextOverflow.ellipsis, + style: const TextStyle(fontSize: 12)), leading: pictureWidget(user.picture, context), trailing: const Icon(Icons.keyboard_arrow_right, color: Colors.black), diff --git a/lib/screens/profession/profession_screen.dart b/lib/screens/profession/profession_screen.dart index e852c45..4daa4fb 100644 --- a/lib/screens/profession/profession_screen.dart +++ b/lib/screens/profession/profession_screen.dart @@ -29,7 +29,6 @@ class _ProfessionScreenState extends State { void _loadProfessions() { professionRepository.getProfessions().then((Professions element) { setState(() { - log(element.toString()); _professions = element.professions; _filteredProfessions = _professions; _isLoading = false; diff --git a/lib/screens/professional/professional_form_screen.dart b/lib/screens/professional/professional_form_screen.dart index c77d28a..1edd473 100644 --- a/lib/screens/professional/professional_form_screen.dart +++ b/lib/screens/professional/professional_form_screen.dart @@ -7,7 +7,10 @@ import 'package:image_picker/image_picker.dart'; import 'package:prosappco/blocs/auth_bloc/auth_bloc.dart'; import 'package:prosappco/blocs/my_user_bloc/my_user_bloc.dart'; import 'package:prosappco/blocs/professional_bloc/professional_bloc.dart'; +import 'package:prosappco/blocs/profile_bloc/profile_bloc.dart'; import 'package:prosappco/screens/profession/profession_screen.dart'; +import 'package:user_repository/user_repository.dart'; +import 'package:file_picker/file_picker.dart'; class ProfessionalFormScreen extends StatefulWidget { const ProfessionalFormScreen({super.key}); @@ -19,10 +22,13 @@ class ProfessionalFormScreen extends StatefulWidget { class _ProfessionalFormScreenState extends State { final TextEditingController _cedulaController = TextEditingController(); final TextEditingController _professionController = TextEditingController(); - final TextEditingController _controller = TextEditingController(); + final TextEditingController _specialityController = TextEditingController(); final List _items = []; XFile? _imageFile; + PlatformFile? _cedulaPdfFile; + PlatformFile? _certificadoPdfFile; + List? _especializacionesPdfFiles = []; late final AuthBloc authBloc; @@ -83,6 +89,19 @@ class _ProfessionalFormScreenState extends State { return null; }, ), + const SizedBox(height: 10.0), + ElevatedButton( + onPressed: _pickCedulaPDF, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text('Cargar pdf de la cedula'), + _cedulaPdfFile != null + ? const Icon(Icons.check) + : const Icon(Icons.file_upload_outlined), + ], + ), + ), const SizedBox(height: 20.0), TextFormField( controller: _professionController, @@ -123,9 +142,22 @@ class _ProfessionalFormScreenState extends State { return null; }, ), + const SizedBox(height: 10.0), + ElevatedButton( + onPressed: _pickCertificadoPDF, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text('Cargar pdf del certificado'), + _certificadoPdfFile != null + ? const Icon(Icons.check) + : const Icon(Icons.file_upload_outlined), + ], + ), + ), const SizedBox(height: 20.0), TextField( - controller: _controller, + controller: _specialityController, onSubmitted: (value) { _addItemToList(); }, @@ -151,24 +183,140 @@ class _ProfessionalFormScreenState extends State { ), ), const SizedBox(height: 10.0), + ElevatedButton( + onPressed: _pickEspecializacionesPDF, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Expanded( + child: Text( + 'Cargar pdfs de tus especializaciones', + overflow: TextOverflow.ellipsis, + maxLines: 2, + ), + ), + _especializacionesPdfFiles?.isNotEmpty == true + ? const Icon(Icons.check) + : const Icon(Icons.file_upload_outlined), + ], + ), + ), + const SizedBox(height: 10.0), Wrap( spacing: 8.0, runSpacing: 4.0, children: _items .map((item) => Chip( label: Text(item), - backgroundColor: Theme.of(context).primaryColor, - labelStyle: - const TextStyle(color: Colors.white), - deleteIconColor: Colors.white, + backgroundColor: + Theme.of(context).colorScheme.tertiary, + labelStyle: const TextStyle(color: Colors.blue), + deleteIconColor: Colors.blue, onDeleted: () { _removeItemFromList(item); }, + shape: RoundedRectangleBorder( + side: const BorderSide( + color: Colors.blue, width: 0.3), + borderRadius: BorderRadius.circular(8), + ), )) .toList(), ), - const SizedBox(height: 60.0), - saveButton(state, context), + const SizedBox(height: 40), + ElevatedButton( + onPressed: () { + final userId = + context.read().state.user!.id; + + final String? cedulaPdfPath = _cedulaPdfFile?.path; + final String? certificadoPdfPath = + _certificadoPdfFile?.path; + + final List especializacionesPdfPaths = + _especializacionesPdfFiles + ?.map((file) => file.path) + .where((file) => file != null) + .map((e) => e!) + .toList() ?? + []; + + if (_cedulaController.text.isEmpty || + _professionController.text.isEmpty) { + ScaffoldMessenger.of(context).clearSnackBars(); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text( + 'Llena los campos de cedula y profesión'))); + return; + } + + if (cedulaPdfPath == null) { + ScaffoldMessenger.of(context).clearSnackBars(); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Carga el pdf de tu cedula'))); + return; + } + if (certificadoPdfPath == null) { + ScaffoldMessenger.of(context).clearSnackBars(); + ScaffoldMessenger.of(context).showSnackBar(const SnackBar( + content: Text( + 'Carga el pdf de tu certificado profesional'))); + return; + } + + context + .read() + .add(SendProfessionalToReviewEvent( + id: userId, + identification: _cedulaController.text, + identificationPicture: cedulaPdfPath, + profession: _professionController.text, + certificatePicture: certificadoPdfPath, + specializations: _items, + specializationsPictures: + especializacionesPdfPaths, + )); + + final myUser = + state.user!.copyWith(proState: ProState.pending); + + context.read().add(UpdateUserInfo( + myUser: myUser, filePicture: _imageFile?.path)); + + ScaffoldMessenger.of(context).clearSnackBars(); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Información enviada a revisión')), + ); + + Navigator.pop(context); + }, + style: ElevatedButton.styleFrom( + backgroundColor: Colors.blue, + padding: const EdgeInsets.symmetric(vertical: 5), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(50), + ), + shadowColor: Colors.grey, + // elevation: 0, + ), + child: Container( + constraints: const BoxConstraints( + maxWidth: 300.0, minHeight: 50.0), + alignment: Alignment.center, + child: const Text( + 'Enviar a revisión', + style: TextStyle( + color: Colors.white, + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + const SizedBox(height: 20), ], ), ), @@ -181,10 +329,10 @@ class _ProfessionalFormScreenState extends State { void _addItemToList() { setState(() { - String newItem = _controller.text.trim(); + String newItem = _specialityController.text.trim(); if (newItem.isNotEmpty) { _items.add(newItem); - _controller.clear(); + _specialityController.clear(); } }); } @@ -195,81 +343,44 @@ class _ProfessionalFormScreenState extends State { }); } - Widget saveButton(MyUserState state, BuildContext context) { - return ElevatedButton( - onPressed: () { - final userId = context.read().state.user!.id; - context.read().add(SendProfessionalToReviewEvent( - id: userId, - identification: _cedulaController.text, - identificationPicture: _cedulaController.text, - profession: _professionController.text, - certificatePicture: _professionController.text, - specializations: _items, - specializationsPictures: _items, - )); - - // if (isLoading) { - // return; - // } - - // if (_nameController.text.isEmpty) { - // ScaffoldMessenger.of(context).clearSnackBars(); - // ScaffoldMessenger.of(context).showSnackBar( - // const SnackBar(content: Text('Por favor, ingrese su nombre'))); - - // return; - // } - - // if (_cityController.text.isEmpty) { - // ScaffoldMessenger.of(context).clearSnackBars(); - // ScaffoldMessenger.of(context).showSnackBar( - // const SnackBar(content: Text('Por favor, ingrese su ciudad'))); - // } - - // final myUser = state.user!.copyWith( - // name: _nameController.text, - // city: _cityController.text, - // nickname: _nameController.text.trim().toLowerCase(), - // email: _emailController.text, - // phone: _phoneController.text, - // birthday: _birthdayController.text, - // gender: _genderController.text, - // ); - - // context - // .read() - // .add(UpdateUserInfo(myUser: myUser, filePicture: _imageFile?.path)); - - ScaffoldMessenger.of(context).clearSnackBars(); - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('Información actualizada...')), - ); - - Navigator.pop(context); - }, - style: ElevatedButton.styleFrom( - backgroundColor: Colors.blue, - padding: const EdgeInsets.symmetric(vertical: 5), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(50), - ), - shadowColor: Colors.grey, - // elevation: 0, - ), - child: Container( - constraints: const BoxConstraints(maxWidth: 300.0, minHeight: 50.0), - alignment: Alignment.center, - child: const Text( - 'Actualizar', - style: TextStyle( - color: Colors.white, - fontSize: 16, - fontWeight: FontWeight.bold, - ), - ), - ), + Future _pickCedulaPDF() async { + FilePickerResult? result = await FilePicker.platform.pickFiles( + type: FileType.custom, + allowedExtensions: ['pdf'], ); + + if (result != null) { + setState(() { + _cedulaPdfFile = result.files.first; + }); + } + } + + Future _pickCertificadoPDF() async { + FilePickerResult? result = await FilePicker.platform.pickFiles( + type: FileType.custom, + allowedExtensions: ['pdf'], + ); + + if (result != null) { + setState(() { + _certificadoPdfFile = result.files.first; + }); + } + } + + Future _pickEspecializacionesPDF() async { + FilePickerResult? result = await FilePicker.platform.pickFiles( + type: FileType.custom, + allowedExtensions: ['pdf'], + allowMultiple: true, + ); + + if (result != null) { + setState(() { + _especializacionesPdfFiles = result.files; + }); + } } Widget pictureWidget(MyUserState state, BuildContext context) { diff --git a/packages/professional_repository/lib/src/repositories/firebase_professional_repository.dart b/packages/professional_repository/lib/src/repositories/firebase_professional_repository.dart index 9c65dd2..1c080b3 100644 --- a/packages/professional_repository/lib/src/repositories/firebase_professional_repository.dart +++ b/packages/professional_repository/lib/src/repositories/firebase_professional_repository.dart @@ -1,11 +1,12 @@ import 'dart:async'; +import 'dart:developer'; +import 'dart:io'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:professional_repository/professional_repository.dart'; - +import 'package:firebase_storage/firebase_storage.dart'; class FirebaseProfessionalRepository { - bool isProModeActive = false; final professionalCollection = FirebaseFirestore.instance.collection('professional_info'); @@ -28,4 +29,52 @@ class FirebaseProfessionalRepository { Future saveProfessionalInfo(ProfessionalEntity entity) async { await professionalCollection.doc(entity.id).set(entity.toJson()); } + + Future uploadPdfCedula(String file, String userId) async { + try { + File pdfFile = File(file); + Reference firebaseStoreRef = + FirebaseStorage.instance.ref().child('$userId/PDF/${userId}_cedula'); + await firebaseStoreRef.putFile(pdfFile); + String url = await firebaseStoreRef.getDownloadURL(); + return url; + } catch (e) { + log(e.toString()); + rethrow; + } + } + + Future uploadPdfCertificado(String file, String userId) async { + try { + File pdfFile = File(file); + Reference firebaseStoreRef = FirebaseStorage.instance + .ref() + .child('$userId/PDF/${userId}_certificado'); + await firebaseStoreRef.putFile(pdfFile); + String url = await firebaseStoreRef.getDownloadURL(); + return url; + } catch (e) { + log(e.toString()); + rethrow; + } + } + + Future> uploadPdfsEspecializaciones( + List files, String userId) async { + try { + List urls = []; + for (String file in files) { + File pdfFile = File(file); + Reference firebaseStoreRef = FirebaseStorage.instance.ref().child( + '$userId/PDF/${userId}_${DateTime.now().millisecondsSinceEpoch}_especializacion'); + await firebaseStoreRef.putFile(pdfFile); + String url = await firebaseStoreRef.getDownloadURL(); + urls.add(url); + } + return urls; + } catch (e) { + log(e.toString()); + rethrow; + } + } } diff --git a/packages/professional_repository/pubspec.lock b/packages/professional_repository/pubspec.lock index f0c51e2..3ec5301 100644 --- a/packages/professional_repository/pubspec.lock +++ b/packages/professional_repository/pubspec.lock @@ -113,6 +113,30 @@ packages: url: "https://pub.dev" source: hosted version: "2.11.5" + firebase_storage: + dependency: "direct main" + description: + name: firebase_storage + sha256: ce1b0efe8dc111058c5f079b2f2ce84906d030d0fd2eef70c42ca1253c67039a + url: "https://pub.dev" + source: hosted + version: "11.6.9" + firebase_storage_platform_interface: + dependency: transitive + description: + name: firebase_storage_platform_interface + sha256: "4a2b64d4dac096390a0b7f2e7b6d086d0546c4a1bf7ee3fc4b5ae4cc41005c46" + url: "https://pub.dev" + source: hosted + version: "5.1.12" + firebase_storage_web: + dependency: transitive + description: + name: firebase_storage_web + sha256: "4153814db8d59138e816d9f016736e4095c45675a2c18f2868d11ffd8cc6a4ca" + url: "https://pub.dev" + source: hosted + version: "3.7.3" flutter: dependency: "direct main" description: flutter @@ -136,6 +160,22 @@ packages: description: flutter source: sdk version: "0.0.0" + http: + dependency: transitive + description: + name: http + sha256: a2bbf9d017fcced29139daa8ed2bba4ece450ab222871df93ca9eec6f80c34ba + url: "https://pub.dev" + source: hosted + version: "1.2.0" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.dev" + source: hosted + version: "4.0.2" intl: dependency: "direct main" description: @@ -253,6 +293,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.1" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + url: "https://pub.dev" + source: hosted + version: "1.3.2" vector_math: dependency: transitive description: diff --git a/packages/professional_repository/pubspec.yaml b/packages/professional_repository/pubspec.yaml index e313776..d71c85d 100644 --- a/packages/professional_repository/pubspec.yaml +++ b/packages/professional_repository/pubspec.yaml @@ -14,6 +14,7 @@ dependencies: # Firebase cloud_firestore: ^4.15.4 + firebase_storage: ^11.6.5 firebase_core: ^2.25.4 intl: ^0.18.1 diff --git a/packages/user_repository/lib/src/repositories/firebase_user_repository.dart b/packages/user_repository/lib/src/repositories/firebase_user_repository.dart index 427978c..7e38927 100644 --- a/packages/user_repository/lib/src/repositories/firebase_user_repository.dart +++ b/packages/user_repository/lib/src/repositories/firebase_user_repository.dart @@ -367,8 +367,7 @@ class FirebaseUserRepository implements UserRepository { Future uploadPicture(String file, String userId) async { try { File imageFile = File(file); - Reference firebaseStoreRef = - FirebaseStorage.instance.ref().child('$userId/PP/${userId}_lead'); + Reference firebaseStoreRef = FirebaseStorage.instance.ref().child('$userId/PP/${userId}_lead'); await firebaseStoreRef.putFile(imageFile); String url = await firebaseStoreRef.getDownloadURL(); return url;