diff --git a/CHANGELOG.md b/CHANGELOG.md index bdfe733..887959a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -ID: DOC_000001 | Version: 0.1.1 | Status: Final +ID: DOC_000001 | Version: 0.1.2 | Status: Final By: Codex (GPT-5) # Projekt-Logbuch (Changelog) @@ -43,6 +43,7 @@ By: Codex (GPT-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 | | 30.12.2025 | 🎨 UI | ID: TASK_000035 Login-Landing und Versionsanzeige im Header der Web-UI. By: Codex (GPT-5) | +| 30.12.2025 | 🎨 UI | ID: TASK_000036 Login-/Dashboard-URLs und Redirect nach Login. By: Codex (GPT-5) | --- ## Legende diff --git a/VERSION b/VERSION index 17e51c3..d917d3e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.1 +0.1.2 diff --git a/backend/app.py b/backend/app.py index 893c39f..ad2c197 100644 --- a/backend/app.py +++ b/backend/app.py @@ -294,5 +294,7 @@ def update_logs(settings: Settings = Depends(get_settings), limit: int = 200) -> @app.get("/", response_class=HTMLResponse) +@app.get("/login", response_class=HTMLResponse) +@app.get("/dashboard", response_class=HTMLResponse) def index(request: Request) -> HTMLResponse: return templates.TemplateResponse("index.html", {"request": request}) diff --git a/backend/templates/index.html b/backend/templates/index.html index a027b10..f209bc3 100644 --- a/backend/templates/index.html +++ b/backend/templates/index.html @@ -286,19 +286,25 @@ } // Authentication + function setPath(path) { + if (window.location.pathname !== path) { + history.replaceState(null, '', path); + } + } + function showLanding() { document.getElementById('loginLanding').classList.remove('hidden'); document.getElementById('appContainer').classList.add('hidden'); document.getElementById('headerVersion').textContent = 'v-'; - history.replaceState(null, '', '#login'); + if (window.location.pathname !== '/login') { + setPath('/login'); + } } function showApp() { document.getElementById('loginLanding').classList.add('hidden'); document.getElementById('appContainer').classList.remove('hidden'); - if (window.location.hash !== '#app') { - history.replaceState(null, '', '#app'); - } + setPath('/dashboard'); } async function checkSession() { diff --git a/project-management/feedback/static/styles.css b/project-management/feedback/static/styles.css new file mode 100755 index 0000000..881fd21 --- /dev/null +++ b/project-management/feedback/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/project-management/feedback/templates/admin/clients.html b/project-management/feedback/templates/admin/clients.html new file mode 100644 index 0000000..bf547d0 --- /dev/null +++ b/project-management/feedback/templates/admin/clients.html @@ -0,0 +1,91 @@ + + + + + + Client Administration + + + + +
+
+

OIDC Clients

+

Manage OIDC clients - Logged in as: {{ admin_user.username }}

+
+ {% if message %} +
+ {{ message }} +
+ {% endif %} +
+ + + + + + + + + +
+
+ + + + + + + + + + + + + {% for client in clients %} + + + + + + + + + {% endfor %} + +
IDClient IDClient NameRedirect URIsAllowed ScopesActions
{{ client.id }}{{ client.client_id }}{{ client.client_name }} +
    + {% for uri in client.get_redirect_uris() %} +
  • {{ uri }}
  • + {% endfor %} +
+
{{ client.get_allowed_scopes()|join(', ') }} +
+ + + +
+ +
+
+
+
+
+ + + diff --git a/project-management/feedback/templates/admin/create_client.html b/project-management/feedback/templates/admin/create_client.html new file mode 100644 index 0000000..59d9780 --- /dev/null +++ b/project-management/feedback/templates/admin/create_client.html @@ -0,0 +1,80 @@ + + + + + + Create New Client + + + + + +
+
+

Create New OIDC Client

+

Add a new client application to the system

+
+ + +
+ + + + diff --git a/project-management/feedback/templates/admin/create_user.html b/project-management/feedback/templates/admin/create_user.html new file mode 100644 index 0000000..cb2fa66 --- /dev/null +++ b/project-management/feedback/templates/admin/create_user.html @@ -0,0 +1,106 @@ + + + + + + Create New User + + + + + +
+
+

Create New User

+

Add a new user to the system

+
+ + +
+ + + + diff --git a/project-management/feedback/templates/admin/dashboard.html b/project-management/feedback/templates/admin/dashboard.html new file mode 100644 index 0000000..34129b7 --- /dev/null +++ b/project-management/feedback/templates/admin/dashboard.html @@ -0,0 +1,158 @@ + + + + + + User Administration + + + + + +
+
+

User Administration

+

Manage OIDC users - Logged in as: {{ admin_user.username }}

+
+ + {% if message %} +
+ {{ message }} +
+ {% endif %} + +
+
+

Total Users

