Files
2026-06-13 11:24:58 +07:00

236 lines
11 KiB
PHP
Raw Permalink 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
require_once __DIR__ . '/../config/db.php';
require_once __DIR__ . '/../config/auth_check.php';
requireAnyRole(['operator']);
$pdo = Database::getConnection();
$rows = $pdo->query(
"SELECT id, username, email, role, is_active, wilayah_tugas, nama_lengkap, last_login, created_at
FROM users ORDER BY id ASC"
)->fetchAll();
$wilayahList = $pdo->query("SELECT nama FROM wilayah ORDER BY nama")->fetchAll();
$pageTitle = 'Kelola Users';
$activeNav = 'users';
require_once __DIR__ . '/partials/header.php';
// Helper badge per role
function roleBadge(string $role): string {
return match ($role) {
'operator' => '<span class="badge badge-danger">🛠️ Operator</span>',
'pimpinan' => '<span class="badge" style="background:#EDE9FE;color:#6D28D9;">📊 Pimpinan</span>',
'enumerator' => '<span class="badge badge-info">📋 Enumerator</span>',
'publik' => '<span class="badge" style="background:#F3F4F6;color:#6B7280;">🌐 Publik</span>',
default => htmlspecialchars($role),
};
}
?>
<div class="page-header">
<div class="breadcrumb"><span>Operator</span><span class="sep">/</span><span>Users</span></div>
<h1>👤 Manajemen User Sistem</h1>
<p>Kelola akun 4 peran: Publik, Enumerator, Operator, dan Pimpinan (KF-02 / KF-03).</p>
</div>
<div class="page-toolbar">
<div class="search-box">
<i class="fas fa-search search-icon"></i>
<input type="text" id="searchInput" placeholder="Cari user..." oninput="filterTable('searchInput','usersTable')">
</div>
<button class="btn btn-primary" onclick="openModal('modalTambah')">
<i class="fas fa-plus"></i> Tambah User
</button>
</div>
<div class="table-wrap">
<table class="data-table" id="usersTable">
<thead><tr>
<th>#</th><th>Username</th><th>Nama Lengkap</th><th>Email</th><th>Role</th>
<th>Wilayah</th><th>Status</th><th>Login Terakhir</th><th>Aksi</th>
</tr></thead>
<tbody>
<?php foreach ($rows as $i => $r): ?>
<tr>
<td style="color:var(--text-muted)"><?= $i+1 ?></td>
<td><strong><?= htmlspecialchars($r['username']) ?></strong></td>
<td><?= htmlspecialchars($r['nama_lengkap'] ?: '-') ?></td>
<td style="font-size:0.82rem;color:var(--text-muted)"><?= htmlspecialchars($r['email'] ?: '-') ?></td>
<td><?= roleBadge($r['role']) ?></td>
<td style="font-size:0.82rem"><?= htmlspecialchars($r['wilayah_tugas'] ?: '-') ?></td>
<td>
<?php if ((int)$r['is_active'] === 1): ?>
<span class="badge badge-success">Aktif</span>
<?php else: ?>
<span class="badge badge-danger">Nonaktif</span>
<?php endif; ?>
</td>
<td style="color:var(--text-muted); font-size:0.78rem;">
<?= $r['last_login'] ? date('d M Y H:i', strtotime($r['last_login'])) : 'Belum pernah' ?>
</td>
<td><div class="actions">
<button class="btn btn-ghost btn-sm btn-icon" onclick='editRow(<?= json_encode($r, JSON_HEX_APOS|JSON_HEX_QUOT) ?>)' title="Edit">
<i class="fas fa-edit"></i>
</button>
<button class="btn btn-danger btn-sm btn-icon" onclick="hapus(<?= $r['id'] ?>,'<?= addslashes($r['username']) ?>')" title="Hapus">
<i class="fas fa-trash"></i>
</button>
</div></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<!-- Modal Tambah -->
<div class="modal-overlay" id="modalTambah">
<div class="modal">
<div class="modal-header">
<span class="modal-title">👤 Tambah User Baru</span>
<button class="modal-close" onclick="closeModal('modalTambah')">×</button>
</div>
<form id="formTambah">
<div class="form-group"><label class="form-label">Username *</label><input type="text" id="tUsername" class="form-control" placeholder="username unik" required></div>
<div class="form-group"><label class="form-label">Nama Lengkap</label><input type="text" id="tNama" class="form-control" placeholder="Nama lengkap"></div>
<div class="form-group"><label class="form-label">Email</label><input type="email" id="tEmail" class="form-control" placeholder="email@contoh.id"></div>
<div class="form-group"><label class="form-label">Role *</label>
<select id="tRole" class="form-control" onchange="toggleWilayah('t')">
<option value="enumerator">📋 Enumerator</option>
<option value="operator">🛠️ Operator</option>
<option value="pimpinan">📊 Pimpinan</option>
<option value="publik">🌐 Publik</option>
</select>
</div>
<div class="form-group" id="tWilayahGroup"><label class="form-label">Wilayah Tugas</label>
<select id="tWilayah" class="form-control"><option value="">- pilih kelurahan -</option>
<?php foreach ($wilayahList as $w): ?><option value="<?= htmlspecialchars($w['nama']) ?>"><?= htmlspecialchars($w['nama']) ?></option><?php endforeach; ?>
</select></div>
<div class="form-group"><label class="form-label">Password *</label><input type="password" id="tPassword" class="form-control" placeholder="Minimal 6 karakter" required minlength="6"></div>
</form>
<div class="modal-footer">
<button class="btn btn-ghost" onclick="closeModal('modalTambah')">Batal</button>
<button class="btn btn-primary" onclick="simpan()"><i class="fas fa-save"></i> Simpan</button>
</div>
</div>
</div>
<!-- Modal Edit -->
<div class="modal-overlay" id="modalEdit">
<div class="modal">
<div class="modal-header">
<span class="modal-title">✏️ Edit User</span>
<button class="modal-close" onclick="closeModal('modalEdit')">×</button>
</div>
<input type="hidden" id="editId">
<div class="form-group"><label class="form-label">Username *</label><input type="text" id="editUsername" class="form-control" required></div>
<div class="form-group"><label class="form-label">Nama Lengkap</label><input type="text" id="editNama" class="form-control"></div>
<div class="form-group"><label class="form-label">Email</label><input type="email" id="editEmail" class="form-control"></div>
<div class="form-group"><label class="form-label">Role *</label>
<select id="editRole" class="form-control" onchange="toggleWilayah('edit')">
<option value="enumerator">📋 Enumerator</option>
<option value="operator">🛠️ Operator</option>
<option value="pimpinan">📊 Pimpinan</option>
<option value="publik">🌐 Publik</option>
</select>
</div>
<div class="form-group" id="editWilayahGroup"><label class="form-label">Wilayah Tugas</label>
<select id="editWilayah" class="form-control"><option value="">- pilih kelurahan -</option>
<?php foreach ($wilayahList as $w): ?><option value="<?= htmlspecialchars($w['nama']) ?>"><?= htmlspecialchars($w['nama']) ?></option><?php endforeach; ?>
</select></div>
<div class="form-group"><label class="form-label">Status</label>
<select id="editActive" class="form-control">
<option value="1">Aktif</option>
<option value="0">Nonaktif</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Password Baru</label>
<input type="password" id="editPassword" class="form-control" placeholder="Kosongkan jika tidak diubah" minlength="6">
</div>
<div class="modal-footer">
<button class="btn btn-ghost" onclick="closeModal('modalEdit')">Batal</button>
<button class="btn btn-primary" onclick="update()"><i class="fas fa-save"></i> Perbarui</button>
</div>
</div>
</div>
<?php $extraScript = <<<'JS'
<script>
const API = APP_BASE + '/api/users.php';
// Wilayah tugas hanya relevan untuk enumerator
function toggleWilayah(prefix) {
const role = document.getElementById(prefix + 'Role').value;
const group = document.getElementById(prefix + 'WilayahGroup');
group.style.display = (role === 'enumerator') ? 'block' : 'none';
}
function editRow(r) {
document.getElementById('editId').value = r.id;
document.getElementById('editUsername').value = r.username;
document.getElementById('editNama').value = r.nama_lengkap || '';
document.getElementById('editEmail').value = r.email || '';
document.getElementById('editRole').value = r.role;
document.getElementById('editWilayah').value = r.wilayah_tugas || '';
document.getElementById('editActive').value = String(r.is_active);
document.getElementById('editPassword').value = '';
toggleWilayah('edit');
openModal('modalEdit');
}
async function simpan() {
const u = document.getElementById('tUsername').value.trim();
const p = document.getElementById('tPassword').value;
if (!u) return showToast('Username wajib!', 'error');
if (p.length < 6) return showToast('Password minimal 6 karakter!', 'error');
const res = await fetch(API, {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
username: u,
nama_lengkap: document.getElementById('tNama').value,
email: document.getElementById('tEmail').value,
role: document.getElementById('tRole').value,
wilayah_tugas: document.getElementById('tWilayah').value,
password: p
})
});
const d = await res.json();
if (d.status === 'success') { showToast('User berhasil ditambahkan!'); setTimeout(() => location.reload(), 900); }
else showToast(d.message || 'Gagal', 'error');
}
async function update() {
const id = document.getElementById('editId').value;
const u = document.getElementById('editUsername').value.trim();
const p = document.getElementById('editPassword').value;
if (!u) return showToast('Username wajib!', 'error');
if (p && p.length < 6) return showToast('Password minimal 6 karakter!', 'error');
const body = {
username: u,
nama_lengkap: document.getElementById('editNama').value,
email: document.getElementById('editEmail').value,
role: document.getElementById('editRole').value,
wilayah_tugas: document.getElementById('editWilayah').value,
is_active: document.getElementById('editActive').value
};
if (p) body.password = p;
const res = await fetch(API + '?id=' + id, {
method: 'PUT', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body)
});
const d = await res.json();
if (d.status === 'success') { showToast('User diperbarui!'); setTimeout(() => location.reload(), 900); }
else showToast(d.message || 'Gagal', 'error');
}
async function hapus(id, username) {
if (id == 1) return showToast('Akun utama (ID=1) tidak dapat dihapus!', 'error');
if (!confirm(`Hapus user "${username}"?`)) return;
const res = await fetch(API + '?id=' + id, { method: 'DELETE' });
const d = await res.json();
if (d.status === 'success') { showToast('User dihapus!'); setTimeout(() => location.reload(), 900); }
else showToast(d.message || 'Gagal', 'error');
}
toggleWilayah('t');
</script>
JS; ?>
<?php require_once __DIR__ . '/partials/footer.php'; ?>