update
This commit is contained in:
@@ -124,35 +124,10 @@ class _CciExpertiseWizardState extends State<CciExpertiseWizard> {
|
||||
Step(
|
||||
title: const Text('Data Alat'),
|
||||
isActive: _step == 2,
|
||||
content: Column(
|
||||
children: [
|
||||
_toolSection(
|
||||
title: 'Vitek',
|
||||
bacteriaField: 'bakteri',
|
||||
sirField: 'bakterisir',
|
||||
colonyField: 'id_bakterihitungkol',
|
||||
colonyTextField: 'id_bakterihitungkolteks',
|
||||
printField: 'id_baktericetak',
|
||||
),
|
||||
_toolSection(
|
||||
title: 'MALDITOF',
|
||||
bacteriaField: 'id_bakteri01',
|
||||
antibioticField: 'id_antibiotikmanual1',
|
||||
sirField: 'id_bakterisir01',
|
||||
colonyField: 'id_bakterihitungkol01',
|
||||
colonyTextField: 'id_bakterihitungkolteks01',
|
||||
printField: 'id_bakteri01cetak',
|
||||
),
|
||||
_toolSection(
|
||||
title: 'Bakteri Manual I',
|
||||
bacteriaField: 'id_bakteri02',
|
||||
antibioticField: 'id_antibiotikmanual2',
|
||||
sirField: 'id_bakterisir02',
|
||||
colonyField: 'id_bakterihitungkol02',
|
||||
colonyTextField: 'id_bakterihitungkolteks02',
|
||||
printField: 'id_bakteri02cetak',
|
||||
),
|
||||
],
|
||||
content: ToolDataPanel(
|
||||
textControllers: widget.textControllers,
|
||||
selectValues: widget.selectValues,
|
||||
onChanged: widget.onChanged,
|
||||
),
|
||||
),
|
||||
Step(
|
||||
@@ -229,6 +204,7 @@ class _CciExpertiseWizardState extends State<CciExpertiseWizard> {
|
||||
return values.isEmpty ? fallback : values;
|
||||
}
|
||||
|
||||
// ignore: unused_element
|
||||
Widget _toolSection({
|
||||
required String title,
|
||||
required String bacteriaField,
|
||||
|
||||
@@ -141,35 +141,10 @@ class _CovidExpertiseWizardState extends State<CovidExpertiseWizard> {
|
||||
}
|
||||
|
||||
Widget _toolData() {
|
||||
return Column(
|
||||
children: [
|
||||
_toolSection(
|
||||
title: 'Vitek',
|
||||
bacteriaField: 'bakteri',
|
||||
sirField: 'bakterisir',
|
||||
colonyField: 'id_bakterihitungkol',
|
||||
colonyTextField: 'id_bakterihitungkolteks',
|
||||
printField: 'id_baktericetak',
|
||||
),
|
||||
_toolSection(
|
||||
title: 'MALDITOF',
|
||||
bacteriaField: 'id_bakteri01',
|
||||
antibioticField: 'id_antibiotikmanual1',
|
||||
sirField: 'id_bakterisir01',
|
||||
colonyField: 'id_bakterihitungkol01',
|
||||
colonyTextField: 'id_bakterihitungkolteks01',
|
||||
printField: 'id_bakteri01cetak',
|
||||
),
|
||||
_toolSection(
|
||||
title: 'Bakteri Manual I',
|
||||
bacteriaField: 'id_bakteri02',
|
||||
antibioticField: 'id_antibiotikmanual2',
|
||||
sirField: 'id_bakterisir02',
|
||||
colonyField: 'id_bakterihitungkol02',
|
||||
colonyTextField: 'id_bakterihitungkolteks02',
|
||||
printField: 'id_bakteri02cetak',
|
||||
),
|
||||
],
|
||||
return ToolDataPanel(
|
||||
textControllers: widget.textControllers,
|
||||
selectValues: widget.selectValues,
|
||||
onChanged: widget.onChanged,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -245,6 +220,7 @@ class _CovidExpertiseWizardState extends State<CovidExpertiseWizard> {
|
||||
);
|
||||
}
|
||||
|
||||
// ignore: unused_element
|
||||
Widget _toolSection({
|
||||
required String title,
|
||||
required String bacteriaField,
|
||||
|
||||
@@ -1055,6 +1055,656 @@ class _AntibioticSensitivityPanelState
|
||||
}
|
||||
}
|
||||
|
||||
class ToolDataPanel extends StatefulWidget {
|
||||
const ToolDataPanel({
|
||||
super.key,
|
||||
required this.textControllers,
|
||||
required this.selectValues,
|
||||
required this.onChanged,
|
||||
});
|
||||
|
||||
final Map<String, TextEditingController> textControllers;
|
||||
final Map<String, String> selectValues;
|
||||
final VoidCallback onChanged;
|
||||
|
||||
@override
|
||||
State<ToolDataPanel> createState() => _ToolDataPanelState();
|
||||
}
|
||||
|
||||
class _ToolDataPanelState extends State<ToolDataPanel> {
|
||||
int _selectedTab = 0;
|
||||
|
||||
static const List<_ToolTabConfig> _tabs = [
|
||||
_ToolTabConfig(
|
||||
label: 'Vitek',
|
||||
tableId: 'tblvitek',
|
||||
rowField: 'vitek_antibiotic_rows',
|
||||
bacteriaField: 'bakteri',
|
||||
sirField: 'bakterisir',
|
||||
colonyField: 'id_bakterihitungkol',
|
||||
colonyTextField: 'id_bakterihitungkolteks',
|
||||
printField: 'id_baktericetak',
|
||||
automatic: true,
|
||||
),
|
||||
_ToolTabConfig(
|
||||
label: 'Malditof',
|
||||
tableId: 'tblkumanmanual1',
|
||||
rowField: 'malditof_antibiotic_rows',
|
||||
bacteriaField: 'id_bakteri01',
|
||||
antibioticSetField: 'id_antibiotikmanual1',
|
||||
sirField: 'id_bakterisir01',
|
||||
colonyField: 'id_bakterihitungkol01',
|
||||
colonyTextField: 'id_bakterihitungkolteks01',
|
||||
printField: 'id_bakteri01cetak',
|
||||
),
|
||||
_ToolTabConfig(
|
||||
label: 'Manual',
|
||||
tableId: 'tblkumanmanual2',
|
||||
rowField: 'manual_antibiotic_rows',
|
||||
bacteriaField: 'id_bakteri02',
|
||||
antibioticSetField: 'id_antibiotikmanual2',
|
||||
sirField: 'id_bakterisir02',
|
||||
colonyField: 'id_bakterihitungkol02',
|
||||
colonyTextField: 'id_bakterihitungkolteks02',
|
||||
printField: 'id_bakteri02cetak',
|
||||
),
|
||||
];
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final tab = _tabs[_selectedTab];
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Data Alat',
|
||||
style: Theme.of(context).textTheme.titleSmall?.copyWith(
|
||||
fontWeight: FontWeight.w800,
|
||||
color: const Color(0xFF0F766E),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
_tabBar(),
|
||||
const SizedBox(height: 12),
|
||||
_toolForm(tab),
|
||||
const SizedBox(height: 12),
|
||||
_toolButtons(tab),
|
||||
const SizedBox(height: 12),
|
||||
_antibioticTable(tab),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _tabBar() {
|
||||
return SizedBox(
|
||||
height: 54,
|
||||
child: ListView.separated(
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: _tabs.length,
|
||||
separatorBuilder: (_, _) => const SizedBox(width: 8),
|
||||
itemBuilder: (context, index) {
|
||||
final tab = _tabs[index];
|
||||
final selected = index == _selectedTab;
|
||||
return ChoiceChip(
|
||||
selected: selected,
|
||||
showCheckmark: false,
|
||||
avatar: Icon(
|
||||
index == 0
|
||||
? Icons.memory_outlined
|
||||
: index == 1
|
||||
? Icons.biotech_outlined
|
||||
: Icons.edit_note_outlined,
|
||||
size: 18,
|
||||
color: selected ? const Color(0xFF0F766E) : Colors.black54,
|
||||
),
|
||||
label: Text(tab.label),
|
||||
onSelected: (_) => setState(() => _selectedTab = index),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _toolForm(_ToolTabConfig tab) {
|
||||
final rows = _rows(tab);
|
||||
return Card(
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
tab.label,
|
||||
style: Theme.of(context).textTheme.titleSmall?.copyWith(
|
||||
fontWeight: FontWeight.w800,
|
||||
),
|
||||
),
|
||||
),
|
||||
BadgeLabel(
|
||||
text: '${rows.length} antibiotik',
|
||||
color: const Color(0xFF0F766E),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
if (tab.automatic)
|
||||
_readonlyBacteria(tab)
|
||||
else
|
||||
_bacteriaAutocomplete(tab),
|
||||
if (tab.antibioticSetField != null)
|
||||
_selectText(
|
||||
tab.antibioticSetField!,
|
||||
'Set Antibiotik',
|
||||
_antibioticSetOptions,
|
||||
onChanged: () => _autoGenerate(tab),
|
||||
),
|
||||
_selectText(
|
||||
tab.sirField,
|
||||
'Resistensi',
|
||||
_sirOptions,
|
||||
onChanged: () => _autoGenerate(tab),
|
||||
),
|
||||
_selectText(tab.colonyField, 'Hitung Koloni', _colonyOptions),
|
||||
_text(tab.colonyTextField, 'Hitung Koloni Lainnya'),
|
||||
_selectText(tab.printField, 'Cetak', const ['YA', 'TIDAK']),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _readonlyBacteria(_ToolTabConfig tab) {
|
||||
final controller = widget.textControllers[tab.bacteriaField] ??=
|
||||
TextEditingController();
|
||||
final text = controller.text.trim();
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 12),
|
||||
child: TextField(
|
||||
controller: controller,
|
||||
readOnly: true,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Kuman',
|
||||
helperText: text.isEmpty
|
||||
? 'Readonly dari hasil alat Vitek'
|
||||
: 'Readonly dari hasil alat Vitek',
|
||||
border: const OutlineInputBorder(),
|
||||
prefixIcon: const Icon(Icons.lock_outline),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _bacteriaAutocomplete(_ToolTabConfig tab) {
|
||||
final controller = widget.textControllers[tab.bacteriaField] ??=
|
||||
TextEditingController();
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 12),
|
||||
child: Autocomplete<String>(
|
||||
initialValue: TextEditingValue(text: controller.text),
|
||||
optionsBuilder: (value) {
|
||||
final query = value.text.trim().toLowerCase();
|
||||
if (query.isEmpty) {
|
||||
return _bacteriaOptions;
|
||||
}
|
||||
return _bacteriaOptions.where(
|
||||
(option) => option.toLowerCase().contains(query),
|
||||
);
|
||||
},
|
||||
onSelected: (value) {
|
||||
controller.text = value;
|
||||
_autoGenerate(tab);
|
||||
},
|
||||
fieldViewBuilder:
|
||||
(context, fieldController, focusNode, onFieldSubmitted) {
|
||||
if (fieldController.text != controller.text) {
|
||||
fieldController.text = controller.text;
|
||||
}
|
||||
return TextField(
|
||||
controller: fieldController,
|
||||
focusNode: focusNode,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Kuman',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
onChanged: (value) {
|
||||
controller.text = value;
|
||||
widget.onChanged();
|
||||
},
|
||||
onSubmitted: (_) => _autoGenerate(tab),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _toolButtons(_ToolTabConfig tab) {
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.all(10),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFF1F5F9),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
children: [
|
||||
FilledButton.icon(
|
||||
onPressed: () => _generateRows(tab, replace: true),
|
||||
icon: const Icon(Icons.table_rows_outlined),
|
||||
label: Text(
|
||||
tab.automatic ? 'Muat Tabel Otomatis' : 'Generate Tabel',
|
||||
),
|
||||
),
|
||||
OutlinedButton.icon(
|
||||
onPressed: () => _setAllPrint(tab, printRow: true, printMic: true),
|
||||
icon: const Icon(Icons.check_circle_outline),
|
||||
label: const Text('Print Semua'),
|
||||
),
|
||||
OutlinedButton.icon(
|
||||
onPressed: () =>
|
||||
_setAllPrint(tab, printRow: false, printMic: false),
|
||||
icon: const Icon(Icons.block_outlined),
|
||||
label: const Text('Unprint Semua'),
|
||||
),
|
||||
OutlinedButton.icon(
|
||||
onPressed: () => _clearRows(tab),
|
||||
icon: const Icon(Icons.delete_sweep_outlined),
|
||||
label: const Text('Reset Tabel'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _antibioticTable(_ToolTabConfig tab) {
|
||||
final rows = _rows(tab);
|
||||
if (rows.isEmpty) {
|
||||
return EmptyPanel(
|
||||
text: tab.automatic
|
||||
? 'Belum ada tabel antibiotik otomatis dari Vitek.'
|
||||
: 'Pilih kuman dan resistensi untuk membuat tabel antibiotik.',
|
||||
);
|
||||
}
|
||||
|
||||
return Card(
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(12, 12, 12, 4),
|
||||
child: Text(
|
||||
tab.tableId,
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.labelMedium?.copyWith(color: Colors.black54),
|
||||
),
|
||||
),
|
||||
SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: DataTable(
|
||||
headingRowHeight: 40,
|
||||
dataRowMinHeight: 60,
|
||||
dataRowMaxHeight: 76,
|
||||
columns: [
|
||||
const DataColumn(label: Text('#')),
|
||||
const DataColumn(label: Text('Print')),
|
||||
const DataColumn(label: Text('Antibiotik')),
|
||||
const DataColumn(label: Text('Value')),
|
||||
const DataColumn(label: Text('Interpretation')),
|
||||
if (!tab.automatic) ...[
|
||||
const DataColumn(label: Text('S')),
|
||||
const DataColumn(label: Text('I')),
|
||||
const DataColumn(label: Text('R')),
|
||||
],
|
||||
const DataColumn(label: Text('Print Value')),
|
||||
const DataColumn(label: Text('Delete')),
|
||||
],
|
||||
rows: [
|
||||
for (var i = 0; i < rows.length; i += 1)
|
||||
DataRow(
|
||||
cells: [
|
||||
DataCell(Text('${i + 1}')),
|
||||
DataCell(
|
||||
IconButton(
|
||||
tooltip: rows[i]['printrow'] == true
|
||||
? 'Unprint row'
|
||||
: 'Print row',
|
||||
onPressed: () => _toggleBool(tab, i, 'printrow'),
|
||||
icon: Icon(
|
||||
rows[i]['printrow'] == true
|
||||
? Icons.check_circle
|
||||
: Icons.block,
|
||||
color: rows[i]['printrow'] == true
|
||||
? const Color(0xFF15803D)
|
||||
: const Color(0xFFDC2626),
|
||||
),
|
||||
),
|
||||
),
|
||||
DataCell(
|
||||
SizedBox(
|
||||
width: 150,
|
||||
child: Text(
|
||||
_cell(rows[i], 'antibiotic'),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
),
|
||||
DataCell(
|
||||
SizedBox(
|
||||
width: 104,
|
||||
child: TextFormField(
|
||||
initialValue: _cell(rows[i], 'value'),
|
||||
decoration: const InputDecoration(
|
||||
isDense: true,
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
onChanged: (value) =>
|
||||
_updateRow(tab, i, 'value', value),
|
||||
),
|
||||
),
|
||||
),
|
||||
DataCell(
|
||||
SizedBox(
|
||||
width: 118,
|
||||
child: DropdownButtonFormField<String>(
|
||||
initialValue:
|
||||
_interpretationOptions.contains(
|
||||
rows[i]['interpretation'],
|
||||
)
|
||||
? rows[i]['interpretation'].toString()
|
||||
: '',
|
||||
isExpanded: true,
|
||||
decoration: const InputDecoration(
|
||||
isDense: true,
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
items: _interpretationOptions
|
||||
.map(
|
||||
(option) => DropdownMenuItem(
|
||||
value: option,
|
||||
child: Text(
|
||||
option.isEmpty ? 'null' : option,
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
onChanged: (value) => _updateRow(
|
||||
tab,
|
||||
i,
|
||||
'interpretation',
|
||||
value ?? '',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (!tab.automatic) ...[
|
||||
DataCell(Text(_cell(rows[i], 'batasatas'))),
|
||||
DataCell(Text(_cell(rows[i], 'midrange'))),
|
||||
DataCell(Text(_cell(rows[i], 'batasbawah'))),
|
||||
],
|
||||
DataCell(
|
||||
IconButton(
|
||||
tooltip: rows[i]['printcol'] == true
|
||||
? 'Unprint value'
|
||||
: 'Print value',
|
||||
onPressed: () => _toggleBool(tab, i, 'printcol'),
|
||||
icon: Icon(
|
||||
rows[i]['printcol'] == true
|
||||
? Icons.check_circle
|
||||
: Icons.block,
|
||||
color: rows[i]['printcol'] == true
|
||||
? const Color(0xFF15803D)
|
||||
: const Color(0xFFDC2626),
|
||||
),
|
||||
),
|
||||
),
|
||||
DataCell(
|
||||
IconButton(
|
||||
tooltip: 'Delete MIC',
|
||||
color: const Color(0xFFDC2626),
|
||||
onPressed: () => _deleteRow(tab, i),
|
||||
icon: const Icon(Icons.delete_outline),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _selectText(
|
||||
String name,
|
||||
String label,
|
||||
List<String> options, {
|
||||
VoidCallback? onChanged,
|
||||
}) {
|
||||
final current = widget.selectValues[name] ?? '';
|
||||
final effectiveOptions = [
|
||||
if (current.isNotEmpty && !options.contains(current)) current,
|
||||
...options,
|
||||
];
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 12),
|
||||
child: DropdownButtonFormField<String>(
|
||||
initialValue: 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(decodeHtmlEntities(option)),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
onChanged: (value) {
|
||||
widget.selectValues[name] = value ?? '';
|
||||
widget.onChanged();
|
||||
onChanged?.call();
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _text(String name, String label) {
|
||||
final controller = widget.textControllers[name] ??= TextEditingController();
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 12),
|
||||
child: TextField(
|
||||
controller: controller,
|
||||
decoration: InputDecoration(
|
||||
labelText: label,
|
||||
border: const OutlineInputBorder(),
|
||||
),
|
||||
onChanged: (_) => widget.onChanged(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<Map<String, dynamic>> _rows(_ToolTabConfig tab) {
|
||||
final raw = widget.textControllers[tab.rowField]?.text ?? '';
|
||||
if (raw.trim().isEmpty) {
|
||||
return <Map<String, dynamic>>[];
|
||||
}
|
||||
try {
|
||||
final decoded = jsonDecode(raw);
|
||||
if (decoded is List) {
|
||||
return decoded.map((item) => asMap(item)).toList();
|
||||
}
|
||||
} catch (_) {
|
||||
return <Map<String, dynamic>>[];
|
||||
}
|
||||
return <Map<String, dynamic>>[];
|
||||
}
|
||||
|
||||
void _saveRows(_ToolTabConfig tab, List<Map<String, dynamic>> rows) {
|
||||
widget.textControllers[tab.rowField] ??= TextEditingController();
|
||||
widget.textControllers[tab.rowField]!.text = jsonEncode(rows);
|
||||
widget.onChanged();
|
||||
}
|
||||
|
||||
void _autoGenerate(_ToolTabConfig tab) {
|
||||
if (tab.automatic) {
|
||||
return;
|
||||
}
|
||||
final bacteria =
|
||||
widget.textControllers[tab.bacteriaField]?.text.trim() ?? '';
|
||||
final sir = widget.selectValues[tab.sirField]?.trim() ?? '';
|
||||
if (bacteria.isEmpty || sir.isEmpty) {
|
||||
return;
|
||||
}
|
||||
_generateRows(tab, replace: true);
|
||||
}
|
||||
|
||||
void _generateRows(_ToolTabConfig tab, {required bool replace}) {
|
||||
final rows = replace ? <Map<String, dynamic>>[] : _rows(tab);
|
||||
final bacteria =
|
||||
widget.textControllers[tab.bacteriaField]?.text.trim() ?? '';
|
||||
final resistance = widget.selectValues[tab.sirField]?.trim() ?? '';
|
||||
final setName = tab.antibioticSetField == null
|
||||
? 'otomatis'
|
||||
: widget.selectValues[tab.antibioticSetField!]?.trim() ?? 'default';
|
||||
final antibiotics = _antibioticsFor(tab, bacteria, resistance, setName);
|
||||
|
||||
for (var i = 0; i < antibiotics.length; i += 1) {
|
||||
final antibiotic = antibiotics[i];
|
||||
rows.add({
|
||||
'id': '${tab.rowField}_${DateTime.now().microsecondsSinceEpoch}_$i',
|
||||
'antibiotic': antibiotic,
|
||||
'value': '',
|
||||
'interpretation': '',
|
||||
'batasatas': 'S',
|
||||
'midrange': 'I',
|
||||
'batasbawah': 'R',
|
||||
'printrow': true,
|
||||
'printcol': true,
|
||||
'bacteria': bacteria,
|
||||
'resistance': resistance,
|
||||
'set': setName,
|
||||
});
|
||||
}
|
||||
_saveRows(tab, rows);
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
List<String> _antibioticsFor(
|
||||
_ToolTabConfig tab,
|
||||
String bacteria,
|
||||
String resistance,
|
||||
String setName,
|
||||
) {
|
||||
final base = tab.automatic
|
||||
? _vitekAntibiotics
|
||||
: bacteria.toLowerCase().contains('candida') ||
|
||||
bacteria.toLowerCase().contains('yeast') ||
|
||||
bacteria.toLowerCase().contains('jamur')
|
||||
? _fungalAntibiotics
|
||||
: _manualAntibiotics;
|
||||
if (resistance.contains('Carbapenem')) {
|
||||
return [...base, 'ETP', 'DOR', 'IPM'];
|
||||
}
|
||||
if (resistance == 'MRSA') {
|
||||
return ['FOX', 'OXA', 'VAN', 'LZD', 'CLI', 'ERY', ...base.take(5)];
|
||||
}
|
||||
if (setName.toLowerCase() != 'default') {
|
||||
return [setName, ...base.where((item) => item != setName)];
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
||||
void _setAllPrint(
|
||||
_ToolTabConfig tab, {
|
||||
required bool printRow,
|
||||
required bool printMic,
|
||||
}) {
|
||||
final rows = _rows(tab)
|
||||
.map((row) => {...row, 'printrow': printRow, 'printcol': printMic})
|
||||
.toList();
|
||||
_saveRows(tab, rows);
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
void _clearRows(_ToolTabConfig tab) {
|
||||
_saveRows(tab, <Map<String, dynamic>>[]);
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
void _toggleBool(_ToolTabConfig tab, int index, String key) {
|
||||
final rows = _rows(tab);
|
||||
if (index < 0 || index >= rows.length) {
|
||||
return;
|
||||
}
|
||||
rows[index][key] = rows[index][key] != true;
|
||||
_saveRows(tab, rows);
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
void _updateRow(_ToolTabConfig tab, int index, String key, String value) {
|
||||
final rows = _rows(tab);
|
||||
if (index < 0 || index >= rows.length) {
|
||||
return;
|
||||
}
|
||||
rows[index][key] = value;
|
||||
_saveRows(tab, rows);
|
||||
}
|
||||
|
||||
void _deleteRow(_ToolTabConfig tab, int index) {
|
||||
final rows = _rows(tab);
|
||||
if (index < 0 || index >= rows.length) {
|
||||
return;
|
||||
}
|
||||
rows.removeAt(index);
|
||||
_saveRows(tab, rows);
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
String _cell(Map<String, dynamic> row, String key) {
|
||||
final value = row[key]?.toString() ?? '';
|
||||
return value.isEmpty ? '-' : decodeHtmlEntities(value);
|
||||
}
|
||||
}
|
||||
|
||||
class _ToolTabConfig {
|
||||
const _ToolTabConfig({
|
||||
required this.label,
|
||||
required this.tableId,
|
||||
required this.rowField,
|
||||
required this.bacteriaField,
|
||||
required this.sirField,
|
||||
required this.colonyField,
|
||||
required this.colonyTextField,
|
||||
required this.printField,
|
||||
this.antibioticSetField,
|
||||
this.automatic = false,
|
||||
});
|
||||
|
||||
final String label;
|
||||
final String tableId;
|
||||
final String rowField;
|
||||
final String bacteriaField;
|
||||
final String sirField;
|
||||
final String colonyField;
|
||||
final String colonyTextField;
|
||||
final String printField;
|
||||
final String? antibioticSetField;
|
||||
final bool automatic;
|
||||
}
|
||||
|
||||
class _AntibioticMedia {
|
||||
const _AntibioticMedia({
|
||||
required this.label,
|
||||
@@ -1106,6 +1756,88 @@ const List<String> _kumanOptions = [
|
||||
'Kuman 5',
|
||||
];
|
||||
|
||||
const List<String> _bacteriaOptions = [
|
||||
'Kuman 1',
|
||||
'Kuman 2',
|
||||
'Kuman 3',
|
||||
'Kuman 4',
|
||||
'Kuman 5',
|
||||
'Escherichia coli',
|
||||
'Klebsiella pneumoniae',
|
||||
'Pseudomonas aeruginosa',
|
||||
'Acinetobacter baumannii',
|
||||
'Staphylococcus aureus',
|
||||
'Staphylococcus epidermidis',
|
||||
'Enterococcus faecalis',
|
||||
'Enterococcus faecium',
|
||||
'Streptococcus pneumoniae',
|
||||
'Candida albicans',
|
||||
'Candida tropicalis',
|
||||
'Candida glabrata',
|
||||
];
|
||||
|
||||
const List<String> _antibioticSetOptions = [
|
||||
'default',
|
||||
'Gram Positive',
|
||||
'Gram Negative',
|
||||
'Urine',
|
||||
'Blood',
|
||||
'Respiratory',
|
||||
'Candida',
|
||||
];
|
||||
|
||||
const List<String> _interpretationOptions = [
|
||||
'',
|
||||
'S',
|
||||
'I',
|
||||
'R',
|
||||
'Invalid',
|
||||
'No Result',
|
||||
'Error',
|
||||
'SDD',
|
||||
];
|
||||
|
||||
const List<String> _vitekAntibiotics = [
|
||||
'AMK',
|
||||
'AMP',
|
||||
'AMC',
|
||||
'CAZ',
|
||||
'CIP',
|
||||
'CTX',
|
||||
'CRO',
|
||||
'FEP',
|
||||
'GEN',
|
||||
'LEV',
|
||||
'MEM',
|
||||
'SXT',
|
||||
];
|
||||
|
||||
const List<String> _manualAntibiotics = [
|
||||
'AMP',
|
||||
'AMC',
|
||||
'SAM',
|
||||
'TZP',
|
||||
'CAZ',
|
||||
'CTX',
|
||||
'CRO',
|
||||
'FEP',
|
||||
'CIP',
|
||||
'LEV',
|
||||
'GEN',
|
||||
'AMK',
|
||||
'MEM',
|
||||
'SXT',
|
||||
];
|
||||
|
||||
const List<String> _fungalAntibiotics = [
|
||||
'Fluconazole',
|
||||
'Voriconazole',
|
||||
'Itraconazole',
|
||||
'Amphotericin B',
|
||||
'Caspofungin',
|
||||
'Micafungin',
|
||||
];
|
||||
|
||||
const List<String> _fungalKumanOptions = [
|
||||
'Bakteri Kuman 1',
|
||||
'Yeast Kuman 1',
|
||||
@@ -1387,6 +2119,12 @@ const List<String> kAntibioticMediaRowFields = [
|
||||
'media_tamlainnya_rows',
|
||||
];
|
||||
|
||||
const List<String> kToolAntibioticRowFields = [
|
||||
'vitek_antibiotic_rows',
|
||||
'malditof_antibiotic_rows',
|
||||
'manual_antibiotic_rows',
|
||||
];
|
||||
|
||||
class ExpertiseForm extends StatelessWidget {
|
||||
const ExpertiseForm({
|
||||
super.key,
|
||||
|
||||
@@ -97,6 +97,11 @@ class _ExpertiseScreenState extends State<ExpertiseScreen> {
|
||||
text: components[name]?.toString() ?? '',
|
||||
);
|
||||
}
|
||||
for (final name in kToolAntibioticRowFields) {
|
||||
_textControllers[name] = TextEditingController(
|
||||
text: components[name]?.toString() ?? '',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _setTemplate(String dlp) async {
|
||||
|
||||
@@ -303,35 +303,10 @@ class _KulturExpertiseWizardState extends State<KulturExpertiseWizard> {
|
||||
}
|
||||
|
||||
Widget _toolData() {
|
||||
return Column(
|
||||
children: [
|
||||
_toolSection(
|
||||
title: 'Vitek',
|
||||
bacteriaField: 'bakteri',
|
||||
sirField: 'bakterisir',
|
||||
colonyField: 'id_bakterihitungkol',
|
||||
colonyTextField: 'id_bakterihitungkolteks',
|
||||
printField: 'id_baktericetak',
|
||||
),
|
||||
_toolSection(
|
||||
title: 'MALDITOF',
|
||||
bacteriaField: 'id_bakteri01',
|
||||
antibioticField: 'id_antibiotikmanual1',
|
||||
sirField: 'id_bakterisir01',
|
||||
colonyField: 'id_bakterihitungkol01',
|
||||
colonyTextField: 'id_bakterihitungkolteks01',
|
||||
printField: 'id_bakteri01cetak',
|
||||
),
|
||||
_toolSection(
|
||||
title: 'Bakteri Manual I',
|
||||
bacteriaField: 'id_bakteri02',
|
||||
antibioticField: 'id_antibiotikmanual2',
|
||||
sirField: 'id_bakterisir02',
|
||||
colonyField: 'id_bakterihitungkol02',
|
||||
colonyTextField: 'id_bakterihitungkolteks02',
|
||||
printField: 'id_bakteri02cetak',
|
||||
),
|
||||
],
|
||||
return ToolDataPanel(
|
||||
textControllers: widget.textControllers,
|
||||
selectValues: widget.selectValues,
|
||||
onChanged: widget.onChanged,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -417,6 +392,7 @@ class _KulturExpertiseWizardState extends State<KulturExpertiseWizard> {
|
||||
);
|
||||
}
|
||||
|
||||
// ignore: unused_element
|
||||
Widget _toolSection({
|
||||
required String title,
|
||||
required String bacteriaField,
|
||||
|
||||
@@ -131,35 +131,10 @@ class _LeptospiraExpertiseWizardState extends State<LeptospiraExpertiseWizard> {
|
||||
}
|
||||
|
||||
Widget _toolData() {
|
||||
return Column(
|
||||
children: [
|
||||
_toolSection(
|
||||
title: 'Vitek',
|
||||
bacteriaField: 'bakteri',
|
||||
sirField: 'bakterisir',
|
||||
colonyField: 'id_bakterihitungkol',
|
||||
colonyTextField: 'id_bakterihitungkolteks',
|
||||
printField: 'id_baktericetak',
|
||||
),
|
||||
_toolSection(
|
||||
title: 'MALDITOF',
|
||||
bacteriaField: 'id_bakteri01',
|
||||
antibioticField: 'id_antibiotikmanual1',
|
||||
sirField: 'id_bakterisir01',
|
||||
colonyField: 'id_bakterihitungkol01',
|
||||
colonyTextField: 'id_bakterihitungkolteks01',
|
||||
printField: 'id_bakteri01cetak',
|
||||
),
|
||||
_toolSection(
|
||||
title: 'Bakteri Manual I',
|
||||
bacteriaField: 'id_bakteri02',
|
||||
antibioticField: 'id_antibiotikmanual2',
|
||||
sirField: 'id_bakterisir02',
|
||||
colonyField: 'id_bakterihitungkol02',
|
||||
colonyTextField: 'id_bakterihitungkolteks02',
|
||||
printField: 'id_bakteri02cetak',
|
||||
),
|
||||
],
|
||||
return ToolDataPanel(
|
||||
textControllers: widget.textControllers,
|
||||
selectValues: widget.selectValues,
|
||||
onChanged: widget.onChanged,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -235,6 +210,7 @@ class _LeptospiraExpertiseWizardState extends State<LeptospiraExpertiseWizard> {
|
||||
);
|
||||
}
|
||||
|
||||
// ignore: unused_element
|
||||
Widget _toolSection({
|
||||
required String title,
|
||||
required String bacteriaField,
|
||||
|
||||
@@ -241,35 +241,10 @@ class _PewarnaanLangsungExpertiseWizardState
|
||||
}
|
||||
|
||||
Widget _toolData() {
|
||||
return Column(
|
||||
children: [
|
||||
_toolSection(
|
||||
title: 'Vitek',
|
||||
bacteriaField: 'bakteri',
|
||||
sirField: 'bakterisir',
|
||||
colonyField: 'id_bakterihitungkol',
|
||||
colonyTextField: 'id_bakterihitungkolteks',
|
||||
printField: 'id_baktericetak',
|
||||
),
|
||||
_toolSection(
|
||||
title: 'MALDITOF',
|
||||
bacteriaField: 'id_bakteri01',
|
||||
antibioticField: 'id_antibiotikmanual1',
|
||||
sirField: 'id_bakterisir01',
|
||||
colonyField: 'id_bakterihitungkol01',
|
||||
colonyTextField: 'id_bakterihitungkolteks01',
|
||||
printField: 'id_bakteri01cetak',
|
||||
),
|
||||
_toolSection(
|
||||
title: 'Bakteri Manual I',
|
||||
bacteriaField: 'id_bakteri02',
|
||||
antibioticField: 'id_antibiotikmanual2',
|
||||
sirField: 'id_bakterisir02',
|
||||
colonyField: 'id_bakterihitungkol02',
|
||||
colonyTextField: 'id_bakterihitungkolteks02',
|
||||
printField: 'id_bakteri02cetak',
|
||||
),
|
||||
],
|
||||
return ToolDataPanel(
|
||||
textControllers: widget.textControllers,
|
||||
selectValues: widget.selectValues,
|
||||
onChanged: widget.onChanged,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -355,6 +330,7 @@ class _PewarnaanLangsungExpertiseWizardState
|
||||
);
|
||||
}
|
||||
|
||||
// ignore: unused_element
|
||||
Widget _toolSection({
|
||||
required String title,
|
||||
required String bacteriaField,
|
||||
|
||||
@@ -367,35 +367,10 @@ class _TbcExpertiseWizardState extends State<TbcExpertiseWizard> {
|
||||
}
|
||||
|
||||
Widget _toolData() {
|
||||
return Column(
|
||||
children: [
|
||||
_toolSection(
|
||||
title: 'Vitek',
|
||||
bacteriaField: 'bakteri',
|
||||
sirField: 'bakterisir',
|
||||
colonyField: 'id_bakterihitungkol',
|
||||
colonyTextField: 'id_bakterihitungkolteks',
|
||||
printField: 'id_baktericetak',
|
||||
),
|
||||
_toolSection(
|
||||
title: 'MALDITOF',
|
||||
bacteriaField: 'id_bakteri01',
|
||||
antibioticField: 'id_antibiotikmanual1',
|
||||
sirField: 'id_bakterisir01',
|
||||
colonyField: 'id_bakterihitungkol01',
|
||||
colonyTextField: 'id_bakterihitungkolteks01',
|
||||
printField: 'id_bakteri01cetak',
|
||||
),
|
||||
_toolSection(
|
||||
title: 'Bakteri Manual I',
|
||||
bacteriaField: 'id_bakteri02',
|
||||
antibioticField: 'id_antibiotikmanual2',
|
||||
sirField: 'id_bakterisir02',
|
||||
colonyField: 'id_bakterihitungkol02',
|
||||
colonyTextField: 'id_bakterihitungkolteks02',
|
||||
printField: 'id_bakteri02cetak',
|
||||
),
|
||||
],
|
||||
return ToolDataPanel(
|
||||
textControllers: widget.textControllers,
|
||||
selectValues: widget.selectValues,
|
||||
onChanged: widget.onChanged,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -471,6 +446,7 @@ class _TbcExpertiseWizardState extends State<TbcExpertiseWizard> {
|
||||
);
|
||||
}
|
||||
|
||||
// ignore: unused_element
|
||||
Widget _toolSection({
|
||||
required String title,
|
||||
required String bacteriaField,
|
||||
|
||||
@@ -144,35 +144,10 @@ class _ViralLoadExpertiseWizardState extends State<ViralLoadExpertiseWizard> {
|
||||
}
|
||||
|
||||
Widget _toolData() {
|
||||
return Column(
|
||||
children: [
|
||||
_toolSection(
|
||||
title: 'Vitek',
|
||||
bacteriaField: 'bakteri',
|
||||
sirField: 'bakterisir',
|
||||
colonyField: 'id_bakterihitungkol',
|
||||
colonyTextField: 'id_bakterihitungkolteks',
|
||||
printField: 'id_baktericetak',
|
||||
),
|
||||
_toolSection(
|
||||
title: 'MALDITOF',
|
||||
bacteriaField: 'id_bakteri01',
|
||||
antibioticField: 'id_antibiotikmanual1',
|
||||
sirField: 'id_bakterisir01',
|
||||
colonyField: 'id_bakterihitungkol01',
|
||||
colonyTextField: 'id_bakterihitungkolteks01',
|
||||
printField: 'id_bakteri01cetak',
|
||||
),
|
||||
_toolSection(
|
||||
title: 'Bakteri Manual I',
|
||||
bacteriaField: 'id_bakteri02',
|
||||
antibioticField: 'id_antibiotikmanual2',
|
||||
sirField: 'id_bakterisir02',
|
||||
colonyField: 'id_bakterihitungkol02',
|
||||
colonyTextField: 'id_bakterihitungkolteks02',
|
||||
printField: 'id_bakteri02cetak',
|
||||
),
|
||||
],
|
||||
return ToolDataPanel(
|
||||
textControllers: widget.textControllers,
|
||||
selectValues: widget.selectValues,
|
||||
onChanged: widget.onChanged,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -258,6 +233,7 @@ class _ViralLoadExpertiseWizardState extends State<ViralLoadExpertiseWizard> {
|
||||
);
|
||||
}
|
||||
|
||||
// ignore: unused_element
|
||||
Widget _toolSection({
|
||||
required String title,
|
||||
required String bacteriaField,
|
||||
|
||||
Reference in New Issue
Block a user