From 976f04e9f8904253ecf5fc74c40d994678256d4e Mon Sep 17 00:00:00 2001 From: Admin9705 <9705@duck.com> Date: Fri, 2 May 2025 12:50:17 -0400 Subject: [PATCH] a301 --- frontend/static/css/style.css | 169 +++++++++- frontend/static/js/apps.js | 308 ++++++++++++++++++ frontend/static/js/new-main.js | 12 + .../templates/components/apps_section.html | 37 +++ .../components/settings_section.html | 25 +- frontend/templates/components/sidebar.html | 4 + frontend/templates/index.html | 5 + src/primary/web_server.py | 133 ++++---- 8 files changed, 590 insertions(+), 103 deletions(-) create mode 100644 frontend/static/js/apps.js create mode 100644 frontend/templates/components/apps_section.html diff --git a/frontend/static/css/style.css b/frontend/static/css/style.css index 8739e6db..f9927f49 100644 --- a/frontend/static/css/style.css +++ b/frontend/static/css/style.css @@ -873,7 +873,6 @@ input:checked + .toggle-slider:before { cursor: pointer; display: inline-flex; align-items: center; - justify-content: center; gap: 4px; transition: background-color 0.2s ease; width: fit-content; @@ -1115,7 +1114,6 @@ input:checked + .toggle-slider:before { cursor: pointer; display: flex; align-items: center; - justify-content: center; gap: 10px; transition: background-color 0.3s; } @@ -1276,4 +1274,171 @@ input:checked + .toggle-slider:before { #reset_stateful_btn i { font-size: 13px; +} + +/* Apps Section */ +/* Use the existing log dropdown styles for app section. No custom CSS needed for the dropdown itself. */ + +/* App settings content styling */ +.settings-content { + margin-top: 20px; +} + +.app-apps-panel { + display: none; + width: 100%; +} + +.app-apps-panel.active { + display: block; +} + +/* Instance panel styling */ +.instance-panel { + background-color: var(--bg-secondary, #2c2c2c); + border-radius: 4px; + padding: 15px; + margin-bottom: 15px; + border: 1px solid var(--border-color, #3c3c3c); +} + +.instance-header { + display: flex; + align-items: center; + margin-bottom: 15px; + gap: 10px; + padding-bottom: 10px; + border-bottom: 1px solid var(--border-color, #3c3c3c); +} + +.instance-name { + flex: 1; + padding: 8px; + background-color: var(--bg-tertiary, #252525); + border: 1px solid var(--border-color, #3c3c3c); + border-radius: 4px; + color: var(--text-primary, white); + font-size: 14px; +} + +.form-field { + margin-bottom: 15px; +} + +.form-field label { + display: block; + margin-bottom: 5px; + font-weight: 400; + color: var(--text-primary, #f0f0f0); + font-size: 14px; +} + +.form-field input { + padding: 8px; + background-color: var(--bg-tertiary, #252525); + border: 1px solid var(--border-color, #3c3c3c); + border-radius: 4px; + color: var(--text-primary, white); + width: 100%; + max-width: 500px; + font-size: 14px; +} + +/* Button styling */ +.add-instance-btn { + background-color: var(--accent-color, #007bff); + color: white; + border: none; + padding: 8px 16px; + border-radius: 4px; + cursor: pointer; + display: inline-flex; + align-items: center; + gap: 5px; + font-size: 14px; + margin-top: 15px; +} + +.add-instance-btn:hover { + background-color: var(--accent-hover, #0069d9); +} + +.remove-instance-btn { + background-color: #dc3545; + color: white; + border: none; + width: 30px; + height: 30px; + border-radius: 4px; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; +} + +.remove-instance-btn:hover { + background-color: #c82333; +} + +.test-connection-btn { + background-color: #28a745; + color: white; + border: none; + padding: 6px 12px; + border-radius: 4px; + cursor: pointer; + display: inline-flex; + align-items: center; + gap: 5px; + font-size: 13px; + margin-top: 5px; +} + +.test-connection-btn:hover { + background-color: #218838; +} + +/* Match styling with existing settings UI */ +#appsSection .section-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 20px; + border-bottom: 1px solid var(--border-color, #3c3c3c); + padding-bottom: 10px; +} + +#appsSection .settings-group { + margin-top: 20px; + margin-bottom: 30px; + background-color: var(--bg-secondary, #252525); + border-radius: 4px; + padding: 20px; +} + +#appsSection .settings-group-header { + margin-bottom: 15px; + padding-bottom: 10px; + border-bottom: 1px solid var(--border-color, #363636); + font-size: 16px; + font-weight: 500; + color: var(--text-primary, #f0f0f0); +} + +.loading-panel { + display: flex; + align-items: center; + justify-content: center; + padding: 20px; + color: var(--text-primary, #f0f0f0); + gap: 10px; +} + +.error-panel { + display: flex; + align-items: center; + justify-content: center; + padding: 20px; + color: #dc3545; + gap: 10px; } \ No newline at end of file diff --git a/frontend/static/js/apps.js b/frontend/static/js/apps.js new file mode 100644 index 00000000..57146e2d --- /dev/null +++ b/frontend/static/js/apps.js @@ -0,0 +1,308 @@ +/** + * Huntarr - Apps Module + * Handles displaying and managing app settings for media server applications + */ + +const appsModule = { + // State + currentApp: 'sonarr', + isLoading: false, + settingsChanged: false, + originalSettings: {}, + + // DOM elements + elements: {}, + + // Initialize the apps module + init: function() { + this.cacheElements(); + this.setupEventListeners(); + + // Initial load if apps is active section + if (huntarrUI && huntarrUI.currentSection === 'apps') { + this.loadApps(); + } + }, + + // Cache DOM elements + cacheElements: function() { + this.elements = { + // Apps dropdown + appsOptions: document.querySelectorAll('#appsSection .log-option'), + currentAppsApp: document.getElementById('current-apps-app'), + appsDropdownBtn: document.querySelector('#appsSection .log-dropdown-btn'), + appsDropdownContent: document.querySelector('#appsSection .log-dropdown-content'), + + // Apps panels + appAppsPanels: document.querySelectorAll('.app-apps-panel'), + + // Controls + saveAppsButton: document.getElementById('saveAppsButton') + }; + }, + + // Set up event listeners + setupEventListeners: function() { + // App selection + if (this.elements.appsOptions) { + this.elements.appsOptions.forEach(option => { + option.addEventListener('click', e => this.handleAppsAppChange(e)); + }); + } + + // Dropdown toggle + if (this.elements.appsDropdownBtn) { + this.elements.appsDropdownBtn.addEventListener('click', () => { + this.elements.appsDropdownContent.classList.toggle('show'); + + // Close all other dropdowns + document.querySelectorAll('.log-dropdown-content.show').forEach(dropdown => { + if (dropdown !== this.elements.appsDropdownContent) { + dropdown.classList.remove('show'); + } + }); + }); + } + + // Close dropdown when clicking outside + document.addEventListener('click', e => { + if (!e.target.matches('#appsSection .log-dropdown-btn') && + !e.target.closest('#appsSection .log-dropdown-btn')) { + if (this.elements.appsDropdownContent && this.elements.appsDropdownContent.classList.contains('show')) { + this.elements.appsDropdownContent.classList.remove('show'); + } + } + }); + + // Save button + if (this.elements.saveAppsButton) { + this.elements.saveAppsButton.addEventListener('click', () => this.saveApps()); + } + }, + + // Load apps data when section becomes active + loadApps: function() { + console.log('[Apps] Loading apps data for ' + this.currentApp); + + // Disable save button until changes are made + if (this.elements.saveAppsButton) { + this.elements.saveAppsButton.disabled = true; + } + this.settingsChanged = false; + + // Get all settings to populate forms + fetch('/api/settings') + .then(response => response.json()) + .then(data => { + console.log('Loaded settings:', data); + + // Store original settings for comparison + this.originalSettings = data; + + // Ensure current app panel is visible + this.showAppPanel(this.currentApp); + + // Populate each app's settings form + this.populateAllAppPanels(data); + }) + .catch(error => { + console.error('Error loading settings:', error); + const appPanel = document.getElementById(this.currentApp + 'Apps'); + if (appPanel) { + appPanel.innerHTML = '