Files
lis/mylis/lib/screens/expertise/covid_expertise_wizard.dart
T
2026-08-04 05:55:29 +07:00

386 lines
11 KiB
Dart

part of '../../app/app.dart';
class CovidExpertiseWizard extends StatefulWidget {
const CovidExpertiseWizard({
super.key,
required this.item,
required this.user,
required this.staffOptions,
required this.textControllers,
required this.selectValues,
required this.staffValues,
required this.criticalValue,
required this.isSupervisor,
required this.saving,
required this.onCriticalChanged,
required this.onChanged,
required this.onSave,
required this.initialStep,
required this.onStepChanged,
});
final Map<String, dynamic> item;
final Map<String, dynamic> user;
final Map<String, dynamic> staffOptions;
final Map<String, TextEditingController> textControllers;
final Map<String, String> selectValues;
final Map<String, String> staffValues;
final bool criticalValue;
final bool isSupervisor;
final bool saving;
final ValueChanged<bool> onCriticalChanged;
final VoidCallback onChanged;
final ValueChanged<String> onSave;
final int initialStep;
final ValueChanged<int> onStepChanged;
@override
State<CovidExpertiseWizard> createState() => _CovidExpertiseWizardState();
}
class _CovidExpertiseWizardState extends State<CovidExpertiseWizard> {
late int _step;
@override
void initState() {
super.initState();
_step = widget.initialStep.clamp(0, 4);
}
@override
void didUpdateWidget(covariant CovidExpertiseWizard oldWidget) {
super.didUpdateWidget(oldWidget);
final nextStep = widget.initialStep.clamp(0, 4);
if (nextStep != _step) {
_step = nextStep;
}
}
void _setStep(int step) {
final nextStep = step.clamp(0, 4);
setState(() => _step = nextStep);
widget.onStepChanged(nextStep);
}
@override
Widget build(BuildContext context) {
return ExpertiseWizardShell(
currentStep: _step,
onStepChanged: _setStep,
steps: [
Step(
title: const Text('Pasien & Petugas'),
isActive: _step == 0,
content: PatientStaffPanel(
item: widget.item,
user: widget.user,
staffOptions: widget.staffOptions,
staffValues: widget.staffValues,
onChanged: widget.onChanged,
),
),
Step(
title: const Text('Parameter'),
isActive: _step == 1,
content: _parameters(),
),
Step(
title: const Text('Tes Kepekaan Antibiotik'),
isActive: _step == 2,
content: _antibioticSensitivity(),
),
Step(
title: const Text('Data Alat'),
isActive: _step == 3,
content: _toolData(),
),
Step(
title: const Text('Expertise'),
isActive: _step == 4,
content: _finalExpertise(),
),
],
);
}
Widget _parameters() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
for (var i = 0; i < _covidVirusFields.length; i += 1)
CollapsibleExpertiseSection(
index: i + 1,
title: _covidVirusFields[i].label,
completed: _hasAny([
_covidVirusFields[i].resultField,
_covidVirusFields[i].unitField,
_covidVirusFields[i].referenceField,
]),
initiallyExpanded:
i == 0 &&
!_hasAny([
_covidVirusFields[i].resultField,
_covidVirusFields[i].unitField,
]),
children: [
_select(
_covidVirusFields[i].resultField,
_covidVirusFields[i].label,
_covidResultOptions,
),
_text(_covidVirusFields[i].unitField, 'Satuan'),
_text(_covidVirusFields[i].referenceField, 'Nilai Rujukan'),
],
),
CollapsibleExpertiseSection(
index: _covidVirusFields.length + 1,
title: 'Interpretasi',
completed: _hasAny(['covid_interpretasi']),
children: [
_text(
'covid_interpretasi',
'Interpretasi',
minLines: 6,
maxLines: 9,
),
],
),
],
);
}
bool _hasAny(List<String> names) {
for (final name in names) {
if ((widget.selectValues[name] ?? '').trim().isNotEmpty) {
return true;
}
if ((widget.textControllers[name]?.text ?? '').trim().isNotEmpty) {
return true;
}
}
return false;
}
Widget _antibioticSensitivity() {
return AntibioticSensitivityPanel(
textControllers: widget.textControllers,
selectValues: widget.selectValues,
onChanged: widget.onChanged,
);
}
Widget _toolData() {
return ToolDataPanel(
textControllers: widget.textControllers,
selectValues: widget.selectValues,
onChanged: widget.onChanged,
);
}
Widget _finalExpertise() {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
ExpertiseHtmlEditor(
controller: widget.textControllers['keterangan'] ??=
TextEditingController(),
label: 'Expertise',
),
CheckboxListTile(
value: widget.criticalValue,
onChanged: (value) => widget.onCriticalChanged(value ?? false),
contentPadding: EdgeInsets.zero,
controlAffinity: ListTileControlAffinity.leading,
title: const Text('Nilai Kritis'),
),
FilledButton.icon(
onPressed: widget.saving ? null : () => widget.onSave('Draft'),
icon: widget.saving
? const SizedBox.square(
dimension: 18,
child: CircularProgressIndicator(strokeWidth: 2),
)
: const Icon(Icons.save_outlined),
label: const Text('Save as Draft'),
),
if (widget.isSupervisor) ...[
OutlinedButton.icon(
onPressed: widget.saving
? null
: () => widget.onSave('preliminary'),
icon: const Icon(Icons.done_all_outlined),
label: const Text('Save Preliminary result'),
),
OutlinedButton.icon(
onPressed: widget.saving ? null : () => widget.onSave('verifikasi'),
icon: const Icon(Icons.verified_outlined),
label: const Text('Save Final Result'),
),
] else ...[
OutlinedButton.icon(
onPressed: widget.saving
? null
: () => widget.onSave('Permohonan Verifikasi'),
icon: const Icon(Icons.send_outlined),
label: const Text('Save and Send To SPV'),
),
OutlinedButton.icon(
onPressed: widget.saving
? null
: () => widget.onSave('Permohonan Verifikasi Preliminary'),
icon: const Icon(Icons.outgoing_mail),
label: const Text('Kirim SPV Preliminary'),
),
],
],
);
}
Widget _sectionTitle(String title, {bool compact = false}) {
return Padding(
padding: EdgeInsets.only(bottom: compact ? 8 : 12),
child: Text(
title,
style: Theme.of(context).textTheme.titleSmall?.copyWith(
fontWeight: FontWeight.w800,
color: const Color(0xFF0F766E),
),
),
);
}
// ignore: unused_element
Widget _toolSection({
required String title,
required String bacteriaField,
String? antibioticField,
required String sirField,
required String colonyField,
required String colonyTextField,
required String printField,
}) {
return Card(
margin: const EdgeInsets.only(bottom: 12),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
child: Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_sectionTitle(title, compact: true),
_text(bacteriaField, 'Bakteri'),
if (antibioticField != null)
_text(antibioticField, 'Set Antibiotik'),
_select(sirField, 'SIR', _sirOptions),
_select(colonyField, 'Hitung Koloni', _colonyOptions),
_text(colonyTextField, 'Hitung Koloni Lainnya'),
_select(printField, 'Cetak', const ['YA', 'TIDAK']),
],
),
),
);
}
Widget _select(String name, String label, List<String> options) {
final current = widget.selectValues[name];
final effectiveOptions = [
if (current != null && current.isNotEmpty && !options.contains(current))
current,
...options,
];
return Padding(
padding: const EdgeInsets.only(bottom: 12),
child: DropdownButtonFormField<String>(
initialValue:
current != null &&
current.isNotEmpty &&
effectiveOptions.contains(current)
? current
: null,
isExpanded: true,
decoration: InputDecoration(
labelText: label,
border: const OutlineInputBorder(),
),
items: effectiveOptions
.map(
(option) => DropdownMenuItem(value: option, child: Text(option)),
)
.toList(),
onChanged: (value) {
widget.selectValues[name] = value ?? '';
widget.onChanged();
},
),
);
}
Widget _text(
String name,
String label, {
int minLines = 1,
int maxLines = 1,
}) {
final controller = widget.textControllers[name] ??= TextEditingController();
return Padding(
padding: const EdgeInsets.only(bottom: 12),
child: TextField(
controller: controller,
minLines: minLines,
maxLines: maxLines,
decoration: InputDecoration(
labelText: label,
border: const OutlineInputBorder(),
),
),
);
}
}
class _CovidVirusField {
const _CovidVirusField(
this.label,
this.resultField,
this.unitField,
this.referenceField,
);
final String label;
final String resultField;
final String unitField;
final String referenceField;
}
const List<String> _covidResultOptions = [
'Positif',
'Negatif',
'Tidak Diperiksa',
];
const List<_CovidVirusField> _covidVirusFields = [
_CovidVirusField(
'Virus SARS-Cov-2',
'covid_virus01',
'covid_satuanvirus01',
'covid_rujukanvirus01',
),
_CovidVirusField(
'Influenza A',
'covid_virus02',
'covid_satuanvirus02',
'covid_rujukanvirus02',
),
_CovidVirusField(
'Influenza B',
'covid_virus03',
'covid_satuanvirus03',
'covid_rujukanvirus03',
),
_CovidVirusField(
'RSV',
'covid_virus04',
'covid_satuanvirus04',
'covid_rujukanvirus04',
),
];