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,939 @@
import 'package:flutter/material.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:intl/intl.dart';
import 'package:mobx/mobx.dart';
import '../../../../core/theme/app_colors.dart';
import '../store/patient_store.dart';
class PatientClinicalTabs extends StatefulWidget {
final PatientStore store;
const PatientClinicalTabs({super.key, required this.store});
@override
State<PatientClinicalTabs> createState() => _PatientClinicalTabsState();
}
class _PatientClinicalTabsState extends State<PatientClinicalTabs>
with SingleTickerProviderStateMixin {
late final TabController _tabController;
late final TextEditingController _motConsController;
late final FocusNode _motConsFocusNode;
late final TextEditingController _hisEnfActController;
late final FocusNode _hisEnfActFocusNode;
ReactionDisposer? _disposerMotCons;
ReactionDisposer? _disposerHisEnfAct;
@override
void initState() {
super.initState();
_tabController = TabController(length: 7, vsync: this);
_motConsController = TextEditingController(text: widget.store.motCons);
_motConsFocusNode = FocusNode();
_hisEnfActController = TextEditingController(text: widget.store.hisEnfAct);
_hisEnfActFocusNode = FocusNode();
_disposerMotCons = reaction((_) => widget.store.motCons, (String val) {
if (_motConsController.text != val) {
_motConsController.text = val;
}
});
_disposerHisEnfAct = reaction((_) => widget.store.hisEnfAct, (String val) {
if (_hisEnfActController.text != val) {
_hisEnfActController.text = val;
}
});
}
@override
void dispose() {
_disposerMotCons?.call();
_disposerHisEnfAct?.call();
_tabController.dispose();
_motConsController.dispose();
_motConsFocusNode.dispose();
_hisEnfActController.dispose();
_hisEnfActFocusNode.dispose();
super.dispose();
}
void _insertDateAtCursor({
required TextEditingController controller,
required FocusNode focusNode,
required ValueChanged<String> onUpdateStore,
}) {
final now = DateTime.now();
final dateStr = DateFormat('dd/MM/yyyy').format(now);
final insertionText = '$dateStr: ';
final currentText = controller.text;
final selection = controller.selection;
int start = selection.start;
int end = selection.end;
// Si el cursor no es válido o no está enfocado, insertar al final
if (start < 0 || end < 0) {
start = currentText.length;
end = currentText.length;
}
if (start > end) {
final temp = start;
start = end;
end = temp;
}
final before = currentText.substring(0, start);
final after = currentText.substring(end);
final newText = '$before$insertionText$after';
final newOffset = start + insertionText.length;
controller.value = TextEditingValue(
text: newText,
selection: TextSelection.collapsed(offset: newOffset),
);
onUpdateStore(newText);
focusNode.requestFocus();
}
InputDecoration _cleanTextAreaDecoration(String hintText) {
return InputDecoration(
hintText: hintText,
hintStyle: const TextStyle(color: Color(0xFF94A3B8), fontSize: 13),
filled: true,
fillColor: const Color(0xFFF8FAFC),
contentPadding: const EdgeInsets.all(16),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: Color(0xFFE2E8F0)),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: Color(0xFFE2E8F0)),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: AppColors.primary, width: 1.5),
),
);
}
InputDecoration _cleanDropdownDecoration(String label) {
return InputDecoration(
labelText: label,
floatingLabelBehavior: FloatingLabelBehavior.always,
filled: true,
fillColor: Colors.white,
isDense: true,
contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: const BorderSide(color: Color(0xFFE2E8F0)),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: const BorderSide(color: Color(0xFFE2E8F0)),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: const BorderSide(color: AppColors.primary, width: 1.5),
),
labelStyle: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w700,
color: AppColors.textPrimary,
),
floatingLabelStyle: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w700,
color: AppColors.primary,
),
hintStyle: const TextStyle(
fontSize: 13.5,
color: Color(0xFF94A3B8),
),
);
}
Widget _buildClinicalTextArea({
required String name,
String? initialValue,
TextEditingController? controller,
FocusNode? focusNode,
required ValueChanged<String> onChanged,
required String hintText,
VoidCallback? onInsertDate,
String? dateLabel,
}) {
return Padding(
padding: const EdgeInsets.all(16),
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
child: FormBuilderTextField(
name: name,
controller: controller,
initialValue: controller == null ? initialValue : null,
focusNode: focusNode,
maxLines: null,
expands: true,
textAlignVertical: TextAlignVertical.top,
style: const TextStyle(fontSize: 13.5, height: 1.5, color: AppColors.textPrimary),
decoration: _cleanTextAreaDecoration(hintText),
onChanged: (v) => onChanged(v ?? ''),
),
),
if (onInsertDate != null) ...[
const SizedBox(width: 16),
SizedBox(
width: 200,
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
if (dateLabel != null && dateLabel.isNotEmpty) ...[
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: AppColors.primaryContainer.withValues(alpha: 0.6),
borderRadius: BorderRadius.circular(10),
border: Border.all(color: AppColors.primary.withValues(alpha: 0.2)),
),
child: Row(
children: [
const Icon(Icons.history_rounded, size: 16, color: AppColors.primary),
const SizedBox(width: 8),
Expanded(
child: Text(
dateLabel,
style: const TextStyle(
fontSize: 11.5,
fontWeight: FontWeight.w600,
color: AppColors.primary,
),
),
),
],
),
),
const SizedBox(height: 12),
],
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: const Color(0xFFF8FAFC),
borderRadius: BorderRadius.circular(10),
border: Border.all(color: const Color(0xFFE2E8F0)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const Text(
'Entrada de Consulta',
style: TextStyle(fontSize: 11, fontWeight: FontWeight.w700, color: AppColors.textSecondary),
),
const SizedBox(height: 4),
const Text(
'Inserte el sello de tiempo para documentar una nueva nota clínica.',
style: TextStyle(fontSize: 11, color: AppColors.textMuted, height: 1.3),
),
const SizedBox(height: 10),
ElevatedButton.icon(
onPressed: onInsertDate,
icon: const Icon(Icons.add_alarm_rounded, size: 16),
label: const Text('Fecha actual', style: TextStyle(fontSize: 12, fontWeight: FontWeight.w600)),
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.primary,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 12),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
elevation: 0,
),
),
],
),
),
],
),
),
),
],
],
),
);
}
Widget _buildDropdownItem({
required String name,
required String label,
required String initialValue,
required List<String> items,
required ValueChanged<String> onChanged,
}) {
final list = items.contains(initialValue)
? items
: (initialValue.isNotEmpty ? [initialValue, ...items] : items);
return FormBuilderDropdown<String>(
name: name,
initialValue: initialValue.isNotEmpty ? initialValue : null,
decoration: _cleanDropdownDecoration(label),
hint: const Text(
'Seleccionar...',
style: TextStyle(fontSize: 13.5, color: Color(0xFF94A3B8)),
),
style: const TextStyle(fontSize: 13.5, color: AppColors.textPrimary, fontWeight: FontWeight.w500),
onChanged: (v) {
if (v != null) onChanged(v);
},
items: list.map((item) {
return DropdownMenuItem<String>(
value: item,
child: Text(
item,
style: const TextStyle(fontSize: 13.5),
overflow: TextOverflow.ellipsis,
),
);
}).toList(),
);
}
Widget _buildAntecedentesTab(PatientStore s) {
return SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Patológicos
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: const Color(0xFFE2E8F0)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Row(
children: [
Icon(Icons.healing_rounded, size: 18, color: AppColors.primary),
SizedBox(width: 8),
Text(
'Antecedentes Personales Patológicos',
style: TextStyle(fontSize: 13, fontWeight: FontWeight.w700, color: AppColors.textPrimary),
),
],
),
const SizedBox(height: 14),
Row(
children: [
Expanded(
child: _buildDropdownItem(
name: 'dat_ni',
label: 'Infancia',
initialValue: s.datNi,
items: const ['sarampión', 'varicela', 'Tos ferina', 'Hepatitis', 'Ninguna'],
onChanged: (v) => s.datNi = v,
),
),
const SizedBox(width: 12),
Expanded(
child: _buildDropdownItem(
name: 'dat_ado',
label: 'Adolescencia',
initialValue: s.datAdo,
items: const ['Asma', 'Trastornos alimenticios', 'TBC', 'VIH', 'Ninguna'],
onChanged: (v) => s.datAdo = v,
),
),
const SizedBox(width: 12),
Expanded(
child: _buildDropdownItem(
name: 'dat_adu',
label: 'Adultez',
initialValue: s.datAdu,
items: const [
'TBC',
'VIH',
'Hepatopatías',
'Cardiopatías',
'Diabetes',
'HTA',
'Dislipidemias',
'Ninguna',
],
onChanged: (v) => s.datAdu = v,
),
),
const SizedBox(width: 12),
Expanded(
child: _buildDropdownItem(
name: 'dat_alerg',
label: 'Alergias',
initialValue: s.datAlerg,
items: const ['Ninguna', 'Penicilina', 'Sulfas', 'AINEs'],
onChanged: (v) => s.datAlerg = v,
),
),
],
),
const SizedBox(height: 12),
Row(
children: [
Expanded(
child: _buildDropdownItem(
name: 'dat_tran',
label: 'Transfusión sanguínea',
initialValue: s.datTran,
items: const ['1 ves', '2 veces', '3 veces', 'ninguna'],
onChanged: (v) => s.datTran = v,
),
),
const SizedBox(width: 12),
Expanded(
child: _buildDropdownItem(
name: 'dat_cir',
label: 'Intervenciones quirúrgicas',
initialValue: s.datCir,
items: const ['ninguna', 'Apendicectomía', 'Cesárea', 'Colecistectomía'],
onChanged: (v) => s.datCir = v,
),
),
const SizedBox(width: 12),
Expanded(
child: _buildDropdownItem(
name: 'dat_ac',
label: 'Accidentes',
initialValue: s.datAc,
items: const ['Si', 'ninguno'],
onChanged: (v) => s.datAc = v,
),
),
const SizedBox(width: 12),
Expanded(
child: _buildDropdownItem(
name: 'dat_otrosp',
label: 'Otros patológicos',
initialValue: s.datOtrosp,
items: const ['ninguno'],
onChanged: (v) => s.datOtrosp = v,
),
),
],
),
],
),
),
const SizedBox(height: 16),
// No patológicos
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: const Color(0xFFE2E8F0)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Row(
children: [
Icon(Icons.family_restroom_rounded, size: 18, color: AppColors.primary),
SizedBox(width: 8),
Text(
'Antecedentes Personales No Patológicos (Familiares)',
style: TextStyle(fontSize: 13, fontWeight: FontWeight.w700, color: AppColors.textPrimary),
),
],
),
const SizedBox(height: 14),
Row(
children: [
Expanded(
child: _buildDropdownItem(
name: 'dat_mad',
label: 'Madre',
initialValue: s.datMad,
items: const ['Viva y sana', 'Fallecida', 'Enferma'],
onChanged: (v) => s.datMad = v,
),
),
const SizedBox(width: 12),
Expanded(
child: _buildDropdownItem(
name: 'dat_esp',
label: 'Pareja',
initialValue: s.datEsp,
items: const ['Viva y sana', 'Fallecida', 'Enferma'],
onChanged: (v) => s.datEsp = v,
),
),
const SizedBox(width: 12),
Expanded(
child: _buildDropdownItem(
name: 'dat_padre',
label: 'Padre',
initialValue: s.datPadre,
items: const ['Vivo y sano', 'Fallecido', 'Enfermo'],
onChanged: (v) => s.datPadre = v,
),
),
],
),
const SizedBox(height: 12),
Row(
children: [
Expanded(
child: _buildDropdownItem(
name: 'dat_hijos',
label: 'Hijos',
initialValue: s.datHijos,
items: const ['Vivos y sanos', 'Enfermos', 'Ninguno'],
onChanged: (v) => s.datHijos = v,
),
),
const SizedBox(width: 12),
Expanded(
child: _buildDropdownItem(
name: 'dat_her',
label: 'Hermanos',
initialValue: s.datHer,
items: const ['Vivos y sanos', 'Fallecidos', 'Enfermos', 'Ninguno'],
onChanged: (v) => s.datHer = v,
),
),
const SizedBox(width: 12),
Expanded(
child: _buildDropdownItem(
name: 'dat_otrosnp',
label: 'Otros no patológicos',
initialValue: s.datOtrosnp,
items: const ['Ningunos'],
onChanged: (v) => s.datOtrosnp = v,
),
),
],
),
],
),
),
],
),
);
}
Widget _buildDiagnosticosTab(PatientStore s) {
return Padding(
padding: const EdgeInsets.all(16),
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Row(
children: [
Icon(Icons.biotech_rounded, size: 16, color: AppColors.primary),
SizedBox(width: 6),
Text(
'Métodos diagnósticos, radiológicos e invasivos',
style: TextStyle(fontSize: 12, fontWeight: FontWeight.w700, color: AppColors.textPrimary),
),
],
),
const SizedBox(height: 8),
Expanded(
child: FormBuilderTextField(
name: 'met_diagn_rad',
initialValue: s.metDiagnRad,
maxLines: null,
expands: true,
textAlignVertical: TextAlignVertical.top,
style: const TextStyle(fontSize: 13),
decoration: _cleanTextAreaDecoration('Describa radiografías, ecografías, resonancias o procedimientos invasivos...'),
onChanged: (v) => s.metDiagnRad = v ?? '',
),
),
const SizedBox(height: 12),
const Row(
children: [
Icon(Icons.star_outline_rounded, size: 16, color: AppColors.primary),
SizedBox(width: 6),
Text(
'Diagnósticos a destacar',
style: TextStyle(fontSize: 12, fontWeight: FontWeight.w700, color: AppColors.textPrimary),
),
],
),
const SizedBox(height: 8),
Expanded(
child: FormBuilderTextField(
name: 'diagn_desc',
initialValue: s.diagnDesc,
maxLines: null,
expands: true,
textAlignVertical: TextAlignVertical.top,
style: const TextStyle(fontSize: 13),
decoration: _cleanTextAreaDecoration('Patologías principales, hallazgos o diagnósticos relevantes...'),
onChanged: (v) => s.diagnDesc = v ?? '',
),
),
],
),
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Row(
children: [
Icon(Icons.verified_outlined, size: 16, color: AppColors.primary),
SizedBox(width: 6),
Text(
'Diagnóstico definitivo',
style: TextStyle(fontSize: 12, fontWeight: FontWeight.w700, color: AppColors.textPrimary),
),
],
),
const SizedBox(height: 8),
Expanded(
child: FormBuilderTextField(
name: 'diagn_def',
initialValue: s.diagnDef,
maxLines: null,
expands: true,
textAlignVertical: TextAlignVertical.top,
style: const TextStyle(fontSize: 13),
decoration: _cleanTextAreaDecoration('Conclusión diagnóstica definitiva y codificación clínica...'),
onChanged: (v) => s.diagnDef = v ?? '',
),
),
],
),
),
],
),
);
}
Widget _buildProximaCitaTab(PatientStore s) {
return Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
ElevatedButton.icon(
onPressed: () async {
final now = DateTime.now();
final picked = await showDatePicker(
context: context,
initialDate: now.add(const Duration(days: 7)),
firstDate: now.subtract(const Duration(days: 30)),
lastDate: now.add(const Duration(days: 365 * 3)),
builder: (context, child) {
return Theme(
data: Theme.of(context).copyWith(
colorScheme: const ColorScheme.light(
primary: AppColors.primary,
onPrimary: Colors.white,
surface: Colors.white,
),
),
child: child!,
);
},
);
if (picked != null) {
s.fecProxcita = DateFormat('dd/MM/yyyy').format(picked);
}
},
icon: const Icon(Icons.calendar_month_rounded, size: 18),
label: const Text('Seleccionar Fecha de Cita', style: TextStyle(fontWeight: FontWeight.w600)),
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.primary,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 14),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
elevation: 0,
),
),
const SizedBox(width: 14),
if (s.fecProxcita.isNotEmpty)
TextButton.icon(
onPressed: () => s.fecProxcita = '',
icon: const Icon(Icons.clear_rounded, size: 16, color: AppColors.error),
label: const Text('Quitar próxima cita', style: TextStyle(color: AppColors.error, fontWeight: FontWeight.w600)),
),
],
),
const SizedBox(height: 20),
Container(
padding: const EdgeInsets.all(18),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(14),
border: Border.all(color: const Color(0xFFE2E8F0)),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.03),
blurRadius: 10,
offset: const Offset(0, 4),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: AppColors.primaryContainer,
borderRadius: BorderRadius.circular(10),
),
child: const Icon(Icons.event_available_rounded, size: 22, color: AppColors.primary),
),
const SizedBox(width: 14),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Control de Citas del Paciente',
style: TextStyle(fontSize: 12, color: AppColors.textMuted, fontWeight: FontWeight.w500),
),
const SizedBox(height: 2),
Text(
s.fecProxcita.isNotEmpty
? 'Próxima cita programada: ${s.fecProxcita}'
: 'Sin próxima cita programada',
style: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w700,
color: AppColors.textPrimary,
),
),
],
),
),
],
),
if (s.diasParaProximaCita != null) ...[
const SizedBox(height: 14),
Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(
color: s.diasParaProximaCita! < 0
? AppColors.error.withValues(alpha: 0.1)
: (s.diasParaProximaCita! == 0
? AppColors.warning.withValues(alpha: 0.1)
: AppColors.success.withValues(alpha: 0.1)),
borderRadius: BorderRadius.circular(8),
),
child: Text(
'Tiempo restante para la cita: ${s.diasParaProximaCita} días',
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w600,
color: s.diasParaProximaCita! < 0
? AppColors.error
: (s.diasParaProximaCita! == 0 ? AppColors.warning : AppColors.success),
),
),
),
],
],
),
),
],
),
);
}
@override
Widget build(BuildContext context) {
final s = widget.store;
return Column(
children: [
// Barra de Pestañas Moderna con Estilo Limpio
Container(
decoration: const BoxDecoration(
color: Color(0xFFF8FAFC),
border: Border(
bottom: BorderSide(color: Color(0xFFE2E8F0)),
),
),
child: TabBar(
controller: _tabController,
isScrollable: true,
labelColor: AppColors.primary,
unselectedLabelColor: AppColors.textSecondary,
labelStyle: const TextStyle(fontWeight: FontWeight.w700, fontSize: 12.5),
unselectedLabelStyle: const TextStyle(fontWeight: FontWeight.w500, fontSize: 12.5),
indicatorColor: AppColors.primary,
indicatorWeight: 2.5,
tabAlignment: TabAlignment.start,
tabs: const [
Tab(
height: 38,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.notes_rounded, size: 15),
SizedBox(width: 6),
Text('Consultas'),
],
),
),
Tab(
height: 38,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.history_edu_rounded, size: 15),
SizedBox(width: 6),
Text('Hist. enfermedad actual'),
],
),
),
Tab(
height: 38,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.medical_information_outlined, size: 15),
SizedBox(width: 6),
Text('Antec. patológicos'),
],
),
),
Tab(
height: 38,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.biotech_outlined, size: 15),
SizedBox(width: 6),
Text('Diagnósticos'),
],
),
),
Tab(
height: 38,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.science_outlined, size: 15),
SizedBox(width: 6),
Text('Laboratorios'),
],
),
),
Tab(
height: 38,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.medication_outlined, size: 15),
SizedBox(width: 6),
Text('Tratamiento'),
],
),
),
Tab(
height: 38,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.event_outlined, size: 15),
SizedBox(width: 6),
Text('Próxima cita'),
],
),
),
],
),
),
Expanded(
child: Observer(
builder: (_) => TabBarView(
controller: _tabController,
children: [
// 1. Consultas
_buildClinicalTextArea(
name: 'mot_cons',
controller: _motConsController,
focusNode: _motConsFocusNode,
hintText: 'Registro de consultas clínicas, motivo y evolución...',
dateLabel: s.fecUltcon.isNotEmpty ? 'Última consulta: ${s.fecUltcon}' : null,
onInsertDate: () {
_insertDateAtCursor(
controller: _motConsController,
focusNode: _motConsFocusNode,
onUpdateStore: (v) => s.motCons = v,
);
},
onChanged: (v) => s.motCons = v,
),
// 2. Hist. enfermedad actual
_buildClinicalTextArea(
name: 'his_enf_act',
controller: _hisEnfActController,
focusNode: _hisEnfActFocusNode,
hintText: 'Historia y cronología de la enfermedad actual...',
onInsertDate: () {
_insertDateAtCursor(
controller: _hisEnfActController,
focusNode: _hisEnfActFocusNode,
onUpdateStore: (v) => s.hisEnfAct = v,
);
},
onChanged: (v) => s.hisEnfAct = v,
),
// 3. Antec. patológicos
_buildAntecedentesTab(s),
// 4. Diagnósticos
_buildDiagnosticosTab(s),
// 5. Laboratorios
_buildClinicalTextArea(
name: 'dat_lab',
initialValue: s.datLab,
hintText: 'Resultados de laboratorio, hemogramas, analíticas...',
onChanged: (v) => s.datLab = v,
),
// 6. Tratamiento
_buildClinicalTextArea(
name: 'tratamiento',
initialValue: s.tratamiento,
hintText: 'Plan terapéutico, recetas, dosis y recomendaciones...',
onChanged: (v) => s.tratamiento = v,
),
// 7. Próxima cita
_buildProximaCitaTab(s),
],
),
),
),
],
);
}
}