import 'dart:typed_data'; import 'package:file_picker/file_picker.dart'; import 'package:prosapp_web_app/models/pro_state.dart'; import 'package:prosapp_web_app/models/profession.dart'; import 'package:prosapp_web_app/models/usuario.dart'; import 'package:prosapp_web_app/providers/auth_provider.dart'; import 'package:prosapp_web_app/providers/professional_form_provider.dart'; import 'package:prosapp_web_app/providers/professional_provider.dart'; import 'package:prosapp_web_app/providers/professions_provider.dart'; import 'package:prosapp_web_app/providers/profile_form_provider.dart'; import 'package:prosapp_web_app/providers/theme_provider.dart'; import 'package:prosapp_web_app/services/notifications_service.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; class RequestProfessionalView extends StatefulWidget { const RequestProfessionalView({super.key}); @override State createState() => _RequestProfessionalViewState(); } class _RequestProfessionalViewState extends State { @override void initState() { super.initState(); final authProvider = Provider.of(context, listen: false); final profileFormProvider = Provider.of(context, listen: false); final professionalFormProvider = Provider.of(context, listen: false); final proProvider = Provider.of(context, listen: false); profileFormProvider.user = authProvider.user; proProvider.getProfessional(authProvider.user!.id).then((value) { professionalFormProvider.setProfesional(value); }); } @override Widget build(BuildContext context) { return ListView( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), children: [ Center( child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 680), child: const _ProfessionalForm(), ), ), ], ); } } class _ProfessionalForm extends StatefulWidget { const _ProfessionalForm(); @override State<_ProfessionalForm> createState() => _ProfessionalFormState(); } class _ProfessionalFormState extends State<_ProfessionalForm> { final TextEditingController _specialityController = TextEditingController(); List specializations = []; void _addSpecialization() { final item = _specialityController.text.trim(); if (item.isNotEmpty && !specializations.contains(item)) { setState(() { specializations.add(item); _specialityController.clear(); }); } } @override Widget build(BuildContext context) { final isDark = context.watch().isDark; final authProvider = Provider.of(context); final professionsProvider = Provider.of(context); final user = authProvider.user!; final cardBg = isDark ? const Color(0xFF1E293B) : Colors.white; final border = isDark ? const Color(0xFF334155) : const Color(0xFFE5E7EB); final textPrimary = isDark ? Colors.white : const Color(0xFF111827); final textSecondary = isDark ? const Color(0xFF94A3B8) : const Color(0xFF6B7280); return Consumer( builder: (context, fp, _) { if (fp.profesional == null) { return const Center(child: Padding( padding: EdgeInsets.all(40), child: CircularProgressIndicator(), )); } final pro = fp.profesional!; switch (enumToInt(user.proState)) { // ── Estado 0: Formulario ────────────────────────────────────────── case 0: return Form( key: fp.formKey, autovalidateMode: AutovalidateMode.onUserInteraction, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // Header Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( gradient: const LinearGradient( colors: [Color(0xFF42A4EF), Color(0xFF1565C0)], begin: Alignment.topLeft, end: Alignment.bottomRight, ), borderRadius: BorderRadius.circular(16), ), child: Column( children: const [ Icon(Icons.work_outline_rounded, color: Colors.white, size: 36), SizedBox(height: 10), Text('Solicitud de Profesional', style: TextStyle( color: Colors.white, fontSize: 20, fontWeight: FontWeight.bold, )), SizedBox(height: 4), Text('Completa tu información para comenzar a ofrecer servicios', style: TextStyle(color: Colors.white70, fontSize: 12), textAlign: TextAlign.center), ], ), ), const SizedBox(height: 20), // ── Sección 1: Identificación ── _Section( title: 'Identificación', icon: Icons.badge_outlined, cardBg: cardBg, border: border, textPrimary: textPrimary, children: [ TextFormField( initialValue: pro.identification, style: TextStyle(color: textPrimary), validator: (v) { if (v == null || v.isEmpty) return 'La cédula es obligatoria'; if (v.trim().length < 6) return 'Mínimo 6 caracteres'; return null; }, onChanged: (v) => fp.copyProfesionalWith(identification: v), decoration: _inputDec('Número de cédula', Icons.badge_outlined, isDark), ), const SizedBox(height: 12), _UploadButton( label: 'PDF de la cédula', uploaded: pro.identificationPicture.isNotEmpty, isDark: isDark, onTap: () async { final bytes = await _pickPdf(); if (bytes != null) { NotificationsService.showBusyIndicator(context); await fp.uploadPdfIdentification(bytes, user.id); if (context.mounted) Navigator.pop(context); } }, ), ], ), const SizedBox(height: 12), // ── Sección 2: Certificación ── _Section( title: 'Certificación', icon: Icons.health_and_safety_outlined, cardBg: cardBg, border: border, textPrimary: textPrimary, children: [ TextFormField( initialValue: pro.rethusCode, style: TextStyle(color: textPrimary), onChanged: (v) => fp.copyProfesionalWith(rethusCode: v), decoration: _inputDec('Código RETHUS (opcional)', Icons.health_and_safety_outlined, isDark), ), const SizedBox(height: 6), Text('El código RETHUS es el registro del talento humano en salud de Colombia.', style: TextStyle(fontSize: 11, color: textSecondary)), const SizedBox(height: 12), _UploadButton( label: 'PDF del certificado profesional', uploaded: pro.certificatePicture.isNotEmpty, isDark: isDark, onTap: () async { final bytes = await _pickPdf(); if (bytes != null) { NotificationsService.showBusyIndicator(context); await fp.uploadPdfCertificate(bytes, user.id); if (context.mounted) Navigator.pop(context); } }, ), ], ), const SizedBox(height: 12), // ── Sección 3: Profesión ── _Section( title: 'Profesión', icon: Icons.work_outline_outlined, cardBg: cardBg, border: border, textPrimary: textPrimary, children: [ DropdownButtonFormField( value: pro.profession.isEmpty ? null : pro.profession, dropdownColor: cardBg, style: TextStyle(color: textPrimary), validator: (v) => v == null ? 'La profesión es obligatoria' : null, decoration: _inputDec('Selecciona tu profesión', Icons.work_outline_outlined, isDark), items: professionsProvider.professions .map((p) => DropdownMenuItem( value: p.name, child: Text(p.name, style: TextStyle(color: textPrimary)), )) .toList(), onChanged: (v) => fp.copyProfesionalWith(profession: v), ), ], ), const SizedBox(height: 12), // ── Sección 4: Especializaciones ── _Section( title: 'Especializaciones', icon: Icons.assignment_outlined, cardBg: cardBg, border: border, textPrimary: textPrimary, children: [ Row( children: [ Expanded( child: TextFormField( controller: _specialityController, style: TextStyle(color: textPrimary), onFieldSubmitted: (_) => _addSpecialization(), decoration: _inputDec( 'Agregar especialización', Icons.assignment_outlined, isDark, ), ), ), const SizedBox(width: 8), Material( color: const Color(0xFF42A4EF), borderRadius: BorderRadius.circular(10), child: InkWell( onTap: _addSpecialization, borderRadius: BorderRadius.circular(10), child: const Padding( padding: EdgeInsets.all(14), child: Icon(Icons.add, color: Colors.white, size: 20), ), ), ), ], ), if (specializations.isNotEmpty) ...[ const SizedBox(height: 10), Wrap( spacing: 8, runSpacing: 6, children: specializations .map((item) => Chip( label: Text(item, style: const TextStyle( color: Color(0xFF42A4EF), fontSize: 12)), backgroundColor: const Color(0xFF42A4EF).withOpacity(0.1), deleteIconColor: const Color(0xFF42A4EF), side: const BorderSide( color: Color(0xFF42A4EF), width: 0.5), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8)), onDeleted: () => setState( () => specializations.remove(item)), )) .toList(), ), ], const SizedBox(height: 12), _UploadButton( label: 'PDFs de especializaciones (múltiples)', uploaded: pro.specializationsPictures.isNotEmpty, isDark: isDark, onTap: () async { final result = await FilePicker.platform.pickFiles( type: FileType.custom, allowMultiple: true, allowedExtensions: ['pdf'], withData: true, ); if (result != null) { final filesBytes = result.files .where((f) => f.bytes != null) .map((f) => f.bytes!) .toList(); if (filesBytes.isNotEmpty && context.mounted) { NotificationsService.showBusyIndicator(context); await fp.uploadPdfSpecializations( filesBytes, user.id); if (context.mounted) Navigator.pop(context); } } }, ), ], ), const SizedBox(height: 24), // ── Botón enviar ── SizedBox( width: double.infinity, child: ElevatedButton.icon( icon: const Icon(Icons.send_rounded, size: 18), label: const Text('Enviar a revisión', style: TextStyle( fontSize: 15, fontWeight: FontWeight.w600)), style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF42A4EF), foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(vertical: 16), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12)), elevation: 0, ), onPressed: () async { fp.copyProfesionalWith(specializations: specializations); final res = await fp.updateProfesionalInfo(user.id); if (res && context.mounted) { Provider.of(context, listen: false) .refreshUser(); final pfp = Provider.of(context, listen: false); pfp.copyUserWith(proState: ProState.pending); pfp.updateUserInfoNoValid(); } }, ), ), const SizedBox(height: 20), ], ), ); // ── Estado 1: En revisión ───────────────────────────────────────── case 1: return Container( margin: const EdgeInsets.only(top: 20), padding: const EdgeInsets.all(32), decoration: BoxDecoration( color: cardBg, borderRadius: BorderRadius.circular(16), border: Border.all(color: border), ), child: Column( children: [ const Image(image: AssetImage('assets/checklist.gif'), width: 200), const SizedBox(height: 20), Text('Solicitud en revisión', style: TextStyle( fontSize: 20, fontWeight: FontWeight.bold, color: textPrimary, )), const SizedBox(height: 12), Text( 'Gracias por enviar tu información. Estamos revisando tus datos y te notificaremos cuando tu cuenta esté aprobada.', textAlign: TextAlign.center, style: TextStyle(color: textSecondary, fontSize: 14, height: 1.5), ), const SizedBox(height: 20), Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10), decoration: BoxDecoration( color: const Color(0xFFF59E0B).withOpacity(0.12), borderRadius: BorderRadius.circular(10), border: Border.all(color: const Color(0xFFF59E0B).withOpacity(0.4)), ), child: const Row( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.hourglass_empty_rounded, color: Color(0xFFF59E0B), size: 18), SizedBox(width: 8), Text('Revisión en proceso', style: TextStyle( color: Color(0xFFF59E0B), fontWeight: FontWeight.w600, fontSize: 13, )), ], ), ), ], ), ); // ── Estado 3: Rechazado ─────────────────────────────────────────── case 3: return Container( margin: const EdgeInsets.only(top: 20), padding: const EdgeInsets.all(32), decoration: BoxDecoration( color: cardBg, borderRadius: BorderRadius.circular(16), border: Border.all(color: border), ), child: Column( children: [ Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: const Color(0xFFEF4444).withOpacity(0.1), shape: BoxShape.circle, ), child: const Icon(Icons.sentiment_dissatisfied_outlined, size: 56, color: Color(0xFFEF4444)), ), const SizedBox(height: 20), Text('Solicitud no aprobada', style: TextStyle( fontSize: 20, fontWeight: FontWeight.bold, color: textPrimary, )), const SizedBox(height: 12), Text( 'Tu solicitud no fue aprobada en esta ocasión. Por favor revisa tus documentos y vuelve a intentarlo.', textAlign: TextAlign.center, style: TextStyle(color: textSecondary, fontSize: 14, height: 1.5), ), ], ), ); default: return const SizedBox(); } }, ); } Future _pickPdf() async { final result = await FilePicker.platform.pickFiles( type: FileType.custom, allowedExtensions: ['pdf'], withData: true, ); return result?.files.first.bytes; } InputDecoration _inputDec(String label, IconData icon, bool isDark) { final borderColor = isDark ? const Color(0xFF334155) : const Color(0xFFD1D5DB); final labelColor = isDark ? const Color(0xFF94A3B8) : const Color(0xFF6B7280); return InputDecoration( labelText: label, labelStyle: TextStyle(color: labelColor, fontSize: 13), hintStyle: TextStyle(color: labelColor), prefixIcon: Icon(icon, color: const Color(0xFF42A4EF), size: 20), filled: true, fillColor: isDark ? const Color(0xFF0F172A) : const Color(0xFFF9FAFB), border: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: BorderSide(color: borderColor), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: BorderSide(color: borderColor), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: const BorderSide(color: Color(0xFF42A4EF), width: 2), ), errorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: const BorderSide(color: Colors.red), ), contentPadding: const EdgeInsets.symmetric(horizontal: 14, vertical: 14), ); } } // ── Helpers ────────────────────────────────────────────────────────────────── class _Section extends StatelessWidget { final String title; final IconData icon; final Color cardBg; final Color border; final Color textPrimary; final List children; const _Section({ required this.title, required this.icon, required this.cardBg, required this.border, required this.textPrimary, required this.children, }); @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: cardBg, borderRadius: BorderRadius.circular(14), border: Border.all(color: border), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon(icon, color: const Color(0xFF42A4EF), size: 18), const SizedBox(width: 8), Text(title, style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, color: textPrimary, )), ], ), const SizedBox(height: 14), ...children, ], ), ); } } class _UploadButton extends StatelessWidget { final String label; final bool uploaded; final bool isDark; final VoidCallback onTap; const _UploadButton({ required this.label, required this.uploaded, required this.isDark, required this.onTap, }); @override Widget build(BuildContext context) { final color = uploaded ? const Color(0xFF10B981) : const Color(0xFF42A4EF); final bg = color.withOpacity(0.08); return InkWell( onTap: onTap, borderRadius: BorderRadius.circular(10), child: Container( padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12), decoration: BoxDecoration( color: bg, borderRadius: BorderRadius.circular(10), border: Border.all( color: color.withOpacity(0.4), style: BorderStyle.solid, ), ), child: Row( children: [ Icon( uploaded ? Icons.check_circle_outline : Icons.upload_file_outlined, color: color, size: 20, ), const SizedBox(width: 10), Expanded( child: Text( uploaded ? '$label (subido ✓)' : label, style: TextStyle( fontSize: 13, color: color, fontWeight: FontWeight.w500, ), ), ), Icon(Icons.chevron_right, color: color.withOpacity(0.6), size: 18), ], ), ), ); } }