|
Server : LiteSpeed System : Linux terra.hostitbro.com 5.14.0-611.54.3.el9_7.x86_64 #1 SMP PREEMPT_DYNAMIC Thu May 7 16:31:24 EDT 2026 x86_64 User : outerorb ( 1091) PHP Version : 8.1.34 Disable Function : mail Directory : /home2/outerorb/emp.outerorbittech.in/admin/ | |
|
Path: /home2/outerorb/emp.outerorbittech.in/admin/admins.php
Size: 19.99 KB
Permissions: 0666
<?php
require __DIR__ . '/../includes/helpers.php';
require_super_admin();
$admin = current_admin();
$flash = flash();
// Wrap DB/bootstrap in a guard so production failures don't render a blank page.
try {
$pdo = db();
ensure_admin_users_table();
ensure_admin_departments_table();
} catch (Throwable $e) {
error_log('[admins.php] bootstrap failed: ' . $e->getMessage());
http_response_code(500);
echo 'Admin page setup failed. Please check server logs.';
exit;
}
// Ensure admin_users table exists (safety if schema not yet applied)
$totalAdmins = (int) $pdo->query('SELECT COUNT(*) FROM admin_users')->fetchColumn();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (!isset($_POST['csrf_token']) || !verify_csrf($_POST['csrf_token'])) {
redirect_with_message('admins.php', 'Invalid session token.', 'error');
}
$action = $_POST['action'] ?? 'create';
if ($action === 'update_access') {
$id = (int) ($_POST['id'] ?? 0);
$role = $_POST['role'] === 'super' ? 'super' : 'admin';
$deptInput = (array) ($_POST['departments'] ?? []);
$departments = array_values(array_unique(array_filter(array_map('trim', $deptInput))));
if ($id <= 0) {
redirect_with_message('admins.php', 'Invalid admin selection.', 'error');
}
if ($role === 'admin' && empty($departments)) {
redirect_with_message('admins.php', 'Select at least one department for admin users.', 'error');
}
try {
$pdo->prepare('UPDATE admin_users SET role = ? WHERE id = ?')->execute([$role, $id]);
if ($role === 'admin') {
set_admin_departments($id, $departments);
} else {
set_admin_departments($id, []);
}
redirect_with_message('admins.php', 'Access updated.');
} catch (Throwable $e) {
redirect_with_message('admins.php', 'Could not update access.', 'error');
}
}
if ($action === 'delete') {
$id = (int) ($_POST['id'] ?? 0);
if ($id <= 0) {
redirect_with_message('admins.php', 'Invalid admin selection.', 'error');
}
if ($totalAdmins <= 1) {
redirect_with_message('admins.php', 'Cannot delete the last admin account.', 'error');
}
try {
$stmt = $pdo->prepare('DELETE FROM admin_users WHERE id = ?');
$stmt->execute([$id]);
redirect_with_message('admins.php', 'Admin deleted.');
} catch (Throwable $e) {
redirect_with_message('admins.php', 'Could not delete admin.', 'error');
}
}
if ($action === 'update_password') {
$id = (int) ($_POST['id'] ?? 0);
$password = $_POST['password'] ?? '';
$confirm = $_POST['confirm'] ?? '';
if ($id <= 0) {
redirect_with_message('admins.php', 'Invalid admin selection.', 'error');
}
if ($password === '' || $confirm === '') {
redirect_with_message('admins.php', 'Password and confirmation are required.', 'error');
}
if ($password !== $confirm) {
redirect_with_message('admins.php', 'Passwords do not match.', 'error');
}
if (strlen($password) < 8) {
redirect_with_message('admins.php', 'Password must be at least 8 characters.', 'error');
}
try {
$hash = password_hash($password, PASSWORD_DEFAULT);
$stmt = $pdo->prepare('UPDATE admin_users SET password_hash = ?, password_plain = ? WHERE id = ?');
$stmt->execute([$hash, $password, $id]);
redirect_with_message('admins.php', 'Password updated.');
} catch (Throwable $e) {
redirect_with_message('admins.php', 'Could not update password.', 'error');
}
}
// Create admin (default action)
$username = sanitize_text($_POST['username'] ?? '');
$password = $_POST['password'] ?? '';
$confirm = $_POST['confirm'] ?? '';
$role = $_POST['role'] === 'super' ? 'super' : 'admin';
$deptInput = (array) ($_POST['departments'] ?? []);
$departments = array_values(array_unique(array_filter(array_map('trim', $deptInput))));
if ($username === '' || $password === '' || $confirm === '') {
redirect_with_message('admins.php', 'All fields are required.', 'error');
}
if ($password !== $confirm) {
redirect_with_message('admins.php', 'Passwords do not match.', 'error');
}
if (strlen($password) < 8) {
redirect_with_message('admins.php', 'Password must be at least 8 characters.', 'error');
}
if ($role === 'admin' && empty($departments)) {
redirect_with_message('admins.php', 'Select at least one department for admin users.', 'error');
}
$hash = password_hash($password, PASSWORD_DEFAULT);
try {
$stmt = $pdo->prepare('INSERT INTO admin_users (username, password_hash, password_plain, role) VALUES (?, ?, ?, ?)');
$stmt->execute([$username, $hash, $password, $role]);
$newId = (int) $pdo->lastInsertId();
if ($role === 'admin') {
set_admin_departments($newId, $departments);
}
redirect_with_message('admins.php', 'Admin user created.');
} catch (Throwable $e) {
redirect_with_message('admins.php', 'Could not create admin (possibly duplicate username).', 'error');
}
}
$stmt = $pdo->query('SELECT id, username, role, created_at FROM admin_users ORDER BY created_at DESC');
$admins = $stmt->fetchAll();
$allDepartments = list_departments();
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Manage Admins</title>
<link rel="stylesheet" href="../assets/css/style.css?v=<?php echo filemtime(__DIR__ . '/../assets/css/style.css'); ?>" />
<link rel="stylesheet" href="../assets/css/polish.css?v=<?php echo filemtime(__DIR__ . '/../assets/css/polish.css'); ?>" />
<style>
.admin-actions { display: grid; gap: 8px; align-items: flex-start; max-width: 420px; }
.admin-role { width: 180px; }
table.table th, table.table td { padding: 6px 10px; vertical-align: top; }
.ghost-btn { background: linear-gradient(135deg, rgba(30, 41, 59, 0.6), rgba(15, 23, 42, 0.7)); border: 1.5px solid rgba(59, 130, 246, 0.4); color: #e5e7eb; padding: 8px 12px; border-radius: 6px; cursor: pointer; transition: all 0.2s ease; font-size: 13px; font-weight: 500; }
.ghost-btn:hover { border-color: #60a5fa; background: linear-gradient(135deg, rgba(30, 41, 59, 0.8), rgba(15, 23, 42, 0.8)); }
.dept-modal-overlay { position: fixed; inset: 0; background: rgba(15, 23, 42, 0.35); display: none; align-items: center; justify-content: center; padding: 12px; z-index: 999; }
.dept-modal-overlay.show { display: flex; }
.dept-modal { background: var(--card); border-radius: 14px; padding: 18px 20px; max-width: 580px; width: 100%; box-shadow: var(--shadow); border: 1px solid rgba(226, 232, 240, 0.6); }
.dept-modal h4 { margin: 0 0 6px 0; }
.dept-modal .helper { margin: 0 0 12px 0; }
.dept-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 10px; padding: 6px 0; max-height: 340px; overflow: auto; }
.dept-pill { background: linear-gradient(135deg, rgba(30, 41, 59, 0.5), rgba(15, 23, 42, 0.6)); border: 1.5px solid rgba(59, 130, 246, 0.3); border-radius: 8px; padding: 10px 12px; display: flex; align-items: center; gap: 10px; box-shadow: none; transition: all 0.2s ease; }
.dept-pill:hover { border-color: #60a5fa; background: linear-gradient(135deg, rgba(30, 41, 59, 0.7), rgba(15, 23, 42, 0.7)); box-shadow: 0 0 0 3px rgba(96, 165, 250, 0.1); }
.dept-pill input { accent-color: #2563eb; width: 16px; height: 16px; cursor: pointer; }
.dept-pill span { font-size: 13px; color: #e5e7eb; font-weight: 500; }
.dept-modal .action-buttons { justify-content: flex-end; }
.action-buttons { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
.card { padding: 16px 18px; }
</style>
</head>
<body class="dashboard-layout">
<div class="app-wrapper">
<?php require '_sidebar.php'; ?>
<div class="main-content">
<div class="top-bar">
<div class="top-bar-left">
<button class="sidebar-toggle" id="sidebarToggle" aria-label="Toggle sidebar">☰</button>
<div>
<h1 class="page-title">Admin Users</h1>
<div class="breadcrumb-nav"><span>Create additional admin logins.</span></div>
</div>
</div>
<div class="top-bar-right">
<a href="dashboard.php" class="badge">Dashboard</a>
<a href="logout.php" class="badge">Logout</a>
</div>
</div>
<div class="dashboard-container">
<?php if ($flash): ?>
<div class="alert <?php echo $flash['type'] === 'error' ? 'alert-error' : 'alert-success'; ?>">
<?php echo htmlspecialchars($flash['message'], ENT_QUOTES, 'UTF-8'); ?>
</div>
<?php endif; ?>
<div class="card" style="margin-bottom: 20px;">
<h3 style="margin-top: 0;">Create Admin</h3>
<form method="post" action="admins.php" style="display: grid; gap: 12px; max-width: 520px;">
<input type="hidden" name="csrf_token" value="<?php echo csrf_token(); ?>" />
<input type="hidden" name="action" value="create" />
<div>
<label for="username">Username</label>
<input id="username" name="username" type="text" required />
</div>
<div>
<label for="password">Password</label>
<input id="password" name="password" type="password" required />
<p class="helper">Min 8 characters. Choose a strong, unique password.</p>
</div>
<div>
<label for="confirm">Confirm Password</label>
<input id="confirm" name="confirm" type="password" required />
</div>
<div>
<label for="role">Role</label>
<select id="role" name="role" class="admin-role">
<option value="admin">Admin</option>
<option value="super">Super Admin</option>
</select>
<p class="helper">Super Admins see all departments and can manage users.</p>
</div>
<div>
<label>Departments (for Admin)</label>
<div class="dept-grid">
<?php foreach ($allDepartments as $dept): ?>
<label class="doc-checkbox dept-pill">
<input type="checkbox" name="departments[]" value="<?php echo htmlspecialchars($dept, ENT_QUOTES, 'UTF-8'); ?>" />
<span><?php echo htmlspecialchars($dept, ENT_QUOTES, 'UTF-8'); ?></span>
</label>
<?php endforeach; ?>
</div>
<?php if (empty($allDepartments)): ?>
<p class="helper">No departments yet. Create departments first.</p>
<?php endif; ?>
</div>
<div class="action-buttons" style="justify-content: flex-end;">
<button type="submit">Create Admin</button>
</div>
</form>
</div>
<div class="card">
<h3 style="margin-top: 0;">Existing Admins</h3>
<?php if (empty($admins)): ?>
<p class="helper">No admin users yet.</p>
<?php else: ?>
<table class="table">
<thead>
<tr>
<th>Username</th>
<th>Role & Access</th>
<th>Created</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($admins as $admin): ?>
<?php $assigned = $admin['role'] === 'super' ? [] : fetch_admin_departments((int) $admin['id']); ?>
<tr>
<td>
<strong><?php echo htmlspecialchars($admin['username'], ENT_QUOTES, 'UTF-8'); ?></strong>
<?php if ($admin['role'] === 'super'): ?>
<span class="badge" style="background:#eef2ff; color:#1e3a8a; margin-left:6px;">Super Admin</span>
<?php endif; ?>
</td>
<td>
<form method="post" action="admins.php" class="admin-actions">
<input type="hidden" name="csrf_token" value="<?php echo csrf_token(); ?>" />
<input type="hidden" name="id" value="<?php echo $admin['id']; ?>" />
<input type="hidden" name="action" value="update_access" />
<label style="display: block;">
<span class="helper">Role</span>
<select name="role" class="admin-role">
<option value="admin" <?php echo $admin['role'] === 'admin' ? 'selected' : ''; ?>>Admin</option>
<option value="super" <?php echo $admin['role'] === 'super' ? 'selected' : ''; ?>>Super Admin</option>
</select>
</label>
<div class="helper">Departments (Admin only)</div>
<?php if ($admin['role'] === 'admin' && !empty($assigned)): ?>
<div class="badge-list subtext">
<?php foreach ($assigned as $dept): ?>
<span class="badge" style="background:#eef2ff; color:#1e3a8a;"><?php echo htmlspecialchars($dept, ENT_QUOTES, 'UTF-8'); ?></span>
<?php endforeach; ?>
</div>
<?php elseif ($admin['role'] === 'super'): ?>
<div class="helper">Access to all departments.</div>
<?php endif; ?>
<div class="action-buttons">
<button type="button" class="ghost-btn" data-open-dept="<?php echo $admin['id']; ?>">Select Departments</button>
<button type="submit" class="compact-btn">Update</button>
</div>
<div class="dept-modal-overlay" data-dept-modal="<?php echo $admin['id']; ?>">
<div class="dept-modal">
<h4>Select Departments</h4>
<p class="helper">Choose at least one department for Admin role.</p>
<div class="dept-grid">
<?php foreach ($allDepartments as $dept): ?>
<?php $checked = in_array($dept, $assigned, true); ?>
<label class="doc-checkbox dept-pill">
<input type="checkbox" name="departments[]" value="<?php echo htmlspecialchars($dept, ENT_QUOTES, 'UTF-8'); ?>" <?php echo $checked ? 'checked' : ''; ?> />
<span><?php echo htmlspecialchars($dept, ENT_QUOTES, 'UTF-8'); ?></span>
</label>
<?php endforeach; ?>
<?php if (empty($allDepartments)): ?>
<p class="helper">No departments yet.</p>
<?php endif; ?>
</div>
<div class="action-buttons" style="justify-content: flex-end;">
<button type="button" class="ghost-btn" data-close-dept>Close</button>
<button type="submit" class="compact-btn">Update</button>
</div>
</div>
</div>
</form>
</td>
<td><?php echo htmlspecialchars($admin['created_at'], ENT_QUOTES, 'UTF-8'); ?></td>
<td>
<div style="display:grid; gap:8px; align-items:start; min-width:220px;">
<form method="post" action="admins.php" style="display:grid; gap:6px;">
<input type="hidden" name="csrf_token" value="<?php echo csrf_token(); ?>" />
<input type="hidden" name="action" value="update_password" />
<input type="hidden" name="id" value="<?php echo $admin['id']; ?>" />
<label class="helper" style="margin:0;">Change password</label>
<input type="password" name="password" placeholder="New password" minlength="8" required />
<input type="password" name="confirm" placeholder="Confirm password" minlength="8" required />
<button type="submit" class="compact-btn">Update Password</button>
</form>
<form method="post" action="admins.php" onsubmit="return confirm('Delete this admin?');">
<input type="hidden" name="csrf_token" value="<?php echo csrf_token(); ?>" />
<input type="hidden" name="action" value="delete" />
<input type="hidden" name="id" value="<?php echo $admin['id']; ?>" />
<button type="submit" style="background: var(--danger); color: #fff; border: none; padding: 6px 10px; border-radius: 6px; cursor: pointer;">Delete</button>
</form>
</div>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
const openButtons = document.querySelectorAll('[data-open-dept]');
openButtons.forEach((btn) => {
btn.addEventListener('click', () => {
const id = btn.getAttribute('data-open-dept');
const modal = document.querySelector(`[data-dept-modal="${id}"]`);
if (modal) {
modal.classList.add('show');
}
});
});
const closeButtons = document.querySelectorAll('[data-close-dept]');
closeButtons.forEach((btn) => {
btn.addEventListener('click', () => {
const modal = btn.closest('[data-dept-modal]');
if (modal) {
modal.classList.remove('show');
}
});
});
document.querySelectorAll('[data-dept-modal]').forEach((overlay) => {
overlay.addEventListener('click', (e) => {
if (e.target === overlay) {
overlay.classList.remove('show');
}
});
});
});
</script>
</div>
</div>
</div>
</body>
</html>