Files
UAS_SIG/build.js
T
2026-06-08 21:16:23 +07:00

407 lines
20 KiB
JavaScript

const fs = require('fs');
const admin = fs.readFileSync('admin.html', 'utf8');
// Extract CSS
const cssMatch = admin.match(/<style>([\s\S]*?)<\/style>/);
const css = cssMatch ? cssMatch[1] : '';
// Extract Sidebar
const sidebarMatch = admin.match(/<aside class="sidebar">([\s\S]*?)<\/aside>/);
let sidebarBase = sidebarMatch ? sidebarMatch[0] : '';
// Function to build SPBU
function buildSPBU() {
let sidebar = sidebarBase;
sidebar = sidebar.replace(/<div class="sidebar-section-title">Data<\/div>[\s\S]*?<div class="sidebar-section">/, '<div class="sidebar-section">');
sidebar = sidebar.replace(/<div class="sidebar-section-title">Tools<\/div>[\s\S]*?<div class="sidebar-section">/, '<div class="sidebar-section">');
sidebar = sidebar.replace(/<div class="sidebar-item" data-panel="panel-dashboard">[\s\S]*?<\/div>/g, '');
sidebar = sidebar.replace(/<div class="sidebar-section-title">Settings<\/div>[\s\S]*?<\/nav>/, '<div class="sidebar-section-title">Account</div><div class="sidebar-menu"><a href="login.html" class="sidebar-item" style="text-decoration:none;"><div class="sidebar-item-icon">⎋</div><div class="sidebar-item-text">Login Admin</div></a></div></nav>');
const html = `<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Peta SPBU Publik</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
<style>
${css}
.controls-panel { position: absolute; top: 80px; right: 20px; background: white; padding: 16px; border-radius: 8px; box-shadow: 0 4px 15px rgba(0,0,0,0.2); z-index: 1000; width: 280px; color:#333; }
.controls-panel h3 { font-size: 14px; margin-bottom: 12px; color: #1e293b; }
.btn-action { display: block; width: 100%; padding: 10px; background: #3b82f6; color: white; border: none; border-radius: 6px; font-weight: 600; cursor: pointer; transition: 0.2s; margin-bottom: 10px; }
.btn-action:hover { background: #2563eb; }
.btn-action:disabled { background: #94a3b8; cursor: not-allowed; }
.info-box { background: #f1f5f9; padding: 12px; border-radius: 6px; font-size: 13px; color: #334155; margin-top: 10px; display: none; }
.info-box.active { display: block; }
.search-box2 { width: 100%; padding: 10px; margin-bottom: 15px; border: 1px solid #cbd5e1; border-radius: 6px; font-size: 13px; outline: none; }
.search-box2:focus { border-color: #3b82f6; }
.legend-panel { position: absolute; bottom: 30px; right: 20px; background: white; padding: 16px; border-radius: 8px; box-shadow: 0 4px 15px rgba(0,0,0,0.2); z-index: 1000; width: 200px; font-size: 13px; color: #334155; }
.legend-panel h3 { font-size: 14px; margin-bottom: 12px; color: #1e293b; }
.legend-item2 { display: flex; align-items: center; margin-bottom: 8px; }
.legend-icon2 { width: 16px; height: 16px; border-radius: 50%; margin-right: 10px; display: inline-block; }
.bg-blue { background: #3b82f6; }
.bg-red { background: #ef4444; }
</style>
</head>
<body>
<div class="app-wrapper">
${sidebar}
<div class="main-wrapper">
<header>
<div class="header-title">
<h1 id="page-title">Peta Persebaran SPBU</h1>
<div class="header-breadcrumb">
<span id="breadcrumb-text">Public Map</span>
</div>
</div>
</header>
<div class="content-container">
<div class="content-panel active" style="position: relative;">
<div id="map" style="width:100%; height:100%; z-index:1;"></div>
<div class="controls-panel">
<h3>Fitur SPBU Terdekat</h3>
<input type="text" id="search-spbu" class="search-box2" placeholder="Cari nama SPBU...">
<button id="btn-location" class="btn-action">Dapatkan Lokasi Saya</button>
<button id="btn-nearest" class="btn-action" disabled>Cari SPBU Terdekat</button>
<div id="info-box" class="info-box">
<strong>SPBU Terdekat:</strong> <span id="nearest-name">-</span><br>
<strong>Jarak:</strong> <span id="nearest-distance">-</span> km
</div>
</div>
<div class="legend-panel">
<h3>Legenda</h3>
<div class="legend-item2">
<span class="legend-icon2 bg-blue"></span> SPBU
</div>
<div class="legend-item2">
<span class="legend-icon2 bg-red"></span> Lokasi Anda
</div>
</div>
</div>
</div>
</div>
</div>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@turf/turf@6/turf.min.js"></script>
<script>
// Sidebar toggle
document.getElementById('sidebar-toggle').addEventListener('click', () => {
document.querySelector('.sidebar').classList.toggle('collapsed');
});
// Init Map
const map = L.map('map').setView([-0.026, 109.342], 13);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
}).addTo(map);
let spbuData = [];
let spbuLayer = L.layerGroup().addTo(map);
let userMarker = null;
let routeLine = null;
let userLocation = null;
const spbuIcon = L.icon({
iconUrl: 'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-blue.png',
shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/images/marker-shadow.png',
iconSize: [25, 41], iconAnchor: [12, 41], popupAnchor: [1, -34], shadowSize: [41, 41]
});
const userIcon = L.icon({
iconUrl: 'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-red.png',
shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/images/marker-shadow.png',
iconSize: [25, 41], iconAnchor: [12, 41], popupAnchor: [1, -34], shadowSize: [41, 41]
});
async function loadSPBU() {
try {
const res = await fetch('backend/read.php');
const result = await res.json();
if (result.status === 'success' && result.data) {
spbuData = result.data;
renderSPBU();
}
} catch (err) { console.error("Gagal memuat SPBU:", err); }
}
function renderSPBU(filterText = '') {
spbuLayer.clearLayers();
spbuData.forEach(spbu => {
if (filterText && !spbu.nama.toLowerCase().includes(filterText.toLowerCase())) return;
const lat = parseFloat(spbu.latitude);
const lng = parseFloat(spbu.longitude);
if (!isNaN(lat) && !isNaN(lng)) {
L.marker([lat, lng], {icon: spbuIcon})
.bindPopup('<strong>' + spbu.nama + '</strong><br>No: ' + spbu.no_spbu + '<br>Status: ' + spbu.status)
.addTo(spbuLayer);
}
});
}
document.getElementById('search-spbu').addEventListener('input', (e) => {
renderSPBU(e.target.value);
});
document.getElementById('btn-location').addEventListener('click', () => {
if (!navigator.geolocation) return alert("Geolocation tidak didukung");
const btn = document.getElementById('btn-location');
btn.textContent = "Mencari...";
navigator.geolocation.getCurrentPosition((pos) => {
const lat = pos.coords.latitude;
const lng = pos.coords.longitude;
userLocation = [lng, lat];
if (userMarker) map.removeLayer(userMarker);
userMarker = L.marker([lat, lng], {icon: userIcon}).bindPopup("Lokasi Anda").addTo(map);
map.flyTo([lat, lng], 14);
btn.textContent = "Lokasi Diperbarui";
document.getElementById('btn-nearest').disabled = false;
}, (err) => {
alert("Gagal: " + err.message);
btn.textContent = "Dapatkan Lokasi Saya";
});
});
document.getElementById('btn-nearest').addEventListener('click', () => {
if (!userLocation || spbuData.length === 0) return;
let nearestSPBU = null, shortestDistance = Infinity;
const userPoint = turf.point(userLocation);
spbuData.forEach(spbu => {
const lat = parseFloat(spbu.latitude);
const lng = parseFloat(spbu.longitude);
if (isNaN(lat) || isNaN(lng)) return;
const dist = turf.distance(userPoint, turf.point([lng, lat]), {units: 'kilometers'});
if (dist < shortestDistance) { shortestDistance = dist; nearestSPBU = spbu; }
});
if (nearestSPBU) {
if (routeLine) map.removeLayer(routeLine);
const lat = parseFloat(nearestSPBU.latitude);
const lng = parseFloat(nearestSPBU.longitude);
routeLine = L.polyline([[userLocation[1], userLocation[0]], [lat, lng]], { color: '#ef4444', weight: 4, dashArray: '10, 10' }).addTo(map);
map.fitBounds(routeLine.getBounds(), { padding: [50, 50] });
document.getElementById('nearest-name').textContent = nearestSPBU.nama;
document.getElementById('nearest-distance').textContent = shortestDistance.toFixed(2);
document.getElementById('info-box').classList.add('active');
}
});
loadSPBU();
</script>
</body>
</html>`;
fs.writeFileSync('spbu.html', html);
console.log("spbu.html updated");
}
// Function to build Poverty
function buildPoverty() {
let sidebar = sidebarBase;
sidebar = sidebar.replace(/<div class="sidebar-section-title">Data<\/div>[\s\S]*?<div class="sidebar-section">/, '<div class="sidebar-section">');
sidebar = sidebar.replace(/<div class="sidebar-section-title">Tools<\/div>[\s\S]*?<div class="sidebar-section">/, '<div class="sidebar-section">');
sidebar = sidebar.replace(/<div class="sidebar-item" data-panel="panel-dashboard">[\s\S]*?<\/div>/g, '');
sidebar = sidebar.replace(/<div class="sidebar-section-title">Settings<\/div>[\s\S]*?<\/nav>/, '<div class="sidebar-section-title">Account</div><div class="sidebar-menu"><a href="admin.html" class="sidebar-item" style="text-decoration:none;"><div class="sidebar-item-icon">⚙</div><div class="sidebar-item-text">Dashboard Admin</div></a><div class="sidebar-item" id="logout-btn"><div class="sidebar-item-icon">⎋</div><div class="sidebar-item-text">Logout</div></div></div></nav>');
const html = `<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Peta Kemiskinan & Rumah Ibadah</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
<style>
${css}
.controls-panel { position: absolute; top: 80px; right: 20px; background: white; padding: 16px; border-radius: 8px; box-shadow: 0 4px 15px rgba(0,0,0,0.2); z-index: 1000; width: 280px; color:#333; }
.controls-panel h3 { font-size: 14px; margin-bottom: 12px; color: #1e293b; }
.search-box2 { width: 100%; padding: 10px; margin-bottom: 10px; border: 1px solid #cbd5e1; border-radius: 6px; font-size: 13px; outline: none; }
.search-box2:focus { border-color: #3b82f6; }
.popup-slider { width: 100%; margin-top: 10px; }
.popup-radius-label { font-size: 12px; font-weight: 600; color: #334155; display: block; margin-top: 10px; }
.legend-panel { position: absolute; bottom: 30px; right: 20px; background: white; padding: 16px; border-radius: 8px; box-shadow: 0 4px 15px rgba(0,0,0,0.2); z-index: 1000; width: 250px; font-size: 13px; color: #334155; }
.legend-panel h3 { font-size: 14px; margin-bottom: 12px; color: #1e293b; }
.legend-item2 { display: flex; align-items: center; margin-bottom: 8px; }
.legend-icon2 { width: 16px; height: 16px; border-radius: 50%; margin-right: 10px; display: inline-block; }
.bg-black { background: #1e293b; }
.bg-red { background: #ef4444; }
.bg-green { background: #22c55e; }
#loader { position: fixed; top:0; left:0; width:100%; height:100%; background: #0f172a; color: white; display: flex; justify-content: center; align-items: center; z-index: 9999; font-size: 18px; }
</style>
</head>
<body>
<div id="loader">Verifikasi Akses...</div>
<div class="app-wrapper">
${sidebar}
<div class="main-wrapper">
<header>
<div class="header-title">
<h1 id="page-title">Peta Analisis Kemiskinan</h1>
<div class="header-breadcrumb">
<span id="breadcrumb-text" style="color:#fff; font-weight:normal;"></span>
</div>
</div>
</header>
<div class="content-container">
<div class="content-panel active" style="position: relative;">
<div id="map" style="width:100%; height:100%; z-index:1;"></div>
<div class="controls-panel">
<h3>Pencarian Data</h3>
<input type="text" id="search-ibadah" class="search-box2" placeholder="Cari Rumah Ibadah...">
<input type="text" id="search-miskin" class="search-box2" placeholder="Cari Penduduk Miskin (Nama KK)...">
</div>
<div class="legend-panel">
<h3>Legenda</h3>
<div class="legend-item2">
<span class="legend-icon2 bg-green"></span> Rumah Ibadah
</div>
<div class="legend-item2">
<span class="legend-icon2 bg-red"></span> Penduduk Miskin (Dalam Radius)
</div>
<div class="legend-item2">
<span class="legend-icon2 bg-black"></span> Penduduk Miskin (Luar Radius)
</div>
</div>
</div>
</div>
</div>
</div>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@turf/turf@6/turf.min.js"></script>
<script>
// Sidebar toggle
document.getElementById('sidebar-toggle').addEventListener('click', () => {
document.querySelector('.sidebar').classList.toggle('collapsed');
});
async function checkAuth() {
try {
const res = await fetch('backend/check_auth.php');
if (!res.ok) { window.location.href = 'login.html?redirect=poverty.html'; return; }
const data = await res.json();
document.getElementById('breadcrumb-text').textContent = 'Halo, ' + data.username;
document.getElementById('loader').style.display = 'none';
initMap();
} catch (err) { window.location.href = 'login.html?redirect=poverty.html'; }
}
document.getElementById('logout-btn').addEventListener('click', async () => {
await fetch('backend/logout.php');
window.location.href = 'index.html';
});
// Global variables
let ibadahData = []; let miskinData = [];
let ibadahLayer; let miskinLayer; let map;
let ibadahRadiuses = {}; let ibadahPoints = [];
let ibadahIcon, miskinRedIcon, miskinBlackIcon;
async function initMap() {
map = L.map('map').setView([-0.026, 109.342], 13);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '© OpenStreetMap contributors' }).addTo(map);
ibadahLayer = L.layerGroup().addTo(map);
miskinLayer = L.layerGroup().addTo(map);
ibadahIcon = L.icon({ iconUrl: 'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-green.png', shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/images/marker-shadow.png', iconSize: [25, 41], iconAnchor: [12, 41], popupAnchor: [1, -34], shadowSize: [41, 41] });
miskinRedIcon = L.icon({ iconUrl: 'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-red.png', shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/images/marker-shadow.png', iconSize: [25, 41], iconAnchor: [12, 41], popupAnchor: [1, -34], shadowSize: [41, 41] });
miskinBlackIcon = L.icon({ iconUrl: 'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-black.png', shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/images/marker-shadow.png', iconSize: [25, 41], iconAnchor: [12, 41], popupAnchor: [1, -34], shadowSize: [41, 41] });
try {
const [ibadahRes, miskinRes] = await Promise.all([ fetch('backend/read_ibadah.php'), fetch('backend/read_miskin.php') ]);
ibadahData = (await ibadahRes.json()).data || [];
miskinData = (await miskinRes.json()).data || [];
ibadahData.forEach(item => { ibadahRadiuses[item.id] = 0; });
renderMap();
} catch (err) { console.error("Error loading data"); }
}
window.updateRadius = function(id, value, lat, lng) {
ibadahRadiuses[id] = parseInt(value);
document.getElementById('radius-val-'+id).textContent = value;
renderBuffers();
renderMiskin();
};
function renderBuffers() {
map.eachLayer((layer) => { if (layer.options && layer.options.isBuffer) { map.removeLayer(layer); } });
ibadahData.forEach(item => {
const radius = ibadahRadiuses[item.id];
if (radius > 0) {
const lat = parseFloat(item.latitude), lng = parseFloat(item.longitude);
if (!isNaN(lat) && !isNaN(lng)) {
L.circle([lat, lng], { radius: radius, color: '#ef4444', fillColor: '#ef4444', fillOpacity: 0.1, weight: 2, isBuffer: true }).addTo(map);
}
}
});
}
function renderMap() { renderIbadah(); renderBuffers(); renderMiskin(); }
function renderIbadah() {
ibadahLayer.clearLayers(); ibadahPoints = [];
const filterIbadah = document.getElementById('search-ibadah').value.toLowerCase();
ibadahData.forEach(item => {
if (filterIbadah && !item.nama.toLowerCase().includes(filterIbadah)) return;
const lat = parseFloat(item.latitude), lng = parseFloat(item.longitude);
if (!isNaN(lat) && !isNaN(lng)) {
ibadahPoints.push({ id: item.id, pt: turf.point([lng, lat]) });
const popupContent = '<strong>' + item.nama + '</strong><br>' + item.alamat + '<br><span class="popup-radius-label">Atur Radius: <span id="radius-val-' + item.id + '">' + ibadahRadiuses[item.id] + '</span> m</span><input type="range" class="popup-slider" id="radius-slider-' + item.id + '" min="0" max="5000" step="100" value="' + ibadahRadiuses[item.id] + '" oninput="window.updateRadius(' + item.id + ', this.value, ' + lat + ', ' + lng + ')">';
L.marker([lat, lng], {icon: ibadahIcon}).bindPopup(popupContent).addTo(ibadahLayer);
}
});
}
function renderMiskin() {
miskinLayer.clearLayers();
const filterMiskin = document.getElementById('search-miskin').value.toLowerCase();
miskinData.forEach(item => {
if (filterMiskin && !item.nama_kk.toLowerCase().includes(filterMiskin)) return;
const lat = parseFloat(item.latitude), lng = parseFloat(item.longitude);
if (isNaN(lat) || isNaN(lng)) return;
const miskinPt = turf.point([lng, lat]);
let isWithinRadius = false;
for (const ibadahObj of ibadahPoints) {
const currentRadiusKm = ibadahRadiuses[ibadahObj.id] / 1000;
if (currentRadiusKm > 0) {
const distance = turf.distance(miskinPt, ibadahObj.pt, {units: 'kilometers'});
if (distance <= currentRadiusKm) { isWithinRadius = true; break; }
}
}
const iconToUse = isWithinRadius ? miskinRedIcon : miskinBlackIcon;
L.marker([lat, lng], {icon: iconToUse}).bindPopup('<strong>KK: ' + item.nama_kk + '</strong><br>' + (item.keterangan || '-') + '<br>Status: ' + (isWithinRadius ? 'Masuk Area Radius' : 'Luar Area Radius')).addTo(miskinLayer);
});
}
document.getElementById('search-ibadah').addEventListener('input', renderIbadah);
document.getElementById('search-miskin').addEventListener('input', renderMiskin);
checkAuth();
</script>
</body>
</html>`;
fs.writeFileSync('poverty.html', html);
console.log("poverty.html updated");
}
buildSPBU();
buildPoverty();