First commit
This commit is contained in:
@@ -0,0 +1,846 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="id">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="Login – WebGIS Pemetaan Kemiskinan Berbasis Partisipasi Rumah Ibadah">
|
||||
<title>Masuk – WebGIS Poverty Mapping</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@300;400;500;600;700;800&display=swap"
|
||||
rel="stylesheet">
|
||||
<style>
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
:root {
|
||||
--bg: #0a0e1a;
|
||||
--panel: #0f1422;
|
||||
--card: #141928;
|
||||
--border: rgba(255, 255, 255, .07);
|
||||
--blue: #4f7cff;
|
||||
--blue-dim: rgba(79, 124, 255, .12);
|
||||
--green: #22d3a5;
|
||||
--orange: #ff8c42;
|
||||
--purple: #9b6dff;
|
||||
--text: #e8edf5;
|
||||
--muted: #5c6880;
|
||||
--sub: #8a96b0;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
min-height: 100vh;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 480px;
|
||||
}
|
||||
|
||||
/* ── LEFT PANEL ── */
|
||||
.left-panel {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background: #080c18;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
padding: 48px;
|
||||
}
|
||||
|
||||
.map-bg {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background:
|
||||
radial-gradient(ellipse 70% 60% at 30% 40%, rgba(79, 124, 255, .18) 0%, transparent 65%),
|
||||
radial-gradient(ellipse 50% 70% at 70% 70%, rgba(34, 211, 165, .1) 0%, transparent 60%),
|
||||
radial-gradient(ellipse 40% 40% at 60% 20%, rgba(155, 109, 255, .08) 0%, transparent 55%);
|
||||
}
|
||||
|
||||
/* Animated grid lines */
|
||||
.grid-lines {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-image:
|
||||
linear-gradient(rgba(255, 255, 255, .025) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(255, 255, 255, .025) 1px, transparent 1px);
|
||||
background-size: 60px 60px;
|
||||
animation: gridShift 20s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes gridShift {
|
||||
from {
|
||||
background-position: 0 0;
|
||||
}
|
||||
|
||||
to {
|
||||
background-position: 60px 60px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Floating map dots */
|
||||
.dot {
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.dot-1 {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background: var(--blue);
|
||||
box-shadow: 0 0 20px var(--blue), 0 0 40px rgba(79, 124, 255, .4);
|
||||
top: 32%;
|
||||
left: 28%;
|
||||
animation: pulseDot 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.dot-2 {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background: var(--green);
|
||||
box-shadow: 0 0 15px var(--green);
|
||||
top: 55%;
|
||||
left: 55%;
|
||||
animation: pulseDot 3s ease-in-out infinite 1s;
|
||||
}
|
||||
|
||||
.dot-3 {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
background: var(--orange);
|
||||
box-shadow: 0 0 15px var(--orange);
|
||||
top: 22%;
|
||||
left: 60%;
|
||||
animation: pulseDot 3s ease-in-out infinite 2s;
|
||||
}
|
||||
|
||||
.dot-4 {
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
background: var(--purple);
|
||||
box-shadow: 0 0 12px var(--purple);
|
||||
top: 68%;
|
||||
left: 35%;
|
||||
animation: pulseDot 3s ease-in-out infinite .5s;
|
||||
}
|
||||
|
||||
/* Connecting lines between dots */
|
||||
.conn-lines {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.conn-lines svg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@keyframes pulseDot {
|
||||
|
||||
0%,
|
||||
100% {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: scale(1.4);
|
||||
opacity: .6;
|
||||
}
|
||||
}
|
||||
|
||||
/* Ripple rings on dots */
|
||||
.dot::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: -8px;
|
||||
border-radius: 50%;
|
||||
border: 1.5px solid currentColor;
|
||||
opacity: 0;
|
||||
animation: ripple 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.dot-1::after {
|
||||
color: var(--blue);
|
||||
}
|
||||
|
||||
.dot-2::after {
|
||||
color: var(--green);
|
||||
animation-delay: 1s;
|
||||
}
|
||||
|
||||
.dot-3::after {
|
||||
color: var(--orange);
|
||||
animation-delay: 2s;
|
||||
}
|
||||
|
||||
.dot-4::after {
|
||||
color: var(--purple);
|
||||
animation-delay: .5s;
|
||||
}
|
||||
|
||||
@keyframes ripple {
|
||||
0% {
|
||||
inset: -4px;
|
||||
opacity: .7;
|
||||
}
|
||||
|
||||
100% {
|
||||
inset: -24px;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.left-content {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.left-tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
background: rgba(79, 124, 255, .15);
|
||||
border: 1px solid rgba(79, 124, 255, .3);
|
||||
border-radius: 20px;
|
||||
padding: 5px 14px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
color: #7ba4ff;
|
||||
letter-spacing: .5px;
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.left-tag-dot {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
background: var(--blue);
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 0 8px var(--blue);
|
||||
animation: pulseDot 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.left-title {
|
||||
font-size: clamp(28px, 3.5vw, 42px);
|
||||
font-weight: 800;
|
||||
line-height: 1.15;
|
||||
letter-spacing: -.5px;
|
||||
color: #fff;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.left-title em {
|
||||
font-style: normal;
|
||||
background: linear-gradient(90deg, var(--blue), var(--green));
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
.left-desc {
|
||||
font-size: 14px;
|
||||
color: var(--sub);
|
||||
line-height: 1.7;
|
||||
max-width: 400px;
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.stat-row {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.stat {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.stat-val {
|
||||
font-size: 24px;
|
||||
font-weight: 800;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.stat-lbl {
|
||||
font-size: 11px;
|
||||
color: var(--muted);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.stat-sep {
|
||||
width: 1px;
|
||||
background: var(--border);
|
||||
align-self: stretch;
|
||||
}
|
||||
|
||||
/* ── RIGHT PANEL ── */
|
||||
.right-panel {
|
||||
background: var(--panel);
|
||||
border-left: 1px solid var(--border);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
padding: 48px 40px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.logo-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.logo-icon {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
background: linear-gradient(135deg, #1a3a8f, var(--blue));
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 20px;
|
||||
box-shadow: 0 6px 20px rgba(79, 124, 255, .3);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.logo-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.logo-name {
|
||||
font-size: 14px;
|
||||
font-weight: 800;
|
||||
color: var(--text);
|
||||
letter-spacing: -.2px;
|
||||
}
|
||||
|
||||
.logo-sub {
|
||||
font-size: 11px;
|
||||
color: var(--muted);
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
.form-title {
|
||||
font-size: 26px;
|
||||
font-weight: 800;
|
||||
color: var(--text);
|
||||
letter-spacing: -.4px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.form-sub {
|
||||
font-size: 13px;
|
||||
color: var(--sub);
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
/* Role tabs */
|
||||
.role-tabs {
|
||||
display: flex;
|
||||
background: var(--card);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 10px;
|
||||
padding: 4px;
|
||||
gap: 4px;
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
|
||||
.role-tab {
|
||||
flex: 1;
|
||||
padding: 8px 6px;
|
||||
border: none;
|
||||
border-radius: 7px;
|
||||
background: transparent;
|
||||
color: var(--muted);
|
||||
font-family: inherit;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all .2s;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 3px;
|
||||
}
|
||||
|
||||
.role-tab-icon {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.role-tab-name {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.role-tab:hover {
|
||||
color: var(--text);
|
||||
background: rgba(255, 255, 255, .04);
|
||||
}
|
||||
|
||||
.role-tab.active {
|
||||
background: var(--blue-dim);
|
||||
color: var(--blue);
|
||||
border: 1px solid rgba(79, 124, 255, .25);
|
||||
}
|
||||
|
||||
.role-tab.active[data-role="pengurus"] {
|
||||
color: var(--purple);
|
||||
background: rgba(155, 109, 255, .1);
|
||||
border-color: rgba(155, 109, 255, .25);
|
||||
}
|
||||
|
||||
.role-tab.active[data-role="walikota"] {
|
||||
color: var(--orange);
|
||||
background: rgba(255, 140, 66, .1);
|
||||
border-color: rgba(255, 140, 66, .25);
|
||||
}
|
||||
|
||||
/* Fields */
|
||||
.field {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.field-label {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: var(--sub);
|
||||
margin-bottom: 7px;
|
||||
letter-spacing: .3px;
|
||||
}
|
||||
|
||||
.field-wrap {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.field-icon {
|
||||
position: absolute;
|
||||
left: 14px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 16px;
|
||||
text-align: center;
|
||||
color: var(--muted);
|
||||
font-size: 14px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.field-input {
|
||||
width: 100%;
|
||||
padding: 12px 14px 12px 42px;
|
||||
background: var(--card);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 10px;
|
||||
color: var(--text);
|
||||
font-family: inherit;
|
||||
font-size: 13.5px;
|
||||
outline: none;
|
||||
transition: border-color .2s, box-shadow .2s;
|
||||
}
|
||||
|
||||
.field-input:focus {
|
||||
border-color: rgba(79, 124, 255, .5);
|
||||
box-shadow: 0 0 0 3px rgba(79, 124, 255, .1);
|
||||
}
|
||||
|
||||
.field-input::placeholder {
|
||||
color: var(--muted);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.pw-toggle {
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--muted);
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
transition: color .2s;
|
||||
}
|
||||
|
||||
.pw-toggle:hover {
|
||||
color: var(--sub);
|
||||
}
|
||||
|
||||
/* Error */
|
||||
.error-msg {
|
||||
display: none;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
background: rgba(239, 68, 68, .08);
|
||||
border: 1px solid rgba(239, 68, 68, .2);
|
||||
color: #fca5a5;
|
||||
border-radius: 8px;
|
||||
padding: 10px 14px;
|
||||
font-size: 12.5px;
|
||||
margin-bottom: 18px;
|
||||
animation: shake .3s ease;
|
||||
}
|
||||
|
||||
.error-msg.show {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
@keyframes shake {
|
||||
|
||||
0%,
|
||||
100% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
25% {
|
||||
transform: translateX(-5px);
|
||||
}
|
||||
|
||||
75% {
|
||||
transform: translateX(5px);
|
||||
}
|
||||
}
|
||||
|
||||
/* Login button */
|
||||
.btn-masuk {
|
||||
width: 100%;
|
||||
padding: 14px;
|
||||
background: var(--blue);
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
font-family: inherit;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
transition: all .25s;
|
||||
box-shadow: 0 4px 20px rgba(79, 124, 255, .35);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.btn-masuk:hover {
|
||||
background: #6b91ff;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 6px 28px rgba(79, 124, 255, .45);
|
||||
}
|
||||
|
||||
.btn-masuk:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.btn-masuk.loading {
|
||||
opacity: .7;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Quick fill */
|
||||
.quick-fill-label {
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
color: var(--muted);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .6px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.quick-row {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.quick-btn {
|
||||
flex: 1;
|
||||
padding: 9px 8px;
|
||||
background: var(--card);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
color: var(--sub);
|
||||
font-family: inherit;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all .2s;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.quick-btn:hover {
|
||||
background: rgba(255, 255, 255, .05);
|
||||
border-color: rgba(255, 255, 255, .15);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.footer-note {
|
||||
text-align: center;
|
||||
margin-top: 28px;
|
||||
font-size: 11px;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 768px) {
|
||||
body {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.left-panel {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.right-panel {
|
||||
padding: 36px 24px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<!-- LEFT: Map Visual Panel -->
|
||||
<div class="left-panel">
|
||||
<div class="map-bg"></div>
|
||||
<div class="grid-lines"></div>
|
||||
|
||||
<!-- Animated map dots -->
|
||||
<div class="dot dot-1"></div>
|
||||
<div class="dot dot-2"></div>
|
||||
<div class="dot dot-3"></div>
|
||||
<div class="dot dot-4"></div>
|
||||
|
||||
<!-- SVG connecting lines -->
|
||||
<div class="conn-lines">
|
||||
<svg viewBox="0 0 100 100" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<line x1="28" y1="32" x2="55" y2="55" stroke="rgba(79,124,255,.2)" stroke-width=".3"
|
||||
stroke-dasharray="2 2" />
|
||||
<line x1="55" y1="55" x2="60" y2="22" stroke="rgba(34,211,165,.15)" stroke-width=".3"
|
||||
stroke-dasharray="2 2" />
|
||||
<line x1="28" y1="32" x2="35" y2="68" stroke="rgba(155,109,255,.15)" stroke-width=".3"
|
||||
stroke-dasharray="2 2" />
|
||||
<line x1="35" y1="68" x2="55" y2="55" stroke="rgba(255,140,66,.12)" stroke-width=".3"
|
||||
stroke-dasharray="2 2" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div class="left-content">
|
||||
<div class="left-tag">
|
||||
<div class="left-tag-dot"></div>
|
||||
Sistem Aktif
|
||||
</div>
|
||||
<h1 class="left-title">
|
||||
Peta Kemiskinan<br>Berbasis <em>Rumah Ibadah</em>
|
||||
</h1>
|
||||
<p class="left-desc">
|
||||
Platform pemetaan partisipatif yang menghubungkan data kemiskinan dengan jaringan rumah ibadah untuk
|
||||
perencanaan program yang lebih tepat sasaran.
|
||||
</p>
|
||||
<div class="stat-row">
|
||||
<div class="stat">
|
||||
<span class="stat-val" style="color:var(--blue)">3</span>
|
||||
<span class="stat-lbl">Jenis Peran</span>
|
||||
</div>
|
||||
<div class="stat-sep"></div>
|
||||
<div class="stat">
|
||||
<span class="stat-val" style="color:var(--green)">6+</span>
|
||||
<span class="stat-lbl">Jenis Rumah Ibadah</span>
|
||||
</div>
|
||||
<div class="stat-sep"></div>
|
||||
<div class="stat">
|
||||
<span class="stat-val" style="color:var(--orange)">GIS</span>
|
||||
<span class="stat-lbl">Berbasis Peta</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- RIGHT: Login Panel -->
|
||||
<div class="right-panel">
|
||||
|
||||
<div class="logo-row">
|
||||
<div class="logo-icon">🗺️</div>
|
||||
<div class="logo-text">
|
||||
<span class="logo-name">WebGIS Poverty Mapping</span>
|
||||
<span class="logo-sub">Sistem Informasi Geografis</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2 class="form-title">Masuk ke Sistem</h2>
|
||||
<p class="form-sub">Pilih peran, lalu masukkan kredensial Anda.</p>
|
||||
|
||||
<!-- Role Tabs -->
|
||||
<div class="role-tabs" id="role-tabs">
|
||||
<button class="role-tab" id="tab-admin" data-role="admin" onclick="pilihRole('admin')">
|
||||
<span class="role-tab-icon">🛡️</span>
|
||||
<span class="role-tab-name">Admin</span>
|
||||
</button>
|
||||
<button class="role-tab" id="tab-pengurus" data-role="pengurus" onclick="pilihRole('pengurus')">
|
||||
<span class="role-tab-icon">🕌</span>
|
||||
<span class="role-tab-name">Pengurus</span>
|
||||
</button>
|
||||
<button class="role-tab" id="tab-walikota" data-role="walikota" onclick="pilihRole('walikota')">
|
||||
<span class="role-tab-icon">👑</span>
|
||||
<span class="role-tab-name">Walikota</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Error -->
|
||||
<div class="error-msg" id="error-msg">
|
||||
<span>⚠️</span>
|
||||
<span id="error-text">Username atau password salah.</span>
|
||||
</div>
|
||||
|
||||
<!-- Form -->
|
||||
<form id="login-form" onsubmit="handleLogin(event)" novalidate>
|
||||
<div class="field">
|
||||
<label class="field-label" for="inp-username">Username</label>
|
||||
<div class="field-wrap">
|
||||
<span class="field-icon">👤</span>
|
||||
<input class="field-input" type="text" id="inp-username" placeholder="Masukkan username"
|
||||
autocomplete="username" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="field-label" for="inp-password">Password</label>
|
||||
<div class="field-wrap">
|
||||
<span class="field-icon">🔑</span>
|
||||
<input class="field-input" type="password" id="inp-password" placeholder="Masukkan password"
|
||||
autocomplete="current-password" required>
|
||||
<button type="button" class="pw-toggle" id="pw-toggle" onclick="togglePw()">👁️</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn-masuk" id="btn-masuk">
|
||||
<span id="btn-text">Masuk</span>
|
||||
<span id="btn-arrow">→</span>
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div style="margin-bottom: 28px;">
|
||||
<a href="index.html" style="display:flex; justify-content:center; align-items:center; gap:8px; padding:14px; background:rgba(255,255,255,0.03); color:var(--text); border:1px solid var(--border); border-radius:10px; text-decoration:none; font-size:13.5px; font-weight:600; transition:all 0.25s;" onmouseover="this.style.background='rgba(255,255,255,0.08)'" onmouseout="this.style.background='rgba(255,255,255,0.03)'">
|
||||
<span>🗺️ Lihat Peta & Lapor (Akses Warga)</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Quick Fill -->
|
||||
<div class="quick-fill-label">Akun Demo — Klik untuk Isi Otomatis</div>
|
||||
<div class="quick-row">
|
||||
<button class="quick-btn" onclick="isiDemo('admin','admin123','admin')">🛡️ Admin</button>
|
||||
<button class="quick-btn" onclick="isiDemo('pengurus','pengurus123','pengurus')">🕌 Pengurus</button>
|
||||
<button class="quick-btn" onclick="isiDemo('walikota','walikota123','walikota')">👑 Walikota</button>
|
||||
</div>
|
||||
|
||||
<div class="footer-note">WebGIS Poverty Mapping · 2026</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let roleSelected = null;
|
||||
|
||||
function pilihRole(role) {
|
||||
roleSelected = role;
|
||||
document.querySelectorAll('.role-tab').forEach(t => t.classList.remove('active'));
|
||||
document.getElementById('tab-' + role).classList.add('active');
|
||||
}
|
||||
|
||||
function isiDemo(user, pass, role) {
|
||||
document.getElementById('inp-username').value = user;
|
||||
document.getElementById('inp-password').value = pass;
|
||||
pilihRole(role);
|
||||
sembunyiError();
|
||||
}
|
||||
|
||||
function togglePw() {
|
||||
const inp = document.getElementById('inp-password');
|
||||
const btn = document.getElementById('pw-toggle');
|
||||
inp.type = inp.type === 'password' ? 'text' : 'password';
|
||||
btn.textContent = inp.type === 'password' ? '👁️' : '🙈';
|
||||
}
|
||||
|
||||
function tampilError(msg) {
|
||||
const el = document.getElementById('error-msg');
|
||||
document.getElementById('error-text').textContent = msg;
|
||||
el.classList.add('show');
|
||||
el.style.animation = 'none';
|
||||
el.offsetHeight;
|
||||
el.style.animation = '';
|
||||
}
|
||||
|
||||
function sembunyiError() {
|
||||
document.getElementById('error-msg').classList.remove('show');
|
||||
}
|
||||
|
||||
async function handleLogin(e) {
|
||||
e.preventDefault();
|
||||
sembunyiError();
|
||||
|
||||
const username = document.getElementById('inp-username').value.trim();
|
||||
const password = document.getElementById('inp-password').value;
|
||||
|
||||
if (!username) { tampilError('Username wajib diisi.'); return; }
|
||||
if (!password) { tampilError('Password wajib diisi.'); return; }
|
||||
if (!roleSelected) { tampilError('Pilih peran terlebih dahulu.'); return; }
|
||||
|
||||
const btn = document.getElementById('btn-masuk');
|
||||
const btnText = document.getElementById('btn-text');
|
||||
const btnArrow = document.getElementById('btn-arrow');
|
||||
btn.classList.add('loading');
|
||||
btnText.textContent = 'Memeriksa...';
|
||||
btnArrow.textContent = '⏳';
|
||||
|
||||
try {
|
||||
const res = await fetch('api_auth.php', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ username, password, role: roleSelected })
|
||||
});
|
||||
|
||||
const data = await res.json();
|
||||
|
||||
if (data.success) {
|
||||
sessionStorage.setItem('webgis_user', JSON.stringify({
|
||||
id: data.user.id,
|
||||
username: data.user.username,
|
||||
nama: data.user.nama,
|
||||
role: data.user.role
|
||||
}));
|
||||
btnText.textContent = 'Berhasil!';
|
||||
btnArrow.textContent = '✓';
|
||||
btn.style.background = '#22d3a5';
|
||||
const target = data.user.role === 'walikota' ? 'dashboard_walikota.html' : 'index.html';
|
||||
setTimeout(() => { window.location.href = target; }, 500);
|
||||
} else {
|
||||
tampilError(data.message || 'Username, password, atau peran tidak sesuai.');
|
||||
btn.classList.remove('loading');
|
||||
btnText.textContent = 'Masuk';
|
||||
btnArrow.textContent = '→';
|
||||
}
|
||||
} catch {
|
||||
tampilError('Gagal terhubung ke server. Pastikan Apache & MySQL aktif.');
|
||||
btn.classList.remove('loading');
|
||||
btnText.textContent = 'Masuk';
|
||||
btnArrow.textContent = '→';
|
||||
}
|
||||
}
|
||||
|
||||
// Handle logout redirect
|
||||
if (new URLSearchParams(location.search).get('logout')) {
|
||||
sessionStorage.removeItem('webgis_user');
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Reference in New Issue
Block a user