File size: 10.06Kb
<?php
/**
* CMS: LaiCMS (v1.0 Edition 2026)
* File: admin/users.php
* Оптимизация: Ultra-Compact User & Economy Manager + Уведомления
*/
require_once '../system/db.php';
require_once '../system/functions.php';
if (!function_exists('isAdmin') || !isAdmin()) { die("403 Forbidden"); }
// 1. БЕЗОПАСНОЕ ОБНОВЛЕНИЕ
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['edit_user'])) {
$target_id = (int)$_POST['user_id'];
$new_user = trim($_POST['username']);
$new_role = ($target_id === $_SESSION['user_id']) ? 'admin' : ($_POST['role'] === 'admin' ? 'admin' : 'user');
$new_bal = (int)$_POST['balance'];
$stmt = $mysqli->prepare("UPDATE users SET username = ?, role = ?, balance = ? WHERE id = ?");
$stmt->bind_param("ssii", $new_user, $new_role, $new_bal, $target_id);
$stmt->execute();
if (!empty($_POST['new_password'])) {
$hash = password_hash($_POST['new_password'], PASSWORD_DEFAULT);
$stmt_pw = $mysqli->prepare("UPDATE users SET password = ? WHERE id = ?");
$stmt_pw->bind_param("si", $hash, $target_id);
$stmt_pw->execute();
}
header("Location: users.php?success=upd"); exit;
}
// 2. БЕЗОПАСНОЕ УДАЛЕНИЕ
if (isset($_GET['delete'])) {
$id = (int)$_GET['delete'];
if ($id !== $_SESSION['user_id']) {
$stmt = $mysqli->prepare("DELETE FROM users WHERE id = ?");
$stmt->bind_param("i", $id);
$stmt->execute();
}
header("Location: users.php?success=del"); exit;
}
include '../system/header.php';
$users = $mysqli->query("SELECT * FROM users ORDER BY id DESC");
// Логика отображения уведомлений
$msg = "";
if (isset($_GET['success'])) {
if ($_GET['success'] == 'upd') $msg = "Данные пользователя успешно обновлены!";
if ($_GET['success'] == 'del') $msg = "Пользователь удален из системы.";
}
?>
<style>
.u-container { padding: 10px; max-width: 100%; }
.u-card { background: var(--pico-card-background-color); border-radius: 16px; border: 1px solid var(--pico-muted-border-color); overflow: hidden; }
.u-table { width: 100%; border-collapse: collapse; font-size: 0.85rem; }
.u-table td { padding: 12px 8px; border-bottom: 1px solid var(--pico-muted-border-color); vertical-align: middle; }
.av-box { width: 36px; height: 36px; border-radius: 10px; display: flex; align-items: center; justify-content: center; font-weight: bold; background: var(--pico-primary-background); color: #fff; flex-shrink: 0; }
.badge { font-size: 0.6rem; font-weight: 800; padding: 2px 6px; border-radius: 6px; text-transform: uppercase; }
.b-admin { background: #e74c3c22; color: #e74c3c; border: 1px solid #e74c3c; }
.b-user { background: #3498db22; color: #3498db; border: 1px solid #3498db; }
/* Красивое уведомление (Toast) */
.toast-notify {
position: fixed; top: 80px; right: 20px; z-index: 2000;
background: var(--pico-primary); color: white;
padding: 12px 24px; border-radius: 12px;
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
display: flex; align-items: center; gap: 10px;
animation: slideInRight 0.5s ease, fadeOut 0.5s ease 4s forwards;
}
@keyframes slideInRight { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } }
@keyframes fadeOut { to { opacity: 0; visibility: hidden; } }
@media (max-width: 600px) {
.u-table thead { display: none; }
.u-table tr { display: block; padding: 10px; border-bottom: 5px solid var(--pico-muted-border-color); }
.u-table td { display: flex; justify-content: space-between; border: none; padding: 4px 0; text-align: right; }
.u-table td::before { content: attr(data-label); font-weight: bold; opacity: 0.5; font-size: 0.7rem; }
.hide-mob { display: none; }
.toast-notify { left: 20px; right: 20px; top: 20px; }
}
</style>
<?php if ($msg): ?>
<div class="toast-notify animate__animated">
<i class="fa-solid fa-circle-check"></i>
<span><?= $msg ?></span>
</div>
<?php endif; ?>
<div class="u-container animate__animated animate__fadeIn">
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:1rem;">
<h3 style="margin:0;"><i class="fa-solid fa-users-gear" style="color:var(--pico-primary)"></i> Управление</h3>
<input type="text" id="uSearch" placeholder="Поиск пользователя..." onkeyup="uFilter()" style="margin:0; width:180px; font-size:0.8rem;">
</div>
<div class="u-card">
<table class="u-table" id="uTable">
<thead>
<tr style="opacity:0.6; font-size:0.7rem;">
<th>ID</th>
<th>Профиль</th>
<th class="hide-mob">Роль</th>
<th>Баланс</th>
<th style="text-align:right;">Действия</th>
</tr>
</thead>
<tbody>
<?php while($u = $users->fetch_assoc()): ?>
<tr class="u-item" data-search="<?= strtolower(htmlspecialchars($u['username'])) ?>">
<td data-label="ID"><kbd><?= $u['id'] ?></kbd></td>
<td data-label="Имя">
<div style="display:flex; align-items:center; gap:10px; justify-content: flex-end;">
<div style="text-align:right;">
<div style="font-weight:700;"><?= htmlspecialchars($u['username']) ?></div>
<div class="hide-mob" style="font-size:0.6rem; opacity:0.5;">Регистрация: <?= date('d.m.Y', strtotime($u['created_at'] ?? 'now')) ?></div>
</div>
<div class="av-box"><?= mb_strtoupper(mb_substr($u['username'], 0, 1)) ?></div>
</div>
</td>
<td data-label="Доступ" class="hide-mob">
<span class="badge b-<?= $u['role'] ?>"><?= $u['role'] ?></span>
</td>
<td data-label="Счет"><b style="color:var(--pico-primary);"><?= number_format($u['balance'], 0, '', ' ') ?></b> <small>LC</small></td>
<td data-label="Действие">
<div style="display:flex; gap:5px; justify-content:flex-end;">
<button class="outline" style="padding: 5px 10px;" onclick='openEdit(<?= json_encode($u) ?>)' title="Редактировать">
<i class="fa-solid fa-user-pen"></i>
</button>
<?php if($u['id'] !== $_SESSION['user_id']): ?>
<a href="?delete=<?= $u['id'] ?>" class="button outline secondary" style="padding: 5px 10px;" onclick="return confirm('Вы действительно хотите удалить пользователя?')" title="Удалить">
<i class="fa-solid fa-user-xmark"></i>
</a>
<?php endif; ?>
</div>
</td>
</tr>
<?php endwhile; ?>
</tbody>
</table>
</div>
</div>
<dialog id="uModal">
<article style="padding:25px; border-radius:24px; border: 1px solid var(--pico-primary);">
<header style="margin-bottom:20px; padding:0; background:none;">
<a href="javascript:closeEdit()" class="close"></a>
<h4 style="margin:0; display:flex; align-items:center; gap:10px;">
<i class="fa-solid fa-id-card-clip"></i> Настройки аккаунта
</h4>
</header>
<form method="POST">
<input type="hidden" name="user_id" id="u_id">
<div class="grid">
<label>Логин пользователя
<input type="text" name="username" id="u_name" required>
</label>
<label>Приоритет (Роль)
<select name="role" id="u_role">
<option value="user">Пользователь</option>
<option value="admin">Администратор</option>
</select>
</label>
</div>
<label>Баланс валюты (LC)
<input type="number" name="balance" id="u_bal">
</label>
<label>Новый пароль <small>(оставьте пустым для сохранения старого)</small>
<input type="password" name="new_password" placeholder="••••••••">
</label>
<footer style="margin-top:20px; padding: 0; background: none;">
<button type="submit" name="edit_user" style="width:100%; border-radius:12px;">
<i class="fa-solid fa-save"></i> Сохранить изменения
</button>
</footer>
</form>
</article>
</dialog>
<script>
const uM = document.getElementById('uModal');
function openEdit(u) {
document.getElementById('u_id').value = u.id;
document.getElementById('u_name').value = u.username;
document.getElementById('u_role').value = u.role;
document.getElementById('u_bal').value = u.balance;
uM.showModal();
}
function closeEdit() { uM.close(); }
function uFilter() {
const q = document.getElementById('uSearch').value.toLowerCase();
document.querySelectorAll('.u-item').forEach(i => {
i.style.display = i.dataset.search.includes(q) ? "" : "none";
});
}
// Закрытие при клике на фон
uM.addEventListener('click', (e) => { if(e.target === uM) closeEdit(); });
// Авто-скрытие уведомления через 4.5 сек
setTimeout(() => {
const toast = document.querySelector('.toast-notify');
if(toast) toast.style.display = 'none';
}, 4500);
</script>
<?php include '../system/footer.php'; ?>