-
Notifications
You must be signed in to change notification settings - Fork 0
Open
Description
<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>
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels