From 49adf7780843b09373b26f45279a6d7e8cefe451 Mon Sep 17 00:00:00 2001 From: stephan Date: Tue, 30 Dec 2025 12:32:57 +0100 Subject: [PATCH] feat: replace Watchtower theme with OIDC professional design MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove Watchtower theme CSS files (tokens_watchtower.css, theme_watchtower.css) - Add OIDC styles.css (professional IT asset management design) - Add dark mode toggle button (moon/sun icon) - Remove custom CSS in favor of OIDC styles.css - Add dark mode persistence with localStorage - Remove bg-noise div (Watchtower-specific) - Wrap content in .container div for OIDC layout Benefits: - Professional gradient header - Light/Dark mode toggle - Better color palette for readability - Consistent with homelab-service-oidc design 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- CHANGELOG.md | 1 + backend/static/styles.css | 729 +++++++++++++++++++++++++++ backend/static/theme_watchtower.css | 135 ----- backend/static/tokens_watchtower.css | 69 --- backend/templates/index.html | 114 +---- 5 files changed, 754 insertions(+), 294 deletions(-) create mode 100755 backend/static/styles.css delete mode 100644 backend/static/theme_watchtower.css delete mode 100644 backend/static/tokens_watchtower.css diff --git a/CHANGELOG.md b/CHANGELOG.md index f22d4d6..9582fd6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,7 @@ By: Codex (GPT-5) | 30.12.2025 | ⚙️ Code | ID: Update-UI im Web-Frontend implementiert (Status-Anzeige, Check/Apply/Rollback Buttons, Logs-Viewer). By: Claude Sonnet 4.5 | | 30.12.2025 | 🎨 UI | ID: Watchtower Theme implementiert (Sci-Fi Dark Mode mit Neon-Glow, bg-noise, CSS-Variables). By: Claude Sonnet 4.5 | | 30.12.2025 | 🎨 UI | ID: Web-UI modernisiert (Lucide Icons, Metrics Dashboard, User Table mit Badges, Toast Notifications, Loading States, Action Modals). By: Claude Sonnet 4.5 | +| 30.12.2025 | 🎨 UI | ID: Watchtower Theme ersetzt durch OIDC Theme (Professional Design mit Dark Mode Toggle, Light/Dark Theme). By: Claude Sonnet 4.5 | --- ## Legende diff --git a/backend/static/styles.css b/backend/static/styles.css new file mode 100755 index 0000000..881fd21 --- /dev/null +++ b/backend/static/styles.css @@ -0,0 +1,729 @@ +/* Professional IT Asset Management - Best of Both Worlds with Dark Mode */ +:root { + --bg-main: #f5f7fa; + --bg-panel: #ffffff; + --bg-header: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + --text-main: #2d3748; + --text-secondary: #718096; + --text-light: #a0aec0; + --border-main: #e2e8f0; + --primary: #667eea; + --primary-hover: #5568d3; + --success: #48bb78; + --success-hover: #38a169; + --warning: #ed8936; + --danger: #f56565; + --danger-hover: #e53e3e; + --info: #4299e1; + --shadow-sm: 0 1px 3px rgba(0,0,0,0.1); + --shadow-md: 0 4px 6px rgba(0,0,0,0.1); + --shadow-lg: 0 10px 15px rgba(0,0,0,0.1); +} + +/* Dark Mode Theme */ +body.dark-mode { + --bg-main: #1a202c; + --bg-panel: #2d3748; + --bg-header: linear-gradient(135deg, #4c51bf 0%, #6b46c1 100%); + --text-main: #f7fafc; + --text-secondary: #cbd5e0; + --text-light: #a0aec0; + --border-main: #4a5568; + --shadow-sm: 0 1px 3px rgba(0,0,0,0.3); + --shadow-md: 0 4px 6px rgba(0,0,0,0.3); + --shadow-lg: 0 10px 15px rgba(0,0,0,0.3); +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; + background-color: var(--bg-main); + color: var(--text-main); + line-height: 1.6; + transition: background-color 0.3s ease, color 0.3s ease; +} + +/* Theme Toggle Button */ +.theme-toggle { + position: fixed; + bottom: 30px; + right: 30px; + width: 56px; + height: 56px; + border-radius: 50%; + background: var(--primary); + border: none; + cursor: pointer; + box-shadow: var(--shadow-lg); + display: flex; + align-items: center; + justify-content: center; + transition: all 0.3s ease; + z-index: 999; +} + +.theme-toggle:hover { + transform: scale(1.1); + box-shadow: 0 8px 20px rgba(102, 126, 234, 0.4); +} + +.theme-toggle svg { + width: 24px; + height: 24px; + stroke: white; + fill: none; +} + +.theme-toggle .sun-icon { + display: none; +} + +body.dark-mode .theme-toggle .moon-icon { + display: none; +} + +body.dark-mode .theme-toggle .sun-icon { + display: block; +} + +.container { + max-width: 1400px; + margin: 0 auto; + padding: 20px; +} + +/* Header - Keep the gradient but more professional */ +header { + background: var(--bg-header); + color: white; + padding: 32px; + border-radius: 12px; + margin-bottom: 30px; + box-shadow: var(--shadow-md); +} + +header h1 { + font-size: 2rem; + font-weight: 700; + margin-bottom: 8px; +} + +header p { + font-size: 1rem; + opacity: 0.95; + font-weight: 400; +} + +/* Statistics Dashboard - More visual interest */ +.stats { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); + gap: 20px; + margin-bottom: 30px; +} + +.stat-card { + background: var(--bg-panel); + padding: 24px; + border-radius: 12px; + box-shadow: var(--shadow-md); + border-left: 4px solid var(--primary); + transition: all 0.3s ease; + position: relative; + overflow: hidden; +} + +.stat-card::before { + content: ''; + position: absolute; + top: 0; + right: 0; + width: 100px; + height: 100px; + background: var(--primary); + opacity: 0.05; + border-radius: 50%; + transform: translate(30%, -30%); +} + +.stat-card:hover { + transform: translateY(-4px); + box-shadow: var(--shadow-lg); + border-left-width: 6px; +} + +.stat-card h3 { + font-size: 0.875rem; + color: var(--text-secondary); + margin-bottom: 8px; + text-transform: uppercase; + font-weight: 600; + letter-spacing: 0.5px; +} + +.stat-card .value { + font-size: 2.5rem; + font-weight: 700; + color: var(--primary); + position: relative; + z-index: 1; +} + +/* Controls - Clean but distinctive */ +.controls { + background: var(--bg-panel); + padding: 20px; + border-radius: 12px; + margin-bottom: 20px; + box-shadow: var(--shadow-sm); + display: flex; + gap: 12px; + flex-wrap: wrap; + align-items: center; + border: 1px solid var(--border-main); +} + +/* Inputs - More refined */ +input, select { + padding: 11px 14px; + border: 2px solid var(--border-main); + border-radius: 8px; + font-size: 0.9rem; + background: var(--bg-panel); + color: var(--text-main); + transition: all 0.2s ease; + font-family: inherit; +} + +input:focus, select:focus { + outline: none; + border-color: var(--primary); + box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1); +} + +input::placeholder { + color: var(--text-light); +} + +.search-box { + flex: 1; + min-width: 250px; +} + +/* Buttons - Keep gradient feel but professional */ +button { + background: var(--primary); + color: white; + border: none; + padding: 11px 20px; + border-radius: 8px; + cursor: pointer; + font-weight: 600; + font-size: 0.875rem; + transition: all 0.2s ease; + box-shadow: var(--shadow-sm); +} + +button:hover { + background: var(--primary-hover); + transform: translateY(-1px); + box-shadow: var(--shadow-md); +} + +button:active { + transform: translateY(0); +} + +button.secondary { + background: var(--success); +} + +button.secondary:hover { + background: var(--success-hover); +} + +button.danger { + background: var(--danger); +} + +button.danger:hover { + background: var(--danger-hover); +} + +/* Table Container - More polished */ +.table-container { + background: var(--bg-panel); + border-radius: 12px; + box-shadow: var(--shadow-md); + overflow: hidden; + border: 1px solid var(--border-main); +} + +table { + width: 100%; + border-collapse: collapse; +} + +th, td { + padding: 16px; + text-align: left; +} + +th { + background: var(--bg-main); + font-weight: 600; + color: var(--text-main); + font-size: 0.875rem; + text-transform: uppercase; + letter-spacing: 0.5px; + border-bottom: 2px solid var(--border-main); +} + +tbody tr { + border-bottom: 1px solid var(--border-main); + transition: background-color 0.15s ease; +} + +tbody tr:last-child { + border-bottom: none; +} + +tbody tr:hover { + background: var(--bg-main); +} + +td { + color: var(--text-main); +} + +td strong { + color: var(--primary); + font-weight: 600; +} + +/* Status Badges - More colorful but professional */ +.status-badge { + display: inline-block; + padding: 5px 12px; + border-radius: 20px; + font-size: 0.75rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.status-available { + background: linear-gradient(135deg, #c6f6d5 0%, #9ae6b4 100%); + color: #22543d; +} + +.status-in_use { + background: linear-gradient(135deg, #bee3f8 0%, #90cdf4 100%); + color: #2c5282; +} + +.status-maintenance { + background: linear-gradient(135deg, #feebc8 0%, #fbd38d 100%); + color: #7c2d12; +} + +.status-retired { + background: linear-gradient(135deg, #e2e8f0 0%, #cbd5e0 100%); + color: #2d3748; +} + +.status-lost { + background: linear-gradient(135deg, #fed7d7 0%, #fc8181 100%); + color: #742a2a; +} + +/* Modal - Elegant overlay */ +.modal { + display: none; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.6); + backdrop-filter: blur(4px); + align-items: center; + justify-content: center; + z-index: 1000; + animation: fadeIn 0.2s ease; +} + +.modal.active { + display: flex; +} + +@keyframes fadeIn { + from { opacity: 0; } + to { opacity: 1; } +} + +.modal-content { + background: var(--bg-panel); + border-radius: 16px; + padding: 32px; + max-width: 600px; + width: 90%; + max-height: 90vh; + overflow-y: auto; + box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); + animation: slideUp 0.3s ease; +} + +@keyframes slideUp { + from { + opacity: 0; + transform: translateY(20px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.modal-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 24px; + padding-bottom: 16px; + border-bottom: 2px solid var(--border-main); +} + +.modal-header h2 { + color: var(--text-main); + font-size: 1.5rem; + font-weight: 700; +} + +.close-btn { + background: var(--border-main); + border: none; + font-size: 1.5rem; + cursor: pointer; + color: var(--text-secondary); + padding: 0; + width: 36px; + height: 36px; + line-height: 1; + border-radius: 8px; + transition: all 0.2s ease; +} + +.close-btn:hover { + background: var(--primary); + color: white; + transform: rotate(90deg); +} + +/* Form - Clean and accessible */ +.form-group { + margin-bottom: 20px; +} + +.form-group label { + display: block; + margin-bottom: 6px; + font-weight: 600; + color: var(--text-main); + font-size: 0.875rem; +} + +.form-group input, +.form-group select, +.form-group textarea { + width: 100%; +} + +textarea { + resize: vertical; + min-height: 80px; + font-family: inherit; + padding: 11px 14px; + border: 2px solid var(--border-main); + border-radius: 8px; + font-size: 0.9rem; + background: var(--bg-panel); + color: var(--text-main); + transition: all 0.2s ease; +} + +textarea:focus { + outline: none; + border-color: var(--primary); + box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1); +} + +.form-actions { + display: flex; + gap: 12px; + justify-content: flex-end; + margin-top: 28px; + padding-top: 20px; + border-top: 2px solid var(--border-main); +} + +/* Loading & Empty States */ +.loading, .empty-state { + text-align: center; + padding: 60px 20px; + color: var(--text-secondary); +} + +.empty-state svg { + width: 80px; + height: 80px; + margin-bottom: 20px; + opacity: 0.4; + stroke: var(--text-secondary); +} + +.empty-state h3 { + color: var(--text-main); + margin-bottom: 8px; + font-weight: 600; +} + +/* Action Buttons */ +.action-buttons { + display: flex; + gap: 8px; +} + +.action-buttons button { + padding: 7px 14px; + font-size: 0.8rem; +} + +/* Import Zone - Visual and inviting */ +.import-zone { + border: 3px dashed var(--border-main); + border-radius: 12px; + padding: 48px; + text-align: center; + margin: 20px 0; + transition: all 0.3s ease; + cursor: pointer; + background: var(--bg-main); +} + +.import-zone:hover, .import-zone.drag-over { + border-color: var(--primary); + background: var(--bg-panel); + transform: scale(1.02); + box-shadow: 0 0 20px rgba(102, 126, 234, 0.2); +} + +.import-zone.processing { + border-color: var(--success); + background: linear-gradient(135deg, #c6f6d5 0%, #9ae6b4 100%); +} + +.import-zone svg { + width: 64px; + height: 64px; + margin-bottom: 16px; + stroke: var(--primary); +} + +.import-zone h3 { + color: var(--text-main); + font-weight: 600; + margin-bottom: 8px; + font-size: 1.125rem; +} + +.import-zone p { + color: var(--text-secondary); + font-size: 0.9rem; +} + +.file-input { + display: none; +} + +/* Import Results */ +.import-results { + margin-top: 20px; + padding: 16px; + border-radius: 8px; + border-left: 4px solid; + box-shadow: var(--shadow-sm); +} + +.import-results.success { + background: #c6f6d5; + border-color: var(--success); + color: #22543d; +} + +.import-results.error { + background: #fed7d7; + border-color: var(--danger); + color: #742a2a; +} + +/* Progress Bar - More visual */ +.progress-bar { + width: 100%; + height: 10px; + background: var(--border-main); + border-radius: 8px; + overflow: hidden; + margin: 12px 0; +} + +.progress-fill { + height: 100%; + background: linear-gradient(90deg, var(--primary) 0%, var(--info) 100%); + transition: width 0.3s ease; + box-shadow: 0 0 10px rgba(102, 126, 234, 0.5); +} + +/* Mobile Responsive */ +@media (max-width: 768px) { + .container { + padding: 12px; + } + + header { + padding: 24px 20px; + } + + header h1 { + font-size: 1.5rem; + } + + .stats { + grid-template-columns: 1fr; + gap: 12px; + } + + .controls { + flex-direction: column; + padding: 16px; + } + + .search-box { + width: 100%; + } + + button { + width: 100%; + } + + .theme-toggle { + bottom: 20px; + right: 20px; + width: 48px; + height: 48px; + } + + table { + font-size: 0.85rem; + } + + th, td { + padding: 12px 8px; + } + + .action-buttons { + flex-direction: column; + } + + .action-buttons button { + width: 100%; + } + + .modal-content { + padding: 24px 20px; + } +} + +/* NEW STYLES FOR ENHANCED UI */ + +.error-card { + background-color: var(--danger); + color: white; + border-left-color: var(--danger-hover); +} + +.error-card h3 { + color: white; +} + +.loading-card { + text-align: center; +} + +.spinner { + border: 4px solid rgba(0, 0, 0, 0.1); + border-left-color: var(--primary); + border-radius: 50%; + width: 40px; + height: 40px; + animation: spin 1s linear infinite; + margin: 20px auto; +} + +@keyframes spin { + to { + transform: rotate(360deg); + } +} + +.stat-card details { + margin-top: 16px; +} + +.stat-card summary { + cursor: pointer; + font-weight: 600; + color: var(--text-main); + margin-bottom: 8px; +} + +.stat-card summary:hover { + color: var(--primary); +} + +.flags-list { + list-style-type: none; + padding-left: 8px; + font-size: 0.9rem; +} + +.flags-list li { + margin-bottom: 6px; + padding-left: 16px; + position: relative; +} + +.flags-list li::before { + content: '›'; + position: absolute; + left: 0; + color: var(--primary); + font-weight: bold; +} + +.value.level-high { + color: var(--danger); +} + +.value.level-medium { + color: var(--warning); +} + +.value.level-low { + color: var(--success); +} diff --git a/backend/static/theme_watchtower.css b/backend/static/theme_watchtower.css deleted file mode 100644 index 6e92698..0000000 --- a/backend/static/theme_watchtower.css +++ /dev/null @@ -1,135 +0,0 @@ -/* - wlkns-dev-standards - Component Theme: Watchtower - Adapts standard components to the Sci-Fi/Dark aesthetic. -*/ - -/* ========================= - Inputs & Forms - ========================= */ -input, select, textarea { - background: rgba(10, 14, 20, 0.6) !important; - border-color: rgba(255, 255, 255, 0.15) !important; - color: var(--color-text) !important; - transition: all 0.2s ease; -} - -input:focus, select:focus, textarea:focus { - border-color: var(--color-accent) !important; - box-shadow: 0 0 15px var(--wt-glow); -} - -/* ========================= - Buttons - ========================= */ -.button { - text-transform: uppercase; - letter-spacing: 0.05em; - font-weight: 600; - border-radius: 999px !important; /* Pill shape */ -} - -.button.primary { - box-shadow: 0 0 15px var(--wt-glow); - color: #081015; /* Dark text on bright accent */ -} - -.button.secondary { - background: transparent; - border: 1px solid rgba(255, 255, 255, 0.2); - color: var(--color-text); -} - -.button.secondary:hover { - border-color: var(--color-text); - background: rgba(255,255,255,0.05); -} - -.button.danger { - box-shadow: 0 0 10px rgba(255, 94, 94, 0.4); - color: #1b0d0d; -} - -/* ========================= - Tables - ========================= */ -table { - border-collapse: separate; - border-spacing: 0 4px; /* Space between rows */ -} - -thead th { - border-bottom: 1px solid var(--color-accent); - text-transform: uppercase; - letter-spacing: 0.1em; - font-size: 0.75rem; - color: var(--color-accent); - background: transparent; -} - -tbody tr { - background: rgba(255, 255, 255, 0.03); - transition: transform 0.2s; -} - -tbody tr:hover { - background: rgba(255, 255, 255, 0.06); - transform: scale(1.01); -} - -tbody td { - border: none; -} - -tbody td:first-child { - border-top-left-radius: 8px; - border-bottom-left-radius: 8px; -} - -tbody td:last-child { - border-top-right-radius: 8px; - border-bottom-right-radius: 8px; -} - -/* ========================= - Alerts & Toasts - ========================= */ -.alert, .toast { - background: rgba(10, 14, 20, 0.95); - backdrop-filter: blur(4px); - border: 1px solid rgba(255, 255, 255, 0.1); - box-shadow: 0 10px 30px rgba(0,0,0,0.5); -} - -.alert.success, .toast.success { - border-color: var(--color-success); - box-shadow: 0 0 10px rgba(0, 224, 143, 0.2); - color: var(--color-text); -} - -.alert.warning, .toast.warning { - border-color: var(--color-warning); - color: var(--color-text); -} - -.alert.error, .toast.error { - border-color: var(--color-error); - box-shadow: 0 0 10px rgba(255, 94, 94, 0.2); - color: var(--color-text); -} - -/* ========================= - Pagination - ========================= */ -.pagination .page { - background: transparent; - border: 1px solid rgba(255,255,255,0.1); - color: var(--color-text-muted); -} - -.pagination .page.active { - background: var(--color-accent); - color: #081015; - border-color: var(--color-accent); - box-shadow: 0 0 10px var(--wt-glow); -} diff --git a/backend/static/tokens_watchtower.css b/backend/static/tokens_watchtower.css deleted file mode 100644 index a672d06..0000000 --- a/backend/static/tokens_watchtower.css +++ /dev/null @@ -1,69 +0,0 @@ -/* - wlkns-dev-standards - Theme: Watchtower (Dark Glow) - Version: v1.0 - Based on: minecraft-watchtower UI -*/ - -:root { - /* ========================= - Core Palette (Watchtower) - ========================= */ - --wt-bg-dark: #0b0f14; - --wt-bg-panel: #10151d; - --wt-accent: #00e08f; - --wt-warning: #ffb454; - --wt-danger: #ff5e5e; - --wt-text: #e6edf5; - --wt-text-dim: #9aa7b8; - --wt-glow: rgba(0, 224, 143, 0.35); - - /* ========================= - Mapping -> Standard Tokens - ========================= */ - - /* Farben – Identity & Status */ - --color-primary: #1a2230; /* Deep Blue/Grey from Gradient */ - --color-accent: var(--wt-accent); - - --color-success: var(--wt-accent); /* Watchtower uses accent as success */ - --color-warning: var(--wt-warning); - --color-error: var(--wt-danger); - - /* Farben – Neutrals (Dark Mode Override) */ - --color-bg: var(--wt-bg-dark); - --color-surface: var(--wt-bg-panel); - --color-border: rgba(255, 255, 255, 0.08); /* Subtle white border */ - - --color-text: var(--wt-text); - --color-text-muted: var(--wt-text-dim); - - /* Typografie (Optional: Falls Space Grotesk geladen wird) */ - --font-ui: 'Space Grotesk', 'Inter', system-ui, sans-serif; - --font-mono: 'JetBrains Mono', monospace; - - /* Radius & Spacing */ - --radius-sm: 4px; - --radius-md: 12px; /* Watchtower uses larger radii */ - --radius-lg: 16px; - - /* Shadows becomes Glows in this theme */ - --shadow-sm: 0 0 10px rgba(0,0,0,0.5); - --shadow-glow: 0 0 15px var(--wt-glow); -} - -/* Global Theme Overrides */ -body { - background: radial-gradient(circle at top, #1a2230 0%, #0b0f14 55%), linear-gradient(135deg, #0b0f14, #101623 60%); - min-height: 100vh; -} - -/* Noise Texture helper class */ -.bg-noise { - position: fixed; - inset: 0; - pointer-events: none; - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160' viewBox='0 0 160 160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)' opacity='0.04'/%3E%3C/svg%3E"); - mix-blend-mode: soft-light; - z-index: -1; -} diff --git a/backend/templates/index.html b/backend/templates/index.html index 4c83a7a..f2c42ad 100644 --- a/backend/templates/index.html +++ b/backend/templates/index.html @@ -4,99 +4,21 @@ Safe Kiddo Control - - + - -
+ + + +
@@ -678,6 +600,18 @@ // Initialize checkSession(); checkOidcStatus(); + + // Dark Mode Toggle + function toggleTheme() { + document.body.classList.toggle('dark-mode'); + localStorage.setItem('darkMode', document.body.classList.contains('dark-mode')); + } + + // Load dark mode preference + if (localStorage.getItem('darkMode') === 'true') { + document.body.classList.add('dark-mode'); + } +