Files
UAS_WEBGIS_CindyDyraIslamay…/03PovertyMapping/login.html
T

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>