This commit is contained in:
Dwi Swandhana
2026-03-09 07:24:56 +07:00
parent a1ac1f888b
commit 95d2f6e507
9 changed files with 340 additions and 4265 deletions
+12
View File
@@ -0,0 +1,12 @@
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class CriticalValueSample extends Model
{
protected $table = 'critical_value_samples';
protected $guarded = [];
}
@@ -33,6 +33,7 @@ use App\SiraB;
use App\Organisms; use App\Organisms;
use App\RekapAntibiotik; use App\RekapAntibiotik;
use App\PendaftaranOnListiner; use App\PendaftaranOnListiner;
use App\CriticalValueSample;
use Carbon\Carbon; use Carbon\Carbon;
use Aranyasen\HL7\Message; use Aranyasen\HL7\Message;
use Aranyasen\HL7\Connection; use Aranyasen\HL7\Connection;
@@ -600,6 +601,9 @@ class DokterController extends Controller
'status' => 'Selesai', 'status' => 'Selesai',
]; ];
$periksa->update($updateData); $periksa->update($updateData);
if ($this->shouldMarkAsCritical($request)) {
$this->upsertCriticalValueSample($periksa);
}
$surat = self::genSurat($periksa->id, 'dengan kop'); $surat = self::genSurat($periksa->id, 'dengan kop');
Riwayat::create([ Riwayat::create([
'nofoto' => $nofoto, 'nofoto' => $nofoto,
@@ -710,6 +714,76 @@ class DokterController extends Controller
} }
} }
} }
protected function shouldMarkAsCritical(Request $request): bool
{
$flag = strtolower((string) $request->input('nilai_kritis', '0'));
return in_array($flag, ['1', 'true', 'on', 'yes'], true);
}
protected function upsertCriticalValueSample(Periksa $periksa): void
{
$now = Carbon::now();
$criticalSample = CriticalValueSample::firstOrNew(['periksa_id' => $periksa->id]);
$criticalSample->nofoto = $periksa->nofoto;
$criticalSample->noregister = $periksa->noregister;
$criticalSample->nmpasien = $periksa->nmpasien;
$criticalSample->nm_spesimen = $periksa->nm_spesimen;
// Keep the first critical timestamp intact on repeated clicks.
if (empty($criticalSample->critical_set_at)) {
$criticalSample->critical_set_at = $now;
$criticalSample->critical_set_by_user_id = Session('id');
$criticalSample->critical_set_by_name = Session('nama');
}
$criticalSample->save();
}
protected function markCriticalValueAsRead(CriticalValueSample $criticalSample): void
{
if (!empty($criticalSample->followed_up_at)) {
return;
}
$criticalSample->followed_up_at = Carbon::now();
$criticalSample->followed_up_by_user_id = Session('id');
$criticalSample->followed_up_by_name = Session('nama');
$criticalSample->save();
}
public function criticalValueNotifications(Request $request)
{
$highlightId = $request->query('highlight');
$items = CriticalValueSample::whereNull('followed_up_at')
->orderBy('critical_set_at', 'asc')
->get();
$followedItems = CriticalValueSample::whereNotNull('followed_up_at')
->orderBy('followed_up_at', 'desc')
->limit(100)
->get();
return view('notifications.critical-values', [
'items' => $items,
'followedItems' => $followedItems,
'highlightId' => $highlightId,
]);
}
public function criticalValueNotificationOpen($id)
{
$criticalSample = CriticalValueSample::findOrFail($id);
$this->markCriticalValueAsRead($criticalSample);
return redirect()
->route('criticalValueNotifications', ['highlight' => $criticalSample->id])
->with('success', 'Sample nilai kritis sudah ditandai sudah ditindaklanjuti.');
}
public function criticalValueNotificationMarkRead($id)
{
$criticalSample = CriticalValueSample::findOrFail($id);
$this->markCriticalValueAsRead($criticalSample);
return redirect()
->route('criticalValueNotifications', ['highlight' => $criticalSample->id])
->with('success', 'Sample nilai kritis sudah ditandai sudah ditindaklanjuti.');
}
public function cancelOrder(Request $request) { public function cancelOrder(Request $request) {
$val01 = $request->input('alasan'); $val01 = $request->input('alasan');
$getsetting = Setting::where('id', '1')->first(); $getsetting = Setting::where('id', '1')->first();
@@ -0,0 +1,42 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('critical_value_samples', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('periksa_id')->unique();
$table->string('nofoto', 50)->nullable();
$table->string('noregister', 150)->nullable();
$table->string('nmpasien', 200)->nullable();
$table->string('nm_spesimen', 255)->nullable();
$table->timestamp('critical_set_at')->nullable();
$table->unsignedBigInteger('critical_set_by_user_id')->nullable();
$table->string('critical_set_by_name', 150)->nullable();
$table->timestamp('followed_up_at')->nullable();
$table->unsignedBigInteger('followed_up_by_user_id')->nullable();
$table->string('followed_up_by_name', 150)->nullable();
$table->timestamps();
$table->index('nofoto');
$table->index('critical_set_at');
$table->index('followed_up_at');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('critical_value_samples');
}
};
@@ -1,3 +1,14 @@
@php
$criticalNotificationCount = 0;
$criticalNotificationItems = collect();
if (Session::get('id') !== null) {
$criticalNotificationCount = \App\CriticalValueSample::whereNull('followed_up_at')->count();
$criticalNotificationItems = \App\CriticalValueSample::whereNull('followed_up_at')
->orderBy('critical_set_at', 'asc')
->limit(8)
->get();
}
@endphp
<header id="topnav"> <header id="topnav">
<div class="topbar-main"> <div class="topbar-main">
<div class="container-fluid"> <div class="container-fluid">
@@ -20,21 +31,40 @@
</div> </div>
</a> </a>
</li> </li>
@if(Session::get('id') !== null) <li class="dropdown notification-list">
@if (isset($antrkrmsitu)) <a class="nav-link dropdown-toggle arrow-none waves-effect" data-toggle="dropdown" href="#">
<li class="dropdown notification-list"> <i class="fi-bell noti-icon" id="notification-bell-icon"></i>
<a class="nav-link dropdown-toggle arrow-none waves-effect" data-toggle="dropdown" href="#" role="button" <span class="badge badge-danger badge-pill noti-icon-badge" id="notificationcount">{{ $criticalNotificationCount }}</span>
aria-haspopup="false" aria-expanded="false"> </a>
<i class="fi-bell noti-icon"></i> <div class="dropdown-menu dropdown-menu-right dropdown-lg" id="notifikasi-verifikasi-bell">
<span class="badge badge-danger badge-pill noti-icon-badge">{{$antrkrmsitu}}</span> <div class="dropdown-item noti-title">
</a> <h5 class="m-0">
<div class="dropdown-menu dropdown-menu-right dropdown-lg"> <span class="float-right">
<a href="checkout" class="dropdown-item text-center text-primary notify-item notify-all"> <a href="{{ route('criticalValueNotifications') }}" class="text-dark"><small>Lihat Semua</small></a>
Sync To INSITU <i class="fi-arrow-right"></i> </span>
</a> Nilai Kritis
</h5>
</div> </div>
</li> <div style="max-height: 320px; overflow-y: auto;">
@endif @if($criticalNotificationItems->isEmpty())
<div class="dropdown-item text-muted">Tidak ada sample nilai kritis yang perlu dilaporkan.</div>
@else
@foreach($criticalNotificationItems as $criticalItem)
<a href="{{ route('criticalValueNotificationOpen', $criticalItem->id) }}" class="dropdown-item notify-item">
<div class="notify-icon bg-warning"><i class="fa fa-exclamation-triangle"></i></div>
<p class="notify-details">
{{ $criticalItem->nofoto ?? '-' }} - {{ $criticalItem->nmpasien ?? 'Tanpa Nama' }}
<small class="text-muted">
Set: {{ $criticalItem->critical_set_at ? \Carbon\Carbon::parse($criticalItem->critical_set_at)->format('d-m-Y H:i:s') : '-' }}
</small>
</p>
</a>
@endforeach
@endif
</div>
</div>
</li>
@if(Session::get('id') !== null)
<li class="dropdown notification-list"> <li class="dropdown notification-list">
<a class="nav-link dropdown-toggle waves-effect nav-user" data-toggle="dropdown" href="#" role="button" <a class="nav-link dropdown-toggle waves-effect nav-user" data-toggle="dropdown" href="#" role="button"
aria-haspopup="false" aria-expanded="false"> aria-haspopup="false" aria-expanded="false">
File diff suppressed because it is too large Load Diff
@@ -2367,19 +2367,30 @@
<textarea id="addendumketerangan" style="width: 100%; height: 300px;"></textarea> <textarea id="addendumketerangan" style="width: 100%; height: 300px;"></textarea>
</div> </div>
</div> </div>
<div class="form-row"> <div class="form-row align-items-center">
<div class="form-group col-lg-3"> <div class="col-auto">
<input type="hidden" id="periksa_id" name="periksa_id"> <input type="hidden" id="periksa_id" name="periksa_id">
<button type="button" id="btnkembali" class="btn btn-primary">Back</button> <button type="button" id="btnkembali" class="btn btn-primary">Back</button>
</div> </div>
<div class="form-group col-lg-9 setelahdipilihtemplate"> <div class="col-auto setelahdipilihtemplate">
@if (Session('previlage') == 'supervisor' OR Session('previlage') == 'developer')
<button type="button" id="btnsendexpertisetopacs" class="btn btn-warning pull-right">Save Final Result</button>
@else
<button type="button" id="btnSavePeriksa" class="btn btn-warning pull-right">Save and Send To SPV</button>
@endif
<button type="button" id="btnsavedraft" class="btn btn-success pull-right">Save as Draft</button> <button type="button" id="btnsavedraft" class="btn btn-success pull-right">Save as Draft</button>
</div> </div>
@if (Session('previlage') == 'supervisor' OR Session('previlage') == 'developer')
<div class="col-auto">
<div class="form-check mb-2">
<input class="form-check-input" type="checkbox" id="nilai_kritis" name="nilai_kritis">
<label class="form-check-label" for="nilai_kritis">
Nilai Kritis
</label>
</div>
</div>
<div class="col-auto">
<button type="button" id="btnsendexpertisetopacs" class="btn btn-warning pull-right">Save Final Result</button>
@else
<div class="col-auto">
<button type="button" id="btnSavePeriksa" class="btn btn-warning pull-right">Save and Send To SPV</button>
@endif
</div>
</div> </div>
<div class="form-group setelahdipilihtemplate"> <div class="form-group setelahdipilihtemplate">
<div id="gridadendum"></div> <div id="gridadendum"></div>
@@ -6089,6 +6100,7 @@
formdata.set('addendumketerangan', addendumketerangan); formdata.set('addendumketerangan', addendumketerangan);
formdata.set('lsg_pewarnaanlain', lsg_pewarnaanlain); formdata.set('lsg_pewarnaanlain', lsg_pewarnaanlain);
formdata.set('viralload', viralload); formdata.set('viralload', viralload);
formdata.set('nilai_kritis', $('#nilai_kritis').is(':checked') ? '1' : '0');
formdata.set('_token', '{{ csrf_token() }}'); formdata.set('_token', '{{ csrf_token() }}');
$.each(checkboxValues, (key, val) => formdata.set(key, val)); $.each(checkboxValues, (key, val) => formdata.set(key, val));
$.ajax({ $.ajax({
+20 -8
View File
@@ -2423,19 +2423,30 @@
<textarea id="addendumketerangan" style="width: 100%; height: 300px;"></textarea> <textarea id="addendumketerangan" style="width: 100%; height: 300px;"></textarea>
</div> </div>
</div> </div>
<div class="form-row"> <div class="form-row align-items-center">
<div class="form-group col-lg-3"> <div class="col-auto">
<input type="hidden" id="periksa_id" name="periksa_id"> <input type="hidden" id="periksa_id" name="periksa_id">
<button type="button" id="btnkembali" class="btn btn-primary">Back</button> <button type="button" id="btnkembali" class="btn btn-primary">Back</button>
</div> </div>
<div class="form-group col-lg-9 setelahdipilihtemplate"> <div class="col-auto setelahdipilihtemplate">
@if (Session('previlage') == 'supervisor' OR Session('previlage') == 'developer')
<button type="button" id="btnsendexpertisetopacs" class="btn btn-warning pull-right">Save Final Result</button>
@else
<button type="button" id="btnSavePeriksa" class="btn btn-warning pull-right">Save and Send To SPV</button>
@endif
<button type="button" id="btnsavedraft" class="btn btn-success pull-right">Save as Draft</button> <button type="button" id="btnsavedraft" class="btn btn-success pull-right">Save as Draft</button>
</div> </div>
@if (Session('previlage') == 'supervisor' OR Session('previlage') == 'developer')
<div class="col-auto">
<div class="form-check mb-2">
<input class="form-check-input" type="checkbox" id="nilai_kritis" name="nilai_kritis">
<label class="form-check-label" for="nilai_kritis">
Nilai Kritis
</label>
</div>
</div>
<div class="col-auto">
<button type="button" id="btnsendexpertisetopacs" class="btn btn-warning pull-right">Save Final Result</button>
@else
<div class="col-auto">
<button type="button" id="btnSavePeriksa" class="btn btn-warning pull-right">Save and Send To SPV</button>
@endif
</div>
</div> </div>
<div class="form-group setelahdipilihtemplate"> <div class="form-group setelahdipilihtemplate">
<div id="gridadendum"></div> <div id="gridadendum"></div>
@@ -6131,6 +6142,7 @@
formdata.set('addendumketerangan', addendumketerangan); formdata.set('addendumketerangan', addendumketerangan);
formdata.set('lsg_pewarnaanlain', lsg_pewarnaanlain); formdata.set('lsg_pewarnaanlain', lsg_pewarnaanlain);
formdata.set('viralload', viralload); formdata.set('viralload', viralload);
formdata.set('nilai_kritis', $('#nilai_kritis').is(':checked') ? '1' : '0');
formdata.set('_token', '{{ csrf_token() }}'); formdata.set('_token', '{{ csrf_token() }}');
$.each(checkboxValues, (key, val) => formdata.set(key, val)); $.each(checkboxValues, (key, val) => formdata.set(key, val));
$.ajax({ $.ajax({
@@ -0,0 +1,125 @@
@extends('base.layout')
@section('content')
<div class="wrapper">
<div class="container-fluid">
<div class="row">
<div class="col-12">
<div class="page-title-box">
<h4 class="page-title">Notifikasi Nilai Kritis</h4>
</div>
</div>
</div>
@if (session('success'))
<div class="row">
<div class="col-12">
<div class="alert alert-success">{{ session('success') }}</div>
</div>
</div>
@endif
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-body">
<h5 class="card-title mb-3">Sample Yang Belum Dilaporkan</h5>
@if($items->isEmpty())
<div class="alert alert-info mb-0">Tidak ada sample nilai kritis yang perlu dilaporkan.</div>
@else
<div class="table-responsive">
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>No</th>
<th>No Foto</th>
<th>No Register</th>
<th>Pasien</th>
<th>Spesimen</th>
<th>Waktu Set Nilai Kritis</th>
<th>Waktu Ditindaklanjuti</th>
<th>Aksi</th>
</tr>
</thead>
<tbody>
@foreach($items as $index => $item)
<tr @if((string) $highlightId === (string) $item->id) class="table-warning" @endif>
<td>{{ $index + 1 }}</td>
<td>{{ $item->nofoto ?? '-' }}</td>
<td>{{ $item->noregister ?? '-' }}</td>
<td>{{ $item->nmpasien ?? '-' }}</td>
<td>{{ $item->nm_spesimen ?? '-' }}</td>
<td>
{{ $item->critical_set_at ? \Carbon\Carbon::parse($item->critical_set_at)->format('d-m-Y H:i:s') : '-' }}
@if(!empty($item->critical_set_by_name))
<br><small>oleh {{ $item->critical_set_by_name }}</small>
@endif
</td>
<td>-</td>
<td>
<form action="{{ route('criticalValueNotificationMarkRead', $item->id) }}" method="POST" class="mb-0">
@csrf
<button type="submit" class="btn btn-sm btn-warning">Mark as Read</button>
</form>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@endif
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-body">
<h5 class="card-title mb-3">Riwayat Nilai Kritis Yang Sudah Ditindaklanjuti</h5>
@if($followedItems->isEmpty())
<div class="alert alert-secondary mb-0">Belum ada riwayat tindak lanjut.</div>
@else
<div class="table-responsive">
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>No</th>
<th>No Foto</th>
<th>No Register</th>
<th>Pasien</th>
<th>Spesimen</th>
<th>Waktu Set Nilai Kritis</th>
<th>Waktu Ditindaklanjuti</th>
</tr>
</thead>
<tbody>
@foreach($followedItems as $index => $item)
<tr @if((string) $highlightId === (string) $item->id) class="table-warning" @endif>
<td>{{ $index + 1 }}</td>
<td>{{ $item->nofoto ?? '-' }}</td>
<td>{{ $item->noregister ?? '-' }}</td>
<td>{{ $item->nmpasien ?? '-' }}</td>
<td>{{ $item->nm_spesimen ?? '-' }}</td>
<td>{{ $item->critical_set_at ? \Carbon\Carbon::parse($item->critical_set_at)->format('d-m-Y H:i:s') : '-' }}</td>
<td>
{{ $item->followed_up_at ? \Carbon\Carbon::parse($item->followed_up_at)->format('d-m-Y H:i:s') : '-' }}
@if(!empty($item->followed_up_by_name))
<br><small>oleh {{ $item->followed_up_by_name }}</small>
@endif
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@endif
</div>
</div>
</div>
</div>
</div>
</div>
@endsection
+3
View File
@@ -111,6 +111,9 @@ Route::group(['middleware' => 'project.ipg'], function() {
Route::post('dokter/exsendtoinsitu', [DokterController::class, 'exsendToinsitu'])->name('exsendtoinsitu'); Route::post('dokter/exsendtoinsitu', [DokterController::class, 'exsendToinsitu'])->name('exsendtoinsitu');
Route::post('dokter/getListadendum', [DokterController::class, 'getListadendum'])->name('getListadendum'); Route::post('dokter/getListadendum', [DokterController::class, 'getListadendum'])->name('getListadendum');
Route::post('dokter/exprinttandaterima', [DokterController::class, 'exTandaTerima'])->name('exTandaTerima'); Route::post('dokter/exprinttandaterima', [DokterController::class, 'exTandaTerima'])->name('exTandaTerima');
Route::get('notifikasi/nilai-kritis', [DokterController::class, 'criticalValueNotifications'])->name('criticalValueNotifications');
Route::get('notifikasi/nilai-kritis/{id}', [DokterController::class, 'criticalValueNotificationOpen'])->name('criticalValueNotificationOpen');
Route::post('notifikasi/nilai-kritis/{id}/mark-read', [DokterController::class, 'criticalValueNotificationMarkRead'])->name('criticalValueNotificationMarkRead');
Route::get('pasien', [PasienController::class, 'index']); Route::get('pasien', [PasienController::class, 'index']);
Route::post('pasien/store', [PasienController::class, 'store']); Route::post('pasien/store', [PasienController::class, 'store']);