Fixed 89 violations across vendor, admin, and shared JavaScript files: JS-008 (raw fetch → apiClient): - Added postFormData() and getBlob() methods to api-client.js - Updated inventory.js, messages.js to use apiClient.postFormData() - Added noqa for file downloads that need response headers JS-009 (window.showToast → Utils.showToast): - Updated admin/messages.js, notifications.js, vendor/messages.js - Replaced alert() in customers.js JS-006 (async error handling): - Added try/catch to all async init() and reload() methods - Fixed vendor: billing, dashboard, login, messages, onboarding - Fixed shared: feature-store, upgrade-prompts - Fixed admin: all page components JS-005 (init guards): - Added initialization guards to prevent duplicate init() calls - Pattern: if (window._componentInitialized) return; 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
138 lines
4.3 KiB
JavaScript
138 lines
4.3 KiB
JavaScript
// noqa: js-006 - async init pattern is safe, loadData has try/catch
|
|
/**
|
|
* Background Tasks Monitoring Component
|
|
* Manages the background tasks monitoring page
|
|
*/
|
|
|
|
// Use centralized logger
|
|
const backgroundTasksLog = window.LogConfig.createLogger('BACKGROUND-TASKS');
|
|
|
|
function backgroundTasks() {
|
|
return {
|
|
// Extend base data
|
|
...data(),
|
|
|
|
// Set current page for navigation
|
|
currentPage: 'background-tasks',
|
|
|
|
// Page-specific data
|
|
loading: false,
|
|
error: null,
|
|
filterType: null,
|
|
pollInterval: null,
|
|
|
|
// Statistics
|
|
stats: {
|
|
total_tasks: 0,
|
|
running: 0,
|
|
completed: 0,
|
|
failed: 0,
|
|
tasks_today: 0,
|
|
avg_duration_seconds: null,
|
|
import_jobs: {},
|
|
test_runs: {}
|
|
},
|
|
|
|
// Tasks
|
|
tasks: [],
|
|
runningTasks: [],
|
|
|
|
async init() {
|
|
// Guard against multiple initialization
|
|
if (window._adminBackgroundTasksInitialized) return;
|
|
window._adminBackgroundTasksInitialized = true;
|
|
|
|
try {
|
|
backgroundTasksLog.info('Initializing background tasks monitor');
|
|
await this.loadStats();
|
|
await this.loadTasks();
|
|
await this.loadRunningTasks();
|
|
|
|
// Poll for updates every 5 seconds
|
|
this.pollInterval = setInterval(() => {
|
|
this.loadRunningTasks();
|
|
if (this.runningTasks.length > 0) {
|
|
this.loadStats();
|
|
}
|
|
}, 5000);
|
|
} catch (error) {
|
|
backgroundTasksLog.error('Failed to initialize background tasks:', error);
|
|
}
|
|
},
|
|
|
|
destroy() {
|
|
if (this.pollInterval) {
|
|
clearInterval(this.pollInterval);
|
|
}
|
|
},
|
|
|
|
async loadStats() {
|
|
try {
|
|
const stats = await apiClient.get('/admin/background-tasks/tasks/stats');
|
|
this.stats = stats;
|
|
backgroundTasksLog.info('Stats loaded:', stats);
|
|
} catch (err) {
|
|
backgroundTasksLog.error('Failed to load stats:', err);
|
|
}
|
|
},
|
|
|
|
async loadTasks() {
|
|
this.loading = true;
|
|
this.error = null;
|
|
|
|
try {
|
|
let url = '/admin/background-tasks/tasks?limit=50';
|
|
if (this.filterType) {
|
|
url += `&task_type=${this.filterType}`;
|
|
}
|
|
|
|
const tasks = await apiClient.get(url);
|
|
this.tasks = tasks;
|
|
backgroundTasksLog.info('Tasks loaded:', tasks.length);
|
|
} catch (err) {
|
|
backgroundTasksLog.error('Failed to load tasks:', err);
|
|
this.error = err.message;
|
|
|
|
if (err.message.includes('Unauthorized')) {
|
|
window.location.href = '/admin/login';
|
|
}
|
|
} finally {
|
|
this.loading = false;
|
|
}
|
|
},
|
|
|
|
async loadRunningTasks() {
|
|
try {
|
|
const running = await apiClient.get('/admin/background-tasks/tasks/running');
|
|
this.runningTasks = running;
|
|
|
|
// Update elapsed time for running tasks
|
|
const now = new Date();
|
|
this.runningTasks.forEach(task => {
|
|
if (task.started_at) {
|
|
const started = new Date(task.started_at);
|
|
task.duration_seconds = (now - started) / 1000;
|
|
}
|
|
});
|
|
} catch (err) {
|
|
backgroundTasksLog.error('Failed to load running tasks:', err);
|
|
}
|
|
},
|
|
|
|
async refresh() {
|
|
await this.loadStats();
|
|
await this.loadTasks();
|
|
await this.loadRunningTasks();
|
|
},
|
|
|
|
formatDuration(seconds) {
|
|
if (seconds === null || seconds === undefined) return 'N/A';
|
|
if (seconds < 1) return `${Math.round(seconds * 1000)}ms`;
|
|
if (seconds < 60) return `${Math.round(seconds)}s`;
|
|
const minutes = Math.floor(seconds / 60);
|
|
const secs = Math.round(seconds % 60);
|
|
return `${minutes}m ${secs}s`;
|
|
}
|
|
};
|
|
}
|