Files
webgis_geospesial/login.php
T
2026-06-11 13:17:31 +07:00

307 lines
16 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
session_start();
// Jika sudah login, redirect ke dashboard.php
if (isset($_SESSION['user_id'])) {
header('Location: dashboard.php');
exit;
}
include 'koneksi.php';
// Ambil pengaturan show_demo_accounts
$show_demo = 1; // default
try {
$stmt = $pdo->query("SELECT setting_value FROM settings WHERE setting_key = 'show_demo_accounts'");
if ($row = $stmt->fetch()) {
$show_demo = (int)$row['setting_value'];
}
} catch (Exception $e) {}
$error = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = trim($_POST['username'] ?? ''); // Bisa email atau username
$password = trim($_POST['password'] ?? '');
if ($username && $password) {
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ? OR email = ?");
$stmt->execute([$username, $username]);
$user = $stmt->fetch();
if ($user && password_verify($password, $user['password'])) {
// Cek status pending
if ($user['status'] === 'pending') {
$error = 'Akun Anda sedang menunggu persetujuan Admin.';
} else {
$_SESSION['user_id'] = $user['id'];
$_SESSION['username'] = $user['username'];
$_SESSION['role'] = $user['role'];
header('Location: dashboard.php');
exit;
}
} else {
$error = 'Email/Username atau password salah.';
}
} else {
$error = 'Silakan isi Email/Username dan password.';
}
}
?>
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login WebGIS Pengentasan Kemiskinan</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script>
tailwind.config = {
theme: {
extend: {
fontFamily: {
sans: ['"Plus Jakarta Sans"', 'sans-serif'],
heading: ['"Plus Jakarta Sans"', 'sans-serif'],
mono: ['"JetBrains Mono"', 'monospace'],
},
colors: {
background: '#f8fafc',
foreground: '#0f172a',
primary: { DEFAULT: '#e11d48', foreground: '#ffffff' },
muted: { DEFAULT: '#f1f5f9', foreground: '#64748b' },
border: '#e2e8f0',
accent: '#e11d48',
}
}
}
}
</script>
<style>
.group-focus-within\:text-primary:focus-within { color: #e11d48; }
.focus-visible\:ring-primary\/20:focus-visible { --tw-ring-color: rgba(225, 29, 72, 0.2); }
.hover\:border-primary\/60:hover { border-color: rgba(225, 29, 72, 0.6); }
.focus-visible\:border-primary:focus-visible { border-color: #e11d48; }
/* Checkbox styling */
input[type="checkbox"]:checked {
background-color: #e11d48;
border-color: #e11d48;
background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e");
}
/* Custom Pink Scrollbar */
::-webkit-scrollbar { width: 6px; height: 6px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: #e11d48; border-radius: 4px; }
::-webkit-scrollbar-thumb:hover { background: #be123c; }
</style>
</head>
<body>
<?php if ($error): ?>
<script>
document.addEventListener('DOMContentLoaded', function() {
Swal.fire({
icon: 'error',
title: 'Akses Ditolak',
text: '<?= htmlspecialchars($error) ?>',
confirmButtonColor: '#e11d48'
});
});
</script>
<?php endif; ?>
<main class="flex h-screen w-full overflow-hidden bg-background font-sans text-foreground">
<!-- Left: hero / map -->
<section class="relative hidden w-1/2 overflow-hidden lg:block">
<img src="assets/img/map-bg.png" alt="Visualisasi peta analisis spasial" class="absolute inset-0 object-cover w-full h-full" onerror="this.src='https://images.unsplash.com/photo-1524661135-423995f22d0b?q=80&w=2074&auto=format&fit=crop'" />
<div aria-hidden="true" class="absolute inset-0 bg-gradient-to-br from-red-900/85 via-red-800/80 to-rose-900/90"></div>
<div aria-hidden="true" class="absolute inset-0 opacity-[0.06]" style="background-image: linear-gradient(to right, white 1px, transparent 1px), linear-gradient(to bottom, white 1px, transparent 1px); background-size: 52px 52px;"></div>
<div class="relative z-10 flex h-full flex-col justify-between p-12 xl:p-16">
<div class="flex items-center gap-2.5">
<div class="flex h-9 w-9 items-center justify-center rounded-xl bg-white/15 backdrop-blur-sm ring-1 ring-white/25">
<svg viewBox="0 0 24 24" fill="none" class="h-5 w-5 text-white" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M12 21s-7-5.6-7-11a7 7 0 1 1 14 0c0 5.4-7 11-7 11Z" />
<circle cx="12" cy="10" r="2.5" />
</svg>
</div>
<div>
<h1 class="font-heading text-sm font-bold tracking-wide text-white shadow-sm">WebGIS Geospasial</h1>
<p class="text-[11px] font-medium text-white/70 uppercase tracking-wider">Kementerian Analisis Spasial</p>
</div>
</div>
<div>
<h2 class="font-heading text-4xl font-bold leading-tight tracking-tight text-white xl:text-5xl">
Sistem Informasi <br> Geografis <span class="text-rose-200">Terpadu</span>
</h2>
<p class="mt-4 max-w-md leading-relaxed text-rose-100/90 text-sm xl:text-base">
Platform pemetaan cerdas untuk analisis spasial persebaran infrastruktur, demografi kemiskinan, dan evaluasi penyaluran bantuan secara real-time.
</p>
</div>
</div>
</section>
<!-- Right: Login Form -->
<section class="flex w-full lg:w-1/2 h-full overflow-y-auto p-6">
<div class="w-full max-w-sm sm:max-w-md m-auto">
<!-- Mobile branding -->
<div class="mb-10 flex flex-col items-center gap-3 lg:hidden">
<div class="flex h-12 w-12 items-center justify-center rounded-2xl bg-gradient-to-br from-primary to-rose-700 text-white shadow-md shadow-primary/20">
<svg viewBox="0 0 24 24" fill="none" class="h-6 w-6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M12 21s-7-5.6-7-11a7 7 0 1 1 14 0c0 5.4-7 11-7 11Z" />
<circle cx="12" cy="10" r="2.5" />
</svg>
</div>
<div class="text-center">
<h1 class="font-heading text-lg font-bold text-foreground">WebGIS Geospasial</h1>
<p class="text-xs font-medium text-muted-foreground uppercase tracking-wider">Kementerian Analisis Spasial</p>
</div>
</div>
<!-- Header -->
<div class="mb-8">
<h2 class="font-heading text-2xl font-bold tracking-tight text-foreground sm:text-3xl">Selamat Datang Kembali</h2>
<p class="mt-2 text-sm text-muted-foreground">Silakan masuk menggunakan kredensial Anda untuk melanjutkan ke dashboard.</p>
</div>
<!-- Card -->
<div class="rounded-2xl border border-white/60 bg-white/70 p-6 shadow-[0_8px_30px_rgb(0,0,0,0.04)] backdrop-blur-xl sm:p-8">
<form action="login.php" method="POST" class="flex flex-col gap-5">
<!-- Input Email / Username -->
<div class="group">
<label for="username" class="mb-1.5 block text-sm font-semibold text-foreground group-focus-within:text-primary transition-colors">
Email atau Username
</label>
<div class="relative">
<div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3.5 text-muted-foreground group-focus-within:text-primary transition-colors">
<svg viewBox="0 0 24 24" fill="none" class="h-4.5 w-4.5" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"></path>
<circle cx="12" cy="7" r="4"></circle>
</svg>
</div>
<input type="text" id="username" name="username" class="block w-full rounded-xl border border-border bg-white py-2.5 pl-10 pr-3 text-sm text-foreground transition-all focus:outline-none focus-visible:border-primary focus-visible:ring-4 focus-visible:ring-primary/20 hover:border-primary/60" placeholder="admin@domain.com" required autocomplete="username">
</div>
</div>
<!-- Input Password -->
<div class="group">
<label for="password" class="mb-1.5 block text-sm font-semibold text-foreground group-focus-within:text-primary transition-colors">
Kata Sandi
</label>
<div class="relative">
<div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3.5 text-muted-foreground group-focus-within:text-primary transition-colors">
<svg viewBox="0 0 24 24" fill="none" class="h-4.5 w-4.5" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect>
<path d="M7 11V7a5 5 0 0 1 10 0v4"></path>
</svg>
</div>
<input type="password" id="password" name="password" class="block w-full rounded-xl border border-border bg-white py-2.5 pl-10 pr-10 text-sm text-foreground transition-all focus:outline-none focus-visible:border-primary focus-visible:ring-4 focus-visible:ring-primary/20 hover:border-primary/60" placeholder="••••••••" required autocomplete="current-password">
<button type="button" id="togglePassword" class="absolute inset-y-0 right-0 flex items-center pr-3.5 text-muted-foreground hover:text-foreground focus:outline-none" aria-label="Toggle password visibility">
<svg id="eyeIcon" viewBox="0 0 24 24" fill="none" class="h-4.5 w-4.5" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path>
<circle cx="12" cy="12" r="3"></circle>
</svg>
</button>
</div>
</div>
<!-- Options -->
<div class="flex items-center justify-between mt-1">
<label class="flex items-center gap-2 cursor-pointer group">
<input type="checkbox" name="remember" class="h-4 w-4 rounded border-border text-primary shadow-sm transition-all focus:ring-primary focus:ring-offset-0 disabled:cursor-not-allowed disabled:opacity-50">
<span class="text-sm font-medium text-muted-foreground group-hover:text-foreground transition-colors">Ingat saya</span>
</label>
<a href="#" class="text-sm font-semibold text-primary transition-colors hover:text-primary/80">
Lupa sandi?
</a>
</div>
<!-- Submit -->
<button type="submit" class="group mt-1 flex h-12 items-center justify-center gap-2 rounded-xl bg-primary text-sm font-semibold text-white shadow-lg shadow-primary/25 transition-all duration-300 hover:-translate-y-0.5 hover:bg-rose-700 hover:shadow-xl hover:shadow-primary/35 active:translate-y-0">
Masuk
<svg class="h-4 w-4 transition-transform duration-300 group-hover:translate-x-1 text-white" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<line x1="5" y1="12" x2="19" y2="12"></line>
<polyline points="12 5 19 12 12 19"></polyline>
</svg>
</button>
</form>
<div class="mt-6 text-center text-sm text-muted-foreground">
Belum punya akun? <a href="register.php" class="font-semibold text-primary hover:underline">Daftar sekarang</a>
</div>
<?php if ($show_demo === 1): ?>
<!-- Demo accounts helper -->
<div class="mt-7 rounded-xl border border-border/70 bg-gray-50 p-4">
<div class="mb-3 flex items-center gap-2">
<span class="h-1.5 w-1.5 rounded-full bg-primary"></span>
<p class="text-xs font-bold uppercase tracking-wide text-foreground">Info Akun Demo</p>
<span class="text-[11px] font-medium text-muted-foreground">· klik untuk mengisi</span>
</div>
<div class="flex flex-col gap-1.5">
<button type="button" onclick="fillDemo('admin', 'password')" class="group flex cursor-pointer items-center justify-between gap-3 rounded-lg border border-transparent bg-white/60 px-3 py-2 text-left transition-all hover:border-primary/30 hover:bg-white hover:shadow-sm w-full">
<span class="flex items-center gap-2.5">
<span aria-hidden="true" class="text-sm leading-none">👑</span>
<span class="text-xs font-semibold text-foreground">Admin</span>
</span>
<span class="font-mono text-[11px] text-muted-foreground transition-colors group-hover:text-primary">
admin · password
</span>
</button>
<button type="button" onclick="fillDemo('petugas', 'password')" class="group flex cursor-pointer items-center justify-between gap-3 rounded-lg border border-transparent bg-white/60 px-3 py-2 text-left transition-all hover:border-primary/30 hover:bg-white hover:shadow-sm w-full">
<span class="flex items-center gap-2.5">
<span aria-hidden="true" class="text-sm leading-none">📝</span>
<span class="text-xs font-semibold text-foreground">Petugas</span>
</span>
<span class="font-mono text-[11px] text-muted-foreground transition-colors group-hover:text-primary">
petugas · password
</span>
</button>
<button type="button" onclick="fillDemo('guest', 'password')" class="group flex cursor-pointer items-center justify-between gap-3 rounded-lg border border-transparent bg-white/60 px-3 py-2 text-left transition-all hover:border-primary/30 hover:bg-white hover:shadow-sm w-full">
<span class="flex items-center gap-2.5">
<span aria-hidden="true" class="text-sm leading-none">👁️</span>
<span class="text-xs font-semibold text-foreground">Guest</span>
</span>
<span class="font-mono text-[11px] text-muted-foreground transition-colors group-hover:text-primary">
guest · password
</span>
</button>
</div>
</div>
<?php endif; ?>
</div>
<p class="mt-6 text-center text-xs text-muted-foreground">
&copy; <script>document.write(new Date().getFullYear())</script> WebGIS Geospasial &middot; Kementerian Analisis Spasial
</p>
</div>
</section>
</main>
<script>
// Toggle Password Visibility
const toggleBtn = document.getElementById('togglePassword');
const passInput = document.getElementById('password');
const eyeIcon = document.getElementById('eyeIcon');
if (toggleBtn && passInput) {
toggleBtn.addEventListener('click', function() {
if (passInput.type === 'password') {
passInput.type = 'text';
eyeIcon.innerHTML = '<path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"></path><line x1="1" y1="1" x2="23" y2="23"></line>';
} else {
passInput.type = 'password';
eyeIcon.innerHTML = '<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path><circle cx="12" cy="12" r="3"></circle>';
}
});
}
function fillDemo(user, pass) {
document.getElementById('username').value = user;
document.getElementById('password').value = pass;
}
</script>
</body>
</html>