diff --git a/lib/app.dart b/lib/app.dart index 20b77c2..072e52e 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -4,6 +4,7 @@ import 'package:injector/injector.dart'; import 'package:prosappco/blocs/authentication_bloc/authentication_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/professional_profile_bloc/professional_profile_bloc.dart'; import 'package:prosappco/blocs/profile_bloc/profile_bloc.dart'; import 'app_view.dart'; @@ -27,6 +28,9 @@ class MainApp extends StatelessWidget { BlocProvider( create: (context) => Injector.appInstance.get(), ), + BlocProvider( + create: (context) => Injector.appInstance.get(), + ) ], child: BlocBuilder( builder: (context, state) { diff --git a/lib/app_view.dart b/lib/app_view.dart index 362d23d..dc43765 100644 --- a/lib/app_view.dart +++ b/lib/app_view.dart @@ -17,14 +17,15 @@ class MyAppView extends StatelessWidget { theme: ThemeData( colorScheme: const ColorScheme.light( background: Colors.white, - onBackground: Colors.black, + onBackground: Colors.grey, primary: Color.fromRGBO(66, 164, 239, 1), - onPrimary: Colors.black, - secondary: Color.fromRGBO(35, 108, 244, 1), + onPrimary: Colors.white, + secondary: Colors.white, onSecondary: Colors.white, tertiary: Color.fromRGBO(214, 244, 255, 1), error: Colors.red, outline: Color(0xFF424242), + secondaryContainer: Colors.red, ), ), home: BlocBuilder( diff --git a/lib/blocs/professional_bloc/professional_bloc.dart b/lib/blocs/professional_bloc/professional_bloc.dart index 151204e..7ef0e14 100644 --- a/lib/blocs/professional_bloc/professional_bloc.dart +++ b/lib/blocs/professional_bloc/professional_bloc.dart @@ -74,8 +74,8 @@ class ProfessionalBloc extends Bloc { specializations: event.specializations, specializationsPictures: specializationsPdfUrls, certificatePicture: certificatePdfUrl, - latitude: '', - longitude: '', + latitude: 0, + longitude: 0, rate: '', locationPreferences: LocationPreferences.office, bannerPicture: '', diff --git a/lib/blocs/professional_profile_bloc/professional_profile_bloc.dart b/lib/blocs/professional_profile_bloc/professional_profile_bloc.dart index f560bc8..0026e61 100644 --- a/lib/blocs/professional_profile_bloc/professional_profile_bloc.dart +++ b/lib/blocs/professional_profile_bloc/professional_profile_bloc.dart @@ -5,16 +5,46 @@ import 'package:professional_repository/professional_repository.dart'; part 'professional_profile_event.dart'; part 'professional_profile_state.dart'; -class ProfessionalProfileBloc extends Bloc { +class ProfessionalProfileBloc + extends Bloc { final FirebaseProfessionalRepository _professionalRepository; - ProfessionalProfileBloc({required FirebaseProfessionalRepository professionalRepository}) : _professionalRepository = professionalRepository, super((UpdateProfessionalInfoInitial())) { - on(_onUpdateProfessionalInfo); + ProfessionalProfileBloc( + {required FirebaseProfessionalRepository professionalRepository}) + : _professionalRepository = professionalRepository, + super((UpdateProfessionalInfoInitial())) { + on(_onUpdateProfessionalBannerInfo); + on(_onUpdateProfessionalProfileInfo); } - void _onUpdateProfessionalInfo(UpdateProfessionalInfo event, Emitter emit) async { + void _onUpdateProfessionalBannerInfo(UpdateProfessionalBannerInfo event, + Emitter emit) async { emit(UpdateProfessionalInfoLoading()); try { + event.fileBanner != null + ? await _professionalRepository.uploadBannerPicture(event.fileBanner!) + : null; + + emit(const UpdateProfessionalInfoSuccess()); + } catch (e) { + emit(UpdateProfessionalInfoFailure()); + } + } + + void _onUpdateProfessionalProfileInfo(UpdateProfessionalProfileInfo event, + Emitter emit) async { + emit(UpdateProfessionalInfoLoading()); + try { + await _professionalRepository.updateProfessionalInfo( + event.address, + event.aditionalAddress, + event.rate, + event.locationPreferences, + event.latitude, + event.longitude, + event.paymentMethods, + ); + emit(const UpdateProfessionalInfoSuccess()); } catch (e) { emit(UpdateProfessionalInfoFailure()); diff --git a/lib/blocs/professional_profile_bloc/professional_profile_event.dart b/lib/blocs/professional_profile_bloc/professional_profile_event.dart index 2e5217b..220ad49 100644 --- a/lib/blocs/professional_profile_bloc/professional_profile_event.dart +++ b/lib/blocs/professional_profile_bloc/professional_profile_event.dart @@ -7,15 +7,47 @@ abstract class ProfessionalProfileEvent extends Equatable { List get props => []; } -class UpdateProfessionalInfo extends ProfessionalProfileEvent { - final ProfessionalEntity professional; +class UpdateProfessionalBannerInfo extends ProfessionalProfileEvent { final String? fileBanner; - const UpdateProfessionalInfo({ - required this.professional, - this.fileBanner, + const UpdateProfessionalBannerInfo({ + required this.fileBanner, }); @override - List get props => [professional, fileBanner]; + List get props => [fileBanner]; +} + +class UpdateProfessionalProfileInfo extends ProfessionalProfileEvent { + final String address; + final String aditionalAddress; + final String rate; + final LocationPreferences locationPreferences; + final double latitude; + final double longitude; + // final Schedules schedules; + final PaymentMethodEntity paymentMethods; + + const UpdateProfessionalProfileInfo({ + required this.address, + required this.aditionalAddress, + required this.rate, + required this.locationPreferences, + required this.latitude, + required this.longitude, + // required this.schedules, + required this.paymentMethods, + }); + + @override + List get props => [ + address, + aditionalAddress, + rate, + locationPreferences, + latitude, + longitude, + // schedules, + paymentMethods + ]; } diff --git a/lib/components/general_drawer.dart b/lib/components/general_drawer.dart index 31d34e2..7a36721 100644 --- a/lib/components/general_drawer.dart +++ b/lib/components/general_drawer.dart @@ -38,7 +38,7 @@ class GeneralDrawer extends StatelessWidget { return BlocBuilder( builder: (context, professionalState) { return Drawer( - backgroundColor: Theme.of(context).colorScheme.background, + backgroundColor: Theme.of(context).colorScheme.secondary, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ @@ -166,12 +166,12 @@ class GeneralDrawer extends StatelessWidget { switch (user.proState) { case ProState.active: + Navigator.pop(context); + context .read() .add(const SwitchProModeEvent()); - Navigator.pop(context); - break; case ProState.inactive: Navigator.push( diff --git a/lib/dependency/app_di.dart b/lib/dependency/app_di.dart index e67138c..73189da 100644 --- a/lib/dependency/app_di.dart +++ b/lib/dependency/app_di.dart @@ -6,6 +6,7 @@ import 'package:prosappco/blocs/auth_bloc/auth_bloc.dart'; import 'package:prosappco/blocs/authentication_bloc/authentication_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/professional_profile_bloc/professional_profile_bloc.dart'; import 'package:prosappco/blocs/profile_bloc/profile_bloc.dart'; import 'package:prosappco/blocs/setting_bloc/setting_bloc.dart'; import 'package:prosappco/blocs/sign_up_bloc/sign_up_bloc.dart'; @@ -59,5 +60,8 @@ class AppDI { injector.registerDependency( (() => AuthBloc(userRepository: injector.get()))); + + injector.registerSingleton((() => + ProfessionalProfileBloc(professionalRepository: injector.get()))); } } diff --git a/lib/screens/professional/professional_map_screen.dart b/lib/screens/professional/professional_map_screen.dart new file mode 100644 index 0000000..2268a18 --- /dev/null +++ b/lib/screens/professional/professional_map_screen.dart @@ -0,0 +1,283 @@ +import 'dart:developer'; + +import 'package:flutter/material.dart'; +import 'package:flutter_animate/flutter_animate.dart'; +import 'package:geocoding/geocoding.dart'; +import 'package:geolocator/geolocator.dart'; +import 'package:google_maps_flutter/google_maps_flutter.dart'; +import 'package:prosappco/components/general_primary_button.dart'; + +class ProfessionalMapScreen extends StatefulWidget { + const ProfessionalMapScreen({super.key}); + + @override + State createState() => _ProfessionalMapScreenState(); +} + +class _ProfessionalMapScreenState extends State { + final TextEditingController _addressController = TextEditingController(); + bool _locationPermissionGranted = false; + late GoogleMapController googleMapController; + late Position currentPosition; + var coords; + + @override + void initState() { + super.initState(); + _getLocation(); + } + + Future _getLocation() async { + try { + final position = await _determinePosition(); + setState(() { + currentPosition = position; + _locationPermissionGranted = true; + }); + } catch (e) { + log('Error getting location: $e'); + } + } + + Future _determinePosition() async { + bool serviceEnabled; + LocationPermission permission; + + serviceEnabled = await Geolocator.isLocationServiceEnabled(); + + if (!serviceEnabled) { + throw 'Location services are disabled'; + } + + permission = await Geolocator.checkPermission(); + + if (permission == LocationPermission.denied) { + permission = await Geolocator.requestPermission(); + + if (permission == LocationPermission.denied) { + throw 'Location permission denied'; + } + } + + if (permission == LocationPermission.deniedForever) { + throw 'Location permissions are permanently denied'; + } + + return await Geolocator.getCurrentPosition(); + } + + Future getLocationName(double latitude, double longitude) async { + String address; + List placemarks = + await placemarkFromCoordinates(latitude, longitude); + Placemark place = placemarks[0]; + + if (place.thoroughfare != '' || place.subThoroughfare != '') { + address = + "${place.thoroughfare} ${place.subThoroughfare} ${place.subLocality}, ${place.locality}, ${place.administrativeArea}"; + } else { + address = ''; + } + return address; + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Ubicación'), + // leading: BackButton( + // onPressed: () { + // Navigator.pop( + // context, {"address": '', 'longitude': 0, 'latitude': 0}); + // }, + // ), + actions: [ + IconButton( + icon: const Icon(Icons.my_location), + onPressed: () async { + try { + final position = await _determinePosition(); + + if (googleMapController != null) { + googleMapController.animateCamera( + CameraUpdate.newCameraPosition( + CameraPosition( + target: LatLng(position.latitude, position.longitude), + zoom: 18, + ), + ), + ); + } else { + log('Google Map Controller is not initialized.'); + } + + setState(() { + currentPosition = position; + _locationPermissionGranted = true; + }); + } catch (e) { + log('Error getting location: $e'); + setState(() { + _locationPermissionGranted = false; + }); + ScaffoldMessenger.of(context).clearSnackBars(); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: const Text( + 'Los servicios de ubicación están desactivados'), + action: SnackBarAction( + label: 'Configuración', + onPressed: () { + Geolocator.openLocationSettings(); + }, + ), + ), + ); + } + }, + ), + ], + ), + backgroundColor: const Color(0xFFD6F4FF), + body: Stack( + children: [ + _locationPermissionGranted + ? GoogleMap( + mapType: MapType.normal, + initialCameraPosition: CameraPosition( + target: LatLng( + currentPosition.latitude, currentPosition.longitude), + zoom: 16, + ), + zoomControlsEnabled: false, + myLocationEnabled: true, + onMapCreated: (GoogleMapController controller) { + googleMapController = controller; + }, + onCameraMove: (position) { + setState(() { + coords = position.target; + }); + }, + onCameraIdle: () async { + final isLocationEnabled = + await Geolocator.isLocationServiceEnabled(); + if (!isLocationEnabled) { + setState(() { + _locationPermissionGranted = false; + }); + ScaffoldMessenger.of(context).clearSnackBars(); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: const Text( + 'Los servicios de ubicación están desactivados'), + action: SnackBarAction( + label: 'Configuración', + onPressed: () { + Geolocator.openLocationSettings(); + }, + ), + ), + ); + } + if (coords != null) { + getLocationName(coords.latitude, coords.longitude) + .then((locationName) { + setState(() { + _addressController.text = locationName; + }); + }); + } + }, + ) + : Container( + color: Colors.white, + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const CircularProgressIndicator() + .animate() + .fadeOut(duration: const Duration(seconds: 2)), + const SizedBox(height: 8), + const Text('Cargando...') + .animate() + .fadeOut(duration: const Duration(seconds: 2)), + const Text('Permita los permisos de ubicación') + .animate() + .fadeIn(delay: const Duration(seconds: 2)) + .scale(), + ], + ), + ), + ), + _locationPermissionGranted + ? Container( + decoration: BoxDecoration( + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.5), + spreadRadius: 1, + blurRadius: 5, + offset: const Offset(0, 2), + ), + ], + ), + child: Padding( + padding: const EdgeInsets.only( + left: 18, + right: 18, + bottom: 15, + ), + child: TextFormField( + readOnly: true, + controller: _addressController, + decoration: const InputDecoration( + prefixIcon: Icon(Icons.near_me), + hintText: 'Dirección', + ), + ), + ), + ) + : const SizedBox(), + _locationPermissionGranted + ? const Positioned( + bottom: 30, + right: 0, + left: 0, + top: 0, + child: Icon( + Icons.location_on, + size: 40, + color: Colors.red, + ), + ) + : const SizedBox(), + _locationPermissionGranted + ? Positioned( + bottom: 30, + child: SizedBox( + width: MediaQuery.of(context).size.width, + child: Center( + child: GeneralPrimaryButton( + label: 'Guardar', + isEnabled: _locationPermissionGranted, + onPressed: () { + Navigator.pop(context, { + "address": _addressController.text, + 'latitude': coords.latitude, + 'longitude': coords.longitude, + }); + }, + ), + ), + ), + ) + : const SizedBox(), + ], + ), + ); + } +} diff --git a/lib/screens/professional/professional_profile_screen.dart b/lib/screens/professional/professional_profile_screen.dart index 628e434..f4240bc 100644 --- a/lib/screens/professional/professional_profile_screen.dart +++ b/lib/screens/professional/professional_profile_screen.dart @@ -1,7 +1,21 @@ +import 'dart:developer'; +import 'dart:io'; + +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_animate/flutter_animate.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:image_picker/image_picker.dart'; +import 'package:injector/injector.dart'; import 'package:professional_repository/professional_repository.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/professional_profile_bloc/professional_profile_bloc.dart'; +import 'package:prosappco/components/general_primary_button.dart'; +import 'package:prosappco/screens/professional/professional_map_screen.dart'; +import 'package:prosappco/screens/professional/professional_schedule_screen.dart'; +import 'package:setting_repository/setting_repository.dart'; class ProfessionalProfileScreen extends StatefulWidget { const ProfessionalProfileScreen({super.key}); @@ -12,6 +26,52 @@ class ProfessionalProfileScreen extends StatefulWidget { } class _ProfessionalProfileScreenState extends State { + final settingRepository = Injector.appInstance.get(); + SettingEntity? settings; + XFile? _imageFile; + + bool loadFinish = false; + + bool officeValue = false; + bool deliveryValue = false; + bool rateValue = false; + + final TextEditingController _addressController = TextEditingController(); + final TextEditingController _aditionalAddressController = + TextEditingController(); + final TextEditingController _rateController = TextEditingController(); + + double _longitudeController = 0; + double _latitudeController = 0; + + bool isNequiActive = false; + bool isDatafonoActive = false; + bool isTransferActive = false; + + @override + void initState() { + super.initState(); + + _loadSettings(); + } + + @override + void dispose() { + _addressController.dispose(); + _aditionalAddressController.dispose(); + _rateController.dispose(); + + super.dispose(); + } + + void _loadSettings() { + settingRepository.getSettings().then( + (value) => setState(() { + settings = value; + }), + ); + } + @override Widget build(BuildContext context) { return Scaffold( @@ -21,7 +81,7 @@ class _ProfessionalProfileScreenState extends State { body: BlocBuilder( builder: (context, state) { return Scaffold( - body: Center( + body: SingleChildScrollView( child: content(context, state), ), ); @@ -44,6 +104,353 @@ class _ProfessionalProfileScreenState extends State { } body(ProfessionalEntity proInfo) { - return Text('Bienvenido Profesional ${proInfo.address}'); + if (!loadFinish) { + _addressController.text = proInfo.address; + _aditionalAddressController.text = proInfo.aditionalAddress; + + isNequiActive = proInfo.paymentMethods.nequi; + isDatafonoActive = proInfo.paymentMethods.datafono; + isTransferActive = proInfo.paymentMethods.transferencia; + + // proInfo.bannerPicture != '' + // ? _imageFile = XFile(proInfo.bannerPicture) + // : _imageFile = null; + + if (proInfo.locationPreferences == LocationPreferences.both) { + officeValue = true; + deliveryValue = true; + } else if (proInfo.locationPreferences == LocationPreferences.office) { + officeValue = true; + deliveryValue = false; + } else if (proInfo.locationPreferences == LocationPreferences.delivery) { + officeValue = false; + deliveryValue = true; + } + + _rateController.text = proInfo.rate; + + loadFinish = true; + } + + return Column( + children: [ + pictureWidget(proInfo.bannerPicture, context), + const Divider(height: 0), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 25, vertical: 15), + child: Column( + children: [ + Row( + children: [ + const Expanded( + child: Text( + 'Servicio a domicilio', + style: TextStyle( + fontSize: 17, + fontWeight: FontWeight.w500, + color: Colors.black, + ), + ), + ), + Switch( + value: deliveryValue, + onChanged: (value) { + setState(() { + deliveryValue = value; + if (settings?.domicilios == true) { + deliveryValue = value; + if (!deliveryValue) { + officeValue = true; + } + } else { + deliveryValue = false; + } + }); + }, + ), + ], + ), + Row( + children: [ + const Expanded( + child: Text( + 'Servicio en sitio', + style: TextStyle( + fontSize: 17, + fontWeight: FontWeight.w500, + color: Colors.black, + ), + ), + ), + Switch( + value: officeValue, + onChanged: (value) { + setState(() { + if (settings?.domicilios == true) { + officeValue = value; + if (!officeValue) { + deliveryValue = true; + } + } else { + officeValue = true; + deliveryValue = false; + } + }); + }, + ), + ], + ), + officeValue + ? Column( + children: [ + TextField( + decoration: const InputDecoration( + prefixIcon: Icon(Icons.location_on_outlined), + hintText: 'Dirección', + ), + controller: _addressController, + readOnly: true, + onTap: () async { + final Map? mapInfo = + await Navigator.push( + context, + CupertinoPageRoute( + builder: (context) => + const ProfessionalMapScreen()), + ); + + if (mapInfo != null) { + setState(() { + _addressController.text = + mapInfo['address'] ?? ''; + _latitudeController = + mapInfo['latitude'] ?? 0; + _longitudeController = + mapInfo['longitude'] ?? 0; + }); + } + }), + TextField( + controller: _aditionalAddressController, + decoration: const InputDecoration( + prefixIcon: Icon(Icons.house_outlined), + hintText: 'Piso / Conjunto / Apartamento', + ), + ), + ], + ) + .animate() + .moveY(duration: const Duration(milliseconds: 100)) + : const SizedBox(), + ], + ), + ), + const Divider(height: 0), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 25, vertical: 15), + child: Column( + children: [ + Row( + children: [ + const Expanded( + child: Text( + 'Tarifa', + style: TextStyle( + fontSize: 17, + fontWeight: FontWeight.w500, + color: Colors.black, + ), + ), + ), + Switch( + value: rateValue, + onChanged: (value) { + setState(() { + if (settings?.tarifas == true) { + rateValue = value; + } else { + rateValue = false; + } + }); + }, + ), + ], + ), + rateValue + ? TextField( + controller: _rateController, + keyboardType: TextInputType.number, + decoration: const InputDecoration( + prefixIcon: Icon(Icons.attach_money), + hintText: 'COP', + ), + inputFormatters: [FilteringTextInputFormatter.digitsOnly], + ) + .animate() + .moveY(duration: const Duration(milliseconds: 100)) + : const SizedBox(), + ], + ), + ), + const Divider(height: 0), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 15), + child: Column( + children: [ + const Padding( + padding: EdgeInsets.only(bottom: 10), + child: Text( + 'Metodos de pago', + style: TextStyle( + fontSize: 17, + fontWeight: FontWeight.w500, + color: Colors.black, + ), + ), + ), + CheckboxListTile( + value: isDatafonoActive, + onChanged: (value) { + setState(() { + isDatafonoActive = value!; + }); + }, + title: const Text('Datafono'), + ), + CheckboxListTile( + value: isNequiActive, + onChanged: (value) { + setState(() { + isNequiActive = value!; + }); + }, + title: const Text('Nequi'), + ), + CheckboxListTile( + value: isTransferActive, + onChanged: (value) { + setState(() { + isTransferActive = value!; + }); + }, + title: const Text('Transferencia bancaria'), + ), + ], + ), + ), + const Divider(height: 0), + GestureDetector( + onTap: () { + Navigator.push( + context, + CupertinoPageRoute( + builder: (context) => const ProfessionalScheduleScreen(), + ), + ); + }, + child: Text(proInfo.schedules.toString()), + ), + const Divider(height: 0), + const SizedBox(height: 20), + GeneralPrimaryButton( + onPressed: () { + context.read().add( + UpdateProfessionalProfileInfo( + address: _addressController.text, + aditionalAddress: _aditionalAddressController.text, + latitude: _latitudeController, + longitude: _longitudeController, + locationPreferences: officeValue && deliveryValue + ? LocationPreferences.both + : officeValue && !deliveryValue + ? LocationPreferences.office + : deliveryValue && !officeValue + ? LocationPreferences.delivery + : LocationPreferences.office, + paymentMethods: PaymentMethodEntity( + datafono: isDatafonoActive, + nequi: isNequiActive, + transferencia: isTransferActive, + ), + rate: _rateController.text, + // schedules: Schedules( + // monday: monday, + // tuesday: tuesday, + // wednesday: wednesday, + // thursday: thursday, + // friday: friday, + // saturday: saturday, + // sunday: sunday, + // ), + ), + ); + context.read().add( + UpdateProfessionalBannerInfo(fileBanner: _imageFile?.path), + ); + }, + label: 'Guardar', + ), + const SizedBox(height: 20), + ], + ); + } + + Widget pictureWidget(String? bannerPicture, BuildContext context) { + final pictureUrl = bannerPicture; + final pathImageFile = _imageFile?.path; + + ImageProvider? imageProvider; + + if (pathImageFile != null && pathImageFile.isNotEmpty) { + imageProvider = FileImage(File(pathImageFile)); + } else if (pictureUrl != null && pictureUrl.isNotEmpty) { + imageProvider = NetworkImage(pictureUrl); + } + + return GestureDetector( + onTap: () async { + final ImagePicker picker = ImagePicker(); + final XFile? image = await picker.pickImage( + source: ImageSource.gallery, + maxHeight: 1000, + maxWidth: 2000, + imageQuality: 40, + ); + + if (image != null) { + setState(() { + _imageFile = image; + }); + } + }, + child: pictureContainerWidget(imageProvider), + ); + } + + Widget pictureContainerWidget(ImageProvider? imageProvider) { + final image = imageProvider == null + ? null + : DecorationImage( + image: imageProvider, + fit: BoxFit.cover, + ); + + final widget = image == null + ? Icon( + Icons.image_outlined, + color: Colors.grey.shade400, + size: 40, + ) + : null; + + return Container( + width: double.infinity, + height: 200, + decoration: BoxDecoration( + color: Colors.grey.shade300, + image: image, + ), + child: widget, + ); } } diff --git a/lib/screens/professional/professional_schedule_screen.dart b/lib/screens/professional/professional_schedule_screen.dart new file mode 100644 index 0000000..42787ab --- /dev/null +++ b/lib/screens/professional/professional_schedule_screen.dart @@ -0,0 +1,47 @@ +import 'package:flutter/material.dart'; + +class ProfessionalScheduleScreen extends StatefulWidget { + const ProfessionalScheduleScreen({super.key}); + + @override + State createState() => + _ProfessionalScheduleScreenState(); +} + +class _ProfessionalScheduleScreenState + extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Horario'), + ), + body: SingleChildScrollView( + child: Column( + children: [ + Row( + children: [ + Expanded( + child: Text( + 'Lunes', + style: TextStyle( + fontSize: 17, + fontWeight: FontWeight.w500, + color: Colors.black, + ), + ), + ), + Switch( + value: false, + onChanged: (value) { + setState(() {}); + }, + ), + ], + ), + ], + ), + ), + ); + } +} diff --git a/packages/professional_repository/lib/src/entities/payment_method_entity.dart b/packages/professional_repository/lib/src/entities/payment_method_entity.dart index c4354f9..b74c8c9 100644 --- a/packages/professional_repository/lib/src/entities/payment_method_entity.dart +++ b/packages/professional_repository/lib/src/entities/payment_method_entity.dart @@ -25,7 +25,7 @@ class PaymentMethodEntity extends Equatable { ); } - Map toJson() { + Map toDocument() { return { 'nequi': nequi, 'datafono': datafono, diff --git a/packages/professional_repository/lib/src/entities/professional_entity.dart b/packages/professional_repository/lib/src/entities/professional_entity.dart index c4002eb..6272ab3 100644 --- a/packages/professional_repository/lib/src/entities/professional_entity.dart +++ b/packages/professional_repository/lib/src/entities/professional_entity.dart @@ -1,6 +1,5 @@ import 'package:equatable/equatable.dart'; import 'package:professional_repository/professional_repository.dart'; -import 'package:professional_repository/src/models/location_preferences.dart'; class ProfessionalEntity extends Equatable { final String id; @@ -13,8 +12,8 @@ class ProfessionalEntity extends Equatable { final String bannerPicture; final String identificationPicture; final String certificatePicture; - final String latitude; - final String longitude; + final double latitude; + final double longitude; final List specializations; final List specializationsPictures; final Schedules schedules; @@ -51,8 +50,8 @@ class ProfessionalEntity extends Equatable { bannerPicture: doc['banner_picture'] as String, identificationPicture: doc['identification_picture'] as String, certificatePicture: doc['certificate_picture'] as String, - latitude: doc['latitude'] as String, - longitude: doc['longitude'] as String, + latitude: double.parse(doc['latitude'].toString()), + longitude: double.parse(doc['longitude'].toString()), specializations: List.from(doc['specializations']), specializationsPictures: List.from(doc['specializations_pictures']), @@ -78,7 +77,7 @@ class ProfessionalEntity extends Equatable { 'specializations': specializations, 'specializations_pictures': specializationsPictures, 'schedules': schedules.toJson(), - 'payment_methods': paymentMethods.toJson(), + 'payment_methods': paymentMethods.toDocument(), }; } 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 81a10ee..a308e67 100644 --- a/packages/professional_repository/lib/src/repositories/firebase_professional_repository.dart +++ b/packages/professional_repository/lib/src/repositories/firebase_professional_repository.dart @@ -25,7 +25,7 @@ class FirebaseProfessionalRepository { FirebaseAuth.instance.userChanges().listen((user) async { if (user != null) { - await updateFromFirebase2( + await updateFromFirebase( userId: user.uid, ); } else { @@ -43,7 +43,7 @@ class FirebaseProfessionalRepository { return _proInfoBroadcast.stream; } - Future updateFromFirebase2({ + Future updateFromFirebase({ required String userId, }) async { try { @@ -82,18 +82,46 @@ class FirebaseProfessionalRepository { _isProModeActiveBroadcast.add(isProModeActive); } + Future updateProfessionalInfo( + String address, + String aditionalAddress, + String rate, + LocationPreferences locationPreferences, + double latitude, + double longitude, + PaymentMethodEntity paymentMethods, + ) async { + if (_proInfo == null) { + log('_proInfo is null'); + return; + } + await professionalCollection.doc(_proInfo!.id).update({ + 'address': address, + 'aditional_address': aditionalAddress, + 'rate': rate, + 'location_preferences': enumToInt(locationPreferences), + 'latitude': latitude, + 'longitude': longitude, + 'payment_methods': paymentMethods.toDocument(), + }); + } + Future saveProfessionalInfo(ProfessionalEntity entity) async { await professionalCollection.doc(entity.id).set(entity.toDocument()); } - Future uploadBannerPicture(String file, String userId) async { + Future uploadBannerPicture(String file) async { try { File imageFile = File(file); - Reference firebaseStoreRef = - FirebaseStorage.instance.ref().child('$userId/BN/${userId}_banner'); + Reference firebaseStoreRef = FirebaseStorage.instance + .ref() + .child('${_proInfo!.id}/BN/${_proInfo!.id}_banner'); await firebaseStoreRef.putFile(imageFile); String url = await firebaseStoreRef.getDownloadURL(); - return url; + + await professionalCollection + .doc(_proInfo!.id) + .update({'banner_picture': url}); } catch (e) { log(e.toString()); rethrow;