| |
|
|
| |
| document.addEventListener('DOMContentLoaded', function() { |
| if (typeof feather !== 'undefined') { |
| feather.replace(); |
| } |
| }); |
|
|
| |
| class ThemeManager { |
| constructor() { |
| this.currentTheme = 'light'; |
| this.init(); |
| } |
|
|
| init() { |
| |
| const savedTheme = localStorage.getItem('mootvision-theme'); |
| if (savedTheme) { |
| this.setTheme(savedTheme); |
| } |
| } |
|
|
| setTheme(theme) { |
| this.currentTheme = theme; |
| document.documentElement.setAttribute('data-theme', theme); |
| localStorage.setItem('mootvision-theme', theme); |
| } |
|
|
| toggleTheme() { |
| const newTheme = this.currentTheme === 'light' ? 'dark' : 'light'; |
| this.setTheme(newTheme); |
| } |
| } |
|
|
| |
| const themeManager = new ThemeManager(); |
|
|
| |
| class API { |
| static baseURL = '/api'; |
|
|
| static async get(endpoint) { |
| try { |
| const response = await fetch(`${this.baseURL}${endpoint}`); |
| return await response.json(); |
| } catch (error) { |
| console.error('API Error:', error); |
| throw error; |
| } |
| } |
|
|
| static async post(endpoint, data) { |
| try { |
| const response = await fetch(`${this.baseURL}${endpoint}`, { |
| method: 'POST', |
| headers: { |
| 'Content-Type': 'application/json', |
| }, |
| body: JSON.stringify(data) |
| }); |
| return await response.json(); |
| } catch (error) { |
| console.error('API Error:', error); |
| throw error; |
| } |
| } |
| } |
|
|
| |
| class JobTracker { |
| constructor(jobId) { |
| this.jobId = jobId; |
| this.ws = null; |
| this.connect(); |
| } |
|
|
| connect() { |
| this.ws = new WebSocket(`ws://localhost/api/ws/jobs/${this.jobId}`); |
| |
| this.ws.onopen = () => { |
| console.log(`WebSocket connected for job ${this.jobId}`); |
| this.updateUI('connected'); |
| }; |
|
|
| this.ws.onmessage = (event) => { |
| const data = JSON.parse(event.data); |
| this.handleMessage(data); |
| }; |
|
|
| this.ws.onclose = () => { |
| console.log(`WebSocket disconnected for job ${this.jobId}`); |
| setTimeout(() => this.connect(), 5000); |
| }; |
| } |
|
|
| handleMessage(data) { |
| switch (data.type) { |
| case 'progress': |
| this.updateProgress(data.progress); |
| break; |
| case 'log': |
| this.appendLog(data.message); |
| break; |
| case 'completed': |
| this.jobCompleted(data.results); |
| break; |
| case 'error': |
| this.jobError(data.error); |
| break; |
| } |
| } |
|
|
| updateProgress(progress) { |
| const progressBar = document.getElementById(`progress-${this.jobId}`); |
| const progressText = document.getElementById(`progress-text-${this.jobId}`); |
| |
| if (progressBar) { |
| progressBar.style.width = `${progress}%`; |
| } |
| |
| if (progressText) { |
| progressText.textContent = `${progress}%`; |
| } |
| } |
|
|
| appendLog(message) { |
| const logContainer = document.getElementById(`logs-${this.jobId}`); |
| if (logContainer) { |
| const logEntry = document.createElement('div'); |
| logEntry.className = 'text-sm text-gray-600'; |
| logEntry.textContent = `[${new Date().toLocaleTimeString()}] ${message}`; |
| logContainer.appendChild(logEntry); |
| logContainer.scrollTop = logContainer.scrollHeight; |
| } |
| } |
|
|
| jobCompleted(results) { |
| this.updateUI('completed'); |
| if (typeof window.jobCompletedCallback === 'function') { |
| window.jobCompletedCallback(results); |
| } |
| } |
|
|
| jobError(error) { |
| this.updateUI('error'); |
| console.error('Job error:', error); |
| } |
|
|
| updateUI(status) { |
| |
| const jobElement = document.getElementById(`job-${this.jobId}`); |
| if (jobElement) { |
| jobElement.setAttribute('data-status', status); |
| } |
| } |
| } |
|
|
| |
| const Utils = { |
| formatFileSize(bytes) { |
| const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; |
| if (bytes === 0) return '0 Bytes'; |
| const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024))); |
| return Math.round(bytes / Math.pow(1024, i) * 100) / 100 + ' ' + sizes[i]; |
| }, |
|
|
| formatDuration(seconds) { |
| const hours = Math.floor(seconds / 3600); |
| const minutes = Math.floor((seconds % 3600) / 60); |
| const secs = Math.floor(seconds % 60); |
| return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`; |
| }, |
|
|
| debounce(func, wait) { |
| let timeout; |
| return function executedFunction(...args) { |
| const later = () => { |
| clearTimeout(timeout); |
| func(...args); |
| }; |
| clearTimeout(timeout); |
| timeout = setTimeout(later, wait); |
| }; |
| }, |
|
|
| generateId() { |
| return Date.now().toString(36) + Math.random().toString(36).substr(2); |
| } |
| }; |
|
|
| |
| window.API = API; |
| window.JobTracker = JobTracker; |
| window.Utils = Utils; |
| window.themeManager = themeManager; |