MIF_E31230069/spk_kontrakan/app/Http/Controllers/LaundryController.php

799 lines
33 KiB
PHP

<?php
namespace App\Http\Controllers;
use App\Models\Laundry;
use App\Models\LayananLaundry;
use App\Models\ActivityLog;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\DB;
use Exception;
class LaundryController extends Controller
{
// Koordinat Kampus Polije (FIXED)
const KAMPUS_LAT = -8.15981;
const KAMPUS_LNG = 113.72312;
/**
* Display a listing of laundry with ADVANCED filters and sorting (SEPERTI KONTRAKAN)
*/
public function index(Request $request)
{
// Query builder
$query = Laundry::with('layanan');
// ========== FILTER: SEARCH (Nama, Alamat, Fasilitas) ==========
if ($request->filled('search')) {
$search = $request->search;
$query->where(function($q) use ($search) {
$q->where('nama', 'LIKE', "%{$search}%")
->orWhere('alamat', 'LIKE', "%{$search}%")
->orWhere('fasilitas', 'LIKE', "%{$search}%");
});
}
// ========== FILTER: RANGE HARGA (Min & Max dengan Slider) ==========
if ($request->filled('harga_min') || $request->filled('harga_max')) {
$query->whereHas('layanan', function($q) use ($request) {
if ($request->filled('harga_min')) {
$q->where('harga', '>=', $request->harga_min);
}
if ($request->filled('harga_max')) {
$q->where('harga', '<=', $request->harga_max);
}
});
}
// ========== FILTER: JARAK MAKSIMAL (Slider dalam KM) ==========
if ($request->filled('jarak_max')) {
// Konversi km ke meter (karena jarak di DB dalam meter)
$jarakMeter = $request->jarak_max * 1000;
$query->where('jarak', '<=', $jarakMeter);
}
// ========== FILTER: JENIS LAYANAN (Multiple Checkbox) ==========
if ($request->filled('jenis_layanan_filter')) {
$jenisLayananArray = $request->jenis_layanan_filter;
$query->whereHas('layanan', function($q) use ($jenisLayananArray) {
$q->whereIn('jenis_layanan', $jenisLayananArray);
});
}
// ========== FILTER: FASILITAS (Multiple Checkbox) ==========
if ($request->filled('fasilitas_filter')) {
$fasilitasArray = $request->fasilitas_filter;
foreach ($fasilitasArray as $fasilitas) {
// Cari laundry yang fasilitasnya mengandung kata kunci ini
$query->where('fasilitas', 'LIKE', "%{$fasilitas}%");
}
}
// ========== SORTING ==========
$sortBy = $request->get('sort_by', 'terbaru'); // Default: terbaru
switch ($sortBy) {
case 'nama_asc':
$query->orderBy('nama', 'ASC');
break;
case 'nama_desc':
$query->orderBy('nama', 'DESC');
break;
case 'harga_termurah':
// Sort by minimum price from layanan
$query->leftJoin('layanan_laundry', 'laundry.id', '=', 'layanan_laundry.laundry_id')
->select('laundry.*', DB::raw('MIN(layanan_laundry.harga) as min_harga'))
->groupBy('laundry.id')
->orderBy('min_harga', 'ASC');
break;
case 'harga_termahal':
// Sort by maximum price from layanan
$query->leftJoin('layanan_laundry', 'laundry.id', '=', 'layanan_laundry.laundry_id')
->select('laundry.*', DB::raw('MAX(layanan_laundry.harga) as max_harga'))
->groupBy('laundry.id')
->orderBy('max_harga', 'DESC');
break;
case 'jarak_terdekat':
$query->orderBy('jarak', 'ASC');
break;
case 'jarak_terjauh':
$query->orderBy('jarak', 'DESC');
break;
case 'terlama':
$query->orderBy('created_at', 'ASC');
break;
case 'terbaru':
default:
$query->orderBy('created_at', 'DESC');
break;
}
// Get results dengan pagination
$laundry = $query->paginate(12)->withQueryString(); // 12 items per halaman (sama seperti kontrakan)
// ========== STATISTIK & DATA UNTUK FILTER UI ==========
// Total laundry (semua data, tanpa filter)
$totalLaundry = Laundry::count();
// Jumlah hasil setelah filter
$filteredCount = $laundry->total();
// Range harga untuk slider (min & max dari database layanan)
$hargaMin = LayananLaundry::min('harga') ?? 0;
$hargaMax = LayananLaundry::max('harga') ?? 100000;
// Range jarak untuk slider (dalam km)
$jarakMaxDb = Laundry::max('jarak') ?? 10000; // dalam meter
$jarakMaxKm = ceil($jarakMaxDb / 1000); // konversi ke km, bulatkan ke atas
// Daftar jenis layanan unik untuk checkbox
$jenisLayananUnique = LayananLaundry::distinct('jenis_layanan')
->pluck('jenis_layanan')
->filter()
->sort()
->map(function($item) {
return ucfirst($item); // Kapitalisasi huruf pertama
})
->values();
// Daftar fasilitas unik untuk checkbox (extract dari semua laundry)
$allFasilitas = Laundry::pluck('fasilitas')->filter();
$fasilitasUnique = collect();
foreach ($allFasilitas as $fasilitasString) {
// Split by comma dan trim whitespace
$items = array_map('trim', explode(',', $fasilitasString));
foreach ($items as $item) {
if (!empty($item)) {
// Normalisasi ke lowercase untuk menghindari duplikat
$fasilitasUnique->push(strtolower($item));
}
}
}
// Remove duplicates (case-insensitive), sort alphabetically, capitalize first letter
$fasilitasUnique = $fasilitasUnique->unique()
->sort()
->map(function($item) {
return ucfirst($item); // Kapitalisasi huruf pertama untuk tampilan
})
->values();
// Kirim data filter ke view untuk maintain state
$filters = [
'search' => $request->search,
'harga_min' => $request->harga_min ?? $hargaMin,
'harga_max' => $request->harga_max ?? $hargaMax,
'jarak_max' => $request->jarak_max,
'jenis_layanan_filter' => $request->jenis_layanan_filter ?? [],
'fasilitas_filter' => $request->fasilitas_filter ?? [],
'sort_by' => $sortBy,
];
return view('laundry.index', compact(
'laundry',
'filters',
'totalLaundry',
'filteredCount',
'hargaMin',
'hargaMax',
'jarakMaxKm',
'jenisLayananUnique',
'fasilitasUnique'
));
}
/**
* Tampilkan semua laundry di peta
*/
public function map(Request $request)
{
try {
// Validasi input filter
$request->validate([
'search' => 'nullable|string|max:255',
'harga_min' => 'nullable|numeric|min:0',
'harga_max' => 'nullable|numeric|min:0|gte:harga_min',
'jarak' => 'nullable|in:dekat,sedang,jauh',
'jenis_layanan' => 'nullable|in:jam,harian',
]);
$query = Laundry::with('layanan');
// ========== FILTER SEARCH ==========
if ($request->filled('search')) {
$search = $request->search;
$query->where(function($q) use ($search) {
$q->where('nama', 'LIKE', "%{$search}%")
->orWhere('alamat', 'LIKE', "%{$search}%");
});
}
// ========== FILTER HARGA ==========
if ($request->filled('harga_min') || $request->filled('harga_max')) {
$query->whereHas('layanan', function($q) use ($request) {
if ($request->filled('harga_min')) {
$q->where('harga', '>=', $request->harga_min);
}
if ($request->filled('harga_max')) {
$q->where('harga', '<=', $request->harga_max);
}
});
}
// ========== FILTER JARAK ==========
if ($request->filled('jarak')) {
switch ($request->jarak) {
case 'dekat':
$query->where('jarak', '<', 500);
break;
case 'sedang':
$query->whereBetween('jarak', [500, 1000]);
break;
case 'jauh':
$query->where('jarak', '>', 1000);
break;
}
}
// ========== FILTER JENIS LAYANAN ==========
if ($request->filled('jenis_layanan')) {
$query->whereHas('layanan', function($q) use ($request) {
$q->where('jenis_layanan', $request->jenis_layanan);
});
}
$laundry = $query->get();
$filters = [
'search' => $request->search,
'harga_min' => $request->harga_min,
'harga_max' => $request->harga_max,
'jarak' => $request->jarak,
'jenis_layanan' => $request->jenis_layanan,
];
return view('laundry.map', compact('laundry', 'filters'));
} catch (\Illuminate\Validation\ValidationException $e) {
return redirect()->back()
->withErrors($e->validator)
->withInput();
} catch (Exception $e) {
Log::error('Error di Laundry Map: ' . $e->getMessage());
return redirect()->back()
->with('error', 'Terjadi kesalahan saat memuat peta.');
}
}
/**
* Show the form for creating a new laundry
*/
public function create()
{
try {
return view('laundry.create');
} catch (Exception $e) {
Log::error('Error di Laundry Create: ' . $e->getMessage());
return redirect()->route('laundry.index')
->with('error', 'Tidak dapat membuka form tambah data.');
}
}
/**
* Store a newly created laundry in storage
*/
public function store(Request $request)
{
DB::beginTransaction();
try {
// Validasi input lengkap
$validated = $request->validate([
'nama' => 'required|string|max:255',
'alamat' => 'required|string|max:500',
'latitude' => 'required|numeric|between:-90,90',
'longitude' => 'required|numeric|between:-180,180',
'fasilitas' => 'nullable|string|max:1000',
'jam_buka' => 'required|date_format:H:i',
'jam_tutup' => 'required|date_format:H:i',
'status' => 'required|in:buka,tutup',
'foto' => 'nullable|image|mimes:jpeg,png,jpg,webp|max:2048',
// Validasi Layanan
'layanan' => 'required|array|min:1',
'layanan.*.jenis_layanan' => 'required|in:jam,harian',
'layanan.*.nama_paket' => 'required|string|max:255',
'layanan.*.harga' => 'required|numeric|min:0',
'layanan.*.estimasi_selesai' => 'required|numeric|min:1',
'layanan.*.estimasi_satuan' => 'required|in:jam,harian',
'layanan.*.deskripsi' => 'nullable|string|max:1000',
'layanan.*.status' => 'required|in:aktif,nonaktif',
], [
'nama.required' => 'Nama laundry harus diisi',
'alamat.required' => 'Alamat harus diisi',
'latitude.required' => 'Koordinat latitude harus diisi (klik pada peta)',
'latitude.between' => 'Koordinat latitude tidak valid',
'longitude.required' => 'Koordinat longitude harus diisi (klik pada peta)',
'longitude.between' => 'Koordinat longitude tidak valid',
'foto.image' => 'File harus berupa gambar',
'foto.mimes' => 'Format foto harus: jpeg, png, jpg, atau webp',
'foto.max' => 'Ukuran foto maksimal 2MB',
'layanan.required' => 'Minimal harus ada 1 jenis layanan',
'layanan.min' => 'Minimal harus ada 1 jenis layanan',
'layanan.*.jenis_layanan.required' => 'Jenis layanan harus dipilih',
'layanan.*.jenis_layanan.in' => 'Jenis layanan harus jam atau harian',
'layanan.*.nama_paket.required' => 'Nama paket harus diisi',
'layanan.*.harga.required' => 'Harga layanan harus diisi',
'layanan.*.harga.min' => 'Harga tidak boleh negatif',
'layanan.*.estimasi_selesai.required' => 'Estimasi waktu selesai harus diisi',
'layanan.*.estimasi_selesai.min' => 'Estimasi minimal 1',
'layanan.*.estimasi_satuan.required' => 'Satuan estimasi harus dipilih',
'layanan.*.estimasi_satuan.in' => 'Satuan estimasi harus jam atau harian',
'layanan.*.status.required' => 'Status layanan harus dipilih',
'layanan.*.status.in' => 'Status harus aktif atau nonaktif',
]);
// Validasi tidak ada duplikasi jenis layanan
$jenisLayanan = array_column($request->layanan, 'jenis_layanan');
if (count($jenisLayanan) !== count(array_unique($jenisLayanan))) {
return redirect()->back()
->withInput()
->with('error', 'Tidak boleh ada jenis layanan yang sama!');
}
// Proses Upload Foto
$filename = null;
if ($request->hasFile('foto')) {
try {
$file = $request->file('foto');
if (!$file->isValid()) {
throw new Exception('File tidak valid atau corrupt');
}
$filename = time() . '_' . uniqid() . '.' . $file->getClientOriginalExtension();
$uploadPath = public_path('uploads/Laundry');
if (!File::exists($uploadPath)) {
File::makeDirectory($uploadPath, 0755, true);
}
$file->move($uploadPath, $filename);
} catch (Exception $e) {
Log::error('Error upload foto laundry: ' . $e->getMessage());
return redirect()->back()
->withInput()
->with('error', 'Gagal mengupload foto: ' . $e->getMessage());
}
}
// Hitung jarak otomatis dari kampus (seperti kontrakan)
$jarakKm = 0;
if ($validated['latitude'] && $validated['longitude']) {
try {
// Menggunakan method calculateDistance dari model
$tempLaundry = new Laundry();
$tempLaundry->latitude = $validated['latitude'];
$tempLaundry->longitude = $validated['longitude'];
$jarakKm = $tempLaundry->calculateDistance(self::KAMPUS_LAT, self::KAMPUS_LNG);
Log::info("Jarak laundry '{$validated['nama']}' dari kampus: {$jarakKm} km");
} catch (Exception $e) {
Log::warning("Gagal menghitung jarak untuk laundry '{$validated['nama']}': " . $e->getMessage());
$jarakKm = 0;
}
}
// Simpan Laundry dengan jarak yang sudah dihitung
$laundry = Laundry::create([
'nama' => $validated['nama'],
'alamat' => $validated['alamat'],
'latitude' => $validated['latitude'],
'longitude' => $validated['longitude'],
'jarak' => round($jarakKm * 1000), // Simpan dalam meter (seperti kontrakan)
'fasilitas' => $request->fasilitas,
'jam_buka' => $validated['jam_buka'],
'jam_tutup' => $validated['jam_tutup'],
'status' => $validated['status'],
'foto' => $filename,
]);
// Simpan Layanan
foreach ($request->layanan as $layananData) {
$estimasiJam = (float) $layananData['estimasi_selesai'];
if (($layananData['estimasi_satuan'] ?? 'jam') === 'harian') {
$estimasiJam *= 24;
}
$laundry->layanan()->create([
'jenis_layanan' => $layananData['jenis_layanan'],
'nama_paket' => $layananData['nama_paket'],
'harga' => $layananData['harga'],
'estimasi_selesai' => max(1, (int) round($estimasiJam)),
'deskripsi' => $layananData['deskripsi'] ?? null,
'status' => $layananData['status'] ?? 'aktif',
]);
}
DB::commit();
// Log activity
ActivityLog::log('create', "Membuat laundry baru: {$laundry->nama}", 'Laundry', $laundry->id);
return redirect()->route('laundry.index')
->with('success', 'Data laundry berhasil ditambahkan!');
} catch (\Illuminate\Validation\ValidationException $e) {
DB::rollBack();
// Hapus foto jika ada error
if (isset($filename) && File::exists(public_path('uploads/Laundry/' . $filename))) {
File::delete(public_path('uploads/Laundry/' . $filename));
}
return redirect()->back()
->withErrors($e->validator)
->withInput();
} catch (Exception $e) {
DB::rollBack();
Log::error('Error di Laundry Store: ' . $e->getMessage());
// Hapus foto jika ada error
if (isset($filename) && File::exists(public_path('uploads/Laundry/' . $filename))) {
File::delete(public_path('uploads/Laundry/' . $filename));
}
return redirect()->back()
->withInput()
->with('error', 'Gagal menyimpan data: ' . $e->getMessage());
}
}
/**
* Display the specified laundry
*/
public function show(Laundry $laundry)
{
try {
$laundry->load('layanan');
return view('laundry.show', compact('laundry'));
} catch (Exception $e) {
Log::error('Error di Laundry Show: ' . $e->getMessage());
return redirect()->route('laundry.index')
->with('error', 'Data tidak dapat ditampilkan.');
}
}
/**
* Show the form for editing the specified laundry
*/
public function edit(Laundry $laundry)
{
try {
$laundry->load('layanan');
return view('laundry.edit', compact('laundry'));
} catch (Exception $e) {
Log::error('Error di Laundry Edit: ' . $e->getMessage());
return redirect()->route('laundry.index')
->with('error', 'Tidak dapat membuka form edit.');
}
}
/**
* Update the specified laundry in storage
*/
public function update(Request $request, Laundry $laundry)
{
DB::beginTransaction();
try {
// Validasi input
$validated = $request->validate([
'nama' => 'required|string|max:255',
'alamat' => 'required|string|max:500',
'latitude' => 'required|numeric|between:-90,90',
'longitude' => 'required|numeric|between:-180,180',
'fasilitas' => 'nullable|string|max:1000',
'jam_buka' => 'required|date_format:H:i',
'jam_tutup' => 'required|date_format:H:i',
'status' => 'required|in:buka,tutup',
'foto' => 'nullable|image|mimes:jpeg,png,jpg,webp|max:2048',
'hapus_foto' => 'nullable|in:0,1',
// Validasi Layanan
'layanan' => 'required|array|min:1',
'layanan.*.jenis_layanan' => 'required|in:jam,harian',
'layanan.*.nama_paket' => 'required|string|max:255',
'layanan.*.harga' => 'required|numeric|min:0',
'layanan.*.estimasi_selesai' => 'required|numeric|min:1',
'layanan.*.estimasi_satuan' => 'required|in:jam,harian',
'layanan.*.deskripsi' => 'nullable|string|max:1000',
'layanan.*.status' => 'required|in:aktif,nonaktif',
], [
'nama.required' => 'Nama laundry harus diisi',
'alamat.required' => 'Alamat harus diisi',
'latitude.required' => 'Koordinat latitude harus diisi (klik pada peta)',
'longitude.required' => 'Koordinat longitude harus diisi (klik pada peta)',
'foto.max' => 'Ukuran foto maksimal 2MB',
'layanan.required' => 'Minimal harus ada 1 jenis layanan',
'layanan.min' => 'Minimal harus ada 1 jenis layanan',
'layanan.*.jenis_layanan.required' => 'Jenis layanan harus dipilih',
'layanan.*.jenis_layanan.in' => 'Jenis layanan harus jam atau harian',
'layanan.*.nama_paket.required' => 'Nama paket harus diisi',
'layanan.*.harga.required' => 'Harga layanan harus diisi',
'layanan.*.harga.min' => 'Harga tidak boleh negatif',
'layanan.*.estimasi_selesai.required' => 'Estimasi waktu selesai harus diisi',
'layanan.*.estimasi_selesai.min' => 'Estimasi minimal 1',
'layanan.*.estimasi_satuan.required' => 'Satuan estimasi harus dipilih',
'layanan.*.estimasi_satuan.in' => 'Satuan estimasi harus jam atau harian',
'layanan.*.status.required' => 'Status layanan harus dipilih',
'layanan.*.status.in' => 'Status harus aktif atau nonaktif',
]);
// Validasi duplikasi jenis layanan
$jenisLayanan = array_column($request->layanan, 'jenis_layanan');
if (count($jenisLayanan) !== count(array_unique($jenisLayanan))) {
return redirect()->back()
->withInput()
->with('error', 'Tidak boleh ada jenis layanan yang sama!');
}
$filename = $laundry->foto;
$fotoLamaPath = $laundry->foto ? public_path('uploads/Laundry/' . $laundry->foto) : null;
// Cek apakah user ingin HAPUS FOTO
if ($request->has('hapus_foto') && $request->hapus_foto == '1') {
if ($fotoLamaPath && File::exists($fotoLamaPath)) {
try {
File::delete($fotoLamaPath);
} catch (Exception $e) {
Log::warning('Gagal hapus foto lama: ' . $e->getMessage());
}
}
$filename = null;
}
// Cek apakah ada foto baru diupload
elseif ($request->hasFile('foto')) {
try {
$file = $request->file('foto');
if (!$file->isValid()) {
throw new Exception('File tidak valid atau corrupt');
}
// Hapus foto lama
if ($fotoLamaPath && File::exists($fotoLamaPath)) {
File::delete($fotoLamaPath);
}
// Upload foto baru
$filename = time() . '_' . uniqid() . '.' . $file->getClientOriginalExtension();
$uploadPath = public_path('uploads/Laundry');
if (!File::exists($uploadPath)) {
File::makeDirectory($uploadPath, 0755, true);
}
$file->move($uploadPath, $filename);
} catch (Exception $e) {
Log::error('Error upload foto: ' . $e->getMessage());
return redirect()->back()
->withInput()
->with('error', 'Gagal mengupload foto baru: ' . $e->getMessage());
}
}
// Hitung jarak otomatis dari kampus jika koordinat berubah
$jarakKm = 0;
$needUpdateJarak = ($laundry->latitude != $validated['latitude'] || $laundry->longitude != $validated['longitude']);
if ($needUpdateJarak && $validated['latitude'] && $validated['longitude']) {
try {
// Menggunakan method calculateDistance dari model
$tempLaundry = new Laundry();
$tempLaundry->latitude = $validated['latitude'];
$tempLaundry->longitude = $validated['longitude'];
$jarakKm = $tempLaundry->calculateDistance(self::KAMPUS_LAT, self::KAMPUS_LNG);
Log::info("Jarak laundry '{$validated['nama']}' dari kampus diupdate: {$jarakKm} km");
} catch (Exception $e) {
Log::warning("Gagal menghitung jarak untuk laundry '{$validated['nama']}': " . $e->getMessage());
$jarakKm = $laundry->jarak / 1000; // gunakan jarak lama
}
} else {
$jarakKm = $laundry->jarak / 1000; // gunakan jarak lama jika koordinat tidak berubah
}
// Update data laundry dengan jarak yang sudah dihitung
$laundry->update([
'nama' => $validated['nama'],
'alamat' => $validated['alamat'],
'latitude' => $validated['latitude'],
'longitude' => $validated['longitude'],
'jarak' => round($jarakKm * 1000), // Simpan dalam meter (seperti kontrakan)
'fasilitas' => $request->fasilitas,
'jam_buka' => $validated['jam_buka'],
'jam_tutup' => $validated['jam_tutup'],
'status' => $validated['status'],
'foto' => $filename,
]);
// Hapus layanan lama, buat layanan baru
$laundry->layanan()->delete();
foreach ($request->layanan as $layananData) {
$estimasiJam = (float) $layananData['estimasi_selesai'];
if (($layananData['estimasi_satuan'] ?? 'jam') === 'harian') {
$estimasiJam *= 24;
}
$laundry->layanan()->create([
'jenis_layanan' => $layananData['jenis_layanan'],
'nama_paket' => $layananData['nama_paket'],
'harga' => $layananData['harga'],
'estimasi_selesai' => max(1, (int) round($estimasiJam)),
'deskripsi' => $layananData['deskripsi'] ?? null,
'status' => $layananData['status'] ?? 'aktif',
]);
}
DB::commit();
// Log activity
ActivityLog::log('update', "Memperbarui laundry: {$laundry->nama}", 'Laundry', $laundry->id);
return redirect()->route('laundry.index')
->with('success', 'Data berhasil diperbarui!');
} catch (\Illuminate\Validation\ValidationException $e) {
DB::rollBack();
return redirect()->back()
->withErrors($e->validator)
->withInput();
} catch (Exception $e) {
DB::rollBack();
Log::error('Error di Laundry Update: ' . $e->getMessage());
return redirect()->back()
->withInput()
->with('error', 'Gagal memperbarui data: ' . $e->getMessage());
}
}
/**
* Remove the specified laundry from storage
*/
public function destroy(Laundry $laundry)
{
try {
// Proteksi Role - Hanya Admin dan Super Admin yang bisa hapus
if (!in_array(auth()->user()->role, ['admin', 'super_admin'])) {
return redirect()->route('laundry.index')
->with('error', 'Anda tidak memiliki akses untuk menghapus data!');
}
// Hapus foto
if ($laundry->foto && File::exists(public_path('uploads/Laundry/' . $laundry->foto))) {
try {
File::delete(public_path('uploads/Laundry/' . $laundry->foto));
} catch (Exception $e) {
Log::warning('Gagal hapus foto: ' . $e->getMessage());
}
}
// Store nama laundry untuk logging
$laundryNama = $laundry->nama;
$laundryData = $laundry->toArray();
// Hapus laundry (layanan otomatis terhapus via cascade)
$laundry->delete();
// Log activity
ActivityLog::log('delete', "Menghapus laundry: {$laundryNama}", 'Laundry', $laundry->id, $laundryData, []);
return redirect()->route('laundry.index')
->with('success', 'Data berhasil dihapus!');
} catch (Exception $e) {
Log::error('Error di Laundry Destroy: ' . $e->getMessage());
return redirect()->route('laundry.index')
->with('error', 'Gagal menghapus data: ' . $e->getMessage());
}
}
/**
* Bulk delete selected laundry items
*/
public function bulkDestroy(Request $request)
{
try {
// Proteksi Role - Hanya Admin dan Super Admin yang bisa hapus
if (!in_array(auth()->user()->role, ['admin', 'super_admin'])) {
return redirect()->route('laundry.index')
->with('error', 'Anda tidak memiliki akses untuk menghapus data!');
}
// Handle both JSON string and array input
$ids = $request->ids;
if (is_string($ids)) {
$ids = json_decode($ids, true);
}
if (empty($ids) || !is_array($ids)) {
return redirect()->route('laundry.index')
->with('error', 'Pilih minimal 1 laundry untuk dihapus!');
}
// Merge decoded ids back to request
$request->merge(['ids' => $ids]);
// Validasi
$request->validate([
'ids' => 'required|array|min:1',
'ids.*' => 'exists:laundry,id'
], [
'ids.required' => 'Pilih minimal 1 laundry untuk dihapus',
'ids.min' => 'Pilih minimal 1 laundry',
'ids.*.exists' => 'Data laundry tidak valid'
]);
$deletedCount = 0;
$deletedNames = [];
$errors = [];
$laundryItems = Laundry::whereIn('id', $request->ids)->get();
foreach ($laundryItems as $laundry) {
try {
// Hapus foto jika ada
if ($laundry->foto && File::exists(public_path('uploads/Laundry/' . $laundry->foto))) {
File::delete(public_path('uploads/Laundry/' . $laundry->foto));
}
$deletedNames[] = $laundry->nama;
$laundry->delete();
$deletedCount++;
// Log activity untuk setiap deletion
ActivityLog::log('delete', "Menghapus laundry: {$laundry->nama} (bulk)", 'Laundry', $laundry->id);
} catch (Exception $e) {
Log::error("Error menghapus laundry ID {$laundry->id}: " . $e->getMessage());
$errors[] = "Gagal menghapus {$laundry->nama}";
}
}
if ($deletedCount > 0) {
$message = "Berhasil menghapus {$deletedCount} data laundry!";
if (!empty($errors)) {
$message .= " Namun ada " . count($errors) . " data yang gagal dihapus.";
}
return redirect()->route('laundry.index')->with('success', $message);
} else {
return redirect()->route('laundry.index')
->with('error', 'Tidak ada data yang berhasil dihapus.');
}
} catch (\Illuminate\Validation\ValidationException $e) {
return redirect()->back()
->withErrors($e->validator);
} catch (Exception $e) {
Log::error('Error di Laundry Bulk Destroy: ' . $e->getMessage());
return redirect()->route('laundry.index')
->with('error', 'Terjadi kesalahan saat menghapus data: ' . $e->getMessage());
}
}
}