• Из-за обновления 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) последний раз были обновлены:

rizzaqq

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

Horves

Новый участник
16 Фев 2025
75
3
30
Норм.
Сделайте верстку и на гугл диск
 

rizzaqq

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

jungledev

Мастер
BackEnd developer
16 Янв 2026
412
183
50
Реакции: kwet999

Say_Run

Участник портала
29 Ноя 2023
45
15
57
даже стало интересно откуда это такое, а так выглядит круто
 

🐝 ubees

Мастер
BackEnd developer
11 Фев 2023
1,109
192
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
229
59
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
229
59
50
Я согласен что любой ИИ сможет сверстать это @ubees уже это продемонстрировал.
 
Реакции: 🐝 ubees

rizzaqq

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

Similar threads