diff --git a/lib/screens/lists/professional_service_history_list_screen.dart b/lib/screens/lists/professional_service_history_list_screen.dart index 79e06d1..6bdd4a1 100644 --- a/lib/screens/lists/professional_service_history_list_screen.dart +++ b/lib/screens/lists/professional_service_history_list_screen.dart @@ -1,5 +1,3 @@ -import 'package:user_repository/user_repository.dart'; - import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -10,6 +8,21 @@ import 'package:prosappco/blocs/service_bloc/service_bloc.dart'; import 'package:prosappco/screens/service/professional_service_screen.dart'; import 'package:service_repository/service_repository.dart'; import 'package:shimmer/shimmer.dart'; +import 'package:user_repository/user_repository.dart'; + +const _kPrimary = Color(0xFF1565C0); + +extension _Th on BuildContext { + ThemeData get _t => Theme.of(this); + Color get bg => _t.scaffoldBackgroundColor; + Color get card => _t.cardColor; + Color get onSurface => _t.colorScheme.onSurface; + Color get muted => _t.colorScheme.onSurface.withOpacity(0.55); + Color get subtle => _t.colorScheme.onSurface.withOpacity(0.35); + bool get isDark => _t.brightness == Brightness.dark; + Color get shadowSm => + isDark ? Colors.transparent : Colors.black.withOpacity(0.04); +} class ProfessionalServiceHistoryListScreen extends StatefulWidget { const ProfessionalServiceHistoryListScreen({super.key}); @@ -26,9 +39,7 @@ class _ProfessionalServiceHistoryListScreenState @override void initState() { super.initState(); - serviceBloc = Injector.appInstance.get(); - serviceBloc.add(LoadServicesHistoryForProfessional( ApiUserRepository.currentUserId ?? '')); } @@ -38,174 +49,248 @@ class _ProfessionalServiceHistoryListScreenState return BlocProvider( create: (context) => serviceBloc, child: Scaffold( + backgroundColor: context.bg, appBar: AppBar( title: const Text('Historial'), + backgroundColor: _kPrimary, + foregroundColor: Colors.white, + elevation: 0, ), 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 service = serviceInfo.service; - - return Container( - decoration: BoxDecoration( - border: Border( - bottom: BorderSide( - color: Colors.grey.withOpacity(0.2)), - ), - ), - child: ListTile( - onTap: () { - Navigator.push( - context, - CupertinoPageRoute( - builder: (context) => - ProfessionalServiceScreen( - serviceId: service.id!, - ), - ), - ); - }, - 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), - ], - ), - service.description.isEmpty - ? const SizedBox() - : Text( - '"${service.description.trim()}"', - overflow: TextOverflow.ellipsis, - ), - ], - ), - ), - ); - }, - ); + if (serviceState.services.isEmpty) return _emptyState(context); + return ListView.builder( + padding: const EdgeInsets.symmetric(vertical: 12), + itemCount: serviceState.services.length, + itemBuilder: (_, index) { + final info = serviceState.services[index]; + return _ServiceCard( + user: info.user, + service: info.service, + onTap: () => Navigator.push( + context, + CupertinoPageRoute( + builder: (_) => ProfessionalServiceScreen( + serviceId: info.service.id!)), + ), + ); + }, + ); } - - 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), - ), - ), - ), - ), - ); + return _shimmerList(); }, ), ), ); } +} - Container customStatus(ServiceEntity service) { - if (service.status == ServiceStatus.completed) { - return itemStatus(Colors.blueAccent, 'Completado'); - } - if (service.status == ServiceStatus.denied) { - return itemStatus(Colors.red, 'Rechazado'); - } +Widget _emptyState(BuildContext context) { + final subtle = Theme.of(context).colorScheme.onSurface.withOpacity(0.35); + final muted = Theme.of(context).colorScheme.onSurface.withOpacity(0.55); + return Center( + child: Padding( + padding: const EdgeInsets.all(32), + child: Column(mainAxisSize: MainAxisSize.min, children: [ + Container( + width: 72, height: 72, + decoration: BoxDecoration( + color: subtle.withOpacity(0.1), shape: BoxShape.circle), + child: Icon(Icons.history_outlined, size: 36, color: subtle), + ), + const SizedBox(height: 16), + Text('Sin historial', + style: TextStyle( + fontSize: 16, fontWeight: FontWeight.w700, color: muted)), + const SizedBox(height: 6), + Text('Tus servicios completados aparecerán aquí.', + textAlign: TextAlign.center, + style: TextStyle(fontSize: 13, color: subtle, height: 1.5)), + ]), + ), + ); +} - return itemStatus(Colors.red, 'Cancelado'); - } +Widget _shimmerList() { + return Shimmer.fromColors( + baseColor: Colors.grey[300]!, + highlightColor: Colors.grey[100]!, + child: ListView.builder( + padding: const EdgeInsets.symmetric(vertical: 12), + itemCount: 8, + itemBuilder: (_, __) => Container( + margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 6), + height: 84, + decoration: BoxDecoration( + color: Colors.white, borderRadius: BorderRadius.circular(14)), + ), + ), + ); +} + +class _ServiceCard extends StatelessWidget { + final MyUser user; + final ServiceEntity service; + final VoidCallback onTap; + const _ServiceCard( + {required this.user, required this.service, required this.onTap}); + + @override + Widget build(BuildContext context) { + final si = _statusInfo(service.status); + final dateStr = + DateFormat('dd MMM yyyy', 'es').format(DateTime.parse(service.day)); + final timeStr = ScheduleEntity.getFormatTime(service.range1Hour1) ?? ''; - Container itemStatus(Color color, String text) { return Container( - padding: const EdgeInsets.symmetric( - horizontal: 8, - vertical: 3, - ), + margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 6), decoration: BoxDecoration( - color: color, - borderRadius: BorderRadius.circular(8), + color: context.card, + borderRadius: BorderRadius.circular(14), + boxShadow: [ + BoxShadow( + color: context.shadowSm, + blurRadius: 10, + offset: const Offset(0, 2)) + ], ), - child: Text( - text, - style: const TextStyle( - fontStyle: FontStyle.italic, - color: Colors.white, + child: ClipRRect( + borderRadius: BorderRadius.circular(14), + child: InkWell( + onTap: onTap, + child: IntrinsicHeight( + child: Row(children: [ + Container(width: 4, color: si.color), + Padding( + padding: const EdgeInsets.all(12), + child: Container( + width: 50, height: 50, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: si.color.withOpacity(0.35), width: 2)), + child: ClipOval( + child: (user.picture == null || user.picture!.isEmpty) + ? Container( + color: _kPrimary.withOpacity(0.1), + child: Center( + child: Text( + user.name?.isNotEmpty == true + ? user.name![0].toUpperCase() + : '?', + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.w700, + color: _kPrimary), + ), + ), + ) + : Image.network(user.picture!, fit: BoxFit.cover), + ), + ), + ), + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text(user.name ?? '', + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w700, + color: context.onSurface)), + if (service.description.isNotEmpty) ...[ + const SizedBox(height: 2), + Text('"${service.description}"', + style: TextStyle( + fontSize: 12, + color: context.muted, + fontStyle: FontStyle.italic), + maxLines: 1, + overflow: TextOverflow.ellipsis), + ], + const SizedBox(height: 5), + Row(children: [ + Icon(Icons.calendar_today_outlined, + size: 11, color: context.subtle), + const SizedBox(width: 4), + Text('$dateStr · $timeStr', + style: TextStyle( + fontSize: 11, + color: context.muted, + fontWeight: FontWeight.w500)), + ]), + ], + ), + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(8, 12, 12, 12), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + _StatusBadge(si: si), + const SizedBox(height: 6), + Icon(Icons.chevron_right, size: 18, color: context.subtle), + ], + ), + ), + ]), + ), ), ), ); } } + +class _StatusBadge extends StatelessWidget { + final _StatusInfo si; + const _StatusBadge({required this.si}); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + decoration: BoxDecoration( + color: si.color.withOpacity(0.1), + borderRadius: BorderRadius.circular(20), + border: Border.all(color: si.color.withOpacity(0.3)), + ), + child: Row(mainAxisSize: MainAxisSize.min, children: [ + Icon(si.icon, size: 10, color: si.color), + const SizedBox(width: 4), + Text(si.label, + style: TextStyle( + fontSize: 10, fontWeight: FontWeight.w700, color: si.color)), + ]), + ); + } +} + +class _StatusInfo { + final String label; + final Color color; + final IconData icon; + const _StatusInfo(this.label, this.color, this.icon); +} + +_StatusInfo _statusInfo(ServiceStatus status) { + switch (status) { + case ServiceStatus.completed: + return const _StatusInfo( + 'Completado', Color(0xFF64748B), Icons.task_alt_outlined); + case ServiceStatus.denied: + return const _StatusInfo( + 'Rechazado', Color(0xFFDC2626), Icons.cancel_outlined); + case ServiceStatus.cancelled: + return const _StatusInfo( + 'Cancelado', Color(0xFF9CA3AF), Icons.remove_circle_outline); + default: + return const _StatusInfo( + 'Cancelado', Color(0xFF9CA3AF), Icons.remove_circle_outline); + } +} diff --git a/lib/screens/lists/professional_service_list_screen.dart b/lib/screens/lists/professional_service_list_screen.dart index 5fc91ea..ed002ef 100644 --- a/lib/screens/lists/professional_service_list_screen.dart +++ b/lib/screens/lists/professional_service_list_screen.dart @@ -1,5 +1,3 @@ -import 'package:user_repository/user_repository.dart'; - import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -10,6 +8,21 @@ import 'package:prosappco/blocs/service_bloc/service_bloc.dart'; import 'package:prosappco/screens/service/professional_service_screen.dart'; import 'package:service_repository/service_repository.dart'; import 'package:shimmer/shimmer.dart'; +import 'package:user_repository/user_repository.dart'; + +const _kPrimary = Color(0xFF1565C0); + +extension _Th on BuildContext { + ThemeData get _t => Theme.of(this); + Color get bg => _t.scaffoldBackgroundColor; + Color get card => _t.cardColor; + Color get onSurface => _t.colorScheme.onSurface; + Color get muted => _t.colorScheme.onSurface.withOpacity(0.55); + Color get subtle => _t.colorScheme.onSurface.withOpacity(0.35); + bool get isDark => _t.brightness == Brightness.dark; + Color get shadowSm => + isDark ? Colors.transparent : Colors.black.withOpacity(0.04); +} class ProfessionalServiceListScreen extends StatefulWidget { const ProfessionalServiceListScreen({super.key}); @@ -26,10 +39,9 @@ class _ProfessionalServiceListScreenState @override void initState() { super.initState(); - serviceBloc = Injector.appInstance.get(); - - serviceBloc.add(LoadServicesForProfessional(ApiUserRepository.currentUserId ?? '')); + serviceBloc.add( + LoadServicesForProfessional(ApiUserRepository.currentUserId ?? '')); } @override @@ -37,183 +49,257 @@ class _ProfessionalServiceListScreenState return BlocProvider( create: (context) => serviceBloc, child: Scaffold( + backgroundColor: context.bg, appBar: AppBar( title: const Text('Mis servicios'), + backgroundColor: _kPrimary, + foregroundColor: Colors.white, + elevation: 0, ), 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 service = serviceInfo.service; - - return Container( - decoration: BoxDecoration( - border: Border( - bottom: BorderSide( - color: Colors.grey.withOpacity(0.2)), - ), - ), - child: ListTile( - onTap: () { - Navigator.push( - context, - CupertinoPageRoute( - builder: (context) => - ProfessionalServiceScreen( - serviceId: service.id!, - ), - ), - ); - }, - 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), - ], - ), - service.description.isEmpty - ? Container() - : Text( - '"${service.description.trim()}"', - overflow: TextOverflow.ellipsis, - ), - ], - ), - ), - ); - }, - ); + if (serviceState.services.isEmpty) return _emptyState(context); + return ListView.builder( + padding: const EdgeInsets.symmetric(vertical: 12), + itemCount: serviceState.services.length, + itemBuilder: (_, index) { + final info = serviceState.services[index]; + return _ServiceCard( + user: info.user, + service: info.service, + onTap: () => Navigator.push( + context, + CupertinoPageRoute( + builder: (_) => ProfessionalServiceScreen( + serviceId: info.service.id!)), + ), + ); + }, + ); } - - 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), - ), - ), - ), - ), - ); + return _shimmerList(); }, ), ), ); } - - Container customStatus(ServiceEntity service) { - if (service.status == ServiceStatus.pending) { - return itemStatus(Colors.black54, 'Pendiente'); - } - if (service.status == ServiceStatus.acepted) { - return itemStatus(Colors.green, 'Aceptado'); - } - if (service.status == ServiceStatus.active) { - 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, - ), - ), - ); - } } -// pending - 0 -// acepted - 1 -// active - 2 -// cancelled - 3 -// completed - 4 \ No newline at end of file +Widget _emptyState(BuildContext context) { + final subtle = Theme.of(context).colorScheme.onSurface.withOpacity(0.35); + final muted = Theme.of(context).colorScheme.onSurface.withOpacity(0.55); + return Center( + child: Padding( + padding: const EdgeInsets.all(32), + child: Column(mainAxisSize: MainAxisSize.min, children: [ + Container( + width: 72, height: 72, + decoration: BoxDecoration( + color: subtle.withOpacity(0.1), shape: BoxShape.circle), + child: Icon(Icons.list_alt_outlined, size: 36, color: subtle), + ), + const SizedBox(height: 16), + Text('Sin servicios', + style: TextStyle( + fontSize: 16, fontWeight: FontWeight.w700, color: muted)), + const SizedBox(height: 6), + Text('Tus servicios activos aparecerán aquí.', + textAlign: TextAlign.center, + style: TextStyle(fontSize: 13, color: subtle, height: 1.5)), + ]), + ), + ); +} + +Widget _shimmerList() { + return Shimmer.fromColors( + baseColor: Colors.grey[300]!, + highlightColor: Colors.grey[100]!, + child: ListView.builder( + padding: const EdgeInsets.symmetric(vertical: 12), + itemCount: 8, + itemBuilder: (_, __) => Container( + margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 6), + height: 84, + decoration: BoxDecoration( + color: Colors.white, borderRadius: BorderRadius.circular(14)), + ), + ), + ); +} + +class _ServiceCard extends StatelessWidget { + final MyUser user; + final ServiceEntity service; + final VoidCallback onTap; + const _ServiceCard( + {required this.user, required this.service, required this.onTap}); + + @override + Widget build(BuildContext context) { + final si = _statusInfo(service.status); + final dateStr = + DateFormat('dd MMM yyyy', 'es').format(DateTime.parse(service.day)); + final timeStr = ScheduleEntity.getFormatTime(service.range1Hour1) ?? ''; + + return Container( + margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 6), + decoration: BoxDecoration( + color: context.card, + borderRadius: BorderRadius.circular(14), + boxShadow: [ + BoxShadow( + color: context.shadowSm, + blurRadius: 10, + offset: const Offset(0, 2)) + ], + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(14), + child: InkWell( + onTap: onTap, + child: IntrinsicHeight( + child: Row(children: [ + Container(width: 4, color: si.color), + Padding( + padding: const EdgeInsets.all(12), + child: Container( + width: 50, height: 50, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: si.color.withOpacity(0.35), width: 2)), + child: ClipOval( + child: (user.picture == null || user.picture!.isEmpty) + ? Container( + color: _kPrimary.withOpacity(0.1), + child: Center( + child: Text( + user.name?.isNotEmpty == true + ? user.name![0].toUpperCase() + : '?', + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.w700, + color: _kPrimary), + ), + ), + ) + : Image.network(user.picture!, fit: BoxFit.cover), + ), + ), + ), + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text(user.name ?? '', + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w700, + color: context.onSurface)), + if (service.description.isNotEmpty) ...[ + const SizedBox(height: 2), + Text('"${service.description}"', + style: TextStyle( + fontSize: 12, + color: context.muted, + fontStyle: FontStyle.italic), + maxLines: 1, + overflow: TextOverflow.ellipsis), + ], + const SizedBox(height: 5), + Row(children: [ + Icon(Icons.calendar_today_outlined, + size: 11, color: context.subtle), + const SizedBox(width: 4), + Text('$dateStr · $timeStr', + style: TextStyle( + fontSize: 11, + color: context.muted, + fontWeight: FontWeight.w500)), + ]), + ], + ), + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(8, 12, 12, 12), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + _StatusBadge(si: si), + const SizedBox(height: 6), + Icon(Icons.chevron_right, size: 18, color: context.subtle), + ], + ), + ), + ]), + ), + ), + ), + ); + } +} + +class _StatusBadge extends StatelessWidget { + final _StatusInfo si; + const _StatusBadge({required this.si}); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + decoration: BoxDecoration( + color: si.color.withOpacity(0.1), + borderRadius: BorderRadius.circular(20), + border: Border.all(color: si.color.withOpacity(0.3)), + ), + child: Row(mainAxisSize: MainAxisSize.min, children: [ + Icon(si.icon, size: 10, color: si.color), + const SizedBox(width: 4), + Text(si.label, + style: TextStyle( + fontSize: 10, fontWeight: FontWeight.w700, color: si.color)), + ]), + ); + } +} + +class _StatusInfo { + final String label; + final Color color; + final IconData icon; + const _StatusInfo(this.label, this.color, this.icon); +} + +_StatusInfo _statusInfo(ServiceStatus status) { + switch (status) { + case ServiceStatus.acepted: + return const _StatusInfo( + 'Aceptado', Color(0xFF1565C0), Icons.check_circle_outline); + case ServiceStatus.active: + return const _StatusInfo( + 'En curso', Color(0xFF16A34A), Icons.play_circle_outline); + case ServiceStatus.completed: + return const _StatusInfo( + 'Completado', Color(0xFF64748B), Icons.task_alt_outlined); + case ServiceStatus.denied: + return const _StatusInfo( + 'Rechazado', Color(0xFFDC2626), Icons.cancel_outlined); + case ServiceStatus.cancelled: + return const _StatusInfo( + 'Cancelado', Color(0xFF9CA3AF), Icons.remove_circle_outline); + case ServiceStatus.selfBooked: + return const _StatusInfo( + 'Reservado', Color(0xFF7C3AED), Icons.bookmark_outline); + default: + return const _StatusInfo( + 'Pendiente', Color(0xFFD97706), Icons.hourglass_top_outlined); + } +} diff --git a/lib/screens/lists/user_service_history_list_screen.dart b/lib/screens/lists/user_service_history_list_screen.dart index 15042ec..f04602d 100644 --- a/lib/screens/lists/user_service_history_list_screen.dart +++ b/lib/screens/lists/user_service_history_list_screen.dart @@ -1,5 +1,3 @@ -import 'package:user_repository/user_repository.dart'; - import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -10,6 +8,21 @@ import 'package:prosappco/blocs/service_bloc/service_bloc.dart'; import 'package:prosappco/screens/service/user_service_screen.dart'; import 'package:service_repository/service_repository.dart'; import 'package:shimmer/shimmer.dart'; +import 'package:user_repository/user_repository.dart'; + +const _kPrimary = Color(0xFF1565C0); + +extension _Th on BuildContext { + ThemeData get _t => Theme.of(this); + Color get bg => _t.scaffoldBackgroundColor; + Color get card => _t.cardColor; + Color get onSurface => _t.colorScheme.onSurface; + Color get muted => _t.colorScheme.onSurface.withOpacity(0.55); + Color get subtle => _t.colorScheme.onSurface.withOpacity(0.35); + bool get isDark => _t.brightness == Brightness.dark; + Color get shadowSm => + isDark ? Colors.transparent : Colors.black.withOpacity(0.04); +} class UserServiceHistoryListScreen extends StatefulWidget { const UserServiceHistoryListScreen({super.key}); @@ -26,9 +39,7 @@ class _UserServiceHistoryListScreenState @override void initState() { super.initState(); - serviceBloc = Injector.appInstance.get(); - serviceBloc.add( LoadServicesHistoryForUser(ApiUserRepository.currentUserId ?? '')); } @@ -38,178 +49,245 @@ class _UserServiceHistoryListScreenState return BlocProvider( create: (context) => serviceBloc, child: Scaffold( + backgroundColor: context.bg, appBar: AppBar( title: const Text('Historial'), + backgroundColor: _kPrimary, + foregroundColor: Colors.white, + elevation: 0, ), 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: () { - Navigator.push( - context, - CupertinoPageRoute( - builder: (context) => UserServiceScreen( - serviceId: service.id!, - ), - ), - ); - }, - 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, - ), - ], - ), - ), - ); - }, - ); + if (serviceState.services.isEmpty) return _emptyState(context); + return ListView.builder( + padding: const EdgeInsets.symmetric(vertical: 12), + itemCount: serviceState.services.length, + itemBuilder: (_, index) { + final info = serviceState.services[index]; + return _ServiceCard( + user: info.user, + service: info.service, + onTap: () => Navigator.push( + context, + CupertinoPageRoute( + builder: (_) => + UserServiceScreen(serviceId: info.service.id!)), + ), + ); + }, + ); } - - 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), - ), - ), - ), - ), - ); + return _shimmerList(); }, ), ), ); } - - 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 +Widget _emptyState(BuildContext context) { + final subtle = Theme.of(context).colorScheme.onSurface.withOpacity(0.35); + final muted = Theme.of(context).colorScheme.onSurface.withOpacity(0.55); + return Center( + child: Padding( + padding: const EdgeInsets.all(32), + child: Column(mainAxisSize: MainAxisSize.min, children: [ + Container( + width: 72, height: 72, + decoration: BoxDecoration( + color: subtle.withOpacity(0.1), shape: BoxShape.circle), + child: Icon(Icons.history_outlined, size: 36, color: subtle), + ), + const SizedBox(height: 16), + Text('Sin historial', + style: TextStyle( + fontSize: 16, fontWeight: FontWeight.w700, color: muted)), + const SizedBox(height: 6), + Text('Tus servicios completados aparecerán aquí.', + textAlign: TextAlign.center, + style: TextStyle(fontSize: 13, color: subtle, height: 1.5)), + ]), + ), + ); +} + +Widget _shimmerList() { + return Shimmer.fromColors( + baseColor: Colors.grey[300]!, + highlightColor: Colors.grey[100]!, + child: ListView.builder( + padding: const EdgeInsets.symmetric(vertical: 12), + itemCount: 8, + itemBuilder: (_, __) => Container( + margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 6), + height: 84, + decoration: BoxDecoration( + color: Colors.white, borderRadius: BorderRadius.circular(14)), + ), + ), + ); +} + +class _ServiceCard extends StatelessWidget { + final MyUser user; + final ServiceEntity service; + final VoidCallback onTap; + const _ServiceCard( + {required this.user, required this.service, required this.onTap}); + + @override + Widget build(BuildContext context) { + final si = _statusInfo(service.status); + final dateStr = + DateFormat('dd MMM yyyy', 'es').format(DateTime.parse(service.day)); + final timeStr = ScheduleEntity.getFormatTime(service.range1Hour1) ?? ''; + + return Container( + margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 6), + decoration: BoxDecoration( + color: context.card, + borderRadius: BorderRadius.circular(14), + boxShadow: [ + BoxShadow( + color: context.shadowSm, + blurRadius: 10, + offset: const Offset(0, 2)) + ], + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(14), + child: InkWell( + onTap: onTap, + child: IntrinsicHeight( + child: Row(children: [ + Container(width: 4, color: si.color), + Padding( + padding: const EdgeInsets.all(12), + child: Container( + width: 50, height: 50, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: si.color.withOpacity(0.35), width: 2)), + child: ClipOval( + child: (user.picture == null || user.picture!.isEmpty) + ? Container( + color: _kPrimary.withOpacity(0.1), + child: Center( + child: Text( + user.name?.isNotEmpty == true + ? user.name![0].toUpperCase() + : '?', + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.w700, + color: _kPrimary), + ), + ), + ) + : Image.network(user.picture!, fit: BoxFit.cover), + ), + ), + ), + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text(user.name ?? '', + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w700, + color: context.onSurface)), + if (service.description.isNotEmpty) ...[ + const SizedBox(height: 2), + Text('"${service.description}"', + style: TextStyle( + fontSize: 12, + color: context.muted, + fontStyle: FontStyle.italic), + maxLines: 1, + overflow: TextOverflow.ellipsis), + ], + const SizedBox(height: 5), + Row(children: [ + Icon(Icons.calendar_today_outlined, + size: 11, color: context.subtle), + const SizedBox(width: 4), + Text('$dateStr · $timeStr', + style: TextStyle( + fontSize: 11, + color: context.muted, + fontWeight: FontWeight.w500)), + ]), + ], + ), + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(8, 12, 12, 12), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + _StatusBadge(si: si), + const SizedBox(height: 6), + Icon(Icons.chevron_right, size: 18, color: context.subtle), + ], + ), + ), + ]), + ), + ), + ), + ); + } +} + +class _StatusBadge extends StatelessWidget { + final _StatusInfo si; + const _StatusBadge({required this.si}); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + decoration: BoxDecoration( + color: si.color.withOpacity(0.1), + borderRadius: BorderRadius.circular(20), + border: Border.all(color: si.color.withOpacity(0.3)), + ), + child: Row(mainAxisSize: MainAxisSize.min, children: [ + Icon(si.icon, size: 10, color: si.color), + const SizedBox(width: 4), + Text(si.label, + style: TextStyle( + fontSize: 10, fontWeight: FontWeight.w700, color: si.color)), + ]), + ); + } +} + +class _StatusInfo { + final String label; + final Color color; + final IconData icon; + const _StatusInfo(this.label, this.color, this.icon); +} + +_StatusInfo _statusInfo(ServiceStatus status) { + switch (status) { + case ServiceStatus.completed: + return const _StatusInfo( + 'Completado', Color(0xFF64748B), Icons.task_alt_outlined); + case ServiceStatus.denied: + return const _StatusInfo( + 'Rechazado', Color(0xFFDC2626), Icons.cancel_outlined); + default: + return const _StatusInfo( + 'Cancelado', Color(0xFF9CA3AF), Icons.remove_circle_outline); + } +} diff --git a/lib/screens/lists/user_service_list_screen.dart b/lib/screens/lists/user_service_list_screen.dart index aeb4da1..d6bc50b 100644 --- a/lib/screens/lists/user_service_list_screen.dart +++ b/lib/screens/lists/user_service_list_screen.dart @@ -1,5 +1,3 @@ -import 'package:user_repository/user_repository.dart'; - import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -10,6 +8,21 @@ import 'package:prosappco/blocs/service_bloc/service_bloc.dart'; import 'package:prosappco/screens/service/user_service_screen.dart'; import 'package:service_repository/service_repository.dart'; import 'package:shimmer/shimmer.dart'; +import 'package:user_repository/user_repository.dart'; + +const _kPrimary = Color(0xFF1565C0); + +extension _Th on BuildContext { + ThemeData get _t => Theme.of(this); + Color get bg => _t.scaffoldBackgroundColor; + Color get card => _t.cardColor; + Color get onSurface => _t.colorScheme.onSurface; + Color get muted => _t.colorScheme.onSurface.withOpacity(0.55); + Color get subtle => _t.colorScheme.onSurface.withOpacity(0.35); + bool get isDark => _t.brightness == Brightness.dark; + Color get shadowSm => + isDark ? Colors.transparent : Colors.black.withOpacity(0.04); +} class UserServiceListScreen extends StatefulWidget { const UserServiceListScreen({super.key}); @@ -24,11 +37,8 @@ class _UserServiceListScreenState extends State { @override void initState() { super.initState(); - serviceBloc = Injector.appInstance.get(); - - serviceBloc - .add(LoadServicesForUser(ApiUserRepository.currentUserId ?? '')); + serviceBloc.add(LoadServicesForUser(ApiUserRepository.currentUserId ?? '')); } @override @@ -36,183 +46,257 @@ class _UserServiceListScreenState extends State { return BlocProvider( create: (context) => serviceBloc, child: Scaffold( + backgroundColor: context.bg, appBar: AppBar( title: const Text('Mis Servicios'), + backgroundColor: _kPrimary, + foregroundColor: Colors.white, + elevation: 0, ), 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: () { - Navigator.push( - context, - CupertinoPageRoute( - builder: (context) => UserServiceScreen( - serviceId: service.id!, - ), - ), - ); - }, - 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), - ], - ), - service.description.isEmpty - ? Container() - : Text( - '"${service.description.trim()}"', - overflow: TextOverflow.ellipsis, - ), - ], - ), - ), - ); - }, - ); + if (serviceState.services.isEmpty) return _emptyState(context); + return ListView.builder( + padding: const EdgeInsets.symmetric(vertical: 12), + itemCount: serviceState.services.length, + itemBuilder: (_, index) { + final info = serviceState.services[index]; + return _ServiceCard( + user: info.user, + service: info.service, + onTap: () => Navigator.push( + context, + CupertinoPageRoute( + builder: (_) => + UserServiceScreen(serviceId: info.service.id!)), + ), + ); + }, + ); } - - 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), - ), - ), - ), - ), - ); + return _shimmerList(); }, ), ), ); } - - Container customStatus(ServiceEntity service) { - if (service.status == ServiceStatus.pending) { - return itemStatus(Colors.black54, 'Pendiente'); - } - if (service.status == ServiceStatus.acepted) { - return itemStatus(Colors.green, 'Aceptado'); - } - if (service.status == ServiceStatus.active) { - 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, - ), - ), - ); - } } -// pending - 0 -// acepted - 1 -// active - 2 -// cancelled - 3 -// completed - 4 \ No newline at end of file +Widget _emptyState(BuildContext context) { + final subtle = Theme.of(context).colorScheme.onSurface.withOpacity(0.35); + final muted = Theme.of(context).colorScheme.onSurface.withOpacity(0.55); + return Center( + child: Padding( + padding: const EdgeInsets.all(32), + child: Column(mainAxisSize: MainAxisSize.min, children: [ + Container( + width: 72, height: 72, + decoration: BoxDecoration( + color: subtle.withOpacity(0.1), shape: BoxShape.circle), + child: Icon(Icons.list_alt_outlined, size: 36, color: subtle), + ), + const SizedBox(height: 16), + Text('Sin servicios', + style: TextStyle( + fontSize: 16, fontWeight: FontWeight.w700, color: muted)), + const SizedBox(height: 6), + Text('Tus servicios activos aparecerán aquí.', + textAlign: TextAlign.center, + style: TextStyle(fontSize: 13, color: subtle, height: 1.5)), + ]), + ), + ); +} + +Widget _shimmerList() { + return Shimmer.fromColors( + baseColor: Colors.grey[300]!, + highlightColor: Colors.grey[100]!, + child: ListView.builder( + padding: const EdgeInsets.symmetric(vertical: 12), + itemCount: 8, + itemBuilder: (_, __) => Container( + margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 6), + height: 84, + decoration: BoxDecoration( + color: Colors.white, borderRadius: BorderRadius.circular(14)), + ), + ), + ); +} + +class _ServiceCard extends StatelessWidget { + final MyUser user; + final ServiceEntity service; + final VoidCallback onTap; + const _ServiceCard( + {required this.user, required this.service, required this.onTap}); + + @override + Widget build(BuildContext context) { + final si = _statusInfo(service.status); + final dateStr = + DateFormat('dd MMM yyyy', 'es').format(DateTime.parse(service.day)); + final timeStr = ScheduleEntity.getFormatTime(service.range1Hour1) ?? ''; + + return Container( + margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 6), + decoration: BoxDecoration( + color: context.card, + borderRadius: BorderRadius.circular(14), + boxShadow: [ + BoxShadow( + color: context.shadowSm, + blurRadius: 10, + offset: const Offset(0, 2)) + ], + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(14), + child: InkWell( + onTap: onTap, + child: IntrinsicHeight( + child: Row(children: [ + Container(width: 4, color: si.color), + Padding( + padding: const EdgeInsets.all(12), + child: Container( + width: 50, height: 50, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: si.color.withOpacity(0.35), width: 2)), + child: ClipOval( + child: (user.picture == null || user.picture!.isEmpty) + ? Container( + color: _kPrimary.withOpacity(0.1), + child: Center( + child: Text( + user.name?.isNotEmpty == true + ? user.name![0].toUpperCase() + : '?', + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.w700, + color: _kPrimary), + ), + ), + ) + : Image.network(user.picture!, fit: BoxFit.cover), + ), + ), + ), + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text(user.name ?? '', + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w700, + color: context.onSurface)), + if (service.description.isNotEmpty) ...[ + const SizedBox(height: 2), + Text('"${service.description}"', + style: TextStyle( + fontSize: 12, + color: context.muted, + fontStyle: FontStyle.italic), + maxLines: 1, + overflow: TextOverflow.ellipsis), + ], + const SizedBox(height: 5), + Row(children: [ + Icon(Icons.calendar_today_outlined, + size: 11, color: context.subtle), + const SizedBox(width: 4), + Text('$dateStr · $timeStr', + style: TextStyle( + fontSize: 11, + color: context.muted, + fontWeight: FontWeight.w500)), + ]), + ], + ), + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(8, 12, 12, 12), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + _StatusBadge(si: si), + const SizedBox(height: 6), + Icon(Icons.chevron_right, size: 18, color: context.subtle), + ], + ), + ), + ]), + ), + ), + ), + ); + } +} + +class _StatusBadge extends StatelessWidget { + final _StatusInfo si; + const _StatusBadge({required this.si}); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + decoration: BoxDecoration( + color: si.color.withOpacity(0.1), + borderRadius: BorderRadius.circular(20), + border: Border.all(color: si.color.withOpacity(0.3)), + ), + child: Row(mainAxisSize: MainAxisSize.min, children: [ + Icon(si.icon, size: 10, color: si.color), + const SizedBox(width: 4), + Text(si.label, + style: TextStyle( + fontSize: 10, fontWeight: FontWeight.w700, color: si.color)), + ]), + ); + } +} + +class _StatusInfo { + final String label; + final Color color; + final IconData icon; + const _StatusInfo(this.label, this.color, this.icon); +} + +_StatusInfo _statusInfo(ServiceStatus status) { + switch (status) { + case ServiceStatus.acepted: + return const _StatusInfo( + 'Aceptado', Color(0xFF1565C0), Icons.check_circle_outline); + case ServiceStatus.active: + return const _StatusInfo( + 'En curso', Color(0xFF16A34A), Icons.play_circle_outline); + case ServiceStatus.completed: + return const _StatusInfo( + 'Completado', Color(0xFF64748B), Icons.task_alt_outlined); + case ServiceStatus.denied: + return const _StatusInfo( + 'Rechazado', Color(0xFFDC2626), Icons.cancel_outlined); + case ServiceStatus.cancelled: + return const _StatusInfo( + 'Cancelado', Color(0xFF9CA3AF), Icons.remove_circle_outline); + case ServiceStatus.selfBooked: + return const _StatusInfo( + 'Reservado', Color(0xFF7C3AED), Icons.bookmark_outline); + default: + return const _StatusInfo( + 'Pendiente', Color(0xFFD97706), Icons.hourglass_top_outlined); + } +}