Skip to content

Analytics page for Rake #32

@puleeno

Description

@puleeno
<html lang="vi">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Rake Analytics Dashboard</title>
    <!-- Tailwind CSS CDN -->
    <script src="https://cdn.tailwindcss.com"></script>
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
    <style>
        body {
            font-family: 'Inter', sans-serif;
            background-color: #f3f4f6;
            color: #333;
        }
        /* Custom scrollbar for better aesthetics */
        ::-webkit-scrollbar {
            width: 8px;
            height: 8px;
        }
        ::-webkit-scrollbar-track {
            background: #f1f1f1;
            border-radius: 10px;
        }
        ::-webkit-scrollbar-thumb {
            background: #888;
            border-radius: 10px;
        }
        ::-webkit-scrollbar-thumb:hover {
            background: #555;
        }
    </style>
</head>
<body class="p-4 sm:p-6 md:p-8">
    <div class="max-w-full mx-auto bg-white rounded-lg shadow-xl p-4 sm:p-6 md:p-8">
        <h1 class="text-2xl sm:text-3xl font-bold text-gray-800 mb-6 text-center">Rake Analytics Dashboard</h1>

        <!-- Summary Statistics Section -->
        <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 mb-8">
            <div class="bg-blue-500 text-white p-4 rounded-lg shadow-md flex flex-col items-center justify-center">
                <span class="text-3xl font-bold" id="totalLogs">0</span>
                <span class="text-sm">Tổng số Logs</span>
            </div>
            <div class="bg-green-500 text-white p-4 rounded-lg shadow-md flex flex-col items-center justify-center">
                <span class="text-3xl font-bold" id="infoLogs">0</span>
                <span class="text-sm">Logs INFO</span>
            </div>
            <div class="bg-yellow-500 text-white p-4 rounded-lg shadow-md flex flex-col items-center justify-center">
                <span class="text-3xl font-bold" id="warningLogs">0</span>
                <span class="text-sm">Logs WARNING</span>
            </div>
            <div class="bg-red-500 text-white p-4 rounded-lg shadow-md flex flex-col items-center justify-center">
                <span class="text-3xl font-bold" id="errorLogs">0</span>
                <span class="text-sm">Logs ERROR</span>
            </div>
        </div>

        <!-- Filters and Search Section -->
        <div class="mb-6 bg-gray-50 p-4 rounded-lg shadow-inner">
            <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
                <div>
                    <label for="logLevelFilter" class="block text-sm font-medium text-gray-700 mb-1">Lọc theo Mức độ:</label>
                    <select id="logLevelFilter" class="mt-1 block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm rounded-md shadow-sm">
                        <option value="">Tất cả</option>
                        <option value="INFO">INFO</option>
                        <option value="WARNING">WARNING</option>
                        <option value="ERROR">ERROR</option>
                        <option value="DEBUG">DEBUG</option>
                        <option value="CRITICAL">CRITICAL</option>
                    </select>
                </div>
                <div>
                    <label for="toothNameSearch" class="block text-sm font-medium text-gray-700 mb-1">Tìm kiếm Tên Tooth:</label>
                    <input type="text" id="toothNameSearch" placeholder="Nhập tên Tooth..." class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm">
                </div>
                <div>
                    <label for="feedItemIdSearch" class="block text-sm font-medium text-gray-700 mb-1">Tìm kiếm ID FeedItem:</label>
                    <input type="text" id="feedItemIdSearch" placeholder="Nhập ID FeedItem..." class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm">
                </div>
            </div>
            <div class="mt-4 flex justify-center">
                <button id="applyFiltersBtn" class="px-6 py-2 bg-blue-600 text-white font-semibold rounded-md shadow-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition duration-150 ease-in-out">Áp dụng Bộ lọc</button>
            </div>
        </div>

        <!-- Log Table Section -->
        <div class="overflow-x-auto rounded-lg shadow-md border border-gray-200">
            <table class="min-w-full divide-y divide-gray-200">
                <thead class="bg-gray-100">
                    <tr>
                        <th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-600 uppercase tracking-wider rounded-tl-lg">Mức độ</th>
                        <th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-600 uppercase tracking-wider">Tin nhắn</th>
                        <th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-600 uppercase tracking-wider">Tên Tooth</th>
                        <th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-600 uppercase tracking-wider">ID FeedItem</th>
                        <th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-600 uppercase tracking-wider">URL Nguồn</th>
                        <th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-600 uppercase tracking-wider rounded-tr-lg">Thời gian</th>
                    </tr>
                </thead>
                <tbody id="logTableBody" class="bg-white divide-y divide-gray-200">
                    <!-- Log rows will be inserted here by JavaScript -->
                    <tr>
                        <td colspan="6" class="px-4 py-4 text-center text-gray-500">Đang tải logs...</td>
                    </tr>
                </tbody>
            </table>
        </div>

        <!-- Pagination Controls -->
        <div class="flex justify-between items-center mt-6">
            <button id="prevPageBtn" class="px-4 py-2 bg-gray-300 text-gray-800 font-semibold rounded-md shadow-sm hover:bg-gray-400 focus:outline-none focus:ring-2 focus:ring-gray-300 focus:ring-offset-2 transition duration-150 ease-in-out disabled:opacity-50 disabled:cursor-not-allowed">Trước</button>
            <span class="text-sm text-gray-700">Trang <span id="currentPage">1</span> / <span id="totalPages">1</span></span>
            <button id="nextPageBtn" class="px-4 py-2 bg-gray-300 text-gray-800 font-semibold rounded-md shadow-sm hover:bg-gray-400 focus:outline-none focus:ring-2 focus:ring-gray-300 focus:ring-offset-2 transition duration-150 ease-in-out disabled:opacity-50 disabled:cursor-not-allowed">Tiếp</button>
        </div>

        <!-- Message Box for alerts/errors -->
        <div id="messageBox" class="fixed inset-0 bg-gray-600 bg-opacity-50 flex items-center justify-center hidden">
            <div class="bg-white p-6 rounded-lg shadow-xl max-w-sm w-full text-center">
                <h3 id="messageBoxTitle" class="text-lg font-semibold mb-4"></h3>
                <p id="messageBoxContent" class="text-gray-700 mb-6"></p>
                <button id="messageBoxCloseBtn" class="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700">Đóng</button>
            </div>
        </div>
    </div>

    <script>
        // Placeholder for your Rake PHP Backend API Endpoint
        // Replace this with your actual API endpoint that serves log data from the database.
        // const API_BASE_URL = 'https://your-rake-backend.com/api/logs'; // Example: 'http://localhost:8000/api/rake/logs'
        const LOGS_PER_PAGE = 10; // Number of log entries per page

        let currentPage = 1;
        let totalPages = 1;
        let allLogs = []; // Stores all fetched logs for client-side filtering/pagination
        let filteredLogs = []; // Stores logs after applying filters

        // --- Utility Functions ---

        /**
         * Displays a custom message box.
         * @param {string} title - The title of the message box.
         * @param {string} message - The content message.
         */
        function showMessageBox(title, message) {
            document.getElementById('messageBoxTitle').textContent = title;
            document.getElementById('messageBoxContent').textContent = message;
            document.getElementById('messageBox').classList.remove('hidden');
        }

        /**
         * Hides the custom message box.
         */
        function hideMessageBox() {
            document.getElementById('messageBox').classList.add('hidden');
        }

        // --- Data Fetching and Display ---

        /**
         * Fetches log data from the backend API.
         * @returns {Promise<Array>} A promise that resolves with an array of log objects.
         */
        async function fetchLogs() {
            // try {
            //     const response = await fetch(API_BASE_URL);
            //     if (!response.ok) {
            //         throw new Error(`HTTP error! status: ${response.status}`);
            //     }
            //     const data = await response.json();
            //     // Assuming the API returns an array of log objects directly
            //     // Each log object should have: id, level, message, tooth_name, feed_item_id, source_url, created_at
            //     return data;
            // } catch (error) {
            //     console.error("Error fetching logs:", error);
            //     showMessageBox("Lỗi", "Không thể tải dữ liệu logs. Vui lòng kiểm tra kết nối API.");
            //     return [];
            // }

            // --- Mock API Data (for testing without a backend) ---
            // Uncomment this section and comment out the actual fetchLogs() call above
            // if you want to test the UI without a live backend.
            const mockLogs = [
                { id: 1, level: 'INFO', message: 'FeedItem đã được xử lý thành công.', tooth_name: 'wordpress-blog-posts', feed_item_id: 'item_abc123', source_url: 'https://example.com/post-1', created_at: '2025-07-15T10:00:00Z' },
                { id: 2, level: 'ERROR', message: 'Lỗi đồng bộ hóa FeedItem: API trả về 500.', tooth_name: 'shopify-products-us', feed_item_id: 'item_def456', source_url: 'https://shopify.com/product-a', created_at: '2025-07-15T10:05:00Z' },
                { id: 3, level: 'WARNING', message: 'FeedItem đã bị lọc. Lý do: trùng lặp cơ bản.', tooth_name: 'wordpress-blog-posts', feed_item_id: 'item_ghi789', source_url: 'https://example.com/post-2', created_at: '2025-07-15T10:10:00Z' },
                { id: 4, level: 'INFO', message: 'Đã lấy dữ liệu thô.', tooth_name: 'wordpress-blog-posts', feed_item_id: 'N/A', source_url: 'https://example.com/sitemap.xml', created_at: '2025-07-15T09:55:00Z' },
                { id: 5, level: 'DEBUG', message: 'Đã phân tích dữ liệu.', tooth_name: 'shopify-products-us', feed_item_id: 'N/A', source_url: 'https://shopify.com/api/products', created_at: '2025-07-15T09:58:00Z' },
                { id: 6, level: 'ERROR', message: 'Lỗi khi tải file hình ảnh: Network timeout.', tooth_name: 'wordpress-blog-posts', feed_item_id: 'item_jkl012', source_url: 'https://example.com/post-3', created_at: '2025-07-15T10:15:00Z' },
                { id: 7, level: 'INFO', message: 'FeedItem đã được xử lý thành công.', tooth_name: 'custom-news-scraper', feed_item_id: 'item_mno345', source_url: 'https://news.com/article-1', created_at: '2025-07-15T10:20:00Z' },
                { id: 8, level: 'CRITICAL', message: 'Lỗi nghiêm trọng: Database connection lost.', tooth_name: 'N/A', feed_item_id: 'N/A', source_url: 'N/A', created_at: '2025-07-15T10:25:00Z' },
                { id: 9, level: 'INFO', message: 'Tooth \'wordpress-blog-posts\' hoàn thành.', tooth_name: 'wordpress-blog-posts', feed_item_id: 'N/A', source_url: 'N/A', created_at: '2025-07-15T10:30:00Z' },
                { id: 10, level: 'WARNING', message: 'Dữ liệu không khớp với schema mong đợi.', tooth_name: 'shopify-products-us', feed_item_id: 'item_pqr678', source_url: 'https://shopify.com/product-b', created_at: '2025-07-15T10:35:00Z' },
                { id: 11, level: 'INFO', message: 'FeedItem đã được xử lý thành công.', tooth_name: 'wordpress-blog-posts', feed_item_id: 'item_stu901', source_url: 'https://example.com/post-4', created_at: '2025-07-15T10:40:00Z' },
                { id: 12, level: 'INFO', message: 'FeedItem đã được xử lý thành công.', tooth_name: 'wordpress-blog-posts', feed_item_id: 'item_vwx234', source_url: 'https://example.com/post-5', created_at: '2025-07-15T10:45:00Z' },
                { id: 13, level: 'ERROR', message: 'Lỗi đồng bộ hóa FeedItem: Không thể xác thực API.', tooth_name: 'shopify-products-us', feed_item_id: 'item_yza567', source_url: 'https://shopify.com/product-c', created_at: '2025-07-15T10:50:00Z' },
                { id: 14, level: 'INFO', message: 'FeedItem đã được xử lý thành công.', tooth_name: 'custom-news-scraper', feed_item_id: 'item_bcd890', source_url: 'https://news.com/article-2', created_at: '2025-07-15T10:55:00Z' },
                { id: 15, level: 'WARNING', message: 'FeedItem đã bị lọc. Lý do: trùng lặp nâng cao.', tooth_name: 'wordpress-blog-posts', feed_item_id: 'item_efg123', source_url: 'https://example.com/post-6', created_at: '2025-07-15T11:00:00Z' },
            ];
            return new Promise(resolve => setTimeout(() => resolve(mockLogs), 500)); // Simulate API call
        }
    </script>
</body>
</html>

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions