2nd commit: Pemisahan halaman autentifikasi dengan halaman utama, modifikasi ikon, dan pemisahakan fitur berdasarkan role
This commit is contained in:
@@ -1,117 +1,67 @@
|
|||||||
# 🗺️ WebGIS Pemetaan Kemiskinan dan Distribusi Bantuan (Kota Pontianak)
|
# 🗺️ WebGIS-Pontianak (Pemetaan Kemiskinan & Tata Ruang)
|
||||||
|
|
||||||
Sebuah Sistem Informasi Geografis (*Geographic Information System / GIS*) berbasis web yang dirancang dengan arsitektur *Single Page Application (SPA)* untuk:
|
Sebuah Sistem Informasi Geografis (*Geographic Information System / GIS*) komprehensif berbasis web yang dirancang untuk memetakan infrastruktur kota dan demografi sosial, dengan fokus utama pada pemetaan kemiskinan dan distribusi bantuan di Pontianak.
|
||||||
|
|
||||||
* Memetakan demografi kemiskinan
|
Sistem ini dikembangkan dengan arsitektur **Multi-Page Application** tersinkronisasi dan dilindungi oleh kontrol akses berbasis peran (*Role-Based Access Control / RBAC*) tingkat lanjut.
|
||||||
* Mengkalkulasi kelayakan bantuan secara otomatis
|
|
||||||
* Mengelola respons darurat musibah
|
|
||||||
* Memfasilitasi pendataan talenta untuk pemberdayaan warga
|
|
||||||
|
|
||||||
Sistem ini dibangun dengan kontrol otorisasi tingkat lanjut (*Role-Based Access Control / RBAC* & *Geofencing*) serta menggunakan algoritma *Sistem Pendukung Keputusan (SPK)* terintegrasi.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## ✨ Fitur Utama (Core Features)
|
## ✨ Fitur Utama (Core Features)
|
||||||
|
|
||||||
### 🔐 Gatekeeper Authentication
|
### 🔐 Keamanan & Autentikasi Berlapis
|
||||||
|
- **Halaman Login Terisolasi:** Autentikasi dipisah sepenuhnya dari halaman utama peta (`login.php`) untuk mencegah kebocoran *resource* ke klien yang tidak sah.
|
||||||
|
- **Proteksi Kata Sandi BCRYPT:** Standar keamanan industri, divalidasi minimal 8 karakter baik di sisi klien (*frontend*) maupun server (*backend*).
|
||||||
|
- **Role-Based Access Control (RBAC):** Pemisahan hak akses ketat antara *Admin* (hak penuh) dan *User* biasa (hanya-baca & lapor musibah).
|
||||||
|
- **Server-side Gatekeeper:** Validasi *Session* ketat berbasis PHP di setiap akses masuk API maupun perlindungan pengalihan instan (*instant redirection*) di halaman utama.
|
||||||
|
- **Stylish Notification:** Notifikasi *pop-up* responsif berbasis SweetAlert2 untuk menyambut identitas pengguna.
|
||||||
|
|
||||||
* Sistem layar kunci persisten
|
### 🗺️ Pemetaan Spasial Multi-Layer
|
||||||
* Enkripsi kata sandi menggunakan standar *BCRYPT*
|
- **Leaflet.js Interaktif:** Peta dinamis dengan *FontAwesome Icon* premium untuk visualisasi marker.
|
||||||
* Validasi sesi berbasis API (*Anti Client-Side Amnesia*)
|
- **Peta Aset Beragam:** Menampilkan visualisasi tumpang tindih untuk *Polyline* (Jaringan Jalan), *Polygon* (Parsil Tanah), dan *Marker* (SPBU & Rumah Ibadah).
|
||||||
|
- **Z-Index & Event Handling Terkelola:** Penempatan *interactive: false* pada radius wewenang rumah ibadah agar tidak menutupi klik (pointer events) dari poligon jalan dan tanah di bawahnya.
|
||||||
|
- **Reverse Geocoding:** Pelacakan koordinat GPS *real-time* terintegrasi Nominatim API.
|
||||||
|
|
||||||
### 🗺️ Pemetaan Spasial & Geofencing
|
### 📊 Algoritma SPK Kemiskinan & Kesejahteraan
|
||||||
|
Sistem penentuan status keluarga (Sangat Miskin, Miskin, Rentan) yang dikalkulasi secara terpusat berdasarkan parameter *Garis Kemiskinan* dinamis. Modul ini menyertakan *Smart Autofill* untuk merekomendasikan jumlah logistik.
|
||||||
|
|
||||||
* Visualisasi interaktif menggunakan *Leaflet.js*
|
### 🚨 Manajemen Bantuan & Krisis Darurat
|
||||||
* Penentuan radius wewenang operasional
|
- Pemantauan rumah tangga yang berada di dalam maupun luar radius cakupan wewenang rumah ibadah.
|
||||||
* *Reverse Geocoding* via *Nominatim API*
|
- Peringatan darurat musibah dengan efek kelap-kelip visual pada marker (*SOS Blink*).
|
||||||
|
- Pencatatan riwayat distribusi bantuan dengan hak visibilitas CRUD yang otomatis disembunyikan dari antarmuka User biasa demi mencegah manipulasi data.
|
||||||
### 📊 SPK Kemiskinan Otomatis
|
|
||||||
|
|
||||||
* Klasifikasi tingkat kemiskinan:
|
|
||||||
|
|
||||||
* Sangat Miskin
|
|
||||||
* Miskin
|
|
||||||
* Rentan
|
|
||||||
* Menggunakan:
|
|
||||||
|
|
||||||
* Garis Kemiskinan Regional
|
|
||||||
* *Proxy Means Testing (PMT)*
|
|
||||||
|
|
||||||
### 📦 Distribusi Bantuan Cerdas (Smart Autofill)
|
|
||||||
|
|
||||||
* Rekomendasi logistik berbasis status kemiskinan
|
|
||||||
* Perhitungan proporsional (*pro-rata*)
|
|
||||||
|
|
||||||
### 🚨 Manajemen Krisis (Visual Alert)
|
|
||||||
|
|
||||||
* Peringatan dini berbasis geospasial
|
|
||||||
* Animasi visual (kelap-kelip merah) untuk lokasi darurat
|
|
||||||
|
|
||||||
### 🔄 Resolusi Musibah Otomatis (Auto-Resolve)
|
|
||||||
|
|
||||||
* Menggunakan *Database Transactions*
|
|
||||||
* Status darurat otomatis selesai setelah bantuan disalurkan
|
|
||||||
|
|
||||||
### 🎯 Filter Pelatihan Algoritmik
|
|
||||||
|
|
||||||
* Seleksi otomatis kandidat pelatihan berdasarkan:
|
|
||||||
|
|
||||||
* Usia produktif
|
|
||||||
* Tingkat pendidikan
|
|
||||||
* Status pengangguran
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🛠️ Stack Teknologi (Tech Stack)
|
## 🛠️ Stack Teknologi (Tech Stack)
|
||||||
|
|
||||||
### Frontend
|
### Frontend
|
||||||
|
* **UI/UX:** HTML5, CSS3 Murni (Custom RBAC rules, Smooth Animations, Glassmorphism), SweetAlert2
|
||||||
* HTML5 & CSS3 (*Native*)
|
* **Peta Geospasial:** Leaflet.js
|
||||||
* JavaScript (*ES6+ / Vanilla JS*)
|
* **Logika Aplikasi:** Vanilla JavaScript (ES6+), Fetch API
|
||||||
* *Leaflet.js*
|
|
||||||
* *Fetch API*
|
|
||||||
|
|
||||||
### Backend
|
### Backend
|
||||||
|
* **Logika Server & Sesi:** PHP 8.x (*Stateful Session Management*)
|
||||||
* PHP 8.x (API & Session Handling)
|
* **Jembatan Database:** PDO (*PHP Data Objects*) dengan arsitektur *Prepared Statements*
|
||||||
* PDO (*PHP Data Objects*)
|
* **Database Relasional:** MySQL / MariaDB
|
||||||
* MySQL / MariaDB
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## ⚙️ Persyaratan Sistem (Prerequisites)
|
## 🚀 Panduan Instalasi Lokal
|
||||||
|
|
||||||
Untuk menjalankan proyek ini secara lokal:
|
|
||||||
|
|
||||||
* Web Server (*XAMPP / MAMP / Laragon*)
|
|
||||||
* PHP ≥ 7.4 (disarankan 8.x)
|
|
||||||
* MySQL / MariaDB
|
|
||||||
* Koneksi internet (untuk OpenStreetMap & Nominatim API)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🚀 Panduan Instalasi (Installation Guide)
|
|
||||||
|
|
||||||
### 1. Clone Repositori
|
|
||||||
|
|
||||||
|
### 1. Kloning Repositori
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/ChronosSilver/WebGis-SIG-UAS.git
|
git clone https://github.com/ChronosSilver/SIG-Poverty-Mapping.git
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. Konfigurasi Database
|
### 2. Persiapan Database
|
||||||
|
1. Buka *control panel* MySQL Anda (`http://localhost/phpmyadmin`)
|
||||||
* Buka: `http://localhost/phpmyadmin`
|
2. Buat database kosong baru dengan nama:
|
||||||
* Buat database:
|
```
|
||||||
|
webgis_pontianak
|
||||||
```
|
```
|
||||||
webgis_kemiskinan
|
3. Import file cadangan `.sql` yang disertakan di repositori ini ke dalam database tersebut.
|
||||||
```
|
|
||||||
* Import file `.sql` dari repositori
|
|
||||||
|
|
||||||
### 3. Konfigurasi Koneksi PHP
|
|
||||||
|
|
||||||
Edit file `koneksi.php`:
|
|
||||||
|
|
||||||
|
### 3. Konfigurasi Sistem
|
||||||
|
Sistem ini menggunakan parameter *default* localhost (contoh: XAMPP). Jika *password* MySQL Anda kosong, tidak ada yang perlu diedit. Sesuaikan pengaturan ini di dalam file `koneksi.php`:
|
||||||
```php
|
```php
|
||||||
$host = "localhost";
|
$host = "localhost";
|
||||||
$user = "root";
|
$user = "root";
|
||||||
@@ -119,52 +69,34 @@ $pass = "";
|
|||||||
$dbname = "webgis_pontianak";
|
$dbname = "webgis_pontianak";
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4. Jalankan Aplikasi
|
### 4. Eksekusi WebGIS
|
||||||
|
Akses direktori proyek melalui browser pilihan Anda:
|
||||||
Akses melalui browser:
|
|
||||||
|
|
||||||
```
|
```
|
||||||
http://localhost/004(fin)/
|
http://localhost/SIG1/webgis/Poverty_Mapping/
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🔒 Akses Pengujian (Dummy Credentials)
|
## 📂 Struktur Direktori Terbaru
|
||||||
|
|
||||||
Gunakan akun berikut:
|
|
||||||
|
|
||||||
```
|
```
|
||||||
Username: admin
|
📦 Poverty_Mapping
|
||||||
Password: admin
|
┣ 📂 components # Kerangka antarmuka modular (Sidebar, Modal Template)
|
||||||
```
|
┣ 📂 otak_auth # API gerbang keamanan & manajer sesi PHP
|
||||||
|
┣ 📂 otak_kemiskinan # Endpoint CRUD penduduk, bantuan & laporan musibah
|
||||||
> Anda juga dapat membuat akun baru melalui fitur registrasi.
|
┣ 📂 otak_jalan # Endpoint spasial penyimpan Polyline Jalan
|
||||||
|
┣ 📂 otak_tanah # Endpoint spasial penyimpan Polygon Parsil Tanah
|
||||||
---
|
┣ 📂 otak_spbu # Endpoint penampung data POI SPBU
|
||||||
|
┣ 📜 app.js # Otak logika SPA Frontend & Leaflet Rendering
|
||||||
## 📂 Struktur Direktori
|
┣ 📜 style.css # Desain tema global & Filter Visibilitas RBAC
|
||||||
|
┣ 📜 koneksi.php # Jembatan asinkron ke MySQL PDO
|
||||||
```
|
┣ 📜 login.php # Lobi isolasi layar kunci (Gatekeeper Entry)
|
||||||
📦 root-directory
|
┗ 📜 index.php # Kanvas Peta Utama WebGIS (Terproteksi Sesi)
|
||||||
┣ 📂 components # UI modular (Sidebar, Modal)
|
|
||||||
┣ 📂 otak_auth # API autentikasi
|
|
||||||
┣ 📂 otak_kemiskinan # CRUD & SPK warga
|
|
||||||
┣ 📂 otak_jalan # Data Polyline
|
|
||||||
┣ 📂 otak_tanah # Data Polygon
|
|
||||||
┣ 📂 otak_spbu # Data SPBU
|
|
||||||
┣ 📜 app.js # Logika utama sistem
|
|
||||||
┣ 📜 style.css # UI & animasi
|
|
||||||
┣ 📜 koneksi.php # Koneksi database
|
|
||||||
┗ 📜 index.php # Entry point
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📝 Lisensi
|
## 📝 Pengembang & Lisensi
|
||||||
|
Sistem Informasi Geografis (*SIG*) berbasis web ini merupakan purwarupa penelitian/tugas akhir yang secara kontinu berevolusi dari tahun ke tahun.
|
||||||
|
**Hak Cipta (2026) oleh Nelson Davey (NIM: D1041231058) - Universitas Tanjungpura**.
|
||||||
|
|
||||||
Proyek ini dikembangkan sebagai bagian dari penelitian akademik / tugas perkuliahan
|
Mendukung prinsip kode aman dengan akses terotentikasi berlapis. Segala bentuk salinan, distribusi, dan modifikasi harus mencantumkan sumber orisinalitas serta mengikuti kebijakan akademik yang berlaku.
|
||||||
Universitas Tanjungpura (2026) oleh Nelson Davey (D1041231058).
|
|
||||||
|
|
||||||
Segala bentuk distribusi dan modifikasi mengikuti kebijakan hak cipta yang berlaku.
|
|
||||||
|
|
||||||
---
|
|
||||||
@@ -22,7 +22,7 @@ const state = {
|
|||||||
|
|
||||||
// KUNCI AUTENTIKASI
|
// KUNCI AUTENTIKASI
|
||||||
user: null,
|
user: null,
|
||||||
authMode: 'login' // 'login' atau 'register'
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const dataStore = {
|
const dataStore = {
|
||||||
@@ -160,46 +160,6 @@ function triggerToastNotification(pesan) {
|
|||||||
// ====================================================
|
// ====================================================
|
||||||
// AUTENTIKASI: GATEKEEPER & SINKRONISASI SESI
|
// AUTENTIKASI: GATEKEEPER & SINKRONISASI SESI
|
||||||
// ====================================================
|
// ====================================================
|
||||||
window.switchAuthTab = (mode) => {
|
|
||||||
state.authMode = mode;
|
|
||||||
|
|
||||||
document.getElementById('tab-login').classList.toggle('active', mode === 'login');
|
|
||||||
document.getElementById('tab-register').classList.toggle('active', mode === 'register');
|
|
||||||
|
|
||||||
document.getElementById('btn-submit-auth').innerText = mode === 'login' ? 'Login Sistem' : 'Daftar Akun Baru';
|
|
||||||
document.getElementById('auth-error-msg').innerText = '';
|
|
||||||
};
|
|
||||||
|
|
||||||
window.eksekusiAuth = () => {
|
|
||||||
const user = document.getElementById('auth-username').value;
|
|
||||||
const pass = document.getElementById('auth-password').value;
|
|
||||||
const errMsg = document.getElementById('auth-error-msg');
|
|
||||||
|
|
||||||
if(!user || !pass) {
|
|
||||||
errMsg.innerText = "Username dan Password tidak boleh kosong!";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
errMsg.innerText = "Memproses request ke server...";
|
|
||||||
|
|
||||||
const endpoint = state.authMode === 'login' ? 'otak_auth/api_login.php' : 'otak_auth/api_register.php';
|
|
||||||
|
|
||||||
safeFetch(endpoint, { username: user, password: pass })
|
|
||||||
.then(res => {
|
|
||||||
if(state.authMode === 'register') {
|
|
||||||
triggerToastNotification(res.pesan);
|
|
||||||
switchAuthTab('login');
|
|
||||||
} else {
|
|
||||||
state.user = res.user;
|
|
||||||
document.getElementById('auth-screen').style.display = 'none';
|
|
||||||
triggerToastNotification(`Selamat datang, ${res.user}!`);
|
|
||||||
muatDataSemua(); // Booting peta dilanjutkan
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
errMsg.innerText = err.message;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
window.eksekusiLogout = () => {
|
window.eksekusiLogout = () => {
|
||||||
if(!confirm("Anda yakin ingin keluar dari sistem?")) return;
|
if(!confirm("Anda yakin ingin keluar dari sistem?")) return;
|
||||||
@@ -207,7 +167,7 @@ window.eksekusiLogout = () => {
|
|||||||
safeFetch('otak_auth/api_logout.php')
|
safeFetch('otak_auth/api_logout.php')
|
||||||
.then(res => {
|
.then(res => {
|
||||||
state.user = null;
|
state.user = null;
|
||||||
document.getElementById('auth-screen').style.display = 'flex';
|
window.location.href = 'login.php';
|
||||||
document.getElementById('auth-username').value = '';
|
document.getElementById('auth-username').value = '';
|
||||||
document.getElementById('auth-password').value = '';
|
document.getElementById('auth-password').value = '';
|
||||||
triggerToastNotification("Sesi diakhiri secara aman.");
|
triggerToastNotification("Sesi diakhiri secara aman.");
|
||||||
@@ -227,12 +187,30 @@ const cekSesiBackend = () => {
|
|||||||
safeFetch('otak_auth/api_session.php')
|
safeFetch('otak_auth/api_session.php')
|
||||||
.then(res => {
|
.then(res => {
|
||||||
state.user = res.username;
|
state.user = res.username;
|
||||||
document.getElementById('auth-screen').style.display = 'none';
|
window.USER_ROLE = res.role;
|
||||||
|
if(window.USER_ROLE !== 'admin') {
|
||||||
|
document.body.classList.add('role-user');
|
||||||
|
} else {
|
||||||
|
if(typeof RightToolbar !== 'undefined') map.addControl(new RightToolbar());
|
||||||
|
}
|
||||||
|
if (sessionStorage.getItem('just_logged_in') === 'true') {
|
||||||
|
Swal.fire({
|
||||||
|
title: 'Login Berhasil!',
|
||||||
|
html: `Selamat <b>${res.username}</b> masuk ke WebGIS-Pontianak sebagai <b>${res.role.toUpperCase()}</b>`,
|
||||||
|
icon: 'success',
|
||||||
|
timer: 3000,
|
||||||
|
showConfirmButton: false,
|
||||||
|
toast: true,
|
||||||
|
position: 'top-end'
|
||||||
|
});
|
||||||
|
sessionStorage.removeItem('just_logged_in');
|
||||||
|
}
|
||||||
|
|
||||||
muatDataSemua();
|
muatDataSemua();
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
console.log("Tertahan di gerbang autentikasi: " + err.message);
|
console.log("Tertahan di gerbang autentikasi: " + err.message);
|
||||||
document.getElementById('auth-screen').style.display = 'flex';
|
window.location.href = 'login.php';
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -440,15 +418,15 @@ const RightToolbar = L.Control.extend({
|
|||||||
onAdd: function() {
|
onAdd: function() {
|
||||||
const c = L.DomUtil.create('div', 'custom-leaflet-toolbar');
|
const c = L.DomUtil.create('div', 'custom-leaflet-toolbar');
|
||||||
c.innerHTML = `
|
c.innerHTML = `
|
||||||
<button class="tool-btn" id="btn-tool-marker" title="SPBU" onclick="toggleMarkerMode()">📍</button>
|
<button class="tool-btn" id="btn-tool-marker" title="SPBU" onclick="toggleMarkerMode()"><i class="fa-solid fa-gas-pump"></i></button>
|
||||||
<button class="tool-btn" id="btn-tool-jalan" title="Jalan" onclick="toggleRoadMode()">🛣️</button>
|
<button class="tool-btn" id="btn-tool-jalan" title="Jalan" onclick="toggleRoadMode()"><i class="fa-solid fa-road"></i></button>
|
||||||
<button class="tool-btn" id="btn-tool-tanah" title="Tanah" onclick="toggleLandMode()">🟩</button>
|
<button class="tool-btn" id="btn-tool-tanah" title="Tanah" onclick="toggleLandMode()"><i class="fa-solid fa-draw-polygon"></i></button>
|
||||||
`;
|
`;
|
||||||
L.DomEvent.disableClickPropagation(c);
|
L.DomEvent.disableClickPropagation(c);
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
map.addControl(new RightToolbar());
|
|
||||||
|
|
||||||
// ====================================================
|
// ====================================================
|
||||||
// EVENT LISTENER ROUTER (KLIK PETA UTAMA)
|
// EVENT LISTENER ROUTER (KLIK PETA UTAMA)
|
||||||
@@ -495,7 +473,7 @@ map.on('click', async function(e) {
|
|||||||
if(alamatInput) alamatInput.value = "Sedang melacak alamat dari satelit...";
|
if(alamatInput) alamatInput.value = "Sedang melacak alamat dari satelit...";
|
||||||
|
|
||||||
document.getElementById('spbu-action-wrapper').innerHTML = `
|
document.getElementById('spbu-action-wrapper').innerHTML = `
|
||||||
<button class="btn btn-save" id="btn-eksekusi-spbu" onclick="eksekusiSimpanBaru()" disabled>Tunggu Alamat...</button>
|
<button class="btn btn-save admin-only" id="btn-eksekusi-spbu" onclick="eksekusiSimpanBaru()" disabled>Tunggu Alamat...</button>
|
||||||
<button class="btn btn-cancel" onclick="document.getElementById('modal-spbu').style.display='none'">Batal</button>
|
<button class="btn btn-cancel" onclick="document.getElementById('modal-spbu').style.display='none'">Batal</button>
|
||||||
`;
|
`;
|
||||||
document.getElementById('modal-spbu').style.display = 'flex';
|
document.getElementById('modal-spbu').style.display = 'flex';
|
||||||
@@ -702,8 +680,8 @@ function renderSPBU() {
|
|||||||
|
|
||||||
document.getElementById('spbu-modal-title').innerText = "Detail & Kelola SPBU";
|
document.getElementById('spbu-modal-title').innerText = "Detail & Kelola SPBU";
|
||||||
document.getElementById('spbu-action-wrapper').innerHTML = `
|
document.getElementById('spbu-action-wrapper').innerHTML = `
|
||||||
<button class="btn btn-warning" onclick="eksekusiUpdate(${d.id})">Simpan Perubahan</button>
|
<button class="btn btn-warning admin-only" onclick="eksekusiUpdate(${d.id})">Simpan Perubahan</button>
|
||||||
<button class="btn btn-danger" onclick="eksekusiHapus(${d.id})">Hapus Marker</button>
|
<button class="btn btn-danger admin-only" onclick="eksekusiHapus(${d.id})">Hapus Marker</button>
|
||||||
<button class="btn btn-cancel" onclick="document.getElementById('modal-spbu').style.display='none'">Tutup</button>
|
<button class="btn btn-cancel" onclick="document.getElementById('modal-spbu').style.display='none'">Tutup</button>
|
||||||
`;
|
`;
|
||||||
document.getElementById('modal-spbu').style.display = 'flex';
|
document.getElementById('modal-spbu').style.display = 'flex';
|
||||||
@@ -904,7 +882,8 @@ function renderKemiskinan() {
|
|||||||
fillColor: '#D69E2E',
|
fillColor: '#D69E2E',
|
||||||
fillOpacity: 0.15,
|
fillOpacity: 0.15,
|
||||||
radius: d.radius_meter,
|
radius: d.radius_meter,
|
||||||
weight: 1
|
weight: 1,
|
||||||
|
interactive: false
|
||||||
}).addTo(map);
|
}).addTo(map);
|
||||||
|
|
||||||
mapLayers.radiusIbadah[d.id] = circle;
|
mapLayers.radiusIbadah[d.id] = circle;
|
||||||
@@ -1175,7 +1154,7 @@ window.selesaiGambarJalan = function() {
|
|||||||
bukaGembokForm('jalan');
|
bukaGembokForm('jalan');
|
||||||
|
|
||||||
document.getElementById('jalan-action-wrapper').innerHTML = `
|
document.getElementById('jalan-action-wrapper').innerHTML = `
|
||||||
<button class="btn btn-save" onclick="eksekusiSimpanJalan()">Simpan Jalan</button>
|
<button class="btn btn-save admin-only" onclick="eksekusiSimpanJalan()">Simpan Jalan</button>
|
||||||
<button class="btn btn-cancel" onclick="batalGambarJalan()">Batal</button>
|
<button class="btn btn-cancel" onclick="batalGambarJalan()">Batal</button>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -1211,8 +1190,8 @@ window.handleRoadClick = function(id) {
|
|||||||
|
|
||||||
document.getElementById('jalan-modal-title').innerText = "Detail & Kelola Jalan";
|
document.getElementById('jalan-modal-title').innerText = "Detail & Kelola Jalan";
|
||||||
document.getElementById('jalan-action-wrapper').innerHTML = `
|
document.getElementById('jalan-action-wrapper').innerHTML = `
|
||||||
<button class="btn btn-warning" onclick="eksekusiUpdateJalan(${id})">Simpan Perubahan</button>
|
<button class="btn btn-warning admin-only" onclick="eksekusiUpdateJalan(${id})">Simpan Perubahan</button>
|
||||||
<button class="btn btn-danger" onclick="eksekusiHapusJalan(${id})">Hapus Jalan</button>
|
<button class="btn btn-danger admin-only" onclick="eksekusiHapusJalan(${id})">Hapus Jalan</button>
|
||||||
<button class="btn btn-cancel" onclick="document.getElementById('modal-jalan').style.display='none'">Tutup</button>
|
<button class="btn btn-cancel" onclick="document.getElementById('modal-jalan').style.display='none'">Tutup</button>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -1241,7 +1220,7 @@ window.selesaiGambarTanah = function() {
|
|||||||
bukaGembokForm('tanah');
|
bukaGembokForm('tanah');
|
||||||
|
|
||||||
document.getElementById('tanah-action-wrapper').innerHTML = `
|
document.getElementById('tanah-action-wrapper').innerHTML = `
|
||||||
<button class="btn btn-save" onclick="eksekusiSimpanTanah()">Simpan Area</button>
|
<button class="btn btn-save admin-only" onclick="eksekusiSimpanTanah()">Simpan Area</button>
|
||||||
<button class="btn btn-cancel" onclick="batalGambarTanah()">Batal</button>
|
<button class="btn btn-cancel" onclick="batalGambarTanah()">Batal</button>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -1277,8 +1256,8 @@ window.handleLandClick = function(id) {
|
|||||||
|
|
||||||
document.getElementById('tanah-modal-title').innerText = "Detail & Kelola Tanah";
|
document.getElementById('tanah-modal-title').innerText = "Detail & Kelola Tanah";
|
||||||
document.getElementById('tanah-action-wrapper').innerHTML = `
|
document.getElementById('tanah-action-wrapper').innerHTML = `
|
||||||
<button class="btn btn-warning" onclick="eksekusiUpdateTanah(${id})">Simpan Perubahan</button>
|
<button class="btn btn-warning admin-only" onclick="eksekusiUpdateTanah(${id})">Simpan Perubahan</button>
|
||||||
<button class="btn btn-danger" onclick="eksekusiHapusTanah(${id})">Hapus Tanah</button>
|
<button class="btn btn-danger admin-only" onclick="eksekusiHapusTanah(${id})">Hapus Tanah</button>
|
||||||
<button class="btn btn-cancel" onclick="document.getElementById('modal-tanah').style.display='none'">Tutup</button>
|
<button class="btn btn-cancel" onclick="document.getElementById('modal-tanah').style.display='none'">Tutup</button>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -1825,7 +1804,7 @@ window.muatDaftarAnggota = (idPenduduk, isBernaung) => {
|
|||||||
pelatihanHtml = `
|
pelatihanHtml = `
|
||||||
<div style="display:flex; gap:4px; align-items:center;">
|
<div style="display:flex; gap:4px; align-items:center;">
|
||||||
<select id="sel-pel-${m.id}" style="padding:4px; max-width:120px; font-size:0.75rem; border-radius:4px;">${opsi}</select>
|
<select id="sel-pel-${m.id}" style="padding:4px; max-width:120px; font-size:0.75rem; border-radius:4px;">${opsi}</select>
|
||||||
<button class="btn btn-save" style="padding:4px 8px; font-size:0.75rem;" onclick="daftarPelatihanAnggota(${m.id})">Daftar</button>
|
<button class="btn btn-save admin-only" style="padding:4px 8px; font-size:0.75rem;" onclick="daftarPelatihanAnggota(${m.id})">Daftar</button>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
} else {
|
} else {
|
||||||
@@ -1841,8 +1820,8 @@ window.muatDaftarAnggota = (idPenduduk, isBernaung) => {
|
|||||||
<td>${m.pekerjaan || '<i style="color:#718096;">Menganggur</i>'}</td>
|
<td>${m.pekerjaan || '<i style="color:#718096;">Menganggur</i>'}</td>
|
||||||
<td>${pelatihanHtml}</td>
|
<td>${pelatihanHtml}</td>
|
||||||
<td>
|
<td>
|
||||||
<button class="btn btn-warning" style="padding:4px 8px; font-size:0.75rem; margin-right:4px;" onclick="bukaFormEditAnggota(${m.id}, '${m.nik}', '${m.nama_lengkap}', '${m.tanggal_lahir}', '${m.pendidikan_terakhir}', '${m.pekerjaan || ''}')">Edit</button>
|
<button class="btn btn-warning admin-only" style="padding:4px 8px; font-size:0.75rem; margin-right:4px;" onclick="bukaFormEditAnggota(${m.id}, '${m.nik}', '${m.nama_lengkap}', '${m.tanggal_lahir}', '${m.pendidikan_terakhir}', '${m.pekerjaan || ''}')">Edit</button>
|
||||||
<button class="btn btn-danger" style="padding:4px 8px; font-size:0.75rem;" onclick="eksekusiHapusAnggota(${m.id})">Hapus</button>
|
<button class="btn btn-danger admin-only" style="padding:4px 8px; font-size:0.75rem;" onclick="eksekusiHapusAnggota(${m.id})">Hapus</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
`;
|
`;
|
||||||
@@ -1866,7 +1845,7 @@ window.resetFormAnggota = () => {
|
|||||||
|
|
||||||
const btnContainer = document.getElementById('anggota-pekerjaan').parentElement.nextElementSibling;
|
const btnContainer = document.getElementById('anggota-pekerjaan').parentElement.nextElementSibling;
|
||||||
if(btnContainer) {
|
if(btnContainer) {
|
||||||
btnContainer.innerHTML = `<button class="btn btn-success" style="width:100%; padding: 10px;" onclick="eksekusiSimpanAnggota()">Tambah Anggota</button>`;
|
btnContainer.innerHTML = `<button class="btn btn-success admin-only" style="width:100%; padding: 10px;" onclick="eksekusiSimpanAnggota()">Tambah Anggota</button>`;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1883,7 +1862,7 @@ window.bukaFormEditAnggota = (id, nik, nama, lahir, pendidikan, pekerjaan) => {
|
|||||||
const btnContainer = document.getElementById('anggota-pekerjaan').parentElement.nextElementSibling;
|
const btnContainer = document.getElementById('anggota-pekerjaan').parentElement.nextElementSibling;
|
||||||
if(btnContainer) {
|
if(btnContainer) {
|
||||||
btnContainer.innerHTML = `
|
btnContainer.innerHTML = `
|
||||||
<button class="btn btn-warning" style="flex:1; padding: 10px;" onclick="eksekusiUpdateAnggota(${id})">Update Data</button>
|
<button class="btn btn-warning admin-only" style="flex:1; padding: 10px;" onclick="eksekusiUpdateAnggota(${id})">Update Data</button>
|
||||||
<button class="btn btn-cancel" style="padding: 10px;" onclick="resetFormAnggota()">Batal Edit</button>
|
<button class="btn btn-cancel" style="padding: 10px;" onclick="resetFormAnggota()">Batal Edit</button>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-31
@@ -389,7 +389,7 @@
|
|||||||
<hr class="pop-divider">
|
<hr class="pop-divider">
|
||||||
|
|
||||||
<div class="pop-action-main">
|
<div class="pop-action-main">
|
||||||
<button class="btn btn-success" id="btn-pd-bantuan" style="width:100%; padding: 12px; margin-bottom: 8px;">Catat Log Bantuan</button>
|
<button class="btn btn-success admin-only" id="btn-pd-bantuan" style="width:100%; padding: 12px; margin-bottom: 8px;">Catat Log Bantuan</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="accordion-wrapper">
|
<div class="accordion-wrapper">
|
||||||
@@ -401,8 +401,8 @@
|
|||||||
<button class="btn btn-success" id="btn-pd-riwayat-musibah" style="width:100%; margin-bottom:8px; padding: 10px;">Lihat Laporan Musibah</button>
|
<button class="btn btn-success" id="btn-pd-riwayat-musibah" style="width:100%; margin-bottom:8px; padding: 10px;">Lihat Laporan Musibah</button>
|
||||||
<button class="btn btn-success" id="btn-pd-riwayat-bantuan" style="width:100%; margin-bottom:8px; padding: 10px;">Lihat Log Bantuan</button>
|
<button class="btn btn-success" id="btn-pd-riwayat-bantuan" style="width:100%; margin-bottom:8px; padding: 10px;">Lihat Log Bantuan</button>
|
||||||
<div style="display:flex; gap:8px;">
|
<div style="display:flex; gap:8px;">
|
||||||
<button class="btn btn-warning" id="btn-pd-edit">Edit Data</button>
|
<button class="btn btn-warning admin-only" id="btn-pd-edit">Edit Data</button>
|
||||||
<button class="btn btn-danger" id="btn-pd-hapus">Hapus</button>
|
<button class="btn btn-danger admin-only" id="btn-pd-hapus">Hapus</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -424,8 +424,8 @@
|
|||||||
<hr class="pop-divider">
|
<hr class="pop-divider">
|
||||||
|
|
||||||
<div style="display:flex; gap:8px;">
|
<div style="display:flex; gap:8px;">
|
||||||
<button class="btn btn-warning" id="btn-ib-edit">Edit Data</button>
|
<button class="btn btn-warning admin-only" id="btn-ib-edit">Edit Data</button>
|
||||||
<button class="btn btn-danger" id="btn-ib-hapus">Hapus</button>
|
<button class="btn btn-danger admin-only" id="btn-ib-hapus">Hapus</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -600,8 +600,8 @@
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h4 style="border-bottom:1px solid var(--border); padding-bottom:5px; color: var(--navy);">Tambah Anggota Baru</h4>
|
<h4 class="admin-only" style="border-bottom:1px solid var(--border); padding-bottom:5px; color: var(--navy);">Tambah Anggota Baru</h4>
|
||||||
<div style="display:grid; grid-template-columns: 1fr 1fr 1fr; gap:10px;">
|
<div class="admin-only" style="display:grid; grid-template-columns: 1fr 1fr 1fr; gap:10px;">
|
||||||
<div class="form-group"><label>NIK</label><input type="text" id="anggota-nik" placeholder="16 Digit NIK"></div>
|
<div class="form-group"><label>NIK</label><input type="text" id="anggota-nik" placeholder="16 Digit NIK"></div>
|
||||||
<div class="form-group"><label>Nama Lengkap</label><input type="text" id="anggota-nama"></div>
|
<div class="form-group"><label>Nama Lengkap</label><input type="text" id="anggota-nama"></div>
|
||||||
<div class="form-group"><label>Tanggal Lahir</label><input type="date" id="anggota-lahir"></div>
|
<div class="form-group"><label>Tanggal Lahir</label><input type="date" id="anggota-lahir"></div>
|
||||||
@@ -661,31 +661,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="auth-screen" class="auth-overlay">
|
|
||||||
<div class="auth-box">
|
|
||||||
<h2 style="color: var(--navy); margin-top: 0; text-align: center; border-bottom: 2px solid var(--border); padding-bottom: 10px;">WebGIS Pontianak</h2>
|
|
||||||
|
|
||||||
<!-- Toggle Tabs Form -->
|
|
||||||
<div style="display:flex; margin-bottom: 20px; border-radius: 6px; overflow: hidden; border: 1px solid var(--border);">
|
|
||||||
<button id="tab-login" class="auth-tab active" onclick="switchAuthTab('login')">Masuk</button>
|
|
||||||
<button id="tab-register" class="auth-tab" onclick="switchAuthTab('register')">Daftar</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Form Autentikasi -->
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Username</label>
|
|
||||||
<input type="text" id="auth-username" placeholder="Masukkan username">
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Password</label>
|
|
||||||
<input type="password" id="auth-password" placeholder="Masukkan password">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button id="btn-submit-auth" class="btn btn-save" style="width: 100%; padding: 12px; margin-top: 15px; font-size: 1rem;" onclick="eksekusiAuth()">Login Sistem</button>
|
|
||||||
|
|
||||||
<p id="auth-error-msg" style="color: var(--red-danger); font-size: 0.8rem; text-align: center; margin-top: 10px; font-weight: bold; min-height: 15px;"></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
+10
-10
@@ -7,7 +7,7 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="sidebar-container">
|
<div id="sidebar-container">
|
||||||
<div id="sidebar-header">WebGis-Pontianak</div>
|
<div id="sidebar-header">WebGIS-Pontianak</div>
|
||||||
<!-- Tab Navigasi -->
|
<!-- Tab Navigasi -->
|
||||||
<div class="sidebar-tabs">
|
<div class="sidebar-tabs">
|
||||||
<button class="tab-btn active" onclick="switchTab('tab-kemiskinan')">Pemetaan</button>
|
<button class="tab-btn active" onclick="switchTab('tab-kemiskinan')">Pemetaan</button>
|
||||||
@@ -16,12 +16,12 @@
|
|||||||
<!-- TAB 1: KEMISKINAN (BARU) -->
|
<!-- TAB 1: KEMISKINAN (BARU) -->
|
||||||
<div id="tab-kemiskinan" class="tab-content active">
|
<div id="tab-kemiskinan" class="tab-content active">
|
||||||
<div class="control-panel">
|
<div class="control-panel">
|
||||||
<button class="btn-gps" onclick="lokasiSaya()">📍 Arahkan ke Lokasi Saya</button>
|
<button class="btn-gps" onclick="lokasiSaya()"><i class='fa-solid fa-location-crosshairs'></i> Arahkan ke Lokasi Saya</button>
|
||||||
<button class="btn btn-danger" onclick="eksekusiLogout()" style="margin-top: 15px; width: 100%; display: flex; align-items: center; justify-content: center; gap: 8px; padding: 12px;">
|
<button class="btn btn-danger" onclick="eksekusiLogout()" style="margin-top: 15px; width: 100%; display: flex; align-items: center; justify-content: center; gap: 8px; padding: 12px;">
|
||||||
<span style="font-size: 1.1rem;">🚪</span> Keluar Sesi (Logout)
|
<span style="font-size: 1.1rem;">🚪</span> Keluar Sesi (Logout)
|
||||||
</button>
|
</button>
|
||||||
<!-- Input Garis Kemiskinan -->
|
<!-- Input Garis Kemiskinan -->
|
||||||
<div class="add-marker-box" style="margin-bottom: 0; padding: 10px 12px;">
|
<div class="add-marker-box admin-only" style="margin-bottom: 0; padding: 10px 12px;">
|
||||||
<label>Batas Garis Kemiskinan (Rp):</label>
|
<label>Batas Garis Kemiskinan (Rp):</label>
|
||||||
<div style="display:flex; gap:8px; margin-top:5px;">
|
<div style="display:flex; gap:8px; margin-top:5px;">
|
||||||
<input type="number" id="input-garis-kemiskinan" placeholder="Loading..." class="modern-input">
|
<input type="number" id="input-garis-kemiskinan" placeholder="Loading..." class="modern-input">
|
||||||
@@ -29,15 +29,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Tombol Kelola Program Pelatihan -->
|
<!-- Tombol Kelola Program Pelatihan -->
|
||||||
<button class="btn btn-warning" onclick="bukaModalPelatihan()" style="margin-bottom: 10px; width: 100%; display: flex; align-items: center; justify-content: center; gap: 8px;">
|
<button class="btn btn-warning admin-only" onclick="bukaModalPelatihan()" style="margin-bottom: 10px; width: 100%; display: flex; align-items: center; justify-content: center; gap: 8px;">
|
||||||
<span style="font-size: 1.2rem;">📋</span> Kelola Program Pelatihan
|
<span style="font-size: 1.2rem;"><i class='fa-solid fa-clipboard-list'></i></span> Kelola Program Pelatihan
|
||||||
</button>
|
</button>
|
||||||
<!-- Tombol Tambah Marker Modern -->
|
<!-- Tombol Tambah Marker Modern -->
|
||||||
<div class="modern-action-box">
|
<div class="modern-action-box admin-only">
|
||||||
<label>Mode Penambahan Data:</label>
|
<label>Mode Penambahan Data:</label>
|
||||||
<select id="pilihan-marker-baru" class="modern-select">
|
<select id="pilihan-marker-baru" class="modern-select">
|
||||||
<option value="ibadah">🕌 Pusat Radius (Rumah Ibadah)</option>
|
<option value="ibadah"><i class='fa-solid fa-mosque'></i> Pusat Radius (Rumah Ibadah)</option>
|
||||||
<option value="penduduk">🏠 Data Keluarga (Penduduk)</option>
|
<option value="penduduk"><i class='fa-solid fa-house-chimney-user'></i> Data Keluarga (Penduduk)</option>
|
||||||
</select>
|
</select>
|
||||||
<button class="btn-modern-add" id="btn-add-kemiskinan" onclick="toggleKemiskinanMode()">
|
<button class="btn-modern-add" id="btn-add-kemiskinan" onclick="toggleKemiskinanMode()">
|
||||||
<span class="icon">+</span> Aktifkan Kursor Marker
|
<span class="icon">+</span> Aktifkan Kursor Marker
|
||||||
@@ -58,9 +58,9 @@
|
|||||||
<!-- TAB 2: FILTER & SPBU (LAMA) -->
|
<!-- TAB 2: FILTER & SPBU (LAMA) -->
|
||||||
<div id="tab-filter" class="tab-content">
|
<div id="tab-filter" class="tab-content">
|
||||||
<div id="filter-bar">
|
<div id="filter-bar">
|
||||||
<button class="filter-tool-btn" onclick="bukaModalFilterLayer()">⚙️</button>
|
<button class="filter-tool-btn" onclick="bukaModalFilterLayer()"><i class='fa-solid fa-gear'></i></button>
|
||||||
<span class="filter-label">Status SPBU:</span>
|
<span class="filter-label">Status SPBU:</span>
|
||||||
<button class="filter-tool-btn" onclick="bukaModalFilterSPBU()">🔍</button>
|
<button class="filter-tool-btn" onclick="bukaModalFilterSPBU()"><i class='fa-solid fa-filter'></i></button>
|
||||||
</div>
|
</div>
|
||||||
<div id="card-container" class="card-list-area"></div>
|
<div id="card-container" class="card-list-area"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
if(!isset($_SESSION['user_id'])) {
|
||||||
|
header('Location: login.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
?>
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="id">
|
<html lang="id">
|
||||||
<head>
|
<head>
|
||||||
@@ -8,14 +15,16 @@
|
|||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.css" />
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.css" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="style.css">
|
<link rel="stylesheet" href="style.css">
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" />
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<?php include 'components/sidebar.php'; ?>
|
<?php include 'components/sidebar.php'; ?>
|
||||||
<?php include 'components/modals.php'; ?>
|
<?php include 'components/modals.php'; ?>
|
||||||
|
|
||||||
<button id="btn-selesai-jalan" onclick="selesaiGambarJalan()">✓ Selesai Gambar Jalan</button>
|
<button id="btn-selesai-jalan" onclick="selesaiGambarJalan()"><i class='fa-solid fa-check'></i> Selesai Gambar Jalan</button>
|
||||||
<button id="btn-selesai-tanah" onclick="selesaiGambarTanah()">✓ Selesai Area Tanah</button>
|
<button id="btn-selesai-tanah" onclick="selesaiGambarTanah()"><i class='fa-solid fa-check'></i> Selesai Area Tanah</button>
|
||||||
|
|
||||||
<div id="map"></div>
|
<div id="map"></div>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
if(isset($_SESSION['user_id'])) {
|
||||||
|
header("Location: index.php");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="id">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Login - WebGIS Pontianak V2</title>
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
<style>
|
||||||
|
body { margin: 0; background: #cbd5e0; display: flex; align-items: center; justify-content: center; height: 100vh; }
|
||||||
|
.auth-overlay { position: static; background: transparent; display: flex !important; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="auth-screen" class="auth-overlay">
|
||||||
|
<div class="auth-box">
|
||||||
|
<h2 style="color: var(--navy); margin-top: 0; text-align: center; border-bottom: 2px solid var(--border); padding-bottom: 10px;">WebGIS Pontianak</h2>
|
||||||
|
|
||||||
|
<!-- Toggle Tabs Form -->
|
||||||
|
<div style="display:flex; margin-bottom: 20px; border-radius: 6px; overflow: hidden; border: 1px solid var(--border);">
|
||||||
|
<button id="tab-login" class="auth-tab active" onclick="switchAuthTab('login')">Masuk</button>
|
||||||
|
<button id="tab-register" class="auth-tab" onclick="switchAuthTab('register')">Daftar</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Form Autentikasi -->
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Username</label>
|
||||||
|
<input type="text" id="auth-username" placeholder="Masukkan username">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Password</label>
|
||||||
|
<input type="password" id="auth-password" placeholder="Masukkan password">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button id="btn-submit-auth" class="btn btn-save" style="width: 100%; padding: 12px; margin-top: 15px; font-size: 1rem;" onclick="eksekusiAuth()">Login Sistem</button>
|
||||||
|
|
||||||
|
<p id="auth-error-msg" style="color: var(--red-danger); font-size: 0.8rem; text-align: center; margin-top: 10px; font-weight: bold; min-height: 15px;"></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
let authMode = 'login';
|
||||||
|
|
||||||
|
function switchAuthTab(mode) {
|
||||||
|
authMode = mode;
|
||||||
|
document.getElementById('tab-login').classList.toggle('active', mode === 'login');
|
||||||
|
document.getElementById('tab-register').classList.toggle('active', mode === 'register');
|
||||||
|
document.getElementById('btn-submit-auth').innerText = mode === 'login' ? 'Login Sistem' : 'Daftar Akun Baru';
|
||||||
|
document.getElementById('auth-error-msg').innerText = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function eksekusiAuth() {
|
||||||
|
const user = document.getElementById('auth-username').value;
|
||||||
|
const pass = document.getElementById('auth-password').value;
|
||||||
|
const errMsg = document.getElementById('auth-error-msg');
|
||||||
|
|
||||||
|
if(!user || !pass) { errMsg.innerText = "Username dan Password wajib diisi!"; return; }
|
||||||
|
if(authMode === 'register' && pass.length < 8) {
|
||||||
|
errMsg.innerText = "Password minimal harus 8 karakter!";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
errMsg.innerText = "Memproses...";
|
||||||
|
|
||||||
|
const endpoint = authMode === 'login' ? 'otak_auth/api_login.php' : 'otak_auth/api_register.php';
|
||||||
|
|
||||||
|
fetch(endpoint, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ username: user, password: pass })
|
||||||
|
})
|
||||||
|
.then(res => res.json().then(data => ({ok: res.ok, data})))
|
||||||
|
.then(res => {
|
||||||
|
if(!res.ok) throw new Error(res.data.error || "Terjadi kesalahan.");
|
||||||
|
if(authMode === 'register') {
|
||||||
|
alert(res.data.pesan);
|
||||||
|
switchAuthTab('login');
|
||||||
|
} else {
|
||||||
|
sessionStorage.setItem('just_logged_in', 'true');
|
||||||
|
window.location.href = 'index.php'; // Redirect ke utama
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => { errMsg.innerText = err.message; });
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -7,6 +7,11 @@ require_once '../koneksi.php';
|
|||||||
$data = json_decode(file_get_contents("php://input"));
|
$data = json_decode(file_get_contents("php://input"));
|
||||||
|
|
||||||
if (!empty($data->username) && !empty($data->password)) {
|
if (!empty($data->username) && !empty($data->password)) {
|
||||||
|
if (strlen($data->password) < 8) {
|
||||||
|
http_response_code(400);
|
||||||
|
die(json_encode(["error" => "Password minimal harus 8 karakter!"]));
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Cek apakah username sudah ada
|
// Cek apakah username sudah ada
|
||||||
$stmt = $conn->prepare("SELECT id FROM users WHERE username = :username");
|
$stmt = $conn->prepare("SELECT id FROM users WHERE username = :username");
|
||||||
@@ -17,7 +22,7 @@ if (!empty($data->username) && !empty($data->password)) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Enkripsi password menggunakan BCRYPT
|
// Enkripsi password menggunakan BCRYPT
|
||||||
$query = "INSERT INTO users (username, password) VALUES (:username, :password)";
|
$query = "INSERT INTO users (username, password, role) VALUES (:username, :password, 'user')";
|
||||||
$stmt = $conn->prepare($query);
|
$stmt = $conn->prepare($query);
|
||||||
$stmt->bindValue(':username', htmlspecialchars(strip_tags($data->username)));
|
$stmt->bindValue(':username', htmlspecialchars(strip_tags($data->username)));
|
||||||
$stmt->bindValue(':password', password_hash($data->password, PASSWORD_BCRYPT));
|
$stmt->bindValue(':password', password_hash($data->password, PASSWORD_BCRYPT));
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ session_start();
|
|||||||
header("Content-Type: application/json; charset=UTF-8");
|
header("Content-Type: application/json; charset=UTF-8");
|
||||||
|
|
||||||
if(isset($_SESSION['user_id'])) {
|
if(isset($_SESSION['user_id'])) {
|
||||||
echo json_encode(["status" => "aktif", "username" => $_SESSION['username']]);
|
echo json_encode(["status" => "aktif", "username" => $_SESSION['username'], "role" => $_SESSION['role'] ?? 'user']);
|
||||||
} else {
|
} else {
|
||||||
http_response_code(401);
|
http_response_code(401);
|
||||||
echo json_encode(["status" => "tidak_aktif", "error" => "Sesi kedaluwarsa atau belum login."]);
|
echo json_encode(["status" => "tidak_aktif", "error" => "Sesi kedaluwarsa atau belum login."]);
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
if(session_status() === PHP_SESSION_NONE) session_start();
|
||||||
|
if(!isset($_SESSION['role']) || $_SESSION['role'] !== 'admin') { http_response_code(403); die(json_encode(['error'=>'Akses Ditolak: Anda bukan Admin!'])); }
|
||||||
|
|
||||||
header("Content-Type: application/json; charset=UTF-8");
|
header("Content-Type: application/json; charset=UTF-8");
|
||||||
header("Access-Control-Allow-Methods: POST");
|
header("Access-Control-Allow-Methods: POST");
|
||||||
require_once '../koneksi.php';
|
require_once '../koneksi.php';
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
if(session_status() === PHP_SESSION_NONE) session_start();
|
||||||
|
if(!isset($_SESSION['role']) || $_SESSION['role'] !== 'admin') { http_response_code(403); die(json_encode(['error'=>'Akses Ditolak: Anda bukan Admin!'])); }
|
||||||
|
|
||||||
header("Content-Type: application/json; charset=UTF-8");
|
header("Content-Type: application/json; charset=UTF-8");
|
||||||
header("Access-Control-Allow-Methods: POST");
|
header("Access-Control-Allow-Methods: POST");
|
||||||
require_once '../koneksi.php';
|
require_once '../koneksi.php';
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
if(session_status() === PHP_SESSION_NONE) session_start();
|
||||||
|
if(!isset($_SESSION['role']) || $_SESSION['role'] !== 'admin') { http_response_code(403); die(json_encode(['error'=>'Akses Ditolak: Anda bukan Admin!'])); }
|
||||||
|
|
||||||
header("Content-Type: application/json; charset=UTF-8");
|
header("Content-Type: application/json; charset=UTF-8");
|
||||||
header("Access-Control-Allow-Methods: POST");
|
header("Access-Control-Allow-Methods: POST");
|
||||||
require_once '../koneksi.php';
|
require_once '../koneksi.php';
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
if(session_status() === PHP_SESSION_NONE) session_start();
|
||||||
|
if(!isset($_SESSION['role']) || $_SESSION['role'] !== 'admin') { http_response_code(403); die(json_encode(['error'=>'Akses Ditolak: Anda bukan Admin!'])); }
|
||||||
|
|
||||||
header("Content-Type: application/json; charset=UTF-8");
|
header("Content-Type: application/json; charset=UTF-8");
|
||||||
header("Access-Control-Allow-Methods: POST");
|
header("Access-Control-Allow-Methods: POST");
|
||||||
require_once '../koneksi.php';
|
require_once '../koneksi.php';
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
if(session_status() === PHP_SESSION_NONE) session_start();
|
||||||
|
if(!isset($_SESSION['role']) || $_SESSION['role'] !== 'admin') { http_response_code(403); die(json_encode(['error'=>'Akses Ditolak: Anda bukan Admin!'])); }
|
||||||
|
|
||||||
// otak_spbu/api_hapus_spbu.php
|
// otak_spbu/api_hapus_spbu.php
|
||||||
header("Content-Type: application/json; charset=UTF-8");
|
header("Content-Type: application/json; charset=UTF-8");
|
||||||
header("Access-Control-Allow-Methods: POST");
|
header("Access-Control-Allow-Methods: POST");
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
if(session_status() === PHP_SESSION_NONE) session_start();
|
||||||
|
if(!isset($_SESSION['role']) || $_SESSION['role'] !== 'admin') { http_response_code(403); die(json_encode(['error'=>'Akses Ditolak: Anda bukan Admin!'])); }
|
||||||
|
|
||||||
header("Content-Type: application/json; charset=UTF-8");
|
header("Content-Type: application/json; charset=UTF-8");
|
||||||
header("Access-Control-Allow-Methods: POST");
|
header("Access-Control-Allow-Methods: POST");
|
||||||
require_once '../koneksi.php';
|
require_once '../koneksi.php';
|
||||||
|
|||||||
Reference in New Issue
Block a user