update sistem proyek menjadi lebih struktural. dimulai dengan memisahkan dashboard publik dan administrator pada setiap proyek, membuat dockerfile untuk setup environment docker, dan merapikan database serta membuat file migration.
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
.git
|
||||
.env
|
||||
*.log
|
||||
@@ -0,0 +1,12 @@
|
||||
DB_HOST=localhost
|
||||
DB_PORT=3306
|
||||
DB_DATABASE=webgis_spbu
|
||||
DB_USERNAME=root
|
||||
DB_PASSWORD=
|
||||
|
||||
# Alternatif nama env yang juga didukung:
|
||||
# MYSQL_HOST=localhost
|
||||
# MYSQL_PORT=3306
|
||||
# MYSQL_DATABASE=webgis_spbu
|
||||
# MYSQL_USER=root
|
||||
# MYSQL_PASSWORD=
|
||||
@@ -0,0 +1,9 @@
|
||||
FROM php:8.2-apache
|
||||
|
||||
RUN docker-php-ext-install mysqli
|
||||
|
||||
COPY . /var/www/html/
|
||||
|
||||
RUN chown -R www-data:www-data /var/www/html
|
||||
|
||||
EXPOSE 80
|
||||
@@ -18,11 +18,17 @@ Aplikasi dibuat menggunakan PHP native, MySQL, JavaScript, Leaflet.js, Leaflet D
|
||||
|
||||
### 1. WebGIS Geometri
|
||||
|
||||
Lokasi: `geometri/index.php`
|
||||
Lokasi publik: `geometri/index.php`
|
||||
Lokasi admin: `geometri/admin.php`
|
||||
|
||||
Modul ini digunakan untuk latihan dan pengelolaan data geometri dasar.
|
||||
|
||||
Fitur:
|
||||
Fitur publik:
|
||||
|
||||
- Menampilkan data point, jalan/polyline, dan polygon/parsil.
|
||||
- Melihat detail data melalui popup peta.
|
||||
|
||||
Fitur admin:
|
||||
|
||||
- Menampilkan data point, jalan/polyline, dan polygon/parsil.
|
||||
- Menambah point dengan nama dan kode point.
|
||||
@@ -33,12 +39,20 @@ Fitur:
|
||||
|
||||
### 2. WebGIS SPBU
|
||||
|
||||
Lokasi: `spbu/index.php`
|
||||
Lokasi publik: `spbu/index.php`
|
||||
Lokasi admin: `spbu/admin.php`
|
||||
|
||||
Modul ini digunakan untuk memetakan lokasi SPBU di Pontianak.
|
||||
|
||||
Fitur:
|
||||
Fitur publik:
|
||||
|
||||
- Menampilkan titik SPBU.
|
||||
- Membedakan SPBU 24 jam dan tidak 24 jam.
|
||||
- Toggle layer untuk menampilkan/menyembunyikan SPBU berdasarkan status.
|
||||
|
||||
Fitur admin:
|
||||
|
||||
- Login admin.
|
||||
- Menampilkan titik SPBU.
|
||||
- Membedakan SPBU 24 jam dan tidak 24 jam.
|
||||
- Menambah titik SPBU dari peta.
|
||||
@@ -148,77 +162,110 @@ Pastikan perangkat sudah memiliki:
|
||||
|
||||
3. Jalankan Apache dan MySQL dari XAMPP Control Panel.
|
||||
|
||||
4. Buat database MySQL, contoh:
|
||||
4. Siapkan konfigurasi database melalui environment variable. Contoh variabel tersedia di `.env.example`.
|
||||
|
||||
```text
|
||||
DB_HOST=localhost
|
||||
DB_PORT=3306
|
||||
DB_DATABASE=webgis_spbu
|
||||
DB_USERNAME=root
|
||||
DB_PASSWORD=
|
||||
```
|
||||
|
||||
Pada Coolify, isi variabel tersebut dari menu Environment Variables project. Jangan menulis password database langsung di `php/db.php`.
|
||||
|
||||
5. Jalankan migration database yang sudah disediakan.
|
||||
|
||||
```sql
|
||||
CREATE DATABASE webgis_spbu;
|
||||
SOURCE migrations/001_init_database.sql;
|
||||
```
|
||||
|
||||
5. Sesuaikan konfigurasi koneksi database pada file `php/db.php`.
|
||||
File migration tersebut akan membuat database `webgis_spbu`, seluruh tabel aplikasi, dan akun default.
|
||||
|
||||
```php
|
||||
$conn = new mysqli("localhost", "root", "password_database", "webgis_spbu");
|
||||
```
|
||||
|
||||
6. Buat tabel yang dibutuhkan. Jika belum memiliki file `.sql`, gunakan struktur dasar berikut sebagai acuan.
|
||||
|
||||
```sql
|
||||
CREATE TABLE spbu (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
nama VARCHAR(150) NOT NULL,
|
||||
no_spbu VARCHAR(100) NOT NULL,
|
||||
status VARCHAR(50) NOT NULL,
|
||||
latitude DOUBLE NOT NULL,
|
||||
longitude DOUBLE NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE jalan (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
nama VARCHAR(150) NOT NULL,
|
||||
status VARCHAR(50) NOT NULL,
|
||||
panjang DOUBLE DEFAULT 0,
|
||||
geom LONGTEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE parsil (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
nama VARCHAR(150) NOT NULL,
|
||||
status VARCHAR(50) NOT NULL,
|
||||
luas DOUBLE DEFAULT 0,
|
||||
geom LONGTEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE masjid (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
nama VARCHAR(150) NOT NULL,
|
||||
pic VARCHAR(150) NOT NULL,
|
||||
radius DOUBLE DEFAULT 500,
|
||||
latitude DOUBLE NOT NULL,
|
||||
longitude DOUBLE NOT NULL,
|
||||
radius_layanan DOUBLE DEFAULT 500
|
||||
);
|
||||
|
||||
CREATE TABLE penduduk_miskin (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
nama_kk VARCHAR(150) NOT NULL,
|
||||
jumlah_anggota INT DEFAULT 1,
|
||||
alamat TEXT NOT NULL,
|
||||
latitude DOUBLE NOT NULL,
|
||||
longitude DOUBLE NOT NULL,
|
||||
penghasilan VARCHAR(50),
|
||||
kondisi_rumah VARCHAR(50),
|
||||
sumber_air VARCHAR(50),
|
||||
skor_kerentanan DOUBLE DEFAULT 0,
|
||||
jenis_bantuan VARCHAR(100)
|
||||
);
|
||||
```
|
||||
|
||||
7. Buka aplikasi melalui browser:
|
||||
6. Buka aplikasi melalui browser:
|
||||
|
||||
```text
|
||||
http://localhost/WebGIS2026/
|
||||
```
|
||||
|
||||
## Catatan Deploy Coolify
|
||||
|
||||
Untuk deploy di Coolify, project ini dapat dijalankan sebagai aplikasi PHP native. Pastikan project memiliki service MySQL/MariaDB, lalu set environment variable berikut pada aplikasi:
|
||||
|
||||
```text
|
||||
DB_HOST=<host service database>
|
||||
DB_PORT=3306
|
||||
DB_DATABASE=webgis_spbu
|
||||
DB_USERNAME=<username database>
|
||||
DB_PASSWORD=<password database>
|
||||
```
|
||||
|
||||
Jika service database di Coolify memakai nama variabel `MYSQL_*`, aplikasi juga mendukung:
|
||||
|
||||
```text
|
||||
MYSQL_HOST=<host service database>
|
||||
MYSQL_PORT=3306
|
||||
MYSQL_DATABASE=webgis_spbu
|
||||
MYSQL_USER=<username database>
|
||||
MYSQL_PASSWORD=<password database>
|
||||
```
|
||||
|
||||
Docker tidak wajib jika Coolify/Nixpacks berhasil mendeteksi aplikasi PHP dan menyediakan web server PHP. Namun repository ini sudah menyediakan `Dockerfile`, sehingga mode deployment paling stabil di Coolify adalah memilih build dari Dockerfile. Dockerfile tersebut memakai `php:8.2-apache` dan memasang ekstensi `mysqli`.
|
||||
|
||||
## Menjalankan Dengan Docker Compose
|
||||
|
||||
Repository ini juga menyediakan `docker-compose.yml` untuk menjalankan seluruh service secara lokal:
|
||||
|
||||
- `app`: aplikasi PHP Apache
|
||||
- `db`: MySQL 8.4
|
||||
- `phpmyadmin`: panel database
|
||||
|
||||
Jalankan:
|
||||
|
||||
```bash
|
||||
docker compose up -d --build
|
||||
```
|
||||
|
||||
URL akses:
|
||||
|
||||
```text
|
||||
Aplikasi: http://localhost:8080
|
||||
phpMyAdmin: http://localhost:8081
|
||||
```
|
||||
|
||||
Kredensial database default Docker Compose:
|
||||
|
||||
```text
|
||||
Host: db
|
||||
Port internal: 3306
|
||||
Port host/lokal: 3307
|
||||
Database: webgis_spbu
|
||||
Username: webgis_user
|
||||
Password: webgis_password
|
||||
Root password: root_password
|
||||
```
|
||||
|
||||
File migration di folder `migrations/` akan otomatis dijalankan oleh MySQL saat volume database pertama kali dibuat. File `001_init_database.sql` membuat struktur tabel dan akun login, sedangkan `002_seed_sample_data.sql` mengisi data contoh untuk Docker Compose.
|
||||
|
||||
Data contoh yang disediakan:
|
||||
|
||||
- 3 point geometri
|
||||
- 3 polyline jalan
|
||||
- 3 polygon/parsil
|
||||
- 7 SPBU, terdiri dari 5 SPBU 24 jam dan 2 tidak 24 jam
|
||||
- 3 masjid dengan radius layanan
|
||||
- 14 data penduduk miskin, termasuk minimal 1 blind spot
|
||||
- akun `admin / password` dan `publik / password`
|
||||
|
||||
Jika migration atau seed berubah dan ingin mengulang dari awal, hentikan container lalu hapus volume database:
|
||||
|
||||
```bash
|
||||
docker compose down -v
|
||||
docker compose up -d --build
|
||||
```
|
||||
|
||||
Catatan: perintah `down -v` akan menghapus data database container.
|
||||
|
||||
## Cara Penggunaan
|
||||
|
||||
### Membuka Dashboard Utama
|
||||
@@ -254,20 +301,28 @@ Pastikan perangkat sudah memiliki:
|
||||
6. Gunakan tombol layer `24 Jam` dan `Tidak 24 Jam` untuk memfilter tampilan.
|
||||
7. Klik marker SPBU untuk melihat detail atau menghapus data.
|
||||
|
||||
### Login Admin Modul Sosial
|
||||
### Login Admin
|
||||
|
||||
1. Buka `http://localhost/WebGIS2026/sosial/`.
|
||||
1. Buka salah satu dashboard publik:
|
||||
- `http://localhost/WebGIS2026/geometri/`
|
||||
- `http://localhost/WebGIS2026/spbu/`
|
||||
- `http://localhost/WebGIS2026/sosial/`
|
||||
2. Klik tombol `Admin Login`.
|
||||
3. Masukkan akun default:
|
||||
3. Masukkan salah satu akun default:
|
||||
|
||||
```text
|
||||
Role Admin
|
||||
Username: admin
|
||||
Password: admin123
|
||||
Password: password
|
||||
|
||||
Role Publik
|
||||
Username: publik
|
||||
Password: password
|
||||
```
|
||||
|
||||
4. Setelah berhasil login, sistem akan membuka panel admin di `sosial/admin.php`.
|
||||
4. Akun `admin` akan diarahkan ke panel admin sesuai project yang sedang dibuka. Akun `publik` diarahkan ke dashboard publik. Dashboard publik juga dapat diakses tanpa login.
|
||||
|
||||
Catatan: ubah kredensial default di `sosial/login.php` sebelum proyek digunakan di lingkungan publik.
|
||||
Catatan: akun default berasal dari tabel `users`. Ubah atau tambah akun melalui database, bukan dari `sosial/login.php`.
|
||||
|
||||
### Menambah Data Masjid
|
||||
|
||||
@@ -332,7 +387,8 @@ Beberapa endpoint utama yang digunakan aplikasi:
|
||||
| `php/show_geometri.php` | Mengambil data point, jalan, dan parsil |
|
||||
| `php/show_spbu.php` | Mengambil data SPBU |
|
||||
| `php/show_sosial.php` | Mengambil data masjid dan penduduk miskin |
|
||||
| `php/insert.php` | Menambah data SPBU/point |
|
||||
| `php/insert_point.php` | Menambah data point geometri |
|
||||
| `php/insert.php` | Menambah data SPBU |
|
||||
| `php/insert_line.php` | Menambah data jalan |
|
||||
| `php/insert_polygon.php` | Menambah data parsil |
|
||||
| `php/insert_masjid.php` | Menambah data masjid |
|
||||
@@ -355,7 +411,7 @@ Beberapa endpoint utama yang digunakan aplikasi:
|
||||
|
||||
- Import/export data GeoJSON.
|
||||
- Filter data berdasarkan kecamatan atau kelurahan.
|
||||
- Role user untuk admin, petugas, dan viewer.
|
||||
- Pemisahan akses admin dan publik untuk seluruh modul WebGIS.
|
||||
- Upload dokumentasi/foto penduduk atau masjid.
|
||||
- Dashboard laporan bantuan sosial.
|
||||
- Integrasi data batas administrasi.
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
services:
|
||||
app:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
container_name: webgis_app
|
||||
ports:
|
||||
- "8080:80"
|
||||
environment:
|
||||
DB_HOST: db
|
||||
DB_PORT: 3306
|
||||
DB_DATABASE: webgis_spbu
|
||||
DB_USERNAME: webgis_user
|
||||
DB_PASSWORD: webgis_password
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
restart: unless-stopped
|
||||
|
||||
db:
|
||||
image: mysql:8.4
|
||||
container_name: webgis_db
|
||||
environment:
|
||||
MYSQL_DATABASE: webgis_spbu
|
||||
MYSQL_USER: webgis_user
|
||||
MYSQL_PASSWORD: webgis_password
|
||||
MYSQL_ROOT_PASSWORD: root_password
|
||||
ports:
|
||||
- "3307:3306"
|
||||
volumes:
|
||||
- webgis_db_data:/var/lib/mysql
|
||||
- ./migrations:/docker-entrypoint-initdb.d:ro
|
||||
healthcheck:
|
||||
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-uroot", "-proot_password"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
restart: unless-stopped
|
||||
|
||||
phpmyadmin:
|
||||
image: phpmyadmin:5.2
|
||||
container_name: webgis_phpmyadmin
|
||||
ports:
|
||||
- "8081:80"
|
||||
environment:
|
||||
PMA_HOST: db
|
||||
PMA_PORT: 3306
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
webgis_db_data:
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
require_once '../php/auth.php';
|
||||
require_roles_page(['admin']);
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="id">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Panel Admin - WebGIS Geometri</title>
|
||||
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Space+Mono:wght@400;700&family=DM+Sans:wght@300;400;500;600&display=swap" rel="stylesheet">
|
||||
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css"/>
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet-draw/dist/leaflet.draw.css"/>
|
||||
<link rel="stylesheet" href="../css/style.css"/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<header>
|
||||
<a href="index.php" class="back-btn" title="Lihat Dashboard Publik">←</a>
|
||||
<div class="logo">
|
||||
<div class="logo-icon" style="background:#22c55e;">G</div>
|
||||
<div class="logo-text">ADMIN<span style="color:#22c55e;"> GEOMETRI</span></div>
|
||||
</div>
|
||||
<div class="header-divider"></div>
|
||||
<span style="font-size:12px;color:var(--muted);">Kelola Point, Polyline, Polygon</span>
|
||||
|
||||
<div class="legend">
|
||||
<div class="legend-item"><div class="legend-dot" style="background:#3b82f6"></div> Point</div>
|
||||
<div class="legend-item"><div class="legend-line" style="background:#a855f7"></div> Polyline/Jalan</div>
|
||||
<div class="legend-item"><div class="legend-poly" style="background:#10b981"></div> Polygon/Parsil</div>
|
||||
</div>
|
||||
|
||||
<div class="layer-sep"></div>
|
||||
<div class="layer-control">
|
||||
<a href="logout.php" class="layer-btn" style="text-decoration:none; color:#ef4444; border-color:#ef444433;">Keluar</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div id="map"></div>
|
||||
|
||||
<script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>
|
||||
<script src="https://unpkg.com/leaflet-draw/dist/leaflet.draw.js"></script>
|
||||
<script src="https://unpkg.com/leaflet-geometryutil/src/leaflet.geometryutil.js"></script>
|
||||
<script>window.IS_ADMIN = true;</script>
|
||||
<script src="../js/draw_geometri.js?v=<?= time() ?>"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
+9
-5
@@ -9,7 +9,6 @@
|
||||
<link href="https://fonts.googleapis.com/css2?family=Space+Mono:wght@400;700&family=DM+Sans:wght@300;400;500;600&display=swap" rel="stylesheet">
|
||||
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css"/>
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet-draw/dist/leaflet.draw.css"/>
|
||||
<link rel="stylesheet" href="../css/style.css"/>
|
||||
</head>
|
||||
<body>
|
||||
@@ -23,7 +22,13 @@
|
||||
<div class="header-divider"></div>
|
||||
<span style="font-size:12px;color:var(--muted);">Point, Polyline, Polygon</span>
|
||||
|
||||
<div class="legend">
|
||||
<div class="layer-control" style="margin-left:auto;">
|
||||
<a href="login.php" class="layer-btn" style="text-decoration:none; color:#22c55e; border-color:#22c55e33;">Admin Login</a>
|
||||
</div>
|
||||
|
||||
<div class="layer-sep"></div>
|
||||
|
||||
<div class="legend" style="margin-left:0;">
|
||||
<div class="legend-item">
|
||||
<div class="legend-dot" style="background:#3b82f6"></div> Point
|
||||
</div>
|
||||
@@ -39,9 +44,8 @@
|
||||
<div id="map"></div>
|
||||
|
||||
<script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>
|
||||
<script src="https://unpkg.com/leaflet-draw/dist/leaflet.draw.js"></script>
|
||||
<script src="https://unpkg.com/leaflet-geometryutil/src/leaflet.geometryutil.js"></script>
|
||||
<script src="../js/draw_geometri.js"></script>
|
||||
<script>window.IS_ADMIN = false;</script>
|
||||
<script src="../js/draw_geometri.js?v=<?= time() ?>"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
session_start();
|
||||
include '../php/db.php';
|
||||
|
||||
$error = '';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$user = trim($_POST['username'] ?? '');
|
||||
$pass = $_POST['password'] ?? '';
|
||||
|
||||
$stmt = $conn->prepare("SELECT id, username, password_hash, role, full_name, is_active FROM users WHERE username = ? LIMIT 1");
|
||||
if (!$stmt) {
|
||||
$error = 'Konfigurasi login belum siap. Jalankan migration database terlebih dahulu.';
|
||||
} else {
|
||||
$stmt->bind_param("s", $user);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
$account = $result ? $result->fetch_assoc() : null;
|
||||
|
||||
if ($account && intval($account['is_active']) === 1 && password_verify($pass, $account['password_hash'])) {
|
||||
session_regenerate_id(true);
|
||||
$_SESSION['user_id'] = intval($account['id']);
|
||||
$_SESSION['username'] = $account['username'];
|
||||
$_SESSION['role'] = $account['role'];
|
||||
$_SESSION['full_name'] = $account['full_name'];
|
||||
$_SESSION['is_admin'] = $account['role'] === 'admin';
|
||||
|
||||
header('Location: ' . ($account['role'] === 'admin' ? 'admin.php' : 'index.php'));
|
||||
exit;
|
||||
}
|
||||
|
||||
$error = 'Username atau password salah.';
|
||||
$stmt->close();
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($conn)) {
|
||||
$conn->close();
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="id">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Login Admin - WebGIS Geometri</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Space+Mono:wght@400;700&family=DM+Sans:wght@300;400;500;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
body { font-family:'DM Sans',sans-serif; background:#0a0d14; color:#e2e8f0; min-height:100vh; display:flex; align-items:center; justify-content:center; padding:20px; }
|
||||
body::before { content:''; position:fixed; inset:0; background-image:linear-gradient(rgba(34,197,94,.04) 1px, transparent 1px),linear-gradient(90deg, rgba(34,197,94,.04) 1px, transparent 1px); background-size:40px 40px; }
|
||||
.login-card { position:relative; z-index:1; width:100%; max-width:380px; background:#1a1d27; border:1px solid #2a2d3a; border-radius:16px; padding:36px 32px; box-shadow:0 25px 60px rgba(0,0,0,.5); }
|
||||
.login-logo { display:flex; align-items:center; gap:12px; margin-bottom:28px; }
|
||||
.login-logo-icon { width:44px; height:44px; background:linear-gradient(135deg,#166534,#22c55e); border-radius:12px; display:flex; align-items:center; justify-content:center; font-weight:700; }
|
||||
.login-logo-text { font-family:'Space Mono',monospace; font-size:14px; font-weight:700; letter-spacing:.05em; }
|
||||
.login-logo-text span { color:#22c55e; }
|
||||
.login-logo-sub { font-size:11px; color:#6b7280; margin-top:1px; }
|
||||
h1 { font-size:20px; margin-bottom:6px; }
|
||||
.login-desc { font-size:13px; color:#6b7280; margin-bottom:24px; line-height:1.5; }
|
||||
label { display:block; font-size:11px; color:#9ca3af; font-weight:600; letter-spacing:.05em; text-transform:uppercase; margin-bottom:6px; margin-top:16px; }
|
||||
input { width:100%; background:#0f1117; border:1px solid #2a2d3a; color:#e2e8f0; border-radius:8px; padding:10px 14px; font-size:14px; font-family:'DM Sans',sans-serif; outline:none; }
|
||||
input:focus { border-color:#22c55e; }
|
||||
.error-msg { background:rgba(239,68,68,.1); border:1px solid rgba(239,68,68,.3); color:#f87171; border-radius:8px; padding:10px 14px; font-size:13px; margin-top:16px; }
|
||||
.btn-login { width:100%; margin-top:24px; padding:12px; background:linear-gradient(135deg,#166534,#22c55e); color:#fff; border:0; border-radius:8px; font-size:14px; font-weight:700; font-family:'DM Sans',sans-serif; cursor:pointer; }
|
||||
.back-link { display:block; text-align:center; margin-top:16px; font-size:12px; color:#6b7280; text-decoration:none; }
|
||||
.back-link:hover { color:#e2e8f0; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="login-card">
|
||||
<div class="login-logo">
|
||||
<div class="login-logo-icon">G</div>
|
||||
<div>
|
||||
<div class="login-logo-text">WEB<span>GIS</span> GEOMETRI</div>
|
||||
<div class="login-logo-sub">Portal Administrasi</div>
|
||||
</div>
|
||||
</div>
|
||||
<h1>Masuk Admin</h1>
|
||||
<p class="login-desc">Halaman ini khusus untuk pengelola data point, polyline, dan polygon.</p>
|
||||
<?php if ($error): ?><div class="error-msg"><?= htmlspecialchars($error) ?></div><?php endif; ?>
|
||||
<form method="POST">
|
||||
<label for="username">Username</label>
|
||||
<input type="text" id="username" name="username" placeholder="admin" autocomplete="off" required>
|
||||
<label for="password">Password</label>
|
||||
<input type="password" id="password" name="password" placeholder="password" required>
|
||||
<button type="submit" class="btn-login">Masuk</button>
|
||||
</form>
|
||||
<a href="index.php" class="back-link">Kembali ke Dashboard Publik</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
session_start();
|
||||
session_destroy();
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
?>
|
||||
+71
-62
@@ -13,20 +13,23 @@ L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
var drawnItems = new L.FeatureGroup().addTo(map);
|
||||
var layerGroupPoint = L.featureGroup().addTo(map);
|
||||
var currentLayer = null;
|
||||
var isAdmin = window.IS_ADMIN === true;
|
||||
|
||||
var drawControl = new L.Control.Draw({
|
||||
position: 'topleft',
|
||||
edit: { featureGroup: drawnItems },
|
||||
draw: {
|
||||
marker: true,
|
||||
polyline: { shapeOptions: { color: '#a855f7', weight: 3 } },
|
||||
polygon: { shapeOptions: { color: '#10b981', weight: 2, fillOpacity: 0.35 } },
|
||||
rectangle: false,
|
||||
circle: false,
|
||||
circlemarker: false
|
||||
}
|
||||
});
|
||||
map.addControl(drawControl);
|
||||
if (isAdmin) {
|
||||
var drawControl = new L.Control.Draw({
|
||||
position: 'topleft',
|
||||
edit: { featureGroup: drawnItems },
|
||||
draw: {
|
||||
marker: true,
|
||||
polyline: { shapeOptions: { color: '#a855f7', weight: 3 } },
|
||||
polygon: { shapeOptions: { color: '#10b981', weight: 2, fillOpacity: 0.35 } },
|
||||
rectangle: false,
|
||||
circle: false,
|
||||
circlemarker: false
|
||||
}
|
||||
});
|
||||
map.addControl(drawControl);
|
||||
}
|
||||
|
||||
function getLineColor(status) {
|
||||
return status === 'Nasional' ? '#ef4444' : status === 'Provinsi' ? '#38bdf8' : '#a855f7';
|
||||
@@ -88,9 +91,11 @@ function buildInfoPopup(d, type) {
|
||||
<div class="ip-row"><span class="ip-label">Luas</span><span class="ip-val">${fmtNum(d.luas)} m²</span></div>`;
|
||||
}
|
||||
|
||||
var deleteButton = isAdmin ? `<button class="ip-del" onclick="hapusData(${d.id},'${type}')">Hapus</button>` : '';
|
||||
|
||||
return `<div class="ip">
|
||||
<div class="ip-head" style="background:${headerColors[type]}">${icons[type]} ${titles[type]}</div>
|
||||
<div class="ip-body">${rows}<button class="ip-del" onclick="hapusData(${d.id},'${type}')">Hapus</button></div>
|
||||
<div class="ip-body">${rows}${deleteButton}</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
@@ -142,22 +147,24 @@ function hapusData(id, type) {
|
||||
});
|
||||
}
|
||||
|
||||
map.on(L.Draw.Event.CREATED, function(e) {
|
||||
var layer = e.layer;
|
||||
if (isAdmin) {
|
||||
map.on(L.Draw.Event.CREATED, function(e) {
|
||||
var layer = e.layer;
|
||||
|
||||
if (e.layerType === 'marker') {
|
||||
drawnItems.addLayer(layer);
|
||||
handlePoint(layer);
|
||||
}
|
||||
if (e.layerType === 'polyline') {
|
||||
drawnItems.addLayer(layer);
|
||||
handleLine(layer);
|
||||
}
|
||||
if (e.layerType === 'polygon') {
|
||||
drawnItems.addLayer(layer);
|
||||
handlePolygon(layer);
|
||||
}
|
||||
});
|
||||
if (e.layerType === 'marker') {
|
||||
drawnItems.addLayer(layer);
|
||||
handlePoint(layer);
|
||||
}
|
||||
if (e.layerType === 'polyline') {
|
||||
drawnItems.addLayer(layer);
|
||||
handleLine(layer);
|
||||
}
|
||||
if (e.layerType === 'polygon') {
|
||||
drawnItems.addLayer(layer);
|
||||
handlePolygon(layer);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function handlePoint(layer) {
|
||||
var ll = layer.getLatLng();
|
||||
@@ -180,7 +187,7 @@ function savePoint(lat, lng) {
|
||||
alert('Nama dan kode point wajib diisi!');
|
||||
return;
|
||||
}
|
||||
fetch('../php/insert.php', {
|
||||
fetch('../php/insert_point.php', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ nama, no, status, lat, lng })
|
||||
@@ -294,38 +301,40 @@ function savePolygon() {
|
||||
});
|
||||
}
|
||||
|
||||
map.on('draw:edited', function(e) {
|
||||
e.layers.eachLayer(layer => {
|
||||
if (!layer._db_id) return;
|
||||
if (layer._type === 'line') {
|
||||
fetch('../php/update_line.php', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
id: layer._db_id,
|
||||
geom: JSON.stringify(layer.getLatLngs()),
|
||||
panjang: getLength(layer.getLatLngs())
|
||||
})
|
||||
});
|
||||
}
|
||||
if (layer._type === 'polygon') {
|
||||
fetch('../php/update_polygon.php', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
id: layer._db_id,
|
||||
geom: JSON.stringify(layer.getLatLngs()),
|
||||
luas: L.GeometryUtil.geodesicArea(layer.getLatLngs()[0])
|
||||
})
|
||||
});
|
||||
}
|
||||
if (isAdmin) {
|
||||
map.on('draw:edited', function(e) {
|
||||
e.layers.eachLayer(layer => {
|
||||
if (!layer._db_id) return;
|
||||
if (layer._type === 'line') {
|
||||
fetch('../php/update_line.php', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
id: layer._db_id,
|
||||
geom: JSON.stringify(layer.getLatLngs()),
|
||||
panjang: getLength(layer.getLatLngs())
|
||||
})
|
||||
});
|
||||
}
|
||||
if (layer._type === 'polygon') {
|
||||
fetch('../php/update_polygon.php', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
id: layer._db_id,
|
||||
geom: JSON.stringify(layer.getLatLngs()),
|
||||
luas: L.GeometryUtil.geodesicArea(layer.getLatLngs()[0])
|
||||
})
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
map.on('draw:deleted', function(e) {
|
||||
e.layers.eachLayer(layer => {
|
||||
if (layer._db_id && layer._type) {
|
||||
fetch(`../php/delete.php?id=${layer._db_id}&type=${layer._type}`);
|
||||
}
|
||||
map.on('draw:deleted', function(e) {
|
||||
e.layers.eachLayer(layer => {
|
||||
if (layer._db_id && layer._type) {
|
||||
fetch(`../php/delete.php?id=${layer._db_id}&type=${layer._type}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
+27
-21
@@ -15,6 +15,7 @@ var layerGroup24 = L.featureGroup().addTo(map);
|
||||
var layerGroupTidak = L.featureGroup().addTo(map);
|
||||
var show24 = true;
|
||||
var showTidak = true;
|
||||
var isAdmin = window.IS_ADMIN === true;
|
||||
|
||||
function toggleLayer(type) {
|
||||
if (type === '24jam') {
|
||||
@@ -28,19 +29,21 @@ function toggleLayer(type) {
|
||||
}
|
||||
}
|
||||
|
||||
var drawControl = new L.Control.Draw({
|
||||
position: 'topleft',
|
||||
edit: { featureGroup: drawnItems },
|
||||
draw: {
|
||||
marker: true,
|
||||
polyline: false,
|
||||
polygon: false,
|
||||
rectangle: false,
|
||||
circle: false,
|
||||
circlemarker: false
|
||||
}
|
||||
});
|
||||
map.addControl(drawControl);
|
||||
if (isAdmin) {
|
||||
var drawControl = new L.Control.Draw({
|
||||
position: 'topleft',
|
||||
edit: { featureGroup: drawnItems },
|
||||
draw: {
|
||||
marker: true,
|
||||
polyline: false,
|
||||
polygon: false,
|
||||
rectangle: false,
|
||||
circle: false,
|
||||
circlemarker: false
|
||||
}
|
||||
});
|
||||
map.addControl(drawControl);
|
||||
}
|
||||
|
||||
function makeIcon(status) {
|
||||
var c = status === '24 Jam' ? '#4ade80' : '#f87171';
|
||||
@@ -60,13 +63,14 @@ function statusBadge(status) {
|
||||
}
|
||||
|
||||
function buildInfoPopup(d) {
|
||||
var deleteButton = isAdmin ? `<button class="ip-del" onclick="hapusData(${d.id},'spbu')">Hapus</button>` : '';
|
||||
return `<div class="ip">
|
||||
<div class="ip-head" style="background:linear-gradient(135deg,#1d4ed8,#3b82f6)">⛽ SPBU - ${d.nama}</div>
|
||||
<div class="ip-body">
|
||||
<div class="ip-row"><span class="ip-label">No. SPBU</span><span class="ip-val">${d.no_spbu}</span></div>
|
||||
<div class="ip-row"><span class="ip-label">Status</span><span class="ip-val">${statusBadge(d.status)}</span></div>
|
||||
<div class="ip-row"><span class="ip-label">Koordinat</span><span class="ip-val" style="font-size:10px">${parseFloat(d.latitude).toFixed(5)}, ${parseFloat(d.longitude).toFixed(5)}</span></div>
|
||||
<button class="ip-del" onclick="hapusData(${d.id},'point')">Hapus</button>
|
||||
${deleteButton}
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
@@ -76,7 +80,7 @@ fetch('../php/show_spbu.php')
|
||||
.then(data => {
|
||||
data.forEach(d => {
|
||||
var layer = L.marker([d.latitude, d.longitude], { icon: makeIcon(d.status) });
|
||||
layer._type = 'point';
|
||||
layer._type = 'spbu';
|
||||
layer._db_id = d.id;
|
||||
layer.bindPopup(buildInfoPopup(d), { maxWidth: 260 });
|
||||
d.status === '24 Jam' ? layerGroup24.addLayer(layer) : layerGroupTidak.addLayer(layer);
|
||||
@@ -94,12 +98,14 @@ function hapusData(id, type) {
|
||||
});
|
||||
}
|
||||
|
||||
map.on(L.Draw.Event.CREATED, function(e) {
|
||||
if (e.layerType !== 'marker') return;
|
||||
var layer = e.layer;
|
||||
drawnItems.addLayer(layer);
|
||||
handlePoint(layer);
|
||||
});
|
||||
if (isAdmin) {
|
||||
map.on(L.Draw.Event.CREATED, function(e) {
|
||||
if (e.layerType !== 'marker') return;
|
||||
var layer = e.layer;
|
||||
drawnItems.addLayer(layer);
|
||||
handlePoint(layer);
|
||||
});
|
||||
}
|
||||
|
||||
function handlePoint(layer) {
|
||||
var ll = layer.getLatLng();
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
CREATE DATABASE IF NOT EXISTS webgis_spbu
|
||||
CHARACTER SET utf8mb4
|
||||
COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
USE webgis_spbu;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
username VARCHAR(50) NOT NULL UNIQUE,
|
||||
password_hash VARCHAR(255) NOT NULL,
|
||||
role ENUM('admin', 'publik') NOT NULL DEFAULT 'publik',
|
||||
full_name VARCHAR(150) NOT NULL,
|
||||
is_active TINYINT(1) NOT NULL DEFAULT 1,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS spbu (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
nama VARCHAR(150) NOT NULL,
|
||||
no_spbu VARCHAR(100) NOT NULL,
|
||||
status VARCHAR(50) NOT NULL,
|
||||
latitude DOUBLE NOT NULL,
|
||||
longitude DOUBLE NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS points (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
nama VARCHAR(150) NOT NULL,
|
||||
kode_point VARCHAR(100) NOT NULL,
|
||||
latitude DOUBLE NOT NULL,
|
||||
longitude DOUBLE NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS jalan (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
nama VARCHAR(150) NOT NULL,
|
||||
status VARCHAR(50) NOT NULL,
|
||||
panjang DOUBLE DEFAULT 0,
|
||||
geom LONGTEXT NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS parsil (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
nama VARCHAR(150) NOT NULL,
|
||||
status VARCHAR(50) NOT NULL,
|
||||
luas DOUBLE DEFAULT 0,
|
||||
geom LONGTEXT NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS masjid (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
nama VARCHAR(150) NOT NULL,
|
||||
pic VARCHAR(150) NOT NULL,
|
||||
radius DOUBLE DEFAULT 500,
|
||||
latitude DOUBLE NOT NULL,
|
||||
longitude DOUBLE NOT NULL,
|
||||
radius_layanan DOUBLE DEFAULT 500,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS penduduk_miskin (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
nama_kk VARCHAR(150) NOT NULL,
|
||||
jumlah_anggota INT DEFAULT 1,
|
||||
alamat TEXT NOT NULL,
|
||||
latitude DOUBLE NOT NULL,
|
||||
longitude DOUBLE NOT NULL,
|
||||
penghasilan VARCHAR(50),
|
||||
kondisi_rumah VARCHAR(50),
|
||||
sumber_air VARCHAR(50),
|
||||
skor_kerentanan DOUBLE DEFAULT 0,
|
||||
jenis_bantuan VARCHAR(100),
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
INSERT INTO users (username, password_hash, role, full_name, is_active) VALUES
|
||||
('admin', '$2y$10$X/2pN5ag6A/SmFlm/bZJq.V1Ul41mm3lX1PNDY0ZBncinKhEnQvpO', 'admin', 'Administrator WebGIS', 1),
|
||||
('publik', '$2y$10$X/2pN5ag6A/SmFlm/bZJq.V1Ul41mm3lX1PNDY0ZBncinKhEnQvpO', 'publik', 'Pengguna Publik', 1)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
password_hash = VALUES(password_hash),
|
||||
role = VALUES(role),
|
||||
full_name = VALUES(full_name),
|
||||
is_active = VALUES(is_active);
|
||||
@@ -0,0 +1,73 @@
|
||||
USE webgis_spbu;
|
||||
|
||||
INSERT INTO points (nama, kode_point, latitude, longitude)
|
||||
SELECT *
|
||||
FROM (
|
||||
SELECT 'Titik Survei Alun Kapuas' AS nama, 'P-001' AS kode_point, -0.023700 AS latitude, 109.342900 AS longitude
|
||||
UNION ALL SELECT 'Titik Survei Akcaya', 'P-002', -0.051200, 109.339000
|
||||
UNION ALL SELECT 'Titik Survei Siantan', 'P-003', -0.009500, 109.339500
|
||||
) AS seed
|
||||
WHERE NOT EXISTS (SELECT 1 FROM points LIMIT 1);
|
||||
|
||||
INSERT INTO spbu (nama, no_spbu, status, latitude, longitude)
|
||||
SELECT *
|
||||
FROM (
|
||||
SELECT 'SPBU Ahmad Yani Pontianak' AS nama, '64.781.01' AS no_spbu, '24 Jam' AS status, -0.045050 AS latitude, 109.345900 AS longitude
|
||||
UNION ALL SELECT 'SPBU Imam Bonjol', '64.781.02', '24 Jam', -0.036500, 109.333100
|
||||
UNION ALL SELECT 'SPBU Tanjungpura', '64.781.03', '24 Jam', -0.028950, 109.337800
|
||||
UNION ALL SELECT 'SPBU Sungai Jawi', '64.781.04', '24 Jam', -0.040100, 109.305400
|
||||
UNION ALL SELECT 'SPBU Siantan', '64.781.05', '24 Jam', -0.006700, 109.340800
|
||||
UNION ALL SELECT 'SPBU Paris II', '64.781.06', 'Tidak', -0.061800, 109.351500
|
||||
UNION ALL SELECT 'SPBU Tanjung Hulu', '64.781.07', 'Tidak', -0.023800, 109.364400
|
||||
) AS seed
|
||||
WHERE NOT EXISTS (SELECT 1 FROM spbu LIMIT 1);
|
||||
|
||||
INSERT INTO jalan (nama, status, panjang, geom)
|
||||
SELECT *
|
||||
FROM (
|
||||
SELECT 'Koridor Jalan Ahmad Yani' AS nama, 'Nasional' AS status, 1430.50 AS panjang, '[{"lat":-0.0565,"lng":109.3418},{"lat":-0.0493,"lng":109.3443},{"lat":-0.0402,"lng":109.3475}]' AS geom
|
||||
UNION ALL SELECT 'Koridor Jalan Tanjungpura', 'Provinsi', 1210.25, '[{"lat":-0.0339,"lng":109.3323},{"lat":-0.0304,"lng":109.3372},{"lat":-0.0275,"lng":109.3420}]'
|
||||
UNION ALL SELECT 'Koridor Jalan Kom Yos Sudarso', 'Kabupaten', 1655.75, '[{"lat":-0.0128,"lng":109.3142},{"lat":-0.0187,"lng":109.3237},{"lat":-0.0239,"lng":109.3338}]'
|
||||
) AS seed
|
||||
WHERE NOT EXISTS (SELECT 1 FROM jalan LIMIT 1);
|
||||
|
||||
INSERT INTO parsil (nama, status, luas, geom)
|
||||
SELECT *
|
||||
FROM (
|
||||
SELECT 'Parsil Kawasan GOR Pangsuma' AS nama, 'SHM' AS status, 24500.75 AS luas, '[[{"lat":-0.0493,"lng":109.3419},{"lat":-0.0470,"lng":109.3470},{"lat":-0.0514,"lng":109.3490},{"lat":-0.0535,"lng":109.3440}]]' AS geom
|
||||
UNION ALL SELECT 'Parsil Area Sungai Bangkong', 'HGB', 18250.40, '[[{"lat":-0.0340,"lng":109.3090},{"lat":-0.0308,"lng":109.3134},{"lat":-0.0346,"lng":109.3168},{"lat":-0.0381,"lng":109.3120}]]'
|
||||
UNION ALL SELECT 'Parsil Area Tanjung Hulu', 'HP', 21780.10, '[[{"lat":-0.0211,"lng":109.3634},{"lat":-0.0176,"lng":109.3673},{"lat":-0.0219,"lng":109.3712},{"lat":-0.0251,"lng":109.3669}]]'
|
||||
) AS seed
|
||||
WHERE NOT EXISTS (SELECT 1 FROM parsil LIMIT 1);
|
||||
|
||||
INSERT INTO masjid (nama, pic, radius, latitude, longitude, radius_layanan)
|
||||
SELECT *
|
||||
FROM (
|
||||
SELECT 'Masjid Raya Mujahidin' AS nama, 'H. Sulaiman Arif' AS pic, 900 AS radius, -0.032500 AS latitude, 109.338200 AS longitude, 900 AS radius_layanan
|
||||
UNION ALL SELECT 'Masjid Jami Sultan Syarif Abdurrahman', 'Ust. Rahman Hakim', 700, -0.020600, 109.338900, 700
|
||||
UNION ALL SELECT 'Masjid Al-Falah Sungai Jawi', 'H. Darmawan Yusuf', 820, -0.041900, 109.310500, 820
|
||||
) AS seed
|
||||
WHERE NOT EXISTS (SELECT 1 FROM masjid LIMIT 1);
|
||||
|
||||
INSERT INTO penduduk_miskin (
|
||||
nama_kk, jumlah_anggota, alamat, latitude, longitude,
|
||||
penghasilan, kondisi_rumah, sumber_air, skor_kerentanan, jenis_bantuan
|
||||
)
|
||||
SELECT *
|
||||
FROM (
|
||||
SELECT 'Rahmat Hidayat' AS nama_kk, 5 AS jumlah_anggota, 'Gang Karya Baru, Sungai Bangkong' AS alamat, -0.037800 AS latitude, 109.314100 AS longitude, '< 500rb' AS penghasilan, 'Kayu' AS kondisi_rumah, 'Sumur' AS sumber_air, 75.00 AS skor_kerentanan, 'BLT / Modal Usaha' AS jenis_bantuan
|
||||
UNION ALL SELECT 'Siti Aminah', 4, 'Jl. Tanjungpura Dalam', -0.030500, 109.337600, '500rb - 1jt', 'Kayu', 'PDAM', 42.00, 'BLT / Modal Usaha'
|
||||
UNION ALL SELECT 'Mulyadi Saputra', 6, 'Gang Sepakat, Benua Melayu Darat', -0.034300, 109.343600, '< 500rb', 'Bambu/Tepas', 'Sumur', 90.00, 'Bedah Rumah'
|
||||
UNION ALL SELECT 'Nuraini Hasanah', 3, 'Jl. Gajah Mada Gang Kecil', -0.026900, 109.335900, '500rb - 1jt', 'Tembok', 'PDAM', 30.00, 'Sembako'
|
||||
UNION ALL SELECT 'Agus Salim', 5, 'Kelurahan Tambelan Sampit', -0.019900, 109.340100, '< 500rb', 'Kayu', 'Sungai', 85.00, 'Air Bersih / PDAM'
|
||||
UNION ALL SELECT 'Yuliana Putri', 4, 'Sungai Jawi Dalam', -0.041200, 109.309900, '500rb - 1jt', 'Kayu', 'Sumur', 50.00, 'Sembako'
|
||||
UNION ALL SELECT 'Bambang Irawan', 7, 'Gang Bersama, Mariana', -0.028000, 109.326800, '< 500rb', 'Bambu/Tepas', 'Sungai', 100.00, 'Bedah Rumah'
|
||||
UNION ALL SELECT 'Kartini Rahma', 2, 'Jl. Dr. Sutomo Dalam', -0.033800, 109.331800, '> 1jt', 'Kayu', 'PDAM', 28.00, 'Beasiswa Pendidikan'
|
||||
UNION ALL SELECT 'Firdaus Ahmad', 5, 'Siantan Hilir Gang Masjid', -0.018400, 109.337600, '500rb - 1jt', 'Kayu', 'Sungai', 60.00, 'Air Bersih / PDAM'
|
||||
UNION ALL SELECT 'Mariam Abdullah', 4, 'Tanjung Hulu Gang Keluarga', -0.022300, 109.362800, '< 500rb', 'Kayu', 'Sumur', 75.00, 'BLT / Modal Usaha'
|
||||
UNION ALL SELECT 'Rudi Hartono', 6, 'Parit Mayor Dalam', -0.043800, 109.372900, '< 500rb', 'Bambu/Tepas', 'Sungai', 100.00, 'Bedah Rumah'
|
||||
UNION ALL SELECT 'Desi Wulandari', 3, 'Akcaya Gang Damai', -0.052200, 109.339600, '500rb - 1jt', 'Tembok', 'PDAM', 30.00, 'Beasiswa Pendidikan'
|
||||
UNION ALL SELECT 'Hendra Gunawan', 5, 'Sungai Raya Dalam, ujung permukiman', -0.079500, 109.395200, '< 500rb', 'Bambu/Tepas', 'Sungai', 100.00, 'Air Bersih / PDAM'
|
||||
UNION ALL SELECT 'Rosnawati Idris', 4, 'Pal Lima Gang Mandiri', -0.048800, 109.300500, '500rb - 1jt', 'Kayu', 'Sumur', 50.00, 'Sembako'
|
||||
) AS seed
|
||||
WHERE NOT EXISTS (SELECT 1 FROM penduduk_miskin LIMIT 1);
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
function start_app_session()
|
||||
{
|
||||
if (session_status() !== PHP_SESSION_ACTIVE) {
|
||||
session_start();
|
||||
}
|
||||
}
|
||||
|
||||
function current_user_role()
|
||||
{
|
||||
start_app_session();
|
||||
return $_SESSION['role'] ?? null;
|
||||
}
|
||||
|
||||
function user_has_role(array $allowed_roles)
|
||||
{
|
||||
$role = current_user_role();
|
||||
return $role !== null && in_array($role, $allowed_roles, true);
|
||||
}
|
||||
|
||||
function require_roles_json(array $allowed_roles)
|
||||
{
|
||||
if (!user_has_role($allowed_roles)) {
|
||||
http_response_code(401);
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode([
|
||||
"message" => "Akses ditolak. Silakan login dengan akun yang berwenang."
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
function require_write_access_json()
|
||||
{
|
||||
require_roles_json(['admin']);
|
||||
}
|
||||
|
||||
function require_roles_page(array $allowed_roles, $login_path = 'login.php')
|
||||
{
|
||||
if (!user_has_role($allowed_roles)) {
|
||||
header('Location: ' . $login_path);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
?>
|
||||
+20
-2
@@ -1,7 +1,25 @@
|
||||
<?php
|
||||
$conn = new mysqli("localhost", "root", "bobbyandreanjapri", "webgis_spbu");
|
||||
function env_value($keys, $default = '')
|
||||
{
|
||||
foreach ($keys as $key) {
|
||||
$value = getenv($key);
|
||||
if ($value !== false && $value !== '') {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
$db_host = env_value(['DB_HOST', 'MYSQL_HOST', 'MYSQL_SERVER'], 'localhost');
|
||||
$db_port = intval(env_value(['DB_PORT', 'MYSQL_PORT'], 3306));
|
||||
$db_name = env_value(['DB_DATABASE', 'MYSQL_DATABASE', 'MYSQL_DB'], 'webgis_spbu');
|
||||
$db_user = env_value(['DB_USERNAME', 'MYSQL_USER'], 'root');
|
||||
$db_pass = env_value(['DB_PASSWORD', 'MYSQL_PASSWORD', 'MYSQL_ROOT_PASSWORD'], '');
|
||||
|
||||
$conn = new mysqli($db_host, $db_user, $db_pass, $db_name, $db_port);
|
||||
if ($conn->connect_error) {
|
||||
die(json_encode(["error" => "Koneksi gagal: " . $conn->connect_error]));
|
||||
}
|
||||
$conn->set_charset("utf8");
|
||||
$conn->set_charset("utf8mb4");
|
||||
?>
|
||||
|
||||
+5
-2
@@ -1,4 +1,6 @@
|
||||
<?php
|
||||
require_once 'auth.php';
|
||||
require_write_access_json();
|
||||
header('Content-Type: application/json');
|
||||
include 'db.php';
|
||||
|
||||
@@ -11,7 +13,8 @@ if ($id == 0 || $type == '') {
|
||||
}
|
||||
|
||||
$table = '';
|
||||
if ($type == 'point') $table = 'spbu';
|
||||
if ($type == 'point') $table = 'points';
|
||||
if ($type == 'spbu') $table = 'spbu';
|
||||
if ($type == 'line') $table = 'jalan';
|
||||
if ($type == 'polygon') $table = 'parsil';
|
||||
if ($type == 'masjid') $table = 'masjid';
|
||||
@@ -30,4 +33,4 @@ if ($stmt->execute()) {
|
||||
} else {
|
||||
echo json_encode(["message" => "Gagal hapus", "error" => $stmt->error]);
|
||||
}
|
||||
?>
|
||||
?>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<?php
|
||||
require_once 'auth.php';
|
||||
require_write_access_json();
|
||||
header('Content-Type: application/json');
|
||||
include 'db.php';
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<?php
|
||||
require_once 'auth.php';
|
||||
require_write_access_json();
|
||||
header('Content-Type: application/json');
|
||||
include 'db.php';
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<?php
|
||||
require_once 'auth.php';
|
||||
require_write_access_json();
|
||||
header('Content-Type: application/json');
|
||||
include 'db.php';
|
||||
|
||||
@@ -22,4 +24,4 @@ if ($stmt->execute()) {
|
||||
echo json_encode(["message"=>"Gagal", "error"=>$stmt->error]);
|
||||
}
|
||||
$stmt->close(); $conn->close();
|
||||
?>
|
||||
?>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<?php
|
||||
require_once 'auth.php';
|
||||
require_write_access_json();
|
||||
header('Content-Type: application/json');
|
||||
include 'db.php';
|
||||
|
||||
@@ -48,4 +50,4 @@ if ($stmt->execute()) {
|
||||
echo json_encode(["message"=>"Gagal", "error"=>$stmt->error]);
|
||||
}
|
||||
$stmt->close(); $conn->close();
|
||||
?>
|
||||
?>
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
require_once 'auth.php';
|
||||
require_write_access_json();
|
||||
header('Content-Type: application/json');
|
||||
include 'db.php';
|
||||
|
||||
$data = json_decode(file_get_contents("php://input"), true);
|
||||
|
||||
if (!$data) {
|
||||
echo json_encode(["message" => "Data tidak diterima"]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$nama = $data['nama'] ?? '';
|
||||
$kode = $data['no'] ?? '';
|
||||
$lat = $data['lat'] ?? '';
|
||||
$lng = $data['lng'] ?? '';
|
||||
|
||||
if ($nama === '' || $kode === '' || $lat === '' || $lng === '') {
|
||||
echo json_encode(["message" => "Field tidak lengkap"]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$stmt = $conn->prepare("INSERT INTO points (nama, kode_point, latitude, longitude) VALUES (?, ?, ?, ?)");
|
||||
$stmt->bind_param("ssdd", $nama, $kode, $lat, $lng);
|
||||
|
||||
if ($stmt->execute()) {
|
||||
echo json_encode(["message" => "Data berhasil disimpan", "id" => $stmt->insert_id]);
|
||||
} else {
|
||||
echo json_encode(["message" => "Gagal simpan", "error" => $stmt->error]);
|
||||
}
|
||||
|
||||
$stmt->close();
|
||||
$conn->close();
|
||||
?>
|
||||
@@ -1,4 +1,6 @@
|
||||
<?php
|
||||
require_once 'auth.php';
|
||||
require_write_access_json();
|
||||
header('Content-Type: application/json');
|
||||
include 'db.php';
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ include 'db.php';
|
||||
$data = [];
|
||||
|
||||
/* POINT */
|
||||
$res = $conn->query("SELECT * FROM spbu");
|
||||
$res = $conn->query("SELECT id, nama, kode_point AS no_spbu, latitude, longitude FROM points");
|
||||
if ($res) {
|
||||
while ($r = $res->fetch_assoc()) {
|
||||
$r['type'] = 'point';
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<?php
|
||||
require_once 'auth.php';
|
||||
require_write_access_json();
|
||||
header('Content-Type: application/json');
|
||||
include 'db.php';
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<?php
|
||||
require_once 'auth.php';
|
||||
require_write_access_json();
|
||||
header('Content-Type: application/json');
|
||||
include 'db.php';
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<?php
|
||||
require_once 'auth.php';
|
||||
require_write_access_json();
|
||||
header('Content-Type: application/json');
|
||||
include 'db.php';
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<?php
|
||||
require_once 'auth.php';
|
||||
require_write_access_json();
|
||||
header('Content-Type: application/json');
|
||||
include 'db.php';
|
||||
|
||||
|
||||
+2
-5
@@ -1,9 +1,6 @@
|
||||
<?php
|
||||
session_start();
|
||||
if (!isset($_SESSION['is_admin']) || $_SESSION['is_admin'] !== true) {
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
require_once '../php/auth.php';
|
||||
require_roles_page(['admin']);
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="id">
|
||||
|
||||
@@ -87,13 +87,5 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- App script -->
|
||||
<script src="../js/draw_sosial.js?v=<?= time() ?>"></script>
|
||||
<script>
|
||||
window.setMode = function(mode) {
|
||||
console.log('Mode changed to: ' + mode);
|
||||
};
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+36
-12
@@ -1,23 +1,47 @@
|
||||
<?php
|
||||
session_start();
|
||||
include '../php/db.php';
|
||||
|
||||
// Kredensial Admin — bisa diganti sesuai kebutuhan
|
||||
$ADMIN_USER = 'admin';
|
||||
$ADMIN_PASS = 'admin123';
|
||||
|
||||
$error = '';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$user = $_POST['username'] ?? '';
|
||||
$user = trim($_POST['username'] ?? '');
|
||||
$pass = $_POST['password'] ?? '';
|
||||
if ($user === $ADMIN_USER && $pass === $ADMIN_PASS) {
|
||||
$_SESSION['is_admin'] = true;
|
||||
header('Location: admin.php');
|
||||
exit;
|
||||
|
||||
$stmt = $conn->prepare("SELECT id, username, password_hash, role, full_name, is_active FROM users WHERE username = ? LIMIT 1");
|
||||
if (!$stmt) {
|
||||
$error = 'Konfigurasi login belum siap. Jalankan migration database terlebih dahulu.';
|
||||
} else {
|
||||
$stmt->bind_param("s", $user);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
$account = $result ? $result->fetch_assoc() : null;
|
||||
|
||||
if ($account && intval($account['is_active']) === 1 && password_verify($pass, $account['password_hash'])) {
|
||||
session_regenerate_id(true);
|
||||
$_SESSION['user_id'] = intval($account['id']);
|
||||
$_SESSION['username'] = $account['username'];
|
||||
$_SESSION['role'] = $account['role'];
|
||||
$_SESSION['full_name'] = $account['full_name'];
|
||||
$_SESSION['is_admin'] = $account['role'] === 'admin';
|
||||
|
||||
if ($account['role'] === 'admin') {
|
||||
header('Location: admin.php');
|
||||
} else {
|
||||
header('Location: index.php');
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
$error = 'Username atau password salah.';
|
||||
$stmt->close();
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($conn)) {
|
||||
$conn->close();
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="id">
|
||||
@@ -182,12 +206,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
<div class="login-logo-icon">🕌</div>
|
||||
<div>
|
||||
<div class="login-logo-text">WEB<span>GIS</span> SOSIAL</div>
|
||||
<div class="login-logo-sub">Portal Administrasi</div>
|
||||
<div class="login-logo-sub">Portal Pengguna</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h1>🔐 Masuk sebagai Admin</h1>
|
||||
<p class="login-desc">Halaman ini khusus untuk petugas yang berwenang mengelola data penduduk dan masjid.</p>
|
||||
<h1>🔐 Masuk Pengguna</h1>
|
||||
<p class="login-desc">Gunakan akun sesuai role untuk mengakses dashboard publik atau panel pengelolaan data.</p>
|
||||
|
||||
<?php if ($error): ?>
|
||||
<div class="error-msg">⚠️ <?= htmlspecialchars($error) ?></div>
|
||||
@@ -200,7 +224,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
<label for="password">Password</label>
|
||||
<input type="password" id="password" name="password" placeholder="••••••••" required>
|
||||
|
||||
<button type="submit" class="btn-login">Masuk ke Panel Admin →</button>
|
||||
<button type="submit" class="btn-login">Masuk →</button>
|
||||
</form>
|
||||
|
||||
<a href="index.php" class="back-link">← Kembali ke Dashboard Publik</a>
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
require_once '../php/auth.php';
|
||||
require_roles_page(['admin']);
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="id">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Panel Admin - WebGIS SPBU</title>
|
||||
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Space+Mono:wght@400;700&family=DM+Sans:wght@300;400;500;600&display=swap" rel="stylesheet">
|
||||
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css"/>
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet-draw/dist/leaflet.draw.css"/>
|
||||
<link rel="stylesheet" href="../css/style.css"/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<header>
|
||||
<a href="index.php" class="back-btn" title="Lihat Dashboard Publik">←</a>
|
||||
<div class="logo">
|
||||
<div class="logo-icon" style="background:#3b82f6;">S</div>
|
||||
<div class="logo-text">ADMIN<span style="color:#60a5fa;"> SPBU</span></div>
|
||||
</div>
|
||||
<div class="header-divider"></div>
|
||||
<span style="font-size:12px;color:var(--muted);">Kelola Titik SPBU Pontianak</span>
|
||||
|
||||
<div class="legend">
|
||||
<div class="legend-item"><div class="legend-dot" style="background:#4ade80"></div> SPBU 24 Jam</div>
|
||||
<div class="legend-item"><div class="legend-dot" style="background:#f87171"></div> Tidak 24 Jam</div>
|
||||
<div class="legend-item"><div class="legend-dot" style="background:#3b82f6"></div> Titik SPBU</div>
|
||||
</div>
|
||||
|
||||
<div class="layer-sep"></div>
|
||||
<div class="layer-control">
|
||||
<button class="layer-btn btn-24 active" id="btn24" onclick="toggleLayer('24jam')">
|
||||
<span class="dot" style="background:#4ade80"></span>
|
||||
24 Jam
|
||||
</button>
|
||||
<button class="layer-btn btn-tidak active" id="btnTidak" onclick="toggleLayer('tidak')">
|
||||
<span class="dot" style="background:#f87171"></span>
|
||||
Tidak 24 Jam
|
||||
</button>
|
||||
<div class="layer-sep"></div>
|
||||
<a href="logout.php" class="layer-btn" style="text-decoration:none; color:#ef4444; border-color:#ef444433;">Keluar</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div id="map"></div>
|
||||
|
||||
<script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>
|
||||
<script src="https://unpkg.com/leaflet-draw/dist/leaflet.draw.js"></script>
|
||||
<script>window.IS_ADMIN = true;</script>
|
||||
<script src="../js/draw_spbu_point.js?v=<?= time() ?>"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
+18
-18
@@ -11,7 +11,6 @@
|
||||
|
||||
<!-- Leaflet -->
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css"/>
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet-draw/dist/leaflet.draw.css"/>
|
||||
|
||||
<!-- App styles -->
|
||||
<link rel="stylesheet" href="../css/style.css"/>
|
||||
@@ -27,7 +26,22 @@
|
||||
<div class="header-divider"></div>
|
||||
<span style="font-size:12px;color:var(--muted);">Pontianak, Kalimantan Barat</span>
|
||||
|
||||
<div class="legend">
|
||||
<div class="layer-control" style="margin-left:auto;">
|
||||
<button class="layer-btn btn-24 active" id="btn24" onclick="toggleLayer('24jam')">
|
||||
<span class="dot" style="background:#4ade80"></span>
|
||||
24 Jam
|
||||
</button>
|
||||
<button class="layer-btn btn-tidak active" id="btnTidak" onclick="toggleLayer('tidak')">
|
||||
<span class="dot" style="background:#f87171"></span>
|
||||
Tidak 24 Jam
|
||||
</button>
|
||||
<div class="layer-sep"></div>
|
||||
<a href="login.php" class="layer-btn" style="text-decoration:none; color:#60a5fa; border-color:#60a5fa33;">Admin Login</a>
|
||||
</div>
|
||||
|
||||
<div class="layer-sep"></div>
|
||||
|
||||
<div class="legend" style="margin-left:0;">
|
||||
<div class="legend-item">
|
||||
<div class="legend-dot" style="background:#4ade80"></div> SPBU 24 Jam
|
||||
</div>
|
||||
@@ -38,30 +52,16 @@
|
||||
<div class="legend-dot" style="background:#3b82f6"></div> Titik SPBU
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layer-sep"></div>
|
||||
|
||||
<!-- Toggle SPBU -->
|
||||
<div class="layer-control">
|
||||
<button class="layer-btn btn-24 active" id="btn24" onclick="toggleLayer('24jam')">
|
||||
<span class="dot" style="background:#4ade80"></span>
|
||||
24 Jam
|
||||
</button>
|
||||
<button class="layer-btn btn-tidak active" id="btnTidak" onclick="toggleLayer('tidak')">
|
||||
<span class="dot" style="background:#f87171"></span>
|
||||
Tidak 24 Jam
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div id="map"></div>
|
||||
|
||||
<!-- Leaflet JS -->
|
||||
<script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>
|
||||
<script src="https://unpkg.com/leaflet-draw/dist/leaflet.draw.js"></script>
|
||||
|
||||
<!-- App script -->
|
||||
<script src="../js/draw_spbu_point.js"></script>
|
||||
<script>window.IS_ADMIN = false;</script>
|
||||
<script src="../js/draw_spbu_point.js?v=<?= time() ?>"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
session_start();
|
||||
include '../php/db.php';
|
||||
|
||||
$error = '';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$user = trim($_POST['username'] ?? '');
|
||||
$pass = $_POST['password'] ?? '';
|
||||
|
||||
$stmt = $conn->prepare("SELECT id, username, password_hash, role, full_name, is_active FROM users WHERE username = ? LIMIT 1");
|
||||
if (!$stmt) {
|
||||
$error = 'Konfigurasi login belum siap. Jalankan migration database terlebih dahulu.';
|
||||
} else {
|
||||
$stmt->bind_param("s", $user);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
$account = $result ? $result->fetch_assoc() : null;
|
||||
|
||||
if ($account && intval($account['is_active']) === 1 && password_verify($pass, $account['password_hash'])) {
|
||||
session_regenerate_id(true);
|
||||
$_SESSION['user_id'] = intval($account['id']);
|
||||
$_SESSION['username'] = $account['username'];
|
||||
$_SESSION['role'] = $account['role'];
|
||||
$_SESSION['full_name'] = $account['full_name'];
|
||||
$_SESSION['is_admin'] = $account['role'] === 'admin';
|
||||
|
||||
header('Location: ' . ($account['role'] === 'admin' ? 'admin.php' : 'index.php'));
|
||||
exit;
|
||||
}
|
||||
|
||||
$error = 'Username atau password salah.';
|
||||
$stmt->close();
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($conn)) {
|
||||
$conn->close();
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="id">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Login Admin - WebGIS SPBU</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Space+Mono:wght@400;700&family=DM+Sans:wght@300;400;500;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
body { font-family:'DM Sans',sans-serif; background:#0a0d14; color:#e2e8f0; min-height:100vh; display:flex; align-items:center; justify-content:center; padding:20px; }
|
||||
body::before { content:''; position:fixed; inset:0; background-image:linear-gradient(rgba(59,130,246,.04) 1px, transparent 1px),linear-gradient(90deg, rgba(59,130,246,.04) 1px, transparent 1px); background-size:40px 40px; }
|
||||
.login-card { position:relative; z-index:1; width:100%; max-width:380px; background:#1a1d27; border:1px solid #2a2d3a; border-radius:16px; padding:36px 32px; box-shadow:0 25px 60px rgba(0,0,0,.5); }
|
||||
.login-logo { display:flex; align-items:center; gap:12px; margin-bottom:28px; }
|
||||
.login-logo-icon { width:44px; height:44px; background:linear-gradient(135deg,#1d4ed8,#3b82f6); border-radius:12px; display:flex; align-items:center; justify-content:center; font-weight:700; }
|
||||
.login-logo-text { font-family:'Space Mono',monospace; font-size:14px; font-weight:700; letter-spacing:.05em; }
|
||||
.login-logo-text span { color:#60a5fa; }
|
||||
.login-logo-sub { font-size:11px; color:#6b7280; margin-top:1px; }
|
||||
h1 { font-size:20px; margin-bottom:6px; }
|
||||
.login-desc { font-size:13px; color:#6b7280; margin-bottom:24px; line-height:1.5; }
|
||||
label { display:block; font-size:11px; color:#9ca3af; font-weight:600; letter-spacing:.05em; text-transform:uppercase; margin-bottom:6px; margin-top:16px; }
|
||||
input { width:100%; background:#0f1117; border:1px solid #2a2d3a; color:#e2e8f0; border-radius:8px; padding:10px 14px; font-size:14px; font-family:'DM Sans',sans-serif; outline:none; }
|
||||
input:focus { border-color:#60a5fa; }
|
||||
.error-msg { background:rgba(239,68,68,.1); border:1px solid rgba(239,68,68,.3); color:#f87171; border-radius:8px; padding:10px 14px; font-size:13px; margin-top:16px; }
|
||||
.btn-login { width:100%; margin-top:24px; padding:12px; background:linear-gradient(135deg,#1d4ed8,#3b82f6); color:#fff; border:0; border-radius:8px; font-size:14px; font-weight:700; font-family:'DM Sans',sans-serif; cursor:pointer; }
|
||||
.back-link { display:block; text-align:center; margin-top:16px; font-size:12px; color:#6b7280; text-decoration:none; }
|
||||
.back-link:hover { color:#e2e8f0; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="login-card">
|
||||
<div class="login-logo">
|
||||
<div class="login-logo-icon">S</div>
|
||||
<div>
|
||||
<div class="login-logo-text">WEB<span>GIS</span> SPBU</div>
|
||||
<div class="login-logo-sub">Portal Administrasi</div>
|
||||
</div>
|
||||
</div>
|
||||
<h1>Masuk Admin</h1>
|
||||
<p class="login-desc">Halaman ini khusus untuk pengelola data titik SPBU.</p>
|
||||
<?php if ($error): ?><div class="error-msg"><?= htmlspecialchars($error) ?></div><?php endif; ?>
|
||||
<form method="POST">
|
||||
<label for="username">Username</label>
|
||||
<input type="text" id="username" name="username" placeholder="admin" autocomplete="off" required>
|
||||
<label for="password">Password</label>
|
||||
<input type="password" id="password" name="password" placeholder="password" required>
|
||||
<button type="submit" class="btn-login">Masuk</button>
|
||||
</form>
|
||||
<a href="index.php" class="back-link">Kembali ke Dashboard Publik</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
session_start();
|
||||
session_destroy();
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
?>
|
||||
Reference in New Issue
Block a user