This commit is contained in:
Dwi Swandhana
2026-02-21 18:58:12 +07:00
parent aed5885eb1
commit af2dd2f819
3 changed files with 297 additions and 29 deletions
@@ -122,9 +122,12 @@ class BiorepositoryController extends Controller
'slotnomor' => 'required|integer|min:1',
'boxnomor' => 'required|integer|min:1',
'tubenomor' => 'required|integer|min:1',
'nmbakteri' => 'required|max:200',
'strain' => 'required|in:Gram Negatif,Gram Positif',
'nmbakteri' => 'nullable|max:200',
'strain' => 'nullable|in:Gram Negatif,Gram Positif',
'stored_at' => 'required|date',
'volume' => 'nullable|numeric|min:0',
'volume_ambil' => 'nullable|numeric|min:0',
'existing_specimen_id' => 'nullable|integer',
]);
if ($validator->fails()) {
@@ -151,10 +154,36 @@ class BiorepositoryController extends Controller
$slotTaken = BioSpecimen::where('rack_id', $rack->id)
->where('shelf_number', $request->input('shelfnomor'))
->where('tube_number', $request->input('tubenomor'))
->exists();
->first();
if ($slotTaken) {
return back()->withErrors(['tubenomor' => 'Posisi tube pada shelf ini sudah terisi spesimen.'])->withInput();
$existingId = (int) $request->input('existing_specimen_id');
if ($existingId !== (int) $slotTaken->id) {
return back()->withErrors(['tubenomor' => 'Posisi tube pada shelf ini sudah terisi spesimen.'])->withInput();
}
$volumeAmbil = (float) $request->input('volume_ambil');
if ($volumeAmbil <= 0) {
return back()->withErrors(['volume_ambil' => 'Isi volume yang diambil dari spesimen.'])->withInput();
}
$volumeSekarang = (float) ($slotTaken->volume ?? 0);
if ($volumeAmbil > $volumeSekarang) {
return back()->withErrors(['volume_ambil' => 'Volume diambil melebihi sisa volume spesimen.'])->withInput();
}
$slotTaken->volume = (string) ($volumeSekarang - $volumeAmbil);
$slotTaken->save();
return redirect('/biorepository')->with('success', 'Pengambilan volume berhasil. Sisa volume diperbarui.');
}
if (!$request->filled('nmbakteri') || !$request->filled('strain')) {
return back()->withErrors(['nmbakteri' => 'Nama bakteri dan strain wajib diisi untuk spesimen baru.'])->withInput();
}
if (!$request->filled('volume')) {
return back()->withErrors(['volume' => 'Volume awal spesimen wajib diisi.'])->withInput();
}
$atccByUser = strtoupper(preg_replace('/[^A-Za-z0-9]/', '', Session::get('nama', 'UNKNOWN')));
@@ -195,6 +224,7 @@ class BiorepositoryController extends Controller
'input_by' => Session::get('nama'),
'stored_at' => $request->input('stored_at'),
'storage_condition' => $this->mapStorageCondition($request->input('kategorisimpan')),
'volume' => (string) $request->input('volume'),
'notes' => $request->input('notes'),
]);
@@ -63,7 +63,7 @@
background: #fce8e8;
border-color: #e7adad;
color: #8d1f1f;
cursor: not-allowed;
cursor: pointer;
}
.oldest-highlight {
border: 1px solid #ffd58f;
@@ -206,9 +206,23 @@
@for($tube = 1; $tube <= $tubeCapacity; $tube++)
@php $tubeKey = $shelf.'-'.$tube; @endphp
@if(isset($tubeMap[$tubeKey]))
<div class="slot-btn slot-filled" style="font-size:7px; line-height:1.1; padding:4px 2px;" title="{{ $tubeMap[$tubeKey]->specimen_code }} - {{ $tubeMap[$tubeKey]->bacteria_name ?? $tubeMap[$tubeKey]->specimen_name }} / {{ $tubeMap[$tubeKey]->strain }}">
{{ ($tubeMap[$tubeKey]->bacteria_name ?? $tubeMap[$tubeKey]->specimen_name) }} / {{ $tubeMap[$tubeKey]->strain ?? '-' }}
</div>
<button type="button"
class="slot-btn slot-filled js-slot-filled"
style="font-size:7px; line-height:1.1; padding:4px 2px;"
data-specimen-id="{{ $tubeMap[$tubeKey]->id }}"
data-rack-id="{{ $rack->id }}"
data-shelf="{{ $shelf }}"
data-rackno="{{ $rack->rack_number ?? $rack->id }}"
data-slot="{{ $shelf }}"
data-box="1"
data-tube="{{ $tube }}"
data-bakteri="{{ $tubeMap[$tubeKey]->bacteria_name ?? $tubeMap[$tubeKey]->specimen_name }}"
data-strain="{{ $tubeMap[$tubeKey]->strain }}"
data-volume="{{ $tubeMap[$tubeKey]->volume ?? 0 }}"
title="{{ $tubeMap[$tubeKey]->specimen_code }} - {{ $tubeMap[$tubeKey]->bacteria_name ?? $tubeMap[$tubeKey]->specimen_name }} / {{ $tubeMap[$tubeKey]->strain }}">
{{ ($tubeMap[$tubeKey]->bacteria_name ?? $tubeMap[$tubeKey]->specimen_name) }} / {{ $tubeMap[$tubeKey]->strain ?? '-' }}<br />
{{ $tubeMap[$tubeKey]->volume ?? 0 }} ml
</button>
@else
<button type="button"
class="slot-btn js-slot"
@@ -409,6 +423,7 @@
@csrf
<div class="modal-body">
<input type="hidden" id="rack_id" name="rack_id">
<input type="hidden" id="existing_specimen_id" name="existing_specimen_id">
<div class="form-group m-b-25">
<div class="col-12">
@@ -466,6 +481,21 @@
</div>
</div>
<div class="form-group m-b-25" id="group-volume-awal">
<div class="col-12">
<label>Volume Awal (ml)</label>
<input type="number" step="0.01" min="0" class="form-control" id="volume" name="volume">
</div>
</div>
<div class="form-group m-b-25" id="group-volume-ambil" style="display:none;">
<div class="col-12">
<label>Volume Diambil (ml)</label>
<input type="number" step="0.01" min="0" class="form-control" id="volume_ambil" name="volume_ambil">
<small class="text-muted">Sisa volume saat ini: <span id="volume_sekarang_text">0</span> ml</small>
</div>
</div>
<div class="form-group m-b-0">
<div class="col-12">
<label>Sample Code (Preview Otomatis)</label>
@@ -497,12 +527,39 @@
}
$(document).on('click', '.js-slot', function () {
$('#existing_specimen_id').val('');
$('#rack_id').val($(this).data('rack-id'));
$('#shelfnomor').val($(this).data('shelf'));
$('#raknomor').val($(this).data('rackno'));
$('#slotnomor').val($(this).data('slot'));
$('#boxnomor').val($(this).data('box'));
$('#tubenomor').val($(this).data('tube'));
$('#nmbakteri').val('').prop('readonly', false);
$('#strain').val('Gram Negatif').prop('disabled', false);
$('#volume').val('');
$('#volume_ambil').val('');
$('#group-volume-awal').show();
$('#group-volume-ambil').hide();
$('#volume_sekarang_text').text('0');
buildSampleCodePreview();
$('#modalIsiSlot').modal('show');
});
$(document).on('click', '.js-slot-filled', function () {
$('#existing_specimen_id').val($(this).data('specimen-id'));
$('#rack_id').val($(this).data('rack-id'));
$('#shelfnomor').val($(this).data('shelf'));
$('#raknomor').val($(this).data('rackno'));
$('#slotnomor').val($(this).data('slot'));
$('#boxnomor').val($(this).data('box'));
$('#tubenomor').val($(this).data('tube'));
$('#nmbakteri').val($(this).data('bakteri')).prop('readonly', true);
$('#strain').val($(this).data('strain')).prop('disabled', true);
$('#volume').val('');
$('#volume_ambil').val('');
$('#group-volume-awal').hide();
$('#group-volume-ambil').show();
$('#volume_sekarang_text').text($(this).data('volume'));
buildSampleCodePreview();
$('#modalIsiSlot').modal('show');
});