get(); $data['totalCabinets'] = BioCabinet::count(); $data['totalRacks'] = BioRack::count(); $data['totalSpecimens'] = BioSpecimen::count(); $data['specimenRows'] = BioSpecimen::with(['cabinet', 'rack']) ->orderBy('stored_at', 'DESC') ->orderBy('id', 'DESC') ->get(); $data['cabinets'] = BioCabinet::with([ 'racks' => function ($query) { $query->orderBy('rack_number', 'ASC')->orderBy('code', 'ASC'); }, 'racks.specimens' => function ($query) { $query->orderBy('stored_at', 'ASC'); }, ])->orderBy('name', 'ASC')->get(); $data['oldestSpecimen'] = BioSpecimen::with(['cabinet', 'rack']) ->whereNotNull('stored_at') ->orderBy('stored_at', 'ASC') ->first(); $data['oldestStorageDays'] = 0; if ($data['oldestSpecimen']) { $data['oldestStorageDays'] = Carbon::parse($data['oldestSpecimen']->stored_at)->diffInDays(Carbon::now()); } return view('admin.biorepository', $data); } public function storeCabinet(Request $request) { $validator = Validator::make($request->all(), [ 'code' => 'required|max:50|unique:bio_cabinets,code', 'name' => 'required|max:150', 'location' => 'nullable|max:200', 'notes' => 'nullable', ]); if ($validator->fails()) { return back()->withErrors($validator)->withInput(); } BioCabinet::create([ 'code' => $request->input('code'), 'name' => $request->input('name'), 'location' => $request->input('location'), 'notes' => $request->input('notes'), ]); return redirect('/biorepository')->with('success', 'Lemari biorepository berhasil ditambahkan.'); } public function storeRack(Request $request) { $validator = Validator::make($request->all(), [ 'cabinet_id' => 'required|exists:bio_cabinets,id', 'code' => 'required|max:50', 'name' => 'required|max:150', 'level' => 'required|integer|min:4|max:6', 'capacity' => 'required|integer|min:16|max:20', 'notes' => 'nullable', ]); if ($validator->fails()) { return back()->withErrors($validator)->withInput(); } $exists = BioRack::where('cabinet_id', $request->input('cabinet_id')) ->where('code', $request->input('code')) ->exists(); if ($exists) { return back()->withErrors(['code' => 'Kode rack sudah dipakai pada lemari ini.'])->withInput(); } $nextRackNumber = ((int) BioRack::where('cabinet_id', $request->input('cabinet_id'))->max('rack_number')) + 1; BioRack::create([ 'cabinet_id' => $request->input('cabinet_id'), 'code' => $request->input('code'), 'name' => $request->input('name'), 'level' => $request->input('level'), 'rack_number' => $nextRackNumber, 'box_number' => 1, 'capacity' => $request->input('capacity'), 'notes' => $request->input('notes'), ]); return redirect('/biorepository')->with('success', 'Rack berhasil ditambahkan.'); } public function storeSpecimen(Request $request) { $validator = Validator::make($request->all(), [ 'rack_id' => 'required|exists:bio_racks,id', 'kategorisimpan' => 'required|in:A,B,C,D', 'shelfnomor' => 'required|integer|min:1', 'raknomor' => 'required|integer|min:1', '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', 'stored_at' => 'required|date', ]); if ($validator->fails()) { return back()->withErrors($validator)->withInput(); } $rack = BioRack::find($request->input('rack_id')); $expectedRackNo = $rack->rack_number ?: $rack->id; $expectedBoxNo = $rack->box_number ?: 1; if ((int) $request->input('raknomor') !== (int) $expectedRackNo || (int) $request->input('boxnomor') !== (int) $expectedBoxNo) { return back()->withErrors(['slotnomor' => 'Posisi slot tidak sesuai dengan konfigurasi rack.'])->withInput(); } if ((int) $request->input('shelfnomor') < 1 || (int) $request->input('shelfnomor') > (int) $rack->level) { return back()->withErrors(['shelfnomor' => 'Shelf melebihi konfigurasi rack.'])->withInput(); } if ((int) $request->input('tubenomor') < 1 || (int) $request->input('tubenomor') > (int) $rack->capacity) { return back()->withErrors(['tubenomor' => 'Nomor tube melebihi kapasitas box pada shelf ini.'])->withInput(); } $slotTaken = BioSpecimen::where('rack_id', $rack->id) ->where('shelf_number', $request->input('shelfnomor')) ->where('tube_number', $request->input('tubenomor')) ->exists(); if ($slotTaken) { return back()->withErrors(['tubenomor' => 'Posisi tube pada shelf ini sudah terisi spesimen.'])->withInput(); } $atccByUser = strtoupper(preg_replace('/[^A-Za-z0-9]/', '', Session::get('nama', 'UNKNOWN'))); if ($atccByUser === '') { $atccByUser = 'UNKNOWN'; } $generatedCode = implode('-', [ $request->input('kategorisimpan'), $request->input('shelfnomor'), $request->input('raknomor'), $request->input('slotnomor'), $request->input('boxnomor'), $request->input('tubenomor'), ]); $baseCode = $generatedCode; $counter = 1; while (BioSpecimen::where('specimen_code', $generatedCode)->exists()) { $counter++; $generatedCode = $baseCode.'-'.$counter; } BioSpecimen::create([ 'cabinet_id' => $rack->cabinet_id, 'rack_id' => $rack->id, 'specimen_code' => $generatedCode, 'specimen_name' => $request->input('nmbakteri'), 'category_storage' => $request->input('kategorisimpan'), 'shelf_number' => $request->input('shelfnomor'), 'rack_number' => $request->input('raknomor'), 'slot_number' => $request->input('shelfnomor'), 'box_number' => $request->input('boxnomor'), 'tube_number' => $request->input('tubenomor'), 'bacteria_name' => $request->input('nmbakteri'), 'strain' => $request->input('strain'), 'atcc' => $atccByUser, 'input_by' => Session::get('nama'), 'stored_at' => $request->input('stored_at'), 'storage_condition' => $this->mapStorageCondition($request->input('kategorisimpan')), 'notes' => $request->input('notes'), ]); return redirect('/biorepository')->with('success', 'Spesimen berhasil disimpan ke slot terpilih.'); } public function deleteSpecimen($id) { if (Session::get('previlage') == '') { return redirect('/login'); } $specimen = BioSpecimen::find($id); if (!$specimen) { return redirect('/biorepository')->withErrors(['specimen' => 'Data spesimen tidak ditemukan.']); } $specimen->delete(); return redirect('/biorepository')->with('success', 'Spesimen berhasil dihapus.'); } public function deleteRack($id) { if (Session::get('previlage') == '') { return redirect('/login'); } $rack = BioRack::find($id); if (!$rack) { return redirect('/biorepository')->withErrors(['rack' => 'Data rack tidak ditemukan.']); } BioSpecimen::where('rack_id', $rack->id)->delete(); $rack->delete(); return redirect('/biorepository')->with('success', 'Rack dan semua spesimennya berhasil dihapus.'); } public function deleteCabinet($id) { if (Session::get('previlage') == '') { return redirect('/login'); } $cabinet = BioCabinet::find($id); if (!$cabinet) { return redirect('/biorepository')->withErrors(['cabinet' => 'Data lemari tidak ditemukan.']); } $rackIds = BioRack::where('cabinet_id', $cabinet->id)->pluck('id')->toArray(); if (!empty($rackIds)) { BioSpecimen::whereIn('rack_id', $rackIds)->delete(); BioRack::whereIn('id', $rackIds)->delete(); } $cabinet->delete(); return redirect('/biorepository')->with('success', 'Lemari beserta rack dan spesimennya berhasil dihapus.'); } private function mapStorageCondition($category) { $mapping = [ 'A' => 'Suhu Ruang', 'B' => '4 Derajat', 'C' => '20 Derajat', 'D' => '80 Derajat', ]; return $mapping[$category] ?? 'Tidak Diketahui'; } }