Files
lis/htdocs/resources/views/admin/biorepository.blade.php
T
2026-02-21 06:32:31 +07:00

558 lines
26 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
@extends('base.layout')
@push('styles')
<style>
.bio-summary-card {
border-left: 4px solid #188ae2;
}
.cabinet-visual {
border: 2px solid #2d3e50;
border-radius: 12px;
background: #f7fbff;
padding: 15px;
margin-bottom: 18px;
}
.rack-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: 12px;
}
.rack-box {
border: 1px solid #d7e3ef;
border-radius: 10px;
background: #ffffff;
padding: 12px;
}
.rack-header {
font-weight: 700;
color: #1b3a57;
margin-bottom: 4px;
}
.slot-grid {
display: grid;
grid-template-columns: repeat(8, minmax(26px, 1fr));
gap: 6px;
margin-top: 10px;
}
.shelf-section {
border: 1px dashed #bfd3e8;
border-radius: 8px;
padding: 8px;
margin-top: 8px;
background: #fbfdff;
}
.tube-grid {
display: grid;
grid-template-columns: repeat(10, minmax(24px, 1fr));
gap: 5px;
margin-top: 8px;
}
.slot-btn {
border: 1px solid #bfd3e8;
background: #f4f9ff;
color: #134068;
border-radius: 5px;
font-size: 11px;
line-height: 1;
padding: 7px 0;
cursor: pointer;
text-align: center;
width: 100%;
}
.slot-btn.slot-filled {
background: #fce8e8;
border-color: #e7adad;
color: #8d1f1f;
cursor: not-allowed;
}
.oldest-highlight {
border: 1px solid #ffd58f;
background: #fff8ea;
border-radius: 10px;
padding: 12px;
}
#biorepoTable tfoot input {
width: 100%;
min-width: 90px;
font-size: 11px;
padding: 4px 6px;
}
</style>
@endpush
@section('content')
<div class="wrapper">
<div class="container-fluid">
<div class="row">
<div class="col-sm-12">
<div class="page-title-box">
<div class="btn-group pull-right">
<ol class="breadcrumb hide-phone p-0 m-0">
<li class="breadcrumb-item active">Biorepository</li>
</ol>
</div>
<h4 class="page-title">Biorepository Lab Mikrobiologi</h4>
</div>
</div>
</div>
@if(session('success'))
<div class="row">
<div class="col-lg-12">
<div class="alert alert-success">{{ session('success') }}</div>
</div>
</div>
@endif
@if($errors->any())
<div class="row">
<div class="col-lg-12">
<div class="alert alert-danger">
@foreach($errors->all() as $err)
<div>{{ $err }}</div>
@endforeach
</div>
</div>
</div>
@endif
<div class="row">
<div class="col-md-4">
<div class="card-box bio-summary-card">
<h5>Total Lemari</h5>
<h3>{{ $totalCabinets }}</h3>
<button type="button" class="btn btn-sm btn-primary" data-toggle="modal" data-target="#modalTambahLemari">Tambah Lemari</button>
</div>
</div>
<div class="col-md-4">
<div class="card-box bio-summary-card">
<h5>Total Rack</h5>
<h3>{{ $totalRacks }}</h3>
<button type="button" class="btn btn-sm btn-info" data-toggle="modal" data-target="#modalTambahRack">Tambah Rack</button>
</div>
</div>
<div class="col-md-4">
<div class="card-box bio-summary-card">
<h5>Total Spesimen</h5>
<h3>{{ $totalSpecimens }}</h3>
<button type="button" class="btn btn-sm btn-dark" id="btnOpenListTab">Lihat List</button>
</div>
</div>
</div>
<div class="card-box">
<ul class="nav nav-tabs m-b-20">
<li class="nav-item">
<a href="#tabVisual" data-toggle="tab" aria-expanded="true" class="nav-link active">Visualisasi</a>
</li>
<li class="nav-item">
<a href="#tabList" data-toggle="tab" aria-expanded="false" class="nav-link" id="tabListLink">List Biorepository</a>
</li>
</ul>
<div class="tab-content">
<div class="tab-pane active" id="tabVisual">
<h4 class="m-b-15">Spesimen dengan Waktu Simpan Paling Lama</h4>
@if($oldestSpecimen)
<div class="oldest-highlight m-b-20">
<strong>{{ $oldestSpecimen->specimen_code }}</strong> - {{ $oldestSpecimen->bacteria_name ?? $oldestSpecimen->specimen_name }}<br>
Lemari: {{ $oldestSpecimen->cabinet->name ?? '-' }} | Rack: {{ $oldestSpecimen->rack->name ?? '-' }}<br>
Disimpan sejak: {{ $oldestSpecimen->stored_at }} ({{ $oldestStorageDays }} hari)
</div>
@else
<div class="alert alert-warning m-b-20">Belum ada data spesimen.</div>
@endif
<h4 class="m-b-20">Visualisasi Lemari, Rack, Shelf, Box, dan Tube</h4>
<p class="text-muted">Klik nomor tube berwarna biru untuk mengisi spesimen. Tube merah artinya sudah terisi.</p>
@forelse($cabinets as $cabinet)
<div class="cabinet-visual">
<div class="d-flex justify-content-between align-items-start m-b-15">
<h5 class="m-b-0">{{ $cabinet->code }} - {{ $cabinet->name }} <small class="text-muted">({{ $cabinet->location ?? 'Lokasi belum diisi' }})</small></h5>
<form method="POST" action="{{ route('biorepository.deleteCabinet', $cabinet->id) }}" class="js-delete-cabinet">
@csrf
<button type="submit" class="btn btn-sm btn-danger">Delete Lemari</button>
</form>
</div>
<div class="rack-grid">
@forelse($cabinet->racks as $rack)
@php
$tubeMap = [];
foreach ($rack->specimens as $item) {
if ($item->shelf_number && $item->tube_number) {
$tubeMap[$item->shelf_number.'-'.$item->tube_number] = $item;
}
}
$shelfCount = (int) $rack->level;
$tubeCapacity = (int) $rack->capacity;
@endphp
<div class="rack-box">
<div class="d-flex justify-content-between align-items-start">
<div class="rack-header">{{ $rack->code }} - {{ $rack->name }}</div>
<form method="POST" action="{{ route('biorepository.deleteRack', $rack->id) }}" class="js-delete-rack">
@csrf
<button type="submit" class="btn btn-xs btn-danger">Delete Rack</button>
</form>
</div>
<div style="font-size:12px;">Rack No {{ $rack->rack_number ?? $rack->id }} | Total Shelf {{ $shelfCount }} | 1 Box per Shelf | Tube per Box {{ $tubeCapacity }}</div>
<div style="font-size:12px;">Terisi: {{ $rack->specimens->count() }}</div>
@if($shelfCount > 0 && $tubeCapacity > 0)
@for($shelf = 1; $shelf <= $shelfCount; $shelf++)
<div class="shelf-section">
<div style="font-size:12px;"><b>Shelf {{ $shelf }}</b> - Box 1 (Tube 1-{{ $tubeCapacity }})</div>
<div class="tube-grid">
@for($tube = 1; $tube <= $tubeCapacity; $tube++)
@php $tubeKey = $shelf.'-'.$tube; @endphp
@if(isset($tubeMap[$tubeKey]))
<div class="slot-btn slot-filled" title="{{ $tubeMap[$tubeKey]->specimen_code }} - {{ $tubeMap[$tubeKey]->bacteria_name ?? $tubeMap[$tubeKey]->specimen_name }}">{{ $tube }}</div>
@else
<button type="button"
class="slot-btn js-slot"
data-rack-id="{{ $rack->id }}"
data-shelf="{{ $shelf }}"
data-rackno="{{ $rack->rack_number ?? $rack->id }}"
data-slot="{{ $shelf }}"
data-box="1"
data-tube="{{ $tube }}">
{{ $tube }}
</button>
@endif
@endfor
</div>
</div>
@endfor
@else
<div class="alert alert-light m-b-0 m-t-10">Konfigurasi rack belum lengkap.</div>
@endif
</div>
@empty
<div class="alert alert-light m-b-0">Belum ada rack pada lemari ini.</div>
@endforelse
</div>
</div>
@empty
<div class="alert alert-warning">Belum ada data lemari biorepository.</div>
@endforelse
</div>
<div class="tab-pane" id="tabList">
<h4 class="m-b-20">List Biorepository</h4>
<div class="table-responsive">
<table id="biorepoTable" class="table table-bordered table-striped table-sm">
<thead>
<tr>
<th>Kode Sample</th>
<th>Kategori</th>
<th>Shelf</th>
<th>Rack</th>
<th>Slot</th>
<th>Box</th>
<th>Tube</th>
<th>Bactery</th>
<th>Strain</th>
<th>Lemari</th>
<th>Nama Rack</th>
<th>Tgl Simpan</th>
<th>Input By</th>
<th>Aksi</th>
</tr>
</thead>
<tfoot>
<tr>
<th>Kode Sample</th>
<th>Kategori</th>
<th>Shelf</th>
<th>Rack</th>
<th>Slot</th>
<th>Box</th>
<th>Tube</th>
<th>Bactery</th>
<th>Strain</th>
<th>Lemari</th>
<th>Nama Rack</th>
<th>Tgl Simpan</th>
<th>Input By</th>
<th></th>
</tr>
</tfoot>
<tbody>
@foreach($specimenRows as $row)
<tr>
<td>{{ $row->specimen_code }}</td>
<td>{{ $row->category_storage }}</td>
<td>{{ $row->shelf_number }}</td>
<td>{{ $row->rack_number }}</td>
<td>{{ $row->slot_number }}</td>
<td>{{ $row->box_number }}</td>
<td>{{ $row->tube_number }}</td>
<td>{{ $row->bacteria_name ?? $row->specimen_name }}</td>
<td>{{ $row->strain }}</td>
<td>{{ $row->cabinet->code ?? '-' }}</td>
<td>{{ $row->rack->name ?? '-' }}</td>
<td>{{ $row->stored_at }}</td>
<td>{{ $row->input_by }}</td>
<td class="text-center">
<form method="POST" action="{{ route('biorepository.deleteSpecimen', $row->id) }}" class="d-inline js-delete-specimen">
@csrf
<button type="submit" class="btn btn-sm btn-danger">Hapus</button>
</form>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
<div id="modalTambahLemari" class="modal fade" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title">Tambah Lemari</h4>
</div>
<form method="POST" action="{{ route('biorepository.storeCabinet') }}">
@csrf
<div class="modal-body">
<div class="form-group">
<label>Kode Lemari</label>
<input type="text" name="code" class="form-control" placeholder="LMR-A01" required>
</div>
<div class="form-group">
<label>Nama Lemari</label>
<input type="text" name="name" class="form-control" required>
</div>
<div class="form-group">
<label>Lokasi</label>
<input type="text" name="location" class="form-control" placeholder="Ruang kultur 1">
</div>
<div class="form-group m-b-0">
<label>Catatan</label>
<textarea name="notes" class="form-control" rows="2"></textarea>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Batal</button>
<button class="btn btn-primary" type="submit">Simpan Lemari</button>
</div>
</form>
</div>
</div>
</div>
<div id="modalTambahRack" class="modal fade" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title">Tambah Rack</h4>
</div>
<form method="POST" action="{{ route('biorepository.storeRack') }}">
@csrf
<div class="modal-body">
<div class="form-group">
<label>Pilih Lemari</label>
<select name="cabinet_id" class="form-control" required>
<option value="">-- Pilih --</option>
@foreach($cabinetOptions as $cab)
<option value="{{ $cab->id }}">{{ $cab->code }} - {{ $cab->name }}</option>
@endforeach
</select>
</div>
<div class="form-row">
<div class="form-group col-md-6">
<label>Kode Rack</label>
<input type="text" name="code" class="form-control" placeholder="R01" required>
</div>
<div class="form-group col-md-6">
<label>Nama Rack</label>
<input type="text" name="name" class="form-control" required>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-6">
<label>Jumlah Shelf per Rack (4-6)</label>
<input type="number" name="level" class="form-control" min="4" max="6" value="4" required>
</div>
<div class="form-group col-md-6">
<label>Tube per Box (16-20)</label>
<input type="number" name="capacity" class="form-control" min="16" max="20" value="16" required>
</div>
</div>
<small class="text-muted">Rack number dibuat otomatis oleh sistem. Struktur: 1 rack memiliki 4-6 shelf, setiap shelf memiliki 1 box, setiap box 16-20 tube.</small>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Batal</button>
<button class="btn btn-info" type="submit">Simpan Rack</button>
</div>
</form>
</div>
</div>
</div>
<div id="modalIsiSlot" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title" id="myModalLabel">Isi Spesimen ke Slot</h4>
</div>
<form method="POST" action="{{ route('biorepository.storeSpecimen') }}">
@csrf
<div class="modal-body">
<input type="hidden" id="rack_id" name="rack_id">
<div class="form-group m-b-25">
<div class="col-12">
<label>Category Penyimpanan</label>
<select class="form-control" id="kategorisimpan" name="kategorisimpan">
<option value="A">Penyimpanan Suhu Ruang</option>
<option value="B">Penyimpanan Suhu 4 Derajat</option>
<option value="C">Penyimpanan Suhu 20 Derajat</option>
<option value="D">Penyimpanan Suhu 80 Derajat</option>
</select>
</div>
</div>
<div class="form-group row">
<div class="col-lg-4">
<label>Shelf Nomor</label>
<input type="number" class="form-control" id="shelfnomor" name="shelfnomor" readonly>
</div>
<div class="col-lg-4">
<label>Rack Number</label>
<input type="number" class="form-control" id="raknomor" name="raknomor" readonly>
</div>
<div class="col-lg-4">
<label>Slot Number (Sama dengan Shelf)</label>
<input type="number" class="form-control" id="slotnomor" name="slotnomor" readonly>
</div>
<div class="col-lg-4">
<label>Box Number (1 per Shelf)</label>
<input type="number" class="form-control" id="boxnomor" name="boxnomor" readonly>
</div>
<div class="col-lg-4">
<label>Tube Number</label>
<input type="number" class="form-control" id="tubenomor" name="tubenomor" readonly required>
</div>
<div class="col-lg-4">
<label>Tanggal Simpan</label>
<input type="date" class="form-control" id="stored_at" name="stored_at" value="{{ date('Y-m-d') }}" required>
</div>
</div>
<div class="form-group m-b-25">
<div class="col-12">
<label>Bactery Name</label>
<input type="text" class="form-control" id="nmbakteri" name="nmbakteri" required>
</div>
</div>
<div class="form-group m-b-25">
<div class="col-12">
<label>Strain</label>
<select class="form-control" id="strain" name="strain">
<option value="Gram Negatif">Gram Negatif</option>
<option value="Gram Positif">Gram Positif</option>
</select>
</div>
</div>
<div class="form-group m-b-0">
<div class="col-12">
<label>Sample Code (Preview Otomatis)</label>
<input type="text" class="form-control" id="samplecodepreview" readonly>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Batal</button>
<button type="submit" class="btn btn-primary">Simpan Spesimen</button>
</div>
</form>
</div>
</div>
</div>
@endsection
@push('script')
<script>
function buildSampleCodePreview() {
var category = $('#kategorisimpan').val() || '';
var shelf = $('#shelfnomor').val() || '';
var rack = $('#raknomor').val() || '';
var slot = $('#slotnomor').val() || '';
var box = $('#boxnomor').val() || '';
var tube = $('#tubenomor').val() || '';
var code = [category, shelf, rack, slot, box, tube].join('-');
$('#samplecodepreview').val(code);
}
$(document).on('click', '.js-slot', function () {
$('#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'));
buildSampleCodePreview();
$('#modalIsiSlot').modal('show');
});
$(document).on('keyup change', '#kategorisimpan, #shelfnomor, #raknomor, #slotnomor, #boxnomor, #tubenomor', function () {
buildSampleCodePreview();
});
$(function () {
$('#biorepoTable tfoot th').each(function () {
var title = $(this).text();
if (title !== '') {
$(this).html('<input type="text" placeholder="Filter ' + title + '" />');
} else {
$(this).html('');
}
});
var table = $('#biorepoTable').DataTable({
order: [[11, 'desc']],
pageLength: 25
});
table.columns().every(function () {
var that = this;
$('input', this.footer()).on('keyup change clear', function () {
if (that.search() !== this.value) {
that.search(this.value).draw();
}
});
});
$('#btnOpenListTab').on('click', function () {
$('#tabListLink').tab('show');
});
$(document).on('submit', '.js-delete-specimen', function (e) {
if (!confirm('Yakin hapus spesimen ini?')) {
e.preventDefault();
}
});
$(document).on('submit', '.js-delete-rack', function (e) {
if (!confirm('Yakin hapus rack ini? Semua spesimen pada rack ini juga akan dihapus.')) {
e.preventDefault();
}
});
$(document).on('submit', '.js-delete-cabinet', function (e) {
if (!confirm('Yakin hapus lemari ini? Semua rack dan spesimennya akan dihapus.')) {
e.preventDefault();
}
});
});
</script>
@endpush