315 lines
11 KiB
HTML
315 lines
11 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="id">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
|
<meta name="mobile-web-app-capable" content="yes">
|
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
|
<meta name="apple-mobile-web-app-status-bar-style" content="default">
|
|
<title>Login — WebGIS Poverty Mapping</title>
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@300;400;500;600;700&family=DM+Mono:wght@400;500&display=swap" rel="stylesheet">
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"/>
|
|
<style>
|
|
:root {
|
|
--bg:#eef0f5; --surface:#fff; --border:#e2e6ef; --border-subtle:#edf0f6;
|
|
--text-primary:#0f1623; --text-secondary:#5a6478; --text-muted:#9ba4b5;
|
|
--accent:#3a56d4; --accent-hover:#2d45b8; --accent-light:#eaedfc;
|
|
--accent-subtle:rgba(58,86,212,0.09);
|
|
--success:#0b9e73; --danger:#d63230;
|
|
--shadow-sm:0 1px 4px rgba(15,22,35,0.07),0 1px 2px rgba(15,22,35,0.04);
|
|
--shadow-md:0 4px 16px rgba(15,22,35,0.08),0 2px 6px rgba(15,22,35,0.04);
|
|
--shadow-lg:0 12px 36px rgba(15,22,35,0.11),0 4px 12px rgba(15,22,35,0.06);
|
|
--r-sm:7px; --r:11px; --r-lg:15px; --r-xl:20px;
|
|
--touch-min:44px;
|
|
}
|
|
|
|
*,*::before,*::after { margin:0; padding:0; box-sizing:border-box; }
|
|
|
|
body {
|
|
font-family:'DM Sans',-apple-system,BlinkMacSystemFont,sans-serif;
|
|
background:var(--bg);
|
|
min-height:100vh;
|
|
min-height:calc(var(--vh,1vh)*100);
|
|
display:flex; align-items:center; justify-content:center;
|
|
-webkit-font-smoothing:antialiased;
|
|
background-image:
|
|
radial-gradient(circle at 15% 50%, rgba(58,86,212,0.07) 0%,transparent 55%),
|
|
radial-gradient(circle at 85% 20%, rgba(11,158,115,0.05) 0%,transparent 50%);
|
|
/* Safe area for notched phones */
|
|
padding:env(safe-area-inset-top) env(safe-area-inset-right)
|
|
env(safe-area-inset-bottom) env(safe-area-inset-left);
|
|
overflow-y:auto;
|
|
}
|
|
|
|
/* ===== WRAP ===== */
|
|
.login-wrap {
|
|
width:100%; max-width:400px;
|
|
padding:20px 16px;
|
|
/* On small screens center vertically with flex */
|
|
}
|
|
|
|
/* ===== LOGO ===== */
|
|
.login-logo { text-align:center; margin-bottom:24px; }
|
|
.logo-icon {
|
|
width:52px; height:52px; background:var(--accent); border-radius:14px;
|
|
display:inline-flex; align-items:center; justify-content:center;
|
|
color:white; font-size:21px; margin-bottom:12px;
|
|
box-shadow:0 6px 20px rgba(58,86,212,0.32);
|
|
}
|
|
.login-logo h1 {
|
|
font-size:19px; font-weight:700; color:var(--text-primary); letter-spacing:-0.01em;
|
|
}
|
|
.login-logo p { font-size:12px; color:var(--text-muted); margin-top:3px; }
|
|
|
|
/* ===== CARD ===== */
|
|
.card {
|
|
background:var(--surface); border-radius:var(--r-xl);
|
|
padding:24px 22px; box-shadow:var(--shadow-lg); border:1px solid var(--border);
|
|
}
|
|
|
|
/* ===== FORM ===== */
|
|
.form-group { margin-bottom:15px; }
|
|
label {
|
|
display:block; font-size:11px; font-weight:700; color:var(--text-secondary);
|
|
text-transform:uppercase; letter-spacing:0.04em; margin-bottom:6px;
|
|
}
|
|
.input-wrap { position:relative; }
|
|
.input-wrap i.icon {
|
|
position:absolute; left:13px; top:50%; transform:translateY(-50%);
|
|
color:var(--text-muted); font-size:13px; pointer-events:none; z-index:1;
|
|
}
|
|
.input-wrap input {
|
|
width:100%; padding:12px 12px 12px 40px;
|
|
border:1.5px solid var(--border); border-radius:var(--r-sm);
|
|
/* 16px prevents iOS auto-zoom */
|
|
font-size:16px; font-family:'DM Sans',sans-serif;
|
|
color:var(--text-primary); background:#f9fafe;
|
|
transition:all 0.15s; outline:none;
|
|
min-height:var(--touch-min);
|
|
-webkit-appearance:none;
|
|
}
|
|
.input-wrap input:focus {
|
|
border-color:var(--accent); background:var(--surface);
|
|
box-shadow:0 0 0 3px var(--accent-subtle);
|
|
}
|
|
.input-wrap input.error { border-color:var(--danger); background:#fff8f8; }
|
|
.input-wrap .toggle-pass {
|
|
position:absolute; right:0; top:0; bottom:0;
|
|
width:44px; display:flex; align-items:center; justify-content:center;
|
|
color:var(--text-muted); cursor:pointer; font-size:13px; border:none;
|
|
background:none; padding:0; transition:color 0.14s;
|
|
border-radius:0 var(--r-sm) var(--r-sm) 0;
|
|
touch-action:manipulation; -webkit-tap-highlight-color:transparent;
|
|
}
|
|
.input-wrap .toggle-pass:hover { color:var(--accent); }
|
|
|
|
/* ===== ERROR ALERT ===== */
|
|
.alert-error {
|
|
background:#fff0f0; border:1px solid #fcc; color:var(--danger);
|
|
border-radius:var(--r-sm); padding:10px 13px;
|
|
font-size:13px; margin-bottom:15px;
|
|
display:none; align-items:center; gap:8px;
|
|
animation:slideDown 0.18s ease;
|
|
}
|
|
.alert-error.show { display:flex; }
|
|
@keyframes slideDown { from{opacity:0;transform:translateY(-6px)} to{opacity:1;transform:none} }
|
|
|
|
/* ===== BUTTON ===== */
|
|
.btn-login {
|
|
width:100%; padding:13px; border:none; border-radius:var(--r-sm);
|
|
background:var(--accent); color:white; font-size:15px; font-weight:700;
|
|
font-family:'DM Sans',sans-serif; cursor:pointer;
|
|
display:flex; align-items:center; justify-content:center; gap:9px;
|
|
transition:all 0.15s; margin-top:4px;
|
|
box-shadow:0 4px 14px rgba(58,86,212,0.28);
|
|
min-height:var(--touch-min);
|
|
touch-action:manipulation; -webkit-tap-highlight-color:transparent;
|
|
}
|
|
.btn-login:hover:not(:disabled) {
|
|
background:var(--accent-hover);
|
|
box-shadow:0 6px 20px rgba(58,86,212,0.38);
|
|
transform:translateY(-1px);
|
|
}
|
|
.btn-login:active:not(:disabled) { transform:translateY(0); }
|
|
.btn-login:disabled { opacity:0.65; cursor:not-allowed; transform:none; box-shadow:none; }
|
|
@keyframes spin { to{transform:rotate(360deg)} }
|
|
.spinner { animation:spin 0.8s linear infinite; display:inline-block; }
|
|
|
|
/* ===== PUBLIC LINK ===== */
|
|
.public-link {
|
|
text-align:center; margin-top:14px; font-size:12.5px; color:var(--text-muted);
|
|
}
|
|
.public-link a {
|
|
color:var(--accent); text-decoration:none; font-weight:600;
|
|
padding:2px 4px; border-radius:4px;
|
|
}
|
|
.public-link a:hover { text-decoration:underline; }
|
|
|
|
/* ===== RESPONSIVE ===== */
|
|
|
|
/* Phones: full-width, tighter padding */
|
|
@media (max-width:440px) {
|
|
.login-wrap { padding:16px 12px; max-width:100%; }
|
|
.card { padding:20px 16px; border-radius:var(--r-lg); }
|
|
.login-logo { margin-bottom:18px; }
|
|
.login-logo h1 { font-size:17px; }
|
|
.logo-icon { width:46px; height:46px; font-size:19px; }
|
|
}
|
|
|
|
/* Very small phones */
|
|
@media (max-width:360px) {
|
|
.login-wrap { padding:12px 10px; }
|
|
.card { padding:16px 14px; }
|
|
}
|
|
|
|
/* Landscape phones: reduce vertical spacing */
|
|
@media (max-height:600px) and (orientation:landscape) {
|
|
.login-logo { margin-bottom:14px; }
|
|
.login-logo .logo-icon { margin-bottom:8px; }
|
|
.card { padding:18px 20px; }
|
|
.form-group { margin-bottom:11px; }
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<div class="login-wrap">
|
|
<div class="login-logo">
|
|
<div class="logo-icon"><i class="fas fa-map-marked-alt"></i></div>
|
|
<h1>WebGIS Poverty Mapping</h1>
|
|
<p>Sistem Pemetaan Kemiskinan v2.0</p>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<div class="alert-error" id="alertError">
|
|
<i class="fas fa-exclamation-circle"></i>
|
|
<span id="alertText">Username atau password salah.</span>
|
|
</div>
|
|
|
|
<form id="loginForm" novalidate>
|
|
<div class="form-group">
|
|
<label>Username</label>
|
|
<div class="input-wrap">
|
|
<i class="fas fa-user icon"></i>
|
|
<input type="text" id="email" placeholder="admin / petugas"
|
|
autocomplete="username" required>
|
|
</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Password</label>
|
|
<div class="input-wrap">
|
|
<i class="fas fa-lock icon"></i>
|
|
<input type="password" id="password" placeholder="••••••••"
|
|
autocomplete="current-password" required>
|
|
<button type="button" class="toggle-pass" id="togglePass" tabindex="-1" aria-label="Tampilkan password">
|
|
<i class="fas fa-eye" id="togglePassIcon"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<button type="submit" class="btn-login" id="loginBtn">
|
|
<i class="fas fa-sign-in-alt"></i> Masuk
|
|
</button>
|
|
</form>
|
|
</div>
|
|
|
|
<div class="public-link">
|
|
Masyarakat umum? <a href="lapor.html"><i class="fas fa-flag"></i> Buat Laporan Publik</a>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
'use strict';
|
|
|
|
// Fix 100vh on iOS
|
|
(function() {
|
|
function setVH() {
|
|
document.documentElement.style.setProperty('--vh', (window.innerHeight * 0.01) + 'px');
|
|
}
|
|
setVH();
|
|
window.addEventListener('resize', setVH);
|
|
})();
|
|
|
|
// ---- If already logged in, skip to index.html ----
|
|
(async () => {
|
|
try {
|
|
const r = await fetch('api/auth/check.php');
|
|
const d = await r.json();
|
|
if (d?.data?.logged_in) window.location.replace('index.html');
|
|
} catch { /* not logged in */ }
|
|
})();
|
|
|
|
// ---- Password toggle ----
|
|
document.getElementById('togglePass').addEventListener('click', () => {
|
|
const inp = document.getElementById('password');
|
|
const icon = document.getElementById('togglePassIcon');
|
|
const show = inp.type === 'password';
|
|
inp.type = show ? 'text' : 'password';
|
|
icon.className = show ? 'fas fa-eye-slash' : 'fas fa-eye';
|
|
});
|
|
|
|
// ---- Submit ----
|
|
const form = document.getElementById('loginForm');
|
|
const btn = document.getElementById('loginBtn');
|
|
const alertEl = document.getElementById('alertError');
|
|
const alertTx = document.getElementById('alertText');
|
|
|
|
function showError(msg) {
|
|
alertTx.textContent = msg;
|
|
alertEl.className = 'alert-error show';
|
|
document.getElementById('password').classList.add('error');
|
|
}
|
|
function hideError() {
|
|
alertEl.className = 'alert-error';
|
|
document.getElementById('email').classList.remove('error');
|
|
document.getElementById('password').classList.remove('error');
|
|
}
|
|
|
|
form.addEventListener('submit', async (e) => {
|
|
e.preventDefault();
|
|
hideError();
|
|
|
|
const email = document.getElementById('email').value.trim();
|
|
const password = document.getElementById('password').value;
|
|
|
|
if (!email || !password) {
|
|
showError('Username dan password harus diisi.');
|
|
return;
|
|
}
|
|
|
|
btn.disabled = true;
|
|
btn.innerHTML = '<i class="fas fa-circle-notch spinner"></i> Masuk...';
|
|
|
|
// Blur to hide keyboard on mobile before navigation
|
|
document.activeElement?.blur();
|
|
|
|
try {
|
|
const res = await fetch('api/auth/check.php?action=login', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ email, password }),
|
|
});
|
|
const data = await res.json();
|
|
|
|
if (data.success) {
|
|
btn.innerHTML = '<i class="fas fa-check"></i> Berhasil!';
|
|
window.location.replace('index.html');
|
|
} else {
|
|
showError(data.message || 'Username atau password salah.');
|
|
btn.disabled = false;
|
|
btn.innerHTML = '<i class="fas fa-sign-in-alt"></i> Masuk';
|
|
}
|
|
} catch {
|
|
showError('Koneksi gagal. Periksa server PHP.');
|
|
btn.disabled = false;
|
|
btn.innerHTML = '<i class="fas fa-sign-in-alt"></i> Masuk';
|
|
}
|
|
});
|
|
|
|
// Enter on email → focus password
|
|
document.getElementById('email').addEventListener('keydown', (e) => {
|
|
if (e.key === 'Enter') { e.preventDefault(); document.getElementById('password').focus(); }
|
|
});
|
|
</script>
|
|
</body>
|
|
</html> |