fix: update final code deployment

This commit is contained in:
muthianura
2026-06-15 21:53:17 +07:00
parent 62a5a26fe0
commit 9cc6fcf52d
42 changed files with 177 additions and 23 deletions
+17
View File
@@ -0,0 +1,17 @@
FROM php:8.2-apache
RUN docker-php-ext-install mysqli && docker-php-ext-enable mysqli \
&& apt-get update && apt-get install -y libzip-dev \
&& docker-php-ext-install zip \
&& rm -rf /var/lib/apt/lists/*
RUN a2enmod rewrite
COPY . /var/www/html/
RUN mkdir -p /var/www/html/uploads/foto_rumah \
&& chown -R www-data:www-data /var/www/html \
&& chmod -R 755 /var/www/html \
&& chmod -R 775 /var/www/html/uploads
EXPOSE 80
+12
View File
@@ -0,0 +1,12 @@
<?php
define('DB_HOST', getenv('DB_HOST'));
define('DB_USER', getenv('DB_USER'));
define('DB_PASS', getenv('DB_PASS'));
define('DB_NAME', getenv('DB_NAME'));
$conn = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME);
$conn->set_charset('utf8mb4');
if ($conn->connect_error) {
die("Koneksi gagal: " . $conn->connect_error);
}
?>
+300
View File
@@ -0,0 +1,300 @@
<?php
session_start();
if(!isset($_SESSION['user'])) { header('Location: login.php'); exit; }
if($_SESSION['role']!=='superadmin') { header('Location: index.php'); exit; }
require_once 'config.php';
$total_penduduk = $conn->query("SELECT COUNT(*) c FROM penduduk_miskin")->fetch_assoc()['c'];
$total_anggota = $conn->query("SELECT SUM(jumlah_anggota) c FROM penduduk_miskin")->fetch_assoc()['c'] ?? 0;
$total_ibadah = $conn->query("SELECT COUNT(*) c FROM rumah_ibadah")->fetch_assoc()['c'];
$terdaftar = $conn->query("SELECT COUNT(*) c FROM penduduk_miskin WHERE rumah_ibadah_id IS NOT NULL")->fetch_assoc()['c'];
$belum = $total_penduduk - $terdaftar;
$per_ibadah = $conn->query("SELECT ri.nama, COUNT(pm.id) AS jumlah FROM rumah_ibadah ri LEFT JOIN penduduk_miskin pm ON pm.rumah_ibadah_id=ri.id GROUP BY ri.id ORDER BY jumlah DESC");
$jenis_ibadah= $conn->query("SELECT jenis, COUNT(*) c FROM rumah_ibadah GROUP BY jenis ORDER BY c DESC");
$jenis_colors= ['Masjid'=>'blue','Gereja'=>'violet','Pura'=>'amber','Vihara'=>'green','Kelenteng'=>'cyan','Lainnya'=>'gray'];
$rows_ibadah=[]; $max=1;
while($r=$per_ibadah->fetch_assoc()){ $rows_ibadah[]=$r; if($r['jumlah']>$max) $max=$r['jumlah']; }
$rows_jenis=[];
while($r=$jenis_ibadah->fetch_assoc()) $rows_jenis[]=$r;
// Pagination logic for latest assistance distributions
$limit = 15;
$page = isset($_GET['page']) ? max(1, intval($_GET['page'])) : 1;
$offset = ($page - 1) * $limit;
// Count total records
$total_bantuan = $conn->query("SELECT COUNT(*) c FROM riwayat_bantuan")->fetch_assoc()['c'];
$total_pages = ceil($total_bantuan / $limit);
$recent_bantuan = $conn->query("
SELECT rb.*, pm.nama_kk, ri.nama as nama_ibadah, ri2.nama as ibadah_terdaftar
FROM riwayat_bantuan rb
JOIN penduduk_miskin pm ON rb.penduduk_id = pm.id
LEFT JOIN rumah_ibadah ri ON rb.rumah_ibadah_id = ri.id
LEFT JOIN rumah_ibadah ri2 ON pm.rumah_ibadah_id = ri2.id
ORDER BY rb.tanggal_pemberian DESC, rb.id DESC
LIMIT $limit OFFSET $offset
");
$current = basename($_SERVER['PHP_SELF']);
?>
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>Dashboard — Pemetaan Kemiskinan Terpadu</title>
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&family=DM+Mono:wght@400;500&display=swap" rel="stylesheet">
<style>
:root{--bg:#f0f4f8;--white:#fff;--border:#e2e8f0;--text:#1e293b;--text2:#64748b;--text3:#94a3b8;--blue:#2563eb;--blue-lt:#dbeafe;--green:#16a34a;--green-lt:#dcfce7;--red:#dc2626;--red-lt:#fee2e2;--amber:#d97706;--amber-lt:#fef3c7;--violet:#7c3aed;--violet-lt:#ede9fe;--cyan:#0891b2;--cyan-lt:#cffafe;--sidebar:240px;--radius:12px;--shadow:0 1px 3px rgba(0,0,0,.08),0 4px 16px rgba(0,0,0,.06)}
*{box-sizing:border-box;margin:0;padding:0}body{font-family:'DM Sans',sans-serif;background:var(--bg);color:var(--text);min-height:100vh;display:flex}
#sidebar{width:var(--sidebar);min-width:var(--sidebar);background:var(--white);border-right:1px solid var(--border);display:flex;flex-direction:column;position:fixed;top:0;left:0;height:100vh;box-shadow:2px 0 8px rgba(0,0,0,.05);z-index:100}
.sb-brand{padding:20px 16px 16px;border-bottom:1px solid var(--border)}.sb-brand-logo{width:40px;height:40px;border-radius:10px;background:linear-gradient(135deg,#7c3aed,#2563eb);display:flex;align-items:center;justify-content:center;font-size:18px;margin-bottom:10px}.sb-brand h1{font-size:13px;font-weight:700;color:var(--text);line-height:1.3}.sb-brand p{font-size:10px;color:var(--text3);margin-top:2px}
.sb-nav{flex:1;overflow-y:auto;padding:10px 8px}.sb-lbl{font-size:9px;font-weight:700;color:var(--text3);text-transform:uppercase;letter-spacing:.8px;padding:10px 8px 4px}
.sb-item{display:flex;align-items:center;gap:9px;padding:9px 10px;border-radius:8px;cursor:pointer;font-size:13px;font-weight:500;color:var(--text2);transition:all .15s;text-decoration:none;margin-bottom:2px;border:1px solid transparent}.sb-item:hover{background:var(--bg);color:var(--text)}.sb-item.active{background:var(--violet-lt);color:var(--violet);border-color:#ddd6fe;font-weight:600}.sb-item .ico{font-size:15px;width:20px;text-align:center}
.sb-footer{padding:10px;border-top:1px solid var(--border)}
.btn-logout{width:100%;padding:8px;background:var(--red-lt);border:1px solid #fca5a5;border-radius:8px;font-size:12px;color:var(--red);cursor:pointer;font-family:inherit;font-weight:600;display:flex;align-items:center;justify-content:center;gap:6px;text-decoration:none;transition:all .15s}.btn-logout:hover{background:var(--red);color:#fff}
#main{margin-left:var(--sidebar);flex:1;display:flex;flex-direction:column;min-height:100vh}
.topbar{
height:56px;background:var(--white);border-bottom:1px solid var(--border);
display:flex;align-items:center;padding:0 28px;position:sticky;top:0;z-index:50;
}
.topbar h2{font-size:16px;font-weight:700;flex:1}
.topbar span{font-size:12px;color:var(--text3)}
.content{padding:28px;flex:1}
.stats-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));gap:14px;margin-bottom:24px}
.stat-card{
background:var(--white);border:1px solid var(--border);border-radius:12px;
padding:18px 18px 16px;
}
.stat-label{font-size:11px;font-weight:600;color:var(--text3);text-transform:uppercase;letter-spacing:.04em;margin-bottom:10px}
.stat-n{font-size:30px;font-weight:700;font-family:'DM Mono',monospace;line-height:1}
.stat-n.blue{color:var(--blue)}.stat-n.green{color:var(--green)}.stat-n.violet{color:var(--violet)}
.stat-n.amber{color:var(--amber)}.stat-n.red{color:var(--red)}
.stat-sub{font-size:12px;color:var(--text2);margin-top:6px}
.grid2{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-bottom:20px}
@media(max-width:900px){.grid2{grid-template-columns:1fr}}
.card{background:var(--white);border:1px solid var(--border);border-radius:12px;overflow:hidden}
.card-head{padding:16px 18px;border-bottom:1px solid var(--border)}
.card-head h3{font-size:14px;font-weight:700}
.card-head p{font-size:12px;color:var(--text3);margin-top:3px}
.card-body{padding:16px 18px}
.bar-row{display:flex;align-items:center;gap:10px;margin-bottom:10px;font-size:12px}
.bar-lbl{width:130px;flex-shrink:0;color:var(--text2);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.bar-track{flex:1;background:var(--bg);border-radius:4px;height:7px;overflow:hidden}
.bar-fill{height:100%;border-radius:4px;background:var(--blue)}
.bar-val{width:24px;text-align:right;font-weight:700;font-family:'DM Mono',monospace;font-size:12px;flex-shrink:0}
.jenis-row{display:flex;align-items:center;justify-content:space-between;padding:9px 0;border-bottom:1px solid var(--border)}
.jenis-row:last-child{border-bottom:none}
.badge{display:inline-flex;padding:3px 10px;border-radius:6px;font-size:11px;font-weight:600}
.badge-blue{background:var(--blue-lt);color:var(--blue)}
.badge-green{background:var(--green-lt);color:var(--green)}
.badge-violet{background:var(--violet-lt);color:var(--violet)}
.badge-amber{background:var(--amber-lt);color:var(--amber)}
.badge-cyan{background:var(--cyan-lt);color:var(--cyan)}
.badge-gray{background:var(--bg);color:var(--text2)}
.jenis-count{font-size:13px;font-weight:600;font-family:'DM Mono',monospace}
.shortcut-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px}
@media(max-width:700px){.shortcut-grid{grid-template-columns:1fr}}
.shortcut{
background:var(--white);border:1px solid var(--border);border-radius:12px;
padding:16px 18px;text-decoration:none;display:flex;align-items:flex-start;gap:12px;
transition:border-color .15s,box-shadow .15s;
}
.shortcut:hover{border-color:#93c5fd;box-shadow:0 4px 14px rgba(29,78,216,.08)}
.shortcut-icon{
width:36px;height:36px;border-radius:9px;background:var(--bg);
display:flex;align-items:center;justify-content:center;flex-shrink:0;
border:1px solid var(--border);
}
.shortcut-icon svg{width:18px;height:18px;color:var(--blue)}
.shortcut-lbl{font-size:13px;font-weight:700;color:var(--text)}
.shortcut-sub{font-size:11px;color:var(--text3);margin-top:3px}
.pagination { display: flex; align-items: center; justify-content: center; gap: 6px; padding: 16px; border-top: 1px solid var(--border); }
.pagination a {
padding: 6px 12px; border: 1px solid var(--border); border-radius: 6px;
background: var(--white); color: var(--text2); font-size: 12px; font-weight: 600;
text-decoration: none; transition: all .15s;
}
.pagination a:hover { border-color: var(--blue); color: var(--blue); background: var(--blue-lt); }
.pagination span { font-size: 12px; color: var(--text3); font-weight: 500; }
</style>
</head>
<body>
<aside id="sidebar">
<div class="sb-brand"><div class="sb-brand-logo"><svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="#fff" stroke-width="2"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"></path><circle cx="12" cy="10" r="3"></circle></svg></div><h1>Kemiskinan Terpadu</h1><p>Panel Superadmin</p></div>
<nav class="sb-nav">
<div class="sb-lbl">Menu</div>
<a href="dashboard.php" class="sb-item <?= $current==='dashboard.php'?'active':'' ?>"><span class="ico"><svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/></svg></span> Dashboard</a>
<a href="data_penduduk.php" class="sb-item <?= $current==='data_penduduk.php'?'active':'' ?>"><span class="ico"><svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2"><path d="M17 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 00-3-3.87M16 3.13a4 4 0 010 7.75"/></svg></span> Data Penduduk</a>
<a href="data_ibadah.php" class="sb-item <?= $current==='data_ibadah.php'?'active':'' ?>"><span class="ico"><svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 21h18M5 21V7l7-4 7 4v14"/><path d="M9 21v-6h6v6"/></svg></span> Rumah Ibadah</a>
<div class="sb-lbl">Navigasi</div>
<a href="index.php?readonly=1" class="sb-item" target="_blank"><span class="ico"><svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2"><polygon points="1 6 1 22 8 18 16 22 23 18 23 2 16 6 8 2 1 6"/><line x1="8" y1="2" x2="8" y2="18"/><line x1="16" y1="6" x2="16" y2="22"/></svg></span> Lihat Peta GIS</a>
</nav>
<div class="sb-footer">
<?php include __DIR__.'/includes/user-profile.php'; ?>
<a href="logout.php" class="btn-logout" data-confirm-logout>Keluar</a>
</div>
</aside>
<?php include __DIR__.'/includes/confirm-modal.php'; ?>
<main id="main">
<div class="topbar">
<h2>Dashboard</h2>
<span>Diperbarui <?= date('d M Y, H:i') ?> WIB</span>
</div>
<div class="content">
<div class="stats-grid">
<div class="stat-card">
<div class="stat-label">KK Penduduk Miskin</div>
<div class="stat-n blue"><?= $total_penduduk ?></div>
<div class="stat-sub">Kepala keluarga terdata</div>
</div>
<div class="stat-card">
<div class="stat-label">Total Jiwa</div>
<div class="stat-n green"><?= $total_anggota ?></div>
<div class="stat-sub">Anggota keluarga</div>
</div>
<div class="stat-card">
<div class="stat-label">Rumah Ibadah</div>
<div class="stat-n violet"><?= $total_ibadah ?></div>
<div class="stat-sub">Titik ibadah aktif</div>
</div>
<div class="stat-card">
<div class="stat-label">Terdaftar Radius</div>
<div class="stat-n amber"><?= $terdaftar ?></div>
<div class="stat-sub">KK dalam cakupan</div>
</div>
<div class="stat-card">
<div class="stat-label">Belum Terdaftar</div>
<div class="stat-n red"><?= $belum ?></div>
<div class="stat-sub">Di luar radius ibadah</div>
</div>
</div>
<div class="grid2">
<div class="card">
<div class="card-head">
<h3>Binaan per Rumah Ibadah</h3>
<p>Jumlah KK dalam radius tiap tempat ibadah</p>
</div>
<div class="card-body">
<?php foreach($rows_ibadah as $r): $pct=$max>0?round($r['jumlah']/$max*100):0; ?>
<div class="bar-row">
<div class="bar-lbl" title="<?= htmlspecialchars($r['nama']) ?>"><?= htmlspecialchars($r['nama']) ?></div>
<div class="bar-track"><div class="bar-fill" style="width:<?= $pct ?>%"></div></div>
<div class="bar-val"><?= $r['jumlah'] ?></div>
</div>
<?php endforeach ?>
</div>
</div>
<div class="card">
<div class="card-head">
<h3>Sebaran Jenis Rumah Ibadah</h3>
<p>Komposisi berdasarkan jenis bangunan</p>
</div>
<div class="card-body">
<?php foreach($rows_jenis as $r): $col=$jenis_colors[$r['jenis']]??'gray'; ?>
<div class="jenis-row">
<span class="badge badge-<?= $col ?>"><?= htmlspecialchars($r['jenis']) ?></span>
<span class="jenis-count"><?= $r['c'] ?> tempat</span>
</div>
<?php endforeach ?>
</div>
</div>
</div>
<div class="shortcut-grid" style="margin-bottom: 24px">
<a href="data_penduduk.php" class="shortcut">
<div class="shortcut-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M17 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2"/><circle cx="9" cy="7" r="4"/></svg>
</div>
<div>
<div class="shortcut-lbl">Data Penduduk</div>
<div class="shortcut-sub"><?= $total_penduduk ?> KK · <?= $total_anggota ?> jiwa</div>
</div>
</a>
<a href="data_ibadah.php" class="shortcut">
<div class="shortcut-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 21h18M5 21V7l7-4 7 4v14"/></svg>
</div>
<div>
<div class="shortcut-lbl">Rumah Ibadah</div>
<div class="shortcut-sub"><?= $total_ibadah ?> tempat ibadah</div>
</div>
</a>
</div>
<!-- Card Log Pembagian Bantuan Terbaru -->
<div class="card" style="margin-bottom: 24px">
<div class="card-head">
<h3>Log Pembagian Bantuan Terbaru</h3>
<p>Aktivitas penyaluran bantuan sosial kepada keluarga binaan</p>
</div>
<div class="card-body" style="padding: 0; overflow-x: auto;">
<table style="width: 100%; border-collapse: collapse; font-size: 13px; text-align: left;">
<thead>
<tr style="background: #f8fafc; border-bottom: 1px solid var(--border)">
<th style="padding: 12px 16px; font-weight: 600; color: var(--text2)">Nama Kepala Keluarga</th>
<th style="padding: 12px 16px; font-weight: 600; color: var(--text2)">Lokasi Tempat Ibadah</th>
<th style="padding: 12px 16px; font-weight: 600; color: var(--text2)">Jenis Bantuan</th>
<th style="padding: 12px 16px; font-weight: 600; color: var(--text2)">Tanggal Diberikan</th>
<th style="padding: 12px 16px; font-weight: 600; color: var(--text2)">Keterangan</th>
</tr>
</thead>
<tbody>
<?php if ($recent_bantuan->num_rows > 0): ?>
<?php while($rb = $recent_bantuan->fetch_assoc()): ?>
<tr style="border-bottom: 1px solid #f1f5f9">
<td style="padding: 12px 16px; font-weight: 600">
<?= htmlspecialchars($rb['nama_kk']) ?>
<?php if ($rb['nama_ibadah']): ?>
<br><span style="font-size: 11px; color: var(--blue); font-weight: normal;"><svg viewBox="0 0 24 24" width="10" height="10" fill="none" stroke="currentColor" stroke-width="2" style="display:inline-block;vertical-align:middle;margin-right:3px;margin-top:-2px"><path d="M3 21h18M5 21V7l7-4 7 4v14"/><path d="M9 21v-6h6v6"/></svg><?= htmlspecialchars($rb['nama_ibadah']) ?></span>
<?php endif; ?>
</td>
<td style="padding: 12px 16px;">
<?php if ($rb['ibadah_terdaftar']): ?>
<span style="font-weight: 600; display: inline-flex; align-items: center; gap: 4px; color: var(--blue)">
<svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2" style="display:inline-block;vertical-align:middle"><path d="M3 21h18M5 21V7l7-4 7 4v14"/><path d="M9 21v-6h6v6"/></svg>
<?= htmlspecialchars($rb['ibadah_terdaftar']) ?>
</span>
<?php else: ?>
<span style="color: var(--red); font-weight: 500;">❌ Belum Terdaftar</span>
<?php endif; ?>
</td>
<td style="padding: 12px 16px;"><span class="badge badge-green" style="font-weight:normal"><?= htmlspecialchars($rb['jenis_bantuan']) ?></span></td>
<td style="padding: 12px 16px; font-family: 'DM Mono', monospace; color: var(--text2)"><?= date('d/m/Y', strtotime($rb['tanggal_pemberian'])) ?></td>
<td style="padding: 12px 16px; color: var(--text2)"><?= htmlspecialchars($rb['keterangan'] ?? '—') ?></td>
</tr>
<?php endwhile; ?>
<?php else: ?>
<tr>
<td colspan="5" style="padding: 24px; text-align: center; color: var(--text3)">Belum ada riwayat pembagian bantuan.</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
<?php if ($total_pages > 1): ?>
<div class="pagination">
<?php if ($page > 1): ?>
<a href="?page=<?= $page - 1 ?>"> Seb</a>
<?php endif; ?>
<span>Halaman <?= $page ?> dari <?= $total_pages ?></span>
<?php if ($page < $total_pages): ?>
<a href="?page=<?= $page + 1 ?>">Sel </a>
<?php endif; ?>
</div>
<?php endif; ?>
</div>
</div>
</main>
</body></html>
+175
View File
@@ -0,0 +1,175 @@
<?php
session_start();
if(!isset($_SESSION['user'])) { header('Location: login.php'); exit; }
if($_SESSION['role']!=='superadmin') { header('Location: index.php'); exit; }
require_once 'config.php';
$per_page = 15;
$page = max(1, intval($_GET['page'] ?? 1));
$cari = trim($_GET['cari'] ?? '');
$offset = ($page - 1) * $per_page;
$where = '';
if($cari !== '') {
$c = $conn->real_escape_string($cari);
$where = "WHERE ri.nama LIKE '%$c%' OR ri.alamat LIKE '%$c%' OR ri.pic LIKE '%$c%' OR ri.jenis LIKE '%$c%'";
}
$total_rows = $conn->query("SELECT COUNT(*) c FROM rumah_ibadah ri $where")->fetch_assoc()['c'];
$total_pages = max(1, ceil($total_rows / $per_page));
$page = min($page, $total_pages);
$offset = ($page - 1) * $per_page;
$data = $conn->query("
SELECT ri.*, COUNT(pm.id) AS jumlah_binaan, SUM(pm.jumlah_anggota) AS total_jiwa
FROM rumah_ibadah ri
LEFT JOIN penduduk_miskin pm ON pm.rumah_ibadah_id = ri.id
$where
GROUP BY ri.id ORDER BY ri.nama ASC
LIMIT $per_page OFFSET $offset
");
$all = $conn->query("SELECT ri.nama,ri.jenis,ri.alamat,ri.pic,ri.no_wa,ri.radius,COUNT(pm.id) AS jumlah_binaan,SUM(pm.jumlah_anggota) AS total_jiwa FROM rumah_ibadah ri LEFT JOIN penduduk_miskin pm ON pm.rumah_ibadah_id=ri.id GROUP BY ri.id ORDER BY ri.nama");
$all_rows=[];
while($r=$all->fetch_assoc()) $all_rows[]=$r;
$jenis_colors=['Masjid'=>'blue','Gereja'=>'violet','Pura'=>'amber','Vihara'=>'green','Kelenteng'=>'cyan','Lainnya'=>'gray'];
$current = basename($_SERVER['PHP_SELF']);
function pag_url($p,$cari){
$q=$cari!==''?'&cari='.urlencode($cari):'';
return '?page='.$p.$q;
}
?>
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>Rumah Ibadah — Pemetaan Kemiskinan Terpadu</title>
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@300;400;500;600;700;800&family=DM+Mono:wght@400;500&display=swap" rel="stylesheet">
<style>
:root{--bg:#f0f4f8;--white:#fff;--border:#e2e8f0;--text:#1e293b;--text2:#64748b;--text3:#94a3b8;--blue:#2563eb;--blue-lt:#dbeafe;--green:#16a34a;--green-lt:#dcfce7;--red:#dc2626;--red-lt:#fee2e2;--amber:#d97706;--amber-lt:#fef3c7;--violet:#7c3aed;--violet-lt:#ede9fe;--cyan:#0891b2;--cyan-lt:#cffafe;--sidebar:240px;--radius:12px;--shadow:0 1px 3px rgba(0,0,0,.08),0 4px 16px rgba(0,0,0,.06)}
*{box-sizing:border-box;margin:0;padding:0}body{font-family:'DM Sans',sans-serif;background:var(--bg);color:var(--text);min-height:100vh;display:flex}
#sidebar{width:var(--sidebar);min-width:var(--sidebar);background:var(--white);border-right:1px solid var(--border);display:flex;flex-direction:column;position:fixed;top:0;left:0;height:100vh;box-shadow:2px 0 8px rgba(0,0,0,.05);z-index:100}
.sb-brand{padding:20px 16px 16px;border-bottom:1px solid var(--border)}.sb-brand-logo{width:40px;height:40px;border-radius:10px;background:linear-gradient(135deg,#7c3aed,#2563eb);display:flex;align-items:center;justify-content:center;font-size:18px;margin-bottom:10px}.sb-brand h1{font-size:13px;font-weight:700;color:var(--text);line-height:1.3}.sb-brand p{font-size:10px;color:var(--text3);margin-top:2px}
.sb-nav{flex:1;overflow-y:auto;padding:10px 8px}.sb-lbl{font-size:9px;font-weight:700;color:var(--text3);text-transform:uppercase;letter-spacing:.8px;padding:10px 8px 4px}
.sb-item{display:flex;align-items:center;gap:9px;padding:9px 10px;border-radius:8px;cursor:pointer;font-size:13px;font-weight:500;color:var(--text2);transition:all .15s;text-decoration:none;margin-bottom:2px;border:1px solid transparent}.sb-item:hover{background:var(--bg);color:var(--text)}.sb-item.active{background:var(--violet-lt);color:var(--violet);border-color:#ddd6fe;font-weight:600}.sb-item .ico{font-size:15px;width:20px;text-align:center}
.sb-footer{padding:10px;border-top:1px solid var(--border)}
.btn-logout{width:100%;padding:8px;background:var(--red-lt);border:1px solid #fca5a5;border-radius:8px;font-size:12px;color:var(--red);cursor:pointer;font-family:inherit;font-weight:600;display:flex;align-items:center;justify-content:center;gap:6px;text-decoration:none;transition:all .15s}.btn-logout:hover{background:var(--red);color:#fff}
#main{margin-left:var(--sidebar);flex:1;display:flex;flex-direction:column;min-height:100vh}
.topbar{height:56px;background:var(--white);border-bottom:1px solid var(--border);display:flex;align-items:center;padding:0 24px;gap:12px;position:sticky;top:0;z-index:50}.topbar h2{font-size:15px;font-weight:700;flex:1}.topbar span{font-size:12px;color:var(--text3)}
.content{padding:24px;flex:1}
.page-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:16px}
.page-title h2{font-size:17px;font-weight:800}.page-title p{font-size:12px;color:var(--text3);margin-top:2px}
.btn-dl{display:inline-flex;align-items:center;gap:7px;padding:8px 16px;background:var(--green);color:#fff;border:none;border-radius:8px;font-size:13px;font-weight:600;cursor:pointer;font-family:inherit;transition:background .15s}.btn-dl:hover{background:#15803d}
.tbl-wrap{background:var(--white);border:1px solid var(--border);border-radius:var(--radius);overflow:hidden;box-shadow:var(--shadow)}
.tbl-toolbar{padding:12px 14px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:10px}
.tbl-toolbar form{display:flex;gap:8px;flex:1}.tbl-toolbar input{flex:1;padding:7px 10px;border:1px solid var(--border);border-radius:8px;font-family:inherit;font-size:12.5px;color:var(--text);background:var(--bg);outline:none}.tbl-toolbar input:focus{border-color:var(--blue);background:#fff}
.tbl-toolbar button{padding:7px 14px;background:var(--blue);color:#fff;border:none;border-radius:8px;font-size:12.5px;font-weight:600;cursor:pointer;font-family:inherit}
.tbl-info{font-size:12px;color:var(--text3);white-space:nowrap}
table{width:100%;border-collapse:collapse;font-size:12.5px}
thead th{padding:10px 14px;text-align:left;font-size:10px;font-weight:700;color:var(--text3);text-transform:uppercase;letter-spacing:.5px;background:var(--bg);border-bottom:1px solid var(--border);white-space:nowrap}
tbody td{padding:10px 14px;border-bottom:1px solid var(--border);vertical-align:middle}
tbody tr:last-child td{border-bottom:none}tbody tr:hover{background:#fafbfc}
.badge{display:inline-flex;align-items:center;padding:2px 8px;border-radius:20px;font-size:10px;font-weight:700}
.badge-blue{background:var(--blue-lt);color:var(--blue)}.badge-green{background:var(--green-lt);color:var(--green)}.badge-red{background:var(--red-lt);color:var(--red)}.badge-amber{background:var(--amber-lt);color:var(--amber)}.badge-violet{background:var(--violet-lt);color:var(--violet)}.badge-cyan{background:var(--cyan-lt);color:var(--cyan)}.badge-gray{background:var(--bg);color:var(--text2);border:1px solid var(--border)}
.pagination{display:flex;align-items:center;gap:5px;padding:12px 14px;border-top:1px solid var(--border);flex-wrap:wrap}
.pag-btn{padding:5px 11px;border:1px solid var(--border);border-radius:7px;background:var(--white);font-size:12px;font-weight:600;cursor:pointer;color:var(--text2);text-decoration:none;transition:all .15s;display:inline-flex;align-items:center}
.pag-btn:hover{border-color:var(--blue);color:var(--blue)}.pag-btn.active{background:var(--blue);color:#fff;border-color:var(--blue)}.pag-btn.disabled{opacity:.4;pointer-events:none}
.pag-info{font-size:12px;color:var(--text3);margin-left:auto}
</style>
</head>
<body>
<aside id="sidebar">
<div class="sb-brand"><div class="sb-brand-logo"><svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="#fff" stroke-width="2"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"></path><circle cx="12" cy="10" r="3"></circle></svg></div><h1>Kemiskinan Terpadu</h1><p>Panel Superadmin</p></div>
<nav class="sb-nav">
<div class="sb-lbl">Menu</div>
<a href="dashboard.php" class="sb-item <?= $current==='dashboard.php'?'active':'' ?>"><span class="ico"><svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/></svg></span> Dashboard</a>
<a href="data_penduduk.php" class="sb-item <?= $current==='data_penduduk.php'?'active':'' ?>"><span class="ico"><svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2"><path d="M17 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 00-3-3.87M16 3.13a4 4 0 010 7.75"/></svg></span> Data Penduduk</a>
<a href="data_ibadah.php" class="sb-item <?= $current==='data_ibadah.php'?'active':'' ?>"><span class="ico"><svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 21h18M5 21V7l7-4 7 4v14"/><path d="M9 21v-6h6v6"/></svg></span> Rumah Ibadah</a>
<div class="sb-lbl">Navigasi</div>
<a href="index.php?readonly=1" class="sb-item" target="_blank"><span class="ico"><svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2"><polygon points="1 6 1 22 8 18 16 22 23 18 23 2 16 6 8 2 1 6"/><line x1="8" y1="2" x2="8" y2="18"/><line x1="16" y1="6" x2="16" y2="22"/></svg></span> Lihat Peta GIS</a>
</nav>
<div class="sb-footer">
<?php include __DIR__.'/includes/user-profile.php'; ?>
<a href="logout.php" class="btn-logout" data-confirm-logout>Keluar</a>
</div>
</aside>
<?php include __DIR__.'/includes/confirm-modal.php'; ?>
<main id="main">
<div class="topbar"><h2>Rumah Ibadah</h2><span>Total <?= $total_rows ?> tempat ibadah</span></div>
<div class="content">
<div class="page-head">
<div class="page-title">
<h2>Daftar Rumah Ibadah</h2>
<p>Halaman <?= $page ?> dari <?= $total_pages ?> · <?= $total_rows ?> data total</p>
</div>
<button class="btn-dl" onclick="downloadCSV()"><svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg> Download Excel</button>
</div>
<div class="tbl-wrap">
<div class="tbl-toolbar">
<form method="GET" action="">
<div style="position:relative;flex:1;display:flex;align-items:center;">
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" style="position:absolute;left:10px;color:var(--text3)"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
<input type="text" name="cari" value="<?= htmlspecialchars($cari) ?>" placeholder="Cari nama, jenis, PIC, alamat..." style="padding-left:32px;width:100%;">
</div>
<button type="submit">Cari</button>
<?php if($cari): ?><a href="data_ibadah.php" style="padding:7px 12px;border:1px solid var(--border);border-radius:8px;font-size:12px;color:var(--text2);text-decoration:none">✕ Reset</a><?php endif ?>
</form>
<span class="tbl-info"><?= $per_page*(($page-1)+1)-($per_page-1) ?><?= min($per_page*$page,$total_rows) ?> dari <?= $total_rows ?></span>
</div>
<table>
<thead>
<tr><th>#</th><th>Nama</th><th>Jenis</th><th>Alamat</th><th>PIC</th><th>No. WA</th><th>Radius</th><th>Binaan (KK)</th><th>Total Jiwa</th></tr>
</thead>
<tbody>
<?php $no=($page-1)*$per_page+1; while($r=$data->fetch_assoc()):
$col=$jenis_colors[$r['jenis']]??'gray'; ?>
<tr>
<td style="color:var(--text3);font-family:'DM Mono',monospace"><?= $no++ ?></td>
<td><b><?= htmlspecialchars($r['nama']) ?></b></td>
<td><span class="badge badge-<?= $col ?>"><?= $r['jenis'] ?></span></td>
<td style="color:var(--text2);max-width:160px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis" title="<?= htmlspecialchars($r['alamat']??'') ?>"><?= htmlspecialchars($r['alamat']??'—') ?></td>
<td><?= htmlspecialchars($r['pic']??'—') ?></td>
<td style="font-family:'DM Mono',monospace;font-size:11.5px"><?= htmlspecialchars($r['no_wa']??'—') ?></td>
<td style="font-family:'DM Mono',monospace"><?= number_format($r['radius']) ?> m</td>
<td style="text-align:center"><span class="badge <?= $r['jumlah_binaan']>0?'badge-green':'badge-gray' ?>"><?= $r['jumlah_binaan'] ?> KK</span></td>
<td style="text-align:center;font-family:'DM Mono',monospace"><?= $r['total_jiwa'] ?? 0 ?></td>
</tr>
<?php endwhile ?>
</tbody>
</table>
<div class="pagination">
<a href="<?= pag_url(1,$cari) ?>" class="pag-btn <?= $page<=1?'disabled':'' ?>">«</a>
<a href="<?= pag_url(max(1,$page-1),$cari) ?>" class="pag-btn <?= $page<=1?'disabled':'' ?>"></a>
<?php $from=max(1,$page-2);$to=min($total_pages,$page+2);
for($i=$from;$i<=$to;$i++): ?>
<a href="<?= pag_url($i,$cari) ?>" class="pag-btn <?= $i===$page?'active':'' ?>"><?= $i ?></a>
<?php endfor ?>
<a href="<?= pag_url(min($total_pages,$page+1),$cari) ?>" class="pag-btn <?= $page>=$total_pages?'disabled':'' ?>"></a>
<a href="<?= pag_url($total_pages,$cari) ?>" class="pag-btn <?= $page>=$total_pages?'disabled':'' ?>">»</a>
<span class="pag-info">Halaman <?= $page ?> / <?= $total_pages ?></span>
</div>
</div>
</div>
</main>
<script>
const allData = <?= json_encode($all_rows) ?>;
function downloadCSV(){
if(!allData.length) return;
const keys=['Nama','Jenis','Alamat','PIC','No WA','Radius (m)','Jumlah Binaan (KK)','Total Jiwa'];
const map =['nama','jenis','alamat','pic','no_wa','radius','jumlah_binaan','total_jiwa'];
const rows=[keys,...allData.map(r=>map.map(k=>'"'+(r[k]??'')+'"'))];
const csv='\uFEFF'+rows.map(r=>r.join(',')).join('\n');
const a=document.createElement('a');
a.href='data:text/csv;charset=utf-8,'+encodeURIComponent(csv);
a.download='rumah_ibadah_<?= date('Ymd') ?>.csv';
a.click();
}
</script>
</body></html>
+207
View File
@@ -0,0 +1,207 @@
<?php
session_start();
if(!isset($_SESSION['user'])) { header('Location: login.php'); exit; }
if($_SESSION['role']!=='superadmin') { header('Location: index.php'); exit; }
require_once 'config.php';
$per_page = 15;
$page = max(1, intval($_GET['page'] ?? 1));
$cari = trim($_GET['cari'] ?? '');
$offset = ($page - 1) * $per_page;
$where = '';
if($cari !== '') {
$c = $conn->real_escape_string($cari);
$where = "WHERE pm.nama_kk LIKE '%$c%' OR pm.alamat LIKE '%$c%' OR ri.nama LIKE '%$c%'";
}
$total_rows = $conn->query("SELECT COUNT(*) c FROM penduduk_miskin pm LEFT JOIN rumah_ibadah ri ON ri.id=pm.rumah_ibadah_id $where")->fetch_assoc()['c'];
$total_pages= max(1, ceil($total_rows / $per_page));
$page = min($page, $total_pages);
$offset = ($page - 1) * $per_page;
$data = $conn->query("
SELECT pm.*, ri.nama AS nama_ibadah, ri.jenis AS jenis_ibadah,
(SELECT GROUP_CONCAT(CONCAT(rb.jenis_bantuan, ' (', DATE_FORMAT(rb.tanggal_pemberian, '%d/%m/%Y'), ')') ORDER BY rb.tanggal_pemberian DESC SEPARATOR ', ')
FROM riwayat_bantuan rb WHERE rb.penduduk_id = pm.id) AS riwayat_bantuan_list
FROM penduduk_miskin pm
LEFT JOIN rumah_ibadah ri ON ri.id = pm.rumah_ibadah_id
$where
ORDER BY pm.id ASC
LIMIT $per_page OFFSET $offset
");
// Untuk download CSV — ambil semua tanpa limit
$all = $conn->query("
SELECT pm.nama_kk, pm.jumlah_anggota, pm.alamat,
(SELECT GROUP_CONCAT(CONCAT(rb.jenis_bantuan, ' (', DATE_FORMAT(rb.tanggal_pemberian, '%d/%m/%Y'), ')') ORDER BY rb.tanggal_pemberian DESC SEPARATOR '; ')
FROM riwayat_bantuan rb WHERE rb.penduduk_id = pm.id) AS riwayat_bantuan_list,
ri.nama AS nama_ibadah, ri.jenis AS jenis_ibadah, pm.jarak_ke_ibadah,
IF(pm.rumah_ibadah_id IS NOT NULL,'Terdaftar','Belum Terdaftar') AS status, pm.lat, pm.lng
FROM penduduk_miskin pm
LEFT JOIN rumah_ibadah ri ON ri.id=pm.rumah_ibadah_id
ORDER BY pm.id ASC
");
$all_rows = [];
while($r=$all->fetch_assoc()) $all_rows[]=$r;
$jenis_colors=['Masjid'=>'blue','Gereja'=>'violet','Pura'=>'amber','Vihara'=>'green','Kelenteng'=>'cyan','Lainnya'=>'gray'];
$current = basename($_SERVER['PHP_SELF']);
function pag_url($p, $cari) {
$q = $cari !== '' ? '&cari='.urlencode($cari) : '';
return '?page='.$p.$q;
}
?>
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>Data Penduduk — Pemetaan Kemiskinan Terpadu</title>
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@300;400;500;600;700;800&family=DM+Mono:wght@400;500&display=swap" rel="stylesheet">
<style>
:root{--bg:#f0f4f8;--white:#fff;--border:#e2e8f0;--text:#1e293b;--text2:#64748b;--text3:#94a3b8;--blue:#2563eb;--blue-lt:#dbeafe;--green:#16a34a;--green-lt:#dcfce7;--red:#dc2626;--red-lt:#fee2e2;--amber:#d97706;--amber-lt:#fef3c7;--violet:#7c3aed;--violet-lt:#ede9fe;--cyan:#0891b2;--cyan-lt:#cffafe;--sidebar:240px;--radius:12px;--shadow:0 1px 3px rgba(0,0,0,.08),0 4px 16px rgba(0,0,0,.06)}
*{box-sizing:border-box;margin:0;padding:0}body{font-family:'DM Sans',sans-serif;background:var(--bg);color:var(--text);min-height:100vh;display:flex}
#sidebar{width:var(--sidebar);min-width:var(--sidebar);background:var(--white);border-right:1px solid var(--border);display:flex;flex-direction:column;position:fixed;top:0;left:0;height:100vh;box-shadow:2px 0 8px rgba(0,0,0,.05);z-index:100}
.sb-brand{padding:20px 16px 16px;border-bottom:1px solid var(--border)}.sb-brand-logo{width:40px;height:40px;border-radius:10px;background:linear-gradient(135deg,#7c3aed,#2563eb);display:flex;align-items:center;justify-content:center;font-size:18px;margin-bottom:10px}.sb-brand h1{font-size:13px;font-weight:700;color:var(--text);line-height:1.3}.sb-brand p{font-size:10px;color:var(--text3);margin-top:2px}
.sb-nav{flex:1;overflow-y:auto;padding:10px 8px}.sb-lbl{font-size:9px;font-weight:700;color:var(--text3);text-transform:uppercase;letter-spacing:.8px;padding:10px 8px 4px}
.sb-item{display:flex;align-items:center;gap:9px;padding:9px 10px;border-radius:8px;cursor:pointer;font-size:13px;font-weight:500;color:var(--text2);transition:all .15s;text-decoration:none;margin-bottom:2px;border:1px solid transparent}.sb-item:hover{background:var(--bg);color:var(--text)}.sb-item.active{background:var(--violet-lt);color:var(--violet);border-color:#ddd6fe;font-weight:600}.sb-item .ico{font-size:15px;width:20px;text-align:center}
.sb-footer{padding:10px;border-top:1px solid var(--border)}
.btn-logout{width:100%;padding:8px;background:var(--red-lt);border:1px solid #fca5a5;border-radius:8px;font-size:12px;color:var(--red);cursor:pointer;font-family:inherit;font-weight:600;display:flex;align-items:center;justify-content:center;gap:6px;text-decoration:none;transition:all .15s}.btn-logout:hover{background:var(--red);color:#fff}
#main{margin-left:var(--sidebar);flex:1;display:flex;flex-direction:column;min-height:100vh}
.topbar{height:56px;background:var(--white);border-bottom:1px solid var(--border);display:flex;align-items:center;padding:0 24px;gap:12px;position:sticky;top:0;z-index:50}.topbar h2{font-size:15px;font-weight:700;flex:1}.topbar span{font-size:12px;color:var(--text3)}
.content{padding:24px;flex:1}
.page-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:16px}
.page-title h2{font-size:17px;font-weight:800}.page-title p{font-size:12px;color:var(--text3);margin-top:2px}
.btn-dl{display:inline-flex;align-items:center;gap:7px;padding:8px 16px;background:var(--green);color:#fff;border:none;border-radius:8px;font-size:13px;font-weight:600;cursor:pointer;font-family:inherit;transition:background .15s}.btn-dl:hover{background:#15803d}
.tbl-wrap{background:var(--white);border:1px solid var(--border);border-radius:var(--radius);overflow:hidden;box-shadow:var(--shadow)}
.tbl-toolbar{padding:12px 14px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:10px}
.tbl-toolbar form{display:flex;gap:8px;flex:1}
.tbl-toolbar input{flex:1;padding:7px 10px;border:1px solid var(--border);border-radius:8px;font-family:inherit;font-size:12.5px;color:var(--text);background:var(--bg);outline:none}.tbl-toolbar input:focus{border-color:var(--blue);background:#fff}
.tbl-toolbar button{padding:7px 14px;background:var(--blue);color:#fff;border:none;border-radius:8px;font-size:12.5px;font-weight:600;cursor:pointer;font-family:inherit}
.tbl-info{font-size:12px;color:var(--text3);white-space:nowrap}
table{width:100%;border-collapse:collapse;font-size:12.5px}
thead th{padding:10px 14px;text-align:left;font-size:10px;font-weight:700;color:var(--text3);text-transform:uppercase;letter-spacing:.5px;background:var(--bg);border-bottom:1px solid var(--border);white-space:nowrap}
tbody td{padding:10px 14px;border-bottom:1px solid var(--border);vertical-align:middle}
tbody tr:last-child td{border-bottom:none}tbody tr:hover{background:#fafbfc}
.badge{display:inline-flex;align-items:center;padding:2px 8px;border-radius:20px;font-size:10px;font-weight:700}
.badge-blue{background:var(--blue-lt);color:var(--blue)}.badge-green{background:var(--green-lt);color:var(--green)}.badge-red{background:var(--red-lt);color:var(--red)}.badge-amber{background:var(--amber-lt);color:var(--amber)}.badge-violet{background:var(--violet-lt);color:var(--violet)}.badge-cyan{background:var(--cyan-lt);color:var(--cyan)}.badge-gray{background:var(--bg);color:var(--text2);border:1px solid var(--border)}
.pagination{display:flex;align-items:center;gap:5px;padding:12px 14px;border-top:1px solid var(--border);flex-wrap:wrap}
.pag-btn{padding:5px 11px;border:1px solid var(--border);border-radius:7px;background:var(--white);font-size:12px;font-weight:600;cursor:pointer;color:var(--text2);text-decoration:none;transition:all .15s;font-family:inherit;display:inline-flex;align-items:center}
.pag-btn:hover{border-color:var(--blue);color:var(--blue)}.pag-btn.active{background:var(--blue);color:#fff;border-color:var(--blue)}.pag-btn.disabled{opacity:.4;pointer-events:none}
.pag-info{font-size:12px;color:var(--text3);margin-left:auto}
</style>
</head>
<body>
<aside id="sidebar">
<div class="sb-brand"><div class="sb-brand-logo"><svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="#fff" stroke-width="2"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"></path><circle cx="12" cy="10" r="3"></circle></svg></div><h1>Kemiskinan Terpadu</h1><p>Panel Superadmin</p></div>
<nav class="sb-nav">
<div class="sb-lbl">Menu</div>
<a href="dashboard.php" class="sb-item <?= $current==='dashboard.php'?'active':'' ?>"><span class="ico"><svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/></svg></span> Dashboard</a>
<a href="data_penduduk.php" class="sb-item <?= $current==='data_penduduk.php'?'active':'' ?>"><span class="ico"><svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2"><path d="M17 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 00-3-3.87M16 3.13a4 4 0 010 7.75"/></svg></span> Data Penduduk</a>
<a href="data_ibadah.php" class="sb-item <?= $current==='data_ibadah.php'?'active':'' ?>"><span class="ico"><svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 21h18M5 21V7l7-4 7 4v14"/><path d="M9 21v-6h6v6"/></svg></span> Rumah Ibadah</a>
<div class="sb-lbl">Navigasi</div>
<a href="index.php?readonly=1" class="sb-item" target="_blank"><span class="ico"><svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2"><polygon points="1 6 1 22 8 18 16 22 23 18 23 2 16 6 8 2 1 6"/><line x1="8" y1="2" x2="8" y2="18"/><line x1="16" y1="6" x2="16" y2="22"/></svg></span> Lihat Peta GIS</a>
</nav>
<div class="sb-footer">
<?php include __DIR__.'/includes/user-profile.php'; ?>
<a href="logout.php" class="btn-logout" data-confirm-logout>Keluar</a>
</div>
</aside>
<?php include __DIR__.'/includes/confirm-modal.php'; ?>
<main id="main">
<div class="topbar"><h2>Data Penduduk Miskin</h2><span>Total <?= $total_rows ?> KK ditemukan</span></div>
<div class="content">
<div class="page-head">
<div class="page-title">
<h2>Daftar Penduduk Miskin</h2>
<p>Menampilkan halaman <?= $page ?> dari <?= $total_pages ?> · <?= $total_rows ?> data total</p>
</div>
<button class="btn-dl" onclick="downloadCSV()"><svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg> Download Excel</button>
</div>
<div class="tbl-wrap">
<div class="tbl-toolbar">
<form method="GET" action="">
<div style="position:relative;flex:1;display:flex;align-items:center;">
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" style="position:absolute;left:10px;color:var(--text3)"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
<input type="text" name="cari" value="<?= htmlspecialchars($cari) ?>" placeholder="Cari nama KK, alamat, rumah ibadah..." style="padding-left:32px;width:100%;">
</div>
<button type="submit">Cari</button>
<?php if($cari): ?><a href="data_penduduk.php" style="padding:7px 12px;border:1px solid var(--border);border-radius:8px;font-size:12px;color:var(--text2);text-decoration:none">✕ Reset</a><?php endif ?>
</form>
<span class="tbl-info"><?= $per_page*(($page-1)+1)-($per_page-1) ?><?= min($per_page*$page,$total_rows) ?> dari <?= $total_rows ?></span>
</div>
<table>
<thead>
<tr>
<th>#</th><th>Nama KK</th><th>Anggota</th><th>Alamat</th>
<th>Riwayat Bantuan</th>
<th>Rumah Ibadah Terdekat</th><th>Jarak</th><th>Status</th><th>Foto</th>
</tr>
</thead>
<tbody>
<?php $no=($page-1)*$per_page+1; while($r=$data->fetch_assoc()): ?>
<tr>
<td style="color:var(--text3);font-family:'DM Mono',monospace"><?= $no++ ?></td>
<td><b><?= htmlspecialchars($r['nama_kk']) ?></b></td>
<td style="font-family:'DM Mono',monospace;text-align:center"><?= $r['jumlah_anggota'] ?></td>
<td style="color:var(--text2);max-width:180px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis" title="<?= htmlspecialchars($r['alamat']??'') ?>"><?= htmlspecialchars($r['alamat']??'—') ?></td>
<td>
<?php if($r['riwayat_bantuan_list']): ?>
<?php
$items = explode(', ', $r['riwayat_bantuan_list']);
foreach($items as $idx => $item):
if ($idx < 2): ?>
<span class="badge badge-green" style="margin:2px; font-weight:normal"><?= htmlspecialchars($item) ?></span>
<?php else: ?>
<span class="badge badge-gray" style="margin:2px; font-weight:normal" title="<?= htmlspecialchars($r['riwayat_bantuan_list']) ?>">+<?= count($items) - 2 ?> lainnya</span>
<?php break;
endif;
endforeach; ?>
<?php else: ?>
<span style="color:var(--text3)">Belum ada bantuan</span>
<?php endif; ?>
</td>
<td><?php if($r['nama_ibadah']): $col=$jenis_colors[$r['jenis_ibadah']]??'gray'; ?><span class="badge badge-<?= $col ?>"><?= htmlspecialchars($r['nama_ibadah']) ?></span><?php else: ?><span style="color:var(--text3)">—</span><?php endif ?></td>
<td style="font-family:'DM Mono',monospace;color:var(--text2)"><?= $r['jarak_ke_ibadah'] ? number_format($r['jarak_ke_ibadah'],0).' m' : '—' ?></td>
<td><?= $r['rumah_ibadah_id'] ? '<span class="badge badge-green"><svg viewBox="0 0 24 24" width="10" height="10" fill="none" stroke="currentColor" stroke-width="3" style="margin-right:4px"><polyline points="20 6 9 17 4 12"></polyline></svg>Terdaftar</span>' : '<span class="badge badge-red"><svg viewBox="0 0 24 24" width="10" height="10" fill="none" stroke="currentColor" stroke-width="3" style="margin-right:4px"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>Belum</span>' ?></td>
<td><?php if($r['foto_rumah']): ?><a href="<?= htmlspecialchars($r['foto_rumah']) ?>" target="_blank" style="display:flex;align-items:center;color:var(--blue);text-decoration:none" title="Lihat foto"><svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg></a><?php else: ?><span style="color:var(--text3)">—</span><?php endif ?></td>
</tr>
<?php endwhile ?>
</tbody>
</table>
<div class="pagination">
<a href="<?= pag_url(1,$cari) ?>" class="pag-btn <?= $page<=1?'disabled':'' ?>">«</a>
<a href="<?= pag_url(max(1,$page-1),$cari) ?>" class="pag-btn <?= $page<=1?'disabled':'' ?>"></a>
<?php
$from=max(1,$page-2); $to=min($total_pages,$page+2);
for($i=$from;$i<=$to;$i++): ?>
<a href="<?= pag_url($i,$cari) ?>" class="pag-btn <?= $i===$page?'active':'' ?>"><?= $i ?></a>
<?php endfor ?>
<a href="<?= pag_url(min($total_pages,$page+1),$cari) ?>" class="pag-btn <?= $page>=$total_pages?'disabled':'' ?>"></a>
<a href="<?= pag_url($total_pages,$cari) ?>" class="pag-btn <?= $page>=$total_pages?'disabled':'' ?>">»</a>
<span class="pag-info">Halaman <?= $page ?> / <?= $total_pages ?></span>
</div>
</div>
</div>
</main>
<script>
const allData = <?= json_encode($all_rows) ?>;
function downloadCSV(){
if(!allData.length) return;
const keys=['Nama KK','Jumlah Anggota','Alamat','Riwayat Penerimaan Bantuan','Rumah Ibadah','Jenis Ibadah','Jarak (m)','Status','Lat','Lng'];
const map =['nama_kk','jumlah_anggota','alamat','riwayat_bantuan_list','nama_ibadah','jenis_ibadah','jarak_ke_ibadah','status','lat','lng'];
const rows=[keys,...allData.map(r=>map.map(k=>'"'+(r[k]??'')+'"'))];
const csv='\uFEFF'+rows.map(r=>r.join(',')).join('\n');
const a=document.createElement('a');
a.href='data:text/csv;charset=utf-8,'+encodeURIComponent(csv);
a.download='penduduk_miskin_<?= date('Ymd') ?>.csv';
a.click();
}
</script>
</body></html>
+63
View File
@@ -0,0 +1,63 @@
-- ============================================
-- DATABASE: gis_penduduk_miskin
-- GIS Web App - Rumah Ibadah & Penduduk Miskin
-- ============================================
CREATE DATABASE IF NOT EXISTS gis_penduduk_miskin CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE gis_penduduk_miskin;
-- Tabel Rumah Ibadah
CREATE TABLE IF NOT EXISTS rumah_ibadah (
id INT AUTO_INCREMENT PRIMARY KEY,
nama VARCHAR(200) NOT NULL,
jenis ENUM('Masjid','Gereja','Pura','Vihara','Kelenteng','Lainnya') DEFAULT 'Masjid',
alamat TEXT,
pic VARCHAR(150) COMMENT 'Person In Charge',
no_wa VARCHAR(20),
lat DOUBLE NOT NULL,
lng DOUBLE NOT NULL,
radius INT DEFAULT 500 COMMENT 'Radius dalam meter',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
-- Tabel Penduduk Miskin
CREATE TABLE IF NOT EXISTS penduduk_miskin (
id INT AUTO_INCREMENT PRIMARY KEY,
nama_kk VARCHAR(200) NOT NULL COMMENT 'Nama Kepala Keluarga',
jumlah_anggota INT NOT NULL DEFAULT 1 COMMENT 'Jumlah Anggota KK',
alamat TEXT,
lat DOUBLE NOT NULL,
lng DOUBLE NOT NULL,
rumah_ibadah_id INT NULL COMMENT 'Rumah ibadah terdekat (FK)',
jarak_ke_ibadah DOUBLE NULL COMMENT 'Jarak dalam meter ke rumah ibadah terdekat',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (rumah_ibadah_id) REFERENCES rumah_ibadah(id) ON DELETE SET NULL
);
-- Index untuk performa query geospasial
CREATE INDEX idx_rumah_ibadah_coords ON rumah_ibadah(lat, lng);
CREATE INDEX idx_penduduk_coords ON penduduk_miskin(lat, lng);
CREATE INDEX idx_penduduk_ibadah ON penduduk_miskin(rumah_ibadah_id);
-- =====================
-- DATA CONTOH (Pontianak)
-- =====================
INSERT INTO rumah_ibadah (nama, jenis, alamat, pic, no_wa, lat, lng, radius) VALUES
('Masjid Raya Mujahidin', 'Masjid', 'Jl. Ahmad Yani, Pontianak Selatan', 'H. Abdurrahman', '081234567890', -0.0397, 109.3319, 500),
('Masjid Jami Pontianak', 'Masjid', 'Jl. Tanjungpura, Pontianak Kota', 'H. Mukhlis', '081234567891', -0.0263, 109.3413, 500),
('GBI Pontianak', 'Gereja', 'Jl. Gajahmada, Pontianak Kota', 'Pdt. Stevanus', '081234567892', -0.0280, 109.3350, 500),
('Vihara Bodhisattva Karaniya Metta', 'Vihara', 'Jl. Khatulistiwa, Pontianak Utara', 'Bhikkhu Santoso', '081234567893', -0.0120, 109.3250, 500);
INSERT INTO penduduk_miskin (nama_kk, jumlah_anggota, alamat, lat, lng) VALUES
('Budi Santoso', 4, 'Jl. Ahmad Yani Gg. 3, Pontianak', -0.0410, 109.3330),
('Siti Rahayu', 3, 'Jl. Ahmad Yani Gg. 5, Pontianak', -0.0380, 109.3310),
('Muhammad Amin', 5, 'Jl. Tanjungpura No. 12, Pontianak', -0.0255, 109.3420),
('Dewi Lestari', 2, 'Jl. Gajahmada No. 7, Pontianak', -0.0290, 109.3360),
('Hendra Kusuma', 6, 'Jl. Khatulistiwa Gg. 2, Pontianak', -0.0130, 109.3260),
('Yanti Wulandari', 3, 'Jl. Reformasi, Pontianak Utara', -0.0100, 109.3240),
('Agus Prasetyo', 4, 'Jl. Sultan Hamid, Pontianak Barat', -0.0450, 109.3200);
-- Update rumah_ibadah_id dan jarak untuk data contoh (akan dihitung via PHP)
+220
View File
@@ -0,0 +1,220 @@
-- phpMyAdmin SQL Dump
-- version 5.2.1
-- https://www.phpmyadmin.net/
--
-- Host: 127.0.0.1
-- Waktu pembuatan: 13 Jun 2026 pada 06.34
-- Versi server: 10.4.32-MariaDB
-- Versi PHP: 8.2.12
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
START TRANSACTION;
SET time_zone = "+00:00";
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;
--
-- Database: `gis_penduduk_miskin`
--
-- --------------------------------------------------------
--
-- Struktur dari tabel `penduduk_miskin`
--
CREATE TABLE `penduduk_miskin` (
`id` int(11) NOT NULL,
`nama_kk` varchar(200) NOT NULL COMMENT 'Nama Kepala Keluarga',
`jumlah_anggota` int(11) NOT NULL DEFAULT 1 COMMENT 'Jumlah Anggota KK',
`alamat` text DEFAULT NULL,
`foto_rumah` varchar(255) DEFAULT NULL COMMENT 'Path foto rumah penduduk',
`foto` varchar(255) DEFAULT NULL,
`lat` double NOT NULL,
`lng` double NOT NULL,
`rumah_ibadah_id` int(11) DEFAULT NULL COMMENT 'Rumah ibadah terdekat (FK)',
`jarak_ke_ibadah` double DEFAULT NULL COMMENT 'Jarak dalam meter ke rumah ibadah terdekat',
`created_at` timestamp NOT NULL DEFAULT current_timestamp(),
`updated_at` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
`jenis_bantuan` varchar(100) DEFAULT NULL,
`tanggal_bantuan` date DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
--
-- Dumping data untuk tabel `penduduk_miskin`
--
INSERT INTO `penduduk_miskin` (`id`, `nama_kk`, `jumlah_anggota`, `alamat`, `foto_rumah`, `foto`, `lat`, `lng`, `rumah_ibadah_id`, `jarak_ke_ibadah`, `created_at`, `updated_at`, `jenis_bantuan`, `tanggal_bantuan`) VALUES
(1, 'Budi Santoso', 4, 'Jl. Ahmad Yani Gg. 3, Pontianak', NULL, NULL, -0.041, 109.333, 1, 7.3496927573051005, '2026-04-21 00:39:18', '2026-04-27 23:39:22', NULL, NULL),
(2, 'Siti Rahayu', 3, 'Jl. Ahmad Yani Gg. 5, Pontianak', NULL, NULL, -0.038, 109.331, 1, 402.74089736140365, '2026-04-21 00:39:18', '2026-04-27 23:39:22', NULL, NULL),
(3, 'Muhammad Amin', 5, 'Jl. Tanjungpura No. 12, Pontianak', NULL, NULL, -0.0255, 109.342, 2, 118.20182314920238, '2026-04-21 00:39:18', '2026-06-11 07:43:32', 'BPH', '2026-06-11'),
(4, 'Dewi Lestari', 2, 'Jl. Gajahmada No. 7, Pontianak', 'uploads/foto_rumah/foto_4_1779805588.png', NULL, -0.029, 109.336, 6, 365.21444098246127, '2026-04-21 00:39:18', '2026-06-11 10:48:25', 'BPH', NULL),
(5, 'Hendra Kusuma', 6, 'Jl. Khatulistiwa Gg. 2, Pontianak', NULL, NULL, -0.013, 109.326, NULL, NULL, '2026-04-21 00:39:18', '2026-06-11 10:45:24', NULL, NULL),
(6, 'Yanti Wulandari', 3, 'Jl. Reformasi, Pontianak Utara', NULL, NULL, -0.01, 109.324, NULL, NULL, '2026-04-21 00:39:18', '2026-04-21 01:31:32', NULL, NULL),
(7, 'Agus Prasetyo', 4, 'Jl. Sultan Hamid, Pontianak Barat', NULL, NULL, -0.045, 109.32, 7, 424.59694642882687, '2026-04-21 00:39:18', '2026-06-11 10:51:32', NULL, NULL),
(8, 'Sumanto', 1, 'Jalan Teuku Umar, Pontianak Kota, Pontianak, Kalimantan Barat', NULL, NULL, -0.031044206655679432, 109.33486461639406, 6, 229.58461183844528, '2026-04-27 23:23:35', '2026-06-11 10:48:25', NULL, NULL),
(9, 'Andi', 1, 'Gang Dua, Pontianak Kota, Pontianak, Kalimantan Barat', 'uploads/foto_rumah/foto_9_1779806089.jpeg', NULL, -0.03261019378425077, 109.33256864547731, 6, 444.5282858786418, '2026-04-27 23:24:17', '2026-06-11 10:48:25', NULL, NULL);
-- --------------------------------------------------------
--
-- Struktur dari tabel `riwayat_bantuan`
--
CREATE TABLE `riwayat_bantuan` (
`id` int(11) NOT NULL,
`penduduk_id` int(11) NOT NULL,
`jenis_bantuan` varchar(100) NOT NULL,
`tanggal_pemberian` date NOT NULL,
`keterangan` text DEFAULT NULL,
`created_at` timestamp NOT NULL DEFAULT current_timestamp(),
`rumah_ibadah_id` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
--
-- Dumping data untuk tabel `riwayat_bantuan`
--
INSERT INTO `riwayat_bantuan` (`id`, `penduduk_id`, `jenis_bantuan`, `tanggal_pemberian`, `keterangan`, `created_at`, `rumah_ibadah_id`) VALUES
(1, 3, 'BPH', '2026-06-11', 'Bantuan Pangan Tahap I', '2026-06-11 07:43:32', NULL);
-- --------------------------------------------------------
--
-- Struktur dari tabel `rumah_ibadah`
--
CREATE TABLE `rumah_ibadah` (
`id` int(11) NOT NULL,
`nama` varchar(200) NOT NULL,
`jenis` enum('Masjid','Gereja','Pura','Vihara','Kelenteng','Lainnya') DEFAULT 'Masjid',
`alamat` text DEFAULT NULL,
`pic` varchar(150) DEFAULT NULL COMMENT 'Person In Charge',
`no_wa` varchar(20) DEFAULT NULL,
`lat` double NOT NULL,
`lng` double NOT NULL,
`radius` int(11) DEFAULT 500 COMMENT 'Radius dalam meter',
`created_at` timestamp NOT NULL DEFAULT current_timestamp(),
`updated_at` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp()
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
--
-- Dumping data untuk tabel `rumah_ibadah`
--
INSERT INTO `rumah_ibadah` (`id`, `nama`, `jenis`, `alamat`, `pic`, `no_wa`, `lat`, `lng`, `radius`, `created_at`, `updated_at`) VALUES
(1, 'Masjid Raya Mujahidin', 'Masjid', 'Jl. Ahmad Yani, Pontianak Selatan', 'H. Abdurrahman', '081234567890', -0.040977560852975214, 109.33306217193605, 500, '2026-04-21 00:39:18', '2026-04-27 23:39:22'),
(2, 'Masjid Jami Pontianak', 'Masjid', 'Jl. Tanjungpura, Pontianak Kota', 'H. Mukhlis', '081234567891', -0.0263, 109.3413, 500, '2026-04-21 00:39:18', '2026-04-27 13:30:18'),
(6, 'Vihara Paticca Samuppada', 'Vihara', 'Jalan Wage Rudolf Supratman, Pontianak Selatan, Pontianak, Kalimantan Barat', 'Vira', '0812090807', -0.032238236952232296, 109.33654904365541, 500, '2026-06-11 10:48:25', '2026-06-11 10:48:25'),
(7, 'GBI El Shaddai', 'Gereja', 'Jalan Mohammad Yamin, Pontianak Kota, Pontianak, Kubu Raya, Kalimantan Barat', 'Gerry', '08912192112', -0.048485944221699245, 109.31844145059587, 500, '2026-06-11 10:51:32', '2026-06-11 10:51:32');
-- --------------------------------------------------------
--
-- Struktur dari tabel `users`
--
CREATE TABLE `users` (
`id` int(11) NOT NULL,
`nama` varchar(100) NOT NULL,
`username` varchar(50) NOT NULL,
`password` varchar(255) NOT NULL,
`role` enum('admin','superadmin') NOT NULL DEFAULT 'admin',
`created_at` timestamp NOT NULL DEFAULT current_timestamp()
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
--
-- Dumping data untuk tabel `users`
--
INSERT INTO `users` (`id`, `nama`, `username`, `password`, `role`, `created_at`) VALUES
(1, 'Super Admin', 'superadmin', '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', 'superadmin', '2026-05-26 15:26:44'),
(2, 'Admin', 'admin', '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', 'admin', '2026-05-26 15:26:44');
--
-- Indexes for dumped tables
--
--
-- Indeks untuk tabel `penduduk_miskin`
--
ALTER TABLE `penduduk_miskin`
ADD PRIMARY KEY (`id`),
ADD KEY `idx_penduduk_coords` (`lat`,`lng`),
ADD KEY `idx_penduduk_ibadah` (`rumah_ibadah_id`);
--
-- Indeks untuk tabel `riwayat_bantuan`
--
ALTER TABLE `riwayat_bantuan`
ADD PRIMARY KEY (`id`),
ADD KEY `penduduk_id` (`penduduk_id`),
ADD KEY `rumah_ibadah_id` (`rumah_ibadah_id`);
--
-- Indeks untuk tabel `rumah_ibadah`
--
ALTER TABLE `rumah_ibadah`
ADD PRIMARY KEY (`id`),
ADD KEY `idx_rumah_ibadah_coords` (`lat`,`lng`);
--
-- Indeks untuk tabel `users`
--
ALTER TABLE `users`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `username` (`username`);
--
-- AUTO_INCREMENT untuk tabel yang dibuang
--
--
-- AUTO_INCREMENT untuk tabel `penduduk_miskin`
--
ALTER TABLE `penduduk_miskin`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=11;
--
-- AUTO_INCREMENT untuk tabel `riwayat_bantuan`
--
ALTER TABLE `riwayat_bantuan`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=2;
--
-- AUTO_INCREMENT untuk tabel `rumah_ibadah`
--
ALTER TABLE `rumah_ibadah`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=8;
--
-- AUTO_INCREMENT untuk tabel `users`
--
ALTER TABLE `users`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=3;
--
-- Ketidakleluasaan untuk tabel pelimpahan (Dumped Tables)
--
--
-- Ketidakleluasaan untuk tabel `penduduk_miskin`
--
ALTER TABLE `penduduk_miskin`
ADD CONSTRAINT `penduduk_miskin_ibfk_1` FOREIGN KEY (`rumah_ibadah_id`) REFERENCES `rumah_ibadah` (`id`) ON DELETE SET NULL;
--
-- Ketidakleluasaan untuk tabel `riwayat_bantuan`
--
ALTER TABLE `riwayat_bantuan`
ADD CONSTRAINT `riwayat_bantuan_ibfk_1` FOREIGN KEY (`penduduk_id`) REFERENCES `penduduk_miskin` (`id`) ON DELETE CASCADE,
ADD CONSTRAINT `riwayat_bantuan_ibfk_2` FOREIGN KEY (`rumah_ibadah_id`) REFERENCES `rumah_ibadah` (`id`) ON DELETE SET NULL;
COMMIT;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
+127
View File
@@ -0,0 +1,127 @@
<style>
#confirm-overlay {
display: none; position: fixed; inset: 0; z-index: 99999;
background: rgba(15,23,42,.4); backdrop-filter: blur(2px);
align-items: center; justify-content: center; padding: 20px;
}
#confirm-overlay.show { display: flex; }
#confirm-overlay.confirm-top {
align-items: flex-start; justify-content: center;
padding-top: 72px; background: transparent; backdrop-filter: none;
pointer-events: none;
}
#confirm-box {
background: #fff; border: 1px solid #e2e8f0; border-radius: 12px;
width: 100%; max-width: 400px;
box-shadow: 0 16px 48px rgba(15,23,42,.14);
animation: confirmIn .18s ease; overflow: hidden;
}
#confirm-overlay.confirm-top #confirm-box {
pointer-events: all; max-width: 460px;
box-shadow: 0 8px 28px rgba(15,23,42,.12);
}
@keyframes confirmIn { from { opacity:0; transform: translateY(-6px); } to { opacity:1; transform: none; } }
#confirm-box .cf-head {
display: flex; gap: 14px; padding: 20px 22px 0; align-items: flex-start;
}
#confirm-box .cf-icon {
width: 40px; height: 40px; border-radius: 10px; flex-shrink: 0;
display: flex; align-items: center; justify-content: center;
}
#confirm-box .cf-icon.warn { background: #fffbeb; border: 1px solid #fde68a; }
#confirm-box .cf-icon.danger { background: #fef2f2; border: 1px solid #fecaca; }
#confirm-box .cf-icon.info { background: #eff6ff; border: 1px solid #bfdbfe; }
#confirm-box .cf-text { flex: 1; min-width: 0; }
#confirm-box .cf-title { font-size: 15px; font-weight: 700; color: #0f172a; margin-bottom: 4px; }
#confirm-box .cf-msg { font-size: 13px; color: #64748b; line-height: 1.55; }
#confirm-box .cf-actions {
display: flex; gap: 8px; padding: 18px 22px 20px; justify-content: flex-end;
}
#confirm-box .cf-btn {
padding: 9px 16px; border-radius: 8px; font-size: 13px;
font-weight: 600; cursor: pointer; border: 1px solid transparent;
font-family: inherit; transition: background .15s;
}
#confirm-box .cf-cancel { background: #fff; color: #475569; border-color: #e2e8f0; }
#confirm-box .cf-cancel:hover { background: #f8fafc; }
#confirm-box .cf-ok { background: #1d4ed8; color: #fff; }
#confirm-box .cf-ok:hover { background: #1e40af; }
#confirm-box .cf-ok.danger { background: #dc2626; }
#confirm-box .cf-ok.danger:hover { background: #b91c1c; }
</style>
<div id="confirm-overlay" role="dialog" aria-modal="true">
<div id="confirm-box">
<div class="cf-head">
<div class="cf-icon warn" id="cf-icon"></div>
<div class="cf-text">
<div class="cf-title" id="cf-title">Konfirmasi</div>
<div class="cf-msg" id="cf-msg"></div>
</div>
</div>
<div class="cf-actions">
<button type="button" class="cf-btn cf-cancel" id="cf-cancel">Batal</button>
<button type="button" class="cf-btn cf-ok" id="cf-ok">Lanjutkan</button>
</div>
</div>
</div>
<script>
(function(){
var overlay = document.getElementById('confirm-overlay');
var iconEl = document.getElementById('cf-icon');
var titleEl = document.getElementById('cf-title');
var msgEl = document.getElementById('cf-msg');
var btnOk = document.getElementById('cf-ok');
var btnCancel = document.getElementById('cf-cancel');
var resolver = null;
var icons = {
warn: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#d97706" stroke-width="2" stroke-linecap="round"><path d="M12 9v4m0 4h.01"/><path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/></svg>',
danger: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#dc2626" stroke-width="2" stroke-linecap="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 01-2 2H8a2 2 0 01-2-2L5 6"/><path d="M10 11v6M14 11v6"/><path d="M9 6V4a1 1 0 011-1h4a1 1 0 011 1v2"/></svg>',
info: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#2563eb" stroke-width="2" stroke-linecap="round"><circle cx="12" cy="12" r="9"/><path d="M12 11v5M12 8h.01"/></svg>',
move: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#2563eb" stroke-width="2" stroke-linecap="round"><path d="M12 2v20M2 12h20"/><circle cx="12" cy="12" r="3"/></svg>'
};
function closeConfirm(result) {
overlay.classList.remove('show', 'confirm-top');
document.body.style.overflow = '';
if (resolver) { var r = resolver; resolver = null; r(result); }
}
btnCancel.addEventListener('click', function(){ closeConfirm(false); });
btnOk.addEventListener('click', function(){ closeConfirm(true); });
overlay.addEventListener('click', function(e){ if (e.target === overlay && !overlay.classList.contains('confirm-top')) closeConfirm(false); });
document.addEventListener('keydown', function(e){
if (!overlay.classList.contains('show')) return;
if (e.key === 'Escape') closeConfirm(false);
});
window.showConfirm = function(opts) {
opts = opts || {};
var danger = !!opts.danger;
var kind = danger ? 'danger' : (opts.icon || 'warn');
iconEl.className = 'cf-icon ' + (kind === 'move' || kind === 'info' ? kind : (danger ? 'danger' : 'warn'));
iconEl.innerHTML = icons[kind] || icons.warn;
titleEl.textContent = opts.title || 'Konfirmasi';
msgEl.innerHTML = opts.message || 'Apakah Anda yakin?';
btnOk.textContent = opts.confirmText || (danger ? 'Ya, Hapus' : 'Lanjutkan');
btnCancel.textContent = opts.cancelText || 'Batal';
btnOk.classList.toggle('danger', danger);
overlay.classList.toggle('confirm-top', opts.position === 'top');
if (opts.position !== 'top') document.body.style.overflow = 'hidden';
overlay.classList.add('show');
return new Promise(function(resolve){ resolver = resolve; });
};
document.querySelectorAll('[data-confirm-logout]').forEach(function(el){
el.addEventListener('click', function(e){
e.preventDefault();
showConfirm({
title: 'Keluar dari akun',
message: 'Sesi Anda akan diakhiri. Lanjutkan keluar?',
confirmText: 'Ya, Keluar',
danger: true
}).then(function(ok){ if (ok) window.location = el.getAttribute('href'); });
});
});
})();
</script>
+46
View File
@@ -0,0 +1,46 @@
<?php
$nama = htmlspecialchars($_SESSION['nama'] ?? 'Pengguna');
$role = $_SESSION['role'] ?? '';
$roleLabel = $role === 'superadmin' ? 'Super Admin' : (in_array($role, ['admin','user'], true) ? 'Admin' : ($role ? ucfirst($role) : 'User'));
$parts = preg_split('/\s+/', trim($_SESSION['nama'] ?? 'U'));
$initials = '';
foreach (array_slice($parts, 0, 2) as $p) { $initials .= mb_strtoupper(mb_substr($p, 0, 1)); }
if ($initials === '') $initials = 'U';
$avatarClass = $role === 'superadmin' ? 'avatar-admin' : 'avatar-user';
?>
<style>
.sb-account {
display: flex; align-items: center; gap: 10px;
padding: 10px; margin-bottom: 8px;
background: var(--bg, #f0f4f8); border-radius: 10px;
border: 1px solid var(--border, #e2e8f0);
}
.sb-avatar {
width: 38px; height: 38px; border-radius: 50%; flex-shrink: 0;
display: flex; align-items: center; justify-content: center;
font-size: 13px; font-weight: 700; color: #fff;
letter-spacing: .3px;
}
.sb-avatar.avatar-admin { background: linear-gradient(135deg, #7c3aed, #2563eb); }
.sb-avatar.avatar-user { background: linear-gradient(135deg, #0891b2, #2563eb); }
.sb-account-info { flex: 1; min-width: 0; text-align: left; }
.sb-account-name {
font-size: 13px; font-weight: 700; color: var(--text, #1e293b);
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
line-height: 1.3;
}
.sb-account-role {
display: inline-block; margin-top: 3px;
font-size: 10px; font-weight: 700; padding: 2px 8px; border-radius: 20px;
text-transform: capitalize;
}
.sb-account-role.role-admin { background: #ede9fe; color: #7c3aed; }
.sb-account-role.role-user { background: #dbeafe; color: #2563eb; }
</style>
<div class="sb-account">
<div class="sb-avatar <?= $avatarClass ?>"><?= $initials ?></div>
<div class="sb-account-info">
<div class="sb-account-name"><?= $nama ?></div>
<span class="sb-account-role <?= $role === 'superadmin' ? 'role-admin' : 'role-user' ?>"><?= htmlspecialchars($roleLabel) ?></span>
</div>
</div>
+2711
View File
File diff suppressed because it is too large Load Diff
+200
View File
@@ -0,0 +1,200 @@
<?php
session_start();
if (isset($_SESSION['user'])) {
header('Location: ' . ($_SESSION['role'] === 'superadmin' ? 'dashboard.php' : 'index.php'));
exit;
}
$err = '';
$selectedRole = $_POST['role'] ?? 'admin';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
require 'config.php';
$u = $conn->real_escape_string($_POST['username'] ?? '');
$role = $_POST['role'] ?? '';
if ($role === 'admin') {
$row = $conn->query("SELECT * FROM users WHERE username='$u' AND role IN ('admin','user')")->fetch_assoc();
} else {
$roleEsc = $conn->real_escape_string($role);
$row = $conn->query("SELECT * FROM users WHERE username='$u' AND role='$roleEsc'")->fetch_assoc();
}
if ($row && password_verify($_POST['password'] ?? '', $row['password'])) {
session_regenerate_id(true);
$_SESSION['user'] = $row['id'];
$_SESSION['nama'] = $row['nama'];
$_SESSION['role'] = $row['role'];
header('Location: ' . ($row['role'] === 'superadmin' ? 'dashboard.php' : 'index.php'));
exit;
}
$err = 'Kredensial tidak valid untuk peran yang dipilih.';
}
?>
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Masuk — Poverty Map</title>
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
:root {
--text: #0f172a; --text2: #64748b; --border: #e2e8f0;
--navy: #08184fff; --blue: #08184fff;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
html, body { height: 100%; }
body {
display: flex; flex-direction: column; font-family: 'DM Sans', sans-serif;
background: #fff; color: var(--text); overflow: hidden;
}
.login-header {
flex-shrink: 0; height: 56px; display: flex; align-items: center;
padding: 0 28px; border-bottom: 1px solid var(--border); background: #fff;
}
.login-header a {
font-size: 13px; font-weight: 600; color: var(--text2); text-decoration: none;
display: inline-flex; align-items: center; gap: 6px;
}
.login-header a:hover { color: var(--text); }
.login-header span {
margin-left: auto; font-size: 12px; color: var(--text2);
}
.login-body { flex: 1; display: flex; min-height: 0; }
.panel-left {
flex: 1; display: flex; flex-direction: column; justify-content: center;
padding: 40px 56px; background: var(--navy); color: #fff; overflow-y: auto;
}
.pl-label {
font-size: 11px; font-weight: 700; letter-spacing: .08em;
text-transform: uppercase; color: rgba(255,255,255,.5); margin-bottom: 16px;
}
.pl-title { font-size: clamp(26px, 3vw, 36px); font-weight: 700; line-height: 1.25; margin-bottom: 14px; max-width: 400px; }
.pl-desc { font-size: 15px; line-height: 1.65; color: rgba(255,255,255,.7); max-width: 380px; margin-bottom: 32px; }
.pl-list { list-style: none; display: flex; flex-direction: column; gap: 10px; }
.pl-list li {
font-size: 14px; color: rgba(255,255,255,.75); padding-left: 18px; position: relative;
}
.pl-list li::before {
content: ''; position: absolute; left: 0; top: 8px;
width: 5px; height: 5px; border-radius: 50%; background: rgba(255,255,255,.35);
}
.panel-right {
flex: 1; display: flex; align-items: center; justify-content: center;
padding: 40px 48px; background: #f8fafc; overflow-y: auto;
}
.login-form { width: 100%; max-width: 380px; }
.lf-title { font-size: 22px; font-weight: 700; margin-bottom: 4px; }
.lf-sub { font-size: 14px; color: var(--text2); margin-bottom: 28px; }
.role-picker { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 22px; }
.role-card { cursor: pointer; }
.role-card input { position: absolute; opacity: 0; pointer-events: none; }
.role-card-inner {
display: block; padding: 14px; border: 1px solid var(--border);
border-radius: 10px; background: #fff; transition: border-color .15s, box-shadow .15s;
}
.role-card input:checked + .role-card-inner {
border-color: var(--blue); box-shadow: 0 0 0 3px rgba(30,58,138,.08);
}
.role-card strong { display: block; font-size: 13px; font-weight: 700; margin-bottom: 2px; }
.role-card small { font-size: 11px; color: var(--text2); line-height: 1.4; }
.field-label {
display: block; font-size: 12px; font-weight: 600;
color: var(--text2); margin-bottom: 6px;
}
.field-input {
width: 100%; padding: 11px 13px; border: 1px solid var(--border);
border-radius: 8px; font-size: 14px; font-family: inherit;
margin-bottom: 14px; background: #fff; transition: border-color .15s, box-shadow .15s;
}
.field-input:focus {
outline: none; border-color: var(--blue);
box-shadow: 0 0 0 3px rgba(30,58,138,.08);
}
.btn-submit {
width: 100%; padding: 12px; background: var(--blue); color: #fff;
border: none; border-radius: 8px; font-size: 14px; font-weight: 600;
cursor: pointer; font-family: inherit; margin-top: 6px;
}
.btn-submit:hover { background: #1e3a8a; }
.err {
background: #fef2f2; color: #b91c1c; padding: 11px 13px;
border-radius: 8px; font-size: 13px; margin-bottom: 16px; border: 1px solid #fecaca;
}
.lf-foot { margin-top: 22px; font-size: 12px; color: var(--text2); text-align: center; }
@media (max-width: 860px) {
body { overflow: auto; height: auto; min-height: 100vh; }
.login-body { flex-direction: column; }
.panel-left { padding: 32px 28px; }
.panel-right { padding: 32px 24px 48px; }
.pl-list { display: none; }
}
</style>
</head>
<body>
<header class="login-header">
<a href="../index.php">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><path d="M19 12H5M12 19l-7-7 7-7"/></svg>
Portal WebGIS
</a>
<span>Poverty Map · Pontianak</span>
</header>
<div class="login-body">
<div class="panel-left">
<div class="pl-label">Sistem Informasi Geografis</div>
<h1 class="pl-title">Poverty Map</h1>
<p class="pl-desc">Pemetaan rumah ibadah dan penduduk miskin untuk perencanaan program binaan berbasis lokasi.</p>
<ul class="pl-list">
<li>Peta interaktif rumah ibadah dan kepala keluarga</li>
<li>Analisis radius cakupan binaan</li>
<li>Manajemen data untuk super admin</li>
</ul>
</div>
<div class="panel-right">
<div class="login-form">
<h2 class="lf-title">Masuk ke sistem</h2>
<p class="lf-sub">Pilih peran, lalu masukkan username dan password.</p>
<?php if ($err): ?><div class="err"><?= htmlspecialchars($err) ?></div><?php endif ?>
<form method="POST" autocomplete="on">
<span class="field-label">Peran akses</span>
<div class="role-picker">
<label class="role-card">
<input type="radio" name="role" value="superadmin" <?= $selectedRole === 'superadmin' ? 'checked' : '' ?> required>
<span class="role-card-inner">
<strong>Super Admin</strong>
<small>Dashboard dan kelola data</small>
</span>
</label>
<label class="role-card">
<input type="radio" name="role" value="admin" <?= $selectedRole === 'admin' ? 'checked' : '' ?>>
<span class="role-card-inner">
<strong>Admin</strong>
<small>Peta input dan edit lapangan</small>
</span>
</label>
</div>
<label class="field-label" for="username">Username</label>
<input class="field-input" type="text" id="username" name="username" required placeholder="Username" value="<?= htmlspecialchars($_POST['username'] ?? '') ?>">
<label class="field-label" for="password">Password</label>
<input class="field-input" type="password" id="password" name="password" required placeholder="Password">
<button type="submit" class="btn-submit">Masuk</button>
</form>
<p class="lf-foot">Dinas Sosial · Kabupaten/Kota Pontianak</p>
</div>
</div>
</div>
</body>
</html>
+5
View File
@@ -0,0 +1,5 @@
<?php
session_start();
session_destroy();
header('Location: login.php');
exit;
Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB