386 lines
11 KiB
Dart
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',
|
|
),
|
|
];
|