Initial commit - SIG Bansos
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
.git
|
||||
.gitignore
|
||||
README.md
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="id">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>SIG Bansos - Login</title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<style>
|
||||
body { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; display: flex; align-items: center; }
|
||||
.login-card { background: white; border-radius: 15px; padding: 40px; box-shadow: 0 20px 40px rgba(0,0,0,0.3); max-width: 400px; width: 100%; }
|
||||
.login-card h3 { text-align: center; margin-bottom: 30px; color: #333; }
|
||||
.btn-login { width: 100%; padding: 10px; font-size: 16px; }
|
||||
.error-msg { display: none; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-4">
|
||||
<div class="login-card">
|
||||
<h3><i class="fas fa-map-marked-alt"></i> SIG Bansos</h3>
|
||||
<div class="alert alert-danger error-msg" id="errorMsg"></div>
|
||||
<form id="loginForm">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Username</label>
|
||||
<input type="text" class="form-control" id="username" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Password</label>
|
||||
<input type="password" class="form-control" id="password" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary btn-login">
|
||||
<i class="fas fa-sign-in-alt"></i> Login
|
||||
</button>
|
||||
</form>
|
||||
<div class="text-center mt-3">
|
||||
<a href="/sig-bansos/PENGGUNA%20UMUM.html" class="text-muted">Lihat sebagai Pengguna Umum</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.getElementById('loginForm').addEventListener('submit', async function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const username = document.getElementById('username').value;
|
||||
const password = document.getElementById('password').value;
|
||||
|
||||
try {
|
||||
const response = await fetch('/sig-jalan/api_login.php?action=login', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({username, password})
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
sessionStorage.setItem('user', JSON.stringify(result.data));
|
||||
|
||||
if (result.data.role === 'admin') {
|
||||
window.location.href = '/sig-bansos/DASHBOARD%20ADMIN.html';
|
||||
} else {
|
||||
window.location.href = '/sig-bansos/PENGGUNA%20UMUM.html';
|
||||
}
|
||||
} else {
|
||||
document.getElementById('errorMsg').style.display = 'block';
|
||||
document.getElementById('errorMsg').textContent = result.message;
|
||||
}
|
||||
} catch (error) {
|
||||
document.getElementById('errorMsg').style.display = 'block';
|
||||
document.getElementById('errorMsg').textContent = 'Gagal terhubung ke server';
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
root /app;
|
||||
index index.html;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ =404;
|
||||
}
|
||||
|
||||
location /sig-jalan/ {
|
||||
try_files $uri $uri/ =404;
|
||||
}
|
||||
|
||||
location ~ \.php$ {
|
||||
fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
|
||||
fastcgi_index index.php;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
include fastcgi_params;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
[providers]
|
||||
type = "php"
|
||||
|
||||
[phases.setup]
|
||||
nixPkgs = ["php", "php82Extensions.mysqli", "php82Extensions.pdo", "php82Extensions.pdo_mysql"]
|
||||
|
||||
[phases.build]
|
||||
cmds = []
|
||||
|
||||
[phases.install]
|
||||
cmds = []
|
||||
|
||||
[start]
|
||||
cmd = "php-fpm -D && nginx -g 'daemon off;'"
|
||||
@@ -0,0 +1,142 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="id">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>SIG Bansos - Dashboard Admin</title>
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body { font-family: 'Segoe UI', sans-serif; background: #f0f2f5; }
|
||||
#map { height: 75vh; width: 100%; border-radius: 10px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); }
|
||||
.stat-card { background: white; border-radius: 10px; padding: 15px; text-align: center; box-shadow: 0 2px 4px rgba(0,0,0,0.1); cursor: pointer; transition: 0.3s; }
|
||||
.stat-card:hover { transform: translateY(-3px); }
|
||||
.stat-card h3 { font-size: 28px; font-weight: 700; margin: 0; }
|
||||
.stat-card p { margin: 0; font-size: 12px; color: #6c757d; }
|
||||
.nav-card { background: white; border-radius: 10px; padding: 20px; text-align: center; box-shadow: 0 2px 4px rgba(0,0,0,0.1); cursor: pointer; transition: 0.3s; text-decoration: none; color: #333; display: block; }
|
||||
.nav-card:hover { transform: translateY(-3px); box-shadow: 0 6px 12px rgba(0,0,0,0.15); }
|
||||
.nav-card i { font-size: 36px; display: block; margin-bottom: 10px; }
|
||||
.leaflet-popup-content-wrapper { border-radius: 12px; padding: 0; overflow: hidden; }
|
||||
.leaflet-popup-content { margin: 0 !important; width: 250px; }
|
||||
.popup-header { padding: 10px; color: white; font-weight: 600; font-size: 13px; }
|
||||
.popup-body { padding: 10px; font-size: 12px; }
|
||||
.popup-row { display: flex; justify-content: space-between; margin-bottom: 3px; }
|
||||
.popup-label { color: #888; }
|
||||
.popup-value { font-weight: 500; }
|
||||
.legend { background: white; padding: 8px 12px; border-radius: 8px; font-size: 11px; }
|
||||
.legend-item { display: inline-flex; align-items: center; gap: 5px; margin-right: 12px; }
|
||||
.legend-dot { width: 10px; height: 10px; border-radius: 50%; display: inline-block; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
const user = JSON.parse(sessionStorage.getItem('user'));
|
||||
if (!user || user.role !== 'admin') {
|
||||
window.location.href = '/login.html';
|
||||
}
|
||||
</script>
|
||||
|
||||
<nav class="navbar navbar-dark bg-dark mb-3">
|
||||
<div class="container-fluid">
|
||||
<span class="navbar-brand"><i class="fas fa-map-marked-alt"></i> SIG Bantuan Sosial</span>
|
||||
<div>
|
||||
<span class="badge bg-warning text-dark me-2">Administrator</span>
|
||||
<button class="btn btn-danger btn-sm" onclick="logout()"><i class="fas fa-sign-out-alt"></i> Logout</button>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-3">
|
||||
<div class="stat-card">
|
||||
<h3 id="totalOrang" style="color:#17a2b8;">0</h3>
|
||||
<p>Orang Berkebutuhan</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="stat-card">
|
||||
<h3 id="totalIbadah" style="color:#28a745;">0</h3>
|
||||
<p>Tempat Ibadah</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<a href="/sig-bansos/ORANG%20BERKEBUTUHAN.html" class="nav-card">
|
||||
<i class="fas fa-user-edit" style="color:#17a2b8;"></i>
|
||||
Kelola Orang
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<a href="/sig-bansos/RUMAH%20IBADAT.html" class="nav-card">
|
||||
<i class="fas fa-place-of-worship" style="color:#28a745;"></i>
|
||||
Kelola Ibadah
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="legend mb-2">
|
||||
<strong>Legenda:</strong>
|
||||
<span class="legend-item"><span class="legend-dot" style="background:#17a2b8;"></span> Orang</span>
|
||||
<span class="legend-item"><span class="legend-dot" style="background:#28a745;"></span> Masjid</span>
|
||||
<span class="legend-item"><span class="legend-dot" style="background:#dc3545;"></span> Gereja</span>
|
||||
<span class="legend-item"><span class="legend-dot" style="background:#ff6f00;"></span> Klenteng</span>
|
||||
<span class="legend-item"><span class="legend-dot" style="background:#ff9800;"></span> Vihara</span>
|
||||
<span class="legend-item"><span class="legend-dot" style="background:#9c27b0;"></span> Pura</span>
|
||||
</div>
|
||||
<div id="map"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function logout() {
|
||||
sessionStorage.removeItem('user');
|
||||
window.location.href = '/login.html';
|
||||
}
|
||||
|
||||
const API_ORANG = '/sig-jalan/api_orang.php';
|
||||
const API_IBADAH = '/sig-jalan/api_ibadah.php';
|
||||
const W = { masjid:'#28a745', gereja:'#dc3545', klenteng:'#ff6f00', vihara:'#ff9800', pura:'#9c27b0' };
|
||||
const I = { masjid:'fa-mosque', gereja:'fa-church', klenteng:'fa-torii-gate', vihara:'fa-om', pura:'fa-gopuram' };
|
||||
|
||||
var map = L.map('map').setView([-0.026330, 109.342519], 13);
|
||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 19 }).addTo(map);
|
||||
|
||||
async function load() {
|
||||
const ro = await fetch(API_ORANG+'?action=getAll').then(r=>r.json());
|
||||
const orang = ro.success ? ro.data : [];
|
||||
|
||||
let ibadah = [];
|
||||
for(const j of ['masjid','gereja','klenteng','vihara','pura']) {
|
||||
const ri = await fetch(API_IBADAH+'?type='+j+'&action=getAll').then(r=>r.json());
|
||||
if(ri.success) ri.data.forEach(d => { d._jenis=j; ibadah.push(d); });
|
||||
}
|
||||
|
||||
document.getElementById('totalOrang').textContent = orang.length;
|
||||
document.getElementById('totalIbadah').textContent = ibadah.length;
|
||||
|
||||
orang.forEach(o => {
|
||||
const m = L.marker([parseFloat(o.latitude), parseFloat(o.longitude)], {
|
||||
icon: L.divIcon({html:'<i class="fas fa-user" style="font-size:22px;color:#17a2b8;"></i>',className:'',iconSize:[22,22],iconAnchor:[11,22]})
|
||||
}).addTo(map);
|
||||
m.bindPopup(`<div><div class="popup-header" style="background:#17a2b8"><i class="fas fa-user"></i> ${o.nama}</div><div class="popup-body"><div class="popup-row"><span class="popup-label">Kebutuhan</span><span class="popup-value">${o.jenis_kebutuhan||'-'}</span></div><div class="popup-row"><span class="popup-label">Alamat</span><span class="popup-value">${o.alamat||'-'}</span></div></div></div>`);
|
||||
});
|
||||
|
||||
ibadah.forEach(d => {
|
||||
const m = L.marker([parseFloat(d.latitude), parseFloat(d.longitude)], {
|
||||
icon: L.divIcon({html:'<i class="fas '+I[d._jenis]+'" style="font-size:22px;color:'+W[d._jenis]+';"></i>',className:'',iconSize:[22,22],iconAnchor:[11,22]})
|
||||
}).addTo(map);
|
||||
m.bindPopup(`<div><div class="popup-header" style="background:${W[d._jenis]}"><i class="fas ${I[d._jenis]}"></i> ${d.nama}</div><div class="popup-body"><div class="popup-row"><span class="popup-label">Jenis</span><span class="popup-value">${d._jenis}</span></div><div class="popup-row"><span class="popup-label">Alamat</span><span class="popup-value">${d.alamat||'-'}</span></div></div></div>`);
|
||||
});
|
||||
}
|
||||
|
||||
load();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,274 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="id">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>SIG Bansos - Orang Berkebutuhan</title>
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body { font-family: 'Segoe UI', sans-serif; background: #f0f2f5; }
|
||||
#map { height: 70vh; width: 100%; border-radius: 10px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); }
|
||||
.sidebar { background: white; border-radius: 10px; padding: 20px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); height: 70vh; overflow-y: auto; }
|
||||
.form-group { margin-bottom: 12px; }
|
||||
.form-group label { font-weight: 600; margin-bottom: 3px; display: block; color: #495057; font-size: 13px; }
|
||||
.form-group input, .form-group select, .form-group textarea { width: 100%; padding: 8px 10px; border: 1px solid #ced4da; border-radius: 6px; font-size: 13px; }
|
||||
.btn { padding: 8px 16px; font-size: 13px; border-radius: 6px; margin-right: 5px; }
|
||||
.data-list { max-height: 300px; overflow-y: auto; margin-top: 15px; }
|
||||
.data-item { background: #f8f9fa; padding: 10px; margin-bottom: 8px; border-radius: 8px; cursor: pointer; border-left: 4px solid #17a2b8; }
|
||||
.data-item:hover { background: #e9ecef; }
|
||||
.data-item strong { display: block; color: #17a2b8; font-size: 14px; }
|
||||
.data-item small { color: #6c757d; font-size: 11px; display: block; }
|
||||
.draw-controls { margin-top: 10px; padding: 10px; background: white; border-radius: 8px; }
|
||||
.draw-status { background: #d1ecf1; padding: 10px; border-radius: 6px; margin-top: 10px; font-size: 13px; border-left: 4px solid #17a2b8; display: none; }
|
||||
.toast-notification { position: fixed; top: 20px; right: 20px; z-index: 10000; min-width: 280px; }
|
||||
.leaflet-popup-content-wrapper { border-radius: 12px; padding: 0; overflow: hidden; }
|
||||
.leaflet-popup-content { margin: 0 !important; width: 260px; }
|
||||
.popup-header { padding: 10px; color: white; font-weight: 600; font-size: 14px; }
|
||||
.popup-body { padding: 10px 12px; font-size: 12px; }
|
||||
.popup-row { display: flex; justify-content: space-between; margin-bottom: 3px; }
|
||||
.popup-label { color: #888; }
|
||||
.popup-value { font-weight: 500; }
|
||||
.popup-footer { display: flex; border-top: 1px solid #eee; }
|
||||
.popup-btn { flex: 1; padding: 8px; border: none; font-size: 11px; cursor: pointer; }
|
||||
.popup-btn-edit { background: #fff3cd; color: #856404; }
|
||||
.popup-btn-delete { background: #f8d7da; color: #842029; }
|
||||
.search-box { margin-bottom: 10px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
const user = JSON.parse(sessionStorage.getItem('user'));
|
||||
if (!user || user.role !== 'admin') {
|
||||
window.location.href = '/login.html';
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="container-fluid p-4">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h4><i class="fas fa-map-marked-alt"></i> SIG Bansos - Orang Berkebutuhan</h4>
|
||||
<button class="btn btn-danger btn-sm" onclick="logout()"><i class="fas fa-sign-out-alt"></i> Logout</button>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<div id="map"></div>
|
||||
<div class="draw-controls">
|
||||
<button class="btn btn-info btn-sm" onclick="startDrawing()"><i class="fas fa-user-plus"></i> Tambah Orang</button>
|
||||
<button class="btn btn-secondary btn-sm" onclick="cancelDrawing()"><i class="fas fa-times"></i> Batal</button>
|
||||
</div>
|
||||
<div id="drawStatus" class="draw-status"></div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="sidebar">
|
||||
<h5><i class="fas fa-user"></i> Form Orang Berkebutuhan</h5>
|
||||
<form id="orangForm">
|
||||
<input type="hidden" id="orangId">
|
||||
<div class="form-group"><label>Nama Lengkap</label><input type="text" id="namaOrang" class="form-control" placeholder="Nama" required></div>
|
||||
<div class="form-group"><label>Alamat</label><input type="text" id="alamatOrang" class="form-control" placeholder="Alamat"></div>
|
||||
<div class="form-group"><label>Jenis Kebutuhan</label><select id="jenisKebutuhan" class="form-control"><option value="">-- Pilih --</option><option value="sembako">🍚 Sembako</option><option value="kesehatan">💊 Kesehatan</option><option value="pendidikan">📚 Pendidikan</option><option value="sandang">👕 Sandang</option><option value="uang">💰 Uang Tunai</option><option value="lainnya">📋 Lainnya</option></select></div>
|
||||
<div class="form-group"><label>No HP</label><input type="text" id="noHpOrang" class="form-control" placeholder="No HP"></div>
|
||||
<div class="form-group"><label>Koordinat</label><input type="text" id="koordinatOrang" class="form-control" readonly placeholder="Klik pada peta"></div>
|
||||
<div class="form-group"><label>Keterangan</label><textarea id="keteranganOrang" class="form-control" rows="2" placeholder="Keterangan tambahan..."></textarea></div>
|
||||
<button type="submit" class="btn btn-primary"><i class="fas fa-save"></i> Simpan</button>
|
||||
<button type="button" class="btn btn-danger" onclick="resetForm()"><i class="fas fa-times"></i> Batal</button>
|
||||
</form>
|
||||
<hr>
|
||||
<h6>Daftar Orang Berkebutuhan</h6>
|
||||
<div class="search-box"><input type="text" id="searchOrang" class="form-control form-control-sm" placeholder="Cari nama..."></div>
|
||||
<div id="orangList" class="data-list"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="toast-notification" id="toastNotification"></div>
|
||||
|
||||
<script>
|
||||
function logout() {
|
||||
sessionStorage.removeItem('user');
|
||||
window.location.href = '/login.html';
|
||||
}
|
||||
|
||||
const API_URL = '/sig-jalan/api_orang.php';
|
||||
|
||||
var map = L.map('map').setView([-0.026330, 109.342519], 13);
|
||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 19 }).addTo(map);
|
||||
|
||||
let orangData = [];
|
||||
let layersOrang = {};
|
||||
let drawingMode = false;
|
||||
let currentMarker = null;
|
||||
let pendingCoord = null;
|
||||
|
||||
function showMsg(msg, type='success') {
|
||||
const bg = type==='success'?'bg-success':'bg-danger';
|
||||
const icon = type==='success'?'fa-check-circle':'fa-exclamation-circle';
|
||||
const el = document.getElementById('toastNotification');
|
||||
el.innerHTML = `<div class="alert ${bg} text-white alert-dismissible fade show mb-0"><i class="fas ${icon}"></i> ${msg}<button class="btn-close" data-bs-dismiss="alert"></button></div>`;
|
||||
setTimeout(()=>el.innerHTML='',3000);
|
||||
}
|
||||
|
||||
async function apiCall(url, method='GET', data=null) {
|
||||
const opt = { method };
|
||||
if(data && (method==='POST'||method==='PUT')) {
|
||||
opt.headers = {'Content-Type':'application/json'};
|
||||
opt.body = JSON.stringify(data);
|
||||
}
|
||||
const res = await fetch(url, opt);
|
||||
return await res.json();
|
||||
}
|
||||
|
||||
async function loadData() {
|
||||
const result = await apiCall(API_URL+'?action=getAll');
|
||||
if(result && result.success) { orangData = result.data; renderMarkers(); }
|
||||
}
|
||||
|
||||
function renderMarkers() {
|
||||
Object.values(layersOrang).forEach(l => map.removeLayer(l));
|
||||
layersOrang = {};
|
||||
|
||||
orangData.forEach(o => {
|
||||
const color = o.jenis_kebutuhan ? '#28a745' : '#dc3545';
|
||||
const icon = L.divIcon({
|
||||
html: `<i class="fas fa-user" style="font-size:28px;color:${color}"></i>`,
|
||||
className: 'custom-marker', iconSize: [28,28], iconAnchor: [14,28], popupAnchor: [0,-28]
|
||||
});
|
||||
|
||||
const marker = L.marker([parseFloat(o.latitude), parseFloat(o.longitude)], {icon}).addTo(map);
|
||||
|
||||
marker.bindPopup(`
|
||||
<div>
|
||||
<div class="popup-header" style="background:${color}"><i class="fas fa-user"></i> ${o.nama}</div>
|
||||
<div class="popup-body">
|
||||
<div class="popup-row"><span class="popup-label">Alamat</span><span class="popup-value">${o.alamat||'-'}</span></div>
|
||||
<div class="popup-row"><span class="popup-label">Kebutuhan</span><span class="popup-value">${o.jenis_kebutuhan||'-'}</span></div>
|
||||
<div class="popup-row"><span class="popup-label">Keterangan</span><span class="popup-value">${o.keterangan||'-'}</span></div>
|
||||
</div>
|
||||
<div class="popup-footer">
|
||||
<button class="popup-btn popup-btn-edit" onclick="editData(${o.id})"><i class="fas fa-edit"></i></button>
|
||||
<button class="popup-btn popup-btn-delete" onclick="deleteData(${o.id})"><i class="fas fa-trash"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
|
||||
layersOrang[o.id] = marker;
|
||||
});
|
||||
displayList();
|
||||
}
|
||||
|
||||
function displayList(data=null) {
|
||||
const list = data || orangData;
|
||||
document.getElementById('orangList').innerHTML = list.length ? list.map(o => `
|
||||
<div class="data-item" onclick="zoomTo(${o.id})">
|
||||
<strong><i class="fas fa-user"></i> ${o.nama}</strong>
|
||||
<small>${o.jenis_kebutuhan||'Belum diisi'} | ${o.alamat||'-'}</small>
|
||||
</div>
|
||||
`).join('') : '<p class="text-muted text-center">Tidak ada data</p>';
|
||||
}
|
||||
|
||||
function startDrawing() {
|
||||
drawingMode = true;
|
||||
document.getElementById('drawStatus').style.display = 'block';
|
||||
document.getElementById('drawStatus').innerHTML = '<i class="fas fa-info-circle"></i> Klik pada peta untuk menentukan lokasi';
|
||||
map.on('click', onMapClick);
|
||||
}
|
||||
|
||||
function onMapClick(e) {
|
||||
if(!drawingMode) return;
|
||||
if(currentMarker) map.removeLayer(currentMarker);
|
||||
|
||||
const lat = e.latlng.lat;
|
||||
const lng = e.latlng.lng;
|
||||
|
||||
currentMarker = L.marker([lat,lng], {
|
||||
icon: L.divIcon({html:'<i class="fas fa-user" style="font-size:28px;color:#dc3545"></i>',className:'custom-marker',iconSize:[28,28],iconAnchor:[14,28]}),
|
||||
draggable: true
|
||||
}).addTo(map);
|
||||
|
||||
pendingCoord = {lat, lng};
|
||||
document.getElementById('koordinatOrang').value = lat.toFixed(6)+', '+lng.toFixed(6);
|
||||
|
||||
currentMarker.on('dragend', function() {
|
||||
const p = currentMarker.getLatLng();
|
||||
pendingCoord = {lat: p.lat, lng: p.lng};
|
||||
document.getElementById('koordinatOrang').value = p.lat.toFixed(6)+', '+p.lng.toFixed(6);
|
||||
});
|
||||
}
|
||||
|
||||
function cancelDrawing() {
|
||||
drawingMode = false;
|
||||
map.off('click', onMapClick);
|
||||
document.getElementById('drawStatus').style.display = 'none';
|
||||
if(currentMarker) { map.removeLayer(currentMarker); currentMarker = null; }
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
document.getElementById('orangForm').reset();
|
||||
document.getElementById('orangId').value = '';
|
||||
document.getElementById('koordinatOrang').value = '';
|
||||
pendingCoord = null;
|
||||
cancelDrawing();
|
||||
}
|
||||
|
||||
async function saveData(data) {
|
||||
const action = data.id ? 'update&id='+data.id : 'create';
|
||||
const result = await apiCall(API_URL+'?action='+action, data.id?'PUT':'POST', data);
|
||||
if(result && result.success) { showMsg('Data berhasil disimpan'); resetForm(); loadData(); }
|
||||
else { showMsg(result?.message||'Gagal menyimpan','error'); }
|
||||
}
|
||||
|
||||
async function editData(id) {
|
||||
const o = orangData.find(d => d.id === id);
|
||||
if(!o) return;
|
||||
document.getElementById('orangId').value = o.id;
|
||||
document.getElementById('namaOrang').value = o.nama;
|
||||
document.getElementById('alamatOrang').value = o.alamat||'';
|
||||
document.getElementById('jenisKebutuhan').value = o.jenis_kebutuhan||'';
|
||||
document.getElementById('noHpOrang').value = o.no_hp||'';
|
||||
document.getElementById('koordinatOrang').value = o.latitude+', '+o.longitude;
|
||||
document.getElementById('keteranganOrang').value = o.keterangan||'';
|
||||
pendingCoord = {lat: parseFloat(o.latitude), lng: parseFloat(o.longitude)};
|
||||
map.setView([o.latitude, o.longitude], 17);
|
||||
}
|
||||
|
||||
async function deleteData(id) {
|
||||
if(!confirm('Hapus data ini?')) return;
|
||||
const result = await apiCall(API_URL+'?action=delete&id='+id, 'DELETE');
|
||||
if(result && result.success) { showMsg('Data dihapus'); loadData(); }
|
||||
else { showMsg('Gagal menghapus','error'); }
|
||||
}
|
||||
|
||||
function zoomTo(id) {
|
||||
const o = orangData.find(d => d.id === id);
|
||||
if(o) map.flyTo([o.latitude, o.longitude], 17);
|
||||
}
|
||||
|
||||
document.getElementById('searchOrang').addEventListener('input', function() {
|
||||
const kw = this.value.toLowerCase();
|
||||
displayList(orangData.filter(o => o.nama.toLowerCase().includes(kw)));
|
||||
});
|
||||
|
||||
document.getElementById('orangForm').addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
if(!pendingCoord && !document.getElementById('orangId').value) {
|
||||
showMsg('Klik pada peta dulu!','error'); return;
|
||||
}
|
||||
saveData({
|
||||
id: document.getElementById('orangId').value || null,
|
||||
nama: document.getElementById('namaOrang').value,
|
||||
alamat: document.getElementById('alamatOrang').value,
|
||||
jenis_kebutuhan: document.getElementById('jenisKebutuhan').value,
|
||||
no_hp: document.getElementById('noHpOrang').value,
|
||||
keterangan: document.getElementById('keteranganOrang').value,
|
||||
latitude: pendingCoord?.lat,
|
||||
longitude: pendingCoord?.lng
|
||||
});
|
||||
});
|
||||
|
||||
loadData();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,236 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="id">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>SIG Bansos - Pengguna Umum</title>
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body { font-family: 'Segoe UI', sans-serif; background: #f0f2f5; }
|
||||
#map { height: 80vh; width: 100%; border-radius: 10px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); }
|
||||
.sidebar { background: white; border-radius: 10px; padding: 15px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); height: 80vh; overflow-y: auto; }
|
||||
.stat-box { background: white; border-radius: 10px; padding: 15px; text-align: center; box-shadow: 0 2px 4px rgba(0,0,0,0.1); margin-bottom: 10px; }
|
||||
.stat-box h3 { font-size: 24px; font-weight: 700; margin: 0; }
|
||||
.stat-box p { margin: 0; font-size: 12px; color: #6c757d; }
|
||||
.data-list { max-height: 400px; overflow-y: auto; }
|
||||
.data-item { background: #f8f9fa; padding: 10px; margin-bottom: 6px; border-radius: 8px; cursor: pointer; border-left: 4px solid #0d6efd; }
|
||||
.data-item:hover { background: #e9ecef; }
|
||||
.data-item strong { display: block; font-size: 13px; }
|
||||
.data-item small { color: #6c757d; font-size: 11px; display: block; }
|
||||
.legend { background: white; padding: 10px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
|
||||
.legend-item { display: flex; align-items: center; gap: 8px; margin-bottom: 5px; font-size: 12px; }
|
||||
.legend-dot { width: 12px; height: 12px; border-radius: 50%; }
|
||||
.leaflet-popup-content-wrapper { border-radius: 12px; padding: 0; overflow: hidden; }
|
||||
.leaflet-popup-content { margin: 0 !important; width: 260px; }
|
||||
.popup-header { padding: 10px; color: white; font-weight: 600; font-size: 14px; }
|
||||
.popup-body { padding: 10px 12px; font-size: 12px; }
|
||||
.popup-row { display: flex; justify-content: space-between; margin-bottom: 3px; }
|
||||
.popup-label { color: #888; }
|
||||
.popup-value { font-weight: 500; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container-fluid p-3">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h4><i class="fas fa-map-marked-alt"></i> SIG Bantuan Sosial - Peta Persebaran</h4>
|
||||
<span class="badge bg-info">Pengguna Umum</span>
|
||||
</div>
|
||||
|
||||
<!-- Statistik -->
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-3">
|
||||
<div class="stat-box">
|
||||
<h3 id="totalOrang" style="color:#17a2b8;">0</h3>
|
||||
<p>Orang Berkebutuhan</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="stat-box">
|
||||
<h3 id="totalIbadah" style="color:#28a745;">0</h3>
|
||||
<p>Tempat Ibadah</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="legend">
|
||||
<strong><i class="fas fa-list"></i> Legenda:</strong>
|
||||
<div class="row mt-2">
|
||||
<div class="col-6">
|
||||
<div class="legend-item"><div class="legend-dot" style="background:#17a2b8;"></div> Orang Berkebutuhan</div>
|
||||
<div class="legend-item"><div class="legend-dot" style="background:#28a745;"></div> Masjid</div>
|
||||
<div class="legend-item"><div class="legend-dot" style="background:#dc3545;"></div> Gereja</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="legend-item"><div class="legend-dot" style="background:#ff6f00;"></div> Klenteng</div>
|
||||
<div class="legend-item"><div class="legend-dot" style="background:#ff9800;"></div> Vihara</div>
|
||||
<div class="legend-item"><div class="legend-dot" style="background:#9c27b0;"></div> Pura</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<div id="map"></div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="sidebar">
|
||||
<ul class="nav nav-tabs" id="listTab" role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link active" data-bs-toggle="tab" data-bs-target="#tabOrang">👤 Orang Berkebutuhan</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#tabIbadah">🕌 Tempat Ibadah</button>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tab-content mt-3">
|
||||
<div class="tab-pane fade show active" id="tabOrang">
|
||||
<input type="text" class="form-control form-control-sm mb-2" placeholder="Cari orang..." oninput="filterList('orang', this.value)">
|
||||
<div id="orangList" class="data-list"></div>
|
||||
</div>
|
||||
<div class="tab-pane fade" id="tabIbadah">
|
||||
<input type="text" class="form-control form-control-sm mb-2" placeholder="Cari tempat ibadah..." oninput="filterList('ibadah', this.value)">
|
||||
<div id="ibadahList" class="data-list"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const API_ORANG = '/sig-jalan/api_orang.php';
|
||||
const API_IBADAH = '/sig-jalan/api_ibadah.php';
|
||||
const warnaIbadah = { masjid: '#28a745', gereja: '#dc3545', klenteng: '#ff6f00', vihara: '#ff9800', pura: '#9c27b0' };
|
||||
const ikonIbadah = { masjid: 'fa-mosque', gereja: 'fa-church', klenteng: 'fa-torii-gate', vihara: 'fa-om', pura: 'fa-gopuram' };
|
||||
|
||||
var map = L.map('map').setView([-0.026330, 109.342519], 13);
|
||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 19 }).addTo(map);
|
||||
|
||||
let orangData = [];
|
||||
let ibadahData = [];
|
||||
|
||||
async function apiCall(url) {
|
||||
const res = await fetch(url);
|
||||
return await res.json();
|
||||
}
|
||||
|
||||
async function loadAllData() {
|
||||
// Load orang
|
||||
const resOrang = await apiCall(API_ORANG+'?action=getAll');
|
||||
if(resOrang.success) orangData = resOrang.data;
|
||||
|
||||
// Load ibadah
|
||||
const jenisList = ['masjid','gereja','klenteng','vihara','pura'];
|
||||
ibadahData = [];
|
||||
for(const j of jenisList) {
|
||||
const res = await apiCall(API_IBADAH+'?type='+j+'&action=getAll');
|
||||
if(res.success) {
|
||||
res.data.forEach(d => d._jenis = j);
|
||||
ibadahData = ibadahData.concat(res.data);
|
||||
}
|
||||
}
|
||||
|
||||
updateStats();
|
||||
renderMarkers();
|
||||
renderLists();
|
||||
}
|
||||
|
||||
function updateStats() {
|
||||
document.getElementById('totalOrang').textContent = orangData.length;
|
||||
document.getElementById('totalIbadah').textContent = ibadahData.length;
|
||||
}
|
||||
|
||||
function renderMarkers() {
|
||||
// Marker Orang
|
||||
orangData.forEach(o => {
|
||||
const icon = L.divIcon({
|
||||
html: '<i class="fas fa-user" style="font-size:24px;color:#17a2b8;"></i>',
|
||||
className: 'custom-marker', iconSize: [24,24], iconAnchor: [12,24], popupAnchor: [0,-24]
|
||||
});
|
||||
const m = L.marker([parseFloat(o.latitude), parseFloat(o.longitude)], {icon}).addTo(map);
|
||||
m.bindPopup(`
|
||||
<div>
|
||||
<div class="popup-header" style="background:#17a2b8"><i class="fas fa-user"></i> ${o.nama}</div>
|
||||
<div class="popup-body">
|
||||
<div class="popup-row"><span class="popup-label">Alamat</span><span class="popup-value">${o.alamat||'-'}</span></div>
|
||||
<div class="popup-row"><span class="popup-label">Kebutuhan</span><span class="popup-value">${o.jenis_kebutuhan||'-'}</span></div>
|
||||
<div class="popup-row"><span class="popup-label">Keterangan</span><span class="popup-value">${o.keterangan||'-'}</span></div>
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
});
|
||||
|
||||
// Marker Ibadah
|
||||
ibadahData.forEach(d => {
|
||||
const color = warnaIbadah[d._jenis];
|
||||
const icon = L.divIcon({
|
||||
html: '<i class="fas '+ikonIbadah[d._jenis]+'" style="font-size:24px;color:'+color+';"></i>',
|
||||
className: 'custom-marker', iconSize: [24,24], iconAnchor: [12,24], popupAnchor: [0,-24]
|
||||
});
|
||||
const m = L.marker([parseFloat(d.latitude), parseFloat(d.longitude)], {icon}).addTo(map);
|
||||
m.bindPopup(`
|
||||
<div>
|
||||
<div class="popup-header" style="background:${color}"><i class="fas ${ikonIbadah[d._jenis]}"></i> ${d.nama}</div>
|
||||
<div class="popup-body">
|
||||
<div class="popup-row"><span class="popup-label">Jenis</span><span class="popup-value">${d._jenis}</span></div>
|
||||
<div class="popup-row"><span class="popup-label">Alamat</span><span class="popup-value">${d.alamat||'-'}</span></div>
|
||||
<div class="popup-row"><span class="popup-label">Keterangan</span><span class="popup-value">${d.keterangan||'-'}</span></div>
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
});
|
||||
}
|
||||
|
||||
function renderLists() {
|
||||
document.getElementById('orangList').innerHTML = orangData.map(o => `
|
||||
<div class="data-item" onclick="zoomTo(${o.latitude},${o.longitude})">
|
||||
<strong><i class="fas fa-user"></i> ${o.nama}</strong>
|
||||
<small>${o.jenis_kebutuhan||'-'} | ${o.alamat||'-'}</small>
|
||||
</div>
|
||||
`).join('') || '<p class="text-muted text-center">Tidak ada data</p>';
|
||||
|
||||
document.getElementById('ibadahList').innerHTML = ibadahData.map(d => `
|
||||
<div class="data-item" style="border-left-color:${warnaIbadah[d._jenis]}" onclick="zoomTo(${d.latitude},${d.longitude})">
|
||||
<strong style="color:${warnaIbadah[d._jenis]}"><i class="fas ${ikonIbadah[d._jenis]}"></i> ${d.nama}</strong>
|
||||
<small>${d._jenis} | ${d.alamat||'-'}</small>
|
||||
</div>
|
||||
`).join('') || '<p class="text-muted text-center">Tidak ada data</p>';
|
||||
}
|
||||
|
||||
function filterList(type, keyword) {
|
||||
const kw = keyword.toLowerCase();
|
||||
const data = type === 'orang' ? orangData : ibadahData;
|
||||
const filtered = data.filter(d => d.nama.toLowerCase().includes(kw) || (d.alamat||'').toLowerCase().includes(kw));
|
||||
const container = type === 'orang' ? document.getElementById('orangList') : document.getElementById('ibadahList');
|
||||
|
||||
if(type === 'orang') {
|
||||
container.innerHTML = filtered.map(o => `
|
||||
<div class="data-item" onclick="zoomTo(${o.latitude},${o.longitude})">
|
||||
<strong><i class="fas fa-user"></i> ${o.nama}</strong>
|
||||
<small>${o.jenis_kebutuhan||'-'} | ${o.alamat||'-'}</small>
|
||||
</div>
|
||||
`).join('') || '<p class="text-muted text-center">Tidak ada data</p>';
|
||||
} else {
|
||||
container.innerHTML = filtered.map(d => `
|
||||
<div class="data-item" style="border-left-color:${warnaIbadah[d._jenis]}" onclick="zoomTo(${d.latitude},${d.longitude})">
|
||||
<strong style="color:${warnaIbadah[d._jenis]}"><i class="fas ${ikonIbadah[d._jenis]}"></i> ${d.nama}</strong>
|
||||
<small>${d._jenis} | ${d.alamat||'-'}</small>
|
||||
</div>
|
||||
`).join('') || '<p class="text-muted text-center">Tidak ada data</p>';
|
||||
}
|
||||
}
|
||||
|
||||
function zoomTo(lat, lng) {
|
||||
map.flyTo([parseFloat(lat), parseFloat(lng)], 17);
|
||||
}
|
||||
|
||||
loadAllData();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,329 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="id">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>SIG Bansos - Tempat Ibadah</title>
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body { font-family: 'Segoe UI', sans-serif; background: #f0f2f5; }
|
||||
#map { height: 70vh; width: 100%; border-radius: 10px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); }
|
||||
.sidebar { background: white; border-radius: 10px; padding: 20px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); height: 70vh; overflow-y: auto; }
|
||||
.form-group { margin-bottom: 12px; }
|
||||
.form-group label { font-weight: 600; margin-bottom: 3px; display: block; color: #495057; font-size: 13px; }
|
||||
.form-group input, .form-group select, .form-group textarea { width: 100%; padding: 8px 10px; border: 1px solid #ced4da; border-radius: 6px; font-size: 13px; }
|
||||
.btn { padding: 8px 16px; font-size: 13px; border-radius: 6px; margin-right: 5px; }
|
||||
.data-list { max-height: 300px; overflow-y: auto; margin-top: 15px; }
|
||||
.data-item { background: #f8f9fa; padding: 10px; margin-bottom: 8px; border-radius: 8px; cursor: pointer; }
|
||||
.data-item:hover { background: #e9ecef; }
|
||||
.data-item strong { display: block; font-size: 14px; }
|
||||
.data-item small { color: #6c757d; font-size: 11px; display: block; }
|
||||
.draw-controls { margin-top: 10px; padding: 10px; background: white; border-radius: 8px; display: flex; flex-wrap: wrap; gap: 5px; }
|
||||
.draw-status { background: #e8f5e9; padding: 10px; border-radius: 6px; margin-top: 10px; font-size: 13px; border-left: 4px solid #28a745; display: none; }
|
||||
.toast-notification { position: fixed; top: 20px; right: 20px; z-index: 10000; min-width: 280px; }
|
||||
.leaflet-popup-content-wrapper { border-radius: 12px; padding: 0; overflow: hidden; }
|
||||
.leaflet-popup-content { margin: 0 !important; width: 260px; }
|
||||
.popup-header { padding: 10px; color: white; font-weight: 600; font-size: 14px; }
|
||||
.popup-body { padding: 10px 12px; font-size: 12px; }
|
||||
.popup-row { display: flex; justify-content: space-between; margin-bottom: 3px; }
|
||||
.popup-label { color: #888; }
|
||||
.popup-value { font-weight: 500; }
|
||||
.popup-footer { display: flex; border-top: 1px solid #eee; }
|
||||
.popup-btn { flex: 1; padding: 8px; border: none; font-size: 11px; cursor: pointer; }
|
||||
.popup-btn-edit { background: #fff3cd; color: #856404; }
|
||||
.popup-btn-delete { background: #f8d7da; color: #842029; }
|
||||
.search-box { margin-bottom: 10px; }
|
||||
.border-masjid { border-left: 4px solid #28a745; }
|
||||
.border-gereja { border-left: 4px solid #dc3545; }
|
||||
.border-klenteng { border-left: 4px solid #ff6f00; }
|
||||
.border-vihara { border-left: 4px solid #ff9800; }
|
||||
.border-pura { border-left: 4px solid #9c27b0; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
const user = JSON.parse(sessionStorage.getItem('user'));
|
||||
if (!user || user.role !== 'admin') {
|
||||
window.location.href = '/login.html';
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="container-fluid p-4">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h4><i class="fas fa-map-marked-alt"></i> SIG Bansos - Tempat Ibadah</h4>
|
||||
<button class="btn btn-danger btn-sm" onclick="logout()"><i class="fas fa-sign-out-alt"></i> Logout</button>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<div id="map"></div>
|
||||
<div class="draw-controls">
|
||||
<button class="btn btn-success btn-sm" onclick="startDrawing('masjid')"><i class="fas fa-mosque"></i> Masjid</button>
|
||||
<button class="btn btn-danger btn-sm" onclick="startDrawing('gereja')"><i class="fas fa-church"></i> Gereja</button>
|
||||
<button class="btn btn-warning btn-sm" onclick="startDrawing('klenteng')"><i class="fas fa-torii-gate"></i> Klenteng</button>
|
||||
<button class="btn btn-warning btn-sm" onclick="startDrawing('vihara')"><i class="fas fa-om"></i> Vihara</button>
|
||||
<button class="btn btn-sm" onclick="startDrawing('pura')" style="background:#9c27b0;color:white;"><i class="fas fa-gopuram"></i> Pura</button>
|
||||
<button class="btn btn-secondary btn-sm" onclick="cancelDrawing()"><i class="fas fa-times"></i> Batal</button>
|
||||
</div>
|
||||
<div id="drawStatus" class="draw-status"></div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="sidebar">
|
||||
<h5><i class="fas fa-place-of-worship"></i> Form Tempat Ibadah</h5>
|
||||
<form id="ibadahForm">
|
||||
<input type="hidden" id="ibadahId">
|
||||
<div class="form-group"><label>Jenis Ibadah</label><select id="jenisIbadah" class="form-control"><option value="masjid">🕌 Masjid</option><option value="gereja">⛪ Gereja</option><option value="klenteng">🏮 Klenteng</option><option value="vihara">🛕 Vihara</option><option value="pura">🕉️ Pura</option></select></div>
|
||||
<div class="form-group"><label>Nama</label><input type="text" id="namaIbadah" class="form-control" placeholder="Nama tempat ibadah" required></div>
|
||||
<div class="form-group"><label>Alamat</label><input type="text" id="alamatIbadah" class="form-control" placeholder="Alamat"></div>
|
||||
<div class="form-group"><label>No HP</label><input type="text" id="noHpIbadah" class="form-control" placeholder="No HP"></div>
|
||||
<div class="form-group"><label>Koordinat</label><input type="text" id="koordinatIbadah" class="form-control" readonly placeholder="Klik pada peta"></div>
|
||||
<div class="form-group"><label>Keterangan</label><textarea id="keteranganIbadah" class="form-control" rows="2" placeholder="Keterangan tambahan..."></textarea></div>
|
||||
<button type="submit" class="btn btn-primary"><i class="fas fa-save"></i> Simpan</button>
|
||||
<button type="button" class="btn btn-danger" onclick="resetForm()"><i class="fas fa-times"></i> Batal</button>
|
||||
</form>
|
||||
<hr>
|
||||
<h6>Daftar Tempat Ibadah</h6>
|
||||
<div class="search-box"><input type="text" id="searchIbadah" class="form-control form-control-sm" placeholder="Cari nama..."></div>
|
||||
<div style="margin-bottom:8px;">
|
||||
<button class="btn btn-outline-success btn-sm" onclick="filterData('masjid')">🕌</button>
|
||||
<button class="btn btn-outline-danger btn-sm" onclick="filterData('gereja')">⛪</button>
|
||||
<button class="btn btn-outline-warning btn-sm" onclick="filterData('klenteng')">🏮</button>
|
||||
<button class="btn btn-outline-warning btn-sm" onclick="filterData('vihara')">🛕</button>
|
||||
<button class="btn btn-outline-secondary btn-sm" onclick="filterData('pura')">🕉️</button>
|
||||
<button class="btn btn-outline-dark btn-sm" onclick="filterData('semua')">Semua</button>
|
||||
</div>
|
||||
<div id="ibadahList" class="data-list"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="toast-notification" id="toastNotification"></div>
|
||||
|
||||
<script>
|
||||
function logout() {
|
||||
sessionStorage.removeItem('user');
|
||||
window.location.href = '/login.html';
|
||||
}
|
||||
|
||||
const API_URL = '/sig-jalan/api_ibadah.php';
|
||||
const warna = { masjid: '#28a745', gereja: '#dc3545', klenteng: '#ff6f00', vihara: '#ff9800', pura: '#9c27b0' };
|
||||
const ikon = { masjid: 'fa-mosque', gereja: 'fa-church', klenteng: 'fa-torii-gate', vihara: 'fa-om', pura: 'fa-gopuram' };
|
||||
|
||||
var map = L.map('map').setView([-0.026330, 109.342519], 13);
|
||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 19 }).addTo(map);
|
||||
|
||||
let ibadahData = [];
|
||||
let layersIbadah = {};
|
||||
let drawingMode = false;
|
||||
let currentJenis = 'masjid';
|
||||
let currentMarker = null;
|
||||
let currentCircle = null;
|
||||
let pendingCoord = null;
|
||||
let currentFilter = 'semua';
|
||||
|
||||
function showMsg(msg, type='success') {
|
||||
const bg = type==='success'?'bg-success':'bg-danger';
|
||||
const icon = type==='success'?'fa-check-circle':'fa-exclamation-circle';
|
||||
const el = document.getElementById('toastNotification');
|
||||
el.innerHTML = `<div class="alert ${bg} text-white alert-dismissible fade show mb-0"><i class="fas ${icon}"></i> ${msg}<button class="btn-close" data-bs-dismiss="alert"></button></div>`;
|
||||
setTimeout(()=>el.innerHTML='',3000);
|
||||
}
|
||||
|
||||
async function apiCall(url, method='GET', data=null) {
|
||||
const opt = { method };
|
||||
if(data && (method==='POST'||method==='PUT')) {
|
||||
opt.headers = {'Content-Type':'application/json'};
|
||||
opt.body = JSON.stringify(data);
|
||||
}
|
||||
const res = await fetch(url, opt);
|
||||
return await res.json();
|
||||
}
|
||||
|
||||
async function loadData() {
|
||||
const jenisList = ['masjid','gereja','klenteng','vihara','pura'];
|
||||
ibadahData = [];
|
||||
for(const j of jenisList) {
|
||||
const result = await apiCall(API_URL+'?type='+j+'&action=getAll');
|
||||
if(result && result.success) {
|
||||
result.data.forEach(d => d._jenis = j);
|
||||
ibadahData = ibadahData.concat(result.data);
|
||||
}
|
||||
}
|
||||
renderMarkers();
|
||||
}
|
||||
|
||||
function renderMarkers() {
|
||||
Object.values(layersIbadah).forEach(l => {
|
||||
if(l.marker) map.removeLayer(l.marker);
|
||||
if(l.circle) map.removeLayer(l.circle);
|
||||
});
|
||||
layersIbadah = {};
|
||||
|
||||
const filtered = currentFilter==='semua' ? ibadahData : ibadahData.filter(d => d._jenis===currentFilter);
|
||||
|
||||
filtered.forEach(d => {
|
||||
const color = warna[d._jenis];
|
||||
const icon = L.divIcon({
|
||||
html: `<i class="fas ${ikon[d._jenis]}" style="font-size:28px;color:${color}"></i>`,
|
||||
className: 'custom-marker', iconSize: [28,28], iconAnchor: [14,28], popupAnchor: [0,-28]
|
||||
});
|
||||
|
||||
const marker = L.marker([parseFloat(d.latitude), parseFloat(d.longitude)], {icon}).addTo(map);
|
||||
const circle = L.circle([parseFloat(d.latitude), parseFloat(d.longitude)], {
|
||||
radius: d.radius||500, color: color, weight: 2, dashArray: '5,5', fillOpacity: 0.1
|
||||
}).addTo(map);
|
||||
|
||||
marker.bindPopup(`
|
||||
<div>
|
||||
<div class="popup-header" style="background:${color}"><i class="fas ${ikon[d._jenis]}"></i> ${d.nama}</div>
|
||||
<div class="popup-body">
|
||||
<div class="popup-row"><span class="popup-label">Jenis</span><span class="popup-value">${d._jenis}</span></div>
|
||||
<div class="popup-row"><span class="popup-label">Alamat</span><span class="popup-value">${d.alamat||'-'}</span></div>
|
||||
<div class="popup-row"><span class="popup-label">Keterangan</span><span class="popup-value">${d.keterangan||'-'}</span></div>
|
||||
</div>
|
||||
<div class="popup-footer">
|
||||
<button class="popup-btn popup-btn-edit" onclick="editData(${d.id},'${d._jenis}')"><i class="fas fa-edit"></i></button>
|
||||
<button class="popup-btn popup-btn-delete" onclick="deleteData(${d.id},'${d._jenis}')"><i class="fas fa-trash"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
|
||||
layersIbadah[d._jenis+'_'+d.id] = {marker, circle};
|
||||
});
|
||||
displayList();
|
||||
}
|
||||
|
||||
function displayList(data=null) {
|
||||
const list = data || (currentFilter==='semua' ? ibadahData : ibadahData.filter(d => d._jenis===currentFilter));
|
||||
document.getElementById('ibadahList').innerHTML = list.length ? list.map(d => `
|
||||
<div class="data-item border-${d._jenis}" onclick="zoomTo(${d.id},'${d._jenis}')">
|
||||
<strong style="color:${warna[d._jenis]}"><i class="fas ${ikon[d._jenis]}"></i> ${d.nama}</strong>
|
||||
<small>${d._jenis} | ${d.alamat||'-'}</small>
|
||||
</div>
|
||||
`).join('') : '<p class="text-muted text-center">Tidak ada data</p>';
|
||||
}
|
||||
|
||||
function startDrawing(jenis) {
|
||||
cancelDrawing();
|
||||
currentJenis = jenis;
|
||||
drawingMode = true;
|
||||
document.getElementById('jenisIbadah').value = jenis;
|
||||
document.getElementById('drawStatus').style.display = 'block';
|
||||
document.getElementById('drawStatus').style.borderLeftColor = warna[jenis];
|
||||
document.getElementById('drawStatus').innerHTML = '<i class="fas '+ikon[jenis]+'"></i> Klik peta untuk tentukan lokasi '+jenis;
|
||||
map.on('click', onMapClick);
|
||||
}
|
||||
|
||||
function onMapClick(e) {
|
||||
if(!drawingMode) return;
|
||||
if(currentMarker) map.removeLayer(currentMarker);
|
||||
if(currentCircle) map.removeLayer(currentCircle);
|
||||
|
||||
const lat = e.latlng.lat;
|
||||
const lng = e.latlng.lng;
|
||||
const color = warna[currentJenis];
|
||||
|
||||
currentMarker = L.marker([lat,lng], {
|
||||
icon: L.divIcon({html:'<i class="fas '+ikon[currentJenis]+'" style="font-size:28px;color:'+color+'"></i>',className:'custom-marker',iconSize:[28,28],iconAnchor:[14,28]}),
|
||||
draggable: true
|
||||
}).addTo(map);
|
||||
|
||||
currentCircle = L.circle([lat,lng], {radius:500, color:color, weight:2, dashArray:'5,5', fillOpacity:0.1}).addTo(map);
|
||||
|
||||
pendingCoord = {lat, lng};
|
||||
document.getElementById('koordinatIbadah').value = lat.toFixed(6)+', '+lng.toFixed(6);
|
||||
|
||||
currentMarker.on('dragend', function() {
|
||||
const p = currentMarker.getLatLng();
|
||||
pendingCoord = {lat: p.lat, lng: p.lng};
|
||||
document.getElementById('koordinatIbadah').value = p.lat.toFixed(6)+', '+p.lng.toFixed(6);
|
||||
currentCircle.setLatLng([p.lat, p.lng]);
|
||||
});
|
||||
}
|
||||
|
||||
function cancelDrawing() {
|
||||
drawingMode = false;
|
||||
map.off('click', onMapClick);
|
||||
document.getElementById('drawStatus').style.display = 'none';
|
||||
if(currentMarker) { map.removeLayer(currentMarker); currentMarker = null; }
|
||||
if(currentCircle) { map.removeLayer(currentCircle); currentCircle = null; }
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
document.getElementById('ibadahForm').reset();
|
||||
document.getElementById('ibadahId').value = '';
|
||||
document.getElementById('koordinatIbadah').value = '';
|
||||
pendingCoord = null;
|
||||
cancelDrawing();
|
||||
}
|
||||
|
||||
async function saveData(data) {
|
||||
const action = data.id ? 'update&id='+data.id : 'create';
|
||||
const result = await apiCall(API_URL+'?type='+data._jenis+'&action='+action, data.id?'PUT':'POST', data);
|
||||
if(result && result.success) { showMsg('Data berhasil disimpan'); resetForm(); loadData(); }
|
||||
else { showMsg(result?.message||'Gagal menyimpan','error'); }
|
||||
}
|
||||
|
||||
async function editData(id, jenis) {
|
||||
const d = ibadahData.find(item => item.id===id && item._jenis===jenis);
|
||||
if(!d) return;
|
||||
document.getElementById('ibadahId').value = d.id;
|
||||
document.getElementById('jenisIbadah').value = jenis;
|
||||
document.getElementById('namaIbadah').value = d.nama;
|
||||
document.getElementById('alamatIbadah').value = d.alamat||'';
|
||||
document.getElementById('noHpIbadah').value = d.no_hp||'';
|
||||
document.getElementById('koordinatIbadah').value = d.latitude+', '+d.longitude;
|
||||
document.getElementById('keteranganIbadah').value = d.keterangan||'';
|
||||
pendingCoord = {lat: parseFloat(d.latitude), lng: parseFloat(d.longitude)};
|
||||
currentJenis = jenis;
|
||||
map.setView([d.latitude, d.longitude], 17);
|
||||
}
|
||||
|
||||
async function deleteData(id, jenis) {
|
||||
if(!confirm('Hapus data ini?')) return;
|
||||
const result = await apiCall(API_URL+'?type='+jenis+'&action=delete&id='+id, 'DELETE');
|
||||
if(result && result.success) { showMsg('Data dihapus'); loadData(); }
|
||||
else { showMsg('Gagal menghapus','error'); }
|
||||
}
|
||||
|
||||
function zoomTo(id, jenis) {
|
||||
const d = ibadahData.find(item => item.id===id && item._jenis===jenis);
|
||||
if(d) map.flyTo([d.latitude, d.longitude], 17);
|
||||
}
|
||||
|
||||
function filterData(jenis) {
|
||||
currentFilter = jenis;
|
||||
renderMarkers();
|
||||
}
|
||||
|
||||
document.getElementById('searchIbadah').addEventListener('input', function() {
|
||||
const kw = this.value.toLowerCase();
|
||||
const base = currentFilter==='semua' ? ibadahData : ibadahData.filter(d => d._jenis===currentFilter);
|
||||
displayList(base.filter(d => d.nama.toLowerCase().includes(kw)));
|
||||
});
|
||||
|
||||
document.getElementById('ibadahForm').addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
if(!pendingCoord && !document.getElementById('ibadahId').value) {
|
||||
showMsg('Klik pada peta dulu!','error'); return;
|
||||
}
|
||||
saveData({
|
||||
id: document.getElementById('ibadahId').value || null,
|
||||
_jenis: document.getElementById('jenisIbadah').value,
|
||||
nama: document.getElementById('namaIbadah').value,
|
||||
alamat: document.getElementById('alamatIbadah').value,
|
||||
no_hp: document.getElementById('noHpIbadah').value,
|
||||
keterangan: document.getElementById('keteranganIbadah').value,
|
||||
latitude: pendingCoord?.lat,
|
||||
longitude: pendingCoord?.lng
|
||||
});
|
||||
});
|
||||
|
||||
loadData();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,300 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
header('Access-Control-Allow-Origin: *');
|
||||
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
|
||||
header('Access-Control-Allow-Headers: Content-Type');
|
||||
|
||||
// Handle preflight request
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||
http_response_code(200);
|
||||
exit();
|
||||
}
|
||||
|
||||
// Database configuration
|
||||
$host = 'sql208.infinityfree.com';
|
||||
$dbname = 'if0_42170546_sig_db';
|
||||
$username = 'if0_42170546';
|
||||
$password = 'SABTU01012005';
|
||||
|
||||
try {
|
||||
$pdo = new PDO("mysql:host=$host;dbname=$dbname;charset=utf8", $username, $password);
|
||||
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
} catch(PDOException $e) {
|
||||
echo json_encode(['success' => false, 'message' => 'Database connection failed: ' . $e->getMessage()]);
|
||||
exit();
|
||||
}
|
||||
|
||||
// Get request parameters
|
||||
$type = isset($_GET['type']) ? $_GET['type'] : '';
|
||||
$action = isset($_GET['action']) ? $_GET['action'] : '';
|
||||
$id = isset($_GET['id']) ? intval($_GET['id']) : null;
|
||||
$method = $_SERVER['REQUEST_METHOD'];
|
||||
|
||||
// Map type to table name
|
||||
$tables = [
|
||||
'spbu' => 'spbu',
|
||||
'jalan' => 'jalan',
|
||||
'parsil' => 'parsil',
|
||||
'rusak' => 'jalan_rusak'
|
||||
];
|
||||
|
||||
if (!isset($tables[$type])) {
|
||||
echo json_encode(['success' => false, 'message' => 'Invalid type']);
|
||||
exit();
|
||||
}
|
||||
|
||||
$table = $tables[$type];
|
||||
|
||||
// Handle different actions
|
||||
switch ($action) {
|
||||
case 'getAll':
|
||||
getAll($pdo, $table);
|
||||
break;
|
||||
case 'getOne':
|
||||
getOne($pdo, $table, $id);
|
||||
break;
|
||||
case 'create':
|
||||
if ($method === 'POST') {
|
||||
create($pdo, $table, $type);
|
||||
}
|
||||
break;
|
||||
case 'update':
|
||||
if ($method === 'PUT') {
|
||||
update($pdo, $table, $type, $id);
|
||||
}
|
||||
break;
|
||||
case 'delete':
|
||||
if ($method === 'DELETE') {
|
||||
delete($pdo, $table, $id);
|
||||
}
|
||||
break;
|
||||
case 'add': // Alias for create (SPBU)
|
||||
if ($method === 'POST') {
|
||||
create($pdo, $table, $type);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
echo json_encode(['success' => false, 'message' => 'Invalid action']);
|
||||
}
|
||||
|
||||
// ==================== FUNCTIONS ====================
|
||||
|
||||
function getAll($pdo, $table) {
|
||||
try {
|
||||
if ($table === 'jalan' || $table === 'parsil') {
|
||||
$stmt = $pdo->query("SELECT * FROM $table ORDER BY id DESC");
|
||||
$data = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// Decode JSON coordinates
|
||||
foreach ($data as &$row) {
|
||||
if (isset($row['coordinates'])) {
|
||||
$row['coordinates'] = json_decode($row['coordinates'], true);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$stmt = $pdo->query("SELECT * FROM $table ORDER BY id DESC");
|
||||
$data = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
echo json_encode(['success' => true, 'data' => $data]);
|
||||
} catch(PDOException $e) {
|
||||
echo json_encode(['success' => false, 'message' => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
function getOne($pdo, $table, $id) {
|
||||
try {
|
||||
$stmt = $pdo->prepare("SELECT * FROM $table WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
$data = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($data) {
|
||||
if (isset($data['coordinates'])) {
|
||||
$data['coordinates'] = json_decode($data['coordinates'], true);
|
||||
}
|
||||
echo json_encode(['success' => true, 'data' => $data]);
|
||||
} else {
|
||||
echo json_encode(['success' => false, 'message' => 'Data not found']);
|
||||
}
|
||||
} catch(PDOException $e) {
|
||||
echo json_encode(['success' => false, 'message' => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
function create($pdo, $table, $type) {
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
if (!$input) {
|
||||
// Check if it's form-data (for file upload)
|
||||
if ($type === 'rusak' && isset($_POST['nama'])) {
|
||||
createRusak($pdo);
|
||||
return;
|
||||
}
|
||||
echo json_encode(['success' => false, 'message' => 'No input data']);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
switch ($type) {
|
||||
case 'spbu':
|
||||
$stmt = $pdo->prepare("INSERT INTO spbu (nama, no_spbu, tipe, latitude, longitude) VALUES (?, ?, ?, ?, ?)");
|
||||
$stmt->execute([
|
||||
$input['nama'],
|
||||
$input['no_spbu'],
|
||||
$input['tipe'],
|
||||
$input['latitude'],
|
||||
$input['longitude']
|
||||
]);
|
||||
break;
|
||||
|
||||
case 'jalan':
|
||||
$coordinates = json_encode($input['coordinates']);
|
||||
$stmt = $pdo->prepare("INSERT INTO jalan (nama, status, panjang, coordinates) VALUES (?, ?, ?, ?)");
|
||||
$stmt->execute([
|
||||
$input['nama'],
|
||||
$input['status'],
|
||||
$input['panjang'],
|
||||
$coordinates
|
||||
]);
|
||||
break;
|
||||
|
||||
case 'parsil':
|
||||
$coordinates = json_encode($input['coordinates']);
|
||||
$stmt = $pdo->prepare("INSERT INTO parsil (nama, status, luas, coordinates) VALUES (?, ?, ?, ?)");
|
||||
$stmt->execute([
|
||||
$input['nama'],
|
||||
$input['status'],
|
||||
$input['luas'],
|
||||
$coordinates
|
||||
]);
|
||||
break;
|
||||
}
|
||||
|
||||
$newId = $pdo->lastInsertId();
|
||||
echo json_encode(['success' => true, 'message' => 'Data created successfully', 'id' => $newId]);
|
||||
} catch(PDOException $e) {
|
||||
echo json_encode(['success' => false, 'message' => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
function createRusak($pdo) {
|
||||
try {
|
||||
$nama = $_POST['nama'];
|
||||
$latitude = $_POST['latitude'];
|
||||
$longitude = $_POST['longitude'];
|
||||
$fotoPath = '';
|
||||
|
||||
// Handle file upload
|
||||
if (isset($_FILES['foto']) && $_FILES['foto']['error'] === UPLOAD_ERR_OK) {
|
||||
$uploadDir = 'uploads/rusak/';
|
||||
if (!is_dir($uploadDir)) {
|
||||
mkdir($uploadDir, 0777, true);
|
||||
}
|
||||
|
||||
$fileExtension = pathinfo($_FILES['foto']['name'], PATHINFO_EXTENSION);
|
||||
$fileName = 'rusak_' . time() . '_' . uniqid() . '.' . $fileExtension;
|
||||
$uploadPath = $uploadDir . $fileName;
|
||||
|
||||
if (move_uploaded_file($_FILES['foto']['tmp_name'], $uploadPath)) {
|
||||
$fotoPath = $uploadPath;
|
||||
}
|
||||
}
|
||||
|
||||
$stmt = $pdo->prepare("INSERT INTO jalan_rusak (nama, latitude, longitude, foto) VALUES (?, ?, ?, ?)");
|
||||
$stmt->execute([$nama, $latitude, $longitude, $fotoPath]);
|
||||
|
||||
$newId = $pdo->lastInsertId();
|
||||
echo json_encode(['success' => true, 'message' => 'Data created successfully', 'id' => $newId]);
|
||||
} catch(PDOException $e) {
|
||||
echo json_encode(['success' => false, 'message' => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
function update($pdo, $table, $type, $id) {
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
if (!$input) {
|
||||
echo json_encode(['success' => false, 'message' => 'No input data']);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
switch ($type) {
|
||||
case 'spbu':
|
||||
$stmt = $pdo->prepare("UPDATE spbu SET nama=?, no_spbu=?, tipe=?, latitude=?, longitude=? WHERE id=?");
|
||||
$stmt->execute([
|
||||
$input['nama'],
|
||||
$input['no_spbu'],
|
||||
$input['tipe'],
|
||||
$input['latitude'],
|
||||
$input['longitude'],
|
||||
$id
|
||||
]);
|
||||
break;
|
||||
|
||||
case 'jalan':
|
||||
$coordinates = json_encode($input['coordinates']);
|
||||
$stmt = $pdo->prepare("UPDATE jalan SET nama=?, status=?, panjang=?, coordinates=? WHERE id=?");
|
||||
$stmt->execute([
|
||||
$input['nama'],
|
||||
$input['status'],
|
||||
$input['panjang'],
|
||||
$coordinates,
|
||||
$id
|
||||
]);
|
||||
break;
|
||||
|
||||
case 'parsil':
|
||||
$coordinates = json_encode($input['coordinates']);
|
||||
$stmt = $pdo->prepare("UPDATE parsil SET nama=?, status=?, luas=?, coordinates=? WHERE id=?");
|
||||
$stmt->execute([
|
||||
$input['nama'],
|
||||
$input['status'],
|
||||
$input['luas'],
|
||||
$coordinates,
|
||||
$id
|
||||
]);
|
||||
break;
|
||||
|
||||
case 'rusak':
|
||||
$stmt = $pdo->prepare("UPDATE jalan_rusak SET nama=?, latitude=?, longitude=? WHERE id=?");
|
||||
$stmt->execute([
|
||||
$input['nama'],
|
||||
$input['latitude'],
|
||||
$input['longitude'],
|
||||
$id
|
||||
]);
|
||||
break;
|
||||
}
|
||||
|
||||
echo json_encode(['success' => true, 'message' => 'Data updated successfully']);
|
||||
} catch(PDOException $e) {
|
||||
echo json_encode(['success' => false, 'message' => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
function delete($pdo, $table, $id) {
|
||||
try {
|
||||
// Delete associated file for rusak
|
||||
if ($table === 'jalan_rusak') {
|
||||
$stmt = $pdo->prepare("SELECT foto FROM jalan_rusak WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
$data = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
if ($data && $data['foto'] && file_exists($data['foto'])) {
|
||||
unlink($data['foto']);
|
||||
}
|
||||
}
|
||||
|
||||
$stmt = $pdo->prepare("DELETE FROM $table WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
|
||||
if ($stmt->rowCount() > 0) {
|
||||
echo json_encode(['success' => true, 'message' => 'Data deleted successfully']);
|
||||
} else {
|
||||
echo json_encode(['success' => false, 'message' => 'Data not found']);
|
||||
}
|
||||
} catch(PDOException $e) {
|
||||
echo json_encode(['success' => false, 'message' => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
header('Access-Control-Allow-Origin: *');
|
||||
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
|
||||
header('Access-Control-Allow-Headers: Content-Type');
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||
http_response_code(200);
|
||||
exit();
|
||||
}
|
||||
|
||||
$host = 'sql208.infinityfree.com';
|
||||
$dbname = 'if0_42170546_sig_db';
|
||||
$username = 'if0_42170546';
|
||||
$password = 'SABTU01012005';
|
||||
|
||||
try {
|
||||
$pdo = new PDO("mysql:host=$host;dbname=$dbname;charset=utf8", $username, $password);
|
||||
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
} catch(PDOException $e) {
|
||||
echo json_encode(['success' => false, 'message' => 'Database connection failed']);
|
||||
exit();
|
||||
}
|
||||
|
||||
$type = isset($_GET['type']) ? $_GET['type'] : '';
|
||||
$action = isset($_GET['action']) ? $_GET['action'] : '';
|
||||
$id = isset($_GET['id']) ? intval($_GET['id']) : null;
|
||||
$method = $_SERVER['REQUEST_METHOD'];
|
||||
|
||||
$validTypes = ['masjid', 'gereja', 'klenteng', 'vihara', 'pura'];
|
||||
|
||||
if (!in_array($type, $validTypes)) {
|
||||
echo json_encode(['success' => false, 'message' => 'Invalid type']);
|
||||
exit();
|
||||
}
|
||||
|
||||
$table = 'tempat_ibadah';
|
||||
|
||||
switch ($action) {
|
||||
case 'getAll':
|
||||
$stmt = $pdo->prepare("SELECT * FROM $table WHERE jenis = ? ORDER BY nama ASC");
|
||||
$stmt->execute([$type]);
|
||||
$data = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
echo json_encode(['success' => true, 'data' => $data]);
|
||||
break;
|
||||
|
||||
case 'getOne':
|
||||
$stmt = $pdo->prepare("SELECT * FROM $table WHERE id = ? AND jenis = ?");
|
||||
$stmt->execute([$id, $type]);
|
||||
$data = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
if ($data) {
|
||||
echo json_encode(['success' => true, 'data' => $data]);
|
||||
} else {
|
||||
echo json_encode(['success' => false, 'message' => 'Data tidak ditemukan']);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'create':
|
||||
if ($method === 'POST') {
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
$stmt = $pdo->prepare("INSERT INTO $table (jenis, nama, alamat, no_hp, latitude, longitude, radius, keterangan) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
|
||||
$stmt->execute([
|
||||
$type,
|
||||
$input['nama'],
|
||||
$input['alamat'] ?? '',
|
||||
$input['no_hp'] ?? '',
|
||||
$input['latitude'],
|
||||
$input['longitude'],
|
||||
$input['radius'] ?? 500,
|
||||
$input['keterangan'] ?? ''
|
||||
]);
|
||||
echo json_encode(['success' => true, 'message' => 'Data berhasil disimpan', 'id' => $pdo->lastInsertId()]);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'update':
|
||||
if ($method === 'PUT') {
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
$stmt = $pdo->prepare("UPDATE $table SET nama=?, alamat=?, no_hp=?, latitude=?, longitude=?, radius=?, keterangan=? WHERE id=? AND jenis=?");
|
||||
$stmt->execute([
|
||||
$input['nama'],
|
||||
$input['alamat'] ?? '',
|
||||
$input['no_hp'] ?? '',
|
||||
$input['latitude'],
|
||||
$input['longitude'],
|
||||
$input['radius'] ?? 500,
|
||||
$input['keterangan'] ?? '',
|
||||
$id,
|
||||
$type
|
||||
]);
|
||||
echo json_encode(['success' => true, 'message' => 'Data berhasil diupdate']);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
if ($method === 'DELETE') {
|
||||
$stmt = $pdo->prepare("DELETE FROM $table WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
echo json_encode(['success' => true, 'message' => 'Data berhasil dihapus']);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
echo json_encode(['success' => false, 'message' => 'Invalid action']);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
header('Access-Control-Allow-Origin: *');
|
||||
header('Access-Control-Allow-Methods: POST, OPTIONS');
|
||||
header('Access-Control-Allow-Headers: Content-Type');
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||
http_response_code(200);
|
||||
exit();
|
||||
}
|
||||
|
||||
$host = 'sql208.infinityfree.com';
|
||||
$dbname = 'if0_42170546_sig_db';
|
||||
$username = 'if0_42170546';
|
||||
$password = 'SABTU01012005';
|
||||
|
||||
try {
|
||||
$pdo = new PDO("mysql:host=$host;dbname=$dbname;charset=utf8", $username, $password);
|
||||
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
} catch(PDOException $e) {
|
||||
echo json_encode(['success' => false, 'message' => 'Database connection failed']);
|
||||
exit();
|
||||
}
|
||||
|
||||
$action = isset($_GET['action']) ? $_GET['action'] : '';
|
||||
|
||||
if ($action === 'login' && $_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
if (!$input || !isset($input['username']) || !isset($input['password'])) {
|
||||
echo json_encode(['success' => false, 'message' => 'Username dan password harus diisi']);
|
||||
exit();
|
||||
}
|
||||
|
||||
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ? AND password = MD5(?)");
|
||||
$stmt->execute([$input['username'], $input['password']]);
|
||||
$user = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($user) {
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'message' => 'Login berhasil',
|
||||
'data' => [
|
||||
'id' => $user['id'],
|
||||
'username' => $user['username'],
|
||||
'role' => $user['role']
|
||||
]
|
||||
]);
|
||||
} else {
|
||||
echo json_encode(['success' => false, 'message' => 'Username atau password salah']);
|
||||
}
|
||||
} else {
|
||||
echo json_encode(['success' => false, 'message' => 'Invalid action']);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
header('Access-Control-Allow-Origin: *');
|
||||
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
|
||||
header('Access-Control-Allow-Headers: Content-Type');
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||
http_response_code(200);
|
||||
exit();
|
||||
}
|
||||
|
||||
$host = 'sql208.infinityfree.com';
|
||||
$dbname = 'if0_42170546_sig_db';
|
||||
$username = 'if0_42170546';
|
||||
$password = 'SABTU01012005';
|
||||
|
||||
try {
|
||||
$pdo = new PDO("mysql:host=$host;dbname=$dbname;charset=utf8", $username, $password);
|
||||
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
} catch(PDOException $e) {
|
||||
echo json_encode(['success' => false, 'message' => 'Database connection failed']);
|
||||
exit();
|
||||
}
|
||||
|
||||
$action = isset($_GET['action']) ? $_GET['action'] : '';
|
||||
$id = isset($_GET['id']) ? intval($_GET['id']) : null;
|
||||
$method = $_SERVER['REQUEST_METHOD'];
|
||||
|
||||
switch ($action) {
|
||||
case 'getAll':
|
||||
$stmt = $pdo->query("SELECT * FROM orang_berkebutuhan ORDER BY id DESC");
|
||||
$data = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
echo json_encode(['success' => true, 'data' => $data]);
|
||||
break;
|
||||
|
||||
case 'getOne':
|
||||
$stmt = $pdo->prepare("SELECT * FROM orang_berkebutuhan WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
$data = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
if ($data) {
|
||||
echo json_encode(['success' => true, 'data' => $data]);
|
||||
} else {
|
||||
echo json_encode(['success' => false, 'message' => 'Data tidak ditemukan']);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'create':
|
||||
if ($method === 'POST') {
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
$stmt = $pdo->prepare("INSERT INTO orang_berkebutuhan (nama, alamat, jenis_kebutuhan, no_hp, latitude, longitude, keterangan) VALUES (?, ?, ?, ?, ?, ?, ?)");
|
||||
$stmt->execute([
|
||||
$input['nama'],
|
||||
$input['alamat'] ?? '',
|
||||
$input['jenis_kebutuhan'] ?? '',
|
||||
$input['no_hp'] ?? '',
|
||||
$input['latitude'],
|
||||
$input['longitude'],
|
||||
$input['keterangan'] ?? ''
|
||||
]);
|
||||
echo json_encode(['success' => true, 'message' => 'Data berhasil disimpan', 'id' => $pdo->lastInsertId()]);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'update':
|
||||
if ($method === 'PUT') {
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
$stmt = $pdo->prepare("UPDATE orang_berkebutuhan SET nama=?, alamat=?, jenis_kebutuhan=?, no_hp=?, latitude=?, longitude=?, keterangan=? WHERE id=?");
|
||||
$stmt->execute([
|
||||
$input['nama'],
|
||||
$input['alamat'] ?? '',
|
||||
$input['jenis_kebutuhan'] ?? '',
|
||||
$input['no_hp'] ?? '',
|
||||
$input['latitude'],
|
||||
$input['longitude'],
|
||||
$input['keterangan'] ?? '',
|
||||
$id
|
||||
]);
|
||||
echo json_encode(['success' => true, 'message' => 'Data berhasil diupdate']);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
if ($method === 'DELETE') {
|
||||
$stmt = $pdo->prepare("DELETE FROM orang_berkebutuhan WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
echo json_encode(['success' => true, 'message' => 'Data berhasil dihapus']);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
echo json_encode(['success' => false, 'message' => 'Invalid action']);
|
||||
}
|
||||
?>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 6.1 KiB |
Reference in New Issue
Block a user