diff --git a/htdocs/app/Http/Controllers/MobileApiController.php b/htdocs/app/Http/Controllers/MobileApiController.php
index 3ff63093..4ce8cc1b 100644
--- a/htdocs/app/Http/Controllers/MobileApiController.php
+++ b/htdocs/app/Http/Controllers/MobileApiController.php
@@ -2,8 +2,13 @@
namespace App\Http\Controllers;
+use App\KomponenJawaban;
+use App\CriticalValueSample;
+use App\Organisms;
use App\Periksa;
use App\PeriksaSYNC;
+use App\Riwayat;
+use App\Subjawaban;
use App\User;
use Carbon\Carbon;
use Illuminate\Http\Request;
@@ -143,9 +148,16 @@ class MobileApiController extends Controller
'reques' => $row->reques,
'asalpasien' => $row->asalpasien,
'ruangan' => $row->ruangan,
+ 'analis' => $row->analis,
+ 'ppds3' => $row->ppdssenior,
+ 'dokter_id' => $row->dokter_id,
'klinisi' => $row->klinisi,
+ 'klinis' => $row->klinis,
'nmdokter' => $row->nmdokter,
'nm_spesimen' => $row->nm_spesimen,
+ 'kesimpulan' => $row->kesimpulan,
+ 'dlp' => $row->dlp,
+ 'tanggalregis' => $row->tanggalregis,
'tanggalsampel' => $row->tanggalsampel,
'pengambilan' => $row->pengambilan,
'asalpengirim' => $row->asalpengirim,
@@ -270,6 +282,682 @@ class MobileApiController extends Controller
]);
}
+ public function expertise(Request $request, int $id)
+ {
+ $user = $this->requireUser($request);
+ $row = Periksa::find($id);
+ abort_if(!$row, 404, 'Data pemeriksaan tidak ditemukan.');
+
+ $dlp = trim((string) ($row->dlp ?? ''));
+ return response()->json([
+ 'item' => $this->examinationPayload($row),
+ 'user' => [
+ 'id' => $user->id,
+ 'nama' => $user->nama,
+ 'previlage' => $user->previlage,
+ ],
+ 'dlp' => $dlp,
+ 'templates' => $this->templateList(),
+ 'staff_options' => $this->staffOptions(),
+ 'option_sets' => $this->expertiseOptionSets(),
+ 'is_critical' => CriticalValueSample::where('periksa_id', $row->id)->exists(),
+ 'components' => $dlp === '' ? (object) [] : $this->componentMap($row->nofoto, $dlp),
+ 'fields' => $dlp === '' ? [] : $this->expertiseFields($dlp),
+ ]);
+ }
+
+ public function setExpertiseTemplate(Request $request, int $id)
+ {
+ $user = $this->requireUser($request);
+ $request->validate(['dlp' => ['required', 'string']]);
+ $row = Periksa::find($id);
+ abort_if(!$row, 404, 'Data pemeriksaan tidak ditemukan.');
+
+ $dlp = trim((string) $request->input('dlp'));
+ $row->update([
+ 'dlp' => $dlp,
+ 'nmdrafter' => $user->nama,
+ 'nmexcutor' => $user->nama,
+ 'excutor' => $user->id,
+ 'status' => $row->status ?: 'Pemeriksaan awal',
+ ]);
+
+ return response()->json([
+ 'message' => 'Template expertise dipilih.',
+ 'item' => $this->examinationPayload($row->fresh()),
+ 'dlp' => $dlp,
+ 'components' => $this->componentMap($row->nofoto, $dlp),
+ 'fields' => $this->expertiseFields($dlp),
+ ]);
+ }
+
+ public function saveExpertise(Request $request, int $id)
+ {
+ $user = $this->requireUser($request);
+ $request->validate([
+ 'dlp' => ['required', 'string'],
+ 'action' => ['nullable', 'string'],
+ 'fields' => ['nullable', 'array'],
+ 'keterangan' => ['nullable', 'string'],
+ 'analis' => ['nullable'],
+ 'ppds3' => ['nullable'],
+ 'dokter' => ['nullable'],
+ 'nilai_kritis' => ['nullable'],
+ ]);
+
+ $row = Periksa::find($id);
+ abort_if(!$row, 404, 'Data pemeriksaan tidak ditemukan.');
+
+ $dlp = trim((string) $request->input('dlp'));
+ $fields = $request->input('fields', []);
+ $keterangan = (string) $request->input('keterangan', '');
+ $action = (string) $request->input('action', 'Draft');
+ $analis = $request->input('analis');
+ $ppds3 = $request->input('ppds3');
+ $dokter = $request->input('dokter');
+ $atlmUser = User::find($analis);
+ $ppdsUser = User::find($ppds3);
+ $spvUser = User::find($dokter);
+
+ foreach ($fields as $key => $value) {
+ if ($value === '' || $value === null || $value === 'Pilih Salah Satu') {
+ KomponenJawaban::where('accnumber', $row->nofoto)
+ ->where('template', $dlp)
+ ->where('komponen', $key)
+ ->delete();
+ continue;
+ }
+
+ KomponenJawaban::updateOrCreate(
+ [
+ 'accnumber' => $row->nofoto,
+ 'komponen' => $key,
+ ],
+ [
+ 'isidata' => is_array($value) ? json_encode($value) : $value,
+ 'template' => $dlp,
+ 'created_by' => $user->nama,
+ ]
+ );
+ }
+
+ $status = match ($action) {
+ 'verifikasi' => 'Selesai',
+ 'preliminary' => 'Preliminary Results',
+ 'Permohonan Verifikasi' => 'Expertise Saved (Un Verified)',
+ 'Permohonan Verifikasi Preliminary' => 'Otor Maldi (Un Verified)',
+ default => 'Pemeriksaan awal',
+ };
+
+ $updateData = [
+ 'keterangan' => $keterangan,
+ 'dlp' => $dlp,
+ 'analis' => $analis,
+ 'nmanalis' => $atlmUser->nama ?? null,
+ 'dokter_id' => $action === 'verifikasi' || $action === 'preliminary' ? $user->id : $dokter,
+ 'nmdokter' => $action === 'verifikasi' || $action === 'preliminary' ? $user->nama : ($spvUser->nama ?? null),
+ 'ppdssenior' => $ppds3,
+ 'nmppdssenior' => $ppdsUser->nama ?? null,
+ 'nmdrafter' => $user->nama,
+ 'nmexcutor' => $user->nama,
+ 'excutor' => $user->id,
+ 'baca' => in_array($action, ['Permohonan Verifikasi', 'Permohonan Verifikasi Preliminary'], true) ? now() : $row->baca,
+ 'nmpembaca' => in_array($action, ['Permohonan Verifikasi', 'Permohonan Verifikasi Preliminary'], true) ? $user->nama : $row->nmpembaca,
+ 'status' => $status,
+ ];
+ if (in_array($action, ['verifikasi', 'preliminary'], true)) {
+ $updateData['verifikasi'] = now();
+ $updateData['akhir'] = $action === 'verifikasi' ? now() : $row->akhir;
+ }
+ $row->update($updateData);
+
+ if ($this->shouldMarkAsCritical($request)) {
+ $this->upsertCriticalValueSample($row->fresh(), $user);
+ }
+
+ Riwayat::create([
+ 'nofoto' => $row->nofoto,
+ 'jawaban' => $keterangan !== '' ? $keterangan : json_encode($fields),
+ 'inputor' => $user->nama,
+ 'keterangan' => match ($action) {
+ 'verifikasi' => 'Expertise',
+ 'preliminary' => 'Preliminary Results',
+ 'Permohonan Verifikasi' => 'Kirim ke SPV',
+ 'Permohonan Verifikasi Preliminary' => 'Kirim Preliminary ke SPV',
+ default => 'Draft',
+ },
+ 'verifikasi' => in_array($action, ['verifikasi', 'preliminary'], true) ? 'Accepted' : '',
+ ]);
+
+ return response()->json([
+ 'message' => match ($action) {
+ 'verifikasi' => 'Expertise Saved',
+ 'preliminary' => 'Preliminary Results Saved',
+ 'Permohonan Verifikasi' => 'Permohonan Verifikasi Terkirim',
+ 'Permohonan Verifikasi Preliminary' => 'Permohonan Verifikasi Preliminary Terkirim',
+ default => 'Draft Expertise Saved',
+ },
+ 'item' => $this->examinationPayload($row->fresh()),
+ 'components' => $this->componentMap($row->nofoto, $dlp),
+ ]);
+ }
+
+ protected function templateList(): array
+ {
+ return Subjawaban::orderBy('kategori')
+ ->orderBy('judul')
+ ->get(['id', 'kategori', 'judul', 'subjawaban', 'kesimpulan'])
+ ->map(fn ($row) => [
+ 'id' => $row->id,
+ 'kategori' => (string) $row->kategori,
+ 'judul' => (string) $row->judul,
+ 'isine' => trim((string) $row->subjawaban.' '.(string) $row->kesimpulan),
+ ])
+ ->values()
+ ->toArray();
+ }
+
+ protected function componentMap($nofoto, string $dlp): array
+ {
+ return KomponenJawaban::where('accnumber', $nofoto)
+ ->where('template', $dlp)
+ ->get(['komponen', 'isidata'])
+ ->mapWithKeys(fn ($row) => [$row->komponen => $this->decodeComponentValue($row->isidata)])
+ ->toArray();
+ }
+
+ protected function decodeComponentValue($value)
+ {
+ if (!is_string($value)) {
+ return $value;
+ }
+ $decoded = json_decode($value, true);
+ return json_last_error() === JSON_ERROR_NONE ? $decoded : $value;
+ }
+
+ protected function staffOptions(): array
+ {
+ $mapUser = fn ($row) => [
+ 'id' => $row->id,
+ 'nama' => $row->nama,
+ 'username' => $row->username,
+ 'previlage' => $row->previlage,
+ ];
+
+ return [
+ 'analis' => User::where('previlage', 'analis')->orderBy('nama')->get()->map($mapUser)->values()->toArray(),
+ 'ppds' => User::where('previlage', 'ppds')->orderBy('nama')->get()->map($mapUser)->values()->toArray(),
+ 'dokters' => User::where('previlage', 'supervisor')->orderBy('nama')->get()->map($mapUser)->values()->toArray(),
+ ];
+ }
+
+ protected function expertiseOptionSets(): array
+ {
+ $organismNames = fn ($kelompok) => Organisms::where('kelompok', $kelompok)
+ ->orderBy('category')
+ ->orderBy('name')
+ ->pluck('name')
+ ->filter()
+ ->values()
+ ->toArray();
+
+ return [
+ 'jsonsputum' => $organismNames('Sputum'),
+ 'jsonswabtenggorok' => $organismNames('Swab Tenggorok'),
+ 'jsonurine' => $organismNames('Urine'),
+ 'jsonswabperineum' => $organismNames('Swab perineum'),
+ 'jsonselepitel' => $organismNames('Sel Epitel'),
+ 'jsonselradang' => $organismNames('Sel Radang'),
+ 'jsonmikroorganisme' => $organismNames('Mikroorganisme'),
+ 'jsonmikroorganismeoptional' => Organisms::orderBy('category')
+ ->orderBy('name')
+ ->pluck('name')
+ ->filter()
+ ->values()
+ ->toArray(),
+ 'jsonjumlahlactobacillus' => $organismNames('Jumlah Lactobacillus'),
+ 'jsonjumlahgardnerella' => $organismNames('Jumlah Gardnerella'),
+ 'jsonjumlahmobiluncus' => $organismNames('Jumlah Mobiluncus'),
+ 'jsonpewarnaanziehlnielsen' => $organismNames('Pewarnaan Ziehl Neelsen'),
+ 'jsonpewarnaankoh' => $organismNames('Pewarnaan KOH'),
+ 'jsonpewarnaanneisser' => $organismNames('Pewarnaan Neisser'),
+ 'jsonpewarnaannegatif' => $organismNames('Pewarnaan Negatif'),
+ 'jsonpewarnaanspora' => $organismNames('Pewarnaan Spora'),
+ 'jsonpewarnaangiemsa' => $organismNames('Pewarnaan Giemsa'),
+ 'jsonbiakankultur' => $organismNames('Biakan Kultur'),
+ 'jsonvlparameter' => $organismNames('Viral Load Parameter'),
+ ];
+ }
+
+ protected function shouldMarkAsCritical(Request $request): bool
+ {
+ $flag = strtolower((string) $request->input('nilai_kritis', '0'));
+ return in_array($flag, ['1', 'true', 'yes', 'on'], true);
+ }
+
+ protected function upsertCriticalValueSample(Periksa $periksa, User $user): void
+ {
+ $criticalSample = CriticalValueSample::firstOrNew(['periksa_id' => $periksa->id]);
+ $criticalSample->fill([
+ 'nofoto' => $periksa->nofoto,
+ 'noregister' => $periksa->noregister,
+ 'nmpasien' => $periksa->nmpasien,
+ 'nm_spesimen' => $periksa->nm_spesimen,
+ 'asalpasien' => $periksa->asalpasien,
+ 'critical_set_at' => now(),
+ 'critical_set_by_user_id' => $user->id,
+ 'critical_set_by_name' => $user->nama,
+ ]);
+ $criticalSample->save();
+ }
+
+ protected function expertiseFields(string $dlp): array
+ {
+ $select = fn ($name, $label, $options) => ['name' => $name, 'label' => $label, 'type' => 'select', 'options' => $options];
+ $text = fn ($name, $label) => ['name' => $name, 'label' => $label, 'type' => 'text'];
+ $area = fn ($name, $label) => ['name' => $name, 'label' => $label, 'type' => 'textarea'];
+ $multi = fn ($name, $label, $options) => ['name' => $name, 'label' => $label, 'type' => 'multiselect', 'options' => $options];
+ $yn = ['Positif', 'Negatif'];
+ $tbResult = ['+++', '++', '+', '1-9', 'Neg'];
+ $organismOptions = ['Tidak ditemukan', 'Sedikit', 'Sedang', 'Banyak', 'lainnya'];
+ $sirOptions = ['ESBL', 'MRSA', 'MDR', 'XDR', 'PDR', 'Carbapenem Resistant', 'MDR Carbapenem Resistant', 'XDR Carbapenem Resistant'];
+ $colonyOptions = ['>= 10^5 CFU/ml Urine (Bakteriuria bermakna)', '>= 10^5 CFU/ml Urine (Candiduria bermakna)', '>= 10^3 CFU/ml Urine (Bakteriuria bermakna)', '8 x 10^3 CFU/ml Urine (Bakteriuria bermakna)', 'lainnya'];
+ $printOptions = ['YA', 'TIDAK'];
+
+ return match ($dlp) {
+ 'CCI' => [
+ $select('id_sputum', 'Sputum', $organismOptions),
+ $select('id_swabtenggorok', 'Swab Tenggorok', $organismOptions),
+ $select('id_urine', 'Urine', $organismOptions),
+ $select('id_swabperineum', 'Swab Perineum', $organismOptions),
+ $text('bakteri', 'Vitek - Bakteri'),
+ $select('bakterisir', 'Vitek - SIR', $sirOptions),
+ $select('id_bakterihitungkol', 'Vitek - Hitung Koloni', $colonyOptions),
+ $text('id_bakterihitungkolteks', 'Vitek - Hitung Koloni Lainnya'),
+ $select('id_baktericetak', 'Vitek - Cetak', $printOptions),
+ $text('id_bakteri01', 'MALDITOF - Bakteri'),
+ $text('id_antibiotikmanual1', 'MALDITOF - Set Antibiotik'),
+ $select('id_bakterisir01', 'MALDITOF - SIR', $sirOptions),
+ $select('id_bakterihitungkol01', 'MALDITOF - Hitung Koloni', $colonyOptions),
+ $text('id_bakterihitungkolteks01', 'MALDITOF - Hitung Koloni Lainnya'),
+ $select('id_bakteri01cetak', 'MALDITOF - Cetak', $printOptions),
+ $text('id_bakteri02', 'Bakteri Manual I - Bakteri'),
+ $text('id_antibiotikmanual2', 'Bakteri Manual I - Set Antibiotik'),
+ $select('id_bakterisir02', 'Bakteri Manual I - SIR', $sirOptions),
+ $select('id_bakterihitungkol02', 'Bakteri Manual I - Hitung Koloni', $colonyOptions),
+ $text('id_bakterihitungkolteks02', 'Bakteri Manual I - Hitung Koloni Lainnya'),
+ $select('id_bakteri02cetak', 'Bakteri Manual I - Cetak', $printOptions),
+ $area('keterangan', 'Keterangan Expertise'),
+ ],
+ 'Kultur' => [
+ $select('id_selepitel', 'Sel Epitel', $organismOptions),
+ $select('id_selradang', 'Sel Radang', $organismOptions),
+ $select('id_mikroorganisme', 'Mikroorganisme', $organismOptions),
+ $multi('id_mikroorganismeoptional', 'Mikroorganisme Tambahan', ['Gram positif', 'Gram negatif', 'Yeast cell', 'Basil', 'Kokus']),
+ $select('id_jumlahlactobacillus', 'Lactobacillus - Jumlah Per Lapang', $organismOptions),
+ $select('id_lactobacillus', 'Lactobacillus - Skor', ['0', '1', '2', '3', '4']),
+ $select('id_jumlahgardnerella', 'Gardnerella - Jumlah Per Lapang', $organismOptions),
+ $select('id_gardnerella', 'Gardnerella - Skor', ['0', '1', '2', '3', '4']),
+ $select('id_jumlahmobiluncus', 'Mobiluncus - Jumlah Per Lapang', $organismOptions),
+ $select('id_mobiluncus', 'Mobiluncus - Skor', ['0', '1', '2', '3', '4']),
+ $select('id_pewarnaanziehlnielsen', 'Pewarnaan Ziehl Neelsen Pagi', $organismOptions),
+ $select('id_pewarnaanziehlnielsensewaktu', 'Pewarnaan Ziehl Neelsen Sewaktu', $organismOptions),
+ $select('id_pewarnaangiesma', 'Pewarnaan Giemsa', ['Tidak ditemukan', 'Ditemukan', 'lainnya']),
+ $text('id_pewarnaangiesmaoptional', 'Pewarnaan Giemsa Lainnya'),
+ $select('id_pewarnaankoh', 'Pewarnaan KOH', ['Tidak ditemukan', 'Ditemukan', 'lainnya']),
+ $text('id_pewarnaankohoptional', 'Pewarnaan KOH Lainnya'),
+ $select('id_pewarnaanneisser', 'Pewarnaan Neisser', $organismOptions),
+ $select('id_pewarnaannegatif', 'Pewarnaan Negatif', $organismOptions),
+ $select('id_pewarnaanspora', 'Pewarnaan Spora', $organismOptions),
+ $text('bd_result', 'BD Result'),
+ $text('bd_result_date', 'BD Result Date'),
+ $select('id_kbamc', 'Kirby Bauer AMC', array_merge(['Tidak dilakukan'], range(1, 42))),
+ $select('id_sirkbamc', 'Kirby Bauer AMC SIR', ['S', 'I', 'R']),
+ $select('id_kbak', 'Kirby Bauer AK', array_merge(['Tidak dilakukan'], range(1, 42))),
+ $select('id_sirkbak', 'Kirby Bauer AK SIR', ['S', 'I', 'R']),
+ $select('id_kbfos', 'Kirby Bauer FOS', array_merge(['Tidak dilakukan'], range(1, 42))),
+ $select('id_sirkbfos', 'Kirby Bauer FOS SIR', ['S', 'I', 'R']),
+ $select('id_kbscf', 'Kirby Bauer SCF', array_merge(['Tidak dilakukan'], range(1, 42))),
+ $select('id_sirkbscf', 'Kirby Bauer SCF SIR', ['S', 'I', 'R']),
+ $select('id_biakankultur', 'Biakan Kultur (Aerob / Anaerob)', $organismOptions),
+ $select('media_bap_status', 'Media BAP Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_bap_note', 'Media BAP Catatan'),
+ $select('media_cap_status', 'Media CAP Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_cap_note', 'Media CAP Catatan'),
+ $select('media_mcconkey_status', 'Media Mc Conkey Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_mcconkey_note', 'Media Mc Conkey Catatan'),
+ $select('media_sdar1_status', 'Kultur Jamur R1 Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_sdar1_note', 'Kultur Jamur R1 Catatan'),
+ $select('media_sdar2_status', 'Kultur Jamur R2 Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_sdar2_note', 'Kultur Jamur R2 Catatan'),
+ $select('media_sdai1_status', 'Kultur Jamur I1 Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_sdai1_note', 'Kultur Jamur I1 Catatan'),
+ $select('media_sdai2_status', 'Kultur Jamur I2 Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_sdai2_note', 'Kultur Jamur I2 Catatan'),
+ $select('media_sellainnya_status', 'Media Selektif Lainnya Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_sellainnya_note', 'Media Selektif Lainnya Catatan'),
+ $select('media_tamlainnya_status', 'Pemeriksaan Tambahan Lainnya Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_tamlainnya_note', 'Pemeriksaan Tambahan Lainnya Catatan'),
+ $text('bakteri', 'Vitek - Bakteri'),
+ $select('bakterisir', 'Vitek - SIR', $sirOptions),
+ $select('id_bakterihitungkol', 'Vitek - Hitung Koloni', $colonyOptions),
+ $text('id_bakterihitungkolteks', 'Vitek - Hitung Koloni Lainnya'),
+ $select('id_baktericetak', 'Vitek - Cetak', $printOptions),
+ $text('id_bakteri01', 'MALDITOF - Bakteri'),
+ $text('id_antibiotikmanual1', 'MALDITOF - Set Antibiotik'),
+ $select('id_bakterisir01', 'MALDITOF - SIR', $sirOptions),
+ $select('id_bakterihitungkol01', 'MALDITOF - Hitung Koloni', $colonyOptions),
+ $text('id_bakterihitungkolteks01', 'MALDITOF - Hitung Koloni Lainnya'),
+ $select('id_bakteri01cetak', 'MALDITOF - Cetak', $printOptions),
+ $text('id_bakteri02', 'Bakteri Manual I - Bakteri'),
+ $text('id_antibiotikmanual2', 'Bakteri Manual I - Set Antibiotik'),
+ $select('id_bakterisir02', 'Bakteri Manual I - SIR', $sirOptions),
+ $select('id_bakterihitungkol02', 'Bakteri Manual I - Hitung Koloni', $colonyOptions),
+ $text('id_bakterihitungkolteks02', 'Bakteri Manual I - Hitung Koloni Lainnya'),
+ $select('id_bakteri02cetak', 'Bakteri Manual I - Cetak', $printOptions),
+ $area('keterangan', 'Keterangan Expertise'),
+ ],
+ 'Pewarna Langsung' => [
+ $select('lsg_selepitel', 'Sel Epitel', $organismOptions),
+ $select('lsg_selradang', 'Sel Radang', $organismOptions),
+ $select('lsg_mikroorganisme', 'Mikroorganisme', $organismOptions),
+ $multi('lsg_mikroorganismeoptional', 'Mikroorganisme Tambahan', ['Gram positif', 'Gram negatif', 'Yeast cell', 'Basil', 'Kokus']),
+ $select('lsg_pewarnaanziehlnielsen', 'Pewarnaan Ziehl Neelsen Pagi', $organismOptions),
+ $select('lsg_pewarnaanziehlnielsensewaktu', 'Pewarnaan Ziehl Neelsen Sewaktu', $organismOptions),
+ $select('lsg_pewarnaankoh', 'Pewarnaan KOH', ['Tidak ditemukan', 'Ditemukan', 'lainnya']),
+ $text('lsg_pewarnaankohoptional', 'Pewarnaan KOH Lainnya'),
+ $select('lsg_pewarnaanneisser', 'Pewarnaan Neisser', $organismOptions),
+ $select('lsg_pewarnaannegatif', 'Pewarnaan Negatif', $organismOptions),
+ $select('lsg_pewarnaangiesma', 'Pewarnaan Giemsa', ['Tidak ditemukan', 'Ditemukan', 'lainnya']),
+ $text('lsg_pewarnaangiesmaoptional', 'Pewarnaan Giemsa Lainnya'),
+ $select('lsg_pewarnaanspora', 'Pewarnaan Spora', $organismOptions),
+ $area('lsg_pewarnaanlain', 'Pewarnaan Lain'),
+ $select('lsg_jumlahlactobacillus', 'Lactobacillus - Jumlah Per Lapang', $organismOptions),
+ $select('lsg_lactobacillus', 'Lactobacillus - Skor', ['0', '1', '2', '3', '4']),
+ $select('lsg_jumlahgardnerella', 'Gardnerella - Jumlah Per Lapang', $organismOptions),
+ $select('lsg_gardnerella', 'Gardnerella - Skor', ['0', '1', '2', '3', '4']),
+ $select('lsg_jumlahmobiluncus', 'Mobiluncus - Jumlah Per Lapang', $organismOptions),
+ $select('lsg_mobiluncus', 'Mobiluncus - Skor', ['0', '1', '2', '3', '4']),
+ $select('media_bap_status', 'Media BAP Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_bap_note', 'Media BAP Catatan'),
+ $select('media_cap_status', 'Media CAP Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_cap_note', 'Media CAP Catatan'),
+ $select('media_mcconkey_status', 'Media Mc Conkey Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_mcconkey_note', 'Media Mc Conkey Catatan'),
+ $select('media_sdar1_status', 'Kultur Jamur R1 Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_sdar1_note', 'Kultur Jamur R1 Catatan'),
+ $select('media_sdar2_status', 'Kultur Jamur R2 Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_sdar2_note', 'Kultur Jamur R2 Catatan'),
+ $select('media_sdai1_status', 'Kultur Jamur I1 Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_sdai1_note', 'Kultur Jamur I1 Catatan'),
+ $select('media_sdai2_status', 'Kultur Jamur I2 Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_sdai2_note', 'Kultur Jamur I2 Catatan'),
+ $select('media_sellainnya_status', 'Media Selektif Lainnya Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_sellainnya_note', 'Media Selektif Lainnya Catatan'),
+ $select('media_tamlainnya_status', 'Pemeriksaan Tambahan Lainnya Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_tamlainnya_note', 'Pemeriksaan Tambahan Lainnya Catatan'),
+ $text('bakteri', 'Vitek - Bakteri'),
+ $select('bakterisir', 'Vitek - SIR', $sirOptions),
+ $select('id_bakterihitungkol', 'Vitek - Hitung Koloni', $colonyOptions),
+ $text('id_bakterihitungkolteks', 'Vitek - Hitung Koloni Lainnya'),
+ $select('id_baktericetak', 'Vitek - Cetak', $printOptions),
+ $text('id_bakteri01', 'MALDITOF - Bakteri'),
+ $text('id_antibiotikmanual1', 'MALDITOF - Set Antibiotik'),
+ $select('id_bakterisir01', 'MALDITOF - SIR', $sirOptions),
+ $select('id_bakterihitungkol01', 'MALDITOF - Hitung Koloni', $colonyOptions),
+ $text('id_bakterihitungkolteks01', 'MALDITOF - Hitung Koloni Lainnya'),
+ $select('id_bakteri01cetak', 'MALDITOF - Cetak', $printOptions),
+ $text('id_bakteri02', 'Bakteri Manual I - Bakteri'),
+ $text('id_antibiotikmanual2', 'Bakteri Manual I - Set Antibiotik'),
+ $select('id_bakterisir02', 'Bakteri Manual I - SIR', $sirOptions),
+ $select('id_bakterihitungkol02', 'Bakteri Manual I - Hitung Koloni', $colonyOptions),
+ $text('id_bakterihitungkolteks02', 'Bakteri Manual I - Hitung Koloni Lainnya'),
+ $select('id_bakteri02cetak', 'Bakteri Manual I - Cetak', $printOptions),
+ $area('keterangan', 'Keterangan Expertise'),
+ ],
+ 'TBC' => [
+ $multi('jenis_pasien_tbc', 'Jenis Terduga / Pasien TBC', ['TBC SO', 'TBC RO', 'Anak', 'HIV', 'DM']),
+ $text('id_noidsediaan', 'No. Identitas Sediaan'),
+ $text('id_tanggalpengambilancontohuji', 'Tanggal Pengambilan Contoh Uji'),
+ $text('id_tanggalpengirimancontohuji', 'Tanggal Pengiriman Contoh Uji'),
+ $select('id_lamariwayatpengobatantb', 'Lama Riwayat Pengobatan TBC', ['Riwayat Pengobatan >= 5 tahun', 'Riwayat Pengobatan < 5 tahun']),
+ $select('id_jenispemeriksaantb', 'Jenis Pemeriksaan', ['Mikroskopis', 'TCM MTB Rif (Xpert)', 'TCM XDR (Xpert)', 'TCM MTB Rif (Truenat)', 'TCM MTB Rif INH (BDMAX)', 'PCR Open System', 'LPA lini 1', 'LPA lini 2', 'Biakan', 'Paket standar uji kepekaan']),
+ $select('id_lokasianatomi', 'Lokasi Anatomi', ['Paru', 'Ekstraparu']),
+ $text('id_ekstraparu', 'Ekstraparu'),
+ $select('id_alasanpemeriksaan', 'Alasan Pemeriksaan', ['Diagnosis TBC', 'Diagnosis Baseline TBC', 'Akhir Pengobatan']),
+ $text('id_bulankemajuantbfollow', 'Pemantauan kemajuan pengobatan'),
+ $text('id_bulanpemeriksaanulang', 'Pemeriksaan ulang'),
+ $text('id_bulanpemeriksaansetelahselesai', 'Pemeriksaan setelah selesai pengobatan'),
+ $text('id_noregfasyankes', 'No.Reg.TBC/TBC RO Fasyankes'),
+ $text('id_noregkota', 'No.Reg.TBC/TBC RO Kab/Kota'),
+ $select('id_contohuji', 'Contoh Uji', ['Dahak', 'Lainnya']),
+ $text('id_contohujilainnya', 'Contoh Uji Lainnya'),
+ $multi('visual_dahak_sewaktu', 'Visual dahak Sewaktu', ['Nanah Lendir', 'Bercak darah', 'Air liur']),
+ $multi('visual_dahak_pagi', 'Visual dahak Pagi', ['Nanah Lendir', 'Bercak darah', 'Air liur']),
+ $text('hasilpemeriksaantmikroskopis_tglsewaktu', 'Tanggal Mikroskopis Sewaktu'),
+ $select('hasilpemeriksaantmikroskopis_hasilsewaktu', 'Mikroskopis Sewaktu', $tbResult),
+ $text('hasilpemeriksaantmikroskopis_tglpagi', 'Tanggal Mikroskopis Pagi'),
+ $select('hasilpemeriksaantmikroskopis_hasilpagi', 'Mikroskopis Pagi', $tbResult),
+ $text('hasilpemeriksaanttcmmtbrifxpert_tglsewaktu', 'Tanggal TCM MTB Rif Sewaktu'),
+ $text('hasilpemeriksaanttcmmtbrifxpert_kodeunik', 'Kode Unik TCM MTB Rif Sewaktu'),
+ $select('hasilpemeriksaanttcmmtbrifxpert_hasilsewaktu', 'TCM MTB Rif Sewaktu', ['Neg', 'Rif Sen', 'Rif Res', 'Rif Indet', 'Invalid', 'Error', 'No result']),
+ $text('hasilpemeriksaanttcmmtbrifxpert_tglpagi', 'Tanggal TCM MTB Rif Pagi'),
+ $text('hasilpemeriksaanttcmmtbrifxpert_kodeunikpagi', 'Kode Unik TCM MTB Rif Pagi'),
+ $select('hasilpemeriksaanttcmmtbrifxpert_hasilpagi', 'TCM MTB Rif Pagi', ['Neg', 'Rif Sen', 'Rif Res', 'Rif Indet', 'Invalid', 'Error', 'No result']),
+ $text('hasilpemeriksaantbbiakan_tgl', 'Tanggal Mikroskopis Biakan'),
+ $select('hasilpemeriksaantbbiakan_pagi', 'Biakan Mikroskopis Pagi', $tbResult),
+ $select('hasilpemeriksaantbbiakan_sewaktu', 'Biakan Mikroskopis Sewaktu', $tbResult),
+ $text('hasilpemeriksaantbbiakan_tglbiakan', 'Tanggal Biakan'),
+ $select('hasilpemeriksaantbbiakan_hasilbiakanmgit', 'Hasil Biakan MGIT', ['Pos', 'Neg', 'NTM', 'KTM', 'TDL']),
+ $select('hasilpemeriksaantbbiakan_hasilbiakanlj', 'Hasil Biakan LJ', ['3+', '2+', '1+', '1-9', 'Neg', 'NTM', 'KTM', 'TDL']),
+ $text('hasilpemeriksaanttcmmtbxdrxpert_tglpagi', 'Tanggal TCM XDR Pagi'),
+ $text('hasilpemeriksaanttcmmtbxdrxpert_kodeunikpagi', 'Kode Unik TCM XDR Pagi'),
+ $text('hasilpemeriksaanttcmmtbxdrxpert_tglsewaktu', 'Tanggal TCM XDR Sewaktu'),
+ $text('hasilpemeriksaanttcmmtbxdrxpert_kodeunik', 'Kode Unik TCM XDR Sewaktu'),
+ $select('hasilpemeriksaanttcmmtbxdrxpert_pagi', 'TCM XDR Pagi', ['Valid', 'Invalid', 'No Result', 'Error']),
+ $select('hasilpemeriksaanttcmmtbxdrxpert_pagimtb', 'MTB Pagi', ['Detected', 'Not Detected']),
+ $select('hasilpemeriksaanttcmmtbxdrxpert_paginhlow', 'H Low Pagi', ['S', 'I', 'R', 'Invalid', 'Error', 'No Result']),
+ $select('hasilpemeriksaanttcmmtbxdrxpert_pagih', 'H Pagi', ['S', 'I', 'R', 'Invalid', 'Error', 'No Result']),
+ $select('hasilpemeriksaanttcmmtbxdrxpert_pagifqlow', 'FQ Low Pagi', ['S', 'I', 'R', 'Invalid', 'Error', 'No Result']),
+ $select('hasilpemeriksaanttcmmtbxdrxpert_pagifq', 'FQ Pagi', ['S', 'I', 'R', 'Invalid', 'Error', 'No Result']),
+ $select('hasilpemeriksaanttcmmtbxdrxpert_pagiamk', 'Amk Pagi', ['S', 'I', 'R', 'Invalid', 'Error', 'No Result']),
+ $select('hasilpemeriksaanttcmmtbxdrxpert_pagikm', 'Km Pagi', ['S', 'I', 'R', 'Invalid', 'Error', 'No Result']),
+ $select('hasilpemeriksaanttcmmtbxdrxpert_pagicm', 'Cm Pagi', ['S', 'I', 'R', 'Invalid', 'Error', 'No Result']),
+ $select('hasilpemeriksaanttcmmtbxdrxpert_pagieto', 'Eto Pagi', ['S', 'I', 'R', 'Invalid', 'Error', 'No Result']),
+ $select('hasilpemeriksaanttcmmtbxdrxpert_sewaktu', 'TCM XDR Sewaktu', ['Valid', 'Invalid', 'No Result', 'Error']),
+ $select('hasilpemeriksaanttcmmtbxdrxpert_sewaktumtb', 'MTB Sewaktu', ['Detected', 'Not Detected']),
+ $select('hasilpemeriksaanttcmmtbxdrxpert_sewaktunhlow', 'H Low Sewaktu', ['S', 'I', 'R', 'Invalid', 'Error', 'No Result']),
+ $select('hasilpemeriksaanttcmmtbxdrxpert_sewaktuh', 'H Sewaktu', ['S', 'I', 'R', 'Invalid', 'Error', 'No Result']),
+ $select('hasilpemeriksaanttcmmtbxdrxpert_sewaktufqlow', 'FQ Low Sewaktu', ['S', 'I', 'R', 'Invalid', 'Error', 'No Result']),
+ $select('hasilpemeriksaanttcmmtbxdrxpert_sewaktufq', 'FQ Sewaktu', ['S', 'I', 'R', 'Invalid', 'Error', 'No Result']),
+ $select('hasilpemeriksaanttcmmtbxdrxpert_sewaktuamk', 'Amk Sewaktu', ['S', 'I', 'R', 'Invalid', 'Error', 'No Result']),
+ $select('hasilpemeriksaanttcmmtbxdrxpert_sewaktukm', 'Km Sewaktu', ['S', 'I', 'R', 'Invalid', 'Error', 'No Result']),
+ $select('hasilpemeriksaanttcmmtbxdrxpert_sewaktucm', 'Cm Sewaktu', ['S', 'I', 'R', 'Invalid', 'Error', 'No Result']),
+ $select('hasilpemeriksaanttcmmtbxdrxpert_sewaktueto', 'Eto Sewaktu', ['S', 'I', 'R', 'Invalid', 'Error', 'No Result']),
+ $select('media_bap_status', 'Media BAP Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_bap_note', 'Media BAP Catatan'),
+ $select('media_cap_status', 'Media CAP Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_cap_note', 'Media CAP Catatan'),
+ $select('media_mcconkey_status', 'Media Mc Conkey Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_mcconkey_note', 'Media Mc Conkey Catatan'),
+ $select('media_sdar1_status', 'Kultur Jamur R1 Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_sdar1_note', 'Kultur Jamur R1 Catatan'),
+ $select('media_sdar2_status', 'Kultur Jamur R2 Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_sdar2_note', 'Kultur Jamur R2 Catatan'),
+ $select('media_sdai1_status', 'Kultur Jamur I1 Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_sdai1_note', 'Kultur Jamur I1 Catatan'),
+ $select('media_sdai2_status', 'Kultur Jamur I2 Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_sdai2_note', 'Kultur Jamur I2 Catatan'),
+ $select('media_sellainnya_status', 'Media Selektif Lainnya Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_sellainnya_note', 'Media Selektif Lainnya Catatan'),
+ $select('media_tamlainnya_status', 'Pemeriksaan Tambahan Lainnya Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_tamlainnya_note', 'Pemeriksaan Tambahan Lainnya Catatan'),
+ $text('bakteri', 'Vitek - Bakteri'),
+ $select('bakterisir', 'Vitek - SIR', $sirOptions),
+ $select('id_bakterihitungkol', 'Vitek - Hitung Koloni', $colonyOptions),
+ $text('id_bakterihitungkolteks', 'Vitek - Hitung Koloni Lainnya'),
+ $select('id_baktericetak', 'Vitek - Cetak', $printOptions),
+ $text('id_bakteri01', 'MALDITOF - Bakteri'),
+ $text('id_antibiotikmanual1', 'MALDITOF - Set Antibiotik'),
+ $select('id_bakterisir01', 'MALDITOF - SIR', $sirOptions),
+ $select('id_bakterihitungkol01', 'MALDITOF - Hitung Koloni', $colonyOptions),
+ $text('id_bakterihitungkolteks01', 'MALDITOF - Hitung Koloni Lainnya'),
+ $select('id_bakteri01cetak', 'MALDITOF - Cetak', $printOptions),
+ $text('id_bakteri02', 'Bakteri Manual I - Bakteri'),
+ $text('id_antibiotikmanual2', 'Bakteri Manual I - Set Antibiotik'),
+ $select('id_bakterisir02', 'Bakteri Manual I - SIR', $sirOptions),
+ $select('id_bakterihitungkol02', 'Bakteri Manual I - Hitung Koloni', $colonyOptions),
+ $text('id_bakterihitungkolteks02', 'Bakteri Manual I - Hitung Koloni Lainnya'),
+ $select('id_bakteri02cetak', 'Bakteri Manual I - Cetak', $printOptions),
+ $area('keterangan', 'Keterangan Expertise'),
+ ],
+ 'Viral Load' => [
+ $select('vl_parameter01', 'Parameter', ['HIV RNA', 'HCV RNA', 'HBV DNA']),
+ $select('vl_hasil01', 'Hasil', ['NOT DETECTED', '<40', '<1.60', '40', '41', '44', '45', '145', '<10', '20', '322', '511', '144', '211', 'lainnya']),
+ $text('vl_hasil01teks', 'Hasil Lainnya'),
+ $select('vl_satuan01', 'Satuan', ['copies/mL', 'IU/mL']),
+ $select('vl_rujukan01', 'Nilai Rujukan', ['40-10.000.000', '10-100.000.000']),
+ $select('vl_parameter02', 'Parameter 02', ['LOG']),
+ $select('vl_hasil02', 'Hasil 02', ['NOT DETECTED', '<1,60', '<1,85', '1,60', '1,61', '1,64', '1,65', '2,16', '1,00', '1,30', '2,51', '2,71', '2,34', 'lainnya']),
+ $text('vl_hasil02teks', 'Hasil 02 Lainnya'),
+ $select('vl_satuan02', 'Satuan 02', ['LOG']),
+ $select('vl_rujukan02', 'Nilai Rujukan 02', ['1,6-7', ' ']),
+ $area('viralload', 'Catatan Viral Load'),
+ $select('media_bap_status', 'Media BAP Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_bap_note', 'Media BAP Catatan'),
+ $select('media_cap_status', 'Media CAP Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_cap_note', 'Media CAP Catatan'),
+ $select('media_mcconkey_status', 'Media Mc Conkey Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_mcconkey_note', 'Media Mc Conkey Catatan'),
+ $select('media_sdar1_status', 'Kultur Jamur R1 Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_sdar1_note', 'Kultur Jamur R1 Catatan'),
+ $select('media_sdar2_status', 'Kultur Jamur R2 Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_sdar2_note', 'Kultur Jamur R2 Catatan'),
+ $select('media_sdai1_status', 'Kultur Jamur I1 Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_sdai1_note', 'Kultur Jamur I1 Catatan'),
+ $select('media_sdai2_status', 'Kultur Jamur I2 Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_sdai2_note', 'Kultur Jamur I2 Catatan'),
+ $select('media_sellainnya_status', 'Media Selektif Lainnya Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_sellainnya_note', 'Media Selektif Lainnya Catatan'),
+ $select('media_tamlainnya_status', 'Pemeriksaan Tambahan Lainnya Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_tamlainnya_note', 'Pemeriksaan Tambahan Lainnya Catatan'),
+ $text('bakteri', 'Vitek - Bakteri'),
+ $select('bakterisir', 'Vitek - SIR', $sirOptions),
+ $select('id_bakterihitungkol', 'Vitek - Hitung Koloni', $colonyOptions),
+ $text('id_bakterihitungkolteks', 'Vitek - Hitung Koloni Lainnya'),
+ $select('id_baktericetak', 'Vitek - Cetak', $printOptions),
+ $text('id_bakteri01', 'MALDITOF - Bakteri'),
+ $text('id_antibiotikmanual1', 'MALDITOF - Set Antibiotik'),
+ $select('id_bakterisir01', 'MALDITOF - SIR', $sirOptions),
+ $select('id_bakterihitungkol01', 'MALDITOF - Hitung Koloni', $colonyOptions),
+ $text('id_bakterihitungkolteks01', 'MALDITOF - Hitung Koloni Lainnya'),
+ $select('id_bakteri01cetak', 'MALDITOF - Cetak', $printOptions),
+ $text('id_bakteri02', 'Bakteri Manual I - Bakteri'),
+ $text('id_antibiotikmanual2', 'Bakteri Manual I - Set Antibiotik'),
+ $select('id_bakterisir02', 'Bakteri Manual I - SIR', $sirOptions),
+ $select('id_bakterihitungkol02', 'Bakteri Manual I - Hitung Koloni', $colonyOptions),
+ $text('id_bakterihitungkolteks02', 'Bakteri Manual I - Hitung Koloni Lainnya'),
+ $select('id_bakteri02cetak', 'Bakteri Manual I - Cetak', $printOptions),
+ $area('keterangan', 'Keterangan Expertise'),
+ ],
+ 'IgM IgG Leptospira' => [
+ $select('igg_parameter', 'IgG Leptospira', $yn),
+ $select('igm_parameter', 'IgM Leptospira', $yn),
+ $area('iggigm_interpretasi', 'Interpretasi'),
+ $select('media_bap_status', 'Media BAP Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_bap_note', 'Media BAP Catatan'),
+ $select('media_cap_status', 'Media CAP Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_cap_note', 'Media CAP Catatan'),
+ $select('media_mcconkey_status', 'Media Mc Conkey Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_mcconkey_note', 'Media Mc Conkey Catatan'),
+ $select('media_sdar1_status', 'Kultur Jamur R1 Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_sdar1_note', 'Kultur Jamur R1 Catatan'),
+ $select('media_sdar2_status', 'Kultur Jamur R2 Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_sdar2_note', 'Kultur Jamur R2 Catatan'),
+ $select('media_sdai1_status', 'Kultur Jamur I1 Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_sdai1_note', 'Kultur Jamur I1 Catatan'),
+ $select('media_sdai2_status', 'Kultur Jamur I2 Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_sdai2_note', 'Kultur Jamur I2 Catatan'),
+ $select('media_sellainnya_status', 'Media Selektif Lainnya Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_sellainnya_note', 'Media Selektif Lainnya Catatan'),
+ $select('media_tamlainnya_status', 'Pemeriksaan Tambahan Lainnya Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_tamlainnya_note', 'Pemeriksaan Tambahan Lainnya Catatan'),
+ $text('bakteri', 'Vitek - Bakteri'),
+ $select('bakterisir', 'Vitek - SIR', $sirOptions),
+ $select('id_bakterihitungkol', 'Vitek - Hitung Koloni', $colonyOptions),
+ $text('id_bakterihitungkolteks', 'Vitek - Hitung Koloni Lainnya'),
+ $select('id_baktericetak', 'Vitek - Cetak', $printOptions),
+ $text('id_bakteri01', 'MALDITOF - Bakteri'),
+ $text('id_antibiotikmanual1', 'MALDITOF - Set Antibiotik'),
+ $select('id_bakterisir01', 'MALDITOF - SIR', $sirOptions),
+ $select('id_bakterihitungkol01', 'MALDITOF - Hitung Koloni', $colonyOptions),
+ $text('id_bakterihitungkolteks01', 'MALDITOF - Hitung Koloni Lainnya'),
+ $select('id_bakteri01cetak', 'MALDITOF - Cetak', $printOptions),
+ $text('id_bakteri02', 'Bakteri Manual I - Bakteri'),
+ $text('id_antibiotikmanual2', 'Bakteri Manual I - Set Antibiotik'),
+ $select('id_bakterisir02', 'Bakteri Manual I - SIR', $sirOptions),
+ $select('id_bakterihitungkol02', 'Bakteri Manual I - Hitung Koloni', $colonyOptions),
+ $text('id_bakterihitungkolteks02', 'Bakteri Manual I - Hitung Koloni Lainnya'),
+ $select('id_bakteri02cetak', 'Bakteri Manual I - Cetak', $printOptions),
+ $area('keterangan', 'Keterangan Expertise'),
+ ],
+ 'PCR COVID' => [
+ $select('covid_virus01', 'Virus SARS-Cov-2', ['Positif', 'Negatif', 'Tidak Diperiksa']),
+ $text('covid_satuanvirus01', 'Satuan SARS-Cov-2'),
+ $text('covid_rujukanvirus01', 'Nilai Rujukan SARS-Cov-2'),
+ $select('covid_virus02', 'Influenza A', ['Positif', 'Negatif', 'Tidak Diperiksa']),
+ $text('covid_satuanvirus02', 'Satuan Influenza A'),
+ $text('covid_rujukanvirus02', 'Nilai Rujukan Influenza A'),
+ $select('covid_virus03', 'Influenza B', ['Positif', 'Negatif', 'Tidak Diperiksa']),
+ $text('covid_satuanvirus03', 'Satuan Influenza B'),
+ $text('covid_rujukanvirus03', 'Nilai Rujukan Influenza B'),
+ $select('covid_virus04', 'RSV', ['Positif', 'Negatif', 'Tidak Diperiksa']),
+ $text('covid_satuanvirus04', 'Satuan RSV'),
+ $text('covid_rujukanvirus04', 'Nilai Rujukan RSV'),
+ $area('covid_interpretasi', 'Interpretasi'),
+ $select('media_bap_status', 'Media BAP Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_bap_note', 'Media BAP Catatan'),
+ $select('media_cap_status', 'Media CAP Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_cap_note', 'Media CAP Catatan'),
+ $select('media_mcconkey_status', 'Media Mc Conkey Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_mcconkey_note', 'Media Mc Conkey Catatan'),
+ $select('media_sdar1_status', 'Kultur Jamur R1 Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_sdar1_note', 'Kultur Jamur R1 Catatan'),
+ $select('media_sdar2_status', 'Kultur Jamur R2 Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_sdar2_note', 'Kultur Jamur R2 Catatan'),
+ $select('media_sdai1_status', 'Kultur Jamur I1 Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_sdai1_note', 'Kultur Jamur I1 Catatan'),
+ $select('media_sdai2_status', 'Kultur Jamur I2 Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_sdai2_note', 'Kultur Jamur I2 Catatan'),
+ $select('media_sellainnya_status', 'Media Selektif Lainnya Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_sellainnya_note', 'Media Selektif Lainnya Catatan'),
+ $select('media_tamlainnya_status', 'Pemeriksaan Tambahan Lainnya Status', ['Ada Pertumbuhan', 'Tidak Ada Pertumbuhan', 'Pertumbuhan Primer']),
+ $text('media_tamlainnya_note', 'Pemeriksaan Tambahan Lainnya Catatan'),
+ $text('bakteri', 'Vitek - Bakteri'),
+ $select('bakterisir', 'Vitek - SIR', $sirOptions),
+ $select('id_bakterihitungkol', 'Vitek - Hitung Koloni', $colonyOptions),
+ $text('id_bakterihitungkolteks', 'Vitek - Hitung Koloni Lainnya'),
+ $select('id_baktericetak', 'Vitek - Cetak', $printOptions),
+ $text('id_bakteri01', 'MALDITOF - Bakteri'),
+ $text('id_antibiotikmanual1', 'MALDITOF - Set Antibiotik'),
+ $select('id_bakterisir01', 'MALDITOF - SIR', $sirOptions),
+ $select('id_bakterihitungkol01', 'MALDITOF - Hitung Koloni', $colonyOptions),
+ $text('id_bakterihitungkolteks01', 'MALDITOF - Hitung Koloni Lainnya'),
+ $select('id_bakteri01cetak', 'MALDITOF - Cetak', $printOptions),
+ $text('id_bakteri02', 'Bakteri Manual I - Bakteri'),
+ $text('id_antibiotikmanual2', 'Bakteri Manual I - Set Antibiotik'),
+ $select('id_bakterisir02', 'Bakteri Manual I - SIR', $sirOptions),
+ $select('id_bakterihitungkol02', 'Bakteri Manual I - Hitung Koloni', $colonyOptions),
+ $text('id_bakterihitungkolteks02', 'Bakteri Manual I - Hitung Koloni Lainnya'),
+ $select('id_bakteri02cetak', 'Bakteri Manual I - Cetak', $printOptions),
+ $area('keterangan', 'Keterangan Expertise'),
+ ],
+ default => [
+ $area('keterangan', 'Keterangan Expertise'),
+ ],
+ };
+ }
+
public function earlyWarning(Request $request)
{
$this->requireUser($request);
diff --git a/htdocs/resources/views/dokter/ppdsdeveloper.blade.php b/htdocs/resources/views/dokter/ppdsdeveloper.blade.php
index fc342260..ea0dc7b5 100644
--- a/htdocs/resources/views/dokter/ppdsdeveloper.blade.php
+++ b/htdocs/resources/views/dokter/ppdsdeveloper.blade.php
@@ -772,7 +772,7 @@
- C. Tes Kepekaan Antibiotika: (S: Sensitif ; I : Intermediate ; R: Resisten)
+ C. Tes Kepekaan Antibiotik: (S: Sensitif ; I : Intermediate ; R: Resisten)
diff --git a/htdocs/routes/api.php b/htdocs/routes/api.php
index e01ad440..f2a27231 100644
--- a/htdocs/routes/api.php
+++ b/htdocs/routes/api.php
@@ -26,6 +26,9 @@ Route::prefix('mobile')->group(function () {
Route::get('early-warning', [MobileApiController::class, 'earlyWarning']);
Route::get('books/{master}/examinations', [MobileApiController::class, 'examinations']);
Route::get('examinations/{id}', [MobileApiController::class, 'examination']);
+ Route::get('examinations/{id}/expertise', [MobileApiController::class, 'expertise']);
+ Route::post('examinations/{id}/expertise/template', [MobileApiController::class, 'setExpertiseTemplate']);
+ Route::post('examinations/{id}/expertise', [MobileApiController::class, 'saveExpertise']);
});
Route::group([
diff --git a/listener/geneexpert.py b/listener/geneexpert.py
index fd54029c..431e81e9 100644
--- a/listener/geneexpert.py
+++ b/listener/geneexpert.py
@@ -135,40 +135,40 @@ TARGET_MAPPING = {
# Kanan: 'Host Test Code' dari Dokumen Word Anda
GENEXPERT_TEST_MAPPING = {
# Mapping untuk IP 10.10.120.75 (Multi-Assay)
- "HIV": "HIV1-VL", # Xpert HIV-1 Viral Load XC Version 3
- "TCM TB": "MTBRIF", # Xpert MTBRIF Assay G4 Version 6
- "TCM TB ULTRA": "MTBRIF", # Xpert MTBRIF Ultra Version 4
- "TCM TB XDR": "MTB-XDR", # Xpert MTB-XDR Version 1
- "HCV VL": "HCV", # Xpert HCV Viral Load Version 1
- "COVID-19": "SARSCOV2FLURSV", # Xpert Xpress SARS-CoV-2 Version 2
- "17.3.1 TCM COVID-19": "SARSCOV2FLURSV", # Xpert Xpress SARS-CoV-2 Version 2
- "17.3.2 PCR COVID-19": "SARSCOV2FLURSV", # Xpert Xpress SARS-CoV-2 Version 2
+ "HIV": "HIV1-VL",
+ "TCM TB": "MTBRIF",
+ "TCM TB ULTRA": "MTBRIF",
+ "TCM TB XDR": "MTB-XDR 2",
+ "HCV VL": "HCV",
+ "COVID-19": "SARSCOV2FLURSV",
+ "17.3.1 TCM COVID-19": "SARSCOV2FLURSV",
+ "17.3.2 PCR COVID-19": "SARSCOV2FLURSV",
"E.2.5 HCV TCM": "HCV",
"18.1.1 TCM HCV": "HCV",
"18.1.2 TCM HIV VIRAL LOAD": "HIV1-VL",
"18.1.4 TCM HPV": "HCV",
"7.3.7 KULTUR TBC MGIT (AUTOMATIC)": "MTBRIF",
"5.3.8 KULTUR TBC MGIT (AUTOMATIC)": "MTBRIF",
- "5.3.7 KULTUR TBC MEDIA LJ (KONVENSIONAL)": "MTB-XDR",
- "3.3.6 KULTUR TBC MEDIA LJ (KONVENSIONAL) ": "MTB-XDR",
- "2.3.7 KULTUR TBC MEDIA LJ (KONVENSIONAL)": "MTB-XDR",
- "1.3.6 KULTUR TBC MEDIA LJ (KONVENSIONAL)": "MTB-XDR",
- "1.3.7 KULTUR TBC MGIT (AUTOMATIC)": "MTB-XDR",
- "15.2.1 KULTUR TB MEDIA LJ": "MTB-XDR",
- "8.3.7 KULTUR TBC MGIT (AUTOMATIC)": "MTB-XDR",
- "9.3.5 KULTUR TBC MEDIA LJ (KONVENSIONAL)": "MTB-XDR",
- "9.3.6 KULTUR TBC MGIT (AUTOMATIC)": "MTB-XDR",
- "12.3.7 KULTUR TBC MGIT (AUTOMATIC)": "MTB-XDR",
- "H.2.5 PEMERIKSAAN KULTUR MYCROBACTERIUM TBC": "MTB-XDR",
- "12.3.6 KULTUR TBC MEDIA LJ (KONVENSIONAL)": "MTB-XDR",
- "11.3.6 KULTUR TBC MEDIA LJ (KONVENSIONAL)": "MTB-XDR",
- "10.3.7 KULTUR TBC MGIT (AUTOMATIC)": "MTB-XDR",
- "10.3.6 KULTUR TBC MEDIA LJ (KONVENSIONAL)": "MTB-XDR",
- "3.3.7 KULTUR TBC MGIT (AUTOMATIC)": "MTB-XDR",
- "15.2.2 KULTUR TB MEDIA MGIT (AUTOMATIC)": "MTB-XDR",
- "11.3.7 KULTUR TBC MGIT (AUTOMATIC)": "MTB-XDR",
- "8.3.6 KULTUR TBC MEDIA LJ (KONVENSIONAL)": "MTB-XDR",
- "7.3.6 KULTUR TBC MEDIA LJ (KONVENSIONAL)": "MTB-XDR",
+ "5.3.7 KULTUR TBC MEDIA LJ (KONVENSIONAL)": "MTB-XDR 2",
+ "3.3.6 KULTUR TBC MEDIA LJ (KONVENSIONAL) ": "MTB-XDR 2",
+ "2.3.7 KULTUR TBC MEDIA LJ (KONVENSIONAL)": "MTB-XDR 2",
+ "1.3.6 KULTUR TBC MEDIA LJ (KONVENSIONAL)": "MTB-XDR 2",
+ "1.3.7 KULTUR TBC MGIT (AUTOMATIC)": "MTB-XDR 2",
+ "15.2.1 KULTUR TB MEDIA LJ": "MTB-XDR 2",
+ "8.3.7 KULTUR TBC MGIT (AUTOMATIC)": "MTB-XDR 2",
+ "9.3.5 KULTUR TBC MEDIA LJ (KONVENSIONAL)": "MTB-XDR 2",
+ "9.3.6 KULTUR TBC MGIT (AUTOMATIC)": "MTB-XDR 2",
+ "12.3.7 KULTUR TBC MGIT (AUTOMATIC)": "MTB-XDR 2",
+ "H.2.5 PEMERIKSAAN KULTUR MYCROBACTERIUM TBC": "MTB-XDR 2",
+ "12.3.6 KULTUR TBC MEDIA LJ (KONVENSIONAL)": "MTB-XDR 2",
+ "11.3.6 KULTUR TBC MEDIA LJ (KONVENSIONAL)": "MTB-XDR 2",
+ "10.3.7 KULTUR TBC MGIT (AUTOMATIC)": "MTB-XDR 2",
+ "10.3.6 KULTUR TBC MEDIA LJ (KONVENSIONAL)": "MTB-XDR 2",
+ "3.3.7 KULTUR TBC MGIT (AUTOMATIC)": "MTB-XDR 2",
+ "15.2.2 KULTUR TB MEDIA MGIT (AUTOMATIC)": "MTB-XDR 2",
+ "11.3.7 KULTUR TBC MGIT (AUTOMATIC)": "MTB-XDR 2",
+ "8.3.6 KULTUR TBC MEDIA LJ (KONVENSIONAL)": "MTB-XDR 2",
+ "7.3.6 KULTUR TBC MEDIA LJ (KONVENSIONAL)": "MTB-XDR 2",
"2.3.10 TCM CLAMIDIA TRACHOMATIS / NEISSERIA GONORRHOE": "MTBRIF",
"12.3.8 TCM TB (GENE EXPERT)": "MTBRIF",
"15.2.3 TCM TB (GENE EXPERT)": "MTBRIF",
@@ -183,11 +183,10 @@ GENEXPERT_TEST_MAPPING = {
}
GENEXPERT_IP_CAPABILITIES = {
- "10.10.120.75": ["MTBRIF", "HBVVL", "HIV1-VL", "MTB-XDR", "HCV VL", "SARSCOV2FLURSV"],
- "10.10.120.108": ["HCV", "HIV", "HBV"],
- "10.10.120.73": ["MTBRIF"]
+ "10.10.120.75": ["MTBRIF", "HBVVL", "HIV1-VL", "MTB-XDR 2", "HCV", "SARSCOV2FLURSV"],
+ "10.10.120.108": ["HCV", "HIV1-VL", "HBVVL"],
+ "10.10.120.73": ["MTBRIF", "HBVVL", "HIV1-VL", "HCV", "SARSCOV2FLURSV"],
}
-# Default code jika nama tes di database tidak dikenali
DEFAULT_GXP_CODE = "MTBRIF"
DEVICE_CONFIGS = [
diff --git a/mylis/README.md b/mylis/README.md
index d9003aeb..8bca9095 100644
--- a/mylis/README.md
+++ b/mylis/README.md
@@ -1,17 +1,212 @@
-# mylis
+# MyLIS Flutter
-A new Flutter project.
+MyLIS Flutter adalah aplikasi pendamping untuk Laravel LIS lokal. Aplikasi ini fokus pada akses mobile/desktop untuk login, dashboard Early Warning Sistem, daftar pemeriksaan, detail pemeriksaan, dan pengisian expertise per template DLP.
-## Getting Started
+Default URL Laravel lokal:
-This project is a starting point for a Flutter application.
+```text
+https://lis.swandhana.test/
+```
-A few resources to get you started if this is your first Flutter project:
+URL tersebut bisa diganti dari halaman sebelum login. Tersedia tombol `Ping` agar pengguna dapat memastikan server Laravel sudah dapat diakses sebelum login.
-- [Learn Flutter](https://docs.flutter.dev/get-started/learn-flutter)
-- [Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
-- [Flutter learning resources](https://docs.flutter.dev/reference/learning-resources)
+## Fitur Utama
-For help getting started with Flutter development, view the
-[online documentation](https://docs.flutter.dev/), which offers tutorials,
-samples, guidance on mobile development, and a full API reference.
+- Login tanpa register.
+- Pengaturan URL Laravel dari layar login.
+- Ping koneksi ke Laravel.
+- Dashboard dengan menu utama `Early Warning Sistem`.
+- Daftar pemeriksaan per buku/kategori.
+- Detail pemeriksaan dengan status dan tombol `Expertise`.
+- Screen expertise Flutter native, tidak membuka Blade Laravel.
+- Pemilihan template saat `periksa.dlp` masih kosong.
+- Penyimpanan draft, preliminary, final, dan kirim ke SPV sesuai role pengguna.
+- Dukungan build Android, iOS, dan macOS.
+
+## Alur Expertise
+
+Jika `periksa.dlp` kosong, aplikasi menampilkan pilihan template. Jika sudah terisi, aplikasi langsung membuka wizard sesuai DLP.
+
+Screen DLP yang sudah dibuat:
+
+- `CCI`
+- `Kultur`
+- `Pewarna Langsung`
+- `TBC`
+- `Viral Load`
+- `IgM IgG Leptospira`
+- `PCR COVID`
+
+Semua wizard memakai pola umum:
+
+1. Data pasien dan petugas.
+2. Isian parameter/template sesuai DLP.
+3. Tes Kepekaan Antibiotik jika berlaku.
+4. Data alat jika berlaku.
+5. Isian expertise akhir.
+
+Navigasi wizard tidak memakai validasi wajib, sehingga pengguna bebas `Next` dan `Previous`.
+
+## Role dan Tombol Simpan
+
+Untuk role supervisor/SPV:
+
+- `Save as Draft`
+- `Save Preliminary result`
+- `Save Final Result`
+
+Untuk role selain supervisor/SPV:
+
+- `Save as Draft`
+- `Save and Send To SPV`
+- `Kirim SPV Preliminary`
+
+Checkbox `Nilai Kritis` tersedia di step akhir dan dikirim ke API saat menyimpan.
+
+## Struktur Folder
+
+```text
+lib/
+ app/
+ app.dart
+ data/
+ expertise_data.dart
+ providers/
+ session_provider.dart
+ screens/
+ dashboard/
+ examinations/
+ expertise/
+ login/
+ services/
+ api_client.dart
+ session_store.dart
+ widgets/
+ common_widgets.dart
+ main.dart
+```
+
+Ringkasan:
+
+- `main.dart`: entry point aplikasi.
+- `app/app.dart`: root aplikasi dan registrasi part screen.
+- `services/api_client.dart`: komunikasi HTTP ke Laravel.
+- `services/session_store.dart`: penyimpanan token dan base URL.
+- `providers/session_provider.dart`: pengecekan session awal.
+- `screens/login`: halaman login, URL Laravel, dan ping.
+- `screens/dashboard`: dashboard dan Early Warning Sistem.
+- `screens/examinations`: daftar dan detail pemeriksaan.
+- `screens/expertise`: semua screen wizard DLP.
+- `widgets/common_widgets.dart`: komponen UI umum.
+- `data/expertise_data.dart`: daftar DLP dan label template.
+
+## API Laravel
+
+Aplikasi memakai endpoint mobile Laravel dengan prefix:
+
+```text
+/api/mobile
+```
+
+Endpoint utama:
+
+- `GET /api/mobile/ping`
+- `POST /api/mobile/login`
+- `GET /api/mobile/dashboard`
+- `GET /api/mobile/early-warning`
+- `GET /api/mobile/books/{master}/examinations`
+- `GET /api/mobile/examinations/{id}`
+- `GET /api/mobile/examinations/{id}/expertise`
+- `POST /api/mobile/examinations/{id}/expertise/template`
+- `POST /api/mobile/examinations/{id}/expertise`
+
+Controller Laravel berada di:
+
+```text
+/Users/duidev/htdocs/lis/htdocs/app/Http/Controllers/MobileApiController.php
+```
+
+Route API berada di:
+
+```text
+/Users/duidev/htdocs/lis/htdocs/routes/api.php
+```
+
+## Menjalankan Aplikasi
+
+Ambil dependency:
+
+```bash
+flutter pub get
+```
+
+Jalankan di device/simulator:
+
+```bash
+flutter run
+```
+
+Jalankan dengan default URL lain:
+
+```bash
+flutter run --dart-define=LIS_BASE_URL=https://lis.swandhana.test/
+```
+
+## Verifikasi
+
+Analisis kode Flutter:
+
+```bash
+flutter analyze
+```
+
+Test Flutter:
+
+```bash
+flutter test
+```
+
+Cek route Laravel mobile:
+
+```bash
+php artisan route:list --path=mobile
+```
+
+Cek sintaks controller Laravel:
+
+```bash
+php -l app/Http/Controllers/MobileApiController.php
+```
+
+## Build
+
+Android debug:
+
+```bash
+flutter build apk --debug
+```
+
+iOS debug tanpa code signing:
+
+```bash
+flutter build ios --debug --no-codesign
+```
+
+macOS debug:
+
+```bash
+flutter build macos --debug
+```
+
+Output build yang umum:
+
+- Android: `build/app/outputs/flutter-apk/app-debug.apk`
+- iOS: `build/ios/iphoneos/Runner.app`
+- macOS: `build/macos/Build/Products/Debug/mylis.app`
+
+## Catatan Lokal
+
+- Pastikan Laravel lokal `https://lis.swandhana.test/` aktif.
+- Pastikan sertifikat lokal dipercaya oleh device/simulator yang dipakai.
+- Untuk iOS device fisik, build `--no-codesign` tetap perlu signing manual sebelum deploy.
+- Jangan menjalankan beberapa command Flutter berat secara bersamaan karena bisa berebut file sementara pada folder platform.
diff --git a/mylis/lib/app/app.dart b/mylis/lib/app/app.dart
new file mode 100644
index 00000000..8773a8ef
--- /dev/null
+++ b/mylis/lib/app/app.dart
@@ -0,0 +1,52 @@
+import 'dart:convert';
+
+import 'package:flutter/material.dart';
+import 'package:http/http.dart' as http;
+import 'package:shared_preferences/shared_preferences.dart';
+import 'package:url_launcher/url_launcher.dart';
+
+part '../providers/session_provider.dart';
+part '../services/api_client.dart';
+part '../services/session_store.dart';
+part '../screens/login/login_screen.dart';
+part '../screens/dashboard/dashboard_screen.dart';
+part '../screens/examinations/examination_list_screen.dart';
+part '../screens/examinations/examination_detail_screen.dart';
+part '../screens/expertise/expertise_screen.dart';
+part '../screens/expertise/template_picker.dart';
+part '../screens/expertise/cci_expertise_wizard.dart';
+part '../screens/expertise/kultur_expertise_wizard.dart';
+part '../screens/expertise/pewarnaan_langsung_expertise_wizard.dart';
+part '../screens/expertise/viral_load_expertise_wizard.dart';
+part '../screens/expertise/leptospira_expertise_wizard.dart';
+part '../screens/expertise/covid_expertise_wizard.dart';
+part '../screens/expertise/tbc_expertise_wizard.dart';
+part '../screens/expertise/expertise_form.dart';
+part '../data/expertise_data.dart';
+part '../widgets/common_widgets.dart';
+
+const String kDefaultBaseUrl = String.fromEnvironment(
+ 'LIS_BASE_URL',
+ defaultValue: 'https://lis.swandhana.test/',
+);
+
+class MyLisApp extends StatelessWidget {
+ const MyLisApp({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return MaterialApp(
+ title: 'MyLIS Mobile',
+ debugShowCheckedModeBanner: false,
+ theme: ThemeData(
+ colorScheme: ColorScheme.fromSeed(
+ seedColor: const Color(0xFF0F766E),
+ brightness: Brightness.light,
+ ),
+ scaffoldBackgroundColor: const Color(0xFFF7FAF9),
+ useMaterial3: true,
+ ),
+ home: const SessionGate(),
+ );
+ }
+}
diff --git a/mylis/lib/data/expertise_data.dart b/mylis/lib/data/expertise_data.dart
new file mode 100644
index 00000000..7ba1b7df
--- /dev/null
+++ b/mylis/lib/data/expertise_data.dart
@@ -0,0 +1,29 @@
+part of '../app/app.dart';
+
+const Set
_knownDlp = {
+ 'CCI',
+ 'Kultur',
+ 'Pewarna Langsung',
+ 'TBC',
+ 'Viral Load',
+ 'IgM IgG Leptospira',
+ 'PCR COVID',
+};
+
+String _templateTitle(String dlp) {
+ return switch (dlp) {
+ 'CCI' => 'Pemeriksaan Candida Colonization Index',
+ 'Kultur' => 'Kultur',
+ 'Pewarna Langsung' => 'Pewarna Langsung',
+ 'TBC' => 'TBC',
+ 'Viral Load' => 'Viral Load',
+ 'IgM IgG Leptospira' => 'IgM IgG Leptospira',
+ 'PCR COVID' => 'PCR COVID',
+ _ => dlp,
+ };
+}
+
+bool _isSupervisor(String? role) {
+ final normalized = role?.toLowerCase().trim() ?? '';
+ return normalized.contains('supervisor') || normalized == 'spv';
+}
diff --git a/mylis/lib/main.dart b/mylis/lib/main.dart
index 845499b7..0c2eebd2 100644
--- a/mylis/lib/main.dart
+++ b/mylis/lib/main.dart
@@ -1,1274 +1,8 @@
-import 'dart:convert';
-
import 'package:flutter/material.dart';
-import 'package:http/http.dart' as http;
-import 'package:shared_preferences/shared_preferences.dart';
-import 'package:url_launcher/url_launcher.dart';
-const String kDefaultBaseUrl = String.fromEnvironment(
- 'LIS_BASE_URL',
- defaultValue: 'https://lis.swandhana.test/',
-);
+import 'app/app.dart';
+export 'app/app.dart';
void main() {
runApp(const MyLisApp());
}
-
-class MyLisApp extends StatelessWidget {
- const MyLisApp({super.key});
-
- @override
- Widget build(BuildContext context) {
- return MaterialApp(
- title: 'MyLIS Mobile',
- debugShowCheckedModeBanner: false,
- theme: ThemeData(
- colorScheme: ColorScheme.fromSeed(
- seedColor: const Color(0xFF0F766E),
- brightness: Brightness.light,
- ),
- scaffoldBackgroundColor: const Color(0xFFF7FAF9),
- useMaterial3: true,
- ),
- home: const SessionGate(),
- );
- }
-}
-
-class SessionGate extends StatefulWidget {
- const SessionGate({super.key});
-
- @override
- State createState() => _SessionGateState();
-}
-
-class _SessionGateState extends State {
- late final Future _sessionFuture;
-
- @override
- void initState() {
- super.initState();
- _sessionFuture = SessionStore.session();
- }
-
- @override
- Widget build(BuildContext context) {
- return FutureBuilder(
- future: _sessionFuture,
- builder: (context, snapshot) {
- if (!snapshot.hasData &&
- snapshot.connectionState != ConnectionState.done) {
- return const LoadingScreen();
- }
- final session = snapshot.data ?? const SessionData();
- return session.token == null
- ? const LoginScreen()
- : DashboardScreen(token: session.token!, baseUrl: session.baseUrl);
- },
- );
- }
-}
-
-class ApiClient {
- ApiClient({required this.baseUrl, this.token});
-
- final String baseUrl;
- final String? token;
-
- Uri _uri(String path, [Map? query]) {
- final normalizedBase = normalizeBaseUrl(baseUrl);
- final base = Uri.parse(normalizedBase);
- return base.replace(
- path: '${base.path.replaceAll(RegExp(r'/$'), '')}/$path',
- queryParameters: query,
- );
- }
-
- Map get _headers => {
- 'Accept': 'application/json',
- 'Content-Type': 'application/json',
- if (token != null) 'Authorization': 'Bearer $token',
- };
-
- Future