diff --git a/lib/app.dart b/lib/app.dart index 03ca4ba..20b77c2 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -3,6 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; 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/profile_bloc/profile_bloc.dart'; import 'app_view.dart'; @@ -23,6 +24,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/blocs/professional_bloc/professional_bloc.dart b/lib/blocs/professional_bloc/professional_bloc.dart new file mode 100644 index 0000000..bbf97b4 --- /dev/null +++ b/lib/blocs/professional_bloc/professional_bloc.dart @@ -0,0 +1,63 @@ +import 'dart:developer'; + +// ignore: depend_on_referenced_packages +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:professional_repository/professional_repository.dart'; +import 'package:user_repository/user_repository.dart'; + +part 'professional_event.dart'; +part 'professional_state.dart'; + +class ProfessionalBloc extends Bloc { + final FirebaseProfessionalRepository _professionalRepository; + final UserRepository _userRepository; + + ProfessionalBloc( + {required FirebaseProfessionalRepository professionalRepository, + required UserRepository userRepository}) + : _professionalRepository = professionalRepository, + _userRepository = userRepository, + super(ProfessionalInitial()) { + bool isProModeActive = _professionalRepository.isProModeActive; + emit(LoadedModeProState(isProModeActive)); + + _professionalRepository.sreamIsProModeActive().listen((isProModeActive) { + log('xdd despues de escuchar'); + + add(UpdateProfessionalEvent(isProModeActive: isProModeActive)); + }); + + on((event, emit) async { + await _professionalRepository.switchProMode(); + }); + + on((event, emit) async { + emit(LoadedModeProState(event.isProModeActive)); + }); + + 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, + )); + + _userRepository + .updateUserInfo(myUser.copyWith(proState: ProState.pending)); + }); + } +} diff --git a/lib/blocs/professional_bloc/professional_event.dart b/lib/blocs/professional_bloc/professional_event.dart new file mode 100644 index 0000000..1be9bb0 --- /dev/null +++ b/lib/blocs/professional_bloc/professional_event.dart @@ -0,0 +1,53 @@ +part of 'professional_bloc.dart'; + +abstract class ProfessionalEvent extends Equatable { + const ProfessionalEvent(); + + @override + List get props => []; +} + +class UpdateProfessionalEvent extends ProfessionalEvent { + final bool isProModeActive; + + const UpdateProfessionalEvent({ + required this.isProModeActive, + }); +} + +class SwitchProModeEvent extends ProfessionalEvent { + const SwitchProModeEvent(); +} + +class SendProfessionalToReviewEvent extends ProfessionalEvent { + final String id; + final String identification; + final String identificationPicture; + + final String profession; + final String certificatePicture; + + final List specializations; + final List specializationsPictures; + + const SendProfessionalToReviewEvent({ + required this.id, + required this.identification, + required this.identificationPicture, + required this.profession, + required this.certificatePicture, + required this.specializations, + required this.specializationsPictures, + }); + + @override + List get props => [ + id, + identification, + identificationPicture, + profession, + certificatePicture, + specializations, + specializationsPictures + ]; +} diff --git a/lib/blocs/professional_bloc/professional_state.dart b/lib/blocs/professional_bloc/professional_state.dart new file mode 100644 index 0000000..430d559 --- /dev/null +++ b/lib/blocs/professional_bloc/professional_state.dart @@ -0,0 +1,19 @@ +part of 'professional_bloc.dart'; + +abstract class ProfessionalState extends Equatable { + const ProfessionalState(); + + @override + List get props => []; +} + +class ProfessionalInitial extends ProfessionalState {} + +class LoadedModeProState extends ProfessionalState { + final bool isProModeActive; + + const LoadedModeProState(this.isProModeActive); + + @override + List get props => [isProModeActive]; +} diff --git a/lib/components/general_drawer.dart b/lib/components/general_drawer.dart index 5faa3dd..74800e6 100644 --- a/lib/components/general_drawer.dart +++ b/lib/components/general_drawer.dart @@ -1,12 +1,19 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_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/components/general_drawer_header.dart'; import 'package:prosappco/components/general_drawer_item.dart'; import 'package:prosappco/screens/configuration/configuration_screen.dart'; import 'package:prosappco/screens/configuration/configuration_support_screen.dart'; +import 'package:prosappco/screens/professional/professional_denied_screen.dart'; +import 'package:prosappco/screens/professional/professional_form_screen.dart'; +import 'package:prosappco/screens/professional/professional_pending_screen.dart'; import 'package:prosappco/screens/web/web_view_screen.dart'; import 'package:url_launcher/url_launcher.dart'; +import 'package:user_repository/user_repository.dart'; class GeneralDrawer extends StatelessWidget { const GeneralDrawer({super.key}); @@ -25,104 +32,164 @@ class GeneralDrawer extends StatelessWidget { @override Widget build(BuildContext context) { - return Drawer( - backgroundColor: Theme.of(context).colorScheme.background, - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - const GeneralDrawerHeader(), - Divider( - color: Theme.of(context).colorScheme.onSurface.withOpacity(0.1), - thickness: 0.5, - height: 1, - ), - Expanded( - child: SingleChildScrollView( + return BlocBuilder( + builder: (context, userState) { + return BlocBuilder( + builder: (context, professionalState) { + return Drawer( + backgroundColor: Theme.of(context).colorScheme.background, child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - GeneralDrawerItem( - leading: Icons.history, - label: 'Mis servicios', - onTap: () { - Navigator.pop(context); - }, + const GeneralDrawerHeader(), + Divider( + color: Theme.of(context) + .colorScheme + .onSurface + .withOpacity(0.1), + thickness: 0.5, + height: 1, ), - GeneralDrawerItem( - leading: Icons.settings_outlined, - label: 'Configuración', - onTap: () { - Navigator.pop(context); - Navigator.push( - context, - CupertinoPageRoute( - builder: (context) => const ConfigurationScreen(), - ), - ); - }, - ), - GeneralDrawerItem( - leading: Icons.help_outline, - label: 'Soporte', - onTap: () { - Navigator.pop(context); - Navigator.push( - context, - CupertinoPageRoute( - builder: (context) => - const ConfigurationSupportScreen(), - ), - ); - }, - ), - GeneralDrawerItem( - leading: Icons.campaign_outlined, - label: 'Sugerencias', - onTap: () { - if (kIsWeb) { - _irSugerencias(); - } else { - Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return WebViewScreen( - label: 'Sugerencias', - link: 'https://admin.prosapp.co/sugerencias'); + Expanded( + child: SingleChildScrollView( + child: Column( + children: [ + GeneralDrawerItem( + leading: Icons.history, + label: 'Mis servicios', + onTap: () { + Navigator.pop(context); }, ), - ); - } - }, + GeneralDrawerItem( + leading: Icons.settings_outlined, + label: 'Configuración', + onTap: () { + Navigator.push( + context, + CupertinoPageRoute( + builder: (context) => + const ConfigurationScreen(), + ), + ); + }, + ), + GeneralDrawerItem( + leading: Icons.help_outline, + label: 'Soporte', + onTap: () { + Navigator.push( + context, + CupertinoPageRoute( + builder: (context) => + const ConfigurationSupportScreen(), + ), + ); + }, + ), + GeneralDrawerItem( + leading: Icons.campaign_outlined, + label: 'Sugerencias', + onTap: () { + if (kIsWeb) { + _irSugerencias(); + } else { + Navigator.push( + context, + CupertinoPageRoute( + builder: (BuildContext context) { + return WebViewScreen( + label: 'Sugerencias', + link: + 'https://admin.prosapp.co/sugerencias'); + }, + ), + ); + } + }, + ), + ], + ), + ), ), + Divider( + color: Theme.of(context) + .colorScheme + .onSurface + .withOpacity(0.1), + thickness: 0.5, + height: 1, + ), + const SizedBox(height: 15), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: buttonOfState(context, professionalState), + ), + const SizedBox(height: 15), ], ), - ), - ), - Divider( - color: Theme.of(context).colorScheme.onSurface.withOpacity(0.1), - thickness: 0.5, - height: 1, - ), - const SizedBox(height: 15), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 20), - child: ElevatedButton( - onPressed: () {}, - style: ElevatedButton.styleFrom( - backgroundColor: Theme.of(context).colorScheme.primary, - padding: const EdgeInsets.symmetric(vertical: 15), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10), - )), - child: const Text( - 'Modo Profesional', - style: TextStyle(color: Colors.white, fontSize: 18), - ), - ), - ), - const SizedBox(height: 15), - ], - ), + ); + }, + ); + }, ); } + + buttonOfState(BuildContext context, ProfessionalState state) { + return ElevatedButton( + onPressed: () { +// get state of bloc by context + final myUserState = context.read().state; + if (myUserState.status == MyUserStatus.success) { + final user = myUserState.user!; + + switch (user.proState) { + case ProState.active: + // Get boc and add event + context + .read() + .add(const SwitchProModeEvent()); + break; + case ProState.inactive: + Navigator.push( + context, + CupertinoPageRoute( + builder: (context) => const ProfessionalFormScreen(), + ), + ); + break; + case ProState.pending: + Navigator.push( + context, + CupertinoPageRoute( + builder: (context) => const ProfessionalPendingScreen(), + ), + ); + break; + case ProState.denied: + Navigator.push( + context, + CupertinoPageRoute( + builder: (context) => const ProfessionalDeniedScreen(), + ), + ); + break; + } + } else {} + }, + style: ElevatedButton.styleFrom( + backgroundColor: Theme.of(context).colorScheme.primary, + padding: const EdgeInsets.symmetric(vertical: 15), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + )), + child: (state is LoadedModeProState) + ? Text( + state.isProModeActive ? 'Modo cliente' : 'Modo profesional', + style: const TextStyle(color: Colors.white, fontSize: 18), + ) + : const CircularProgressIndicator( + color: Colors.white, + )); + } } diff --git a/lib/components/general_drawer_header.dart b/lib/components/general_drawer_header.dart index 25765ed..af92f37 100644 --- a/lib/components/general_drawer_header.dart +++ b/lib/components/general_drawer_header.dart @@ -2,7 +2,6 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:prosappco/blocs/my_user_bloc/my_user_bloc.dart'; -import 'package:prosappco/screens/city/city_screen.dart'; import 'package:prosappco/screens/profile/profile_screen.dart'; class GeneralDrawerHeader extends StatelessWidget { diff --git a/lib/dependency/app_di.dart b/lib/dependency/app_di.dart index 5da4eb5..e67138c 100644 --- a/lib/dependency/app_di.dart +++ b/lib/dependency/app_di.dart @@ -1,8 +1,11 @@ import 'package:firebase_auth/firebase_auth.dart'; import 'package:injector/injector.dart'; +import 'package:profession_repository/profession_repository.dart'; +import 'package:professional_repository/professional_repository.dart'; 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/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'; @@ -20,9 +23,16 @@ class AppDI { injector.registerSingleton(() => FirebaseCityRepository()); + injector.registerSingleton( + () => FirebaseProfessionRepository()); + injector.registerSingleton( () => FirebaseSettingRepository()); + injector.registerSingleton( + () => FirebaseProfessionalRepository(), + ); + injector.registerSingleton((() => AuthenticationBloc(myUserRepository: injector.get()))); @@ -32,6 +42,12 @@ class AppDI { injector.registerSingleton( (() => ProfileBloc(userRepository: injector.get()))); + injector.registerSingleton( + () => ProfessionalBloc( + professionalRepository: injector.get(), + userRepository: injector.get()), + ); + injector.registerDependency( (() => SignInBloc(userRepository: injector.get()))); diff --git a/lib/screens/city/city_screen.dart b/lib/screens/city/city_screen.dart index f10cd8d..08b1e7a 100644 --- a/lib/screens/city/city_screen.dart +++ b/lib/screens/city/city_screen.dart @@ -1,8 +1,7 @@ -import 'package:city_repository/city_repository.dart'; import 'package:flutter/material.dart'; +import 'package:city_repository/city_repository.dart'; import 'package:injector/injector.dart'; import 'package:intl_phone_field/helpers.dart'; -import 'package:prosappco/src/components/pop_appbar.dart'; import 'package:shimmer/shimmer.dart'; class CityScreen extends StatefulWidget { @@ -17,7 +16,6 @@ class _CityScreenState extends State { final cityRepository = Injector.appInstance.get(); List? _cities; List? _filteredCities; - bool _isLoading = true; @override @@ -38,74 +36,67 @@ class _CityScreenState extends State { @override Widget build(BuildContext context) { - return SafeArea( - child: Scaffold( - appBar: PopAppbar( - onPressed: () { - Navigator.pop(context); - }, - label: 'Ciudad', - ), - body: Column( - children: [ - TextField( - controller: _searchController, - onChanged: (value) { - _filterCities(value); - }, - decoration: const InputDecoration( - hintText: 'Busca una ciudad', - prefixIcon: Icon(Icons.search), - enabledBorder: UnderlineInputBorder( - borderSide: BorderSide(color: Colors.grey), - ), - focusedBorder: UnderlineInputBorder( - borderSide: BorderSide(color: Colors.grey), - ), + return Scaffold( + appBar: AppBar( + title: const Text('Ciudad'), + ), + body: Column( + children: [ + TextField( + controller: _searchController, + onChanged: (value) { + _filterCities(value); + }, + decoration: const InputDecoration( + hintText: 'Busca una ciudad', + prefixIcon: Icon(Icons.search), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Colors.grey), + ), + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Colors.grey), ), ), - _isLoading - ? _buildShimmerEffect() - : Expanded( - child: _filteredCities != null && - _filteredCities!.isNotEmpty - ? ListView.builder( - itemCount: _filteredCities!.length, - itemBuilder: (BuildContext context, int index) { - return Container( - decoration: BoxDecoration( - border: Border( - bottom: BorderSide( - color: Colors.grey.withOpacity(0.2)), + ), + _isLoading + ? _buildShimmerEffect() + : Expanded( + child: _filteredCities != null && _filteredCities!.isNotEmpty + ? ListView.builder( + itemCount: _filteredCities!.length, + itemBuilder: (BuildContext context, int index) { + return Container( + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + color: Colors.grey.withOpacity(0.2)), + ), + ), + child: ListTile( + title: Text( + _filteredCities![index].cityName, + style: const TextStyle( + fontWeight: FontWeight.bold, + color: Colors.black87, ), ), - child: ListTile( - title: Text( - _filteredCities![index].cityName, - style: const TextStyle( - fontWeight: FontWeight.bold, - color: Colors.black87, - ), - ), - subtitle: Text( - "${_filteredCities![index].stateOfCity}, ${_filteredCities![index].countryOfCity}", - style: - const TextStyle(color: Colors.black54), - ), - onTap: () { - Navigator.pop(context, - _filteredCities![index].cityName); - }, + subtitle: Text( + "${_filteredCities![index].stateOfCity}, ${_filteredCities![index].countryOfCity}", + style: const TextStyle(color: Colors.black54), ), - ); - }, - ) - : const Center( - child: Text('No se encontraron ciudades.'), - ), - ), - ], - ), + onTap: () { + Navigator.pop(context, + _filteredCities![index].cityName); + }, + ), + ); + }, + ) + : const Center( + child: Text('No se encontraron coincidencias'), + ), + ), + ], ), ); } diff --git a/lib/screens/profession/profession_screen.dart b/lib/screens/profession/profession_screen.dart new file mode 100644 index 0000000..e852c45 --- /dev/null +++ b/lib/screens/profession/profession_screen.dart @@ -0,0 +1,164 @@ +import 'dart:developer'; + +import 'package:flutter/material.dart'; +import 'package:injector/injector.dart'; +import 'package:intl_phone_field/helpers.dart'; +import 'package:profession_repository/profession_repository.dart'; +import 'package:shimmer/shimmer.dart'; + +class ProfessionScreen extends StatefulWidget { + const ProfessionScreen({super.key}); + + @override + State createState() => _ProfessionScreenState(); +} + +class _ProfessionScreenState extends State { + final _searchController = TextEditingController(); + final professionRepository = Injector.appInstance.get(); + List? _professions; + List? _filteredProfessions; + bool _isLoading = true; + + @override + void initState() { + super.initState(); + _loadProfessions(); + } + + void _loadProfessions() { + professionRepository.getProfessions().then((Professions element) { + setState(() { + log(element.toString()); + _professions = element.professions; + _filteredProfessions = _professions; + _isLoading = false; + }); + }); + } + + void _filterProfessions(String query) { + if (_professions != null) { + setState(() { + _filteredProfessions = _professions! + .where((profession) => removeDiacritics(profession.toLowerCase()) + .contains(removeDiacritics(query.toLowerCase()))) + .toList(); + }); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Profesiones'), + ), + body: Column( + children: [ + TextField( + controller: _searchController, + onChanged: (value) { + _filterProfessions(value); + }, + decoration: const InputDecoration( + hintText: 'Busca una profesión', + prefixIcon: Icon(Icons.search), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Colors.grey), + ), + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Colors.grey), + ), + ), + ), + _isLoading + ? _buildShimmerEffect() + : Expanded( + child: _filteredProfessions != null && + _filteredProfessions!.isNotEmpty + ? ListView.builder( + itemCount: _filteredProfessions!.length, + itemBuilder: (BuildContext context, int index) { + final profession = _filteredProfessions![index]; + return ListTile( + title: Text(profession), + onTap: () { + Navigator.pop( + context, _filteredProfessions![index]); + }, + ); + }, + ) + : const Center( + child: Text('No se encontraron coincidencias'), + ), + ) + ], + ), + ); + } + + Widget _buildShimmerEffect() { + return Expanded( + child: Shimmer.fromColors( + baseColor: Colors.grey[300]!, + highlightColor: Colors.grey[100]!, + child: ListView.builder( + itemCount: 8, + itemBuilder: (_, __) => const ListTileShimmer(), + ), + ), + ); + } +} + +class ListTileShimmer extends StatelessWidget { + const ListTileShimmer({super.key}); + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + border: Border( + bottom: BorderSide(color: Colors.grey.withOpacity(0.4)), + ), + ), + child: ListTile( + title: Row( + children: [ + Container( + width: MediaQuery.of(context).size.width * 0.8, + height: 20.0, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8), + ), + ), + ], + ), + subtitle: Row( + children: [ + Container( + width: MediaQuery.of(context).size.width * 0.3, + height: 15.0, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8), + ), + ), + const SizedBox(width: 5), + Container( + width: MediaQuery.of(context).size.width * 0.3, + height: 15.0, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8), + ), + ) + ], + ), + ), + ); + } +} diff --git a/lib/screens/professional/professional_denied_screen.dart b/lib/screens/professional/professional_denied_screen.dart new file mode 100644 index 0000000..8968594 --- /dev/null +++ b/lib/screens/professional/professional_denied_screen.dart @@ -0,0 +1,13 @@ +import 'package:flutter/material.dart'; + +class ProfessionalDeniedScreen extends StatelessWidget { + const ProfessionalDeniedScreen({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('Perfil profesional')), + body: const Center(child: Text('Cuenta denegada')), + ); + } +} diff --git a/lib/screens/professional/professional_form_screen.dart b/lib/screens/professional/professional_form_screen.dart new file mode 100644 index 0000000..c77d28a --- /dev/null +++ b/lib/screens/professional/professional_form_screen.dart @@ -0,0 +1,337 @@ +import 'dart:io'; +import 'package:flutter/material.dart'; +import 'package:injector/injector.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +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/screens/profession/profession_screen.dart'; + +class ProfessionalFormScreen extends StatefulWidget { + const ProfessionalFormScreen({super.key}); + + @override + State createState() => _ProfessionalFormScreenState(); +} + +class _ProfessionalFormScreenState extends State { + final TextEditingController _cedulaController = TextEditingController(); + final TextEditingController _professionController = TextEditingController(); + final TextEditingController _controller = TextEditingController(); + final List _items = []; + + XFile? _imageFile; + + late final AuthBloc authBloc; + + @override + void initState() { + super.initState(); + authBloc = Injector.appInstance.get(); + } + + @override + void dispose() { + _cedulaController.dispose(); + _professionController.dispose(); + + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (context) => authBloc, + child: Scaffold( + appBar: AppBar( + title: const Text('Perfil profesional'), + ), + body: BlocBuilder( + builder: (context, state) { + return SingleChildScrollView( + child: Padding( + padding: + const EdgeInsets.symmetric(horizontal: 40, vertical: 10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + pictureWidget(state, context), + const SizedBox(height: 30), + TextFormField( + controller: _cedulaController, + decoration: const InputDecoration( + labelText: 'Cedula', + prefixIcon: Icon(Icons.assignment_ind), + hintText: 'Ingresa tu cedula', + border: OutlineInputBorder( + borderRadius: BorderRadius.all( + Radius.circular(10.0), + )), + errorBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.red), + ), + focusedErrorBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.red, width: 2.0), + ), + ), + validator: (value) { + if (value == null || value.isEmpty) { + return 'Por favor, ingrese su nombre'; + } + return null; + }, + ), + const SizedBox(height: 20.0), + TextFormField( + controller: _professionController, + readOnly: true, + onTap: () async { + final professionName = await Navigator.push( + context, + CupertinoPageRoute( + builder: (BuildContext context) { + return const ProfessionScreen(); + }, + ), + ); + + if (professionName != null) { + _professionController.text = professionName; + } + }, + decoration: const InputDecoration( + labelText: 'Profesión', + prefixIcon: Icon(Icons.work_rounded), + hintText: 'Elige tu profesión', + border: OutlineInputBorder( + borderRadius: BorderRadius.all( + Radius.circular(10.0), + )), + errorBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.red), + ), + focusedErrorBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.red, width: 2.0), + ), + ), + validator: (value) { + if (value == null || value.isEmpty) { + return 'Por favor, ingrese su nombre'; + } + return null; + }, + ), + const SizedBox(height: 20.0), + TextField( + controller: _controller, + onSubmitted: (value) { + _addItemToList(); + }, + decoration: InputDecoration( + labelText: 'Especializaciones', + prefixIcon: const Icon(Icons.assignment_rounded), + suffixIcon: IconButton( + onPressed: () { + _addItemToList(); + }, + icon: const Icon(Icons.add)), + hintText: 'Ingresa tus especializaciones', + border: const OutlineInputBorder( + borderRadius: BorderRadius.all( + Radius.circular(10.0), + )), + errorBorder: const OutlineInputBorder( + borderSide: BorderSide(color: Colors.red), + ), + focusedErrorBorder: const OutlineInputBorder( + borderSide: BorderSide(color: Colors.red, width: 2.0), + ), + ), + ), + 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, + onDeleted: () { + _removeItemFromList(item); + }, + )) + .toList(), + ), + const SizedBox(height: 60.0), + saveButton(state, context), + ], + ), + ), + ); + }, + ), + ), + ); + } + + void _addItemToList() { + setState(() { + String newItem = _controller.text.trim(); + if (newItem.isNotEmpty) { + _items.add(newItem); + _controller.clear(); + } + }); + } + + void _removeItemFromList(String item) { + setState(() { + _items.remove(item); + }); + } + + 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, + ), + ), + ), + ); + } + + Widget pictureWidget(MyUserState state, BuildContext context) { + final pictureUrl = state.user?.picture; + 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: 500, + maxWidth: 500, + imageQuality: 40, + ); + + if (image != null) { + setState(() { + _imageFile = image; + }); + } + }, + child: Hero( + tag: 'picture-profile', + child: pictureContainerWidget(imageProvider), + ), + ); + } + + Widget pictureContainerWidget(ImageProvider? imageProvider) { + final image = imageProvider == null + ? null + : DecorationImage( + image: imageProvider, + fit: BoxFit.contain, + ); + + final widget = image == null + ? Icon( + CupertinoIcons.person, + color: Colors.grey.shade400, + size: 40, + ) + : null; + + return Container( + width: 120, + height: 120, + decoration: BoxDecoration( + color: Colors.grey.shade300, + shape: BoxShape.circle, + image: image, + ), + child: widget, + ); + } +} diff --git a/lib/screens/professional/professional_pending_screen.dart b/lib/screens/professional/professional_pending_screen.dart new file mode 100644 index 0000000..c513926 --- /dev/null +++ b/lib/screens/professional/professional_pending_screen.dart @@ -0,0 +1,81 @@ +import 'package:flutter/material.dart'; + +class ProfessionalPendingScreen extends StatelessWidget { + const ProfessionalPendingScreen({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('Perfil profesional')), + body: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.only(top: 40), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.access_time, + color: Color(0xFF2BA4EC), + ), + SizedBox(width: 8), + Text( + 'Información en revisión', + style: TextStyle( + color: Color(0xFF2BA4EC), + fontSize: 17, + fontWeight: FontWeight.w500, + ), + ), + ], + ), + Image( + image: const AssetImage('images/checklist.gif'), + width: MediaQuery.of(context).size.width * 0.7, + ), + Container( + margin: const EdgeInsets.only(left: 40, right: 40, top: 20), + decoration: BoxDecoration( + color: const Color(0xFFD6F4FF), + borderRadius: BorderRadius.circular(30), + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.5), + spreadRadius: 2, + blurRadius: 5, + offset: const Offset(0, 3), + ), + ], + ), + padding: + const EdgeInsets.symmetric(vertical: 15, horizontal: 25), + child: Column( + children: [ + SizedBox( + width: MediaQuery.of(context).size.width * 0.8, + child: const Column( + children: [ + Text( + 'Gracias por proporcionar tu información. Actualmente, estamos revisando tus datos y una vez aprobados, podrás acceder al perfil profesional sin problemas. Te notificaremos tan pronto como tu cuenta esté lista.', + style: TextStyle(fontSize: 14), + ), + SizedBox(height: 10), + Text( + '¡Gracias por tu paciencia!', + style: TextStyle(fontSize: 14), + ), + ], + ), + ), + ], + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/screens/profile/profile_register_phone_screen.dart b/lib/screens/profile/profile_register_phone_screen.dart index c97b2bc..78bb6b0 100644 --- a/lib/screens/profile/profile_register_phone_screen.dart +++ b/lib/screens/profile/profile_register_phone_screen.dart @@ -10,7 +10,6 @@ import 'package:prosappco/blocs/auth_bloc/auth_bloc.dart'; import 'package:prosappco/blocs/my_user_bloc/my_user_bloc.dart'; import 'package:prosappco/components/general_input_decoration.dart'; import 'package:prosappco/components/general_primary_button.dart'; -import 'package:prosappco/screens/authentication/otp_auth_screen.dart'; class ProfileRegisterPhoneScreen extends StatefulWidget { const ProfileRegisterPhoneScreen({super.key}); @@ -22,8 +21,6 @@ class ProfileRegisterPhoneScreen extends StatefulWidget { class _ProfileRegisterPhoneScreenState extends State { - final TextEditingController _actualPasswordController = - TextEditingController(); late final AuthBloc authBloc; late String verificationCode; diff --git a/lib/screens/web/web_view_screen.dart b/lib/screens/web/web_view_screen.dart index a6c891b..f2274fb 100644 --- a/lib/screens/web/web_view_screen.dart +++ b/lib/screens/web/web_view_screen.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:prosappco/src/components/pop_appbar.dart'; import 'package:webview_flutter/webview_flutter.dart'; class WebViewScreen extends StatefulWidget { @@ -56,11 +55,8 @@ class _WebViewScreenState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: PopAppbar( - onPressed: () { - Navigator.pop(context); - }, - label: widget.label, + appBar: AppBar( + title: Text(widget.label), ), body: Stack( children: [ diff --git a/lib/simple_bloc_observer.dart b/lib/simple_bloc_observer.dart index 369fc27..308a98c 100644 --- a/lib/simple_bloc_observer.dart +++ b/lib/simple_bloc_observer.dart @@ -1,5 +1,4 @@ import 'dart:developer'; - import 'package:flutter_bloc/flutter_bloc.dart'; class SimpleBlocObserver extends BlocObserver { diff --git a/lib/src/authentication/authentication_repository.dart b/lib/src/authentication/authentication_repository.dart deleted file mode 100644 index dcfe850..0000000 --- a/lib/src/authentication/authentication_repository.dart +++ /dev/null @@ -1,342 +0,0 @@ -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:firebase_auth/firebase_auth.dart'; -import 'package:flutter/foundation.dart'; -import 'package:get/get.dart'; -import 'package:google_sign_in/google_sign_in.dart'; -import 'package:prosappco/src/authentication/exceptions/register_failed.dart'; -import 'package:prosappco/src/presentation/screens/login/login.dart'; -// import 'package:prosappco/src/presentation/screens/service.dart'; -import 'package:prosappco/src/presentation/screens/map/service.dart'; -import 'package:prosappco/src/presentation/screens/service_web.dart'; - -class AuthenticationRepository extends GetxController { - static AuthenticationRepository get instance => Get.find(); - - //Variables - final _auth = FirebaseAuth.instance; - late final Rx firebaseUser; - final firebase = FirebaseFirestore.instance; - final GoogleSignIn googleSignIn = GoogleSignIn(); - // final _userRef = firebase - var verificationId = ''.obs; - - @override - void onReady() { - // Future.delayed(const Duration(seconds: 6)); - firebaseUser = Rx(_auth.currentUser); - firebaseUser.bindStream(_auth.userChanges()); - ever(firebaseUser, _setInitialScreen); - } - - _setInitialScreen(User? user) { - user == null - ? Get.offAll(const LoginScreen()) - : kIsWeb - ? Get.offAll(const ServiceWebScreen()) - : Get.offAll(const ServiceScreen()); - } - - Future phoneAuthentication(String phoneNo) async { - await _auth.verifyPhoneNumber( - phoneNumber: phoneNo, - verificationCompleted: (credential) async { - await _auth.signInWithCredential(credential); - }, - codeSent: (verificationId, resendToken) { - this.verificationId.value = verificationId; - }, - codeAutoRetrievalTimeout: (verificationId) { - this.verificationId.value = verificationId; - }, - verificationFailed: (e) { - if (e.code == 'invalid-phone-number') { - Get.snackbar('Error', 'El numero no es valido.'); - } else { - Get.snackbar('Error', 'Algo ha ido mal. Inténtalo de nuevo. $e'); - } - }, - ); - } - - Future verifyOTP(String otp) async { - var credentials = await _auth.signInWithCredential( - PhoneAuthProvider.credential( - verificationId: verificationId.value, smsCode: otp)); - return credentials.user != null ? true : false; - } - - Future updatePhoneNumber(String verificationId, String smsCode) async { - try { - PhoneAuthCredential credential = PhoneAuthProvider.credential( - verificationId: verificationId, smsCode: smsCode); - await FirebaseAuth.instance.currentUser!.updatePhoneNumber(credential); - print("Phone number updated successfully"); - } catch (e) { - print("Error updating phone number: $e"); - } - } - - Future createUserWithEmailAndPassword( - String email, String password) async { - try { - await _auth.createUserWithEmailAndPassword( - email: email, password: password); - - firebaseUser.value != null - ? kIsWeb - ? Get.offAll(const ServiceWebScreen()) - : Get.to(const ServiceScreen()) - : Get.to(const LoginScreen()); - } on FirebaseAuthException catch (e) { - final ex = SignUpWithEmailAndPasswordFailure.code(e.code); - Get.snackbar( - 'Correo ya registrado', - 'Por favor pruebe con otro.', - snackPosition: SnackPosition.BOTTOM, - ); - } catch (_) { - const ex = SignUpWithEmailAndPasswordFailure(); - print('EXCEPTION - ${ex.message}'); - throw ex; - } - } - - Future loginWithEmailAndPassword(String email, String password) async { - try { - await _auth.signInWithEmailAndPassword(email: email, password: password); - } on FirebaseAuthException catch (e) { - if (e.code == 'wrong-password') { - Get.snackbar( - 'Contraseña incorrecta', - 'Por favor intentelo de nuevo.', - snackPosition: SnackPosition.BOTTOM, - ); - } - if (e.code == 'invalid-email') { - Get.snackbar( - 'Ingrese un email valido', - 'Por favor pruebe con otro.', - snackPosition: SnackPosition.BOTTOM, - ); - } - if (e.code == 'user-not-found') { - Get.snackbar( - 'Email no encontrado', - 'Este correo no se encuentra registrado.', - snackPosition: SnackPosition.BOTTOM, - ); - } - } catch (_) {} - } - - Future signInWithGoogle() async { - try { - final GoogleSignInAccount? googleUser = await GoogleSignIn().signIn(); - - if (googleUser != null) { - final GoogleSignInAuthentication googleAuth = - await googleUser.authentication; - final OAuthCredential credential = GoogleAuthProvider.credential( - accessToken: googleAuth.accessToken, - idToken: googleAuth.idToken, - ); - - await FirebaseAuth.instance.signInWithCredential(credential); - - // Continúa con el flujo de la aplicación después del inicio de sesión exitoso - // Por ejemplo, redirecciona a la siguiente pantalla - firebaseUser.value != null - ? kIsWeb - ? Get.offAll(const ServiceWebScreen()) - : Get.to(const ServiceScreen()) - : Get.to(const LoginScreen()); - } else { - // El usuario canceló el inicio de sesión con Google - // Puedes manejarlo según tus necesidades - print('Error'); - } - } catch (e) { - print('Error - $e'); - } - } - - Future logout(String uid) async { - try { - await FirebaseFirestore.instance - .collection('users') - .doc(uid) - .update({'token': FieldValue.delete()}); - } catch (e) { - print(e); - } - - _auth.signOut(); - } - - String? getCurrentUserPhone() { - final User? user = _auth.currentUser; - return user?.phoneNumber; - } - - String? getCurrentUserUid() { - final User? user = _auth.currentUser; - return user?.uid; - } - - Future getCity(String uid) async { - String city = ''; - try { - final snapshot = - await FirebaseFirestore.instance.collection('users').doc(uid).get(); - final Map? data = snapshot.data(); - city = data?['city'] ?? ''; - } catch (e) { - print('Error getting city: $e'); - } - return city; - } - - Future getGender(String uid) async { - String gender = ''; - try { - final snapshot = - await FirebaseFirestore.instance.collection('users').doc(uid).get(); - final Map? data = snapshot.data(); - gender = data?['gender'] ?? ''; - } catch (e) { - print('Error getting gender: $e'); - } - return gender; - } - - Future getBirthday(String uid) async { - String birthday = ''; - try { - final snapshot = - await FirebaseFirestore.instance.collection('users').doc(uid).get(); - final Map? data = snapshot.data(); - birthday = data?['birth_date'] ?? ''; - } catch (e) { - print('Error getting birthday: $e'); - } - return birthday; - } - - Future getCoordsOfCity(String uid) async { - String coords = ''; - try { - final snapshot = - await FirebaseFirestore.instance.collection('users').doc(uid).get(); - final Map? data = snapshot.data(); - coords = data?['coordsOfCity'] ?? ''; - } catch (e) { - print('Error getting coords of city: $e'); - } - print('Error getting coords of city: $coords'); - return coords; - } - - Future getAddress(String uid) async { - String address = ''; - try { - final snapshot = - await FirebaseFirestore.instance.collection('users').doc(uid).get(); - final Map? data = snapshot.data(); - address = data?['address'] ?? ''; - } catch (e) { - print('Error getting address: $e'); - } - return address; - } - - Future getUbicacion(String uid) async { - String location = ''; - try { - final snapshot = - await FirebaseFirestore.instance.collection('users').doc(uid).get(); - final Map? data = snapshot.data(); - location = data?['ubicacion'] ?? ''; - } catch (e) { - print('Error getting address: $e'); - } - return location; - } - - Future getOpcionalAddress(String uid) async { - String opcional_location = ''; - try { - final snapshot = - await FirebaseFirestore.instance.collection('users').doc(uid).get(); - final Map? data = snapshot.data(); - opcional_location = data?['opcional_address'] ?? ''; - } catch (e) { - print('Error getting address: $e'); - } - return opcional_location; - } - - Future getTarifa(String uid) async { - int tarifa = 0; - try { - final snapshot = - await FirebaseFirestore.instance.collection('users').doc(uid).get(); - final Map? data = snapshot.data(); - tarifa = data?['tarifas'] ?? 0; - } catch (e) { - print('Error getting tarifa: $e'); - } - return tarifa; - } - - Future getPhoto(String uid) async { - String photo = ''; - try { - final snapshot = - await FirebaseFirestore.instance.collection('users').doc(uid).get(); - final Map? data = snapshot.data(); - photo = data?['photo'] ?? ''; - } catch (e) { - print('Error getting photo: $e'); - } - return photo; - } - - Future getBanner(String uid) async { - String photo = ''; - try { - final snapshot = - await FirebaseFirestore.instance.collection('users').doc(uid).get(); - final Map? data = snapshot.data(); - photo = data?['banner'] ?? ''; - } catch (e) { - print('Error getting banner: $e'); - } - return photo; - } - - Future getProfession(String uid) async { - String profession = ''; - try { - final snapshot = - await FirebaseFirestore.instance.collection('users').doc(uid).get(); - final Map? data = snapshot.data(); - profession = data?['profesion'] ?? ''; - } catch (e) { - print('Error getting profesion: $e'); - } - return profession; - } - - Future getState(String uid) async { - String state = ''; - try { - final snapshot = - await FirebaseFirestore.instance.collection('users').doc(uid).get(); - final Map? data = snapshot.data(); - state = data?['estado'] ?? ''; - } catch (e) { - print('Error getting estado: $e'); - } - return state; - } -} diff --git a/lib/src/authentication/exceptions/register_failed.dart b/lib/src/authentication/exceptions/register_failed.dart deleted file mode 100644 index f2c6fd4..0000000 --- a/lib/src/authentication/exceptions/register_failed.dart +++ /dev/null @@ -1,19 +0,0 @@ -class SignUpWithEmailAndPasswordFailure { - final String message; - - const SignUpWithEmailAndPasswordFailure( - [this.message = "An Unknown error ocurred."]); - - factory SignUpWithEmailAndPasswordFailure.code(String code) { - switch (code) { - case 'weak-password': - return const SignUpWithEmailAndPasswordFailure( - 'Please enter a stronger password.'); - case 'email-alredy-in-use': - return const SignUpWithEmailAndPasswordFailure( - 'An account alredy exists for that email.'); - default: - return const SignUpWithEmailAndPasswordFailure(); - } - } -} diff --git a/lib/src/components/banner_photo.dart b/lib/src/components/banner_photo.dart deleted file mode 100644 index 2a5a091..0000000 --- a/lib/src/components/banner_photo.dart +++ /dev/null @@ -1,156 +0,0 @@ -import 'dart:io'; - -import 'package:firebase_storage/firebase_storage.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_animate/flutter_animate.dart'; -import 'package:prosappco/src/components/column_padding.dart'; - -const double photoSize = 150; - -class ReferenceBannerPhoto extends StatelessWidget { - Reference? ref; - double size; - double sizeCircle; - - ReferenceBannerPhoto({ - super.key, - required this.ref, - this.size = photoSize, - this.sizeCircle = photoSize, - }); - - Future downloadImage() async { - try { - if (ref != null) { - final imageData = await ref!.getData(); - if (imageData != null) { - return Image.memory( - imageData, - width: double.infinity, - height: size, - fit: BoxFit.fill, - ); - } - } - // ignore: empty_catches - } catch (e) {} - - return DefaultPhoto( - sizeDefault: sizeCircle, - ); - } - - @override - Widget build(BuildContext context) { - return FutureBuilder( - future: downloadImage(), - builder: (BuildContext context, AsyncSnapshot snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { - // mientras la llamada asíncrona está en proceso, muestra un mensaje de carga - return DefaultPhoto(); - } else if (snapshot.connectionState == ConnectionState.done && - snapshot.hasData) { - return snapshot.data!; - } else { - return DefaultPhoto(); - } - }); - } -} - -class DefaultPhoto extends StatelessWidget { - double sizeDefault; - DefaultPhoto({ - super.key, - this.sizeDefault = photoSize, - }); - - @override - Widget build(BuildContext context) { - return Container( - decoration: BoxDecoration( - color: Colors.white, - boxShadow: [ - BoxShadow( - color: Colors.black.withOpacity(0.15), - blurRadius: 5, - offset: const Offset(0, 1), - ), - ], - ), - height: 150, - child: ColumnPadding( - alineacion: MainAxisAlignment.spaceAround, - padding: const EdgeInsets.symmetric(horizontal: 70), - children: [ - Container( - decoration: BoxDecoration( - color: const Color(0xFFD6F4FF), - borderRadius: BorderRadius.circular(50), - boxShadow: [ - BoxShadow( - color: Colors.grey.withOpacity(0.3), - spreadRadius: 2, - blurRadius: 5, - offset: const Offset(0, 3), // changes position of shadow - ), - ], - ), - constraints: const BoxConstraints(minWidth: 250, minHeight: 50), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: const [ - Expanded( - child: Center( - child: Text( - 'Foto portada', - style: TextStyle( - color: Color(0xFF2BA4EC), - fontSize: 17, - ), - ), - ), - ), - Padding( - padding: EdgeInsets.only(right: 15), - child: Icon( - Icons.file_upload_outlined, - color: Color(0xFF2BA4EC), - size: 30, - ), - ), - ], - ), - ), - ], - ), - ); - } -} - -class LocalPhoto extends StatelessWidget { - File file; - LocalPhoto({super.key, required this.file}); - - @override - Widget build(BuildContext context) { - return Container( - decoration: BoxDecoration( - color: Colors.white, - boxShadow: [ - BoxShadow( - color: Colors.black.withOpacity(0.15), - blurRadius: 8, - offset: const Offset(0, 2), - ), - ], - ), - child: Image.file( - file, - width: double.infinity, - height: photoSize, - fit: BoxFit.fill, - ), - ); - } -} diff --git a/lib/src/components/bottom_sheet.dart b/lib/src/components/bottom_sheet.dart deleted file mode 100644 index e6a1388..0000000 --- a/lib/src/components/bottom_sheet.dart +++ /dev/null @@ -1,69 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:animate_do/animate_do.dart'; - -class BottomSheetExpanded extends StatelessWidget { - final List children; - final double horizontalPadding; - - const BottomSheetExpanded({ - Key? key, - required this.children, - this.horizontalPadding = 35, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return Scaffold( - body: Container( - width: double.infinity, - decoration: const BoxDecoration( - gradient: LinearGradient( - begin: Alignment.topCenter, - colors: [ - Color.fromARGB(255, 139, 224, 255), - Color.fromARGB(255, 152, 228, 255), - Color(0xFFD6F4FF), - ], - ), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox(height: 50), - Center( - child: FadeInLeft( - duration: const Duration(milliseconds: 1000), - child: const Image( - image: AssetImage('images/logo_prosapp.png'), - ), - ), - ), - const SizedBox(height: 30), - Expanded( - child: FadeInUpBig( - duration: const Duration(milliseconds: 1000), - child: Container( - decoration: const BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.only( - topLeft: Radius.circular(60), - topRight: Radius.circular(60), - ), - ), - child: Padding( - padding: EdgeInsets.symmetric( - horizontal: horizontalPadding, - vertical: 15, - ), - child: SingleChildScrollView( - child: Column(children: children)), - ), - ), - ), - ), - ], - ), - ), - ); - } -} diff --git a/lib/src/components/column_padding.dart b/lib/src/components/column_padding.dart deleted file mode 100644 index fffe675..0000000 --- a/lib/src/components/column_padding.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:flutter/material.dart'; - -class ColumnPadding extends StatelessWidget { - final List children; - final EdgeInsetsGeometry padding; - final MainAxisAlignment alineacion; - - const ColumnPadding({ - super.key, - required this.children, - required this.padding, - required this.alineacion, - }); - - @override - Widget build(BuildContext context) { - return Padding( - padding: padding, - child: Column(mainAxisAlignment: alineacion, children: children)); - } -} diff --git a/lib/src/components/drawer_professional.dart b/lib/src/components/drawer_professional.dart deleted file mode 100644 index 3e4100b..0000000 --- a/lib/src/components/drawer_professional.dart +++ /dev/null @@ -1,434 +0,0 @@ -import 'package:firebase_auth/firebase_auth.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_rating_bar/flutter_rating_bar.dart'; -import 'package:prosappco/screens/web/web_view_screen.dart'; -import 'package:prosappco/src/authentication/authentication_repository.dart'; -import 'package:prosappco/src/components/photo_view.dart'; -import 'package:prosappco/src/models/user_model.dart'; -import 'package:prosappco/src/presentation/screens/calendar.dart'; -import 'package:prosappco/src/presentation/screens/my_services_pro.dart'; -import 'package:prosappco/src/presentation/screens/profile/profile.dart'; -import 'package:prosappco/src/presentation/screens/profile/profile_pro.dart'; -import 'package:prosappco/src/presentation/screens/profile/profile_pro_web.dart'; -import 'package:prosappco/src/presentation/screens/reputacion_pro.dart'; -import 'package:prosappco/src/presentation/screens/web_view.dart'; -import '../models/scores_model.dart'; -import '../presentation/screens/configuracion.dart'; -import '../presentation/screens/support.dart'; -import 'package:url_launcher/url_launcher.dart'; - -class DrawerProfessional extends StatefulWidget { - @override - State createState() => _DrawerProfessionalState(); -} - -class _DrawerProfessionalState extends State { - final uid = AuthenticationRepository.instance.getCurrentUserUid(); - UserModel? user; - ScoresModel? scoresModel; - - Future _irSugerencias() async { - const url = 'https://admin.prosapp.co/sugerencias'; - if (await canLaunch(url)) { - await launch(url); - } else { - throw 'No se pudo abrir la URL $url'; - } - } - - @override - void initState() { - super.initState(); - - if (user == null) { - UserModel.getUser(uid.toString()).then( - (UserModel s) => setState(() => user = s), - ); - } - - if (scoresModel == null) { - ScoresModel.scoreTo(uid.toString(), true, false).then( - (ScoresModel s) => setState(() => scoresModel = s), - ); - } - } - - @override - Widget build(BuildContext context) { - final User? currentUser = FirebaseAuth.instance.currentUser; - - return Drawer( - child: Container( - color: const Color(0xFFE9F9FF), - child: Column( - children: [ - Container( - color: Colors.white, - child: Column( - children: [ - ListTile( - onTap: () { - Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return const ProfileScreen(); - }, - ), - ); - }, - title: Text(user?.name ?? '', - style: const TextStyle(fontWeight: FontWeight.bold)), - subtitle: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - currentUser?.phoneNumber ?? '', - style: const TextStyle(fontSize: 12), - ), - Text( - user?.city ?? '', - style: const TextStyle(fontSize: 12), - ), - ], - ), - leading: ReferencePhoto( - ref: user?.photo, - size: 55, - sizeCircle: 60, - ), - trailing: const Icon(Icons.keyboard_arrow_right, - color: Colors.black), - contentPadding: const EdgeInsets.symmetric( - vertical: 20, horizontal: 16), - ), - ], - ), - ), - Container( - decoration: BoxDecoration( - boxShadow: [ - BoxShadow( - color: Colors.grey.withOpacity(0.3), - spreadRadius: 1, - blurRadius: 3, - offset: const Offset(0, 0), // changes position of shadow - ), - ], - ), - child: Divider( - height: 0, - color: Colors.grey[300], - ), - ), - Expanded( - child: Column( - children: [ - ListTile( - onTap: () { - Navigator.of(context).push( - CupertinoPageRoute( - builder: (BuildContext context) { - return MyServicesProScreen(); - }, - ), - ); - }, - leading: const Icon( - Icons.history, - color: Colors.black, - ), - title: const Text( - 'Mis servicios', - style: TextStyle(fontSize: 15), - ), - ), - ListTile( - onTap: () { - Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - if (kIsWeb) { - return const ProfileProWebScreen(); - } else { - return const ProfileProScreen(); - } - }, - ), - ); - }, - leading: const Icon( - Icons.person_outline, - color: Colors.black, - ), - title: const Text( - 'Perfil profesional', - style: TextStyle(fontSize: 15), - ), - ), - ListTile( - onTap: () { - Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return const ConfiguracionScreen(); - }, - ), - ); - }, - leading: const Icon( - Icons.construction_outlined, - color: Colors.black, - ), - title: const Text( - 'Configuración', - style: TextStyle(fontSize: 15), - ), - ), - ListTile( - onTap: () { - Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return const SupportScreen(); - }, - ), - ); - }, - leading: const Icon( - Icons.question_mark_rounded, - color: Colors.black, - ), - title: const Text( - 'Soporte', - style: TextStyle(fontSize: 15), - ), - ), - ListTile( - onTap: () { - if (kIsWeb) { - _irSugerencias(); - } else { - Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return WebViewScreen( - label: 'Sugerencias', - link: 'https://admin.prosapp.co/sugerencias'); - }, - ), - ); - } - }, - leading: const Icon( - Icons.campaign_outlined, - color: Colors.black, - ), - title: const Text( - 'Sugerencias', - style: TextStyle(fontSize: 15), - ), - ), - // ListTile( - // onTap: () { - // Navigator.of(context).push( - // CupertinoPageRoute( - // builder: (BuildContext context) { - // return const MessagesScreen(); - // }, - // ), - // ); - // }, - // leading: const Icon( - // Icons.messenger_outline, - // color: Colors.black, - // ), - // title: const Text( - // 'Mensajes', - // style: TextStyle(fontSize: 15), - // ), - // ), - ListTile( - onTap: () { - Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return const CalendarScreen(); - }, - ), - ); - }, - leading: const Icon( - Icons.calendar_month, - color: Colors.black, - ), - title: const Text( - 'Calendario', - style: TextStyle(fontSize: 15), - ), - ), - Builder(builder: (BuildContext context) { - return Container( - color: const Color(0xFF2BA4EC), - child: ListTile( - onTap: () { - if (ModalRoute.of(context)?.settings.name != - '/solicitud') { - Navigator.pushNamed(context, '/solicitud'); - } else { - Scaffold.of(context).openEndDrawer(); - } - }, - trailing: const Icon( - Icons.keyboard_arrow_right, - color: Colors.white, - ), - title: const Text( - 'Solicitudes', - style: TextStyle( - color: Colors.white, - fontSize: 17, - fontWeight: FontWeight.bold), - ), - contentPadding: const EdgeInsets.symmetric( - vertical: 5, horizontal: 16), - ), - ); - }), - Container( - decoration: BoxDecoration( - boxShadow: [ - BoxShadow( - color: Colors.grey.withOpacity(0.5), - spreadRadius: 2, - blurRadius: 3, - offset: - const Offset(0, 2), // changes position of shadow - ), - ], - ), - child: Container( - color: Colors.white, - child: ListTile( - onTap: () async { - Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return const ReputationProScreen(); - }, - ), - ); - }, - trailing: const Icon(Icons.keyboard_arrow_right, - color: Colors.black), - title: const Text( - 'Reputación', - style: TextStyle(color: Colors.black), - ), - subtitle: Row( - children: [ - RatingBar.builder( - initialRating: scoresModel?.average ?? 0, - minRating: 1, - direction: Axis.horizontal, - allowHalfRating: true, - itemCount: 5, - itemSize: 25, - maxRating: 5, - itemPadding: - const EdgeInsets.symmetric(horizontal: 0), - itemBuilder: (context, _) => const Icon( - Icons.star, - color: Color(0xFF2BA4EC), - ), - onRatingUpdate: (rating) {}, - ignoreGestures: true, - ), - const SizedBox(width: 5), - Text( - '(${scoresModel?.total.toString()}) ${scoresModel?.average.toStringAsFixed(1)}'), - ], - ), - ), - ), - ), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - 'Prosapp', - style: TextStyle(fontSize: 10, color: Colors.grey[700]), - ), - Padding( - padding: - const EdgeInsets.only(top: 7, left: 3, right: 3), - child: Text( - '®', - style: - TextStyle(fontSize: 25, color: Colors.grey[700]), - ), - ), - Text( - 'todos los derechos reservados', - style: TextStyle(fontSize: 10, color: Colors.grey[700]), - ), - ], - ), - ], - ), - ), - ElevatedButton( - onPressed: () { - Navigator.pushReplacementNamed(context, '/servicio'); - }, - style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFF2BA4EC), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(50), - ), - elevation: 0, - minimumSize: const Size(230, 45), - ), - child: const Text( - 'Modo usuario', - style: TextStyle( - color: Colors.white, - fontSize: 15, - ), - ), - ), - const SizedBox(height: 5), - ElevatedButton( - onPressed: () { - AuthenticationRepository.instance.logout(uid!); - }, - style: ElevatedButton.styleFrom( - backgroundColor: Colors.red, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(20)), - ), - minimumSize: const Size(230, 40), - ), - child: const Text( - 'Cerrar Sesión', - style: TextStyle( - color: Colors.white, - fontSize: 15, - ), - ), - ), - const SizedBox(height: 5), - ], - ), - ), - ); - } -} diff --git a/lib/src/components/network_utility.dart b/lib/src/components/network_utility.dart deleted file mode 100644 index 303f1ed..0000000 --- a/lib/src/components/network_utility.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:http/http.dart' as http; - -class NetworkUtility { - static Future fetchUrl(Uri uri, - {Map? headers}) async { - try { - final response = await http.get(uri, headers: headers); - if (response.statusCode == 200) { - return response.body; - } - } catch (e) { - debugPrint('error - ${e.toString()}'); - } - return null; - } -} diff --git a/lib/src/components/photo_view.dart b/lib/src/components/photo_view.dart deleted file mode 100644 index 2959a67..0000000 --- a/lib/src/components/photo_view.dart +++ /dev/null @@ -1,125 +0,0 @@ -import 'dart:io'; - -import 'package:firebase_storage/firebase_storage.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_animate/flutter_animate.dart'; - -const double photoSize = 100; -const double iconSize = 35; - -class ReferencePhoto extends StatelessWidget { - Reference? ref; - double size; - double sizeIcon; - double sizeCircle; - ReferencePhoto({ - super.key, - required this.ref, - this.sizeIcon = iconSize, - this.size = photoSize, - this.sizeCircle = photoSize, - }); - - Future downloadImage() async { - try { - if (ref != null) { - final imageData = await ref!.getData(); - if (imageData != null) { - return ClipOval( - child: Image.memory( - imageData, - width: size, - height: size, - fit: BoxFit.cover, - ), - ); - } - } - // ignore: empty_catches - } catch (e) {} - - return DefaultPhoto( - sizeDefault: sizeCircle, - iconDefault: sizeIcon, - ); - } - - @override - Widget build(BuildContext context) { - return FutureBuilder( - future: downloadImage(), - builder: (BuildContext context, AsyncSnapshot snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { - // mientras la llamada asíncrona está en proceso, muestra un mensaje de carga - return SizedBox( - width: size, - height: size, - child: Center( - child: DefaultPhoto(), - ), - ); - } else if (snapshot.connectionState == ConnectionState.done && - snapshot.hasData) { - return snapshot.data!; - } else { - return DefaultPhoto(); - } - }); - } -} - -class DefaultPhoto extends StatelessWidget { - double sizeDefault; - double iconDefault; - DefaultPhoto({ - super.key, - this.sizeDefault = photoSize, - this.iconDefault = iconSize, - }); - - @override - Widget build(BuildContext context) { - return Container( - width: sizeDefault, - height: sizeDefault, - decoration: BoxDecoration( - color: const Color(0xFF2BA4EC), - borderRadius: BorderRadius.circular(50), - ), - child: Icon( - Icons.person, - color: const Color.fromARGB(255, 255, 255, 255), - size: iconDefault, - ), - ); - } -} - -class LocalPhoto extends StatelessWidget { - File file; - LocalPhoto({super.key, required this.file}); - - @override - Widget build(BuildContext context) { - if (kIsWeb) { - return ClipOval( - child: Image.network( - file.path, - width: photoSize, - height: photoSize, - fit: BoxFit.cover, - ), - ); - } - - return ClipOval( - child: Image.file( - file, - width: photoSize, - height: photoSize, - fit: BoxFit.cover, - ), - ); - } -} diff --git a/lib/src/components/photo_view_web.dart b/lib/src/components/photo_view_web.dart deleted file mode 100644 index 77a0957..0000000 --- a/lib/src/components/photo_view_web.dart +++ /dev/null @@ -1,118 +0,0 @@ -import 'package:firebase_storage/firebase_storage.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_animate/flutter_animate.dart'; - -const double photoSize = 100; -const double iconSize = 55; - -class ReferencePhotoWeb extends StatelessWidget { - Reference? ref; - double size; - double sizeIcon; - double sizeCircle; - - ReferencePhotoWeb({ - super.key, - required this.ref, - this.sizeIcon = iconSize, - this.size = photoSize, - this.sizeCircle = photoSize, - }); - - Future downloadImage() async { - try { - if (ref != null) { - final imageData = await ref!.getData(); - if (imageData != null) { - return ClipOval( - child: Image.memory( - imageData, - width: size, - height: size, - fit: BoxFit.cover, - ), - ); - } - } - // ignore: empty_catches - } catch (e) {} - - return DefaultPhotoWeb( - sizeDefault: sizeCircle, - iconDefault: sizeIcon, - ); - } - - @override - Widget build(BuildContext context) { - return FutureBuilder( - future: downloadImage(), - builder: (BuildContext context, AsyncSnapshot snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { - // mientras la llamada asíncrona está en proceso, muestra un mensaje de carga - return SizedBox( - width: size, - height: size, - child: const Center( - child: CircularProgressIndicator(), - ), - ); - } else if (snapshot.connectionState == ConnectionState.done && - snapshot.hasData) { - return snapshot.data!; - } else { - return DefaultPhotoWeb(); - } - }); - } -} - -class DefaultPhotoWeb extends StatelessWidget { - double sizeDefault; - double iconDefault; - - DefaultPhotoWeb({ - super.key, - this.sizeDefault = photoSize, - this.iconDefault = iconSize, - }); - - @override - Widget build(BuildContext context) { - return Container( - width: sizeDefault, - height: sizeDefault, - decoration: BoxDecoration( - color: const Color(0xFF2BA4EC), - borderRadius: BorderRadius.circular(50), - ), - child: Icon( - Icons.person, - color: const Color.fromARGB(255, 255, 255, 255), - size: iconDefault, - ), - ); - } -} - -class LocalPhotoWeb extends StatelessWidget { - Uint8List? file; - LocalPhotoWeb({super.key, required this.file}); - - @override - Widget build(BuildContext context) { - if (file == null) { - return DefaultPhotoWeb(); - } else { - return ClipOval( - child: Image.memory( - file!, - width: photoSize, - height: photoSize, - fit: BoxFit.cover, - ), - ); - } - } -} diff --git a/lib/src/components/pop_appbar.dart b/lib/src/components/pop_appbar.dart deleted file mode 100644 index 338c93b..0000000 --- a/lib/src/components/pop_appbar.dart +++ /dev/null @@ -1,35 +0,0 @@ -import 'package:flutter/material.dart'; - -class PopAppbar extends StatelessWidget implements PreferredSizeWidget { - final VoidCallback onPressed; - final String label; - - const PopAppbar({ - super.key, - required this.onPressed, - required this.label, - }); - - @override - Size get preferredSize => Size.fromHeight(kToolbarHeight); - - @override - Widget build(BuildContext context) { - return AppBar( - backgroundColor: Colors.white, - leading: IconButton( - icon: const Icon(Icons.arrow_back), - onPressed: onPressed, - ), - iconTheme: const IconThemeData( - color: Colors.black, - ), - title: Text( - label, - style: const TextStyle( - color: Colors.black, - ), - ), - ); - } -} diff --git a/lib/src/components/primary_btn.dart b/lib/src/components/primary_btn.dart deleted file mode 100644 index 7197924..0000000 --- a/lib/src/components/primary_btn.dart +++ /dev/null @@ -1,46 +0,0 @@ -import 'package:flutter/material.dart'; - -class PrimaryButtom extends StatelessWidget { - final VoidCallback onPressed; - final String label; - final bool - isEnabled; // Nuevo parámetro para indicar si el botón está habilitado - - const PrimaryButtom({ - Key? key, - required this.onPressed, - required this.label, - this.isEnabled = true, // Valor predeterminado: habilitado - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return ElevatedButton( - onPressed: isEnabled - ? onPressed - : null, // Habilita/deshabilita el botón según isEnabled - style: ElevatedButton.styleFrom( - backgroundColor: isEnabled - ? const Color(0xFF2BA4EC) - : Colors.grey, // Cambia el color de fondo - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(50), - ), - elevation: isEnabled - ? 0 - : 0, // Cambia la elevación para dar una sensación de clickeabilidad - minimumSize: const Size(230, 60), - ), - child: Text( - label, - style: TextStyle( - color: isEnabled - ? Colors.white - : Colors.black, // Cambia el color del texto - fontWeight: FontWeight.bold, - fontSize: 18, - ), - ), - ); - } -} diff --git a/lib/src/components/schedule_picker.dart b/lib/src/components/schedule_picker.dart deleted file mode 100644 index fb2cec8..0000000 --- a/lib/src/components/schedule_picker.dart +++ /dev/null @@ -1,212 +0,0 @@ -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:flutter/material.dart'; -import 'package:intl/intl.dart'; - -typedef TimeCallback = void Function(TimeOfDay? pickedTime); - -class SchedulePicker extends StatefulWidget { - final String name; - Schedule schedule; - - SchedulePicker({super.key, required this.name, required this.schedule}); - - @override - State createState() => _SchedulePickerState(); -} - -class _SchedulePickerState extends State { - @override - Widget build(BuildContext context) { - return Column( - children: [ - customSwitch(widget.name, widget.schedule.habilitado, (value) { - widget.schedule.habilitado = value; - }), - ...datePickers(widget.schedule.habilitado), - const Divider( - height: 15, - color: Colors.grey, - ), - ], - ); - } - - List datePickers(bool value) { - if (!value) return []; - return [ - customSwitch( - 'Jornada continua', - widget.schedule.jornadaContinua, - (value) { - widget.schedule.jornadaContinua = value; - }, - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 25), - child: Row(children: [ - datePicker(widget.schedule.range1Hour1, - (pickedTime) => widget.schedule.range1Hour1 = pickedTime), - const Text('-'), - ...(!widget.schedule.jornadaContinua - ? [ - datePicker(widget.schedule.range1Hour2, - (pickedTime) => widget.schedule.range1Hour2 = pickedTime), - const Text(' '), - ] - : []), - ...(!widget.schedule.jornadaContinua - ? [ - datePicker(widget.schedule.range2Hour1, - (pickedTime) => widget.schedule.range2Hour1 = pickedTime), - const Text('-'), - ] - : []), - datePicker(widget.schedule.range2Hour2, - (pickedTime) => widget.schedule.range2Hour2 = pickedTime), - ]), - ), - ]; - } - - datePicker(TimeOfDay? time, TimeCallback callback) { - return Expanded( - child: TextFormField( - textAlign: TextAlign.center, - onTap: () async { - final TimeOfDay? pickedTime = await showTimePicker( - context: context, - initialTime: TimeOfDay.now(), - ); - if (pickedTime != null) { - setState(() { - callback(pickedTime); - }); - } - }, - readOnly: true, - decoration: const InputDecoration( - hintText: 'Hora', - ), - controller: TextEditingController( - text: time == null ? '' : time.format(context), - ), - style: const TextStyle(fontSize: 15), - ), - ); - } - - customSwitch(String text, bool switchValue, ValueChanged onChanged) { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 30), - child: SizedBox( - height: 40, - child: Row( - children: [ - Expanded( - child: Text( - text, - style: const TextStyle( - fontSize: 15, - color: Colors.black, - ), - )), - Transform.scale( - scale: 1.2, - child: Switch( - value: switchValue, - onChanged: (bool newValue) { - setState(() { - onChanged(newValue); - }); - }, - ), - ), - ], - ), - ), - ); - } -} - -class Schedule { - bool habilitado; - bool jornadaContinua; - TimeOfDay? range1Hour1; - TimeOfDay? range1Hour2; - TimeOfDay? range2Hour1; - TimeOfDay? range2Hour2; - - Schedule( - this.habilitado, - this.jornadaContinua, - this.range1Hour1, - this.range1Hour2, - this.range2Hour1, - this.range2Hour2, - ); - - static Schedule fromJson(Map json) { - return Schedule( - json['habilitado'], - json['jornadaContinua'], - _parseTime(json['range1Hour1']), - _parseTime(json['range1Hour2']), - _parseTime(json['range2Hour1']), - _parseTime(json['range2Hour2']), - ); - } - - static TimeOfDay? _parseTime(String? time) { - if (time == null) return null; - final components = time.split(' '); - final hourMinutes = components[0].split(':'); - final hour = int.parse(hourMinutes[0]); - final minutes = int.parse(hourMinutes[1]); - if (components[1] == 'PM' && hour < 12) { - return TimeOfDay(hour: hour + 12, minute: minutes); - } else if (components[1] == 'AM' && hour == 12) { - return TimeOfDay(hour: 0, minute: minutes); - } - return TimeOfDay(hour: hour, minute: minutes); - } - - static TimeOfDay stringToTimeOfDay(String? tod) { - if (tod == null) { - return TimeOfDay.now(); - } - final format = DateFormat.jm(); - return TimeOfDay.fromDateTime(format.parse(tod)); - } - - @override - String toString() { - return 'Schedule(habilitado: $habilitado, jornadaContinua: $jornadaContinua, ' - 'range1Hour1: $range1Hour1, range1Hour2: $range1Hour2, ' - 'range2Hour1: $range2Hour1, range2Hour2: $range2Hour2)'; - } - - static Future> getHorarios(String uid) async { - try { - final snapshot = - await FirebaseFirestore.instance.collection('users').doc(uid).get(); - final Map? data = snapshot.data(); - final Map? horarioData = data?['horario']; - final horarios = {}; - horarioData?.forEach((key, value) { - horarios[key] = Schedule.fromJson(value); - }); - return horarios; - } catch (e) { - print('Error getting user: $e'); - return { - "1": Schedule(false, false, null, null, null, null), - "2": Schedule(false, false, null, null, null, null), - "3": Schedule(false, false, null, null, null, null), - "4": Schedule(false, false, null, null, null, null), - "5": Schedule(false, false, null, null, null, null), - "6": Schedule(false, false, null, null, null, null), - "7": Schedule(false, false, null, null, null, null), - }; - } - } -} diff --git a/lib/src/controllers/add_name_email_city.dart b/lib/src/controllers/add_name_email_city.dart deleted file mode 100644 index 614072e..0000000 --- a/lib/src/controllers/add_name_email_city.dart +++ /dev/null @@ -1,10 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:get/get.dart'; - -class NameEmailCityController extends GetxController { - static NameEmailCityController get instance => Get.find(); - - final name = TextEditingController(); - final email = TextEditingController(); - final city = TextEditingController(); -} diff --git a/lib/src/controllers/info_ professional.dart b/lib/src/controllers/info_ professional.dart deleted file mode 100644 index db957c7..0000000 --- a/lib/src/controllers/info_ professional.dart +++ /dev/null @@ -1,10 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:get/get.dart'; - -class InforProfessionalController extends GetxController { - static InforProfessionalController get instance => Get.find(); - - final cedula = TextEditingController(); - final profesion = TextEditingController(); - final especializacion = TextEditingController(); -} diff --git a/lib/src/controllers/login_email_controller.dart b/lib/src/controllers/login_email_controller.dart deleted file mode 100644 index f109c11..0000000 --- a/lib/src/controllers/login_email_controller.dart +++ /dev/null @@ -1,15 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:get/get.dart'; -import 'package:prosappco/src/authentication/authentication_repository.dart'; - -class LoginEmailController extends GetxController { - static LoginEmailController get instance => Get.find(); - - final email = TextEditingController(); - final password = TextEditingController(); - - Future loginUser(String email, String password) async { - await AuthenticationRepository.instance - .loginWithEmailAndPassword(email, password); - } -} diff --git a/lib/src/controllers/new_phone_controller.dart b/lib/src/controllers/new_phone_controller.dart deleted file mode 100644 index 55d4a5e..0000000 --- a/lib/src/controllers/new_phone_controller.dart +++ /dev/null @@ -1,116 +0,0 @@ -import 'package:firebase_auth/firebase_auth.dart'; -import 'package:flutter/material.dart'; -import 'package:get/get.dart'; - -class NewPhoneController extends GetxController { - final FirebaseAuth _auth = FirebaseAuth.instance; - - final newPhoneNo = TextEditingController(text: ''); - final otpCode = TextEditingController(text: ''); - - Future updatePhoneNumber(newPhoneNo) async { - final currentUser = _auth.currentUser; - - if (currentUser?.phoneNumber == newPhoneNo) { - Get.snackbar( - 'Ya estas registrado', - 'Este es tu numero actual.', - snackPosition: SnackPosition.BOTTOM, - ); - } else { - try { - final PhoneVerificationCompleted verificationCompleted = - (PhoneAuthCredential credential) async { - await currentUser?.updatePhoneNumber(credential); - Get.snackbar( - 'Número de teléfono actualizado', - 'El número de teléfono se ha actualizado correctamente.', - snackPosition: SnackPosition.BOTTOM, - ); - }; - - final PhoneVerificationFailed verificationFailed = - (FirebaseAuthException e) { - Get.snackbar( - 'Ingresa un numero de telefono valido', - 'verifica que el campo tenga todos los caracteres o intentalo de nuevo ${e}', - snackPosition: SnackPosition.BOTTOM, - ); - }; - - final PhoneCodeSent codeSent = - (String verificationId, [int? forceResendingToken]) { - Get.defaultDialog( - title: 'Ingrese el código de verificación', - content: Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - children: [ - TextField( - controller: otpCode, - decoration: InputDecoration( - labelText: 'Código de verificación', - ), - ), - ], - ), - ), - actions: [ - TextButton( - onPressed: () { - Get.back(); - }, - child: Text('Cancelar'), - ), - ElevatedButton( - onPressed: () async { - try { - final PhoneAuthCredential credential = - PhoneAuthProvider.credential( - verificationId: verificationId, - smsCode: otpCode.text, - ); - await currentUser?.updatePhoneNumber(credential); - Get.back(); - Get.snackbar( - 'Número de teléfono actualizado', - 'El número de teléfono se ha actualizado correctamente.', - snackPosition: SnackPosition.BOTTOM, - ); - } catch (e) { - Get.snackbar( - 'Numero ya registrado', - 'El numero de telefono ingresado ya se encuentra registrado', - snackPosition: SnackPosition.BOTTOM, - ); - } - }, - child: Text('Actualizar'), - ), - ], - ); - }; - - final PhoneCodeAutoRetrievalTimeout codeAutoRetrievalTimeout = - (String verificationId) { - // Aquí puedes hacer algo si se agota el tiempo de espera para ingresar el código de verificación automáticamente. - }; - - await _auth.verifyPhoneNumber( - phoneNumber: newPhoneNo, - verificationCompleted: verificationCompleted, - verificationFailed: verificationFailed, - codeSent: codeSent, - codeAutoRetrievalTimeout: codeAutoRetrievalTimeout, - ); - } catch (e) { - print('Error actualizando el número de teléfono: $e'); - Get.snackbar( - 'Error actualizando el número de teléfono', - 'Ha ocurrido un error al actualizar el número de teléfono: $e', - snackPosition: SnackPosition.BOTTOM, - ); - } - } - } -} diff --git a/lib/src/controllers/otp_controller.dart b/lib/src/controllers/otp_controller.dart deleted file mode 100644 index 6819caa..0000000 --- a/lib/src/controllers/otp_controller.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:get/get.dart'; -import 'package:prosappco/src/authentication/authentication_repository.dart'; -// import 'package:prosappco/src/presentation/screens/service.dart'; -import 'package:prosappco/src/presentation/screens/map/service.dart'; -import 'package:prosappco/src/presentation/screens/service_web.dart'; - -class OTPController extends GetxController { - static OTPController get instance => Get.find(); - - Future verifyOTP(String otp) async { - var isVerified = AuthenticationRepository.instance.verifyOTP(otp); - await isVerified - ? kIsWeb - ? Get.offAll(const ServiceWebScreen()) - : Get.to(const ServiceScreen()) - : Get.back(); - } -} diff --git a/lib/src/controllers/phone_auth_controller.dart b/lib/src/controllers/phone_auth_controller.dart deleted file mode 100644 index 0a179be..0000000 --- a/lib/src/controllers/phone_auth_controller.dart +++ /dev/null @@ -1,13 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:get/get.dart'; -import 'package:prosappco/src/authentication/authentication_repository.dart'; - -class PhoneAuthController extends GetxController { - static PhoneAuthController get instance => Get.find(); - - final phoneNo = TextEditingController(); - - Future phoneAuthentication(String phoneNo) async { - await AuthenticationRepository.instance.phoneAuthentication(phoneNo); - } -} diff --git a/lib/src/controllers/register_controller.dart b/lib/src/controllers/register_controller.dart deleted file mode 100644 index 210a85f..0000000 --- a/lib/src/controllers/register_controller.dart +++ /dev/null @@ -1,15 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:get/get.dart'; -import 'package:prosappco/src/authentication/authentication_repository.dart'; - -class RegisterController extends GetxController { - static RegisterController get instance => Get.find(); - - final email = TextEditingController(); - final password = TextEditingController(); - - Future registerUser(String email, String password) async { - await AuthenticationRepository.instance - .createUserWithEmailAndPassword(email, password); - } -} diff --git a/lib/src/models/chat_model.dart b/lib/src/models/chat_model.dart deleted file mode 100644 index b632d7a..0000000 --- a/lib/src/models/chat_model.dart +++ /dev/null @@ -1,114 +0,0 @@ -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:prosappco/src/models/user_model.dart'; - -class ChatModel { - List messages; - String professional_id; - String user_id; - UserModel? user; - UserModel? professional; - String id; - - ChatModel( - {required this.messages, - required this.professional_id, - required this.user_id, - this.user, - this.professional, - required this.id}); - - static Future fromDocumentSnapshot2( - DocumentSnapshot> snapshot, - bool fillUserModel) async { - try { - List messages = []; - List messagesData = snapshot.get('message') ?? []; - - for (var data in messagesData) { - messages.add(MessageModel( - user: data['user'] ?? '', - content: data['content'] ?? '', - timestamp: (data['timestamp'] ?? '' as Timestamp).toDate(), - )); - } - - return ChatModel( - messages: messages, - professional_id: snapshot.get('professional_id') ?? '', - user_id: snapshot.get('user_id') ?? '', - user: fillUserModel - ? await UserModel.getUser(snapshot.get('user_id') ?? '') - : null, - professional: fillUserModel - ? await UserModel.getUser(snapshot.get('professional_id') ?? '') - : null, - id: snapshot.id); - } catch (e) { - print('error $e'); - return ChatModel(messages: [], professional_id: '', user_id: '', id: ''); - } - } - - static ChatModel fromDocumentSnapshot( - DocumentSnapshot> snapshot, - ) { - try { - List messages = []; - List messagesData = snapshot.get('message') ?? []; - - for (var data in messagesData) { - messages.add(MessageModel( - user: data['user'] ?? '', - content: data['content'] ?? '', - timestamp: (data['timestamp'] as Timestamp).toDate(), - )); - } - - return ChatModel( - messages: messages, - professional_id: snapshot.get('professional_id'), - user_id: snapshot.get('user_id'), - id: snapshot.id); - } catch (e) { - print('error $e'); - return ChatModel(messages: [], professional_id: '', user_id: '', id: ''); - } - } - - static Future> getChatsByProId(String userReceived) async { - final receivedScoresQuery = FirebaseFirestore.instance - .collection('chats') - .where('professional_id', isEqualTo: userReceived); - - final receivedScoresSnapshot = await receivedScoresQuery.get(); - - final receivedScores = await Future.wait(receivedScoresSnapshot.docs - .map((doc) async => await fromDocumentSnapshot2(doc, true)) - .toList()); - - return receivedScores; - } - - static Future> getChatsByUserId(String userReceived) async { - final receivedScoresQuery = FirebaseFirestore.instance - .collection('chats') - .where('user_id', isEqualTo: userReceived); - - final receivedScoresSnapshot = await receivedScoresQuery.get(); - - final receivedScores = await Future.wait(receivedScoresSnapshot.docs - .map((doc) async => await fromDocumentSnapshot2(doc, true)) - .toList()); - - return receivedScores; - } -} - -class MessageModel { - String user; - String content; - DateTime timestamp; - - MessageModel( - {required this.user, required this.content, required this.timestamp}); -} diff --git a/lib/src/models/event_model.dart b/lib/src/models/event_model.dart deleted file mode 100644 index 16bf23b..0000000 --- a/lib/src/models/event_model.dart +++ /dev/null @@ -1,259 +0,0 @@ -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:prosappco/src/authentication/authentication_repository.dart'; -import 'package:prosappco/src/models/scores_model.dart'; - -final uid = AuthenticationRepository.instance.getCurrentUserUid(); - -class EventoService { - Future createEvent( - String title, - String description, - String day, - String range1Hour1, - String range1Hour2, - String professionalId, - String ubicacion, - String address, - double latitude, - double longitude, - String status, - int? tarifa, - bool professionalScored, - bool userScored, - ) async { - try { - DateTime ahora = DateTime.now(); - - final eventId = - await FirebaseFirestore.instance.collection('services').add({ - 'user_id': uid, - 'title': title, - 'description': description, - 'day': day, - 'range1Hour1': range1Hour1, - 'range1Hour2': range1Hour2, - 'professional_id': professionalId, - 'ubicacion': ubicacion, - 'address': address, - 'latitude': latitude, - 'longitude': longitude, - 'status': status, - 'Timestamp': ahora, - 'tarifa': tarifa ?? 0, - 'professional_scored': professionalScored, - 'user_scored': userScored, - }).then((value) { - FirebaseFirestore.instance.collection('users').doc(uid).update({ - 'services': FieldValue.arrayUnion([value.id]) - }); - - return value.id; - }); - - return eventId; - } catch (e) { - print('Evento $e'); - } - return null; - } -} - -Future> getByProId(String day) async { - try { - var snapshot = await FirebaseFirestore.instance - .collection('services') - // .where('professional_id', isEqualTo: uid) - .where('day', isEqualTo: day.toString()) - .where('status', isEqualTo: 'aprobado') - // .orderBy('Timestamp', descending: true) - .get(); - - List eventos = []; - for (var element in snapshot.docs) { - final event = Event.fromJson(element.data()); - event.scoresModel = await ScoresModel.scoreTo(event.userId, false, false); - event.id = element.id; - eventos.add(event); - } - return eventos; - } catch (e) { - print('Error getByProId $e'); - return []; - } -} - -Future> getByProIdAll(String state1, String state2) async { - try { - var snapshot = await FirebaseFirestore.instance - .collection('services') - .where('professional_id', isEqualTo: uid) - .where('status', whereIn: [state1, state2]).get(); - - List eventos = []; - for (var element in snapshot.docs) { - final event = Event.fromJson(element.data()); - event.scoresModel = await ScoresModel.scoreTo(event.userId, false, false); - event.id = element.id; - eventos.add(event); - } - return eventos; - } catch (e) { - print('Error getByProId $e'); - return []; - } -} - -Future> getByUserIdAll(String state1, String state2) async { - try { - var snapshot = await FirebaseFirestore.instance - .collection('services') - .where('user_id', isEqualTo: uid) - .where('status', whereIn: [state1, state2]).get(); - - List eventos = []; - for (var element in snapshot.docs) { - final event = Event.fromJson(element.data()); - event.scoresModel = await ScoresModel.scoreTo(event.userId, false, false); - event.id = element.id; - eventos.add(event); - } - return eventos; - } catch (e) { - print('Error getByUserId $e'); - return []; - } -} - -class Event { - String? id; - String title; - String? description; - String day; - String range1Hour1; - String? range1Hour2; - String userId; - String professionalId; - String? ubicacion; - String? address; - double? longitud; - double? latitud; - String status; - ScoresModel? scoresModel; - int? tarifa; - bool professionalScored; - bool userScored; - Timestamp? timeStamp; - - Event({ - this.id, - required this.title, - this.description, - required this.day, - required this.range1Hour1, - this.range1Hour2, - required this.userId, - required this.professionalId, - this.ubicacion, - this.address, - this.longitud, - this.latitud, - this.status = 'pendiente', - this.timeStamp, - this.tarifa, - this.professionalScored = false, - this.userScored = false, - }); - - factory Event.fromJson(Map json) { - return Event( - id: json['id'] ?? '', - title: json['title'], - description: json['description'], - day: json['day'], - range1Hour1: json['range1Hour1'], - range1Hour2: json['range1Hour2'], - userId: json['user_id'], - professionalId: json['professional_id'], - ubicacion: json['ubicacion'] ?? '', - address: json['address'] ?? '', - longitud: json['longitude'] ?? 0, - latitud: json['latitude'] ?? 0, - status: json['status'], - timeStamp: json['Timestamp'] ?? 0, - tarifa: json['tarifas'] ?? 0, - professionalScored: json['professional_scored'], - userScored: json['user_scored'], - ); - } - - static Future getEventById(String uid) async { - try { - final snapshot = await FirebaseFirestore.instance - .collection('services') - .doc(uid) - .get(); - final Map? data = snapshot.data(); - return Event.fromJson(data!); - } catch (e) { - print('Error getting user: $e'); - return Event( - title: '', day: '', range1Hour1: '', userId: '', professionalId: ''); - } - } - - static Future> getEventsAllById(String uid) async { - try { - var snapshot = await FirebaseFirestore.instance - .collection('services') - .where('professional_id', isEqualTo: uid) - .get(); - - List eventos = []; - for (var element in snapshot.docs) { - eventos.add(Event.fromJson(element.data())); - } - return eventos; - } catch (e) { - print('Error getByProId $e'); - return []; - } - } - - static Future> getEventsAllByIdAndStatus(String uid) async { - try { - var snapshot = await FirebaseFirestore.instance - .collection('services') - .where('professional_id', isEqualTo: uid) - .where('status', whereIn: ['aprobado', 'pendiente']).get(); - - List eventos = []; - for (var element in snapshot.docs) { - eventos.add(Event.fromJson(element.data())); - } - return eventos; - } catch (e) { - print('Error getByProId $e'); - return []; - } - } - - static Future> getEventsAllByIdStatus( - String uid, String state) async { - try { - var snapshot = await FirebaseFirestore.instance - .collection('services') - .where('professional_id', isEqualTo: uid) - .where('status', isEqualTo: state) - .get(); - - List eventos = []; - for (var element in snapshot.docs) { - eventos.add(Event.fromJson(element.data())); - } - return eventos; - } catch (e) { - print('Error getByProId $e'); - return []; - } - } -} diff --git a/lib/src/models/professional_model.dart b/lib/src/models/professional_model.dart deleted file mode 100644 index 7a9f483..0000000 --- a/lib/src/models/professional_model.dart +++ /dev/null @@ -1,87 +0,0 @@ -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:firebase_storage/firebase_storage.dart'; -import 'package:prosappco/src/authentication/authentication_repository.dart'; -import 'package:prosappco/src/models/scores_model.dart'; - -class Professional { - final String id; - final Reference professionalRef; - final String name; - final String professionName; - final String cityName; - final String ubicacion; - final String realAddress; - final double latitude; - final double longitude; - final List professionalEspecializado; - final ScoresModel scores; - final int? tarifa; - final String? token; - - Professional({ - required this.id, - required this.professionalRef, - required this.name, - required this.professionName, - required this.cityName, - required this.ubicacion, - required this.professionalEspecializado, - required this.scores, - required this.realAddress, - required this.latitude, - required this.longitude, - this.tarifa, - this.token, - }); - - String getEspecializaciones() { - return professionalEspecializado.join(',\n'); - } - - @override - String toString() { - return 'Professional { professionalRef: $professionalRef, name: $name, professionName: $professionName, cityName: $cityName, ubicacion: $ubicacion, realAddress: $realAddress, latitude: $latitude, longitude: $longitude, professionalEspecializado: ${getEspecializaciones()} tarifa: $tarifa token: $token, }'; - } - - static Future getProfessional(String uid) async { - var photo = '...'; - final FirebaseStorage storage = FirebaseStorage.instance; - - try { - DocumentSnapshot user = - await FirebaseFirestore.instance.collection('users').doc(uid).get(); - - Map data = user.data() as Map; - - photo = await AuthenticationRepository.instance.getPhoto(user.id); - - if (data['estado'] == 'activo') { - List especializaciones; - - especializaciones = (data['especializaciones'] as List) - .map((e) => e.toString()) - .toList(); - - Professional professional = Professional( - id: user.id, - name: data['name'], - professionName: data['profesion'], - cityName: data['city'], - professionalRef: storage.ref().child(photo), - professionalEspecializado: especializaciones, - ubicacion: data['ubicacion'] ?? '', - realAddress: data['address'] ?? '', - latitude: data['latitude'] ?? 0, - longitude: data['longitude'] ?? 0, - scores: await ScoresModel.scoreFrom(uid, true, true), - tarifa: data['tarifas'] ?? 0, - token: data['token'] ?? '', - ); - return professional; - } - } catch (e) { - print('Error al obtener profesionales: $e'); - } - return null; - } -} diff --git a/lib/src/models/scores_model.dart b/lib/src/models/scores_model.dart deleted file mode 100644 index 1b5a255..0000000 --- a/lib/src/models/scores_model.dart +++ /dev/null @@ -1,112 +0,0 @@ -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:firebase_storage/firebase_storage.dart'; -import 'package:prosappco/src/models/user_model.dart'; - -class ScoresModel { - late int total; - late double average; - final List details; - - ScoresModel(this.details) { - total = details.length; - average = averageScore(details); - } - - @override - String toString() { - return 'ScoresModel{total: $total, average: $average, details: $details}'; - } - - double averageScore(List details) { - if (details.isEmpty) { - return 0.0; - } - final sum = details.map((detail) => detail.score).reduce((a, b) => a + b); - return sum / details.length; - } - - // Me - static Future scoreTo( - String? userReceived, bool isFromClient, bool userInfo) async { - final receivedScoresQuery = FirebaseFirestore.instance - .collection('scores') - .where('is_from_professional', isEqualTo: isFromClient) - .where('to_user', isEqualTo: userReceived); - - final receivedScoresSnapshot = await receivedScoresQuery.get(); - - final receivedScores = await Future.wait(receivedScoresSnapshot.docs - .map((doc) async => - await ScoreDetailModel.fromDocumentSnapshot(doc, userInfo)) - .toList()); - - return ScoresModel(receivedScores); - } - - // You - static Future scoreFrom( - String userGiven, bool isFromClient, bool userInfo) async { - final givenScoresQuery = FirebaseFirestore.instance - .collection('scores') - .where('is_from_professional', isEqualTo: isFromClient) - .where('from_user', isEqualTo: userGiven); - - final givenScoresSnapshot = await givenScoresQuery.get(); - - final givenScores = await Future.wait(givenScoresSnapshot.docs - .map((doc) async => - await ScoreDetailModel.fromDocumentSnapshot(doc, userInfo)) - .toList()); - - return ScoresModel(givenScores); - } -} - -class ScoreDetailModel { - final String id; - final double score; - final String fromUser; - final String toUser; - final String comment; - final bool isFromClient; - - final String name; - final Reference? avatar; - - ScoreDetailModel({ - required this.id, - required this.score, - required this.fromUser, - required this.toUser, - required this.comment, - required this.isFromClient, - required this.name, - required this.avatar, - }); - - @override - String toString() { - return 'ScoreDetailModel{id: $id, score: $score, fromUser: $fromUser, toUser: $toUser, comment: $comment, isFromClient: $isFromClient, name: $name, avatar: $avatar}'; - } - - static Future fromDocumentSnapshot( - DocumentSnapshot> snapshot, bool userInfo) async { - try { - final Map data = snapshot.data()!; - final user = userInfo ? await UserModel.getUser(data['from_user']) : null; - - return ScoreDetailModel( - id: snapshot.id, - score: double.parse(data['score'].toString()), - fromUser: data['from_user'], - toUser: data['to_user'], - comment: data['comment'], - isFromClient: data['is_from_professional'], - name: user?.name ?? "...", - avatar: user?.photo); - } catch (e) { - print('error en score $e'); - rethrow; - } - } -} diff --git a/lib/src/models/setting_model.dart b/lib/src/models/setting_model.dart deleted file mode 100644 index 2869680..0000000 --- a/lib/src/models/setting_model.dart +++ /dev/null @@ -1,110 +0,0 @@ -import 'package:cloud_firestore/cloud_firestore.dart'; - -class SettingModel { - final bool domicilios; - final bool google; - final bool tarifas; - final String titulo; - final String parrafo; - final String numero; - final String email; - final String dias; - final String horas; - final String version; - final String proliticasPrivacidad; - final String terminosCondiciones; - - SettingModel( - this.domicilios, - this.google, - this.tarifas, - this.titulo, - this.parrafo, - this.numero, - this.email, - this.dias, - this.horas, - this.version, - this.proliticasPrivacidad, - this.terminosCondiciones, - ); - - static Future fromJson(Map? json) async { - try { - if (json == null) { - return SettingModel( - false, - false, - false, - '', - '', - '', - '', - '', - '', - '', - '', - '', - ); - } - - return SettingModel( - json['domicilios'] ?? false, - json['google'] ?? false, - json['tarifas'] ?? false, - json['titulo_soporte'] ?? '', - json['parrafo_soporte'] ?? '', - json['numero_soporte'] ?? '', - json['email_soporte'] ?? '', - json['dias_soporte'] ?? '', - json['horas_soporte'] ?? '', - json['version'] ?? '', // Asegúrate de manejar nulos aquí - json['politicas_privacidad'] ?? '', - json['terminos_condiciones'] ?? '', - ); - } catch (e) { - print('Error settings: $e'); - return SettingModel( - false, - false, - false, - '', - '', - '', - '', - '', - '', - '', - '', - '', - ); - } - } - - static Future getSettings() async { - try { - final DocumentSnapshot> snapshot = - await FirebaseFirestore.instance - .collection('settings') - .doc('global') - .get(); - - final Map? data = snapshot.data(); - - return fromJson(data!); - } catch (e) { - print('Error getting settings: $e'); - return SettingModel( - false, false, false, '', '', '', '', '', '', '', '', ''); - } - } - - @override - String toString() { - return 'SettingModel { domicilios: $domicilios,google: $google, tarifas: $tarifas, ' - 'titulo: $titulo, parrafo: $parrafo, numero: $numero, ' - 'email: $email, dias: $dias, horas: $horas, ' - 'version: $version, politicasPrivacidad: $proliticasPrivacidad, ' - 'terminosCondiciones: $terminosCondiciones }'; - } -} diff --git a/lib/src/models/user_model.dart b/lib/src/models/user_model.dart deleted file mode 100644 index 4204d60..0000000 --- a/lib/src/models/user_model.dart +++ /dev/null @@ -1,75 +0,0 @@ -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:firebase_storage/firebase_storage.dart'; -import 'package:prosappco/src/presentation/screens/professional.dart'; - -class UserModel { - final String name; - final String city; - final String? profession; - final String? state; - final Reference? photo; - final int? tarifa; - final String? phoneNumber; - final String? token; - - UserModel(this.name, this.city, this.profession, this.state, this.photo, - this.tarifa, this.phoneNumber, this.token); - - static Future fromJson( - Map? json, String uid) async { - try { - if (json == null) return UserModel('', '', '', null, null, 0, '', ''); - - String? avatar = json['photo']; - return UserModel( - json['name'], - json['city'], - json['profesion'], - json['estado'], - avatar != null ? storage.ref().child(avatar) : null, - json['tarifas'] ?? 0, - json['phoneNumber'], - json['token'], - ); - } catch (e) { - print('$e'); - return UserModel('', '', '', null, null, 0, '', ''); - } - } - - @override - String toString() { - return 'UserModel(name: $name, city: $city, profession: $profession, state: $state, tarifa: $tarifa, phoneNumber: $phoneNumber, token: $token)'; - } - - static UserModel fromFirestore(Map firestoreMap) { - try { - String? avatar = firestoreMap['photo']; - return UserModel( - firestoreMap['name'] ?? 'Sin nombre', - firestoreMap['city'] ?? 'Sin ciudad', - firestoreMap['profesion'], - firestoreMap['estado'], - avatar != null ? storage.ref().child(avatar) : null, - firestoreMap['tarifas'] ?? 0, - firestoreMap['phoneNumber'] ?? 'Sin numero', - firestoreMap['token'], - ); - } catch (e) { - print('DesdeProvider $e'); - return UserModel('', '', '', null, null, 0, '', ''); - } - } - - static Future getUser(String uid) async { - try { - final snapshot = - await FirebaseFirestore.instance.collection('users').doc(uid).get(); - final Map? data = snapshot.data(); - return fromJson(data, uid); - } catch (e) { - print('Error getting user: $e'); - return UserModel('', '', '', null, null, 0, '', ''); - } - } -} diff --git a/lib/src/presentation/screens/about.dart b/lib/src/presentation/screens/about.dart deleted file mode 100644 index 4bf1e71..0000000 --- a/lib/src/presentation/screens/about.dart +++ /dev/null @@ -1,101 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:prosappco/src/components/pop_appbar.dart'; -import 'package:prosappco/src/models/setting_model.dart'; -import 'package:prosappco/src/presentation/screens/web_view.dart'; -import 'package:url_launcher/url_launcher.dart'; - -class AboutScreen extends StatefulWidget { - const AboutScreen({super.key}); - - @override - State createState() => _AboutScreenState(); -} - -class _AboutScreenState extends State { - SettingModel? settings; - - @override - void initState() { - super.initState(); - if (settings == null) { - SettingModel.getSettings().then( - (SettingModel value) => setState(() { - settings = value; - }), - ); - } - } - - void _launchURL(String url) async { - if (await canLaunch(url)) { - await launch(url, forceSafariVC: false, forceWebView: false); - } else { - throw 'No se pudo abrir el enlace $url'; - } - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: PopAppbar( - onPressed: () { - Navigator.pop(context); - }, - label: 'Acerca de la aplicación'), - body: ListView( - children: [ - ListTile( - onTap: () { - if (kIsWeb) { - _launchURL(settings?.proliticasPrivacidad ?? ''); - } else { - Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return WebViewScreen( - label: 'Políticas de privacidad', - link: settings?.proliticasPrivacidad ?? '', - ); - }, - ), - ); - } - }, - title: const Text('Políticas de privacidad'), - trailing: - const Icon(Icons.keyboard_arrow_right, color: Colors.black), - ), - ListTile( - onTap: () { - if (kIsWeb) { - _launchURL(settings?.terminosCondiciones ?? ''); - } else { - Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return WebViewScreen( - label: 'Términos y condiciones', - link: settings?.terminosCondiciones ?? '', - ); - }, - ), - ); - } - }, - title: const Text('Términos y condiciones'), - trailing: - const Icon(Icons.keyboard_arrow_right, color: Colors.black), - ), - ListTile( - title: const Text('Versión de la aplicación'), - subtitle: Text(settings?.version ?? ''), - ), - ], - ), - ); - } -} diff --git a/lib/src/presentation/screens/calendar.dart b/lib/src/presentation/screens/calendar.dart deleted file mode 100644 index 69daf47..0000000 --- a/lib/src/presentation/screens/calendar.dart +++ /dev/null @@ -1,496 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_rating_bar/flutter_rating_bar.dart'; -import 'package:prosappco/src/authentication/authentication_repository.dart'; -import 'package:prosappco/src/components/pop_appbar.dart'; -import 'package:prosappco/src/components/primary_btn.dart'; -import 'package:prosappco/src/models/event_model.dart'; -import 'package:prosappco/src/presentation/screens/cita.dart'; -import 'package:prosappco/src/presentation/widgets/shared/loading_item_list.dart'; -import 'package:table_calendar/table_calendar.dart'; -import 'package:intl/intl.dart'; - -class CalendarScreen extends StatefulWidget { - const CalendarScreen({super.key}); - - @override - State createState() => _CalendarScreenState(); -} - -class _CalendarScreenState extends State { - final uid = AuthenticationRepository.instance.getCurrentUserUid(); - List? _events; - - final _titleController = TextEditingController(); - final _descriptionController = TextEditingController(); - - EventoService eventoService = EventoService(); - CalendarFormat _calendarFormat = CalendarFormat.month; - - DateTime today = DateTime.now(); - DateTime now = DateTime.now(); - - TimeOfDay? _selectedTime1; - TimeOfDay? _selectedTime2; - - Future _selectTime1(BuildContext context) async { - final TimeOfDay? pickedTime1 = await showTimePicker( - context: context, - initialTime: TimeOfDay.now(), - ); - if (pickedTime1 != null) { - setState(() { - _selectedTime1 = pickedTime1; - }); - } - return pickedTime1; - } - - Future _selectTime2(BuildContext context) async { - final TimeOfDay? pickedTime2 = await showTimePicker( - context: context, - initialTime: TimeOfDay.now(), - ); - if (pickedTime2 != null) { - setState(() { - _selectedTime2 = pickedTime2; - }); - } - - return pickedTime2; - } - - @override - void initState() { - super.initState(); - - today = DateTime.utc(today.year, today.month, today.day); - - if (_events == null) { - Event.getEventsAllByIdStatus(uid ?? "", 'aprobado') - .then((value) => setState(() { - _events = value; - })); - } - } - - void _onDaySelected(DateTime day, DateTime focusedDay) { - setState(() { - today = day; - }); - } - - void _onFormatChange(CalendarFormat format) { - setState(() { - _calendarFormat = format; - }); - } - - @override - Widget build(BuildContext context) { - if (_events == null) { - return const Scaffold( - body: Center( - child: CircularProgressIndicator(), - ), - ); - } - - var events = _events!; - // DateTime firstDay = today.subtract(Duration(days: 365)); - DateTime lastDay = today.add(const Duration(days: 365)); - - return Scaffold( - floatingActionButtonLocation: kIsWeb - ? FloatingActionButtonLocation.startFloat - : FloatingActionButtonLocation.endFloat, - resizeToAvoidBottomInset: false, - appBar: PopAppbar( - onPressed: () { - Navigator.pop(context); - }, - label: 'Calendario', - ), - body: Column( - children: [ - Container( - color: const Color.fromARGB(255, 224, 247, 255), - child: TableCalendar( - locale: 'es_MX', - firstDay: DateTime.utc(2010, 10, 16), - lastDay: lastDay, - focusedDay: today, - availableGestures: AvailableGestures.all, - onDaySelected: _onDaySelected, - selectedDayPredicate: (day) => isSameDay(day, today), - calendarFormat: _calendarFormat, - onFormatChanged: _onFormatChange, - eventLoader: (date) { - return events - .where((element) { - DateTime day = DateTime.parse(element.day); - return (date.year == day.year && - date.month == day.month && - date.day == day.day); - }) - .map((e) => e.description) - .toList(); - }, - availableCalendarFormats: const { - CalendarFormat.month: 'Mes', - CalendarFormat.week: 'Semana', - CalendarFormat.twoWeeks: '2 Semanas', - }, - ), - ), - SizedBox( - width: double.infinity, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 8), - child: Text(DateFormat('dd MMMM yyyy', 'es').format(today), - style: const TextStyle( - color: Colors.black, - fontSize: 16, - fontWeight: FontWeight.w600, - )), - ), - ), - const Divider( - height: 0, - ), - Expanded(child: SingleChildScrollView(child: _eventList())) - ], - ), - floatingActionButton: FloatingActionButton( - onPressed: _showDialog, - child: const Icon(Icons.add), - ), - ); - } - - void _showDialog() { - showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(15), - ), - content: StatefulBuilder( - builder: (BuildContext context, StateSetter setStateDialog) { - return SizedBox( - height: 800, - child: Column( - children: [ - Padding( - padding: const EdgeInsets.symmetric( - horizontal: 0, vertical: 15), - child: Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - Text( - _selectedTime1 == null - ? '' - : _selectedTime1!.format(context), - style: TextStyle( - color: Colors.grey[500], - fontSize: 12, - ), - ), - _selectedTime2 != null && _selectedTime1 != null - ? Text( - ' - ', - style: TextStyle( - color: Colors.grey[500], - fontSize: 12, - ), - ) - : const SizedBox(), - Text( - _selectedTime2 == null - ? '' - : _selectedTime2!.format(context), - style: TextStyle( - color: Colors.grey[500], - fontSize: 12, - ), - ), - Text( - ' | ', - style: TextStyle( - color: Colors.grey[500], - ), - ), - Text( - DateFormat('dd MMMM yyyy', 'es').format(today), - style: const TextStyle( - fontSize: 13, - ), - ) - ], - ), - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10), - child: TextFormField( - controller: _titleController, - decoration: const InputDecoration(hintText: 'Titulo'), - ), - ), - Padding( - padding: const EdgeInsets.symmetric( - horizontal: 10, vertical: 20), - child: TextFormField( - controller: _descriptionController, - decoration: - const InputDecoration(hintText: 'Descripción'), - ), - ), - Padding( - padding: const EdgeInsets.only(bottom: 40), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - SizedBox( - width: 80, - child: TextFormField( - textAlign: TextAlign.center, - onTap: () async { - var value = await _selectTime1(context); - setStateDialog(() { - _selectedTime1 = value; - }); - }, - readOnly: true, - decoration: const InputDecoration( - hintText: 'Hora', - ), - controller: TextEditingController( - text: _selectedTime1 == null - ? '' - : ' ${_selectedTime1!.format(context)}'), - style: const TextStyle(fontSize: 15), - ), - ), - const Text(' - '), - SizedBox( - width: 80, - child: TextFormField( - textAlign: TextAlign.center, - onTap: () async { - var value = await _selectTime2(context); - setStateDialog(() { - _selectedTime2 = value; - }); - }, - readOnly: true, - decoration: const InputDecoration( - hintText: 'Hora', - ), - controller: TextEditingController( - text: _selectedTime2 == null - ? '' - : ' ${_selectedTime2!.format(context)}'), - style: const TextStyle(fontSize: 15), - ), - ), - ], - ), - ), - PrimaryButtom( - onPressed: () async { - final DateTime combinedDate1 = DateTime( - today.year, - today.month, - today.day, - _selectedTime1!.hour, - _selectedTime1!.minute, - ); - final DateTime combinedDate2 = DateTime( - today.year, - today.month, - today.day, - _selectedTime2!.hour, - _selectedTime2!.minute, - ); - - await eventoService - .createEvent( - _titleController.text, - _descriptionController.text, - DateFormat('yyyy-MM-dd HH:mm:ss.SSS').format(today), - '$combinedDate1', - '$combinedDate2', - uid.toString(), - 'sitio', - '', - 0, - 0, - 'aprobado', - 0, - false, - false, - ) - .then((value) { - Navigator.pop(context); - _titleController.text = ''; - _descriptionController.text = ''; - }); - - Event.getEventsAllByIdStatus(uid ?? "", 'aprobado') - .then( - (value) => setState(() { - _events = value; - }), - ); - }, - label: 'Añadir evento', - ), - ], - ), - ); - }, - ), - ); - }, - ); - } - - Widget _eventList() { - return FutureBuilder( - future: getByProId(DateFormat("yyyy-MM-dd 00:00:00.000").format(today)), - builder: (BuildContext context, AsyncSnapshot> snapshot) { - List eventos = []; - - if (snapshot.connectionState == ConnectionState.waiting) { - return const Column( - children: [ - LoadingItemList(useCircleAvatar: false), - LoadingItemList(useCircleAvatar: false), - LoadingItemList(useCircleAvatar: false), - LoadingItemList(useCircleAvatar: false), - LoadingItemList(useCircleAvatar: false), - ], - ); - } - try { - snapshot.data!.sort((a, b) { - String? range1Hour1A = a.range1Hour1; - String? range1Hour1B = b.range1Hour1; - - DateTime dateTimeA = DateTime.parse(range1Hour1A); - DateTime dateTimeB = DateTime.parse(range1Hour1B); - - return dateTimeB.compareTo(dateTimeA); - }); - - eventos.addAll(snapshot.data!); - } catch (e) { - print("Error al cargar eventos: inflar $e"); - } - - if (eventos.isEmpty) { - return const Padding( - padding: EdgeInsets.only(top: 30), - child: Center( - child: Text( - 'No tienes citas', - style: TextStyle( - color: Colors.black, - fontSize: - 18, // Tamaño de fuente ajustado según tus preferencias - fontWeight: - FontWeight.w500, // Puedes ajustar el peso de la fuente - fontStyle: FontStyle.italic, // Puedes agregar estilo italic - // Otros estilos según tus preferencias - ), - ), - ), - ); - } - return Column( - children: [ - ...eventos.map( - (e) => ListTile( - onTap: () { - Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return CitaScreen(evento: e); - }, - ), - ); - }, - leading: Text( - TimeOfDay.fromDateTime(DateTime.parse(e.range1Hour1)) - .format(context)), - title: RichText( - text: TextSpan( - children: [ - TextSpan( - text: '${e.title}, ', - style: const TextStyle( - color: Colors.black, - fontWeight: FontWeight.bold, - fontSize: 16, - ), - ), - TextSpan( - text: DateFormat('dd MMM', 'es') - .format(DateTime.parse(e.day)), - style: const TextStyle( - color: Colors.grey, - fontSize: 16, - ), - ), - ], - ), - ), - subtitle: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - e.professionalId == e.userId - ? const SizedBox() - : RatingBar.builder( - initialRating: e.scoresModel?.average ?? 0, - minRating: 1, - direction: Axis.horizontal, - allowHalfRating: true, - itemCount: 5, - itemSize: 25, - maxRating: 5, - itemPadding: - const EdgeInsets.symmetric(horizontal: 0), - itemBuilder: (context, _) => const Icon( - Icons.star, - color: Color(0xFF2BA4EC), - ), - onRatingUpdate: (rating) {}, - ignoreGestures: true, - ), - const SizedBox(width: 5), - e.professionalId == e.userId - ? const SizedBox() - : Text( - '(${e.scoresModel?.total.toString()}) ${e.scoresModel?.average.toStringAsFixed(1)}'), - ], - ), - Text( - '" ${e.description} "', - style: const TextStyle(fontStyle: FontStyle.italic), - ), - ], - ), - trailing: const Icon(Icons.keyboard_arrow_right), - ), - ), - ], - ); - }, - ); - } -} diff --git a/lib/src/presentation/screens/calendar_pro.dart b/lib/src/presentation/screens/calendar_pro.dart deleted file mode 100644 index 77627c7..0000000 --- a/lib/src/presentation/screens/calendar_pro.dart +++ /dev/null @@ -1,315 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:prosappco/src/authentication/authentication_repository.dart'; -import 'package:prosappco/src/components/pop_appbar.dart'; -import 'package:prosappco/src/components/schedule_picker.dart'; -import 'package:prosappco/src/models/event_model.dart'; -import 'package:prosappco/src/models/professional_model.dart'; -import 'package:prosappco/src/presentation/widgets/shared/warning_snackbar.dart'; -import 'package:prosappco/src/utils/time_of_day_utils.dart'; -import 'package:table_calendar/table_calendar.dart'; -import 'package:intl/intl.dart'; - -class CalendarProScreen extends StatefulWidget { - final Professional professional; - - const CalendarProScreen({super.key, required this.professional}); - - @override - State createState() => _CalendarProScreenState(); -} - -class _CalendarProScreenState extends State { - final uid = AuthenticationRepository.instance.getCurrentUserUid(); - - EventoService eventoService = EventoService(); - CalendarFormat _calendarFormat = CalendarFormat.month; - - DateTime today = DateTime.now(); - DateTime now = DateTime.now(); - late int numDay; - - List? _events; - - Map? _horarios; - - @override - void initState() { - super.initState(); - - if (_horarios == null) { - Schedule.getHorarios(widget.professional.id.toString()).then( - (Map data) { - setState(() { - _horarios = data; - }); - }, - ); - } - - if (_events == null) { - Event.getEventsAllByIdAndStatus(widget.professional.id.toString()) - .then((value) { - setState(() { - _events = value; - }); - }); - } - - today = DateTime.utc(today.year, today.month, today.day); - numDay = today.weekday; - } - - void _onDaySelected(DateTime day, DateTime focusedDay) { - setState(() { - today = day; - numDay = today.weekday; - }); - } - - void _onFormatChange(CalendarFormat format) { - setState(() { - _calendarFormat = format; - }); - } - - @override - Widget build(BuildContext context) { - if (_events == null) { - return const Scaffold( - body: Center( - child: CircularProgressIndicator(), - ), - ); - } - - DateTime lastDay = today.add(const Duration(days: 365)); - - return Scaffold( - floatingActionButtonLocation: kIsWeb - ? FloatingActionButtonLocation.startFloat - : FloatingActionButtonLocation.endFloat, - resizeToAvoidBottomInset: false, - appBar: PopAppbar( - onPressed: () { - Navigator.pop(context); - }, - label: 'Calendario', - ), - body: Column( - children: [ - Container( - color: const Color.fromARGB(255, 224, 247, 255), - child: TableCalendar( - locale: 'es_MX', - firstDay: DateTime.now(), - lastDay: lastDay, - focusedDay: today, - availableGestures: AvailableGestures.all, - onDaySelected: _onDaySelected, - selectedDayPredicate: (day) => isSameDay(day, today), - calendarFormat: _calendarFormat, - onFormatChanged: _onFormatChange, - availableCalendarFormats: const { - CalendarFormat.month: 'Mes', - CalendarFormat.week: 'Semana', - CalendarFormat.twoWeeks: '2 Semanas', - }, - ), - ), - SizedBox( - width: double.infinity, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 8), - child: Text(DateFormat('dd MMMM yyyy', 'es').format(today), - style: const TextStyle( - color: Colors.black, - fontSize: 16, - fontWeight: FontWeight.w600, - )), - ), - ), - const Divider( - height: 0, - ), - Expanded( - child: SingleChildScrollView( - padding: const EdgeInsets.only(bottom: 15), - child: Column( - children: [...rangesItems(_horarios?[numDay.toString()])], - ), - )) - ], - ), - ); - } - - List rangesItems(Schedule? schedule) { - if (schedule == null) { - return const [ - Padding( - padding: EdgeInsets.only(top: 20, left: 30, right: 30), - child: Text( - 'El profesional no acepta turnos este día', - style: TextStyle(fontSize: 15, fontWeight: FontWeight.w500), - textAlign: TextAlign.center, - ), - ), - ]; - } - if (!schedule.habilitado) { - return const [ - Padding( - padding: EdgeInsets.only(top: 20, left: 30, right: 30), - child: Text( - 'El profesional no acepta turnos este día', - style: TextStyle(fontSize: 15, fontWeight: FontWeight.w500), - textAlign: TextAlign.center, - ), - ) - ]; - } - if (schedule.jornadaContinua) { - List ranges = TimeOfDayUtils.genRanges( - schedule.range1Hour1!, - schedule.range2Hour2!, - ); - - return rangesItemList(ranges, _events); - } else { - List ranges1 = TimeOfDayUtils.genRanges( - schedule.range1Hour1!, - schedule.range1Hour2!, - ); - List ranges2 = TimeOfDayUtils.genRanges( - schedule.range2Hour1!, - schedule.range2Hour2!, - ); - - return [ - ...rangesItemList(ranges1, _events), - ...rangesItemList(ranges2, _events), - ]; - } - } - - List rangesItemList(List ranges, List? events) { - return ranges.map((time) { - if (_isHora1Ocupada(time, events)) { - return Card( - elevation: 4, - margin: const EdgeInsets.symmetric(vertical: 5, horizontal: 10), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10), - ), - child: ListTile( - onTap: () { - WarningSnackbar.show( - title: 'Ocupado', - message: 'Este horário ya se encuentra ocupado', - ); - }, - contentPadding: const EdgeInsets.all(16), - leading: Container( - width: 40, - height: 40, - decoration: const BoxDecoration( - gradient: LinearGradient( - colors: [Colors.yellow, Colors.red, Colors.red], - begin: Alignment.topLeft, - end: Alignment.bottomRight, - ), - shape: BoxShape.circle, - ), - child: const Center( - child: Icon( - Icons.access_time, - color: Colors.white, - ), - ), - ), - title: Text( - time.format(context), - style: const TextStyle(fontSize: 15, fontWeight: FontWeight.bold), - ), - subtitle: const Text( - 'Ocupado', - style: TextStyle( - color: Colors.red, fontSize: 13, fontWeight: FontWeight.bold), - ), - trailing: const Icon( - Icons.arrow_forward_ios, - color: Colors.grey, - ), - ), - ); - } else { - return Card( - elevation: 4, - margin: const EdgeInsets.symmetric(vertical: 5, horizontal: 10), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10), - ), - child: ListTile( - onTap: () { - Navigator.pop(context, [today, time, widget.professional]); - }, - contentPadding: const EdgeInsets.all(16), - leading: Container( - width: 40, - height: 40, - decoration: const BoxDecoration( - gradient: LinearGradient( - colors: [Colors.blue, Colors.green], - begin: Alignment.topLeft, - end: Alignment.bottomRight, - ), - shape: BoxShape.circle, - ), - child: const Center( - child: Icon( - Icons.access_time, - color: Colors.white, - ), - ), - ), - title: Text( - time.format(context), - style: const TextStyle(fontSize: 15, fontWeight: FontWeight.bold), - ), - subtitle: const Text( - 'Disponible', - style: TextStyle( - color: Colors.green, - fontSize: 13, - fontWeight: FontWeight.bold), - ), - trailing: const Icon( - Icons.arrow_forward_ios, - color: Colors.grey, - ), - ), - ); - } - }).toList(); - } - - bool _isHora1Ocupada(TimeOfDay hora1, List? events) { - if (events != null) { - for (Event event in events) { - DateTime time1 = DateTime( - today.year, - today.month, - today.day, - hora1.hour, - hora1.minute, - ); - - if (event.range1Hour1 == time1.toString()) { - return true; - } - } - } - return false; - } -} diff --git a/lib/src/presentation/screens/chat.dart b/lib/src/presentation/screens/chat.dart deleted file mode 100644 index 8acd705..0000000 --- a/lib/src/presentation/screens/chat.dart +++ /dev/null @@ -1,396 +0,0 @@ -import 'dart:convert'; - -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:intl/intl.dart'; -import 'package:prosappco/src/authentication/authentication_repository.dart'; -import 'package:prosappco/src/components/photo_view.dart'; -import 'package:prosappco/src/components/pop_appbar.dart'; -import 'package:prosappco/src/models/chat_model.dart'; -import 'package:prosappco/src/models/event_model.dart'; -import 'package:prosappco/src/models/professional_model.dart'; -import 'package:prosappco/src/models/user_model.dart'; -import 'package:prosappco/src/presentation/screens/professional_info.dart'; -import 'package:http/http.dart' as http; - -class ChatScreen extends StatefulWidget { - final String? eventoId; - const ChatScreen({super.key, this.eventoId}); - - @override - State createState() => _ChatScreenState(); -} - -class _ChatScreenState extends State { - final _textController = TextEditingController(); - final uid = AuthenticationRepository.instance.getCurrentUserUid(); - UserModel? user; - Professional? professional; - bool pro = false; - - Future sendPushNotification(String token) async { - try { - http.Response response = await http.post( - Uri.parse('https://fcm.googleapis.com/fcm/send'), - headers: { - 'Content-Type': 'application/json; charset=UTF-8', - 'Authorization': - 'key=AAAAORdR-xU:APA91bF_wblg86jHAC-uexrXPHavYRlk5wge1Gf46m56V4J2D2L37Cp_hf46JZUzpvsWPSpqc5ewHelKI9LTifUG_s2mciMI6e5VLKo7E1R8btbNo7iaM9do2ctoyHKUm1atlZBdKaN2', - }, - body: jsonEncode( - { - 'notification': { - 'body': 'Tienes un nuevo mensaje', - 'title': 'Nuevo mensaje', - }, - 'priority': 'high', - 'data': { - 'click_action': 'FLUTTER_NOTIFICATION_CLICK', - 'id': '1', - 'status': 'done' - }, - 'to': token, - }, - ), - ); - response; - } catch (e) { - print('error al enviar notificacion $e'); - } - } - - @override - void initState() { - super.initState(); - Event.getEventById(widget.eventoId!).then((event) { - if (user == null) { - if (uid != event.userId) { - UserModel.getUser(event.userId).then( - (UserModel s) => setState(() => user = s), - ); - } else { - Professional.getProfessional(event.professionalId) - .then((value) => {professional = value}); - UserModel.getUser(event.professionalId).then( - (UserModel s) => setState(() => {user = s, pro = true}), - ); - } - } - }); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: PopAppbar( - onPressed: () { - Navigator.pop(context); - }, - label: 'Chat'), - body: Column( - children: [ - Container( - decoration: BoxDecoration( - boxShadow: [ - BoxShadow( - color: Colors.grey.withOpacity(0.3), - spreadRadius: 2, - blurRadius: 3, - offset: const Offset(0, 2), - ), - ], - ), - child: Container( - padding: const EdgeInsets.symmetric(vertical: 8), - color: const Color(0xFFD6F4FF), - alignment: Alignment.topCenter, - child: ListTile( - leading: GestureDetector( - onTap: () { - if (pro) { - Navigator.of(context).push( - CupertinoPageRoute( - builder: (BuildContext context) { - return ProfessionalInfoScreen( - professional: professional!, - ); - }, - ), - ); - } - }, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 10), - child: ReferencePhoto( - ref: user?.photo, - size: 55, - sizeCircle: 60, - ), - ), - ), - title: Text( - '${user?.name}', - style: const TextStyle( - color: Colors.black, fontWeight: FontWeight.w600), - ), - subtitle: Text(user?.profession ?? ''), - trailing: const Icon(Icons.keyboard_arrow_right), - ), - ), - ), - Expanded( - child: SingleChildScrollView( - padding: const EdgeInsets.only(top: 10), - reverse: true, - child: streamB(uid!), - ), - ), - Container( - alignment: Alignment.bottomCenter, - width: MediaQuery.of(context).size.width, - child: Container( - padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 18), - width: MediaQuery.of(context).size.width, - child: Row( - children: [ - Expanded( - child: TextFormField( - controller: _textController, - style: const TextStyle(color: Colors.black), - decoration: InputDecoration( - hintText: 'Mensaje', - hintStyle: - TextStyle(color: Colors.grey[600], fontSize: 16), - border: OutlineInputBorder( - borderSide: const BorderSide( - color: Colors.grey, width: 1.0), - borderRadius: BorderRadius.circular(50)), - focusedBorder: OutlineInputBorder( - borderSide: const BorderSide( - color: Colors.grey, width: 1.0), - borderRadius: BorderRadius.circular(50)), - contentPadding: const EdgeInsets.symmetric( - horizontal: 20, vertical: 15), - filled: true, - fillColor: Colors.grey[200], - ), - onFieldSubmitted: (value) async { - String muestra = _textController.text.trim(); - if (muestra.isNotEmpty) { - final nuevoMensaje = MessageModel( - user: uid!, - content: - _textController.text.trimLeft().trimRight(), - timestamp: DateTime.now()); - - final nuevoMensajeMap = { - 'user': nuevoMensaje.user, - 'content': nuevoMensaje.content, - 'timestamp': nuevoMensaje.timestamp, - }; - - FirebaseFirestore.instance - .collection('chats') - .doc(widget.eventoId) - .update({ - 'message': FieldValue.arrayUnion([nuevoMensajeMap]) - }); - - if (user?.token != '') { - sendPushNotification(user!.token!); - } - - // if (user?.token != '') { - // final mensajesQuerySnapshot = - // await FirebaseFirestore.instance - // .collection('chats') - // .doc(widget.eventoId) - // .get(); - // final mensajes = - // mensajesQuerySnapshot.data()?['message']; - // if (mensajes != null && mensajes.isNotEmpty) { - // final ultimoMensaje = mensajes.last; - // final ultimoMensajeUser = ultimoMensaje['user']; - // if (ultimoMensajeUser == uid) { - // // El último mensaje fue enviado por ti, no se envía la notificación - // } else { - // sendPushNotification(user!.token!); - // } - // } else { - // sendPushNotification(user!.token!); - // } - // } - _textController.clear(); - } - }, - ), - ), - const SizedBox(width: 12), - GestureDetector( - onTap: () async { - String muestra = _textController.text.trim(); - if (muestra.isNotEmpty) { - final nuevoMensaje = MessageModel( - user: uid!, - content: - _textController.text.trimLeft().trimRight(), - timestamp: DateTime.now()); - - final nuevoMensajeMap = { - 'user': nuevoMensaje.user, - 'content': nuevoMensaje.content, - 'timestamp': nuevoMensaje.timestamp, - }; - - FirebaseFirestore.instance - .collection('chats') - .doc(widget.eventoId) - .update({ - 'message': FieldValue.arrayUnion([nuevoMensajeMap]) - }); - - if (user?.token != '') { - final mensajesQuerySnapshot = await FirebaseFirestore - .instance - .collection('chats') - .doc(widget.eventoId) - .get(); - final mensajes = - mensajesQuerySnapshot.data()?['message']; - if (mensajes != null && mensajes.isNotEmpty) { - final ultimoMensaje = mensajes.last; - final ultimoMensajeUser = ultimoMensaje['user']; - if (ultimoMensajeUser == uid) { - } else { - sendPushNotification(user!.token!); - } - } else { - sendPushNotification(user!.token!); - } - } - - _textController.clear(); - } - }, - child: Container( - height: 50, - width: 50, - decoration: BoxDecoration( - color: Theme.of(context).primaryColor, - borderRadius: BorderRadius.circular(30), - ), - child: const Center( - child: Icon( - Icons.send, - color: Colors.white, - ), - ), - ), - ) - ], - ), - ), - ), - ], - ), - ); - } - - StreamBuilder>> streamB(String uid) { - return StreamBuilder( - stream: FirebaseFirestore.instance - .collection('chats') - .doc(widget.eventoId) - .snapshots(), - builder: (context, snapshot) { - if (!snapshot.hasData) { - return const Center(child: CircularProgressIndicator()); - } - - final data = snapshot.data!; - final chat = ChatModel.fromDocumentSnapshot(data); - - return Column( - children: [ - ...chat.messages.map( - (e) => uid != e.user - ? ListTile( - title: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - margin: const EdgeInsets.only(right: 60), - padding: const EdgeInsets.symmetric( - vertical: 10, horizontal: 16), - decoration: BoxDecoration( - color: Colors.grey.shade200, - borderRadius: const BorderRadius.only( - topRight: Radius.circular(20), - bottomLeft: Radius.circular(20), - bottomRight: Radius.circular(20), - ), - ), - child: Text( - e.content, - style: const TextStyle(fontSize: 16), - ), - ), - const SizedBox(width: 5), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10), - child: Text( - DateFormat('h:mm a').format(e.timestamp), - style: const TextStyle( - color: Colors.grey, fontSize: 12), - ), - ), - ], - ), - ) - : ListTile( - title: Column( - mainAxisAlignment: MainAxisAlignment.end, - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Container( - margin: const EdgeInsets.only(left: 60), - padding: const EdgeInsets.symmetric( - vertical: 10, - horizontal: 16, - ), - decoration: const BoxDecoration( - color: Color(0xFFD5EFFF), - borderRadius: BorderRadius.only( - topLeft: Radius.circular(20), - bottomLeft: Radius.circular(20), - bottomRight: Radius.circular(20), - ), - ), - child: Text( - e.content, - style: const TextStyle(fontSize: 16), - ), - ), - const SizedBox(width: 5), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10), - child: Text( - DateFormat('h:mm a').format(e.timestamp), - style: const TextStyle( - color: Colors.grey, - fontSize: 12, - ), - ), - ), - ], - ), - ), - ) - ], - ); - }, - ); - } -} diff --git a/lib/src/presentation/screens/cita.dart b/lib/src/presentation/screens/cita.dart deleted file mode 100644 index de60a8b..0000000 --- a/lib/src/presentation/screens/cita.dart +++ /dev/null @@ -1,799 +0,0 @@ -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:firebase_auth/firebase_auth.dart'; -import 'package:firebase_storage/firebase_storage.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_rating_bar/flutter_rating_bar.dart'; -import 'package:get/get.dart'; -import 'package:intl/intl.dart'; -import 'package:prosappco/src/authentication/authentication_repository.dart'; -import 'package:prosappco/src/components/photo_view.dart'; -import 'package:prosappco/src/components/pop_appbar.dart'; -import 'package:prosappco/src/models/event_model.dart'; -import 'package:prosappco/src/models/scores_model.dart'; -import 'package:prosappco/src/models/setting_model.dart'; -import 'package:prosappco/src/models/user_model.dart'; -import 'package:prosappco/src/presentation/screens/chat.dart'; -import 'package:prosappco/src/presentation/screens/score.dart'; -import 'package:url_launcher/url_launcher.dart'; -import 'package:community_material_icon/community_material_icon.dart'; -import 'package:http/http.dart' as http; -import 'dart:convert'; - -class CitaScreen extends StatefulWidget { - final Event evento; - const CitaScreen({super.key, required this.evento}); - - @override - State createState() => _CitaScreenState(); -} - -class _CitaScreenState extends State { - final uid = AuthenticationRepository.instance.getCurrentUserUid(); - UserModel? user; - DateTime today = DateTime.now(); - String nombre = ''; - String userToken = ''; - String numberPhone = ''; - int tarifa = 0; - Reference? ref_photo; - ScoresModel? scoresModel; - bool? ver = true; - bool? pro; - String proName = ''; - late final FirebaseAuth _auth; - - String formatCurrency(int number) { - final formatter = - NumberFormat.currency(locale: 'es_CO', decimalDigits: 0, symbol: ''); - return '\$${formatter.format(number)}'; - } - - Future sendPushNotification( - String user, String accion, String proName) async { - try { - http.Response response = await http.post( - Uri.parse('https://fcm.googleapis.com/fcm/send'), - headers: { - 'Content-Type': 'application/json; charset=UTF-8', - 'Authorization': - 'key=AAAAORdR-xU:APA91bF_wblg86jHAC-uexrXPHavYRlk5wge1Gf46m56V4J2D2L37Cp_hf46JZUzpvsWPSpqc5ewHelKI9LTifUG_s2mciMI6e5VLKo7E1R8btbNo7iaM9do2ctoyHKUm1atlZBdKaN2', - }, - body: jsonEncode( - { - 'notification': { - 'body': accion == 'rechazo' - ? '$proName a rechazado tu solicitud de servicio' - : '$proName a aprobado tu solicitud de servicio', - 'title': '$proName $accion', - }, - 'priority': 'high', - 'data': { - 'click_action': 'FLUTTER_NOTIFICATION_CLICK', - 'id': '1', - 'status': 'done', - 'screen': 'misservicios', - }, - 'to': user - }, - ), - ); - - response; - } catch (e) { - print('error al enviar notificacion $e'); - } - } - - Future _openMap(double lat, double lng) async { - final Uri _url = - Uri.parse('https://www.google.com/maps/search/?api=1&query=$lat,$lng'); - - if (!await launchUrl(_url)) { - throw Exception('Could not launch $_url'); - } - } - - Future _sendWhatsapp(String phoneNumber) async { - final whatsappUrl = - 'https://wa.me/$phoneNumber?text=${Uri.parse('Hola! me contactaste por Prossapp')}'; - if (!await launch(whatsappUrl)) { - throw Exception('Could not launch $whatsappUrl'); - } - } - - SettingModel? settings; - @override - void initState() { - super.initState(); - - final uid = AuthenticationRepository.instance.getCurrentUserUid(); - _auth = FirebaseAuth.instance; - - final currentUser = _auth.currentUser; - if (currentUser != null && currentUser.displayName != null) { - proName = currentUser.displayName!; - } - - if (settings == null) { - SettingModel.getSettings().then( - (SettingModel value) => setState(() { - settings = value; - }), - ); - } - - if (scoresModel == null) { - if (uid != widget.evento.userId) { - ScoresModel.scoreTo(widget.evento.userId, false, false).then( - (ScoresModel s) => setState(() { - scoresModel = s; - pro = true; - }), - ); - } else { - ScoresModel.scoreTo(widget.evento.professionalId, true, false).then( - (ScoresModel s) => setState(() { - scoresModel = s; - pro = false; - }), - ); - } - } - } - - @override - Widget build(BuildContext context) { - today.difference(DateTime.parse(widget.evento.range1Hour1)); - final eventDate = DateFormat('yyyy-MM-dd').parse(widget.evento.day); - - if (nombre == '') { - if (uid != widget.evento.userId) { - UserModel.getUser(widget.evento.userId).then((value) { - UserModel.getUser(uid.toString()).then((me) { - setState(() { - nombre = value.name; - ref_photo = value.photo; - userToken = value.token ?? ''; - numberPhone = value.phoneNumber ?? ''; - }); - }); - }); - } else { - UserModel.getUser(widget.evento.professionalId).then((value) { - setState(() { - nombre = value.name; - ref_photo = value.photo; - numberPhone = value.phoneNumber ?? ''; - }); - }); - } - } - - return Scaffold( - appBar: PopAppbar( - onPressed: () { - Navigator.pop(context); - }, - label: 'Servicio'), - body: Column( - children: [ - Expanded( - child: Column( - children: [ - ListTile( - leading: ReferencePhoto( - ref: ref_photo, - size: 50, - sizeCircle: 50, - sizeIcon: 35, - ), - title: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - nombre, - style: const TextStyle( - color: Colors.black, - fontWeight: FontWeight.bold, - fontSize: 16, - ), - ), - Text( - '${DateFormat('dd MMMM', 'es').format(DateTime.parse(widget.evento.day))} ${DateFormat('h:mm a').format(DateTime.parse(widget.evento.range1Hour1))}', - style: const TextStyle( - color: Colors.grey, - fontSize: 16, - ), - ) - ], - ), - subtitle: Row( - children: [ - RatingBar.builder( - initialRating: scoresModel?.average ?? 0, - minRating: 1, - direction: Axis.horizontal, - allowHalfRating: true, - itemCount: 5, - itemSize: 25, - maxRating: 5, - itemPadding: const EdgeInsets.symmetric(horizontal: 0), - itemBuilder: (context, _) => const Icon( - Icons.star, - color: Color(0xFF2BA4EC), - ), - onRatingUpdate: (rating) {}, - ignoreGestures: true, - ), - const SizedBox(width: 5), - Text( - '(${scoresModel?.total.toString()}) ${scoresModel?.average.toStringAsFixed(1)}'), - ], - ), - ), - widget.evento.userId == widget.evento.professionalId - ? const SizedBox() - : Container( - margin: const EdgeInsets.only( - left: 40, right: 40, top: 20, bottom: 20), - padding: const EdgeInsets.symmetric( - horizontal: 20, vertical: 15), - decoration: BoxDecoration( - color: const Color(0xFFD6F4FF), - borderRadius: BorderRadius.circular(20), - boxShadow: [ - BoxShadow( - color: Colors.grey.withOpacity(0.5), - spreadRadius: 1, - blurRadius: 5, - offset: const Offset(1, 3), - ), - ], - ), - child: Row( - children: [ - const Icon( - Icons.error_outline, - size: 27, - color: Colors.black54, - ), - const SizedBox(width: 15), - widget.evento.ubicacion != 'sitio' - ? const Text( - 'Servicio a domicilio.', - style: TextStyle( - color: Colors.black, fontSize: 14), - ) - : const Text( - 'Servicio en su sitio / consultorio', - style: TextStyle( - color: Colors.black, fontSize: 14), - ), - ], - ), - ), - settings?.tarifas == true && widget.evento.tarifa != 0 - ? Column( - children: [ - Text( - formatCurrency(widget.evento.tarifa ?? 0), - style: const TextStyle( - fontWeight: FontWeight.w600, fontSize: 25), - ), - const Text('Tarifa consulta', - style: TextStyle(fontSize: 15)), - ], - ) - : const SizedBox(), - const SizedBox(height: 15), - // Text('${widget.evento.range1Hour1} - ${DateTime.now()}'), - Text( - textAlign: TextAlign.center, - '"${widget.evento.description?.trim()}"', - style: const TextStyle( - color: Colors.grey, fontStyle: FontStyle.italic), - ), - widget.evento.userId == widget.evento.professionalId - ? const SizedBox() - : widget.evento.status == 'aprobado' || - widget.evento.status == 'iniciado' - ? const Padding( - padding: EdgeInsets.symmetric(vertical: 20), - child: Text( - 'Medios de comunicación con el usuario.', - style: TextStyle(color: Color(0xFF2BA4EC)), - ), - ) - : const SizedBox(height: 10), - widget.evento.userId == widget.evento.professionalId - ? const SizedBox() - : widget.evento.status == 'terminado' - ? pro == true - ? widget.evento.professionalScored == true - ? const SizedBox() - : Column( - children: [ - const SizedBox(height: 120), - ElevatedButton( - onPressed: () { - Navigator.pushReplacement( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return ScoreScreen( - evento: widget.evento, - pro: pro!, - ); - }, - ), - ); - }, - style: ElevatedButton.styleFrom( - backgroundColor: - const Color(0xFF2BA4EC), - shape: RoundedRectangleBorder( - borderRadius: - BorderRadius.circular(50), - ), - elevation: 0, - minimumSize: const Size(230, 60), - ), - child: const Text( - 'Puntuar servicio', - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 18, - ), - ), - ), - ], - ) - : widget.evento.userScored == true - ? const SizedBox() - : Column( - children: [ - const SizedBox(height: 120), - ElevatedButton( - onPressed: () { - Navigator.pushReplacement( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return ScoreScreen( - evento: widget.evento, - pro: pro!, - ); - }, - ), - ); - }, - style: ElevatedButton.styleFrom( - backgroundColor: - const Color(0xFF2BA4EC), - shape: RoundedRectangleBorder( - borderRadius: - BorderRadius.circular(50), - ), - elevation: 0, - minimumSize: const Size(230, 60), - ), - child: const Text( - 'Puntuar servicio', - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 18, - ), - ), - ), - ], - ) - : widget.evento.status == 'aprobado' || - widget.evento.status == 'iniciado' - ? Row( - children: [ - const Expanded(child: SizedBox()), - ElevatedButton( - onPressed: () => launch("tel:$numberPhone"), - style: ElevatedButton.styleFrom( - foregroundColor: const Color(0xFF2BA4EC), - backgroundColor: Colors.white, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(50), - side: const BorderSide( - color: Color(0xFF2BA4EC), - width: 2, - ), - ), - ), - child: const Padding( - padding: EdgeInsets.symmetric( - vertical: 18, horizontal: 0), - child: Icon( - Icons.phone_android, - size: 30, - color: Color(0xFF2BA4EC), - ), - ), - ), - const SizedBox(width: 20), - ElevatedButton( - onPressed: () async { - if (widget.evento.status != 'pendiente') { - final chatDoc = FirebaseFirestore - .instance - .collection('chats') - .doc(widget.evento.id); - final chatSnapshot = - await chatDoc.get(); - - if (!chatSnapshot.exists || - chatSnapshot.data()!['message'] == - null) { - await chatDoc.set( - { - 'professional_id': - widget.evento.professionalId, - 'user_id': widget.evento.userId, - 'message': [], - }, - SetOptions(merge: true), - ).catchError((error) => print( - 'Error al crear el documento: $error')); - } - - Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return ChatScreen( - eventoId: widget.evento.id); - }, - ), - ); - } - - // if (widget.evento.status != 'pendiente') { - // await FirebaseFirestore.instance - // .collection('chats') - // .doc(widget.evento.id) - // .set( - // { - // 'professional_id': - // widget.evento.professionalId, - // 'user_id': widget.evento.userId, - // 'message': [], - // }, - // SetOptions( - // merge: - // true)).catchError((error) => print( - // 'Error al crear el documento: $error')); - - // Navigator.push( - // context, - // CupertinoPageRoute( - // builder: (BuildContext context) { - // return ChatScreen( - // eventoId: widget.evento.id); - // }, - // ), - // ); - // } - else { - Get.snackbar( - 'El profesional aun no ha aceptado tu solicitud', - 'Debes esperar a que el profesional acepte tu solicitud para poder iniciar un chat.', - snackPosition: SnackPosition.BOTTOM, - ); - } - }, - style: ElevatedButton.styleFrom( - foregroundColor: const Color(0xFF2BA4EC), - backgroundColor: Colors.white, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(50), - side: const BorderSide( - color: Color(0xFF2BA4EC), - width: 2, - ), - ), - ), - child: const Padding( - padding: EdgeInsets.symmetric( - vertical: 18, horizontal: 0), - child: Icon( - Icons.message, - size: 30, - color: Color(0xFF2BA4EC), - ), - ), - ), - const SizedBox(width: 20), - ElevatedButton( - onPressed: () { - _sendWhatsapp(numberPhone); - }, - style: ElevatedButton.styleFrom( - foregroundColor: const Color(0xFF2BA4EC), - backgroundColor: Colors.white, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(50), - side: const BorderSide( - color: Color(0xFF2BA4EC), - width: 2, - ), - ), - ), - child: const Padding( - padding: EdgeInsets.symmetric( - vertical: 18, horizontal: 0), - child: Icon( - CommunityMaterialIcons.whatsapp, - size: 30, - color: Color(0xFF2BA4EC), - ), - ), - ), - const Expanded(child: SizedBox()), - ], - ) - : SizedBox(), - widget.evento.ubicacion == 'sitio' - ? const SizedBox() - : Padding( - padding: const EdgeInsets.only(top: 40), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - ElevatedButton( - onPressed: () { - _openMap(widget.evento.latitud!, - widget.evento.longitud!); - }, - style: ElevatedButton.styleFrom( - foregroundColor: const Color(0xFF2BA4EC), - backgroundColor: const Color(0xFF2BA4EC), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(50), - side: const BorderSide( - color: Color(0xFF2BA4EC), - width: 2, - ), - ), - ), - child: const Padding( - padding: EdgeInsets.symmetric( - vertical: 18, horizontal: 0), - child: Icon( - Icons.near_me, - size: 30, - color: Color(0xFFFFFFFF), - ), - ), - ), - const SizedBox(width: 20), - SizedBox( - width: 200, - child: Text('${widget.evento.address}'), - ) - ], - ), - ), - ], - ), - ), - widget.evento.status == 'aprobado' - ? Padding( - padding: const EdgeInsets.only(bottom: 30), - child: Column( - children: [ - Padding( - padding: const EdgeInsets.only(bottom: 20), - child: eventDate.year == today.year && - eventDate.month == today.month && - eventDate.day == today.day - ? (DateTime.now() - .difference(DateTime.parse( - widget.evento.range1Hour1)) - .abs() <= - const Duration(minutes: 30) && - ver == true) - ? ElevatedButton( - onPressed: () { - FirebaseFirestore.instance - .collection("services") - .doc('${widget.evento.id}') - .update({"status": "iniciado"}).then( - (value) { - setState(() { - ver = false; - }); - }); - }, - style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFF2BA4EC), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(50), - ), - elevation: 0, - minimumSize: const Size(230, 60), - ), - child: const Text( - 'Iniciar servicio', - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 18, - ), - ), - ) - : const SizedBox() - : const SizedBox(), - ), - widget.evento.professionalId == uid - ? ElevatedButton( - onPressed: () { - FirebaseFirestore.instance - .collection("services") - .doc('${widget.evento.id}') - .update({"status": "denegado"}).then( - (value) { - if (userToken != '') { - sendPushNotification( - userToken, 'rechazo', proName); - } - Navigator.pushReplacementNamed( - context, '/solicitud'); - }); - }, - style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFFEC2B2B), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(50), - ), - elevation: 0, - minimumSize: const Size(230, 60), - ), - child: const Text( - 'Cancelar servicio', - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 18, - ), - ), - ) - : const SizedBox(), - ], - ), - ) - : const SizedBox(), - widget.evento.status == 'pendiente' - ? Padding( - padding: const EdgeInsets.only(bottom: 30), - child: Column( - children: [ - Padding( - padding: const EdgeInsets.only(bottom: 20), - child: widget.evento.professionalId == uid - ? ElevatedButton( - onPressed: () { - FirebaseFirestore.instance - .collection("services") - .doc('${widget.evento.id}') - .update({"status": "aprobado"}).then( - (value) { - if (userToken != '') { - sendPushNotification( - userToken, 'acepto', proName); - } - Navigator.pushReplacementNamed( - context, '/solicitud'); - }); - }, - style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFF2BA4EC), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(50), - ), - elevation: 0, - minimumSize: const Size(230, 60), - ), - child: const Text( - 'Aceptar', - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 18, - ), - ), - ) - : const SizedBox(), - ), - ElevatedButton( - onPressed: () { - FirebaseFirestore.instance - .collection("services") - .doc('${widget.evento.id}') - .update({"status": "denegado"}).then((value) { - if (userToken != '') { - sendPushNotification( - userToken, 'rechazo', proName); - } - Navigator.pushReplacementNamed( - context, '/solicitud'); - }); - }, - style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFFEC2B2B), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(50), - ), - elevation: 0, - minimumSize: const Size(230, 60), - ), - child: const Text( - 'Cancelar servicio', - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 18, - ), - ), - ), - ], - ), - ) - : const SizedBox(), - widget.evento.status == 'iniciado' || ver == false - ? Padding( - padding: const EdgeInsets.only(bottom: 30), - child: Column( - children: [ - ElevatedButton( - onPressed: () { - FirebaseFirestore.instance - .collection("services") - .doc('${widget.evento.id}') - .update({"status": "terminado"}).then((value) { - Navigator.pushReplacement( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return ScoreScreen( - evento: widget.evento, - pro: pro!, - ); - }, - ), - ); - }); - }, - style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFF2BA4EC), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(50), - ), - elevation: 0, - minimumSize: const Size(230, 60), - ), - child: const Text( - 'Terminar servicio', - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 18, - ), - ), - ), - ], - ), - ) - : const SizedBox(), - ], - ), - ); - } -} diff --git a/lib/src/presentation/screens/city.dart b/lib/src/presentation/screens/city.dart deleted file mode 100644 index 8f0b752..0000000 --- a/lib/src/presentation/screens/city.dart +++ /dev/null @@ -1,206 +0,0 @@ -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:diacritic/diacritic.dart'; -import 'package:firebase_auth/firebase_auth.dart'; -import 'package:flutter/material.dart'; -import 'package:prosappco/src/authentication/authentication_repository.dart'; -import 'package:prosappco/src/components/pop_appbar.dart'; - -class CityScreen extends StatefulWidget { - const CityScreen({super.key}); - - @override - State createState() => _CityScreenState(); -} - -final CollectionReference countriesCollection = - FirebaseFirestore.instance.collection('countries'); - -class City { - String? cityName; - String? coordsOfCity; - String? stateOfCity; - String? countryOfCity; - - City({ - this.cityName, - this.coordsOfCity, - this.stateOfCity, - this.countryOfCity, - }); - - @override - String toString() { - return "${cityName ?? ""}, ${coordsOfCity ?? ""}, ${stateOfCity ?? ""}, ${countryOfCity ?? ""}"; - } -} - -Future> getCountries() async { - List citys = []; - - try { - QuerySnapshot countries = await countriesCollection.get(); - for (DocumentSnapshot country in countries.docs) { - String countryName = country.id; - Map data = country.data() as Map; - Map> states = {}; - - for (var entry in data.entries) { - String key = entry.key; - Map cityData = Map.from(entry.value); - states[key] = cityData; - } - - for (var state in states.entries) { - var citysState = state.value.entries.map((city) => City( - cityName: city.key, - coordsOfCity: city.value, - stateOfCity: state.key, - countryOfCity: countryName, - )); - - citys.addAll(citysState); - } - } - } catch (e) { - print('$e'); - } - - return citys; -} - -class _CityScreenState extends State { - List? filteredCities; - - TextEditingController searchController = TextEditingController(); - final User? user = FirebaseAuth.instance.currentUser; - - final uid = AuthenticationRepository.instance.getCurrentUserUid(); - List? _cities; - - @override - void initState() { - super.initState(); - searchController.addListener(() { - setState(() { - if (_cities != null) { - if (searchController.text.isEmpty) { - filteredCities = _cities!; - } else { - filteredCities = _cities! - .where((city) => removeDiacritics(city.cityName!) - .toLowerCase() - .contains( - removeDiacritics(searchController.text.toLowerCase()))) - .toList(); - } - } - }); - }); - - if (_cities == null) { - getCountries().then((List element) => setState(() { - _cities = element; - filteredCities = element; - })); - } - } - - Future updateCity(String cityName, String coordsCity) async { - try { - await FirebaseFirestore.instance - .collection('users') - .doc(uid) - .update({'city': cityName}); - - await FirebaseFirestore.instance - .collection('users') - .doc(uid) - .update({'coordsOfCity': coordsCity}); - } catch (e) { - try { - await FirebaseFirestore.instance - .collection('users') - .doc(uid) - .set({'city': cityName}); - - await FirebaseFirestore.instance - .collection('users') - .doc(uid) - .set({'coordsOfCity': coordsCity}); - } catch (e) { - print('Error al agregar la ciudad: $e'); - } - - print('Error al actualizar la ciudad: $e'); - } - } - - @override - Widget build(BuildContext context) { - if (filteredCities == null) { - return const Center( - child: CircularProgressIndicator( - valueColor: AlwaysStoppedAnimation(Color(0xFF2BA4EC)), - ), - ); - } - var citys = filteredCities!; - - return SafeArea( - child: Scaffold( - appBar: PopAppbar( - onPressed: () { - Navigator.pop(context); - }, - label: 'Selecciona tu ciudad'), - body: Column( - children: [ - Padding( - padding: const EdgeInsets.only(left: 10, right: 10, top: 10), - child: TextField( - controller: searchController, - decoration: const InputDecoration( - hintText: 'Busca una ciudad', - prefixIcon: Icon(Icons.near_me), - ), - ), - ), - Expanded( - child: ListView.builder( - itemCount: citys.length, - itemBuilder: (BuildContext context, int index) { - return ListTile( - title: RichText( - text: TextSpan( - style: const TextStyle( - fontSize: 18.0, - color: Colors.black, - ), - children: [ - TextSpan( - text: '${citys[index].cityName ?? ""}, ', - style: const TextStyle(fontWeight: FontWeight.bold), - ), - TextSpan( - text: - "${citys[index].stateOfCity ?? ""}, ${citys[index].countryOfCity ?? ""}", - style: TextStyle(color: Colors.grey[600]), - ), - ], - ), - ), - onTap: () { - updateCity(citys[index].cityName ?? "", - citys[index].coordsOfCity ?? ""); - Navigator.pop(context, citys[index].cityName ?? ""); - }, - ); - }, - ), - ), - ], - ), - ), - ); - } -} diff --git a/lib/src/presentation/screens/code_validation.dart b/lib/src/presentation/screens/code_validation.dart deleted file mode 100644 index 8f34430..0000000 --- a/lib/src/presentation/screens/code_validation.dart +++ /dev/null @@ -1,274 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_otp_text_field/flutter_otp_text_field.dart'; -import 'package:get/get.dart'; -import 'package:prosappco/src/components/bottom_sheet.dart'; -import 'package:prosappco/src/components/column_padding.dart'; -import 'package:prosappco/src/components/primary_btn.dart'; -import 'package:prosappco/src/controllers/otp_controller.dart'; -import 'package:prosappco/src/controllers/phone_auth_controller.dart'; -import 'package:responsive_builder/responsive_builder.dart'; - -class CodeValidationScreen extends StatelessWidget { - CodeValidationScreen({super.key, this.phoneNumber}); - String? phoneNumber; - var otp; - - final controller = Get.put(OTPController()); - - @override - Widget build(BuildContext context) { - return ScreenTypeLayout.builder( - mobile: (BuildContext context) => _mobileView(context), - tablet: (BuildContext context) => _mobileView(context), - desktop: (BuildContext context) => _desktopView(context), - ); - } - - Widget _mobileView(BuildContext context) { - return BottomSheetExpanded( - horizontalPadding: 10, - children: [ - Row( - children: [ - IconButton( - icon: const Icon( - Icons.arrow_back, - size: 30, - ), - onPressed: () { - Navigator.pop(context); - }, - ), - const Text( - 'Valida el código', - style: TextStyle( - color: Color(0xFF262626), - fontSize: 30.0, - fontWeight: FontWeight.bold, - ), - ), - ], - ), - ColumnPadding( - alineacion: MainAxisAlignment.start, - padding: const EdgeInsets.symmetric(horizontal: 25), - children: [ - const SizedBox(height: 10), - const SizedBox( - width: double.infinity, - child: Text( - 'Numero de celular', - style: TextStyle( - fontSize: 18.0, - color: Color(0xFF65676B), - ), - ), - ), - const SizedBox(height: 10), - Row( - children: [ - Expanded( - child: TextField( - onChanged: (value) { - phoneNumber = value; - }, - controller: TextEditingController(text: phoneNumber ?? ''), - decoration: const InputDecoration( - border: InputBorder.none, - hintText: '', - suffixIcon: Icon(Icons.edit), - ), - ), - ), - TextButton( - child: const Text('Reenviar código'), - onPressed: () { - if (phoneNumber!.isNotEmpty) { - PhoneAuthController.instance.phoneAuthentication( - phoneNumber!, - ); - } - }, - ), - ], - ), - const SizedBox(height: 10), - const SizedBox( - width: double.infinity, - child: Text( - 'Codigo', - textAlign: TextAlign.left, - style: TextStyle( - fontSize: 18.0, - color: Color(0xFF65676B), - ), - ), - ), - const SizedBox(height: 10), - OtpTextField( - numberOfFields: 6, - focusedBorderColor: Colors.blue, - fillColor: Colors.black.withOpacity(0.1), - filled: true, - keyboardType: TextInputType.number, - onSubmit: (code) { - otp = code; - OTPController.instance.verifyOTP(otp); - }, - ), - const SizedBox(height: 40), - PrimaryButtom( - onPressed: () { - OTPController.instance.verifyOTP(otp); - }, - label: 'Valida el código', - ), - const SizedBox(height: 30), - ], - ), - ], - ); - } - - Widget _desktopView(BuildContext context) { - double height = MediaQuery.of(context).size.height; - double width = MediaQuery.of(context).size.width; - return Scaffold( - backgroundColor: const Color(0xFFD6F4FF), - body: SizedBox( - height: height, - width: width, - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Expanded( - child: SizedBox( - height: height, - child: const Center( - child: Image( - image: AssetImage('images/logo_prosapp.png'), - ), - ), - ), - ), - Expanded( - child: Container( - padding: EdgeInsets.symmetric(horizontal: width * 0.07), - color: Colors.white, - height: height, - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Row( - children: [ - IconButton( - icon: const Icon( - Icons.arrow_back, - size: 30, - ), - onPressed: () { - Navigator.pop(context); - }, - ), - SizedBox(width: width * 0.01), - const Text( - 'Validar código', - style: TextStyle( - color: Color(0xFF262626), - fontSize: 38.0, - fontWeight: FontWeight.bold, - ), - ), - ], - ), - ColumnPadding( - alineacion: MainAxisAlignment.start, - padding: const EdgeInsets.symmetric(horizontal: 25), - children: [ - const SizedBox(height: 10), - const SizedBox( - width: double.infinity, - child: Text( - 'Numero de celular', - style: TextStyle( - fontSize: 18.0, - color: Color(0xFF65676B), - ), - ), - ), - const SizedBox(height: 10), - Row( - children: [ - Expanded( - child: TextField( - onChanged: (value) { - phoneNumber = value; - }, - controller: TextEditingController( - text: phoneNumber ?? ''), - decoration: const InputDecoration( - border: InputBorder.none, - hintText: '', - suffixIcon: Icon(Icons.edit), - ), - ), - ), - TextButton( - child: const Text('Reenviar código'), - onPressed: () { - if (phoneNumber!.isNotEmpty) { - PhoneAuthController.instance - .phoneAuthentication( - phoneNumber!, - ); - } - }, - ), - ], - ), - const SizedBox(height: 10), - const SizedBox( - width: double.infinity, - child: Text( - 'Codigo', - textAlign: TextAlign.left, - style: TextStyle( - fontSize: 18.0, - color: Color(0xFF65676B), - ), - ), - ), - const SizedBox(height: 10), - OtpTextField( - numberOfFields: 6, - focusedBorderColor: Colors.blue, - fillColor: Colors.black.withOpacity(0.1), - filled: true, - keyboardType: TextInputType.number, - onSubmit: (code) { - otp = code; - OTPController.instance.verifyOTP(otp); - }, - ), - const SizedBox(height: 40), - PrimaryButtom( - onPressed: () { - OTPController.instance.verifyOTP(otp); - }, - label: 'Valida el código', - ), - const SizedBox(height: 30), - ], - ), - ], - ), - ), - ), - ], - ), - ), - ); - } -} diff --git a/lib/src/presentation/screens/configuracion.dart b/lib/src/presentation/screens/configuracion.dart deleted file mode 100644 index 00613fb..0000000 --- a/lib/src/presentation/screens/configuracion.dart +++ /dev/null @@ -1,126 +0,0 @@ -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:firebase_auth/firebase_auth.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:get/get.dart'; -import 'package:prosappco/src/components/pop_appbar.dart'; -import 'package:prosappco/src/presentation/screens/about.dart'; - -class ConfiguracionScreen extends StatefulWidget { - const ConfiguracionScreen({super.key}); - - @override - State createState() => _ConfiguracionScreenState(); -} - -class _ConfiguracionScreenState extends State { - late final FirebaseAuth _auth; - - @override - void initState() { - super.initState(); - _auth = FirebaseAuth.instance; - } - - Future deleteAccount() async { - try { - final currentUser = _auth.currentUser; - - if (currentUser != null) { - final uid = currentUser.uid; - - await FirebaseFirestore.instance.collection('users').doc(uid).delete(); - - await currentUser.delete(); - - await _auth.signOut(); - - Get.snackbar( - 'Cuenta Eliminada', - 'Tu cuenta ha sido eliminada con éxito.', - snackPosition: SnackPosition.BOTTOM, - ); - } - } catch (e) { - Get.snackbar( - 'Error al Eliminar Cuenta', - 'Hubo un error al eliminar tu cuenta. Por favor, inténtalo de nuevo más tarde.', - snackPosition: SnackPosition.BOTTOM, - ); - } - } - - Future _showDeleteAccountConfirmationDialog( - BuildContext context) async { - return showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - title: const Text('Eliminar Cuenta'), - content: const Text( - '¿Estás seguro de que deseas eliminar tu cuenta? Esta acción no se puede deshacer.'), - actions: [ - TextButton( - onPressed: () { - Navigator.of(context).pop(); - }, - child: const Text('Cancelar'), - ), - TextButton( - onPressed: () { - deleteAccount(); - Navigator.of(context).pop(); - }, - child: const Text( - 'Eliminar', - style: - TextStyle(color: Colors.red, fontWeight: FontWeight.w600), - ), - ), - ], - ); - }, - ); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: PopAppbar( - onPressed: () { - Navigator.pop(context); - }, - label: 'Configuración'), - body: ListView( - children: [ - ListTile( - onTap: () { - Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return const AboutScreen(); - }, - ), - ); - }, - title: const Text('Acerca de la aplicación'), - trailing: const Icon( - Icons.keyboard_arrow_right, - color: Colors.black, - ), - ), - ListTile( - onTap: () { - _showDeleteAccountConfirmationDialog(context); - }, - title: const Text( - 'Eliminar cuenta', - style: TextStyle(color: Colors.red), - ), - ), - ], - ), - ); - } -} diff --git a/lib/src/presentation/screens/horario.dart b/lib/src/presentation/screens/horario.dart deleted file mode 100644 index 177ddb2..0000000 --- a/lib/src/presentation/screens/horario.dart +++ /dev/null @@ -1,202 +0,0 @@ -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:flutter/material.dart'; -import 'package:intl/intl.dart'; -import 'package:prosappco/src/authentication/authentication_repository.dart'; -import 'package:prosappco/src/components/pop_appbar.dart'; -import 'package:prosappco/src/components/primary_btn.dart'; -import '../../components/schedule_picker.dart'; - -class HorarioScreen extends StatelessWidget { - Map horarios; - HorarioScreen({super.key, required this.horarios}); - - final uid = AuthenticationRepository.instance.getCurrentUserUid(); - bool lunesValue = false; - bool martesValue = false; - bool miercolesValue = false; - bool juevesValue = false; - bool viernesValue = false; - bool sabadoValue = false; - bool domingoValue = false; - bool jornadaContinuaLunes = false; - - Future updateHorario(BuildContext context) async { - try { - Map horariosMap = {}; - - horarios.forEach((key, value) { - if (value.habilitado && !value.jornadaContinua) { - if (value.range1Hour1 == null || - value.range1Hour2 == null || - value.range2Hour1 == null || - value.range2Hour2 == null) { - value.habilitado = false; - value.jornadaContinua = false; - } - } - - if (value.habilitado && value.jornadaContinua) { - if (value.range1Hour1 == null || value.range2Hour2 == null) { - value.habilitado = false; - value.jornadaContinua = false; - } - } - - horariosMap[key] = { - 'habilitado': value.habilitado, - 'jornadaContinua': value.jornadaContinua, - 'range1Hour1': formatTimeOfDay(value.range1Hour1), - 'range1Hour2': formatTimeOfDay(value.range1Hour2), - 'range2Hour1': formatTimeOfDay(value.range2Hour1), - 'range2Hour2': formatTimeOfDay(value.range2Hour2), - }; - }); - - await FirebaseFirestore.instance.collection('users').doc(uid).update({ - 'horario': horariosMap, - }); - } catch (e) { - print('Error al actualizar el horario: $e'); - } - } - - // Future updateHorario(BuildContext context) async { - // try { - // Map horariosMap = {}; - // horarios.forEach((key, value) { - // if (value.habilitado && !value.jornadaContinua) { - // if (value.range1Hour1 == null || - // value.range1Hour2 == null || - // value.range2Hour1 == null || - // value.range2Hour2 == null) { - // value.habilitado = false; - // value.jornadaContinua = false; - // return; - // } - // } - - // if (value.habilitado && value.jornadaContinua) { - // if (value.range1Hour1 == null || value.range2Hour2 == null) { - // value.habilitado = false; - // value.jornadaContinua = false; - // } - // } - - // horariosMap[key] = { - // 'habilitado': value.habilitado, - // 'jornadaContinua': value.jornadaContinua, - // 'range1Hour1': formatTimeOfDay(value.range1Hour1), - // 'range1Hour2': formatTimeOfDay(value.range1Hour2), - // 'range2Hour1': formatTimeOfDay(value.range2Hour1), - // 'range2Hour2': formatTimeOfDay(value.range2Hour2), - // }; - // }); - - // await FirebaseFirestore.instance.collection('users').doc(uid).update({ - // 'horario': horariosMap, - // }); - // } catch (e) { - // print('Error al actualizar el horario: $e'); - // } - // } - - String? formatTimeOfDay(TimeOfDay? time) { - if (time != null) { - final now = DateTime.now(); - final dateTime = - DateTime(now.year, now.month, now.day, time.hour, time.minute); - final format = DateFormat.jm(); - return format.format(dateTime); - } - return null; - } - - int _dayOfWeekToInt(String dayOfWeek) { - switch (dayOfWeek) { - case '1': - return 1; - case '2': - return 2; - case '3': - return 3; - case '4': - return 4; - case '5': - return 5; - case '6': - return 6; - case '7': - return 7; - default: - throw ArgumentError('Invalid day of week: $dayOfWeek'); - } - } - - String _stringToDayOfWeek(String dayOfWeek) { - switch (dayOfWeek) { - case '1': - return 'Lunes'; - case '2': - return 'Martes'; - case '3': - return 'Miércoles'; - case '4': - return 'Jueves'; - case '5': - return 'Viernes'; - case '6': - return 'Sábado'; - case '7': - return 'Domingo'; - default: - throw ArgumentError('Invalid day of week: $dayOfWeek'); - } - } - - @override - Widget build(BuildContext context) { - final sortedHorarios = Map.fromEntries( - horarios.entries.toList() - ..sort( - (a, b) => _dayOfWeekToInt(a.key).compareTo(_dayOfWeekToInt(b.key))), - ); - return SafeArea( - child: Scaffold( - appBar: PopAppbar( - onPressed: () { - Navigator.pop(context); - }, - label: 'Horario'), - body: SingleChildScrollView( - child: Column( - children: [ - const Divider( - height: 5, - ), - Column( - children: sortedHorarios.entries.map( - (entry) { - return SchedulePicker( - name: _stringToDayOfWeek(entry.key), - schedule: entry.value, - ); - }, - ).toList(), - ), - Padding( - padding: const EdgeInsets.only(top: 30, bottom: 30), - child: PrimaryButtom( - onPressed: () async { - await updateHorario(context); - Navigator.pop(context); - }, - label: 'Guardar', - ), - ) - ], - ), - ), - ), - ); - } -} diff --git a/lib/src/presentation/screens/login/login.dart b/lib/src/presentation/screens/login/login.dart deleted file mode 100644 index d1d8dd2..0000000 --- a/lib/src/presentation/screens/login/login.dart +++ /dev/null @@ -1,452 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:get/get.dart'; -import 'package:intl_phone_field/intl_phone_field.dart'; -import 'package:prosappco/src/components/bottom_sheet.dart'; -import 'package:prosappco/src/presentation/widgets/shared/primary_button.dart'; -import 'package:prosappco/src/controllers/phone_auth_controller.dart'; -import 'package:prosappco/src/models/setting_model.dart'; -import 'package:prosappco/src/providers/user_provider.dart'; -import 'package:prosappco/src/presentation/screens/code_validation.dart'; -import 'package:prosappco/src/presentation/screens/web_view.dart'; -import 'package:provider/provider.dart'; -import 'package:responsive_builder/responsive_builder.dart'; -import 'package:url_launcher/url_launcher.dart'; - -class LoginScreen extends StatefulWidget { - const LoginScreen({super.key}); - - @override - State createState() => _LoginScreenState(); -} - -class _LoginScreenState extends State { - final controller = Get.put(PhoneAuthController()); - final _formKey = GlobalKey(); - String completePhoneNumber = ''; - bool _isChecked = false; - SettingModel? settings; - - void _clearPhoneNumber() { - if (mounted) { - setState(() { - controller.phoneNo.text = ''; - }); - } - } - - void _launchURL(String url) async { - if (await canLaunch(url)) { - await launch(url, forceSafariVC: false, forceWebView: false); - } else { - throw 'No se pudo abrir el enlace $url'; - } - } - - @override - void initState() { - super.initState(); - if (settings == null) { - SettingModel.getSettings().then((SettingModel value) { - if (mounted) { - setState(() { - settings = value; - }); - } - }); - } - } - - @override - Widget build(BuildContext context) { - return ScreenTypeLayout.builder( - mobile: (BuildContext context) => _mobileView(context), - tablet: (BuildContext context) => _mobileView(context), - desktop: (BuildContext context) => _desktopView(context), - ); - } - - Widget _mobileView(BuildContext context) { - const inputDecoration = InputDecoration( - border: OutlineInputBorder( - borderSide: BorderSide(color: Color(0xFFECECEC)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - errorBorder: OutlineInputBorder( - borderSide: BorderSide(color: Color.fromARGB(255, 184, 0, 0)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - enabledBorder: OutlineInputBorder( - borderSide: BorderSide(color: Color(0xFFECECEC)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - focusedBorder: OutlineInputBorder( - borderSide: BorderSide(color: Color(0xFFECECEC)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - fillColor: Color.fromARGB(255, 239, 239, 239), - filled: true, - ); - return BottomSheetExpanded( - children: [ - const SizedBox( - width: double.infinity, - child: Text( - 'Iniciar sesión', - style: TextStyle( - color: Color(0xFF262626), - fontSize: 30.0, - fontWeight: FontWeight.bold, - ), - ), - ), - const SizedBox(height: 10), - const SizedBox( - width: double.infinity, - child: Text( - 'Numero de celular', - style: TextStyle( - fontSize: 18.0, - color: Color(0xFF65676B), - ), - ), - ), - Form( - key: _formKey, - child: IntlPhoneField( - controller: controller.phoneNo, - initialCountryCode: 'CO', - keyboardType: TextInputType.number, - inputFormatters: [FilteringTextInputFormatter.digitsOnly], - onChanged: (phoneNo) { - completePhoneNumber = phoneNo.completeNumber; - }, - decoration: inputDecoration, - ), - ), - const Text( - 'Un código será enviado a este numero de celular.', - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 13.0, - color: Color(0xFF65676B), - ), - ), - Padding( - padding: const EdgeInsets.symmetric(vertical: 20), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Checkbox( - value: _isChecked, - onChanged: (value) { - setState(() { - _isChecked = value!; - }); - }, - ), - GestureDetector( - onTap: () { - if (kIsWeb) { - _launchURL(settings?.terminosCondiciones ?? ''); - } else { - Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return WebViewScreen( - label: 'Términos y condiciones', - link: settings?.terminosCondiciones ?? '', - ); - }, - ), - ); - } - }, - child: const Text( - 'Acepto los términos y condiciones.', - style: TextStyle( - fontSize: 13.0, - color: Color(0xFF65676B), - decoration: TextDecoration.underline, - ), - ), - ), - ], - ), - ), - PrimaryButton( - onPressed: () async { - if (_formKey.currentState!.validate()) { - PhoneAuthController.instance.phoneAuthentication( - completePhoneNumber.trim(), - ); - _clearPhoneNumber(); - await Get.to( - () => CodeValidationScreen( - phoneNumber: completePhoneNumber.trim(), - ), - ); - Provider.of(context, listen: false) - .initUserProvider(); - } - }, - text: 'Enviar código', - isEnabled: _isChecked, - ), - const SizedBox(height: 20), - GestureBottom(clearPhoneNumber: _clearPhoneNumber), - const SizedBox(height: 20), - const RichTxTBottom(), - const SizedBox(height: 20), - ], - ); - } - - Widget _desktopView(BuildContext context) { - double height = MediaQuery.of(context).size.height; - double width = MediaQuery.of(context).size.width; - return Scaffold( - backgroundColor: const Color(0xFFD6F4FF), - body: SizedBox( - height: height, - width: width, - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Expanded( - child: SizedBox( - height: height, - child: const Center( - child: Image( - image: AssetImage('images/logo_prosapp.png'), - ), - ), - ), - ), - Expanded( - child: Container( - padding: EdgeInsets.symmetric(horizontal: width * 0.1), - color: Colors.white, - height: height, - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const SizedBox( - width: double.infinity, - child: Text( - 'Iniciar sesión', - style: TextStyle( - color: Color(0xFF262626), - fontSize: 30.0, - fontWeight: FontWeight.bold, - ), - ), - ), - const SizedBox(height: 20), - const SizedBox( - width: double.infinity, - child: Text( - 'Numero de celular', - style: TextStyle( - fontSize: 18.0, - color: Color(0xFF65676B), - ), - ), - ), - Form( - key: _formKey, - child: IntlPhoneField( - controller: controller.phoneNo, - initialCountryCode: 'CO', - keyboardType: TextInputType.number, - inputFormatters: [ - FilteringTextInputFormatter.digitsOnly - ], - onChanged: (phoneNo) { - completePhoneNumber = phoneNo.completeNumber; - }, - decoration: const InputDecoration( - border: OutlineInputBorder( - borderSide: BorderSide(color: Color(0xFFECECEC)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - errorBorder: OutlineInputBorder( - borderSide: BorderSide( - color: Color.fromARGB(255, 184, 0, 0)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - enabledBorder: OutlineInputBorder( - borderSide: BorderSide(color: Color(0xFFECECEC)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - focusedBorder: OutlineInputBorder( - borderSide: BorderSide(color: Color(0xFFECECEC)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - fillColor: Color.fromARGB(255, 239, 239, 239), - filled: true, - ), - ), - ), - const Text( - 'Se enviará un código a este número de celular.', - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 13.0, - color: Color(0xFF65676B), - ), - ), - Padding( - padding: const EdgeInsets.symmetric(vertical: 20), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Checkbox( - value: _isChecked, - onChanged: (value) { - setState(() { - _isChecked = value!; - }); - }, - ), - GestureDetector( - onTap: () { - if (kIsWeb) { - _launchURL(settings?.terminosCondiciones ?? ''); - } else { - Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return WebViewScreen( - label: 'Términos y condiciones', - link: - settings?.terminosCondiciones ?? '', - ); - }, - ), - ); - } - }, - child: const Text( - 'Acepto los términos y condiciones.', - style: TextStyle( - fontSize: 13.0, - color: Color(0xFF65676B), - decoration: TextDecoration - .underline, // Add underline style - ), - ), - ), - ], - ), - ), - PrimaryButton( - onPressed: () async { - if (_formKey.currentState!.validate()) { - PhoneAuthController.instance.phoneAuthentication( - completePhoneNumber.trim(), - ); - await Get.to( - () => CodeValidationScreen( - phoneNumber: completePhoneNumber.trim(), - ), - ); - _clearPhoneNumber(); - Provider.of(context, listen: false) - .initUserProvider(); - } - }, - text: 'Enviar código', - isEnabled: _isChecked, - ), - const SizedBox(height: 20), - GestureBottom(clearPhoneNumber: _clearPhoneNumber), - const SizedBox(height: 20), - const RichTxTBottom(), - ], - ), - ), - ), - ], - ), - ), - ); - } -} - -class GestureBottom extends StatelessWidget { - final VoidCallback clearPhoneNumber; - - GestureBottom({ - super.key, - required this.clearPhoneNumber, - }); - - @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: () { - clearPhoneNumber(); - Navigator.pushNamed(context, '/login'); - }, - child: const Text( - 'Inicia sesión con tu correo electrónico', - style: TextStyle( - fontSize: 15.0, color: Color(0xFF65676B), - decoration: TextDecoration.underline, // Subrayado - ), - ), - ); - } -} - -class RichTxTBottom extends StatelessWidget { - const RichTxTBottom({ - super.key, - }); - - @override - Widget build(BuildContext context) { - return RichText( - text: TextSpan( - style: const TextStyle( - fontSize: 16.0, - color: Color(0xFF65676B), - fontFamily: 'Poppins', - ), - children: [ - const TextSpan(text: '¿No estás registrado? '), - WidgetSpan( - child: GestureDetector( - onTap: () { - Navigator.pushNamed(context, '/register'); - }, - child: const Text( - 'Regístrate', - style: TextStyle( - fontSize: 16.0, - color: Color(0xFF2BA4EC), - fontWeight: FontWeight.w600, - ), - ), - ), - ), - ], - ), - ); - } -} diff --git a/lib/src/presentation/screens/login/login_email.dart b/lib/src/presentation/screens/login/login_email.dart deleted file mode 100644 index a567f9c..0000000 --- a/lib/src/presentation/screens/login/login_email.dart +++ /dev/null @@ -1,552 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:font_awesome_flutter/font_awesome_flutter.dart'; -import 'package:get/get.dart'; -import 'package:prosappco/src/authentication/authentication_repository.dart'; -import 'package:prosappco/src/components/bottom_sheet.dart'; -import 'package:prosappco/src/presentation/widgets/shared/primary_button.dart'; -import 'package:prosappco/src/controllers/login_email_controller.dart'; -import 'package:prosappco/src/models/setting_model.dart'; -import 'package:prosappco/src/providers/user_provider.dart'; -import 'package:provider/provider.dart'; -import 'package:responsive_builder/responsive_builder.dart'; - -class LoginEmailScreen extends StatefulWidget { - const LoginEmailScreen({super.key}); - - @override - State createState() => _LoginEmailScreenState(); -} - -class _LoginEmailScreenState extends State { - bool _obscureText = true; - final controller = Get.put(LoginEmailController()); - final _formKey = GlobalKey(); - SettingModel? settings; - - @override - void initState() { - super.initState(); - if (settings == null) { - SettingModel.getSettings().then( - (SettingModel value) => setState(() { - settings = value; - }), - ); - } - } - - @override - Widget build(BuildContext context) { - return ScreenTypeLayout.builder( - mobile: (BuildContext context) => _mobileView(context), - tablet: (BuildContext context) => _mobileView(context), - desktop: (BuildContext context) => _desktopView(context), - ); - } - - Widget _mobileView(BuildContext context) { - bool isIOS = Theme.of(context).platform == TargetPlatform.iOS; - - return BottomSheetExpanded( - horizontalPadding: 10, - children: [ - Row( - children: [ - IconButton( - icon: const Icon( - Icons.arrow_back, - size: 30, - ), - onPressed: () { - Navigator.pop(context); - }, - ), - const Text( - 'Iniciar sesión', - style: TextStyle( - color: Color(0xFF262626), - fontSize: 30.0, - fontWeight: FontWeight.bold, - ), - textAlign: TextAlign.right, - ), - ], - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 25), - child: Form( - key: _formKey, - child: Container( - padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 20), - child: Column( - children: [ - !isIOS && !kIsWeb && settings?.google == true - ? Padding( - padding: const EdgeInsets.only(bottom: 20), - child: ElevatedButton( - onPressed: () async { - await AuthenticationRepository.instance - .signInWithGoogle() - .then((value) => { - Provider.of(context, - listen: false) - .initUserProvider() - }); - }, - style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFF2BA4EC), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(50), - ), - elevation: 0, - minimumSize: const Size(230, 60), - ), - child: const Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - 'Entra con Google ', - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 18, - ), - ), - SizedBox(width: 5), - FaIcon(FontAwesomeIcons.google), - ], - )), - ) - : const SizedBox(), - !isIOS && !kIsWeb && settings?.google == true - ? const Padding( - padding: EdgeInsets.symmetric(vertical: 5), - child: Row( - children: [ - Expanded( - child: Divider( - color: Colors.black38, - thickness: 1, - ), - ), - Padding( - padding: EdgeInsets.symmetric(horizontal: 10), - child: Text("ó"), - ), - Expanded( - child: Divider( - color: Colors.black38, - thickness: 1, - ), - ), - ], - ), - ) - : const SizedBox(), - const Padding( - padding: EdgeInsets.only(bottom: 5), - child: Align( - alignment: Alignment.topLeft, - child: Text('Email', - style: TextStyle( - fontSize: 18.0, color: Color(0xFF65676B))), - ), - ), - FormEmail(controller: controller), - const Padding( - padding: EdgeInsets.only(bottom: 5), - child: Align( - alignment: Alignment.topLeft, - child: Text('Password', - style: TextStyle( - fontSize: 18.0, color: Color(0xFF65676B))), - ), - ), - TextFormField( - controller: controller.password, - obscureText: _obscureText, - validator: (value) { - if (value == null || value.isEmpty) { - return 'Por favor, ingresa una contraseña'; - } - return null; - }, - decoration: InputDecoration( - enabledBorder: const OutlineInputBorder( - borderSide: BorderSide(color: Color(0xFFECECEC)), - borderRadius: BorderRadius.all( - Radius.circular(50), - ), - ), - focusedBorder: const OutlineInputBorder( - borderSide: BorderSide(color: Color(0xFFECECEC)), - borderRadius: BorderRadius.all( - Radius.circular(50), - ), - ), - border: const OutlineInputBorder( - borderSide: BorderSide(color: Color(0xFFECECEC)), - borderRadius: BorderRadius.all( - Radius.circular(50), - ), - ), - errorBorder: const OutlineInputBorder( - borderSide: - BorderSide(color: Color.fromARGB(255, 184, 0, 0)), - borderRadius: BorderRadius.all( - Radius.circular(50), - ), - ), - hintText: 'Contraseña', - fillColor: const Color.fromARGB(255, 239, 239, 239), - filled: true, - prefixIcon: const Icon(Icons.lock_outline), - suffixIcon: IconButton( - icon: Icon( - _obscureText - ? Icons.visibility - : Icons.visibility_off, - color: Colors.grey, - ), - onPressed: () { - setState(() { - _obscureText = !_obscureText; - }); - }, - ), - hintStyle: const TextStyle( - color: Colors.grey, - ), - ), - ), - Padding( - padding: const EdgeInsets.symmetric(vertical: 10), - child: TextButton( - onPressed: () { - Navigator.pushNamed(context, '/resetpassword'); - }, - child: const Text( - 'Olvidé la contraseña', - style: TextStyle( - color: Colors.blue, - ), - ), - ), - ), - PaddingButtomBottom( - formKey: _formKey, - controller: controller, - ), - const RichTxtBottom() - ], - ), - ), - ), - ), - ], - ); - } - - Widget _desktopView(BuildContext context) { - double height = MediaQuery.of(context).size.height; - double width = MediaQuery.of(context).size.width; - return Scaffold( - backgroundColor: const Color(0xFFD6F4FF), - body: SizedBox( - height: height, - width: width, - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Expanded( - child: SizedBox( - height: height, - child: const Center( - child: Image( - image: AssetImage('images/logo_prosapp.png'), - ), - ), - ), - ), - Expanded( - child: Container( - padding: EdgeInsets.symmetric(horizontal: width * 0.07), - color: Colors.white, - height: height, - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Row( - children: [ - IconButton( - icon: const Icon( - Icons.arrow_back, - size: 30, - ), - onPressed: () { - Navigator.pop(context); - }, - ), - SizedBox(width: width * 0.01), - const Text( - 'Iniciar sesión', - style: TextStyle( - color: Color(0xFF262626), - fontSize: 30.0, - fontWeight: FontWeight.bold, - ), - textAlign: TextAlign.right, - ), - ], - ), - Form( - key: _formKey, - child: Container( - padding: const EdgeInsets.symmetric( - horizontal: 0, vertical: 20), - child: Column(children: [ - const Padding( - padding: EdgeInsets.only(bottom: 5), - child: Align( - alignment: Alignment.topLeft, - child: Text('Email', - style: TextStyle( - fontSize: 18.0, - color: Color(0xFF65676B))), - )), - FormEmail(controller: controller), - const Padding( - padding: EdgeInsets.only(bottom: 5), - child: Align( - alignment: Alignment.topLeft, - child: Text('Password', - style: TextStyle( - fontSize: 18.0, - color: Color(0xFF65676B))), - ), - ), - Padding( - padding: const EdgeInsets.only(bottom: 50), - child: TextFormField( - controller: controller.password, - obscureText: _obscureText, - validator: (value) { - if (value == null || value.isEmpty) { - return 'Por favor, ingresa una contraseña'; - } - return null; - }, - decoration: InputDecoration( - enabledBorder: const OutlineInputBorder( - borderSide: - BorderSide(color: Color(0xFFECECEC)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - focusedBorder: const OutlineInputBorder( - borderSide: - BorderSide(color: Color(0xFFECECEC)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - border: const OutlineInputBorder( - borderSide: - BorderSide(color: Color(0xFFECECEC)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - errorBorder: const OutlineInputBorder( - borderSide: BorderSide( - color: Color.fromARGB(255, 184, 0, 0)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - hintText: 'Contraseña', - hintStyle: const TextStyle( - color: Colors.grey, - ), - fillColor: - const Color.fromARGB(255, 239, 239, 239), - filled: true, - prefixIcon: const Icon(Icons.lock_outline), - suffixIcon: IconButton( - icon: Icon( - _obscureText - ? Icons.visibility - : Icons.visibility_off, - color: Colors.grey, - ), - onPressed: () { - setState(() { - _obscureText = !_obscureText; - }); - }, - ), - ), - ), - ), - Padding( - padding: const EdgeInsets.symmetric(vertical: 20), - child: TextButton( - onPressed: () { - Navigator.pushNamed(context, '/resetpassword'); - }, - child: const Text( - 'Olvidé la contraseña', - style: TextStyle( - color: Colors.blue, - ), - ), - ), - ), - PaddingButtomBottom( - formKey: _formKey, controller: controller), - const RichTxtBottom() - ]), - ), - ), - ], - ), - ), - ), - ], - ), - ), - ); - } -} - -class FormEmail extends StatelessWidget { - const FormEmail({ - super.key, - required this.controller, - }); - - final LoginEmailController controller; - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.only(bottom: 20), - child: TextFormField( - controller: controller.email, - validator: (String? value) { - if (value == null || value.isEmpty) { - return 'Por favor, ingresa un Email'; - } - final RegExp emailRegExp = - RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$'); - if (!emailRegExp.hasMatch(value)) { - return 'Por favor, ingresa un Email válido'; - } - return null; - }, - decoration: const InputDecoration( - border: OutlineInputBorder( - borderSide: BorderSide(color: Color(0xFFECECEC)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - enabledBorder: OutlineInputBorder( - borderSide: BorderSide(color: Color(0xFFECECEC)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - focusedBorder: OutlineInputBorder( - borderSide: BorderSide(color: Color(0xFFECECEC)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - errorBorder: OutlineInputBorder( - borderSide: BorderSide(color: Color.fromARGB(255, 184, 0, 0)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - hintText: 'Hello@gmail.com', - fillColor: Color.fromARGB(255, 239, 239, 239), - filled: true, - prefixIcon: Icon(Icons.email_outlined), - hintStyle: TextStyle( - color: Colors.grey, - ), - ), - ), - ); - } -} - -class PaddingButtomBottom extends StatelessWidget { - const PaddingButtomBottom({ - super.key, - required GlobalKey formKey, - required this.controller, - }) : _formKey = formKey; - - final GlobalKey _formKey; - final LoginEmailController controller; - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.only(bottom: 25), - child: Center( - child: PrimaryButton( - onPressed: () { - if (_formKey.currentState!.validate()) { - LoginEmailController.instance - .loginUser( - controller.email.text.trim(), - controller.password.text.trim(), - ) - .then((value) { - Provider.of(context, listen: false) - .initUserProvider(); - }); - } - }, - text: 'Iniciar', - ), - ), - ); - } -} - -class RichTxtBottom extends StatelessWidget { - const RichTxtBottom({ - super.key, - }); - - @override - Widget build(BuildContext context) { - return RichText( - text: TextSpan( - style: const TextStyle( - fontSize: 16.0, - color: Color(0xFF65676B), - fontFamily: 'Poppins', - ), - children: [ - const TextSpan(text: '¿No estás registrado? '), - WidgetSpan( - child: GestureDetector( - onTap: () { - Navigator.pushReplacementNamed(context, '/register'); - }, - child: const Text( - 'Registrarse', - style: TextStyle( - fontSize: 16.0, - color: Color(0xFF2BA4EC), - fontWeight: FontWeight.w600, - ), - ), - ), - ), - ], - ), - ); - } -} diff --git a/lib/src/presentation/screens/map/service.dart b/lib/src/presentation/screens/map/service.dart deleted file mode 100644 index 839da1c..0000000 --- a/lib/src/presentation/screens/map/service.dart +++ /dev/null @@ -1,1304 +0,0 @@ -import 'dart:async'; -import 'dart:convert'; -import 'dart:io'; -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:firebase_auth/firebase_auth.dart'; -import 'package:firebase_messaging/firebase_messaging.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:geocoding/geocoding.dart' as geocoding; -import 'package:http/http.dart' as http; -import 'package:geolocator/geolocator.dart'; -import 'package:google_maps_flutter/google_maps_flutter.dart'; -import 'package:flutter_polyline_points/flutter_polyline_points.dart'; -import 'package:intl/intl.dart'; -import 'package:location/location.dart'; -import 'package:package_info_plus/package_info_plus.dart'; -import 'package:prosappco/constansts.dart'; -import 'package:prosappco/src/authentication/authentication_repository.dart'; -import 'package:prosappco/src/components/network_utility.dart'; -import 'package:prosappco/src/models/event_model.dart'; -import 'package:prosappco/src/models/professional_model.dart'; -import 'package:prosappco/src/models/setting_model.dart'; -import 'package:prosappco/src/models/user_model.dart'; -import 'package:prosappco/src/presentation/screens/calendar_pro.dart'; -import 'package:prosappco/src/presentation/screens/professional.dart'; -import 'package:prosappco/src/presentation/screens/profile/profile.dart'; -import 'package:prosappco/src/presentation/screens/service_after.dart'; -import 'package:prosappco/src/presentation/screens/service_type.dart'; -import 'package:prosappco/src/presentation/widgets/shared/drawer_menu.dart'; -import 'package:prosappco/src/presentation/widgets/shared/primary_button.dart'; -import 'package:prosappco/src/presentation/widgets/shared/warning_snackbar.dart'; -import 'package:prosappco/src/providers/user_provider.dart'; -import 'package:provider/provider.dart'; -import 'package:url_launcher/url_launcher.dart'; - -class ServiceScreen extends StatefulWidget { - const ServiceScreen({ - super.key, - }); - - @override - State createState() => _ServiceScreenState(); -} - -class _ServiceScreenState extends State { - late String appVersion; - final Uri _url = Uri.parse( - 'https://play.google.com/store/apps/details?id=com.prosapp.prosapp'); - final Uri _urlIos = - Uri.parse('https://apps.apple.com/co/app/prosapp/id6469028900'); - - final Location _locationController = Location(); - - final Completer _mapController = - Completer(); - - static const LatLng _pGooglePlex = LatLng(7.080486, -73.087447); - LatLng? _currentP; - - Map polylines = {}; - - final TextEditingController _locacionController = TextEditingController(); - final TextEditingController _ubicationController = TextEditingController(); - final TextEditingController _profesionalController = TextEditingController(); - final TextEditingController _serviceTypeController = TextEditingController(); - final TextEditingController _observacionController = TextEditingController(); - final DateTime now = DateTime.now(); - EventoService eventoService = EventoService(); - String _serviceType = 'Servicio'; - String ubicacion = ''; - String professionalId = ''; - String professionalToken = ''; - String professionalAddress = ''; - String professionalUbicacion = ''; - int? professionalTarifa; - double? professionalLatitude; - double? professionalLongitude; - - String _ciudad = '...'; - - final DateFormat formatter = DateFormat('dd/MM/yyyy'); - dynamic coordinates; - Set markers = {}; - BitmapDescriptor? _markerIcon; - List _placesList = []; - String _coordsOfCity = '0.0,0.0'; - - final uid = AuthenticationRepository.instance.getCurrentUserUid(); - - String? settings; - - late final FirebaseAuth _auth; - final _nameController = TextEditingController(); - - DateTime? fechaSeleccionada; - TimeOfDay? horaSeleccionada; - Professional? profesionalSeleccionado; - - @override - void initState() { - super.initState(); - - _getAppVersion(); - - getLocationUpdates(); - - _auth = FirebaseAuth.instance; - - final currentUser = _auth.currentUser; - - if (currentUser != null && currentUser.displayName != null) { - _nameController.text = currentUser.displayName!; - } - - if (settings == null) { - SettingModel.getSettings().then( - (SettingModel value) => (value) { - if (mounted) { - setState(() { - settings = value.version; - _getAppVersion(); - }); - } - }, - ); - } - - if (_ciudad == '...') { - AuthenticationRepository.instance - .getCity(uid.toString()) - .then((String s) { - if (s.isEmpty) { - FirebaseFirestore.instance.collection('users').doc(uid).set({ - 'city': 'Cúcuta', - }).then((_) { - if (mounted) { - setState(() { - _ciudad = 'Cúcuta'; - }); - } - }); - } else { - if (mounted) { - setState(() { - _ciudad = s; - }); - } - } - }); - } - - if (_coordsOfCity == '0.0,0.0') { - AuthenticationRepository.instance - .getCoordsOfCity(uid.toString()) - .then((String s) { - if (mounted) { - setState(() { - _coordsOfCity = s; - - _animateInitialCameraToPosition(_coordsOfCity); - }); - } - }); - } - - _saveToken(); - - if (Platform.isAndroid) { - BitmapDescriptor.fromAssetImage( - const ImageConfiguration(size: Size(2, 2)), - 'images/pro_marke_android.png', - ).then((icon) { - if (mounted) { - setState(() { - _markerIcon = icon; - }); - } - }); - } else { - BitmapDescriptor.fromAssetImage( - const ImageConfiguration(size: Size(1, 1)), - 'images/pro_marke.png', - ).then((icon) { - if (mounted) { - setState(() { - _markerIcon = icon; - }); - } - }); - } - - updateMarkersForServiceType(_serviceType); - } - - @override - void dispose() { - _locacionController.dispose(); - _ubicationController.dispose(); - _profesionalController.dispose(); - _serviceTypeController.dispose(); - _observacionController.dispose(); - - _locationController.onLocationChanged.listen((_) {}).cancel(); - - super.dispose(); - } - - @override - Widget build(BuildContext context) { - final userProvider = Provider.of(context); - UserModel? user = userProvider.user; - dynamic datos; - - return SafeArea( - child: Scaffold( - drawer: DrawerMenu(), - body: Column( - children: [ - Expanded( - child: Stack( - children: [ - GoogleMap( - onMapCreated: ((GoogleMapController controller) { - _mapController.complete(controller); - - _applyMapStyle(controller); - }), - initialCameraPosition: const CameraPosition( - target: _pGooglePlex, - zoom: 15, - ), - markers: { - ...markers, - if (_currentP != null) - Marker( - markerId: const MarkerId("_currentLocation"), - icon: BitmapDescriptor.defaultMarker, - position: _currentP!, - ), - }, - polylines: Set.of(polylines.values), - myLocationButtonEnabled: false, - onCameraIdle: () { - if (professionalAddress == '') { - if (coordinates != null) { - getLocationName( - coordinates.latitude, coordinates.longitude) - .then((locationName) { - if (mounted) { - setState(() { - _locacionController.text = locationName; - }); - } - }); - } - } - }, - onCameraMove: (position) { - if (mounted) { - setState(() { - coordinates = position.target; - }); - } - }, - ), - Positioned( - top: 10, - left: 10, - child: Builder( - builder: (context) { - return ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: Colors.white, - shape: const CircleBorder(), - elevation: 3, - minimumSize: const Size(50, 50), - ), - child: const Icon( - Icons.menu, - color: Colors.black, - size: 35, - ), - onPressed: () { - Scaffold.of(context).openDrawer(); - }, - ); - }, - ), - ), - const Positioned( - bottom: 25, - right: 0, - left: 0, - top: 0, - child: Icon( - Icons.location_on, - size: 40, - color: Color(0xFFFF0000), - ), - ), - Positioned( - top: 10, - right: 20, - child: FloatingActionButton( - onPressed: () async { - try { - Position position = await _determinePosition(); - - if (mounted) { - setState(() { - _currentP = LatLng( - position.latitude, - position.longitude, - ); - }); - - _animateCameraToPosition(_currentP!); - } - } catch (e) { - WarningSnackbar.show( - title: 'Ubicación desactivada', - message: - 'Por favor activa la ubicacion de tu telefono.', - ); - } - }, - elevation: 0, - child: const Icon( - Icons.gps_fixed, - size: 30, - ), - ), - ), - ], - ), - ), - Container( - padding: const EdgeInsets.symmetric(horizontal: 30, vertical: 10), - color: Colors.white, - child: Form( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - TextFormField( - controller: _serviceTypeController, - readOnly: true, - onTap: () async { - if (_nameController.text == '') { - WarningSnackbar.show( - title: 'Completa tu perfil', - message: - 'Asegurate de llenar todos los campos de tu perfil antes de seleccionar un tipo de servicio.', - ); - - return; - } - - final String? serviceType = await Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return const ServiceTypeScreen(); - }, - ), - ) as String?; - - if (serviceType != null) { - if (mounted) { - setState(() { - _serviceType = serviceType; - _serviceTypeController.text = _serviceType; - - updateMarkersForServiceType(_serviceType); - }); - - datos = await Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return ProfessionalScreen( - profession: _serviceTypeController.text, - ); - }, - ), - ); - - if (datos != null) { - fechaSeleccionada = datos[0]; - horaSeleccionada = datos[1]; - Professional? profesional = datos[2]; - profesionalSeleccionado = profesional; - - if (profesional != null) { - ubicacion = datos[3]; - if (mounted) { - setState(() { - _profesionalController.text = - profesional.name.toString(); - - professionalId = profesional.id; - - if (profesional.token != '') { - professionalToken = profesional.token!; - } - - professionalTarifa = - profesional.tarifa ?? 0; - - professionalUbicacion = - profesional.ubicacion; - - professionalLatitude = profesional.latitude; - professionalLongitude = - profesional.longitude; - - if (ubicacion == 'sitio') { - professionalAddress = - profesional.realAddress; - - _locacionController.text = - profesional.realAddress; - - LatLng professionalCoordinates = LatLng( - professionalLatitude!, - professionalLongitude!); - - if (professionalLatitude != null && - professionalLatitude != 0 && - professionalLongitude != null && - professionalLongitude != 0) { - _animateCameraToPosition( - LatLng(professionalLatitude!, - professionalLongitude!), - ); - - getPolylinePoints(_currentP!, - professionalCoordinates) - .then( - (coordinates) => { - generatePolyLineFromPoints( - coordinates), - }, - ); - } - } else { - polylines.clear(); - } - }); - } - } - } - } - } - }, - decoration: const InputDecoration( - prefixIcon: Icon(Icons.room_service_rounded), - suffixIcon: Icon(Icons.arrow_drop_down), - hintText: 'Tipo de Servicio', - ), - ), - const SizedBox(height: 15), - TextFormField( - controller: _profesionalController, - readOnly: true, - onTap: () async { - if (_currentP == null) { - try { - Position position = await _determinePosition(); - - if (mounted) { - setState(() { - _currentP = LatLng( - position.latitude, - position.longitude, - ); - }); - - _animateCameraToPosition(_currentP!); - } - } catch (e) { - WarningSnackbar.show( - title: 'Ubicación desactivada', - message: - 'Por favor activa la ubicacion de tu telefono para solicitar un servicio.', - ); - } - - return; - } - - if (_nameController.text == '') { - WarningSnackbar.show( - title: 'Completa tu perfil', - message: - 'Asegurate de llenar todos los campos de tu perfil antes de seleccionar un profesional.', - ); - - return; - } - - datos = await Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return ProfessionalScreen( - profession: _serviceTypeController.text, - ); - }, - ), - ); - - if (datos != null) { - fechaSeleccionada = datos[0]; - horaSeleccionada = datos[1]; - Professional? profesional = datos[2]; - profesionalSeleccionado = profesional; - - if (profesional != null) { - ubicacion = datos[3]; - if (mounted) { - setState(() { - _profesionalController.text = - profesional.name.toString(); - - professionalId = profesional.id; - - if (profesional.token != '') { - professionalToken = profesional.token!; - } - - professionalTarifa = profesional.tarifa ?? 0; - - professionalUbicacion = profesional.ubicacion; - - professionalLatitude = profesional.latitude; - professionalLongitude = profesional.longitude; - - if (ubicacion == 'sitio') { - professionalAddress = profesional.realAddress; - - _locacionController.text = - profesional.realAddress; - - LatLng professionalCoordinates = LatLng( - professionalLatitude!, - professionalLongitude!); - - if (professionalLatitude != null && - professionalLatitude != 0 && - professionalLongitude != null && - professionalLongitude != 0) { - _animateCameraToPosition( - LatLng(professionalLatitude!, - professionalLongitude!), - ); - - getPolylinePoints( - _currentP!, professionalCoordinates) - .then( - (coordinates) => { - generatePolyLineFromPoints(coordinates), - }, - ); - } - } else { - polylines.clear(); - } - }); - } - } - } - }, - decoration: const InputDecoration( - prefixIcon: Icon(Icons.assignment_ind_rounded), - suffixIcon: Icon(Icons.arrow_drop_down), - hintText: 'Seleccionar profesional', - ), - ), - const SizedBox(height: 15), - TextFormField( - controller: _locacionController, - readOnly: ubicacion == 'sitio' ? true : false, - decoration: const InputDecoration( - prefixIcon: Icon(Icons.near_me), - hintText: 'Dirección', - ), - onChanged: (value) { - String modifiedValue = value.replaceAll(' ', '_'); - placeAutoComplete(modifiedValue); - }, - ), - ubicacion == 'sitio' - ? const SizedBox() - : SizedBox( - height: _placesList.isNotEmpty ? 200 : 0, - child: ListView.builder( - itemCount: _placesList.length, - itemBuilder: (context, index) { - return ListTile( - onTap: () { - final selectedAddress = - _placesList[index]['formatted_address']; - final selectedLatitude = _placesList[index] - ['geometry']['location']['lat']; - final selectedLongitude = _placesList[index] - ['geometry']['location']['lng']; - - _locacionController.text = selectedAddress; - - _animateCameraToPosition(LatLng( - selectedLatitude, - selectedLongitude, - )); - - if (mounted) { - setState(() { - _placesList = []; - }); - } - }, - title: Text( - _placesList[index]['formatted_address']), - ); - }, - ), - ), - fechaSeleccionada == null && horaSeleccionada == null - ? const SizedBox() - : const SizedBox(height: 15), - fechaSeleccionada == null && horaSeleccionada == null - ? const SizedBox() - : Row( - children: [ - Expanded( - child: TextFormField( - readOnly: true, - onTap: () async { - datos = await Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return CalendarProScreen( - professional: - profesionalSeleccionado!, - ); - }, - ), - ); - - if (datos != null) { - fechaSeleccionada = datos[0]; - horaSeleccionada = datos[1]; - Professional? profesional = datos[2]; - profesionalSeleccionado = profesional; - - if (profesional != null) { - if (mounted) { - setState(() { - _profesionalController.text = - profesional.name.toString(); - - professionalId = profesional.id; - - if (profesional.token != '') { - professionalToken = - profesional.token!; - } - - professionalTarifa = - profesional.tarifa ?? 0; - - professionalUbicacion = - profesional.ubicacion; - - professionalLatitude = - profesional.latitude; - professionalLongitude = - profesional.longitude; - - if (ubicacion == 'sitio') { - professionalAddress = - profesional.realAddress; - - _locacionController.text = - profesional.realAddress; - - LatLng professionalCoordinates = - LatLng(professionalLatitude!, - professionalLongitude!); - - if (professionalLatitude != - null && - professionalLatitude != 0 && - professionalLongitude != - null && - professionalLongitude != 0) { - _animateCameraToPosition( - LatLng(professionalLatitude!, - professionalLongitude!), - ); - - getPolylinePoints(_currentP!, - professionalCoordinates) - .then( - (coordinates) => { - generatePolyLineFromPoints( - coordinates), - }, - ); - } - } else { - polylines.clear(); - } - }); - } - } - } - }, - decoration: const InputDecoration( - prefixIcon: Icon(Icons.calendar_month), - hintText: 'Fecha', - ), - controller: TextEditingController( - text: fechaSeleccionada == null - ? '' - : formatter.format(fechaSeleccionada!), - ), - ), - ), - Expanded( - child: TextFormField( - readOnly: true, - onTap: () async { - datos = await Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return CalendarProScreen( - professional: - profesionalSeleccionado!, - ); - }, - ), - ); - - if (datos != null) { - fechaSeleccionada = datos[0]; - horaSeleccionada = datos[1]; - Professional? profesional = datos[2]; - profesionalSeleccionado = profesional; - - if (profesional != null) { - if (mounted) { - setState(() { - _profesionalController.text = - profesional.name.toString(); - - professionalId = profesional.id; - - if (profesional.token != '') { - professionalToken = - profesional.token!; - } - - professionalTarifa = - profesional.tarifa ?? 0; - - professionalUbicacion = - profesional.ubicacion; - - professionalLatitude = - profesional.latitude; - professionalLongitude = - profesional.longitude; - - if (ubicacion == 'sitio') { - professionalAddress = - profesional.realAddress; - - _locacionController.text = - profesional.realAddress; - - LatLng professionalCoordinates = - LatLng(professionalLatitude!, - professionalLongitude!); - - if (professionalLatitude != null && - professionalLatitude != 0 && - professionalLongitude != null && - professionalLongitude != 0) { - _animateCameraToPosition( - LatLng(professionalLatitude!, - professionalLongitude!), - ); - - getPolylinePoints(_currentP!, - professionalCoordinates) - .then( - (coordinates) => { - generatePolyLineFromPoints( - coordinates), - }, - ); - } - } else { - polylines.clear(); - } - }); - } - } - } - }, - decoration: const InputDecoration( - prefixIcon: Icon(Icons.watch_later_outlined), - hintText: 'Hora', - ), - controller: TextEditingController( - text: horaSeleccionada == null - ? '' - : ' ${horaSeleccionada!.format(context)}', - ), - )) - ], - ), - fechaSeleccionada == null && horaSeleccionada == null - ? const SizedBox() - : const SizedBox(height: 15), - fechaSeleccionada == null && horaSeleccionada == null - ? const SizedBox() - : TextFormField( - controller: _observacionController, - decoration: const InputDecoration( - prefixIcon: Icon(Icons.message_outlined), - hintText: 'Observaciones', - ), - ), - const SizedBox(height: 20), - Center( - child: PrimaryButton( - onPressed: () { - if (user?.name == null || - user?.name == '' || - user?.phoneNumber == null || - user?.phoneNumber == '') { - WarningSnackbar.show( - title: 'Completa tu perfil', - message: - 'Diligencia la información de tu perfil antes de solicitar un servicio.', - ); - - Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return const ProfileScreen(); - }, - ), - ); - } else { - try { - DateTime time1 = DateTime( - fechaSeleccionada!.year, - fechaSeleccionada!.month, - fechaSeleccionada!.day, - horaSeleccionada!.hour, - horaSeleccionada!.minute, - ); - - DateTime time2 = - time1.add(const Duration(hours: 2)); - - UserModel.getUser(uid.toString()).then((value) { - eventoService - .createEvent( - value.name, - _observacionController.text, - fechaSeleccionada.toString().substring(0, - fechaSeleccionada.toString().length - 1), - '$time1', - '$time2', - professionalId, - ubicacion, - _locacionController.text, - ubicacion != 'sitio' - ? coordinates.latitude - : professionalLatitude, - ubicacion != 'sitio' - ? coordinates.longitude - : professionalLongitude, - 'pendiente', - professionalTarifa == 0 - ? 0 - : professionalTarifa, - false, - false, - ) - .then((value) { - if (professionalToken != '') { - sendPushNotification(professionalToken); - } - Navigator.pushReplacement( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return ServiceAfterScreen( - eventoId: value, - ); - }, - ), - ); - }); - }); - } catch (e) { - WarningSnackbar.show( - title: 'Llena todos los campos', - message: - 'Asegurate de llenar todos los campos antes de solicitar un servicio.', - ); - } - } - }, - text: 'Solicitar servicio', - minWidth: 230, - ), - ), - ], - ), - ), - ) - ], - ), - ), - ); - } - - Future getLocationUpdates() async { - bool _serviceEnabled; - PermissionStatus _permissionGranted; - - _serviceEnabled = await _locationController.serviceEnabled(); - if (_serviceEnabled) { - _serviceEnabled = await _locationController.requestService(); - } else { - return; - } - - _permissionGranted = await _locationController.hasPermission(); - if (_permissionGranted == PermissionStatus.denied) { - _permissionGranted = await _locationController.requestPermission(); - if (_permissionGranted != PermissionStatus.granted) { - return; - } - } - - _locationController.onLocationChanged - .listen((LocationData currentLocation) { - if (currentLocation.latitude != null && - currentLocation.longitude != null) { - if (mounted) { - setState(() { - _currentP = LatLng( - currentLocation.latitude!, - currentLocation.longitude!, - ); - }); - } - } - }); - } - - Future> getPolylinePoints( - LatLng originP, LatLng destinationP) async { - List polylineCoordinates = []; - PolylinePoints polylinePoints = PolylinePoints(); - PolylineResult result = await polylinePoints.getRouteBetweenCoordinates( - GOOGLE_MAPS_API_KEY, - PointLatLng(originP.latitude, originP.longitude), - PointLatLng(destinationP.latitude, destinationP.longitude), - travelMode: TravelMode.driving, - ); - if (result.points.isNotEmpty) { - result.points.forEach((PointLatLng point) { - polylineCoordinates.add(LatLng(point.latitude, point.longitude)); - }); - } else { - print(result.errorMessage); - } - return polylineCoordinates; - } - - void generatePolyLineFromPoints(List polylineCoordinates) async { - PolylineId id = const PolylineId("poly"); - Polyline polyline = Polyline( - polylineId: id, - color: Colors.blue, - points: polylineCoordinates, - width: 6, - ); - if (mounted) { - setState(() { - polylines[id] = polyline; - }); - } - } - - void _applyMapStyle(GoogleMapController controller) { - controller.setMapStyle(MAP_STYLE); - } - - Future _animateCameraToPosition(LatLng position) async { - final GoogleMapController controller = await _mapController.future; - controller.animateCamera( - CameraUpdate.newCameraPosition( - CameraPosition( - target: position, - zoom: 17.5, - ), - ), - ); - } - - Future _animateInitialCameraToPosition(String coordsOfCity) async { - List coords = coordsOfCity.split(','); - double lat = double.parse(coords[0]); - double lng = double.parse(coords[1]); - - _animateCameraToPosition(LatLng(lat, lng)); - - try { - Position position = await _determinePosition(); - - _animateCameraToPosition(LatLng(position.latitude, position.longitude)); - - if (mounted) { - setState(() {}); - } - } catch (e) { - print('Error: $e'); - } - } - - void updateMarkersForServiceType(String serviceType) { - getUsersWithActiveStatus(serviceType).then((value) { - markers.clear(); - for (var doc in value) { - final element = doc.data()!; - - if (element['latitude'] != null && element['longitude'] != null) { - markers.add( - Marker( - icon: _markerIcon!, - markerId: MarkerId(doc.id), - position: LatLng( - element['latitude'], - element['longitude'], - ), - ), - ); - } - } - }).catchError((e) { - print('Error al actualizar los marcadores: $e'); - }); - } - - Future _determinePosition() async { - bool serviceEnabled; - LocationPermission permission; - - serviceEnabled = await Geolocator.isLocationServiceEnabled(); - - if (!serviceEnabled) { - return Future.error('Location services are disabled'); - } - - permission = await Geolocator.checkPermission(); - - if (permission == LocationPermission.denied) { - permission = await Geolocator.requestPermission(); - - if (permission == LocationPermission.denied) { - return Future.error('Location permission denied'); - } - } - - if (permission == LocationPermission.deniedForever) { - return Future.error('Location permissions are permanently denied'); - } - - Position position = await Geolocator.getCurrentPosition(); - - return position; - } - - Future getLocationName(double latitude, double longitude) async { - String address; - try { - List placemarks = - await geocoding.placemarkFromCoordinates(latitude, longitude); - geocoding.Placemark place = placemarks[0]; - - if (place.thoroughfare != '' || place.subThoroughfare != '') { - address = - "${place.thoroughfare} ${place.subThoroughfare} ${place.subLocality}, ${place.locality}, ${place.administrativeArea}"; - } else { - address = ''; - } - } catch (e) { - address = ''; - } - - return address; - } - - void placeAutoComplete(String query) async { - Uri uri = Uri.https("admin.prosapp.co", "/autocomplete", { - "input": query, - "location": _coordsOfCity, - }); - - String? response = await NetworkUtility.fetchUrl(uri); - - if (response != null) { - if (mounted) { - setState(() { - _placesList = jsonDecode(response.toString())['results']; - }); - } - } - } - - Future>>> getUsersWithActiveStatus( - String serviceType) async { - dynamic querySnapshot; - - if (serviceType != "Servicio") { - querySnapshot = await FirebaseFirestore.instance - .collection('users') - .where('estado', isEqualTo: 'activo') - .where('profesion', isEqualTo: serviceType) - .get(); - } else { - querySnapshot = await FirebaseFirestore.instance - .collection('users') - .where('estado', isEqualTo: 'activo') - .get(); - } - - return querySnapshot.docs; - } - - void _saveToken() async { - FirebaseMessaging messaging = FirebaseMessaging.instance; - - final token = await messaging.getToken(); - - try { - await FirebaseFirestore.instance - .collection('users') - .doc(uid) - .update({'token': token}); - } catch (e) { - print(e); - } - } - - Future _getAppVersion() async { - PackageInfo packageInfo = await PackageInfo.fromPlatform(); - String version = packageInfo.version; - - print('App version: $version'); - - if (mounted) { - setState(() { - appVersion = version; - }); - } - - print('App version: $settings'); - - if (settings != null) { - _checkForUpdate(settings); - } - } - - void _checkForUpdate(String? settings) { - if (appVersion == settings) { - showDialog( - context: context, - builder: (context) { - return WillPopScope( - onWillPop: () async { - return false; - }, - child: Center( - child: AlertDialog( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16.0), - ), - title: const Text( - "Debes Actualizar la Aplicación", - style: TextStyle(fontWeight: FontWeight.bold), - ), - content: const Text( - "Tienes una versión desactualizada de nuestra aplicación. Te recomendamos actualizarla para disfrutar de las últimas características y mejoras.", - ), - actions: [ - TextButton( - child: const Text( - "Actualizar", - style: - TextStyle(fontWeight: FontWeight.w600, fontSize: 20), - ), - onPressed: () { - if (Platform.isAndroid) { - launchUrl(_url); - } else { - launchUrl(_urlIos); - } - }, - ), - ], - ), - ), - ); - }, - ); - } - } - - Future _selectDateAndTime(BuildContext context) async { - final DateTime? pickedDate = await showDatePicker( - context: context, - initialDate: now, - firstDate: now, - lastDate: DateTime(now.year + 1), - ); - - if (pickedDate == null) { - return; - } - - final TimeOfDay? pickedTime = await showTimePicker( - context: context, - initialTime: TimeOfDay.now(), - ); - - if (pickedTime != null) { - final DateTime selectedDateTime = DateTime( - pickedDate.year, - pickedDate.month, - pickedDate.day, - pickedTime.hour, - pickedTime.minute, - ); - - if (selectedDateTime.isBefore(DateTime.now())) { - WarningSnackbar.show( - title: 'Hora inválida', - message: - 'La hora seleccionada no es valida porque es anterior a la hora actual.', - ); - return; - } - - if (mounted) { - setState(() {}); - } - } - } - - Future sendPushNotification(String pro) async { - try { - http.Response response = await http.post( - Uri.parse('https://fcm.googleapis.com/fcm/send'), - headers: { - 'Content-Type': 'application/json; charset=UTF-8', - 'Authorization': - 'key=AAAAORdR-xU:APA91bF_wblg86jHAC-uexrXPHavYRlk5wge1Gf46m56V4J2D2L37Cp_hf46JZUzpvsWPSpqc5ewHelKI9LTifUG_s2mciMI6e5VLKo7E1R8btbNo7iaM9do2ctoyHKUm1atlZBdKaN2', - }, - body: jsonEncode( - { - 'notification': { - 'body': 'alguien a solicitado tus servicios', - 'title': 'Nueva solicitud', - }, - 'priority': 'high', - 'data': { - 'click_action': 'FLUTTER_NOTIFICATION_CLICK', - 'id': '1', - 'status': 'done', - 'screen': 'solicitud' - }, - 'to': pro - }, - ), - ); - - response; - } catch (e) { - print('error al enviar notificacion $e'); - } - } -} diff --git a/lib/src/presentation/screens/messages.dart b/lib/src/presentation/screens/messages.dart deleted file mode 100644 index 0cd2626..0000000 --- a/lib/src/presentation/screens/messages.dart +++ /dev/null @@ -1,104 +0,0 @@ -import 'package:community_material_icon/community_material_icon.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:prosappco/src/authentication/authentication_repository.dart'; -import 'package:prosappco/src/components/photo_view.dart'; -import 'package:prosappco/src/components/pop_appbar.dart'; -import 'package:prosappco/src/models/chat_model.dart'; -import 'package:prosappco/src/models/event_model.dart'; -import 'package:prosappco/src/presentation/screens/chat.dart'; - -class MessagesScreen extends StatefulWidget { - const MessagesScreen({super.key}); - - @override - State createState() => _MessagesScreenState(); -} - -class _MessagesScreenState extends State { - final uid = AuthenticationRepository.instance.getCurrentUserUid(); - List list = []; - @override - void initState() { - super.initState(); - - ChatModel.getChatsByProId(uid!).then( - (List s) => setState(() { - list = s; - }), - ); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: PopAppbar( - onPressed: () { - Navigator.pop(context); - }, - label: 'Mensajes'), - body: Column( - children: [ - const Padding( - padding: EdgeInsets.all(10), - child: TextField( - // controller: searchController, - decoration: InputDecoration( - hintText: 'Escribe un nombre', - prefixIcon: Icon(CommunityMaterialIcons.stethoscope), - ), - ), - ), - Expanded( - child: ListView.builder( - itemCount: list.length, - itemBuilder: (BuildContext context, int index) { - if (list[index].messages.isNotEmpty) { - final user = list[index].user; - final lastMsg = list[index].messages.last; - return ListTile( - title: Text( - (user?.name ?? ''), - ), - subtitle: Text( - '" ${lastMsg.content} "', - style: const TextStyle(fontStyle: FontStyle.italic), - ), - leading: ReferencePhoto( - ref: user?.photo, - size: 55, - sizeCircle: 60, - ), - trailing: const Column( - children: [ - SizedBox(height: 8), - Icon( - Icons.keyboard_arrow_right, - color: Colors.black, - ), - ], - ), - onTap: () { - Event.getEventById(list[index].id).then((value) { - Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return ChatScreen(eventoId: list[index].id); - }, - ), - ); - }); - }, - ); - } else { - return const SizedBox(); - } - }, - ), - ), - ], - ), - ); - } -} diff --git a/lib/src/presentation/screens/messages_user.dart b/lib/src/presentation/screens/messages_user.dart deleted file mode 100644 index 30f5b61..0000000 --- a/lib/src/presentation/screens/messages_user.dart +++ /dev/null @@ -1,113 +0,0 @@ -import 'package:community_material_icon/community_material_icon.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:intl/intl.dart'; -import 'package:prosappco/src/authentication/authentication_repository.dart'; -import 'package:prosappco/src/components/photo_view.dart'; -import 'package:prosappco/src/components/pop_appbar.dart'; -import 'package:prosappco/src/models/chat_model.dart'; -import 'package:prosappco/src/models/event_model.dart'; -import 'package:prosappco/src/presentation/screens/chat.dart'; - -class MessagesUserScreen extends StatefulWidget { - const MessagesUserScreen({super.key}); - - @override - State createState() => _MessagesUserScreenState(); -} - -class _MessagesUserScreenState extends State { - final uid = AuthenticationRepository.instance.getCurrentUserUid(); - List list = []; - @override - void initState() { - super.initState(); - - ChatModel.getChatsByUserId(uid!).then( - (List s) => setState(() { - list = s; - }), - ); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: PopAppbar( - onPressed: () { - Navigator.pop(context); - }, - label: 'Mensajes'), - body: Column( - children: [ - const Padding( - padding: EdgeInsets.all(10), - child: TextField( - // controller: searchController, - decoration: InputDecoration( - hintText: 'Escribe un nombre', - prefixIcon: Icon(CommunityMaterialIcons.stethoscope), - ), - ), - ), - Expanded( - child: ListView.builder( - itemCount: list.length, - itemBuilder: (BuildContext context, int index) { - if (list[index].messages.isNotEmpty) { - final pro = list[index].professional; - final lastMsg = list[index].messages.last; - return ListTile( - title: Text( - (pro?.name ?? ''), - ), - subtitle: Text( - '" ${lastMsg.content} "', - style: const TextStyle(fontStyle: FontStyle.italic), - ), - leading: ReferencePhoto( - ref: pro?.photo, - size: 55, - sizeCircle: 60, - ), - trailing: Column( - children: [ - SizedBox(height: 8), - const Icon( - Icons.keyboard_arrow_right, - color: Colors.black, - ), - Text( - lastMsg.timestamp.day >= DateTime.now().day - ? DateFormat('h:mm a').format(lastMsg.timestamp) - : DateFormat('dd/MM/yyyy', 'es') - .format(lastMsg.timestamp), - style: - const TextStyle(color: Colors.grey, fontSize: 12), - ) - ], - ), - onTap: () { - Event.getEventById(list[index].id).then((value) { - Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return ChatScreen(eventoId: list[index].id); - }, - ), - ); - }); - }, - ); - } else { - return const SizedBox(); - } - }, - ), - ), - ], - ), - ); - } -} diff --git a/lib/src/presentation/screens/my_services.dart b/lib/src/presentation/screens/my_services.dart deleted file mode 100644 index 9124b0d..0000000 --- a/lib/src/presentation/screens/my_services.dart +++ /dev/null @@ -1,302 +0,0 @@ -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_rating_bar/flutter_rating_bar.dart'; -import 'package:intl/intl.dart'; -import 'package:prosappco/src/authentication/authentication_repository.dart'; -import 'package:prosappco/src/components/drawer_professional.dart'; -import 'package:prosappco/src/components/pop_appbar.dart'; -import 'package:prosappco/src/models/event_model.dart'; -import 'package:prosappco/src/models/scores_model.dart'; -import 'package:prosappco/src/presentation/screens/cita.dart'; -import 'package:prosappco/src/presentation/screens/score.dart'; - -class MyServicesScreen extends StatelessWidget { - MyServicesScreen({super.key}); - - DateTime today = DateTime.now(); - - final uid = AuthenticationRepository.instance.getCurrentUserUid(); - - @override - Widget build(BuildContext context) { - return SafeArea( - child: Scaffold( - appBar: PopAppbar( - onPressed: () { - Navigator.pop(context); - }, - label: 'Mis servicios'), - drawer: DrawerProfessional(), - body: SingleChildScrollView( - child: Column( - children: [ - _eventList(), - ], - ), - ), - ), - ); - } - - Widget _eventList() { - return StreamBuilder>( - stream: FirebaseFirestore.instance - .collection('services') - .where('user_id', isEqualTo: uid) - .where('status', whereIn: [ - 'aprobado', - 'denegado', - 'iniciado', - 'terminado', - 'pendiente' - ]) - .snapshots() - .asyncMap((snapshot) async { - try { - List eventos = []; - - for (var element in snapshot.docs) { - final event = Event.fromJson(element.data()); - event.scoresModel = - await ScoresModel.scoreTo(event.userId, false, false); - event.id = element.id; - eventos.add(event); - } - return eventos; - } catch (e) { - print('Error getByProId $e'); - return []; - } - }), - builder: (BuildContext context, AsyncSnapshot> snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { - return const Center( - child: CircularProgressIndicator(), - ); - } - - List eventos = []; - - try { - eventos.addAll(snapshot.data!); - eventos.sort((a, b) => a.timeStamp!.compareTo(b.timeStamp!)); - - print('snapshot mi b ${eventos}'); - } catch (e) { - print("Error snapshot" + e.toString()); - } - - if (eventos.isEmpty) { - return const Padding( - padding: EdgeInsets.symmetric(vertical: 50), - child: Center(child: Text('No tienes citas')), - ); - } - - return Column( - children: [ - ...eventos - .where((event) => event.professionalId != event.userId) - .map( - (event) => FutureBuilder( - future: FirebaseFirestore.instance - .collection('users') - .doc(event.professionalId) - .get(), - builder: (BuildContext context, - AsyncSnapshot profSnapshot) { - if (profSnapshot.connectionState == - ConnectionState.waiting) { - return const CircularProgressIndicator(); - } - - if (profSnapshot.hasError) { - return const Text( - 'Error al obtener los datos del profesional', - ); - } - - final professionalData = profSnapshot.data; - final professionalName = - professionalData?['name'] ?? 'N/D'; - - return ListTile( - tileColor: event.status == 'denegado' - ? Colors.red[100] - : Colors.blue[100], - onTap: () { - if (event.status == 'terminado') { - if (event.userId == uid) { - if (event.userScored) { - Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return CitaScreen(evento: event); - }, - ), - ); - } else { - Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return ScoreScreen( - evento: event, pro: false); - }, - ), - ); - } - } - } else { - Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return CitaScreen(evento: event); - }, - ), - ); - } - }, - leading: event.status == 'aprobado' - ? const Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - Icons.check, - color: Colors.blue, - size: 30, - ), - Text('Aceptado', - style: TextStyle(fontSize: 12)), - ], - ) - : event.status == 'iniciado' - ? const Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - Icons.access_time, - color: Colors.blue, - size: 30, - ), - Text('Iniciado', - style: TextStyle(fontSize: 12)), - ], - ) - : event.status == 'pendiente' - ? const Column( - mainAxisAlignment: - MainAxisAlignment.center, - children: [ - Icon( - Icons.access_time_outlined, - color: Colors.blue, - size: 30, - ), - Text('Pendiente', - style: TextStyle(fontSize: 12)), - ], - ) - : event.status == 'terminado' - ? const Column( - mainAxisAlignment: - MainAxisAlignment.center, - children: [ - Icon( - Icons.rocket_launch, - color: Colors.blue, - size: 30, - ), - Text('Finalizado', - style: - TextStyle(fontSize: 12)), - ], - ) - : const Column( - mainAxisAlignment: - MainAxisAlignment.center, - children: [ - Icon( - Icons.close, - color: Colors.red, - size: 30, - ), - Text('Cancelado', - style: - TextStyle(fontSize: 12)), - ], - ), - title: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - '$professionalName', - style: const TextStyle( - color: Colors.black, - fontWeight: FontWeight.bold, - fontSize: 16, - ), - ), - Text( - '${DateFormat('dd MMMM', 'es').format(DateTime.parse(event.day))} - ${DateFormat('h:mm a').format(DateTime.parse(event.range1Hour1))}', - style: TextStyle( - color: Colors.grey[600], - fontSize: 16, - ), - ), - ], - ), - subtitle: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - RatingBar.builder( - initialRating: - event.scoresModel?.average ?? 0, - minRating: 1, - direction: Axis.horizontal, - allowHalfRating: true, - itemCount: 5, - itemSize: 25, - maxRating: 5, - itemPadding: - const EdgeInsets.symmetric(horizontal: 0), - itemBuilder: (context, _) => const Icon( - Icons.star, - color: Color(0xFF2BA4EC), - ), - onRatingUpdate: (rating) {}, - ignoreGestures: true, - ), - const SizedBox(width: 5), - Text( - '(${event.scoresModel?.total.toString()}) ${event.scoresModel?.average.toStringAsFixed(1)}'), - ], - ), - Text( - '"${event.description}"', - style: - const TextStyle(fontStyle: FontStyle.italic), - ), - ], - ), - trailing: const Column( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - Icon(Icons.keyboard_arrow_right), - ], - ), - ); - }, - ), - ), - ], - ); - }, - ); - } -} diff --git a/lib/src/presentation/screens/my_services_pro.dart b/lib/src/presentation/screens/my_services_pro.dart deleted file mode 100644 index d593ae5..0000000 --- a/lib/src/presentation/screens/my_services_pro.dart +++ /dev/null @@ -1,250 +0,0 @@ -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_rating_bar/flutter_rating_bar.dart'; -import 'package:intl/intl.dart'; -import 'package:prosappco/src/authentication/authentication_repository.dart'; -import 'package:prosappco/src/components/drawer_professional.dart'; -import 'package:prosappco/src/components/pop_appbar.dart'; -import 'package:prosappco/src/models/event_model.dart'; -import 'package:prosappco/src/models/scores_model.dart'; -import 'package:prosappco/src/presentation/screens/cita.dart'; -import 'package:prosappco/src/presentation/screens/score.dart'; - -class MyServicesProScreen extends StatelessWidget { - MyServicesProScreen({super.key}); - - DateTime today = DateTime.now(); - - final uid = AuthenticationRepository.instance.getCurrentUserUid(); - - @override - Widget build(BuildContext context) { - return SafeArea( - child: Scaffold( - appBar: PopAppbar( - onPressed: () { - Navigator.pop(context); - }, - label: 'Mis servicios', - ), - drawer: DrawerProfessional(), - body: SingleChildScrollView( - child: Column( - children: [ - _eventList(), - ], - ), - ), - ), - ); - } - - Widget _eventList() { - return StreamBuilder>( - stream: FirebaseFirestore.instance - .collection('services') - .where('professional_id', isEqualTo: uid) - .where('status', whereIn: [ - 'aprobado', - 'denegado', - 'iniciado', - 'terminado', - ]) - .snapshots() - .asyncMap((snapshot) async { - try { - List eventos = []; - for (var element in snapshot.docs) { - final event = Event.fromJson(element.data()); - event.scoresModel = - await ScoresModel.scoreTo(event.userId, false, false); - event.id = element.id; - eventos.add(event); - } - return eventos; - } catch (e) { - print('Error getByProId $e'); - return []; - } - }), - builder: (BuildContext context, AsyncSnapshot> snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { - return const Center( - child: CircularProgressIndicator(), - ); - } - - List eventos = []; - - try { - eventos.addAll(snapshot.data!); - eventos.sort((a, b) => a.timeStamp!.compareTo(b.timeStamp!)); - } catch (e) { - print("Error" + e.toString()); - } - - if (eventos.isEmpty) { - return const Padding( - padding: EdgeInsets.symmetric(vertical: 50), - child: Center(child: Text('No tienes citas')), - ); - } - - return Column( - children: [ - ...eventos.map( - (event) => ListTile( - tileColor: event.status == 'denegado' - ? Colors.red[100] - : Colors.blue[100], - onTap: () { - if (event.status == 'terminado') { - if (event.professionalId == uid) { - if (event.professionalScored) { - Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return CitaScreen(evento: event); - }, - ), - ); - } else { - Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return ScoreScreen(evento: event, pro: true); - }, - ), - ); - } - } - } else { - Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return CitaScreen(evento: event); - }, - ), - ); - } - }, - leading: event.status == 'aprobado' - ? const Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - Icons.check, - color: Colors.blue, - size: 30, - ), - Text('Aceptado', style: TextStyle(fontSize: 12)), - ], - ) - : event.status == 'iniciado' - ? const Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - Icons.access_time, - color: Colors.blue, - size: 30, - ), - Text('Iniciado', style: TextStyle(fontSize: 12)), - ], - ) - : event.status == 'terminado' - ? const Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - Icons.rocket_launch, - color: Colors.blue, - size: 30, - ), - Text('Finalizado', - style: TextStyle(fontSize: 12)), - ], - ) - : const Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - Icons.close, - color: Colors.red, - size: 30, - ), - Text('Cancelado', - style: TextStyle(fontSize: 12)), - ], - ), - title: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - event.title, - style: const TextStyle( - color: Colors.black, - fontWeight: FontWeight.bold, - fontSize: 16, - ), - ), - Text( - '${DateFormat('dd MMMM', 'es').format(DateTime.parse(event.day))} - ${DateFormat('h:mm a').format(DateTime.parse(event.range1Hour1))}', - style: TextStyle( - color: Colors.grey[600], - fontSize: 16, - ), - ), - ], - ), - subtitle: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - RatingBar.builder( - initialRating: event.scoresModel?.average ?? 0, - minRating: 1, - direction: Axis.horizontal, - allowHalfRating: true, - itemCount: 5, - itemSize: 25, - maxRating: 5, - itemPadding: - const EdgeInsets.symmetric(horizontal: 0), - itemBuilder: (context, _) => const Icon( - Icons.star, - color: Color(0xFF2BA4EC), - ), - onRatingUpdate: (rating) {}, - ignoreGestures: true, - ), - const SizedBox(width: 5), - Text( - '(${event.scoresModel?.total.toString()}) ${event.scoresModel?.average.toStringAsFixed(1)}'), - ], - ), - Text( - '"${event.description}"', - style: const TextStyle(fontStyle: FontStyle.italic), - ), - ], - ), - trailing: const Column( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - Icon(Icons.keyboard_arrow_right), - ], - ), - ), - ), - ], - ); - }, - ); - } -} diff --git a/lib/src/presentation/screens/new_number.dart b/lib/src/presentation/screens/new_number.dart deleted file mode 100644 index d415122..0000000 --- a/lib/src/presentation/screens/new_number.dart +++ /dev/null @@ -1,275 +0,0 @@ -import 'package:firebase_auth/firebase_auth.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:get/get.dart'; -import 'package:intl_phone_field/intl_phone_field.dart'; -import 'package:prosappco/src/components/pop_appbar.dart'; -import 'package:prosappco/src/components/primary_btn.dart'; -import 'package:prosappco/src/controllers/new_phone_controller.dart'; - -class NewNumberScreen extends StatefulWidget { - const NewNumberScreen({super.key}); - - @override - State createState() => _NewNumberScreenState(); -} - -// Actualizar número de teléfono en Firebase -Future updatePhoneNumber(String verificationId, String smsCode) async { - try { - PhoneAuthCredential credential = PhoneAuthProvider.credential( - verificationId: verificationId, smsCode: smsCode); - await FirebaseAuth.instance.currentUser!.updatePhoneNumber(credential); - print("Phone number updated successfully"); - } catch (e) { - print("Error updating phone number: $e"); - } -} - -class _NewNumberScreenState extends State { - final controller = Get.put(NewPhoneController()); - String completePhoneNumber = ''; - final _formKey = GlobalKey(); - - @override - Widget build(BuildContext context) { - return SafeArea( - child: Scaffold( - resizeToAvoidBottomInset: false, - appBar: PopAppbar( - onPressed: () { - _formKey.currentState!.reset(); - Navigator.pop(context); - }, - label: 'Añadir numero', - ), - body: !kIsWeb - ? Container( - padding: - const EdgeInsets.symmetric(horizontal: 0, vertical: 20), - margin: const EdgeInsets.only(top: 30, left: 50, right: 50), - child: Column( - children: [ - const Padding( - padding: EdgeInsets.only(bottom: 5), - child: Align( - alignment: Alignment.topLeft, - child: Text('Numero de celular', - style: TextStyle( - fontSize: 18.0, color: Color(0xFF65676B))), - )), - Form( - key: _formKey, - child: Padding( - padding: const EdgeInsets.only(bottom: 5), - child: IntlPhoneField( - controller: controller.newPhoneNo, - initialCountryCode: 'CO', - onChanged: (newPhoneNo) { - completePhoneNumber = newPhoneNo.completeNumber; - }, - decoration: const InputDecoration( - border: OutlineInputBorder( - borderSide: - BorderSide(color: Color(0xFFECECEC)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - errorBorder: OutlineInputBorder( - borderSide: BorderSide( - color: Color.fromARGB(255, 184, 0, 0)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - enabledBorder: OutlineInputBorder( - borderSide: - BorderSide(color: Color(0xFFECECEC)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - focusedBorder: OutlineInputBorder( - borderSide: - BorderSide(color: Color(0xFFECECEC)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - fillColor: Color.fromARGB(255, 239, 239, 239), - filled: true, - ), - ), - ), - ), - const Padding( - padding: EdgeInsets.only(bottom: 30), - child: Text( - 'Se enviará un código a este número de celular', - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 13.0, color: Color(0xFF65676B))), - ), - Container( - margin: const EdgeInsets.only(top: 10, bottom: 30), - padding: const EdgeInsets.symmetric( - horizontal: 20, vertical: 15), - decoration: BoxDecoration( - color: const Color(0xFFD6F4FF), - borderRadius: BorderRadius.circular(20), - boxShadow: [ - BoxShadow( - color: Colors.grey.withOpacity(0.5), - spreadRadius: 1, - blurRadius: 5, - offset: const Offset(1, 3), - ), - ], - ), - child: const Row( - children: [ - Icon( - Icons.error_outline, - size: 27, - color: Colors.black54, - ), - SizedBox(width: 15), - Expanded( - child: Text( - '¡Al actualizar tu número, se cerrará la sesión para confirmar que eres tú!.', - style: - TextStyle(color: Colors.black, fontSize: 14), - ), - ), - ], - ), - ), - Padding( - padding: const EdgeInsets.only(bottom: 30), - child: Center( - child: PrimaryButtom( - onPressed: () { - controller.updatePhoneNumber( - completePhoneNumber.toString()); - }, - label: 'Enviar código'), - ), - ), - ], - ), - ) - : Center( - child: Container( - padding: - const EdgeInsets.symmetric(horizontal: 0, vertical: 20), - width: 400, - margin: const EdgeInsets.only(top: 30, left: 50, right: 50), - child: Column( - children: [ - const Padding( - padding: EdgeInsets.only(bottom: 5), - child: Align( - alignment: Alignment.topLeft, - child: Text('Numero de celular', - style: TextStyle( - fontSize: 18.0, color: Color(0xFF65676B))), - )), - Form( - key: _formKey, - child: Padding( - padding: const EdgeInsets.only(bottom: 5), - child: IntlPhoneField( - controller: controller.newPhoneNo, - initialCountryCode: 'CO', - onChanged: (newPhoneNo) { - completePhoneNumber = newPhoneNo.completeNumber; - }, - decoration: const InputDecoration( - border: OutlineInputBorder( - borderSide: - BorderSide(color: Color(0xFFECECEC)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - errorBorder: OutlineInputBorder( - borderSide: BorderSide( - color: Color.fromARGB(255, 184, 0, 0)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - enabledBorder: OutlineInputBorder( - borderSide: - BorderSide(color: Color(0xFFECECEC)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - focusedBorder: OutlineInputBorder( - borderSide: - BorderSide(color: Color(0xFFECECEC)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - fillColor: Color.fromARGB(255, 239, 239, 239), - filled: true, - ), - ), - ), - ), - const Padding( - padding: EdgeInsets.only(bottom: 30), - child: Text( - 'Se enviará un código a este número de celular', - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 13.0, color: Color(0xFF65676B))), - ), - Container( - margin: const EdgeInsets.only(top: 10, bottom: 30), - padding: const EdgeInsets.symmetric( - horizontal: 20, vertical: 15), - decoration: BoxDecoration( - color: const Color(0xFFD6F4FF), - borderRadius: BorderRadius.circular(20), - boxShadow: [ - BoxShadow( - color: Colors.grey.withOpacity(0.5), - spreadRadius: 1, - blurRadius: 5, - offset: const Offset(1, 3), - ), - ], - ), - child: const Row( - children: [ - Icon( - Icons.error_outline, - size: 27, - color: Colors.black54, - ), - SizedBox(width: 15), - Expanded( - child: Text( - '¡Al actualizar tu número, se cerrará la sesión para confirmar que eres tú!.', - style: TextStyle( - color: Colors.black, fontSize: 14), - ), - ), - ], - ), - ), - Padding( - padding: const EdgeInsets.only(bottom: 30), - child: Center( - child: PrimaryButtom( - onPressed: () { - controller.updatePhoneNumber( - completePhoneNumber.toString()); - }, - label: 'Enviar código'), - ), - ), - ], - ), - ), - ), - ), - ); - } -} diff --git a/lib/src/presentation/screens/new_number_validation.dart b/lib/src/presentation/screens/new_number_validation.dart deleted file mode 100644 index f98123e..0000000 --- a/lib/src/presentation/screens/new_number_validation.dart +++ /dev/null @@ -1,153 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_otp_text_field/flutter_otp_text_field.dart'; -import 'package:prosappco/src/controllers/otp_controller.dart'; - -class NewNumberValidationScreen extends StatefulWidget { - const NewNumberValidationScreen({super.key}); - - @override - State createState() => - _NewNumberValidationScreenState(); -} - -class _NewNumberValidationScreenState extends State { - dynamic otp; - @override - Widget build(BuildContext context) { - return SafeArea( - child: Scaffold( - resizeToAvoidBottomInset: false, - backgroundColor: const Color(0xFFD6F4FF), - body: Stack( - children: [ - Container( - margin: const EdgeInsets.only(top: 280), - width: double.infinity, - height: 600, - decoration: const BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.only( - topRight: Radius.circular(50), - topLeft: Radius.circular(50))), - ), - Container( - margin: const EdgeInsets.only(top: 120, left: 70, right: 70), - child: const Image(image: AssetImage('images/logo_prosapp.png')), - ), - Container( - padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 20), - margin: const EdgeInsets.only(top: 280, left: 25), - child: Row( - children: [ - IconButton( - icon: const Icon( - Icons.arrow_back, - size: 30, - ), - onPressed: () { - Navigator.pop(context); - }, - ), - const Text( - 'Valida el código', - style: TextStyle( - color: Color(0xFF262626), - fontSize: 30.0, - fontWeight: FontWeight.bold, - ), - textAlign: TextAlign.right, - ), - ], - ), - ), - Container( - padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 20), - margin: const EdgeInsets.only(top: 350, left: 50, right: 50), - child: Column(children: [ - const Padding( - padding: EdgeInsets.only(bottom: 5), - child: Align( - alignment: Alignment.topLeft, - child: Text('Numero de celular', - style: TextStyle( - fontSize: 18.0, color: Color(0xFF65676B))), - )), - Padding( - padding: const EdgeInsets.only(bottom: 20), - child: Row( - children: [ - const Expanded( - child: TextField( - decoration: InputDecoration( - border: InputBorder.none, - hintText: '+57', - suffixIcon: Icon(Icons.edit), - ), - ), - ), - TextButton( - onPressed: () { - // Acción a realizar cuando se hace clic en el texto - }, - child: const Text('Reenviar código'), - ), - ], - ), - ), - const Align( - alignment: Alignment.topLeft, - child: Padding( - padding: EdgeInsets.only(bottom: 5), - child: Text('Código', - textAlign: TextAlign.left, - style: - TextStyle(fontSize: 18.0, color: Color(0xFF65676B))), - ), - ), - Padding( - padding: const EdgeInsets.only(bottom: 20), - child: OtpTextField( - numberOfFields: 6, - focusedBorderColor: Colors.blue, - fillColor: Colors.black.withOpacity(0.1), - filled: true, - keyboardType: TextInputType.number, - onSubmit: (code) { - otp = code; - OTPController.instance.verifyOTP(otp); - }, - ), - ), - Padding( - padding: const EdgeInsets.only(bottom: 40), - child: Center( - child: ElevatedButton( - onPressed: () { - OTPController.instance.verifyOTP(otp); - }, - style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFF2BA4EC), // Color del botón - shape: RoundedRectangleBorder( - borderRadius: - BorderRadius.circular(50), // Bordes redondeados - ), - elevation: 0, - minimumSize: const Size(230, 60), // Tamaño mínimo del botón - ), - child: const Text( - 'Valida el código', - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 18, - ), - ), - )), - ), - ]), - ), - ], - ), - )); - } -} diff --git a/lib/src/presentation/screens/new_password.dart b/lib/src/presentation/screens/new_password.dart deleted file mode 100644 index 09afd16..0000000 --- a/lib/src/presentation/screens/new_password.dart +++ /dev/null @@ -1,160 +0,0 @@ -import 'package:firebase_auth/firebase_auth.dart'; -import 'package:flutter/material.dart'; -import 'package:get/get.dart'; -import 'package:prosappco/src/components/pop_appbar.dart'; -import 'package:prosappco/src/components/primary_btn.dart'; - -class NewPasswordScreen extends StatefulWidget { - NewPasswordScreen({super.key}); - - @override - State createState() => _NewPasswordScreenState(); -} - -class _NewPasswordScreenState extends State { - final _currentPasswordController = TextEditingController(); - final _newPasswordController = TextEditingController(); - bool _obscureText = true; - bool _obscureText2 = true; - - final FirebaseAuth _auth = FirebaseAuth.instance; - - Future updatePassword( - String currentPassword, String newPassword) async { - final User user = _auth.currentUser!; - - final credential = EmailAuthProvider.credential( - email: user.email!, - password: currentPassword, - ); - - try { - if (newPassword == currentPassword) { - Get.snackbar( - 'Misma contraseña', - 'La nueva contraseña debe ser distinta a la contraseña actual.', - snackPosition: SnackPosition.BOTTOM, - ); - return; - } else if (newPassword.isEmpty) { - Get.snackbar( - 'Ingrese una contraseña valida', - 'La nueva contraseña no puede estar vacia.', - snackPosition: SnackPosition.BOTTOM, - ); - return; - } else { - await user.reauthenticateWithCredential(credential); - try { - await user.updatePassword(newPassword); - // Muestra un mensaje de éxito - Get.snackbar( - 'Contraseña actualizada', - 'Tu contraseña ha sido cambiada con éxito.', - snackPosition: SnackPosition.BOTTOM, - ); - } catch (e) { - print("Error al verificar la contraseña actual: $e"); - } - } - } catch (e) { - Get.snackbar( - 'Contraseña incorrecta', - 'Ha ocurrido un error al actualizar la contraseña. Asegúrate de ingresar correctamente la contraseña actual.', - snackPosition: SnackPosition.BOTTOM, - ); - return; - } - } - - @override - void initState() { - super.initState(); - } - - @override - Widget build(BuildContext context) { - return SafeArea( - child: Scaffold( - appBar: PopAppbar( - onPressed: () { - Navigator.pop(context); - }, - label: ' Cambia tu contraseña', - ), - body: Center( - child: SizedBox( - width: 300, - child: Padding( - padding: const EdgeInsets.only(top: 20), - child: Column( - children: [ - const Text( - "Ten en cuenta que al cambiar tu contraseña, se cerrará automáticamente tu sesión.", - textAlign: TextAlign.center, - style: TextStyle(color: Colors.grey), - ), - const SizedBox(height: 40), - TextFormField( - controller: _currentPasswordController, - obscureText: _obscureText, - decoration: InputDecoration( - prefixIcon: const Icon(Icons.lock_outline), - suffixIcon: IconButton( - icon: Icon( - _obscureText - ? Icons.visibility - : Icons.visibility_off, - color: Colors.grey, - ), - onPressed: () { - setState(() { - _obscureText = !_obscureText; - }); - }, - ), - hintText: 'Contraseña (Actual)'), - ), - const SizedBox(height: 40), - TextFormField( - controller: _newPasswordController, - obscureText: _obscureText2, - decoration: InputDecoration( - prefixIcon: const Icon(Icons.lock_outline), - suffixIcon: IconButton( - icon: Icon( - _obscureText2 - ? Icons.visibility - : Icons.visibility_off, - color: Colors.grey, - ), - onPressed: () { - setState(() { - _obscureText2 = !_obscureText2; - }); - }, - ), - hintText: 'Contraseña (Nueva)'), - ), - const SizedBox(height: 80), - PrimaryButtom( - onPressed: () { - updatePassword(_currentPasswordController.text.trim(), - _newPasswordController.text.trim()); - }, - label: 'Actualizar contraseña'), - const SizedBox(height: 30), - const Text( - "Esta contraseña es valida si el inicio de sesión es por email.", - textAlign: TextAlign.center, - style: TextStyle(color: Colors.grey), - ), - ], - ), - ), - ), - ), - ), - ); - } -} diff --git a/lib/src/presentation/screens/profession.dart b/lib/src/presentation/screens/profession.dart deleted file mode 100644 index 7b5e21e..0000000 --- a/lib/src/presentation/screens/profession.dart +++ /dev/null @@ -1,248 +0,0 @@ -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:diacritic/diacritic.dart'; -import 'package:firebase_auth/firebase_auth.dart'; -import 'package:flutter/material.dart'; -import 'package:prosappco/src/authentication/authentication_repository.dart'; -import 'package:prosappco/src/components/pop_appbar.dart'; - -final CollectionReference professionsCollection = - FirebaseFirestore.instance.collection('professions'); - -Future> getProfessions() async { - try { - DocumentSnapshot profession = - await professionsCollection.doc('professions').get(); - - Map data = profession.data() as Map; - - var professionsList = (data['professions'] as List) - .map((e) => e.toString()) - .toList(); - - return professionsList; - } catch (e) { - print('$e'); - } - - return []; -} - -class ProfessionScreen extends StatefulWidget { - const ProfessionScreen({super.key}); - - @override - State createState() => _ProfessionScreenState(); -} - -class _ProfessionScreenState extends State { - List? filteredProfessions; - TextEditingController searchController = TextEditingController(); - final User? user = FirebaseAuth.instance.currentUser; - List? _professions; - final ScrollController _scrollController = ScrollController(); - final uid = AuthenticationRepository.instance.getCurrentUserUid(); - bool isNewProfessionAdded = false; - - @override - void initState() { - super.initState(); - searchController.addListener(() { - setState(() { - if (_professions != null) { - if (searchController.text.isEmpty) { - filteredProfessions = _professions!; - } else { - filteredProfessions = _professions! - .where((profession) => removeDiacritics(profession) - .toLowerCase() - .contains( - removeDiacritics(searchController.text.toLowerCase()))) - .toList(); - } - } - }); - }); - - if (_professions == null) { - getProfessions().then((List element) => setState(() { - _professions = element; - filteredProfessions = element; - })); - } - } - - Future updateProfession(String profession) async { - try { - await FirebaseFirestore.instance - .collection('users') - .doc(uid) - .update({'profesion': profession}); - } catch (e) { - try { - await FirebaseFirestore.instance - .collection('users') - .doc(uid) - .set({'profesion': profession}); - } catch (e) { - print('Error al agregar la profesion: $e'); - } - - print('Error al actualizar la profesion: $e'); - } - } - - Future saveProfession(String newProfession) async { - final DocumentReference professionsDocRef = - professionsCollection.doc('professions'); - - try { - final DocumentSnapshot profession = - await professionsDocRef.get(); - - Map data = profession.data() as Map; - - List professions = []; - - if (data['professions'] != null) { - professions = List.from(data['professions']); - } - - professions.add(newProfession); - - await professionsDocRef.set({ - 'professions': professions, - }, SetOptions(merge: true)); - - setState(() { - getProfessions().then((List element) => setState(() { - _professions = element; - filteredProfessions = element; - int newIndex = professions.indexOf(newProfession); - if (newIndex != -1) { - _scrollController.animateTo( - newIndex * 50.0, - duration: const Duration(milliseconds: 600), - curve: Curves.easeIn, - ); - } - isNewProfessionAdded = true; - Future.delayed(const Duration(seconds: 2), () { - setState(() { - isNewProfessionAdded = false; - }); - }); - })); - }); - } catch (e) { - print('Error al guardar la profesión: $e'); - } - } - - @override - Widget build(BuildContext context) { - if (filteredProfessions == null) { - return const Center( - child: CircularProgressIndicator( - valueColor: AlwaysStoppedAnimation(Color(0xFF2BA4EC)), - ), - ); - } - var professions = filteredProfessions!; - - return SafeArea( - child: Scaffold( - appBar: PopAppbar( - onPressed: () { - Navigator.pop(context); - }, - label: 'Seleccione su profesión', - ), - body: Column( - children: [ - GestureDetector( - onTap: () { - String newProfession = ""; - - showDialog( - context: context, - builder: (context) { - return AlertDialog( - title: const Text("Agregar una profesión"), - content: TextField( - controller: TextEditingController(), - onChanged: (value) { - newProfession = value; - }, - ), - actions: [ - ElevatedButton( - onPressed: () { - if (newProfession.isNotEmpty) { - saveProfession(newProfession); - Navigator.pop(context); - } - }, - child: const Text("Guardar"), - ), - ], - ); - }, - ); - }, - child: Container( - padding: const EdgeInsets.all(10), - child: const Text( - 'Si no vez tu profesion, presiona aquí', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w600, - color: Colors.blue, - ), - textAlign: TextAlign.center, - ), - ), - ), - Padding( - padding: const EdgeInsets.only(left: 10, right: 10, top: 0), - child: TextField( - controller: searchController, - decoration: const InputDecoration( - hintText: 'Busca tu profesión', - prefixIcon: Icon(Icons.assignment_ind_rounded), - ), - ), - ), - Expanded( - child: ListView.builder( - controller: _scrollController, - itemCount: professions.length, - itemBuilder: (BuildContext context, int index) { - return ListTile( - title: Text( - professions[index], - style: TextStyle( - fontSize: 18.0, - color: isNewProfessionAdded && - index == professions.length - 1 - ? Colors.white - : Colors.black, - ), - ), - tileColor: - isNewProfessionAdded && index == professions.length - 1 - ? Colors.blue - : null, - onTap: () { - updateProfession(professions[index]); - Navigator.pop(context, professions[index]); - }, - ); - }, - ), - ), - ], - ), - ), - ); - } -} diff --git a/lib/src/presentation/screens/professional.dart b/lib/src/presentation/screens/professional.dart deleted file mode 100644 index 6535f36..0000000 --- a/lib/src/presentation/screens/professional.dart +++ /dev/null @@ -1,445 +0,0 @@ -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:diacritic/diacritic.dart'; -import 'package:firebase_storage/firebase_storage.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:intl/intl.dart'; -import 'package:prosappco/src/authentication/authentication_repository.dart'; -import 'package:prosappco/src/components/photo_view.dart'; -import 'package:prosappco/src/components/pop_appbar.dart'; -import 'package:prosappco/src/models/professional_model.dart'; -import 'package:prosappco/src/models/setting_model.dart'; -import 'package:prosappco/src/models/user_model.dart'; -import 'package:prosappco/src/presentation/screens/calendar_pro.dart'; -import 'package:prosappco/src/presentation/screens/professional_info.dart'; -import 'package:prosappco/src/presentation/widgets/shared/loading_item_list.dart'; -import '../../models/scores_model.dart'; - -class ProfessionalScreen extends StatefulWidget { - final String profession; - const ProfessionalScreen({ - super.key, - required this.profession, - }); - - @override - State createState() => _ProfessionalScreenState(); -} - -var _photo = '.../images/perfil-2.png'; -final FirebaseStorage storage = FirebaseStorage.instance; - -final CollectionReference usersCollection = - FirebaseFirestore.instance.collection('users'); - -class _ProfessionalScreenState extends State { - UserModel? userme; - SettingModel? settings; - - @override - void initState() { - super.initState(); - - if (userme == null) { - UserModel.getUser(uid.toString()).then( - (UserModel s) => setState(() => userme = s), - ); - } - - if (settings == null) { - SettingModel.getSettings().then( - (SettingModel value) => setState(() { - settings = value; - - print('initState settings: $settings'); - }), - ); - } - searchController.addListener(() { - setState(() { - if (_professionals != null) { - if (searchController.text.isEmpty) { - filteredProfessionals = _professionals! - .where((professional) => professional.id != uid) - .toList(); - } else { - filteredProfessionals = _professionals! - .where((professional) => - removeDiacritics(professional.name).toLowerCase().contains( - removeDiacritics( - searchController.text.toLowerCase())) && - professional.id != uid) - .toList(); - } - } - }); - }); - - if (_professionals == null) { - getProfessionals().then((List element) => setState(() { - _professionals = element; - filteredProfessionals = element; - })); - } - } - - var photo = '.../images/perfil-2.png'; - - String formatCurrency(int number) { - final formatter = - NumberFormat.currency(locale: 'es_CO', decimalDigits: 0, symbol: ''); - return '\$${formatter.format(number)}'; - } - - Future _showChoiceDialog(BuildContext context) async { - String? selectedOption = await showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - content: SingleChildScrollView( - child: ListBody( - children: [ - GestureDetector( - child: const Text( - textAlign: TextAlign.center, - "A domicilio", - style: TextStyle(color: Color(0xFF2BA4EC)), - ), - onTap: () { - Navigator.of(context).pop("domicilio"); - }, - ), - const Divider(color: Colors.black54), - GestureDetector( - child: const Text( - textAlign: TextAlign.center, - "En sitio", - style: TextStyle(color: Color(0xFF2BA4EC)), - ), - onTap: () { - Navigator.of(context).pop("sitio"); - }, - ), - ], - ), - ), - ); - }, - ); - return selectedOption; - } - - List? filteredProfessionals; - - TextEditingController searchController = TextEditingController(); - - final uid = AuthenticationRepository.instance.getCurrentUserUid(); - List? _professionals; - - Future> getProfessionals() async { - List professionals = []; - - try { - QuerySnapshot users = await usersCollection.get(); - for (DocumentSnapshot user in users.docs) { - Map data = user.data() as Map; - - _photo = await AuthenticationRepository.instance.getPhoto(user.id); - - if (data['estado'] == 'activo') { - if (user.id != uid) { - List especializaciones; - - especializaciones = (data['especializaciones'] as List) - .map((e) => e.toString()) - .toList(); - - if (settings?.domicilios == false) { - if (data['ubicacion'] == 'ambos' || - data['ubicacion'] == 'sitio') { - if (widget.profession == data['profesion'] || - (widget.profession == '' && - userme?.city == data['city'] && - data['ubicacion'] != null)) { - Professional professional = Professional( - id: user.id, - name: data['name'], - professionName: data['profesion'], - cityName: data['city'], - professionalRef: storage.ref().child(_photo), - professionalEspecializado: especializaciones, - ubicacion: data['ubicacion'] ?? '', - realAddress: data['address'] ?? '', - latitude: data['latitude'] ?? 0, - longitude: data['longitude'] ?? 0, - scores: await ScoresModel.scoreTo(user.id, true, true), - tarifa: data['tarifas'] ?? 0, - token: data['token'] ?? '', - ); - professionals.add(professional); - } - } - } else { - if (widget.profession == data['profesion'] || - (widget.profession == '' && - userme?.city == data['city'] && - data['ubicacion'] != null)) { - Professional professional = Professional( - id: user.id, - name: data['name'], - professionName: data['profesion'], - cityName: data['city'], - professionalRef: storage.ref().child(_photo), - professionalEspecializado: especializaciones, - ubicacion: data['ubicacion'] ?? '', - realAddress: data['address'] ?? '', - latitude: data['latitude'] ?? 0, - longitude: data['longitude'] ?? 0, - scores: await ScoresModel.scoreTo(user.id, true, true), - tarifa: data['tarifas'] ?? 0, - token: data['token'] ?? '', - ); - professionals.add(professional); - } - } - } - } - } - } catch (e) { - print('Error al obtener profesionales: $e'); - } - - return professionals; - } - - @override - Widget build(BuildContext context) { - if (filteredProfessionals == null) { - return SafeArea( - child: Scaffold( - appBar: PopAppbar( - onPressed: () { - Navigator.pop(context); - }, - label: 'Seleccione un profesional', - ), - body: Column( - children: [ - const Padding( - padding: EdgeInsets.all(10), - child: TextField( - readOnly: true, - decoration: InputDecoration( - hintText: 'Escriba un nombre', - prefixIcon: Icon(Icons.assignment_ind_rounded), - ), - ), - ), - Expanded( - child: ListView.builder( - itemCount: 8, - itemBuilder: (BuildContext context, int index) { - return const LoadingItemList(useCircleAvatar: true); - }, - ), - ), - ], - ), - ), - ); - } - - if (_photo == null || _photo.isEmpty) { - _photo = '.../images/perfil-2.png'; - } - - var professionals = filteredProfessionals!; - - return SafeArea( - child: Scaffold( - appBar: PopAppbar( - onPressed: () { - Navigator.pop(context); - }, - label: 'Seleccione un profesional'), - body: Column( - children: [ - Padding( - padding: const EdgeInsets.all(10), - child: TextField( - controller: searchController, - decoration: const InputDecoration( - hintText: 'Escriba un nombre', - prefixIcon: Icon(Icons.assignment_ind_rounded), - ), - ), - ), - professionals.isEmpty - ? Expanded( - child: Padding( - padding: - const EdgeInsets.only(left: 20, right: 20, top: 50), - child: - Text('Aún no tenemos ningun(a) ${widget.profession}'), - )) - : Expanded( - child: ListView.builder( - itemCount: professionals.length, - itemBuilder: (BuildContext context, int index) { - return ListTile( - leading: GestureDetector( - onTap: () { - Navigator.of(context).push( - CupertinoPageRoute( - builder: (BuildContext context) { - return ProfessionalInfoScreen( - professional: professionals[index], - ); - }, - ), - ); - }, - child: ReferencePhoto( - ref: professionals[index].professionalRef, - sizeCircle: 50, - size: 50, - sizeIcon: 35, - ), - ), - trailing: GestureDetector( - child: const Icon(Icons.keyboard_arrow_right), - onTap: () { - Navigator.of(context).push( - CupertinoPageRoute( - builder: (BuildContext context) { - return ProfessionalInfoScreen( - professional: professionals[index], - ); - }, - ), - ); - }, - ), - title: RichText( - text: TextSpan( - style: const TextStyle( - fontSize: 15.0, - color: Colors.black, - ), - children: [ - TextSpan( - text: '${professionals[index].name}, ', - style: const TextStyle( - fontWeight: FontWeight.bold), - ), - TextSpan( - text: - "${professionals[index].professionName}, ${professionals[index].cityName}", - style: TextStyle(color: Colors.grey[600]), - ), - ], - ), - ), - subtitle: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - settings?.tarifas == true - ? professionals[index].tarifa == 0 - ? const SizedBox() - : Container( - padding: const EdgeInsets.symmetric( - horizontal: 10, vertical: 5), - decoration: BoxDecoration( - borderRadius: - BorderRadius.circular(20.0), - color: const Color(0xFFD6F4FF), - ), - child: Text( - formatCurrency( - professionals[index].tarifa ?? - 0), - style: TextStyle( - color: Colors.grey[850], - fontWeight: FontWeight.w600, - ), - ), - ) - : const SizedBox(), - professionals[index].ubicacion == 'ambos' && - settings?.domicilios == true - ? const Text( - 'Disponibilidad a domicilio y en sitio', - style: TextStyle( - color: Colors.blue, - ), - ) - : professionals[index].ubicacion == 'sitio' || - settings?.domicilios == false - ? const Text( - 'Disponibilidad en sitio', - style: TextStyle( - color: Colors.blue, - ), - ) - : const Text( - 'Disponibilidad a domicilio', - style: TextStyle( - color: Colors.blue, - ), - ), - ], - ), - onTap: () async { - if (professionals[index].ubicacion == 'ambos' && - settings?.domicilios == true) { - _showChoiceDialog(context) - .then((String? value) async { - if (value != null) { - var datos = await Navigator.of(context).push( - CupertinoPageRoute( - builder: (BuildContext context) { - return CalendarProScreen( - professional: professionals[index], - ); - }, - ), - ); - - if (datos != null) { - datos.add(value); - Navigator.pop(context, datos); - } - } - }); - } else if (professionals[index].ubicacion == - 'sitio' || - settings?.domicilios == false) { - var datos = await Navigator.of(context).push( - CupertinoPageRoute( - builder: (BuildContext context) { - return CalendarProScreen( - professional: professionals[index], - ); - }, - ), - ); - - if (datos != null) { - datos.add('sitio'); - Navigator.pop(context, datos); - } - } else { - Navigator.pop( - context, [professionals[index], 'domicilio']); - } - }, - ); - }, - ), - ), - ], - ), - ), - ); - } -} diff --git a/lib/src/presentation/screens/professional_direccion.dart b/lib/src/presentation/screens/professional_direccion.dart deleted file mode 100644 index 329a855..0000000 --- a/lib/src/presentation/screens/professional_direccion.dart +++ /dev/null @@ -1,255 +0,0 @@ -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; -import 'package:geocoding/geocoding.dart'; -import 'package:geolocator/geolocator.dart'; -import 'package:get/get.dart'; -import 'package:google_maps_flutter/google_maps_flutter.dart'; -import 'package:prosappco/src/authentication/authentication_repository.dart'; -import 'package:prosappco/src/components/pop_appbar.dart'; -import 'package:prosappco/src/components/primary_btn.dart'; - -class ProfessionalDireccionScreen extends StatefulWidget { - const ProfessionalDireccionScreen({super.key}); - - @override - State createState() => - _ProfessionalDireccionScreenState(); -} - -class _ProfessionalDireccionScreenState - extends State { - final TextEditingController _locationController = TextEditingController(); - final String _locationPosition = ''; - final uid = AuthenticationRepository.instance.getCurrentUserUid(); - - late GoogleMapController googleMapController; - - static const CameraPosition initialCameraPosition = CameraPosition( - target: LatLng(7.8939100, -72.5078200), - zoom: 14.4746, - ); - - Set markers = {}; - - Future _determinePosition() async { - bool serviceEnabled; - LocationPermission permission; - - serviceEnabled = await Geolocator.isLocationServiceEnabled(); - - if (!serviceEnabled) { - return Future.error('Location services are disabled'); - } - - permission = await Geolocator.checkPermission(); - - if (permission == LocationPermission.denied) { - permission = await Geolocator.requestPermission(); - - if (permission == LocationPermission.denied) { - return Future.error('Location permission denied'); - } - } - - if (permission == LocationPermission.deniedForever) { - return Future.error('Location permissions are permanently denied'); - } - - Position position = await Geolocator.getCurrentPosition(); - - return position; - } - - @override - void initState() { - super.initState(); - } - - late String lat; - late String long; - var coordinates; - - 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; - } - - Future updateAddress( - String addressName, double latitude, double longitude) async { - try { - await FirebaseFirestore.instance.collection('users').doc(uid).update({ - 'address': addressName, - 'latitude': latitude, - 'longitude': longitude, - }); - } catch (e) { - try { - await FirebaseFirestore.instance.collection('users').doc(uid).set({ - 'address': addressName, - 'latitude': latitude, - 'longitude': longitude, - }); - } catch (e) { - print('Error al agregar la ciudad: $e'); - } - - print('Error al actualizar la ciudad: $e'); - } - } - - @override - Widget build(BuildContext context) { - return SafeArea( - child: Scaffold( - appBar: PopAppbar( - onPressed: () { - Navigator.pop(context); - }, - label: 'Ubicación'), - backgroundColor: const Color(0xFFD6F4FF), - body: Stack( - children: [ - GoogleMap( - mapType: MapType.normal, - initialCameraPosition: initialCameraPosition, - markers: markers, - zoomControlsEnabled: false, - onMapCreated: (GoogleMapController controller) { - googleMapController = controller; - }, - onCameraIdle: () { - if (coordinates != null) { - getLocationName(coordinates.latitude, coordinates.longitude) - .then((locationName) { - setState(() { - _locationController.text = locationName; - }); - }); - } - }, - onCameraMove: (position) { - setState(() { - coordinates = position.target; - }); - }, - gestureRecognizers: >{ - Factory( - () => EagerGestureRecognizer(), - ), - }, - ), - 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: 35, right: 35, bottom: 15, top: 5), - child: TextFormField( - controller: _locationController, - decoration: const InputDecoration( - prefixIcon: Icon(Icons.near_me), - hintText: 'Dirección', - ), - ), - ), - ), - const Positioned( - bottom: 10, - right: 0, - left: 0, - top: 0, - child: Icon( - Icons.location_on, - size: 40, - color: Colors.red, - ), - ), - Positioned( - bottom: 130, - right: 20, - child: FloatingActionButton( - onPressed: () async { - try { - Position position = await _determinePosition(); - - googleMapController.animateCamera( - CameraUpdate.newCameraPosition( - CameraPosition( - target: LatLng( - position.latitude, - position.longitude, - ), - zoom: 17), - ), - ); - setState(() {}); - } catch (e) { - Get.snackbar( - 'Ubicación desactivada', - 'Por favor activa la ubicacion de tu telefono.', - snackPosition: SnackPosition.TOP, - ); - } - - // markers.clear(); - - // markers.add(Marker( - // markerId: const MarkerId('currentLocation'), - // position: - // LatLng(position.latitude, position.longitude))); - }, - elevation: 0, - child: const Icon( - Icons.gps_fixed, - size: 30, - ), - ), - ), - Positioned( - bottom: 30, - left: 0, - right: 0, - child: SizedBox( - width: MediaQuery.of(context).size.width, - child: Align( - alignment: Alignment.center, - child: PrimaryButtom( - onPressed: () { - updateAddress( - _locationController.text, - coordinates.latitude, - coordinates.longitude, - ); - Navigator.pop(context, _locationController.text); - }, - label: 'Guardar'), - ), - ), - ) - ], - ), - ), - ); - } -} diff --git a/lib/src/presentation/screens/professional_info.dart b/lib/src/presentation/screens/professional_info.dart deleted file mode 100644 index d33f87f..0000000 --- a/lib/src/presentation/screens/professional_info.dart +++ /dev/null @@ -1,361 +0,0 @@ -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_rating_bar/flutter_rating_bar.dart'; -import 'package:intl/intl.dart'; -import 'package:prosappco/src/components/photo_view.dart'; -import 'package:prosappco/src/components/pop_appbar.dart'; -import 'package:prosappco/src/models/professional_model.dart'; -import 'package:prosappco/src/models/setting_model.dart'; -import 'package:prosappco/src/presentation/screens/reputation.dart'; - -import '../../models/scores_model.dart'; - -class ProfessionalInfoScreen extends StatefulWidget { - Professional professional; - - ProfessionalInfoScreen({super.key, required this.professional}); - - @override - State createState() => _ProfessionalInfoScreenState(); -} - -class _ProfessionalInfoScreenState extends State { - SettingModel? settings; - - String formatCurrency(int number) { - final formatter = - NumberFormat.currency(locale: 'es_CO', decimalDigits: 0, symbol: ''); - return '\$${formatter.format(number)}'; - } - - @override - void initState() { - super.initState(); - if (settings == null) { - SettingModel.getSettings().then( - (SettingModel value) => setState(() { - settings = value; - }), - ); - } - - loadPaymentMethods(); - } - - Map paymentMethods = {}; - - String _formatPaymentMethods(Map paymentMethods) { - List enabledMethods = paymentMethods.entries - .where((entry) => entry.value) - .map((entry) => entry.key) - .toList(); - - return enabledMethods.join(', '); - } - - void loadPaymentMethods() { - FirebaseFirestore.instance - .collection('users') - .doc(widget.professional.id) - .get() - .then((doc) { - if (doc.exists) { - setState(() { - paymentMethods = Map.from(doc['paymentMethods'] ?? {}); - }); - } - }); - } - - @override - Widget build(BuildContext context) { - return SafeArea( - child: Scaffold( - appBar: PopAppbar( - onPressed: () { - Navigator.pop(context); - }, - label: widget.professional.name), - body: SingleChildScrollView( - child: Stack( - children: [ - Column( - children: [ - Container( - width: double.infinity, - height: 140, - decoration: BoxDecoration( - color: const Color(0xFFD6F4FF), - boxShadow: [ - BoxShadow( - color: Colors.grey.withOpacity(0.5), - spreadRadius: 1, - blurRadius: 7, - offset: const Offset(0, 2), - ), - ], - ), - ), - Padding( - padding: const EdgeInsets.only(left: 50), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox( - height: 5, - ), - Text( - widget.professional.name, - style: const TextStyle(fontWeight: FontWeight.w500), - ), - Text( - widget.professional.professionName, - style: const TextStyle(color: Color(0xFF1688C9)), - ), - widget.professional.getEspecializaciones().isEmpty - ? const SizedBox() - : Text( - 'Especializado/a en ${widget.professional.getEspecializaciones()}', - style: const TextStyle(color: Colors.black54), - ), - Text( - widget.professional.cityName, - ), - ], - ), - ), - widget.professional.ubicacion == 'domicilio' - ? Container( - margin: const EdgeInsets.only( - left: 40, right: 40, top: 20, bottom: 20), - padding: const EdgeInsets.symmetric( - horizontal: 20, vertical: 15), - decoration: BoxDecoration( - color: const Color(0xFFD6F4FF), - borderRadius: BorderRadius.circular(20), - boxShadow: [ - BoxShadow( - color: Colors.grey.withOpacity(0.5), - spreadRadius: 1, - blurRadius: 5, - offset: const Offset(1, 3), - ), - ], - ), - child: const Row( - children: [ - Icon( - Icons.error_outline, - size: 27, - color: Colors.black54, - ), - SizedBox(width: 15), - Text( - 'Este profesional solo atiende en\nsu dirección de trabajo.', - style: TextStyle( - color: Colors.black, fontSize: 13), - ), - ], - ), - ) - : const SizedBox(), - const SizedBox(height: 10), - settings?.tarifas == true - ? RichText( - text: TextSpan( - children: [ - const TextSpan( - text: 'Tarifa consulta ', - style: TextStyle( - color: Colors.black, - ), - ), - TextSpan( - text: formatCurrency( - widget.professional.tarifa ?? 0), - style: const TextStyle( - color: Colors.black, - fontSize: 16, - fontWeight: FontWeight.w600, - ), - ), - ], - ), - ) - : const SizedBox(), - const SizedBox(height: 5), - paymentMethods.containsValue(true) - ? Container( - width: MediaQuery.of(context).size.width * 0.8, - padding: const EdgeInsets.symmetric( - horizontal: 16, vertical: 8), - decoration: BoxDecoration( - color: Colors.blue.withOpacity(0.1), - borderRadius: BorderRadius.circular(10), - ), - child: RichText( - text: TextSpan( - style: const TextStyle( - fontSize: 12, - color: Colors.blue, - ), - children: [ - const TextSpan( - text: 'Métodos de pago recibidos: ', - style: - TextStyle(fontWeight: FontWeight.normal), - ), - TextSpan( - text: _formatPaymentMethods(paymentMethods), - style: const TextStyle( - fontWeight: FontWeight.bold), - ), - ], - ), - )) - : const SizedBox(), - const SizedBox(height: 5), - const Text( - 'Se unió el 02 de abril del 2023', - style: TextStyle(fontSize: 12), - ), - const SizedBox(height: 15), - Container( - decoration: BoxDecoration( - color: const Color(0xFFD6F4FF), - boxShadow: [ - BoxShadow( - color: Colors.grey.withOpacity(0.2), - spreadRadius: 3, - blurRadius: 5, - offset: const Offset(0, 3), - ), - ], - ), - child: ListTile( - onTap: () { - Navigator.of(context).push( - CupertinoPageRoute( - builder: (BuildContext context) { - return const ReputationScreen(); - }, - ), - ); - }, - trailing: const Icon(Icons.keyboard_arrow_right, - color: Colors.black), - title: const Text( - 'Reputación', - style: TextStyle(color: Colors.black), - ), - subtitle: Row( - children: [ - RatingBar.builder( - initialRating: widget.professional.scores.average, - minRating: 1, - direction: Axis.horizontal, - allowHalfRating: true, - itemCount: 5, - itemSize: 25, - maxRating: 5, - itemPadding: - const EdgeInsets.symmetric(horizontal: 0), - itemBuilder: (context, _) => const Icon( - Icons.star, - color: Color(0xFF2BA4EC), - ), - onRatingUpdate: (rating) {}, - ignoreGestures: true, - ), - const SizedBox(width: 5), - Text( - '(${widget.professional.scores.total.toString()}) ${widget.professional.scores.average.toStringAsFixed(1)}'), - ], - ), - ), - ), - ..._scoresList(widget.professional.scores.details), - const SizedBox( - height: 10, - ), - ], - ), - Container( - padding: const EdgeInsets.only( - top: 110, - left: 10, - ), - child: ReferencePhoto( - ref: widget.professional.professionalRef, - size: 100, - sizeCircle: 100, - sizeIcon: 50, - ), - ) - ], - ), - ), - ), - ); - } - - List _scoresList(List list) { - return list.map((e) => _scoreItem(e)).toList(); - } - - Widget _scoreItem(ScoreDetailModel scoreDetails) { - return ListTile( - onTap: () {}, - leading: ReferencePhoto( - ref: scoreDetails.avatar, - size: 50, - sizeCircle: 50, - sizeIcon: 35, - ), - title: Row( - children: [ - RatingBar.builder( - initialRating: scoreDetails.score, - minRating: 1, - direction: Axis.horizontal, - allowHalfRating: true, - itemCount: 5, - itemSize: 22, - maxRating: 5, - itemPadding: const EdgeInsets.symmetric(horizontal: 0), - itemBuilder: (context, _) => const Icon( - Icons.star, - color: Color(0xFF2BA4EC), - ), - onRatingUpdate: (rating) {}, - ignoreGestures: true, - ), - Text( - ' (${scoreDetails.score})', - style: const TextStyle(color: Colors.black54, fontSize: 13), - ) - ], - ), - subtitle: Row( - children: [ - Expanded( - child: Text.rich( - TextSpan( - children: [ - TextSpan( - text: '${scoreDetails.name}, ', - style: const TextStyle(fontSize: 15, color: Colors.black), - ), - TextSpan( - text: '"${scoreDetails.comment}"', - style: const TextStyle(fontSize: 15, color: Colors.grey), - ), - ], - ), - ), - ), - ], - )); - } -} diff --git a/lib/src/presentation/screens/professional_profile.dart b/lib/src/presentation/screens/professional_profile.dart deleted file mode 100644 index db6397b..0000000 --- a/lib/src/presentation/screens/professional_profile.dart +++ /dev/null @@ -1,857 +0,0 @@ -import 'dart:io'; -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:firebase_storage/firebase_storage.dart'; -import 'package:flutter/material.dart'; -import 'package:get/get.dart'; -import 'package:intl/intl.dart'; -import 'package:prosappco/src/authentication/authentication_repository.dart'; -import 'package:prosappco/src/components/photo_view.dart'; -import 'package:prosappco/src/components/pop_appbar.dart'; -import 'package:prosappco/src/controllers/info_%20professional.dart'; -import 'package:prosappco/src/presentation/widgets/shared/warning_snackbar.dart'; -import 'package:prosappco/src/services/select_image_profile.dart'; -import 'package:file_picker/file_picker.dart'; - -class ProfessionalProfileScreen extends StatefulWidget { - const ProfessionalProfileScreen({super.key}); - - @override - State createState() => - ProfessionalProfileScreenState(); -} - -class ProfessionalProfileScreenState extends State { - File? imagen_to_upload; - File? image_cedula; - File? image_certificado; - - final FirebaseStorage storage = FirebaseStorage.instance; - - final uid = AuthenticationRepository.instance.getCurrentUserUid(); - final _formKey = GlobalKey(); - - final controller = Get.put(InforProfessionalController()); - final _cedulaController = TextEditingController(); - final _especializacionController = TextEditingController(); - List images_especializacion = []; - var _profession = '...'; - var photoTemp = ''; - var photoCedulaTemp = ''; - var photoCertificadoTemp = ''; - var _photo = '...'; - - @override - void initState() { - super.initState(); - if (_photo == '...') { - AuthenticationRepository.instance - .getPhoto(uid.toString()) - .then((String s) => setState(() { - _photo = s; - })); - } - - if (_profession == '...') { - AuthenticationRepository.instance - .getProfession(uid.toString()) - .then((String s) => setState(() { - _profession = s; - })); - } - } - - Future getPdf() async { - FilePickerResult? result = await FilePicker.platform.pickFiles( - type: FileType.custom, - allowedExtensions: ['pdf'], - ); - - if (result != null) { - File file = File(result.files.single.path!); - return file; - } else { - return null; - } - } - - Future _showChoiceDialog(BuildContext context) async { - return showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - content: SingleChildScrollView( - child: ListBody( - children: [ - GestureDetector( - child: const Text( - textAlign: TextAlign.center, - "Tomar foto", - style: TextStyle(color: Color(0xFF2BA4EC)), - ), - onTap: () async { - final imagen = await getImage(1); - setState(() { - imagen_to_upload = File(imagen[0]!.path); - }); - Navigator.of(context).pop(); - }, - ), - const Divider(color: Colors.black54), - GestureDetector( - child: const Text( - textAlign: TextAlign.center, - "Abrir Galería", - style: TextStyle(color: Color(0xFF2BA4EC)), - ), - onTap: () async { - final imagen = await getImage(2); - setState(() { - imagen_to_upload = File(imagen[0]!.path); - }); - Navigator.of(context).pop(); - }, - ), - ], - ), - ), - ); - }, - ); - } - - Future _showChoiceDialogCedula(BuildContext context) async { - return showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - content: SingleChildScrollView( - child: ListBody( - children: [ - GestureDetector( - child: const Text( - textAlign: TextAlign.center, - "Abrir Galería", - style: TextStyle(color: Color(0xFF2BA4EC)), - ), - onTap: () async { - final imagen = await getPdf(); - setState(() { - image_cedula = File(imagen!.path); - }); - Navigator.of(context).pop(); - }, - ), - ], - ), - ), - ); - }, - ); - } - - Future _showChoiceDialogCertificado(BuildContext context) async { - return showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - content: SingleChildScrollView( - child: ListBody( - children: [ - GestureDetector( - child: const Text( - textAlign: TextAlign.center, - "Abrir Galería", - style: TextStyle(color: Color(0xFF2BA4EC)), - ), - onTap: () async { - final imagen = await getPdf(); - setState(() { - image_certificado = File(imagen!.path); - }); - Navigator.of(context).pop(); - }, - ), - ], - ), - ), - ); - }, - ); - } - - Future uploadCedula(File image) async { - final now = DateTime.now(); - final formattedDate = DateFormat('HHmmssddMMyyyy').format(now); - final milliseconds = (now.microsecondsSinceEpoch / 1000).round(); - final random = 'c$formattedDate$milliseconds'; - - Reference ref = - storage.ref().child('users').child(uid!).child('cedula').child(random); - - final UploadTask uploadTask = ref.putFile(image, metadata); - - final TaskSnapshot snapshot = await uploadTask.whenComplete(() => true); - - photoCedulaTemp = ref.fullPath; - - if (snapshot.state == TaskState.success) { - updateImageCedula(photoCedulaTemp); - - return true; - } else { - return false; - } - } - - Future updateImageCedula(image) async { - try { - final userRef = FirebaseFirestore.instance.collection('users').doc(uid); - final userSnapshot = await userRef.get(); - - if (userSnapshot.exists) { - await userRef.update({'imgCedula': image}); - } else { - await userRef.set({'imgCedula': image}); - } - } catch (e) { - print('Error al agregar o actualizar la imagen de cédula: $e'); - } - } - - final metadata = SettableMetadata( - contentType: 'application/pdf', - ); - - Future uploadCertificado(File image) async { - final now = DateTime.now(); - final formattedDate = DateFormat('HHmmssddMMyyyy').format(now); - final milliseconds = (now.microsecondsSinceEpoch / 1000).round(); - final random = 'f$formattedDate$milliseconds'; - - Reference ref = storage - .ref() - .child('users') - .child(uid!) - .child('certificado_profesional') - .child(random); - - final UploadTask uploadTask = ref.putFile(image, metadata); - - final TaskSnapshot snapshot = await uploadTask.whenComplete(() => true); - - photoCertificadoTemp = ref.fullPath; - - if (snapshot.state == TaskState.success) { - updateImageCertificado(photoCertificadoTemp); - - return true; - } else { - return false; - } - } - - Future updateImageCertificado(image) async { - try { - final userRef = FirebaseFirestore.instance.collection('users').doc(uid); - final userSnapshot = await userRef.get(); - - if (userSnapshot.exists) { - await userRef.update({'imgCertificado': image}); - } else { - await userRef.set({'imgCertificado': image}); - } - } catch (e) { - print('Error al agregar o actualizar la imagen de certificado: $e'); - } - } - - Future updateImage(image) async { - try { - await FirebaseFirestore.instance - .collection('users') - .doc(uid) - .update({'photo': image}); - } catch (e) { - try { - await FirebaseFirestore.instance - .collection('users') - .doc(uid) - .set({'photo': image}); - } catch (e) { - print('Error al agregar la imagen de perfil: $e'); - } - - print('Error al actualizar la imagen de perfil: $e'); - } - } - - Future uploadImage(File image) async { - try { - final String namefile = image.path.split('/').last; - - Reference ref = storage - .ref() - .child('users') - .child(uid!) - .child('profile') - .child(namefile); - - final UploadTask uploadTask = ref.putFile(image); - - final TaskSnapshot snapshot = await uploadTask; - - if (snapshot.state == TaskState.success) { - // Obtén la URL de descarga de la imagen y actualiza en Firestore - String downloadURL = await ref.getDownloadURL(); - await updateImage(downloadURL); - - return true; - } else { - return false; - } - } catch (e) { - print('Error al cargar la imagen: $e'); - return false; - } - } - - Future _showChoiceDialogEspecializaciones(BuildContext context) async { - return showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - content: SingleChildScrollView( - child: ListBody( - children: [ - GestureDetector( - child: const Text( - textAlign: TextAlign.center, - "Abrir Galería", - style: TextStyle(color: Color(0xFF2BA4EC)), - ), - onTap: () async { - final List? images = await getPdfs(); - if (images != null) { - setState(() { - images_especializacion = images; - }); - } - Navigator.of(context).pop(); - }, - ), - ], - ), - ), - ); - }, - ); - } - - Future?> getPdfs() async { - FilePickerResult? result = await FilePicker.platform.pickFiles( - type: FileType.custom, - allowedExtensions: ['pdf'], - allowMultiple: true, - ); - - if (result != null) { - List files = result.files.map((file) => File(file.path!)).toList(); - return files; - } else { - return null; - } - } - - Future> uploadEspecializaciones(List images) async { - List photoPaths = []; - - for (File image in images) { - Reference ref = storage - .ref() - .child('users') - .child(uid!) - .child('especializaciones') - .child('e${DateTime.now().millisecondsSinceEpoch}.pdf'); - - final UploadTask uploadTask = ref.putFile(image, metadata); - - final TaskSnapshot snapshot = await uploadTask.whenComplete(() => true); - - if (snapshot.state == TaskState.success) { - photoPaths.add(ref.fullPath); - } else { - updateImagesEspecializaciones(photoPaths); - return []; - } - } - - updateImagesEspecializaciones(photoPaths); - return photoPaths; - } - - Future updateImagesEspecializaciones(List photoPaths) async { - try { - final userRef = FirebaseFirestore.instance.collection('users').doc(uid); - final userSnapshot = await userRef.get(); - - if (userSnapshot.exists) { - await userRef.update({'imgEspecializaciones': photoPaths}); - } else { - await userRef.set({'imgEspecializaciones': photoPaths}); - } - } catch (e) { - print( - 'Error al agregar o actualizar las imágenes de especializaciones: $e'); - } - } - - void _showCustomSnackBar(BuildContext context, String message) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Container( - height: 50, - child: Center( - child: Text( - message, - style: TextStyle(fontSize: 18), - ), - ), - ), - duration: Duration(seconds: 3), - backgroundColor: Colors.red, // Personaliza el color de fondo - behavior: SnackBarBehavior.floating, - ), - ); - } - - Future sendInfo() async { - final String cedula = _cedulaController.text.trim(); - final String especializacion = _especializacionController.text.trim(); - final List especializaciones = - especializacion.split(',').map((e) => e.trim()).toList(); - - if (cedula.isEmpty) { - WarningSnackbar.show( - title: 'Te faltan campos!!', - message: 'Por favor, ingresa tu cedula', - ); - return; - } - - if (image_cedula == null) { - WarningSnackbar.show( - title: 'Te faltan archivos!!', - message: 'Por favor, adjunta el documento PDF de tu cedula', - ); - return; - } - - if (_profession.isEmpty) { - WarningSnackbar.show( - title: 'Te falta elegir una profesión!!', - message: - 'Por favor, elige tu profesión antes de enviar la información.', - ); - return; - } - - if (image_certificado == null) { - WarningSnackbar.show( - title: 'Te faltan archivos!!', - message: 'Por favor, adjunta el documento PDF de tu certificado', - ); - return; - } - if (imagen_to_upload == null && _photo == '...') { - WarningSnackbar.show( - title: 'Sube una foto de perfil', - message: 'Para continuar debes subir una imagen de perfil', - ); - return; - } else { - // Actualiza la imagen de perfil si hay cambios - updateImage(photoTemp); - } - - // Actualiza los datos del usuario en Firestore - await FirebaseFirestore.instance.collection('users').doc(uid).update({ - 'cedula': cedula, - 'estado': 'revision', - 'especializaciones': especializaciones - }); - - // Sube las imágenes al storage de Firebase - uploadCedula(image_cedula!); - uploadCertificado(image_certificado!); - uploadEspecializaciones(images_especializacion); - - Navigator.pushReplacementNamed(context, '/solicitudEnviada'); - } - - void showSnackBar(String message) { - ScaffoldMessenger.of(context) - .showSnackBar(SnackBar(content: Text(message))); - } - - Future downloadImage(Reference ref) async { - try { - if (_photo == '...' || _photo.isEmpty) { - return GestureDetector( - onTap: () { - _showChoiceDialog(context); - }, - child: Container( - margin: const EdgeInsets.symmetric(vertical: 50), - width: 100, - height: 100, - decoration: BoxDecoration( - color: const Color(0xFF2BA4EC), - borderRadius: BorderRadius.circular(50), - ), - child: const Icon( - Icons.person, - color: Colors.white, - size: 90, - ), - ), - ); - } else { - final imageData = await ref.getData(); - if (imageData != null) { - // final widgetImage = Image.memory(imageData); - final widgetImage = GestureDetector( - onTap: () { - _showChoiceDialog(context); - }, - child: Container( - margin: const EdgeInsets.symmetric(vertical: 50), - child: ClipOval( - child: Image.memory( - imageData, - width: 60, - height: 60, - fit: BoxFit.cover, - ), - ), - ), - ); - return widgetImage; - } else { - return GestureDetector( - onTap: () { - _showChoiceDialog(context); - }, - child: Container( - margin: const EdgeInsets.symmetric(vertical: 50), - width: 100, - height: 100, - decoration: BoxDecoration( - color: const Color(0xFF2BA4EC), - borderRadius: BorderRadius.circular(50), - ), - child: const Icon( - Icons.person, - color: Colors.white, - size: 90, - ), - ), - ); - } - } - } catch (e) { - print('$e'); - return GestureDetector( - onTap: () { - _showChoiceDialog(context); - }, - child: Container( - margin: const EdgeInsets.symmetric(vertical: 50), - width: 100, - height: 100, - decoration: BoxDecoration( - color: const Color(0xFF2BA4EC), - borderRadius: BorderRadius.circular(50), - ), - child: const Icon( - Icons.person, - color: Colors.white, - size: 90, - ), - ), - ); - } - } - - @override - Widget build(BuildContext context) { - String profession = _profession.toString(); - double _space = 10; - - return SafeArea( - child: Scaffold( - appBar: PopAppbar( - onPressed: () { - Navigator.pop(context); - }, - label: 'Perfil profesional', - ), - body: SingleChildScrollView( - reverse: true, - child: Center( - child: Column( - children: [ - GestureDetector( - onTap: () { - _showChoiceDialog(context); - }, - child: Container( - margin: const EdgeInsets.symmetric(vertical: 25), - child: (imagen_to_upload != null) - ? LocalPhoto( - file: imagen_to_upload!, - ) - : ReferencePhoto( - ref: storage.ref().child(_photo), - size: 100, - sizeCircle: 100, - ), - ), - ), - Container( - width: 300, - padding: const EdgeInsets.only(top: 0), - child: Form( - key: _formKey, - child: Column( - children: [ - TextFormField( - keyboardType: TextInputType.number, - controller: _cedulaController, - validator: (String? value) { - if (value == null || value.isEmpty) { - return 'Ingrese una cedula válida'; - } - return null; - }, - decoration: const InputDecoration( - prefixIcon: Icon(Icons.person_outline), - hintText: 'Cedula (Obligatorio)'), - ), - SizedBox(height: _space), - ElevatedButton( - onPressed: () { - _showChoiceDialogCedula(context); - }, - style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFFD6F4FF), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(50), - ), - elevation: 0, - minimumSize: const Size(250, 50), - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text( - 'Cedula', - style: TextStyle( - color: Color(0xFF2BA4EC), - fontSize: 17, - ), - ), - const SizedBox(width: 15), - Icon( - image_cedula != null - ? Icons.check - : Icons.file_upload_outlined, - color: const Color(0xFF2BA4EC), - size: 30, - ), - ], - ), - ), - SizedBox(height: _space), - TextFormField( - readOnly: true, - onTap: () async { - final String? profesion = (await Navigator.pushNamed( - context, '/profession')) as String?; - - if (profesion != null) { - setState(() { - _profession = profesion; - }); - } - }, - decoration: InputDecoration( - prefixIcon: - const Icon(Icons.assignment_ind_rounded), - suffixIcon: const Icon(Icons.arrow_drop_down), - hintStyle: profession == '' - ? const TextStyle() - : const TextStyle(color: Colors.black87), - hintText: profession == '' - ? 'Profesión (Obligatorio)' - : profession), - ), - SizedBox(height: _space), - ElevatedButton( - onPressed: () { - _showChoiceDialogCertificado(context); - }, - style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFFD6F4FF), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(50), - ), - elevation: 0, - minimumSize: const Size(250, 50), - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text( - 'Certificado profesional', - style: TextStyle( - color: Color(0xFF2BA4EC), - fontSize: 17, - ), - ), - const SizedBox(width: 15), - Icon( - image_certificado != null - ? Icons.check - : Icons.file_upload_outlined, - color: const Color(0xFF2BA4EC), - size: 30, - ), - ], - ), - ), - SizedBox(height: _space), - TextFormField( - controller: _especializacionController, - decoration: const InputDecoration( - prefixIcon: Icon(Icons.assignment_ind_rounded), - hintText: 'Especialización'), - ), - SizedBox(height: _space), - ElevatedButton( - onPressed: () async { - _showChoiceDialogEspecializaciones(context); - }, - style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFFD6F4FF), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(50), - ), - elevation: 0, - minimumSize: const Size(250, 50), - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text( - 'Especialización', - style: TextStyle( - color: Color(0xFF2BA4EC), - fontSize: 17, - ), - ), - const SizedBox(width: 15), - Icon( - images_especializacion.isEmpty - ? Icons.file_upload_outlined - : Icons.check, - color: const Color(0xFF2BA4EC), - size: 30, - ), - ], - ), - ), - ], - ), - ), - ), - Container( - margin: const EdgeInsets.only( - left: 40, right: 40, top: 40, bottom: 0), - padding: - const EdgeInsets.symmetric(horizontal: 20, vertical: 15), - decoration: BoxDecoration( - color: const Color(0xFFD6F4FF), - borderRadius: BorderRadius.circular(20), - boxShadow: [ - BoxShadow( - color: Colors.grey.withOpacity(0.5), - spreadRadius: 1, - blurRadius: 5, - offset: const Offset(1, 3), - ), - ], - ), - child: const Row( - children: [ - Icon( - Icons.error_outline, - size: 27, - color: Colors.black54, - ), - SizedBox(width: 15), - Expanded( - child: Text( - 'Si tienes más de una especialidad, por favor, adjunta un archivo con el diploma de respaldo para cada una de ellas y sepáralos por comas. ¡Gracias!', - style: TextStyle(color: Colors.black, fontSize: 14), - ), - ) - ], - ), - ), - Container( - alignment: Alignment.bottomCenter, - margin: const EdgeInsets.only( - top: 80, right: 20, left: 20, bottom: 30), - child: ElevatedButton( - onPressed: () { - if (_formKey.currentState!.validate()) { - sendInfo(); - } - }, - style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFF2BA4EC), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(50), - ), - elevation: 0, - minimumSize: const Size(250, 50), - maximumSize: const Size(350, 50), - ), - child: const Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - 'Enviar información', - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 17, - ), - ), - SizedBox(width: 15), - Icon( - Icons.send, - color: Colors.white, - size: 20, - ), - ], - ), - ), - ), - ], - ), - ), - ), - )); - } -} diff --git a/lib/src/presentation/screens/professional_profile_web.dart b/lib/src/presentation/screens/professional_profile_web.dart deleted file mode 100644 index 9f83e78..0000000 --- a/lib/src/presentation/screens/professional_profile_web.dart +++ /dev/null @@ -1,705 +0,0 @@ -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:file_picker/file_picker.dart'; -import 'package:firebase_auth/firebase_auth.dart'; -import 'package:firebase_storage/firebase_storage.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:get/get.dart'; -import 'package:image_picker/image_picker.dart'; -import 'package:prosappco/src/authentication/authentication_repository.dart'; -import 'package:prosappco/src/components/photo_view_web.dart'; -import 'package:prosappco/src/components/pop_appbar.dart'; -import 'package:intl/intl.dart'; -import 'dart:io'; - -class ProfessionalProfileWebScreen extends StatefulWidget { - const ProfessionalProfileWebScreen({super.key}); - - @override - State createState() => - _ProfessionalProfileWebScreenState(); -} - -class _ProfessionalProfileWebScreenState - extends State { - final uid = AuthenticationRepository.instance.getCurrentUserUid(); - final FirebaseStorage storage = FirebaseStorage.instance; - - // variables imagen - String selectedImage = ''; - String selectedCedulaImage = ''; - String selectedCertificadoImage = ''; - - List selectedEspecializacionImages = []; - List imagesEspecializacionsBytes = []; - - XFile? file; - Uint8List? selectedImagInBytes; - XFile? image_cedula; - Uint8List? imageCedulaBytes; - XFile? image_certificado; - Uint8List? imageCertificadoBytes; - XFile? image_especializaciones; - Uint8List? imageEspecializacionesBytes; - - // controllers - final _formKey = GlobalKey(); - final TextEditingController _cedulaController = TextEditingController(); - final TextEditingController _especializacionController = - TextEditingController(); - - // variables - bool _isLoading = false; - String _profession = '...'; - String photoTemp = ''; - String photoCedulaTemp = ''; - String photoCertificadoTemp = ''; - String _photo = '...'; - - @override - void initState() { - super.initState(); - if (_photo == '...') { - AuthenticationRepository.instance.getPhoto(uid.toString()).then( - (String s) => setState(() { - _photo = s; - }), - ); - } - - if (_profession == '...') { - AuthenticationRepository.instance.getProfession(uid.toString()).then( - (String s) => setState(() { - _profession = s; - }), - ); - } - } - - _selectFile(bool imageFrom) async { - FilePickerResult? fileResult = await FilePicker.platform.pickFiles(); - - if (fileResult != null) { - setState(() { - selectedImage = fileResult.files.first.name; - selectedImagInBytes = fileResult.files.first.bytes; - }); - } - } - - _selectFileCedula(bool imageFrom) async { - FilePickerResult? fileResult = await FilePicker.platform.pickFiles(); - - if (fileResult != null) { - setState(() { - selectedCedulaImage = fileResult.files.first.name; - imageCedulaBytes = fileResult.files.first.bytes; - }); - } - } - - _selectFileCertificado(bool imageFrom) async { - FilePickerResult? fileResult = await FilePicker.platform.pickFiles(); - - if (fileResult != null) { - setState(() { - selectedCertificadoImage = fileResult.files.first.name; - imageCertificadoBytes = fileResult.files.first.bytes; - }); - } - } - - _selectFilesEspecializaciones(bool imageFrom) async { - FilePickerResult? fileResult = - await FilePicker.platform.pickFiles(allowMultiple: true); - try { - if (fileResult != null) { - List selectedFileBytes = []; - - for (var file in fileResult.files) { - Uint8List? bytes = file.bytes; - if (bytes != null) { - selectedFileBytes.add(bytes); - } - } - - setState(() { - imagesEspecializacionsBytes = selectedFileBytes; - }); - } - } catch (e) { - print('$e'); - } - } - - Future sendInfo() async { - setState(() { - _isLoading = true; - }); - - final String cedula = _cedulaController.text.trim(); - final String especializacion = _especializacionController.text.trim(); - final List especializaciones = - especializacion.split(',').map((e) => e.trim()).toList(); - - if (cedula.isEmpty) { - showSnackBar('Cedula invalida', 'Ingrese una cedula válida'); - return; - } - - if (imageCedulaBytes == null) { - showSnackBar('Cedula', 'Ingrese una imagen de su cedula'); - return; - } - - if (imageCertificadoBytes == null) { - showSnackBar('Certificado', 'Ingrese una imagen de su certificado'); - return; - } - - // Actualiza los datos del usuario en Firestore - await FirebaseFirestore.instance.collection('users').doc(uid).update({ - 'cedula': cedula, - 'estado': 'revision', - 'especializaciones': especializaciones - }); - - // Sube las imágenes al storage de Firebase - await uploadCedula(); - await uploadCertificado(); - List uploadedPhotoPaths = - await uploadEspecializaciones(imagesEspecializacionsBytes); - - if (uploadedPhotoPaths.isNotEmpty) { - // Las imágenes se cargaron correctamente - // Actualiza las imágenes en Firestore - await updateFilesEspecializaciones(uploadedPhotoPaths); - - // Navega a la siguiente pantalla - Navigator.pushReplacementNamed(context, '/solicitudEnviada'); - } else { - showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - title: const Text('Error'), - content: const Text('Ocurrió un error al cargar las imágenes.'), - actions: [ - TextButton( - onPressed: () { - Navigator.pop(context); - }, - child: const Text('Aceptar'), - ), - ], - ); - }, - ); - } - - // Actualiza la imagen de perfil si hay cambios - if (selectedImagInBytes != null) { - await uploadFile(); - await updateImage(photoTemp); - } - setState(() { - _isLoading = false; - }); - - // Navega a la siguiente pantalla - Navigator.pushReplacementNamed(context, '/solicitudEnviada'); - } - - uploadFile() async { - try { - final now = DateTime.now(); - final formattedDate = DateFormat('HHmmssddMMyyyy').format(now); - final milliseconds = (now.microsecondsSinceEpoch / 1000).round(); - final random = '$formattedDate$milliseconds'; - - final Reference ref = FirebaseStorage.instance - .ref() - .child('users') - .child(uid!) - .child('profile') - .child(random); - - final metaData = SettableMetadata(contentType: 'image/jpeg'); - - final UploadTask uploadTask = ref.putData(selectedImagInBytes!, metaData); - - final TaskSnapshot snapshot = await uploadTask.whenComplete(() => true); - - photoTemp = ref.fullPath; - - if (snapshot.state == TaskState.success) { - return true; - } else { - return false; - } - } catch (e) { - print('web image error - $e'); - } - } - - uploadCedula() async { - try { - final now = DateTime.now(); - final formattedDate = DateFormat('HHmmssddMMyyyy').format(now); - final milliseconds = (now.microsecondsSinceEpoch / 1000).round(); - final random = 'c$formattedDate$milliseconds'; - - final Reference ref = FirebaseStorage.instance - .ref() - .child('users') - .child(uid!) - .child('cedula') - .child(random); - - final metaData = SettableMetadata(contentType: 'application/pdf'); - - final UploadTask uploadTask = ref.putData(imageCedulaBytes!, metaData); - - final TaskSnapshot snapshot = await uploadTask.whenComplete(() => true); - - photoCedulaTemp = ref.fullPath; - - if (snapshot.state == TaskState.success) { - updateImageCedula(photoCedulaTemp); - return true; - } else { - return false; - } - } catch (e) { - print('web image cedula error - $e'); - } - } - - uploadCertificado() async { - try { - final now = DateTime.now(); - final formattedDate = DateFormat('HHmmssddMMyyyy').format(now); - final milliseconds = (now.microsecondsSinceEpoch / 1000).round(); - final random = 'f$formattedDate$milliseconds'; - - final Reference ref = FirebaseStorage.instance - .ref() - .child('users') - .child(uid!) - .child('certificado_profesional') - .child(random); - - final metaData = SettableMetadata(contentType: 'application/pdf'); - - final UploadTask uploadTask = - ref.putData(imageCertificadoBytes!, metaData); - - final TaskSnapshot snapshot = await uploadTask.whenComplete(() => true); - - photoCertificadoTemp = ref.fullPath; - - if (snapshot.state == TaskState.success) { - updateImageCertificado(photoCertificadoTemp); - return true; - } else { - return false; - } - } catch (e) { - print('web image certificado error - $e'); - } - } - - Future> uploadEspecializaciones(List files) async { - List filePaths = []; - - for (Uint8List fileBytes in files) { - final Reference ref = FirebaseStorage.instance - .ref() - .child('users') - .child(uid!) - .child('especializaciones') - .child('e${DateTime.now().millisecondsSinceEpoch}.pdf'); - - final SettableMetadata metaData = - SettableMetadata(contentType: 'application/pdf'); - - final UploadTask uploadTask = ref.putData(fileBytes, metaData); - - final TaskSnapshot snapshot = await uploadTask.whenComplete(() => true); - - if (snapshot.state == TaskState.success) { - filePaths.add(ref.fullPath); - } else { - await updateFilesEspecializaciones(filePaths); - return []; - } - } - - await updateFilesEspecializaciones(filePaths); - return filePaths; - } - - Future updateImageCedula(image) async { - try { - final userRef = FirebaseFirestore.instance.collection('users').doc(uid); - final userSnapshot = await userRef.get(); - - if (userSnapshot.exists) { - await userRef.update({'imgCedula': image}); - } else { - await userRef.set({'imgCedula': image}); - } - } catch (e) { - print('Error al agregar o actualizar la imagen de cédula: $e'); - } - } - - Future updateImageCertificado(image) async { - try { - final userRef = FirebaseFirestore.instance.collection('users').doc(uid); - final userSnapshot = await userRef.get(); - - if (userSnapshot.exists) { - await userRef.update({'imgCertificado': image}); - } else { - await userRef.set({'imgCertificado': image}); - } - } catch (e) { - print('Error al agregar o actualizar la imagen de certificado: $e'); - } - } - - Future updateImage(image) async { - try { - await FirebaseFirestore.instance - .collection('users') - .doc(uid) - .update({'photo': image}); - } catch (e) { - try { - await FirebaseFirestore.instance - .collection('users') - .doc(uid) - .set({'photo': image}); - } catch (e) { - print('Error al agregar la imagen de perfil: $e'); - } - - print('Error al actualizar la imagen de perfil: $e'); - } - } - - Future updateFilesEspecializaciones(List filePaths) async { - try { - final userRef = FirebaseFirestore.instance.collection('users').doc(uid); - final userSnapshot = await userRef.get(); - - if (userSnapshot.exists) { - await userRef.update({'imgEspecializaciones': filePaths}); - } else { - await userRef.set({'imgEspecializaciones': filePaths}); - } - } catch (e) { - print('Error al actualizar los archivos de especializaciones: $e'); - } - } - - void showSnackBar(String title, String message) { - Get.snackbar( - title, - message, - snackPosition: SnackPosition.TOP, - ); - } - - @override - Widget build(BuildContext context) { - String profession = _profession.toString(); - double _space = 10; - - return Scaffold( - backgroundColor: const Color(0xFFD6F4FF), - appBar: PopAppbar( - onPressed: () { - Navigator.pop(context); - }, - label: 'Perfil profesional'), - body: SingleChildScrollView( - child: Center( - child: SizedBox( - width: 350, - child: Card( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10), - ), - color: Colors.white, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 20.0), - child: _isLoading - ? const Padding( - padding: EdgeInsets.symmetric(vertical: 30), - child: CircularProgressIndicator(), - ) - : Column( - children: [ - Container( - padding: const EdgeInsets.only(top: 20), - child: (selectedImagInBytes != null) - ? LocalPhotoWeb(file: selectedImagInBytes) - : ReferencePhotoWeb( - ref: storage.ref().child(_photo)), - ), - SizedBox( - width: 300, - child: Form( - key: _formKey, - child: Column( - children: [ - TextFormField( - keyboardType: TextInputType.number, - controller: _cedulaController, - inputFormatters: [ - FilteringTextInputFormatter - .digitsOnly // Solo permite caracteres numéricos - ], - validator: (String? value) { - if (value == null || value.isEmpty) { - return 'Ingrese una cedula válida'; - } - return null; - }, - decoration: const InputDecoration( - prefixIcon: Icon(Icons.person_outline), - hintText: 'Cedula (Obligatorio)', - ), - ), - SizedBox(height: _space), - ElevatedButton( - onPressed: () { - _selectFileCedula(true); - }, - style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFFD6F4FF), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(50), - ), - elevation: 0, - minimumSize: const Size(250, 50), - ), - child: Row( - mainAxisAlignment: - MainAxisAlignment.center, - children: [ - const Text( - 'Cedula', - style: TextStyle( - color: Color(0xFF2BA4EC), - fontSize: 17, - ), - ), - const SizedBox(width: 15), - Icon( - imageCedulaBytes != null - ? Icons.check - : Icons.file_upload_outlined, - color: const Color(0xFF2BA4EC), - size: 30, - ), - ], - ), - ), - SizedBox(height: _space), - TextFormField( - readOnly: true, - onTap: () async { - final String? profesion = - (await Navigator.pushNamed( - context, '/profession')) - as String?; - - if (profesion != null) { - setState(() { - _profession = profesion; - }); - } - }, - decoration: InputDecoration( - prefixIcon: const Icon( - Icons.assignment_ind_rounded), - suffixIcon: - const Icon(Icons.arrow_drop_down), - hintStyle: profession == '' - ? const TextStyle() - : const TextStyle( - color: Colors.black87), - hintText: profession == '' - ? 'Profesión (Obligatorio)' - : profession), - ), - SizedBox(height: _space), - ElevatedButton( - onPressed: () { - _selectFileCertificado(true); - }, - style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFFD6F4FF), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(50), - ), - elevation: 0, - minimumSize: const Size(250, 50), - ), - child: Row( - mainAxisAlignment: - MainAxisAlignment.center, - children: [ - const Text( - 'Certificado profesional', - style: TextStyle( - color: Color(0xFF2BA4EC), - fontSize: 17, - ), - ), - const SizedBox(width: 15), - Icon( - imageCertificadoBytes != null - ? Icons.check - : Icons.file_upload_outlined, - color: const Color(0xFF2BA4EC), - size: 30, - ), - ], - ), - ), - SizedBox(height: _space), - TextFormField( - controller: _especializacionController, - decoration: const InputDecoration( - prefixIcon: - Icon(Icons.assignment_ind_rounded), - hintText: 'Especialización'), - ), - SizedBox(height: _space), - ElevatedButton( - onPressed: () async { - _selectFilesEspecializaciones(true); - }, - style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFFD6F4FF), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(50), - ), - elevation: 0, - minimumSize: const Size(250, 50), - ), - child: Row( - mainAxisAlignment: - MainAxisAlignment.center, - children: [ - const Text( - 'Especialización', - style: TextStyle( - color: Color(0xFF2BA4EC), - fontSize: 17, - ), - ), - const SizedBox(width: 15), - Icon( - imagesEspecializacionsBytes.isEmpty - ? Icons.file_upload_outlined - : Icons.check, - color: const Color(0xFF2BA4EC), - size: 30, - ), - ], - ), - ), - ], - ), - ), - ), - Container( - margin: const EdgeInsets.only(top: 40), - padding: const EdgeInsets.symmetric( - horizontal: 20, vertical: 15), - decoration: BoxDecoration( - color: const Color(0xFFD6F4FF), - borderRadius: BorderRadius.circular(20), - boxShadow: [ - BoxShadow( - color: Colors.grey.withOpacity(0.5), - spreadRadius: 1, - blurRadius: 5, - offset: const Offset(1, 3), - ), - ], - ), - child: const Row( - children: [ - Icon( - Icons.error_outline, - size: 27, - color: Colors.black54, - ), - SizedBox(width: 15), - Expanded( - child: Text( - 'Si tienes más de una especialidad, por favor, adjunta un archivo con el diploma de respaldo para cada una de ellas y sepáralos por comas. ¡Gracias!', - style: TextStyle( - color: Colors.black, fontSize: 14), - ), - ), - ], - ), - ), - Container( - alignment: Alignment.bottomCenter, - margin: const EdgeInsets.only( - top: 30, right: 20, left: 20, bottom: 30), - child: ElevatedButton( - onPressed: () { - if (_formKey.currentState!.validate()) { - sendInfo(); - } - }, - style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFF2BA4EC), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(50), - ), - elevation: 0, - minimumSize: const Size(250, 50), - maximumSize: const Size(350, 50), - ), - child: const Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - 'Enviar información', - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 17, - ), - ), - SizedBox(width: 15), - Icon( - Icons.send, - color: Colors.white, - size: 20, - ), - ], - ), - ), - ), - ], - ), - ), - ), - ), - ), - ), - ); - } -} diff --git a/lib/src/presentation/screens/professional_revision.dart b/lib/src/presentation/screens/professional_revision.dart deleted file mode 100644 index 9bd2f11..0000000 --- a/lib/src/presentation/screens/professional_revision.dart +++ /dev/null @@ -1,95 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:prosappco/src/components/pop_appbar.dart'; - -class ProfessionalRevisionScreen extends StatefulWidget { - const ProfessionalRevisionScreen({super.key}); - - @override - State createState() => - _ProfessionalRevisionScreenState(); -} - -class _ProfessionalRevisionScreenState - extends State { - @override - Widget build(BuildContext context) { - return SafeArea( - child: Scaffold( - appBar: PopAppbar( - onPressed: () { - Navigator.pop(context); - }, - label: 'Perfil profesional', - ), - body: SingleChildScrollView( - child: Container( - color: Colors.white, - child: Padding( - padding: const EdgeInsets.only(top: 40), - child: Column( - children: [ - const Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - Icons.access_time, - color: Color(0xFF2BA4EC), - ), - SizedBox(width: 8), - Text('Información en revisión.', - style: TextStyle( - color: Color(0xFF2BA4EC), - fontSize: 17, - fontWeight: FontWeight.w500)), - ], - ), - const Image( - image: AssetImage('images/checklist.gif'), - width: 300, - ), - Container( - margin: const EdgeInsets.symmetric(horizontal: 40), - decoration: BoxDecoration( - color: const Color(0xFFD6F4FF), - borderRadius: BorderRadius.circular(30), - boxShadow: [ - BoxShadow( - color: Colors.grey.withOpacity(0.5), - spreadRadius: 2, - blurRadius: 5, - offset: - const Offset(0, 3), // changes position of shadow - ), - ], - ), - padding: - const EdgeInsets.symmetric(vertical: 15, horizontal: 25), - child: const Wrap( - alignment: WrapAlignment.start, // Centra el contenido - children: [ - SizedBox( - width: 350, - child: Row( - children: [ - Expanded( - child: Text( - 'Gracias por proporcionar tu información. Actualmente, estamos revisando tus datos y una vez aprobados, podrás acceder al perfil profesional sin problemas. Te notificaremos tan pronto como tu cuenta esté lista. ¡Gracias por tu paciencia!', - style: TextStyle( - fontSize: 14, - ), - ), - ), - ], - ), - ), - ], - ), - ), - ], - ), - ), - ), - ), - )); - } -} diff --git a/lib/src/presentation/screens/profile/profile.dart b/lib/src/presentation/screens/profile/profile.dart deleted file mode 100644 index 220142d..0000000 --- a/lib/src/presentation/screens/profile/profile.dart +++ /dev/null @@ -1,981 +0,0 @@ -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:firebase_auth/firebase_auth.dart'; -import 'package:firebase_storage/firebase_storage.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:get/get.dart'; -import 'dart:io'; -import 'package:intl/intl.dart'; -import 'package:prosappco/src/authentication/authentication_repository.dart'; -import 'package:prosappco/src/components/pop_appbar.dart'; -import 'package:prosappco/src/controllers/add_name_email_city.dart'; -import 'package:prosappco/src/presentation/widgets/profile/birth_date_picker.dart'; -import 'package:prosappco/src/presentation/screens/city.dart'; -import 'package:prosappco/src/presentation/screens/new_number.dart'; -import 'package:prosappco/src/presentation/screens/new_password.dart'; -import 'package:prosappco/src/presentation/widgets/shared/primary_checkbox.dart'; -import 'package:prosappco/src/presentation/widgets/shared/warning_snackbar.dart'; -import 'package:prosappco/src/providers/user_provider.dart'; -import 'package:prosappco/src/services/select_image_profile.dart'; -import 'package:prosappco/src/presentation/widgets/profile/gender_dropdown.dart'; -import 'package:prosappco/src/presentation/widgets/shared/primary_button.dart'; -import 'package:provider/provider.dart'; -import '../../../components/photo_view.dart'; -import 'package:universal_html/html.dart' as html; - -class ProfileScreen extends StatefulWidget { - const ProfileScreen({Key? key}) : super(key: key); - - @override - State createState() => _ProfileScreenState(); -} - -class _ProfileScreenState extends State { - File? imagen_to_upload; - final DateFormat formatter = DateFormat('dd/MM/yyyy'); - final uid = AuthenticationRepository.instance.getCurrentUserUid(); - bool _obscureText = true; - final _formKey = GlobalKey(); - final controller = Get.put(NameEmailCityController()); - final _phoneNumberController = TextEditingController(); - final _nameController = TextEditingController(); - final _emailController = TextEditingController(); - final _passwordController = TextEditingController(); - late final FirebaseAuth _auth; - final FirebaseStorage storage = FirebaseStorage.instance; - var photoTemp = ''; - var _ciudad = '...'; - var _photo = '..../images/perfil-2.png'; - String? _email = ''; - String gender = ''; - String genderDb = ''; - DateTime? birthDate; - String birthDateDb = ''; - - @override - void initState() { - super.initState(); - - _auth = FirebaseAuth.instance; - - final currentUser = _auth.currentUser; - - if (currentUser != null && currentUser.phoneNumber != null) { - _phoneNumberController.text = currentUser.phoneNumber!; - } - - if (currentUser != null && currentUser.displayName != null) { - _nameController.text = currentUser.displayName!; - } - if (currentUser != null && currentUser.email != null) { - _emailController.text = currentUser.email!; - } - - if (gender.isEmpty) { - AuthenticationRepository.instance.getGender(uid.toString()).then( - (String s) => setState(() { - genderDb = s; - }), - ); - } - - if (birthDate == null) { - AuthenticationRepository.instance.getBirthday(uid.toString()).then( - (String s) => setState(() { - if (s.isNotEmpty) { - birthDateDb = s; - } - }), - ); - } - - _email = currentUser?.email; - - if (_ciudad == '...') { - AuthenticationRepository.instance.getCity(uid.toString()).then( - (String s) => setState(() { - _ciudad = s; - }), - ); - } - - if (_photo == '...') { - AuthenticationRepository.instance.getPhoto(uid.toString()).then( - (String s) => setState(() { - _photo = s; - }), - ); - } - } - - Future updateImage(image) async { - try { - await FirebaseFirestore.instance - .collection('users') - .doc(uid) - .update({'photo': image}); - } catch (e) { - try { - await FirebaseFirestore.instance - .collection('users') - .doc(uid) - .set({'photo': image}); - } catch (e) { - print('Error al agregar la imagen de perfil: $e'); - } - - print('Error al actualizar la imagen de perfil: $e'); - } - } - - Future uploadImage(File image) async { - final now = DateTime.now(); - final formattedDate = DateFormat('HHmmssddMMyyyy').format(now); - final milliseconds = (now.microsecondsSinceEpoch / 1000).round(); - final random = '$formattedDate$milliseconds'; - - Reference ref = - storage.ref().child('users').child(uid!).child('profile').child(random); - - final UploadTask uploadTask = ref.putFile(image); - - final TaskSnapshot snapshot = await uploadTask.whenComplete(() => true); - - photoTemp = ref.fullPath; - - if (snapshot.state == TaskState.success) { - return true; - } else { - return false; - } - } - - Future downloadImage(Reference ref) async { - try { - if (_photo == '...' || _photo.isEmpty) { - return GestureDetector( - onTap: () { - _showChoiceDialog(context); - }, - child: Container( - margin: const EdgeInsets.symmetric(vertical: 50), - width: 100, - height: 100, - decoration: BoxDecoration( - color: const Color(0xFF2BA4EC), - borderRadius: BorderRadius.circular(50), - ), - child: const Icon( - Icons.person, - color: Colors.white, - size: 90, - ), - ), - ); - } else { - final imageData = await ref.getData(); - if (imageData != null) { - final widgetImage = GestureDetector( - onTap: () { - _showChoiceDialog(context); - }, - child: Container( - margin: const EdgeInsets.symmetric(vertical: 50), - child: ClipOval( - child: Image.memory( - imageData, - width: 60, - height: 60, - fit: BoxFit.cover, - ), - ), - ), - ); - return widgetImage; - } else { - return GestureDetector( - onTap: () { - _showChoiceDialog(context); - }, - child: Container( - margin: const EdgeInsets.symmetric(vertical: 50), - width: 100, - height: 100, - decoration: BoxDecoration( - color: const Color(0xFF2BA4EC), - borderRadius: BorderRadius.circular(50), - ), - child: const Icon( - Icons.person, - color: Colors.white, - size: 90, - ), - ), - ); - } - } - } catch (e) { - return GestureDetector( - onTap: () { - _showChoiceDialog(context); - }, - child: Container( - margin: const EdgeInsets.symmetric(vertical: 50), - width: 100, - height: 100, - decoration: BoxDecoration( - color: const Color(0xFF2BA4EC), - borderRadius: BorderRadius.circular(50), - ), - child: const Icon( - Icons.person, - color: Colors.white, - size: 90, - ), - ), - ); - } - } - - Future updateInfo() async { - final currentUser = _auth.currentUser; - final currentPhoneNumber = _auth.currentUser!.phoneNumber; - - String newName = _nameController.text.trim(); - String newEmail = _emailController.text.trim(); - String newPassword = _passwordController.text.trim(); - - if (gender.isNotEmpty) { - try { - await FirebaseFirestore.instance - .collection('users') - .doc(uid) - .set({'gender': gender}, SetOptions(merge: true)); - - genderDb = gender; - } catch (e) { - print('Error al actualizar el genero: $e'); - } - } - - if (birthDate != null) { - try { - await FirebaseFirestore.instance - .collection('users') - .doc(uid) - .set({'birth_date': birthDate.toString()}, SetOptions(merge: true)); - - birthDateDb = birthDate.toString(); - } catch (e) { - print('Error al actualizar la fecha de nacimiento: $e'); - } - } - - setState(() {}); - - if (newName.isEmpty) { - Get.snackbar( - 'Nombre Invalido', - 'Ingresa un nombre válido.', - snackPosition: SnackPosition.BOTTOM, - ); - return; - } - - if (enableLoginWithEmail) { - if (newEmail.isEmpty) { - Get.snackbar( - 'Correo Invalido', - 'Ingresa un email válido.', - snackPosition: SnackPosition.BOTTOM, - ); - return; - } - } - - if (currentUser?.displayName != newName) { - try { - await FirebaseAuth.instance.currentUser!.updateDisplayName(newName); - await FirebaseFirestore.instance - .collection('users') - .doc(uid) - .update({'name': newName, 'lowerName': newName.toLowerCase()}); - } catch (e) { - print('Error al actualizar el nombre: $e'); - } - } - - String? selectedCity = _ciudad; - - if (kIsWeb) { - try { - await FirebaseFirestore.instance - .collection('users') - .doc(uid) - .update({'city': selectedCity}); - } catch (e) { - print('Error al actualizar la ciudad: $e'); - } - } - - if (enableLoginWithEmail) { - if (currentUser?.email != newEmail) { - if (newPassword.isNotEmpty) { - bool updateEmailSuccess = - await updateEmailAndPassword(newEmail, newPassword); - - if (updateEmailSuccess) { - await FirebaseFirestore.instance - .collection('users') - .doc(uid) - .update({'email': newEmail}); - } else { - return; - } - } else { - Get.snackbar( - 'Contraseña Invalida', - 'Por favor ingresa una contraseña.', - snackPosition: SnackPosition.BOTTOM, - ); - } - } - } - - try { - await FirebaseFirestore.instance - .collection('users') - .doc(uid) - .update({'phoneNumber': currentPhoneNumber}); - } catch (e) { - print('$e'); - } - - try { - if (imagen_to_upload == null) { - } else { - final uploaded = await uploadImage(imagen_to_upload!); - updateImage(photoTemp); - } - } catch (e) { - print('Error al actualizar la imagen de perfil $e'); - } - - WarningSnackbar.show( - title: 'Informacion actualizada', - message: 'Tu informacion ha sido actualizada con exito.', - icon: const Icon( - Icons.check, - color: Colors.white, - ), - backgroundColor: Colors.green, - ); - } - - Future updateEmailAndPassword(String email, String password) async { - final User? user = FirebaseAuth.instance.currentUser; - if (user != null) { - try { - await user.updateEmail(email); - await user.updatePassword(password); - - return true; - } catch (e) { - WarningSnackbar.show( - title: 'Inicia sesión de nuevo', - message: 'Inicia la sesión de nuevo para guardar los cambios.', - ); - AuthenticationRepository.instance.logout(uid!); - } - } - return false; - } - - Future _updateEmailAndPassword( - String newEmail, String currentPassword) async { - final user = _auth.currentUser; - - if (user!.email! == newEmail) { - Get.snackbar( - 'No se puede actualizar', - 'El correo actual no puede ser actualizado.', - snackPosition: SnackPosition.BOTTOM, - ); - return; - } - - if (!newEmail.contains('@') || !newEmail.contains('.')) { - Get.snackbar( - 'No se puede actualizar', - 'Ingresa un correo electrónico valido.', - snackPosition: SnackPosition.BOTTOM, - ); - - return; - } - - final emailExistsQuery = await FirebaseFirestore.instance - .collection('users') - .where('email', isEqualTo: newEmail) - .get(); - - if (emailExistsQuery.docs.isNotEmpty) { - Get.snackbar( - 'No se puede actualizar', - 'El nuevo correo electrónico ya está en uso.', - snackPosition: SnackPosition.BOTTOM, - ); - return; - } - - try { - final credential = EmailAuthProvider.credential( - email: user.email!, password: currentPassword); - await user.reauthenticateWithCredential(credential); - - await user.updateEmail(newEmail); - - await FirebaseFirestore.instance - .collection('users') - .doc(uid) - .update({'email': newEmail}); - - setState(() { - _email = newEmail; - }); - - WarningSnackbar.show( - title: 'Actualizado exitosamente', - message: 'Correo electronico actualizado correctamente.', - icon: const Icon(Icons.check, color: Colors.white), - backgroundColor: Colors.green, - ); - - if (Navigator.canPop(context)) { - Navigator.of(context).pop(); - } - } catch (e) { - WarningSnackbar.show( - title: 'No se pudo actualizar el correo', - message: - 'Verifica tu contraseña actual y asegúrate de que el nuevo correo electrónico no se haya utilizado previamente.', - ); - } - } - - Future _showEmailUpdateDialog(BuildContext context) async { - TextEditingController emailController = TextEditingController(); - TextEditingController passwordController = TextEditingController(); - - showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - title: const Text('Actualizar Email'), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - TextField( - controller: emailController, - decoration: const InputDecoration(labelText: 'Nuevo Email'), - ), - TextField( - controller: passwordController, - decoration: - const InputDecoration(labelText: 'Contraseña Actual'), - obscureText: true, - ), - ], - ), - actions: [ - TextButton( - onPressed: () { - Navigator.of(context).pop(); - }, - child: const Text( - 'Cancelar', - style: TextStyle(color: Colors.grey), - ), - ), - TextButton( - onPressed: () { - String newEmail = emailController.text.trim(); - String currentPassword = passwordController.text.trim(); - if (newEmail.isNotEmpty && currentPassword.isNotEmpty) { - _updateEmailAndPassword(newEmail, currentPassword); - } - }, - child: const Text( - 'Guardar', - style: TextStyle( - color: Colors.blue, - fontWeight: FontWeight.w600, - ), - ), - ), - ], - ); - }, - ); - } - - - - bool enableLoginWithEmail = false; - - Future> _getCities() async { - List citys = []; - if (kIsWeb) { - try { - QuerySnapshot countries = await countriesCollection.get(); - for (DocumentSnapshot country in countries.docs) { - String countryName = country.id; - Map data = country.data() as Map; - Map> states = {}; - - for (var entry in data.entries) { - String key = entry.key; - Map cityData = - Map.from(entry.value); - states[key] = cityData; - } - - for (var state in states.entries) { - var citysState = state.value.entries.map((city) => City( - cityName: city.key, - coordsOfCity: city.value, - stateOfCity: state.key, - countryOfCity: countryName, - )); - - citys.addAll(citysState); - } - } - } catch (e) { - print('Error obteniendo las ciudades: $e'); - } - } - - return citys; - } - - Future _showChoiceDialog(BuildContext context) async { - return showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - content: SingleChildScrollView( - child: ListBody( - children: [ - GestureDetector( - child: const Text( - textAlign: TextAlign.center, - "Tomar foto", - style: TextStyle(color: Color(0xFF2BA4EC)), - ), - onTap: () async { - final imagen = await getImage(1); - setState(() { - imagen_to_upload = File(imagen[0]!.path); - }); - Navigator.of(context).pop(); - }, - ), - const Divider(color: Colors.black54), - GestureDetector( - child: const Text( - textAlign: TextAlign.center, - "Abrir Galería", - style: TextStyle(color: Color(0xFF2BA4EC)), - ), - onTap: () async { - final imagen = await getImage(2); - setState(() { - imagen_to_upload = File(imagen[0]!.path); - }); - Navigator.of(context).pop(); - }, - ), - ], - ), - ), - ); - }, - ); - } - - @override - Widget build(BuildContext context) { - String city = _ciudad.toString(); - - final userProvider = Provider.of(context); - - return Scaffold( - appBar: PopAppbar( - onPressed: () { - Navigator.pop(context); - }, - label: 'Perfil', - ), - body: SingleChildScrollView( - reverse: true, - child: Center( - child: Column( - children: [ - GestureDetector( - onTap: () { - _showChoiceDialog(context); - }, - child: Container( - margin: const EdgeInsets.symmetric(vertical: 20), - child: (imagen_to_upload != null) - ? LocalPhoto(file: imagen_to_upload!) - : ReferencePhoto(ref: storage.ref().child(_photo))), - ), - Container( - width: 300, - padding: const EdgeInsets.only(top: 0), - child: Form( - key: _formKey, - child: Column( - children: [ - TextFormField( - controller: _nameController, - maxLength: 50, - inputFormatters: [ - FilteringTextInputFormatter.deny(RegExp(r'\s{2,}')), - ], - validator: (value) { - if (value == null || value.trim().isEmpty) { - return 'Porfavor ingrese un nombre.'; - } - if (value.trim().length < 5) { - return 'Debe tener al menos 5 caracteres.'; - } - return null; - }, - decoration: const InputDecoration( - prefixIcon: Icon(Icons.person_outline), - hintText: 'Nombre (Obligatorio)', - ), - ), - const SizedBox(), - kIsWeb - ? FutureBuilder>( - future: _getCities(), - builder: (context, snapshot) { - if (snapshot.connectionState == - ConnectionState.waiting) { - return const Center( - child: CircularProgressIndicator(), - ); - } else if (snapshot.hasError) { - return const Center( - child: - Text('Error al obtener las ciudades'), - ); - } else { - List filteredCities = snapshot.data!; - - return DropdownButtonFormField( - value: _ciudad, - onChanged: (String? newValue) { - setState(() { - _ciudad = newValue!; - }); - }, - items: filteredCities.map((City city) { - return DropdownMenuItem( - value: city.cityName, - child: Text(city.cityName ?? ''), - ); - }).toList(), - decoration: InputDecoration( - prefixIcon: const Icon(Icons.near_me), - hintText: _ciudad, - ), - ); - } - }, - ) - : TextFormField( - readOnly: true, - onTap: () async { - final String? ciudad = await Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return const CityScreen(); - }, - ), - ) as String?; - - if (ciudad != null) { - setState(() { - _ciudad = ciudad; - }); - } - }, - decoration: InputDecoration( - prefixIcon: const Icon(Icons.near_me), - suffixIcon: const Icon(Icons.arrow_drop_down), - hintStyle: city == '' - ? const TextStyle() - : const TextStyle(color: Colors.black87), - hintText: city == '' ? 'Ciudad' : city, - ), - ), - const SizedBox(height: 20.0), - TextFormField( - controller: _phoneNumberController, - readOnly: true, - onTap: () { - Navigator.of(context).push( - CupertinoPageRoute( - builder: (BuildContext context) { - return const NewNumberScreen(); - }, - ), - ); - }, - decoration: const InputDecoration( - prefixIcon: Icon(Icons.phone_android), - suffixIcon: Icon(Icons.edit_outlined), - hintText: '+57', - ), - ), - genderDb == '' - ? const SizedBox(height: 20.0) - : const SizedBox(), - genderDb == '' - ? GenderDropdown( - onChanged: (selectedGender) { - setState(() { - gender = selectedGender; - }); - }, - ) - : const SizedBox(), - genderDb == '' - ? const SizedBox(height: 20.0) - : const SizedBox(), - birthDateDb == '' - ? BirthDatePicker( - onDateSelected: (birthDay) { - setState(() { - birthDate = birthDay; - }); - }, - controller: TextEditingController( - text: birthDate == null - ? '' - : formatter.format(birthDate!), - ), - ) - : const SizedBox(), - _email != null && _email != '' - ? const SizedBox(height: 20.0) - : const SizedBox(), - _email != null && _email != '' - ? TextFormField( - onTap: () { - _showEmailUpdateDialog(context); - }, - readOnly: true, - controller: _emailController, - decoration: const InputDecoration( - prefixIcon: Icon(Icons.email_outlined), - hintText: 'Email (Obligatorio)', - ), - ) - : const SizedBox(), - const SizedBox(height: 20), - _email == null || _email == '' - ? PrimaryCheckbox( - text: - 'Habilitar inicio de sesión con correo (Opcional)', - initialValue: enableLoginWithEmail, - onChanged: (value) { - setState(() { - enableLoginWithEmail = value; - }); - }, - ) - : const SizedBox(), - const SizedBox(height: 15), - enableLoginWithEmail - ? Container( - decoration: BoxDecoration( - border: Border.all( - color: Colors.blue, - width: 0.5, - ), - borderRadius: BorderRadius.circular(10), - ), - padding: const EdgeInsets.all(10), - child: Column( - children: [ - TextFormField( - controller: _emailController, - validator: (String? value) { - if (enableLoginWithEmail) { - if (value == null || value.isEmpty) { - return 'Por favor ingrese un email'; - } - final RegExp emailRegExp = RegExp( - r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$'); - if (!emailRegExp.hasMatch(value)) { - return 'Por favor ingrese un email válido'; - } - return null; - } else { - return null; - } - }, - decoration: const InputDecoration( - prefixIcon: Icon(Icons.email_outlined), - hintText: 'Email', - ), - ), - const SizedBox(height: 20.0), - _email != null && _email != '' - ? const SizedBox.shrink() - : TextFormField( - controller: _passwordController, - obscureText: _obscureText, - validator: (value) { - if (enableLoginWithEmail) { - if (value == null || - value.isEmpty) { - return 'Por favor ingrese una contraseña.'; - } - if (value.length < 5) { - return 'Debe tener al menos 5 caracteres.'; - } - return null; - } else { - return null; - } - }, - decoration: InputDecoration( - prefixIcon: const Icon( - Icons.lock_outline), - suffixIcon: IconButton( - icon: Icon( - _obscureText - ? Icons.visibility - : Icons.visibility_off, - color: Colors.grey, - ), - onPressed: () { - setState(() { - _obscureText = - !_obscureText; - }); - }, - ), - hintText: 'Contraseña'), - ), - _email != null && _email != '' - ? const SizedBox.shrink() - : const SizedBox(height: 20), - Container( - margin: const EdgeInsets.only( - left: 5, - right: 5, - top: 5, - bottom: 5, - ), - padding: const EdgeInsets.symmetric( - horizontal: 10, vertical: 8), - decoration: BoxDecoration( - color: const Color(0xFFD6F4FF), - borderRadius: BorderRadius.circular(20), - boxShadow: [ - BoxShadow( - color: Colors.grey.withOpacity(0.5), - spreadRadius: 1, - blurRadius: 5, - offset: const Offset(1, 3), - ), - ], - ), - child: const Row( - children: [ - Icon( - Icons.error_outline, - size: 20, - color: Colors.black54, - ), - SizedBox(width: 10), - Expanded( - child: Text( - 'Al habilitar el inicio de sesión con correo, se cerrara la sesión actual.', - style: TextStyle( - color: Colors.black54, - fontSize: 13, - ), - ), - ), - ], - ), - ) - ], - ), - ) - : const SizedBox(), - ], - ), - ), - ), - Container( - alignment: Alignment.bottomCenter, - margin: const EdgeInsets.only(top: 35), - padding: const EdgeInsets.only(bottom: 30), - child: Column( - children: [ - _email != null && _email != '' - ? PrimaryButton( - onPressed: () { - Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return NewPasswordScreen(); - }, - ), - ); - }, - text: 'Cambiar Contraseña', - minWidth: 300, - minHeight: 45, - ) - : const SizedBox(height: 20), - const SizedBox(height: 40), - PrimaryButton( - onPressed: () async { - if (_formKey.currentState!.validate()) { - if (kIsWeb) { - await updateInfo().whenComplete(() { - html.window.location.reload(); - }); - } else { - await updateInfo(); - } - } - await userProvider.updateUserDataAndScores(); - }, - text: 'Guardar', - ), - ], - ), - ), - ], - ), - ), - ), - ); - } -} diff --git a/lib/src/presentation/screens/profile/profile_pro.dart b/lib/src/presentation/screens/profile/profile_pro.dart deleted file mode 100644 index dde8e6b..0000000 --- a/lib/src/presentation/screens/profile/profile_pro.dart +++ /dev/null @@ -1,811 +0,0 @@ -import 'dart:io'; -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:firebase_storage/firebase_storage.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_animate/flutter_animate.dart'; -import 'package:get/get.dart'; -import 'package:prosappco/src/authentication/authentication_repository.dart'; -import 'package:prosappco/src/components/banner_photo.dart'; -import 'package:prosappco/src/components/pop_appbar.dart'; -import 'package:prosappco/src/components/schedule_picker.dart'; -import 'package:prosappco/src/models/setting_model.dart'; -import 'package:prosappco/src/presentation/screens/horario.dart'; -import 'package:prosappco/src/presentation/screens/professional.dart'; -import 'package:prosappco/src/presentation/screens/professional_direccion.dart'; -import 'package:prosappco/src/presentation/widgets/shared/primary_button.dart'; -import 'package:prosappco/src/presentation/widgets/shared/primary_checkbox.dart'; -import 'package:prosappco/src/services/select_image_profile.dart'; - -class ProfileProScreen extends StatefulWidget { - const ProfileProScreen({super.key}); - - @override - State createState() => _ProfileProScreenState(); -} - -class _ProfileProScreenState extends State { - final uid = AuthenticationRepository.instance.getCurrentUserUid(); - final TextEditingController _opcionalAddressController = - TextEditingController(); - final TextEditingController _tarifaController = TextEditingController(); - - File? image_portada; - File? imagen_to_upload; - - bool tarifaValue = false; - - bool domicilioValue = false; - bool sitioValue = false; - - var photoTemp = ''; - - var _photo = '...'; - var _direccion = '...'; - var _ubicacion = '...'; - var _opcionalAddress = '...'; - int _tarifa = 0; - SettingModel? settings; - - bool nequiValue = false; - bool banktransferValue = false; - bool datafoneValue = false; - - Map? _horarios; - - Map paymentMethods = { - 'Nequi': false, - 'Transferencia Bancaria': false, - 'Datafono': false, - }; - - @override - void initState() { - super.initState(); - if (settings == null) { - SettingModel.getSettings().then((SettingModel value) => setState( - () => settings = value, - )); - } - - final uid = AuthenticationRepository.instance.getCurrentUserUid(); - - if (_photo == '...') { - AuthenticationRepository.instance.getBanner(uid.toString()).then( - (String s) => setState( - () { - _photo = s; - }, - ), - ); - } - - if (_horarios == null) { - Schedule.getHorarios(uid.toString()).then( - (Map data) { - setState(() { - _horarios = data; - }); - }, - ); - } - - if (_direccion == '...') { - AuthenticationRepository.instance - .getAddress(uid.toString()) - .then((String s) => setState(() { - _direccion = s; - })); - } - if (_ubicacion == '...') { - AuthenticationRepository.instance.getUbicacion(uid.toString()).then( - (String s) => setState( - () { - _ubicacion = s; - if (_ubicacion == 'ambos') { - sitioValue = true; - domicilioValue = true; - } else if (_ubicacion == 'sitio') { - sitioValue = true; - } else if (_ubicacion == 'domicilio') { - domicilioValue = true; - } - }, - ), - ); - } - if (_opcionalAddress == '...') { - AuthenticationRepository.instance.getOpcionalAddress(uid.toString()).then( - (String s) => setState( - () { - _opcionalAddress = s; - - if (_opcionalAddress != '...') { - _opcionalAddressController.text = _opcionalAddress; - } - }, - ), - ); - } - if (_tarifa == 0) { - AuthenticationRepository.instance.getTarifa(uid.toString()).then( - (s) => setState( - () { - _tarifa = s; - - if (_tarifa != 0) { - tarifaValue = true; - _tarifaController.text = _tarifa.toString(); - } - }, - ), - ); - } - - loadPaymentMethods(); - } - - void createSchedules() async { - if (_horarios == null || _horarios!.isEmpty) { - final defaultSchedule = { - '1': { - 'habilitado': false, - 'jornadaContinua': false, - 'range1Hour1': null, - 'range1Hour2': null, - 'range2Hour1': null, - 'range2Hour2': null - }, - '2': { - 'habilitado': false, - 'jornadaContinua': false, - 'range1Hour1': null, - 'range1Hour2': null, - 'range2Hour1': null, - 'range2Hour2': null - }, - '3': { - 'habilitado': false, - 'jornadaContinua': false, - 'range1Hour1': null, - 'range1Hour2': null, - 'range2Hour1': null, - 'range2Hour2': null - }, - '4': { - 'habilitado': false, - 'jornadaContinua': false, - 'range1Hour1': null, - 'range1Hour2': null, - 'range2Hour1': null, - 'range2Hour2': null - }, - '5': { - 'habilitado': false, - 'jornadaContinua': false, - 'range1Hour1': null, - 'range1Hour2': null, - 'range2Hour1': null, - 'range2Hour2': null - }, - '6': { - 'habilitado': false, - 'jornadaContinua': false, - 'range1Hour1': null, - 'range1Hour2': null, - 'range2Hour1': null, - 'range2Hour2': null - }, - '7': { - 'habilitado': false, - 'jornadaContinua': false, - 'range1Hour1': null, - 'range1Hour2': null, - 'range2Hour1': null, - 'range2Hour2': null - } - }; - - try { - await FirebaseFirestore.instance - .collection('users') - .doc(uid) - .update({'horario': defaultSchedule}); - - Navigator.pop(context); - } catch (e) { - print(e); - } - } else { - Navigator.pushReplacement( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return HorarioScreen( - horarios: _horarios!, - ); - }, - ), - ); - } - } - - Future updateInfo() async { - if (_opcionalAddressController.text.isNotEmpty) { - FirebaseFirestore.instance - .collection('users') - .doc(uid) - .update({'opcional_address': _opcionalAddressController.text}); - } - if (tarifaValue && _tarifaController.text.isNotEmpty) { - FirebaseFirestore.instance - .collection('users') - .doc(uid) - .update({'tarifa': int.parse(_tarifaController.text)}); - } else { - FirebaseFirestore.instance - .collection('users') - .doc(uid) - .update({'tarifa': 0}); - } - - if (settings?.domicilios == false) { - if (sitioValue) { - FirebaseFirestore.instance - .collection('users') - .doc(uid) - .update({'ubicacion': 'sitio'}); - } else { - Get.snackbar( - 'Elige como vas a dar tu servicio', - 'Selecciona si tu servicio es a domicilio o en tu consultorio.', - snackPosition: SnackPosition.TOP, - backgroundColor: Colors.black.withOpacity(0.2), - messageText: const Text( - 'Selecciona si tu servicio es a domicilio o en tu consultorio.', - style: TextStyle(color: Colors.white), - ), - ); - - FirebaseFirestore.instance - .collection('users') - .doc(uid) - .update({'ubicacion': null}); - return; - } - } else { - if (domicilioValue && sitioValue) { - FirebaseFirestore.instance - .collection('users') - .doc(uid) - .update({'ubicacion': 'ambos'}); - } else if (domicilioValue) { - FirebaseFirestore.instance - .collection('users') - .doc(uid) - .update({'ubicacion': 'domicilio'}); - } else if (sitioValue) { - FirebaseFirestore.instance - .collection('users') - .doc(uid) - .update({'ubicacion': 'sitio'}); - } else { - Get.snackbar( - 'Elige como vas a dar tu servicio', - 'Selecciona si tu servicio es a domicilio o en tu consultorio.', - snackPosition: SnackPosition.TOP, - backgroundColor: Colors.black.withOpacity(0.2), - messageText: const Text( - 'Selecciona si tu servicio es a domicilio o en tu consultorio.', - style: TextStyle(color: Colors.white), - ), - ); - return; - } - } - - try { - if (imagen_to_upload == null) { - } else { - updateImage(photoTemp); - //image - } - } catch (e) { - print('Error al actualizar la imagen de perfil $e'); - } - - updatePaymentMethods(); - - if (settings?.domicilios == false && sitioValue == false) { - Get.defaultDialog( - title: 'Donde vas a dar tu servicio?', - middleText: - 'Si no eliges servicio en sitio, no serás visible para los usuarios.', - actions: [ - ElevatedButton( - onPressed: () { - Get.back(); - }, - child: const Text('Entendido'), - ), - ], - ); - } else { - Get.snackbar( - 'Información actualizada', - 'Tu información ha sido actualizada con éxito.', - snackPosition: SnackPosition.TOP, - ); - Navigator.pop(context); - } - - return; - } - - Future updateImage(image) async { - try { - await FirebaseFirestore.instance - .collection('users') - .doc(uid) - .update({'banner': image}); - } catch (e) { - try { - await FirebaseFirestore.instance - .collection('users') - .doc(uid) - .set({'banner': image}); - } catch (e) { - print('Error al agregar la imagen de perfil: $e'); - } - - print('Error al actualizar la imagen de perfil: $e'); - } - } - - Future uploadImage(File image) async { - final String namefile = image.path.split('/').last; - - Reference ref = storage - .ref() - .child('users') - .child(uid!) - .child('profile') - .child(namefile); - - final UploadTask uploadTask = ref.putFile(image); - - final TaskSnapshot snapshot = await uploadTask.whenComplete(() => true); - - photoTemp = ref.fullPath; - - if (snapshot.state == TaskState.success) { - return true; - } else { - return false; - } - } - - Future _showChoiceDialog(BuildContext context) async { - return showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - content: SingleChildScrollView( - child: ListBody( - children: [ - GestureDetector( - child: const Text( - textAlign: TextAlign.center, - "Tomar foto", - style: TextStyle(color: Color(0xFF2BA4EC)), - ), - onTap: () async { - final imagen = await getImage(1); - setState(() { - imagen_to_upload = File(imagen[0]!.path); - }); - Navigator.of(context).pop(); - }, - ), - const Divider(color: Colors.black54), - GestureDetector( - child: const Text( - textAlign: TextAlign.center, - "Abrir Galería", - style: TextStyle(color: Color(0xFF2BA4EC)), - ), - onTap: () async { - final imagen = await getImage(2); - setState(() { - imagen_to_upload = File(imagen[0]!.path); - }); - Navigator.of(context).pop(); - }, - ), - ], - ), - ), - ); - }, - ); - } - - void toggleDomicilio(bool newValue) { - setState(() { - domicilioValue = newValue; - if (newValue == false && sitioValue == false) { - sitioValue = true; - } - }); - } - - void toggleSitio(bool newValue) { - setState(() { - sitioValue = newValue; - if (newValue == false && domicilioValue == false) { - domicilioValue = true; - } - }); - } - - void updatePaymentMethods() { - FirebaseFirestore.instance.collection('users').doc(uid).update({ - 'paymentMethods': paymentMethods, - }); - } - - void loadPaymentMethods() { - FirebaseFirestore.instance.collection('users').doc(uid).get().then((doc) { - if (doc.exists) { - setState(() { - paymentMethods = Map.from(doc['paymentMethods'] ?? {}); - }); - } - }); - } - - @override - Widget build(BuildContext context) { - String address = _direccion.toString(); - - return Scaffold( - appBar: PopAppbar( - onPressed: () { - Navigator.pop(context); - }, - label: 'Perfil profesional'), - body: SingleChildScrollView( - reverse: true, - child: Column( - children: [ - GestureDetector( - onTap: () { - _showChoiceDialog(context); - }, - child: Container( - child: (imagen_to_upload != null) - ? LocalPhoto( - file: imagen_to_upload!, - ) - : ReferenceBannerPhoto( - ref: storage.ref().child(_photo), - ), - ), - ), - if (settings?.tarifas == true) - const Divider( - color: Colors.white, - height: 12, - ), - if (settings?.tarifas == true) - customSwitch( - 'Tarifa', - false, - (value) { - tarifaValue = value; - }, - ), - tarifaValue - ? Padding( - padding: - const EdgeInsets.only(left: 40, right: 40, bottom: 15), - child: Column( - children: [ - TextFormField( - controller: _tarifaController, - 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(), - if (settings?.domicilios == true) - const Divider( - color: Colors.white, - height: 12, - ), - if (settings?.domicilios == true) - customSwitch( - 'Servicio a domicilio', domicilioValue, toggleDomicilio), - const Divider(), - customSwitch('Servicio en sitio', sitioValue, toggleSitio), - sitioValue - ? Padding( - padding: - const EdgeInsets.only(left: 40, right: 40, bottom: 15), - child: Column( - children: [ - TextFormField( - readOnly: true, - onTap: () async { - final String? direccion = await Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return const ProfessionalDireccionScreen(); - }, - ), - ) as String?; - - if (direccion != null) { - setState(() { - _direccion = direccion; - }); - } - }, - decoration: InputDecoration( - prefixIcon: const Icon(Icons.near_me), - hintStyle: address == '' - ? const TextStyle() - : const TextStyle(color: Colors.black87), - hintText: address == '' ? 'Dirección' : address), - ), - const SizedBox(height: 10), - TextFormField( - controller: _opcionalAddressController, - decoration: const InputDecoration( - hintText: 'Oficina / Piso / Conjunto'), - ), - ], - ) - .animate() - .moveY(duration: const Duration(milliseconds: 100)), - ) - : const SizedBox(), - const Divider(), - Padding( - padding: const EdgeInsets.only(left: 30, right: 30, bottom: 30), - child: SizedBox( - width: double.infinity, - child: Column( - children: [ - const Text( - 'Metodos de pago', - style: TextStyle( - color: Colors.black, - fontSize: 17, - ), - ), - for (var entry in paymentMethods.entries) - PrimaryCheckbox( - text: entry.key, - initialValue: entry.value, - onChanged: (value) { - setState(() { - paymentMethods[entry.key] = value; - }); - }, - ), - ], - ), - ), - ), - const Padding( - padding: EdgeInsets.symmetric(horizontal: 30), - child: SizedBox( - width: double.infinity, - child: Text( - 'Horario estandar', - style: TextStyle( - color: Colors.black, - fontSize: 17, - ), - ), - ), - ), - GestureDetector( - onTap: () { - createSchedules(); - }, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 10), - child: Table( - defaultColumnWidth: const IntrinsicColumnWidth(), - children: [ - TableRow( - children: [ - TableCell( - child: Container( - padding: const EdgeInsets.all(8.0), - child: const Text('Lunes'), - ), - ), - TableCell( - child: Container( - padding: const EdgeInsets.all(8.0), - child: Text(timeList(_horarios?['1'], context)), - ), - ), - ], - ), - TableRow( - children: [ - TableCell( - child: Container( - padding: const EdgeInsets.all(8.0), - child: const Text('Martes'), - ), - ), - TableCell( - child: Container( - padding: const EdgeInsets.all(8.0), - child: Text(timeList(_horarios?['2'], context)), - ), - ), - ], - ), - TableRow( - children: [ - TableCell( - child: Container( - padding: const EdgeInsets.all(8.0), - child: const Text('Miercoles'), - ), - ), - TableCell( - child: Container( - padding: const EdgeInsets.all(8.0), - child: Text(timeList(_horarios?['3'], context)), - ), - ), - ], - ), - TableRow( - children: [ - TableCell( - child: Container( - padding: const EdgeInsets.all(8.0), - child: const Text('Jueves'), - ), - ), - TableCell( - child: Container( - padding: const EdgeInsets.all(8.0), - child: Text(timeList(_horarios?['4'], context)), - ), - ), - ], - ), - TableRow( - children: [ - TableCell( - child: Container( - padding: const EdgeInsets.all(8.0), - child: const Text('Viernes'), - ), - ), - TableCell( - child: Container( - padding: const EdgeInsets.all(8.0), - child: Text(timeList(_horarios?['5'], context)), - ), - ), - ], - ), - TableRow( - children: [ - TableCell( - child: Container( - padding: const EdgeInsets.all(8.0), - child: const Text('Sabado'), - ), - ), - TableCell( - child: Container( - padding: const EdgeInsets.all(8.0), - child: Text(timeList(_horarios?['6'], context)), - ), - ), - ], - ), - TableRow( - children: [ - TableCell( - child: Container( - padding: const EdgeInsets.all(8.0), - child: const Text('Domingo'), - ), - ), - TableCell( - child: Container( - padding: const EdgeInsets.all(8.0), - child: Text(timeList(_horarios?['7'], context)), - ), - ), - ], - ), - ], - ), - ), - ), - const SizedBox(height: 10), - PrimaryButton( - onPressed: () { - updateInfo(); - }, - text: 'Guardar', - ), - const SizedBox(height: 20), - ], - ), - ), - ); - } - - Widget customSwitch( - String text, - bool switchValue, - ValueChanged onChanged, - ) { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 30), - child: SizedBox( - height: 40, - child: Row( - children: [ - Expanded( - child: Text( - text, - style: const TextStyle( - fontSize: 17, - fontWeight: FontWeight.w500, - color: Colors.black, - ), - ), - ), - Transform.scale( - scale: 1.2, - child: Switch( - value: switchValue, - onChanged: onChanged, - ), - ), - ], - ), - ), - ); - } - - String timeList(Schedule? schedule, BuildContext context) { - if (schedule == null) { - return 'N/A'; - } - if (!schedule.habilitado) { - return 'N/A'; - } - if (schedule.jornadaContinua) { - return '${schedule.range1Hour1?.format(context).toString()} - ${schedule.range2Hour2?.format(context).toString()}'; - } else { - return '${schedule.range1Hour1?.format(context).toString()} - ${schedule.range1Hour2?.format(context).toString()}; ${schedule.range2Hour1?.format(context).toString()} - ${schedule.range2Hour2?.format(context).toString()}'; - } - } -} diff --git a/lib/src/presentation/screens/profile/profile_pro_web.dart b/lib/src/presentation/screens/profile/profile_pro_web.dart deleted file mode 100644 index f1ce504..0000000 --- a/lib/src/presentation/screens/profile/profile_pro_web.dart +++ /dev/null @@ -1,678 +0,0 @@ -import 'dart:io'; -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:firebase_storage/firebase_storage.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_animate/flutter_animate.dart'; -import 'package:get/get.dart'; -import 'package:prosappco/src/authentication/authentication_repository.dart'; -import 'package:prosappco/src/components/pop_appbar.dart'; -import 'package:prosappco/src/components/primary_btn.dart'; -import 'package:prosappco/src/components/schedule_picker.dart'; -import 'package:prosappco/src/models/setting_model.dart'; -import 'package:prosappco/src/presentation/screens/horario.dart'; -import 'package:prosappco/src/presentation/screens/professional.dart'; -import 'package:prosappco/src/presentation/screens/ubicacion.dart'; - -class ProfileProWebScreen extends StatefulWidget { - const ProfileProWebScreen({super.key}); - - @override - State createState() => _ProfileProWebScreenState(); -} - -class _ProfileProWebScreenState extends State { - final uid = AuthenticationRepository.instance.getCurrentUserUid(); - - final TextEditingController _opcionalAddressController = - TextEditingController(); - final TextEditingController _tarifaController = TextEditingController(); - final TextEditingController _ubicationController = TextEditingController(); - - double latUser = 0.0; - double lngUser = 0.0; - - bool domicilioValue = true; - bool tarifaValue = false; - bool sitioValue = false; - var photoTemp = ''; - - var _direccion = '...'; - var _ubicacion = '...'; - var _opcionalAddress = '...'; - int _tarifa = 0; - SettingModel? settings; - - Map? _horarios; - - @override - void initState() { - super.initState(); - if (settings == null) { - SettingModel.getSettings().then( - (SettingModel value) => setState(() => settings = value), - ); - } - - final uid = AuthenticationRepository.instance.getCurrentUserUid(); - - if (_horarios == null) { - Schedule.getHorarios(uid.toString()).then( - (Map data) { - setState(() { - _horarios = data; - }); - }, - ); - } - - if (_direccion == '...') { - AuthenticationRepository.instance - .getAddress(uid.toString()) - .then((String s) => setState(() { - _direccion = s; - _ubicationController.text = _direccion; - })); - } - if (_ubicacion == '...') { - AuthenticationRepository.instance.getUbicacion(uid.toString()).then( - (String s) => setState( - () { - _ubicacion = s; - if (_ubicacion == 'ambos') { - sitioValue = true; - domicilioValue = true; - } else if (_ubicacion == 'sitio') { - sitioValue = true; - } else if (_ubicacion == 'domicilio') { - domicilioValue = true; - } - }, - ), - ); - } - if (_opcionalAddress == '...') { - AuthenticationRepository.instance.getOpcionalAddress(uid.toString()).then( - (String s) => setState( - () { - _opcionalAddress = s; - - if (_opcionalAddress != '...') { - _opcionalAddressController.text = _opcionalAddress; - } - }, - ), - ); - } - if (_tarifa == 0) { - AuthenticationRepository.instance.getTarifa(uid.toString()).then( - (s) => setState( - () { - _tarifa = s; - - if (_tarifa != 0) { - tarifaValue = true; - _tarifaController.text = _tarifa.toString(); - } - }, - ), - ); - } - } - - Future updateInfo() async { - if (_ubicationController.text.isNotEmpty) { - FirebaseFirestore.instance.collection('users').doc(uid).update({ - 'address': _ubicationController.text, - if (latUser != 0.0) 'latitude': latUser, - if (latUser != 0.0) 'longitude': lngUser, - }); - } - if (_opcionalAddressController.text.isNotEmpty) { - FirebaseFirestore.instance - .collection('users') - .doc(uid) - .update({'opcional_address': _opcionalAddressController.text}); - } - if (tarifaValue && _tarifaController.text.isNotEmpty) { - FirebaseFirestore.instance - .collection('users') - .doc(uid) - .update({'tarifa': int.parse(_tarifaController.text)}); - } else { - FirebaseFirestore.instance - .collection('users') - .doc(uid) - .update({'tarifa': 0}); - } - - if (settings?.domicilios == false) { - if (sitioValue) { - FirebaseFirestore.instance - .collection('users') - .doc(uid) - .update({'ubicacion': 'sitio'}); - } else { - Get.snackbar( - 'Elige como vas a dar tu servicio', - 'Selecciona si tu servicio es a domicilio o en tu consultorio.', - snackPosition: SnackPosition.TOP, - backgroundColor: Colors.black.withOpacity(0.2), - messageText: const Text( - 'Selecciona si tu servicio es a domicilio o en tu consultorio.', - style: TextStyle(color: Colors.white), - ), - ); - - FirebaseFirestore.instance - .collection('users') - .doc(uid) - .update({'ubicacion': null}); - return; - } - } else { - if (domicilioValue && sitioValue) { - FirebaseFirestore.instance - .collection('users') - .doc(uid) - .update({'ubicacion': 'ambos'}); - } else if (domicilioValue) { - FirebaseFirestore.instance - .collection('users') - .doc(uid) - .update({'ubicacion': 'domicilio'}); - } else if (sitioValue) { - FirebaseFirestore.instance - .collection('users') - .doc(uid) - .update({'ubicacion': 'sitio'}); - } else { - Get.snackbar( - 'Elige como vas a dar tu servicio', - 'Selecciona si tu servicio es a domicilio o en tu consultorio.', - snackPosition: SnackPosition.TOP, - backgroundColor: Colors.black.withOpacity(0.2), - messageText: const Text( - 'Selecciona si tu servicio es a domicilio o en tu consultorio.', - style: TextStyle(color: Colors.white), - ), - ); - return; - } - } - - if (settings?.domicilios == false && sitioValue == false) { - Get.defaultDialog( - title: 'Donde vas a dar tu servicio?', - middleText: - 'Si no eliges servicio en sitio, no serás visible para los usuarios.', - actions: [ - ElevatedButton( - onPressed: () { - Get.back(); - }, - child: const Text('Entendido'), - ), - ], - ); - } else { - Get.snackbar( - 'Información actualizada', - 'Tu información ha sido actualizada con éxito.', - snackPosition: SnackPosition.TOP, - ); - Navigator.pop(context); - } - - return; - } - - Future updateImage(image) async { - try { - await FirebaseFirestore.instance - .collection('users') - .doc(uid) - .update({'banner': image}); - } catch (e) { - try { - await FirebaseFirestore.instance - .collection('users') - .doc(uid) - .set({'banner': image}); - } catch (e) { - print('Error al agregar la imagen de perfil: $e'); - } - - print('Error al actualizar la imagen de perfil: $e'); - } - } - - void createSchedules() async { - if (_horarios == null || _horarios!.isEmpty) { - final defaultSchedule = { - '1': { - 'habilitado': false, - 'jornadaContinua': false, - 'range1Hour1': null, - 'range1Hour2': null, - 'range2Hour1': null, - 'range2Hour2': null - }, - '2': { - 'habilitado': false, - 'jornadaContinua': false, - 'range1Hour1': null, - 'range1Hour2': null, - 'range2Hour1': null, - 'range2Hour2': null - }, - '3': { - 'habilitado': false, - 'jornadaContinua': false, - 'range1Hour1': null, - 'range1Hour2': null, - 'range2Hour1': null, - 'range2Hour2': null - }, - '4': { - 'habilitado': false, - 'jornadaContinua': false, - 'range1Hour1': null, - 'range1Hour2': null, - 'range2Hour1': null, - 'range2Hour2': null - }, - '5': { - 'habilitado': false, - 'jornadaContinua': false, - 'range1Hour1': null, - 'range1Hour2': null, - 'range2Hour1': null, - 'range2Hour2': null - }, - '6': { - 'habilitado': false, - 'jornadaContinua': false, - 'range1Hour1': null, - 'range1Hour2': null, - 'range2Hour1': null, - 'range2Hour2': null - }, - '7': { - 'habilitado': false, - 'jornadaContinua': false, - 'range1Hour1': null, - 'range1Hour2': null, - 'range2Hour1': null, - 'range2Hour2': null - } - }; - - try { - await FirebaseFirestore.instance - .collection('users') - .doc(uid) - .update({'horario': defaultSchedule}); - - Navigator.pop(context); - } catch (e) { - print(e); - } - } else { - Navigator.pushReplacement( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return HorarioScreen( - horarios: _horarios!, - ); - }, - ), - ); - } - } - - Future uploadImage(File image) async { - final String namefile = image.path.split('/').last; - - Reference ref = storage - .ref() - .child('users') - .child(uid!) - .child('profile') - .child(namefile); - - final UploadTask uploadTask = ref.putFile(image); - - final TaskSnapshot snapshot = await uploadTask.whenComplete(() => true); - - photoTemp = ref.fullPath; - - if (snapshot.state == TaskState.success) { - return true; - } else { - return false; - } - } - - void toggleDomicilio(bool newValue) { - setState(() { - domicilioValue = newValue; - if (newValue == false && sitioValue == false) { - sitioValue = true; - } - }); - } - - void toggleSitio(bool newValue) { - setState(() { - sitioValue = newValue; - if (newValue == false && domicilioValue == false) { - domicilioValue = true; - } - }); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: PopAppbar( - onPressed: () { - Navigator.pop(context); - }, - label: 'Perfil profesional'), - body: SingleChildScrollView( - reverse: true, - child: Column( - children: [ - if (settings?.tarifas == true) - const Divider( - color: Colors.white, - height: 12, - ), - if (settings?.tarifas == true) - customSwitch( - 'Tarifa', - tarifaValue, - (value) { - tarifaValue = value; - }, - ), - tarifaValue - ? Padding( - padding: - const EdgeInsets.only(left: 40, right: 40, bottom: 15), - child: Column( - children: [ - TextFormField( - controller: _tarifaController, - 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(), - if (settings?.domicilios == true) - const Divider( - color: Colors.white, - height: 12, - ), - if (settings?.domicilios == true) - customSwitch( - 'Servicio a domicilio', domicilioValue, toggleDomicilio), - const Divider(), - customSwitch('Servicio en sitio', sitioValue, toggleSitio), - sitioValue - ? Padding( - padding: - const EdgeInsets.only(left: 40, right: 40, bottom: 15), - child: Column( - children: [ - TextFormField( - controller: _ubicationController, - readOnly: true, - onTap: () async { - final List datos = await Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return const UbicacionScreen(); - }, - ), - ); - - if (datos.length == 3) { - final formattedAddress = datos[0]; - final lat = datos[1]; - final lng = datos[2]; - - setState(() { - _ubicationController.text = formattedAddress; - latUser = lat; - lngUser = lng; - }); - } - }, - decoration: const InputDecoration( - hintText: 'Escribe tu ubicación', - prefixIcon: Icon(Icons.near_me), - ), - ), - const SizedBox(height: 10), - TextFormField( - controller: _opcionalAddressController, - decoration: const InputDecoration( - hintText: 'Oficina / Piso / Conjunto'), - ), - ], - ) - .animate() - .moveY(duration: const Duration(milliseconds: 100)), - ) - : const SizedBox(), - const Divider(), - const Padding( - padding: EdgeInsets.symmetric(horizontal: 30), - child: SizedBox( - width: double.infinity, - child: Text( - 'Horario estandar', - style: TextStyle( - color: Colors.black, - fontSize: 15, - ), - ), - ), - ), - GestureDetector( - onTap: () { - createSchedules(); - }, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 10), - child: Table( - defaultColumnWidth: const IntrinsicColumnWidth(), - children: [ - TableRow( - children: [ - TableCell( - child: Container( - padding: const EdgeInsets.all(8.0), - child: const Text('Lunes'), - ), - ), - TableCell( - child: Container( - padding: const EdgeInsets.all(8.0), - child: Text(timeList(_horarios?['1'], context)), - ), - ), - ], - ), - TableRow( - children: [ - TableCell( - child: Container( - padding: const EdgeInsets.all(8.0), - child: const Text('Martes'), - ), - ), - TableCell( - child: Container( - padding: const EdgeInsets.all(8.0), - child: Text(timeList(_horarios?['2'], context)), - ), - ), - ], - ), - TableRow( - children: [ - TableCell( - child: Container( - padding: const EdgeInsets.all(8.0), - child: const Text('Miercoles'), - ), - ), - TableCell( - child: Container( - padding: const EdgeInsets.all(8.0), - child: Text(timeList(_horarios?['3'], context)), - ), - ), - ], - ), - TableRow( - children: [ - TableCell( - child: Container( - padding: const EdgeInsets.all(8.0), - child: const Text('Jueves'), - ), - ), - TableCell( - child: Container( - padding: const EdgeInsets.all(8.0), - child: Text(timeList(_horarios?['4'], context)), - ), - ), - ], - ), - TableRow( - children: [ - TableCell( - child: Container( - padding: const EdgeInsets.all(8.0), - child: const Text('Viernes'), - ), - ), - TableCell( - child: Container( - padding: const EdgeInsets.all(8.0), - child: Text(timeList(_horarios?['5'], context)), - ), - ), - ], - ), - TableRow( - children: [ - TableCell( - child: Container( - padding: const EdgeInsets.all(8.0), - child: const Text('Sabado'), - ), - ), - TableCell( - child: Container( - padding: const EdgeInsets.all(8.0), - child: Text(timeList(_horarios?['6'], context)), - ), - ), - ], - ), - TableRow( - children: [ - TableCell( - child: Container( - padding: const EdgeInsets.all(8.0), - child: const Text('Domingo'), - ), - ), - TableCell( - child: Container( - padding: const EdgeInsets.all(8.0), - child: Text(timeList(_horarios?['7'], context)), - ), - ), - ], - ), - ], - ), - ), - ), - PrimaryButtom( - onPressed: () { - updateInfo(); - }, - label: 'Guardar'), - const SizedBox( - height: 20, - ) - ], - ), - ), - ); - } - - Widget customSwitch( - String text, bool switchValue, ValueChanged onChanged) { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 30), - child: SizedBox( - height: 40, - child: Row( - children: [ - Expanded( - child: Text( - text, - style: const TextStyle( - fontSize: 15, - color: Colors.black, - ), - ), - ), - Transform.scale( - scale: 1.2, - child: Switch( - value: switchValue, - onChanged: onChanged, - ), - ), - ], - ), - ), - ); - } - - String timeList(Schedule? schedule, BuildContext context) { - if (schedule == null) { - return 'N/A'; - } - if (!schedule.habilitado) { - return 'N/A'; - } - if (schedule.jornadaContinua) { - return '${schedule.range1Hour1?.format(context).toString()} - ${schedule.range2Hour2?.format(context).toString()}'; - } else { - return '${schedule.range1Hour1?.format(context).toString()} - ${schedule.range1Hour2?.format(context).toString()}; ${schedule.range2Hour1?.format(context).toString()} - ${schedule.range2Hour2?.format(context).toString()}'; - } - } -} diff --git a/lib/src/presentation/screens/register/register.dart b/lib/src/presentation/screens/register/register.dart deleted file mode 100644 index 31a8280..0000000 --- a/lib/src/presentation/screens/register/register.dart +++ /dev/null @@ -1,693 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:font_awesome_flutter/font_awesome_flutter.dart'; -import 'package:get/get.dart'; -import 'package:prosappco/src/authentication/authentication_repository.dart'; -import 'package:prosappco/src/components/bottom_sheet.dart'; -import 'package:prosappco/src/components/column_padding.dart'; -import 'package:prosappco/src/presentation/widgets/shared/primary_button.dart'; -import 'package:prosappco/src/controllers/register_controller.dart'; -import 'package:prosappco/src/models/setting_model.dart'; -import 'package:prosappco/src/providers/user_provider.dart'; -import 'package:prosappco/src/presentation/screens/web_view.dart'; -import 'package:provider/provider.dart'; -import 'package:responsive_builder/responsive_builder.dart'; -import 'package:url_launcher/url_launcher.dart'; - -class RegisterScreen extends StatefulWidget { - const RegisterScreen({super.key}); - - @override - State createState() => _RegisterScreenState(); -} - -class _RegisterScreenState extends State { - bool _obscureText = true; - final controller = Get.put(RegisterController()); - final _formKey = GlobalKey(); - bool _isChecked = false; - SettingModel? settings; - - void _launchURL(String url) async { - if (await canLaunch(url)) { - await launch(url, forceSafariVC: false, forceWebView: false); - } else { - throw 'No se pudo abrir el enlace $url'; - } - } - - @override - void initState() { - super.initState(); - if (settings == null) { - SettingModel.getSettings().then( - (SettingModel value) => setState(() { - settings = value; - }), - ); - } - } - - @override - Widget build(BuildContext context) { - return ScreenTypeLayout.builder( - mobile: (BuildContext context) => _mobileView(context), - tablet: (BuildContext context) => _mobileView(context), - desktop: (BuildContext context) => _desktopView(context), - ); - } - - Widget _mobileView(BuildContext context) { - bool isIOS = Theme.of(context).platform == TargetPlatform.iOS; - - return BottomSheetExpanded( - horizontalPadding: 10, - children: [ - Row( - children: [ - IconButton( - icon: const Icon( - Icons.arrow_back, - size: 30, - ), - onPressed: () { - Navigator.pop(context); - }, - ), - const Text( - 'Registro', - style: TextStyle( - color: Color(0xFF262626), - fontSize: 30.0, - fontWeight: FontWeight.bold, - ), - textAlign: TextAlign.right, - ), - ], - ), - ColumnPadding( - alineacion: MainAxisAlignment.start, - padding: const EdgeInsets.symmetric(horizontal: 25, vertical: 20), - children: [ - !isIOS && !kIsWeb && settings?.google == true - ? Padding( - padding: const EdgeInsets.only(bottom: 20), - child: ElevatedButton( - onPressed: () async { - await AuthenticationRepository.instance - .signInWithGoogle(); - }, - style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFF2BA4EC), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(50), - ), - elevation: 0, - minimumSize: const Size(230, 60), - ), - child: const Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - 'Entrar con Google ', - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 18, - ), - ), - SizedBox(width: 5), - FaIcon(FontAwesomeIcons.google), - ], - )), - ) - : const SizedBox(), - !isIOS && !kIsWeb && settings?.google == true - ? const Padding( - padding: EdgeInsets.symmetric(vertical: 0), - child: Row( - children: [ - Expanded( - child: Divider( - color: Colors.black38, - thickness: 1, - ), - ), - Padding( - padding: EdgeInsets.symmetric(horizontal: 10), - child: Text("ó"), - ), - Expanded( - child: Divider( - color: Colors.black38, - thickness: 1, - ), - ), - ], - ), - ) - : const SizedBox(), - Form( - key: _formKey, - child: Column( - children: [ - const Padding( - padding: EdgeInsets.only(bottom: 5), - child: Align( - alignment: Alignment.topLeft, - child: Text('Email', - style: TextStyle( - fontSize: 18.0, color: Color(0xFF65676B))), - )), - Padding( - padding: const EdgeInsets.only(bottom: 18), - child: TextFormField( - controller: controller.email, - validator: (String? value) { - if (value == null || value.isEmpty) { - return 'Por favor ingresa un email'; - } - final RegExp emailRegExp = - RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$'); - if (!emailRegExp.hasMatch(value)) { - return 'Por favor ingresa un email válido'; - } - return null; - }, - decoration: const InputDecoration( - border: OutlineInputBorder( - borderSide: BorderSide(color: Color(0xFFECECEC)), - borderRadius: BorderRadius.all( - Radius.circular(50), - ), - ), - enabledBorder: OutlineInputBorder( - borderSide: BorderSide(color: Color(0xFFECECEC)), - borderRadius: BorderRadius.all( - Radius.circular(50), - ), - ), - focusedBorder: OutlineInputBorder( - borderSide: BorderSide(color: Color(0xFFECECEC)), - borderRadius: BorderRadius.all( - Radius.circular(50), - ), - ), - errorBorder: OutlineInputBorder( - borderSide: - BorderSide(color: Color.fromARGB(255, 184, 0, 0)), - borderRadius: BorderRadius.all( - Radius.circular(50), - ), - ), - hintText: 'Hello@gmail.com', - fillColor: Color.fromARGB(255, 239, 239, 239), - filled: true, - prefixIcon: Icon(Icons.email_outlined), - hintStyle: TextStyle( - color: Colors.grey, - ), - ), - ), - ), - const Padding( - padding: EdgeInsets.only(bottom: 5), - child: Align( - alignment: Alignment.topLeft, - child: Text('Password', - style: TextStyle( - fontSize: 18.0, color: Color(0xFF65676B))), - )), - Padding( - padding: const EdgeInsets.only(bottom: 25), - child: TextFormField( - controller: controller.password, - obscureText: _obscureText, - validator: (value) { - if (value == null || value.isEmpty) { - return 'Por favor ingresa una contraseña'; - } - if (value.length <= 6) { - return 'Contraseña muy corta'; - } - return null; - }, - decoration: InputDecoration( - errorBorder: const OutlineInputBorder( - borderSide: BorderSide( - color: Color.fromARGB(255, 184, 0, 0)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - enabledBorder: const OutlineInputBorder( - borderSide: BorderSide(color: Color(0xFFECECEC)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - border: const OutlineInputBorder( - borderSide: BorderSide(color: Color(0xFFECECEC)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - focusedBorder: const OutlineInputBorder( - borderSide: BorderSide(color: Color(0xFFECECEC)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - hintText: 'Contraseña', - hintStyle: const TextStyle( - color: Colors.grey, - ), - fillColor: const Color.fromARGB(255, 239, 239, 239), - filled: true, - prefixIcon: const Icon(Icons.lock_outline), - suffixIcon: IconButton( - icon: Icon( - _obscureText - ? Icons.visibility - : Icons.visibility_off, - color: Colors.grey, - ), - onPressed: () { - setState(() { - _obscureText = !_obscureText; - }); - }, - ), - ), - ), - ), - ], - ), - ), - Padding( - padding: const EdgeInsets.only(bottom: 20), - child: Row( - mainAxisAlignment: - MainAxisAlignment.center, // Centra horizontalmente - children: [ - Checkbox( - value: _isChecked, - onChanged: (value) { - setState(() { - _isChecked = value!; - }); - }, - ), - GestureDetector( - onTap: () { - if (kIsWeb) { - _launchURL(settings?.terminosCondiciones ?? ''); - } else { - Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return WebViewScreen( - label: 'Políticas de privacidad', - link: settings?.terminosCondiciones ?? '', - ); - }, - ), - ); - } - }, - child: const Text( - 'Acepto los términos y condiciones.', - style: TextStyle( - color: Color(0xFF65676B), - decoration: TextDecoration.underline, - ), - ), - ), - ], - ), - ), - Padding( - padding: const EdgeInsets.only(bottom: 25), - child: Center( - child: PrimaryButton( - onPressed: () { - if (_formKey.currentState!.validate()) { - RegisterController.instance - .registerUser( - controller.email.text.trim(), - controller.password.text.trim(), - ) - .then((value) => - Provider.of(context, listen: false) - .initUserProvider()); - } - }, - text: 'Registrarme', - isEnabled: _isChecked, - ), - ), - ), - RichText( - text: TextSpan( - style: const TextStyle( - fontSize: 16.0, - color: Color(0xFF65676B), - fontFamily: 'Poppins', - ), - children: [ - const TextSpan(text: 'Ya estas registrado? '), - WidgetSpan( - child: GestureDetector( - onTap: () { - Navigator.pushReplacementNamed(context, '/login'); - }, - child: const Text( - 'Iniciar Sesión', - style: TextStyle( - fontSize: 16.0, - color: Color(0xFF2BA4EC), - fontWeight: FontWeight.w600, - ), - ), - ), - ), - ], - ), - ) - ], - ), - ], - ); - } - - Widget _desktopView(BuildContext context) { - double height = MediaQuery.of(context).size.height; - double width = MediaQuery.of(context).size.width; - return Scaffold( - backgroundColor: const Color(0xFFD6F4FF), - body: SizedBox( - height: height, - width: width, - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Expanded( - child: SizedBox( - height: height, - child: const Center( - child: Image( - image: AssetImage('images/logo_prosapp.png'), - ), - ), - ), - ), - Expanded( - child: Container( - padding: EdgeInsets.symmetric(horizontal: width * 0.07), - color: Colors.white, - height: height, - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Container( - padding: const EdgeInsets.symmetric( - horizontal: 0, vertical: 20), - child: Row( - children: [ - IconButton( - icon: const Icon( - Icons.arrow_back, - size: 30, - ), - onPressed: () { - Navigator.pop(context); - }, - ), - SizedBox(width: width * 0.01), - const Text( - 'Registro', - style: TextStyle( - color: Color(0xFF262626), - fontSize: 30.0, - fontWeight: FontWeight.bold, - ), - textAlign: TextAlign.right, - ), - ], - ), - ), - Container( - padding: const EdgeInsets.symmetric( - horizontal: 0, vertical: 20), - child: Column( - children: [ - Form( - key: _formKey, - child: Column( - children: [ - const Padding( - padding: EdgeInsets.only(bottom: 5), - child: Align( - alignment: Alignment.topLeft, - child: Text('Email', - style: TextStyle( - fontSize: 18.0, - color: Color(0xFF65676B))), - )), - Padding( - padding: const EdgeInsets.only(bottom: 18), - child: TextFormField( - controller: controller.email, - validator: (String? value) { - if (value == null || value.isEmpty) { - return 'Por favor ingresa un email'; - } - final RegExp emailRegExp = RegExp( - r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$'); - if (!emailRegExp.hasMatch(value)) { - return 'Por favor ingresa un email válido'; - } - return null; - }, - decoration: const InputDecoration( - border: OutlineInputBorder( - borderSide: BorderSide( - color: Color(0xFFECECEC)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - enabledBorder: OutlineInputBorder( - borderSide: BorderSide( - color: Color(0xFFECECEC)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - focusedBorder: OutlineInputBorder( - borderSide: BorderSide( - color: Color(0xFFECECEC)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - errorBorder: OutlineInputBorder( - borderSide: BorderSide( - color: Color.fromARGB( - 255, 184, 0, 0)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - hintText: 'Hello@gmail.com', - fillColor: - Color.fromARGB(255, 239, 239, 239), - filled: true, - prefixIcon: Icon(Icons.email_outlined), - hintStyle: TextStyle( - color: Colors.grey, - ), - ), - ), - ), - const Padding( - padding: EdgeInsets.only(bottom: 5), - child: Align( - alignment: Alignment.topLeft, - child: Text('Password', - style: TextStyle( - fontSize: 18.0, - color: Color(0xFF65676B))), - )), - Padding( - padding: const EdgeInsets.only(bottom: 25), - child: TextFormField( - controller: controller.password, - obscureText: _obscureText, - validator: (value) { - if (value == null || value.isEmpty) { - return 'Por favor ingresa una contraseña'; - } - if (value.length <= 6) { - return 'Contraseña muy corta'; - } - return null; - }, - decoration: InputDecoration( - errorBorder: const OutlineInputBorder( - borderSide: BorderSide( - color: Color.fromARGB( - 255, 184, 0, 0)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - enabledBorder: const OutlineInputBorder( - borderSide: BorderSide( - color: Color(0xFFECECEC)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - border: const OutlineInputBorder( - borderSide: BorderSide( - color: Color(0xFFECECEC)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - focusedBorder: const OutlineInputBorder( - borderSide: BorderSide( - color: Color(0xFFECECEC)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - hintText: 'Contraseña', - hintStyle: const TextStyle( - color: Colors.grey, - ), - fillColor: const Color.fromARGB( - 255, 239, 239, 239), - filled: true, - prefixIcon: - const Icon(Icons.lock_outline), - suffixIcon: IconButton( - icon: Icon( - _obscureText - ? Icons.visibility - : Icons.visibility_off, - color: Colors.grey, - ), - onPressed: () { - setState(() { - _obscureText = !_obscureText; - }); - }, - ), - ), - ), - ), - ], - ), - ), - Padding( - padding: const EdgeInsets.symmetric(vertical: 20), - child: Row( - mainAxisAlignment: MainAxisAlignment - .center, // Centra horizontalmente - children: [ - Checkbox( - value: _isChecked, - onChanged: (value) { - setState(() { - _isChecked = value!; - }); - }, - ), - GestureDetector( - onTap: () { - if (kIsWeb) { - _launchURL( - settings?.terminosCondiciones ?? ''); - } else { - Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return WebViewScreen( - label: 'Políticas de privacidad', - link: settings - ?.terminosCondiciones ?? - '', - ); - }, - ), - ); - } - }, - child: const Text( - 'Acepto los términos y condiciones.', - style: TextStyle( - color: Color(0xFF65676B), - decoration: TextDecoration.underline, - ), - ), - ), - ], - ), - ), - Padding( - padding: const EdgeInsets.only(bottom: 25), - child: Center( - child: PrimaryButton( - onPressed: () { - if (_formKey.currentState!.validate()) { - RegisterController.instance - .registerUser( - controller.email.text.trim(), - controller.password.text.trim(), - ) - .then((value) => - Provider.of(context, - listen: false) - .initUserProvider()); - } - }, - text: 'Registrarme', - isEnabled: _isChecked, - )), - ), - RichText( - text: TextSpan( - style: const TextStyle( - fontSize: 16.0, - color: Color(0xFF65676B), - fontFamily: 'Poppins', - ), - children: [ - const TextSpan(text: 'Ya estas registrado? '), - WidgetSpan( - child: GestureDetector( - onTap: () { - Navigator.pushReplacementNamed( - context, '/login'); - }, - child: const Text( - 'Iniciar Sesión', - style: TextStyle( - fontSize: 16.0, - color: Color(0xFF2BA4EC), - fontWeight: FontWeight.w600, - ), - ), - ), - ), - ], - ), - ) - ], - ), - ), - ], - ), - ), - ), - ], - ), - ), - ); - } -} diff --git a/lib/src/presentation/screens/reputacion_pro.dart b/lib/src/presentation/screens/reputacion_pro.dart deleted file mode 100644 index 3b58482..0000000 --- a/lib/src/presentation/screens/reputacion_pro.dart +++ /dev/null @@ -1,115 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_rating_bar/flutter_rating_bar.dart'; -import 'package:prosappco/src/authentication/authentication_repository.dart'; -import 'package:prosappco/src/components/photo_view.dart'; -import 'package:prosappco/src/components/pop_appbar.dart'; -import 'package:prosappco/src/models/scores_model.dart'; - -class ReputationProScreen extends StatefulWidget { - const ReputationProScreen({ - super.key, - }); - - @override - State createState() => _ReputationProScreenState(); -} - -class _ReputationProScreenState extends State { - ScoresModel? scoresModel; - - @override - void initState() { - super.initState(); - final uid = AuthenticationRepository.instance.getCurrentUserUid(); - - if (scoresModel == null) { - ScoresModel.scoreTo(uid.toString(), true, true).then( - (ScoresModel s) => setState(() => scoresModel = s), - ); - } - } - - Widget _scoreList(List list) { - if (list.isEmpty) { - return const Padding( - padding: EdgeInsets.symmetric(vertical: 40), - child: Center( - child: Text('Sin calificaciones'), - ), - ); - } else { - return Column( - children: list.map((e) => _scoreItem(e)).toList(), - ); - } - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: PopAppbar( - onPressed: () { - Navigator.pop(context); - }, - label: 'Reputación'), - body: SingleChildScrollView( - child: _scoreList(scoresModel?.details ?? []), - ), - ); - } - - Widget _scoreItem(ScoreDetailModel scoreDetails) { - return ListTile( - onTap: () {}, - leading: ReferencePhoto( - ref: scoreDetails.avatar, - size: 50, - sizeCircle: 50, - sizeIcon: 35, - ), - title: Row( - children: [ - RatingBar.builder( - initialRating: scoreDetails.score, - minRating: 1, - direction: Axis.horizontal, - allowHalfRating: true, - itemCount: 5, - itemSize: 22, - maxRating: 5, - itemPadding: const EdgeInsets.symmetric(horizontal: 0), - itemBuilder: (context, _) => const Icon( - Icons.star, - color: Color(0xFF2BA4EC), - ), - onRatingUpdate: (rating) {}, - ignoreGestures: true, - ), - Text( - ' (${scoreDetails.score})', - style: const TextStyle(color: Colors.black54, fontSize: 13), - ) - ], - ), - subtitle: Row( - children: [ - Expanded( - child: Text.rich( - TextSpan( - children: [ - TextSpan( - text: '${scoreDetails.name}, ', - style: const TextStyle(fontSize: 15, color: Colors.black), - ), - TextSpan( - text: '"${scoreDetails.comment}"', - style: const TextStyle(fontSize: 15, color: Colors.grey), - ), - ], - ), - ), - ), - ], - )); - } -} diff --git a/lib/src/presentation/screens/reputation.dart b/lib/src/presentation/screens/reputation.dart deleted file mode 100644 index e37c28b..0000000 --- a/lib/src/presentation/screens/reputation.dart +++ /dev/null @@ -1,115 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_rating_bar/flutter_rating_bar.dart'; -import 'package:prosappco/src/authentication/authentication_repository.dart'; -import 'package:prosappco/src/components/photo_view.dart'; -import 'package:prosappco/src/components/pop_appbar.dart'; -import 'package:prosappco/src/models/scores_model.dart'; - -class ReputationScreen extends StatefulWidget { - const ReputationScreen({ - super.key, - }); - - @override - State createState() => _ReputationScreenState(); -} - -class _ReputationScreenState extends State { - ScoresModel? scoresModel; - - @override - void initState() { - super.initState(); - final uid = AuthenticationRepository.instance.getCurrentUserUid(); - - if (scoresModel == null) { - ScoresModel.scoreTo(uid.toString(), false, true).then( - (ScoresModel s) => setState(() => scoresModel = s), - ); - } - } - - Widget _scoreList(List list) { - if (list.isEmpty) { - return const Padding( - padding: EdgeInsets.symmetric(vertical: 40), - child: Center( - child: Text('Sin calificaciones'), - ), - ); - } else { - return Column( - children: list.map((e) => _scoreItem(e)).toList(), - ); - } - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: PopAppbar( - onPressed: () { - Navigator.pop(context); - }, - label: 'Reputación'), - body: SingleChildScrollView( - child: _scoreList(scoresModel?.details ?? []), - ), - ); - } - - Widget _scoreItem(ScoreDetailModel scoreDetails) { - return ListTile( - onTap: () {}, - leading: ReferencePhoto( - ref: scoreDetails.avatar, - size: 50, - sizeCircle: 50, - sizeIcon: 35, - ), - title: Row( - children: [ - RatingBar.builder( - initialRating: scoreDetails.score, - minRating: 1, - direction: Axis.horizontal, - allowHalfRating: true, - itemCount: 5, - itemSize: 22, - maxRating: 5, - itemPadding: const EdgeInsets.symmetric(horizontal: 0), - itemBuilder: (context, _) => const Icon( - Icons.star, - color: Color(0xFF2BA4EC), - ), - onRatingUpdate: (rating) {}, - ignoreGestures: true, - ), - Text( - ' (${scoreDetails.score})', - style: const TextStyle(color: Colors.black54, fontSize: 13), - ) - ], - ), - subtitle: Row( - children: [ - Expanded( - child: Text.rich( - TextSpan( - children: [ - TextSpan( - text: '${scoreDetails.name}, ', - style: const TextStyle(fontSize: 15, color: Colors.black), - ), - TextSpan( - text: '"${scoreDetails.comment}"', - style: const TextStyle(fontSize: 15, color: Colors.grey), - ), - ], - ), - ), - ), - ], - )); - } -} diff --git a/lib/src/presentation/screens/request_sent.dart b/lib/src/presentation/screens/request_sent.dart deleted file mode 100644 index ddb9e12..0000000 --- a/lib/src/presentation/screens/request_sent.dart +++ /dev/null @@ -1,82 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:prosappco/src/components/pop_appbar.dart'; -import 'package:prosappco/src/components/primary_btn.dart'; - -class RequestSentScreen extends StatelessWidget { - const RequestSentScreen({super.key}); - - @override - Widget build(BuildContext context) { - return SafeArea( - child: Scaffold( - appBar: PopAppbar( - onPressed: () { - Navigator.pushReplacementNamed(context, '/servicio'); - }, - label: 'Solicitud enviada', - ), - body: Center( - child: Column( - children: [ - const Padding( - padding: EdgeInsets.only(top: 30, bottom: 30), - child: Icon( - Icons.check_circle_outline, - size: 35, - color: Color(0xFF35A8ED), - ), - ), - const Padding( - padding: EdgeInsets.only(bottom: 0, left: 30, right: 30), - child: Text( - 'Información enviada con éxito.', - textAlign: TextAlign.center, - style: TextStyle(color: Color(0xFF2BA4EC), fontSize: 20), - ), - ), - Container( - margin: const EdgeInsets.only( - left: 40, right: 40, top: 50, bottom: 100), - padding: - const EdgeInsets.symmetric(horizontal: 20, vertical: 15), - decoration: BoxDecoration( - color: const Color(0xFFD6F4FF), - borderRadius: BorderRadius.circular(20), - boxShadow: [ - BoxShadow( - color: Colors.grey.withOpacity(0.5), - spreadRadius: 1, - blurRadius: 5, - offset: const Offset(1, 3), - ), - ], - ), - child: const SizedBox( - width: 350, - child: Row( - children: [ - Expanded( - child: Text( - "¡Gracias por suministrar tu información! Revisaremos los datos proporcionados y, una vez confirmados, podrás convertirte en un profesional registrado en ProsApp. ¡Esperamos contar contigo pronto!", - style: TextStyle(color: Colors.black, fontSize: 14), - ), - ) - ], - ), - ), - ), - const Expanded(child: SizedBox()), - PrimaryButtom( - onPressed: () { - Navigator.pushReplacementNamed(context, '/servicio'); - }, - label: 'Inicio', - ), - const SizedBox(height: 20), - ], - ), - ), - ), - ); - } -} diff --git a/lib/src/presentation/screens/reset_password/reset_password.dart b/lib/src/presentation/screens/reset_password/reset_password.dart deleted file mode 100644 index 8402ab8..0000000 --- a/lib/src/presentation/screens/reset_password/reset_password.dart +++ /dev/null @@ -1,126 +0,0 @@ -import 'package:firebase_auth/firebase_auth.dart'; -import 'package:flutter/material.dart'; -import 'package:get/get.dart'; -import 'package:prosappco/src/components/pop_appbar.dart'; -import 'package:prosappco/src/components/primary_btn.dart'; -import 'package:prosappco/src/controllers/login_email_controller.dart'; - -class ResetPasswordScreen extends StatelessWidget { - const ResetPasswordScreen({super.key}); - - @override - Widget build(BuildContext context) { - final controller = Get.put(LoginEmailController()); - - return Scaffold( - appBar: PopAppbar( - onPressed: () { - Navigator.pop(context); - }, - label: 'Restablecer Contraseña'), - body: SafeArea( - child: GestureDetector( - onTap: () => FocusScope.of(context).unfocus(), - child: Center( - child: Container( - padding: const EdgeInsets.all(15), - color: Colors.transparent, - width: MediaQuery.of(context).size.width * 0.9, - child: Column( - children: [ - const SizedBox(height: 20), - const Text( - 'Restablecer contraseña', - style: TextStyle(fontWeight: FontWeight.w800, fontSize: 25), - textAlign: TextAlign.center, - ), - const SizedBox(height: 20), - const Text( - 'Ingresa ingresa tu correo electrónico y te enviaremos un enlace para restablecer tu contraseña', - style: TextStyle(color: Colors.grey, fontSize: 12), - textAlign: TextAlign.center, - ), - const SizedBox(height: 40), - const Padding( - padding: EdgeInsets.only(bottom: 5), - child: Align( - alignment: Alignment.topLeft, - child: Text('Email', - style: TextStyle( - fontSize: 18.0, color: Color(0xFF65676B))), - ), - ), - TextFormField( - controller: controller.email, - validator: (String? value) { - if (value == null || value.isEmpty) { - return 'Por favor, ingresa un Email'; - } - final RegExp emailRegExp = - RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$'); - if (!emailRegExp.hasMatch(value)) { - return 'Por favor, ingresa un Email válido'; - } - return null; - }, - decoration: const InputDecoration( - border: OutlineInputBorder( - borderSide: BorderSide(color: Color(0xFFECECEC)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - enabledBorder: OutlineInputBorder( - borderSide: BorderSide(color: Color(0xFFECECEC)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - focusedBorder: OutlineInputBorder( - borderSide: BorderSide(color: Color(0xFFECECEC)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - errorBorder: OutlineInputBorder( - borderSide: - BorderSide(color: Color.fromARGB(255, 184, 0, 0)), - borderRadius: BorderRadius.all( - Radius.circular(50), - )), - hintText: 'Hello@gmail.com', - fillColor: Color.fromARGB(255, 239, 239, 239), - filled: true, - prefixIcon: Icon(Icons.email_outlined), - hintStyle: TextStyle( - color: Colors.grey, - ), - ), - ), - const SizedBox(height: 80), - PrimaryButtom( - onPressed: () async { - try { - await FirebaseAuth.instance.sendPasswordResetEmail( - email: controller.email.text.trim()); - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text( - 'Se ha enviado un enlace de restablecimiento de contraseña a tu correo electrónico.'), - ), - ); - Navigator.pop(context); - } catch (e) { - ScaffoldMessenger.of(context) - .showSnackBar(const SnackBar( - content: Text( - 'Hubo un error al enviar el enlace de restablecimiento de contraseña.'), - )); - } - }, - label: 'Enviar'), - ], - ), - ), - ), - )), - ); - } -} diff --git a/lib/src/presentation/screens/score.dart b/lib/src/presentation/screens/score.dart deleted file mode 100644 index 7bf4a97..0000000 --- a/lib/src/presentation/screens/score.dart +++ /dev/null @@ -1,182 +0,0 @@ -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:firebase_storage/firebase_storage.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_rating_bar/flutter_rating_bar.dart'; -import 'package:intl/intl.dart'; -import 'package:prosappco/src/components/photo_view.dart'; -import 'package:prosappco/src/components/pop_appbar.dart'; -import 'package:prosappco/src/models/event_model.dart'; -import 'package:prosappco/src/models/user_model.dart'; - -class ScoreScreen extends StatefulWidget { - final Event evento; - final bool pro; - const ScoreScreen({super.key, required this.evento, required this.pro}); - - @override - State createState() => ScoreScreenState(); -} - -class ScoreScreenState extends State { - TextEditingController commentController = TextEditingController(); - Reference? ref_photo; - String nombre = ''; - double _rating = 1.0; - - @override - Widget build(BuildContext context) { - if (nombre == '') { - if (uid != widget.evento.userId) { - UserModel.getUser(widget.evento.userId).then((value) { - UserModel.getUser(uid.toString()).then((me) { - setState(() { - nombre = value.name; - ref_photo = value.photo; - }); - }); - }); - } else { - UserModel.getUser(widget.evento.professionalId).then((value) { - setState(() { - nombre = value.name; - ref_photo = value.photo; - }); - }); - } - } - - return Scaffold( - appBar: PopAppbar( - onPressed: () { - Navigator.pop(context); - }, - label: 'Puntuación'), - body: Column( - children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 15), - child: ListTile( - leading: Padding( - padding: const EdgeInsets.symmetric(horizontal: 5), - child: ReferencePhoto( - ref: ref_photo, - size: 50, - sizeCircle: 50, - sizeIcon: 35, - ), - ), - title: Text( - nombre, - style: const TextStyle( - color: Colors.black, - fontWeight: FontWeight.w600, - fontSize: 16), - ), - subtitle: Text( - '${DateFormat('dd MMM', 'es').format(DateTime.parse(widget.evento.day))} ${TimeOfDay.fromDateTime(DateTime.parse(widget.evento.range1Hour1)).format(context)}'), - ), - ), - RatingBar.builder( - initialRating: _rating, - minRating: 1, - direction: Axis.horizontal, - allowHalfRating: true, - itemCount: 5, - itemSize: 40, - glow: false, - maxRating: 5, - itemPadding: const EdgeInsets.symmetric(horizontal: 5), - itemBuilder: (context, _) => const Icon( - Icons.star, - color: Color(0xFF2BA4EC), - ), - onRatingUpdate: (rating) { - setState(() { - _rating = rating; - }); - }, - ignoreGestures: false, - ), - Expanded( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 40, vertical: 40), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text( - 'Comentario', - style: TextStyle(fontSize: 18), - ), - TextFormField( - maxLines: null, - maxLength: 250, - keyboardType: TextInputType.multiline, - controller: commentController, - ), - ], - ), - ), - ), - Padding( - padding: const EdgeInsets.only(bottom: 30), - child: Column( - children: [ - ElevatedButton( - onPressed: () { - if (widget.pro) { - FirebaseFirestore.instance - .collection("services") - .doc(widget.evento.id) - .update({'professional_scored': true}).then((value) { - FirebaseFirestore.instance.collection("scores").add({ - "comment": commentController.text, - "from_user": widget.evento.professionalId, - "is_from_professional": false, - "score": _rating, - "to_user": widget.evento.userId, - }).then((value) { - Navigator.pop(context); - }); - }); - } else { - FirebaseFirestore.instance - .collection("services") - .doc(widget.evento.id) - .update({'user_scored': true}).then((value) { - FirebaseFirestore.instance.collection("scores").add({ - "comment": commentController.text, - "from_user": widget.evento.userId, - "is_from_professional": true, - "score": _rating, - "to_user": widget.evento.professionalId, - }).then((value) { - Navigator.pop(context); - }); - }); - } - }, - style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFF2BA4EC), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(50), - ), - elevation: 0, - minimumSize: const Size(230, 60), - ), - child: const Text( - 'Enviar', - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 18, - ), - ), - ), - ], - ), - ), - ], - ), - ); - } -} diff --git a/lib/src/presentation/screens/service_after.dart b/lib/src/presentation/screens/service_after.dart deleted file mode 100644 index e8e4e8b..0000000 --- a/lib/src/presentation/screens/service_after.dart +++ /dev/null @@ -1,230 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_rating_bar/flutter_rating_bar.dart'; -import 'package:intl/intl.dart'; -import 'package:prosappco/src/components/photo_view.dart'; -import 'package:prosappco/src/components/pop_appbar.dart'; -import 'package:prosappco/src/models/event_model.dart'; -import 'package:prosappco/src/models/scores_model.dart'; -import 'package:prosappco/src/models/setting_model.dart'; -import 'package:prosappco/src/models/user_model.dart'; -import 'package:prosappco/src/presentation/screens/map/service.dart'; -import 'package:prosappco/src/presentation/screens/service_web.dart'; - -class ServiceAfterScreen extends StatefulWidget { - var eventoId; - ServiceAfterScreen({super.key, this.eventoId}); - - @override - State createState() => _ServiceAfterScreenState(); -} - -class _ServiceAfterScreenState extends State { - Event? evento; - UserModel? user; - ScoresModel? scoresModel; - SettingModel? settings; - - @override - void initState() { - super.initState(); - - if (settings == null) { - SettingModel.getSettings().then( - (SettingModel value) => setState(() { - settings = value; - }), - ); - } - - Event.getEventById(widget.eventoId).then((value) { - setState(() { - evento = value; - }); - if (scoresModel == null) { - ScoresModel.scoreTo(value.professionalId, true, false).then( - (ScoresModel s) => setState(() { - scoresModel = s; - }), - ); - } - UserModel.getUser(value.professionalId).then((s) { - setState( - () => user = s, - ); - }); - }); - } - - String formatCurrency(int number) { - final formatter = - NumberFormat.currency(locale: 'es_CO', decimalDigits: 0, symbol: ''); - return '\$${formatter.format(number)}'; - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: PopAppbar( - onPressed: () { - kIsWeb - ? Navigator.pushReplacement( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return const ServiceWebScreen(); - }, - ), - ) - : Navigator.pushReplacement( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return const ServiceScreen(); - }, - ), - ); - }, - label: 'Servicio'), - body: Column( - children: [ - ListTile( - leading: ReferencePhoto( - ref: user?.photo, - size: 55, - sizeCircle: 60, - ), - title: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - '${user?.name}', - style: const TextStyle( - color: Colors.black, - fontWeight: FontWeight.bold, - fontSize: 16, - ), - ), - Text( - '${DateFormat('dd MMMM', 'es').format(DateTime.parse(evento?.day ?? '2023-01-01 00:00:00.000Z'))} ${evento?.range1Hour1 != null ? DateFormat('h:mm a').format(DateTime.parse(evento!.range1Hour1)) : ''}', - style: TextStyle( - color: Colors.grey[600], - fontSize: 16, - ), - ), - ], - ), - subtitle: Column( - children: [ - Row( - children: [ - RatingBar.builder( - initialRating: scoresModel?.average ?? 0, - minRating: 1, - direction: Axis.horizontal, - allowHalfRating: true, - itemCount: 5, - itemSize: 25, - maxRating: 5, - itemPadding: const EdgeInsets.symmetric(horizontal: 0), - itemBuilder: (context, _) => const Icon( - Icons.star, - color: Color(0xFF2BA4EC), - ), - onRatingUpdate: (rating) {}, - ignoreGestures: true, - ), - const SizedBox(width: 5), - Text( - '(${scoresModel?.total.toString()}) ${scoresModel?.average.toStringAsFixed(1)}'), - ], - ), - ], - ), - ), - Container( - margin: - const EdgeInsets.only(left: 40, right: 40, top: 20, bottom: 20), - padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 15), - decoration: BoxDecoration( - color: const Color(0xFFD6F4FF), - borderRadius: BorderRadius.circular(20), - boxShadow: [ - BoxShadow( - color: Colors.grey.withOpacity(0.5), - spreadRadius: 1, - blurRadius: 5, - offset: const Offset(1, 3), - ), - ], - ), - child: Row( - children: [ - const Icon( - Icons.error_outline, - size: 27, - color: Colors.black54, - ), - const SizedBox(width: 15), - evento?.ubicacion != 'sitio' - ? const Text( - 'Servicio a su domicilio.', - style: TextStyle(color: Colors.black, fontSize: 14), - ) - : const Text( - 'Servicio en sitio / consultorio', - style: TextStyle(color: Colors.black, fontSize: 14), - ), - ], - ), - ), - settings?.tarifas == true && evento?.tarifa != 0 - ? Column(children: [ - Text( - formatCurrency(evento?.tarifa ?? 0), - style: const TextStyle( - fontWeight: FontWeight.w600, fontSize: 25), - ), - const Text('Tarifa consulta'), - const SizedBox(height: 10) - ]) - : const SizedBox(), - ListTile( - leading: const Icon(Icons.near_me), - title: Text( - '${evento?.address}', - style: TextStyle(fontSize: 15, color: Colors.grey[600]), - ), - ), - Text( - '"${evento?.description}"', - style: - TextStyle(color: Colors.grey[600], fontStyle: FontStyle.italic), - ), - const Center( - child: Column( - children: [ - Padding( - padding: EdgeInsets.symmetric(vertical: 20), - child: Icon( - Icons.check_circle_outline_rounded, - color: Color(0xFF35A8ED), - size: 70, - ), - ), - Text( - 'Servicio solicitado exitosamente', - style: TextStyle( - color: Color(0xFF35A8ED), - fontSize: 17, - fontWeight: FontWeight.w600), - ), - ], - ), - ), - ], - ), - ); - } -} diff --git a/lib/src/presentation/screens/service_type.dart b/lib/src/presentation/screens/service_type.dart deleted file mode 100644 index ed4a247..0000000 --- a/lib/src/presentation/screens/service_type.dart +++ /dev/null @@ -1,99 +0,0 @@ -import 'package:diacritic/diacritic.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:prosappco/src/components/pop_appbar.dart'; -import 'package:prosappco/src/presentation/screens/profession.dart'; - -class ServiceTypeScreen extends StatefulWidget { - const ServiceTypeScreen({super.key}); - - @override - State createState() => _ServiceTypeScreenState(); -} - -class _ServiceTypeScreenState extends State { - List? filteredProfessions; - TextEditingController searchController = TextEditingController(); - List? _professions; - - @override - void initState() { - super.initState(); - searchController.addListener(() { - setState(() { - if (_professions != null) { - if (searchController.text.isEmpty) { - filteredProfessions = _professions!; - } else { - filteredProfessions = _professions! - .where((profession) => removeDiacritics(profession) - .toLowerCase() - .contains( - removeDiacritics(searchController.text.toLowerCase()))) - .toList(); - } - } - }); - }); - - if (_professions == null) { - getProfessions().then((List element) => setState(() { - _professions = element; - filteredProfessions = element; - })); - } - } - - @override - Widget build(BuildContext context) { - if (filteredProfessions == null) { - return const Center( - child: CircularProgressIndicator( - valueColor: AlwaysStoppedAnimation(Color(0xFF2BA4EC)), - ), - ); - } - List professions = filteredProfessions!; - - return Scaffold( - appBar: PopAppbar( - onPressed: () { - Navigator.pop(context); - }, - label: 'Tipo de servicio'), - body: Column( - children: [ - Padding( - padding: const EdgeInsets.all(10), - child: TextField( - controller: searchController, - decoration: const InputDecoration( - hintText: 'Escribe el tipo de servicio', - prefixIcon: Icon(Icons.assignment_ind_rounded), - ), - ), - ), - Expanded( - child: ListView.builder( - itemCount: professions.length, - itemBuilder: (BuildContext context, int index) { - return ListTile( - title: Text( - professions[index], - style: const TextStyle( - fontSize: 18.0, - color: Colors.black, - ), - ), - onTap: () { - Navigator.pop(context, professions[index]); - }, - ); - }, - ), - ), - ], - ), - ); - } -} diff --git a/lib/src/presentation/screens/service_web.dart b/lib/src/presentation/screens/service_web.dart deleted file mode 100644 index 12fd9f5..0000000 --- a/lib/src/presentation/screens/service_web.dart +++ /dev/null @@ -1,653 +0,0 @@ -import 'dart:convert'; -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:firebase_messaging/firebase_messaging.dart'; -import 'package:firebase_storage/firebase_storage.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:geocoding/geocoding.dart'; -import 'package:geolocator/geolocator.dart'; -import 'package:google_maps_flutter/google_maps_flutter.dart'; -import 'package:prosappco/src/authentication/authentication_repository.dart'; -import 'package:prosappco/src/models/professional_model.dart'; -import 'package:prosappco/src/presentation/screens/profile/profile.dart'; -import 'package:prosappco/src/presentation/widgets/shared/drawer_menu.dart'; -import 'package:prosappco/src/models/event_model.dart'; -import 'package:prosappco/src/models/user_model.dart'; -import 'package:prosappco/src/presentation/screens/professional.dart'; -import 'package:intl/intl.dart'; -import 'package:prosappco/src/presentation/screens/service_after.dart'; -import 'package:prosappco/src/presentation/screens/ubicacion.dart'; -import 'package:http/http.dart' as http; -import 'package:prosappco/src/components/network_utility.dart'; -import 'package:prosappco/src/presentation/widgets/shared/warning_snackbar.dart'; -import 'package:prosappco/src/providers/user_provider.dart'; -import 'package:provider/provider.dart'; - -class ServiceWebScreen extends StatefulWidget { - const ServiceWebScreen({super.key}); - - @override - State createState() => _ServiceOldScreenState(); -} - -class _ServiceOldScreenState extends State { - void _saveToken() async { - FirebaseMessaging messaging = FirebaseMessaging.instance; - - final token = await messaging.getToken(); - - try { - await FirebaseFirestore.instance - .collection('users') - .doc(uid) - .update({'token': token}); - } catch (e) { - print(e); - } - } - - Future sendPushNotification(String pro) async { - try { - http.Response response = await http.post( - Uri.parse('https://fcm.googleapis.com/fcm/send'), - headers: { - 'Content-Type': 'application/json; charset=UTF-8', - 'Authorization': - 'key=AAAAORdR-xU:APA91bF_wblg86jHAC-uexrXPHavYRlk5wge1Gf46m56V4J2D2L37Cp_hf46JZUzpvsWPSpqc5ewHelKI9LTifUG_s2mciMI6e5VLKo7E1R8btbNo7iaM9do2ctoyHKUm1atlZBdKaN2', - }, - body: jsonEncode( - { - 'notification': { - 'body': 'alguien a solicitado tus servicios', - 'title': 'Nueva solicitud', - }, - 'priority': 'high', - 'data': { - 'click_action': 'FLUTTER_NOTIFICATION_CLICK', - 'id': '1', - 'status': 'done', - 'screen': 'solicitud' - }, - 'to': pro - }, - ), - ); - - response; - } catch (e) { - print('error al enviar notificacion $e'); - } - } - - EventoService eventoService = EventoService(); - String ubicacion = ''; - final TextEditingController _locationController = TextEditingController(); - final TextEditingController _ubicationController = TextEditingController(); - final TextEditingController _profesionalController = TextEditingController(); - final TextEditingController _serviceTypeController = TextEditingController(); - final TextEditingController _observacionController = TextEditingController(); - String professionalId = ''; - String professionalToken = ''; - int? professionalTarifa; - String professionalAddress = ''; - String professionalUbicacion = ''; - double? professionalLatitude; - double? professionalLongitude; - String _serviceType = 'Servicio'; - final DateFormat formatter = DateFormat('dd/MM/yyyy'); - final DateTime now = DateTime.now(); - List _placesList = []; - String selectedPlace = ''; - String _coordsOfCity = '0.0,0.0'; - - DateTime? _selectedDate; - TimeOfDay? _selectedTime; - - GoogleMapController? googleMapController; - - Set markers = {}; - - Future _determinePosition() async { - bool serviceEnabled; - LocationPermission permission; - - serviceEnabled = await Geolocator.isLocationServiceEnabled(); - - if (!serviceEnabled) { - return Future.error('Location services are disabled'); - } - - permission = await Geolocator.checkPermission(); - - if (permission == LocationPermission.denied) { - permission = await Geolocator.requestPermission(); - - if (permission == LocationPermission.denied) { - return Future.error('Location permission denied'); - } - } - - if (permission == LocationPermission.deniedForever) { - return Future.error('Location permissions are permanently denied'); - } - - Position position = await Geolocator.getCurrentPosition(); - - return position; - } - - Future _selectDate(BuildContext context) async { - final DateTime? picked = await showDatePicker( - context: context, - initialDate: now, - firstDate: now, - lastDate: DateTime(now.year + 1), - // builder: (context, child) { - // return Theme(data: ThemeData.dark(), child: child!); - // }, - ); - - if (picked != null && picked != _selectedDate) { - if (mounted) { - setState(() { - _selectedDate = picked; - }); - } - } - } - - Future _selectTime(BuildContext context) async { - final TimeOfDay? pickedTime = await showTimePicker( - context: context, - initialTime: TimeOfDay.now(), - ); - - if (pickedTime != null) { - if (mounted) { - setState(() { - _selectedTime = pickedTime; - }); - } - } - } - - Future>>> getUsersWithActiveStatus( - String _serviceType) async { - var querySnapshot; - - if (_serviceType != "Servicio") { - querySnapshot = await FirebaseFirestore.instance - .collection('users') - .where('estado', isEqualTo: 'activo') - .where('profesion', isEqualTo: _serviceType) - .get(); - } else { - querySnapshot = await FirebaseFirestore.instance - .collection('users') - .where('estado', isEqualTo: 'activo') - .get(); - } - - return querySnapshot.docs; - } - - final FirebaseStorage storage = FirebaseStorage.instance; - final uid = AuthenticationRepository.instance.getCurrentUserUid(); - final fcmToken = FirebaseMessaging.instance.getToken(); - - BitmapDescriptor? _markerIcon; - - String _ciudad = '...'; - - void _setInitialCameraPosition(String coordsOfCity) async { - List coords = coordsOfCity.split(','); - double lat = double.parse(coords[0]); - double lng = double.parse(coords[1]); - - googleMapController?.moveCamera( - CameraUpdate.newLatLngZoom( - LatLng(lat, lng), - 14.4746, - ), - ); - - try { - Position position = await _determinePosition(); - - googleMapController?.animateCamera( - CameraUpdate.newCameraPosition( - CameraPosition( - target: LatLng( - position.latitude, - position.longitude, - ), - zoom: 17, - ), - ), - ); - if (mounted) { - setState(() {}); - } - } catch (e) { - print('Error: $e'); - } - } - - @override - void dispose() { - _locationController.dispose(); - _ubicationController.dispose(); - _profesionalController.dispose(); - _serviceTypeController.dispose(); - _observacionController.dispose(); - - super.dispose(); - } - - void updateMarkersForServiceType(String serviceType) { - getUsersWithActiveStatus(serviceType).then((value) { - markers.clear(); - for (var doc in value) { - final element = doc.data()!; - - if (element['latitude'] != null && element['longitude'] != null) { - markers.add( - Marker( - icon: _markerIcon!, - markerId: MarkerId(doc.id), - position: LatLng( - element['latitude'], - element['longitude'], - ), - ), - ); - } - } - }).catchError((e) { - print('Error al actualizar los marcadores: $e'); - }); - } - - String? settings; - - @override - void initState() { - super.initState(); - - if (_ciudad == '...') { - AuthenticationRepository.instance - .getCity(uid.toString()) - .then((String s) { - if (s.isEmpty) { - FirebaseFirestore.instance.collection('users').doc(uid).set({ - 'city': 'Cúcuta', - }).then((_) { - if (mounted) { - setState(() { - _ciudad = 'Cúcuta'; - }); - } - }); - } else { - if (mounted) { - setState(() { - _ciudad = s; - }); - } - } - }); - } - - if (_coordsOfCity == '0.0,0.0') { - AuthenticationRepository.instance - .getCoordsOfCity(uid.toString()) - .then((String s) { - if (mounted) { - setState(() { - _coordsOfCity = s; - _setInitialCameraPosition(_coordsOfCity); - }); - } - }); - } - - _saveToken(); - - BitmapDescriptor.fromAssetImage( - const ImageConfiguration(size: Size(6, 6)), 'images/pro_marke.png') - .then((icon) { - if (mounted) { - setState(() { - _markerIcon = icon; - }); - } - }); - - updateMarkersForServiceType(_serviceType); - } - - static CameraPosition initialCameraPosition = const CameraPosition( - target: LatLng(7.8939100, -72.5078200), - zoom: 14.4746, - ); - - void setInitialCameraPosition(String coordsOfCity) { - if (coordsOfCity != '0.0,0.0') { - List coords = coordsOfCity.split(','); - double lat = double.parse(coords[0]); - double lng = double.parse(coords[1]); - - initialCameraPosition = CameraPosition( - target: LatLng(lat, lng), - zoom: 14.4746, - ); - } - } - - late String lat; - late String long; - - double latUser = 0.0; - double lngUser = 0.0; - - var coordinates; - - 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; - } - - void placeAutoComplete(String query) async { - Uri uri = Uri.https("admin.prosapp.co", "/autocomplete", { - "input": query, - "location": _coordsOfCity, - }); - - String? response = await NetworkUtility.fetchUrl(uri); - - if (response != null) { - if (mounted) { - setState(() { - _placesList = jsonDecode(response.toString())['results']; - }); - } - } - } - - @override - Widget build(BuildContext context) { - final userProvider = Provider.of(context); - UserModel? user = userProvider.user; - - return Scaffold( - backgroundColor: const Color(0xFFD6F4FF), - drawer: DrawerMenu(), - appBar: AppBar( - elevation: 0, - title: const Text( - 'Prosapp', - style: TextStyle( - color: Colors.white, - fontSize: 20, - ), - )), - body: Center( - child: SizedBox( - width: 300, - height: 470, - child: Card( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10), - ), - color: Colors.white, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 20.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - TextFormField( - controller: _profesionalController, - readOnly: true, - onTap: () async { - final dynamic datos = await Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return ProfessionalScreen( - profession: _serviceTypeController.text, - ); - }, - ), - ); - if (datos != null) { - Professional? profesional = datos[0]; - ubicacion = datos[1]; - - if (profesional != null) { - if (mounted) { - setState(() { - _profesionalController.text = - profesional.name.toString(); - professionalId = profesional.id; - professionalTarifa = profesional.tarifa ?? 0; - professionalUbicacion = profesional.ubicacion; - professionalAddress = _ubicationController.text; - if (profesional.token != '') { - professionalToken = profesional.token!; - print(professionalToken); - } - - if (ubicacion == 'sitio') { - professionalAddress = profesional.realAddress; - _ubicationController.text = - profesional.realAddress; - latUser = profesional.latitude; - lngUser = profesional.longitude; - } - }); - } - } - } - }, - decoration: const InputDecoration( - prefixIcon: Icon(Icons.assignment_ind_rounded), - suffixIcon: Icon(Icons.arrow_drop_down), - hintText: 'Seleccionar profesional'), - ), - const SizedBox(height: 20.0), - TextFormField( - controller: _ubicationController, - readOnly: true, - onTap: () async { - final List datos = await Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return const UbicacionScreen(); - }, - ), - ); - - if (datos.length == 3) { - final formattedAddress = datos[0]; - final lat = datos[1]; - final lng = datos[2]; - - if (mounted) { - setState(() { - _ubicationController.text = formattedAddress; - latUser = lat; - lngUser = lng; - }); - } - } - }, - decoration: const InputDecoration( - hintText: 'Escribe tu ubicación', - prefixIcon: Icon(Icons.location_on), - ), - ), - const SizedBox(height: 20.0), - TextFormField( - onTap: () { - if (_profesionalController.text.isNotEmpty) { - _selectDate(context); - } else { - WarningSnackbar.show( - title: 'Selecciona un profesional', - message: - 'Selecciona un profesional antes de elegir la fecha y la hora de la cita.', - ); - } - }, - readOnly: true, - decoration: InputDecoration( - prefixIcon: const Icon(Icons.calendar_month), - suffixIcon: _selectedDate == null - ? const Icon(Icons.arrow_drop_down) - : null, - hintText: 'Fecha', - ), - controller: TextEditingController( - text: _selectedDate == null - ? '' - : formatter.format(_selectedDate!)), - ), - const SizedBox(height: 20.0), - TextFormField( - onTap: () { - if (_profesionalController.text.isNotEmpty) { - _selectTime(context); - } else { - WarningSnackbar.show( - title: 'Selecciona un profesional', - message: - 'Selecciona un profesional antes de elegir la fecha y la hora de la cita.', - ); - } - }, - readOnly: true, - decoration: const InputDecoration( - prefixIcon: Icon(Icons.access_time), - suffixIcon: Icon(Icons.arrow_drop_down), - hintText: 'Hora', - ), - controller: TextEditingController( - text: _selectedTime == null - ? '' - : ' ${_selectedTime!.format(context)}'), - ), - const SizedBox(height: 20.0), - TextFormField( - controller: _observacionController, - decoration: const InputDecoration( - prefixIcon: Icon(Icons.message_outlined), - hintText: 'Observaciones'), - ), - const SizedBox(height: 40.0), - ElevatedButton( - onPressed: () { - if (user?.name == null || - user?.name == '' || - user?.phoneNumber == null || - user?.phoneNumber == '') { - Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return const ProfileScreen(); - }, - ), - ); - } else { - try { - final DateTime combinedDate = DateTime( - _selectedDate!.year, - _selectedDate!.month, - _selectedDate!.day, - _selectedTime!.hour, - _selectedTime!.minute, - ); - - DateTime time2 = - combinedDate.add(const Duration(hours: 2)); - - UserModel.getUser(uid.toString()).then((value) { - eventoService - .createEvent( - value.name, - _observacionController.text, - '$_selectedDate', - '$combinedDate', - '$time2', - professionalId, - ubicacion, - professionalAddress, - latUser, - lngUser, - 'pendiente', - professionalTarifa == 0 ? 0 : professionalTarifa, - false, - false, - ) - .then((value) { - if (professionalToken != '') { - sendPushNotification(professionalToken); - } - Navigator.pushReplacement( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return ServiceAfterScreen( - eventoId: value, - ); - }, - ), - ); - }); - }); - } catch (e) { - WarningSnackbar.show( - title: 'Llena todos los campos', - message: 'Asegurate de llenar todos los campos.', - ); - } - } - }, - style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFF2BA4EC), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(50), - ), - elevation: 0, - minimumSize: const Size(230, 50), - ), - child: const Text( - 'Solicitar servicio', - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 18, - ), - ), - ), - ], - ), - ), - ), - ), - ), - ); - } -} diff --git a/lib/src/presentation/screens/solicitudes.dart b/lib/src/presentation/screens/solicitudes.dart deleted file mode 100644 index 9f988b6..0000000 --- a/lib/src/presentation/screens/solicitudes.dart +++ /dev/null @@ -1,194 +0,0 @@ -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_rating_bar/flutter_rating_bar.dart'; -import 'package:intl/intl.dart'; -import 'package:prosappco/src/components/drawer_professional.dart'; -import 'package:prosappco/src/models/event_model.dart'; -import 'package:prosappco/src/models/scores_model.dart'; -import 'package:prosappco/src/presentation/screens/cita.dart'; - -class SolicitudScreen extends StatelessWidget { - SolicitudScreen({super.key}); - - DateTime today = DateTime.now(); - - @override - Widget build(BuildContext context) { - return SafeArea( - child: Scaffold( - appBar: AppBar( - backgroundColor: Colors.white, - iconTheme: const IconThemeData( - color: Colors.black, - ), - title: const Text( - 'Solicitudes', - style: TextStyle( - color: Colors.black, - ), - ), - ), - drawer: DrawerProfessional(), - body: SingleChildScrollView( - child: Column( - children: [ - _eventList(), - ], - ), - ), - ), - ); - } - - Widget _eventList() { - return StreamBuilder>( - stream: FirebaseFirestore.instance - .collection('services') - .where('professional_id', isEqualTo: uid) - .where('status', whereIn: ['pendiente', '']) - .snapshots() - .asyncMap((snapshot) async { - try { - List eventos = []; - for (var element in snapshot.docs) { - final event = Event.fromJson(element.data()); - event.scoresModel = - await ScoresModel.scoreTo(event.userId, false, false); - event.id = element.id; - eventos.add(event); - } - return eventos; - } catch (e) { - print('Error getByProId $e'); - return []; - } - }), - builder: (BuildContext context, AsyncSnapshot> snapshot) { - if (!snapshot.hasData) { - return const Center( - child: CircularProgressIndicator(), - ); - } - - List eventos = []; - - try { - eventos.addAll(snapshot.data!); - eventos.sort((a, b) => a.timeStamp!.compareTo(b.timeStamp!)); - } catch (e) { - print("Error" + e.toString()); - } - - if (eventos.isEmpty) { - return const Padding( - padding: EdgeInsets.symmetric(vertical: 50), - child: Center(child: Text('No tienes citas')), - ); - } - return Column( - children: [ - ...eventos.map( - (event) => ListTile( - onTap: () { - Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return CitaScreen(evento: event); - }, - ), - ); - }, - leading: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text(DateFormat('h:mm a') - .format(DateTime.parse(event.range1Hour1))), - ], - ), - title: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - event.title, - style: const TextStyle( - color: Colors.black, - fontWeight: FontWeight.bold, - fontSize: 16, - ), - ), - Text( - '${DateFormat('dd MMMM', 'es').format(DateTime.parse(event.day))} - ${DateFormat('h:mm a').format(DateTime.parse(event.range1Hour1))}', - style: const TextStyle( - color: Colors.grey, - fontSize: 16, - ), - ) - ], - ), - - // RichText( - // text: TextSpan( - // children: [ - // TextSpan( - // text: '${event.title}, ', - // style: const TextStyle( - // color: Colors.black, - // fontWeight: FontWeight.bold, - // fontSize: 16, - // ), - // ), - // TextSpan( - // text: DateFormat('dd MMM', 'es') - // .format(DateTime.parse(event.day)), - // style: const TextStyle( - // color: Colors.grey, - // fontSize: 16, - // ), - // ), - // ], - // ), - // ), - subtitle: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - RatingBar.builder( - initialRating: event.scoresModel?.average ?? 0, - minRating: 1, - direction: Axis.horizontal, - allowHalfRating: true, - itemCount: 5, - itemSize: 25, - maxRating: 5, - itemPadding: - const EdgeInsets.symmetric(horizontal: 0), - itemBuilder: (context, _) => const Icon( - Icons.star, - color: Color(0xFF2BA4EC), - ), - onRatingUpdate: (rating) {}, - ignoreGestures: true, - ), - const SizedBox(width: 5), - Text( - '(${event.scoresModel?.total.toString()}) ${event.scoresModel?.average.toStringAsFixed(1)}'), - ], - ), - Text( - '"${event.description}"', - style: const TextStyle(fontStyle: FontStyle.italic), - ), - ], - ), - trailing: const Icon(Icons.keyboard_arrow_right), - ), - ), - ], - ); - }, - ); - } -} diff --git a/lib/src/presentation/screens/support.dart b/lib/src/presentation/screens/support.dart deleted file mode 100644 index a2a8572..0000000 --- a/lib/src/presentation/screens/support.dart +++ /dev/null @@ -1,157 +0,0 @@ -import 'package:community_material_icon/community_material_icon.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_email_sender/flutter_email_sender.dart'; -import 'package:prosappco/src/components/pop_appbar.dart'; -import 'package:prosappco/src/models/setting_model.dart'; -import 'package:url_launcher/url_launcher.dart'; - -class SupportScreen extends StatefulWidget { - const SupportScreen({super.key}); - - @override - State createState() => SsupportStateScreen(); -} - -class SsupportStateScreen extends State { - SettingModel? settings; - final String subject = 'Soporte Prosapp'; - final String body = ''; - - Future _sendWhatsapp(String? number) async { - final _whatsappUrl = 'https://api.whatsapp.com/send?phone=$number&text=Hola%21+soy+usuario+de+Prosapp+y+quisiera+conocer+mas+sobre+esta+app+%F0%9F%98%81'; - if (!await launch(_whatsappUrl)) { - throw Exception('Could not launch $_whatsappUrl'); - } - } - - Future _sendEmail(String recipients) async { - final Email email = Email( - body: body, - subject: subject, - recipients: [recipients], - isHTML: false, - ); - - await FlutterEmailSender.send(email); - } - - void _sendEmailWeb(String recipients) async { - final email = 'mailto:$recipients?subject=${Uri.encodeComponent(recipients)}&body=${Uri.encodeComponent(body)}'; - if (await canLaunch(email)) { - await launch(email); - } else {} - } - - @override - void initState() { - super.initState(); - if (settings == null) { - SettingModel.getSettings().then( - (SettingModel value) => setState(() { - settings = value; - }), - ); - } - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: PopAppbar( - onPressed: () { - Navigator.pop(context); - }, - label: 'Soporte'), - body: Column( - children: [ - Padding( - padding: const EdgeInsets.only(top: 30, left: 35, right: 35), - child: Text( - '${settings?.titulo}', - style: const TextStyle(fontWeight: FontWeight.w600), - ), - ), - Padding( - padding: const EdgeInsets.symmetric(vertical: 30, horizontal: 35), - child: Text('${settings?.parrafo}'), - ), - Row( - children: [ - const Expanded(child: SizedBox()), - ElevatedButton( - onPressed: () { - _sendWhatsapp(settings?.numero); - }, - style: ElevatedButton.styleFrom( - foregroundColor: Colors.white, - backgroundColor: const Color(0xFF2BA4EC), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(50), - side: const BorderSide( - color: Color(0xFF2BA4EC), - width: 2, - ), - ), - ), - child: const Padding( - padding: EdgeInsets.symmetric(vertical: 18, horizontal: 0), - child: Icon( - CommunityMaterialIcons.whatsapp, - size: 30, - color: Colors.white, - ), - ), - ), - const SizedBox(width: 20), - ElevatedButton( - onPressed: () { - if (kIsWeb) { - _sendEmailWeb(settings?.email ?? ''); - } else { - _sendEmail(settings?.email ?? ''); - } - }, - style: ElevatedButton.styleFrom( - foregroundColor: Colors.white, - backgroundColor: const Color(0xFF2BA4EC), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(50), - side: const BorderSide( - color: Color(0xFF2BA4EC), - width: 2, - ), - ), - ), - child: const Padding( - padding: EdgeInsets.symmetric(vertical: 18, horizontal: 0), - child: Icon( - Icons.email_outlined, - size: 30, - color: Colors.white, - ), - ), - ), - const Expanded(child: SizedBox()), - ], - ), - Padding( - padding: const EdgeInsets.symmetric(vertical: 50), - child: Column( - children: [ - const Text( - 'Horario de atención:', - style: TextStyle(fontWeight: FontWeight.w600), - ), - const SizedBox(height: 20), - Text('${settings?.dias}'), - const SizedBox(height: 5), - Text('${settings?.horas}'), - ], - ), - ), - ], - ), - ); - } -} diff --git a/lib/src/presentation/screens/ubicacion.dart b/lib/src/presentation/screens/ubicacion.dart deleted file mode 100644 index 4af661b..0000000 --- a/lib/src/presentation/screens/ubicacion.dart +++ /dev/null @@ -1,98 +0,0 @@ -import 'package:flutter/material.dart'; -import 'dart:convert'; -import 'package:prosappco/src/components/network_utility.dart'; -import 'package:prosappco/src/components/pop_appbar.dart'; - -import '../../authentication/authentication_repository.dart'; - -class UbicacionScreen extends StatefulWidget { - const UbicacionScreen({super.key}); - - @override - State createState() => _UbicacionScreenState(); -} - -class _UbicacionScreenState extends State { - List _placesList = []; - String selectedPlace = ''; - String _coordsOfCity = '0.0,0.0'; - - @override - void initState() { - super.initState(); - - final uid = AuthenticationRepository.instance.getCurrentUserUid(); - - if (_coordsOfCity == '0.0,0.0') { - AuthenticationRepository.instance - .getCoordsOfCity(uid.toString()) - .then((String s) => setState(() { - _coordsOfCity = s; - })); - } - } - - void placeAutoComplete(String query) async { - Uri uri = Uri.https("admin.prosapp.co", "/autocomplete", { - "input": query, - "location": _coordsOfCity, - }); - - String? response = await NetworkUtility.fetchUrl(uri); - - if (response != null) { - setState(() { - _placesList = jsonDecode(response.toString())['results']; - }); - } - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: PopAppbar( - onPressed: () { - Navigator.pop(context); - }, - label: 'Tu ubicación'), - body: Center( - child: SizedBox( - width: 300, - child: Column( - children: [ - const SizedBox(height: 20.0), - TextFormField( - decoration: const InputDecoration( - hintText: 'Escribe tu ubicación', - prefixIcon: Icon(Icons.location_on), - ), - onChanged: (value) { - String modifiedValue = value.replaceAll(' ', '_'); - placeAutoComplete(modifiedValue); - }, - ), - const SizedBox(height: 20.0), - Expanded( - child: ListView.builder( - itemCount: _placesList.length, - itemBuilder: (context, index) { - return ListTile( - onTap: () { - Navigator.pop(context, [ - _placesList[index]['formatted_address'], - _placesList[index]['geometry']['location']['lat'], - _placesList[index]['geometry']['location']['lng'] - ]); - }, - title: Text(_placesList[index]['formatted_address']), - ); - }, - ), - ), - ], - ), - ), - ), - ); - } -} diff --git a/lib/src/presentation/widgets/shared/drawer_menu.dart b/lib/src/presentation/widgets/shared/drawer_menu.dart deleted file mode 100644 index 7fa8cda..0000000 --- a/lib/src/presentation/widgets/shared/drawer_menu.dart +++ /dev/null @@ -1,410 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_rating_bar/flutter_rating_bar.dart'; -import 'package:prosappco/src/authentication/authentication_repository.dart'; -import 'package:prosappco/src/components/photo_view.dart'; -import 'package:prosappco/src/models/scores_model.dart'; -import 'package:prosappco/src/models/user_model.dart'; -import 'package:prosappco/src/presentation/widgets/shared/warning_snackbar.dart'; -import 'package:prosappco/src/providers/user_provider.dart'; -import 'package:prosappco/src/presentation/screens/configuracion.dart'; -import 'package:prosappco/src/presentation/screens/professional_profile_web.dart'; -import 'package:prosappco/src/presentation/screens/profile/profile.dart'; -import 'package:prosappco/src/presentation/screens/reputation.dart'; -import 'package:prosappco/src/presentation/screens/support.dart'; -import 'package:prosappco/src/presentation/screens/web_view.dart'; -import 'package:url_launcher/url_launcher.dart'; -import 'package:get/get.dart'; -import 'package:provider/provider.dart'; - -class DrawerMenu extends StatelessWidget { - final uid = AuthenticationRepository.instance.getCurrentUserUid(); - - Future _irSugerencias() async { - const url = 'https://admin.prosapp.co/sugerencias'; - - final Uri _url = Uri.parse(url); - - if (await canLaunchUrl(_url)) { - await launchUrl(_url); - } else { - throw 'No se pudo abrir la URL $url'; - } - } - - @override - Widget build(BuildContext context) { - final userProvider = Provider.of(context); - UserModel? user = userProvider.user; - ScoresModel? score = userProvider.score; - - return Drawer( - child: Column( - children: [ - Container( - color: Colors.white, - child: Column( - children: [ - infoUser(context, user), - ], // - ), - ), - Container( - decoration: BoxDecoration( - boxShadow: [ - BoxShadow( - color: Colors.grey.withOpacity(0.3), - spreadRadius: 1, - blurRadius: 3, - offset: const Offset(0, 0), // changes position of shadow - ), - ], - ), - child: Divider( - height: 0, - color: Colors.grey[300], - ), - ), - Expanded( - child: Column( - children: [ - ListTile( - onTap: () { - if (ModalRoute.of(context)?.settings.name != - '/misservicios') { - Navigator.pushNamed(context, '/misservicios'); - } else { - Scaffold.of(context).openEndDrawer(); - } - }, - leading: const Icon( - Icons.history, - color: Colors.black, - ), - title: const Text( - 'Mis servicios', - style: TextStyle(fontSize: 15), - ), - ), - ListTile( - onTap: () { - Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return const ProfileScreen(); - }, - ), - ); - }, - leading: const Icon( - Icons.person_outline, - color: Colors.black, - ), - title: const Text( - 'Mi perfil', - style: TextStyle(fontSize: 15), - ), - ), - ListTile( - onTap: () { - Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return const ConfiguracionScreen(); - }, - ), - ); - }, - leading: const Icon( - Icons.construction_outlined, - color: Colors.black, - ), - title: const Text( - 'Configuración', - style: TextStyle(fontSize: 15), - ), - ), - ListTile( - onTap: () { - Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return const SupportScreen(); - }, - ), - ); - }, - leading: const Icon( - Icons.question_mark_rounded, - color: Colors.black, - ), - title: const Text( - 'Soporte', - style: TextStyle(fontSize: 15), - ), - ), - ListTile( - onTap: () { - if (kIsWeb) { - _irSugerencias(); - } else { - Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return WebViewScreen( - label: 'Sugerencias', - link: 'https://admin.prosapp.co/sugerencias', - ); - }, - ), - ); - } - }, - leading: const Icon( - Icons.campaign_outlined, - color: Colors.black, - ), - title: const Text( - 'Sugerencias', - style: TextStyle(fontSize: 15), - ), - ), - Builder(builder: (BuildContext context) { - return ListTile( - onTap: () { - if (ModalRoute.of(context)?.settings.name != - '/servicio') { - Navigator.pushNamed(context, '/servicio'); - } else { - Scaffold.of(context).openEndDrawer(); - } - }, - trailing: const Icon( - Icons.keyboard_arrow_right, - color: Colors.white, - ), - title: const Text( - 'Solicitar servicio', - style: TextStyle( - color: Colors.white, - fontSize: 17, - fontWeight: FontWeight.bold), - ), - tileColor: const Color(0xFF2BA4EC), - contentPadding: - const EdgeInsets.symmetric(vertical: 5, horizontal: 16), - ); - }), - Container( - decoration: BoxDecoration( - boxShadow: [ - BoxShadow( - color: Colors.grey.withOpacity(0.5), - spreadRadius: 2, - blurRadius: 3, - offset: - const Offset(0, 2), // changes position of shadow - ), - ], - ), - child: Container( - color: const Color(0xFFD6F4FF), - child: ListTile( - tileColor: const Color(0xFFD6F4FF), - onTap: () { - Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return const ReputationScreen(); - }, - ), - ); - }, - trailing: const Icon(Icons.keyboard_arrow_right, - color: Colors.black), - title: const Text( - 'Reputación', - style: TextStyle(color: Colors.black), - ), - subtitle: Row( - children: [ - RatingBar.builder( - initialRating: score?.average ?? 0, - minRating: 1, - direction: Axis.horizontal, - allowHalfRating: true, - itemCount: 5, - itemSize: 25, - maxRating: 5, - itemPadding: - const EdgeInsets.symmetric(horizontal: 0), - itemBuilder: (context, _) => const Icon( - Icons.star, - color: Color(0xFF2BA4EC), - ), - onRatingUpdate: (rating) {}, - ignoreGestures: true, - ), - const SizedBox(width: 5), - Text( - '(${score?.total.toString()}) ${score?.average.toStringAsFixed(1)}'), - ], - ), - ), - ), - ), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - 'Prosapp', - style: TextStyle(fontSize: 10, color: Colors.grey[700]), - ), - Padding( - padding: const EdgeInsets.only(top: 7, left: 3, right: 3), - child: Text( - '®', - style: TextStyle(fontSize: 25, color: Colors.grey[700]), - ), - ), - Text( - 'todos los derechos reservados', - style: TextStyle(fontSize: 10, color: Colors.grey[700]), - ), - ], - ), - ], - ), - ), - Column( - children: [ - ElevatedButton( - onPressed: () { - UserModel? user = userProvider.user; - if (user?.name == '' || - user?.city == '' || - user?.phoneNumber == '' || - user?.phoneNumber == null) { - WarningSnackbar.show( - title: 'Completa tu perfil', - message: - 'Para ser un profesional registrado, asegúrate de llenar todos los campos necesarios y no olvides guardar tus cambios para que surtan efecto.', - ); - } else { - if (user?.phoneNumber != user?.phoneNumber) { - Get.snackbar( - 'Tu número de teléfono sigue sin cambios.', - 'Para guardar esta información, dirígete a tu perfil y selecciona la opción Guardar', - snackPosition: SnackPosition.BOTTOM, - ); - } else { - if (user?.state == 'pendiente' || user?.state == null) { - Navigator.pop(context); - if (kIsWeb) { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => - const ProfessionalProfileWebScreen(), - ), - ); - } else { - Navigator.pushNamed(context, '/profesionalProfile'); - } - } else if (user?.state == 'revision') { - Navigator.pushNamed(context, '/profesionalRevision'); - } else if (user?.state == 'activo') { - Navigator.pushNamed(context, '/solicitud'); - } - } - } - }, - style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFF2BA4EC), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(50), - ), - elevation: 0, - minimumSize: const Size(230, 45), - ), - child: const Text( - 'Modo profesional', - style: TextStyle( - color: Colors.white, - fontSize: 15, - ), - ), - ), - const SizedBox(height: 5), - ElevatedButton( - onPressed: () async { - await AuthenticationRepository.instance.logout(uid!); - - userProvider.setNullUser(); - }, - style: ElevatedButton.styleFrom( - backgroundColor: Colors.red, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(20)), - ), - minimumSize: const Size(230, 40), - ), - child: const Text( - 'Cerrar Sesión', - style: TextStyle( - color: Colors.white, - fontSize: 15, - ), - ), - ), - ], - ), - const SizedBox(height: 10), - ], - ), - ); - } - - ListTile infoUser(BuildContext context, UserModel? user) { - return ListTile( - onTap: () { - Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return const ProfileScreen(); - }, - ), - ); - }, - title: Text(user?.name ?? '', - style: const TextStyle(fontWeight: FontWeight.bold)), - subtitle: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - user?.phoneNumber ?? '', - style: const TextStyle(fontSize: 12), - ), - Text( - user?.city ?? '', - style: const TextStyle(fontSize: 12), - ), - ], - ), - leading: ReferencePhoto( - ref: user?.photo, - size: 50, - sizeCircle: 50, - ), - trailing: const Icon(Icons.keyboard_arrow_right, color: Colors.black), - contentPadding: const EdgeInsets.symmetric(vertical: 20, horizontal: 16), - ); - } -} diff --git a/lib/src/presentation/widgets/shared/loading_item_list.dart b/lib/src/presentation/widgets/shared/loading_item_list.dart deleted file mode 100644 index f4efedd..0000000 --- a/lib/src/presentation/widgets/shared/loading_item_list.dart +++ /dev/null @@ -1,50 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:shimmer/shimmer.dart'; - -class LoadingItemList extends StatelessWidget { - final bool useCircleAvatar; - - const LoadingItemList({super.key, required this.useCircleAvatar}); - - @override - Widget build(BuildContext context) { - return Shimmer.fromColors( - baseColor: Colors.grey[300]!, - highlightColor: Colors.grey[100]!, - child: ListTile( - leading: useCircleAvatar - ? const CircleAvatar( - radius: 25, - backgroundColor: Colors.white, - ) - : Container( - width: 45, - height: 20, - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(8), - ), - ), - title: Container( - height: 20, - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(8), - ), - ), - subtitle: Container( - height: 15, - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(8), - ), - ), - trailing: const Icon( - Icons.chevron_right, - color: Colors.white, - size: 30, - ), - ), - ); - } -} diff --git a/lib/src/presentation/widgets/shared/notificacion_snackbar.dart b/lib/src/presentation/widgets/shared/notificacion_snackbar.dart deleted file mode 100644 index 4262954..0000000 --- a/lib/src/presentation/widgets/shared/notificacion_snackbar.dart +++ /dev/null @@ -1,51 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:get/get.dart'; - -class NotificationSnackbar { - static void show({ - required String title, - required String message, - SnackPosition position = SnackPosition.TOP, - Duration duration = const Duration(seconds: 4), - Color backgroundColor = Colors.red, - Color textColor = Colors.white, - double borderRadius = 10.0, - EdgeInsets margin = const EdgeInsets.all(10.0), - SnackStyle snackStyle = SnackStyle.FLOATING, - Duration animationDuration = const Duration(milliseconds: 800), - bool isDismissible = true, - DismissDirection dismissDirection = DismissDirection.horizontal, - Curve forwardAnimationCurve = Curves.easeOutBack, - Curve reverseAnimationCurve = Curves.easeInBack, - Icon icon = const Icon(Icons.warning, color: Colors.white), - bool shouldIconPulse = true, - }) { - Get.snackbar( - title, - message, - snackPosition: position, - duration: duration, - backgroundColor: backgroundColor, - colorText: textColor, - borderRadius: borderRadius, - margin: margin, - snackStyle: snackStyle, - animationDuration: animationDuration, - isDismissible: isDismissible, - dismissDirection: dismissDirection, - forwardAnimationCurve: forwardAnimationCurve, - reverseAnimationCurve: reverseAnimationCurve, - icon: icon, - shouldIconPulse: shouldIconPulse, - titleText: Text( - title, - style: const TextStyle( - fontSize: 18.0, fontWeight: FontWeight.bold, color: Colors.white), - ), - messageText: Text( - message, - style: const TextStyle(fontSize: 16.0, color: Colors.white), - ), - ); - } -} diff --git a/lib/src/presentation/widgets/shared/primary_button.dart b/lib/src/presentation/widgets/shared/primary_button.dart deleted file mode 100644 index 2ecbea1..0000000 --- a/lib/src/presentation/widgets/shared/primary_button.dart +++ /dev/null @@ -1,41 +0,0 @@ -import 'package:flutter/material.dart'; - -class PrimaryButton extends StatelessWidget { - final VoidCallback onPressed; - final String text; - final double? minWidth; - final double? minHeight; - final bool isEnabled; - - const PrimaryButton({ - super.key, - required this.onPressed, - required this.text, - this.minWidth = 200, - this.minHeight = 50, - this.isEnabled = true, - }); - - @override - Widget build(BuildContext context) { - return ElevatedButton( - onPressed: isEnabled ? onPressed : null, - style: ElevatedButton.styleFrom( - backgroundColor: isEnabled ? const Color(0xFF2BA4EC) : Colors.grey, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(50), - ), - elevation: 0, - minimumSize: Size(minWidth!, minHeight!), - ), - child: Text( - text, - style: TextStyle( - color: isEnabled ? Colors.white : Colors.black, - fontWeight: FontWeight.bold, - fontSize: 17, - ), - ), - ); - } -} diff --git a/lib/src/presentation/widgets/shared/primary_checkbox.dart b/lib/src/presentation/widgets/shared/primary_checkbox.dart deleted file mode 100644 index fa448d7..0000000 --- a/lib/src/presentation/widgets/shared/primary_checkbox.dart +++ /dev/null @@ -1,28 +0,0 @@ -import 'package:flutter/material.dart'; - -class PrimaryCheckbox extends StatelessWidget { - final String text; - final bool initialValue; - final Function(bool) onChanged; - - const PrimaryCheckbox({ - Key? key, - required this.text, - required this.initialValue, - required this.onChanged, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return CheckboxListTile( - title: Text( - text, - style: const TextStyle(fontSize: 15), - ), - value: initialValue, - onChanged: (value) { - onChanged(value!); - }, - ); - } -} diff --git a/lib/src/presentation/widgets/shared/warning_snackbar.dart b/lib/src/presentation/widgets/shared/warning_snackbar.dart deleted file mode 100644 index e8028fe..0000000 --- a/lib/src/presentation/widgets/shared/warning_snackbar.dart +++ /dev/null @@ -1,51 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:get/get.dart'; - -class WarningSnackbar { - static void show({ - required String title, - required String message, - SnackPosition position = SnackPosition.TOP, - Duration duration = const Duration(seconds: 4), - Color backgroundColor = Colors.red, - Color textColor = Colors.white, - double borderRadius = 10.0, - EdgeInsets margin = const EdgeInsets.all(10.0), - SnackStyle snackStyle = SnackStyle.FLOATING, - Duration animationDuration = const Duration(milliseconds: 800), - bool isDismissible = true, - DismissDirection dismissDirection = DismissDirection.horizontal, - Curve forwardAnimationCurve = Curves.easeOutBack, - Curve reverseAnimationCurve = Curves.easeInBack, - Icon icon = const Icon(Icons.warning, color: Colors.white), - bool shouldIconPulse = true, - }) { - Get.snackbar( - title, - message, - snackPosition: position, - duration: duration, - backgroundColor: backgroundColor, - colorText: textColor, - borderRadius: borderRadius, - margin: margin, - snackStyle: snackStyle, - animationDuration: animationDuration, - isDismissible: isDismissible, - dismissDirection: dismissDirection, - forwardAnimationCurve: forwardAnimationCurve, - reverseAnimationCurve: reverseAnimationCurve, - icon: icon, - shouldIconPulse: shouldIconPulse, - titleText: Text( - title, - style: const TextStyle( - fontSize: 18.0, fontWeight: FontWeight.bold, color: Colors.white), - ), - messageText: Text( - message, - style: const TextStyle(fontSize: 16.0, color: Colors.white), - ), - ); - } -} diff --git a/lib/src/providers/user_provider.dart b/lib/src/providers/user_provider.dart deleted file mode 100644 index f884178..0000000 --- a/lib/src/providers/user_provider.dart +++ /dev/null @@ -1,54 +0,0 @@ -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:flutter/material.dart'; -import 'package:firebase_auth/firebase_auth.dart'; -import 'package:prosappco/src/models/scores_model.dart'; -import 'package:prosappco/src/models/user_model.dart'; - -class UserProvider extends ChangeNotifier { - final FirebaseFirestore _firestore = FirebaseFirestore.instance; - UserModel? _user; - ScoresModel? _score; - Stream>>? _stream; - - UserProvider() { - initUserProvider(); - } - - void initUserProvider() { - var uid = FirebaseAuth.instance.currentUser?.uid; - if (uid == null) return; - _stream = _firestore.collection('users').doc(uid).snapshots(); - _stream?.listen((documentSnapshot) async { - if (documentSnapshot.exists) { - final data = documentSnapshot.data() as Map; - _user = UserModel.fromFirestore(data); - _score = await ScoresModel.scoreTo(uid, false, false); - notifyListeners(); - } - }, onDone: () {}, onError: (error) {}); - } - - void setNullUser() { - _user = null; - _score = null; - _stream = null; - notifyListeners(); - } - - Future updateUserDataAndScores() async { - var uid = FirebaseAuth.instance.currentUser?.uid; - if (uid == null) return; - - var documentSnapshot = await _firestore.collection('users').doc(uid).get(); - - if (documentSnapshot.exists) { - final data = documentSnapshot.data() as Map; - _user = UserModel.fromFirestore(data); - _score = await ScoresModel.scoreTo(uid, false, false); - notifyListeners(); - } - } - - UserModel? get user => _user; - ScoresModel? get score => _score; -} diff --git a/lib/src/services/firebase_messaging.dart b/lib/src/services/firebase_messaging.dart deleted file mode 100644 index 56e5610..0000000 --- a/lib/src/services/firebase_messaging.dart +++ /dev/null @@ -1,56 +0,0 @@ -import 'package:firebase_messaging/firebase_messaging.dart'; -import 'package:get/get.dart'; - -class FirebaseMessagingService { - FirebaseMessaging _firebaseMessaging = FirebaseMessaging.instance; - - Future initializeFirebaseMessaging() async { - // Solicitar permisos de notificación si es necesario (opcional) - NotificationSettings settings = await _firebaseMessaging.requestPermission( - alert: true, - badge: true, - sound: true, - ); - - // Verificar si los permisos de notificación están habilitados - if (settings.authorizationStatus == AuthorizationStatus.authorized || - settings.authorizationStatus == AuthorizationStatus.provisional) { - // Obtener el token de registro para la instancia de la aplicación - String? token = await _firebaseMessaging.getToken(); - print('Token FCM: $token'); - - FirebaseMessaging.onMessage.listen((RemoteMessage message) { - print( - 'Mensaje FCM recibido: ${message.notification?.title} - ${message.notification?.body}'); - - // Obtener el valor de la clave "screen" de los datos de la notificación - String? notificationScreen = message.data['screen']; - - // Navegar a la pantalla correspondiente según el valor de la clave "screen" - if (notificationScreen == "misservicios") { - // Navegar a MyServicesScreen - Get.offNamed('/misservicios'); - } else if (notificationScreen == "solicitud") { - // Navegar a SolicitudScreen - Get.offNamed('/solicitud'); - } - }); - - // Manejar la notificación cuando se toca y la aplicación está en primer plano (opcional) - FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) { - print( - 'Mensaje FCM abierto desde la aplicación en primer plano: ${message.notification?.title} - ${message.notification?.body}'); - // Aquí puedes redirigir al usuario a una pantalla específica o realizar acciones según los datos recibidos - }); - - // Manejar la notificación cuando se toca y la aplicación está cerrada (opcional) - RemoteMessage? initialMessage = - await FirebaseMessaging.instance.getInitialMessage(); - if (initialMessage != null) { - print( - 'Mensaje FCM abierto desde la aplicación cerrada: ${initialMessage.notification?.title} - ${initialMessage.notification?.body}'); - // Aquí puedes redirigir al usuario a una pantalla específica o realizar acciones según los datos recibidos - } - } - } -} diff --git a/lib/src/services/local_notifications.dart b/lib/src/services/local_notifications.dart deleted file mode 100644 index 8a5dbf0..0000000 --- a/lib/src/services/local_notifications.dart +++ /dev/null @@ -1,47 +0,0 @@ -import 'package:flutter_local_notifications/flutter_local_notifications.dart'; -import 'package:prosappco/src/services/firebase_messaging.dart'; - -final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = - FlutterLocalNotificationsPlugin(); - -Future initNotifications() async { - const AndroidInitializationSettings initializationSettingsAndroid = - AndroidInitializationSettings('@mipmap/ic_launcher'); - - const DarwinInitializationSettings initializationSettingsIOS = - DarwinInitializationSettings( - requestAlertPermission: true, - requestBadgePermission: true, - requestSoundPermission: true, - ); - const InitializationSettings initializationSettings = InitializationSettings( - android: initializationSettingsAndroid, - iOS: initializationSettingsIOS, - ); - - await flutterLocalNotificationsPlugin.initialize(initializationSettings); - - // Inicializar Firebase Messaging - FirebaseMessagingService firebaseMessagingService = - FirebaseMessagingService(); - await firebaseMessagingService.initializeFirebaseMessaging(); -} - -Future showNotification(String title, String body) async { - const AndroidNotificationDetails androidNotificationDetails = - AndroidNotificationDetails('solicitud_servicio', 'Solicitud de Servicio', - importance: Importance.max, priority: Priority.high); - -// IOSNotificationDetails --> DarwinNotificationDetails - const DarwinNotificationDetails iOSNotificationDetails = - DarwinNotificationDetails( - presentAlert: true, - presentBadge: true, - presentSound: true, - ); - const NotificationDetails notificationDetails = NotificationDetails( - android: androidNotificationDetails, iOS: iOSNotificationDetails); - - await flutterLocalNotificationsPlugin.show( - 1, title, body, notificationDetails); -} diff --git a/lib/src/services/select_image_profile.dart b/lib/src/services/select_image_profile.dart deleted file mode 100644 index a972fb6..0000000 --- a/lib/src/services/select_image_profile.dart +++ /dev/null @@ -1,16 +0,0 @@ -import 'package:image_picker/image_picker.dart'; - -Future> getImage(opc) async { - final ImagePicker picker = ImagePicker(); - - if (opc == 1) { - XFile? image = await picker.pickImage(source: ImageSource.camera); - return [image]; - } else if (opc == 2) { - XFile? image = await picker.pickImage(source: ImageSource.gallery); - return [image]; - } else { - final List images = await picker.pickMultiImage(); - return images; - } -} diff --git a/lib/src/utils/time_of_day_extension.dart b/lib/src/utils/time_of_day_extension.dart deleted file mode 100644 index a631b51..0000000 --- a/lib/src/utils/time_of_day_extension.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'package:flutter/material.dart'; - -extension TimeOfDayExtension on TimeOfDay { - TimeOfDay add({int hour = 0, int minute = 0}) { - return replacing(hour: this.hour + hour, minute: this.minute + minute); - } - - int compareTo(TimeOfDay other) { - if (hour < other.hour) return -1; - if (hour > other.hour) return 1; - if (minute < other.minute) return -1; - if (minute > other.minute) return 1; - return 0; - } - - bool isBefore(TimeOfDay other) { - return compareTo(other) == -1; - } -} diff --git a/lib/src/utils/time_of_day_utils.dart b/lib/src/utils/time_of_day_utils.dart deleted file mode 100644 index d4ce6e9..0000000 --- a/lib/src/utils/time_of_day_utils.dart +++ /dev/null @@ -1,15 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:prosappco/src/utils/time_of_day_extension.dart'; - -class TimeOfDayUtils { - static List genRanges(TimeOfDay timeStart, TimeOfDay timeEnd) { - List ranges = []; - TimeOfDay current = timeStart; - while (current.isBefore(timeEnd)) { - ranges.add(current); - // Sumar 2 horas al objeto DateTime - current = current.add(hour: 2); - } - return ranges; - } -} diff --git a/packages/city_repository/lib/src/repositories/city_repo.dart b/packages/city_repository/lib/src/repositories/city_repo.dart index 5d6de2f..021ee64 100644 --- a/packages/city_repository/lib/src/repositories/city_repo.dart +++ b/packages/city_repository/lib/src/repositories/city_repo.dart @@ -1,5 +1,4 @@ import 'package:city_repository/city_repository.dart'; -import 'package:city_repository/src/models/city_ui.dart'; abstract class CityRepository { Future> getCities(); diff --git a/packages/profession_repository/lib/profession_repository.dart b/packages/profession_repository/lib/profession_repository.dart new file mode 100644 index 0000000..d8700d0 --- /dev/null +++ b/packages/profession_repository/lib/profession_repository.dart @@ -0,0 +1,6 @@ +library profession_repository; + +export 'src/models/models.dart'; +export 'src/entities/entities.dart'; +export 'src/repositories/profession_repo.dart'; +export 'src/repositories/firebase_profession_repository.dart'; \ No newline at end of file diff --git a/packages/profession_repository/lib/src/entities/entities.dart b/packages/profession_repository/lib/src/entities/entities.dart new file mode 100644 index 0000000..5690db1 --- /dev/null +++ b/packages/profession_repository/lib/src/entities/entities.dart @@ -0,0 +1 @@ +export '/src/entities/profession_entity.dart'; diff --git a/packages/profession_repository/lib/src/entities/profession_entity.dart b/packages/profession_repository/lib/src/entities/profession_entity.dart new file mode 100644 index 0000000..c481f1f --- /dev/null +++ b/packages/profession_repository/lib/src/entities/profession_entity.dart @@ -0,0 +1,29 @@ +import 'package:equatable/equatable.dart'; + +class ProfessionEntity extends Equatable { + final String name; + + const ProfessionEntity({required this.name}); + + Map toDocument() { + return { + 'name': name, + }; + } + + static ProfessionEntity fromDocument(Map doc) { + return ProfessionEntity( + name: doc['name'] as String, + ); + } + + @override + List get props => [name]; + + @override + String toString() { + return '''ProfessionEntity { + name: $name + }'''; + } +} diff --git a/packages/profession_repository/lib/src/models/models.dart b/packages/profession_repository/lib/src/models/models.dart new file mode 100644 index 0000000..285be39 --- /dev/null +++ b/packages/profession_repository/lib/src/models/models.dart @@ -0,0 +1 @@ +export 'profession_ui.dart'; diff --git a/packages/profession_repository/lib/src/models/profession_ui.dart b/packages/profession_repository/lib/src/models/profession_ui.dart new file mode 100644 index 0000000..c3ab976 --- /dev/null +++ b/packages/profession_repository/lib/src/models/profession_ui.dart @@ -0,0 +1,12 @@ +import 'package:equatable/equatable.dart'; + +class ProfessionUi extends Equatable { + final String name; + + const ProfessionUi({ + required this.name, + }); + + @override + List get props => [name]; +} diff --git a/packages/profession_repository/lib/src/repositories/firebase_profession_repository.dart b/packages/profession_repository/lib/src/repositories/firebase_profession_repository.dart new file mode 100644 index 0000000..bc8bc68 --- /dev/null +++ b/packages/profession_repository/lib/src/repositories/firebase_profession_repository.dart @@ -0,0 +1,30 @@ +import 'dart:developer'; + +import 'package:profession_repository/profession_repository.dart'; +import 'package:cloud_firestore/cloud_firestore.dart'; + +class FirebaseProfessionRepository implements ProfessionRepository { + final professionsCollection = + FirebaseFirestore.instance.collection('professions'); + + @override + Future getProfessions() async { + try { + final doc = await professionsCollection.doc("professions").get(); + return Professions.fromDocument(doc.data() as Map); + } catch (e) { + log('Error getting documents: $e'); + rethrow; + } + } +} + +class Professions { + final List professions; + + Professions(this.professions); + + factory Professions.fromDocument(Map json) { + return Professions(List.from(json['professions'])); + } +} diff --git a/packages/profession_repository/lib/src/repositories/profession_repo.dart b/packages/profession_repository/lib/src/repositories/profession_repo.dart new file mode 100644 index 0000000..0db6413 --- /dev/null +++ b/packages/profession_repository/lib/src/repositories/profession_repo.dart @@ -0,0 +1,5 @@ +import 'package:profession_repository/profession_repository.dart'; + +abstract class ProfessionRepository { + Future getProfessions(); +} diff --git a/packages/profession_repository/pubspec.lock b/packages/profession_repository/pubspec.lock new file mode 100644 index 0000000..cb1d0da --- /dev/null +++ b/packages/profession_repository/pubspec.lock @@ -0,0 +1,266 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + _flutterfire_internals: + dependency: transitive + description: + name: _flutterfire_internals + sha256: "4eec93681221723a686ad580c2e7d960e1017cf1a4e0a263c2573c2c6b0bf5cd" + url: "https://pub.dev" + source: hosted + version: "1.3.25" + async: + dependency: transitive + description: + name: async + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" + source: hosted + version: "2.11.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + characters: + dependency: transitive + description: + name: characters + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + url: "https://pub.dev" + source: hosted + version: "1.3.0" + clock: + dependency: transitive + description: + name: clock + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" + source: hosted + version: "1.1.1" + cloud_firestore: + dependency: "direct main" + description: + name: cloud_firestore + sha256: "31cfa4d65d6e9ea837234fffe121304034c30c9214c06207b4a35867e3757900" + url: "https://pub.dev" + source: hosted + version: "4.15.8" + cloud_firestore_platform_interface: + dependency: transitive + description: + name: cloud_firestore_platform_interface + sha256: a0097a26569b015faf8142e159e855241609ea9a1738b5fd1c40bfe8411b41a0 + url: "https://pub.dev" + source: hosted + version: "6.1.9" + cloud_firestore_web: + dependency: transitive + description: + name: cloud_firestore_web + sha256: ed680ece29a5750985119c09cdc276b460c3a2fa80e8c12f9b7241f6b4a7ca16 + url: "https://pub.dev" + source: hosted + version: "3.10.8" + collection: + dependency: transitive + description: + name: collection + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + url: "https://pub.dev" + source: hosted + version: "1.18.0" + equatable: + dependency: "direct main" + description: + name: equatable + sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 + url: "https://pub.dev" + source: hosted + version: "2.0.5" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" + source: hosted + version: "1.3.1" + firebase_core: + dependency: "direct main" + description: + name: firebase_core + sha256: "53316975310c8af75a96e365f9fccb67d1c544ef0acdbf0d88bbe30eedd1c4f9" + url: "https://pub.dev" + source: hosted + version: "2.27.0" + firebase_core_platform_interface: + dependency: transitive + description: + name: firebase_core_platform_interface + sha256: c437ae5d17e6b5cc7981cf6fd458a5db4d12979905f9aafd1fea930428a9fe63 + url: "https://pub.dev" + source: hosted + version: "5.0.0" + firebase_core_web: + dependency: transitive + description: + name: firebase_core_web + sha256: c8e1d59385eee98de63c92f961d2a7062c5d9a65e7f45bdc7f1b0b205aab2492 + url: "https://pub.dev" + source: hosted + version: "2.11.5" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 + url: "https://pub.dev" + source: hosted + version: "2.0.3" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + js: + dependency: transitive + description: + name: js + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + url: "https://pub.dev" + source: hosted + version: "0.6.7" + lints: + dependency: transitive + description: + name: lints + sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + matcher: + dependency: transitive + description: + name: matcher + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + url: "https://pub.dev" + source: hosted + version: "0.12.16" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + url: "https://pub.dev" + source: hosted + version: "0.5.0" + meta: + dependency: transitive + description: + name: meta + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e + url: "https://pub.dev" + source: hosted + version: "1.10.0" + path: + dependency: transitive + description: + name: path + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + url: "https://pub.dev" + source: hosted + version: "1.8.3" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" + source: hosted + version: "1.10.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + url: "https://pub.dev" + source: hosted + version: "1.11.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + url: "https://pub.dev" + source: hosted + version: "2.1.2" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + test_api: + dependency: transitive + description: + name: test_api + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + url: "https://pub.dev" + source: hosted + version: "0.6.1" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + web: + dependency: transitive + description: + name: web + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 + url: "https://pub.dev" + source: hosted + version: "0.3.0" +sdks: + dart: ">=3.2.0 <4.0.0" + flutter: ">=3.3.0" diff --git a/packages/profession_repository/pubspec.yaml b/packages/profession_repository/pubspec.yaml new file mode 100644 index 0000000..9c2145f --- /dev/null +++ b/packages/profession_repository/pubspec.yaml @@ -0,0 +1,26 @@ +name: profession_repository +description: Dart package which manages the professions. +publish_to: "none" + +version: 1.0.11+11 + +environment: + sdk: ">=2.19.3 <3.0.0" + +dependencies: + flutter: + sdk: flutter + equatable: ^2.0.5 + + # Firebase + cloud_firestore: ^4.15.4 + firebase_core: ^2.25.4 + + +dev_dependencies: + flutter_lints: ^2.0.0 + flutter_test: + sdk: flutter + +flutter: + uses-material-design: true \ No newline at end of file diff --git a/packages/professional_repository/lib/professional_repository.dart b/packages/professional_repository/lib/professional_repository.dart new file mode 100644 index 0000000..02a279e --- /dev/null +++ b/packages/professional_repository/lib/professional_repository.dart @@ -0,0 +1,4 @@ +library professional_repository; + +export 'src/entities/entities.dart'; +export 'src/repositories/firebase_professional_repository.dart'; diff --git a/packages/professional_repository/lib/src/entities/entities.dart b/packages/professional_repository/lib/src/entities/entities.dart new file mode 100644 index 0000000..0939fa3 --- /dev/null +++ b/packages/professional_repository/lib/src/entities/entities.dart @@ -0,0 +1,4 @@ +export '/src/entities/payment_method_entity.dart'; +export '/src/entities/professional_entity.dart'; +export '/src/entities/schedule_entity.dart'; +export '/src/entities/schedules.dart'; diff --git a/packages/professional_repository/lib/src/entities/payment_method_entity.dart b/packages/professional_repository/lib/src/entities/payment_method_entity.dart new file mode 100644 index 0000000..c4354f9 --- /dev/null +++ b/packages/professional_repository/lib/src/entities/payment_method_entity.dart @@ -0,0 +1,51 @@ +import 'package:equatable/equatable.dart'; + +class PaymentMethodEntity extends Equatable { + final bool nequi; + final bool datafono; + final bool transferencia; + + static const empty = PaymentMethodEntity( + nequi: false, + datafono: false, + transferencia: false, + ); + + const PaymentMethodEntity({ + required this.nequi, + required this.datafono, + required this.transferencia, + }); + + static PaymentMethodEntity fromDocument(Map doc) { + return PaymentMethodEntity( + nequi: doc['nequi'] as bool, + datafono: doc['datafono'] as bool, + transferencia: doc['transferencia'] as bool, + ); + } + + Map toJson() { + return { + 'nequi': nequi, + 'datafono': datafono, + 'transferencia': transferencia, + }; + } + + @override + List get props => [ + nequi, + datafono, + transferencia, + ]; + + @override + String toString() { + return '''SettingEntity { + nequi: $nequi + datafono: $datafono + transferencia: $transferencia + }'''; + } +} diff --git a/packages/professional_repository/lib/src/entities/professional_entity.dart b/packages/professional_repository/lib/src/entities/professional_entity.dart new file mode 100644 index 0000000..44cc78d --- /dev/null +++ b/packages/professional_repository/lib/src/entities/professional_entity.dart @@ -0,0 +1,113 @@ +import 'package:equatable/equatable.dart'; +import 'package:professional_repository/professional_repository.dart'; + +class ProfessionalEntity extends Equatable { + final String id; + final String identification; + final String address; + final String profession; + final String rate; + final String location; + final String identificationPicture; + final String certificatePicture; + final String latitude; + final String longitude; + final List specializations; + final List specializationsPictures; + final Schedules schedules; + final PaymentMethodEntity paymentMethods; + + const ProfessionalEntity({ + required this.id, + required this.identification, + required this.address, + required this.profession, + required this.rate, + required this.location, + 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 ProfessionalEntity fromDocument(Map doc) { + return ProfessionalEntity( + id: doc['id'] as String, + identification: doc['identification'] as String, + address: doc['address'] as String, + profession: doc['profession'] as String, + rate: doc['rate'] as String, + location: doc['location'] as String, + identificationPicture: doc['identification_picture'] as String, + certificatePicture: doc['certificate_picture'] as String, + latitude: doc['latitude'] as String, + longitude: doc['longitude'] as String, + specializations: List.from(doc['specializations']), + specializationsPictures: + List.from(doc['specializations_pictures']), + schedules: Schedules.fromDocument(doc['schedules']), + paymentMethods: PaymentMethodEntity.fromDocument(doc['payment_methods']), + ); + } + + Map toJson() { + return { + 'id': id, + 'identification': identification, + 'address': address, + 'profession': profession, + 'rate': rate, + 'location': location, + 'identification_picture': identificationPicture, + 'certificate_picture': certificatePicture, + 'latitude': latitude, + 'longitude': longitude, + 'specializations': specializations, + 'specializations_pictures': specializationsPictures, + 'schedules': schedules.toJson(), + 'payment_methods': paymentMethods.toJson(), + }; + } + + @override + List get props => [ + id, + identification, + address, + profession, + rate, + location, + identificationPicture, + certificatePicture, + latitude, + longitude, + specializations, + specializationsPictures, + schedules, + paymentMethods + ]; + + @override + String toString() { + return '''ProfessionalEntity{ { + id: $id, + identification: $identification, + address: $address, + profession: $profession, + rate: $rate, + location: $location, + identificationPicture: $identificationPicture, + certificatePicture: $certificatePicture, + latitude: $latitude, + longitude: $longitude, + specializations: $specializations, + specializationsPictures: $specializationsPictures, + schedule: $schedules, + paymentMethods: $paymentMethods + }'''; + } +} diff --git a/packages/professional_repository/lib/src/entities/schedule_entity.dart b/packages/professional_repository/lib/src/entities/schedule_entity.dart new file mode 100644 index 0000000..143d6e4 --- /dev/null +++ b/packages/professional_repository/lib/src/entities/schedule_entity.dart @@ -0,0 +1,99 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; + +class ScheduleEntity extends Equatable { + final bool enabled; + final bool continuousDay; + final TimeOfDay? range1Hour1; + final TimeOfDay? range1Hour2; + final TimeOfDay? range2Hour1; + final TimeOfDay? range2Hour2; + + const ScheduleEntity({ + required this.enabled, + required this.continuousDay, + required this.range1Hour1, + required this.range1Hour2, + required this.range2Hour1, + required this.range2Hour2, + }); + + static const empty = ScheduleEntity( + enabled: false, + continuousDay: false, + range1Hour1: null, + range1Hour2: null, + range2Hour1: null, + range2Hour2: null, + ); + + static ScheduleEntity fromDocument(Map doc) { + return ScheduleEntity( + enabled: doc['habilitado'] as bool, + continuousDay: doc['continuous_day'] as bool, + range1Hour1: _parseTime(doc['range1Hour1']), + range1Hour2: _parseTime(doc['range1Hour2']), + range2Hour1: _parseTime(doc['range2Hour1']), + range2Hour2: _parseTime(doc['range2Hour2']), + ); + } + + static TimeOfDay? _parseTime(String? time) { + if (time == null) return null; + final components = time.split(' '); + final hourMinutes = components[0].split(':'); + final hour = int.parse(hourMinutes[0]); + final minutes = int.parse(hourMinutes[1]); + if (components[1] == 'PM' && hour < 12) { + return TimeOfDay(hour: hour + 12, minute: minutes); + } else if (components[1] == 'AM' && hour == 12) { + return TimeOfDay(hour: 0, minute: minutes); + } + return TimeOfDay(hour: hour, minute: minutes); + } + + Map toJson() { + return { + 'habilitado': enabled, + 'continuous_day': continuousDay, + 'range1Hour1': formatTimeOfDay(range1Hour1), + 'range1Hour2': formatTimeOfDay(range1Hour2), + 'range2Hour1': formatTimeOfDay(range2Hour1), + 'range2Hour2': formatTimeOfDay(range2Hour2), + }; + } + + String? formatTimeOfDay(TimeOfDay? time) { + if (time != null) { + final now = DateTime.now(); + final dateTime = + DateTime(now.year, now.month, now.day, time.hour, time.minute); + final format = DateFormat.jm(); + return format.format(dateTime); + } + return null; + } + + @override + List get props => [ + enabled, + continuousDay, + range1Hour1, + range1Hour2, + range2Hour1, + range2Hour2, + ]; + + @override + String toString() { + return '''ScheduleEntity { + enabled: $enabled, + continuousDay: $continuousDay, + range1Hour1: $range1Hour1, + range1Hour2: $range1Hour2, + range2Hour1: $range2Hour1, + range2Hour2: $range2Hour2 + }'''; + } +} diff --git a/packages/professional_repository/lib/src/entities/schedules.dart b/packages/professional_repository/lib/src/entities/schedules.dart new file mode 100644 index 0000000..98a20b3 --- /dev/null +++ b/packages/professional_repository/lib/src/entities/schedules.dart @@ -0,0 +1,59 @@ +import 'package:equatable/equatable.dart'; +import 'package:professional_repository/src/entities/schedule_entity.dart'; + +class Schedules extends Equatable { + final ScheduleEntity monday; + final ScheduleEntity tuesday; + final ScheduleEntity wednesday; + final ScheduleEntity thursday; + final ScheduleEntity friday; + final ScheduleEntity saturday; + final ScheduleEntity sunday; + + const Schedules( + {required this.monday, + required this.tuesday, + required this.wednesday, + required this.thursday, + required this.friday, + required this.saturday, + required this.sunday}); + + static const empty = Schedules( + monday: ScheduleEntity.empty, + tuesday: ScheduleEntity.empty, + wednesday: ScheduleEntity.empty, + thursday: ScheduleEntity.empty, + friday: ScheduleEntity.empty, + saturday: ScheduleEntity.empty, + sunday: ScheduleEntity.empty, + ); + + factory Schedules.fromDocument(Map doc) { + return Schedules( + monday: ScheduleEntity.fromDocument(doc['monday']), + tuesday: ScheduleEntity.fromDocument(doc['tuesday']), + wednesday: ScheduleEntity.fromDocument(doc['wednesday']), + thursday: ScheduleEntity.fromDocument(doc['thursday']), + friday: ScheduleEntity.fromDocument(doc['friday']), + saturday: ScheduleEntity.fromDocument(doc['saturday']), + sunday: ScheduleEntity.fromDocument(doc['sunday']), + ); + } + + Map toJson() { + return { + 'monday': monday.toJson(), + 'tuesday': tuesday.toJson(), + 'wednesday': wednesday.toJson(), + 'thursday': thursday.toJson(), + 'friday': friday.toJson(), + 'saturday': saturday.toJson(), + 'sunday': sunday.toJson(), + }; + } + + @override + List get props => + [monday, tuesday, wednesday, thursday, friday, saturday, sunday]; +} diff --git a/packages/professional_repository/lib/src/repositories/firebase_professional_repository.dart b/packages/professional_repository/lib/src/repositories/firebase_professional_repository.dart new file mode 100644 index 0000000..9c65dd2 --- /dev/null +++ b/packages/professional_repository/lib/src/repositories/firebase_professional_repository.dart @@ -0,0 +1,31 @@ +import 'dart:async'; + +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:professional_repository/professional_repository.dart'; + + +class FirebaseProfessionalRepository { + + bool isProModeActive = false; + final professionalCollection = + FirebaseFirestore.instance.collection('professional_info'); + final StreamController _userStreamController = + StreamController.broadcast(); + + FirebaseProfessionalRepository() { + _userStreamController.add(isProModeActive); + } + + Stream sreamIsProModeActive() { + return _userStreamController.stream; + } + + switchProMode() async { + isProModeActive = !isProModeActive; + _userStreamController.add(isProModeActive); + } + + Future saveProfessionalInfo(ProfessionalEntity entity) async { + await professionalCollection.doc(entity.id).set(entity.toJson()); + } +} diff --git a/packages/professional_repository/pubspec.lock b/packages/professional_repository/pubspec.lock new file mode 100644 index 0000000..f0c51e2 --- /dev/null +++ b/packages/professional_repository/pubspec.lock @@ -0,0 +1,274 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + _flutterfire_internals: + dependency: transitive + description: + name: _flutterfire_internals + sha256: "4eec93681221723a686ad580c2e7d960e1017cf1a4e0a263c2573c2c6b0bf5cd" + url: "https://pub.dev" + source: hosted + version: "1.3.25" + async: + dependency: transitive + description: + name: async + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" + source: hosted + version: "2.11.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + characters: + dependency: transitive + description: + name: characters + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + url: "https://pub.dev" + source: hosted + version: "1.3.0" + clock: + dependency: transitive + description: + name: clock + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" + source: hosted + version: "1.1.1" + cloud_firestore: + dependency: "direct main" + description: + name: cloud_firestore + sha256: "31cfa4d65d6e9ea837234fffe121304034c30c9214c06207b4a35867e3757900" + url: "https://pub.dev" + source: hosted + version: "4.15.8" + cloud_firestore_platform_interface: + dependency: transitive + description: + name: cloud_firestore_platform_interface + sha256: a0097a26569b015faf8142e159e855241609ea9a1738b5fd1c40bfe8411b41a0 + url: "https://pub.dev" + source: hosted + version: "6.1.9" + cloud_firestore_web: + dependency: transitive + description: + name: cloud_firestore_web + sha256: ed680ece29a5750985119c09cdc276b460c3a2fa80e8c12f9b7241f6b4a7ca16 + url: "https://pub.dev" + source: hosted + version: "3.10.8" + collection: + dependency: transitive + description: + name: collection + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + url: "https://pub.dev" + source: hosted + version: "1.18.0" + equatable: + dependency: "direct main" + description: + name: equatable + sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 + url: "https://pub.dev" + source: hosted + version: "2.0.5" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" + source: hosted + version: "1.3.1" + firebase_core: + dependency: "direct main" + description: + name: firebase_core + sha256: "53316975310c8af75a96e365f9fccb67d1c544ef0acdbf0d88bbe30eedd1c4f9" + url: "https://pub.dev" + source: hosted + version: "2.27.0" + firebase_core_platform_interface: + dependency: transitive + description: + name: firebase_core_platform_interface + sha256: c437ae5d17e6b5cc7981cf6fd458a5db4d12979905f9aafd1fea930428a9fe63 + url: "https://pub.dev" + source: hosted + version: "5.0.0" + firebase_core_web: + dependency: transitive + description: + name: firebase_core_web + sha256: c8e1d59385eee98de63c92f961d2a7062c5d9a65e7f45bdc7f1b0b205aab2492 + url: "https://pub.dev" + source: hosted + version: "2.11.5" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 + url: "https://pub.dev" + source: hosted + version: "2.0.3" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + intl: + dependency: "direct main" + description: + name: intl + sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" + url: "https://pub.dev" + source: hosted + version: "0.18.1" + js: + dependency: transitive + description: + name: js + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + url: "https://pub.dev" + source: hosted + version: "0.6.7" + lints: + dependency: transitive + description: + name: lints + sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + matcher: + dependency: transitive + description: + name: matcher + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + url: "https://pub.dev" + source: hosted + version: "0.12.16" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + url: "https://pub.dev" + source: hosted + version: "0.5.0" + meta: + dependency: transitive + description: + name: meta + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e + url: "https://pub.dev" + source: hosted + version: "1.10.0" + path: + dependency: transitive + description: + name: path + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + url: "https://pub.dev" + source: hosted + version: "1.8.3" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" + source: hosted + version: "1.10.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + url: "https://pub.dev" + source: hosted + version: "1.11.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + url: "https://pub.dev" + source: hosted + version: "2.1.2" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + test_api: + dependency: transitive + description: + name: test_api + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + url: "https://pub.dev" + source: hosted + version: "0.6.1" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + web: + dependency: transitive + description: + name: web + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 + url: "https://pub.dev" + source: hosted + version: "0.3.0" +sdks: + dart: ">=3.2.0 <4.0.0" + flutter: ">=3.3.0" diff --git a/packages/professional_repository/pubspec.yaml b/packages/professional_repository/pubspec.yaml new file mode 100644 index 0000000..e313776 --- /dev/null +++ b/packages/professional_repository/pubspec.yaml @@ -0,0 +1,26 @@ +name: professional_repository +description: Dart package which manages the professional data. +publish_to: "none" + +version: 1.0.11+11 + +environment: + sdk: ">=2.19.3 <3.0.0" + +dependencies: + flutter: + sdk: flutter + equatable: ^2.0.5 + + # Firebase + cloud_firestore: ^4.15.4 + firebase_core: ^2.25.4 + intl: ^0.18.1 + +dev_dependencies: + flutter_lints: ^2.0.0 + flutter_test: + sdk: flutter + +flutter: + uses-material-design: true \ No newline at end of file diff --git a/packages/setting_repository/lib/setting_repository.dart b/packages/setting_repository/lib/setting_repository.dart index 9f234a6..3d21cd5 100644 --- a/packages/setting_repository/lib/setting_repository.dart +++ b/packages/setting_repository/lib/setting_repository.dart @@ -1,6 +1,5 @@ library setting_repository; -export 'src/models/models.dart'; export 'src/entities/entities.dart'; export 'src/repositories/setting_repo.dart'; export 'src/repositories/firebase_setting_repository.dart'; diff --git a/packages/setting_repository/lib/src/models/models.dart b/packages/setting_repository/lib/src/models/models.dart deleted file mode 100644 index e69de29..0000000 diff --git a/packages/setting_repository/lib/src/models/setting_ui.dart b/packages/setting_repository/lib/src/models/setting_ui.dart deleted file mode 100644 index 1f1d490..0000000 --- a/packages/setting_repository/lib/src/models/setting_ui.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'package:equatable/equatable.dart'; - -class SettingUi extends Equatable { - final String cityName; - final String coordsOfCity; - final String stateOfCity; - final String countryOfCity; - - const SettingUi({ - required this.cityName, - required this.coordsOfCity, - required this.stateOfCity, - required this.countryOfCity, - }); - - @override - List get props => - [cityName, coordsOfCity, stateOfCity, countryOfCity]; -} diff --git a/packages/user_repository/lib/src/entities/my_user_entity.dart b/packages/user_repository/lib/src/entities/my_user_entity.dart index 19e486e..2ad4504 100644 --- a/packages/user_repository/lib/src/entities/my_user_entity.dart +++ b/packages/user_repository/lib/src/entities/my_user_entity.dart @@ -1,4 +1,5 @@ import 'package:equatable/equatable.dart'; +import 'package:user_repository/src/models/models.dart'; class MyUserEntity extends Equatable { final String id; @@ -10,6 +11,7 @@ class MyUserEntity extends Equatable { final String? picture; final String? birthday; final String? gender; + final ProState proState; const MyUserEntity({ required this.id, @@ -21,6 +23,7 @@ class MyUserEntity extends Equatable { required this.picture, required this.birthday, required this.gender, + required this.proState, }); Map toDocument() { @@ -34,6 +37,7 @@ class MyUserEntity extends Equatable { 'picture': picture, 'birthday': birthday, 'gender': gender, + 'professional_state': enumToInt(proState), }; } @@ -48,6 +52,7 @@ class MyUserEntity extends Equatable { picture: doc['picture'] as String?, birthday: doc['birthday'] as String?, gender: doc['gender'] as String?, + proState: intToEnum(doc['professional_state'] as int), ); } @@ -66,7 +71,8 @@ class MyUserEntity extends Equatable { city: $city picture: $picture birthday: $birthday - gender: $gender + gender: $gender, + proState: ${proState.name} }'''; } } diff --git a/packages/user_repository/lib/src/models/models.dart b/packages/user_repository/lib/src/models/models.dart index caacbe0..2460e73 100644 --- a/packages/user_repository/lib/src/models/models.dart +++ b/packages/user_repository/lib/src/models/models.dart @@ -1 +1,2 @@ -export 'my_user.dart'; \ No newline at end of file +export 'my_user.dart'; +export 'pro_state.dart'; diff --git a/packages/user_repository/lib/src/models/my_user.dart b/packages/user_repository/lib/src/models/my_user.dart index 38af5d5..d85dce9 100644 --- a/packages/user_repository/lib/src/models/my_user.dart +++ b/packages/user_repository/lib/src/models/my_user.dart @@ -1,6 +1,5 @@ import 'package:equatable/equatable.dart'; - -import '../entities/entities.dart'; +import 'package:user_repository/user_repository.dart'; class MyUser extends Equatable { final String id; @@ -12,6 +11,7 @@ class MyUser extends Equatable { final String? picture; final String? birthday; final String? gender; + final ProState proState; const MyUser({ required this.id, @@ -23,6 +23,7 @@ class MyUser extends Equatable { this.picture, this.birthday, this.gender, + required this.proState, }); get drawerLabel => email != null && email!.isNotEmpty @@ -42,6 +43,7 @@ class MyUser extends Equatable { picture: '', birthday: '', gender: '', + proState: ProState.inactive, ); /// Modify MyUser parameters @@ -55,6 +57,7 @@ class MyUser extends Equatable { String? picture, String? birthday, String? gender, + ProState? proState, }) { return MyUser( id: id ?? this.id, @@ -66,6 +69,7 @@ class MyUser extends Equatable { picture: picture ?? this.picture, birthday: birthday ?? this.birthday, gender: gender ?? this.gender, + proState: proState ?? this.proState, ); } @@ -86,6 +90,7 @@ class MyUser extends Equatable { picture: picture, birthday: birthday, gender: gender, + proState: proState, ); } @@ -100,6 +105,7 @@ class MyUser extends Equatable { picture: entity.picture, birthday: entity.birthday, gender: entity.gender, + proState: entity.proState, ); } @@ -114,5 +120,6 @@ class MyUser extends Equatable { picture, birthday, gender, + proState, ]; } diff --git a/packages/user_repository/lib/src/models/pro_state.dart b/packages/user_repository/lib/src/models/pro_state.dart new file mode 100644 index 0000000..90c9f3b --- /dev/null +++ b/packages/user_repository/lib/src/models/pro_state.dart @@ -0,0 +1,11 @@ +export 'pro_state.dart'; + +enum ProState { inactive, pending, active, denied } + +int enumToInt(ProState state) { + return state.index; +} + +ProState intToEnum(int value) { + return ProState.values[value]; +} \ No newline at end of file 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 ebab2d2..427978c 100644 --- a/packages/user_repository/lib/src/repositories/firebase_user_repository.dart +++ b/packages/user_repository/lib/src/repositories/firebase_user_repository.dart @@ -5,11 +5,13 @@ import 'dart:io'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:firebase_storage/firebase_storage.dart'; -import 'package:user_repository/src/models/my_user.dart'; +import 'package:user_repository/src/models/models.dart'; import '../entities/entities.dart'; import 'user_repo.dart'; class FirebaseUserRepository implements UserRepository { + MyUser? _lastUser; + final FirebaseAuth _firebaseAuth; final usersCollection = FirebaseFirestore.instance.collection('users'); final StreamController _userStreamController = @@ -28,11 +30,27 @@ class FirebaseUserRepository implements UserRepository { nickname: user.displayName?.trim().toLowerCase(), ); } else { + _lastUser = null; _userStreamController.add(null); } }); } + @override + Future lastUser() async { + return _lastUser; + } + + Future refreshUser() async { + final user = _firebaseAuth.currentUser; + if (user != null) { + await updateFromFirebase(user.uid); + } else { + _lastUser = null; + _userStreamController.add(null); + } + } + Future updateFromFirebase(String userId) async { return updateFromFirebase2(userId: userId); } @@ -56,12 +74,15 @@ class FirebaseUserRepository implements UserRepository { phone: phone, picture: picture, nickname: name?.trim().toLowerCase(), + proState: ProState.inactive, )); myUser = await getMyUser(userId); } + _lastUser = myUser; _userStreamController.add(myUser); } catch (e) { log('xd -- Error updating from firebase ${e.toString()}'); + _lastUser = null; _userStreamController.add(null); } } @@ -131,6 +152,7 @@ class FirebaseUserRepository implements UserRepository { await setUserData(MyUser( id: credentials.user?.uid ?? '', phone: credentials.user?.phoneNumber ?? '', + proState: ProState.inactive, )); } diff --git a/packages/user_repository/lib/src/repositories/user_repo.dart b/packages/user_repository/lib/src/repositories/user_repo.dart index 9486467..97e5aff 100644 --- a/packages/user_repository/lib/src/repositories/user_repo.dart +++ b/packages/user_repository/lib/src/repositories/user_repo.dart @@ -3,6 +3,8 @@ import 'package:firebase_auth/firebase_auth.dart'; import '../../user_repository.dart'; abstract class UserRepository { + Future lastUser(); + Stream streamUser(); Stream isAuthenticated(); @@ -22,12 +24,9 @@ abstract class UserRepository { Future verifyOTP(String code); Future addPhoneAuthCredential(String password, String phoneNumber, - { - required Future Function(FirebaseAuthException) verificationFailed, + {required Future Function(FirebaseAuthException) verificationFailed, required Future Function(String) codeSent, - required Future Function(String) codeAutoRetrievalTimeout - - }); + required Future Function(String) codeAutoRetrievalTimeout}); Future linkWithOTP( String phoneNumber, String verificationId, String code); diff --git a/pubspec.lock b/pubspec.lock index 362f3b4..33f967a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -964,6 +964,20 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.6" + profession_repository: + dependency: "direct main" + description: + path: "packages/profession_repository" + relative: true + source: path + version: "1.0.11+11" + professional_repository: + dependency: "direct main" + description: + path: "packages/professional_repository" + relative: true + source: path + version: "1.0.11+11" provider: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 3740ca9..4ddf796 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -58,6 +58,10 @@ dependencies: path: packages/city_repository setting_repository: path: packages/setting_repository + professional_repository: + path: packages/professional_repository + profession_repository: + path: packages/profession_repository webview_flutter: ^4.4.1 dev_dependencies: