diff --git a/src/public/js/ui/settings.js b/src/public/js/ui/settings.js
index 726fa03c8..b2190de61 100644
--- a/src/public/js/ui/settings.js
+++ b/src/public/js/ui/settings.js
@@ -1,6 +1,7 @@
PulseApp.ui = PulseApp.ui || {};
PulseApp.ui.settings = (() => {
+ let currentConfig = {};
let isInitialized = false;
function init() {
@@ -8,19 +9,84 @@ PulseApp.ui.settings = (() => {
console.log('[Settings] Initializing settings module...');
- // Set up form submission handler
- const form = document.getElementById('settings-config-form');
- if (form) {
- form.addEventListener('submit', handleFormSubmission);
+ // Set up modal event listeners
+ const settingsButton = document.getElementById('settings-button');
+ const modal = document.getElementById('settings-modal');
+ const closeButton = document.getElementById('settings-modal-close');
+ const cancelButton = document.getElementById('settings-cancel-button');
+ const testButton = document.getElementById('settings-test-button');
+ const saveButton = document.getElementById('settings-save-button');
+
+ if (settingsButton) {
+ settingsButton.addEventListener('click', openModal);
}
+
+ if (closeButton) {
+ closeButton.addEventListener('click', closeModal);
+ }
+
+ if (cancelButton) {
+ cancelButton.addEventListener('click', closeModal);
+ }
+
+ if (testButton) {
+ testButton.addEventListener('click', testConnections);
+ }
+
+ if (saveButton) {
+ saveButton.addEventListener('click', saveConfiguration);
+ }
+
+ // Close modal when clicking outside
+ if (modal) {
+ modal.addEventListener('click', (e) => {
+ if (e.target === modal) {
+ closeModal();
+ }
+ });
+ }
+
+ // Handle escape key
+ document.addEventListener('keydown', (e) => {
+ if (e.key === 'Escape' && !modal.classList.contains('hidden')) {
+ closeModal();
+ }
+ });
isInitialized = true;
console.log('[Settings] Settings module initialized');
}
- function load() {
- console.log('[Settings] Loading current configuration...');
- loadCurrentConfig();
+ async function openModal() {
+ console.log('[Settings] Opening settings modal...');
+
+ const modal = document.getElementById('settings-modal');
+ const body = document.getElementById('settings-modal-body');
+
+ if (!modal || !body) return;
+
+ // Show modal
+ modal.classList.remove('hidden');
+ modal.classList.add('flex');
+
+ // Load current configuration
+ try {
+ await loadCurrentConfig();
+ renderConfigurationForm();
+ } catch (error) {
+ console.error('[Settings] Failed to load configuration:', error);
+ body.innerHTML = `
Failed to load configuration: ${error.message}
`;
+ }
+ }
+
+ function closeModal() {
+ console.log('[Settings] Closing settings modal...');
+
+ const modal = document.getElementById('settings-modal');
+ if (modal) {
+ modal.classList.add('hidden');
+ modal.classList.remove('flex');
+ }
}
async function loadCurrentConfig() {
@@ -30,103 +96,354 @@ PulseApp.ui.settings = (() => {
throw new Error(`HTTP ${response.status}`);
}
- const config = await response.json();
- console.log('[Settings] Current config loaded:', config);
-
- // Populate Proxmox fields
- if (config.proxmox) {
- document.getElementById('settings-proxmox-host').value = config.proxmox.host || '';
- document.getElementById('settings-proxmox-port').value = config.proxmox.port || '';
- document.getElementById('settings-proxmox-token-id').value = config.proxmox.tokenId || '';
- // Don't populate the secret for security
- document.getElementById('settings-proxmox-token-secret').placeholder = 'Enter new secret or leave blank to keep current';
- }
+ currentConfig = await response.json();
+ console.log('[Settings] Current config loaded:', currentConfig);
} catch (error) {
console.error('[Settings] Failed to load current configuration:', error);
- showError('Failed to load current configuration: ' + error.message);
+ throw error;
}
}
- async function handleFormSubmission(e) {
- e.preventDefault();
+ function renderConfigurationForm() {
+ const body = document.getElementById('settings-modal-body');
+ if (!body) return;
+
+ const html = `
+
+ `;
+
+ body.innerHTML = html;
- const formData = new FormData(e.target);
- const config = {
- proxmox: {
- host: formData.get('proxmox-host'),
- port: formData.get('proxmox-port') || '8006',
- tokenId: formData.get('proxmox-token-id'),
- tokenSecret: formData.get('proxmox-token-secret')
- }
- };
-
- // Validate required fields
- if (!config.proxmox.host || !config.proxmox.tokenId) {
- showError('Please fill in all required fields (Host and Token ID)');
- return;
- }
-
- // If token secret is empty, don't include it (keep existing)
- if (!config.proxmox.tokenSecret) {
- delete config.proxmox.tokenSecret;
- }
-
- hideMessages();
- const button = document.getElementById('settings-save-button');
- const originalText = button.textContent;
- button.disabled = true;
- button.textContent = 'Saving...';
-
- try {
- const response = await fetch('/api/config', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify(config)
- });
-
- const result = await response.json();
-
- if (response.ok && result.success) {
- showSuccess('Configuration saved successfully!');
- // Reload the current config to show updated values
- setTimeout(() => {
- loadCurrentConfig();
- }, 1000);
- } else {
- showError(result.error || 'Failed to save configuration');
- }
- } catch (error) {
- showError('Failed to save configuration: ' + error.message);
- } finally {
- button.disabled = false;
- button.textContent = originalText;
- }
+ // Render additional endpoints
+ renderAdditionalEndpoints();
}
- async function testConnection() {
- const formData = new FormData(document.getElementById('settings-config-form'));
- const config = {
- proxmox: {
- host: formData.get('proxmox-host'),
- port: formData.get('proxmox-port') || '8006',
- tokenId: formData.get('proxmox-token-id'),
- tokenSecret: formData.get('proxmox-token-secret')
- }
- };
+ function renderAdditionalEndpoints() {
+ // This will be populated based on what additional endpoints exist in config
+ // For now, we'll support dynamic addition
+ }
- // Validate required fields
- if (!config.proxmox.host || !config.proxmox.tokenId || !config.proxmox.tokenSecret) {
- showError('Please fill in all required fields to test connection');
- return;
- }
+ function addPveEndpoint() {
+ const container = document.getElementById('pve-endpoints-container');
+ if (!container) return;
- hideMessages();
- const button = event.target;
- const originalText = button.textContent;
- button.disabled = true;
- button.textContent = 'Testing...';
+ const index = container.children.length + 2; // Start from _2
+ const endpointHtml = `
+
+
+
PVE Server #${index}
+
+
+ `;
+ container.insertAdjacentHTML('beforeend', endpointHtml);
+ }
+
+ function addPbsEndpoint() {
+ const container = document.getElementById('pbs-endpoints-container');
+ if (!container) return;
+
+ const index = container.children.length + 2; // Start from _2
+ const endpointHtml = `
+
+
+
PBS Server #${index}
+
+
+ `;
+
+ container.insertAdjacentHTML('beforeend', endpointHtml);
+ }
+
+ async function testConnections() {
+ showMessage('Testing connections...', 'info');
+
+ const config = collectFormData();
+
try {
const response = await fetch('/api/config/test', {
method: 'POST',
@@ -137,63 +454,114 @@ PulseApp.ui.settings = (() => {
const result = await response.json();
if (response.ok && result.success) {
- showSuccess('Connection test successful!');
- setTimeout(() => hideMessages(), 3000);
+ showMessage('All connections tested successfully!', 'success');
} else {
- showError(result.error || 'Connection test failed');
+ showMessage(result.error || 'Connection test failed', 'error');
}
} catch (error) {
- showError('Failed to test connection: ' + error.message);
+ showMessage('Failed to test connections: ' + error.message, 'error');
+ }
+ }
+
+ async function saveConfiguration() {
+ const saveButton = document.getElementById('settings-save-button');
+ if (!saveButton) return;
+
+ const originalText = saveButton.textContent;
+ saveButton.disabled = true;
+ saveButton.textContent = 'Saving...';
+
+ try {
+ const config = collectFormData();
+
+ const response = await fetch('/api/config', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(config)
+ });
+
+ const result = await response.json();
+
+ if (response.ok && result.success) {
+ showMessage('Configuration saved successfully!', 'success');
+ setTimeout(() => {
+ closeModal();
+ }, 1500);
+ } else {
+ showMessage(result.error || 'Failed to save configuration', 'error');
+ }
+ } catch (error) {
+ showMessage('Failed to save configuration: ' + error.message, 'error');
} finally {
- button.disabled = false;
- button.textContent = originalText;
+ saveButton.disabled = false;
+ saveButton.textContent = originalText;
}
}
- function togglePasswordVisibility(inputId) {
- const input = document.getElementById(inputId);
- if (input) {
- input.type = input.type === 'password' ? 'text' : 'password';
+ function collectFormData() {
+ const form = document.getElementById('settings-form');
+ if (!form) return {};
+
+ const formData = new FormData(form);
+ const config = {};
+
+ // Build the config object from form data
+ for (const [name, value] of formData.entries()) {
+ if (value.trim() === '') continue; // Skip empty values
+
+ // Handle checkbox values
+ if (form.querySelector(`[name="${name}"]`).type === 'checkbox') {
+ config[name] = 'true';
+ } else {
+ config[name] = value;
+ }
}
+
+ // Also handle unchecked checkboxes
+ const checkboxes = form.querySelectorAll('input[type="checkbox"]');
+ checkboxes.forEach(checkbox => {
+ if (!checkbox.checked && !config[checkbox.name]) {
+ config[checkbox.name] = 'false';
+ }
+ });
+
+ return config;
}
- function showError(message) {
- const errorDiv = document.getElementById('settings-error-message');
- const errorText = document.getElementById('settings-error-text');
- const successDiv = document.getElementById('settings-success-message');
-
- if (errorText && errorDiv && successDiv) {
- errorText.textContent = message;
- errorDiv.classList.remove('hidden');
- successDiv.classList.add('hidden');
- }
- }
+ function showMessage(message, type) {
+ const container = document.getElementById('settings-messages');
+ if (!container) return;
- function showSuccess(message) {
- const errorDiv = document.getElementById('settings-error-message');
- const successDiv = document.getElementById('settings-success-message');
- const successText = document.getElementById('settings-success-text');
-
- if (successText && successDiv && errorDiv) {
- successText.textContent = message;
- errorDiv.classList.add('hidden');
- successDiv.classList.remove('hidden');
- }
- }
+ const typeClasses = {
+ error: 'bg-red-50 dark:bg-red-900/20 border-red-200 dark:border-red-800 text-red-700 dark:text-red-300',
+ success: 'bg-green-50 dark:bg-green-900/20 border-green-200 dark:border-green-800 text-green-700 dark:text-green-300',
+ info: 'bg-blue-50 dark:bg-blue-900/20 border-blue-200 dark:border-blue-800 text-blue-700 dark:text-blue-300'
+ };
- function hideMessages() {
- const errorDiv = document.getElementById('settings-error-message');
- const successDiv = document.getElementById('settings-success-message');
-
- if (errorDiv) errorDiv.classList.add('hidden');
- if (successDiv) successDiv.classList.add('hidden');
+ const html = `
+
+ ${message}
+
+ `;
+
+ container.innerHTML = html;
+
+ // Clear message after 5 seconds for non-error messages
+ if (type !== 'error') {
+ setTimeout(() => {
+ container.innerHTML = '';
+ }, 5000);
+ }
}
// Public API
return {
init,
- load,
- testConnection,
- togglePasswordVisibility
+ openModal,
+ closeModal,
+ addPveEndpoint,
+ addPbsEndpoint,
+ testConnections,
+ saveConfiguration
};
})();
\ No newline at end of file