feat: add bulk delete for ibadah and miskin with checkboxes (admin-only)
This commit is contained in:
@@ -72,4 +72,18 @@ class IbadahController extends Controller
|
||||
ActivityLog::record('delete', "Menghapus rumah ibadah: {$ibadah->nama}", 'ibadah', $id);
|
||||
return response()->json(['success' => true]);
|
||||
}
|
||||
|
||||
public function bulkDestroy(Request $request)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'ids' => 'required|array|min:1',
|
||||
'ids.*' => 'integer',
|
||||
]);
|
||||
|
||||
$count = RumahIbadah::whereIn('id', $validated['ids'])->count();
|
||||
RumahIbadah::whereIn('id', $validated['ids'])->delete();
|
||||
|
||||
ActivityLog::record('bulk_delete', "Menghapus massal {$count} rumah ibadah", 'ibadah');
|
||||
return response()->json(['success' => true, 'deleted' => $count]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,4 +62,18 @@ class MiskinController extends Controller
|
||||
ActivityLog::record('delete', "Menghapus keluarga miskin: {$id_rumah}", 'miskin', $id_rumah);
|
||||
return response()->json(['success' => true]);
|
||||
}
|
||||
|
||||
public function bulkDestroy(Request $request)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'ids' => 'required|array|min:1',
|
||||
'ids.*' => 'string',
|
||||
]);
|
||||
|
||||
$count = RumahMiskin::whereIn('id_rumah', $validated['ids'])->count();
|
||||
RumahMiskin::whereIn('id_rumah', $validated['ids'])->delete();
|
||||
|
||||
ActivityLog::record('bulk_delete', "Menghapus massal {$count} keluarga miskin", 'miskin');
|
||||
return response()->json(['success' => true, 'deleted' => $count]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,10 +132,19 @@
|
||||
<div role="tablist" class="tabs tabs-lifted tabs-lg mb-6">
|
||||
<input type="radio" name="data_tabs" role="tab" class="tab font-bold" aria-label="🕌 Rumah Ibadah" checked />
|
||||
<div role="tabpanel" class="tab-content bg-base-100 border-base-300 rounded-box p-6 shadow-sm">
|
||||
@if(auth()->user()->hasRole('administrator'))
|
||||
<div id="bulkBar_ibadah" class="hidden alert alert-warning py-2 mb-2 flex items-center justify-between">
|
||||
<span class="text-sm font-semibold"><span id="bulkCount_ibadah">0</span> item terpilih</span>
|
||||
<button onclick="bulkDelete('ibadah')" class="btn btn-xs btn-error text-white"><i class="fa-solid fa-trash mr-1"></i>Hapus Terpilih</button>
|
||||
</div>
|
||||
@endif
|
||||
<div class="overflow-x-auto">
|
||||
<table class="table table-zebra w-full" id="tableIbadah">
|
||||
<thead>
|
||||
<tr class="text-xs font-bold uppercase opacity-50">
|
||||
@if(auth()->user()->hasRole('administrator'))
|
||||
<th><input type="checkbox" class="checkbox checkbox-xs" onchange="toggleSelectAll('ibadah', this.checked)"></th>
|
||||
@endif
|
||||
<th>#</th>
|
||||
<th>Nama</th>
|
||||
<th>Jenis</th>
|
||||
@@ -152,10 +161,19 @@
|
||||
|
||||
<input type="radio" name="data_tabs" role="tab" class="tab font-bold" aria-label="🏠 Rumah Miskin" />
|
||||
<div role="tabpanel" class="tab-content bg-base-100 border-base-300 rounded-box p-6 shadow-sm">
|
||||
@if(auth()->user()->hasRole('administrator'))
|
||||
<div id="bulkBar_miskin" class="hidden alert alert-warning py-2 mb-2 flex items-center justify-between">
|
||||
<span class="text-sm font-semibold"><span id="bulkCount_miskin">0</span> item terpilih</span>
|
||||
<button onclick="bulkDelete('miskin')" class="btn btn-xs btn-error text-white"><i class="fa-solid fa-trash mr-1"></i>Hapus Terpilih</button>
|
||||
</div>
|
||||
@endif
|
||||
<div class="overflow-x-auto">
|
||||
<table class="table table-zebra w-full" id="tableMiskin">
|
||||
<thead>
|
||||
<tr class="text-xs font-bold uppercase opacity-50">
|
||||
@if(auth()->user()->hasRole('administrator'))
|
||||
<th><input type="checkbox" class="checkbox checkbox-xs" onchange="toggleSelectAll('miskin', this.checked)"></th>
|
||||
@endif
|
||||
<th>ID</th>
|
||||
<th>Nama KK</th>
|
||||
<th>KK/Jiwa</th>
|
||||
@@ -308,6 +326,9 @@
|
||||
const canEditIbadah = ['administrator', 'pengurus_ibadah'].includes(authUserRole);
|
||||
const canEditMiskin = ['administrator', 'petugas_pendataan'].includes(authUserRole);
|
||||
const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
|
||||
const isAdmin = authUserRole === 'administrator';
|
||||
let selectedIbadah = new Set();
|
||||
let selectedMiskin = new Set();
|
||||
|
||||
let ibadahData = [], miskinData = [];
|
||||
document.addEventListener('DOMContentLoaded', loadAllData);
|
||||
@@ -340,6 +361,7 @@
|
||||
const canEditThis = canEditIbadah && (authUserRole === 'administrator' || item.user_id === authUserId);
|
||||
return `
|
||||
<tr>
|
||||
${isAdmin ? `<td><input type="checkbox" class="checkbox checkbox-xs" ${selectedIbadah.has(String(item.id)) ? 'checked' : ''} onchange="toggleSelect('ibadah', '${item.id}', this.checked)"></td>` : ''}
|
||||
<td class="text-xs font-bold opacity-30">${i + 1}</td>
|
||||
<td>
|
||||
<div class="font-bold text-primary">${item.nama}</div>
|
||||
@@ -359,12 +381,13 @@
|
||||
</div>
|
||||
</td>
|
||||
</tr>`;
|
||||
}).join('') : '<tr><td colspan="7" class="text-center py-10 opacity-30 italic">Belum ada data</td></tr>';
|
||||
}).join('') : '<tr><td colspan="8" class="text-center py-10 opacity-30 italic">Belum ada data</td></tr>';
|
||||
|
||||
// Miskin Table
|
||||
const bodyM = document.getElementById('bodyMiskin');
|
||||
bodyM.innerHTML = dataM.length ? dataM.map(item => `
|
||||
<tr>
|
||||
${isAdmin ? `<td><input type="checkbox" class="checkbox checkbox-xs" ${selectedMiskin.has(String(item.id_rumah)) ? 'checked' : ''} onchange="toggleSelect('miskin', '${item.id_rumah}', this.checked)"></td>` : ''}
|
||||
<td class="font-bold text-error">${item.id_rumah}</td>
|
||||
<td>
|
||||
<div class="font-bold">${item.nama_kk || '-'}</div>
|
||||
@@ -384,7 +407,7 @@
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
`).join('') : '<tr><td colspan="7" class="text-center py-10 opacity-30 italic">Belum ada data</td></tr>';
|
||||
`).join('') : '<tr><td colspan="8" class="text-center py-10 opacity-30 italic">Belum ada data</td></tr>';
|
||||
}
|
||||
|
||||
function filterData() {
|
||||
@@ -532,6 +555,54 @@
|
||||
}
|
||||
}
|
||||
|
||||
// ── BULK DELETE ─────────────────────────────────────────────
|
||||
function toggleSelect(type, id, checked) {
|
||||
const set = type === 'ibadah' ? selectedIbadah : selectedMiskin;
|
||||
if (checked) set.add(String(id)); else set.delete(String(id));
|
||||
updateBulkBar(type);
|
||||
}
|
||||
|
||||
function toggleSelectAll(type, checked) {
|
||||
const set = type === 'ibadah' ? selectedIbadah : selectedMiskin;
|
||||
set.clear();
|
||||
if (checked) {
|
||||
(type === 'ibadah' ? ibadahData : miskinData).forEach(it =>
|
||||
set.add(String(type === 'ibadah' ? it.id : it.id_rumah)));
|
||||
}
|
||||
renderTables();
|
||||
updateBulkBar(type);
|
||||
}
|
||||
|
||||
function updateBulkBar(type) {
|
||||
const set = type === 'ibadah' ? selectedIbadah : selectedMiskin;
|
||||
const bar = document.getElementById(`bulkBar_${type}`);
|
||||
if (!bar) return;
|
||||
bar.classList.toggle('hidden', set.size === 0);
|
||||
document.getElementById(`bulkCount_${type}`).textContent = set.size;
|
||||
}
|
||||
|
||||
async function bulkDelete(type) {
|
||||
const set = type === 'ibadah' ? selectedIbadah : selectedMiskin;
|
||||
const ids = [...set];
|
||||
if (!ids.length) return;
|
||||
if (!confirm(`Hapus ${ids.length} data terpilih secara permanen?`)) return;
|
||||
|
||||
const res = await fetch(`{{ url('/api/admin') }}/${type}/bulk-delete`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken },
|
||||
body: JSON.stringify({ ids }),
|
||||
});
|
||||
const json = await res.json();
|
||||
if (json.success) {
|
||||
showToast(`${json.deleted} data dihapus`, 'success');
|
||||
set.clear();
|
||||
updateBulkBar(type);
|
||||
loadAllData();
|
||||
} else {
|
||||
showToast('Gagal hapus massal', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
let deleteTarget = null;
|
||||
function prepDelete(type, id) {
|
||||
deleteTarget = { type, id };
|
||||
|
||||
@@ -57,6 +57,9 @@ Route::middleware('auth')->group(function () {
|
||||
Route::put('/users/{id}', [AdminUserController::class, 'update']);
|
||||
Route::delete('/users/{id}', [AdminUserController::class, 'destroy']);
|
||||
Route::put('/users/{id}/password', [AdminUserController::class, 'resetPassword']);
|
||||
|
||||
Route::post('/ibadah/bulk-delete', [IbadahController::class, 'bulkDestroy']);
|
||||
Route::post('/miskin/bulk-delete', [MiskinController::class, 'bulkDestroy']);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user