diff --git a/lib/blocs/service_bloc/service_bloc.dart b/lib/blocs/service_bloc/service_bloc.dart index 96e9536..6b89643 100644 --- a/lib/blocs/service_bloc/service_bloc.dart +++ b/lib/blocs/service_bloc/service_bloc.dart @@ -29,6 +29,7 @@ class ServiceBloc extends Bloc { on(_onLoadService); on(_onUpdateServiceStatus); on(_onLoadServicesForUser); + on(_onLoadServicesHistoryForUser); } void _onCreateService(CreateService event, Emitter emit) async { @@ -76,13 +77,17 @@ class ServiceBloc extends Bloc { } } - void _onLoadServicesForUser( - LoadServicesForUser event, Emitter emit) async { + void _onLoadServicesHistoryForUser( + LoadServicesHistoryForUser event, Emitter emit) async { try { final servicesStream = - _serviceRepository.getServicesForUser(event.userId); + _serviceRepository.getServicesHistoryForUser(event.userId); await for (var services in servicesStream) { + if (services.isEmpty) { + emit(const ServicesForUserLoaded([])); + return; + } final professionalIds = services.map((e) => e.professionalId); final List users = await _userRepository.getUsersFromIds(professionalIds); @@ -91,7 +96,46 @@ class ServiceBloc extends Bloc { final List professionsList = await _professionalRepository - .getProfessionsFromIds(professionalIds); + .getProfessionalsFromIds(professionalIds); + + final professionsDir = {for (var e in professionsList) e.id: e}; + + final servicesInfo = services.map((e) { + return ServiceInfoUI( + service: e, + user: usersDir[e.professionalId]!, + professional: professionsDir[e.professionalId]!); + }).toList(); + + emit(ServicesForUserLoaded(servicesInfo)); + } + } catch (e) { + log(e.toString()); + emit(CreateServiceFailure()); + } + } + + void _onLoadServicesForUser( + LoadServicesForUser event, Emitter emit) async { + try { + final servicesStream = + _serviceRepository.getServicesForUser(event.userId); + + await for (var services in servicesStream) { + if (services.isEmpty) { + emit(const ServicesForUserLoaded([])); + return; + } + final professionalIds = services.map((e) => e.professionalId); + + final List users = + await _userRepository.getUsersFromIds(professionalIds); + + final usersDir = {for (var e in users) e.id: e}; + + final List professionsList = + await _professionalRepository + .getProfessionalsFromIds(professionalIds); final professionsDir = {for (var e in professionsList) e.id: e}; @@ -129,6 +173,9 @@ class ServiceInfoUI { final MyUser user; final ProfessionalEntity professional; - ServiceInfoUI( - {required this.service, required this.user, required this.professional}); + ServiceInfoUI({ + required this.service, + required this.user, + required this.professional, + }); } diff --git a/lib/blocs/service_bloc/service_event.dart b/lib/blocs/service_bloc/service_event.dart index c99f680..074babb 100644 --- a/lib/blocs/service_bloc/service_event.dart +++ b/lib/blocs/service_bloc/service_event.dart @@ -35,6 +35,15 @@ class LoadServicesForUser extends ServiceEvent { List get props => [userId]; } +class LoadServicesHistoryForUser extends ServiceEvent { + final String userId; + + const LoadServicesHistoryForUser(this.userId); + + @override + List get props => [userId]; +} + class CreateService extends ServiceEvent { final String professionalId; final bool? professionalScored; diff --git a/lib/blocs/service_bloc/service_state.dart b/lib/blocs/service_bloc/service_state.dart index d7aa80e..92d9179 100644 --- a/lib/blocs/service_bloc/service_state.dart +++ b/lib/blocs/service_bloc/service_state.dart @@ -16,6 +16,7 @@ class ServiceLoaded extends ServiceState { List get props => [service]; } + class ServicesForUserLoaded extends ServiceState { final List services; diff --git a/lib/components/general_drawer.dart b/lib/components/general_drawer.dart index a4056af..36d398d 100644 --- a/lib/components/general_drawer.dart +++ b/lib/components/general_drawer.dart @@ -9,6 +9,8 @@ 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/lists/professional_list_screen.dart'; +import 'package:prosappco/screens/lists/professional_service_history_list_screen.dart'; +import 'package:prosappco/screens/lists/user_service_history_list_screen.dart'; import 'package:prosappco/screens/lists/user_service_list_screen.dart'; import 'package:prosappco/screens/professional/professional_calendar_screen.dart'; import 'package:prosappco/screens/professional/professional_denied_screen.dart'; @@ -93,13 +95,21 @@ class GeneralDrawer extends StatelessWidget { leading: Icons.access_time_outlined, label: 'Historial', onTap: () { - Navigator.push( - context, - CupertinoPageRoute( - builder: (context) => - const UserHistoryServicesScreen(), - ), - ); + shouldProModeActive(context, professionalState) + ? Navigator.push( + context, + CupertinoPageRoute( + builder: (context) => + const ProfessionalServiceHistoryListScreen(), + ), + ) + : Navigator.push( + context, + CupertinoPageRoute( + builder: (context) => + const UserServiceHistoryListScreen(), + ), + ); }, ), shouldProModeActive(context, professionalState) diff --git a/lib/screens/lists/profession_list_screen.dart b/lib/screens/lists/profession_list_screen.dart index ce82133..16535c5 100644 --- a/lib/screens/lists/profession_list_screen.dart +++ b/lib/screens/lists/profession_list_screen.dart @@ -78,12 +78,20 @@ class _ProfessionListScreenState extends State { itemCount: _filteredProfessions!.length, itemBuilder: (BuildContext context, int index) { final profession = _filteredProfessions![index]; - return ListTile( - title: Text(profession), - onTap: () { - Navigator.pop( - context, _filteredProfessions![index]); - }, + return Container( + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + color: Colors.grey.withOpacity(0.2)), + ), + ), + child: ListTile( + title: Text(profession), + onTap: () { + Navigator.pop( + context, _filteredProfessions![index]); + }, + ), ); }, ) diff --git a/lib/screens/lists/professional_service_history_list_screen.dart b/lib/screens/lists/professional_service_history_list_screen.dart new file mode 100644 index 0000000..8f4fa1b --- /dev/null +++ b/lib/screens/lists/professional_service_history_list_screen.dart @@ -0,0 +1,204 @@ +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:injector/injector.dart'; +import 'package:intl/intl.dart'; +import 'package:professional_repository/professional_repository.dart'; +import 'package:prosappco/blocs/service_bloc/service_bloc.dart'; +import 'package:service_repository/service_repository.dart'; +import 'package:shimmer/shimmer.dart'; + +class ProfessionalServiceHistoryListScreen extends StatefulWidget { + const ProfessionalServiceHistoryListScreen({super.key}); + + @override + State createState() => + _ProfessionalServiceHistoryListScreenState(); +} + +class _ProfessionalServiceHistoryListScreenState + extends State { + late final ServiceBloc serviceBloc; + + @override + void initState() { + super.initState(); + + serviceBloc = Injector.appInstance.get(); + + serviceBloc.add( + LoadServicesHistoryForUser(FirebaseAuth.instance.currentUser!.uid)); + } + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (context) => serviceBloc, + child: Scaffold( + appBar: AppBar( + title: const Text('Historial'), + ), + body: BlocBuilder( + builder: (context, serviceState) { + if (serviceState is ServicesForUserLoaded) { + return serviceState.services.isEmpty + ? const Center( + child: Text('No tienes servicios'), + ) + : ListView.builder( + itemCount: serviceState.services.length, + itemBuilder: (_, index) { + final serviceInfo = serviceState.services[index]; + final user = serviceInfo.user; + final professional = serviceInfo.professional; + final service = serviceInfo.service; + + return Container( + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + color: Colors.grey.withOpacity(0.2)), + ), + ), + child: ListTile( + onTap: () {}, + leading: Container( + width: 60, + height: 60, + decoration: BoxDecoration( + color: Colors.grey.shade300, + shape: BoxShape.circle, + image: user.picture == null + ? null + : DecorationImage( + image: NetworkImage(user.picture!), + fit: BoxFit.contain, + ), + ), + child: user.picture == null + ? Icon( + CupertinoIcons.person, + color: Colors.grey.shade400, + size: 40, + ) + : null, + ), + title: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Flexible( + child: Text( + '${user.name}', + overflow: TextOverflow.ellipsis, + style: const TextStyle( + fontWeight: FontWeight.bold, + ), + ), + ), + const SizedBox(width: 5), + Text( + '${DateFormat('dd MMMM', 'es').format(DateTime.parse(service.day))} - ${ScheduleEntity.getFormatTime(service.range1Hour1)}', + style: TextStyle( + color: Colors.grey[600], + ), + ), + ], + ), + subtitle: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + const Text( + 'Estado:', + style: TextStyle( + fontStyle: FontStyle.italic, + ), + ), + const SizedBox(width: 5), + customStatus(service), + ], + ), + Text( + '"${service.description.trim()}"', + overflow: TextOverflow.ellipsis, + ), + ], + ), + ), + ); + }, + ); + } + + return Shimmer.fromColors( + baseColor: Colors.grey[300]!, + highlightColor: Colors.grey[100]!, + child: ListView.builder( + itemCount: 10, + itemBuilder: (_, __) => ListTile( + leading: CircleAvatar( + backgroundColor: Colors.grey[300], + radius: 30, + ), + title: Container( + height: 20, + decoration: BoxDecoration( + color: Colors.grey.shade300, + borderRadius: BorderRadius.circular(8), + ), + ), + subtitle: Container( + height: 15, + decoration: BoxDecoration( + color: Colors.grey.shade300, + borderRadius: BorderRadius.circular(8), + ), + ), + ), + ), + ); + }, + ), + ), + ); + } + + Container customStatus(ServiceEntity service) { + if (service.status == ServiceStatus.completed) { + return itemStatus(Colors.blueAccent, 'Completado'); + } + if (service.status == ServiceStatus.cancelled) { + return itemStatus(Colors.red, 'Cancelado'); + } + + return itemStatus(Colors.red, 'Cancelado'); + } + + Container itemStatus(Color color, String text) { + return Container( + padding: const EdgeInsets.symmetric( + horizontal: 8, + vertical: 3, + ), + decoration: BoxDecoration( + color: color, + borderRadius: BorderRadius.circular(8), + ), + child: Text( + text, + style: const TextStyle( + fontStyle: FontStyle.italic, + color: Colors.white, + ), + ), + ); + } +} + +// pending - 0 +// acepted - 1 +// active - 2 +// cancelled - 3 +// completed - 4 \ No newline at end of file diff --git a/lib/screens/lists/user_service_history_list_screen.dart b/lib/screens/lists/user_service_history_list_screen.dart index e68729a..bad94d8 100644 --- a/lib/screens/lists/user_service_history_list_screen.dart +++ b/lib/screens/lists/user_service_history_list_screen.dart @@ -1,16 +1,204 @@ +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:injector/injector.dart'; +import 'package:intl/intl.dart'; +import 'package:professional_repository/professional_repository.dart'; +import 'package:prosappco/blocs/service_bloc/service_bloc.dart'; +import 'package:service_repository/service_repository.dart'; +import 'package:shimmer/shimmer.dart'; -class UserServiceHistoryScreen extends StatefulWidget { - const UserServiceHistoryScreen({super.key}); +class UserServiceHistoryListScreen extends StatefulWidget { + const UserServiceHistoryListScreen({super.key}); @override - State createState() => - _UserServiceHistoryScreenState(); + State createState() => + _UserServiceHistoryListScreenState(); } -class _UserServiceHistoryScreenState extends State { +class _UserServiceHistoryListScreenState + extends State { + late final ServiceBloc serviceBloc; + + @override + void initState() { + super.initState(); + + serviceBloc = Injector.appInstance.get(); + + serviceBloc.add( + LoadServicesHistoryForUser(FirebaseAuth.instance.currentUser!.uid)); + } + @override Widget build(BuildContext context) { - return Container(); + return BlocProvider( + create: (context) => serviceBloc, + child: Scaffold( + appBar: AppBar( + title: const Text('Historial'), + ), + body: BlocBuilder( + builder: (context, serviceState) { + if (serviceState is ServicesForUserLoaded) { + return serviceState.services.isEmpty + ? const Center( + child: Text('No tienes servicios'), + ) + : ListView.builder( + itemCount: serviceState.services.length, + itemBuilder: (_, index) { + final serviceInfo = serviceState.services[index]; + final user = serviceInfo.user; + final professional = serviceInfo.professional; + final service = serviceInfo.service; + + return Container( + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + color: Colors.grey.withOpacity(0.2)), + ), + ), + child: ListTile( + onTap: () {}, + leading: Container( + width: 60, + height: 60, + decoration: BoxDecoration( + color: Colors.grey.shade300, + shape: BoxShape.circle, + image: user.picture == null + ? null + : DecorationImage( + image: NetworkImage(user.picture!), + fit: BoxFit.contain, + ), + ), + child: user.picture == null + ? Icon( + CupertinoIcons.person, + color: Colors.grey.shade400, + size: 40, + ) + : null, + ), + title: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Flexible( + child: Text( + '${user.name}', + overflow: TextOverflow.ellipsis, + style: const TextStyle( + fontWeight: FontWeight.bold, + ), + ), + ), + const SizedBox(width: 5), + Text( + '${DateFormat('dd MMMM', 'es').format(DateTime.parse(service.day))} - ${ScheduleEntity.getFormatTime(service.range1Hour1)}', + style: TextStyle( + color: Colors.grey[600], + ), + ), + ], + ), + subtitle: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + const Text( + 'Estado:', + style: TextStyle( + fontStyle: FontStyle.italic, + ), + ), + const SizedBox(width: 5), + customStatus(service), + ], + ), + Text( + '"${service.description.trim()}"', + overflow: TextOverflow.ellipsis, + ), + ], + ), + ), + ); + }, + ); + } + + return Shimmer.fromColors( + baseColor: Colors.grey[300]!, + highlightColor: Colors.grey[100]!, + child: ListView.builder( + itemCount: 10, + itemBuilder: (_, __) => ListTile( + leading: CircleAvatar( + backgroundColor: Colors.grey[300], + radius: 30, + ), + title: Container( + height: 20, + decoration: BoxDecoration( + color: Colors.grey.shade300, + borderRadius: BorderRadius.circular(8), + ), + ), + subtitle: Container( + height: 15, + decoration: BoxDecoration( + color: Colors.grey.shade300, + borderRadius: BorderRadius.circular(8), + ), + ), + ), + ), + ); + }, + ), + ), + ); + } + + Container customStatus(ServiceEntity service) { + if (service.status == ServiceStatus.completed) { + return itemStatus(Colors.blueAccent, 'Completado'); + } + if (service.status == ServiceStatus.cancelled) { + return itemStatus(Colors.red, 'Cancelado'); + } + + return itemStatus(Colors.red, 'Cancelado'); + } + + Container itemStatus(Color color, String text) { + return Container( + padding: const EdgeInsets.symmetric( + horizontal: 8, + vertical: 3, + ), + decoration: BoxDecoration( + color: color, + borderRadius: BorderRadius.circular(8), + ), + child: Text( + text, + style: const TextStyle( + fontStyle: FontStyle.italic, + color: Colors.white, + ), + ), + ); } } + +// pending - 0 +// acepted - 1 +// active - 2 +// cancelled - 3 +// completed - 4 \ No newline at end of file diff --git a/lib/screens/lists/user_service_list_screen.dart b/lib/screens/lists/user_service_list_screen.dart index e67db51..954087e 100644 --- a/lib/screens/lists/user_service_list_screen.dart +++ b/lib/screens/lists/user_service_list_screen.dart @@ -52,68 +52,78 @@ class _UserServiceListScreenState extends State { final professional = serviceInfo.professional; final service = serviceInfo.service; - return ListTile( - onTap: () {}, - leading: Container( - width: 60, - height: 60, - decoration: BoxDecoration( - color: Colors.grey.shade300, - shape: BoxShape.circle, - image: user.picture == null - ? null - : DecorationImage( - image: NetworkImage(user.picture!), - fit: BoxFit.contain, - ), + return Container( + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + color: Colors.grey.withOpacity(0.2)), ), - child: user.picture == null - ? Icon( - CupertinoIcons.person, - color: Colors.grey.shade400, - size: 40, - ) - : null, ), - title: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Flexible( - child: Text( - '${user.name}', + child: ListTile( + onTap: () {}, + leading: Container( + width: 60, + height: 60, + decoration: BoxDecoration( + color: Colors.grey.shade300, + shape: BoxShape.circle, + image: user.picture == null + ? null + : DecorationImage( + image: NetworkImage(user.picture!), + fit: BoxFit.contain, + ), + ), + child: user.picture == null + ? Icon( + CupertinoIcons.person, + color: Colors.grey.shade400, + size: 40, + ) + : null, + ), + title: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Flexible( + child: Text( + '${user.name}', + overflow: TextOverflow.ellipsis, + style: const TextStyle( + fontWeight: FontWeight.bold, + ), + ), + ), + const SizedBox(width: 5), + Text( + '${DateFormat('dd MMMM', 'es').format(DateTime.parse(service.day))} - ${ScheduleEntity.getFormatTime(service.range1Hour1)}', + style: TextStyle( + color: Colors.grey[600], + ), + ), + ], + ), + subtitle: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + const Text( + 'Estado:', + style: TextStyle( + fontStyle: FontStyle.italic, + ), + ), + const SizedBox(width: 5), + customStatus(service), + ], + ), + Text( + '"${service.description.trim()}"', overflow: TextOverflow.ellipsis, - style: const TextStyle( - fontWeight: FontWeight.bold), ), - ), - const SizedBox(width: 5), - Text( - '${DateFormat('dd MMMM', 'es').format(DateTime.parse(service.day))} - ${ScheduleEntity.getFormatTime(service.range1Hour1)}', - style: TextStyle( - color: Colors.grey[600], - ), - ), - ], - ), - subtitle: Row( - children: [ - const Text( - 'Estado:', - style: TextStyle(fontStyle: FontStyle.italic), - ), - const SizedBox(width: 5), - Container( - padding: const EdgeInsets.symmetric( - horizontal: 8, - vertical: 3, - ), - decoration: BoxDecoration( - color: Colors.blueAccent, - borderRadius: BorderRadius.circular(8), - ), - child: customStatus(service), - ), - ], + ], + ), ), ); }, @@ -153,48 +163,43 @@ class _UserServiceListScreenState extends State { ); } - Text customStatus(ServiceEntity service) { + Container customStatus(ServiceEntity service) { if (service.status == ServiceStatus.pending) { - return const Text( - 'Pendiente', - style: TextStyle( - fontStyle: FontStyle.italic, - color: Colors.white, - ), - ); + return itemStatus(Colors.black54, 'Pendiente'); } - if (service.status == ServiceStatus.acepted) { - return const Text( - 'Aceptado', - style: TextStyle( - fontStyle: FontStyle.italic, - color: Colors.white, - ), - ); + return itemStatus(Colors.green, 'Aceptado'); } if (service.status == ServiceStatus.active) { - return const Text( - 'Activo', - style: TextStyle( + return itemStatus(Colors.blueAccent, 'Activo'); + } + + return itemStatus(Colors.red, 'Cancelado'); + } + + Container itemStatus(Color color, String text) { + return Container( + padding: const EdgeInsets.symmetric( + horizontal: 8, + vertical: 3, + ), + decoration: BoxDecoration( + color: color, + borderRadius: BorderRadius.circular(8), + ), + child: Text( + text, + style: const TextStyle( fontStyle: FontStyle.italic, color: Colors.white, ), - ); - } - - return const Text( - 'Cancelado', - style: TextStyle( - fontStyle: FontStyle.italic, - color: Colors.white, ), ); } } -// pending -// acepted -// active -// cancelled -// completed \ No newline at end of file +// pending - 0 +// acepted - 1 +// active - 2 +// cancelled - 3 +// completed - 4 \ No newline at end of file diff --git a/lib/screens/professional/professional_form_screen.dart b/lib/screens/professional/professional_form_screen.dart index 2c4674c..31e91b2 100644 --- a/lib/screens/professional/professional_form_screen.dart +++ b/lib/screens/professional/professional_form_screen.dart @@ -56,175 +56,197 @@ class _ProfessionalFormScreenState extends State { ), 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: 10.0), - ElevatedButton( - onPressed: _pickCedulaPDF, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + return Column( + children: [ + Expanded( + child: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 40, vertical: 10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - const Text('Cargar pdf de la cedula'), - _cedulaPdfFile != null - ? const Icon(Icons.check) - : const Icon(Icons.file_upload_outlined), - ], - ), - ), - const SizedBox(height: 20.0), - TextFormField( - controller: _professionController, - readOnly: true, - onTap: () async { - final professionName = await Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return const ProfessionListScreen(); + 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; }, ), - ); - - if (professionName != null) { - _professionController.text = professionName; - } - }, - decoration: const InputDecoration( - labelText: 'Profesión', - prefixIcon: Icon(Icons.work_rounded), - hintText: 'Selecciona 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: 10.0), - ElevatedButton( - onPressed: _pickCertificadoPDF, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const Text('Cargar pdf del certificado'), - _certificadoPdfFile != null - ? const Icon(Icons.check) - : const Icon(Icons.file_upload_outlined), - ], - ), - ), - const SizedBox(height: 20.0), - TextField( - controller: _specialityController, - 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), - ElevatedButton( - onPressed: _pickEspecializacionesPDF, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const Expanded( - child: Text( - 'Cargar pdfs de tus especializaciones', - overflow: TextOverflow.ellipsis, - maxLines: 2, + const SizedBox(height: 10.0), + ElevatedButton( + onPressed: _pickCedulaPDF, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text('Cargar pdf de la cedula'), + _cedulaPdfFile != null + ? const Icon(Icons.check) + : const Icon(Icons.file_upload_outlined), + ], ), ), - _especializacionesPdfFiles?.isNotEmpty == true - ? const Icon(Icons.check) - : const Icon(Icons.file_upload_outlined), + const SizedBox(height: 20.0), + TextFormField( + controller: _professionController, + readOnly: true, + onTap: () async { + final professionName = await Navigator.push( + context, + CupertinoPageRoute( + builder: (BuildContext context) { + return const ProfessionListScreen(); + }, + ), + ); + + if (professionName != null) { + _professionController.text = professionName; + } + }, + decoration: const InputDecoration( + labelText: 'Profesión', + prefixIcon: Icon(Icons.work_rounded), + hintText: 'Selecciona 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: 10.0), + ElevatedButton( + onPressed: _pickCertificadoPDF, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text('Cargar pdf del certificado'), + _certificadoPdfFile != null + ? const Icon(Icons.check) + : const Icon(Icons.file_upload_outlined), + ], + ), + ), + const SizedBox(height: 20.0), + TextField( + controller: _specialityController, + 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), + ElevatedButton( + onPressed: _pickEspecializacionesPDF, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Expanded( + child: Text( + 'Cargar pdfs de tus especializaciones', + overflow: TextOverflow.ellipsis, + maxLines: 2, + ), + ), + _especializacionesPdfFiles?.isNotEmpty == true + ? const Icon(Icons.check) + : const Icon(Icons.file_upload_outlined), + ], + ), + ), + const SizedBox(height: 10.0), + Wrap( + spacing: 8.0, + runSpacing: 4.0, + children: _items + .map((item) => Chip( + label: Text(item), + backgroundColor: Theme.of(context) + .colorScheme + .tertiary, + labelStyle: + const TextStyle(color: Colors.blue), + deleteIconColor: Colors.blue, + onDeleted: () { + _removeItemFromList(item); + }, + shape: RoundedRectangleBorder( + side: const BorderSide( + color: Colors.blue, width: 0.3), + borderRadius: BorderRadius.circular(8), + ), + )) + .toList(), + ), + const SizedBox(height: 20), ], ), ), - const SizedBox(height: 10.0), - Wrap( - spacing: 8.0, - runSpacing: 4.0, - children: _items - .map((item) => Chip( - label: Text(item), - backgroundColor: - Theme.of(context).colorScheme.tertiary, - labelStyle: const TextStyle(color: Colors.blue), - deleteIconColor: Colors.blue, - onDeleted: () { - _removeItemFromList(item); - }, - shape: RoundedRectangleBorder( - side: const BorderSide( - color: Colors.blue, width: 0.3), - borderRadius: BorderRadius.circular(8), - ), - )) - .toList(), + ), + ), + const Divider( + height: 1, + thickness: 0.5, + ), + Padding( + padding: const EdgeInsets.symmetric( + vertical: 10, + horizontal: 15, ), - const SizedBox(height: 40), - ElevatedButton( + child: FilledButton( onPressed: () { final userId = context.read().state.user!.id; @@ -293,33 +315,26 @@ class _ProfessionalFormScreenState extends State { Navigator.pop(context); }, - style: ElevatedButton.styleFrom( - backgroundColor: Colors.blue, - padding: const EdgeInsets.symmetric(vertical: 5), + style: FilledButton.styleFrom( + backgroundColor: Theme.of(context).colorScheme.primary, + padding: const EdgeInsets.symmetric(vertical: 15), shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(50), + borderRadius: BorderRadius.circular(10), ), - shadowColor: Colors.grey, - // elevation: 0, ), child: Container( - constraints: const BoxConstraints( - maxWidth: 300.0, minHeight: 50.0), alignment: Alignment.center, + width: double.infinity, child: const Text( 'Enviar a revisión', style: TextStyle( color: Colors.white, - fontSize: 16, - fontWeight: FontWeight.bold, + fontSize: 18, ), ), ), - ), - const SizedBox(height: 20), - ], - ), - ), + )), + ], ); }, ), diff --git a/lib/screens/service/service_screen.dart b/lib/screens/service/service_screen.dart new file mode 100644 index 0000000..cc6935c --- /dev/null +++ b/lib/screens/service/service_screen.dart @@ -0,0 +1,15 @@ +import 'package:flutter/material.dart'; + +class ServiceScreen extends StatefulWidget { + const ServiceScreen({super.key}); + + @override + State createState() => _ServiceScreenState(); +} + +class _ServiceScreenState extends State { + @override + Widget build(BuildContext context) { + return Container(); + } +} diff --git a/packages/professional_repository/lib/src/repositories/firebase_professional_repository.dart b/packages/professional_repository/lib/src/repositories/firebase_professional_repository.dart index 2915777..ce52bbb 100644 --- a/packages/professional_repository/lib/src/repositories/firebase_professional_repository.dart +++ b/packages/professional_repository/lib/src/repositories/firebase_professional_repository.dart @@ -190,10 +190,9 @@ class FirebaseProfessionalRepository { } } - Future> getProfessionsFromIds( + Future> getProfessionalsFromIds( Iterable ids) async { try { - // Realizar una consulta única para obtener la información de todos los usuarios final querySnapshot = await professionalCollection .where(FieldPath.documentId, whereIn: ids) .get(); diff --git a/packages/service_repository/lib/src/repositories/firebase_service_repository.dart b/packages/service_repository/lib/src/repositories/firebase_service_repository.dart index 57e7d84..4f08d37 100644 --- a/packages/service_repository/lib/src/repositories/firebase_service_repository.dart +++ b/packages/service_repository/lib/src/repositories/firebase_service_repository.dart @@ -37,4 +37,14 @@ class FirebaseServiceRepository { .map((doc) => ServiceEntity.fromDocument(doc.data())) .toList()); } + + Stream> getServicesHistoryForUser(String userId) { + return serviceCollection + .where('user_id', isEqualTo: userId) + .where('status', whereIn: [3, 4]) + .snapshots() + .map((querySnapshot) => querySnapshot.docs + .map((doc) => ServiceEntity.fromDocument(doc.data())) + .toList()); + } } 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 6b6d95f..52b94c6 100644 --- a/packages/user_repository/lib/src/repositories/firebase_user_repository.dart +++ b/packages/user_repository/lib/src/repositories/firebase_user_repository.dart @@ -407,7 +407,6 @@ class FirebaseUserRepository implements UserRepository { @override Future> getUsersFromIds(Iterable ids) async { try { - // Realizar una consulta única para obtener la información de todos los usuarios final querySnapshot = await usersCollection.where(FieldPath.documentId, whereIn: ids).get();