Files
D1041231009-WebGIS-PovertyMap/resources/views/data.blade.php
T

637 lines
35 KiB
PHP

<!DOCTYPE html>
<html lang="id" data-theme="light">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>Data Tabular - WebGIS Pemetaan Kemiskinan</title>
<!-- DaisyUI + Tailwind -->
<link href="https://cdn.jsdelivr.net/npm/daisyui@4/dist/full.min.css" rel="stylesheet">
<script src="https://cdn.tailwindcss.com"></script>
<!-- Font Awesome -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
<!-- Plus Jakarta Sans Font -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:ital,wght@0,200..800;1,200..800&display=swap" rel="stylesheet">
<style>
* { font-family: 'Plus Jakarta Sans', sans-serif; }
body { font-weight: 500; background-color: #f8fafc; }
h1, h2, h3, h4, h5, h6, .btn, .card-title, .stat-value { font-weight: 700 !important; letter-spacing: -0.01em; }
</style>
</head>
<body class="min-h-screen">
<!-- ═══ NAVBAR ═══════════════════════════════════════════════════════════ -->
<div class="navbar bg-base-100 border-b border-base-200 shadow-sm sticky top-0 z-[1000]">
<div class="flex-1">
<a href="{{ url('/') }}" class="btn btn-ghost text-lg font-bold gap-2 text-primary">
<i class="fa-solid fa-house-chimney-crack"></i>
WebGIS Pemetaan Kemiskinan
</a>
</div>
<div class="flex-none">
<ul class="menu menu-horizontal px-1 gap-1">
<li><a href="{{ url('/map') }}" class="font-medium"><i class="fa-solid fa-map"></i> Peta</a></li>
<li><a href="{{ url('/data') }}" class="font-semibold bg-primary/10 text-primary rounded-lg"><i class="fa-solid fa-table-list"></i> Data Tabular</a></li>
<li>
<a href="{{ url('/dashboard') }}" class="font-medium">
<i class="fa-solid fa-chart-bar"></i> Dashboard
</a>
</li>
@if(auth()->user()->hasRole('administrator'))
<li>
<a href="{{ url('/admin/users') }}" class="font-medium">
<i class="fa-solid fa-users-gear"></i> Admin
</a>
</li>
@endif
</ul>
<!-- User dropdown -->
<div class="dropdown dropdown-end ml-2">
<div tabindex="0" role="button" class="btn btn-ghost gap-2">
<div class="avatar placeholder">
<div class="bg-primary text-primary-content rounded-full w-8">
<span class="text-xs font-bold">{{ substr(auth()->user()->name, 0, 1) }}</span>
</div>
</div>
<span class="hidden sm:inline text-sm font-semibold">{{ auth()->user()->name }}</span>
<i class="fa-solid fa-chevron-down text-xs opacity-50"></i>
</div>
<ul tabindex="0" class="dropdown-content menu bg-base-100 rounded-box z-50 w-52 p-2 shadow-lg border border-base-200 mt-2">
<li class="menu-title">
<span class="text-xs text-base-content/50 capitalize">{{ str_replace('_', ' ', auth()->user()->getRoleNames()->first() ?? 'user') }}</span>
</li>
<li>
<form method="POST" action="{{ route('logout') }}">
@csrf
<button type="submit" class="text-error w-full text-left flex items-center gap-2">
<i class="fa-solid fa-right-from-bracket"></i>
Keluar
</button>
</form>
</li>
</ul>
</div>
</div>
</div>
<!-- ═══ MAIN CONTENT ════════════════════════════════════════════════════ -->
<main class="max-w-screen-xl mx-auto px-4 py-8">
<!-- Header -->
<div class="flex flex-wrap items-center justify-between gap-4 mb-8">
<div>
<h1 class="text-3xl font-bold flex items-center gap-3">
<i class="fa-solid fa-database text-primary"></i>
Manajemen Data
</h1>
<p class="text-base-content/50 mt-1">Daftar rumah ibadah dan keluarga miskin dalam matriks tabular.</p>
</div>
<div class="flex flex-wrap gap-2">
<div class="join">
<input class="input input-bordered join-item input-md w-64" placeholder="Cari data..." id="searchInput" onkeyup="filterData()"/>
<button class="btn btn-md join-item btn-ghost border-base-300"><i class="fa-solid fa-search"></i></button>
</div>
@if(in_array(auth()->user()->getRoleNames()->first(), ['administrator', 'petugas_pendataan', 'pengurus_ibadah', 'pemangku_kebijakan']))
<button onclick="exportCSV()" class="btn btn-md btn-outline border-base-300 gap-2">
<i class="fa-solid fa-file-csv"></i> Ekspor CSV
</button>
<a href="{{ route('export.pdf.all') }}" target="_blank" class="btn btn-md btn-outline border-base-300 gap-2">
<i class="fa-solid fa-file-pdf text-error"></i> Ekspor PDF
</a>
@endif
<a href="{{ url('/map') }}" class="btn btn-md btn-primary shadow-lg gap-2">
<i class="fa-solid fa-plus"></i> Tambah Data
</a>
</div>
</div>
<!-- Stats Grid -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
<div class="stat bg-white shadow rounded-2xl border border-base-200">
<div class="stat-figure text-success text-3xl"><i class="fa-solid fa-mosque"></i></div>
<div class="stat-title text-xs uppercase font-bold opacity-50">Total Rumah Ibadah</div>
<div class="stat-value text-success" id="countIbadah">-</div>
</div>
<div class="stat bg-white shadow rounded-2xl border border-base-200">
<div class="stat-figure text-error text-3xl"><i class="fa-solid fa-house-user"></i></div>
<div class="stat-title text-xs uppercase font-bold opacity-50">Total Rumah Miskin</div>
<div class="stat-value text-error" id="countMiskin">-</div>
</div>
<div class="stat bg-white shadow rounded-2xl border border-base-200">
<div class="stat-figure text-warning text-3xl"><i class="fa-solid fa-users"></i></div>
<div class="stat-title text-xs uppercase font-bold opacity-50">Total Jiwa Terdata</div>
<div class="stat-value text-warning" id="countJiwa">-</div>
</div>
</div>
<!-- Tabs -->
<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>
<th>Pengurus/Telp</th>
<th>Radius</th>
<th>Koordinat</th>
<th class="text-center">Aksi</th>
</tr>
</thead>
<tbody id="bodyIbadah"></tbody>
</table>
</div>
</div>
<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>
<th>Pekerjaan</th>
<th>Kondisi</th>
<th>Koordinat</th>
<th class="text-center">Aksi</th>
</tr>
</thead>
<tbody id="bodyMiskin"></tbody>
</table>
</div>
</div>
</div>
</main>
<!-- Modal: Edit Rumah Ibadah -->
<dialog id="modal_edit_ibadah" class="modal">
<div class="modal-box w-11/12 max-w-lg">
<h3 class="font-bold text-lg mb-4">
<i class="fa-solid fa-mosque mr-2 text-success"></i>Edit Rumah Ibadah
</h3>
<input type="hidden" id="edit_ib_id">
<div class="grid grid-cols-2 gap-3">
<div class="form-control col-span-2">
<label class="label"><span class="label-text font-bold">Nama *</span></label>
<input id="edit_ib_nama" type="text" class="input input-bordered w-full" placeholder="Nama rumah ibadah">
</div>
<div class="form-control">
<label class="label"><span class="label-text font-bold">Jenis</span></label>
<select id="edit_ib_jenis" class="select select-bordered w-full">
<option value="">Pilih Jenis</option>
<option value="masjid">Masjid</option>
<option value="gereja">Gereja</option>
<option value="pura">Pura</option>
<option value="klenteng">Klenteng</option>
<option value="vihara">Vihara</option>
</select>
</div>
<div class="form-control">
<label class="label"><span class="label-text font-bold">Radius Analisis (m)</span></label>
<input id="edit_ib_radius" type="number" class="input input-bordered w-full" value="500" min="50" max="2000">
</div>
<div class="form-control col-span-2">
<label class="label"><span class="label-text font-bold">Alamat</span></label>
<textarea id="edit_ib_alamat" class="textarea textarea-bordered w-full" rows="2" placeholder="Alamat lengkap"></textarea>
</div>
<div class="form-control">
<label class="label"><span class="label-text font-bold">Pengurus</span></label>
<input id="edit_ib_pengurus" type="text" class="input input-bordered w-full" placeholder="Nama pengurus">
</div>
<div class="form-control">
<label class="label"><span class="label-text font-bold">Telepon</span></label>
<input id="edit_ib_telepon" type="text" class="input input-bordered w-full" placeholder="No. telepon">
</div>
<div class="form-control">
<label class="label"><span class="label-text font-bold">Jumlah Jemaah</span></label>
<input id="edit_ib_jemaah" type="number" class="input input-bordered w-full" value="0" min="0">
</div>
<div class="form-control col-span-2">
<label class="label"><span class="label-text font-bold">Keterangan</span></label>
<textarea id="edit_ib_ket" class="textarea textarea-bordered w-full" rows="2" placeholder="Keterangan tambahan"></textarea>
</div>
</div>
<div class="modal-action">
<button onclick="document.getElementById('modal_edit_ibadah').close()" class="btn btn-ghost">Batal</button>
<button onclick="saveEditIbadah()" class="btn btn-success">
<i class="fa-solid fa-floppy-disk mr-1"></i>Simpan Perubahan
</button>
</div>
</div>
<form method="dialog" class="modal-backdrop"><button>close</button></form>
</dialog>
<!-- Modal: Edit Keluarga Miskin -->
<dialog id="modal_edit_miskin" class="modal">
<div class="modal-box w-11/12 max-w-lg">
<h3 class="font-bold text-lg mb-4">
<i class="fa-solid fa-house mr-2 text-error"></i>Edit Keluarga Miskin
</h3>
<input type="hidden" id="edit_rm_id">
<div class="grid grid-cols-2 gap-3">
<div class="form-control col-span-2">
<label class="label"><span class="label-text font-bold">Nama Kepala Keluarga</span></label>
<input id="edit_rm_nama_kk" type="text" class="input input-bordered w-full" placeholder="Nama KK">
</div>
<div class="form-control">
<label class="label"><span class="label-text font-bold">Jumlah KK</span></label>
<input id="edit_rm_kk" type="number" class="input input-bordered w-full" value="1" min="1">
</div>
<div class="form-control">
<label class="label"><span class="label-text font-bold">Jumlah Jiwa</span></label>
<input id="edit_rm_jiwa" type="number" class="input input-bordered w-full" value="4" min="1">
</div>
<div class="form-control col-span-2">
<label class="label"><span class="label-text font-bold">Alamat</span></label>
<textarea id="edit_rm_alamat" class="textarea textarea-bordered w-full" rows="2" placeholder="Alamat lengkap"></textarea>
</div>
<div class="form-control">
<label class="label"><span class="label-text font-bold">Pekerjaan</span></label>
<input id="edit_rm_kerja" type="text" class="input input-bordered w-full" placeholder="Pekerjaan KK">
</div>
<div class="form-control">
<label class="label"><span class="label-text font-bold">Pendapatan (Rp)</span></label>
<input id="edit_rm_duit" type="number" class="input input-bordered w-full" value="0" min="0">
</div>
<div class="form-control">
<label class="label"><span class="label-text font-bold">Kondisi Rumah</span></label>
<select id="edit_rm_kondisi" class="select select-bordered w-full">
<option value="">Pilih Kondisi</option>
<option value="sedang">Sedang</option>
<option value="buruk">Buruk</option>
<option value="sangat_buruk">Sangat Buruk</option>
</select>
</div>
<div class="form-control col-span-2">
<label class="label"><span class="label-text font-bold">Keterangan</span></label>
<textarea id="edit_rm_ket" class="textarea textarea-bordered w-full" rows="2" placeholder="Keterangan tambahan"></textarea>
</div>
</div>
<div class="modal-action">
<button onclick="document.getElementById('modal_edit_miskin').close()" class="btn btn-ghost">Batal</button>
<button onclick="saveEditMiskin()" class="btn btn-error">
<i class="fa-solid fa-floppy-disk mr-1"></i>Simpan Perubahan
</button>
</div>
</div>
<form method="dialog" class="modal-backdrop"><button>close</button></form>
</dialog>
<!-- Modal: Confirm Delete -->
<dialog id="deleteModal" class="modal">
<div class="modal-box max-w-sm text-center">
<div class="text-5xl mb-4 text-error"><i class="fa-solid fa-triangle-exclamation"></i></div>
<h3 class="font-bold text-xl mb-2">Hapus Item?</h3>
<p class="text-sm text-base-content/60 mb-6">Penghapusan tidak dapat dibatalkan.</p>
<div class="flex gap-2 justify-center">
<button class="btn btn-ghost px-8" onclick="deleteModal.close()">Batal</button>
<button class="btn btn-error px-8 text-white" id="confirmDeleteBtn">Konfirmasi Hapus</button>
</div>
</div>
</dialog>
<!-- Toast Container -->
<div class="toast toast-end toast-bottom z-[9999]" id="toastBox"></div>
<script>
const authUserRole = '{{ auth()->user()->getRoleNames()->first() ?? '' }}';
const authUserId = {{ auth()->id() }};
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);
const searchInput = document.getElementById('searchInput');
async function loadAllData() {
try {
const [ri, rm] = await Promise.all([
fetch('{{ url('/api/ibadah') }}').then(r => r.json()),
fetch('{{ url('/api/miskin') }}').then(r => r.json())
]);
ibadahData = ri;
miskinData = rm;
renderTables();
} catch (e) { showToast('Gagal memuat data', 'error'); }
}
function renderTables(filteredI = null, filteredM = null) {
const dataI = filteredI || ibadahData;
const dataM = filteredM || miskinData;
// Stats
document.getElementById('countIbadah').innerText = ibadahData.length;
document.getElementById('countMiskin').innerText = miskinData.length;
document.getElementById('countJiwa').innerText = miskinData.reduce((acc, h) => acc + parseInt(h.jumlah_orang), 0);
// Ibadah Table
const bodyI = document.getElementById('bodyIbadah');
bodyI.innerHTML = dataI.length ? dataI.map((item, i) => {
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>
<div class="text-[10px] opacity-50 truncate max-w-[200px]">${item.alamat}</div>
</td>
<td class="capitalize text-xs">${item.jenis || '-'}</td>
<td class="text-xs">
<div class="font-semibold">${item.pengurus || '-'}</div>
<div class="opacity-50">${item.telepon || '-'}</div>
</td>
<td><span class="badge badge-outline badge-sm">${item.radius}m</span></td>
<td class="font-mono text-[10px]">${parseFloat(item.latitude).toFixed(4)}, ${parseFloat(item.longitude).toFixed(4)}</td>
<td class="text-center">
<div class="flex gap-1 justify-center">
${canEditThis ? `<button class="btn btn-xs btn-warning btn-outline" onclick="openEditIbadahById(${item.id})"><i class="fa-solid fa-pen"></i></button>` : ''}
${canEditThis ? `<button class="btn btn-xs btn-error btn-outline" onclick="prepDelete('ibadah', ${item.id})"><i class="fa-solid fa-trash"></i></button>` : ''}
</div>
</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>
<div class="text-[10px] opacity-50 truncate max-w-[200px]">${item.alamat || '-'}</div>
</td>
<td>
<div class="badge badge-warning badge-xs">${item.jumlah_kk} KK</div>
<div class="badge badge-info badge-xs">${item.jumlah_orang} Jiwa</div>
</td>
<td class="text-xs">${item.pekerjaan || '-'}</td>
<td class="capitalize text-xs">${item.kondisi_rumah || '-'}</td>
<td class="font-mono text-[10px]">${parseFloat(item.latitude).toFixed(4)}, ${parseFloat(item.longitude).toFixed(4)}</td>
<td class="text-center">
<div class="flex gap-1 justify-center">
${canEditMiskin ? `<button class="btn btn-xs btn-warning btn-outline" onclick="openEditMiskinById('${item.id_rumah}')"><i class="fa-solid fa-pen"></i></button>` : ''}
${canEditMiskin ? `<button class="btn btn-xs btn-error btn-outline" onclick="prepDelete('miskin', '${item.id_rumah}')"><i class="fa-solid fa-trash"></i></button>` : ''}
</div>
</td>
</tr>
`).join('') : '<tr><td colspan="8" class="text-center py-10 opacity-30 italic">Belum ada data</td></tr>';
}
function filterData() {
const query = searchInput.value.toLowerCase();
const filteredI = ibadahData.filter(i =>
i.nama.toLowerCase().includes(query) ||
(i.alamat && i.alamat.toLowerCase().includes(query)) ||
(i.pengurus && i.pengurus.toLowerCase().includes(query))
);
const filteredM = miskinData.filter(m =>
(m.nama_kk && m.nama_kk.toLowerCase().includes(query)) ||
(m.alamat && m.alamat.toLowerCase().includes(query)) ||
m.id_rumah.toLowerCase().includes(query)
);
renderTables(filteredI, filteredM);
}
function exportCSV() {
const activeTab = document.querySelector('input[name="data_tabs"]:checked').getAttribute('aria-label');
let csvContent = "data:text/csv;charset=utf-8,";
if (activeTab.includes("Ibadah")) {
csvContent += "ID,Nama,Jenis,Alamat,Radius,Jemaah,Pengurus,Telepon,Lat,Lon\n";
ibadahData.forEach(i => {
csvContent += `${i.id},"${i.nama}","${i.jenis}","${i.alamat}",${i.radius},${i.jumlah_jemaah},"${i.pengurus}","${i.telepon}",${i.latitude},${i.longitude}\n`;
});
downloadFile(csvContent, "data_rumah_ibadah.csv");
} else {
csvContent += "ID Rumah,Nama KK,Alamat,KK,Jiwa,Pekerjaan,Pendapatan,Kondisi,Lat,Lon\n";
miskinData.forEach(m => {
csvContent += `"${m.id_rumah}","${m.nama_kk}","${m.alamat}",${m.jumlah_kk},${m.jumlah_orang},"${m.pekerjaan}",${m.pendapatan},"${m.kondisi_rumah}",${m.latitude},${m.longitude}\n`;
});
downloadFile(csvContent, "data_rumah_miskin.csv");
}
}
function downloadFile(content, filename) {
const encodedUri = encodeURI(content);
const link = document.createElement("a");
link.setAttribute("href", encodedUri);
link.setAttribute("download", filename);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
// ── EDIT FORMS ──────────────────────────────────────────────
function openEditIbadahById(id) {
const item = ibadahData.find(i => i.id === id);
if (!item) return;
document.getElementById('edit_ib_id').value = item.id;
document.getElementById('edit_ib_nama').value = item.nama || '';
document.getElementById('edit_ib_jenis').value = item.jenis || '';
document.getElementById('edit_ib_alamat').value = item.alamat || '';
document.getElementById('edit_ib_pengurus').value = item.pengurus || '';
document.getElementById('edit_ib_telepon').value = item.telepon || '';
document.getElementById('edit_ib_jemaah').value = item.jumlah_jemaah || 0;
document.getElementById('edit_ib_radius').value = item.radius || 500;
document.getElementById('edit_ib_ket').value = item.keterangan || '';
document.getElementById('modal_edit_ibadah').showModal();
}
async function saveEditIbadah() {
const id = document.getElementById('edit_ib_id').value;
const item = ibadahData.find(i => i.id == id);
if (!item) return;
const body = {
nama: document.getElementById('edit_ib_nama').value,
jenis: document.getElementById('edit_ib_jenis').value,
alamat: document.getElementById('edit_ib_alamat').value,
pengurus: document.getElementById('edit_ib_pengurus').value,
telepon: document.getElementById('edit_ib_telepon').value,
jumlah_jemaah: parseInt(document.getElementById('edit_ib_jemaah').value) || 0,
radius: parseInt(document.getElementById('edit_ib_radius').value) || 500,
keterangan: document.getElementById('edit_ib_ket').value,
latitude: item.latitude,
longitude: item.longitude,
};
const res = await fetch(`{{ url('/api/ibadah') }}/${id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken },
body: JSON.stringify(body),
});
const resJson = await res.json();
if (resJson.success) {
document.getElementById('modal_edit_ibadah').close();
showToast('Data berhasil diperbarui', 'success');
await loadAllData();
} else if (res.status === 403) {
showToast(resJson.message || 'Tidak diizinkan mengubah data ini', 'error');
} else {
showToast('Gagal memperbarui data', 'error');
}
}
function openEditMiskinById(idRumah) {
const item = miskinData.find(i => i.id_rumah === idRumah);
if (!item) return;
document.getElementById('edit_rm_id').value = item.id_rumah;
document.getElementById('edit_rm_nama_kk').value = item.nama_kk || '';
document.getElementById('edit_rm_kk').value = item.jumlah_kk || 1;
document.getElementById('edit_rm_jiwa').value = item.jumlah_orang || 4;
document.getElementById('edit_rm_alamat').value = item.alamat || '';
document.getElementById('edit_rm_kerja').value = item.pekerjaan || '';
document.getElementById('edit_rm_duit').value = item.pendapatan || 0;
document.getElementById('edit_rm_kondisi').value = item.kondisi_rumah || '';
document.getElementById('edit_rm_ket').value = item.keterangan || '';
document.getElementById('modal_edit_miskin').showModal();
}
async function saveEditMiskin() {
const idRumah = document.getElementById('edit_rm_id').value;
const item = miskinData.find(i => i.id_rumah === idRumah);
if (!item) return;
const body = {
nama_kk: document.getElementById('edit_rm_nama_kk').value,
jumlah_kk: parseInt(document.getElementById('edit_rm_kk').value) || 1,
jumlah_orang: parseInt(document.getElementById('edit_rm_jiwa').value) || 1,
alamat: document.getElementById('edit_rm_alamat').value,
pekerjaan: document.getElementById('edit_rm_kerja').value,
pendapatan: parseInt(document.getElementById('edit_rm_duit').value) || 0,
kondisi_rumah: document.getElementById('edit_rm_kondisi').value,
keterangan: document.getElementById('edit_rm_ket').value,
latitude: item.latitude,
longitude: item.longitude,
};
const res = await fetch(`{{ url('/api/miskin') }}/${idRumah}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken },
body: JSON.stringify(body),
});
const resJson = await res.json();
if (resJson.success) {
document.getElementById('modal_edit_miskin').close();
showToast('Data berhasil diperbarui', 'success');
await loadAllData();
} else {
showToast('Gagal memperbarui data', 'error');
}
}
// ── 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 };
deleteModal.showModal();
}
document.getElementById('confirmDeleteBtn').onclick = async () => {
const { type, id } = deleteTarget;
const res = await fetch(`{{ url('/api') }}/${type}/${id}`, {
method: 'DELETE',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken }
});
const resJson = await res.json();
if (resJson.success) {
showToast('Data dihapus', 'success');
deleteModal.close();
loadAllData();
} else showToast('Gagal menghapus', 'error');
};
function showToast(msg, type = 'info') {
const t = document.createElement('div');
const alertCls = type === 'success' ? 'alert-success' : type === 'error' ? 'alert-error' : 'alert-info';
t.className = `alert ${alertCls} shadow-lg py-3 text-xs font-bold`;
t.innerHTML = `<span><i class="fa-solid fa-circle-info mr-2"></i>${msg}</span>`;
document.getElementById('toastBox').appendChild(t);
setTimeout(() => t.remove(), 3000);
}
</script>
</body>
</html>