File size: 21.75Kb
<?php
require '../config/bootstrap.php';
require '../config/auth.php';
if (!is_admin()) {
header('Location: ' . BASE_URL . 'login.php');
exit;
}
$stats = [];
try {
global $pdo;
$stmt = $pdo->query("SELECT COUNT(*) as count FROM orders");
$stats['orders'] = $stmt->fetchColumn();
$stmt = $pdo->query("SELECT COUNT(*) as count FROM users");
$stats['users'] = $stmt->fetchColumn();
$stmt = $pdo->query("SELECT COUNT(*) as count FROM photos");
$stats['photos'] = $stmt->fetchColumn();
$stmt = $pdo->query("SELECT COUNT(*) as count FROM discounts WHERE expires_at IS NULL OR expires_at >= CURDATE()");
$stats['discounts'] = $stmt->fetchColumn();
$stmt = $pdo->query("SELECT status, COUNT(*) as count FROM orders GROUP BY status");
$order_statuses = $stmt->fetchAll(PDO::FETCH_ASSOC);
$total_orders = $stats['orders'];
$completed_orders = 0;
foreach ($order_statuses as $status) {
if ($status['status'] === 'Доставлен') {
$completed_orders = $status['count'];
break;
}
}
$stats['order_statuses'] = $order_statuses;
$stats['completed_orders'] = $completed_orders;
$stats['completed_percent'] = $total_orders > 0 ? round(($completed_orders / $total_orders) * 100) : 0;
$stmt = $pdo->query("SELECT o.*, u.name as user_name FROM orders o LEFT JOIN users u ON o.user_id = u.id ORDER BY o.created_at DESC LIMIT 5");
$stats['recent_orders'] = $stmt->fetchAll(PDO::FETCH_ASSOC);
$stmt = $pdo->query("SELECT * FROM users ORDER BY created_at DESC LIMIT 5");
$stats['recent_users'] = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
$error = 'Ошибка базы данных: ' . $e->getMessage();
}
require '../includes/header.php';
?>
<div class="container-fluid py-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="h3 fw-bold">
<i class="bi bi-speedometer2 me-2"></i> Административная панель
</h1>
<div>
<span class="badge bg-primary">
<i class="bi bi-person-circle"></i> <?= e($_SESSION['user_name'] ?? 'Админ') ?>
</span>
</div>
</div>
<div class="row g-4 mb-4">
<div class="col-xl-3 col-lg-6">
<div class="card border-left-primary shadow h-100 py-2">
<div class="card-body">
<div class="row no-gutters align-items-center">
<div class="col mr-2">
<div class="text-xs font-weight-bold text-primary text-uppercase mb-1">
Заказы
</div>
<div class="h5 mb-0 font-weight-bold text-gray-800">
<?= e($stats['orders'] ?? 0) ?>
</div>
<div class="mt-2">
<small class="text-muted">
<i class="bi bi-check-circle text-success"></i>
Выполнено: <?= e($stats['completed_orders'] ?? 0) ?>
</small>
</div>
</div>
<div class="col-auto">
<i class="bi bi-cart-check fa-2x text-gray-300"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-lg-6">
<div class="card border-left-success shadow h-100 py-2">
<div class="card-body">
<div class="row no-gutters align-items-center">
<div class="col mr-2">
<div class="text-xs font-weight-bold text-success text-uppercase mb-1">
Пользователи
</div>
<div class="h5 mb-0 font-weight-bold text-gray-800">
<?= e($stats['users'] ?? 0) ?>
</div>
<div class="mt-2">
<small class="text-muted">
<i class="bi bi-people"></i>
Всего зарегистрировано
</small>
</div>
</div>
<div class="col-auto">
<i class="bi bi-people fa-2x text-gray-300"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-lg-6">
<div class="card border-left-info shadow h-100 py-2">
<div class="card-body">
<div class="row no-gutters align-items-center">
<div class="col mr-2">
<div class="text-xs font-weight-bold text-info text-uppercase mb-1">
Фотографии
</div>
<div class="h5 mb-0 font-weight-bold text-gray-800">
<?= e($stats['photos'] ?? 0) ?>
</div>
<div class="mt-2">
<small class="text-muted">
<i class="bi bi-image"></i>
Всего загружено
</small>
</div>
</div>
<div class="col-auto">
<i class="bi bi-images fa-2x text-gray-300"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-lg-6">
<div class="card border-left-warning shadow h-100 py-2">
<div class="card-body">
<div class="row no-gutters align-items-center">
<div class="col mr-2">
<div class="text-xs font-weight-bold text-warning text-uppercase mb-1">
Активные скидки
</div>
<div class="h5 mb-0 font-weight-bold text-gray-800">
<?= e($stats['discounts'] ?? 0) ?>
</div>
<div class="mt-2">
<small class="text-muted">
<i class="bi bi-percent"></i>
Действующие промокоды
</small>
</div>
</div>
<div class="col-auto">
<i class="bi bi-percent fa-2x text-gray-300"></i>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row mb-4">
<div class="col-lg-8">
<div class="card shadow">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">
<i class="bi bi-bar-chart me-2"></i>Статусы заказов
</h6>
</div>
<div class="card-body">
<div class="mb-4">
<h4 class="small font-weight-bold">
Выполненные заказы
<span class="float-right"><?= e($stats['completed_percent'] ?? 0) ?>%</span>
</h4>
<div class="progress mb-4">
<div class="progress-bar bg-success"
role="progressbar"
style="width: <?= e($stats['completed_percent'] ?? 0) ?>%"
aria-valuenow="<?= e($stats['completed_percent'] ?? 0) ?>"
aria-valuemin="0"
aria-valuemax="100">
</div>
</div>
</div>
<div class="row">
<?php if (!empty($stats['order_statuses'])): ?>
<?php foreach ($stats['order_statuses'] as $status): ?>
<div class="col-md-3 mb-3">
<div class="card border-0">
<div class="card-body text-center">
<div class="h5 mb-1"><?= e($status['count']) ?></div>
<div class="text-muted small">
<?php
$badge_class = [
'Новый' => 'bg-primary',
'В обработке' => 'bg-warning',
'Готов' => 'bg-info',
'Доставлен' => 'bg-success',
'Отменен' => 'bg-danger'
][$status['status']] ?? 'bg-secondary';
?>
<span class="badge <?= $badge_class ?>">
<?= e($status['status']) ?>
</span>
</div>
</div>
</div>
</div>
<?php endforeach; ?>
<?php else: ?>
<div class="col-12">
<div class="alert alert-info">
<i class="bi bi-info-circle"></i> Нет данных о заказах
</div>
</div>
<?php endif; ?>
</div>
</div>
</div>
</div>
<div class="col-lg-4">
<div class="card shadow">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">
<i class="bi bi-clock-history me-2"></i>Быстрые действия
</h6>
</div>
<div class="card-body">
<div class="list-group list-group-flush">
<a href="<?= BASE_URL ?>admin/orders.php" class="list-group-item list-group-item-action d-flex align-items-center">
<i class="bi bi-cart me-3 text-primary"></i>
<div>
<div class="fw-bold">Управление заказами</div>
<small class="text-muted">Просмотр и редактирование заказов</small>
</div>
</a>
<a href="<?= BASE_URL ?>admin/users.php" class="list-group-item list-group-item-action d-flex align-items-center">
<i class="bi bi-people me-3 text-success"></i>
<div>
<div class="fw-bold">Управление пользователями</div>
<small class="text-muted">Редактирование пользователей</small>
</div>
</a>
<a href="<?= BASE_URL ?>admin/photos.php" class="list-group-item list-group-item-action d-flex align-items-center">
<i class="bi bi-images me-3 text-info"></i>
<div>
<div class="fw-bold">Модерация фото</div>
<small class="text-muted">Одобрение и отклонение фото</small>
</div>
</a>
<a href="<?= BASE_URL ?>admin/discounts.php" class="list-group-item list-group-item-action d-flex align-items-center">
<i class="bi bi-percent me-3 text-warning"></i>
<div>
<div class="fw-bold">Управление скидками</div>
<small class="text-muted">Создание промокодов</small>
</div>
</a>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-6">
<div class="card shadow mb-4">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">
<i class="bi bi-clock me-2"></i>Последние заказы
</h6>
</div>
<div class="card-body">
<?php if (!empty($stats['recent_orders'])): ?>
<div class="table-responsive">
<table class="table table-sm table-hover">
<thead>
<tr>
<th>ID</th>
<th>Клиент</th>
<th>Сумма</th>
<th>Статус</th>
<th>Дата</th>
</tr>
</thead>
<tbody>
<?php foreach ($stats['recent_orders'] as $order): ?>
<tr>
<td>
<a href="<?= BASE_URL ?>admin/order_view.php?id=<?= e($order['id']) ?>"
class="text-decoration-none">
#<?= e($order['id']) ?>
</a>
</td>
<td><?= e($order['user_name'] ?? 'Не указан') ?></td>
<td><?= number_format($order['total'], 2) ?> ₽</td>
<td>
<?= status_badge($order['status']) ?>
</td>
<td>
<small class="text-muted">
<?= format_date($order['created_at']) ?>
</small>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<div class="text-center mt-2">
<a href="<?= BASE_URL ?>admin/orders.php" class="btn btn-sm btn-outline-primary">
<i class="bi bi-arrow-right"></i> Все заказы
</a>
</div>
<?php else: ?>
<div class="alert alert-info">
<i class="bi bi-info-circle"></i> Нет последних заказов
</div>
<?php endif; ?>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="card shadow mb-4">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">
<i class="bi bi-person-plus me-2"></i>Новые пользователи
</h6>
</div>
<div class="card-body">
<?php if (!empty($stats['recent_users'])): ?>
<div class="table-responsive">
<table class="table table-sm table-hover">
<thead>
<tr>
<th>ID</th>
<th>Имя</th>
<th>Email</th>
<th>Роль</th>
<th>Дата</th>
</tr>
</thead>
<tbody>
<?php foreach ($stats['recent_users'] as $user): ?>
<tr>
<td>#<?= e($user['id']) ?></td>
<td>
<strong><?= e($user['name']) ?></strong>
</td>
<td>
<small><?= e($user['email']) ?></small>
</td>
<td>
<span class="badge <?= $user['role'] === 'admin' ? 'bg-danger' : 'bg-secondary' ?>">
<?= e($user['role']) ?>
</span>
</td>
<td>
<small class="text-muted">
<?= format_date($user['created_at']) ?>
</small>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<div class="text-center mt-2">
<a href="<?= BASE_URL ?>admin/users.php" class="btn btn-sm btn-outline-primary">
<i class="bi bi-arrow-right"></i> Все пользователи
</a>
</div>
<?php else: ?>
<div class="alert alert-info">
<i class="bi bi-info-circle"></i> Нет новых пользователей
</div>
<?php endif; ?>
</div>
</div>
</div>
</div>
<?php if (isset($error)): ?>
<div class="alert alert-danger">
<i class="bi bi-exclamation-triangle"></i>
<?= e($error) ?>
</div>
<?php endif; ?>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
function updateStats() {
fetch('<?= BASE_URL ?>ajax/admin/dashboard_stats.php')
.then(response => {
if (!response.ok) throw new Error('Network error');
return response.json();
})
.then(data => {
document.querySelectorAll('.card').forEach(card => {
if (card.querySelector('.text-primary')) {
const ordersEl = card.querySelector('.text-primary');
if (ordersEl && ordersEl.textContent.includes('Заказы')) {
const countEl = card.querySelector('.h5');
if (countEl) countEl.textContent = data.orders || 0;
}
}
});
const progressBar = document.querySelector('.progress-bar');
if (progressBar) {
const percent = data.completed_percent || 0;
progressBar.style.width = percent + '%';
progressBar.setAttribute('aria-valuenow', percent);
progressBar.textContent = percent + '%';
const percentText = document.querySelector('.float-right');
if (percentText) {
percentText.textContent = percent + '%';
}
}
console.log('Stats updated:', data);
})
.catch(error => {
console.error('Error updating stats:', error);
});
}
setInterval(updateStats, 30000);
document.addEventListener('visibilitychange', function() {
if (!document.hidden) {
updateStats();
}
});
});
</script>
<?php require '../includes/footer.php'; ?>