Files
lis/htdocs/app/Http/Controllers/MobileApiController.php
T
2026-08-02 10:46:34 +07:00

1544 lines
66 KiB
PHP

<?php
namespace App\Http\Controllers;
use App\KomponenJawaban;
use App\CriticalValueSample;
use App\Organisms;
use App\Periksa;
use App\PeriksaSYNC;
use App\Paslab;
use App\Riwayat;
use App\SIMBHPReport;
use App\Subjawaban;
use App\User;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\Session;
class MobileApiController extends Controller
{
protected array $bookLabels = [
'buku01' => 'Cairan Steril',
'buku02' => 'CCI',
'buku03' => 'Darah',
'buku04' => 'HIV',
'buku05' => 'Kuku, Kulit, Adnexa',
'buku06' => 'Kultur TB',
'buku07' => 'Pus dan Jaringan',
'buku08' => 'Sputum',
'buku09' => 'Swab',
'buku10' => 'TCM TB',
'buku11' => 'TCM non-TB',
'buku12' => 'Urine',
'buku13' => 'Feces',
'buku0' => 'Lainnya',
];
protected function bookPoliMap(): array
{
return [
'buku01' => [15,16,43,47,48,66,96,205,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223],
'buku02' => [20],
'buku03' => [7,8,13,17,29,33,44,107,114,115,199,200,201,202],
'buku04' => [30],
'buku05' => [165,179,180,181,183,184,185,187,188,189,190,192,194,195,196,198],
'buku06' => [26,40,45,52,53,56,60,61,68,69,75,92,102,122,123,136,137,144,145,152,153,233],
'buku07' => [18,19,32,35,46,49,50,54,57,58,59,72,160,182,186,191,193,197,206,207,208,224,225,226,227,228,234,235,236,237],
'buku08' => [1,12,23,24,36,39,229,230,231,232],
'buku09' => [2,4,5,9,10,11,21,22,27,34,41,42,51,63,64,65,85,86,87,88,93,97,118,67,121,125,126,127,128,129,130,132,133,134,135,140,147,158,159,161,162,163,164,166,167,168,169,170,171,172,173,174,175,176,177,178],
'buku10' => [14,28,55,62,94,104,105,124,138,146],
'buku11' => [79,80,81,82,83,84,203],
'buku12' => [3,6,25,31,154,155,156,157],
'buku13' => [37,38,148,149,204],
];
}
protected function activeExaminationQuery()
{
return Periksa::query()
->where(function ($query) {
$query->where(function ($sub) {
$sub->whereNotIn('status', ['Selesai', 'Arsip'])
->where('status', 'not like', 'Dibatalkan%');
})->orWhereNull('status');
});
}
protected function userFromRequest(Request $request): ?User
{
$header = (string) $request->bearerToken();
if ($header === '') {
return null;
}
try {
$payload = Crypt::decryptString($header);
$data = json_decode($payload, true);
$user = User::find($data['id'] ?? null);
if (!$user || ($data['password'] ?? '') !== $user->password) {
return null;
}
return $user;
} catch (\Throwable $e) {
return null;
}
}
protected function requireUser(Request $request): User
{
$user = $this->userFromRequest($request);
abort_if(!$user, 401, 'Token login tidak valid.');
return $user;
}
protected function tokenFor(User $user): string
{
return Crypt::encryptString(json_encode([
'id' => $user->id,
'password' => $user->password,
'issued_at' => now()->toDateTimeString(),
]));
}
protected function resolveTargetDays($modaliti2): int
{
return (!empty($modaliti2) && is_numeric($modaliti2) && intval($modaliti2) > 0)
? intval($modaliti2)
: 1;
}
protected function resolveMasterFromPoliId($poliId): string
{
foreach ($this->bookPoliMap() as $master => $poliIds) {
if (in_array((int) $poliId, $poliIds, true)) {
return $master;
}
}
return 'buku0';
}
protected function examinationPayload(Periksa $row): array
{
$formatDate = function ($value): ?string {
if (!$value) {
return null;
}
try {
return Carbon::parse($value)->format('Y-m-d H:i:s');
} catch (\Throwable $e) {
return (string) $value;
}
};
return [
'id' => $row->id,
'master' => $this->resolveMasterFromPoliId($row->poli_id),
'master_label' => $this->bookLabels[$this->resolveMasterFromPoliId($row->poli_id)] ?? 'Lainnya',
'nofoto' => $row->nofoto,
'noregister' => $row->noregister,
'nmpasien' => $row->nmpasien,
'jkpasien' => $row->jkpasien,
'usia' => $row->usia,
'tgllahirpasien' => $row->tgllahirpasien,
'tlppasien' => $row->tlppasien,
'alamatpasien' => $row->alamatpasien,
'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,
'kd_spesimen' => $row->kd_spesimen,
'nm_spesimen' => $row->nm_spesimen,
'kesimpulan' => $row->kesimpulan,
'dlp' => $row->dlp,
'tanggalregis' => $row->tanggalregis,
'tanggalsampel' => $row->tanggalsampel,
'pengambilan' => $row->pengambilan,
'asalpengirim' => $row->asalpengirim,
'status' => $row->status ?: 'NEW',
'daftar' => $formatDate($row->daftar),
'verifikasi' => $formatDate($row->verifikasi),
];
}
public function login(Request $request)
{
$request->validate([
'username' => ['required'],
'password' => ['required'],
]);
$user = User::where('username', $request->input('username'))->first();
if (!$user || !Hash::check((string) $request->input('password'), $user->password)) {
return response()->json(['message' => 'Username atau password salah.'], 401);
}
$photo = $user->getPhoto->xfile ?? '/doctor.png';
$user->active_status = 1;
$user->save();
return response()->json([
'token' => $this->tokenFor($user),
'user' => [
'id' => $user->id,
'nama' => $user->nama,
'username' => $user->username,
'previlage' => $user->previlage,
'photo' => url($photo),
],
]);
}
public function ping()
{
return response()->json([
'status' => 'ok',
'application' => config('global.namaapps', config('app.name')),
'time' => now()->toDateTimeString(),
]);
}
public function dashboard(Request $request)
{
$user = $this->requireUser($request);
$allData = $this->activeExaminationQuery()->get();
$mapped = collect($this->bookPoliMap())->flatten()->all();
$books = [];
foreach ($this->bookLabels as $master => $label) {
$count = $master === 'buku0'
? $allData->filter(fn ($item) => !in_array($item->poli_id, $mapped))->count()
: $allData->whereIn('poli_id', $this->bookPoliMap()[$master] ?? [])->count();
$books[] = ['master' => $master, 'label' => $label, 'total' => $count];
}
return response()->json([
'user' => [
'id' => $user->id,
'nama' => $user->nama,
'username' => $user->username,
'previlage' => $user->previlage,
],
'summary' => [
'total_ppds' => User::where('previlage', 'ppds')->count(),
'butuh_verifikasi' => Periksa::where('status', 'like', '%Un Verified%')->count(),
'antrian_hari_ini' => Periksa::whereDate('daftar', Carbon::today())->count(),
'antrian_sinkron' => PeriksaSYNC::where('created_by', $user->username)->where('status', '')->count(),
'criticalNotificationCount' => CriticalValueSample::whereNull('followed_up_at')->count(),
],
'books' => $books,
'early_warning_groups' => $this->earlyWarningGroups(),
]);
}
public function criticalValueNotifications(Request $request)
{
$this->requireUser($request);
$range = $this->resolveCriticalValueReportRange($request);
return response()->json([
'count' => CriticalValueSample::whereNull('followed_up_at')->count(),
'pending_items' => $this->criticalValueItems(
CriticalValueSample::whereNull('followed_up_at')
->orderBy('critical_set_at', 'asc')
->limit(100)
->get()
),
'followed_items' => $this->criticalValueItems(
CriticalValueSample::whereNotNull('followed_up_at')
->where('followed_up_at', '>=', $range['start_at'])
->where('followed_up_at', '<=', $range['end_at'])
->orderBy('followed_up_at', 'desc')
->limit(100)
->get()
),
'start_date' => $range['start_date'],
'end_date' => $range['end_date'],
]);
}
public function criticalValueNotificationMarkRead(Request $request, int $id)
{
$user = $this->requireUser($request);
$criticalSample = CriticalValueSample::find($id);
abort_if(!$criticalSample, 404, 'Sample nilai kritis tidak ditemukan.');
if (!empty($criticalSample->followed_up_at)) {
return response()->json([
'message' => 'Sample nilai kritis ini sudah ditindaklanjuti sebelumnya.',
'item' => $this->criticalValueItem($criticalSample),
]);
}
$followUpMethod = trim((string) $request->input('caratindaklanjut', ''));
$followUpRecipient = trim((string) $request->input('penerima_laporan', ''));
$followUpReason = trim((string) $request->input('alasan_belum_laporkan', ''));
if ($followUpMethod === '') {
if ($followUpReason === '') {
return response()->json([
'message' => 'Alasan belum dilaporkan wajib diisi jika belum dilaporkan.',
], 422);
}
} else {
$allowed = ['Telpon', 'Whatshap', 'Email', 'Ketemu di Jalan'];
if (!in_array($followUpMethod, $allowed, true)) {
return response()->json(['message' => 'Cara tindak lanjut tidak valid.'], 422);
}
if ($followUpRecipient === '') {
return response()->json([
'message' => 'Penerima laporan wajib diisi jika sudah dilaporkan.',
], 422);
}
}
$criticalSample->followed_up_at = Carbon::now();
$criticalSample->followed_up_by_user_id = $user->id;
$criticalSample->followed_up_by_name = $user->nama;
$criticalSample->follow_up_status = $followUpMethod === '' ? 'not_reported' : 'reported';
$criticalSample->follow_up_method = $followUpMethod !== '' ? $followUpMethod : null;
$criticalSample->follow_up_recipient = $followUpRecipient !== '' ? $followUpRecipient : null;
$criticalSample->follow_up_reason = $followUpReason !== '' ? $followUpReason : null;
$criticalSample->save();
return response()->json([
'message' => 'Sample nilai kritis sudah ditandai sudah ditindaklanjuti.',
'item' => $this->criticalValueItem($criticalSample),
'count' => CriticalValueSample::whereNull('followed_up_at')->count(),
]);
}
protected function resolveCriticalValueReportRange(Request $request): array
{
$defaultStart = Carbon::now()->startOfMonth()->toDateString();
$defaultEnd = Carbon::now()->endOfMonth()->toDateString();
$startDate = trim((string) $request->query('start_date', $defaultStart));
$endDate = trim((string) $request->query('end_date', $defaultEnd));
try {
$start = Carbon::createFromFormat('Y-m-d', $startDate)->startOfDay();
} catch (\Throwable $e) {
$start = Carbon::createFromFormat('Y-m-d', $defaultStart)->startOfDay();
$startDate = $defaultStart;
}
try {
$end = Carbon::createFromFormat('Y-m-d', $endDate)->endOfDay();
} catch (\Throwable $e) {
$end = Carbon::createFromFormat('Y-m-d', $defaultEnd)->endOfDay();
$endDate = $defaultEnd;
}
if ($start->gt($end)) {
[$start, $end] = [$end->copy()->startOfDay(), $start->copy()->endOfDay()];
$startDate = $start->toDateString();
$endDate = $end->toDateString();
}
return [
'start_date' => $startDate,
'end_date' => $endDate,
'start_at' => $start,
'end_at' => $end,
];
}
protected function criticalValueItems($items): array
{
return $items->map(fn ($item) => $this->criticalValueItem($item))->values()->all();
}
protected function criticalValueItem(CriticalValueSample $item): array
{
$tatMinutes = null;
$tatDurationLabel = '-';
$tatStatusLabel = '-';
if (!empty($item->critical_set_at) && !empty($item->followed_up_at)) {
$criticalSetAt = Carbon::parse($item->critical_set_at);
$followedUpAt = Carbon::parse($item->followed_up_at);
$tatMinutes = $criticalSetAt->diffInMinutes($followedUpAt);
$tatDurationLabel = $this->formatCriticalValueTatDuration($tatMinutes);
$tatStatusLabel = $tatMinutes <= 30 ? 'Memenuhi TAT' : 'Tidak Memenuhi TAT';
}
return [
'id' => $item->id,
'periksa_id' => $item->periksa_id,
'nofoto' => $item->nofoto,
'noregister' => $item->noregister,
'nmpasien' => $item->nmpasien,
'asalpasien' => $item->asalpasien,
'nm_spesimen' => $item->nm_spesimen,
'critical_set_at' => $item->critical_set_at,
'critical_set_by_name' => $item->critical_set_by_name,
'followed_up_at' => $item->followed_up_at,
'followed_up_by_name' => $item->followed_up_by_name,
'tat_minutes' => $tatMinutes,
'tat_duration_label' => $tatDurationLabel,
'tat_status_label' => $tatStatusLabel,
'follow_up_status' => $item->follow_up_status,
'follow_up_method' => $item->follow_up_method,
'follow_up_recipient' => $item->follow_up_recipient,
'follow_up_reason' => $item->follow_up_reason,
];
}
protected function formatCriticalValueTatDuration(int $totalMinutes): string
{
$hours = intdiv($totalMinutes, 60);
$minutes = $totalMinutes % 60;
if ($hours > 0) {
return $hours.' jam '.$minutes.' menit';
}
return $minutes.' menit';
}
public function examinations(Request $request, string $master)
{
$this->requireUser($request);
$query = $this->activeExaminationQuery();
$mapped = collect($this->bookPoliMap())->flatten()->all();
if ($master === 'ALL') {
// Semua spesimen: tidak dibatasi kategori buku/poli.
} elseif ($master === 'buku0') {
$query->whereNotIn('poli_id', $mapped);
} elseif (isset($this->bookPoliMap()[$master])) {
$query->whereIn('poli_id', $this->bookPoliMap()[$master]);
} else {
return response()->json(['message' => 'Kategori pemeriksaan tidak dikenal.'], 404);
}
$search = trim((string) $request->query('search', ''));
if ($search !== '') {
$query->where(function ($q) use ($search) {
$q->where('nofoto', 'like', "%{$search}%")
->orWhere('noregister', 'like', "%{$search}%")
->orWhere('nmpasien', 'like', "%{$search}%")
->orWhere('reques', 'like', "%{$search}%");
});
}
$query->orderBy('daftar', 'ASC');
if ($master !== 'ALL') {
$query->limit(250);
}
$items = $query->get()->map(fn ($row) => $this->examinationPayload($row));
return response()->json([
'master' => $master,
'label' => $master === 'ALL' ? 'Semua Spesimen' : $this->bookLabels[$master],
'items' => $items,
]);
}
public function searchExamination(Request $request)
{
$this->requireUser($request);
$search = trim((string) $request->query('search', ''));
abort_if($search === '', 422, 'Kata kunci pencarian wajib diisi.');
$exactColumns = $this->availablePeriksaColumns(['nofoto', 'noregister', 'orderid', 'acc_number', 'barcode']);
$likeColumns = $this->availablePeriksaColumns(['nofoto', 'noregister', 'nmpasien', 'reques', 'orderid', 'acc_number', 'barcode']);
$row = $this->activeExaminationQuery()
->where(function ($q) use ($search, $exactColumns) {
foreach ($exactColumns as $index => $column) {
$index === 0
? $q->where($column, $search)
: $q->orWhere($column, $search);
}
})
->first();
if (!$row) {
$row = $this->activeExaminationQuery()
->where(function ($q) use ($search, $likeColumns) {
foreach ($likeColumns as $index => $column) {
$index === 0
? $q->where($column, 'like', "%{$search}%")
: $q->orWhere($column, 'like', "%{$search}%");
}
})
->orderBy('daftar', 'desc')
->first();
}
if (!$row) {
return response()->json(['message' => 'Data pemeriksaan tidak ditemukan.'], 404);
}
return response()->json([
'item' => $this->examinationPayload($row),
]);
}
protected function availablePeriksaColumns(array $columns): array
{
return array_values(array_filter(
$columns,
fn ($column) => Schema::hasColumn('periksa', $column)
));
}
public function initialWorkSamples(Request $request)
{
$this->requireUser($request);
$items = Periksa::query()
->whereIn('status', [
'Penerimaan Sampel',
'Pemeriksaan Sampel',
'Pemeriksaan Awal',
'Pemeriksaan awal',
])
->orderBy('id', 'DESC')
->limit(500)
->get()
->map(fn ($row) => $this->examinationPayload($row))
->values();
return response()->json([
'items' => $items,
'total' => $items->count(),
]);
}
public function initialWorkAccept(Request $request, int $id)
{
$user = $this->requireUser($request);
$row = Periksa::find($id);
abort_if(!$row, 404, 'Data pemeriksaan tidak ditemukan.');
$mediaTanam = $request->input('mediatanam', []);
if (!is_array($mediaTanam)) {
$mediaTanam = [$mediaTanam];
}
$mediaTanam = array_values(array_filter($mediaTanam, fn ($item) => trim((string) $item) !== ''));
if (count($mediaTanam) === 0) {
return response()->json([
'message' => 'Media Tanam Yang di gunakan wajib dipilih.',
], 422);
}
$jenis = trim((string) $request->input('jenis', ''));
$bulan = trim((string) $request->input('bulan', ''));
if ($jenis !== '' && $jenis !== 'F' && $bulan === '') {
return response()->json([
'message' => 'Jika jenis pengobatan bukan Follow Up, Bulan Ke wajib diisi.',
], 422);
}
if ($jenis !== '') {
$this->acceptInitialWorkWithTreatmentCode($row, $user, $jenis, $bulan);
} else {
$this->acceptInitialWork($row, $user);
}
$this->recordInitialWorkMediaUsage($row->fresh(), $mediaTanam, $user);
return response()->json([
'message' => 'Sampel berhasil diterima.',
'item' => $this->examinationPayload($row->fresh()),
]);
}
public function initialWorkReject(Request $request, int $id)
{
$this->requireUser($request);
$updated = Periksa::where('id', $id)->update([
'status' => null,
]);
if (!$updated) {
return response()->json([
'message' => 'Data pemeriksaan tidak ditemukan atau tidak dapat ditolak.',
], 404);
}
return response()->json([
'message' => 'Sampel dikembalikan ke loket.',
]);
}
protected function acceptInitialWork(Periksa $row, User $user): void
{
$row->status = 'Pemeriksaan awal';
$row->tgldraft = Carbon::now();
if (empty($row->mulai)) {
$row->mulai = Carbon::now();
}
$row->excutor = $user->id;
$row->nmppdsmiddle2 = $user->nama;
$row->save();
}
protected function acceptInitialWorkWithTreatmentCode(Periksa $row, User $user, string $jenis, string $bulan): void
{
$nofoto = $row->nofoto.$jenis.$bulan;
Paslab::updateOrCreate(
['rnoreg' => $nofoto],
[
'nama' => $row->nmpasien,
'norm' => $row->noregister,
'rtglast' => $row->daftar,
'alamat' => $row->alamatpasien,
'rjenis' => $row->jkpasien,
'umur' => $row->usia,
'namadok' => $row->ktp,
'ruangan' => $row->asalpasien,
'tes' => $row->reques,
'alat' => 'ALL',
'kd_spesimen' => $row->kd_spesimen,
'nm_spesimen' => $row->nm_spesimen,
'tgllahir' => $row->tgllahirpasien,
'flg_vitek1' => true,
'flg_vitek2' => true,
'flg_bd1' => true,
'flg_bd2' => false,
'flg_gxp1' => true,
'flg_gxp2' => true,
'flg_gxp3' => true,
'flg_vitek3' => true,
]
);
$row->nofoto = $nofoto;
$row->status = 'Pemeriksaan awal';
$row->tgldraft = Carbon::now();
$row->mulai = Carbon::now();
$row->excutor = $user->id;
$row->nmppdsmiddle2 = $user->nama;
$row->save();
}
protected function recordInitialWorkMediaUsage(?Periksa $row, array $mediaTanam, User $user): void
{
if (!$row) {
return;
}
foreach ($mediaTanam as $media) {
$jenis = $this->mediaTanamReportName((string) $media);
if ($jenis === '') {
continue;
}
$today = Carbon::today();
SIMBHPReport::create([
'tanggal' => (int) $today->format('d'),
'bulan' => (int) $today->format('m'),
'tahun' => (int) $today->format('Y'),
'deskripsi' => 'Pemeriksaan Awal Nomor Lab.'.$row->nofoto.' petugas '.$user->nama,
'pemasukan' => null,
'pengeluaran' => 1,
'qty_base' => 1,
'satuan_transaksi' => 'besar',
'jenis' => $jenis,
'keterangan' => '',
'marking' => '',
]);
}
}
protected function mediaTanamReportName(string $media): string
{
return match ($media) {
'Media BAP' => 'PLATE BAP (Blood Agar Plate) MEDIA',
'Media CAP' => 'PLATE CAP (Chocolate Agar Plate) Media',
'Media Mc Conkey' => 'PLATE MC (Mac Conkey Agar Plate) MEDIA',
'Media SDA R1', 'Media SDA R2', 'Media SDA I1', 'Media SDA I2' => 'PLATE SDA (Saboroud Dextrose Agar Plate) MEDIA',
default => '',
};
}
public function examination(Request $request, int $id)
{
$this->requireUser($request);
$row = Periksa::find($id);
abort_if(!$row, 404, 'Data pemeriksaan tidak ditemukan.');
return response()->json([
'item' => $this->examinationPayload($row),
'expertise_url' => url('mobile/expertise/'.$row->id).'?token='.rawurlencode((string) $request->bearerToken()),
'result_url' => $row->nofoto ? url('hasil/'.$row->nofoto) : null,
]);
}
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)',
'statuspewarnaanlsg' => $this->resolvePeriksaHierarchyStatus($row->status, 'Pewarnaan Langsung'),
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',
'statuspewarnaanlsg' => 'Pewarnaan Langsung',
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',
'statuspewarnaanlsg' => 'Pewarnaan Langsung tersimpan',
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'),
],
};
}
protected function resolvePeriksaHierarchyStatus($currentStatus, $proposedStatus): string
{
$currentStatus = trim((string) $currentStatus);
$proposedStatus = trim((string) $proposedStatus);
if ($proposedStatus === '') {
return $currentStatus;
}
$currentRank = $this->periksaHierarchyStatusRank($currentStatus);
$proposedRank = $this->periksaHierarchyStatusRank($proposedStatus);
if ($currentStatus === '') {
return $proposedStatus;
}
if ($proposedRank === null) {
return $currentRank === null ? $proposedStatus : $currentStatus;
}
if ($currentRank === null || $proposedRank >= $currentRank) {
return $proposedStatus;
}
return $currentStatus;
}
protected function periksaHierarchyStatusRank($status): ?int
{
$normalized = $this->normalizePeriksaStatus($status);
if ($normalized === '') {
return null;
}
if ($normalized === 'penerimaan sampel') {
return 1;
}
if (in_array($normalized, ['pemeriksaan awal', 'pemeriksaan sampel', 'proses analisis sampel'], true)) {
return 2;
}
if (str_starts_with($normalized, 'data bd di terima') || str_starts_with($normalized, 'data bd diterima')) {
return 3;
}
if ($normalized === 'pewarnaan langsung') {
return 4;
}
if ($this->isProsesingKoloniMediaStatus($normalized)) {
return 5;
}
if (str_starts_with($normalized, 'data malditof diterima') || str_starts_with($normalized, 'data malditof di terima')) {
return 6;
}
if (str_starts_with($normalized, 'otor maldi (un verified)')) {
return 7;
}
if (str_starts_with($normalized, 'preliminary results')) {
return 8;
}
if (str_starts_with($normalized, 'data vitek di terima') || str_starts_with($normalized, 'data vitek diterima')) {
return 9;
}
if (str_starts_with($normalized, 'id/ast pending result') || $normalized === 'sedang id+ast') {
return 10;
}
if (str_starts_with($normalized, 'expertise saved (un verified)')
|| in_array($normalized, ['expertise', 'selesai', 'final result'], true)) {
return 11;
}
return null;
}
protected function normalizePeriksaStatus($status): string
{
$status = strtolower(trim((string) $status));
$status = str_replace(['_', '-'], ' ', $status);
$status = preg_replace('/\s+/', ' ', $status);
return trim($status);
}
protected function isProsesingKoloniMediaStatus(string $normalizedStatus): bool
{
$exactStatuses = [
'tidak ada pertumbuhan',
'tidak lanjut identifikasi',
'tidak tindak lanjut identifikasi',
'inkubasi lanjutan',
'sub kultur',
'menunggu kultur yg lain',
'menunggu kultur yang lain',
'proses identifikasi dan uji kepekaan',
'sedang malditof',
'pertumbuhan primer',
];
if (in_array($normalizedStatus, $exactStatuses, true)) {
return true;
}
return str_starts_with($normalizedStatus, 'prosesing koloni media')
|| str_starts_with($normalizedStatus, 'processing koloni media')
|| str_starts_with($normalizedStatus, 'proses identifikasi')
|| str_starts_with($normalizedStatus, 'proses uji kepekaan');
}
public function earlyWarning(Request $request)
{
$this->requireUser($request);
return response()->json(['groups' => $this->earlyWarningGroups()]);
}
protected function earlyWarningGroups(): array
{
$now = Carbon::now();
$rows = Periksa::leftJoin('poli', 'periksa.poli_id', '=', 'poli.id')
->select('periksa.id', 'periksa.poli_id', 'periksa.nofoto', 'periksa.noregister', 'periksa.nmpasien', 'periksa.reques', 'periksa.asalpasien', 'periksa.daftar', 'periksa.status', 'poli.subpoli', 'poli.modaliti2')
->whereNotNull('periksa.status')
->where('periksa.status', '<>', '')
->whereRaw('LOWER(periksa.status) <> ?', ['selesai'])
->whereRaw('LOWER(periksa.status) not like ?', ['%dibatalkan%'])
->whereNotNull('periksa.daftar')
->orderBy('periksa.daftar', 'ASC')
->get();
$warnings = [];
foreach ($rows as $row) {
$targetDays = $this->resolveTargetDays($row->modaliti2);
$registeredAt = Carbon::parse($row->daftar);
$deadline = $registeredAt->copy()->addDays($targetDays)->endOfDay();
$remainingMinutes = $now->diffInMinutes($deadline, false);
$warningLimit = $targetDays > 1 ? 1440 : 60;
$isOverdue = $now->gt($deadline);
if ($isOverdue || $remainingMinutes <= $warningLimit) {
$warnings[] = array_merge($this->examinationPayload($row), [
'subpoli' => $row->subpoli ?? 'Tanpa Subpoli',
'target_hari' => $targetDays,
'target_selesai' => $deadline->format('Y-m-d H:i:s'),
'sisa_menit' => $remainingMinutes,
'warning_label' => $isOverdue ? 'Melewati Target' : ($targetDays > 1 ? 'Kurang dari 1 Hari' : 'Kurang dari 1 Jam'),
]);
}
}
return collect($warnings)
->groupBy(fn ($item) => $item['subpoli'] ?: 'Tanpa Subpoli')
->map(fn ($items, $subpoli) => [
'subpoli' => $subpoli,
'total' => count($items),
'items' => array_values($items->toArray()),
])
->values()
->toArray();
}
public function openExpertise(Request $request, int $periksa)
{
$token = (string) $request->query('token', '');
$request->headers->set('Authorization', 'Bearer '.$token);
$user = $this->userFromRequest($request);
abort_if(!$user, 401, 'Token login tidak valid.');
Auth::loginUsingId($user->id);
$photo = $user->getPhoto->xfile ?? '/doctor.png';
Session::put('id', $user->id);
Session::put('nama', $user->nama);
Session::put('username', $user->username);
Session::put('previlage', $user->previlage);
Session::put('photo', $photo);
Session::save();
return redirect()->route('mikro.expertise', ['periksa' => $periksa, 'return_to' => 'dashboard']);
}
}