+
{{ total_users }}
+
+
+

Active Users

+
{{ active_users }}
+
+
+

Admin Users

+
{{ admin_users }}
+
+
+

Inactive Users

+
{{ inactive_users }}
+
+
+ +
+ + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + + + {% for user in users %} + + + + + + + + + + + + {% endfor %} + +
IDUsernameNameEmailStatusRolePermissionsCreatedActions
{{ user.id }}{{ user.username }}{{ user.name }}{{ user.email }} + {% if user.is_active %} + Active + {% else %} + Inactive + {% endif %} + + {% if user.role == 'admin' %} + {{ user.role|capitalize }} + {% elif user.role == 'moderator' %} + {{ user.role|capitalize }} + {% elif user.role == 'readonly' %} + {{ user.role|capitalize }} + {% else %} + {{ user.role|capitalize }} + {% endif %} + + {% if user.get_permissions()|length > 0 %} + {{ user.get_permissions()|join(', ') }} + {% else %} + None + {% endif %} + {{ user.created_at.strftime('%Y-%m-%d %H:%M') }} +
+ + + + + {% if user.is_active %} +
+ +
+ {% else %} +
+ +
+ {% endif %} + + {% if not user.is_admin or admin_count > 1 %} +
+ +
+ {% endif %} + + + + +
+
+
+
+ + + + diff --git a/project-management/feedback/templates/admin/edit_client.html b/project-management/feedback/templates/admin/edit_client.html new file mode 100644 index 0000000..03523e4 --- /dev/null +++ b/project-management/feedback/templates/admin/edit_client.html @@ -0,0 +1,79 @@ + + + + + + Edit Client - {{ client.client_name }} + + + + + +
+
+

Edit OIDC Client

+

Modify details for client: {{ client.client_name }}

+
+ + +
+ + + + diff --git a/project-management/feedback/templates/admin/edit_user.html b/project-management/feedback/templates/admin/edit_user.html new file mode 100644 index 0000000..730fca5 --- /dev/null +++ b/project-management/feedback/templates/admin/edit_user.html @@ -0,0 +1,106 @@ + + + + + + Edit User - {{ user.username }} + + + + + +
+
+

Edit User

+

Modify user details for: {{ user.username }}

+
+ + +
+ + + + diff --git a/project-management/feedback/templates/admin/login.html b/project-management/feedback/templates/admin/login.html new file mode 100644 index 0000000..b73d478 --- /dev/null +++ b/project-management/feedback/templates/admin/login.html @@ -0,0 +1,63 @@ + + + + + + Admin Login + + + + + +
+
+

Admin Login

+

User Administration Access

+
+ + +
+ + + + diff --git a/project-management/feedback/templates/admin/user_tokens.html b/project-management/feedback/templates/admin/user_tokens.html new file mode 100644 index 0000000..68d197a --- /dev/null +++ b/project-management/feedback/templates/admin/user_tokens.html @@ -0,0 +1,99 @@ + + + + + + User Tokens + + + +
+
+

Access Tokens for {{ target_user.username }}

+

Logged in as admin: {{ admin_user.username }}

+
+ + {% if message %} +
+ {{ message }} +
+ {% endif %} + + {% if new_token %} +
+ New Token (copy now): {{ new_token }} +
+ {% endif %} + + {% if error %} +
+ {{ error }} +
+ {% endif %} + + + +
+ + + + + + + + + + + + {% for token in tokens %} + + + + + + + + {% endfor %} + +
TokenScopeExpiresStatusActions
{{ token.token }}{{ token.scope }}{{ token.expires_at.strftime('%Y-%m-%d') }} + {% if token.revoked %} + Revoked + {% elif token.is_valid %} + Valid + {% else %} + Expired + {% endif %} + +
+ + +
+
+ + + + +
+
+ + +
+
+
+
+ + diff --git a/project-management/feedback/templates/change_password.html b/project-management/feedback/templates/change_password.html new file mode 100644 index 0000000..08ba68b --- /dev/null +++ b/project-management/feedback/templates/change_password.html @@ -0,0 +1,79 @@ + + + + + + OIDC IdP - Change Password + + + + + +
+
+

Change Password

+

Update your account security

+
+ + +
+ + + + diff --git a/project-management/feedback/templates/dashboard.html b/project-management/feedback/templates/dashboard.html new file mode 100644 index 0000000..92b2e49 --- /dev/null +++ b/project-management/feedback/templates/dashboard.html @@ -0,0 +1,86 @@ + + + + + + User Dashboard + + + + + +
+
+

👤 User Dashboard

+

Logged in as: {{ user.username }}

+
+ + + +
+ + + + + + + + + + + + +
+
+ + + + diff --git a/project-management/feedback/templates/index.html b/project-management/feedback/templates/index.html new file mode 100644 index 0000000..3bd5444 --- /dev/null +++ b/project-management/feedback/templates/index.html @@ -0,0 +1,60 @@ + + + + + + OIDC Identity Provider + + + + + +
+
+

