2e2b42f96b
- Add user_id FK (nullable) to rumah_ibadah for ownership tracking - IbadahController: set user_id on store; enforce ownership on update/destroy - Add edit modals to map and data views (ibadah + miskin) - Popup and table row Edit buttons with per-record ownership check - 7 new tests covering ownership and full-field updates - Fix ExampleTest for auth-protected routes - Fix UserFactory missing email_verified_at column - Mark Plan 2 complete in missing_features.md; update README Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
551 lines
30 KiB
PHP
551 lines
30 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>
|
|
</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>
|
|
@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">
|
|
<div class="overflow-x-auto">
|
|
<table class="table table-zebra w-full" id="tableIbadah">
|
|
<thead>
|
|
<tr class="text-xs font-bold uppercase opacity-50">
|
|
<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">
|
|
<div class="overflow-x-auto">
|
|
<table class="table table-zebra w-full" id="tableMiskin">
|
|
<thead>
|
|
<tr class="text-xs font-bold uppercase opacity-50">
|
|
<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');
|
|
|
|
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>
|
|
<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="7" 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>
|
|
<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="7" 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');
|
|
}
|
|
}
|
|
|
|
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>
|