add file
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
include 'koneksi.php';
|
||||
|
||||
$result = $koneksi->query("SELECT * FROM spbu");
|
||||
|
||||
$data = [];
|
||||
while($row = $result->fetch_assoc()){
|
||||
$data[] = $row;
|
||||
}
|
||||
|
||||
echo json_encode($data);
|
||||
?>
|
||||
@@ -0,0 +1,69 @@
|
||||
-- phpMyAdmin SQL Dump
|
||||
-- version 5.2.1
|
||||
-- https://www.phpmyadmin.net/
|
||||
--
|
||||
-- Host: 127.0.0.1
|
||||
-- Waktu pembuatan: 07 Jun 2026 pada 10.53
|
||||
-- Versi server: 10.4.32-MariaDB
|
||||
-- Versi PHP: 8.2.12
|
||||
|
||||
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
|
||||
START TRANSACTION;
|
||||
SET time_zone = "+00:00";
|
||||
|
||||
|
||||
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
|
||||
/*!40101 SET NAMES utf8mb4 */;
|
||||
|
||||
--
|
||||
-- Database: `db_spbu`
|
||||
--
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- Struktur dari tabel `spbu`
|
||||
--
|
||||
|
||||
CREATE TABLE `spbu` (
|
||||
`id` int(11) NOT NULL,
|
||||
`nama_spbu` varchar(255) DEFAULT NULL,
|
||||
`lat` double DEFAULT NULL,
|
||||
`lng` double DEFAULT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
--
|
||||
-- Dumping data untuk tabel `spbu`
|
||||
--
|
||||
|
||||
INSERT INTO `spbu` (`id`, `nama_spbu`, `lat`, `lng`) VALUES
|
||||
(1, 'spbu kobar', -0.02197187355969219, 109.31207858442205),
|
||||
(3, 'spbu paris', 0.007368924759898348, 109.37010012983221),
|
||||
(4, 'spbu tanray', 0.016651739943910275, 109.29988329640928);
|
||||
|
||||
--
|
||||
-- Indexes for dumped tables
|
||||
--
|
||||
|
||||
--
|
||||
-- Indeks untuk tabel `spbu`
|
||||
--
|
||||
ALTER TABLE `spbu`
|
||||
ADD PRIMARY KEY (`id`);
|
||||
|
||||
--
|
||||
-- AUTO_INCREMENT untuk tabel yang dibuang
|
||||
--
|
||||
|
||||
--
|
||||
-- AUTO_INCREMENT untuk tabel `spbu`
|
||||
--
|
||||
ALTER TABLE `spbu`
|
||||
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=5;
|
||||
COMMIT;
|
||||
|
||||
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
include 'koneksi.php';
|
||||
|
||||
$id = $_POST['id'];
|
||||
|
||||
$stmt = $koneksi->prepare("DELETE FROM spbu WHERE id=?");
|
||||
$stmt->bind_param("i", $id);
|
||||
|
||||
echo $stmt->execute() ? "success" : "error";
|
||||
?>
|
||||
+203
@@ -0,0 +1,203 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Peta SPBU</title>
|
||||
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css"/>
|
||||
<script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>
|
||||
|
||||
<style>
|
||||
#map { height: 100vh; }
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="map"></div>
|
||||
|
||||
<script>
|
||||
const map = L.map('map').setView([-0.02, 109.34], 13);
|
||||
|
||||
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
maxZoom: 19
|
||||
}).addTo(map);
|
||||
|
||||
let layer = L.layerGroup().addTo(map);
|
||||
let data = [];
|
||||
|
||||
// =====================
|
||||
// NOTIFIKASI POPUP
|
||||
// =====================
|
||||
function showNotif(text) {
|
||||
let popup = L.popup()
|
||||
.setLatLng(map.getCenter())
|
||||
.setContent(`<div style="padding:6px;">${text}</div>`)
|
||||
.openOn(map);
|
||||
|
||||
setTimeout(() => {
|
||||
map.closePopup(popup);
|
||||
}, 1500);
|
||||
}
|
||||
|
||||
// =====================
|
||||
// LOAD DATA
|
||||
// =====================
|
||||
function load() {
|
||||
fetch('ambil_data.php')
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
data = res;
|
||||
render();
|
||||
});
|
||||
}
|
||||
|
||||
// =====================
|
||||
// RENDER MARKER
|
||||
// =====================
|
||||
function render() {
|
||||
layer.clearLayers();
|
||||
|
||||
data.forEach(d => {
|
||||
|
||||
let marker = L.marker([d.lat, d.lng], {
|
||||
draggable: true
|
||||
}).addTo(layer);
|
||||
|
||||
marker.bindPopup(`
|
||||
<b>${d.nama_spbu}</b><br><br>
|
||||
|
||||
<button onclick="edit(${d.id}, '${d.nama_spbu}', ${d.lat}, ${d.lng})">
|
||||
Edit
|
||||
</button>
|
||||
|
||||
<button onclick="hapus(${d.id})">
|
||||
Hapus
|
||||
</button>
|
||||
`);
|
||||
|
||||
// =====================
|
||||
// DRAG UPDATE
|
||||
// =====================
|
||||
marker.on('dragend', function(e){
|
||||
let pos = e.target.getLatLng();
|
||||
|
||||
fetch('update.php', {
|
||||
method: 'POST',
|
||||
body: new URLSearchParams({
|
||||
id: d.id,
|
||||
nama: d.nama_spbu,
|
||||
lat: pos.lat,
|
||||
lng: pos.lng
|
||||
})
|
||||
})
|
||||
.then(res => res.text())
|
||||
.then(res => {
|
||||
if(res.trim() === "success"){
|
||||
showNotif("📍 Lokasi berhasil diupdate");
|
||||
load();
|
||||
} else {
|
||||
showNotif("❌ Gagal update");
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
load();
|
||||
|
||||
// =====================
|
||||
// TAMBAH DATA (klik peta)
|
||||
// =====================
|
||||
map.on('click', function(e){
|
||||
|
||||
let lat = e.latlng.lat;
|
||||
let lng = e.latlng.lng;
|
||||
|
||||
let popup = L.popup()
|
||||
.setLatLng(e.latlng)
|
||||
.setContent(`
|
||||
<form id="form">
|
||||
<input name="nama" placeholder="Nama SPBU" required>
|
||||
<button type="submit">Simpan</button>
|
||||
</form>
|
||||
`)
|
||||
.openOn(map);
|
||||
|
||||
setTimeout(() => {
|
||||
document.getElementById("form").onsubmit = function(ev){
|
||||
ev.preventDefault();
|
||||
|
||||
let fd = new FormData(this);
|
||||
fd.append("lat", lat);
|
||||
fd.append("lng", lng);
|
||||
|
||||
fetch('simpan.php', {
|
||||
method: 'POST',
|
||||
body: fd
|
||||
})
|
||||
.then(res => res.text())
|
||||
.then(res => {
|
||||
if(res.trim() === "success"){
|
||||
showNotif("✅ SPBU berhasil ditambahkan");
|
||||
load();
|
||||
map.closePopup();
|
||||
} else {
|
||||
showNotif("❌ Gagal menyimpan");
|
||||
}
|
||||
});
|
||||
}
|
||||
}, 100);
|
||||
});
|
||||
|
||||
// =====================
|
||||
// EDIT DATA
|
||||
// =====================
|
||||
function edit(id, nama, lat, lng){
|
||||
let newNama = prompt("Nama SPBU:", nama);
|
||||
|
||||
if(newNama){
|
||||
fetch('update.php', {
|
||||
method: 'POST',
|
||||
body: new URLSearchParams({
|
||||
id,
|
||||
nama: newNama,
|
||||
lat,
|
||||
lng
|
||||
})
|
||||
})
|
||||
.then(res => res.text())
|
||||
.then(res => {
|
||||
if(res.trim() === "success"){
|
||||
showNotif("✏️ Data berhasil diupdate");
|
||||
load();
|
||||
} else {
|
||||
showNotif("❌ Gagal update");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// =====================
|
||||
// HAPUS DATA
|
||||
// =====================
|
||||
function hapus(id){
|
||||
if(confirm("Yakin mau hapus data ini?")){
|
||||
fetch('hapus.php', {
|
||||
method: 'POST',
|
||||
body: new URLSearchParams({id})
|
||||
})
|
||||
.then(res => res.text())
|
||||
.then(res => {
|
||||
if(res.trim() === "success"){
|
||||
showNotif("🗑️ Data berhasil dihapus");
|
||||
load();
|
||||
} else {
|
||||
showNotif("❌ Gagal hapus");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
$host = getenv('DB_HOST') ?: 'localhost';
|
||||
$user = getenv('DB_USER') ?: 'root';
|
||||
$pass = getenv('DB_PASSWORD') ?: '';
|
||||
$db = getenv('DB_NAME') ?: 'db_spbu';
|
||||
|
||||
$conn = mysqli_connect($host, $user, $pass, $db);
|
||||
|
||||
if (!$conn) {
|
||||
die("Koneksi database gagal: " . mysqli_connect_error());
|
||||
}
|
||||
|
||||
$koneksi = $conn;
|
||||
?>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
include 'koneksi.php';
|
||||
|
||||
$nama = $_POST['nama'];
|
||||
$lat = $_POST['lat'];
|
||||
$lng = $_POST['lng'];
|
||||
|
||||
$stmt = $koneksi->prepare("INSERT INTO spbu (nama_spbu, lat, lng) VALUES (?, ?, ?)");
|
||||
$stmt->bind_param("sdd", $nama, $lat, $lng);
|
||||
|
||||
echo $stmt->execute() ? "success" : "error";
|
||||
?>
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
include 'koneksi.php';
|
||||
|
||||
$id = $_POST['id'];
|
||||
$nama = $_POST['nama'];
|
||||
$lat = $_POST['lat'];
|
||||
$lng = $_POST['lng'];
|
||||
|
||||
$stmt = $koneksi->prepare("UPDATE spbu SET nama_spbu=?, lat=?, lng=? WHERE id=?");
|
||||
$stmt->bind_param("sddi", $nama, $lat, $lng, $id);
|
||||
|
||||
echo $stmt->execute() ? "success" : "error";
|
||||
?>
|
||||
@@ -0,0 +1,29 @@
|
||||
CREATE DATABASE IF NOT EXISTS 02_kerusakan;
|
||||
USE 02_kerusakan;
|
||||
|
||||
CREATE TABLE jalan(
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
nama_jalan VARCHAR(150),
|
||||
status VARCHAR(50),
|
||||
panjang DOUBLE,
|
||||
geom LINESTRING NOT NULL,
|
||||
SPATIAL INDEX idx_jalan(geom)
|
||||
);
|
||||
|
||||
CREATE TABLE parsil(
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
pemilik VARCHAR(100),
|
||||
status VARCHAR(50),
|
||||
luas DOUBLE,
|
||||
geom POLYGON NOT NULL,
|
||||
SPATIAL INDEX idx_parsil(geom)
|
||||
);
|
||||
|
||||
CREATE TABLE jalan_rusak(
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
nama_titik VARCHAR(100),
|
||||
keterangan TEXT,
|
||||
geom POINT NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
SPATIAL INDEX idx_rusak(geom)
|
||||
);
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
$host = getenv('DB_HOST') ?: 'localhost';
|
||||
$user = getenv('DB_USER') ?: 'root';
|
||||
$pass = getenv('DB_PASSWORD') ?: '';
|
||||
$db = getenv('DB_NAME') ?: '02_kerusakan';
|
||||
|
||||
$conn = mysqli_connect($host, $user, $pass, $db);
|
||||
|
||||
if (!$conn) {
|
||||
die("Koneksi database gagal: " . mysqli_connect_error());
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
include "db.php";
|
||||
|
||||
|
||||
$data=json_decode(
|
||||
file_get_contents("php://input"),
|
||||
true
|
||||
);
|
||||
|
||||
|
||||
$id=$data['id'];
|
||||
|
||||
$type=$data['type'];
|
||||
|
||||
|
||||
|
||||
if($type=="jalan"){
|
||||
|
||||
$sql="
|
||||
DELETE FROM jalan
|
||||
WHERE id=$id
|
||||
";
|
||||
|
||||
}
|
||||
|
||||
|
||||
elseif($type=="parsil"){
|
||||
|
||||
$sql="
|
||||
DELETE FROM parsil
|
||||
WHERE id=$id
|
||||
";
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
elseif($type=="rusak"){
|
||||
|
||||
$sql="
|
||||
DELETE FROM jalan_rusak
|
||||
WHERE id=$id
|
||||
";
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
if(mysqli_query($conn,$sql)){
|
||||
|
||||
echo "Data berhasil dihapus";
|
||||
|
||||
}else{
|
||||
|
||||
echo mysqli_error($conn);
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
+830
@@ -0,0 +1,830 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="id">
|
||||
<head>
|
||||
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<title>WebGIS Jalan Parsil Kerusakan</title>
|
||||
|
||||
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css">
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet-draw/dist/leaflet.draw.css">
|
||||
|
||||
|
||||
<script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>
|
||||
<script src="https://unpkg.com/leaflet-draw/dist/leaflet.draw.js"></script>
|
||||
|
||||
|
||||
|
||||
<style>
|
||||
|
||||
|
||||
*{
|
||||
box-sizing:border-box;
|
||||
font-family:Segoe UI,Arial;
|
||||
}
|
||||
|
||||
|
||||
body{
|
||||
|
||||
margin:0;
|
||||
background:#020617;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
#container{
|
||||
|
||||
display:flex;
|
||||
height:100vh;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
#sidebar{
|
||||
|
||||
width:360px;
|
||||
background:#0f172a;
|
||||
color:white;
|
||||
padding:20px;
|
||||
overflow:auto;
|
||||
|
||||
}
|
||||
|
||||
|
||||
.logo{
|
||||
|
||||
font-size:22px;
|
||||
font-weight:bold;
|
||||
margin-bottom:20px;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
.menu button{
|
||||
|
||||
width:100%;
|
||||
padding:12px;
|
||||
margin-bottom:8px;
|
||||
border:none;
|
||||
border-radius:10px;
|
||||
background:#1e293b;
|
||||
color:white;
|
||||
cursor:pointer;
|
||||
|
||||
}
|
||||
|
||||
|
||||
.menu button:hover{
|
||||
|
||||
background:#2563eb;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
.card{
|
||||
|
||||
background:#1e293b;
|
||||
padding:15px;
|
||||
border-radius:15px;
|
||||
margin-top:12px;
|
||||
|
||||
box-shadow:0 5px 15px #0005;
|
||||
|
||||
}
|
||||
|
||||
|
||||
.card b{
|
||||
|
||||
color:#38bdf8;
|
||||
|
||||
}
|
||||
|
||||
|
||||
#map{
|
||||
|
||||
flex:1;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
#info{
|
||||
|
||||
background:#020617;
|
||||
padding:10px;
|
||||
color:#94a3b8;
|
||||
border-radius:10px;
|
||||
margin-top:10px;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
|
||||
<body>
|
||||
|
||||
|
||||
<div id="container">
|
||||
|
||||
|
||||
<div id="sidebar">
|
||||
|
||||
|
||||
<div class="logo">
|
||||
🗺 WebGIS
|
||||
</div>
|
||||
|
||||
|
||||
<div class="menu">
|
||||
|
||||
<button onclick="filterData('all')">
|
||||
Semua Data
|
||||
</button>
|
||||
|
||||
<button onclick="filterData('jalan')">
|
||||
🛣 Jalan
|
||||
</button>
|
||||
|
||||
<button onclick="filterData('parsil')">
|
||||
🏠 Parsil
|
||||
</button>
|
||||
|
||||
<button onclick="filterData('rusak')">
|
||||
⚠ Kerusakan
|
||||
</button>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div id="info">
|
||||
Memuat data...
|
||||
</div>
|
||||
|
||||
|
||||
<div id="list"></div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div id="map"></div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
|
||||
|
||||
const map=L.map('map')
|
||||
.setView([-0.0263,109.3425],14);
|
||||
|
||||
|
||||
L.tileLayer(
|
||||
'https://tile.openstreetmap.org/{z}/{x}/{y}.png'
|
||||
).addTo(map);
|
||||
|
||||
|
||||
|
||||
let group=L.featureGroup().addTo(map);
|
||||
|
||||
let dataGlobal=[];
|
||||
|
||||
let tempLayer=null;
|
||||
let tempType=null;
|
||||
|
||||
|
||||
|
||||
// LOAD DATA
|
||||
|
||||
fetch("load.php")
|
||||
.then(r=>r.json())
|
||||
.then(data=>{
|
||||
|
||||
|
||||
dataGlobal=data;
|
||||
|
||||
|
||||
document.getElementById("info").innerHTML=
|
||||
"Total data : "+data.length;
|
||||
|
||||
|
||||
tampil(data);
|
||||
|
||||
|
||||
})
|
||||
.catch(e=>{
|
||||
|
||||
document.getElementById("info").innerHTML=
|
||||
"Load error "+e;
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
// TAMPIL PETA
|
||||
|
||||
function tampil(data){
|
||||
|
||||
|
||||
group.clearLayers();
|
||||
|
||||
|
||||
let html="";
|
||||
|
||||
|
||||
data.forEach((d,i)=>{
|
||||
|
||||
|
||||
if(!d.geom)return;
|
||||
|
||||
|
||||
let geo=JSON.parse(d.geom);
|
||||
|
||||
|
||||
let layer=L.geoJSON(geo);
|
||||
|
||||
|
||||
|
||||
let popup="";
|
||||
|
||||
|
||||
|
||||
// JALAN
|
||||
|
||||
if(d.type=="jalan"){
|
||||
|
||||
|
||||
layer.setStyle({
|
||||
|
||||
color:"#22c55e",
|
||||
weight:6
|
||||
|
||||
});
|
||||
|
||||
|
||||
popup=`
|
||||
|
||||
<h3>🛣 Jalan</h3>
|
||||
|
||||
<b>Nama Jalan:</b> ${d.nama_jalan}<br>
|
||||
|
||||
<b>Status:</b> ${d.status || '-'}<br>
|
||||
|
||||
<b>Panjang:</b> ${d.panjang || '-'} meter
|
||||
|
||||
<br><br>
|
||||
|
||||
<button onclick="editData(${d.id},'jalan')">
|
||||
Edit
|
||||
</button>
|
||||
|
||||
<button onclick="hapusData(${d.id},'jalan')">
|
||||
Hapus
|
||||
</button>
|
||||
|
||||
`;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// PARSIL
|
||||
|
||||
if(d.type=="parsil"){
|
||||
|
||||
|
||||
layer.setStyle({
|
||||
|
||||
color:"#6366f1",
|
||||
|
||||
fillOpacity:.4
|
||||
|
||||
});
|
||||
|
||||
|
||||
popup=`
|
||||
|
||||
<h3>🏠 Parsil</h3>
|
||||
|
||||
<b>Area:</b> ${d.nama_area}<br>
|
||||
|
||||
<b>Pemilik:</b> ${d.pemilik || '-'}<br>
|
||||
|
||||
<b>Status:</b> ${d.status || '-'}<br>
|
||||
|
||||
<b>Luas:</b> ${d.luas || 0} m²<br>
|
||||
|
||||
<b>Keterangan:</b> ${d.keterangan || '-'}
|
||||
|
||||
`;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// RUSAK
|
||||
|
||||
if(d.type=="rusak"){
|
||||
|
||||
|
||||
popup=`
|
||||
|
||||
<h3>⚠ Kerusakan</h3>
|
||||
|
||||
<b>Lokasi:</b> ${d.nama_titik}<br>
|
||||
|
||||
<b>Keterangan:</b> ${d.keterangan}
|
||||
|
||||
`;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
layer.bindPopup(popup);
|
||||
|
||||
|
||||
|
||||
layer.addTo(group);
|
||||
|
||||
|
||||
|
||||
html+=`
|
||||
|
||||
<div class="card"
|
||||
onclick="zoomData(${i})">
|
||||
|
||||
<b>${d.type}</b>
|
||||
|
||||
<br>
|
||||
|
||||
${d.nama_jalan ||
|
||||
d.nama_area ||
|
||||
d.nama_titik}
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
`;
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
document.getElementById("list")
|
||||
.innerHTML=html;
|
||||
|
||||
|
||||
|
||||
if(group.getLayers().length)
|
||||
map.fitBounds(group.getBounds());
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function zoomData(i){
|
||||
|
||||
let geo=JSON.parse(dataGlobal[i].geom);
|
||||
|
||||
map.fitBounds(
|
||||
L.geoJSON(geo).getBounds()
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function filterData(type){
|
||||
|
||||
|
||||
if(type=="all"){
|
||||
|
||||
tampil(dataGlobal);
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
|
||||
tampil(
|
||||
dataGlobal.filter(x=>x.type==type)
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// ================= DRAW EDIT =================
|
||||
|
||||
|
||||
const draw=new L.Control.Draw({
|
||||
|
||||
edit:{
|
||||
|
||||
featureGroup:group,
|
||||
|
||||
edit:true,
|
||||
|
||||
remove:false
|
||||
|
||||
},
|
||||
|
||||
|
||||
draw:{
|
||||
|
||||
marker:true,
|
||||
|
||||
polyline:true,
|
||||
|
||||
polygon:true,
|
||||
|
||||
circle:false,
|
||||
|
||||
rectangle:false
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
map.addControl(draw);
|
||||
|
||||
|
||||
|
||||
// saat geometry digeser / diedit
|
||||
|
||||
map.on(
|
||||
'draw:edited',
|
||||
function(e){
|
||||
|
||||
|
||||
e.layers.eachLayer(function(layer){
|
||||
|
||||
|
||||
let geo=layer.toGeoJSON();
|
||||
|
||||
|
||||
|
||||
fetch("update.php",{
|
||||
|
||||
method:"POST",
|
||||
|
||||
headers:{
|
||||
"Content-Type":"application/json"
|
||||
},
|
||||
|
||||
body:JSON.stringify({
|
||||
|
||||
id:layer._id,
|
||||
|
||||
type:layer._type,
|
||||
|
||||
geom:geo
|
||||
|
||||
})
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
alert("Lokasi berhasil diperbarui");
|
||||
|
||||
|
||||
});
|
||||
|
||||
// FORM INPUT
|
||||
|
||||
function openForm(){
|
||||
|
||||
|
||||
let html="";
|
||||
|
||||
|
||||
|
||||
if(tempType=="rusak"){
|
||||
|
||||
|
||||
html=`
|
||||
|
||||
<h3>Kerusakan Jalan</h3>
|
||||
|
||||
<input id="nama"
|
||||
placeholder="Nama lokasi">
|
||||
|
||||
<br>
|
||||
|
||||
<textarea id="ket"
|
||||
placeholder="Keterangan"></textarea>
|
||||
|
||||
`;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
if(tempType=="jalan"){
|
||||
|
||||
|
||||
html=`
|
||||
|
||||
<h3>Data Jalan</h3>
|
||||
|
||||
|
||||
<input id="nama"
|
||||
placeholder="Nama jalan">
|
||||
|
||||
|
||||
<select id="status">
|
||||
|
||||
<option>Nasional</option>
|
||||
|
||||
<option>Provinsi</option>
|
||||
|
||||
<option>Kabupaten</option>
|
||||
|
||||
</select>
|
||||
|
||||
`;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
if(tempType=="parsil"){
|
||||
|
||||
|
||||
html=`
|
||||
|
||||
<h3>Data Parsil</h3>
|
||||
|
||||
|
||||
<input id="nama"
|
||||
placeholder="Nama area">
|
||||
|
||||
|
||||
<input id="pemilik"
|
||||
placeholder="Nama pemilik">
|
||||
|
||||
|
||||
<select id="status">
|
||||
|
||||
<option>SHM</option>
|
||||
<option>HGB</option>
|
||||
<option>HGU</option>
|
||||
|
||||
</select>
|
||||
|
||||
|
||||
<input id="luas"
|
||||
placeholder="Luas tanah">
|
||||
|
||||
|
||||
<textarea id="ket"
|
||||
placeholder="Keterangan">
|
||||
|
||||
</textarea>
|
||||
|
||||
|
||||
`;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
let modal=document.createElement("div");
|
||||
|
||||
|
||||
modal.id="inputBox";
|
||||
|
||||
|
||||
modal.style.position="fixed";
|
||||
|
||||
modal.style.top="50%";
|
||||
|
||||
modal.style.left="50%";
|
||||
|
||||
modal.style.transform="translate(-50%,-50%)";
|
||||
|
||||
modal.style.background="white";
|
||||
|
||||
modal.style.padding="20px";
|
||||
|
||||
modal.style.zIndex="9999";
|
||||
|
||||
modal.innerHTML=
|
||||
html+
|
||||
`
|
||||
|
||||
<br>
|
||||
|
||||
<button onclick="simpanData()">
|
||||
Simpan
|
||||
</button>
|
||||
|
||||
<button onclick="batal()">
|
||||
Batal
|
||||
</button>
|
||||
|
||||
`;
|
||||
|
||||
|
||||
|
||||
document.body.appendChild(modal);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function batal(){
|
||||
|
||||
document.getElementById("inputBox").remove();
|
||||
|
||||
tempLayer.remove();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function simpanData(){
|
||||
|
||||
|
||||
let geo=tempLayer.toGeoJSON();
|
||||
|
||||
|
||||
let data={
|
||||
|
||||
type:tempType,
|
||||
|
||||
geom:geo
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
if(tempType=="rusak"){
|
||||
|
||||
data.nama_titik=
|
||||
document.getElementById("nama").value;
|
||||
|
||||
|
||||
data.keterangan=
|
||||
document.getElementById("ket").value;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
if(tempType=="jalan"){
|
||||
|
||||
data.nama=
|
||||
document.getElementById("nama").value;
|
||||
|
||||
|
||||
data.status=
|
||||
document.getElementById("status").value;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
if(tempType=="parsil"){
|
||||
|
||||
|
||||
data.nama_area=
|
||||
document.getElementById("nama").value;
|
||||
|
||||
|
||||
data.pemilik=
|
||||
document.getElementById("pemilik").value;
|
||||
|
||||
|
||||
data.status=
|
||||
document.getElementById("status").value;
|
||||
|
||||
|
||||
data.luas=
|
||||
document.getElementById("luas").value;
|
||||
|
||||
|
||||
data.keterangan=
|
||||
document.getElementById("ket").value;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
fetch("save.php",{
|
||||
|
||||
method:"POST",
|
||||
|
||||
headers:{
|
||||
|
||||
"Content-Type":"application/json"
|
||||
|
||||
},
|
||||
|
||||
body:JSON.stringify(data)
|
||||
|
||||
|
||||
})
|
||||
|
||||
|
||||
.then(r=>r.text())
|
||||
|
||||
.then(r=>{
|
||||
|
||||
|
||||
alert(r);
|
||||
|
||||
|
||||
location.reload();
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
function hapusData(id,type){
|
||||
|
||||
|
||||
if(!confirm("Yakin hapus data ini?"))
|
||||
return;
|
||||
|
||||
|
||||
|
||||
fetch("delete.php",{
|
||||
|
||||
method:"POST",
|
||||
|
||||
headers:{
|
||||
"Content-Type":"application/json"
|
||||
},
|
||||
|
||||
body:JSON.stringify({
|
||||
|
||||
id:id,
|
||||
type:type
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
|
||||
.then(r=>r.text())
|
||||
|
||||
.then(r=>{
|
||||
|
||||
alert(r);
|
||||
|
||||
location.reload();
|
||||
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
<?php
|
||||
|
||||
include "db.php";
|
||||
|
||||
header("Content-Type: application/json; charset=UTF-8");
|
||||
|
||||
$data = [];
|
||||
|
||||
|
||||
// cek koneksi
|
||||
if(!$conn){
|
||||
echo json_encode([
|
||||
"error"=>"Database gagal konek"
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ================= JALAN =================
|
||||
|
||||
$q = mysqli_query($conn,"
|
||||
SELECT
|
||||
id,
|
||||
nama_jalan,
|
||||
status,
|
||||
ST_AsGeoJSON(geom) AS geom
|
||||
FROM jalan
|
||||
");
|
||||
|
||||
|
||||
if($q){
|
||||
|
||||
while($row=mysqli_fetch_assoc($q)){
|
||||
|
||||
$data[]=[
|
||||
|
||||
"id"=>(int)$row['id'],
|
||||
|
||||
"type"=>"jalan",
|
||||
|
||||
"nama_jalan"=>$row['nama_jalan'],
|
||||
|
||||
"status"=>"Kabupaten",
|
||||
|
||||
"panjang"=>0,
|
||||
|
||||
"geom"=>$row['geom']
|
||||
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// ================= PARSIL =================
|
||||
|
||||
$q = mysqli_query($conn,"
|
||||
SELECT
|
||||
id,
|
||||
nama_area,
|
||||
ST_AsGeoJSON(geom) AS geom
|
||||
FROM parsil
|
||||
");
|
||||
|
||||
|
||||
if($q){
|
||||
|
||||
while($row=mysqli_fetch_assoc($q)){
|
||||
|
||||
|
||||
$data[]=[
|
||||
|
||||
"id"=>(int)$row['id'],
|
||||
|
||||
"type"=>"parsil",
|
||||
|
||||
"pemilik"=>$row['nama_area'],
|
||||
|
||||
"status"=>"SHM",
|
||||
|
||||
"luas"=>0,
|
||||
|
||||
"geom"=>$row['geom']
|
||||
|
||||
];
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// ================= KERUSAKAN =================
|
||||
|
||||
$q = mysqli_query($conn,"
|
||||
SELECT
|
||||
id,
|
||||
nama_titik,
|
||||
keterangan,
|
||||
ST_AsGeoJSON(geom) AS geom
|
||||
FROM jalan_rusak
|
||||
");
|
||||
|
||||
|
||||
if($q){
|
||||
|
||||
while($row=mysqli_fetch_assoc($q)){
|
||||
|
||||
|
||||
$data[]=[
|
||||
|
||||
"id"=>(int)$row['id'],
|
||||
|
||||
"type"=>"rusak",
|
||||
|
||||
"nama_titik"=>$row['nama_titik'],
|
||||
|
||||
"keterangan"=>$row['keterangan'],
|
||||
|
||||
"geom"=>$row['geom']
|
||||
|
||||
];
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
echo json_encode($data);
|
||||
|
||||
?>
|
||||
+194
@@ -0,0 +1,194 @@
|
||||
<?php
|
||||
|
||||
include "db.php";
|
||||
|
||||
header("Content-Type: text/plain");
|
||||
|
||||
|
||||
$data=json_decode(file_get_contents("php://input"),true);
|
||||
|
||||
|
||||
|
||||
$type=$data['type'];
|
||||
|
||||
$geom=json_encode($data['geom']);
|
||||
|
||||
$coords=$data['geom']['geometry']['coordinates'];
|
||||
|
||||
|
||||
|
||||
|
||||
// ================= JALAN =================
|
||||
|
||||
if($type=="jalan"){
|
||||
|
||||
|
||||
$nama=mysqli_real_escape_string(
|
||||
$conn,
|
||||
$data['nama']
|
||||
);
|
||||
|
||||
|
||||
$status=mysqli_real_escape_string(
|
||||
$conn,
|
||||
$data['status']
|
||||
);
|
||||
|
||||
|
||||
|
||||
$line="LINESTRING(";
|
||||
|
||||
|
||||
foreach($coords as $c){
|
||||
|
||||
$line .= $c[0]." ".$c[1].",";
|
||||
|
||||
}
|
||||
|
||||
|
||||
$line=rtrim($line,",").")";
|
||||
|
||||
|
||||
|
||||
$sql="
|
||||
INSERT INTO jalan
|
||||
(
|
||||
nama_jalan,
|
||||
geom
|
||||
)
|
||||
VALUES
|
||||
(
|
||||
'$nama',
|
||||
ST_GeomFromText('$line')
|
||||
)
|
||||
";
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ================= PARSIL =================
|
||||
elseif($type=="parsil"){
|
||||
|
||||
|
||||
$nama=mysqli_real_escape_string(
|
||||
$conn,
|
||||
$data['nama_area']
|
||||
);
|
||||
|
||||
|
||||
$pemilik=mysqli_real_escape_string(
|
||||
$conn,
|
||||
$data['pemilik']
|
||||
);
|
||||
|
||||
|
||||
$status=mysqli_real_escape_string(
|
||||
$conn,
|
||||
$data['status']
|
||||
);
|
||||
|
||||
|
||||
|
||||
$poly="POLYGON((";
|
||||
|
||||
|
||||
foreach($coords[0] as $c){
|
||||
|
||||
$poly .= $c[0]." ".$c[1].",";
|
||||
|
||||
}
|
||||
|
||||
|
||||
$poly=rtrim($poly,",")."))";
|
||||
|
||||
|
||||
|
||||
$sql="
|
||||
INSERT INTO parsil
|
||||
(
|
||||
nama_area,
|
||||
pemilik,
|
||||
status,
|
||||
geom
|
||||
)
|
||||
VALUES
|
||||
(
|
||||
'$nama',
|
||||
'$pemilik',
|
||||
'$status',
|
||||
ST_GeomFromText('$poly')
|
||||
)
|
||||
";
|
||||
|
||||
}
|
||||
// ================= RUSAK =================
|
||||
|
||||
|
||||
elseif($type=="rusak"){
|
||||
|
||||
|
||||
|
||||
$nama=mysqli_real_escape_string(
|
||||
$conn,
|
||||
$data['nama_titik']
|
||||
);
|
||||
|
||||
|
||||
$ket=mysqli_real_escape_string(
|
||||
$conn,
|
||||
$data['keterangan']
|
||||
);
|
||||
|
||||
|
||||
|
||||
$p=$coords;
|
||||
|
||||
|
||||
|
||||
$point =
|
||||
"POINT(".$p[0]." ".$p[1].")";
|
||||
|
||||
|
||||
|
||||
$sql="
|
||||
INSERT INTO jalan_rusak
|
||||
(
|
||||
nama_titik,
|
||||
keterangan,
|
||||
geom
|
||||
)
|
||||
VALUES
|
||||
(
|
||||
'$nama',
|
||||
'$ket',
|
||||
ST_GeomFromText('$point')
|
||||
)
|
||||
";
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if(mysqli_query($conn,$sql)){
|
||||
|
||||
|
||||
echo "Data berhasil disimpan";
|
||||
|
||||
|
||||
}else{
|
||||
|
||||
|
||||
echo "ERROR : ".mysqli_error($conn);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
include "db.php";
|
||||
|
||||
$data=json_decode(file_get_contents("php://input"),true);
|
||||
|
||||
|
||||
$id=$data['id'];
|
||||
|
||||
$geom=$data['geom']['geometry'];
|
||||
|
||||
|
||||
$type=$data['type'];
|
||||
|
||||
|
||||
|
||||
if($type=="rusak"){
|
||||
|
||||
$x=$geom['coordinates'][0];
|
||||
$y=$geom['coordinates'][1];
|
||||
|
||||
|
||||
$sql="
|
||||
UPDATE jalan_rusak
|
||||
SET geom =
|
||||
ST_GeomFromText('POINT($x $y)')
|
||||
WHERE id=$id
|
||||
";
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
if($type=="jalan"){
|
||||
|
||||
|
||||
$list=[];
|
||||
|
||||
foreach($geom['coordinates'] as $c){
|
||||
|
||||
$list[]=$c[0]." ".$c[1];
|
||||
|
||||
}
|
||||
|
||||
|
||||
$line="LINESTRING(".implode(",",$list).")";
|
||||
|
||||
|
||||
$sql="
|
||||
UPDATE jalan
|
||||
SET geom =
|
||||
ST_GeomFromText('$line')
|
||||
WHERE id=$id
|
||||
";
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
if($type=="parsil"){
|
||||
|
||||
|
||||
$list=[];
|
||||
|
||||
foreach($geom['coordinates'][0] as $c){
|
||||
|
||||
$list[]=$c[0]." ".$c[1];
|
||||
|
||||
}
|
||||
|
||||
|
||||
$poly="POLYGON((".implode(",",$list)."))";
|
||||
|
||||
|
||||
$sql="
|
||||
UPDATE parsil
|
||||
SET geom =
|
||||
ST_GeomFromText('$poly')
|
||||
WHERE id=$id
|
||||
";
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
if(mysqli_query($conn,$sql)){
|
||||
|
||||
echo "OK";
|
||||
|
||||
}else{
|
||||
|
||||
echo mysqli_error($conn);
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,275 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="id">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<title>WebGIS Choropleth Kota Pontianak</title>
|
||||
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"/>
|
||||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
||||
|
||||
<style>
|
||||
html,body{
|
||||
margin:0;
|
||||
padding:0;
|
||||
height:100%;
|
||||
font-family:Arial, Helvetica, sans-serif;
|
||||
}
|
||||
#map{
|
||||
width:100%;
|
||||
height:100%;
|
||||
}
|
||||
.info{
|
||||
padding:10px 14px;
|
||||
background:white;
|
||||
box-shadow:0 0 15px rgba(0,0,0,0.2);
|
||||
border-radius:10px;
|
||||
line-height:1.5;
|
||||
}
|
||||
.info h4{
|
||||
margin:0 0 8px;
|
||||
color:#444;
|
||||
}
|
||||
.legend{
|
||||
background:white;
|
||||
padding:10px;
|
||||
line-height:18px;
|
||||
color:#555;
|
||||
border-radius:10px;
|
||||
box-shadow:0 0 15px rgba(0,0,0,0.2);
|
||||
}
|
||||
.legend i{
|
||||
width:18px;
|
||||
height:18px;
|
||||
float:left;
|
||||
margin-right:8px;
|
||||
opacity:0.8;
|
||||
}
|
||||
.titlebox{
|
||||
position:absolute;
|
||||
top:10px;
|
||||
left:50%;
|
||||
transform:translateX(-50%);
|
||||
z-index:999;
|
||||
background:#0d6efd;
|
||||
color:white;
|
||||
padding:10px 18px;
|
||||
border-radius:10px;
|
||||
font-weight:bold;
|
||||
}
|
||||
.selector{
|
||||
position:absolute;
|
||||
top:60px;
|
||||
left:10px;
|
||||
z-index:999;
|
||||
background:white;
|
||||
padding:10px;
|
||||
border-radius:10px;
|
||||
box-shadow:0 0 10px rgba(0,0,0,0.2);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="titlebox">WEBGIS CHOROPLETH KOTA PONTIANAK</div>
|
||||
|
||||
<div class="selector">
|
||||
<b>Pilih Tema:</b><br>
|
||||
<select id="mode" onchange="updateMap()">
|
||||
<option value="density">Kepadatan Penduduk</option>
|
||||
<option value="population">Jumlah Penduduk</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div id="map"></div>
|
||||
|
||||
<script src="pontianak-state.js"></script>
|
||||
|
||||
<script>
|
||||
const map = L.map('map').setView([-0.03,109.34],12);
|
||||
|
||||
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png',{
|
||||
maxZoom:19,
|
||||
attribution:'© OpenStreetMap'
|
||||
}).addTo(map);
|
||||
|
||||
let geojson;
|
||||
|
||||
/* ======================
|
||||
INFO PANEL
|
||||
====================== */
|
||||
const info = L.control();
|
||||
|
||||
info.onAdd = function(){
|
||||
this._div = L.DomUtil.create('div','info');
|
||||
this.update();
|
||||
return this._div;
|
||||
};
|
||||
|
||||
info.update = function(props){
|
||||
let mode = document.getElementById("mode").value;
|
||||
|
||||
if(props){
|
||||
this._div.innerHTML = `
|
||||
<h4>Data Kecamatan</h4>
|
||||
<b>${props.name}</b><br>
|
||||
Kepadatan : ${props.density.toLocaleString()} jiwa/km²<br>
|
||||
Penduduk : ${props.population.toLocaleString()} jiwa`;
|
||||
}else{
|
||||
this._div.innerHTML = `<h4>${mode=="density"?"Kepadatan":"Penduduk"}</h4>Arahkan cursor ke wilayah`;
|
||||
}
|
||||
};
|
||||
|
||||
info.addTo(map);
|
||||
|
||||
/* ======================
|
||||
WARNA
|
||||
====================== */
|
||||
function getColorDensity(d){
|
||||
return d > 10000 ? '#800026' :
|
||||
d > 8000 ? '#BD0026' :
|
||||
d > 6000 ? '#E31A1C' :
|
||||
d > 5000 ? '#FC4E2A' :
|
||||
d > 4000 ? '#FD8D3C' :
|
||||
d > 3000 ? '#FEB24C' :
|
||||
'#FED976';
|
||||
}
|
||||
|
||||
function getColorPopulation(d){
|
||||
return d > 120000 ? '#00441b' :
|
||||
d > 100000 ? '#006d2c' :
|
||||
d > 80000 ? '#238b45' :
|
||||
d > 60000 ? '#41ab5d' :
|
||||
d > 50000 ? '#74c476' :
|
||||
d > 40000 ? '#a1d99b' :
|
||||
'#c7e9c0';
|
||||
}
|
||||
|
||||
function style(feature){
|
||||
let mode = document.getElementById("mode").value;
|
||||
|
||||
return {
|
||||
weight:2,
|
||||
opacity:1,
|
||||
color:'white',
|
||||
dashArray:'3',
|
||||
fillOpacity:0.75,
|
||||
fillColor:
|
||||
mode=="density"
|
||||
? getColorDensity(feature.properties.density)
|
||||
: getColorPopulation(feature.properties.population)
|
||||
};
|
||||
}
|
||||
|
||||
/* ======================
|
||||
INTERAKSI
|
||||
====================== */
|
||||
function highlightFeature(e){
|
||||
const layer = e.target;
|
||||
|
||||
layer.setStyle({
|
||||
weight:4,
|
||||
color:'#222',
|
||||
dashArray:'',
|
||||
fillOpacity:0.9
|
||||
});
|
||||
|
||||
layer.bringToFront();
|
||||
info.update(layer.feature.properties);
|
||||
}
|
||||
|
||||
function resetHighlight(e){
|
||||
geojson.resetStyle(e.target);
|
||||
info.update();
|
||||
}
|
||||
|
||||
function zoomToFeature(e){
|
||||
map.fitBounds(e.target.getBounds());
|
||||
}
|
||||
|
||||
function onEachFeature(feature, layer){
|
||||
layer.on({
|
||||
mouseover: highlightFeature,
|
||||
mouseout: resetHighlight,
|
||||
click: zoomToFeature
|
||||
});
|
||||
|
||||
layer.bindPopup(`
|
||||
<b>${feature.properties.name}</b><br>
|
||||
Kepadatan : ${feature.properties.density.toLocaleString()} jiwa/km²<br>
|
||||
Penduduk : ${feature.properties.population.toLocaleString()} jiwa
|
||||
`);
|
||||
}
|
||||
|
||||
/* ======================
|
||||
LOAD DATA
|
||||
====================== */
|
||||
geojson = L.geoJson(statesData,{
|
||||
style:style,
|
||||
onEachFeature:onEachFeature
|
||||
}).addTo(map);
|
||||
|
||||
map.fitBounds(geojson.getBounds());
|
||||
|
||||
/* ======================
|
||||
LEGENDA
|
||||
====================== */
|
||||
const legend = L.control({position:'bottomright'});
|
||||
|
||||
legend.onAdd = function(){
|
||||
this._div = L.DomUtil.create('div','legend');
|
||||
this.update();
|
||||
return this._div;
|
||||
};
|
||||
|
||||
legend.update = function(){
|
||||
let mode = document.getElementById("mode").value;
|
||||
let grades, labels=[];
|
||||
|
||||
if(mode=="density"){
|
||||
grades=[0,3000,4000,5000,6000,8000,10000];
|
||||
|
||||
for(let i=0;i<grades.length;i++){
|
||||
let from=grades[i];
|
||||
let to=grades[i+1];
|
||||
|
||||
labels.push(
|
||||
'<i style="background:'+getColorDensity(from+1)+'"></i> '+
|
||||
from+(to?'–'+to:'+')
|
||||
);
|
||||
}
|
||||
|
||||
this._div.innerHTML="<b>Kepadatan</b><br>"+labels.join("<br>");
|
||||
|
||||
}else{
|
||||
grades=[0,40000,50000,60000,80000,100000,120000];
|
||||
|
||||
for(let i=0;i<grades.length;i++){
|
||||
let from=grades[i];
|
||||
let to=grades[i+1];
|
||||
|
||||
labels.push(
|
||||
'<i style="background:'+getColorPopulation(from+1)+'"></i> '+
|
||||
from+(to?'–'+to:'+')
|
||||
);
|
||||
}
|
||||
|
||||
this._div.innerHTML="<b>Penduduk</b><br>"+labels.join("<br>");
|
||||
}
|
||||
};
|
||||
|
||||
legend.addTo(map);
|
||||
|
||||
/* ======================
|
||||
UPDATE MAP
|
||||
====================== */
|
||||
function updateMap(){
|
||||
geojson.setStyle(style);
|
||||
legend.update();
|
||||
info.update();
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,72 @@
|
||||
var statesData = {
|
||||
"type": "FeatureCollection",
|
||||
"features": [
|
||||
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {"name": "Pontianak Barat", "density": 4200},
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [[
|
||||
[109.30,-0.01],[109.33,-0.01],[109.33,-0.05],[109.30,-0.05],[109.30,-0.01]
|
||||
]]
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {"name": "Pontianak Kota", "density": 9500},
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [[
|
||||
[109.33,-0.01],[109.36,-0.01],[109.36,-0.05],[109.33,-0.05],[109.33,-0.01]
|
||||
]]
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {"name": "Pontianak Selatan", "density": 7800},
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [[
|
||||
[109.33,-0.05],[109.36,-0.05],[109.36,-0.08],[109.33,-0.08],[109.33,-0.05]
|
||||
]]
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {"name": "Pontianak Tenggara", "density": 5200},
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [[
|
||||
[109.36,-0.05],[109.40,-0.05],[109.40,-0.08],[109.36,-0.08],[109.36,-0.05]
|
||||
]]
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {"name": "Pontianak Timur", "density": 4600},
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [[
|
||||
[109.36,-0.01],[109.40,-0.01],[109.40,-0.05],[109.36,-0.05],[109.36,-0.01]
|
||||
]]
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {"name": "Pontianak Utara", "density": 3000},
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [[
|
||||
[109.33,0.01],[109.38,0.01],[109.38,-0.01],[109.33,-0.01],[109.33,0.01]
|
||||
]]
|
||||
}
|
||||
}
|
||||
|
||||
]
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
include 'koneksi.php';
|
||||
|
||||
$data = [];
|
||||
|
||||
$result = $conn->query("SELECT * FROM spbu");
|
||||
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$data[] = $row;
|
||||
}
|
||||
|
||||
echo json_encode($data);
|
||||
?>
|
||||
@@ -0,0 +1,23 @@
|
||||
-- =============================================
|
||||
-- DATABASE: db_24jam
|
||||
-- =============================================
|
||||
|
||||
CREATE DATABASE IF NOT EXISTS `db_24jam`
|
||||
CHARACTER SET utf8mb4
|
||||
COLLATE utf8mb4_general_ci;
|
||||
|
||||
USE `db_24jam`;
|
||||
|
||||
-- ---------------------------------------------
|
||||
-- Tabel: spbu
|
||||
-- ---------------------------------------------
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `spbu` (
|
||||
`id` INT(11) NOT NULL AUTO_INCREMENT,
|
||||
`nama_spbu` VARCHAR(100) NOT NULL,
|
||||
`no_wa` VARCHAR(20) NOT NULL,
|
||||
`status` ENUM('yes','no') NOT NULL DEFAULT 'no',
|
||||
`latitude` DOUBLE NOT NULL,
|
||||
`longitude` DOUBLE NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
ob_start();
|
||||
include 'koneksi.php';
|
||||
ob_clean();
|
||||
|
||||
$id = $conn->real_escape_string($_POST['id'] ?? '');
|
||||
|
||||
$query = "DELETE FROM spbu WHERE id='$id'";
|
||||
|
||||
if ($conn->query($query)) {
|
||||
echo "success";
|
||||
} else {
|
||||
echo "error";
|
||||
}
|
||||
?>
|
||||
+466
@@ -0,0 +1,466 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="id">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Peta SPBU</title>
|
||||
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css"/>
|
||||
<script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>
|
||||
|
||||
<style>
|
||||
*{
|
||||
margin:0;
|
||||
padding:0;
|
||||
box-sizing:border-box;
|
||||
}
|
||||
|
||||
body{
|
||||
font-family:'Segoe UI',sans-serif;
|
||||
}
|
||||
|
||||
#map{
|
||||
height:100vh;
|
||||
}
|
||||
|
||||
/* ========================= */
|
||||
/* POPUP UMUM */
|
||||
/* ========================= */
|
||||
|
||||
.leaflet-popup-content-wrapper{
|
||||
border-radius:20px;
|
||||
overflow:hidden;
|
||||
padding:0;
|
||||
}
|
||||
|
||||
.leaflet-popup-content{
|
||||
margin:0;
|
||||
width:290px !important;
|
||||
}
|
||||
|
||||
.leaflet-popup-tip{
|
||||
background:white;
|
||||
}
|
||||
|
||||
/* ========================= */
|
||||
/* POPUP INFO SPBU */
|
||||
/* ========================= */
|
||||
|
||||
.spbu-card{
|
||||
padding:14px;
|
||||
background:#fff;
|
||||
}
|
||||
|
||||
.spbu-header{
|
||||
background:linear-gradient(135deg,#ff9800,#ff5b00);
|
||||
color:white;
|
||||
padding:16px;
|
||||
border-radius:16px;
|
||||
text-align:center;
|
||||
font-size:20px;
|
||||
font-weight:800;
|
||||
margin-bottom:12px;
|
||||
}
|
||||
|
||||
.spbu-info{
|
||||
background:#f4f4f4;
|
||||
border-radius:14px;
|
||||
padding:14px;
|
||||
margin-bottom:10px;
|
||||
font-size:16px;
|
||||
display:flex;
|
||||
gap:8px;
|
||||
align-items:center;
|
||||
}
|
||||
|
||||
.status-open{
|
||||
color:#18a84c;
|
||||
font-weight:bold;
|
||||
}
|
||||
|
||||
.status-close{
|
||||
color:#ff2020;
|
||||
font-weight:bold;
|
||||
}
|
||||
|
||||
.spbu-actions{
|
||||
display:grid;
|
||||
grid-template-columns:1fr 1fr;
|
||||
gap:10px;
|
||||
}
|
||||
|
||||
.btn{
|
||||
border:none;
|
||||
padding:12px;
|
||||
border-radius:14px;
|
||||
font-size:15px;
|
||||
font-weight:700;
|
||||
color:white;
|
||||
cursor:pointer;
|
||||
}
|
||||
|
||||
.btn-edit{
|
||||
background:#2995f0;
|
||||
}
|
||||
|
||||
.btn-delete{
|
||||
background:#ff4336;
|
||||
}
|
||||
|
||||
/* ========================= */
|
||||
/* FORM */
|
||||
/* ========================= */
|
||||
|
||||
.form-box{
|
||||
padding:16px;
|
||||
background:white;
|
||||
}
|
||||
|
||||
.form-title{
|
||||
text-align:center;
|
||||
font-size:19px;
|
||||
font-weight:800;
|
||||
color:#ff7300;
|
||||
margin-bottom:14px;
|
||||
}
|
||||
|
||||
.form-group{
|
||||
margin-bottom:12px;
|
||||
}
|
||||
|
||||
.form-group label{
|
||||
display:block;
|
||||
margin-bottom:6px;
|
||||
font-size:14px;
|
||||
font-weight:700;
|
||||
color:#444;
|
||||
}
|
||||
|
||||
.form-control{
|
||||
width:100%;
|
||||
padding:10px 12px;
|
||||
border:1px solid #ddd;
|
||||
border-radius:12px;
|
||||
font-size:14px;
|
||||
outline:none;
|
||||
}
|
||||
|
||||
.form-control:focus{
|
||||
border-color:#ff9800;
|
||||
}
|
||||
|
||||
.btn-save{
|
||||
width:100%;
|
||||
margin-top:4px;
|
||||
border:none;
|
||||
background:linear-gradient(135deg,#ff9800,#ff5b00);
|
||||
color:white;
|
||||
padding:12px;
|
||||
border-radius:14px;
|
||||
font-size:15px;
|
||||
font-weight:800;
|
||||
cursor:pointer;
|
||||
}
|
||||
|
||||
/* layer control */
|
||||
.leaflet-control-layers{
|
||||
border-radius:14px !important;
|
||||
overflow:hidden;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="map"></div>
|
||||
|
||||
<script>
|
||||
const map = L.map('map').setView([-0.02,109.34],13);
|
||||
|
||||
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png',{
|
||||
maxZoom:19
|
||||
}).addTo(map);
|
||||
|
||||
/* layer */
|
||||
let buka24 = L.layerGroup().addTo(map);
|
||||
let tidak24 = L.layerGroup().addTo(map);
|
||||
|
||||
/* marker icon */
|
||||
function markerIcon(color){
|
||||
return L.icon({
|
||||
iconUrl:`https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-${color}.png`,
|
||||
shadowUrl:'https://unpkg.com/leaflet@1.7.1/dist/images/marker-shadow.png',
|
||||
iconSize:[25,41],
|
||||
iconAnchor:[12,41]
|
||||
});
|
||||
}
|
||||
|
||||
/* ========================= */
|
||||
/* LOAD DATA */
|
||||
/* ========================= */
|
||||
|
||||
fetch('ambil_data.php')
|
||||
.then(r=>r.json())
|
||||
.then(data=>{
|
||||
|
||||
data.forEach(d=>{
|
||||
|
||||
let warna = d.status === 'yes' ? 'green' : 'red';
|
||||
|
||||
let marker = L.marker(
|
||||
[d.latitude,d.longitude],
|
||||
{
|
||||
icon:markerIcon(warna),
|
||||
draggable:true
|
||||
}
|
||||
);
|
||||
|
||||
marker.bindPopup(`
|
||||
<div class="spbu-card">
|
||||
|
||||
<div class="spbu-header">
|
||||
⛽ ${d.nama_spbu}
|
||||
</div>
|
||||
|
||||
<div class="spbu-info">
|
||||
📞 WA : ${d.no_wa}
|
||||
</div>
|
||||
|
||||
<div class="spbu-info">
|
||||
🕒 Status :
|
||||
<span class="${d.status=='yes' ? 'status-open':'status-close'}">
|
||||
${d.status=='yes' ? 'Buka 24 Jam':'Tidak 24 Jam'}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="spbu-actions">
|
||||
|
||||
<button class="btn btn-edit"
|
||||
onclick="editData(${d.id},'${d.nama_spbu}','${d.no_wa}','${d.status}',${d.latitude},${d.longitude})">
|
||||
✏️ Edit
|
||||
</button>
|
||||
|
||||
<button class="btn btn-delete"
|
||||
onclick="hapusData(${d.id})">
|
||||
🗑 Hapus
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
`);
|
||||
|
||||
marker.on('dragend',function(e){
|
||||
|
||||
let pos = e.target.getLatLng();
|
||||
|
||||
fetch('update.php',{
|
||||
method:'POST',
|
||||
body:new URLSearchParams({
|
||||
id:d.id,
|
||||
nama:d.nama_spbu,
|
||||
wa:d.no_wa,
|
||||
status:d.status,
|
||||
lat:pos.lat,
|
||||
lng:pos.lng
|
||||
})
|
||||
})
|
||||
.then(r=>r.text())
|
||||
.then(res=>{
|
||||
if(res=="success"){
|
||||
alert("Lokasi berhasil diupdate");
|
||||
location.reload();
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
if(d.status=="yes"){
|
||||
marker.addTo(buka24);
|
||||
}else{
|
||||
marker.addTo(tidak24);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
/* layer control */
|
||||
L.control.layers(null,{
|
||||
"🟢 SPBU Buka 24 Jam": buka24,
|
||||
"🔴 SPBU Tidak Buka 24 Jam": tidak24
|
||||
},{collapsed:false}).addTo(map);
|
||||
|
||||
/* ========================= */
|
||||
/* TAMBAH DATA */
|
||||
/* ========================= */
|
||||
|
||||
map.on('click',function(e){
|
||||
|
||||
let lat = e.latlng.lat;
|
||||
let lng = e.latlng.lng;
|
||||
|
||||
L.popup({maxWidth:310})
|
||||
.setLatLng(e.latlng)
|
||||
.setContent(`
|
||||
<div class="form-box">
|
||||
|
||||
<div class="form-title">Tambah SPBU</div>
|
||||
|
||||
<form id="formTambah">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Nama SPBU</label>
|
||||
<input class="form-control" type="text" name="nama" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>No WhatsApp</label>
|
||||
<input class="form-control" type="text" name="wa" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Status 24 Jam</label>
|
||||
<select class="form-control" name="status">
|
||||
<option value="yes">Yes</option>
|
||||
<option value="no">No</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<button class="btn-save">💾 Simpan</button>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
`)
|
||||
.openOn(map);
|
||||
|
||||
setTimeout(()=>{
|
||||
|
||||
document.getElementById("formTambah").onsubmit=function(ev){
|
||||
ev.preventDefault();
|
||||
|
||||
let fd = new FormData(this);
|
||||
fd.append("lat",lat);
|
||||
fd.append("lng",lng);
|
||||
|
||||
fetch('simpan.php',{
|
||||
method:'POST',
|
||||
body:fd
|
||||
})
|
||||
.then(r=>r.text())
|
||||
.then(res=>{
|
||||
if(res=="success"){
|
||||
alert("Data berhasil disimpan");
|
||||
location.reload();
|
||||
}else{
|
||||
alert("Gagal menyimpan");
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
},100);
|
||||
|
||||
});
|
||||
|
||||
/* ========================= */
|
||||
/* EDIT DATA */
|
||||
/* ========================= */
|
||||
|
||||
function editData(id,nama,wa,status,lat,lng){
|
||||
|
||||
L.popup({maxWidth:310})
|
||||
.setLatLng([lat,lng])
|
||||
.setContent(`
|
||||
<div class="form-box">
|
||||
|
||||
<div class="form-title">Edit SPBU</div>
|
||||
|
||||
<form id="formEdit">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Nama SPBU</label>
|
||||
<input class="form-control" type="text" name="nama" value="${nama}" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>No WhatsApp</label>
|
||||
<input class="form-control" type="text" name="wa" value="${wa}" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Status 24 Jam</label>
|
||||
<select class="form-control" name="status">
|
||||
<option value="yes" ${status=='yes'?'selected':''}>Yes</option>
|
||||
<option value="no" ${status=='no'?'selected':''}>No</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<button class="btn-save">💾 Update</button>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
`)
|
||||
.openOn(map);
|
||||
|
||||
setTimeout(()=>{
|
||||
|
||||
document.getElementById("formEdit").onsubmit=function(ev){
|
||||
ev.preventDefault();
|
||||
|
||||
let fd = new FormData(this);
|
||||
|
||||
fetch('update.php',{
|
||||
method:'POST',
|
||||
body:new URLSearchParams({
|
||||
id:id,
|
||||
nama:fd.get("nama"),
|
||||
wa:fd.get("wa"),
|
||||
status:fd.get("status"),
|
||||
lat:lat,
|
||||
lng:lng
|
||||
})
|
||||
})
|
||||
.then(r=>r.text())
|
||||
.then(res=>{
|
||||
if(res=="success"){
|
||||
alert("Data berhasil diupdate");
|
||||
location.reload();
|
||||
}else{
|
||||
alert("Gagal update");
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
},100);
|
||||
|
||||
}
|
||||
|
||||
/* ========================= */
|
||||
/* HAPUS DATA */
|
||||
/* ========================= */
|
||||
|
||||
function hapusData(id){
|
||||
|
||||
if(confirm("Yakin ingin hapus data ini?")){
|
||||
|
||||
fetch('hapus.php',{
|
||||
method:'POST',
|
||||
body:new URLSearchParams({
|
||||
id:id
|
||||
})
|
||||
})
|
||||
.then(r=>r.text())
|
||||
.then(res=>{
|
||||
if(res=="success"){
|
||||
alert("Data berhasil dihapus");
|
||||
location.reload();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
$host = getenv('DB_HOST') ?: 'localhost';
|
||||
$user = getenv('DB_USER') ?: 'root';
|
||||
$pass = getenv('DB_PASSWORD') ?: '';
|
||||
$db = getenv('DB_NAME') ?: 'db_24jam';
|
||||
|
||||
$conn = mysqli_connect($host, $user, $pass, $db);
|
||||
|
||||
if (!$conn) {
|
||||
die("Koneksi database gagal: " . mysqli_connect_error());
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
ob_start();
|
||||
include 'koneksi.php';
|
||||
ob_clean();
|
||||
|
||||
$nama = $conn->real_escape_string($_POST['nama'] ?? '');
|
||||
$wa = $conn->real_escape_string($_POST['wa'] ?? '');
|
||||
$status = $conn->real_escape_string($_POST['status'] ?? '');
|
||||
$lat = $conn->real_escape_string($_POST['lat'] ?? '');
|
||||
$lng = $conn->real_escape_string($_POST['lng'] ?? '');
|
||||
|
||||
$query = "INSERT INTO spbu (nama_spbu, no_wa, status, latitude, longitude)
|
||||
VALUES ('$nama','$wa','$status','$lat','$lng')";
|
||||
|
||||
if ($conn->query($query)) {
|
||||
echo "success";
|
||||
} else {
|
||||
echo "error";
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
ob_start();
|
||||
include 'koneksi.php';
|
||||
ob_clean();
|
||||
|
||||
$id = $conn->real_escape_string($_POST['id'] ?? '');
|
||||
$nama = $conn->real_escape_string($_POST['nama'] ?? '');
|
||||
$wa = $conn->real_escape_string($_POST['wa'] ?? '');
|
||||
$status = $conn->real_escape_string($_POST['status'] ?? '');
|
||||
$lat = $conn->real_escape_string($_POST['lat'] ?? '');
|
||||
$lng = $conn->real_escape_string($_POST['lng'] ?? '');
|
||||
|
||||
$query = "UPDATE spbu SET
|
||||
nama_spbu='$nama',
|
||||
no_wa='$wa',
|
||||
status='$status',
|
||||
latitude='$lat',
|
||||
longitude='$lng'
|
||||
WHERE id='$id'";
|
||||
|
||||
if ($conn->query($query)) {
|
||||
echo "success";
|
||||
} else {
|
||||
echo "error";
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,53 @@
|
||||
# WebGIS Pengentasan Kemiskinan — UAS 06 (FIXED)
|
||||
|
||||
## 📦 Struktur Folder WAJIB
|
||||
|
||||
```
|
||||
webgis_uas06/ ← taruh di htdocs/
|
||||
├── database_uas_06.sql ← Import ke MySQL PERTAMA
|
||||
├── koneksi.php
|
||||
├── auth.php
|
||||
├── ambil.php
|
||||
├── index.php
|
||||
├── login.php
|
||||
├── uploads/ ← buat folder ini (chmod 777)
|
||||
└── api/
|
||||
├── dashboard.php
|
||||
├── penduduk.php
|
||||
├── bantuan.php
|
||||
├── laporan.php
|
||||
├── ibadah.php
|
||||
├── user.php
|
||||
└── chat.php
|
||||
```
|
||||
|
||||
> ⚠️ PENTING: File dashboard.php, penduduk.php, dll HARUS berada di folder `api/`
|
||||
> bukan di root. Jika salah letak → semua halaman gagal memuat.
|
||||
|
||||
## 🚀 Cara Install
|
||||
|
||||
1. Import database: buka phpMyAdmin → Import → `database_uas_06.sql`
|
||||
2. Copy folder `webgis_uas06/` ke `C:\xampp\htdocs\`
|
||||
3. Pastikan folder `uploads/` ada di dalam webgis_uas06/
|
||||
4. Akses: `http://localhost/webgis_uas06/login.php`
|
||||
|
||||
## 👤 Akun Demo (password: `password`)
|
||||
|
||||
| Role | Email |
|
||||
|------|-------|
|
||||
| Pengurus (Admin) | admin@uas.id |
|
||||
| Pimpinan Daerah | pimpinan@uas.id |
|
||||
| Masyarakat | warga@uas.id |
|
||||
|
||||
## 🐛 Bug yang Diperbaiki
|
||||
|
||||
- ✅ Dashboard gagal memuat → fixed null-check pada query result
|
||||
- ✅ Penduduk gagal memuat & filter miskin/rentan tidak ada data → fixed
|
||||
- ✅ Simpan penduduk baru gagal → fixed bind_param & NULL handling
|
||||
- ✅ Edit penduduk gagal → fixed query UPDATE
|
||||
- ✅ Laporan filter tidak berfungsi → fixed query WHERE
|
||||
- ✅ Bantuan/histori gagal muat → fixed error handling
|
||||
- ✅ Chat gagal (unread error saat belum login) → fixed null check user_id
|
||||
- ✅ Ibadah & User gagal memuat → fixed error response
|
||||
- ✅ Detail penduduk gagal → sudah benar, path api/ fixed
|
||||
- ✅ Semua file API dipindah ke folder api/ sesuai path di index.php
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
include 'koneksi.php';
|
||||
$tipe=$_GET['tipe']??'semua';
|
||||
$data=[];
|
||||
|
||||
if ($tipe==='semua'||$tipe==='ibadah') {
|
||||
$res=$conn->query("SELECT ri.*,
|
||||
(SELECT COUNT(*) FROM penduduk p WHERE p.rumah_ibadah_id=ri.id AND p.status_hidup='hidup') AS total_penduduk,
|
||||
(SELECT COUNT(*) FROM penduduk p WHERE p.rumah_ibadah_id=ri.id AND p.status_ekonomi='miskin') AS total_miskin
|
||||
FROM rumah_ibadah ri");
|
||||
if($res) while($r=$res->fetch_assoc()){$r['tipe']='ibadah';$data[]=$r;}
|
||||
}
|
||||
|
||||
if ($tipe==='semua'||$tipe==='penduduk') {
|
||||
$where=["p.lat IS NOT NULL","p.lng IS NOT NULL","p.status_hidup='hidup'"];
|
||||
if(!empty($_GET['status_ekonomi'])){$se=$conn->real_escape_string($_GET['status_ekonomi']);$where[]="p.status_ekonomi='$se'";}
|
||||
if(!empty($_GET['rumah_ibadah_id'])){$ri_id=(int)$_GET['rumah_ibadah_id'];$where[]="p.rumah_ibadah_id=$ri_id";}
|
||||
if(!empty($_GET['status_bantuan'])){
|
||||
if($_GET['status_bantuan']==='sudah') $where[]="p.id IN (SELECT DISTINCT penduduk_id FROM histori_bantuan WHERE status='disalurkan')";
|
||||
else $where[]="p.id NOT IN (SELECT DISTINCT penduduk_id FROM histori_bantuan WHERE status='disalurkan')";
|
||||
}
|
||||
$w=implode(' AND ',$where);
|
||||
$res=$conn->query("SELECT p.id,p.nik,p.nama,p.jenis_kelamin,
|
||||
TIMESTAMPDIFF(YEAR,p.tanggal_lahir,CURDATE()) AS umur,
|
||||
p.status_ekonomi,p.pekerjaan,p.penghasilan,p.lat,p.lng,p.rumah_ibadah_id,
|
||||
ri.nama AS nama_ibadah,
|
||||
(SELECT COUNT(*) FROM histori_bantuan hb WHERE hb.penduduk_id=p.id AND hb.status='disalurkan') AS total_bantuan
|
||||
FROM penduduk p
|
||||
LEFT JOIN rumah_ibadah ri ON p.rumah_ibadah_id=ri.id
|
||||
WHERE $w");
|
||||
if($res) while($r=$res->fetch_assoc()){$r['tipe']='penduduk';$r['penghasilan']=(float)$r['penghasilan'];$data[]=$r;}
|
||||
}
|
||||
|
||||
if ($tipe==='semua'||$tipe==='laporan') {
|
||||
$res=$conn->query("SELECT id,pelapor,deskripsi,lat,lng,urgensi,status,created_at FROM laporan WHERE lat IS NOT NULL AND lng IS NOT NULL");
|
||||
if($res) while($r=$res->fetch_assoc()){$r['tipe']='laporan';$data[]=$r;}
|
||||
}
|
||||
|
||||
json_out($data);
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
session_start();
|
||||
include 'koneksi.php';
|
||||
$aksi = $_REQUEST['aksi'] ?? 'cek';
|
||||
|
||||
if ($aksi === 'cek') {
|
||||
if (!empty($_SESSION['user_id'])) json_out(['login'=>true,'user'=>$_SESSION['user']]);
|
||||
else json_out(['login'=>false]);
|
||||
}
|
||||
|
||||
if ($aksi === 'login') {
|
||||
$email = trim($_POST['email'] ?? '');
|
||||
$password = $_POST['password'] ?? '';
|
||||
if (!$email || !$password) json_out(['error'=>'Email dan password wajib diisi'],400);
|
||||
$stmt = $conn->prepare("SELECT u.*,r.nama AS role_nama FROM user u JOIN role r ON u.role_id=r.id WHERE u.email=? AND u.aktif=1 LIMIT 1");
|
||||
$stmt->bind_param('s',$email);
|
||||
$stmt->execute();
|
||||
$user = $stmt->get_result()->fetch_assoc();
|
||||
$stmt->close();
|
||||
if (!$user || !password_verify($password,$user['password'])) json_out(['error'=>'Email atau password salah'],401);
|
||||
unset($user['password']);
|
||||
$_SESSION['user_id'] = $user['id'];
|
||||
$_SESSION['user'] = $user;
|
||||
log_aktivitas($conn,$user['id'],'LOGIN','user',$user['id'],'Login berhasil');
|
||||
json_out(['login'=>true,'user'=>$user]);
|
||||
}
|
||||
|
||||
if ($aksi === 'logout') {
|
||||
session_destroy();
|
||||
json_out(['logout'=>true]);
|
||||
}
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
error_reporting(0);
|
||||
ini_set("display_errors", 0);
|
||||
ob_start();
|
||||
session_start();
|
||||
include 'koneksi.php';
|
||||
$aksi = $_REQUEST['aksi'] ?? 'list_bantuan';
|
||||
|
||||
if ($aksi === 'list_bantuan') {
|
||||
$res=$conn->query("SELECT * FROM bantuan ORDER BY jenis,nama");
|
||||
if(!$res) json_out(['error'=>$conn->error],500);
|
||||
$rows=[]; while($r=$res->fetch_assoc()) $rows[]=$r;
|
||||
json_out($rows);
|
||||
}
|
||||
|
||||
if ($aksi === 'simpan_master') {
|
||||
$nm=$conn->real_escape_string($_POST['nama']??'');
|
||||
$jn=$conn->real_escape_string($_POST['jenis']??'sembako');
|
||||
$sm=$conn->real_escape_string($_POST['sumber']??'');
|
||||
$bt=$conn->real_escape_string($_POST['bentuk']??'');
|
||||
$conn->query("INSERT INTO bantuan (nama,jenis,sumber,bentuk) VALUES ('$nm','$jn','$sm','$bt')");
|
||||
if($conn->error) json_out(['error'=>$conn->error],500);
|
||||
json_out(['success'=>true,'id'=>$conn->insert_id]);
|
||||
}
|
||||
|
||||
if ($aksi === 'edit_master') {
|
||||
$id=(int)($_POST['id']??0);
|
||||
$nm=$conn->real_escape_string($_POST['nama']??'');
|
||||
$jn=$conn->real_escape_string($_POST['jenis']??'sembako');
|
||||
$sm=$conn->real_escape_string($_POST['sumber']??'');
|
||||
$bt=$conn->real_escape_string($_POST['bentuk']??'');
|
||||
$conn->query("UPDATE bantuan SET nama='$nm',jenis='$jn',sumber='$sm',bentuk='$bt' WHERE id=$id");
|
||||
json_out(['success'=>true]);
|
||||
}
|
||||
|
||||
if ($aksi === 'hapus_master') {
|
||||
$id=(int)($_POST['id']??0);
|
||||
$conn->query("DELETE FROM histori_bantuan WHERE bantuan_id=$id");
|
||||
$conn->query("DELETE FROM bantuan WHERE id=$id");
|
||||
json_out(['success'=>true]);
|
||||
}
|
||||
|
||||
if ($aksi === 'histori') {
|
||||
$where=['1=1']; $params=[]; $types='';
|
||||
if(!empty($_GET['penduduk_id'])){$where[]="hb.penduduk_id=?";$params[]=(int)$_GET['penduduk_id'];$types.='i';}
|
||||
if(!empty($_GET['bantuan_id'])){$where[]="hb.bantuan_id=?";$params[]=(int)$_GET['bantuan_id'];$types.='i';}
|
||||
if(!empty($_GET['status'])){$where[]="hb.status=?";$params[]=$_GET['status'];$types.='s';}
|
||||
if(!empty($_GET['tgl_dari'])){$where[]="hb.tanggal>=?";$params[]=$_GET['tgl_dari'];$types.='s';}
|
||||
if(!empty($_GET['tgl_sampai'])){$where[]="hb.tanggal<=?";$params[]=$_GET['tgl_sampai'];$types.='s';}
|
||||
$w=implode(' AND ',$where);
|
||||
$sql="SELECT hb.*,b.nama AS nama_bantuan,b.jenis,b.sumber,p.nama AS nama_penduduk,p.status_ekonomi,p.nik,u.nama AS nama_pengurus
|
||||
FROM histori_bantuan hb
|
||||
JOIN bantuan b ON hb.bantuan_id=b.id
|
||||
JOIN penduduk p ON hb.penduduk_id=p.id
|
||||
LEFT JOIN user u ON hb.disalurkan_oleh=u.id
|
||||
WHERE $w ORDER BY hb.tanggal DESC,hb.id DESC";
|
||||
if($params){$stmt=$conn->prepare($sql);$stmt->bind_param($types,...$params);$stmt->execute();$res=$stmt->get_result();$stmt->close();}
|
||||
else $res=$conn->query($sql);
|
||||
$rows=[]; while($r=$res->fetch_assoc()) $rows[]=$r;
|
||||
json_out($rows);
|
||||
}
|
||||
|
||||
if ($aksi === 'salurkan') {
|
||||
$pid=(int)($_POST['penduduk_id']??0);
|
||||
$bid=(int)($_POST['bantuan_id']??0);
|
||||
$tgl=$conn->real_escape_string($_POST['tanggal']??date('Y-m-d'));
|
||||
$jml=$conn->real_escape_string($_POST['jumlah']??'');
|
||||
$ket=$conn->real_escape_string($_POST['keterangan']??'');
|
||||
$uid=!empty($_SESSION['user_id'])?(int)$_SESSION['user_id']:'NULL';
|
||||
$conn->query("INSERT INTO histori_bantuan (penduduk_id,bantuan_id,tanggal,jumlah,status,disalurkan_oleh,keterangan) VALUES ($pid,$bid,'$tgl','$jml','disalurkan',$uid,'$ket')");
|
||||
if($conn->error) json_out(['error'=>$conn->error],500);
|
||||
$new_id=$conn->insert_id;
|
||||
if($uid!=='NULL'){
|
||||
log_aktivitas($conn,$uid,'CREATE','histori_bantuan',$new_id,'Salurkan bantuan ke penduduk id='.$pid);
|
||||
}
|
||||
json_out(['success'=>true,'id'=>$new_id]);
|
||||
}
|
||||
|
||||
if ($aksi === 'edit_histori') {
|
||||
$id=(int)($_POST['id']??0);
|
||||
$status=$conn->real_escape_string($_POST['status']??'disalurkan');
|
||||
$jml=$conn->real_escape_string($_POST['jumlah']??'');
|
||||
$ket=$conn->real_escape_string($_POST['keterangan']??'');
|
||||
$conn->query("UPDATE histori_bantuan SET status='$status',jumlah='$jml',keterangan='$ket' WHERE id=$id");
|
||||
json_out(['success'=>true]);
|
||||
}
|
||||
|
||||
if ($aksi === 'hapus_histori') {
|
||||
$id=(int)($_POST['id']??0);
|
||||
$conn->query("DELETE FROM histori_bantuan WHERE id=$id");
|
||||
json_out(['success'=>true]);
|
||||
}
|
||||
|
||||
if ($aksi === 'belum_dibantu') {
|
||||
$sql="SELECT p.id,p.nik,p.nama,p.status_ekonomi,TIMESTAMPDIFF(YEAR,p.tanggal_lahir,CURDATE()) AS umur,ri.nama AS nama_ibadah,k.kelurahan
|
||||
FROM penduduk p
|
||||
LEFT JOIN rumah_ibadah ri ON p.rumah_ibadah_id=ri.id
|
||||
LEFT JOIN keluarga k ON p.keluarga_id=k.id
|
||||
WHERE p.status_hidup='hidup'
|
||||
AND p.status_ekonomi IN ('miskin','rentan')
|
||||
AND p.id NOT IN (SELECT DISTINCT penduduk_id FROM histori_bantuan WHERE status='disalurkan')
|
||||
ORDER BY FIELD(p.status_ekonomi,'miskin','rentan'),p.nama";
|
||||
$res=$conn->query($sql); $rows=[];
|
||||
while($r=$res->fetch_assoc()) $rows[]=$r;
|
||||
json_out($rows);
|
||||
}
|
||||
|
||||
if ($aksi === 'statistik') {
|
||||
$sql="SELECT b.id,b.nama,b.jenis,COUNT(hb.id) AS total_salur,SUM(hb.status='disalurkan') AS berhasil
|
||||
FROM bantuan b
|
||||
LEFT JOIN histori_bantuan hb ON hb.bantuan_id=b.id
|
||||
GROUP BY b.id ORDER BY total_salur DESC";
|
||||
$res=$conn->query($sql); $rows=[];
|
||||
while($r=$res->fetch_assoc()) $rows[]=$r;
|
||||
json_out($rows);
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
error_reporting(0);
|
||||
ini_set("display_errors", 0);
|
||||
ob_start();
|
||||
session_start();
|
||||
include 'koneksi.php';
|
||||
$aksi = $_REQUEST['aksi'] ?? 'inbox';
|
||||
$uid = (int)($_SESSION['user_id']??0);
|
||||
|
||||
if ($aksi === 'inbox') {
|
||||
if(!$uid) json_out([]);
|
||||
$sql="SELECT p.*,u.nama AS nama_pengirim
|
||||
FROM pesan p
|
||||
JOIN user u ON p.dari_user=u.id
|
||||
WHERE (p.ke_user=$uid OR p.ke_user IS NULL)
|
||||
ORDER BY p.created_at DESC LIMIT 80";
|
||||
$res=$conn->query($sql); $rows=[];
|
||||
if($res) while($r=$res->fetch_assoc()) $rows[]=$r;
|
||||
// tandai dibaca
|
||||
$conn->query("UPDATE pesan SET dibaca=1 WHERE ke_user=$uid AND dibaca=0");
|
||||
json_out(array_reverse($rows));
|
||||
}
|
||||
|
||||
if ($aksi === 'history') {
|
||||
$ke=(int)($_GET['ke']??0);
|
||||
$res=$conn->query("SELECT p.*,u.nama AS nama_pengirim FROM pesan p JOIN user u ON p.dari_user=u.id WHERE (p.dari_user=$uid AND p.ke_user=$ke) OR (p.dari_user=$ke AND p.ke_user=$uid) ORDER BY p.created_at ASC LIMIT 100");
|
||||
$rows=[]; if($res) while($r=$res->fetch_assoc()) $rows[]=$r;
|
||||
json_out($rows);
|
||||
}
|
||||
|
||||
if ($aksi === 'kirim') {
|
||||
if(!$uid) json_out(['error'=>'Belum login'],401);
|
||||
$ke=!empty($_POST['ke_user'])?(int)$_POST['ke_user']:'NULL';
|
||||
$isi=$conn->real_escape_string(trim($_POST['isi']??''));
|
||||
if(!$isi) json_out(['error'=>'Pesan kosong'],400);
|
||||
$conn->query("INSERT INTO pesan (dari_user,ke_user,isi) VALUES ($uid,$ke,'$isi')");
|
||||
if($conn->error) json_out(['error'=>$conn->error],500);
|
||||
json_out(['success'=>true,'id'=>$conn->insert_id,'time'=>date('H:i')]);
|
||||
}
|
||||
|
||||
if ($aksi === 'kontak') {
|
||||
$res=$conn->query("SELECT u.id,u.nama,r.nama AS role FROM user u JOIN role r ON u.role_id=r.id WHERE u.aktif=1".($uid?" AND u.id!=$uid":'')." ORDER BY u.nama");
|
||||
$rows=[]; if($res) while($r=$res->fetch_assoc()) $rows[]=$r;
|
||||
json_out($rows);
|
||||
}
|
||||
|
||||
if ($aksi === 'unread') {
|
||||
if(!$uid){ json_out(['count'=>0]); }
|
||||
$res=$conn->query("SELECT COUNT(*) c FROM pesan WHERE (ke_user=$uid OR ke_user IS NULL) AND dibaca=0 AND dari_user!=$uid");
|
||||
json_out(['count'=>(int)($res->fetch_assoc()['c']??0)]);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
error_reporting(0);
|
||||
ini_set("display_errors", 0);
|
||||
ob_start();
|
||||
session_start();
|
||||
include 'koneksi.php';
|
||||
ob_clean();
|
||||
|
||||
$tp=$conn->query("SELECT COUNT(*) c FROM penduduk WHERE status_hidup='hidup'")->fetch_assoc()['c'];
|
||||
$tm=$conn->query("SELECT COUNT(*) c FROM penduduk WHERE status_ekonomi='miskin' AND status_hidup='hidup'")->fetch_assoc()['c'];
|
||||
$tr=$conn->query("SELECT COUNT(*) c FROM penduduk WHERE status_ekonomi='rentan' AND status_hidup='hidup'")->fetch_assoc()['c'];
|
||||
$ta=$conn->query("SELECT COUNT(*) c FROM penduduk WHERE status_ekonomi='mampu' AND status_hidup='hidup'")->fetch_assoc()['c'];
|
||||
$tk=$conn->query("SELECT COUNT(*) c FROM keluarga")->fetch_assoc()['c'];
|
||||
$tl=$conn->query("SELECT COUNT(*) c FROM laporan")->fetch_assoc()['c'];
|
||||
$tld=$conn->query("SELECT COUNT(*) c FROM laporan WHERE urgensi='darurat' AND status='pending'")->fetch_assoc()['c'];
|
||||
$tlp=$conn->query("SELECT COUNT(*) c FROM laporan WHERE status='pending'")->fetch_assoc()['c'];
|
||||
$tbs=$conn->query("SELECT COUNT(*) c FROM histori_bantuan WHERE status='disalurkan'")->fetch_assoc()['c'];
|
||||
$tbd=$conn->query("SELECT COUNT(*) c FROM penduduk WHERE status_ekonomi IN ('miskin','rentan') AND status_hidup='hidup' AND id NOT IN (SELECT DISTINCT penduduk_id FROM histori_bantuan WHERE status='disalurkan')")->fetch_assoc()['c'];
|
||||
$twi=$conn->query("SELECT COUNT(DISTINCT kelurahan) c FROM keluarga")->fetch_assoc()['c'];
|
||||
$tri_res=$conn->query("SELECT COUNT(DISTINCT CONCAT(rt,rw,kelurahan)) c FROM keluarga")->fetch_assoc()['c'];
|
||||
|
||||
// per-bulan bantuan (6 bln terakhir)
|
||||
$tren=[]; $res=$conn->query("SELECT DATE_FORMAT(tanggal,'%Y-%m') bln,COUNT(*) jml FROM histori_bantuan WHERE tanggal>=DATE_SUB(CURDATE(),INTERVAL 6 MONTH) GROUP BY bln ORDER BY bln");
|
||||
if($res) while($r=$res->fetch_assoc()) $tren[]=$r;
|
||||
|
||||
// statistik ibadah
|
||||
$stat_ibadah=[]; $res=$conn->query("SELECT * FROM v_statistik_ibadah");
|
||||
if($res) while($r=$res->fetch_assoc()) $stat_ibadah[]=$r;
|
||||
|
||||
// prioritas
|
||||
$prioritas=[]; $res=$conn->query("SELECT p.id,p.nik,p.nama,p.status_ekonomi,TIMESTAMPDIFF(YEAR,p.tanggal_lahir,CURDATE()) AS umur,ri.nama AS nama_ibadah,(SELECT COUNT(*) FROM laporan la WHERE la.penduduk_id=p.id AND la.urgensi IN ('tinggi','darurat') AND la.status!='selesai') AS lap_darurat FROM penduduk p LEFT JOIN rumah_ibadah ri ON p.rumah_ibadah_id=ri.id WHERE p.status_hidup='hidup' AND p.status_ekonomi IN ('miskin','rentan') ORDER BY lap_darurat DESC,FIELD(p.status_ekonomi,'miskin','rentan') LIMIT 10");
|
||||
if($res) while($r=$res->fetch_assoc()) $prioritas[]=$r;
|
||||
|
||||
// aktivitas
|
||||
$aktivitas=[]; $res=$conn->query("SELECT la.*,u.nama AS nama_user FROM log_aktivitas la LEFT JOIN user u ON la.user_id=u.id ORDER BY la.created_at DESC LIMIT 15");
|
||||
if($res) while($r=$res->fetch_assoc()) $aktivitas[]=$r;
|
||||
|
||||
json_out([
|
||||
'total_penduduk'=>(int)$tp,'total_miskin'=>(int)$tm,'total_rentan'=>(int)$tr,'total_mampu'=>(int)$ta,
|
||||
'total_keluarga'=>(int)$tk,'total_laporan'=>(int)$tl,'total_darurat'=>(int)$tld,'laporan_pending'=>(int)$tlp,
|
||||
'total_bantuan_salur'=>(int)$tbs,'belum_dibantu'=>(int)$tbd,'total_wilayah'=>(int)$twi,'total_rt_rw'=>(int)$tri_res,
|
||||
'tren_bantuan'=>$tren,'stat_ibadah'=>$stat_ibadah,'prioritas'=>$prioritas,'aktivitas'=>$aktivitas,
|
||||
]);
|
||||
@@ -0,0 +1,285 @@
|
||||
-- ============================================================
|
||||
-- WebGIS Pengentasan Kemiskinan | Database: uas_06
|
||||
-- Jalankan sekali untuk setup awal
|
||||
-- ============================================================
|
||||
|
||||
CREATE DATABASE IF NOT EXISTS uas_06 CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
USE uas_06;
|
||||
|
||||
SET FOREIGN_KEY_CHECKS = 0;
|
||||
DROP TABLE IF EXISTS log_aktivitas;
|
||||
DROP TABLE IF EXISTS pesan;
|
||||
DROP TABLE IF EXISTS histori_bantuan;
|
||||
DROP TABLE IF EXISTS bantuan;
|
||||
DROP TABLE IF EXISTS laporan;
|
||||
DROP TABLE IF EXISTS pelatihan;
|
||||
DROP TABLE IF EXISTS penduduk;
|
||||
DROP TABLE IF EXISTS keluarga;
|
||||
DROP TABLE IF EXISTS rumah_ibadah;
|
||||
DROP TABLE IF EXISTS user;
|
||||
DROP TABLE IF EXISTS role;
|
||||
SET FOREIGN_KEY_CHECKS = 1;
|
||||
|
||||
-- ── 1. ROLE ─────────────────────────────────────────────────
|
||||
CREATE TABLE role (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
nama VARCHAR(50) NOT NULL,
|
||||
deskripsi TEXT
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
INSERT INTO role (nama, deskripsi) VALUES
|
||||
('masyarakat', 'Hanya dapat melihat data dan membuat laporan'),
|
||||
('pengurus', 'Input & update data penduduk, verifikasi laporan, salurkan bantuan'),
|
||||
('pimpinan', 'Read-only, monitoring dashboard');
|
||||
|
||||
-- ── 2. USER ─────────────────────────────────────────────────
|
||||
CREATE TABLE user (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
nama VARCHAR(100) NOT NULL,
|
||||
email VARCHAR(100) NOT NULL UNIQUE,
|
||||
password VARCHAR(255) NOT NULL,
|
||||
role_id INT NOT NULL DEFAULT 1,
|
||||
aktif TINYINT(1) DEFAULT 1,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (role_id) REFERENCES role(id)
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
-- Password untuk semua akun demo: "password"
|
||||
INSERT INTO user (nama, email, password, role_id) VALUES
|
||||
('Admin Utama', 'admin@uas.id', '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', 2),
|
||||
('Pimpinan Daerah', 'pimpinan@uas.id', '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', 3),
|
||||
('Warga Biasa', 'warga@uas.id', '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', 1),
|
||||
('Pengurus Masjid', 'pengurus2@uas.id','$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', 2);
|
||||
|
||||
-- ── 3. RUMAH IBADAH ─────────────────────────────────────────
|
||||
CREATE TABLE rumah_ibadah (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
nama VARCHAR(150) NOT NULL,
|
||||
jenis ENUM('masjid','gereja','pura','vihara','klenteng','lainnya') DEFAULT 'masjid',
|
||||
kontak VARCHAR(20),
|
||||
alamat VARCHAR(255),
|
||||
lat DOUBLE NOT NULL,
|
||||
lng DOUBLE NOT NULL,
|
||||
radius DOUBLE DEFAULT 500,
|
||||
user_id INT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (user_id) REFERENCES user(id) ON DELETE SET NULL
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
INSERT INTO rumah_ibadah (nama, jenis, kontak, alamat, lat, lng, radius, user_id) VALUES
|
||||
('Masjid Al-Ikhlas', 'masjid', '081234567890', 'Jl. Merdeka No.1, Pontianak Kota', 0.0269, 109.3425, 600, 1),
|
||||
('Gereja Santo Yosef', 'gereja', '081298765432', 'Jl. Veteran No.5, Pontianak Selatan', 0.0310, 109.3300, 500, 1),
|
||||
('Masjid Al-Furqon', 'masjid', '085678901234', 'Jl. Sudirman No.10, Pontianak Barat', 0.0200, 109.3500, 550, 4);
|
||||
|
||||
-- ── 4. KELUARGA (Kartu Keluarga) ────────────────────────────
|
||||
CREATE TABLE keluarga (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
no_kk VARCHAR(20) NOT NULL UNIQUE,
|
||||
alamat VARCHAR(255),
|
||||
rt VARCHAR(5),
|
||||
rw VARCHAR(5),
|
||||
kelurahan VARCHAR(100),
|
||||
kecamatan VARCHAR(100),
|
||||
lat DOUBLE,
|
||||
lng DOUBLE,
|
||||
rumah_ibadah_id INT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (rumah_ibadah_id) REFERENCES rumah_ibadah(id) ON DELETE SET NULL
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
INSERT INTO keluarga (no_kk, alamat, rt, rw, kelurahan, kecamatan, lat, lng, rumah_ibadah_id) VALUES
|
||||
('6171010101010001','Jl. Dahlia No.3', '001','001','Sungai Bangkong','Pontianak Kota', 0.0250,109.3410,1),
|
||||
('6171010101010002','Jl. Melati No.7', '002','001','Sungai Bangkong','Pontianak Kota', 0.0280,109.3440,1),
|
||||
('6171010101010003','Jl. Kenanga No.12','001','002','Benua Melayu', 'Pontianak Selatan',0.0215,109.3480,2),
|
||||
('6171010101010004','Jl. Mawar No.5', '003','002','Akcaya', 'Pontianak Selatan',0.0195,109.3460,2),
|
||||
('6171010101010005','Jl. Anggrek No.8', '001','003','Bansir Laut', 'Pontianak Tenggara',0.0175,109.3510,3);
|
||||
|
||||
-- ── 5. PENDUDUK ──────────────────────────────────────────────
|
||||
CREATE TABLE penduduk (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
nik VARCHAR(16) NOT NULL UNIQUE,
|
||||
nama VARCHAR(150) NOT NULL,
|
||||
jenis_kelamin ENUM('L','P') NOT NULL DEFAULT 'L',
|
||||
tanggal_lahir DATE NOT NULL,
|
||||
status_keluarga ENUM('kepala_keluarga','anggota') DEFAULT 'anggota',
|
||||
status_perkawinan ENUM('belum_kawin','kawin','cerai_hidup','cerai_mati') DEFAULT 'belum_kawin',
|
||||
status_hidup ENUM('hidup','meninggal') DEFAULT 'hidup',
|
||||
pekerjaan VARCHAR(100),
|
||||
penghasilan DECIMAL(15,2) DEFAULT 0,
|
||||
status_ekonomi ENUM('miskin','rentan','mampu') DEFAULT 'rentan',
|
||||
pendidikan_terakhir ENUM('tidak_sekolah','SD','SMP','SMA','D3','S1','S2','S3') DEFAULT 'tidak_sekolah',
|
||||
status_pendidikan ENUM('sekolah','tidak_sekolah','lulus') DEFAULT 'tidak_sekolah',
|
||||
keluarga_id INT,
|
||||
rumah_ibadah_id INT,
|
||||
lat DOUBLE,
|
||||
lng DOUBLE,
|
||||
no_hp VARCHAR(20),
|
||||
jenis_bantuan VARCHAR(255),
|
||||
foto VARCHAR(255),
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (keluarga_id) REFERENCES keluarga(id) ON DELETE SET NULL,
|
||||
FOREIGN KEY (rumah_ibadah_id) REFERENCES rumah_ibadah(id) ON DELETE SET NULL
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
INSERT INTO penduduk (nik,nama,jenis_kelamin,tanggal_lahir,status_keluarga,status_perkawinan,pekerjaan,penghasilan,status_ekonomi,pendidikan_terakhir,status_pendidikan,keluarga_id,rumah_ibadah_id,lat,lng,no_hp,jenis_bantuan) VALUES
|
||||
('6171010101010001','Ahmad Fauzi', 'L','1985-03-15','kepala_keluarga','kawin', 'Buruh Harian', 800000, 'miskin','SD', 'lulus', 1,1,0.02500,109.34100,'081234567891','sembako'),
|
||||
('6171010101010002','Siti Aminah', 'P','1988-07-20','anggota', 'kawin', 'Ibu RT', 0, 'miskin','SMP','lulus', 1,1,0.02510,109.34110,NULL, 'sembako'),
|
||||
('6171010101010003','Budi Santoso', 'L','2012-01-10','anggota', 'belum_kawin','Pelajar', 0, 'miskin','SMP','sekolah', 1,1,0.02520,109.34090,NULL, NULL),
|
||||
('6171010101010004','Dewi Rahayu', 'P','1979-11-05','kepala_keluarga','cerai_mati','Pedagang', 1500000,'rentan','SMA','lulus', 2,1,0.02800,109.34400,'085678901235','kesehatan'),
|
||||
('6171010101010005','Rizky Pratama', 'L','2008-06-22','anggota', 'belum_kawin','Pelajar', 0, 'rentan','SMP','sekolah', 2,1,0.02790,109.34420,NULL, NULL),
|
||||
('6171010101010006','Hendra Wijaya', 'L','1992-09-30','kepala_keluarga','kawin', 'Wiraswasta', 3000000,'mampu', 'S1', 'lulus', 3,2,0.02150,109.34800,'087890123456',NULL),
|
||||
('6171010101010007','Lina Marlina', 'P','1995-04-18','anggota', 'kawin', 'Guru Honorer', 1800000,'rentan','S1', 'lulus', 3,2,0.02155,109.34810,'081122334455','pendidikan'),
|
||||
('6171010101010008','Samsul Bahri', 'L','1970-08-25','kepala_keluarga','kawin', 'Nelayan', 600000, 'miskin','SD', 'lulus', 4,2,0.01950,109.34600,'089988776655','sembako'),
|
||||
('6171010101010009','Aisyah Putri', 'P','1975-12-30','anggota', 'kawin', 'Ibu RT', 0, 'miskin','SD', 'lulus', 4,2,0.01960,109.34590,NULL, NULL),
|
||||
('6171010101010010','Muhammad Yusuf','L','2005-09-15','anggota', 'belum_kawin','Pelajar', 0, 'miskin','SMA','sekolah', 4,2,0.01970,109.34580,NULL, NULL),
|
||||
('6171010101010011','Surya Darma', 'L','1988-02-14','kepala_keluarga','kawin', 'Tukang Ojek', 1200000,'rentan','SMP','lulus', 5,3,0.01750,109.35100,'082233445566','ekonomi'),
|
||||
('6171010101010012','Ratna Sari', 'P','1990-06-08','anggota', 'kawin', 'Pedagang Kecil', 800000, 'rentan','SMA','lulus', 5,3,0.01760,109.35110,NULL, NULL);
|
||||
|
||||
-- ── 6. PELATIHAN ────────────────────────────────────────────
|
||||
CREATE TABLE pelatihan (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
penduduk_id INT NOT NULL,
|
||||
nama_pelatihan VARCHAR(150),
|
||||
penyelenggara VARCHAR(100),
|
||||
tahun YEAR,
|
||||
keterangan TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (penduduk_id) REFERENCES penduduk(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
INSERT INTO pelatihan (penduduk_id, nama_pelatihan, penyelenggara, tahun) VALUES
|
||||
(1,'Pelatihan Menjahit','Dinas Tenaga Kerja',2023),
|
||||
(4,'Pelatihan UMKM','Kementerian Koperasi',2022),
|
||||
(11,'Pelatihan Ojek Online','Grab/Gojek',2023);
|
||||
|
||||
-- ── 7. BANTUAN (Master) ─────────────────────────────────────
|
||||
CREATE TABLE bantuan (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
nama VARCHAR(150) NOT NULL,
|
||||
jenis ENUM('sembako','pendidikan','kesehatan','ekonomi','perumahan','lainnya') DEFAULT 'sembako',
|
||||
sumber VARCHAR(150),
|
||||
bentuk VARCHAR(255),
|
||||
keterangan TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
INSERT INTO bantuan (nama, jenis, sumber, bentuk) VALUES
|
||||
('Sembako Bulanan', 'sembako', 'Rumah Ibadah', '10kg beras + minyak goreng 1L + gula 1kg'),
|
||||
('Beasiswa Pendidikan', 'pendidikan', 'Pemerintah Daerah','Rp 500.000/bulan'),
|
||||
('BPJS Kesehatan', 'kesehatan', 'Pemerintah Pusat', 'Kartu BPJS PBI gratis'),
|
||||
('Modal Usaha Mikro', 'ekonomi', 'Donatur/CSR', 'Rp 1.000.000 – Rp 5.000.000 sekali'),
|
||||
('Perbaikan Rumah', 'perumahan', 'Pemerintah Daerah','Material bangunan senilai Rp 10.000.000');
|
||||
|
||||
-- ── 8. HISTORI BANTUAN ──────────────────────────────────────
|
||||
CREATE TABLE histori_bantuan (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
penduduk_id INT NOT NULL,
|
||||
bantuan_id INT NOT NULL,
|
||||
tanggal DATE NOT NULL,
|
||||
jumlah VARCHAR(100),
|
||||
status ENUM('direncanakan','disalurkan','ditolak') DEFAULT 'direncanakan',
|
||||
disalurkan_oleh INT,
|
||||
keterangan TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (penduduk_id) REFERENCES penduduk(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (bantuan_id) REFERENCES bantuan(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (disalurkan_oleh) REFERENCES user(id) ON DELETE SET NULL
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
INSERT INTO histori_bantuan (penduduk_id,bantuan_id,tanggal,jumlah,status,disalurkan_oleh,keterangan) VALUES
|
||||
(1,1,'2024-01-10','10kg beras + minyak 1L','disalurkan',1,'Penyaluran Januari 2024'),
|
||||
(1,3,'2024-02-01','1 kartu BPJS', 'disalurkan',1,'Didaftarkan BPJS PBI'),
|
||||
(2,1,'2024-01-10','10kg beras + minyak 1L','disalurkan',1,'Penyaluran Januari 2024'),
|
||||
(4,2,'2024-03-01','Rp 500.000', 'disalurkan',1,'Beasiswa anak sekolah'),
|
||||
(8,1,'2024-01-15','10kg beras', 'disalurkan',4,'Bantuan Masjid Al-Furqon'),
|
||||
(9,3,'2024-02-15','1 kartu BPJS', 'disalurkan',4,'BPJS untuk ibu'),
|
||||
(11,4,'2024-04-01','Rp 2.000.000', 'disalurkan',4,'Modal usaha ojek online');
|
||||
|
||||
-- ── 9. LAPORAN MASYARAKAT ────────────────────────────────────
|
||||
CREATE TABLE laporan (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
pelapor VARCHAR(100),
|
||||
penduduk_id INT,
|
||||
deskripsi TEXT NOT NULL,
|
||||
lat DOUBLE,
|
||||
lng DOUBLE,
|
||||
foto VARCHAR(255),
|
||||
urgensi ENUM('rendah','sedang','tinggi','darurat') DEFAULT 'sedang',
|
||||
status ENUM('pending','diverifikasi','diproses','selesai') DEFAULT 'pending',
|
||||
diverifikasi_oleh INT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (penduduk_id) REFERENCES penduduk(id) ON DELETE SET NULL,
|
||||
FOREIGN KEY (diverifikasi_oleh) REFERENCES user(id) ON DELETE SET NULL
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
INSERT INTO laporan (pelapor,penduduk_id,deskripsi,lat,lng,urgensi,status) VALUES
|
||||
('Anonim', 1,'Pak Ahmad sakit keras sudah seminggu dan tidak punya biaya berobat. Kondisi sangat memprihatinkan.',0.02505,109.34105,'darurat','pending'),
|
||||
('Pak RT 02', 8,'Keluarga Pak Samsul di Jl. Mawar sudah 3 hari tidak makan. Sangat membutuhkan bantuan pangan segera.',0.01955,109.34605,'darurat','diverifikasi'),
|
||||
('Bu Dewi', NULL,'Ada anak putus sekolah di RT 003 karena orang tuanya tidak mampu bayar uang gedung.',0.02810,109.34410,'tinggi','pending'),
|
||||
('Pengurus Masjid',10,'Muhammad Yusuf butuh beasiswa lanjut SMA, keluarga tidak mampu.',0.01970,109.34580,'sedang','diproses'),
|
||||
('Warga RT 001', NULL,'Jalan di Jl. Anggrek rusak parah, bahaya bagi warga lansia.',0.01760,109.35115,'rendah','pending');
|
||||
|
||||
-- ── 10. LOG AKTIVITAS ────────────────────────────────────────
|
||||
CREATE TABLE log_aktivitas (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
user_id INT,
|
||||
aksi VARCHAR(50),
|
||||
tabel VARCHAR(50),
|
||||
data_id INT,
|
||||
keterangan TEXT,
|
||||
ip_address VARCHAR(45),
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (user_id) REFERENCES user(id) ON DELETE SET NULL
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
INSERT INTO log_aktivitas (user_id,aksi,tabel,data_id,keterangan) VALUES
|
||||
(1,'LOGIN','user',1,'Login pertama kali'),
|
||||
(1,'CREATE','histori_bantuan',1,'Salurkan sembako untuk Ahmad Fauzi'),
|
||||
(4,'CREATE','histori_bantuan',5,'Salurkan sembako untuk Samsul Bahri'),
|
||||
(1,'UPDATE','laporan',2,'Verifikasi laporan darurat');
|
||||
|
||||
-- ── 11. PESAN / CHAT ────────────────────────────────────────
|
||||
CREATE TABLE pesan (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
dari_user INT NOT NULL,
|
||||
ke_user INT,
|
||||
isi TEXT NOT NULL,
|
||||
dibaca TINYINT(1) DEFAULT 0,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (dari_user) REFERENCES user(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (ke_user) REFERENCES user(id) ON DELETE SET NULL
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
INSERT INTO pesan (dari_user,ke_user,isi,dibaca) VALUES
|
||||
(1,NULL,'Selamat datang di sistem WebGIS Kemiskinan UAS 06. Semua pengurus harap aktif memperbarui data.',1),
|
||||
(4,NULL,'Sudah ada 3 laporan darurat baru masuk. Mohon segera ditindaklanjuti.',0),
|
||||
(1,NULL,'Penyaluran bantuan bulan Januari 2024 sudah selesai untuk wilayah Pontianak Kota.',1);
|
||||
|
||||
-- ── 12. VIEWS ───────────────────────────────────────────────
|
||||
CREATE OR REPLACE VIEW v_penduduk AS
|
||||
SELECT
|
||||
p.*,
|
||||
TIMESTAMPDIFF(YEAR,p.tanggal_lahir,CURDATE()) AS umur,
|
||||
k.no_kk, k.rt, k.rw, k.kelurahan, k.kecamatan,
|
||||
ri.nama AS nama_ibadah, ri.jenis AS jenis_ibadah,
|
||||
(SELECT COUNT(*) FROM histori_bantuan hb WHERE hb.penduduk_id=p.id AND hb.status='disalurkan') AS total_bantuan_diterima
|
||||
FROM penduduk p
|
||||
LEFT JOIN keluarga k ON p.keluarga_id=k.id
|
||||
LEFT JOIN rumah_ibadah ri ON p.rumah_ibadah_id=ri.id;
|
||||
|
||||
CREATE OR REPLACE VIEW v_statistik_ibadah AS
|
||||
SELECT
|
||||
ri.id, ri.nama, ri.jenis,
|
||||
COUNT(p.id) AS total_penduduk,
|
||||
SUM(p.status_ekonomi='miskin') AS total_miskin,
|
||||
SUM(p.status_ekonomi='rentan') AS total_rentan,
|
||||
SUM(p.status_ekonomi='mampu') AS total_mampu
|
||||
FROM rumah_ibadah ri
|
||||
LEFT JOIN penduduk p ON p.rumah_ibadah_id=ri.id AND p.status_hidup='hidup'
|
||||
GROUP BY ri.id;
|
||||
|
||||
-- ── SELESAI ──────────────────────────────────────────────────
|
||||
SELECT 'Database uas_06 berhasil dibuat!' AS status;
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
error_reporting(0);
|
||||
ini_set("display_errors", 0);
|
||||
ob_start();
|
||||
session_start();
|
||||
include 'koneksi.php';
|
||||
$aksi = $_REQUEST['aksi'] ?? 'list';
|
||||
|
||||
if ($aksi === 'list') {
|
||||
$res=$conn->query("SELECT ri.*,
|
||||
(SELECT COUNT(*) FROM penduduk p WHERE p.rumah_ibadah_id=ri.id AND p.status_hidup='hidup') AS total_penduduk,
|
||||
(SELECT COUNT(*) FROM penduduk p WHERE p.rumah_ibadah_id=ri.id AND p.status_ekonomi='miskin' AND p.status_hidup='hidup') AS total_miskin
|
||||
FROM rumah_ibadah ri ORDER BY ri.nama");
|
||||
if(!$res) json_out(['error'=>$conn->error],500);
|
||||
$rows=[]; while($r=$res->fetch_assoc()) $rows[]=$r;
|
||||
json_out($rows);
|
||||
}
|
||||
|
||||
if ($aksi === 'simpan') {
|
||||
$nm=$conn->real_escape_string($_POST['nama']??'');
|
||||
$jn=$conn->real_escape_string($_POST['jenis']??'masjid');
|
||||
$kt=$conn->real_escape_string($_POST['kontak']??'');
|
||||
$al=$conn->real_escape_string($_POST['alamat']??'');
|
||||
$lat=(float)($_POST['lat']??0);
|
||||
$lng=(float)($_POST['lng']??0);
|
||||
$rad=(float)($_POST['radius']??500);
|
||||
$conn->query("INSERT INTO rumah_ibadah (nama,jenis,kontak,alamat,lat,lng,radius) VALUES ('$nm','$jn','$kt','$al',$lat,$lng,$rad)");
|
||||
if($conn->error) json_out(['error'=>$conn->error],500);
|
||||
json_out(['success'=>true,'id'=>$conn->insert_id]);
|
||||
}
|
||||
|
||||
if ($aksi === 'edit') {
|
||||
$id=(int)($_POST['id']??0);
|
||||
$nm=$conn->real_escape_string($_POST['nama']??'');
|
||||
$jn=$conn->real_escape_string($_POST['jenis']??'masjid');
|
||||
$kt=$conn->real_escape_string($_POST['kontak']??'');
|
||||
$al=$conn->real_escape_string($_POST['alamat']??'');
|
||||
$rad=(float)($_POST['radius']??500);
|
||||
$conn->query("UPDATE rumah_ibadah SET nama='$nm',jenis='$jn',kontak='$kt',alamat='$al',radius=$rad WHERE id=$id");
|
||||
if($conn->error) json_out(['error'=>$conn->error],500);
|
||||
json_out(['success'=>true]);
|
||||
}
|
||||
|
||||
if ($aksi === 'hapus') {
|
||||
$id=(int)($_POST['id']??0);
|
||||
$conn->query("UPDATE penduduk SET rumah_ibadah_id=NULL WHERE rumah_ibadah_id=$id");
|
||||
$conn->query("DELETE FROM rumah_ibadah WHERE id=$id");
|
||||
json_out(['success'=>true]);
|
||||
}
|
||||
+1671
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
// ============================================================
|
||||
// koneksi.php – Database uas_06
|
||||
// ============================================================
|
||||
$host = getenv('DB_HOST') ?: 'localhost';
|
||||
$user = getenv('DB_USER') ?: 'root';
|
||||
$pass = getenv('DB_PASSWORD') ?: '';
|
||||
$db = getenv('DB_NAME') ?: 'uas_06';
|
||||
|
||||
$conn = mysqli_connect($host, $user, $pass, $db);
|
||||
|
||||
if (!$conn) {
|
||||
http_response_code(500);
|
||||
header('Content-Type: application/json');
|
||||
die(json_encode(['error' => 'Koneksi database gagal: ' . mysqli_connect_error()]));
|
||||
}
|
||||
|
||||
$conn->set_charset('utf8mb4');
|
||||
|
||||
function log_aktivitas($conn, $user_id, $aksi, $tabel, $data_id = null, $ket = '') {
|
||||
$ip = $_SERVER['REMOTE_ADDR'] ?? '';
|
||||
$stmt = $conn->prepare("INSERT INTO log_aktivitas (user_id,aksi,tabel,data_id,keterangan,ip_address) VALUES (?,?,?,?,?,?)");
|
||||
if($stmt){
|
||||
$stmt->bind_param('ississ', $user_id, $aksi, $tabel, $data_id, $ket, $ip);
|
||||
$stmt->execute();
|
||||
$stmt->close();
|
||||
}
|
||||
}
|
||||
|
||||
function json_out($data, $code = 200) {
|
||||
http_response_code($code);
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
header('Access-Control-Allow-Origin: *');
|
||||
echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
exit;
|
||||
}
|
||||
|
||||
function requireLogin() {
|
||||
session_start();
|
||||
if (empty($_SESSION['user_id'])) {
|
||||
json_out(['error' => 'Belum login', 'redirect' => 'login.php'], 401);
|
||||
}
|
||||
return $_SESSION['user'];
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
error_reporting(0);
|
||||
ini_set("display_errors", 0);
|
||||
ob_start();
|
||||
session_start();
|
||||
include 'koneksi.php';
|
||||
$aksi = $_REQUEST['aksi'] ?? 'list';
|
||||
|
||||
if ($aksi === 'list') {
|
||||
$where=['1=1'];
|
||||
if(!empty($_GET['status'])){$where[]="l.status='".$conn->real_escape_string($_GET['status'])."'";}
|
||||
if(!empty($_GET['urgensi'])){$where[]="l.urgensi='".$conn->real_escape_string($_GET['urgensi'])."'";}
|
||||
if(!empty($_GET['q'])){
|
||||
$q=$conn->real_escape_string($_GET['q']);
|
||||
$where[]="(l.pelapor LIKE '%$q%' OR l.deskripsi LIKE '%$q%')";
|
||||
}
|
||||
$w=implode(' AND ',$where);
|
||||
$sql="SELECT l.*,p.nama AS nama_penduduk,p.status_ekonomi,p.nik,u.nama AS nama_verifikator
|
||||
FROM laporan l
|
||||
LEFT JOIN penduduk p ON l.penduduk_id=p.id
|
||||
LEFT JOIN user u ON l.diverifikasi_oleh=u.id
|
||||
WHERE $w
|
||||
ORDER BY FIELD(l.urgensi,'darurat','tinggi','sedang','rendah'),l.created_at DESC";
|
||||
$res=$conn->query($sql);
|
||||
if(!$res) json_out(['error'=>$conn->error],500);
|
||||
$rows=[]; while($r=$res->fetch_assoc()) $rows[]=$r;
|
||||
json_out($rows);
|
||||
}
|
||||
|
||||
if ($aksi === 'detail') {
|
||||
$id=(int)($_GET['id']??0);
|
||||
$res=$conn->query("SELECT l.*,p.nama AS nama_penduduk,u.nama AS nama_verifikator FROM laporan l LEFT JOIN penduduk p ON l.penduduk_id=p.id LEFT JOIN user u ON l.diverifikasi_oleh=u.id WHERE l.id=$id LIMIT 1");
|
||||
json_out($res->fetch_assoc()??['error'=>'Tidak ditemukan']);
|
||||
}
|
||||
|
||||
if ($aksi === 'kirim') {
|
||||
$pel=$conn->real_escape_string(trim($_POST['pelapor']??'Anonim'));
|
||||
if(empty($pel)) $pel='Anonim';
|
||||
$desk=$conn->real_escape_string(trim($_POST['deskripsi']??''));
|
||||
if(!$desk) json_out(['error'=>'Deskripsi wajib'],400);
|
||||
$lat=!empty($_POST['lat'])?(float)$_POST['lat']:'NULL';
|
||||
$lng=!empty($_POST['lng'])?(float)$_POST['lng']:'NULL';
|
||||
$urg=$conn->real_escape_string($_POST['urgensi']??'sedang');
|
||||
$pid=!empty($_POST['penduduk_id'])?(int)$_POST['penduduk_id']:'NULL';
|
||||
$foto='NULL';
|
||||
if(!empty($_FILES['foto']['name'])){
|
||||
$ext=pathinfo($_FILES['foto']['name'],PATHINFO_EXTENSION);
|
||||
$fname='laporan_'.time().'.'.$ext;
|
||||
$uploadDir=__DIR__.'/uploads/bukti/';
|
||||
if(!is_dir($uploadDir)) mkdir($uploadDir,0777,true);
|
||||
move_uploaded_file($_FILES['foto']['tmp_name'],$uploadDir.$fname);
|
||||
$foto="'".$fname."'";
|
||||
}
|
||||
$conn->query("INSERT INTO laporan (pelapor,penduduk_id,deskripsi,lat,lng,foto,urgensi) VALUES ('$pel',$pid,'$desk',$lat,$lng,$foto,'$urg')");
|
||||
if($conn->error) json_out(['error'=>$conn->error],500);
|
||||
if(!empty($_SESSION['user_id'])){
|
||||
log_aktivitas($conn,(int)$_SESSION['user_id'],'CREATE','laporan',$conn->insert_id,'Kirim laporan oleh '.$pel);
|
||||
}
|
||||
json_out(['success'=>true,'id'=>$conn->insert_id]);
|
||||
}
|
||||
|
||||
if ($aksi === 'edit') {
|
||||
$id=(int)($_POST['id']??0);
|
||||
$desk=$conn->real_escape_string($_POST['deskripsi']??'');
|
||||
$urg=$conn->real_escape_string($_POST['urgensi']??'sedang');
|
||||
$pel=$conn->real_escape_string($_POST['pelapor']??'');
|
||||
$status=$conn->real_escape_string($_POST['status']??'pending');
|
||||
$uid=!empty($_SESSION['user_id'])?(int)$_SESSION['user_id']:'NULL';
|
||||
$conn->query("UPDATE laporan SET pelapor='$pel',deskripsi='$desk',urgensi='$urg',status='$status',diverifikasi_oleh=$uid WHERE id=$id");
|
||||
json_out(['success'=>true]);
|
||||
}
|
||||
|
||||
if ($aksi === 'update_status') {
|
||||
$id=(int)($_POST['id']??0);
|
||||
$status=$conn->real_escape_string($_POST['status']??'pending');
|
||||
$uid=!empty($_SESSION['user_id'])?(int)$_SESSION['user_id']:'NULL';
|
||||
$conn->query("UPDATE laporan SET status='$status',diverifikasi_oleh=$uid WHERE id=$id");
|
||||
json_out(['success'=>true]);
|
||||
}
|
||||
|
||||
if ($aksi === 'hapus') {
|
||||
$id=(int)($_POST['id']??0);
|
||||
$conn->query("DELETE FROM laporan WHERE id=$id");
|
||||
json_out(['success'=>true]);
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="id">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<title>Login – WebGIS Kemiskinan UAS 06</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;600;700;800&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css"/>
|
||||
<style>
|
||||
:root{--bg:#0d1117;--surface:#161b22;--card:#1c2230;--border:#30363d;--text:#e6edf3;--muted:#8b949e;--accent:#58a6ff;--red:#f85149;--green:#3fb950}
|
||||
*{box-sizing:border-box;margin:0;padding:0}
|
||||
body{font-family:'Plus Jakarta Sans',sans-serif;background:var(--bg);color:var(--text);min-height:100vh;display:flex;align-items:center;justify-content:center;padding:20px}
|
||||
.wrap{width:100%;max-width:900px;display:grid;grid-template-columns:1fr 1fr;gap:0;border-radius:16px;overflow:hidden;box-shadow:0 8px 48px rgba(0,0,0,.7);border:1px solid var(--border)}
|
||||
.left{background:linear-gradient(135deg,#0d1117 0%,#1a2332 100%);padding:48px 40px;display:flex;flex-direction:column;justify-content:center;position:relative;overflow:hidden}
|
||||
.left::before{content:'';position:absolute;top:-60px;right:-60px;width:240px;height:240px;background:rgba(88,166,255,.06);border-radius:50%}
|
||||
.left::after{content:'';position:absolute;bottom:-40px;left:-40px;width:180px;height:180px;background:rgba(63,185,80,.05);border-radius:50%}
|
||||
.left-ico{font-size:40px;margin-bottom:20px;display:block}
|
||||
.left h1{font-size:26px;font-weight:800;letter-spacing:-0.5px;margin-bottom:10px;line-height:1.2}
|
||||
.left p{font-size:13px;color:var(--muted);line-height:1.6;margin-bottom:28px}
|
||||
.feat{display:flex;align-items:center;gap:10px;margin-bottom:10px;font-size:12px;color:var(--muted)}
|
||||
.feat i{color:var(--accent);width:16px}
|
||||
.right{background:var(--surface);padding:48px 40px}
|
||||
.right h2{font-size:20px;font-weight:800;margin-bottom:6px}
|
||||
.right .sub{font-size:12px;color:var(--muted);margin-bottom:28px}
|
||||
.fg{margin-bottom:16px}
|
||||
.fg label{font-size:11px;font-weight:700;color:var(--muted);display:block;margin-bottom:5px;text-transform:uppercase;letter-spacing:.4px}
|
||||
.fg input{width:100%;background:var(--card);border:1px solid var(--border);color:var(--text);padding:10px 13px;border-radius:8px;font-size:13px;font-family:inherit;outline:none;transition:border .15s}
|
||||
.fg input:focus{border-color:var(--accent)}
|
||||
.btn-login{width:100%;background:var(--accent);color:#0d1117;border:none;padding:11px;border-radius:8px;font-size:14px;font-weight:800;font-family:inherit;cursor:pointer;transition:background .15s;margin-top:4px}
|
||||
.btn-login:hover{background:#79c0ff}
|
||||
.btn-login:disabled{opacity:.6;cursor:not-allowed}
|
||||
.err{background:rgba(248,81,73,.08);border:1px solid rgba(248,81,73,.25);border-radius:7px;padding:9px 12px;font-size:12px;color:var(--red);margin-bottom:14px;display:none;align-items:center;gap:7px}
|
||||
.demo-box{background:var(--card);border:1px solid var(--border);border-radius:8px;padding:14px;margin-top:20px}
|
||||
.demo-box strong{display:block;font-size:10px;color:var(--muted);text-transform:uppercase;letter-spacing:1px;margin-bottom:10px}
|
||||
.demo-row{display:flex;align-items:center;justify-content:space-between;padding:5px 0;border-bottom:1px solid var(--border);font-size:12px}
|
||||
.demo-row:last-child{border:none}
|
||||
.demo-row .role{color:var(--muted);font-size:11px}
|
||||
.demo-row .creds{font-size:11px;color:var(--accent);cursor:pointer}
|
||||
.demo-row .creds:hover{text-decoration:underline}
|
||||
@media(max-width:650px){.wrap{grid-template-columns:1fr}.left{display:none}}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrap">
|
||||
<div class="left">
|
||||
<span class="left-ico">🗺️</span>
|
||||
<h1>WebGIS Pengentasan Kemiskinan</h1>
|
||||
<p>Sistem informasi geografis berbasis masyarakat untuk monitoring dan pengentasan kemiskinan yang terintegrasi dengan rumah ibadah.</p>
|
||||
<div class="feat"><i class="fas fa-map-marked-alt"></i> Peta sebaran penduduk real-time</div>
|
||||
<div class="feat"><i class="fas fa-gift"></i> Manajemen bantuan sosial</div>
|
||||
<div class="feat"><i class="fas fa-flag"></i> Sistem laporan masyarakat</div>
|
||||
<div class="feat"><i class="fas fa-chart-bar"></i> Dashboard statistik kemiskinan</div>
|
||||
<div class="feat"><i class="fas fa-comments"></i> Komunikasi antar pengurus</div>
|
||||
<div class="feat"><i class="fas fa-user-shield"></i> Sistem multi-role & autentikasi</div>
|
||||
</div>
|
||||
<div class="right">
|
||||
<h2>Masuk ke Sistem</h2>
|
||||
<p class="sub">Silakan login dengan akun yang telah terdaftar</p>
|
||||
<div class="err" id="err"><i class="fas fa-exclamation-circle"></i><span id="err-txt">–</span></div>
|
||||
<form onsubmit="doLogin(event)">
|
||||
<div class="fg"><label>Email</label><input type="email" id="email" placeholder="email@domain.com" required autofocus></div>
|
||||
<div class="fg"><label>Password</label><input type="password" id="pass" placeholder="••••••••" required></div>
|
||||
<button type="submit" class="btn-login" id="btn-submit"><i class="fas fa-sign-in-alt"></i> Masuk</button>
|
||||
</form>
|
||||
<div class="demo-box">
|
||||
<strong>Akun Demo — klik untuk isi otomatis</strong>
|
||||
<div class="demo-row">
|
||||
<div><div style="font-weight:700">Admin/Pengurus</div><div class="role">Akses penuh input & verifikasi</div></div>
|
||||
<span class="creds" onclick="fillDemo('admin@uas.id','password')">Gunakan →</span>
|
||||
</div>
|
||||
<div class="demo-row">
|
||||
<div><div style="font-weight:700">Pimpinan Daerah</div><div class="role">Read-only + dashboard</div></div>
|
||||
<span class="creds" onclick="fillDemo('pimpinan@uas.id','password')">Gunakan →</span>
|
||||
</div>
|
||||
<div class="demo-row">
|
||||
<div><div style="font-weight:700">Masyarakat</div><div class="role">Hanya lihat & laporan</div></div>
|
||||
<span class="creds" onclick="fillDemo('warga@uas.id','password')">Gunakan →</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
function fillDemo(e,p){
|
||||
document.getElementById('email').value=e;
|
||||
document.getElementById('pass').value=p;
|
||||
document.getElementById('email').focus();
|
||||
}
|
||||
function doLogin(e){
|
||||
e.preventDefault();
|
||||
const btn=document.getElementById('btn-submit');
|
||||
btn.disabled=true; btn.innerHTML='<i class="fas fa-spinner fa-spin"></i> Memverifikasi...';
|
||||
document.getElementById('err').style.display='none';
|
||||
const body=new URLSearchParams({aksi:'login',email:document.getElementById('email').value,password:document.getElementById('pass').value});
|
||||
fetch('auth.php',{method:'POST',body}).then(r=>r.json()).then(d=>{
|
||||
if(d.login){ window.location.href='index.php'; }
|
||||
else {
|
||||
const el=document.getElementById('err');
|
||||
document.getElementById('err-txt').textContent=d.error||'Login gagal';
|
||||
el.style.display='flex';
|
||||
btn.disabled=false; btn.innerHTML='<i class="fas fa-sign-in-alt"></i> Masuk';
|
||||
}
|
||||
}).catch(()=>{
|
||||
document.getElementById('err-txt').textContent='Tidak dapat terhubung ke server';
|
||||
document.getElementById('err').style.display='flex';
|
||||
btn.disabled=false; btn.innerHTML='<i class="fas fa-sign-in-alt"></i> Masuk';
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
+188
@@ -0,0 +1,188 @@
|
||||
<?php
|
||||
error_reporting(0);
|
||||
ini_set('display_errors', 0);
|
||||
ob_start();
|
||||
session_start();
|
||||
include 'koneksi.php';
|
||||
ob_clean(); // buang output apapun sebelum json_out
|
||||
$aksi = $_REQUEST['aksi'] ?? 'list';
|
||||
|
||||
// ── LIST ─────────────────────────────────────────────────────
|
||||
if ($aksi === 'list') {
|
||||
$where = ["p.status_hidup='hidup'"];
|
||||
$params = []; $types = '';
|
||||
if (!empty($_GET['status_ekonomi'])) { $where[] = "p.status_ekonomi=?"; $params[] = $_GET['status_ekonomi']; $types .= 's'; }
|
||||
if (!empty($_GET['rumah_ibadah_id'])) { $where[] = "p.rumah_ibadah_id=?"; $params[] = (int)$_GET['rumah_ibadah_id']; $types .= 'i'; }
|
||||
if (!empty($_GET['status_bantuan'])) {
|
||||
if ($_GET['status_bantuan'] === 'sudah') $where[] = "p.id IN (SELECT DISTINCT penduduk_id FROM histori_bantuan WHERE status='disalurkan')";
|
||||
else $where[] = "p.id NOT IN (SELECT DISTINCT penduduk_id FROM histori_bantuan WHERE status='disalurkan')";
|
||||
}
|
||||
if (!empty($_GET['kelurahan'])) { $where[] = "k.kelurahan LIKE ?"; $params[] = '%'.$_GET['kelurahan'].'%'; $types .= 's'; }
|
||||
if (!empty($_GET['penghasilan_min'])) { $where[] = "p.penghasilan>=?"; $params[] = (float)$_GET['penghasilan_min']; $types .= 'd'; }
|
||||
if (!empty($_GET['penghasilan_max'])) { $where[] = "p.penghasilan<=?"; $params[] = (float)$_GET['penghasilan_max']; $types .= 'd'; }
|
||||
if (!empty($_GET['q'])) { $like = '%'.$_GET['q'].'%'; $where[] = "(p.nama LIKE ? OR p.nik LIKE ?)"; $params[] = $like; $params[] = $like; $types .= 'ss'; }
|
||||
|
||||
$w = implode(' AND ', $where);
|
||||
$sql = "SELECT p.id,p.nik,p.nama,p.jenis_kelamin,p.tanggal_lahir,
|
||||
TIMESTAMPDIFF(YEAR,p.tanggal_lahir,CURDATE()) AS umur,
|
||||
p.status_keluarga,p.status_ekonomi,p.status_hidup,
|
||||
p.pekerjaan,p.penghasilan,p.pendidikan_terakhir,p.status_pendidikan,
|
||||
p.lat,p.lng,p.no_hp,p.jenis_bantuan,p.created_at,
|
||||
k.no_kk,k.rt,k.rw,k.kelurahan,k.kecamatan,
|
||||
ri.nama AS nama_ibadah,
|
||||
(SELECT COUNT(*) FROM histori_bantuan hb WHERE hb.penduduk_id=p.id AND hb.status='disalurkan') AS total_bantuan
|
||||
FROM penduduk p
|
||||
LEFT JOIN keluarga k ON p.keluarga_id=k.id
|
||||
LEFT JOIN rumah_ibadah ri ON p.rumah_ibadah_id=ri.id
|
||||
WHERE $w ORDER BY p.nama";
|
||||
if ($params) {
|
||||
$stmt = $conn->prepare($sql);
|
||||
$stmt->bind_param($types,...$params);
|
||||
$stmt->execute(); $res = $stmt->get_result(); $stmt->close();
|
||||
} else {
|
||||
$res = $conn->query($sql);
|
||||
}
|
||||
if(!$res) json_out(['error'=>$conn->error],500);
|
||||
$rows = [];
|
||||
while ($r = $res->fetch_assoc()) { $r['penghasilan']=(float)$r['penghasilan']; $rows[]=$r; }
|
||||
json_out($rows);
|
||||
}
|
||||
|
||||
// ── DETAIL ─────────────────────────────────────────────
|
||||
if ($aksi === 'detail') {
|
||||
$id = (int)($_GET['id']??0);
|
||||
$stmt = $conn->prepare("SELECT p.*,TIMESTAMPDIFF(YEAR,p.tanggal_lahir,CURDATE()) AS umur,k.no_kk,k.rt,k.rw,k.kelurahan,k.kecamatan,ri.nama AS nama_ibadah,ri.jenis AS jenis_ibadah FROM penduduk p LEFT JOIN keluarga k ON p.keluarga_id=k.id LEFT JOIN rumah_ibadah ri ON p.rumah_ibadah_id=ri.id WHERE p.id=?");
|
||||
$stmt->bind_param('i',$id); $stmt->execute();
|
||||
$data = $stmt->get_result()->fetch_assoc(); $stmt->close();
|
||||
if (!$data) json_out(['error'=>'Tidak ditemukan'],404);
|
||||
if ($data['keluarga_id']) {
|
||||
$stmt2 = $conn->prepare("SELECT p2.id,p2.nik,p2.nama,p2.jenis_kelamin,TIMESTAMPDIFF(YEAR,p2.tanggal_lahir,CURDATE()) AS umur,p2.status_keluarga,p2.pekerjaan,p2.status_ekonomi FROM penduduk p2 WHERE p2.keluarga_id=? AND p2.id!=?");
|
||||
$stmt2->bind_param('ii',$data['keluarga_id'],$id); $stmt2->execute();
|
||||
$res2=$stmt2->get_result(); $anggota=[];
|
||||
while($r=$res2->fetch_assoc()) $anggota[]=$r;
|
||||
$stmt2->close(); $data['anggota_keluarga']=$anggota;
|
||||
} else $data['anggota_keluarga']=[];
|
||||
$stmt3 = $conn->prepare("SELECT hb.*,b.nama AS nama_bantuan,b.jenis FROM histori_bantuan hb JOIN bantuan b ON hb.bantuan_id=b.id WHERE hb.penduduk_id=? ORDER BY hb.tanggal DESC");
|
||||
$stmt3->bind_param('i',$id); $stmt3->execute();
|
||||
$res3=$stmt3->get_result(); $histori=[];
|
||||
while($r=$res3->fetch_assoc()) $histori[]=$r;
|
||||
$stmt3->close(); $data['histori_bantuan']=$histori;
|
||||
json_out($data);
|
||||
}
|
||||
|
||||
// ── SIMPAN ─────────────────────────────────────────────
|
||||
if ($aksi === 'simpan') {
|
||||
$nik = trim($_POST['nik']??'');
|
||||
$nama = trim($_POST['nama']??'');
|
||||
$jk = $_POST['jenis_kelamin']??'L';
|
||||
$tgl = $_POST['tanggal_lahir']??'';
|
||||
$stk = $_POST['status_keluarga']??'anggota';
|
||||
$stm = $_POST['status_perkawinan']??'belum_kawin';
|
||||
$pek = $_POST['pekerjaan']??'';
|
||||
$pgh = (float)($_POST['penghasilan']??0);
|
||||
$sek = $_POST['status_ekonomi']??'rentan';
|
||||
$pend = $_POST['pendidikan_terakhir']??'tidak_sekolah';
|
||||
$spend= $_POST['status_pendidikan']??'tidak_sekolah';
|
||||
$kid = !empty($_POST['keluarga_id'])?(int)$_POST['keluarga_id']:null;
|
||||
$lat = !empty($_POST['lat'])?(float)$_POST['lat']:null;
|
||||
$lng = !empty($_POST['lng'])?(float)$_POST['lng']:null;
|
||||
$nohp = $_POST['no_hp']??'';
|
||||
$jban = $_POST['jenis_bantuan']??'';
|
||||
|
||||
if (!$nik||!$nama||!$tgl) json_out(['error'=>'NIK, nama, dan tanggal lahir wajib diisi'],400);
|
||||
|
||||
$cek=$conn->prepare("SELECT id FROM penduduk WHERE nik=?");
|
||||
$cek->bind_param('s',$nik); $cek->execute();
|
||||
if($cek->get_result()->num_rows>0) { $cek->close(); json_out(['error'=>'NIK sudah terdaftar'],400); }
|
||||
$cek->close();
|
||||
|
||||
// cari ibadah terdekat
|
||||
$ibadah_id=null;
|
||||
if($lat!==null && $lng!==null){
|
||||
$si=$conn->prepare("SELECT id FROM rumah_ibadah ORDER BY (6371*acos(cos(radians(?))*cos(radians(lat))*cos(radians(lng)-radians(?))+sin(radians(?))*sin(radians(lat)))) ASC LIMIT 1");
|
||||
$si->bind_param('ddd',$lat,$lng,$lat); $si->execute();
|
||||
$ri=$si->get_result()->fetch_assoc(); $si->close();
|
||||
$ibadah_id=$ri['id']??null;
|
||||
}
|
||||
|
||||
// Build query with NULL handling
|
||||
$lat_sql = $lat!==null ? $lat : 'NULL';
|
||||
$lng_sql = $lng!==null ? $lng : 'NULL';
|
||||
$kid_sql = $kid!==null ? $kid : 'NULL';
|
||||
$ibd_sql = $ibadah_id!==null ? $ibadah_id : 'NULL';
|
||||
|
||||
$conn->query("INSERT INTO penduduk
|
||||
(nik,nama,jenis_kelamin,tanggal_lahir,status_keluarga,status_perkawinan,
|
||||
pekerjaan,penghasilan,status_ekonomi,pendidikan_terakhir,status_pendidikan,
|
||||
keluarga_id,rumah_ibadah_id,lat,lng,no_hp,jenis_bantuan)
|
||||
VALUES (
|
||||
'".$conn->real_escape_string($nik)."',
|
||||
'".$conn->real_escape_string($nama)."',
|
||||
'".$conn->real_escape_string($jk)."',
|
||||
'".$conn->real_escape_string($tgl)."',
|
||||
'".$conn->real_escape_string($stk)."',
|
||||
'".$conn->real_escape_string($stm)."',
|
||||
'".$conn->real_escape_string($pek)."',
|
||||
$pgh,
|
||||
'".$conn->real_escape_string($sek)."',
|
||||
'".$conn->real_escape_string($pend)."',
|
||||
'".$conn->real_escape_string($spend)."',
|
||||
$kid_sql,$ibd_sql,$lat_sql,$lng_sql,
|
||||
'".$conn->real_escape_string($nohp)."',
|
||||
'".$conn->real_escape_string($jban)."'
|
||||
)");
|
||||
if($conn->error) json_out(['error'=>$conn->error],500);
|
||||
$new_id=$conn->insert_id;
|
||||
|
||||
if(!empty($_SESSION['user_id'])){
|
||||
log_aktivitas($conn,(int)$_SESSION['user_id'],'CREATE','penduduk',$new_id,'Tambah penduduk: '.$nama);
|
||||
}
|
||||
|
||||
$umur=(int)date_diff(date_create($tgl),date_create('today'))->y;
|
||||
$warning=null;
|
||||
if($umur>=7&&$umur<=12&&$spend!='sekolah') $warning='Umur 7-12 th seharusnya masih SD.';
|
||||
elseif($umur>=13&&$umur<=15&&$spend!='sekolah') $warning='Umur 13-15 th seharusnya masih SMP.';
|
||||
elseif($umur>=16&&$umur<=18&&$spend=='tidak_sekolah') $warning='Umur 16-18 th biasanya masih SMA/SMK, mohon verifikasi.';
|
||||
json_out(['success'=>true,'id'=>$new_id,'warning'=>$warning]);
|
||||
}
|
||||
|
||||
// ── EDIT ───────────────────────────────────────────────
|
||||
if ($aksi === 'edit') {
|
||||
$id=(int)($_POST['id']??0);
|
||||
$nm = $conn->real_escape_string($_POST['nama']??'');
|
||||
$jk = $conn->real_escape_string($_POST['jenis_kelamin']??'L');
|
||||
$tgl = $conn->real_escape_string($_POST['tanggal_lahir']??'');
|
||||
$stk = $conn->real_escape_string($_POST['status_keluarga']??'anggota');
|
||||
$stm = $conn->real_escape_string($_POST['status_perkawinan']??'belum_kawin');
|
||||
$sh = $conn->real_escape_string($_POST['status_hidup']??'hidup');
|
||||
$pek = $conn->real_escape_string($_POST['pekerjaan']??'');
|
||||
$pgh = (float)($_POST['penghasilan']??0);
|
||||
$sek = $conn->real_escape_string($_POST['status_ekonomi']??'rentan');
|
||||
$pend= $conn->real_escape_string($_POST['pendidikan_terakhir']??'tidak_sekolah');
|
||||
$spend=$conn->real_escape_string($_POST['status_pendidikan']??'tidak_sekolah');
|
||||
$nohp= $conn->real_escape_string($_POST['no_hp']??'');
|
||||
$jban= $conn->real_escape_string($_POST['jenis_bantuan']??'');
|
||||
|
||||
$conn->query("UPDATE penduduk SET
|
||||
nama='$nm',jenis_kelamin='$jk',tanggal_lahir='$tgl',
|
||||
status_keluarga='$stk',status_perkawinan='$stm',status_hidup='$sh',
|
||||
pekerjaan='$pek',penghasilan=$pgh,status_ekonomi='$sek',
|
||||
pendidikan_terakhir='$pend',status_pendidikan='$spend',
|
||||
no_hp='$nohp',jenis_bantuan='$jban'
|
||||
WHERE id=$id");
|
||||
if($conn->error) json_out(['error'=>$conn->error],500);
|
||||
if(!empty($_SESSION['user_id'])){
|
||||
log_aktivitas($conn,(int)$_SESSION['user_id'],'UPDATE','penduduk',$id,'Edit penduduk id='.$id);
|
||||
}
|
||||
json_out(['success'=>true]);
|
||||
}
|
||||
|
||||
// ── HAPUS ──────────────────────────────────────────────
|
||||
if ($aksi === 'hapus') {
|
||||
$id=(int)($_POST['id']??0);
|
||||
$conn->query("DELETE FROM histori_bantuan WHERE penduduk_id=$id");
|
||||
$conn->query("DELETE FROM pelatihan WHERE penduduk_id=$id");
|
||||
$conn->query("DELETE FROM laporan WHERE penduduk_id=$id");
|
||||
$conn->query("DELETE FROM penduduk WHERE id=$id");
|
||||
json_out(['success'=>true]);
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
error_reporting(0);
|
||||
ini_set("display_errors", 0);
|
||||
ob_start();
|
||||
session_start();
|
||||
include 'koneksi.php';
|
||||
$aksi = $_REQUEST['aksi'] ?? 'list';
|
||||
|
||||
if ($aksi === 'list') {
|
||||
$res=$conn->query("SELECT u.id,u.nama,u.email,u.aktif,u.created_at,r.nama AS role_nama,r.id AS role_id FROM user u JOIN role r ON u.role_id=r.id ORDER BY u.nama");
|
||||
if(!$res) json_out(['error'=>$conn->error],500);
|
||||
$rows=[]; while($r=$res->fetch_assoc()) $rows[]=$r;
|
||||
json_out($rows);
|
||||
}
|
||||
|
||||
if ($aksi === 'simpan') {
|
||||
$nm=$conn->real_escape_string($_POST['nama']??'');
|
||||
$em=$conn->real_escape_string($_POST['email']??'');
|
||||
$pw=password_hash($_POST['password']??'password',PASSWORD_DEFAULT);
|
||||
$rid=(int)($_POST['role_id']??1);
|
||||
$cek=$conn->query("SELECT id FROM user WHERE email='$em'")->num_rows;
|
||||
if($cek>0) json_out(['error'=>'Email sudah digunakan'],400);
|
||||
$conn->query("INSERT INTO user (nama,email,password,role_id) VALUES ('$nm','$em','$pw',$rid)");
|
||||
if($conn->error) json_out(['error'=>$conn->error],500);
|
||||
json_out(['success'=>true,'id'=>$conn->insert_id]);
|
||||
}
|
||||
|
||||
if ($aksi === 'edit') {
|
||||
$id=(int)($_POST['id']??0);
|
||||
$nm=$conn->real_escape_string($_POST['nama']??'');
|
||||
$em=$conn->real_escape_string($_POST['email']??'');
|
||||
$rid=(int)($_POST['role_id']??1);
|
||||
$akt=(int)($_POST['aktif']??1);
|
||||
$conn->query("UPDATE user SET nama='$nm',email='$em',role_id=$rid,aktif=$akt WHERE id=$id");
|
||||
if(!empty($_POST['password'])){
|
||||
$pw=password_hash($_POST['password'],PASSWORD_DEFAULT);
|
||||
$conn->query("UPDATE user SET password='$pw' WHERE id=$id");
|
||||
}
|
||||
json_out(['success'=>true]);
|
||||
}
|
||||
|
||||
if ($aksi === 'hapus') {
|
||||
$id=(int)($_POST['id']??0);
|
||||
$conn->query("DELETE FROM user WHERE id=$id");
|
||||
json_out(['success'=>true]);
|
||||
}
|
||||
|
||||
if ($aksi === 'roles') {
|
||||
$res=$conn->query("SELECT * FROM role"); $rows=[];
|
||||
while($r=$res->fetch_assoc()) $rows[]=$r;
|
||||
json_out($rows);
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
FROM php:8.2-apache
|
||||
|
||||
RUN docker-php-ext-install mysqli pdo pdo_mysql \
|
||||
&& a2enmod rewrite
|
||||
|
||||
WORKDIR /var/www/html
|
||||
|
||||
COPY . /var/www/html/
|
||||
|
||||
RUN mkdir -p /var/www/html/05/uploads/bukti \
|
||||
&& chown -R www-data:www-data /var/www/html
|
||||
+803
@@ -0,0 +1,803 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="id">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Portal WebGIS Terpadu</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg: #f6f9fb;
|
||||
--surface: rgba(255, 255, 255, 0.76);
|
||||
--surface-strong: rgba(255, 255, 255, 0.92);
|
||||
--text: #16212d;
|
||||
--muted: #657486;
|
||||
--line: rgba(154, 170, 185, 0.36);
|
||||
--chrome: #dbe4ec;
|
||||
--silver: #f7fafc;
|
||||
--green: #0f8c7c;
|
||||
--teal: #119eb3;
|
||||
--blue: #3e6fb6;
|
||||
--amber: #c98d28;
|
||||
--rose: #b95473;
|
||||
--shadow: 0 24px 60px rgba(42, 58, 74, 0.17);
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
min-height: 100vh;
|
||||
margin: 0;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
color: var(--text);
|
||||
background:
|
||||
linear-gradient(90deg, rgba(72, 105, 130, 0.1) 1px, transparent 1px),
|
||||
linear-gradient(rgba(72, 105, 130, 0.08) 1px, transparent 1px),
|
||||
linear-gradient(115deg, rgba(255, 255, 255, 0.9), rgba(221, 233, 241, 0.52) 42%, rgba(255, 255, 255, 0.84)),
|
||||
radial-gradient(circle at 16% 18%, rgba(17, 158, 179, 0.18), transparent 30%),
|
||||
radial-gradient(circle at 84% 12%, rgba(201, 141, 40, 0.16), transparent 26%),
|
||||
var(--bg);
|
||||
background-size: 52px 52px, 52px 52px, auto, auto, auto, auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
body::before {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: -1;
|
||||
content: "";
|
||||
background:
|
||||
linear-gradient(128deg, transparent 0 43%, rgba(255, 255, 255, 0.9) 43% 44%, rgba(77, 114, 145, 0.16) 44% 45%, transparent 45%),
|
||||
linear-gradient(35deg, transparent 0 58%, rgba(17, 158, 179, 0.12) 58% 59%, rgba(255, 255, 255, 0.7) 59% 60%, transparent 60%);
|
||||
opacity: 0.86;
|
||||
pointer-events: none;
|
||||
animation: drift-lines 16s ease-in-out infinite alternate;
|
||||
}
|
||||
|
||||
body::after {
|
||||
position: fixed;
|
||||
inset: -20%;
|
||||
z-index: -2;
|
||||
content: "";
|
||||
background:
|
||||
conic-gradient(from 130deg at 50% 50%, rgba(255, 255, 255, 0), rgba(182, 199, 214, 0.34), rgba(255, 255, 255, 0), rgba(17, 158, 179, 0.15), rgba(255, 255, 255, 0));
|
||||
filter: blur(26px);
|
||||
opacity: 0.8;
|
||||
animation: rotate-halo 26s linear infinite;
|
||||
}
|
||||
|
||||
.page {
|
||||
width: min(1120px, calc(100% - 40px));
|
||||
margin: 0 auto;
|
||||
padding: 56px 0 48px;
|
||||
animation: page-rise 650ms ease both;
|
||||
}
|
||||
|
||||
.map-orbit {
|
||||
position: fixed;
|
||||
top: 34px;
|
||||
right: max(20px, calc((100vw - 1120px) / 2));
|
||||
z-index: -1;
|
||||
width: 310px;
|
||||
aspect-ratio: 1;
|
||||
border: 1px solid rgba(78, 100, 120, 0.13);
|
||||
border-radius: 50%;
|
||||
background:
|
||||
repeating-radial-gradient(circle, transparent 0 28px, rgba(61, 95, 123, 0.08) 29px 30px),
|
||||
linear-gradient(145deg, rgba(255, 255, 255, 0.42), rgba(190, 207, 221, 0.16));
|
||||
box-shadow: inset 0 0 34px rgba(255, 255, 255, 0.72);
|
||||
opacity: 0.88;
|
||||
pointer-events: none;
|
||||
animation: float-orbit 8s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.map-orbit span {
|
||||
position: absolute;
|
||||
width: 11px;
|
||||
height: 11px;
|
||||
border-radius: 50%;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 0 0 5px rgba(17, 158, 179, 0.14), 0 0 22px rgba(17, 158, 179, 0.48);
|
||||
}
|
||||
|
||||
.map-orbit span:nth-child(1) {
|
||||
top: 22%;
|
||||
left: 52%;
|
||||
}
|
||||
|
||||
.map-orbit span:nth-child(2) {
|
||||
top: 64%;
|
||||
left: 30%;
|
||||
box-shadow: 0 0 0 5px rgba(201, 141, 40, 0.16), 0 0 22px rgba(201, 141, 40, 0.42);
|
||||
}
|
||||
|
||||
.map-orbit span:nth-child(3) {
|
||||
top: 48%;
|
||||
right: 17%;
|
||||
box-shadow: 0 0 0 5px rgba(62, 111, 182, 0.14), 0 0 22px rgba(62, 111, 182, 0.42);
|
||||
}
|
||||
|
||||
header {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
gap: 24px;
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
|
||||
.title-block {
|
||||
max-width: 720px;
|
||||
}
|
||||
|
||||
.eyebrow {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 12px;
|
||||
color: #0e7f88;
|
||||
font-size: 0.84rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.eyebrow::before {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border: 3px solid #20a8b3;
|
||||
border-radius: 50%;
|
||||
content: "";
|
||||
background: var(--surface);
|
||||
box-shadow: 0 0 18px rgba(32, 168, 179, 0.5);
|
||||
animation: pulse-dot 2.4s ease-in-out infinite;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 0;
|
||||
color: transparent;
|
||||
background: linear-gradient(95deg, #15212d 0%, #627689 34%, #f8fbfd 47%, #184d62 62%, #172534 100%);
|
||||
background-clip: text;
|
||||
-webkit-background-clip: text;
|
||||
font-size: clamp(2rem, 4vw, 4rem);
|
||||
line-height: 1.05;
|
||||
letter-spacing: 0;
|
||||
text-shadow: 0 12px 36px rgba(46, 68, 88, 0.13);
|
||||
}
|
||||
|
||||
.description {
|
||||
max-width: 620px;
|
||||
margin: 16px 0 0;
|
||||
color: var(--muted);
|
||||
font-size: 1.05rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.status-panel {
|
||||
width: 250px;
|
||||
min-width: 250px;
|
||||
padding: 12px;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
background:
|
||||
linear-gradient(135deg, rgba(255, 255, 255, 0.95), rgba(219, 228, 236, 0.7) 45%, rgba(255, 255, 255, 0.86));
|
||||
box-shadow: 0 16px 34px rgba(42, 58, 74, 0.12), inset 0 1px 0 rgba(255, 255, 255, 0.88);
|
||||
backdrop-filter: blur(16px);
|
||||
}
|
||||
|
||||
.pontianak-map {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
min-height: 154px;
|
||||
border: 1px solid rgba(154, 170, 185, 0.3);
|
||||
border-radius: 8px;
|
||||
background:
|
||||
linear-gradient(90deg, rgba(89, 119, 145, 0.1) 1px, transparent 1px),
|
||||
linear-gradient(rgba(89, 119, 145, 0.08) 1px, transparent 1px),
|
||||
linear-gradient(145deg, #f9fcfd, #dfe9f0 48%, #ffffff);
|
||||
background-size: 18px 18px, 18px 18px, auto;
|
||||
}
|
||||
|
||||
.pontianak-map::before {
|
||||
position: absolute;
|
||||
inset: -45%;
|
||||
content: "";
|
||||
background: conic-gradient(from 0deg, transparent, rgba(255, 255, 255, 0.96), transparent, rgba(17, 158, 179, 0.16), transparent);
|
||||
animation: rotate-halo 11s linear infinite;
|
||||
}
|
||||
|
||||
.pontianak-map svg {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
filter: drop-shadow(0 12px 18px rgba(42, 58, 74, 0.16));
|
||||
}
|
||||
|
||||
.map-region {
|
||||
stroke: rgba(255, 255, 255, 0.94);
|
||||
stroke-width: 2;
|
||||
transition: transform 220ms ease, filter 220ms ease;
|
||||
transform-origin: center;
|
||||
}
|
||||
|
||||
.pontianak-map:hover .map-region {
|
||||
filter: brightness(1.05);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.map-river {
|
||||
fill: none;
|
||||
stroke: rgba(30, 136, 163, 0.7);
|
||||
stroke-width: 8;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
|
||||
.map-pin {
|
||||
animation: pulse-pin 2.2s ease-in-out infinite;
|
||||
transform-origin: 124px 78px;
|
||||
}
|
||||
|
||||
.map-caption {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
padding: 9px 4px 0;
|
||||
color: #395065;
|
||||
font-size: 0.82rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.map-caption span:last-child {
|
||||
color: #0e7f88;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.project-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
.project-card {
|
||||
position: relative;
|
||||
display: flex;
|
||||
min-height: 220px;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
overflow: hidden;
|
||||
padding: 22px;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
background:
|
||||
linear-gradient(140deg, rgba(255, 255, 255, 0.96), rgba(224, 233, 241, 0.78) 48%, rgba(255, 255, 255, 0.9)),
|
||||
var(--surface);
|
||||
box-shadow: 0 10px 26px rgba(42, 58, 74, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.9);
|
||||
backdrop-filter: blur(18px);
|
||||
transform: translateY(0);
|
||||
transition: transform 220ms ease, box-shadow 220ms ease, border-color 220ms ease, background-position 220ms ease;
|
||||
animation: card-enter 560ms ease both;
|
||||
}
|
||||
|
||||
.project-card::before {
|
||||
position: absolute;
|
||||
inset: -30%;
|
||||
content: "";
|
||||
background:
|
||||
linear-gradient(105deg, transparent 0 34%, rgba(255, 255, 255, 0.95) 45%, transparent 56%),
|
||||
radial-gradient(circle at 20% 10%, var(--accent-soft), transparent 36%);
|
||||
opacity: 0.68;
|
||||
pointer-events: none;
|
||||
transform: translateX(-34%);
|
||||
transition: transform 650ms ease;
|
||||
}
|
||||
|
||||
.project-card::after {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
content: "";
|
||||
background:
|
||||
linear-gradient(90deg, rgba(90, 115, 138, 0.08) 1px, transparent 1px),
|
||||
linear-gradient(rgba(90, 115, 138, 0.06) 1px, transparent 1px);
|
||||
background-size: 18px 18px;
|
||||
mask-image: linear-gradient(180deg, transparent, #000 18%, transparent 82%);
|
||||
opacity: 0.34;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.project-card:hover,
|
||||
.project-card:focus-visible {
|
||||
border-color: var(--accent);
|
||||
box-shadow: var(--shadow);
|
||||
transform: translateY(-7px) scale(1.01);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.project-card:hover::before,
|
||||
.project-card:focus-visible::before {
|
||||
transform: translateX(34%);
|
||||
}
|
||||
|
||||
.project-card:focus-visible {
|
||||
box-shadow: 0 0 0 4px rgba(31, 154, 138, 0.2), var(--shadow);
|
||||
}
|
||||
|
||||
.card-top,
|
||||
.card-bottom {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.icon-wrap {
|
||||
display: grid;
|
||||
width: 52px;
|
||||
height: 52px;
|
||||
place-items: center;
|
||||
margin-bottom: 22px;
|
||||
border-radius: 8px;
|
||||
color: #ffffff;
|
||||
background:
|
||||
linear-gradient(145deg, var(--accent), rgba(255, 255, 255, 0.42)),
|
||||
var(--accent);
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.7), 0 12px 26px var(--accent-glow);
|
||||
animation: icon-hover 4.6s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.icon-wrap svg {
|
||||
width: 29px;
|
||||
height: 29px;
|
||||
stroke: currentColor;
|
||||
stroke-width: 2;
|
||||
fill: none;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin: 0;
|
||||
font-size: 1.35rem;
|
||||
line-height: 1.28;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.meta {
|
||||
margin: 10px 0 0;
|
||||
color: var(--muted);
|
||||
font-size: 0.93rem;
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
.card-bottom {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 14px;
|
||||
margin-top: 28px;
|
||||
color: var(--accent);
|
||||
font-weight: 700;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.arrow {
|
||||
display: grid;
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
flex: 0 0 auto;
|
||||
place-items: center;
|
||||
border-radius: 50%;
|
||||
color: #ffffff;
|
||||
background: linear-gradient(135deg, var(--accent), rgba(255, 255, 255, 0.46));
|
||||
box-shadow: 0 10px 20px var(--accent-glow);
|
||||
transition: transform 180ms ease;
|
||||
}
|
||||
|
||||
.project-card:hover .arrow,
|
||||
.project-card:focus-visible .arrow {
|
||||
transform: translateX(3px);
|
||||
}
|
||||
|
||||
.arrow svg {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
stroke: currentColor;
|
||||
stroke-width: 2.4;
|
||||
fill: none;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
|
||||
.spbu {
|
||||
--accent: var(--green);
|
||||
--accent-soft: rgba(20, 125, 100, 0.13);
|
||||
--accent-glow: rgba(15, 140, 124, 0.28);
|
||||
}
|
||||
|
||||
.jalan {
|
||||
--accent: var(--blue);
|
||||
--accent-soft: rgba(49, 95, 155, 0.13);
|
||||
--accent-glow: rgba(62, 111, 182, 0.28);
|
||||
}
|
||||
|
||||
.choro {
|
||||
--accent: var(--teal);
|
||||
--accent-soft: rgba(31, 154, 138, 0.14);
|
||||
--accent-glow: rgba(17, 158, 179, 0.28);
|
||||
}
|
||||
|
||||
.upgrade {
|
||||
--accent: var(--amber);
|
||||
--accent-soft: rgba(199, 119, 29, 0.15);
|
||||
--accent-glow: rgba(201, 141, 40, 0.3);
|
||||
}
|
||||
|
||||
.kemiskinan {
|
||||
--accent: var(--rose);
|
||||
--accent-soft: rgba(184, 69, 90, 0.13);
|
||||
--accent-glow: rgba(185, 84, 115, 0.28);
|
||||
}
|
||||
|
||||
.project-card:nth-child(1) {
|
||||
animation-delay: 80ms;
|
||||
}
|
||||
|
||||
.project-card:nth-child(2) {
|
||||
animation-delay: 160ms;
|
||||
}
|
||||
|
||||
.project-card:nth-child(3) {
|
||||
animation-delay: 240ms;
|
||||
}
|
||||
|
||||
.project-card:nth-child(4) {
|
||||
animation-delay: 320ms;
|
||||
}
|
||||
|
||||
.project-card:nth-child(5) {
|
||||
animation-delay: 400ms;
|
||||
}
|
||||
|
||||
footer {
|
||||
margin-top: 28px;
|
||||
color: var(--muted);
|
||||
font-size: 0.9rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
footer a {
|
||||
color: #0e7f88;
|
||||
font-weight: 700;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
footer a:hover,
|
||||
footer a:focus-visible {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
@keyframes page-rise {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(16px);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes card-enter {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(22px) scale(0.98);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0) scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rotate-halo {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes drift-lines {
|
||||
from {
|
||||
transform: translate3d(-14px, -8px, 0);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translate3d(14px, 8px, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes float-orbit {
|
||||
0%,
|
||||
100% {
|
||||
transform: translateY(0) rotate(0deg);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: translateY(18px) rotate(4deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes pulse-dot {
|
||||
0%,
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: scale(1.18);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes pulse-pin {
|
||||
0%,
|
||||
100% {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: scale(1.18);
|
||||
opacity: 0.78;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes icon-hover {
|
||||
0%,
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: translateY(-4px);
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
animation-duration: 1ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
scroll-behavior: auto !important;
|
||||
transition-duration: 1ms !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
header {
|
||||
align-items: stretch;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.status-panel {
|
||||
width: min(100%, 280px);
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.project-grid {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 620px) {
|
||||
.page {
|
||||
width: min(100% - 28px, 1120px);
|
||||
padding: 34px 0 30px;
|
||||
}
|
||||
|
||||
header {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.description {
|
||||
font-size: 0.98rem;
|
||||
}
|
||||
|
||||
.project-grid {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.project-card {
|
||||
min-height: 190px;
|
||||
padding: 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="map-orbit" aria-hidden="true">
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
</div>
|
||||
<main class="page">
|
||||
<header>
|
||||
<div class="title-block">
|
||||
<div class="eyebrow">Katalog aplikasi peta</div>
|
||||
<h1>Portal WebGIS Terpadu</h1>
|
||||
<p class="description">Pilih salah satu aplikasi WebGIS untuk membuka peta interaktif.</p>
|
||||
</div>
|
||||
<div class="status-panel" aria-label="Gambar peta Pontianak">
|
||||
<div class="pontianak-map">
|
||||
<svg viewBox="0 0 240 150" role="img" aria-label="Peta Pontianak">
|
||||
<defs>
|
||||
<linearGradient id="mapSilver" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0" stop-color="#ffffff"></stop>
|
||||
<stop offset="0.52" stop-color="#d8e4ec"></stop>
|
||||
<stop offset="1" stop-color="#f7fbfd"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient id="mapCyan" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0" stop-color="#7bd7df"></stop>
|
||||
<stop offset="1" stop-color="#0f8c9c"></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<path class="map-region" fill="url(#mapSilver)" d="M93 15 133 9 164 27 156 58 128 69 99 58 76 35Z"></path>
|
||||
<path class="map-region" fill="#c7d6e1" d="M43 46 76 35 99 58 91 91 55 96 31 73Z"></path>
|
||||
<path class="map-region" fill="#eef4f8" d="M156 58 198 45 221 71 204 102 164 101 128 69Z"></path>
|
||||
<path class="map-region" fill="#d7e7ee" d="M55 96 91 91 117 118 91 141 51 132 28 105Z"></path>
|
||||
<path class="map-region" fill="#bfcfdc" d="M91 91 128 69 164 101 153 135 117 118Z"></path>
|
||||
<path class="map-region" fill="#f5fafc" d="M164 101 204 102 218 126 191 143 153 135Z"></path>
|
||||
<path class="map-river" d="M24 84 C62 78 83 70 108 82 S151 104 215 91"></path>
|
||||
<path class="map-river" d="M118 20 C112 51 115 78 130 132"></path>
|
||||
<g class="map-pin">
|
||||
<circle cx="124" cy="78" r="14" fill="rgba(17, 158, 179, 0.18)"></circle>
|
||||
<circle cx="124" cy="78" r="6" fill="url(#mapCyan)" stroke="#ffffff" stroke-width="3"></circle>
|
||||
</g>
|
||||
</svg>
|
||||
<div class="map-caption">
|
||||
<span>Peta Pontianak</span>
|
||||
<span>Preview GIS</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<section class="project-grid" aria-label="Daftar project WebGIS">
|
||||
<a class="project-card spbu" href="01/index.php">
|
||||
<div class="card-top">
|
||||
<div class="icon-wrap" aria-hidden="true">
|
||||
<svg viewBox="0 0 24 24">
|
||||
<path d="M4 21V5a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v16"></path>
|
||||
<path d="M8 7h4"></path>
|
||||
<path d="M8 11h4"></path>
|
||||
<path d="M16 8h2l2 2v8a2 2 0 0 1-4 0v-4"></path>
|
||||
<path d="M3 21h14"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<h2>Webgis SPBU Point</h2>
|
||||
<p class="meta">Pemetaan titik lokasi SPBU dengan informasi spasial.</p>
|
||||
</div>
|
||||
<div class="card-bottom">
|
||||
<span>Buka project</span>
|
||||
<span class="arrow" aria-hidden="true">
|
||||
<svg viewBox="0 0 24 24">
|
||||
<path d="M5 12h14"></path>
|
||||
<path d="m13 6 6 6-6 6"></path>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a class="project-card jalan" href="02/index.php">
|
||||
<div class="card-top">
|
||||
<div class="icon-wrap" aria-hidden="true">
|
||||
<svg viewBox="0 0 24 24">
|
||||
<path d="M4 19c4-5 4-9 8-14"></path>
|
||||
<path d="M12 19c2-5 4-9 8-14"></path>
|
||||
<path d="M7 14h4"></path>
|
||||
<path d="M12 9h4"></path>
|
||||
<path d="M3 21h18"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<h2>Webgis Jalan Parsil</h2>
|
||||
<p class="meta">Inventarisasi dan visualisasi data jalan berbasis peta.</p>
|
||||
</div>
|
||||
<div class="card-bottom">
|
||||
<span>Buka project</span>
|
||||
<span class="arrow" aria-hidden="true">
|
||||
<svg viewBox="0 0 24 24">
|
||||
<path d="M5 12h14"></path>
|
||||
<path d="m13 6 6 6-6 6"></path>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a class="project-card choro" href="03/choropleth.html">
|
||||
<div class="card-top">
|
||||
<div class="icon-wrap" aria-hidden="true">
|
||||
<svg viewBox="0 0 24 24">
|
||||
<path d="m3 6 6-3 6 3 6-3v15l-6 3-6-3-6 3Z"></path>
|
||||
<path d="M9 3v15"></path>
|
||||
<path d="M15 6v15"></path>
|
||||
<path d="M6 12h3"></path>
|
||||
<path d="M15 12h3"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<h2>Webgis Choropleth</h2>
|
||||
<p class="meta">Peta tematik untuk membaca pola wilayah secara visual.</p>
|
||||
</div>
|
||||
<div class="card-bottom">
|
||||
<span>Buka project</span>
|
||||
<span class="arrow" aria-hidden="true">
|
||||
<svg viewBox="0 0 24 24">
|
||||
<path d="M5 12h14"></path>
|
||||
<path d="m13 6 6 6-6 6"></path>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a class="project-card upgrade" href="04/index.php">
|
||||
<div class="card-top">
|
||||
<div class="icon-wrap" aria-hidden="true">
|
||||
<svg viewBox="0 0 24 24">
|
||||
<path d="M12 21s7-5.2 7-11a7 7 0 0 0-14 0c0 5.8 7 11 7 11Z"></path>
|
||||
<path d="M12 13a3 3 0 1 0 0-6 3 3 0 0 0 0 6Z"></path>
|
||||
<path d="M12 16v-3"></path>
|
||||
<path d="m9.8 8.8 2.2-2.2 2.2 2.2"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<h2>Webgis SPBU 24 Ja Upgrade</h2>
|
||||
<p class="meta">Versi pengembangan untuk pengelolaan titik SPBU.</p>
|
||||
</div>
|
||||
<div class="card-bottom">
|
||||
<span>Buka project</span>
|
||||
<span class="arrow" aria-hidden="true">
|
||||
<svg viewBox="0 0 24 24">
|
||||
<path d="M5 12h14"></path>
|
||||
<path d="m13 6 6 6-6 6"></path>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a class="project-card kemiskinan" href="05/login.php">
|
||||
<div class="card-top">
|
||||
<div class="icon-wrap" aria-hidden="true">
|
||||
<svg viewBox="0 0 24 24">
|
||||
<path d="M4 19V5"></path>
|
||||
<path d="M4 19h17"></path>
|
||||
<path d="M8 16v-5"></path>
|
||||
<path d="M13 16V8"></path>
|
||||
<path d="M18 16v-9"></path>
|
||||
<path d="M7 7h12"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<h2>Webgis Kemiskinan</h2>
|
||||
<p class="meta">Data wilayah dan indikator sosial dalam tampilan spasial.</p>
|
||||
</div>
|
||||
<div class="card-bottom">
|
||||
<span>Buka project</span>
|
||||
<span class="arrow" aria-hidden="true">
|
||||
<svg viewBox="0 0 24 24">
|
||||
<path d="M5 12h14"></path>
|
||||
<path d="m13 6 6 6-6 6"></path>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
</section>
|
||||
|
||||
<footer>
|
||||
Portal utama untuk memilih aplikasi WebGIS pada folder project gabungan. <a href="manual-book.html">Manual book</a>
|
||||
</footer>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,157 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="id">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Manual Book WebGIS UAS</title>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
color: #16212d;
|
||||
background: #f6f9fb;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
main {
|
||||
width: min(920px, calc(100% - 32px));
|
||||
margin: 0 auto;
|
||||
padding: 42px 0;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2 {
|
||||
line-height: 1.2;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 0 0 10px;
|
||||
font-size: clamp(2rem, 4vw, 3.2rem);
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-top: 32px;
|
||||
font-size: 1.35rem;
|
||||
}
|
||||
|
||||
p {
|
||||
color: #556575;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #0e7f88;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.panel {
|
||||
margin-top: 22px;
|
||||
padding: 22px;
|
||||
border: 1px solid rgba(154, 170, 185, 0.36);
|
||||
border-radius: 8px;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 16px 34px rgba(42, 58, 74, 0.08);
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-top: 14px;
|
||||
overflow: hidden;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
th,
|
||||
td {
|
||||
padding: 12px;
|
||||
border-bottom: 1px solid #e2e9ee;
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
th {
|
||||
background: #edf4f7;
|
||||
}
|
||||
|
||||
code {
|
||||
padding: 2px 5px;
|
||||
border-radius: 5px;
|
||||
background: #edf4f7;
|
||||
color: #243746;
|
||||
}
|
||||
|
||||
ul {
|
||||
padding-left: 20px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<a href="index.html">Kembali ke Portal WebGIS Terpadu</a>
|
||||
<h1>Manual Book WebGIS UAS</h1>
|
||||
<p>Dokumen ringkas ini menjelaskan struktur project, daftar aplikasi, dan konfigurasi environment database untuk deployment Docker atau Coolify.</p>
|
||||
|
||||
<section class="panel">
|
||||
<h2>Struktur Folder</h2>
|
||||
<p>Struktur root project disiapkan seperti berikut:</p>
|
||||
<ul>
|
||||
<li><code>index.html</code> sebagai landing page utama.</li>
|
||||
<li><code>manual-book.html</code> sebagai dokumentasi singkat.</li>
|
||||
<li><code>01</code> sampai <code>05</code> sebagai folder aplikasi WebGIS.</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="panel">
|
||||
<h2>Daftar Aplikasi</h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Folder</th>
|
||||
<th>Nama Aplikasi</th>
|
||||
<th>Entry Point</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>01</code></td>
|
||||
<td>Webgis SPBU Point</td>
|
||||
<td><code>index.php</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>02</code></td>
|
||||
<td>Webgis Jalan Parsil</td>
|
||||
<td><code>index.php</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>03</code></td>
|
||||
<td>Webgis Choropleth</td>
|
||||
<td><code>choropleth.html</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>04</code></td>
|
||||
<td>Webgis SPBU 24 Ja Upgrade</td>
|
||||
<td><code>index.php</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>05</code></td>
|
||||
<td>Webgis Kemiskinan</td>
|
||||
<td><code>login.php</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<section class="panel">
|
||||
<h2>Environment Database</h2>
|
||||
<p>Setiap aplikasi PHP memakai environment variable berikut untuk koneksi database:</p>
|
||||
<ul>
|
||||
<li><code>DB_HOST</code></li>
|
||||
<li><code>DB_USER</code></li>
|
||||
<li><code>DB_PASSWORD</code></li>
|
||||
<li><code>DB_NAME</code></li>
|
||||
</ul>
|
||||
<p>Jika database dipisah sebagai resource Coolify, isi <code>DB_HOST</code> dengan hostname service database, bukan <code>localhost</code>.</p>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user