Files
2026-06-11 21:20:59 +07:00

688 lines
25 KiB
HTML

<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Profil — GeoSosial Pontianak</title>
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&family=Space+Grotesk:wght@500;600;700&display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--bg: #f0f4fb;
--blue: #1a5fff;
--blue2: #0f3cc7;
--teal: #0891b2;
--grn: #10b981;
--red: #f03e5a;
--purple:#7c3aed;
--ink: #0b1120;
--ink2: #3d4b66;
--ink3: #8a97ab;
--card: #ffffff;
--card-h:#f8faff;
--bdr: rgba(11,17,32,.09);
--sh: 0 4px 24px rgba(11,17,32,.08);
--shm: 0 8px 40px rgba(11,17,32,.12);
}
html { scroll-behavior: smooth; }
body {
font-family: 'Inter', sans-serif;
background: var(--bg);
min-height: 100vh;
color: var(--ink);
position: relative;
overflow-x: hidden;
}
/* ── SUBTLE DOT BG ── */
body::before {
content: '';
position: fixed; inset: 0; z-index: 0;
background-image: radial-gradient(rgba(26,95,255,.06) 1px, transparent 1px);
background-size: 28px 28px;
pointer-events: none;
}
/* ── TOPBAR ── */
.topbar {
position: sticky; top: 0; z-index: 500;
height: 54px;
background: rgba(255,255,255,.92);
backdrop-filter: blur(20px) saturate(1.8);
border-bottom: 1px solid var(--bdr);
display: flex; align-items: center; justify-content: space-between;
padding: 0 28px;
box-shadow: 0 1px 12px rgba(11,17,32,.06);
}
.brand {
display: flex; align-items: center; gap: 9px;
text-decoration: none;
}
.brand-logo {
width: 28px; height: 28px; border-radius: 7px;
background: linear-gradient(135deg, var(--blue), var(--teal));
display: flex; align-items: center; justify-content: center; font-size: 14px;
}
.brand-name {
font-family: 'Space Grotesk', sans-serif;
font-size: 15px; font-weight: 700; color: var(--ink);
}
.topbar-right { display: flex; gap: 8px; }
.tbtn {
display: flex; align-items: center; gap: 5px;
padding: 6px 14px; border-radius: 99px;
font-size: 11px; font-weight: 600; font-family: inherit;
cursor: pointer; border: none; text-decoration: none;
transition: all .18s;
}
.tbtn.ghost {
background: var(--bg); border: 1px solid var(--bdr); color: var(--ink2);
}
.tbtn.ghost:hover { background: #e8efff; color: var(--blue); border-color: #c0d0ff; }
.tbtn.danger {
background: #fde8ec; border: 1px solid #f9b8c4; color: var(--red);
}
.tbtn.danger:hover { background: var(--red); color: #fff; border-color: var(--red); }
/* ── PAGE WRAP ── */
.page { position: relative; z-index: 1; padding: 40px 20px 60px; }
/* ── PROFILE HEADER ── */
.phead {
max-width: 700px; margin: 0 auto 28px;
display: flex; align-items: center; gap: 24px;
padding: 28px 32px;
background: var(--card);
border: 1px solid var(--bdr);
border-radius: 24px;
box-shadow: var(--shm);
position: relative; overflow: hidden;
}
/* Colored top strip */
.phead::before {
content: '';
position: absolute; top: 0; left: 0; right: 0; height: 4px;
background: linear-gradient(90deg, var(--blue), var(--teal), var(--purple));
}
/* Avatar */
.av-wrap { position: relative; flex-shrink: 0; }
.av {
width: 80px; height: 80px; border-radius: 50%;
display: flex; align-items: center; justify-content: center;
font-family: 'Space Grotesk', sans-serif;
font-size: 28px; font-weight: 700; color: #fff;
position: relative;
}
.av::before {
content: '';
position: absolute; inset: -3px; border-radius: 50%;
background: linear-gradient(135deg, var(--blue), var(--teal));
z-index: -1;
}
.av::after {
content: '';
position: absolute; inset: -7px; border-radius: 50%;
background: linear-gradient(135deg, rgba(26,95,255,.18), rgba(8,145,178,.18));
z-index: -2;
animation: spin-ring 8s linear infinite;
}
@keyframes spin-ring {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.av-pulse {
position: absolute; bottom: 2px; right: 2px;
width: 16px; height: 16px; border-radius: 50%;
background: var(--grn);
border: 2.5px solid #fff;
}
.av-pulse::after {
content: '';
position: absolute; inset: -3px; border-radius: 50%;
background: rgba(16,185,129,.3);
animation: pulse 2s infinite;
}
@keyframes pulse { 0%{transform:scale(1);opacity:1} 100%{transform:scale(2.2);opacity:0} }
.phead-meta { flex: 1; }
.phead-name {
font-family: 'Space Grotesk', sans-serif;
font-size: 22px; font-weight: 700; color: var(--ink);
letter-spacing: -.4px;
}
.phead-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 8px; }
.chip {
display: inline-flex; align-items: center; gap: 4px;
padding: 3px 10px; border-radius: 99px;
font-size: 11px; font-weight: 600;
border: 1px solid transparent;
}
.chip.role-admin { background: #fde8ec; color: var(--red); border-color: #f9b8c4; }
.chip.role-petugas { background: #e8efff; color: var(--blue); border-color: #c0d0ff; }
.chip.role-wali { background: #f0ebff; color: var(--purple); border-color: #d4bfff; }
.chip.role-viewer { background: var(--bg); color: var(--ink3); border-color: var(--bdr); }
.chip.online { background: #e5f8f1; color: var(--grn); border-color: #a7f3d0; }
.phead-uname { font-size: 12px; color: var(--ink3); margin-top: 5px; }
/* ── MAIN LAYOUT ── */
.main {
max-width: 700px; margin: 0 auto;
display: flex; flex-direction: column; gap: 16px;
}
/* ── GLASS CARD → LIGHT CARD ── */
.gcard {
background: var(--card);
border: 1px solid var(--bdr);
border-radius: 20px;
overflow: hidden;
box-shadow: var(--sh);
transition: box-shadow .2s;
}
.gcard:hover { box-shadow: var(--shm); }
/* ── SECTION HEADER ── */
.sec-head {
display: flex; align-items: center; justify-content: space-between;
padding: 18px 22px;
border-bottom: 1px solid var(--bdr);
background: var(--card);
}
.sec-left { display: flex; align-items: center; gap: 12px; }
.sec-ic {
width: 36px; height: 36px; border-radius: 10px;
display: flex; align-items: center; justify-content: center;
font-size: 17px;
}
.sec-ic.c { background: #e8efff; }
.sec-ic.p { background: #fef3c7; }
.sec-ic.s { background: #f0ebff; }
.sec-title { font-size: 14px; font-weight: 700; color: var(--ink); }
.sec-sub { font-size: 11px; color: var(--ink3); margin-top: 1px; }
/* Edit button */
.ebtn {
display: flex; align-items: center; gap: 5px;
padding: 6px 14px; border-radius: 8px;
font-size: 11px; font-weight: 700; font-family: inherit;
background: var(--bg); color: var(--ink2);
border: 1px solid var(--bdr); cursor: pointer; transition: all .15s;
}
.ebtn:hover { background: #e8efff; color: var(--blue); border-color: #c0d0ff; }
.ebtn.on { background: #e8efff; color: var(--blue); border-color: #c0d0ff; }
/* ── INFO ROWS ── */
.irows { padding: 12px 16px; display: flex; flex-direction: column; gap: 12px; }
.irow {
display: grid; grid-template-columns: 1fr 1fr; gap: 12px;
}
.icell {
padding: 14px 18px;
background: var(--bg);
border: 1px solid var(--bdr);
border-radius: 12px;
}
.ilbl {
font-size: 9px; font-weight: 700; text-transform: uppercase;
letter-spacing: .1em; color: var(--ink3); margin-bottom: 7px;
}
.ival {
font-size: 13px; font-weight: 600; color: var(--ink);
display: flex; align-items: center; gap: 6px;
}
.ival.mono { font-family: 'Space Grotesk', monospace; }
.ival .tag {
display: inline-flex; align-items: center;
padding: 3px 9px; border-radius: 99px;
font-size: 11px; font-weight: 700;
}
/* inputs */
.gi {
width: 100%; padding: 9px 12px;
background: #fff;
border: 1.5px solid var(--bdr);
border-radius: 9px; font-size: 13px;
font-family: inherit; color: var(--ink); outline: none;
transition: border-color .18s;
}
.gi:focus { border-color: var(--blue); }
/* Save bar */
.save-bar {
display: none; justify-content: flex-end; gap: 10px;
padding: 14px 22px; border-top: 1px solid var(--bdr);
background: var(--bg);
}
.save-bar.show { display: flex; }
.sbtn-c {
padding: 9px 18px; border-radius: 9px; font-size: 12px;
font-weight: 600; font-family: inherit; cursor: pointer;
background: #fff; border: 1px solid var(--bdr); color: var(--ink2);
transition: all .15s;
}
.sbtn-c:hover { background: var(--bg); }
.sbtn-s {
padding: 9px 20px; border-radius: 9px; font-size: 12px;
font-weight: 700; font-family: inherit; cursor: pointer;
background: linear-gradient(135deg, var(--blue), var(--teal)); border: none; color: #fff;
transition: opacity .15s;
}
.sbtn-s:hover { opacity: .85; }
/* ── PASSWORD ── */
.pfields { padding: 20px 22px; display: flex; flex-direction: column; gap: 16px; }
.pf > label {
display: block; font-size: 9px; font-weight: 700;
text-transform: uppercase; letter-spacing: .1em;
color: var(--ink3); margin-bottom: 7px;
}
.pf > label span { font-size: 9px; font-weight: 400; text-transform: none; letter-spacing: 0; }
.pwrap { position: relative; }
.pwrap input {
width: 100%; padding: 11px 40px 11px 40px;
background: var(--bg);
border: 1.5px solid var(--bdr);
border-radius: 10px; font-size: 13px;
font-family: inherit; color: var(--ink); outline: none;
transition: border-color .18s, background .18s;
}
.pwrap input:focus { border-color: var(--blue); background: #fff; }
.pwrap input::placeholder { color: var(--ink3); }
.ppfx {
position: absolute; left: 13px; top: 50%; transform: translateY(-50%);
font-size: 14px; pointer-events: none; user-select: none;
}
.ptgl {
position: absolute; right: 12px; top: 50%; transform: translateY(-50%);
background: none; border: none; cursor: pointer; color: var(--ink3);
font-size: 14px; padding: 2px; line-height: 1; transition: color .15s;
}
.ptgl:hover { color: var(--blue); }
.sbar { height: 3px; border-radius: 99px; background: var(--bdr); margin-top: 7px; overflow: hidden; }
.sfill { height: 100%; width: 0; border-radius: 99px; transition: width .3s, background .3s; }
.shint { font-size: 10px; margin-top: 4px; color: var(--ink3); }
.pact { display: flex; justify-content: flex-end; padding-top: 4px; }
.pbtn {
display: flex; align-items: center; gap: 8px;
padding: 11px 28px; border-radius: 10px; border: none;
background: linear-gradient(135deg, var(--blue) 0%, var(--teal) 100%);
color: #fff; font-size: 12px; font-weight: 700;
font-family: inherit; cursor: pointer;
box-shadow: 0 4px 20px rgba(26,95,255,.22);
transition: opacity .18s, transform .15s;
}
.pbtn:hover { opacity: .88; transform: translateY(-1px); }
.pbtn:disabled { opacity: .45; cursor: not-allowed; transform: none; }
/* ── STATS ROW ── */
.stats {
display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px;
}
.stat {
background: var(--card);
border: 1px solid var(--bdr);
border-radius: 16px; padding: 16px;
text-align: center;
box-shadow: var(--sh);
transition: box-shadow .2s, transform .2s;
}
.stat:hover { box-shadow: var(--shm); transform: translateY(-2px); }
.stat-ic { font-size: 22px; margin-bottom: 8px; }
.stat-n { font-size: 11px; color: var(--ink3); margin-bottom: 3px; }
.stat-v { font-size: 13px; font-weight: 700; color: var(--ink); }
/* ── TOAST ── */
#toast {
position: fixed; bottom: 28px; left: 50%;
transform: translateX(-50%) translateY(90px);
padding: 12px 22px; border-radius: 99px;
background: var(--ink);
color: #fff; font-size: 12px; font-weight: 700;
box-shadow: 0 8px 40px rgba(0,0,0,.2);
transition: transform .35s cubic-bezier(.34,1.56,.64,1);
z-index: 9999; white-space: nowrap;
}
#toast.show { transform: translateX(-50%) translateY(0); }
@media (max-width: 600px) {
.phead { flex-direction: column; text-align: center; align-items: center; }
.phead-row { justify-content: center; }
.irow { grid-template-columns: 1fr; }
.icell + .icell { border-left: none; border-top: 1px solid var(--bdr); }
.stats { grid-template-columns: 1fr; }
}
</style>
</head>
<body>
<!-- TOPBAR -->
<nav class="topbar">
<a class="brand" href="sig1.html">
<div class="brand-logo">📍</div>
<span class="brand-name">GeoSosial</span>
</a>
<div class="topbar-right">
<a class="tbtn ghost" href="sig1.html">← Peta</a>
<button class="tbtn danger" onclick="doLogout()">❤ Keluar</button>
</div>
</nav>
<div class="page">
<!-- PROFILE HEADER -->
<div class="phead">
<div class="av-wrap">
<div class="av" id="avEl" style="background:#0d2340">
<span id="avIni">AD</span>
</div>
<div class="av-pulse"></div>
</div>
<div class="phead-meta">
<div class="phead-name" id="phName"></div>
<div class="phead-row">
<span class="chip" id="phRole"></span>
<span class="chip online">● Online</span>
</div>
<div class="phead-uname" id="phUser"></div>
</div>
</div>
<!-- STATS ROW -->
<div class="stats main" style="padding: 0; margin-bottom: 0; display: grid; grid-template-columns: repeat(3,1fr); gap: 12px; max-width: 700px;">
<div class="stat">
<div class="stat-ic">🔐</div>
<div class="stat-n">Hak Akses</div>
<div class="stat-v" id="stAccess"></div>
</div>
<div class="stat">
<div class="stat-ic">🗓</div>
<div class="stat-n">Bergabung</div>
<div class="stat-v" id="stJoin"></div>
</div>
<div class="stat">
<div class="stat-ic"></div>
<div class="stat-n">Login Terakhir</div>
<div class="stat-v" id="stLogin"></div>
</div>
</div>
<!-- MAIN CARDS -->
<div class="main">
<!-- Informasi Akun -->
<div class="gcard" id="secInfo">
<div class="sec-head">
<div class="sec-left">
<div class="sec-ic c">🪪</div>
<div>
<div class="sec-title">Informasi Akun</div>
<div class="sec-sub">Data profil dan pengaturan akun</div>
</div>
</div>
<button class="ebtn" id="editBtn" onclick="toggleEdit()">✏️ Edit</button>
</div>
<div class="irows">
<div class="irow">
<div class="icell">
<div class="ilbl">Username</div>
<div class="ival mono">@ <span id="dUser"></span></div>
</div>
<div class="icell">
<div class="ilbl">Nama Lengkap / Jabatan</div>
<div class="ival" id="dJabW"><span id="dJab"></span></div>
<input class="gi" id="eJab" style="display:none" placeholder="cth. Kadis Sosial">
</div>
</div>
<div class="irow">
<div class="icell">
<div class="ilbl">Role</div>
<div class="ival" id="dRoleW"><span class="tag" id="dRole"></span></div>
<select class="gi" id="eRole" style="display:none">
<option value="admin">🛡 Admin</option>
<option value="petugas">👮 Petugas</option>
<option value="walikota">🏛️ Walikota</option>
<option value="viewer">👁️ Viewer</option>
</select>
</div>
<div class="icell">
<div class="ilbl">Status Akun</div>
<div class="ival"><span id="dStatus" style="color:var(--grn);font-weight:700">● Aktif</span></div>
</div>
</div>
<div class="irow">
<div class="icell">
<div class="ilbl">Sesi Aktif</div>
<div class="ival" style="color:var(--grn);font-weight:700">● Online</div>
</div>
<div class="icell">
<div class="ilbl">Login Terakhir</div>
<div class="ival" id="dLogin"></div>
</div>
</div>
</div>
<div class="save-bar" id="saveBar">
<button class="sbtn-c" onclick="cancelEdit()">Batal</button>
<button class="sbtn-s" onclick="saveInfo()">💾 Simpan</button>
</div>
</div>
<!-- Ganti Password -->
<div class="gcard" id="secPw">
<div class="sec-head">
<div class="sec-left">
<div class="sec-ic p">🔑</div>
<div>
<div class="sec-title">Ganti Password</div>
<div class="sec-sub">Perbarui kata sandi akun Anda</div>
</div>
</div>
</div>
<div class="pfields">
<div class="pf">
<label>Password Lama</label>
<div class="pwrap">
<span class="ppfx">🔒</span>
<input type="password" id="pwOld" placeholder="Masukkan password lama">
<button class="ptgl" onclick="togglePw('pwOld',this)" tabindex="-1">👁</button>
</div>
</div>
<div class="pf">
<label>Password Baru <span>(min. 6 karakter)</span></label>
<div class="pwrap">
<span class="ppfx">🔑</span>
<input type="password" id="pwNew" placeholder="Masukkan password baru" oninput="checkStr(this.value)">
<button class="ptgl" onclick="togglePw('pwNew',this)" tabindex="-1">👁</button>
</div>
<div class="sbar"><div class="sfill" id="sfill"></div></div>
<div class="shint" id="shint"></div>
</div>
<div class="pf">
<label>Konfirmasi Password</label>
<div class="pwrap">
<span class="ppfx"></span>
<input type="password" id="pwCfm" placeholder="Ulangi password baru">
<button class="ptgl" onclick="togglePw('pwCfm',this)" tabindex="-1">👁</button>
</div>
</div>
<div class="pact">
<button class="pbtn" id="btnPw" onclick="savePw()">🔒 Simpan Password</button>
</div>
</div>
</div>
</div>
</div>
<div id="toast"></div>
<script>
let PD = null, editing = false, ttimer;
const RM = {
admin: { cls:'role-admin', lbl:'🛡 Admin', bg:'rgba(248,113,113,.12)', fg:'#fca5a5' },
petugas: { cls:'role-petugas', lbl:'👮 Petugas', bg:'rgba(34,211,238,.1)', fg:'#22d3ee' },
walikota: { cls:'role-wali', lbl:'🏛️ Walikota', bg:'rgba(167,139,250,.12)', fg:'#c4b5fd' },
viewer: { cls:'role-viewer', lbl:'👁️ Viewer', bg:'rgba(148,163,184,.1)', fg:'#94a3b8' },
};
const AVC = ['#1a5fff','#7c3aed','#0891b2','#059669','#dc2626','#b45309'];
async function loadProfile() {
let base = null;
try {
const r = await fetch('api/auth.php?action=check');
const j = await r.json();
if (!j.success) { location.href = 'sig1.html'; return; }
base = j.data;
} catch { location.href = 'sig1.html'; return; }
let full = { ...base };
try {
const r2 = await fetch('api/users.php');
const j2 = await r2.json();
if (j2.success) {
const found = j2.data.find(x => x.id == base.id);
if (found) full = { ...base, ...found };
}
} catch {}
PD = full;
try { render(full); } catch(e) { console.error(e); }
}
function $(id) { return document.getElementById(id); }
function render(u) {
const rm = RM[u.role] || RM.viewer;
const ini = (u.username || 'U').substring(0, 2).toUpperCase();
const color = AVC[(u.id || 0) % AVC.length];
const now = new Date().toLocaleString('id-ID', {day:'2-digit',month:'short',year:'numeric',hour:'2-digit',minute:'2-digit'});
const joined = u.created_at ? new Date(u.created_at).toLocaleDateString('id-ID',{day:'2-digit',month:'short',year:'numeric'}) : '—';
// Avatar
const av = $('avEl'); if (av) av.style.background = color;
const ai = $('avIni'); if (ai) ai.textContent = ini;
// Header
if ($('phName')) $('phName').textContent = u.jabatan || u.username;
const pr = $('phRole');
if (pr) { pr.textContent = rm.lbl; pr.className = 'chip ' + rm.cls; }
if ($('phUser')) $('phUser').textContent = '@ ' + u.username;
// Stats
if ($('stAccess')) $('stAccess').textContent = u.can_edit ? 'Baca & Tulis' : 'Baca Saja';
if ($('stJoin')) $('stJoin').textContent = joined;
if ($('stLogin')) $('stLogin').textContent = now;
// Info card
if ($('dUser')) $('dUser').textContent = u.username;
if ($('dJab')) $('dJab').textContent = u.jabatan || '—';
if ($('eJab')) $('eJab').value = u.jabatan || '';
if ($('eRole')) $('eRole').value = u.role;
const dr = $('dRole');
if (dr) { dr.textContent = rm.lbl; dr.style.background = rm.bg; dr.style.color = rm.fg; }
if ($('dStatus')) {
$('dStatus').textContent = u.aktif ? '● Aktif' : '○ Nonaktif';
$('dStatus').style.color = u.aktif ? 'var(--grn)' : 'var(--ink3)';
}
if ($('dLogin')) $('dLogin').textContent = now;
if ($('eRole')) $('eRole').disabled = !u.is_admin;
}
function toggleEdit() {
editing = !editing;
$('editBtn').textContent = editing ? '✕ Batal' : '✏️ Edit';
$('editBtn').className = 'ebtn' + (editing ? ' on' : '');
$('dJabW').style.display = editing ? 'none' : '';
$('eJab').style.display = editing ? '' : 'none';
$('dRoleW').style.display = editing ? 'none' : '';
$('eRole').style.display = editing ? '' : 'none';
$('saveBar').classList.toggle('show', editing);
if (editing) $('eJab').focus();
}
function cancelEdit() {
editing = false;
$('editBtn').textContent = '✏️ Edit'; $('editBtn').className = 'ebtn';
$('eJab').value = PD?.jabatan || '';
$('eRole').value = PD?.role || 'viewer';
$('dJabW').style.display = ''; $('eJab').style.display = 'none';
$('dRoleW').style.display = ''; $('eRole').style.display = 'none';
$('saveBar').classList.remove('show');
}
async function saveInfo() {
const jabatan = $('eJab').value.trim(), role = $('eRole').value;
try {
const r = await fetch(`api/users.php?id=${PD.id}`, {
method:'PUT', headers:{'Content-Type':'application/json'},
body: JSON.stringify({jabatan, role, can_edit: PD.can_edit, aktif: PD.aktif})
});
const j = await r.json();
if (j.success) { PD.jabatan=jabatan; PD.role=role; render(PD); cancelEdit(); toast('✓ Perubahan disimpan'); }
else alert('Gagal: ' + j.message);
} catch { alert('Koneksi server gagal.'); }
}
function togglePw(id, btn) {
const inp = $(id);
inp.type = inp.type === 'password' ? 'text' : 'password';
btn.textContent = inp.type === 'password' ? '👁' : '🙈';
}
function checkStr(v) {
const bar = $('sfill'), lbl = $('shint');
if (!v) { bar.style.width='0'; lbl.textContent='—'; lbl.style.color='var(--ink3)'; return; }
let s=0;
if(v.length>=6)s++; if(v.length>=10)s++;
if(/[A-Z]/.test(v))s++; if(/[0-9]/.test(v))s++; if(/[^A-Za-z0-9]/.test(v))s++;
const ls=[{w:'20%',c:'#f87171',t:'Sangat Lemah'},{w:'40%',c:'#fb923c',t:'Lemah'},{w:'60%',c:'#fbbf24',t:'Cukup'},{w:'80%',c:'#34d399',t:'Kuat'},{w:'100%',c:'#10b981',t:'Sangat Kuat'}];
const lv=ls[Math.min(s,4)];
bar.style.width=lv.w; bar.style.background=lv.c;
lbl.textContent=lv.t; lbl.style.color=lv.c;
}
async function savePw() {
const old=$('pwOld').value, nw=$('pwNew').value, cf=$('pwCfm').value;
if(!old){alert('Password lama wajib diisi.');return;}
if(nw.length<6){alert('Password baru minimal 6 karakter.');return;}
if(nw!==cf){alert('Konfirmasi password tidak cocok.');return;}
const btn=$('btnPw'); btn.disabled=true; btn.textContent='⏳ Menyimpan...';
try {
const r=await fetch(`api/users.php?id=${PD.id}`,{
method:'PUT',headers:{'Content-Type':'application/json'},
body:JSON.stringify({jabatan:PD.jabatan,role:PD.role,can_edit:PD.can_edit,aktif:PD.aktif,password:nw})
});
const j=await r.json();
if(j.success){
$('pwOld').value=$('pwNew').value=$('pwCfm').value='';
$('sfill').style.width='0'; $('shint').textContent='—';
toast('🔒 Password berhasil diperbarui');
} else alert('Gagal: '+j.message);
} catch { alert('Koneksi server gagal.'); }
btn.disabled=false; btn.textContent='🔒 Simpan Password';
}
async function doLogout() {
try { await fetch('api/auth.php?action=logout',{method:'POST'}); } catch{}
location.href='sig1.html';
}
function toast(msg) {
$('toast').textContent = msg;
$('toast').classList.add('show');
clearTimeout(ttimer);
ttimer = setTimeout(() => $('toast').classList.remove('show'), 2800);
}
loadProfile();
</script>
</body>
</html>