🔐 OIDC Identity Provider

+

Secure authentication server for your services

+
+ + + +
+ + + + + + +
+ +
+

+ Administrators: Access admin panel +

+
+
+ + + + diff --git a/project-management/feedback/templates/login.html b/project-management/feedback/templates/login.html new file mode 100644 index 0000000..172747a --- /dev/null +++ b/project-management/feedback/templates/login.html @@ -0,0 +1,71 @@ + + + + + + OIDC IdP - Login + + + + + +
+
+

🔐 Homelab OIDC Login

+

Secure authentication for your homelab services

+
+ + +
+ + + + diff --git a/project-management/feedback/templates/register.html b/project-management/feedback/templates/register.html new file mode 100644 index 0000000..6ccf4e9 --- /dev/null +++ b/project-management/feedback/templates/register.html @@ -0,0 +1,79 @@ + + + + + + OIDC IdP - Registration + + + + + +
+
+

Create New Account

+

Join your homelab authentication system

+
+ + +
+ + + + diff --git a/project-management/feedback/templates/user_analytics.html b/project-management/feedback/templates/user_analytics.html new file mode 100644 index 0000000..4239cab --- /dev/null +++ b/project-management/feedback/templates/user_analytics.html @@ -0,0 +1,103 @@ + + + + + + My Active Sessions + + + + + +
+
+

My Active Sessions

+

{{ user.name }} ({{ user.email }})

+
+ +
+ + + + + + +
+ + +
+
+

Active Sessions

+
{{ summary.total_active_sessions }}
+
Currently active
+
+
+

Applications

+
{{ summary.total_clients }}
+
You're using
+
+
+ + +

Active Sessions

+ + {% if active_sessions %} + {% set current_client = namespace(value='') %} + {% for session in active_sessions %} + {% if session.client_name != current_client.value %} + {% set current_client.value = session.client_name %} + {% if not loop.first %} +
+ {% endif %} +
+

{{ session.client_name }}

+ {% endif %} + +
+
+
+ Session +
+ Active +
+
+ Created: {{ session.created_at.strftime('%Y-%m-%d %H:%M:%S') }} | + Expires: {{ session.expires_at.strftime('%Y-%m-%d %H:%M:%S') }} +
+
+ + {% if loop.last %} +
+ {% endif %} + {% endfor %} + {% else %} +
+ No active sessions. Log in to an application to see sessions here. +
+ {% endif %} + + + + + diff --git a/project-management/feedback/templates/user_tokens.html b/project-management/feedback/templates/user_tokens.html new file mode 100644 index 0000000..e8c4825 --- /dev/null +++ b/project-management/feedback/templates/user_tokens.html @@ -0,0 +1,99 @@ + + + + + + My Tokens + + + +
+
+

Initial Access Tokens

+

Logged in as: {{ user.username }}

+
+ + {% if message %} +
+ {{ message }} +
+ {% endif %} + + {% if new_token %} +
+ New Token (copy now): {{ new_token }} +
+ {% endif %} + + {% if error %} +
+ {{ error }} +
+ {% endif %} + + + +
+ + + + + + + + + + + + {% for token in tokens %} + + + + + + + + {% endfor %} + +
TokenScopeExpiresStatusActions
{{ token.token }}{{ token.scope }}{{ token.expires_at.strftime('%Y-%m-%d') }} + {% if token.revoked %} + Revoked + {% elif token.is_valid %} + Valid + {% else %} + Expired + {% endif %} + +
+ + +
+
+ + + + +
+
+ + +
+
+
+
+ + diff --git a/project-management/requirements/tasks/TASK_000036.md b/project-management/requirements/tasks/TASK_000036.md new file mode 100644 index 0000000..afc58c0 --- /dev/null +++ b/project-management/requirements/tasks/TASK_000036.md @@ -0,0 +1,20 @@ +ID: TASK_000036 | Version: 0.1.2 | Status: Done +By: Codex (GPT-5) + +# TASK_000036: Login-Landing und Dashboard-Redirect + +## Outcome +Beim Aufruf der URL wird eine Login-Landing-Page gezeigt; nach erfolgreicher Anmeldung erfolgt die Weiterleitung auf das Dashboard unter eigener URL. + +## Story-Bezug +US_000022 + +## Beschreibung +- Zusaetzliche Routen `/login` und `/dashboard` liefern die Web-UI. +- Nicht-authentifizierte Zugriffe auf `/dashboard` wechseln auf `/login`. +- Nach erfolgreichem Login wird die URL auf `/dashboard` gesetzt. + +## Definition of Done (DoD) +- Login-Landing ist Standardansicht ohne aktive Session. +- Nach Login wird `/dashboard` angezeigt. +- `/login` und `/dashboard` laden die UI.