import 'package:flutter/material.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:mask_text_input_formatter/mask_text_input_formatter.dart'; import 'package:toggle_switch/toggle_switch.dart'; import '../../../../core/theme/app_colors.dart'; import '../store/patient_store.dart'; class PatientDemographicsForm extends StatelessWidget { /// Altura estricta y compartida para todos los elementos de la ficha demográfica: /// los 8 campos de texto, las 3 secciones de toggle (sexo, seguro, estado civil) /// y el botón Guardar Paciente siempre tendrán exactamente la misma altura. static const double unifiedRowHeight = 46.0; final PatientStore store; final GlobalKey formKey; final VoidCallback onSave; const PatientDemographicsForm({ super.key, required this.store, required this.formKey, required this.onSave, }); InputDecoration _cleanDecoration({ required String label, IconData? icon, bool isRequired = false, String? hintText, bool readOnly = false, }) { return InputDecoration( labelText: isRequired ? '$label *' : label, hintText: hintText, isDense: true, floatingLabelBehavior: FloatingLabelBehavior.auto, filled: true, fillColor: readOnly ? const Color(0xFFF1F5F9) : Colors.white, prefixIcon: icon != null ? Icon(icon, size: 16, color: isRequired ? AppColors.primary : AppColors.textMuted) : null, prefixIconConstraints: const BoxConstraints(minWidth: 34, minHeight: 34), contentPadding: const EdgeInsets.symmetric(horizontal: 11, vertical: 14), border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: const BorderSide(color: Color(0xFFE2E8F0)), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: const BorderSide(color: Color(0xFFE2E8F0)), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: const BorderSide(color: AppColors.primary, width: 1.5), ), errorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: const BorderSide(color: AppColors.error), ), focusedErrorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: const BorderSide(color: AppColors.error, width: 1.5), ), labelStyle: TextStyle( fontSize: 12, fontWeight: FontWeight.w500, color: isRequired ? AppColors.primary : AppColors.textSecondary, ), hintStyle: const TextStyle(fontSize: 11.5, color: Color(0xFF94A3B8)), ); } Widget _buildCompactSelector({ required BuildContext context, required String title, required IconData titleIcon, required Widget child, bool isRequired = false, }) { return Container( height: unifiedRowHeight, padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 5), decoration: BoxDecoration( color: const Color(0xFFF8FAFC), borderRadius: BorderRadius.circular(8), border: Border.all(color: const Color(0xFFE2E8F0)), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( mainAxisSize: MainAxisSize.min, children: [ Icon(titleIcon, size: 14, color: isRequired ? AppColors.primary : AppColors.textMuted), const SizedBox(width: 4), Text( isRequired ? '$title *' : title, style: TextStyle( fontSize: 11.0, fontWeight: FontWeight.w700, color: isRequired ? AppColors.primary : AppColors.textPrimary, ), ), ], ), const SizedBox(width: 4), Flexible( child: Align( alignment: Alignment.centerRight, child: child, ), ), ], ), ); } @override Widget build(BuildContext context) { final s = store; final cedulaFormatter = MaskTextInputFormatter( mask: '###-#######-#', filter: {'#': RegExp(r'[0-9]')}, ); final telefonoFormatter = MaskTextInputFormatter( mask: '###-###-####', filter: {'#': RegExp(r'[0-9]')}, ); return Container( padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12), decoration: const BoxDecoration( color: Colors.white, border: Border( bottom: BorderSide(color: Color(0xFFE2E8F0), width: 1.2), ), ), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Sub-bloque 1 (Columna 1): Fecha de reg, Nombre, Dirección, Ocupación Expanded( flex: 7, child: Column( children: [ SizedBox( height: unifiedRowHeight, child: FormBuilderTextField( name: 'fec_reg', initialValue: s.fecReg, readOnly: true, style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w500), decoration: _cleanDecoration( label: 'Fecha de Registro', icon: Icons.calendar_today_rounded, readOnly: true, ), ), ), const SizedBox(height: 8), SizedBox( height: unifiedRowHeight, child: FormBuilderTextField( name: 'nombre', initialValue: s.nombre, textCapitalization: TextCapitalization.words, style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w600), decoration: _cleanDecoration( label: 'Nombre', icon: Icons.person_outline_rounded, isRequired: true, hintText: 'Ej. Juan Alberto', ), validator: FormBuilderValidators.compose([ FormBuilderValidators.required(errorText: 'Requerido'), FormBuilderValidators.minLength(2, errorText: 'Mínimo 2 letras'), ]), onChanged: (v) => s.nombre = v ?? '', ), ), const SizedBox(height: 8), SizedBox( height: unifiedRowHeight, child: FormBuilderTextField( name: 'direccion', initialValue: s.direccion, style: const TextStyle(fontSize: 12), decoration: _cleanDecoration( label: 'Dirección', icon: Icons.location_on_outlined, hintText: 'Calle, número o sector', ), onChanged: (v) => s.direccion = v ?? '', ), ), const SizedBox(height: 8), SizedBox( height: unifiedRowHeight, child: FormBuilderTextField( name: 'ocupacion', initialValue: s.ocupacion, style: const TextStyle(fontSize: 12), decoration: _cleanDecoration( label: 'Ocupación', icon: Icons.work_outline_rounded, hintText: 'Profesión u oficio', ), onChanged: (v) => s.ocupacion = v ?? '', ), ), ], ), ), const SizedBox(width: 12), // Sub-bloque 2 (Columna 2): Cédula o Pasaporte, Apellido, Teléfono, Edad Expanded( flex: 7, child: Column( children: [ SizedBox( height: unifiedRowHeight, child: FormBuilderTextField( name: 'cedula', initialValue: s.cedula, inputFormatters: [cedulaFormatter], keyboardType: TextInputType.number, style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w500), decoration: _cleanDecoration( label: 'Cédula o Pasaporte', icon: Icons.badge_outlined, hintText: '001-0000000-0', ), onChanged: (v) => s.cedula = v ?? '', ), ), const SizedBox(height: 8), SizedBox( height: unifiedRowHeight, child: FormBuilderTextField( name: 'apellido', initialValue: s.apellido, textCapitalization: TextCapitalization.words, style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w600), decoration: _cleanDecoration( label: 'Apellido', icon: Icons.badge_rounded, isRequired: true, hintText: 'Ej. Pérez Gómez', ), validator: FormBuilderValidators.compose([ FormBuilderValidators.required(errorText: 'Requerido'), FormBuilderValidators.minLength(2, errorText: 'Mínimo 2 letras'), ]), onChanged: (v) => s.apellido = v ?? '', ), ), const SizedBox(height: 8), SizedBox( height: unifiedRowHeight, child: FormBuilderTextField( name: 'telefono', initialValue: s.telefono, inputFormatters: [telefonoFormatter], keyboardType: TextInputType.phone, style: const TextStyle(fontSize: 12), decoration: _cleanDecoration( label: 'Teléfono', icon: Icons.phone_outlined, hintText: '809-555-0101', ), onChanged: (v) => s.telefono = v ?? '', ), ), const SizedBox(height: 8), SizedBox( height: unifiedRowHeight, child: FormBuilderTextField( name: 'edad', initialValue: s.edad, keyboardType: TextInputType.number, style: const TextStyle(fontSize: 12), decoration: _cleanDecoration( label: 'Edad', icon: Icons.cake_outlined, hintText: 'Años', ), onChanged: (v) => s.edad = v ?? '', ), ), ], ), ), const SizedBox(width: 12), // Sub-bloque 3 (Columna 3): Sexo, Asegurado, Estado Civil, Botón Guardar Expanded( flex: 10, child: Column( children: [ _buildCompactSelector( context: context, title: 'Sexo', titleIcon: Icons.wc_rounded, isRequired: true, child: FormBuilderField( name: 'sexo', initialValue: s.sexo, validator: FormBuilderValidators.required(errorText: 'Requerido'), builder: (FormFieldState field) { final currentVal = field.value ?? s.sexo; final initialIndex = currentVal.toLowerCase().contains('fem') ? 1 : 0; return ToggleSwitch( key: ValueKey('sexo_$currentVal'), customWidths: const [108.0, 108.0], minHeight: 28.0, cornerRadius: 6.0, radiusStyle: true, fontSize: 10.5, iconSize: 13.0, centerText: true, initialLabelIndex: initialIndex, totalSwitches: 2, labels: const ['Masculino', 'Femenino'], icons: const [Icons.male_rounded, Icons.female_rounded], activeBgColor: const [AppColors.primary], activeFgColor: Colors.white, inactiveBgColor: const Color(0xFFE2E8F0), inactiveFgColor: AppColors.textSecondary, animate: true, curve: Curves.easeInOut, onToggle: (index) { final val = (index == 1) ? 'Femenino' : 'Masculino'; field.didChange(val); s.sexo = val; }, ); }, ), ), const SizedBox(height: 8), _buildCompactSelector( context: context, title: 'Seguro', titleIcon: Icons.health_and_safety_outlined, isRequired: true, child: FormBuilderField( name: 'seguro', initialValue: s.seguro, validator: FormBuilderValidators.required(errorText: 'Requerido'), builder: (FormFieldState field) { final currentVal = field.value ?? s.seguro; final cleanVal = currentVal.trim().toLowerCase(); final isNo = cleanVal == 'no' || cleanVal.contains('sin seguro') || cleanVal.contains('no asegurado'); final initialIndex = isNo ? 1 : 0; return ToggleSwitch( key: ValueKey('seguro_$currentVal'), customWidths: const [116.0, 116.0], minHeight: 28.0, cornerRadius: 6.0, radiusStyle: true, fontSize: 10.5, iconSize: 12.0, centerText: true, initialLabelIndex: initialIndex, totalSwitches: 2, labels: const ['Con Seguro', 'Sin Seguro'], icons: const [Icons.check_rounded, Icons.close_rounded], activeBgColor: const [AppColors.primary], activeFgColor: Colors.white, inactiveBgColor: const Color(0xFFE2E8F0), inactiveFgColor: AppColors.textSecondary, animate: true, curve: Curves.easeInOut, onToggle: (index) { final val = (index == 1) ? 'Sin Seguro' : 'Con Seguro'; field.didChange(val); s.seguro = val; }, ); }, ), ), const SizedBox(height: 8), _buildCompactSelector( context: context, title: 'Estado Civil', titleIcon: Icons.favorite_border_rounded, child: FormBuilderField( name: 'est_civil', initialValue: s.estCivil, builder: (FormFieldState field) { const options = ['Casado', 'Soltero', 'Otros']; final currentVal = field.value ?? s.estCivil; int initialIndex = 1; if (currentVal.toLowerCase().contains('casad')) { initialIndex = 0; } else if (currentVal.toLowerCase().contains('otro')) { initialIndex = 2; } return ToggleSwitch( key: ValueKey('est_civil_$currentVal'), customWidths: const [86.0, 90.0, 76.0], minHeight: 28.0, cornerRadius: 6.0, radiusStyle: true, fontSize: 10.5, iconSize: 11.5, centerText: true, initialLabelIndex: initialIndex, totalSwitches: 3, labels: options, icons: const [Icons.people_alt_outlined, Icons.person_outline_rounded, Icons.more_horiz_rounded], activeBgColor: const [AppColors.primary], activeFgColor: Colors.white, inactiveBgColor: const Color(0xFFE2E8F0), inactiveFgColor: AppColors.textSecondary, animate: true, curve: Curves.easeInOut, onToggle: (index) { final selected = options[index ?? 1]; field.didChange(selected); s.estCivil = selected; }, ); }, ), ), const SizedBox(height: 8), SizedBox( height: unifiedRowHeight, width: double.infinity, child: Observer( builder: (_) => ElevatedButton( onPressed: s.isSaving ? null : onSave, style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF00A65A), foregroundColor: Colors.white, elevation: 1, shadowColor: const Color(0xFF00A65A).withValues(alpha: 0.35), padding: const EdgeInsets.symmetric(vertical: 0, horizontal: 16), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), ), child: s.isSaving ? const SizedBox( width: 16, height: 16, child: CircularProgressIndicator( strokeWidth: 2, valueColor: AlwaysStoppedAnimation(Colors.white), ), ) : Row( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.check_circle_outline_rounded, size: 16), const SizedBox(width: 6), Text( s.estadoEspera == 'en_consulta' ? 'Finalizar Consulta' : (s.isEditing ? 'Guardar Cambios' : 'Guardar Paciente'), style: const TextStyle( fontSize: 12.5, fontWeight: FontWeight.w700, letterSpacing: 0.2, ), ), ], ), ), ), ), ], ), ), ], ), ); } }