first commit

This commit is contained in:
=
2026-09-21 16:33:51 -04:00
commit a899479dc9
204 changed files with 21132 additions and 0 deletions
@@ -0,0 +1,533 @@
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:intl/intl.dart';
import '../../../../core/theme/app_colors.dart';
import '../../../../core/widgets/medical_modal.dart';
import '../../../config/presentation/store/config_store.dart';
import '../../services/insurance_auth_pdf_service.dart';
class InsuranceAuthModalView extends StatefulWidget {
final String patientName;
final String? initialDiagnosis;
final ConfigStore? configStore;
const InsuranceAuthModalView({
super.key,
required this.patientName,
this.initialDiagnosis,
this.configStore,
});
@override
State<InsuranceAuthModalView> createState() => _InsuranceAuthModalViewState();
}
class _InsuranceAuthModalViewState extends State<InsuranceAuthModalView> {
static const List<String> arsList = [
'Humano Seguros, S.A.',
'Seguros Universal, S. A.',
'Seguros Reservas, S.A.',
'La Monumental de Seguros, C por A',
'Palic / Primera ARS',
'Seguros Pepín, S. A.',
'La Colonial, S.A.',
'Banesco Seguros, S.A.',
'Seguros La Internacional, S. A.',
'Seguros Patria, S. A.',
'Angloamericana de Seguros, S. A.',
'General de Seguros, S.A.',
'Seguros Ademi, S.A.',
'ARS Senasa',
'ARS Simag',
'ARS Renacer',
'ARS GMA',
'ARS Yunen',
];
late final TextEditingController _patientController;
late final TextEditingController _arsController;
late final TextEditingController _surgeryController;
late final TextEditingController _diagnosisController;
late final TextEditingController _anesthesiaController;
late final TextEditingController _daysController;
late final ConfigStore _configStore;
bool _isPrinting = false;
String? _statusMessage;
bool _hasError = false;
@override
void initState() {
super.initState();
_patientController = TextEditingController(text: widget.patientName);
_arsController = TextEditingController(text: arsList.first);
_surgeryController = TextEditingController();
_diagnosisController = TextEditingController(text: widget.initialDiagnosis ?? '');
_anesthesiaController = TextEditingController(text: 'General');
_daysController = TextEditingController(text: '1');
_configStore = widget.configStore ?? ConfigStore();
_configStore.loadConfig();
}
@override
void dispose() {
_patientController.dispose();
_arsController.dispose();
_surgeryController.dispose();
_diagnosisController.dispose();
_anesthesiaController.dispose();
_daysController.dispose();
super.dispose();
}
Future<void> _handlePrint() async {
final ars = _arsController.text.trim();
final patient = _patientController.text.trim();
final surgery = _surgeryController.text.trim();
final diagnosis = _diagnosisController.text.trim();
if (ars.isEmpty) {
setState(() {
_statusMessage = 'Debe indicar o seleccionar la ARS destinataria.';
_hasError = true;
});
return;
}
if (patient.isEmpty) {
setState(() {
_statusMessage = 'Debe indicar el nombre del paciente.';
_hasError = true;
});
return;
}
if (surgery.isEmpty) {
setState(() {
_statusMessage = 'Debe indicar la cirugía o procedimiento a autorizar.';
_hasError = true;
});
return;
}
if (diagnosis.isEmpty) {
setState(() {
_statusMessage = 'Debe indicar el diagnóstico correspondiente.';
_hasError = true;
});
return;
}
setState(() {
_isPrinting = true;
_statusMessage = 'Generando solicitud de autorización de seguro...';
_hasError = false;
});
try {
await InsuranceAuthPdfService.printInsuranceAuth(
doctorName: _configStore.nombreDoctor,
specialty: _configStore.especialidad,
subspecialty: _configStore.descripEspecialidad,
generalContact: _configStore.descripGeneral,
arsName: ars,
patientName: patient,
surgeryText: surgery,
diagnosisText: diagnosis,
anesthesia: _anesthesiaController.text.trim(),
daysOfStay: _daysController.text.trim(),
);
if (mounted) {
setState(() {
_isPrinting = false;
_statusMessage = 'Autorización generada con éxito.';
_hasError = false;
});
}
} catch (e) {
if (mounted) {
setState(() {
_isPrinting = false;
_statusMessage = 'Error al generar la autorización: $e';
_hasError = true;
});
}
}
}
@override
Widget build(BuildContext context) {
final formattedDate = DateFormat('dd/MM/yyyy').format(DateTime.now());
return Observer(
builder: (_) {
return MedicalModalDialog(
maxWidth: 820,
maxHeight: 650,
title: 'Autorización De Seguro (ARS)',
titleIcon: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: const Color(0xFF00A65A).withValues(alpha: 0.12),
borderRadius: BorderRadius.circular(10),
),
child: const Icon(
Icons.verified_user_rounded,
color: Color(0xFF00A65A),
size: 20,
),
),
subtitle: const Text(
'Formulario oficial de solicitud de cobertura quirúrgica para aseguradoras',
style: TextStyle(fontSize: 12, color: AppColors.textMuted),
),
bottomBar: Container(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
decoration: const BoxDecoration(
color: Color(0xFFF8FAFC),
border: Border(top: BorderSide(color: Color(0xFFE2E8F0))),
),
child: Row(
children: [
Container(
padding: const EdgeInsets.all(4),
decoration: BoxDecoration(
color: _hasError
? AppColors.error.withValues(alpha: 0.12)
: const Color(0xFF00A65A).withValues(alpha: 0.12),
shape: BoxShape.circle,
),
child: Icon(
_hasError
? Icons.error_outline_rounded
: Icons.check_circle_outline_rounded,
size: 15,
color: _hasError ? AppColors.error : const Color(0xFF00A65A),
),
),
const SizedBox(width: 8),
Expanded(
child: Text(
_statusMessage ??
(_isPrinting
? 'Preparando documento para impresión...'
: 'Listo para emitir la autorización de seguro'),
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: _hasError ? AppColors.error : AppColors.textSecondary,
),
),
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(6),
border: Border.all(color: const Color(0xFFE2E8F0)),
),
child: const Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.keyboard_outlined, size: 12, color: AppColors.textMuted),
SizedBox(width: 4),
Text(
'ESC para salir',
style: TextStyle(
fontSize: 11,
color: AppColors.textMuted,
fontWeight: FontWeight.w500,
),
),
],
),
),
const SizedBox(width: 12),
ElevatedButton.icon(
onPressed: _isPrinting ? null : _handlePrint,
icon: _isPrinting
? const SizedBox(
width: 16,
height: 16,
child: CircularProgressIndicator(
strokeWidth: 2,
color: Colors.white,
),
)
: const Icon(Icons.print_rounded, size: 16),
label: Text(
_isPrinting ? 'Generando...' : 'Crear / Imprimir Autorización',
style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 12.5),
),
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF00A65A),
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
elevation: 0,
),
),
],
),
),
child: SingleChildScrollView(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Membrete Informativo del Médico
Container(
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
color: const Color(0xFFF8FAFC),
borderRadius: BorderRadius.circular(12),
border: Border.all(color: const Color(0xFFE2E8F0)),
),
child: Column(
children: [
Text(
_configStore.nombreDoctor.isNotEmpty
? _configStore.nombreDoctor
: 'Dr. Luis A. Castro Sosa',
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w700,
color: AppColors.textPrimary,
letterSpacing: -0.3,
),
),
if (_configStore.especialidad.isNotEmpty) ...[
const SizedBox(height: 2),
Text(
_configStore.especialidad.toUpperCase(),
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 11.5,
fontWeight: FontWeight.w600,
color: AppColors.primary,
letterSpacing: 0.5,
),
),
],
if (_configStore.descripEspecialidad.isNotEmpty) ...[
const SizedBox(height: 2),
Text(
_configStore.descripEspecialidad.toUpperCase(),
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 10,
color: AppColors.textMuted,
),
),
],
const SizedBox(height: 4),
Text(
'Fecha: $formattedDate',
style: const TextStyle(
fontSize: 11,
fontWeight: FontWeight.w500,
color: AppColors.textSecondary,
),
),
],
),
),
const SizedBox(height: 16),
// Fila 1: Señores ARS y Paciente (Favor autorizar al Sr. a)
Row(
children: [
Expanded(
flex: 5,
child: Autocomplete<String>(
initialValue: TextEditingValue(text: _arsController.text),
optionsBuilder: (textEditingValue) {
if (textEditingValue.text.isEmpty) {
return arsList;
}
return arsList.where((ars) => ars
.toLowerCase()
.contains(textEditingValue.text.toLowerCase()));
},
onSelected: (selection) => _arsController.text = selection,
fieldViewBuilder: (context, controller, focusNode, onEditingComplete) {
controller.addListener(() {
_arsController.text = controller.text;
});
return TextField(
controller: controller,
focusNode: focusNode,
onEditingComplete: onEditingComplete,
decoration: InputDecoration(
labelText: 'Señores ARS *',
hintText: 'Seleccione o escriba la ARS',
isDense: true,
floatingLabelBehavior: FloatingLabelBehavior.always,
filled: true,
fillColor: Colors.white,
prefixIcon: const Icon(Icons.security_rounded, size: 18, color: AppColors.primary),
contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
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),
),
),
style: const TextStyle(fontSize: 12.5, fontWeight: FontWeight.w600),
);
},
),
),
const SizedBox(width: 12),
Expanded(
flex: 5,
child: TextField(
controller: _patientController,
decoration: InputDecoration(
labelText: 'Favor autorizar al Sr. (a) *',
hintText: 'Nombre del paciente',
isDense: true,
floatingLabelBehavior: FloatingLabelBehavior.always,
filled: true,
fillColor: Colors.white,
prefixIcon: const Icon(Icons.person_outline_rounded, size: 18, color: AppColors.primary),
contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
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),
),
),
style: const TextStyle(fontSize: 12.5, fontWeight: FontWeight.w600),
),
),
],
),
const SizedBox(height: 14),
// Campo: La cirugía de:
TextField(
controller: _surgeryController,
maxLines: 3,
style: const TextStyle(fontSize: 13, height: 1.4),
decoration: InputDecoration(
labelText: 'La cirugía de: *',
hintText: 'Especifique el procedimiento quirúrgico o intervención (ej. Ureterolitotomía endoscópica láser, Biopsia de próstata...)',
floatingLabelBehavior: FloatingLabelBehavior.always,
filled: true,
fillColor: Colors.white,
contentPadding: const EdgeInsets.all(12),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
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),
),
),
),
const SizedBox(height: 14),
// Campo: Diagnóstico:
TextField(
controller: _diagnosisController,
maxLines: 3,
style: const TextStyle(fontSize: 13, height: 1.4),
decoration: InputDecoration(
labelText: 'Diagnóstico: *',
hintText: 'Diagnóstico principal, patología o hallazgo clínico que justifica la cirugía...',
floatingLabelBehavior: FloatingLabelBehavior.always,
filled: true,
fillColor: Colors.white,
contentPadding: const EdgeInsets.all(12),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
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),
),
),
),
const SizedBox(height: 14),
// Fila 3: Bajo Anestesia y Días de ingreso
Row(
children: [
Expanded(
flex: 6,
child: TextField(
controller: _anesthesiaController,
decoration: InputDecoration(
labelText: 'Bajo Anestesia',
hintText: 'General, Raquídea, Sedación, Local...',
isDense: true,
floatingLabelBehavior: FloatingLabelBehavior.always,
filled: true,
fillColor: Colors.white,
prefixIcon: const Icon(Icons.vaccines_outlined, size: 18, color: AppColors.primary),
contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
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),
),
),
style: const TextStyle(fontSize: 12.5),
),
),
const SizedBox(width: 12),
Expanded(
flex: 4,
child: TextField(
controller: _daysController,
decoration: InputDecoration(
labelText: 'Días de ingreso',
hintText: 'Ej. 1, 2, Ambulatorio',
isDense: true,
floatingLabelBehavior: FloatingLabelBehavior.always,
filled: true,
fillColor: Colors.white,
prefixIcon: const Icon(Icons.hotel_outlined, size: 18, color: AppColors.primary),
contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
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),
),
),
style: const TextStyle(fontSize: 12.5),
),
),
],
),
],
),
),
);
},
);
}
}