diff --git a/.playwright-mcp/back-to-light-theme.png b/.playwright-mcp/back-to-light-theme.png new file mode 100644 index 0000000..1c59750 Binary files /dev/null and b/.playwright-mcp/back-to-light-theme.png differ diff --git a/.playwright-mcp/current-theme-light.png b/.playwright-mcp/current-theme-light.png new file mode 100644 index 0000000..8e786bb Binary files /dev/null and b/.playwright-mcp/current-theme-light.png differ diff --git a/.playwright-mcp/dark-theme-dashboard.png b/.playwright-mcp/dark-theme-dashboard.png new file mode 100644 index 0000000..8518e0f Binary files /dev/null and b/.playwright-mcp/dark-theme-dashboard.png differ diff --git a/.playwright-mcp/dark-theme-fixed.png b/.playwright-mcp/dark-theme-fixed.png new file mode 100644 index 0000000..a81a600 Binary files /dev/null and b/.playwright-mcp/dark-theme-fixed.png differ diff --git a/.playwright-mcp/dark-theme-issues.png b/.playwright-mcp/dark-theme-issues.png new file mode 100644 index 0000000..5eba3e6 Binary files /dev/null and b/.playwright-mcp/dark-theme-issues.png differ diff --git a/.playwright-mcp/desktop-view-light.png b/.playwright-mcp/desktop-view-light.png new file mode 100644 index 0000000..94a77ee Binary files /dev/null and b/.playwright-mcp/desktop-view-light.png differ diff --git a/.playwright-mcp/light-theme-with-toggle.png b/.playwright-mcp/light-theme-with-toggle.png new file mode 100644 index 0000000..209f06a Binary files /dev/null and b/.playwright-mcp/light-theme-with-toggle.png differ diff --git a/.playwright-mcp/tema-claro-final.png b/.playwright-mcp/tema-claro-final.png new file mode 100644 index 0000000..10e6ce9 Binary files /dev/null and b/.playwright-mcp/tema-claro-final.png differ diff --git a/.playwright-mcp/tema-corregido-final.png b/.playwright-mcp/tema-corregido-final.png new file mode 100644 index 0000000..1846d29 Binary files /dev/null and b/.playwright-mcp/tema-corregido-final.png differ diff --git a/src/app.py b/src/app.py index e1d426c..39cc340 100644 --- a/src/app.py +++ b/src/app.py @@ -30,6 +30,9 @@ from routes import register_api_routes, register_web_routes # Crear instancia Flask app = Flask(__name__, template_folder="../templates", static_folder="../static") +# Configuración para desarrollo - habilitar recarga de templates +app.config["TEMPLATES_AUTO_RELOAD"] = True + class AutoBackupsFlaskApp: """Aplicación principal de AutoBackups con Flask""" diff --git a/static/css/styles.css b/static/css/styles.css index 08e0e14..9c2bfcd 100644 --- a/static/css/styles.css +++ b/static/css/styles.css @@ -7,12 +7,41 @@ --warning-color: #ffc107; --danger-color: #dc3545; --dark-color: #212529; + + /* Light theme colors */ + --bg-color: #f8f9fa; + --surface-color: #ffffff; + --text-color: #212529; + --text-muted: #6c757d; + --border-color: #dee2e6; + --hover-bg: rgba(0, 0, 0, 0.025); + --shadow-color: rgba(0, 0, 0, 0.075); + --shadow-hover: rgba(0, 0, 0, 0.15); +} + +/* Dark theme colors */ +[data-theme="dark"] { + --bg-color: #1a1a1a; + --surface-color: #2d2d2d; + --text-color: #e9ecef; + --text-muted: #adb5bd; + --border-color: #495057; + --hover-bg: rgba(255, 255, 255, 0.1); + --shadow-color: rgba(0, 0, 0, 0.25); + --shadow-hover: rgba(0, 0, 0, 0.35); + --primary-color: #4dabf7; + --success-color: #51cf66; + --info-color: #22b8cf; + --warning-color: #ffd43b; + --danger-color: #ff6b6b; } /* General Styles */ body { - background-color: #f8f9fa; + background-color: var(--bg-color); + color: var(--text-color); font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + transition: background-color 0.3s ease, color 0.3s ease; } .navbar-brand { @@ -20,22 +49,40 @@ body { font-size: 1.5rem; } +/* Theme Toggle */ +.theme-toggle { + background: none; + border: none; + color: rgba(255, 255, 255, 0.75); + font-size: 1.2rem; + padding: 0.375rem 0.75rem; + border-radius: 0.375rem; + transition: color 0.3s ease, background-color 0.3s ease; +} + +.theme-toggle:hover { + color: rgba(255, 255, 255, 0.9); + background-color: rgba(255, 255, 255, 0.1); +} + /* Cards */ .card { border: none; border-radius: 0.5rem; - box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075); - transition: box-shadow 0.15s ease-in-out; + background-color: var(--surface-color); + box-shadow: 0 0.125rem 0.25rem var(--shadow-color); + transition: box-shadow 0.15s ease-in-out, background-color 0.3s ease; } .card:hover { - box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15); + box-shadow: 0 0.5rem 1rem var(--shadow-hover); } .card-header { - background-color: #fff; - border-bottom: 1px solid #dee2e6; + background-color: var(--surface-color); + border-bottom: 1px solid var(--border-color); font-weight: 600; + color: var(--text-color); } /* Statistics Cards */ @@ -65,11 +112,12 @@ body { /* Tables */ .table { - background-color: #fff; + background-color: var(--surface-color); + color: var(--text-color); } .table-hover tbody tr:hover { - background-color: rgba(0, 0, 0, 0.025); + background-color: var(--hover-bg); } .table th { @@ -78,11 +126,14 @@ body { font-size: 0.9rem; text-transform: uppercase; letter-spacing: 0.5px; + color: var(--text-color); + border-bottom: 1px solid var(--border-color); } .table td { vertical-align: middle; - border-color: #dee2e6; + border-color: var(--border-color); + color: var(--text-color); } /* Badges */ @@ -118,11 +169,13 @@ body { .modal-content { border: none; border-radius: 0.5rem; - box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15); + background-color: var(--surface-color); + color: var(--text-color); + box-shadow: 0 0.5rem 1rem var(--shadow-hover); } .modal-header { - border-bottom: 1px solid #dee2e6; + border-bottom: 1px solid var(--border-color); padding: 1.5rem; } @@ -131,7 +184,7 @@ body { } .modal-footer { - border-top: 1px solid #dee2e6; + border-top: 1px solid var(--border-color); padding: 1rem 1.5rem; } @@ -139,13 +192,23 @@ body { .form-control, .form-select { border-radius: 0.375rem; - border: 1px solid #ced4da; + border: 1px solid var(--border-color); + background-color: var(--surface-color); + color: var(--text-color); } .form-control:focus, .form-select:focus { border-color: var(--primary-color); box-shadow: 0 0 0 0.2rem rgba(13, 110, 253, 0.25); + background-color: var(--surface-color); + color: var(--text-color); +} + +/* Dark theme form controls */ +[data-theme="dark"] .form-control:focus, +[data-theme="dark"] .form-select:focus { + box-shadow: 0 0 0 0.2rem rgba(77, 171, 247, 0.25); } /* Loading States */ @@ -219,7 +282,7 @@ body { /* Custom Scrollbar */ .custom-scrollbar { scrollbar-width: thin; - scrollbar-color: #ced4da #f8f9fa; + scrollbar-color: var(--border-color) var(--bg-color); } .custom-scrollbar::-webkit-scrollbar { @@ -227,15 +290,175 @@ body { } .custom-scrollbar::-webkit-scrollbar-track { - background: #f8f9fa; + background: var(--bg-color); border-radius: 4px; } .custom-scrollbar::-webkit-scrollbar-thumb { - background: #ced4da; + background: var(--border-color); border-radius: 4px; } .custom-scrollbar::-webkit-scrollbar-thumb:hover { - background: #adb5bd; + background: var(--text-muted); +} + +/* Dark theme specific overrides */ +[data-theme="dark"] .navbar-dark { + background-color: #1e2329 !important; +} + +[data-theme="dark"] .btn-outline-primary { + border-color: var(--primary-color); + color: var(--primary-color); +} + +[data-theme="dark"] .btn-outline-primary:hover { + background-color: var(--primary-color); + border-color: var(--primary-color); +} + +[data-theme="dark"] .alert { + border: 1px solid var(--border-color); +} + +[data-theme="dark"] .dropdown-menu { + background-color: var(--surface-color); + border: 1px solid var(--border-color); +} + +[data-theme="dark"] .dropdown-item { + color: var(--text-color); +} + +[data-theme="dark"] .dropdown-item:hover { + background-color: var(--hover-bg); + color: var(--text-color); +} + +[data-theme="dark"] .text-muted { + color: var(--text-muted) !important; +} + +/* Dark theme table specific fixes */ +[data-theme="dark"] .table { + background-color: var(--surface-color); + color: var(--text-color); +} + +[data-theme="dark"] .table td { + background-color: var(--surface-color); + color: var(--text-color); + border-color: var(--border-color); +} + +[data-theme="dark"] .table th { + background-color: var(--surface-color); + color: var(--text-color); + border-color: var(--border-color); +} + +[data-theme="dark"] .table tbody tr { + background-color: var(--surface-color); +} + +[data-theme="dark"] .table-hover tbody tr:hover { + background-color: var(--hover-bg); +} + +[data-theme="dark"] .table-hover tbody tr:hover td { + background-color: var(--hover-bg); +} + +/* Fix for Bootstrap table striped rows in dark theme */ +[data-theme="dark"] .table-striped tbody tr:nth-of-type(odd) { + background-color: rgba(255, 255, 255, 0.05); +} + +[data-theme="dark"] .table-striped tbody tr:nth-of-type(odd) td { + background-color: rgba(255, 255, 255, 0.05); +} + +/* Dark theme card fixes */ +[data-theme="dark"] .card { + background-color: var(--surface-color); + border: 1px solid var(--border-color); +} + +[data-theme="dark"] .card-body { + background-color: var(--surface-color); + color: var(--text-color); +} + +[data-theme="dark"] .card-header { + background-color: var(--surface-color); + border-bottom: 1px solid var(--border-color); + color: var(--text-color); +} + +/* Dark theme input group fixes */ +[data-theme="dark"] .input-group-text { + background-color: var(--surface-color); + border: 1px solid var(--border-color); + color: var(--text-color); +} + +/* Dark theme pagination fixes */ +[data-theme="dark"] .page-link { + background-color: var(--surface-color); + border: 1px solid var(--border-color); + color: var(--text-color); +} + +[data-theme="dark"] .page-link:hover { + background-color: var(--hover-bg); + color: var(--text-color); +} + +[data-theme="dark"] .page-item.active .page-link { + background-color: var(--primary-color); + border-color: var(--primary-color); +} + +/* Dark theme accordion fixes */ +[data-theme="dark"] .accordion-item { + background-color: var(--surface-color); + border: 1px solid var(--border-color); +} + +[data-theme="dark"] .accordion-button { + background-color: var(--surface-color); + color: var(--text-color); +} + +[data-theme="dark"] .accordion-button:not(.collapsed) { + background-color: var(--hover-bg); + color: var(--text-color); +} + +[data-theme="dark"] .accordion-body { + background-color: var(--surface-color); + color: var(--text-color); +} + +/* Fix for list-group items in dark theme */ +[data-theme="dark"] .list-group-item { + background-color: var(--surface-color) !important; + border-color: var(--border-color) !important; + color: var(--text-color) !important; +} + +/* Fix for table-dark header in light theme */ +[data-theme="light"] .table-dark th, +:root:not([data-theme="dark"]) .table-dark th { + background-color: #343a40 !important; + color: white !important; + border-color: #495057 !important; +} + +/* Ensure table-dark works correctly in both themes */ +[data-theme="dark"] .table-dark th { + background-color: #495057 !important; + color: white !important; + border-color: #6c757d !important; } diff --git a/static/js/app.js b/static/js/app.js index 64b100f..d92e965 100644 --- a/static/js/app.js +++ b/static/js/app.js @@ -6,6 +6,7 @@ class AutoBackupsApp { } init() { + this.initializeTheme(); this.updateCurrentTime(); this.setupEventListeners(); @@ -29,7 +30,45 @@ class AutoBackupsApp { } } + // Theme Management + initializeTheme() { + // Load saved theme from localStorage or default to light + const savedTheme = localStorage.getItem('theme') || 'light'; + this.setTheme(savedTheme); + } + + setTheme(theme) { + const htmlElement = document.documentElement; + const themeIcon = document.getElementById('theme-icon'); + + if (theme === 'dark') { + htmlElement.setAttribute('data-theme', 'dark'); + if (themeIcon) { + themeIcon.className = 'bi bi-moon-fill'; + } + } else { + htmlElement.removeAttribute('data-theme'); + if (themeIcon) { + themeIcon.className = 'bi bi-sun-fill'; + } + } + + localStorage.setItem('theme', theme); + } + + toggleTheme() { + const currentTheme = document.documentElement.getAttribute('data-theme'); + const newTheme = currentTheme === 'dark' ? 'light' : 'dark'; + this.setTheme(newTheme); + } + setupEventListeners() { + // Theme toggle button + const themeToggleBtn = document.getElementById('theme-toggle'); + if (themeToggleBtn) { + themeToggleBtn.addEventListener('click', () => this.toggleTheme()); + } + // Global backup all button const backupAllBtn = document.getElementById('backup-all-btn'); if (backupAllBtn) { diff --git a/templates/base.html b/templates/base.html index 5fd1a4c..092700a 100644 --- a/templates/base.html +++ b/templates/base.html @@ -50,9 +50,15 @@ -
+