/
AngelCareMe
/
task_queue
Обзор
Документация
Войти
/
AngelCareMe
/
task_queue
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
web/static/js/main.js
294 строки
10 KB
AngelCareMe
Dashboard done/project ready
29 июл 2025, 19:11
29 июл 2025, 19:11
1894643
Код
Авторство
О чём код?
class TaskDashboard { constructor() { this.tasks = []; this.stats = {}; this.init(); } init() { this.bindEvents(); this.loadData(); this.startAutoRefresh(); } bindEvents() { // Refresh button document.getElementById('refresh-btn').addEventListener('click', () => { this.loadData(); }); // Search input document.getElementById('search-input').addEventListener('input', (e) => { this.filterTasks(e.target.value); }); // NEW: Create task button in modal document.getElementById('create-task-btn').addEventListener('click', () => { this.createTask(); }); } async loadData() { try { await Promise.all([ this.loadStats(), this.loadTasks(), this.checkHealth() ]); } catch (error) { this.showToast('Error', 'Failed to load data: ' + error.message, 'danger'); } } async loadStats() { const response = await fetch('/api/stats'); if (!response.ok) { throw new Error('Failed to load stats'); } this.stats = await response.json(); this.renderStats(); } async loadTasks() { const response = await fetch('/api/tasks?limit=50'); if (!response.ok) { throw new Error('Failed to load tasks'); } this.tasks = await response.json(); this.renderTasks(this.tasks); } async checkHealth() { const response = await fetch('/api/health'); const health = await response.json(); const healthStatus = document.getElementById('health-status'); if (health.status === 'healthy') { healthStatus.className = 'badge bg-success'; healthStatus.textContent = 'Healthy'; } else { healthStatus.className = 'badge bg-danger'; healthStatus.textContent = 'Unhealthy'; } } renderStats() { const container = document.getElementById('stats-container'); container.innerHTML = ` <div class="col-md-2 col-6 mb-3"> <div class="stat-card total"> <span class="stat-number">${this.stats.total || 0}</span> <span class="stat-label">Total</span> </div> </div> <div class="col-md-2 col-6 mb-3"> <div class="stat-card pending"> <span class="stat-number">${this.stats.pending || 0}</span> <span class="stat-label">Pending</span> </div> </div> <div class="col-md-2 col-6 mb-3"> <div class="stat-card processing"> <span class="stat-number">${this.stats.processing || 0}</span> <span class="stat-label">Processing</span> </div> </div> <div class="col-md-2 col-6 mb-3"> <div class="stat-card success"> <span class="stat-number">${this.stats.success || 0}</span> <span class="stat-label">Success</span> </div> </div> <div class="col-md-2 col-6 mb-3"> <div class="stat-card failed"> <span class="stat-number">${this.stats.failed || 0}</span> <span class="stat-label">Failed</span> </div> </div> `; } renderTasks(tasks) { const tbody = document.getElementById('tasks-table-body'); tbody.innerHTML = ''; tasks.forEach(task => { const row = document.createElement('tr'); row.innerHTML = ` <td><small>${task.id.substring(0, 8)}...</small></td> <td>${task.name}</td> <td><span class="status-badge status-${task.status}">${task.status}</span></td> <td>${task.retry}/${task.max_retry}</td> <td><small>${new Date(task.created_at).toLocaleString()}</small></td> <td> ${task.status !== 'success' && task.retry < task.max_retry ? `<button class="btn btn-outline-primary btn-action btn-retry" data-id="${task.id}"> <i class="bi bi-arrow-repeat"></i> </button>` : ''} <button class="btn btn-outline-danger btn-action btn-delete" data-id="${task.id}"> <i class="bi bi-trash"></i> </button> </td> `; tbody.appendChild(row); }); // Bind events to action buttons document.querySelectorAll('.btn-retry').forEach(btn => { btn.addEventListener('click', (e) => { const taskId = e.currentTarget.dataset.id; this.retryTask(taskId); }); }); document.querySelectorAll('.btn-delete').forEach(btn => { btn.addEventListener('click', (e) => { const taskId = e.currentTarget.dataset.id; this.deleteTask(taskId); }); }); } filterTasks(searchTerm) { if (!searchTerm) { this.renderTasks(this.tasks); return; } const filtered = this.tasks.filter(task => task.name.toLowerCase().includes(searchTerm.toLowerCase()) || task.id.toLowerCase().includes(searchTerm.toLowerCase()) || task.status.toLowerCase().includes(searchTerm.toLowerCase()) ); this.renderTasks(filtered); } async createTask() { const name = document.getElementById('task-name').value.trim(); const payload = document.getElementById('task-payload').value; const maxRetry = parseInt(document.getElementById('task-max-retry').value) || 3; const delay = parseInt(document.getElementById('task-delay').value) || 0; if (!name) { this.showToast('Error', 'Task name is required', 'danger'); return; } const taskData = { name: name, payload: payload, max_retry: maxRetry, delay: delay }; try { const response = await fetch('/api/tasks', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(taskData) }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.error || 'Failed to create task'); } // Закрываем модальное окно const modal = bootstrap.Modal.getInstance(document.getElementById('createTaskModal')); if (modal) { modal.hide(); } // Очищаем форму document.getElementById('create-task-form').reset(); this.showToast('Success', 'Task created successfully', 'success'); this.loadData(); // Обновляем данные } catch (error) { this.showToast('Error', error.message, 'danger'); } } async retryTask(taskId) { if (!confirm('Are you sure you want to retry this task?')) { return; } try { const response = await fetch(`/api/tasks/${taskId}/retry`, { method: 'POST' }); if (!response.ok) { const error = await response.json(); throw new Error(error.error || 'Failed to retry task'); } this.showToast('Success', 'Task scheduled for retry', 'success'); this.loadData(); // Refresh data } catch (error) { this.showToast('Error', error.message, 'danger'); } } async deleteTask(taskId) { if (!confirm('Are you sure you want to delete this task?')) { return; } try { const response = await fetch(`/api/tasks/${taskId}`, { method: 'DELETE' }); if (!response.ok) { const error = await response.json(); throw new Error(error.error || 'Failed to delete task'); } this.showToast('Success', 'Task deleted successfully', 'success'); this.loadData(); // Refresh data } catch (error) { this.showToast('Error', error.message, 'danger'); } } showToast(title, message, type) { const toastEl = document.getElementById('toast'); const toastTitle = document.getElementById('toast-title'); const toastBody = document.getElementById('toast-body'); toastTitle.textContent = title; toastBody.textContent = message; // Set background color based on type toastEl.className = 'toast'; if (type === 'success') { toastEl.classList.add('bg-success', 'text-white'); } else if (type === 'danger') { toastEl.classList.add('bg-danger', 'text-white'); } else { toastEl.classList.add('bg-primary', 'text-white'); } const toast = new bootstrap.Toast(toastEl); toast.show(); } startAutoRefresh() { setInterval(() => { this.loadData(); }, 30000); // Refresh every 30 seconds } } // Initialize dashboard when DOM is loaded document.addEventListener('DOMContentLoaded', () => { new TaskDashboard(); });