• Из-за обновления GTA 5 (был добавлен новый патч) может временно не работать вход в RAGE Multiplayer.

    Ошибка: Ваша версия Grand Theft Auto V не поддерживается RAGE Multiplayer.
    ERROR: Your game version is not supported by RAGE Multiplayer.

    Данная ошибка говорит о том, что GTA V обновилась до новой версии (GTA Online тоже). Вам необходимо обновить саму игру в главном меню вашего приложения (Steam / Epic Games / Rockstar Games).
    Если после этого RAGE:MP все равно не работает - вам нужно дождаться выхода патча для самого мультиплеера (обычно это занимает от нескольких часов до нескольких дней).

    Новости и апдейты Rockstar Games - https://www.rockstargames.com/newswire/
    Статус всех служб для Rockstar Games Launcher и поддерживаемых игр: https://support.rockstargames.com/ru/servicestatus


    Grand Theft Auto 5 (+ GTA Online) последний раз были обновлены:

Majestic Admin Panel

rizzaqq

Начинающий специалист
BackEnd developer
14 Янв 2025
198
54
50
Если кому то было интересно как выглядит админ панель на Majestic RP то вот одна из вкладок
1774225724676.png
 
Реакции: Andrey_winston

rizzaqq

Начинающий специалист
BackEnd developer
14 Янв 2025
198
54
50
Ну... Я конечно же это сверстал вся вёрстка готова просто мне лень привязывать к серверу :(
 

jungledev

Старожил
BackEnd developer
16 Янв 2026
345
201
50
Реакции: kwet999

🐝 ubees

Мастер
BackEnd developer
11 Фев 2023
1,087
185
134
41
HTML:
<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
    <title>Статистика репортов | Административная панель</title>
    <!-- Font Awesome 6 (free) -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
    <!-- Google Fonts: Inter & Roboto Mono -->
    <link href="https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,300;14..32,400;14..32,500;14..32,600;14..32,700&family=Roboto+Mono:wght@400;500&display=swap" rel="stylesheet">
    <!-- Chart.js CDN -->
    <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: 'Inter', sans-serif;
            background: #f1f5f9;
            color: #0f172a;
            line-height: 1.4;
        }

        /* app layout */
        .app {
            display: flex;
            min-height: 100vh;
        }

        /* SIDEBAR */
        .sidebar {
            width: 280px;
            background: #ffffff;
            border-right: 1px solid #e2e8f0;
            display: flex;
            flex-direction: column;
            position: sticky;
            top: 0;
            height: 100vh;
            overflow-y: auto;
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.02);
            transition: all 0.2s;
            z-index: 10;
        }

        /* sidebar inner */
        .sidebar-header {
            padding: 24px 20px 12px 20px;
            border-bottom: 1px solid #eef2ff;
        }

        .logo-area h2 {
            font-size: 1.5rem;
            font-weight: 700;
            background: linear-gradient(135deg, #2563eb, #1e40af);
            background-clip: text;
            -webkit-background-clip: text;
            color: transparent;
            letter-spacing: -0.3px;
        }

        .logo-area p {
            font-size: 0.75rem;
            color: #5b6e8c;
            margin-top: 4px;
        }

        /* search box */
        .search-menu {
            margin: 20px 0 12px 0;
            position: relative;
        }

        .search-menu input {
            width: 100%;
            padding: 10px 36px 10px 38px;
            border: 1px solid #e2e8f0;
            border-radius: 14px;
            background: #f8fafc;
            font-size: 0.85rem;
            font-weight: 500;
            transition: all 0.2s;
            color: #0f172a;
        }

        .search-menu input:focus {
            outline: none;
            border-color: #3b82f6;
            box-shadow: 0 0 0 3px rgba(59,130,246,0.2);
            background: #fff;
        }

        .search-menu i {
            position: absolute;
            left: 12px;
            top: 50%;
            transform: translateY(-50%);
            color: #94a3b8;
            font-size: 0.9rem;
        }

        .kbd-hint {
            position: absolute;
            right: 12px;
            top: 50%;
            transform: translateY(-50%);
            background: #eef2ff;
            padding: 2px 8px;
            border-radius: 8px;
            font-size: 0.7rem;
            font-weight: 600;
            font-family: monospace;
            color: #1e293b;
            pointer-events: none;
            letter-spacing: 0.5px;
        }

        /* navigation sections */
        .nav-section {
            padding: 0 16px 20px 16px;
            border-bottom: 1px solid #f0f2f5;
        }

        .nav-section:last-child {
            border-bottom: none;
        }

        .nav-title {
            font-size: 0.7rem;
            text-transform: uppercase;
            letter-spacing: 0.6px;
            font-weight: 700;
            color: #5b6e8c;
            margin: 16px 0 10px 4px;
        }

        .nav-list {
            list-style: none;
            display: flex;
            flex-direction: column;
            gap: 6px;
        }

        .nav-item {
            padding: 8px 12px;
            border-radius: 12px;
            font-size: 0.9rem;
            font-weight: 500;
            color: #1e293b;
            transition: all 0.2s;
            cursor: default;
            display: flex;
            align-items: center;
            gap: 10px;
        }

        .nav-item i {
            width: 20px;
            font-size: 1rem;
            color: #5b6e8c;
        }

        .nav-item:hover {
            background: #f1f5f9;
            color: #2563eb;
        }

        .nav-item.hidden-menu {
            display: none;
        }

        /* main content */
        .main-content {
            flex: 1;
            padding: 28px 32px;
            overflow-x: auto;
        }

        /* page header */
        .page-header {
            display: flex;
            justify-content: space-between;
            align-items: baseline;
            flex-wrap: wrap;
            margin-bottom: 28px;
        }

        .page-header h1 {
            font-size: 1.9rem;
            font-weight: 700;
            background: linear-gradient(135deg, #0f2b3d, #1e3a8a);
            background-clip: text;
            -webkit-background-clip: text;
            color: transparent;
        }

        .badge-online {
            background: #d9f99d;
            color: #365314;
            padding: 4px 12px;
            border-radius: 40px;
            font-size: 0.75rem;
            font-weight: 600;
        }

        /* cards grid */
        .stats-grid {
            background: white;
            border-radius: 24px;
            padding: 20px 24px;
            box-shadow: 0 1px 3px rgba(0,0,0,0.05), 0 1px 2px rgba(0,0,0,0.03);
            margin-bottom: 28px;
            border: 1px solid #eef2ff;
        }

        .section-title {
            font-size: 1.3rem;
            font-weight: 700;
            margin-bottom: 6px;
            display: flex;
            align-items: center;
            gap: 8px;
        }

        .section-sub {
            font-size: 0.8rem;
            color: #5b6e8c;
            border-left: 3px solid #3b82f6;
            padding-left: 12px;
            margin-bottom: 20px;
        }

        .metrics-row {
            display: flex;
            flex-wrap: wrap;
            gap: 20px;
            margin-top: 12px;
        }

        .metric-card {
            background: #f8fafc;
            border-radius: 20px;
            padding: 16px 18px;
            min-width: 160px;
            flex: 1 1 180px;
            border: 1px solid #eef2ff;
            transition: all 0.2s;
        }

        .metric-label {
            font-size: 0.75rem;
            text-transform: uppercase;
            font-weight: 600;
            color: #4b5563;
            letter-spacing: 0.4px;
            margin-bottom: 8px;
        }

        .metric-value {
            font-size: 2rem;
            font-weight: 800;
            font-family: 'Roboto Mono', monospace;
            color: #0f172a;
            line-height: 1.2;
        }

        .metric-unit {
            font-size: 0.8rem;
            font-weight: 500;
            color: #5b6e8c;
        }

        .badge-moderator {
            background: #e6f0ff;
            border-radius: 30px;
            padding: 4px 12px;
            font-size: 0.7rem;
            font-weight: 600;
            color: #2563eb;
            display: inline-block;
            margin-bottom: 12px;
        }

        /* double chart layout */
        .charts-container {
            display: flex;
            flex-wrap: wrap;
            gap: 28px;
            margin-bottom: 28px;
        }

        .chart-card {
            background: white;
            border-radius: 24px;
            padding: 18px 20px;
            box-shadow: 0 1px 3px rgba(0,0,0,0.05);
            border: 1px solid #eef2ff;
            flex: 1 1 400px;
        }

        .chart-title {
            font-weight: 700;
            font-size: 1.1rem;
            margin-bottom: 16px;
            display: flex;
            align-items: center;
            gap: 8px;
            border-left: 3px solid #3b82f6;
            padding-left: 12px;
        }

        canvas {
            max-height: 280px;
            width: 100%;
        }

        .test-level-card {
            background: linear-gradient(145deg, #ffffff, #fef9e3);
            border-radius: 24px;
            padding: 18px 24px;
            margin-bottom: 28px;
            border: 1px solid #ffedd5;
            display: flex;
            align-items: center;
            justify-content: space-between;
            flex-wrap: wrap;
        }

        .test-info h4 {
            font-size: 1.1rem;
            font-weight: 700;
        }

        .level-badge {
            background: #f97316;
            color: white;
            padding: 8px 24px;
            border-radius: 60px;
            font-weight: 800;
            font-size: 1.5rem;
            font-family: monospace;
            box-shadow: 0 4px 8px rgba(249,115,22,0.2);
        }

        .help-footer {
            background: #f1f5f9;
            border-radius: 20px;
            padding: 14px 20px;
            display: flex;
            justify-content: space-between;
            align-items: center;
            color: #334155;
            font-size: 0.85rem;
            font-weight: 500;
            border: 1px solid #e2e8f0;
        }

        hr {
            margin: 12px 0;
            border: 0;
            border-top: 1px solid #eef2ff;
        }

        @media (max-width: 850px) {
            .app {
                flex-direction: column;
            }
            .sidebar {
                width: 100%;
                height: auto;
                position: relative;
                border-right: none;
                border-bottom: 1px solid #e2e8f0;
            }
            .main-content {
                padding: 20px;
            }
            .metrics-row {
                gap: 12px;
            }
        }

        /* scrollbar */
        ::-webkit-scrollbar {
            width: 6px;
        }
        ::-webkit-scrollbar-track {
            background: #f1f1f1;
            border-radius: 10px;
        }
        ::-webkit-scrollbar-thumb {
            background: #cbd5e1;
            border-radius: 10px;
        }
    </style>
</head>
<body>
<div class="app">
    <!-- Боковая панель с поиском и меню -->
    <aside class="sidebar">
        <div class="sidebar-header">
            <div class="logo-area">
                <h2><i class="fas fa-chart-line" style="color: #2563eb; font-size: 1.5rem;"></i> Test онлайн</h2>
                <p>статистика & репорты</p>
            </div>
            <div class="search-menu">
                <i class="fas fa-search"></i>
                <input type="text" id="menuSearch" placeholder="Поиск по меню" autocomplete="off">
                <span class="kbd-hint">Ctrl+K</span>
            </div>
        </div>

        <!-- Навигационная группа 1 (быстрые ссылки) -->
        <div class="nav-section">
            <div class="nav-title"><i class="fas fa-bolt"></i> БЫСТРЫЙ ДОСТУП</div>
            <ul class="nav-list" id="navListFast">
                <li class="nav-item" data-search-term="Статистика Репорты"><i class="fas fa-chart-pie"></i> Статистика / Репорты</li>
                <li class="nav-item" data-search-term="Правила Государствен"><i class="fas fa-gavel"></i> Правила / Государствен...</li>
                <li class="nav-item" data-search-term="Правила Общие правила"><i class="fas fa-book"></i> Правила / Общие правила</li>
                <li class="nav-item" data-search-term="Семьи"><i class="fas fa-users"></i> Семьи</li>
                <li class="nav-item" data-search-term="Фракции"><i class="fas fa-flag-checkered"></i> Фракции</li>
            </ul>
        </div>

        <!-- Основное меню (полный список) -->
        <div class="nav-section">
            <div class="nav-title"><i class="fas fa-compass"></i> ОСНОВНОЕ</div>
            <ul class="nav-list" id="navListMain">
                <li class="nav-item" data-search-term="Главная"><i class="fas fa-home"></i> Главная</li>
                <li class="nav-item" data-search-term="Команды сервера"><i class="fas fa-terminal"></i> Команды сервера</li>
                <li class="nav-item" data-search-term="Отпуска"><i class="fas fa-umbrella-beach"></i> Отпуска</li>
                <li class="nav-item" data-search-term="Наказания"><i class="fas fa-ban"></i> Наказания</li>
                <li class="nav-item" data-search-term="Модерация"><i class="fas fa-shield-haltered"></i> Модерация</li>
                <li class="nav-item" data-search-term="Штрафные баллы"><i class="fas fa-exclamation-triangle"></i> Штрафные баллы</li>
                <li class="nav-item" data-search-term="Экономика"><i class="fas fa-coins"></i> Экономика</li>
                <li class="nav-item" data-search-term="Транспорт"><i class="fas fa-truck"></i> Транспорт</li>
                <li class="nav-item" data-search-term="Предметы"><i class="fas fa-box"></i> Предметы</li>
                <li class="nav-item" data-search-term="Контент"><i class="fas fa-newspaper"></i> Контент</li>
                <li class="nav-item" data-search-term="Обучение"><i class="fas fa-graduation-cap"></i> Обучение</li>
                <li class="nav-item" data-search-term="Семьи"><i class="fas fa-heart"></i> Семьи</li>
                <li class="nav-item" data-search-term="Фракции"><i class="fas fa-handshake"></i> Фракции</li>
            </ul>
        </div>
        <div style="padding: 16px; margin-top: auto; font-size: 11px; color: #94a3b8; border-top: 1px solid #eef2ff; text-align: center;">
            <i class="fas fa-database"></i> Данные в реальном времени
        </div>
    </aside>

    <!-- Основной контент -->
    <main class="main-content">
        <div class="page-header">
            <h1><i class="fas fa-chart-simple"></i> Статистика репортов</h1>
            <div class="badge-online"><i class="fas fa-circle" style="font-size: 0.5rem; color: #22c55e;"></i> Live обновления</div>
        </div>

        <!-- Карточка статистики репортов -->
        <div class="stats-grid">
            <div class="section-title">
                <i class="fas fa-flag-checkered" style="color:#2563eb"></i> Модераторские репорты
            </div>
            <div class="section-sub">
                Остроковое данные по репортам за выбранный период • актуально на текущую сессию
            </div>
            <div class="metrics-row">
                <div class="metric-card">
                    <div class="metric-label"><i class="fas fa-ticket-alt"></i> Стандартные</div>
                    <div class="metric-value">640</div>
                    <div class="metric-unit">репортов</div>
                </div>
                <div class="metric-card">
                    <div class="metric-label"><i class="fas fa-hourglass-half"></i> Среднее время ответа</div>
                    <div class="metric-value">0.01 <span class="metric-unit">ч</span></div>
                </div>
                <div class="metric-card">
                    <div class="metric-label"><i class="fas fa-user-check"></i> Активисты администраторов</div>
                    <div class="metric-value">0.01 <span class="metric-unit">%</span></div>
                </div>
                <div class="metric-card">
                    <div class="metric-label"><i class="fas fa-clock"></i> Среднее время решения</div>
                    <div class="metric-value">0.01 <span class="metric-unit">ч</span></div>
                </div>
                <div class="metric-card">
                    <div class="metric-label"><i class="fas fa-check-double"></i> Обработали репорты</div>
                    <div class="metric-value">0.01 <span class="metric-unit">тыс.</span></div>
                </div>
            </div>
        </div>

        <!-- Двойной график: основной график (модераторские/стандартные) и тест-уровень -->
        <div class="charts-container">
            <div class="chart-card">
                <div class="chart-title">
                    <i class="fas fa-chart-line" style="color:#3b82f6"></i> График
                    <span style="font-size: 0.7rem; background: #eff6ff; padding: 2px 8px; border-radius: 40px;">Модераторские / Стандартные</span>
                </div>
                <canvas id="trendChart" width="400" height="250" style="width:100%; height:auto; max-height:250px"></canvas>
                <div class="section-sub" style="margin-top: 10px; text-align: center;">Динамика за последние 7 дней</div>
            </div>
            <div class="chart-card">
                <div class="chart-title">
                    <i class="fas fa-chart-bar"></i> Нагрузка по часам
                </div>
                <canvas id="hourlyChart" width="400" height="250" style="width:100%; height:auto; max-height:250px"></canvas>
                <div class="section-sub" style="margin-top: 10px; text-align: center;">Распределение репортов по часам (МСК)</div>
            </div>
        </div>

        <!-- Блок "Тест 9 level" и дополнительная информация -->
        <div class="test-level-card">
            <div class="test-info">
                <h4><i class="fas fa-brain"></i> Тест компетенций</h4>
                <p style="font-size: 0.8rem; color: #92400e;">Квалификация модератора & проверка знаний</p>
            </div>
            <div class="level-badge">
                9 level
            </div>
        </div>

        <!-- Дополнительный блок: справка и завершение работы -->
        <div class="help-footer">
            <span><i class="fas fa-info-circle"></i> Справка: актуальные инструкции по работе с репортами</span>
            <span><i class="fas fa-clock"></i> Завершение работы в 23:59</span>
        </div>
        <div style="margin-top: 18px; font-size: 12px; color: #6c757d; text-align: right;">
            <i class="fas fa-chart-line"></i> Обновлено: сегодня, статистика в реальном времени
        </div>
    </main>
</div>

<script>
    (function() {
        // ------------------- ФИЛЬТРАЦИЯ МЕНЮ ПО ПОИСКУ (Ctrl+K) -------------------
        const searchInput = document.getElementById('menuSearch');
        const fastItems = document.querySelectorAll('#navListFast .nav-item');
        const mainItems = document.querySelectorAll('#navListMain .nav-item');
        const allMenuItems = [...fastItems, ...mainItems];

        function filterMenu() {
            const searchTerm = searchInput.value.trim().toLowerCase();
            allMenuItems.forEach(item => {
                const text = item.innerText.toLowerCase();
                const searchAttr = item.getAttribute('data-search-term')?.toLowerCase() || '';
                const matches = text.includes(searchTerm) || searchAttr.includes(searchTerm);
                if (!matches && searchTerm !== '') {
                    item.classList.add('hidden-menu');
                } else {
                    item.classList.remove('hidden-menu');
                }
            });
        }

        searchInput.addEventListener('input', filterMenu);

        // Ctrl+K фокус на поиск
        window.addEventListener('keydown', function(e) {
            if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
                e.preventDefault();
                searchInput.focus();
            }
            // Escape для очистки
            if (e.key === 'Escape') {
                searchInput.value = '';
                filterMenu();
                searchInput.blur();
            }
        });

        // ------------------- ИНИЦИАЛИЗАЦИЯ ГРАФИКОВ (Chart.js) -------------------
        // 1. График "Модераторские vs Стандартные" (линейный)
        const ctxTrend = document.getElementById('trendChart').getContext('2d');
        const trendChart = new Chart(ctxTrend, {
            type: 'line',
            data: {
                labels: ['Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб', 'Вс'],
                datasets: [
                    {
                        label: 'Модераторские репорты',
                        data: [42, 38, 47, 52, 49, 44, 39],
                        borderColor: '#3b82f6',
                        backgroundColor: 'rgba(59, 130, 246, 0.05)',
                        borderWidth: 2.5,
                        pointBackgroundColor: '#1e40af',
                        pointBorderColor: '#fff',
                        pointRadius: 4,
                        pointHoverRadius: 6,
                        tension: 0.2,
                        fill: true,
                    },
                    {
                        label: 'Стандартные репорты',
                        data: [82, 78, 91, 102, 98, 88, 79],
                        borderColor: '#f97316',
                        backgroundColor: 'rgba(249, 115, 22, 0.03)',
                        borderWidth: 2.5,
                        pointBackgroundColor: '#c2410c',
                        pointBorderColor: '#fff',
                        pointRadius: 4,
                        pointHoverRadius: 6,
                        tension: 0.2,
                        fill: true,
                    }
                ]
            },
            options: {
                responsive: true,
                maintainAspectRatio: true,
                plugins: {
                    tooltip: { mode: 'index', intersect: false },
                    legend: { position: 'top', labels: { usePointStyle: true, boxWidth: 10, font: { size: 11 } } }
                },
                scales: {
                    y: {
                        beginAtZero: true,
                        grid: { color: '#eef2ff' },
                        title: { display: true, text: 'Количество репортов', font: { size: 10 } }
                    },
                    x: {
                        grid: { display: false },
                        title: { display: true, text: 'День недели', font: { size: 10 } }
                    }
                }
            }
        });

        // 2. График "Нагрузка по часам" (распределение репортов)
        const ctxHour = document.getElementById('hourlyChart').getContext('2d');
        const hoursLabels = [];
        for (let i = 0; i < 24; i++) {
            hoursLabels.push(`${i}:00`);
        }
        // Эмуляция нагрузки: утренний рост, пик в 17-20 часов, спад ночью
        const hourlyData = [5, 3, 2, 4, 8, 14, 28, 42, 58, 67, 72, 84, 91, 95, 102, 110, 124, 135, 142, 128, 98, 67, 34, 18];
        
        const hourlyChart = new Chart(ctxHour, {
            type: 'bar',
            data: {
                labels: hoursLabels,
                datasets: [
                    {
                        label: 'Репорты (шт)',
                        data: hourlyData,
                        backgroundColor: 'rgba(59, 130, 246, 0.7)',
                        borderRadius: 8,
                        borderSkipped: false,
                        barPercentage: 0.75,
                        categoryPercentage: 0.9,
                    }
                ]
            },
            options: {
                responsive: true,
                maintainAspectRatio: true,
                plugins: {
                    tooltip: { callbacks: { label: (ctx) => `${ctx.raw} репортов` } },
                    legend: { display: false }
                },
                scales: {
                    y: {
                        beginAtZero: true,
                        grid: { color: '#eef2ff' },
                        title: { display: true, text: 'Количество репортов', font: { size: 10 } }
                    },
                    x: {
                        ticks: { maxRotation: 45, minRotation: 35, autoSkip: true, maxTicksLimit: 12 },
                        title: { display: true, text: 'Часы (UTC+3)', font: { size: 10 } }
                    }
                }
            }
        });

        // дополнительная анимация / подпись с данными (соответствие тексту "Стандартные: 640" и т.д.)
        // Можно добавить декоративный эффект обновления метрик (чисто визуально, числа уже отображены)
        // Добавим эмуляцию динамического изменения метрик для "Активисты администраторов" и прочих - не требуется, но для ощущения живости, сделаем незначительный эффект?
        // В целях соблюдения ТЗ, просто оставляем статические данные, они полностью соответствуют описанию: 640, 0.01 и т.д.
        
        // Визуальный эффект для карточек (необязательно)
        const metricCards = document.querySelectorAll('.metric-card');
        metricCards.forEach(card => {
            card.addEventListener('mouseenter', () => {
                card.style.transform = 'translateY(-2px)';
                card.style.transition = '0.2s';
                card.style.boxShadow = '0 8px 20px rgba(0,0,0,0.05)';
            });
            card.addEventListener('mouseleave', () => {
                card.style.transform = 'translateY(0)';
                card.style.boxShadow = 'none';
            });
        });

        // Убедимся, что при изменении размера окна графики адаптируются
        window.addEventListener('resize', () => {
            trendChart.resize();
            hourlyChart.resize();
        });
        
        // маленькая деталь: добавить плейсхолдер при пустом поиске, убираем hidden при очистке
        // плюс показываем количество видимых элементов - но это опционально.
        // Убедимся, что изначально все пункты видны
        function resetVisibility() {
            allMenuItems.forEach(item => item.classList.remove('hidden-menu'));
        }
        window.resetFilter = resetVisibility;
        
        // Небольшой "live" счетчик для обработки репортов? Но оставим как статику, это соответствует исходным данным.
        // Добавим консольное сообщение об успешной загрузке.
        console.log('Dashboard статистики загружен | Модераторские репорты: стандартные 640, графики активны');
    })();
</script>
</body>
</html>
 

rizzaqq

Начинающий специалист
BackEnd developer
14 Янв 2025
198
54
50
1774332290465.png

HTML:
<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
    <title>Статистика репортов | Административная панель</title>
    <!-- Font Awesome 6 (free) -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
    <!-- Google Fonts: Inter & Roboto Mono -->
    <link href="https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,300;14..32,400;14..32,500;14..32,600;14..32,700&family=Roboto+Mono:wght@400;500&display=swap" rel="stylesheet">
    <!-- Chart.js CDN -->
    <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: 'Inter', sans-serif;
            background: #f1f5f9;
            color: #0f172a;
            line-height: 1.4;
        }

        /* app layout */
        .app {
            display: flex;
            min-height: 100vh;
        }

        /* SIDEBAR */
        .sidebar {
            width: 280px;
            background: #ffffff;
            border-right: 1px solid #e2e8f0;
            display: flex;
            flex-direction: column;
            position: sticky;
            top: 0;
            height: 100vh;
            overflow-y: auto;
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.02);
            transition: all 0.2s;
            z-index: 10;
        }

        /* sidebar inner */
        .sidebar-header {
            padding: 24px 20px 12px 20px;
            border-bottom: 1px solid #eef2ff;
        }

        .logo-area h2 {
            font-size: 1.5rem;
            font-weight: 700;
            background: linear-gradient(135deg, #2563eb, #1e40af);
            background-clip: text;
            -webkit-background-clip: text;
            color: transparent;
            letter-spacing: -0.3px;
        }

        .logo-area p {
            font-size: 0.75rem;
            color: #5b6e8c;
            margin-top: 4px;
        }

        /* search box */
        .search-menu {
            margin: 20px 0 12px 0;
            position: relative;
        }

        .search-menu input {
            width: 100%;
            padding: 10px 36px 10px 38px;
            border: 1px solid #e2e8f0;
            border-radius: 14px;
            background: #f8fafc;
            font-size: 0.85rem;
            font-weight: 500;
            transition: all 0.2s;
            color: #0f172a;
        }

        .search-menu input:focus {
            outline: none;
            border-color: #3b82f6;
            box-shadow: 0 0 0 3px rgba(59,130,246,0.2);
            background: #fff;
        }

        .search-menu i {
            position: absolute;
            left: 12px;
            top: 50%;
            transform: translateY(-50%);
            color: #94a3b8;
            font-size: 0.9rem;
        }

        .kbd-hint {
            position: absolute;
            right: 12px;
            top: 50%;
            transform: translateY(-50%);
            background: #eef2ff;
            padding: 2px 8px;
            border-radius: 8px;
            font-size: 0.7rem;
            font-weight: 600;
            font-family: monospace;
            color: #1e293b;
            pointer-events: none;
            letter-spacing: 0.5px;
        }

        /* navigation sections */
        .nav-section {
            padding: 0 16px 20px 16px;
            border-bottom: 1px solid #f0f2f5;
        }

        .nav-section:last-child {
            border-bottom: none;
        }

        .nav-title {
            font-size: 0.7rem;
            text-transform: uppercase;
            letter-spacing: 0.6px;
            font-weight: 700;
            color: #5b6e8c;
            margin: 16px 0 10px 4px;
        }

        .nav-list {
            list-style: none;
            display: flex;
            flex-direction: column;
            gap: 6px;
        }

        .nav-item {
            padding: 8px 12px;
            border-radius: 12px;
            font-size: 0.9rem;
            font-weight: 500;
            color: #1e293b;
            transition: all 0.2s;
            cursor: default;
            display: flex;
            align-items: center;
            gap: 10px;
        }

        .nav-item i {
            width: 20px;
            font-size: 1rem;
            color: #5b6e8c;
        }

        .nav-item:hover {
            background: #f1f5f9;
            color: #2563eb;
        }

        .nav-item.hidden-menu {
            display: none;
        }

        /* main content */
        .main-content {
            flex: 1;
            padding: 28px 32px;
            overflow-x: auto;
        }

        /* page header */
        .page-header {
            display: flex;
            justify-content: space-between;
            align-items: baseline;
            flex-wrap: wrap;
            margin-bottom: 28px;
        }

        .page-header h1 {
            font-size: 1.9rem;
            font-weight: 700;
            background: linear-gradient(135deg, #0f2b3d, #1e3a8a);
            background-clip: text;
            -webkit-background-clip: text;
            color: transparent;
        }

        .badge-online {
            background: #d9f99d;
            color: #365314;
            padding: 4px 12px;
            border-radius: 40px;
            font-size: 0.75rem;
            font-weight: 600;
        }

        /* cards grid */
        .stats-grid {
            background: white;
            border-radius: 24px;
            padding: 20px 24px;
            box-shadow: 0 1px 3px rgba(0,0,0,0.05), 0 1px 2px rgba(0,0,0,0.03);
            margin-bottom: 28px;
            border: 1px solid #eef2ff;
        }

        .section-title {
            font-size: 1.3rem;
            font-weight: 700;
            margin-bottom: 6px;
            display: flex;
            align-items: center;
            gap: 8px;
        }

        .section-sub {
            font-size: 0.8rem;
            color: #5b6e8c;
            border-left: 3px solid #3b82f6;
            padding-left: 12px;
            margin-bottom: 20px;
        }

        .metrics-row {
            display: flex;
            flex-wrap: wrap;
            gap: 20px;
            margin-top: 12px;
        }

        .metric-card {
            background: #f8fafc;
            border-radius: 20px;
            padding: 16px 18px;
            min-width: 160px;
            flex: 1 1 180px;
            border: 1px solid #eef2ff;
            transition: all 0.2s;
        }

        .metric-label {
            font-size: 0.75rem;
            text-transform: uppercase;
            font-weight: 600;
            color: #4b5563;
            letter-spacing: 0.4px;
            margin-bottom: 8px;
        }

        .metric-value {
            font-size: 2rem;
            font-weight: 800;
            font-family: 'Roboto Mono', monospace;
            color: #0f172a;
            line-height: 1.2;
        }

        .metric-unit {
            font-size: 0.8rem;
            font-weight: 500;
            color: #5b6e8c;
        }

        .badge-moderator {
            background: #e6f0ff;
            border-radius: 30px;
            padding: 4px 12px;
            font-size: 0.7rem;
            font-weight: 600;
            color: #2563eb;
            display: inline-block;
            margin-bottom: 12px;
        }

        /* double chart layout */
        .charts-container {
            display: flex;
            flex-wrap: wrap;
            gap: 28px;
            margin-bottom: 28px;
        }

        .chart-card {
            background: white;
            border-radius: 24px;
            padding: 18px 20px;
            box-shadow: 0 1px 3px rgba(0,0,0,0.05);
            border: 1px solid #eef2ff;
            flex: 1 1 400px;
        }

        .chart-title {
            font-weight: 700;
            font-size: 1.1rem;
            margin-bottom: 16px;
            display: flex;
            align-items: center;
            gap: 8px;
            border-left: 3px solid #3b82f6;
            padding-left: 12px;
        }

        canvas {
            max-height: 280px;
            width: 100%;
        }

        .test-level-card {
            background: linear-gradient(145deg, #ffffff, #fef9e3);
            border-radius: 24px;
            padding: 18px 24px;
            margin-bottom: 28px;
            border: 1px solid #ffedd5;
            display: flex;
            align-items: center;
            justify-content: space-between;
            flex-wrap: wrap;
        }

        .test-info h4 {
            font-size: 1.1rem;
            font-weight: 700;
        }

        .level-badge {
            background: #f97316;
            color: white;
            padding: 8px 24px;
            border-radius: 60px;
            font-weight: 800;
            font-size: 1.5rem;
            font-family: monospace;
            box-shadow: 0 4px 8px rgba(249,115,22,0.2);
        }

        .help-footer {
            background: #f1f5f9;
            border-radius: 20px;
            padding: 14px 20px;
            display: flex;
            justify-content: space-between;
            align-items: center;
            color: #334155;
            font-size: 0.85rem;
            font-weight: 500;
            border: 1px solid #e2e8f0;
        }

        hr {
            margin: 12px 0;
            border: 0;
            border-top: 1px solid #eef2ff;
        }

        @media (max-width: 850px) {
            .app {
                flex-direction: column;
            }
            .sidebar {
                width: 100%;
                height: auto;
                position: relative;
                border-right: none;
                border-bottom: 1px solid #e2e8f0;
            }
            .main-content {
                padding: 20px;
            }
            .metrics-row {
                gap: 12px;
            }
        }

        /* scrollbar */
        ::-webkit-scrollbar {
            width: 6px;
        }
        ::-webkit-scrollbar-track {
            background: #f1f1f1;
            border-radius: 10px;
        }
        ::-webkit-scrollbar-thumb {
            background: #cbd5e1;
            border-radius: 10px;
        }
    </style>
</head>
<body>
<div class="app">
    <!-- Боковая панель с поиском и меню -->
    <aside class="sidebar">
        <div class="sidebar-header">
            <div class="logo-area">
                <h2><i class="fas fa-chart-line" style="color: #2563eb; font-size: 1.5rem;"></i> Test онлайн</h2>
                <p>статистика & репорты</p>
            </div>
            <div class="search-menu">
                <i class="fas fa-search"></i>
                <input type="text" id="menuSearch" placeholder="Поиск по меню" autocomplete="off">
                <span class="kbd-hint">Ctrl+K</span>
            </div>
        </div>

        <!-- Навигационная группа 1 (быстрые ссылки) -->
        <div class="nav-section">
            <div class="nav-title"><i class="fas fa-bolt"></i> БЫСТРЫЙ ДОСТУП</div>
            <ul class="nav-list" id="navListFast">
                <li class="nav-item" data-search-term="Статистика Репорты"><i class="fas fa-chart-pie"></i> Статистика / Репорты</li>
                <li class="nav-item" data-search-term="Правила Государствен"><i class="fas fa-gavel"></i> Правила / Государствен...</li>
                <li class="nav-item" data-search-term="Правила Общие правила"><i class="fas fa-book"></i> Правила / Общие правила</li>
                <li class="nav-item" data-search-term="Семьи"><i class="fas fa-users"></i> Семьи</li>
                <li class="nav-item" data-search-term="Фракции"><i class="fas fa-flag-checkered"></i> Фракции</li>
            </ul>
        </div>

        <!-- Основное меню (полный список) -->
        <div class="nav-section">
            <div class="nav-title"><i class="fas fa-compass"></i> ОСНОВНОЕ</div>
            <ul class="nav-list" id="navListMain">
                <li class="nav-item" data-search-term="Главная"><i class="fas fa-home"></i> Главная</li>
                <li class="nav-item" data-search-term="Команды сервера"><i class="fas fa-terminal"></i> Команды сервера</li>
                <li class="nav-item" data-search-term="Отпуска"><i class="fas fa-umbrella-beach"></i> Отпуска</li>
                <li class="nav-item" data-search-term="Наказания"><i class="fas fa-ban"></i> Наказания</li>
                <li class="nav-item" data-search-term="Модерация"><i class="fas fa-shield-haltered"></i> Модерация</li>
                <li class="nav-item" data-search-term="Штрафные баллы"><i class="fas fa-exclamation-triangle"></i> Штрафные баллы</li>
                <li class="nav-item" data-search-term="Экономика"><i class="fas fa-coins"></i> Экономика</li>
                <li class="nav-item" data-search-term="Транспорт"><i class="fas fa-truck"></i> Транспорт</li>
                <li class="nav-item" data-search-term="Предметы"><i class="fas fa-box"></i> Предметы</li>
                <li class="nav-item" data-search-term="Контент"><i class="fas fa-newspaper"></i> Контент</li>
                <li class="nav-item" data-search-term="Обучение"><i class="fas fa-graduation-cap"></i> Обучение</li>
                <li class="nav-item" data-search-term="Семьи"><i class="fas fa-heart"></i> Семьи</li>
                <li class="nav-item" data-search-term="Фракции"><i class="fas fa-handshake"></i> Фракции</li>
            </ul>
        </div>
        <div style="padding: 16px; margin-top: auto; font-size: 11px; color: #94a3b8; border-top: 1px solid #eef2ff; text-align: center;">
            <i class="fas fa-database"></i> Данные в реальном времени
        </div>
    </aside>

    <!-- Основной контент -->
    <main class="main-content">
        <div class="page-header">
            <h1><i class="fas fa-chart-simple"></i> Статистика репортов</h1>
            <div class="badge-online"><i class="fas fa-circle" style="font-size: 0.5rem; color: #22c55e;"></i> Live обновления</div>
        </div>

        <!-- Карточка статистики репортов -->
        <div class="stats-grid">
            <div class="section-title">
                <i class="fas fa-flag-checkered" style="color:#2563eb"></i> Модераторские репорты
            </div>
            <div class="section-sub">
                Остроковое данные по репортам за выбранный период • актуально на текущую сессию
            </div>
            <div class="metrics-row">
                <div class="metric-card">
                    <div class="metric-label"><i class="fas fa-ticket-alt"></i> Стандартные</div>
                    <div class="metric-value">640</div>
                    <div class="metric-unit">репортов</div>
                </div>
                <div class="metric-card">
                    <div class="metric-label"><i class="fas fa-hourglass-half"></i> Среднее время ответа</div>
                    <div class="metric-value">0.01 <span class="metric-unit">ч</span></div>
                </div>
                <div class="metric-card">
                    <div class="metric-label"><i class="fas fa-user-check"></i> Активисты администраторов</div>
                    <div class="metric-value">0.01 <span class="metric-unit">%</span></div>
                </div>
                <div class="metric-card">
                    <div class="metric-label"><i class="fas fa-clock"></i> Среднее время решения</div>
                    <div class="metric-value">0.01 <span class="metric-unit">ч</span></div>
                </div>
                <div class="metric-card">
                    <div class="metric-label"><i class="fas fa-check-double"></i> Обработали репорты</div>
                    <div class="metric-value">0.01 <span class="metric-unit">тыс.</span></div>
                </div>
            </div>
        </div>

        <!-- Двойной график: основной график (модераторские/стандартные) и тест-уровень -->
        <div class="charts-container">
            <div class="chart-card">
                <div class="chart-title">
                    <i class="fas fa-chart-line" style="color:#3b82f6"></i> График
                    <span style="font-size: 0.7rem; background: #eff6ff; padding: 2px 8px; border-radius: 40px;">Модераторские / Стандартные</span>
                </div>
                <canvas id="trendChart" width="400" height="250" style="width:100%; height:auto; max-height:250px"></canvas>
                <div class="section-sub" style="margin-top: 10px; text-align: center;">Динамика за последние 7 дней</div>
            </div>
            <div class="chart-card">
                <div class="chart-title">
                    <i class="fas fa-chart-bar"></i> Нагрузка по часам
                </div>
                <canvas id="hourlyChart" width="400" height="250" style="width:100%; height:auto; max-height:250px"></canvas>
                <div class="section-sub" style="margin-top: 10px; text-align: center;">Распределение репортов по часам (МСК)</div>
            </div>
        </div>

        <!-- Блок "Тест 9 level" и дополнительная информация -->
        <div class="test-level-card">
            <div class="test-info">
                <h4><i class="fas fa-brain"></i> Тест компетенций</h4>
                <p style="font-size: 0.8rem; color: #92400e;">Квалификация модератора & проверка знаний</p>
            </div>
            <div class="level-badge">
                9 level
            </div>
        </div>

        <!-- Дополнительный блок: справка и завершение работы -->
        <div class="help-footer">
            <span><i class="fas fa-info-circle"></i> Справка: актуальные инструкции по работе с репортами</span>
            <span><i class="fas fa-clock"></i> Завершение работы в 23:59</span>
        </div>
        <div style="margin-top: 18px; font-size: 12px; color: #6c757d; text-align: right;">
            <i class="fas fa-chart-line"></i> Обновлено: сегодня, статистика в реальном времени
        </div>
    </main>
</div>

<script>
    (function() {
        // ------------------- ФИЛЬТРАЦИЯ МЕНЮ ПО ПОИСКУ (Ctrl+K) -------------------
        const searchInput = document.getElementById('menuSearch');
        const fastItems = document.querySelectorAll('#navListFast .nav-item');
        const mainItems = document.querySelectorAll('#navListMain .nav-item');
        const allMenuItems = [...fastItems, ...mainItems];

        function filterMenu() {
            const searchTerm = searchInput.value.trim().toLowerCase();
            allMenuItems.forEach(item => {
                const text = item.innerText.toLowerCase();
                const searchAttr = item.getAttribute('data-search-term')?.toLowerCase() || '';
                const matches = text.includes(searchTerm) || searchAttr.includes(searchTerm);
                if (!matches && searchTerm !== '') {
                    item.classList.add('hidden-menu');
                } else {
                    item.classList.remove('hidden-menu');
                }
            });
        }

        searchInput.addEventListener('input', filterMenu);

        // Ctrl+K фокус на поиск
        window.addEventListener('keydown', function(e) {
            if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
                e.preventDefault();
                searchInput.focus();
            }
            // Escape для очистки
            if (e.key === 'Escape') {
                searchInput.value = '';
                filterMenu();
                searchInput.blur();
            }
        });

        // ------------------- ИНИЦИАЛИЗАЦИЯ ГРАФИКОВ (Chart.js) -------------------
        // 1. График "Модераторские vs Стандартные" (линейный)
        const ctxTrend = document.getElementById('trendChart').getContext('2d');
        const trendChart = new Chart(ctxTrend, {
            type: 'line',
            data: {
                labels: ['Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб', 'Вс'],
                datasets: [
                    {
                        label: 'Модераторские репорты',
                        data: [42, 38, 47, 52, 49, 44, 39],
                        borderColor: '#3b82f6',
                        backgroundColor: 'rgba(59, 130, 246, 0.05)',
                        borderWidth: 2.5,
                        pointBackgroundColor: '#1e40af',
                        pointBorderColor: '#fff',
                        pointRadius: 4,
                        pointHoverRadius: 6,
                        tension: 0.2,
                        fill: true,
                    },
                    {
                        label: 'Стандартные репорты',
                        data: [82, 78, 91, 102, 98, 88, 79],
                        borderColor: '#f97316',
                        backgroundColor: 'rgba(249, 115, 22, 0.03)',
                        borderWidth: 2.5,
                        pointBackgroundColor: '#c2410c',
                        pointBorderColor: '#fff',
                        pointRadius: 4,
                        pointHoverRadius: 6,
                        tension: 0.2,
                        fill: true,
                    }
                ]
            },
            options: {
                responsive: true,
                maintainAspectRatio: true,
                plugins: {
                    tooltip: { mode: 'index', intersect: false },
                    legend: { position: 'top', labels: { usePointStyle: true, boxWidth: 10, font: { size: 11 } } }
                },
                scales: {
                    y: {
                        beginAtZero: true,
                        grid: { color: '#eef2ff' },
                        title: { display: true, text: 'Количество репортов', font: { size: 10 } }
                    },
                    x: {
                        grid: { display: false },
                        title: { display: true, text: 'День недели', font: { size: 10 } }
                    }
                }
            }
        });

        // 2. График "Нагрузка по часам" (распределение репортов)
        const ctxHour = document.getElementById('hourlyChart').getContext('2d');
        const hoursLabels = [];
        for (let i = 0; i < 24; i++) {
            hoursLabels.push(`${i}:00`);
        }
        // Эмуляция нагрузки: утренний рост, пик в 17-20 часов, спад ночью
        const hourlyData = [5, 3, 2, 4, 8, 14, 28, 42, 58, 67, 72, 84, 91, 95, 102, 110, 124, 135, 142, 128, 98, 67, 34, 18];
       
        const hourlyChart = new Chart(ctxHour, {
            type: 'bar',
            data: {
                labels: hoursLabels,
                datasets: [
                    {
                        label: 'Репорты (шт)',
                        data: hourlyData,
                        backgroundColor: 'rgba(59, 130, 246, 0.7)',
                        borderRadius: 8,
                        borderSkipped: false,
                        barPercentage: 0.75,
                        categoryPercentage: 0.9,
                    }
                ]
            },
            options: {
                responsive: true,
                maintainAspectRatio: true,
                plugins: {
                    tooltip: { callbacks: { label: (ctx) => `${ctx.raw} репортов` } },
                    legend: { display: false }
                },
                scales: {
                    y: {
                        beginAtZero: true,
                        grid: { color: '#eef2ff' },
                        title: { display: true, text: 'Количество репортов', font: { size: 10 } }
                    },
                    x: {
                        ticks: { maxRotation: 45, minRotation: 35, autoSkip: true, maxTicksLimit: 12 },
                        title: { display: true, text: 'Часы (UTC+3)', font: { size: 10 } }
                    }
                }
            }
        });

        // дополнительная анимация / подпись с данными (соответствие тексту "Стандартные: 640" и т.д.)
        // Можно добавить декоративный эффект обновления метрик (чисто визуально, числа уже отображены)
        // Добавим эмуляцию динамического изменения метрик для "Активисты администраторов" и прочих - не требуется, но для ощущения живости, сделаем незначительный эффект?
        // В целях соблюдения ТЗ, просто оставляем статические данные, они полностью соответствуют описанию: 640, 0.01 и т.д.
       
        // Визуальный эффект для карточек (необязательно)
        const metricCards = document.querySelectorAll('.metric-card');
        metricCards.forEach(card => {
            card.addEventListener('mouseenter', () => {
                card.style.transform = 'translateY(-2px)';
                card.style.transition = '0.2s';
                card.style.boxShadow = '0 8px 20px rgba(0,0,0,0.05)';
            });
            card.addEventListener('mouseleave', () => {
                card.style.transform = 'translateY(0)';
                card.style.boxShadow = 'none';
            });
        });

        // Убедимся, что при изменении размера окна графики адаптируются
        window.addEventListener('resize', () => {
            trendChart.resize();
            hourlyChart.resize();
        });
       
        // маленькая деталь: добавить плейсхолдер при пустом поиске, убираем hidden при очистке
        // плюс показываем количество видимых элементов - но это опционально.
        // Убедимся, что изначально все пункты видны
        function resetVisibility() {
            allMenuItems.forEach(item => item.classList.remove('hidden-menu'));
        }
        window.resetFilter = resetVisibility;
       
        // Небольшой "live" счетчик для обработки репортов? Но оставим как статику, это соответствует исходным данным.
        // Добавим консольное сообщение об успешной загрузке.
        console.log('Dashboard статистики загружен | Модераторские репорты: стандартные 640, графики активны');
    })();
</script>
</body>
</html>
Аааааа ИИ блэн :)
 
  • Love
Реакции: 🐝 ubees

rizzaqq

Начинающий специалист
BackEnd developer
14 Янв 2025
198
54
50
Я согласен что любой ИИ сможет сверстать это @ubees уже это продемонстрировал.
 
Реакции: 🐝 ubees

rizzaqq

Начинающий специалист
BackEnd developer
14 Янв 2025
198
54
50

Similar threads