Files
abdullahceylan-ac-comfyui-q…/web/queue_manager.js
T

327 lines
11 KiB
JavaScript

/**
* ComfyUI Queue Manager Frontend JavaScript
* Handles the user interface interactions and API communication
*/
class QueueManager {
constructor() {
this.apiBase = '/queue';
this.queueItems = [];
this.selectedItems = new Set();
this.queueState = 'running';
this.init();
}
init() {
this.bindEvents();
this.loadQueueItems();
this.updateQueueState();
}
bindEvents() {
// Control buttons
document.getElementById('pause-resume-btn').addEventListener('click', () => {
this.toggleQueueState();
});
document.getElementById('refresh-btn').addEventListener('click', () => {
this.loadQueueItems();
});
// Bulk actions
document.getElementById('archive-selected-btn').addEventListener('click', () => {
this.archiveSelected();
});
document.getElementById('delete-selected-btn').addEventListener('click', () => {
this.deleteSelected();
});
// Filters
document.getElementById('search-input').addEventListener('input', (e) => {
this.filterItems();
});
document.getElementById('status-filter').addEventListener('change', (e) => {
this.filterItems();
});
}
async loadQueueItems() {
try {
this.showLoading();
// Placeholder for API call - will be implemented in later tasks
// const response = await fetch(`${this.apiBase}/items`);
// this.queueItems = await response.json();
// Mock data for now
this.queueItems = [];
this.renderQueueItems();
} catch (error) {
console.error('Failed to load queue items:', error);
this.showError('Failed to load queue items');
}
}
async updateQueueState() {
try {
// Placeholder for API call - will be implemented in later tasks
// const response = await fetch(`${this.apiBase}/status`);
// const data = await response.json();
// this.queueState = data.state;
this.updatePauseResumeButton();
} catch (error) {
console.error('Failed to update queue state:', error);
}
}
async toggleQueueState() {
try {
const newState = this.queueState === 'running' ? 'paused' : 'running';
// Placeholder for API call - will be implemented in later tasks
// await fetch(`${this.apiBase}/${newState}`, { method: 'POST' });
this.queueState = newState;
this.updatePauseResumeButton();
} catch (error) {
console.error('Failed to toggle queue state:', error);
}
}
updatePauseResumeButton() {
const btn = document.getElementById('pause-resume-btn');
if (this.queueState === 'running') {
btn.textContent = 'Pause';
btn.className = 'btn btn-warning';
} else {
btn.textContent = 'Resume';
btn.className = 'btn btn-primary';
}
}
renderQueueItems() {
const container = document.getElementById('queue-items');
if (this.queueItems.length === 0) {
container.innerHTML = `
<div class="empty-state">
<h3>No queue items</h3>
<p>Queue items will appear here when workflows are added to the queue.</p>
</div>
`;
return;
}
container.innerHTML = this.queueItems.map(item => this.renderQueueItem(item)).join('');
this.bindItemEvents();
}
renderQueueItem(item) {
const createdAt = new Date(item.created_at).toLocaleString();
const updatedAt = new Date(item.updated_at).toLocaleString();
return `
<div class="queue-item ${item.status}" data-id="${item.id}">
<div class="queue-item-header">
<div>
<input type="checkbox" class="checkbox item-checkbox" data-id="${item.id}">
<span class="queue-item-title">${item.workflow_name || 'Unnamed Workflow'}</span>
</div>
<span class="queue-item-status status-${item.status}">${item.status}</span>
</div>
<div class="queue-item-meta">
<span>Created: ${createdAt}</span>
<span>Updated: ${updatedAt}</span>
<span>ID: ${item.id}</span>
</div>
${item.error_message ? `<div class="error-message">${item.error_message}</div>` : ''}
<div class="queue-item-actions">
${this.getItemActions(item)}
</div>
</div>
`;
}
getItemActions(item) {
const actions = [];
if (item.status === 'archived') {
actions.push('<button class="btn btn-secondary restore-btn" data-id="' + item.id + '">Restore</button>');
} else {
actions.push('<button class="btn btn-warning archive-btn" data-id="' + item.id + '">Archive</button>');
}
if (item.status === 'pending' || item.status === 'failed') {
actions.push('<button class="btn btn-primary retry-btn" data-id="' + item.id + '">Retry</button>');
}
actions.push('<button class="btn btn-danger delete-btn" data-id="' + item.id + '">Delete</button>');
return actions.join('');
}
bindItemEvents() {
// Checkbox selection
document.querySelectorAll('.item-checkbox').forEach(checkbox => {
checkbox.addEventListener('change', (e) => {
const itemId = e.target.dataset.id;
if (e.target.checked) {
this.selectedItems.add(itemId);
} else {
this.selectedItems.delete(itemId);
}
});
});
// Individual item actions
document.querySelectorAll('.archive-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
this.archiveItem(e.target.dataset.id);
});
});
document.querySelectorAll('.restore-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
this.restoreItem(e.target.dataset.id);
});
});
document.querySelectorAll('.delete-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
this.deleteItem(e.target.dataset.id);
});
});
document.querySelectorAll('.retry-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
this.retryItem(e.target.dataset.id);
});
});
}
filterItems() {
const searchTerm = document.getElementById('search-input').value.toLowerCase();
const statusFilter = document.getElementById('status-filter').value;
document.querySelectorAll('.queue-item').forEach(item => {
const title = item.querySelector('.queue-item-title').textContent.toLowerCase();
const status = item.classList.contains('pending') ? 'pending' :
item.classList.contains('running') ? 'running' :
item.classList.contains('completed') ? 'completed' :
item.classList.contains('failed') ? 'failed' :
item.classList.contains('archived') ? 'archived' : '';
const matchesSearch = !searchTerm || title.includes(searchTerm);
const matchesStatus = !statusFilter || status === statusFilter;
item.style.display = matchesSearch && matchesStatus ? 'block' : 'none';
});
}
async archiveSelected() {
if (this.selectedItems.size === 0) {
alert('Please select items to archive');
return;
}
try {
// Placeholder for API call - will be implemented in later tasks
console.log('Archiving items:', Array.from(this.selectedItems));
this.selectedItems.clear();
this.loadQueueItems();
} catch (error) {
console.error('Failed to archive items:', error);
}
}
async deleteSelected() {
if (this.selectedItems.size === 0) {
alert('Please select items to delete');
return;
}
if (!confirm('Are you sure you want to delete the selected items?')) {
return;
}
try {
// Placeholder for API call - will be implemented in later tasks
console.log('Deleting items:', Array.from(this.selectedItems));
this.selectedItems.clear();
this.loadQueueItems();
} catch (error) {
console.error('Failed to delete items:', error);
}
}
async archiveItem(itemId) {
try {
// Placeholder for API call - will be implemented in later tasks
console.log('Archiving item:', itemId);
this.loadQueueItems();
} catch (error) {
console.error('Failed to archive item:', error);
}
}
async restoreItem(itemId) {
try {
// Placeholder for API call - will be implemented in later tasks
console.log('Restoring item:', itemId);
this.loadQueueItems();
} catch (error) {
console.error('Failed to restore item:', error);
}
}
async deleteItem(itemId) {
if (!confirm('Are you sure you want to delete this item?')) {
return;
}
try {
// Placeholder for API call - will be implemented in later tasks
console.log('Deleting item:', itemId);
this.loadQueueItems();
} catch (error) {
console.error('Failed to delete item:', error);
}
}
async retryItem(itemId) {
try {
// Placeholder for API call - will be implemented in later tasks
console.log('Retrying item:', itemId);
this.loadQueueItems();
} catch (error) {
console.error('Failed to retry item:', error);
}
}
showLoading() {
document.getElementById('queue-items').innerHTML = `
<div class="loading">
<p>Loading queue items...</p>
</div>
`;
}
showError(message) {
document.getElementById('queue-items').innerHTML = `
<div class="empty-state">
<h3>Error</h3>
<p>${message}</p>
</div>
`;
}
}
// Initialize the queue manager when the page loads
document.addEventListener('DOMContentLoaded', () => {
new QueueManager();
});