import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:prosapp_web_app/models/schedules_entity.dart'; import 'package:prosapp_web_app/models/service.dart'; import 'package:prosapp_web_app/models/service_status.dart'; import 'package:prosapp_web_app/models/servicio_profesional.dart'; import 'package:prosapp_web_app/services/navigation_service.dart'; import 'package:provider/provider.dart'; import 'package:prosapp_web_app/providers/auth_provider.dart'; import 'package:prosapp_web_app/providers/services_provider.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 shadow => isDark ? Colors.transparent : Colors.black.withOpacity(0.05); Color get shadowSm => isDark ? Colors.transparent : Colors.black.withOpacity(0.04); } class ServicesRequestsView extends StatelessWidget { const ServicesRequestsView({super.key}); @override Widget build(BuildContext context) { final sp = Provider.of(context, listen: false); sp.getServicesRequestsForProfessional( Provider.of(context, listen: false).user!.id); return Container( color: context.bg, child: Consumer( builder: (context, sp, _) { if (sp.isLoading) { return const Center(child: CircularProgressIndicator(color: _kPrimary)); } return ListView( physics: const ClampingScrollPhysics(), padding: const EdgeInsets.only(bottom: 32), children: [ Center( child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 720), child: Padding( padding: const EdgeInsets.fromLTRB(16, 20, 16, 4), child: Row(children: [ Container( width: 36, height: 36, decoration: BoxDecoration( color: _kPrimary.withOpacity(0.08), borderRadius: BorderRadius.circular(10)), child: const Icon(Icons.inbox_outlined, size: 19, color: _kPrimary), ), const SizedBox(width: 10), Text('Solicitudes', style: TextStyle( fontSize: 18, fontWeight: FontWeight.w800, color: context.onSurface)), const Spacer(), if (sp.services.isNotEmpty) Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), decoration: BoxDecoration( color: _kPrimary.withOpacity(0.1), borderRadius: BorderRadius.circular(20)), child: Text('${sp.services.length}', style: const TextStyle( fontSize: 13, fontWeight: FontWeight.w700, color: _kPrimary)), ), ]), ), ), ), if (sp.services.isEmpty) _emptyState(context) else ...sp.services.map((data) => _RequestCard(data: data)), ], ); }, ), ); } Widget _emptyState(BuildContext context) { return Center( child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 720), child: Container( margin: const EdgeInsets.fromLTRB(16, 12, 16, 0), padding: const EdgeInsets.symmetric(vertical: 56, horizontal: 24), decoration: BoxDecoration( color: context.card, borderRadius: BorderRadius.circular(16), boxShadow: [BoxShadow(color: context.shadow, blurRadius: 12, offset: const Offset(0, 2))], ), child: Column(children: [ Container( width: 64, height: 64, decoration: BoxDecoration(color: context.subtle.withOpacity(0.1), shape: BoxShape.circle), child: Icon(Icons.inbox_outlined, size: 32, color: context.subtle), ), const SizedBox(height: 16), Text('Sin solicitudes pendientes', style: TextStyle(fontSize: 15, fontWeight: FontWeight.w700, color: context.muted)), const SizedBox(height: 6), Text('Cuando un cliente solicite tus servicios\naparecerá aquí.', textAlign: TextAlign.center, style: TextStyle(fontSize: 13, color: context.subtle, height: 1.5)), ]), ), ), ); } } class _RequestCard extends StatelessWidget { final ServicioProfesional data; const _RequestCard({required this.data}); @override Widget build(BuildContext context) { final service = data.service; final user = data.user; final statusInfo = _statusInfo(service.status); final dateStr = DateFormat('dd MMM yyyy', 'es').format(DateTime.parse(service.day)); final timeStr = ScheduleEntity.getFormatTime(service.range1Hour1) ?? ''; return Center( child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 720), child: Container( margin: const EdgeInsets.fromLTRB(16, 8, 16, 0), 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: MouseRegion( cursor: SystemMouseCursors.click, child: GestureDetector( onTap: () => NavigationService.replaceTo( '/dashboard/professional/service/${service.id}'), child: IntrinsicHeight( child: Row(children: [ Container(width: 4, color: statusInfo.color), Padding( padding: const EdgeInsets.all(14), child: Container( width: 52, height: 52, decoration: BoxDecoration( shape: BoxShape.circle, border: Border.all(color: statusInfo.color.withOpacity(0.3), width: 2)), child: ClipOval( child: (user.picture == null || user.picture!.isEmpty) ? Container( color: _kPrimary.withOpacity(0.1), child: Center( child: Text( user.name.isNotEmpty ? user.name[0].toUpperCase() : '?', style: const TextStyle( fontSize: 20, fontWeight: FontWeight.w700, color: _kPrimary), ), ), ) : FadeInImage.assetNetwork( placeholder: 'loader.gif', image: user.picture!, fit: BoxFit.cover), ), ), ), Expanded( child: Padding( padding: const EdgeInsets.symmetric(vertical: 14), 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: 6), Row(children: [ Icon(Icons.calendar_today_outlined, size: 12, color: context.subtle), const SizedBox(width: 4), Text('$dateStr · $timeStr', style: TextStyle( fontSize: 12, color: context.muted, fontWeight: FontWeight.w500)), ]), ], ), ), ), Padding( padding: const EdgeInsets.fromLTRB(8, 14, 12, 14), child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.end, children: [ _StatusBadge(info: statusInfo), const SizedBox(height: 8), Icon(Icons.chevron_right, size: 18, color: context.subtle), ], ), ), ]), ), ), ), ), ), ), ); } } class _StatusBadge extends StatelessWidget { final _StatusInfo info; const _StatusBadge({required this.info}); @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.symmetric(horizontal: 9, vertical: 4), decoration: BoxDecoration( color: info.color.withOpacity(0.1), borderRadius: BorderRadius.circular(20), border: Border.all(color: info.color.withOpacity(0.3)), ), child: Row(mainAxisSize: MainAxisSize.min, children: [ Icon(info.icon, size: 11, color: info.color), const SizedBox(width: 4), Text(info.label, style: TextStyle(fontSize: 11, fontWeight: FontWeight.w700, color: info.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) { ServiceStatus.pending => const _StatusInfo('Pendiente', Color(0xFFD97706), Icons.hourglass_top_outlined), ServiceStatus.acepted => const _StatusInfo('Aceptado', Color(0xFF1565C0), Icons.check_circle_outline), ServiceStatus.active => const _StatusInfo('En curso', Color(0xFF16A34A), Icons.play_circle_outline), ServiceStatus.completed => const _StatusInfo('Completado', Color(0xFF64748B), Icons.task_alt_outlined), ServiceStatus.denied => const _StatusInfo('Rechazado', Color(0xFFDC2626), Icons.cancel_outlined), ServiceStatus.cancelled => const _StatusInfo('Cancelado', Color(0xFF9CA3AF), Icons.remove_circle_outline), ServiceStatus.selfBooked => const _StatusInfo('Reservado', Color(0xFF7C3AED), Icons.bookmark_outline), };