Initial commit
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
/**
|
||||
* get_data.php v3 — Ambil Semua Data
|
||||
*/
|
||||
|
||||
require_once 'koneksi.php';
|
||||
|
||||
$response = ['success'=>true, 'centers'=>[], 'houses'=>[], 'reports'=>[]];
|
||||
|
||||
// ======================== Centers
|
||||
$result = $conn->query("SELECT * FROM religious_centers ORDER BY id ASC");
|
||||
if ($result) {
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$response['centers'][] = [
|
||||
'id' => (int)$row['id'],
|
||||
'name' => $row['name'],
|
||||
'address' => $row['address'],
|
||||
'kas' => (float)($row['kas'] ?? 0),
|
||||
'latitude' => (float)$row['latitude'],
|
||||
'longitude' => (float)$row['longitude'],
|
||||
'radius' => (int)$row['radius']
|
||||
];
|
||||
}
|
||||
$result->free();
|
||||
}
|
||||
|
||||
// ======================== Houses (all fields incl. anggota JSON)
|
||||
$result = $conn->query("SELECT * FROM houses ORDER BY id ASC");
|
||||
if ($result) {
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$anggota = [];
|
||||
if (!empty($row['anggota'])) {
|
||||
$decoded = json_decode($row['anggota'], true);
|
||||
if (is_array($decoded)) $anggota = $decoded;
|
||||
}
|
||||
$response['houses'][] = [
|
||||
'id' => (int)$row['id'],
|
||||
'latitude' => (float)$row['latitude'],
|
||||
'longitude' => (float)$row['longitude'],
|
||||
'address' => $row['address'] ?? '',
|
||||
'rt' => $row['rt'] ?? '',
|
||||
'rw' => $row['rw'] ?? '',
|
||||
'kelurahan' => $row['kelurahan'] ?? '',
|
||||
'status_miskin' => $row['status_miskin'] ?? '',
|
||||
'jumlah_anggota'=> (int)($row['jumlah_anggota'] ?? 0),
|
||||
'anggota' => $anggota,
|
||||
'aid_status' => $row['aid_status'] ?? 'outside',
|
||||
'has_data' => (int)($row['has_data'] ?? 0)
|
||||
];
|
||||
}
|
||||
$result->free();
|
||||
}
|
||||
|
||||
// ======================== Reports
|
||||
$result = $conn->query("SELECT * FROM laporan ORDER BY created_at DESC LIMIT 50");
|
||||
if ($result) {
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$response['reports'][] = [
|
||||
'id' => (int)$row['id'],
|
||||
'name' => $row['pelapor'] ?? 'Anonim',
|
||||
'text' => $row['deskripsi'],
|
||||
'imgBase64'=> $row['foto_base64'] ?? null,
|
||||
'time' => date('d/m/Y H:i', strtotime($row['created_at']))
|
||||
];
|
||||
}
|
||||
$result->free();
|
||||
}
|
||||
|
||||
echo json_encode($response);
|
||||
$conn->close();
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
/**
|
||||
* hapus_pusat.php — Hapus Rumah Ibadah
|
||||
* POST: id
|
||||
*/
|
||||
require_once 'koneksi.php';
|
||||
|
||||
$id = intval($_POST['id'] ?? 0);
|
||||
if ($id < 1) { echo json_encode(['success'=>false,'message'=>'ID tidak valid']); exit; }
|
||||
|
||||
// Hapus log yang terkait
|
||||
$conn->query("DELETE FROM aid_logs WHERE religious_center_id=$id");
|
||||
|
||||
if ($conn->query("DELETE FROM religious_centers WHERE id=$id")) {
|
||||
echo json_encode(['success'=>true,'id'=>$id]);
|
||||
} else {
|
||||
echo json_encode(['success'=>false,'message'=>$conn->error]);
|
||||
}
|
||||
|
||||
$conn->close();
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
/**
|
||||
* hapus_rumah.php — Hapus Data Rumah Miskin
|
||||
* POST: id
|
||||
*/
|
||||
require_once 'koneksi.php';
|
||||
|
||||
$id = intval($_POST['id'] ?? 0);
|
||||
if ($id < 1) { echo json_encode(['success'=>false,'message'=>'ID tidak valid']); exit; }
|
||||
|
||||
// Hapus log yang terkait
|
||||
$conn->query("DELETE FROM aid_logs WHERE house_id=$id");
|
||||
|
||||
if ($conn->query("DELETE FROM houses WHERE id=$id")) {
|
||||
echo json_encode(['success'=>true,'id'=>$id]);
|
||||
} else {
|
||||
echo json_encode(['success'=>false,'message'=>$conn->error]);
|
||||
}
|
||||
|
||||
$conn->close();
|
||||
+410
@@ -0,0 +1,410 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="id">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>WebGIS — Distribusi Bantuan Sosial Pontianak</title>
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"/>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;1,400&family=DM+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="style.css"/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- =====================================================================
|
||||
TOP NAVBAR — selalu terlihat di semua halaman
|
||||
===================================================================== -->
|
||||
<nav id="topNav">
|
||||
<!-- Kiri: Logo -->
|
||||
<div class="nav-brand">
|
||||
<span class="nav-brand-icon">🕌</span>
|
||||
<div>
|
||||
<div class="nav-brand-title">BantSOSial GIS</div>
|
||||
<div class="nav-brand-sub">Pontianak · Kalimantan Barat</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tengah: Menu navigasi -->
|
||||
<div class="nav-menu">
|
||||
<button class="nav-item active" id="navHome" onclick="navigateTo('map')">
|
||||
<span class="nav-item-icon">🗺</span>
|
||||
<span class="nav-item-label">Peta</span>
|
||||
</button>
|
||||
<button class="nav-item" id="navReport" onclick="navigateTo('report')">
|
||||
<span class="nav-item-icon">📢</span>
|
||||
<span class="nav-item-label">Pelaporan</span>
|
||||
<span class="nav-badge hidden" id="navReportBadge">0</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Kanan: Role switcher -->
|
||||
<div class="nav-right">
|
||||
<div id="roleIndicator" class="role-pill">
|
||||
<span id="roleIcon">👑</span>
|
||||
<span id="roleLabel">Admin</span>
|
||||
</div>
|
||||
<button id="roleSwitchBtn" onclick="openRoleModal()">Ganti Role ▾</button>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- =====================================================================
|
||||
ROLE MODAL
|
||||
===================================================================== -->
|
||||
<div id="roleModal" class="modal-overlay hidden">
|
||||
<div class="modal-box" style="max-width:420px">
|
||||
<div class="modal-header">
|
||||
<div class="modal-title">🔐 Pilih Role Pengguna</div>
|
||||
<button class="modal-close" onclick="closeRoleModal()">✕</button>
|
||||
</div>
|
||||
<div class="modal-body" style="padding:16px">
|
||||
<p style="font-size:12px;color:var(--text-muted);margin-bottom:14px">Pilih role sesuai hak akses Anda. Perubahan langsung berlaku.</p>
|
||||
<div class="role-cards">
|
||||
<div class="role-card" id="roleCard_admin" onclick="setRole('admin')">
|
||||
<div class="role-card-icon">👑</div>
|
||||
<div class="role-card-name">Admin</div>
|
||||
<div class="role-card-desc">Akses penuh — tambah, edit, hapus, lihat semua data dan laporan</div>
|
||||
<div class="role-card-perms">
|
||||
<span class="perm perm-green">✓ Tambah & Edit</span>
|
||||
<span class="perm perm-green">✓ Hapus & Reset</span>
|
||||
<span class="perm perm-green">✓ Lihat laporan</span>
|
||||
<span class="perm perm-green">✓ Kirim laporan</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="role-card" id="roleCard_surveyer" onclick="setRole('surveyer')">
|
||||
<div class="role-card-icon">📋</div>
|
||||
<div class="role-card-name">Surveyer</div>
|
||||
<div class="role-card-desc">Input data & kelola laporan — tidak bisa hapus atau reset</div>
|
||||
<div class="role-card-perms">
|
||||
<span class="perm perm-green">✓ Tambah & Edit</span>
|
||||
<span class="perm perm-red">✕ Hapus & Reset</span>
|
||||
<span class="perm perm-green">✓ Lihat laporan</span>
|
||||
<span class="perm perm-green">✓ Kirim laporan</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="role-card" id="roleCard_viewer" onclick="setRole('viewer')">
|
||||
<div class="role-card-icon">👁</div>
|
||||
<div class="role-card-name">Viewer</div>
|
||||
<div class="role-card-desc">Hanya lihat peta & kirim laporan masyarakat</div>
|
||||
<div class="role-card-perms">
|
||||
<span class="perm perm-green">✓ Lihat peta</span>
|
||||
<span class="perm perm-red">✕ Edit data</span>
|
||||
<span class="perm perm-red">✕ Lihat laporan</span>
|
||||
<span class="perm perm-green">✓ Kirim laporan</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- =====================================================================
|
||||
PAGE: PETA (default)
|
||||
===================================================================== -->
|
||||
<div id="pagePeta" class="page active-page">
|
||||
|
||||
<!-- SIDEBAR -->
|
||||
<div id="sidebar">
|
||||
<div id="modeIndicator" class="mode-badge hidden">
|
||||
<span class="pulse-dot"></span>
|
||||
<span id="modeIndicatorText">Klik peta untuk menempatkan titik</span>
|
||||
</div>
|
||||
|
||||
<!-- Viewer notice -->
|
||||
<div id="viewerNotice" style="display:none;margin:10px 14px 0;padding:10px 12px;background:var(--purple-light);border:1px solid #c4b5fd;border-radius:8px;font-size:11px;color:#5b21b6;font-weight:500;text-align:center;">
|
||||
👁 Mode Viewer — Anda hanya dapat melihat data
|
||||
</div>
|
||||
|
||||
<!-- Action buttons -->
|
||||
<div class="btn-group" id="actionBtnGroup">
|
||||
<button id="btnAddCenter" onclick="startAddingCenter()">🕌 Tambah Rumah Ibadah</button>
|
||||
<button id="btnAddHouse" onclick="startAddingHouse()">🏠 Tambah Rumah Miskin</button>
|
||||
</div>
|
||||
|
||||
<!-- Stats -->
|
||||
<div class="stat-panel">
|
||||
<div class="stat-title">📊 Statistik Bantuan</div>
|
||||
<div class="stat-grid">
|
||||
<div class="stat-card">
|
||||
<div class="stat-num" id="statTotal">0</div>
|
||||
<div class="stat-label">Total Rumah</div>
|
||||
</div>
|
||||
<div class="stat-card red">
|
||||
<div class="stat-num" id="statInside">0</div>
|
||||
<div class="stat-label">Dalam Radius</div>
|
||||
<div class="stat-icon">🔴</div>
|
||||
</div>
|
||||
<div class="stat-card yellow">
|
||||
<div class="stat-num" id="statHelped">0</div>
|
||||
<div class="stat-label">Sudah Dibantu</div>
|
||||
<div class="stat-icon">🟡</div>
|
||||
</div>
|
||||
<div class="stat-card green">
|
||||
<div class="stat-num" id="statOutside">0</div>
|
||||
<div class="stat-label">Luar Radius</div>
|
||||
<div class="stat-icon">🟢</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Rumah Ibadah -->
|
||||
<div class="section">
|
||||
<div class="section-title">
|
||||
🕌 Rumah Ibadah
|
||||
<span id="centerCount" class="badge badge-teal">0</span>
|
||||
</div>
|
||||
<div class="search-wrap">
|
||||
<input type="text" id="searchCenter" class="search-input" placeholder="🔍 Cari nama rumah ibadah..." oninput="filterCenters()"/>
|
||||
</div>
|
||||
<select id="sortCenter" class="filter-select" onchange="filterCenters()">
|
||||
<option value="">— Urutkan —</option>
|
||||
<option value="kas_desc">💰 Kas terbanyak</option>
|
||||
<option value="kas_asc">💰 Kas terkecil</option>
|
||||
<option value="tanggungan_desc">👨👩👧 Tanggungan terbanyak</option>
|
||||
<option value="tanggungan_asc">👨👩👧 Tanggungan terkecil</option>
|
||||
</select>
|
||||
<div id="centerList"><div class="empty-state">Belum ada rumah ibadah.</div></div>
|
||||
</div>
|
||||
|
||||
<!-- Rumah Miskin -->
|
||||
<div class="section">
|
||||
<div class="section-title">
|
||||
🏠 Data Rumah Miskin
|
||||
<span id="houseCount" class="badge badge-orange">0</span>
|
||||
</div>
|
||||
<div class="search-wrap">
|
||||
<input type="text" id="searchHouse" class="search-input" placeholder="🔍 Cari nama kepala keluarga..." oninput="filterHouses()"/>
|
||||
</div>
|
||||
<div class="filter-row">
|
||||
<select id="filterStatus" class="filter-select" onchange="filterHouses()">
|
||||
<option value="">Semua Status Bantuan</option>
|
||||
<option value="yellow">✅ Sudah dibantu</option>
|
||||
<option value="red">⏳ Belum dibantu</option>
|
||||
<option value="green">🟢 Luar radius</option>
|
||||
</select>
|
||||
<select id="filterMiskin" class="filter-select" onchange="filterHouses()">
|
||||
<option value="">Semua Kemiskinan</option>
|
||||
<option value="sangat_miskin">😢 Sangat Miskin</option>
|
||||
<option value="miskin">😟 Miskin</option>
|
||||
<option value="tidak_miskin">😊 Tidak Miskin</option>
|
||||
</select>
|
||||
</div>
|
||||
<div id="houseList"><div class="empty-state">Belum ada data rumah miskin.</div></div>
|
||||
</div>
|
||||
|
||||
<!-- Legenda -->
|
||||
<div class="section">
|
||||
<div class="section-title">🗺 Legenda</div>
|
||||
<div class="legend-list">
|
||||
<div class="legend-item"><span style="font-size:13px">🕌</span> Masjid/Musholla</div>
|
||||
<div class="legend-item"><span style="font-size:13px">⛪</span> Gereja Katolik</div>
|
||||
<div class="legend-item"><span style="font-size:13px">✝️</span> Gereja Protestan</div>
|
||||
<div class="legend-item"><span style="font-size:13px">🛕</span> Vihara/Klenteng/Pura</div>
|
||||
<div class="legend-item"><span class="legend-dot green"></span> Luar radius</div>
|
||||
<div class="legend-item"><span class="legend-dot red"></span> Dalam radius</div>
|
||||
<div class="legend-item"><span class="legend-dot yellow"></span> Sudah dibantu</div>
|
||||
<div class="legend-item"><span class="legend-circle"></span> Area jangkauan</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Reset -->
|
||||
<button id="btnReset" class="btn-danger" onclick="confirmReset()">🗑 Reset Semua Data</button>
|
||||
</div>
|
||||
|
||||
<!-- MAP -->
|
||||
<div id="map"></div>
|
||||
<div id="radiusTooltip" class="radius-tooltip hidden"></div>
|
||||
|
||||
</div><!-- end #pagePeta -->
|
||||
|
||||
|
||||
<!-- =====================================================================
|
||||
PAGE: PELAPORAN
|
||||
===================================================================== -->
|
||||
<div id="pagePelaporan" class="page">
|
||||
|
||||
<div class="report-page">
|
||||
|
||||
<!-- KIRI: Form kirim laporan (semua role bisa) -->
|
||||
<div class="report-col report-col-form">
|
||||
<div class="report-col-header">
|
||||
<div class="report-col-title">📢 Kirim Laporan</div>
|
||||
<div class="report-col-sub">Laporkan kondisi kemiskinan yang belum terdata</div>
|
||||
</div>
|
||||
|
||||
<div class="report-form-body">
|
||||
<div class="form-group">
|
||||
<label class="form-label">Deskripsi Laporan *</label>
|
||||
<textarea id="reportText" class="form-input form-textarea" rows="5"
|
||||
placeholder="Contoh: Ada keluarga miskin di Jl. Merdeka No.5 yang belum terdata, kondisi rumah sangat memprihatinkan dan membutuhkan bantuan segera..."></textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Nama Pelapor (opsional)</label>
|
||||
<input type="text" id="reportName" class="form-input" placeholder="Nama Anda..."/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Lokasi / Alamat (opsional)</label>
|
||||
<input type="text" id="reportLocation" class="form-input" placeholder="Jl. Contoh No.1, Kelurahan..."/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Foto / Bukti (opsional)</label>
|
||||
<div class="upload-area" id="uploadArea" onclick="document.getElementById('reportImg').click()">
|
||||
<div class="upload-icon">📷</div>
|
||||
<div class="upload-text">Klik untuk upload foto</div>
|
||||
<div class="upload-sub">JPG, PNG, maksimal 5MB</div>
|
||||
<input type="file" id="reportImg" accept="image/*" style="display:none" onchange="previewImage(event)"/>
|
||||
</div>
|
||||
<div id="imgPreview" style="display:none;margin-top:8px;">
|
||||
<img id="previewImg" style="width:100%;border-radius:8px;max-height:200px;object-fit:cover;"/>
|
||||
<button class="remove-img-btn" onclick="removeImage()">✕ Hapus foto</button>
|
||||
</div>
|
||||
</div>
|
||||
<button class="form-submit orange-btn" onclick="submitReport()" style="margin-top:4px;padding:12px;font-size:13px;">
|
||||
📤 Kirim Laporan
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- KANAN: Daftar laporan (hanya admin & surveyer) -->
|
||||
<div class="report-col report-col-list" id="reportColList">
|
||||
<div class="report-col-header">
|
||||
<div class="report-col-title">
|
||||
📋 Laporan Masuk
|
||||
<span id="reportCount" class="badge badge-orange" style="margin-left:8px">0</span>
|
||||
</div>
|
||||
<div class="report-col-sub">Daftar laporan yang diterima dari masyarakat</div>
|
||||
</div>
|
||||
|
||||
<!-- Filter & search laporan -->
|
||||
<div class="report-list-toolbar">
|
||||
<input type="text" id="searchReport" class="search-input" placeholder="🔍 Cari laporan..." oninput="filterReports()" style="flex:1"/>
|
||||
<select id="filterReportStatus" class="filter-select" onchange="filterReports()" style="width:auto;margin:0">
|
||||
<option value="">Semua</option>
|
||||
<option value="baru">🆕 Baru</option>
|
||||
<option value="ditangani">🔄 Ditangani</option>
|
||||
<option value="selesai">✅ Selesai</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div id="reportList" class="report-list-container">
|
||||
<div class="empty-state" style="margin-top:20px">Belum ada laporan masuk.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Viewer: panel info (tidak bisa lihat list) -->
|
||||
<div class="report-col report-col-viewer" id="reportColViewer" style="display:none">
|
||||
<div class="report-col-header">
|
||||
<div class="report-col-title">👁 Mode Viewer</div>
|
||||
<div class="report-col-sub">Daftar laporan hanya tersedia untuk Admin dan Surveyer</div>
|
||||
</div>
|
||||
<div style="padding:40px 20px;text-align:center">
|
||||
<div style="font-size:48px;margin-bottom:16px">🔒</div>
|
||||
<div style="font-size:14px;font-weight:600;color:var(--text);margin-bottom:8px">Akses Terbatas</div>
|
||||
<div style="font-size:12px;color:var(--text-muted);line-height:1.6">
|
||||
Sebagai Viewer, Anda dapat mengirim laporan tapi tidak dapat melihat daftar laporan yang masuk.<br><br>
|
||||
Hubungi Admin untuk mengubah role akses Anda.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div><!-- end #pagePelaporan -->
|
||||
|
||||
|
||||
<!-- =====================================================================
|
||||
CUSTOM DATE PICKER
|
||||
===================================================================== -->
|
||||
<div id="datepickerOverlay" class="datepicker-overlay hidden" onclick="closeDatepickerIfOutside(event)">
|
||||
<div class="datepicker-box" id="datepickerBox"></div>
|
||||
</div>
|
||||
|
||||
<!-- =====================================================================
|
||||
MODAL — FORM DATA RUMAH
|
||||
===================================================================== -->
|
||||
<div id="houseModal" class="modal-overlay hidden">
|
||||
<div class="modal-box">
|
||||
<div class="modal-header">
|
||||
<div class="modal-title">🏠 Data Kemiskinan</div>
|
||||
<button class="modal-close" onclick="closeHouseModal()">✕</button>
|
||||
</div>
|
||||
<div class="modal-body" id="houseModalBody"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- =====================================================================
|
||||
MODAL — DETAIL RUMAH
|
||||
===================================================================== -->
|
||||
<div id="detailModal" class="modal-overlay hidden">
|
||||
<div class="modal-box modal-wide">
|
||||
<div class="modal-header">
|
||||
<div class="modal-title">📋 Detail Data Rumah</div>
|
||||
<button class="modal-close" onclick="closeDetailModal()">✕</button>
|
||||
</div>
|
||||
<div class="modal-body" id="detailModalBody"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- =====================================================================
|
||||
MODAL — DETAIL LAPORAN (admin/surveyer)
|
||||
===================================================================== -->
|
||||
<div id="reportDetailModal" class="modal-overlay hidden">
|
||||
<div class="modal-box" style="max-width:500px">
|
||||
<div class="modal-header">
|
||||
<div class="modal-title">📋 Detail Laporan</div>
|
||||
<button class="modal-close" onclick="closeReportDetail()">✕</button>
|
||||
</div>
|
||||
<div class="modal-body" id="reportDetailBody"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- =====================================================================
|
||||
MODAL — EDIT RUMAH IBADAH
|
||||
===================================================================== -->
|
||||
<div id="centerEditModal" class="modal-overlay hidden">
|
||||
<div class="modal-box" style="max-width:460px">
|
||||
<div class="modal-header" style="background:linear-gradient(135deg,#f0fdf4,#e0f2fe)">
|
||||
<div class="modal-title">🕌 Edit Rumah Ibadah</div>
|
||||
<button class="modal-close" onclick="closeCenterEditModal()">✕</button>
|
||||
</div>
|
||||
<div class="modal-body" id="centerEditModalBody">
|
||||
<input type="hidden" id="ce_id"/>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Jenis Rumah Ibadah</label>
|
||||
<select class="form-input" id="ce_type" onchange="updateCenterEditHeader()">
|
||||
<option value="masjid">🕌 Masjid / Musholla</option>
|
||||
<option value="gereja katedral">⛪ Gereja Katolik / Katedral</option>
|
||||
<option value="gereja protestan">✝️ Gereja Protestan / Kapel</option>
|
||||
<option value="vihara">🛕 Vihara / Klenteng / Pura</option>
|
||||
<option value="sinagog">✡️ Sinagog</option>
|
||||
<option value="default">🏛️ Lainnya</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Nama Rumah Ibadah *</label>
|
||||
<input class="form-input" id="ce_name" type="text" placeholder="Nama rumah ibadah..."/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Alamat</label>
|
||||
<input class="form-input" id="ce_address" type="text" placeholder="Alamat..."/>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group" style="flex:1">
|
||||
<label class="form-label">Kas / Dana (Rp)</label>
|
||||
<input class="form-input" id="ce_kas" type="number" min="0" step="1000" placeholder="0"/>
|
||||
</div>
|
||||
<div class="form-group" style="flex:1">
|
||||
<label class="form-label">Radius (meter)</label>
|
||||
<input class="form-input" id="ce_radius" type="number" min="50" step="10" placeholder="300"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-actions">
|
||||
<button class="btn-modal-cancel" onclick="closeCenterEditModal()">Batal</button>
|
||||
<button class="btn-modal-save" onclick="saveCenterEdit()" style="background:var(--primary)">💾 Simpan Perubahan</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
/**
|
||||
* koneksi.php — Koneksi Database MySQL
|
||||
* WebGIS BantSOSial Pontianak
|
||||
*
|
||||
* Ubah kredensial sesuai konfigurasi server lokal Anda.
|
||||
*/
|
||||
|
||||
define('DB_HOST', 'localhost');
|
||||
define('DB_USER', 'root'); // Ganti sesuai MySQL user Anda
|
||||
define('DB_PASS', ''); // Ganti sesuai password MySQL Anda
|
||||
define('DB_NAME', 'webgis_bansos');
|
||||
|
||||
// Buat koneksi
|
||||
$conn = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME);
|
||||
|
||||
// Cek error koneksi
|
||||
if ($conn->connect_error) {
|
||||
http_response_code(500);
|
||||
die(json_encode([
|
||||
'success' => false,
|
||||
'message' => 'Koneksi database gagal: ' . $conn->connect_error
|
||||
]));
|
||||
}
|
||||
|
||||
// Set charset UTF-8
|
||||
$conn->set_charset('utf8mb4');
|
||||
|
||||
// Header JSON untuk semua response
|
||||
header('Content-Type: application/json');
|
||||
header('Access-Control-Allow-Origin: *');
|
||||
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
|
||||
header('Access-Control-Allow-Headers: Content-Type');
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
/**
|
||||
* reset.php — Reset Semua Data
|
||||
* Menghapus semua rumah ibadah, rumah miskin, dan log
|
||||
*/
|
||||
require_once 'koneksi.php';
|
||||
|
||||
$conn->query("DELETE FROM aid_logs");
|
||||
$conn->query("DELETE FROM houses");
|
||||
$conn->query("DELETE FROM religious_centers");
|
||||
$conn->query("ALTER TABLE aid_logs AUTO_INCREMENT = 1");
|
||||
$conn->query("ALTER TABLE houses AUTO_INCREMENT = 1");
|
||||
$conn->query("ALTER TABLE religious_centers AUTO_INCREMENT = 1");
|
||||
|
||||
echo json_encode(['success' => true, 'message' => 'Semua data berhasil direset']);
|
||||
$conn->close();
|
||||
+1636
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
/**
|
||||
* simpan_laporan.php — Simpan Laporan Masyarakat
|
||||
* POST: name, text, img (base64, opsional)
|
||||
*/
|
||||
require_once 'koneksi.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
echo json_encode(['success'=>false,'message'=>'Hanya POST']); exit;
|
||||
}
|
||||
|
||||
$name = $conn->real_escape_string(strip_tags($_POST['name'] ?? 'Anonim'));
|
||||
$text = $conn->real_escape_string(strip_tags($_POST['text'] ?? ''));
|
||||
$img = $_POST['img'] ?? '';
|
||||
|
||||
if (empty($text)) {
|
||||
echo json_encode(['success'=>false,'message'=>'Deskripsi kosong']); exit;
|
||||
}
|
||||
|
||||
// Validasi ukuran base64 img (max ~5MB raw → ~6.7MB base64)
|
||||
if (strlen($img) > 7_000_000) {
|
||||
echo json_encode(['success'=>false,'message'=>'Ukuran foto terlalu besar (max 5MB)']); exit;
|
||||
}
|
||||
|
||||
$imgEsc = $conn->real_escape_string($img);
|
||||
|
||||
$sql = "INSERT INTO laporan (pelapor, deskripsi, foto_base64) VALUES ('$name', '$text', '$imgEsc')";
|
||||
if ($conn->query($sql)) {
|
||||
echo json_encode(['success'=>true,'id'=>$conn->insert_id]);
|
||||
} else {
|
||||
echo json_encode(['success'=>false,'message'=>$conn->error]);
|
||||
}
|
||||
$conn->close();
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
/**
|
||||
* simpan_pusat.php — Simpan / Update Rumah Ibadah
|
||||
*
|
||||
* POST params:
|
||||
* id - ID (jika update, opsional)
|
||||
* name - Nama rumah ibadah
|
||||
* address - Alamat
|
||||
* kas - Jumlah kas/dana (Rp)
|
||||
* lat - Latitude
|
||||
* lng - Longitude
|
||||
* radius - Radius dalam meter
|
||||
* update - "1" jika update existing record
|
||||
*/
|
||||
|
||||
require_once 'koneksi.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
echo json_encode(['success' => false, 'message' => 'Hanya menerima POST']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$id = intval($_POST['id'] ?? 0);
|
||||
$name = $conn->real_escape_string(strip_tags($_POST['name'] ?? ''));
|
||||
$address = $conn->real_escape_string(strip_tags($_POST['address'] ?? ''));
|
||||
$kas = floatval($_POST['kas'] ?? 0);
|
||||
$lat = floatval($_POST['lat'] ?? 0);
|
||||
$lng = floatval($_POST['lng'] ?? 0);
|
||||
$radius = intval($_POST['radius'] ?? 300);
|
||||
$isUpdate = isset($_POST['update']) && $_POST['update'] === '1';
|
||||
|
||||
if (empty($name)) {
|
||||
echo json_encode(['success' => false, 'message' => 'Nama wajib diisi']);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($radius < 50) $radius = 50;
|
||||
|
||||
if ($id > 0 || $isUpdate) {
|
||||
$sql = "UPDATE religious_centers
|
||||
SET name='$name', address='$address', kas=$kas, latitude=$lat, longitude=$lng, radius=$radius, updated_at=NOW()
|
||||
WHERE id=$id";
|
||||
|
||||
if ($conn->query($sql)) {
|
||||
echo json_encode(['success' => true, 'id' => $id, 'action' => 'updated']);
|
||||
} else {
|
||||
echo json_encode(['success' => false, 'message' => $conn->error]);
|
||||
}
|
||||
} else {
|
||||
$sql = "INSERT INTO religious_centers (name, address, kas, latitude, longitude, radius)
|
||||
VALUES ('$name', '$address', $kas, $lat, $lng, $radius)";
|
||||
|
||||
if ($conn->query($sql)) {
|
||||
$newId = $conn->insert_id;
|
||||
echo json_encode(['success' => true, 'id' => $newId, 'action' => 'inserted']);
|
||||
} else {
|
||||
echo json_encode(['success' => false, 'message' => $conn->error]);
|
||||
}
|
||||
}
|
||||
|
||||
$conn->close();
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
/**
|
||||
* simpan_rumah.php v3 — Simpan / Update Data Rumah Miskin
|
||||
*
|
||||
* POST params:
|
||||
* id - ID rumah (opsional, jika update)
|
||||
* lat, lng - Koordinat
|
||||
* address - Alamat lengkap
|
||||
* rt, rw - RT dan RW
|
||||
* kelurahan - Nama kelurahan
|
||||
* status_miskin - sangat_miskin / miskin / tidak_miskin
|
||||
* jumlah_anggota - Jumlah anggota keluarga
|
||||
* anggota - JSON array data tiap anggota
|
||||
* aid_status - helped / not_helped / outside
|
||||
* has_data - 0 atau 1
|
||||
*/
|
||||
|
||||
require_once 'koneksi.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
echo json_encode(['success' => false, 'message' => 'Hanya POST']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$id = intval($_POST['id'] ?? 0);
|
||||
$lat = floatval($_POST['lat'] ?? 0);
|
||||
$lng = floatval($_POST['lng'] ?? 0);
|
||||
$address = $conn->real_escape_string(strip_tags($_POST['address'] ?? ''));
|
||||
$rt = $conn->real_escape_string(strip_tags($_POST['rt'] ?? ''));
|
||||
$rw = $conn->real_escape_string(strip_tags($_POST['rw'] ?? ''));
|
||||
$kelurahan = $conn->real_escape_string(strip_tags($_POST['kelurahan'] ?? ''));
|
||||
$statusMiskin = $conn->real_escape_string($_POST['status_miskin'] ?? '');
|
||||
$jumlahAnggota = intval($_POST['jumlah_anggota'] ?? 0);
|
||||
$anggotaRaw = $_POST['anggota'] ?? '[]';
|
||||
$aidStatus = $conn->real_escape_string($_POST['aid_status'] ?? 'outside');
|
||||
$hasData = intval($_POST['has_data'] ?? 0);
|
||||
|
||||
// Validasi anggota JSON
|
||||
$anggotaDecoded = json_decode($anggotaRaw, true);
|
||||
if (!is_array($anggotaDecoded)) $anggotaDecoded = [];
|
||||
$anggotaJson = $conn->real_escape_string(json_encode($anggotaDecoded, JSON_UNESCAPED_UNICODE));
|
||||
|
||||
$validStatuses = ['sangat_miskin','miskin','tidak_miskin',''];
|
||||
if (!in_array($statusMiskin, $validStatuses)) $statusMiskin = '';
|
||||
|
||||
$validAid = ['helped','not_helped','outside'];
|
||||
if (!in_array($aidStatus, $validAid)) $aidStatus = 'outside';
|
||||
|
||||
if ($id > 0) {
|
||||
// UPDATE
|
||||
$sql = "UPDATE houses SET
|
||||
latitude=$lat, longitude=$lng,
|
||||
address='$address', rt='$rt', rw='$rw', kelurahan='$kelurahan',
|
||||
status_miskin='$statusMiskin', jumlah_anggota=$jumlahAnggota,
|
||||
anggota='$anggotaJson', aid_status='$aidStatus', has_data=$hasData,
|
||||
updated_at=NOW()
|
||||
WHERE id=$id";
|
||||
if ($conn->query($sql)) {
|
||||
echo json_encode(['success'=>true,'id'=>$id,'action'=>'updated']);
|
||||
} else {
|
||||
echo json_encode(['success'=>false,'message'=>$conn->error]);
|
||||
}
|
||||
} else {
|
||||
// INSERT
|
||||
$sql = "INSERT INTO houses
|
||||
(latitude, longitude, address, rt, rw, kelurahan,
|
||||
status_miskin, jumlah_anggota, anggota, aid_status, has_data)
|
||||
VALUES
|
||||
($lat, $lng, '$address', '$rt', '$rw', '$kelurahan',
|
||||
'$statusMiskin', $jumlahAnggota, '$anggotaJson', '$aidStatus', $hasData)";
|
||||
if ($conn->query($sql)) {
|
||||
$newId = $conn->insert_id;
|
||||
echo json_encode(['success'=>true,'id'=>$newId,'action'=>'inserted']);
|
||||
} else {
|
||||
echo json_encode(['success'=>false,'message'=>$conn->error]);
|
||||
}
|
||||
}
|
||||
|
||||
$conn->close();
|
||||
+557
@@ -0,0 +1,557 @@
|
||||
/* ============================================
|
||||
WebGIS BantSOSial — style.css v5
|
||||
Navbar + halaman Peta & Pelaporan terpisah
|
||||
============================================ */
|
||||
|
||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
|
||||
:root {
|
||||
--nav-h: 56px;
|
||||
--sidebar-w: 320px;
|
||||
--bg: #f0f4f8;
|
||||
--sidebar-bg: #ffffff;
|
||||
--border: #e2e8f0;
|
||||
--text: #1e293b;
|
||||
--text-muted: #64748b;
|
||||
--primary: #0f766e;
|
||||
--primary-light: #ccfbf1;
|
||||
--primary-hover: #0d6563;
|
||||
--orange: #ea580c;
|
||||
--orange-light: #ffedd5;
|
||||
--orange-hover: #c2410c;
|
||||
--danger: #dc2626;
|
||||
--danger-light: #fee2e2;
|
||||
--warning: #d97706;
|
||||
--warning-light: #fef3c7;
|
||||
--success: #16a34a;
|
||||
--success-light: #dcfce7;
|
||||
--purple: #7c3aed;
|
||||
--purple-light: #ede9fe;
|
||||
--radius-sm: 8px;
|
||||
--radius-md: 12px;
|
||||
--shadow: 0 4px 24px rgba(0,0,0,0.08);
|
||||
--shadow-lg: 0 8px 40px rgba(0,0,0,0.14);
|
||||
--font: 'DM Sans', sans-serif;
|
||||
--mono: 'DM Mono', monospace;
|
||||
}
|
||||
|
||||
html, body { height: 100%; font-family: var(--font); background: var(--bg); overflow: hidden; color: var(--text); }
|
||||
|
||||
/* ============================================
|
||||
TOP NAVBAR
|
||||
============================================ */
|
||||
#topNav {
|
||||
position: fixed; top: 0; left: 0; right: 0;
|
||||
height: var(--nav-h); z-index: 2000;
|
||||
background: white; border-bottom: 1px solid var(--border);
|
||||
box-shadow: 0 2px 12px rgba(0,0,0,0.07);
|
||||
display: flex; align-items: center;
|
||||
padding: 0 20px; gap: 16px;
|
||||
}
|
||||
|
||||
/* Brand */
|
||||
.nav-brand {
|
||||
display: flex; align-items: center; gap: 10px; flex-shrink: 0;
|
||||
width: var(--sidebar-w);
|
||||
}
|
||||
.nav-brand-icon { font-size: 22px; }
|
||||
.nav-brand-title { font-size: 13px; font-weight: 700; letter-spacing: -.3px; line-height: 1.2; }
|
||||
.nav-brand-sub { font-size: 9px; color: var(--text-muted); font-family: var(--mono); }
|
||||
|
||||
/* Center menu */
|
||||
.nav-menu {
|
||||
display: flex; align-items: center; gap: 4px;
|
||||
flex: 1; justify-content: center;
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
display: flex; align-items: center; gap: 6px;
|
||||
padding: 7px 18px; border: none; background: none;
|
||||
border-radius: 20px; font-family: var(--font);
|
||||
font-size: 13px; font-weight: 600; color: var(--text-muted);
|
||||
cursor: pointer; transition: all .2s; position: relative;
|
||||
}
|
||||
.nav-item:hover { background: var(--bg); color: var(--text); }
|
||||
.nav-item.active {
|
||||
background: var(--primary-light); color: var(--primary);
|
||||
}
|
||||
.nav-item-icon { font-size: 15px; }
|
||||
.nav-badge {
|
||||
position: absolute; top: 2px; right: 8px;
|
||||
background: var(--orange); color: white;
|
||||
font-size: 9px; font-weight: 700; font-family: var(--mono);
|
||||
min-width: 16px; height: 16px; padding: 0 4px;
|
||||
border-radius: 8px; display: flex; align-items: center; justify-content: center;
|
||||
}
|
||||
.nav-badge.hidden { display: none; }
|
||||
|
||||
/* Right: role */
|
||||
.nav-right { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
|
||||
|
||||
.role-pill {
|
||||
display: flex; align-items: center; gap: 6px;
|
||||
background: var(--bg); border: 1.5px solid var(--border);
|
||||
border-radius: 20px; padding: 5px 12px;
|
||||
font-size: 12px; font-weight: 600;
|
||||
}
|
||||
|
||||
#roleSwitchBtn {
|
||||
padding: 6px 14px; background: var(--primary); color: white;
|
||||
border: none; border-radius: 20px; font-family: var(--font);
|
||||
font-size: 12px; font-weight: 600; cursor: pointer; transition: all .2s;
|
||||
box-shadow: 0 2px 8px rgba(15,118,110,.3);
|
||||
}
|
||||
#roleSwitchBtn:hover { background: var(--primary-hover); }
|
||||
|
||||
/* ============================================
|
||||
PAGE SYSTEM
|
||||
============================================ */
|
||||
.page {
|
||||
position: fixed;
|
||||
top: var(--nav-h); left: 0; right: 0;
|
||||
bottom: 0;
|
||||
display: none;
|
||||
opacity: 0;
|
||||
transition: opacity .25s ease;
|
||||
}
|
||||
.page.active-page {
|
||||
display: flex;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
PAGE: PETA — sidebar + map layout
|
||||
============================================ */
|
||||
#pagePeta { display: none; }
|
||||
#pagePeta.active-page { display: block; }
|
||||
|
||||
#sidebar {
|
||||
position: fixed;
|
||||
left: 0; top: var(--nav-h);
|
||||
width: var(--sidebar-w);
|
||||
height: calc(100% - var(--nav-h));
|
||||
background: white; z-index: 1000;
|
||||
overflow-y: auto; overflow-x: hidden;
|
||||
box-shadow: var(--shadow-lg);
|
||||
display: flex; flex-direction: column;
|
||||
border-right: 1px solid var(--border);
|
||||
}
|
||||
#sidebar::-webkit-scrollbar { width: 4px; }
|
||||
#sidebar::-webkit-scrollbar-thumb { background: var(--border); border-radius: 4px; }
|
||||
|
||||
#map {
|
||||
position: fixed;
|
||||
left: var(--sidebar-w); top: var(--nav-h);
|
||||
width: calc(100% - var(--sidebar-w));
|
||||
height: calc(100% - var(--nav-h));
|
||||
z-index: 1;
|
||||
}
|
||||
#map.adding-mode { cursor: crosshair !important; }
|
||||
#map.adding-mode .leaflet-interactive { cursor: crosshair !important; }
|
||||
|
||||
.radius-tooltip {
|
||||
position: fixed; bottom: 24px; right: 24px;
|
||||
background: rgba(15,23,42,.88); color: white;
|
||||
padding: 10px 16px; border-radius: var(--radius-md);
|
||||
font-family: var(--mono); font-size: 14px; font-weight: 500;
|
||||
z-index: 2000; backdrop-filter: blur(8px);
|
||||
box-shadow: var(--shadow-lg); pointer-events: none; transition: opacity .2s;
|
||||
}
|
||||
.radius-tooltip.hidden { opacity: 0; pointer-events: none; }
|
||||
.radius-handle { cursor: ew-resize; }
|
||||
|
||||
/* ============================================
|
||||
PAGE: PELAPORAN — dua kolom
|
||||
============================================ */
|
||||
#pagePelaporan { display: none; background: var(--bg); }
|
||||
#pagePelaporan.active-page { display: block; overflow-y: auto; }
|
||||
|
||||
.report-page {
|
||||
display: grid;
|
||||
grid-template-columns: 420px 1fr;
|
||||
gap: 0;
|
||||
min-height: calc(100vh - var(--nav-h));
|
||||
}
|
||||
|
||||
.report-col {
|
||||
display: flex; flex-direction: column;
|
||||
height: calc(100vh - var(--nav-h));
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.report-col-form {
|
||||
background: white;
|
||||
border-right: 1px solid var(--border);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.report-col-list {
|
||||
background: var(--bg);
|
||||
overflow: hidden;
|
||||
display: flex; flex-direction: column;
|
||||
}
|
||||
|
||||
.report-col-viewer {
|
||||
background: var(--bg);
|
||||
}
|
||||
|
||||
.report-col-header {
|
||||
padding: 20px 24px 16px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
background: white; flex-shrink: 0;
|
||||
}
|
||||
.report-col-title {
|
||||
font-size: 16px; font-weight: 700;
|
||||
display: flex; align-items: center; gap: 6px;
|
||||
}
|
||||
.report-col-sub {
|
||||
font-size: 11px; color: var(--text-muted); margin-top: 3px;
|
||||
}
|
||||
|
||||
.report-form-body { padding: 20px 24px; }
|
||||
|
||||
/* List toolbar */
|
||||
.report-list-toolbar {
|
||||
padding: 12px 20px;
|
||||
background: white; border-bottom: 1px solid var(--border);
|
||||
display: flex; gap: 8px; align-items: center; flex-shrink: 0;
|
||||
}
|
||||
|
||||
.report-list-container {
|
||||
flex: 1; overflow-y: auto; padding: 16px 20px;
|
||||
}
|
||||
.report-list-container::-webkit-scrollbar { width: 4px; }
|
||||
.report-list-container::-webkit-scrollbar-thumb { background: var(--border); border-radius: 4px; }
|
||||
|
||||
/* Report card */
|
||||
.report-card {
|
||||
background: white; border: 1px solid var(--border);
|
||||
border-radius: var(--radius-md); padding: 14px 16px;
|
||||
margin-bottom: 10px; cursor: pointer;
|
||||
transition: all .2s; animation: fadeIn .3s;
|
||||
position: relative;
|
||||
}
|
||||
.report-card:hover { box-shadow: var(--shadow); transform: translateY(-1px); border-color: var(--primary); }
|
||||
|
||||
.report-card-top {
|
||||
display: flex; align-items: flex-start; justify-content: space-between;
|
||||
margin-bottom: 6px; gap: 8px;
|
||||
}
|
||||
.report-card-name { font-size: 13px; font-weight: 700; }
|
||||
.report-card-time { font-size: 10px; color: var(--text-muted); font-family: var(--mono); flex-shrink: 0; }
|
||||
.report-card-text {
|
||||
font-size: 12px; color: var(--text-muted); line-height: 1.5;
|
||||
display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
|
||||
}
|
||||
.report-card-location { font-size: 10px; color: var(--text-muted); margin-top: 5px; }
|
||||
.report-card-footer {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--border);
|
||||
}
|
||||
.report-status-badge {
|
||||
font-size: 10px; font-weight: 700; padding: 2px 8px; border-radius: 12px;
|
||||
}
|
||||
.report-status-badge.baru { background: #eff6ff; color: #1d4ed8; }
|
||||
.report-status-badge.ditangani { background: var(--warning-light); color: var(--warning); }
|
||||
.report-status-badge.selesai { background: var(--success-light); color: var(--success); }
|
||||
.report-card-img { width: 48px; height: 48px; object-fit: cover; border-radius: 6px; flex-shrink: 0; }
|
||||
|
||||
.report-card-has-img { border-left: 3px solid var(--orange); }
|
||||
|
||||
/* Report detail modal */
|
||||
.report-detail-img { width: 100%; border-radius: var(--radius-sm); margin: 10px 0; max-height: 260px; object-fit: cover; }
|
||||
.report-detail-row { display: flex; justify-content: space-between; font-size: 12px; padding: 6px 0; border-bottom: 1px solid var(--border); }
|
||||
.report-detail-row:last-of-type { border: none; }
|
||||
.report-detail-label { color: var(--text-muted); }
|
||||
.report-detail-val { font-weight: 600; text-align: right; max-width: 60%; }
|
||||
|
||||
/* Status change buttons */
|
||||
.status-change-btns { display: flex; gap: 6px; margin-top: 12px; }
|
||||
.btn-status {
|
||||
flex: 1; padding: 8px; border: none; border-radius: var(--radius-sm);
|
||||
font-family: var(--font); font-size: 11px; font-weight: 700; cursor: pointer; transition: all .2s;
|
||||
}
|
||||
.btn-status.baru { background: #eff6ff; color: #1d4ed8; border: 1px solid #bfdbfe; }
|
||||
.btn-status.ditangani { background: var(--warning-light); color: var(--warning); border: 1px solid #fcd34d; }
|
||||
.btn-status.selesai { background: var(--success-light); color: var(--success); border: 1px solid #86efac; }
|
||||
.btn-status.active { opacity: 1; transform: scale(1.02); box-shadow: var(--shadow); }
|
||||
|
||||
/* ============================================
|
||||
SIDEBAR INNER STYLES
|
||||
============================================ */
|
||||
.mode-badge {
|
||||
margin: 10px 14px 0; padding: 8px 12px;
|
||||
background: #fef9c3; border: 1px solid #fde047;
|
||||
border-radius: var(--radius-sm); font-size: 11px;
|
||||
font-weight: 500; color: #854d0e;
|
||||
display: flex; align-items: center; gap: 8px;
|
||||
}
|
||||
.mode-badge.hidden { display: none; }
|
||||
.mode-badge.house-mode { background: #fff7ed; border-color: #fdba74; color: #9a3412; }
|
||||
.pulse-dot { width: 8px; height: 8px; background: #facc15; border-radius: 50%; flex-shrink: 0; animation: pulse 1.2s infinite; }
|
||||
.mode-badge.house-mode .pulse-dot { background: #fb923c; }
|
||||
@keyframes pulse { 0%,100%{opacity:1;transform:scale(1)} 50%{opacity:.5;transform:scale(1.3)} }
|
||||
|
||||
.btn-group { padding: 12px 14px 0; display: flex; flex-direction: column; gap: 7px; }
|
||||
#btnAddCenter {
|
||||
padding: 10px 14px; background: var(--primary); color: white;
|
||||
border: none; border-radius: var(--radius-md); font-family: var(--font);
|
||||
font-size: 13px; font-weight: 600; cursor: pointer;
|
||||
display: flex; align-items: center; justify-content: center; gap: 7px;
|
||||
transition: all .2s; box-shadow: 0 2px 8px rgba(15,118,110,.25);
|
||||
}
|
||||
#btnAddCenter:hover { background: var(--primary-hover); transform: translateY(-1px); }
|
||||
#btnAddCenter.active { background: #0369a1; }
|
||||
#btnAddHouse {
|
||||
padding: 10px 14px; background: var(--orange); color: white;
|
||||
border: none; border-radius: var(--radius-md); font-family: var(--font);
|
||||
font-size: 13px; font-weight: 600; cursor: pointer;
|
||||
display: flex; align-items: center; justify-content: center; gap: 7px;
|
||||
transition: all .2s; box-shadow: 0 2px 8px rgba(234,88,12,.25);
|
||||
}
|
||||
#btnAddHouse:hover { background: var(--orange-hover); transform: translateY(-1px); }
|
||||
#btnAddHouse.active { background: #9a3412; }
|
||||
.btn-danger {
|
||||
margin: 6px 14px 20px; padding: 9px 14px; background: white;
|
||||
color: var(--danger); border: 1.5px solid var(--danger);
|
||||
border-radius: var(--radius-md); font-family: var(--font);
|
||||
font-size: 12px; font-weight: 600; cursor: pointer;
|
||||
width: calc(100% - 28px); transition: all .2s;
|
||||
}
|
||||
.btn-danger:hover { background: var(--danger-light); }
|
||||
|
||||
.stat-panel { margin: 12px 14px 0; background: var(--bg); border-radius: var(--radius-md); padding: 12px; }
|
||||
.stat-title { font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: .8px; color: var(--text-muted); margin-bottom: 8px; }
|
||||
.stat-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; }
|
||||
.stat-card { background: white; border-radius: var(--radius-sm); padding: 8px 10px; border: 1px solid var(--border); position: relative; overflow: hidden; transition: all .3s; }
|
||||
.stat-card:hover { transform: translateY(-1px); box-shadow: var(--shadow); }
|
||||
.stat-card.red { border-color: #fca5a5; background: #fff5f5; }
|
||||
.stat-card.yellow { border-color: #fcd34d; background: #fffbeb; }
|
||||
.stat-card.green { border-color: #86efac; background: #f0fdf4; }
|
||||
.stat-num { font-size: 20px; font-weight: 700; font-family: var(--mono); line-height: 1; transition: all .4s; }
|
||||
.stat-card.red .stat-num { color: var(--danger); }
|
||||
.stat-card.yellow .stat-num { color: var(--warning); }
|
||||
.stat-card.green .stat-num { color: var(--success); }
|
||||
.stat-label { font-size: 9px; color: var(--text-muted); margin-top: 2px; font-weight: 500; }
|
||||
.stat-icon { position: absolute; right: 6px; top: 6px; font-size: 13px; opacity: .3; }
|
||||
|
||||
.section { padding: 12px 14px 0; }
|
||||
.section-title { font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: .8px; color: var(--text-muted); margin-bottom: 8px; display: flex; align-items: center; gap: 6px; }
|
||||
.badge { font-size: 10px; padding: 1px 6px; border-radius: 20px; font-family: var(--mono); margin-left: auto; color: white; }
|
||||
.badge-teal { background: var(--primary); }
|
||||
.badge-orange { background: var(--orange); }
|
||||
|
||||
.search-wrap { margin-bottom: 6px; }
|
||||
.search-input { width: 100%; padding: 7px 10px; border: 1.5px solid var(--border); border-radius: var(--radius-sm); font-family: var(--font); font-size: 12px; outline: none; transition: border-color .2s; background: white; }
|
||||
.search-input:focus { border-color: var(--primary); }
|
||||
|
||||
.filter-row { display: flex; gap: 6px; margin-bottom: 8px; }
|
||||
.filter-select { flex: 1; padding: 6px 8px; border: 1.5px solid var(--border); border-radius: var(--radius-sm); font-family: var(--font); font-size: 11px; outline: none; background: white; color: var(--text); cursor: pointer; margin-bottom: 6px; width: 100%; }
|
||||
.filter-select:focus { border-color: var(--primary); }
|
||||
|
||||
#centerList, #houseList { display: flex; flex-direction: column; gap: 6px; margin-bottom: 12px; }
|
||||
.empty-state { text-align: center; padding: 16px 10px; font-size: 11px; color: var(--text-muted); line-height: 1.6; background: var(--bg); border-radius: var(--radius-sm); border: 1.5px dashed var(--border); }
|
||||
|
||||
.center-item { background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 9px 30px 9px 10px; cursor: pointer; transition: all .2s; position: relative; animation: fadeIn .3s ease; }
|
||||
.center-item:hover { background: var(--primary-light); border-color: var(--primary); transform: translateX(3px); }
|
||||
.center-item-name { font-size: 12px; font-weight: 600; }
|
||||
.center-item-addr { font-size: 10px; color: var(--text-muted); margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.center-item-kas { font-size: 10px; color: var(--primary); font-family: var(--mono); font-weight: 600; margin-top: 2px; }
|
||||
.center-item-radius { font-size: 10px; color: #3b82f6; font-family: var(--mono); margin-top: 1px; }
|
||||
|
||||
.house-item { background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 9px 30px 9px 10px; cursor: pointer; transition: all .2s; position: relative; animation: fadeIn .3s ease; }
|
||||
.house-item:hover { background: var(--orange-light); border-color: var(--orange); transform: translateX(3px); }
|
||||
.house-item.status-red { border-left: 3px solid var(--danger); }
|
||||
.house-item.status-yellow { border-left: 3px solid var(--warning); }
|
||||
.house-item.status-green { border-left: 3px solid var(--success); }
|
||||
.house-item-name { font-size: 12px; font-weight: 600; }
|
||||
.house-item-sub { font-size: 10px; color: var(--text-muted); margin-top: 2px; }
|
||||
.house-item-status { font-size: 10px; font-weight: 600; margin-top: 2px; }
|
||||
.house-item-status.green { color: var(--success); }
|
||||
.house-item-status.red { color: var(--danger); }
|
||||
.house-item-status.yellow { color: var(--warning); }
|
||||
|
||||
.item-delete { position: absolute; right: 7px; top: 7px; background: none; border: none; cursor: pointer; font-size: 12px; opacity: .35; transition: opacity .2s; padding: 2px 4px; border-radius: 4px; }
|
||||
.item-delete:hover { opacity: 1; background: var(--danger-light); }
|
||||
|
||||
.legend-list { display: flex; flex-direction: column; gap: 5px; margin-bottom: 12px; }
|
||||
.legend-item { display: flex; align-items: center; gap: 8px; font-size: 11px; color: var(--text-muted); }
|
||||
.legend-dot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; }
|
||||
.legend-dot.green { background: #16a34a; }
|
||||
.legend-dot.red { background: #dc2626; }
|
||||
.legend-dot.yellow { background: #d97706; }
|
||||
.legend-circle { width: 14px; height: 14px; border-radius: 50%; border: 2px solid #3b82f6; background: rgba(59,130,246,.1); flex-shrink: 0; }
|
||||
|
||||
/* upload & report form */
|
||||
.form-textarea { width: 100%; padding: 8px 10px; resize: vertical; border: 1.5px solid var(--border); border-radius: var(--radius-sm); font-family: var(--font); font-size: 12px; outline: none; transition: border-color .2s; background: white; color: var(--text); }
|
||||
.form-textarea:focus { border-color: var(--orange); }
|
||||
.upload-area { border: 2px dashed var(--border); border-radius: var(--radius-sm); padding: 20px; text-align: center; cursor: pointer; transition: all .2s; background: var(--bg); }
|
||||
.upload-area:hover { border-color: var(--orange); background: var(--orange-light); }
|
||||
.upload-icon { font-size: 28px; margin-bottom: 6px; }
|
||||
.upload-text { font-size: 12px; font-weight: 600; color: var(--text-muted); }
|
||||
.upload-sub { font-size: 10px; color: var(--text-muted); margin-top: 2px; }
|
||||
.remove-img-btn { display: block; width: 100%; margin-top: 6px; padding: 5px; background: var(--danger-light); color: var(--danger); border: 1px solid #fca5a5; border-radius: 6px; font-size: 11px; cursor: pointer; font-family: var(--font); font-weight: 600; }
|
||||
|
||||
/* ============================================
|
||||
LEAFLET POPUP
|
||||
============================================ */
|
||||
.leaflet-popup-content-wrapper { border-radius: var(--radius-md) !important; box-shadow: var(--shadow-lg) !important; border: 1px solid var(--border); font-family: var(--font) !important; overflow: hidden; padding: 0 !important; }
|
||||
.leaflet-popup-content { margin: 0 !important; width: 300px !important; }
|
||||
.leaflet-popup-tip { background: white !important; }
|
||||
|
||||
.map-popup { padding: 13px 15px; }
|
||||
.map-popup-title { font-size: 14px; font-weight: 700; margin-bottom: 4px; }
|
||||
.map-popup-addr { font-size: 11px; color: var(--text-muted); margin-bottom: 10px; line-height: 1.4; }
|
||||
.map-popup-badge { display: inline-flex; align-items: center; gap: 5px; font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: .5px; padding: 2px 8px; border-radius: 20px; margin-bottom: 8px; }
|
||||
.map-popup-badge.green { background: var(--success-light); color: var(--success); }
|
||||
.map-popup-badge.red { background: var(--danger-light); color: var(--danger); }
|
||||
.map-popup-badge.yellow { background: var(--warning-light); color: var(--warning); }
|
||||
.map-popup-badge.nodata { background: #f1f5f9; color: var(--text-muted); }
|
||||
.map-popup-actions { padding: 9px 15px; background: var(--bg); display: flex; gap: 7px; }
|
||||
.popup-btn { flex: 1; padding: 7px 8px; border: none; border-radius: var(--radius-sm); font-family: var(--font); font-size: 11px; font-weight: 600; cursor: pointer; transition: all .2s; }
|
||||
.popup-btn.primary { background: var(--primary); color: white; }
|
||||
.popup-btn.primary:hover { background: var(--primary-hover); }
|
||||
.popup-btn.orange { background: var(--orange); color: white; }
|
||||
.popup-btn.orange:hover { background: var(--orange-hover); }
|
||||
|
||||
.center-popup { padding: 13px 15px; }
|
||||
.center-popup-header { display: flex; align-items: center; gap: 8px; margin-bottom: 5px; }
|
||||
.center-popup-icon { width: 30px; height: 30px; border-radius: 8px; display: flex; align-items: center; justify-content: center; font-size: 16px; }
|
||||
.center-popup-title { font-size: 13px; font-weight: 700; line-height: 1.2; }
|
||||
.center-popup-addr { font-size: 10px; color: var(--text-muted); margin-bottom: 8px; line-height: 1.4; }
|
||||
.kas-badge { display: flex; align-items: center; gap: 6px; background: #f0fdf4; border: 1px solid #86efac; border-radius: var(--radius-sm); padding: 6px 10px; margin-bottom: 7px; }
|
||||
.kas-label { font-size: 10px; color: var(--success); font-weight: 600; }
|
||||
.kas-value { font-size: 13px; font-weight: 700; color: var(--success); font-family: var(--mono); margin-left: auto; }
|
||||
.radius-info-box { background: #eff6ff; border: 1px solid #bfdbfe; border-radius: var(--radius-sm); padding: 7px 10px; font-size: 11px; color: #1d4ed8; display: flex; align-items: center; gap: 6px; margin-bottom: 5px; }
|
||||
.radius-val { font-family: var(--mono); font-weight: 700; font-size: 13px; margin-left: auto; }
|
||||
.radius-drag-hint { font-size: 10px; color: var(--text-muted); text-align: center; padding: 2px 0 4px; }
|
||||
|
||||
/* ============================================
|
||||
FORMS (shared)
|
||||
============================================ */
|
||||
.form-group { margin-bottom: 12px; }
|
||||
.form-label { font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: .6px; color: var(--text-muted); margin-bottom: 4px; display: block; }
|
||||
.form-input { width: 100%; padding: 8px 10px; border: 1.5px solid var(--border); border-radius: var(--radius-sm); font-family: var(--font); font-size: 12px; color: var(--text); outline: none; transition: border-color .2s; background: white; }
|
||||
.form-input:focus { border-color: var(--primary); }
|
||||
.form-row { display: flex; gap: 7px; }
|
||||
.form-row .form-group { flex: 1; }
|
||||
.form-divider { font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: .6px; color: var(--text-muted); border-bottom: 1px solid var(--border); padding-bottom: 4px; margin: 10px 0 8px; }
|
||||
.form-submit { width: 100%; padding: 9px; background: var(--primary); color: white; border: none; border-radius: var(--radius-sm); font-family: var(--font); font-size: 12px; font-weight: 700; cursor: pointer; margin-top: 10px; transition: all .2s; }
|
||||
.form-submit:hover { background: var(--primary-hover); }
|
||||
.form-submit.orange-btn { background: var(--orange); }
|
||||
.form-submit.orange-btn:hover { background: var(--orange-hover); }
|
||||
.form-cancel { width: 100%; padding: 7px; background: none; color: var(--text-muted); border: 1.5px solid var(--border); border-radius: var(--radius-sm); font-family: var(--font); font-size: 11px; cursor: pointer; margin-top: 5px; transition: all .2s; }
|
||||
.form-cancel:hover { background: var(--bg); }
|
||||
.loading-addr { font-size: 10px; color: var(--primary); padding: 4px 0; animation: blink 1s infinite; }
|
||||
@keyframes blink { 0%,100%{opacity:1} 50%{opacity:.4} }
|
||||
.form-popup { padding: 14px 15px; }
|
||||
.form-popup h3 { font-size: 13px; font-weight: 700; margin-bottom: 12px; display: flex; align-items: center; gap: 6px; }
|
||||
|
||||
/* ============================================
|
||||
CUSTOM DATE PICKER
|
||||
============================================ */
|
||||
.datepicker-overlay { position: fixed; inset: 0; z-index: 5000; display: flex; align-items: center; justify-content: center; background: rgba(15,23,42,.4); backdrop-filter: blur(3px); }
|
||||
.datepicker-overlay.hidden { display: none; }
|
||||
.datepicker-box { background: white; border-radius: 16px; box-shadow: 0 24px 80px rgba(0,0,0,.2); width: 320px; overflow: hidden; animation: slideUp .2s ease; }
|
||||
.dp-header { background: linear-gradient(135deg, var(--primary), #0d9488); color: white; padding: 16px 20px; display: flex; align-items: center; justify-content: space-between; }
|
||||
.dp-header-label { font-size: 13px; font-weight: 600; opacity: .8; }
|
||||
.dp-header-value { font-size: 22px; font-weight: 700; font-family: var(--mono); }
|
||||
.dp-tabs { display: flex; border-bottom: 1px solid var(--border); }
|
||||
.dp-tab { flex: 1; padding: 10px; text-align: center; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .5px; cursor: pointer; color: var(--text-muted); border-bottom: 2px solid transparent; transition: all .2s; }
|
||||
.dp-tab.active { color: var(--primary); border-bottom-color: var(--primary); }
|
||||
.dp-body { padding: 14px; min-height: 180px; }
|
||||
.dp-grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; }
|
||||
.dp-grid-7 { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; }
|
||||
.dp-cell { padding: 8px 4px; text-align: center; font-size: 12px; font-weight: 500; border-radius: var(--radius-sm); cursor: pointer; border: 1.5px solid transparent; transition: all .15s; color: var(--text); }
|
||||
.dp-cell:hover { background: var(--primary-light); border-color: var(--primary); color: var(--primary); }
|
||||
.dp-cell.selected { background: var(--primary); color: white; border-color: var(--primary); }
|
||||
.dp-cell.today { border-color: var(--primary); color: var(--primary); font-weight: 700; }
|
||||
.dp-year-nav { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
|
||||
.dp-nav-btn { padding: 5px 12px; background: var(--bg); border: 1px solid var(--border); border-radius: 6px; font-size: 12px; cursor: pointer; font-weight: 700; transition: all .2s; }
|
||||
.dp-nav-btn:hover { background: var(--primary-light); border-color: var(--primary); }
|
||||
.dp-year-range { font-size: 11px; color: var(--text-muted); font-family: var(--mono); }
|
||||
.dp-weekday { text-align: center; font-size: 9px; font-weight: 700; color: var(--text-muted); text-transform: uppercase; padding: 4px 0; }
|
||||
.dp-actions { padding: 10px 14px; border-top: 1px solid var(--border); display: flex; gap: 8px; background: var(--bg); }
|
||||
.dp-btn-ok { flex: 2; padding: 8px; background: var(--primary); color: white; border: none; border-radius: var(--radius-sm); font-family: var(--font); font-size: 12px; font-weight: 700; cursor: pointer; transition: background .2s; }
|
||||
.dp-btn-ok:hover { background: var(--primary-hover); }
|
||||
.dp-btn-cancel { flex: 1; padding: 8px; background: white; color: var(--text-muted); border: 1.5px solid var(--border); border-radius: var(--radius-sm); font-family: var(--font); font-size: 12px; cursor: pointer; }
|
||||
.date-trigger { width: 100%; padding: 7px 9px; border: 1.5px solid var(--border); border-radius: var(--radius-sm); font-family: var(--font); font-size: 12px; color: var(--text); outline: none; cursor: pointer; background: white; text-align: left; transition: border-color .2s; }
|
||||
.date-trigger:hover, .date-trigger:focus { border-color: var(--primary); }
|
||||
.date-trigger-placeholder { color: var(--text-muted); }
|
||||
|
||||
/* ============================================
|
||||
MODAL (shared)
|
||||
============================================ */
|
||||
.modal-overlay { position: fixed; inset: 0; background: rgba(15,23,42,.55); z-index: 3000; display: flex; align-items: center; justify-content: center; padding: 16px; backdrop-filter: blur(3px); }
|
||||
.modal-overlay.hidden { display: none; }
|
||||
.modal-box { background: white; border-radius: 16px; width: 100%; max-width: 540px; max-height: 90vh; overflow: hidden; display: flex; flex-direction: column; box-shadow: 0 24px 80px rgba(0,0,0,.2); animation: slideUp .25s ease; }
|
||||
.modal-wide { max-width: 640px; }
|
||||
@keyframes slideUp { from{opacity:0;transform:translateY(20px)} to{opacity:1;transform:translateY(0)} }
|
||||
.modal-header { display: flex; align-items: center; justify-content: space-between; padding: 16px 20px; border-bottom: 1px solid var(--border); background: linear-gradient(135deg,#f0fdf4,#e0f2fe); flex-shrink: 0; }
|
||||
.modal-title { font-size: 15px; font-weight: 700; }
|
||||
.modal-close { background: none; border: none; font-size: 16px; cursor: pointer; color: var(--text-muted); padding: 4px 8px; border-radius: 6px; transition: all .2s; }
|
||||
.modal-close:hover { background: var(--bg); }
|
||||
.modal-body { overflow-y: auto; padding: 20px; flex: 1; }
|
||||
.modal-body::-webkit-scrollbar { width: 4px; }
|
||||
.modal-body::-webkit-scrollbar-thumb { background: var(--border); border-radius: 4px; }
|
||||
.modal-section { margin-bottom: 16px; }
|
||||
.modal-section-title { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .8px; color: var(--text-muted); padding-bottom: 6px; border-bottom: 1px solid var(--border); margin-bottom: 10px; }
|
||||
.info-box { background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 10px 12px; margin-bottom: 10px; }
|
||||
.info-box-row { display: flex; justify-content: space-between; font-size: 11px; padding: 3px 0; }
|
||||
.info-box-label { color: var(--text-muted); }
|
||||
.info-box-val { font-weight: 600; color: var(--text); font-family: var(--mono); font-size: 10px; text-align: right; max-width: 60%; }
|
||||
.member-card { border: 1.5px solid var(--border); border-radius: var(--radius-md); padding: 12px; margin-bottom: 10px; background: #fafafa; }
|
||||
.member-card-header { display: flex; align-items: center; gap: 8px; font-size: 11px; font-weight: 700; color: var(--text-muted); text-transform: uppercase; letter-spacing: .5px; margin-bottom: 10px; }
|
||||
.member-num { width: 22px; height: 22px; background: var(--primary); color: white; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 11px; font-weight: 700; flex-shrink: 0; }
|
||||
.salary-row { display: none; }
|
||||
.salary-row.visible { display: flex; }
|
||||
.status-pills { display: flex; gap: 7px; margin-bottom: 2px; }
|
||||
.status-pill { flex: 1; padding: 8px 6px; border: 2px solid var(--border); border-radius: var(--radius-sm); text-align: center; cursor: pointer; font-size: 11px; font-weight: 600; transition: all .2s; background: white; }
|
||||
.status-pill:hover { border-color: var(--orange); }
|
||||
.status-pill.selected.sangat_miskin { border-color: var(--danger); background: var(--danger-light); color: var(--danger); }
|
||||
.status-pill.selected.miskin { border-color: var(--warning); background: var(--warning-light); color: var(--warning); }
|
||||
.status-pill.selected.tidak_miskin { border-color: var(--success); background: var(--success-light); color: var(--success); }
|
||||
.modal-actions { padding: 14px 20px; border-top: 1px solid var(--border); display: flex; gap: 8px; background: var(--bg); flex-shrink: 0; }
|
||||
.btn-modal-save { flex: 2; padding: 10px; background: var(--orange); color: white; border: none; border-radius: var(--radius-md); font-family: var(--font); font-size: 13px; font-weight: 700; cursor: pointer; transition: all .2s; }
|
||||
.btn-modal-save:hover { background: var(--orange-hover); }
|
||||
.btn-modal-cancel { flex: 1; padding: 10px; background: white; color: var(--text-muted); border: 1.5px solid var(--border); border-radius: var(--radius-md); font-family: var(--font); font-size: 12px; cursor: pointer; }
|
||||
.btn-modal-cancel:hover { background: var(--bg); }
|
||||
|
||||
/* detail modal */
|
||||
.detail-header-band { background: linear-gradient(135deg,var(--orange-light),#fff7ed); border-radius: var(--radius-sm); padding: 12px 14px; margin-bottom: 14px; }
|
||||
.detail-addr { font-size: 11px; color: var(--text-muted); margin-top: 3px; }
|
||||
.detail-center-row { background: var(--primary-light); border: 1px solid #99f6e4; border-radius: var(--radius-sm); padding: 8px 12px; margin-bottom: 12px; font-size: 11px; display: flex; align-items: center; gap: 8px; }
|
||||
.detail-miskin-badge { display: inline-flex; align-items: center; gap: 5px; padding: 4px 10px; border-radius: 20px; font-size: 11px; font-weight: 700; margin-bottom: 12px; }
|
||||
.detail-miskin-badge.sangat_miskin { background: var(--danger-light); color: var(--danger); }
|
||||
.detail-miskin-badge.miskin { background: var(--warning-light); color: var(--warning); }
|
||||
.detail-miskin-badge.tidak_miskin { background: var(--success-light); color: var(--success); }
|
||||
.members-grid { display: flex; flex-direction: column; gap: 8px; }
|
||||
.member-detail-card { border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 10px 12px; background: var(--bg); }
|
||||
.member-detail-name { font-size: 13px; font-weight: 700; margin-bottom: 4px; }
|
||||
.member-detail-row { display: flex; justify-content: space-between; font-size: 11px; padding: 2px 0; color: var(--text-muted); }
|
||||
.member-detail-row strong { color: var(--text); font-family: var(--mono); font-size: 10px; }
|
||||
.bantuan-section { margin-top: 14px; padding: 12px 14px; background: var(--bg); border-radius: var(--radius-sm); border: 1px solid var(--border); display: flex; align-items: center; gap: 10px; }
|
||||
.bantuan-label { font-size: 12px; font-weight: 600; flex: 1; }
|
||||
.btn-bantuan { padding: 7px 14px; border: none; border-radius: var(--radius-sm); font-family: var(--font); font-size: 11px; font-weight: 700; cursor: pointer; transition: all .2s; }
|
||||
.btn-bantuan.help { background: var(--success); color: white; }
|
||||
.btn-bantuan.unhelp { background: var(--danger-light); color: var(--danger); border: 1px solid #fca5a5; }
|
||||
|
||||
/* Role modal */
|
||||
.role-cards { display: flex; flex-direction: column; gap: 10px; }
|
||||
.role-card { border: 2px solid var(--border); border-radius: var(--radius-md); padding: 14px 16px; cursor: pointer; transition: all .2s; background: var(--bg); }
|
||||
.role-card:hover { border-color: var(--primary); background: var(--primary-light); transform: translateX(3px); }
|
||||
.role-card.active-role { border-color: var(--primary); background: var(--primary-light); }
|
||||
.role-card.active-role[id="roleCard_surveyer"] { border-color: var(--orange); background: var(--orange-light); }
|
||||
.role-card.active-role[id="roleCard_viewer"] { border-color: var(--purple); background: var(--purple-light); }
|
||||
.role-card-icon { font-size: 24px; margin-bottom: 4px; }
|
||||
.role-card-name { font-size: 14px; font-weight: 700; margin-bottom: 3px; }
|
||||
.role-card-desc { font-size: 11px; color: var(--text-muted); margin-bottom: 8px; line-height: 1.4; }
|
||||
.role-card-perms { display: flex; flex-wrap: wrap; gap: 4px; }
|
||||
.perm { font-size: 10px; font-weight: 600; padding: 2px 7px; border-radius: 12px; }
|
||||
.perm-green { background: var(--success-light); color: var(--success); }
|
||||
.perm-red { background: var(--danger-light); color: var(--danger); }
|
||||
|
||||
/* ============================================
|
||||
ANIMATIONS
|
||||
============================================ */
|
||||
@keyframes fadeIn { from{opacity:0;transform:translateX(-8px)} to{opacity:1;transform:translateX(0)} }
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
/**
|
||||
* update_radius.php — Update Radius Pusat Keagamaan
|
||||
*
|
||||
* POST params:
|
||||
* id - ID pusat keagamaan
|
||||
* radius - Radius baru dalam meter
|
||||
*/
|
||||
|
||||
require_once 'koneksi.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
echo json_encode(['success' => false, 'message' => 'Hanya menerima POST']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$id = intval($_POST['id'] ?? 0);
|
||||
$radius = intval($_POST['radius'] ?? 300);
|
||||
|
||||
if ($id < 1) {
|
||||
echo json_encode(['success' => false, 'message' => 'ID tidak valid']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Batasi radius minimum 50 meter
|
||||
if ($radius < 50) $radius = 50;
|
||||
|
||||
$sql = "UPDATE religious_centers SET radius=$radius WHERE id=$id";
|
||||
|
||||
if ($conn->query($sql)) {
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'id' => $id,
|
||||
'radius' => $radius
|
||||
]);
|
||||
} else {
|
||||
echo json_encode(['success' => false, 'message' => $conn->error]);
|
||||
}
|
||||
|
||||
$conn->close();
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
/**
|
||||
* update_status.php — Update Status Rumah
|
||||
*
|
||||
* POST params:
|
||||
* house_id - ID rumah
|
||||
* status - Status baru (green/red/yellow)
|
||||
* center_id - ID pusat keagamaan terdekat
|
||||
*/
|
||||
|
||||
require_once 'koneksi.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
echo json_encode(['success' => false, 'message' => 'Hanya menerima POST']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$houseId = intval($_POST['house_id'] ?? 0);
|
||||
$status = $conn->real_escape_string($_POST['status'] ?? 'green');
|
||||
$centerId = intval($_POST['center_id'] ?? 0);
|
||||
|
||||
// Validasi status
|
||||
$validStatuses = ['green', 'red', 'yellow'];
|
||||
if (!in_array($status, $validStatuses)) {
|
||||
echo json_encode(['success' => false, 'message' => 'Status tidak valid']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Validasi house_id
|
||||
if ($houseId < 1) {
|
||||
echo json_encode(['success' => false, 'message' => 'house_id tidak valid']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// ========================
|
||||
// Upsert status rumah
|
||||
// ========================
|
||||
// Cek apakah rumah sudah ada
|
||||
$check = $conn->query("SELECT id FROM houses WHERE id = $houseId");
|
||||
if ($check && $check->num_rows > 0) {
|
||||
// Update
|
||||
$sql = "UPDATE houses SET status='$status' WHERE id=$houseId";
|
||||
} else {
|
||||
// Insert (ambil posisi dari fixed positions yang sama seperti di JS)
|
||||
// Posisi disimpan juga agar konsisten
|
||||
$lat = floatval($_POST['lat'] ?? 0);
|
||||
$lng = floatval($_POST['lng'] ?? 0);
|
||||
$sql = "INSERT INTO houses (id, latitude, longitude, status) VALUES ($houseId, $lat, $lng, '$status')";
|
||||
}
|
||||
|
||||
if ($conn->query($sql)) {
|
||||
// ========================
|
||||
// Catat ke aid_logs
|
||||
// ========================
|
||||
if ($status === 'yellow' && $centerId > 0) {
|
||||
$logSql = "INSERT INTO aid_logs (house_id, religious_center_id, status, timestamp)
|
||||
VALUES ($houseId, $centerId, 'helped', NOW())";
|
||||
$conn->query($logSql);
|
||||
} elseif ($status !== 'yellow') {
|
||||
// Batalkan bantuan — catat sebagai reverted
|
||||
$logSql = "INSERT INTO aid_logs (house_id, religious_center_id, status, timestamp)
|
||||
VALUES ($houseId, $centerId, 'reverted', NOW())";
|
||||
$conn->query($logSql);
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'house_id' => $houseId,
|
||||
'status' => $status
|
||||
]);
|
||||
} else {
|
||||
echo json_encode(['success' => false, 'message' => $conn->error]);
|
||||
}
|
||||
|
||||
$conn->close();
|
||||
Reference in New Issue
Block a user