From e1a32b1466bec2edee97539ecbe3fce2ff6c5f95 Mon Sep 17 00:00:00 2001 From: Chuck <33324927+ChuckBuilds@users.noreply.github.com> Date: Thu, 24 Jul 2025 14:40:12 -0500 Subject: [PATCH] web ui save button improvements --- templates/index.html | 247 ++++++++++++++++++++++++++++++------------- web_interface.py | 108 ++++++++++++------- 2 files changed, 243 insertions(+), 112 deletions(-) diff --git a/templates/index.html b/templates/index.html index 4fb02740..5c12b443 100644 --- a/templates/index.html +++ b/templates/index.html @@ -333,7 +333,7 @@

Display Schedule

Set the time for the display to be active. A restart is needed for changes to take effect.

-
+
@@ -1864,25 +1864,85 @@ 'config_data': JSON.stringify(config) }) }) - .then(response => { - if (response.redirected) { - window.location.href = response.url; - } else { - return response.text(); - } - }) + .then(response => response.json()) .then(data => { - if (data) { - alert('Sports configuration saved successfully!'); + if (data.status === 'success') { + showMessage(data.message, 'success'); + } else { + showMessage(data.message, 'error'); } }) .catch(error => { - alert('Error: ' + error); + showMessage('Error saving sports configuration: ' + error, 'error'); }); } + function showMessage(message, type) { + // Create a temporary message element + const messageDiv = document.createElement('div'); + messageDiv.className = `flash-message ${type}`; + messageDiv.style.cssText = ` + position: fixed; + top: 20px; + right: 20px; + padding: 15px 20px; + border-radius: 5px; + color: white; + font-weight: bold; + z-index: 1000; + max-width: 400px; + word-wrap: break-word; + `; + + if (type === 'success') { + messageDiv.style.backgroundColor = '#4CAF50'; + } else { + messageDiv.style.backgroundColor = '#f44336'; + } + + messageDiv.textContent = message; + document.body.appendChild(messageDiv); + + // Remove the message after 3 seconds + setTimeout(() => { + if (messageDiv.parentNode) { + messageDiv.parentNode.removeChild(messageDiv); + } + }, 3000); + } + // Add form submission handlers for better UX document.addEventListener('DOMContentLoaded', function() { + // Handle schedule form submission + const scheduleForm = document.getElementById('schedule-form'); + if (scheduleForm) { + scheduleForm.addEventListener('submit', function(e) { + e.preventDefault(); + const formData = new FormData(scheduleForm); + + fetch("{{ url_for('save_schedule_route') }}", { + method: 'POST', + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + body: new URLSearchParams({ + 'schedule_enabled': formData.get('schedule_enabled') ? 'on' : '', + 'start_time': formData.get('start_time'), + 'end_time': formData.get('end_time') + }) + }) + .then(response => response.json()) + .then(data => { + if (data.status === 'success') { + showMessage(data.message, 'success'); + } else { + showMessage(data.message, 'error'); + } + }) + .catch(error => { + showMessage('Error saving schedule: ' + error, 'error'); + }); + }); + } + // Handle display form submission const displayForm = document.getElementById('display-form'); if (displayForm) { @@ -1913,13 +1973,16 @@ 'config_data': JSON.stringify(config) }) }) - .then(response => { - if (response.redirected) { - window.location.href = response.url; + .then(response => response.json()) + .then(data => { + if (data.status === 'success') { + showMessage(data.message, 'success'); + } else { + showMessage(data.message, 'error'); } }) .catch(error => { - alert('Error saving display settings: ' + error); + showMessage('Error saving display settings: ' + error, 'error'); }); }); } @@ -1950,13 +2013,16 @@ 'config_data': JSON.stringify(config) }) }) - .then(response => { - if (response.redirected) { - window.location.href = response.url; + .then(response => response.json()) + .then(data => { + if (data.status === 'success') { + showMessage(data.message, 'success'); + } else { + showMessage(data.message, 'error'); } }) .catch(error => { - alert('Error saving weather settings: ' + error); + showMessage('Error saving weather settings: ' + error, 'error'); }); }); } @@ -1984,13 +2050,16 @@ 'config_data': JSON.stringify(config) }) }) - .then(response => { - if (response.redirected) { - window.location.href = response.url; + .then(response => response.json()) + .then(data => { + if (data.status === 'success') { + showMessage(data.message, 'success'); + } else { + showMessage(data.message, 'error'); } }) .catch(error => { - alert('Error saving stocks settings: ' + error); + showMessage('Error saving stocks settings: ' + error, 'error'); }); }); } @@ -2018,13 +2087,16 @@ 'config_data': JSON.stringify(config) }) }) - .then(response => { - if (response.redirected) { - window.location.href = response.url; + .then(response => response.json()) + .then(data => { + if (data.status === 'success') { + showMessage(data.message, 'success'); + } else { + showMessage(data.message, 'error'); } }) .catch(error => { - alert('Error saving crypto settings: ' + error); + showMessage('Error saving crypto settings: ' + error, 'error'); }); }); } @@ -2052,13 +2124,16 @@ 'config_data': JSON.stringify(config) }) }) - .then(response => { - if (response.redirected) { - window.location.href = response.url; + .then(response => response.json()) + .then(data => { + if (data.status === 'success') { + showMessage(data.message, 'success'); + } else { + showMessage(data.message, 'error'); } }) .catch(error => { - alert('Error saving music settings: ' + error); + showMessage('Error saving music settings: ' + error, 'error'); }); }); } @@ -2087,13 +2162,16 @@ 'config_data': JSON.stringify(config) }) }) - .then(response => { - if (response.redirected) { - window.location.href = response.url; + .then(response => response.json()) + .then(data => { + if (data.status === 'success') { + showMessage(data.message, 'success'); + } else { + showMessage(data.message, 'error'); } }) .catch(error => { - alert('Error saving calendar settings: ' + error); + showMessage('Error saving calendar settings: ' + error, 'error'); }); }); } @@ -2127,13 +2205,16 @@ 'config_data': JSON.stringify(config) }) }) - .then(response => { - if (response.redirected) { - window.location.href = response.url; + .then(response => response.json()) + .then(data => { + if (data.status === 'success') { + showMessage(data.message, 'success'); + } else { + showMessage(data.message, 'error'); } }) .catch(error => { - alert('Error saving API keys: ' + error); + showMessage('Error saving API keys: ' + error, 'error'); }); }); } @@ -2167,13 +2248,16 @@ 'config_data': JSON.stringify(config) }) }) - .then(response => { - if (response.redirected) { - window.location.href = response.url; + .then(response => response.json()) + .then(data => { + if (data.status === 'success') { + showMessage(data.message, 'success'); + } else { + showMessage(data.message, 'error'); } }) .catch(error => { - alert('Error saving display durations: ' + error); + showMessage('Error saving display durations: ' + error, 'error'); }); }); } @@ -2200,13 +2284,16 @@ 'config_data': JSON.stringify(config) }) }) - .then(response => { - if (response.redirected) { - window.location.href = response.url; + .then(response => response.json()) + .then(data => { + if (data.status === 'success') { + showMessage(data.message, 'success'); + } else { + showMessage(data.message, 'error'); } }) .catch(error => { - alert('Error saving general settings: ' + error); + showMessage('Error saving general settings: ' + error, 'error'); }); }); } @@ -2234,13 +2321,16 @@ 'config_data': JSON.stringify(config) }) }) - .then(response => { - if (response.redirected) { - window.location.href = response.url; + .then(response => response.json()) + .then(data => { + if (data.status === 'success') { + showMessage(data.message, 'success'); + } else { + showMessage(data.message, 'error'); } }) .catch(error => { - alert('Error saving clock settings: ' + error); + showMessage('Error saving clock settings: ' + error, 'error'); }); }); } @@ -2266,13 +2356,16 @@ 'config_data': JSON.stringify(config) }) }) - .then(response => { - if (response.redirected) { - window.location.href = response.url; + .then(response => response.json()) + .then(data => { + if (data.status === 'success') { + showMessage(data.message, 'success'); + } else { + showMessage(data.message, 'error'); } }) .catch(error => { - alert('Error saving stock news settings: ' + error); + showMessage('Error saving stock news settings: ' + error, 'error'); }); }); } @@ -2298,13 +2391,16 @@ 'config_data': JSON.stringify(config) }) }) - .then(response => { - if (response.redirected) { - window.location.href = response.url; + .then(response => response.json()) + .then(data => { + if (data.status === 'success') { + showMessage(data.message, 'success'); + } else { + showMessage(data.message, 'error'); } }) .catch(error => { - alert('Error saving odds ticker settings: ' + error); + showMessage('Error saving odds ticker settings: ' + error, 'error'); }); }); } @@ -2331,13 +2427,16 @@ 'config_data': JSON.stringify(config) }) }) - .then(response => { - if (response.redirected) { - window.location.href = response.url; + .then(response => response.json()) + .then(data => { + if (data.status === 'success') { + showMessage(data.message, 'success'); + } else { + showMessage(data.message, 'error'); } }) .catch(error => { - alert('Error saving YouTube settings: ' + error); + showMessage('Error saving YouTube settings: ' + error, 'error'); }); }); } @@ -2368,13 +2467,16 @@ 'config_data': JSON.stringify(config) }) }) - .then(response => { - if (response.redirected) { - window.location.href = response.url; + .then(response => response.json()) + .then(data => { + if (data.status === 'success') { + showMessage(data.message, 'success'); + } else { + showMessage(data.message, 'error'); } }) .catch(error => { - alert('Error saving text display settings: ' + error); + showMessage('Error saving text display settings: ' + error, 'error'); }); }); } @@ -2400,13 +2502,16 @@ 'config_data': JSON.stringify(config) }) }) - .then(response => { - if (response.redirected) { - window.location.href = response.url; + .then(response => response.json()) + .then(data => { + if (data.status === 'success') { + showMessage(data.message, 'success'); + } else { + showMessage(data.message, 'error'); } }) .catch(error => { - alert('Error saving of the day settings: ' + error); + showMessage('Error saving of the day settings: ' + error, 'error'); }); }); } diff --git a/web_interface.py b/web_interface.py index 4e1eb507..97550c29 100644 --- a/web_interface.py +++ b/web_interface.py @@ -50,12 +50,16 @@ def save_schedule_route(): main_config['schedule'] = schedule_data config_manager.save_config(main_config) - flash("Schedule updated successfully! Restart the display for changes to take effect.", "success") + return jsonify({ + 'status': 'success', + 'message': 'Schedule updated successfully! Restart the display for changes to take effect.' + }) except Exception as e: - flash(f"Error saving schedule: {e}", "error") - - return redirect(url_for('index')) + return jsonify({ + 'status': 'error', + 'message': f'Error saving schedule: {e}' + }), 400 @app.route('/save_config', methods=['POST']) def save_config_route(): @@ -180,11 +184,16 @@ def save_config_route(): else: main_config[key] = value except json.JSONDecodeError: - flash("Error: Invalid JSON format in config data.", "error") - return redirect(url_for('index')) + return jsonify({ + 'status': 'error', + 'message': 'Error: Invalid JSON format in config data.' + }), 400 config_manager.save_config(main_config) - flash("Main configuration saved successfully!", "success") + return jsonify({ + 'status': 'success', + 'message': 'Main configuration saved successfully!' + }) elif config_type == 'secrets': # Handle secrets configuration @@ -211,54 +220,71 @@ def save_config_route(): new_data = json.loads(config_data_str) config_manager.save_raw_file_content('secrets', new_data) except json.JSONDecodeError: - flash("Error: Invalid JSON format for secrets config.", "error") - return redirect(url_for('index')) + return jsonify({ + 'status': 'error', + 'message': 'Error: Invalid JSON format for secrets config.' + }), 400 else: config_manager.save_raw_file_content('secrets', secrets_config) - flash("Secrets configuration saved successfully!", "success") + return jsonify({ + 'status': 'success', + 'message': 'Secrets configuration saved successfully!' + }) except json.JSONDecodeError: - flash(f"Error: Invalid JSON format for {config_type} config.", "error") + return jsonify({ + 'status': 'error', + 'message': f'Error: Invalid JSON format for {config_type} config.' + }), 400 except Exception as e: - flash(f"Error saving {config_type} configuration: {e}", "error") - - return redirect(url_for('index')) + return jsonify({ + 'status': 'error', + 'message': f'Error saving {config_type} configuration: {e}' + }), 400 @app.route('/run_action', methods=['POST']) def run_action_route(): - data = request.get_json() - action = data.get('action') - - commands = { - 'start_display': ["sudo", "python3", "display_controller.py"], - 'stop_display': ["sudo", "pkill", "-f", "display_controller.py"], - 'enable_autostart': ["sudo", "systemctl", "enable", "ledmatrix.service"], - 'disable_autostart': ["sudo", "systemctl", "disable", "ledmatrix.service"], - 'reboot_system': ["sudo", "reboot"], - 'git_pull': ["git", "pull"] - } - - command_parts = commands.get(action) - - if not command_parts: - return jsonify({"status": "error", "message": "Invalid action."}), 400 - try: - result = subprocess.run(command_parts, capture_output=True, text=True, check=False) + data = request.get_json() + action = data.get('action') - status = "success" if result.returncode == 0 else "error" - message = f"Action '{action}' completed." + if action == 'start_display': + result = subprocess.run(['sudo', 'systemctl', 'start', 'ledmatrix'], + capture_output=True, text=True) + elif action == 'stop_display': + result = subprocess.run(['sudo', 'systemctl', 'stop', 'ledmatrix'], + capture_output=True, text=True) + elif action == 'enable_autostart': + result = subprocess.run(['sudo', 'systemctl', 'enable', 'ledmatrix'], + capture_output=True, text=True) + elif action == 'disable_autostart': + result = subprocess.run(['sudo', 'systemctl', 'disable', 'ledmatrix'], + capture_output=True, text=True) + elif action == 'reboot_system': + result = subprocess.run(['sudo', 'reboot'], + capture_output=True, text=True) + elif action == 'git_pull': + result = subprocess.run(['git', 'pull'], + capture_output=True, text=True, cwd='/home/pi/LEDMatrix') + else: + return jsonify({ + 'status': 'error', + 'message': f'Unknown action: {action}' + }), 400 return jsonify({ - "status": status, - "message": message, - "stdout": result.stdout, - "stderr": result.stderr + 'status': 'success' if result.returncode == 0 else 'error', + 'message': f'Action {action} completed with return code {result.returncode}', + 'stdout': result.stdout, + 'stderr': result.stderr }) - + except Exception as e: - return jsonify({"status": "error", "message": str(e)}), 500 + return jsonify({ + 'status': 'error', + 'message': f'Error running action: {e}' + }), 400 if __name__ == '__main__': - app.run(debug=True, host='0.0.0.0', port=5000) \ No newline at end of file + app.run(host='0.0.0.0', port=5000, debug=True) \ No newline at end of file