/** * qBit Manage Web UI - Command Panel Component * Handles command execution and monitoring */ import { API } from '../api.js'; import { showToast } from '../utils/toast.js'; import { CLOSE_ICON_SVG } from '../utils/icons.js'; class CommandPanel { constructor(options = {}) { this.container = options.container; this.drawerContainer = options.drawerContainer; this.onCommandExecute = options.onCommandExecute || (() => {}); this.api = new API(); this.isVisible = false; this.runCommandsModal = null; // To store the reference to the run commands modal this.init(); } init() { this.render(); this.bindEvents(); this.setupDrawer(); } setupDrawer() { // Create the drawer container if it doesn't exist if (!this.drawerContainer) { this.drawerContainer = document.createElement('div'); this.drawerContainer.className = 'command-panel-drawer hidden'; document.body.appendChild(this.drawerContainer); } // Move the command panel content to the drawer this.renderDrawer(); this.bindDrawerEvents(); } render() { if (!this.container) return; // Render the toggle button in the footer this.container.innerHTML = `
`; } renderDrawer() { if (!this.drawerContainer) return; this.drawerContainer.innerHTML = `

Command Execution

Quick Actions

`; // Load saved quick action values after rendering this.loadQuickActionValues(); } bindEvents() { if (!this.container) return; // Toggle button in the footer const toggleBtn = this.container.querySelector('#toggle-command-panel-btn'); if (toggleBtn) { toggleBtn.addEventListener('click', () => { this.toggle(); }); } } bindDrawerEvents() { if (!this.drawerContainer) return; // Close button const closeBtn = this.drawerContainer.querySelector('#close-command-panel-btn'); if (closeBtn) { closeBtn.addEventListener('click', () => { this.hide(); }); } // Quick action buttons and other controls this.drawerContainer.addEventListener('click', (e) => { if (e.target.classList.contains('quick-action-btn')) { const command = e.target.dataset.command; this.executeQuickCommand(command); } else if (e.target.id === 'run-commands-btn') { this.showRunCommandsModal(); } }); // Bind quick action input change events for persistence this.bindQuickActionPersistence(); } async executeQuickCommand(command) { try { const dryRunCheckbox = this.drawerContainer.querySelector('#dry-run-checkbox'); const dryRun = dryRunCheckbox ? dryRunCheckbox.checked : false; const skipCleanupCheckbox = this.drawerContainer.querySelector('#quick-skip-cleanup-checkbox'); const skipCleanup = skipCleanupCheckbox ? skipCleanupCheckbox.checked : false; const skipQbVersionCheckCheckbox = this.drawerContainer.querySelector('#quick-skip-qb-version-check-checkbox'); const skipQbVersionCheck = skipQbVersionCheckCheckbox ? skipQbVersionCheckCheckbox.checked : false; const logLevelSelect = this.drawerContainer.querySelector('#quick-log-level-select'); const logLevel = logLevelSelect ? logLevelSelect.value : ''; const result = await this.onCommandExecute([command], { dryRun: dryRun, skip_cleanup: skipCleanup, skip_qb_version_check: skipQbVersionCheck, log_level: logLevel }); this.showToast(`${command} command executed`, 'success'); } catch (error) { console.error('Failed to execute quick command:', error); this.showToast(`Failed to execute ${command}`, 'error'); } } showRunCommandsModal() { if (this.runCommandsModal) { // If modal already exists, just show it (it might be hidden) this.runCommandsModal.classList.remove('hidden'); return; } const modal = document.createElement('div'); modal.className = 'modal-overlay'; modal.innerHTML = ` `; document.body.appendChild(modal); this.runCommandsModal = modal; // Store reference to the modal // Load saved state const savedCommands = JSON.parse(localStorage.getItem('qbm-selected-commands') || '[]'); const savedDryRun = localStorage.getItem('qbm-dry-run-option') === 'true'; const savedSkipCleanup = localStorage.getItem('qbm-skip-cleanup-option') === 'true'; const savedSkipQbVersionCheck = localStorage.getItem('qbm-skip-qb-version-check-option') === 'true'; const savedLogLevel = localStorage.getItem('qbm-log-level-option') || ''; // Set command checkboxes savedCommands.forEach(cmd => { const checkbox = modal.querySelector(`input[name="commands"][value="${cmd}"]`); if (checkbox) { checkbox.checked = true; } }); // Set dry run checkbox modal.querySelector('#dry-run-option').checked = savedDryRun; modal.querySelector('#skip-cleanup-option').checked = savedSkipCleanup; modal.querySelector('#skip-qb-version-check-option').checked = savedSkipQbVersionCheck; modal.querySelector('#log-level-select').value = savedLogLevel; // Bind modal events const closeModal = () => { this.hideRunCommandsModal(); // Use the new hide method }; modal.querySelector('.modal-close-btn').addEventListener('click', closeModal); modal.querySelector('.modal-cancel-btn').addEventListener('click', closeModal); modal.addEventListener('click', (e) => { if (e.target === modal) closeModal(); }); modal.querySelector('.modal-execute-btn').addEventListener('click', async () => { const selectedCommands = Array.from(modal.querySelectorAll('input[name="commands"]:checked')) .map(input => input.value); const dryRun = modal.querySelector('#dry-run-option').checked; const skipCleanup = modal.querySelector('#skip-cleanup-option').checked; const skipQbVersionCheck = modal.querySelector('#skip-qb-version-check-option').checked; const hashesText = modal.querySelector('#torrent-hashes').value.trim(); const hashes = hashesText ? hashesText.split('\n').map(h => h.trim()).filter(h => h) : []; const logLevel = modal.querySelector('#log-level-select').value; if (selectedCommands.length === 0) { this.showToast('Please select at least one command', 'warning'); return; } // Save current selections to localStorage localStorage.setItem('qbm-selected-commands', JSON.stringify(selectedCommands)); localStorage.setItem('qbm-dry-run-option', dryRun); localStorage.setItem('qbm-skip-cleanup-option', skipCleanup); localStorage.setItem('qbm-skip-qb-version-check-option', skipQbVersionCheck); localStorage.setItem('qbm-log-level-option', logLevel); this.hideRunCommandsModal(); // Use the new hide method try { await this.onCommandExecute(selectedCommands, { dryRun, hashes, skip_cleanup: skipCleanup, skip_qb_version_check: skipQbVersionCheck, log_level: logLevel }); } catch (error) { console.error('Failed to execute commands:', error); this.showToast('Failed to execute commands', 'error'); } }); } hideRunCommandsModal() { if (this.runCommandsModal) { // Save current selections before hiding const selectedCommands = Array.from(this.runCommandsModal.querySelectorAll('input[name="commands"]:checked')) .map(input => input.value); const dryRun = this.runCommandsModal.querySelector('#dry-run-option').checked; const skipCleanup = this.runCommandsModal.querySelector('#skip-cleanup-option').checked; const skipQbVersionCheck = this.runCommandsModal.querySelector('#skip-qb-version-check-option').checked; const logLevel = this.runCommandsModal.querySelector('#log-level-select').value; localStorage.setItem('qbm-selected-commands', JSON.stringify(selectedCommands)); localStorage.setItem('qbm-dry-run-option', dryRun); localStorage.setItem('qbm-skip-cleanup-option', skipCleanup); localStorage.setItem('qbm-skip-qb-version-check-option', skipQbVersionCheck); localStorage.setItem('qbm-log-level-option', logLevel); this.runCommandsModal.parentNode.removeChild(this.runCommandsModal); this.runCommandsModal = null; } } toggleRunCommandsModal() { if (this.runCommandsModal && this.runCommandsModal.parentNode) { // If modal exists and is in DOM, hide it this.hideRunCommandsModal(); } else { // Otherwise, show it this.showRunCommandsModal(); } } showToast(message, type = 'info') { // This would typically call a global toast function // For now, we'll use console.log console.log(`[${type.toUpperCase()}] ${message}`); // If there's a global toast function available, use it if (window.qbitManageApp && window.qbitManageApp.showToast) { window.qbitManageApp.showToast(message, type); } } // Show the command panel drawer show() { if (!this.drawerContainer) return; this.isVisible = true; this.drawerContainer.classList.remove('hidden'); // Allow the display property to take effect before starting the transition setTimeout(() => { this.drawerContainer.classList.add('active'); }, 10); // Update toggle button state const toggleBtn = this.container.querySelector('#toggle-command-panel-btn'); if (toggleBtn) { toggleBtn.classList.add('active'); } } // Hide the command panel drawer hide() { if (!this.drawerContainer) return; this.isVisible = false; this.drawerContainer.classList.remove('active'); // Hide with delay to allow transition to complete setTimeout(() => { this.drawerContainer.classList.add('hidden'); }, 300); // Should match the transition duration in CSS // Update toggle button state const toggleBtn = this.container.querySelector('#toggle-command-panel-btn'); if (toggleBtn) { toggleBtn.classList.remove('active'); } } // Toggle the command panel drawer visibility toggle() { if (this.isVisible) { this.hide(); } else { this.show(); } } // Load saved quick action values from localStorage loadQuickActionValues() { if (!this.drawerContainer) return; // Get saved values, defaulting dry run to true if not previously saved const savedDryRunValue = localStorage.getItem('qbm-quick-dry-run'); const savedDryRun = savedDryRunValue !== null ? savedDryRunValue === 'true' : true; // Default to true const savedSkipCleanup = localStorage.getItem('qbm-quick-skip-cleanup') === 'true'; const savedSkipQbVersionCheck = localStorage.getItem('qbm-quick-skip-qb-version-check') === 'true'; const savedLogLevel = localStorage.getItem('qbm-quick-log-level') || ''; const dryRunCheckbox = this.drawerContainer.querySelector('#dry-run-checkbox'); const skipCleanupCheckbox = this.drawerContainer.querySelector('#quick-skip-cleanup-checkbox'); const skipQbVersionCheckCheckbox = this.drawerContainer.querySelector('#quick-skip-qb-version-check-checkbox'); const logLevelSelect = this.drawerContainer.querySelector('#quick-log-level-select'); if (dryRunCheckbox) dryRunCheckbox.checked = savedDryRun; if (skipCleanupCheckbox) skipCleanupCheckbox.checked = savedSkipCleanup; if (skipQbVersionCheckCheckbox) skipQbVersionCheckCheckbox.checked = savedSkipQbVersionCheck; if (logLevelSelect) logLevelSelect.value = savedLogLevel; // Save the default value if it was set if (savedDryRunValue === null) { localStorage.setItem('qbm-quick-dry-run', 'true'); } } // Save quick action values to localStorage saveQuickActionValues() { if (!this.drawerContainer) return; const dryRunCheckbox = this.drawerContainer.querySelector('#dry-run-checkbox'); const skipCleanupCheckbox = this.drawerContainer.querySelector('#quick-skip-cleanup-checkbox'); const skipQbVersionCheckCheckbox = this.drawerContainer.querySelector('#quick-skip-qb-version-check-checkbox'); const logLevelSelect = this.drawerContainer.querySelector('#quick-log-level-select'); const dryRun = dryRunCheckbox ? dryRunCheckbox.checked : false; const skipCleanup = skipCleanupCheckbox ? skipCleanupCheckbox.checked : false; const skipQbVersionCheck = skipQbVersionCheckCheckbox ? skipQbVersionCheckCheckbox.checked : false; const logLevel = logLevelSelect ? logLevelSelect.value : ''; localStorage.setItem('qbm-quick-dry-run', dryRun); localStorage.setItem('qbm-quick-skip-cleanup', skipCleanup); localStorage.setItem('qbm-quick-skip-qb-version-check', skipQbVersionCheck); localStorage.setItem('qbm-quick-log-level', logLevel); } // Bind event listeners for quick action persistence bindQuickActionPersistence() { if (!this.drawerContainer) return; // Bind checkbox change events const checkboxes = this.drawerContainer.querySelectorAll('#dry-run-checkbox, #quick-skip-cleanup-checkbox, #quick-skip-qb-version-check-checkbox'); checkboxes.forEach(checkbox => { checkbox.addEventListener('change', () => { this.saveQuickActionValues(); }); }); // Bind select change event const logLevelSelect = this.drawerContainer.querySelector('#quick-log-level-select'); if (logLevelSelect) { logLevelSelect.addEventListener('change', () => { this.saveQuickActionValues(); }); } } } export { CommandPanel };