Files
TimeTracker/templates/admin/dashboard.html
T
Dries Peeters 77aec94b86 feat: Add project costs tracking and remove license server integration
Major Features:
- Add project costs feature with full CRUD operations
- Implement toast notification system for better user feedback
- Enhance analytics dashboard with improved visualizations
- Add OIDC authentication improvements and debug tools

Improvements:
- Enhance reports with new filtering and export capabilities
- Update command palette with additional shortcuts
- Improve mobile responsiveness across all pages
- Refactor UI components for consistency

Removals:
- Remove license server integration and related dependencies
- Clean up unused license-related templates and utilities

Technical Changes:
- Add new migration 018 for project_costs table
- Update models: Project, Settings, User with new relationships
- Refactor routes: admin, analytics, auth, invoices, projects, reports
- Update static assets: CSS improvements, new JS modules
- Enhance templates: analytics, admin, projects, reports

Documentation:
- Add comprehensive documentation for project costs feature
- Document toast notification system with visual guides
- Update README with new feature descriptions
- Add migration instructions and quick start guides
- Document OIDC improvements and Kanban enhancements

Files Changed:
- Modified: 56 files (core app, models, routes, templates, static assets)
- Deleted: 6 files (license server integration)
- Added: 28 files (new features, documentation, migrations)
2025-10-09 11:50:26 +02:00

277 lines
14 KiB
HTML

{% extends "base.html" %}
{% block title %}{{ _('Admin Dashboard') }} - {{ app_name }}{% endblock %}
{% block content %}
<div class="container-fluid">
{% from "_components.html" import page_header %}
<div class="row">
<div class="col-12">
{% set actions %}
<a href="{{ url_for('admin.system_info') }}" class="btn btn-outline-primary me-2">
<i class="fas fa-info-circle me-2"></i>{{ _('System Info') }}
</a>
<a href="{{ url_for('admin.backup') }}" class="btn btn-outline-primary me-2">
<i class="fas fa-download me-2"></i>{{ _('Create Backup') }}
</a>
<a href="{{ url_for('admin.restore') }}" class="btn btn-outline-primary me-2">
<i class="fas fa-undo-alt me-2"></i>{{ _('Restore') }}
</a>
<a href="{{ url_for('admin.oidc_debug') }}" class="btn btn-outline-warning">
<i class="fas fa-shield-alt me-2"></i>{{ _('OIDC Debug') }}
</a>
{% endset %}
{{ page_header('fas fa-cogs', _('Admin Dashboard'), _('Manage users, system settings, and core operations at a glance.'), actions) }}
</div>
</div>
<!-- System Statistics -->
<div class="row section-spacing">
<div class="col-md-3 col-sm-6 mb-3">
{% from "_components.html" import summary_card %}
{{ summary_card('fas fa-users', 'primary', 'Total Users', stats.total_users) }}
</div>
<div class="col-md-3 col-sm-6 mb-3">
{{ summary_card('fas fa-project-diagram', 'success', 'Total Projects', stats.total_projects) }}
</div>
<div class="col-md-3 col-sm-6 mb-3">
{{ summary_card('fas fa-clock', 'info', 'Time Entries', stats.total_entries) }}
</div>
<div class="col-md-3 col-sm-6 mb-3">
{{ summary_card('fas fa-stopwatch', 'warning', 'Total Hours', "%.1f"|format(stats.total_hours) ~ 'h') }}
</div>
</div>
<!-- OIDC Authentication Status -->
<div class="row section-spacing">
<div class="col-12 mb-4">
<div class="card border-{% if oidc_enabled %}success{% else %}secondary{% endif %} hover-lift">
<div class="card-header bg-{% if oidc_enabled %}success{% else %}light{% endif %} text-{% if oidc_enabled %}white{% else %}dark{% endif %}">
<div class="d-flex justify-content-between align-items-center">
<h5 class="mb-0 d-flex align-items-center">
<i class="fas fa-shield-alt me-2"></i>{{ _('OIDC Authentication Status') }}
</h5>
<a href="{{ url_for('admin.oidc_debug') }}" class="btn btn-sm {% if oidc_enabled %}btn-light{% else %}btn-outline-secondary{% endif %}">
<i class="fas fa-tools me-2"></i>{{ _('Debug Dashboard') }}
</a>
</div>
</div>
<div class="card-body">
<div class="row align-items-center">
<div class="col-md-3 text-center mb-3 mb-md-0">
<div class="d-flex flex-column align-items-center">
{% if oidc_enabled %}
<div class="mb-2">
<i class="fas fa-check-circle fa-3x text-success"></i>
</div>
<h4 class="mb-0 text-success">{{ _('ENABLED') }}</h4>
{% else %}
<div class="mb-2">
<i class="fas fa-times-circle fa-3x text-secondary"></i>
</div>
<h4 class="mb-0 text-secondary">{{ _('DISABLED') }}</h4>
{% endif %}
<small class="text-muted mt-1">{{ _('OIDC SSO') }}</small>
</div>
</div>
<div class="col-md-6">
<table class="table table-sm mb-0">
<tbody>
<tr>
<th width="40%">{{ _('Auth Method') }}:</th>
<td>
<code class="{% if oidc_enabled %}text-success{% else %}text-muted{% endif %}">
{{ oidc_auth_method|upper }}
</code>
</td>
</tr>
<tr>
<th>{{ _('Configuration') }}:</th>
<td>
{% if oidc_configured %}
<span class="badge bg-success">
<i class="fas fa-check me-1"></i>{{ _('Complete') }}
</span>
{% elif oidc_enabled %}
<span class="badge bg-warning">
<i class="fas fa-exclamation-triangle me-1"></i>{{ _('Incomplete') }}
</span>
{% else %}
<span class="badge bg-secondary">
<i class="fas fa-minus me-1"></i>{{ _('Not configured') }}
</span>
{% endif %}
</td>
</tr>
<tr>
<th>{{ _('OIDC Users') }}:</th>
<td>
<strong>{{ oidc_users_count }}</strong> {{ _('user(s)') }}
</td>
</tr>
</tbody>
</table>
</div>
<div class="col-md-3 text-center">
<div class="d-grid gap-2">
{% if oidc_enabled %}
<a href="{{ url_for('admin.oidc_test') }}" class="btn btn-success btn-sm">
<i class="fas fa-vial me-2"></i>{{ _('Test Config') }}
</a>
<a href="{{ url_for('admin.oidc_debug') }}" class="btn btn-outline-success btn-sm">
<i class="fas fa-cog me-2"></i>{{ _('View Details') }}
</a>
{% else %}
<small class="text-muted">
{{ _('Set AUTH_METHOD=oidc to enable') }}
</small>
<a href="{{ url_for('admin.oidc_debug') }}" class="btn btn-outline-secondary btn-sm">
<i class="fas fa-book me-2"></i>{{ _('Setup Guide') }}
</a>
{% endif %}
</div>
</div>
</div>
{% if oidc_enabled and not oidc_configured %}
<div class="alert alert-warning mt-3 mb-0">
<i class="fas fa-exclamation-triangle me-2"></i>
<strong>{{ _('Configuration Incomplete:') }}</strong>
{{ _('OIDC is enabled but missing required environment variables. Check the') }}
<a href="{{ url_for('admin.oidc_debug') }}" class="alert-link">{{ _('Debug Dashboard') }}</a>
{{ _('for details.') }}
</div>
{% endif %}
</div>
</div>
</div>
</div>
<!-- Quick Actions -->
<div class="row section-spacing">
<div class="col-md-6 mb-4">
<div class="card hover-lift">
<div class="card-header">
<h5 class="mb-0 d-flex align-items-center">
<i class="fas fa-user-cog me-2 text-primary"></i>{{ _('User Management') }}
</h5>
</div>
<div class="card-body">
<div class="d-grid gap-2">
<a href="{{ url_for('admin.list_users') }}" class="btn btn-primary">
<i class="fas fa-users me-2"></i>{{ _('Manage Users') }}
</a>
<a href="{{ url_for('admin.create_user') }}" class="btn btn-success">
<i class="fas fa-user-plus me-2"></i>{{ _('Create New User') }}
</a>
</div>
</div>
</div>
</div>
<div class="col-md-6 mb-4">
<div class="card hover-lift">
<div class="card-header">
<h5 class="mb-0 d-flex align-items-center">
<i class="fas fa-cog me-2 text-primary"></i>{{ _('System Settings') }}
</h5>
</div>
<div class="card-body">
<div class="d-grid gap-2">
<a href="{{ url_for('admin.settings') }}" class="btn btn-outline-primary">
<i class="fas fa-sliders-h me-2"></i>{{ _('Configure Settings') }}
</a>
<a href="{{ url_for('admin.backup') }}" class="btn btn-outline-primary">
<i class="fas fa-download me-2"></i>{{ _('Create Backup') }}
</a>
</div>
</div>
</div>
</div>
</div>
<!-- Recent Activity -->
<div class="row">
<div class="col-md-8 mb-4">
<div class="card hover-lift">
<div class="card-header">
<h5 class="mb-0 d-flex align-items-center">
<i class="fas fa-history me-2 text-primary"></i>{{ _('Recent Activity') }}
</h5>
</div>
<div class="card-body p-0">
{% if recent_entries %}
<div class="table-responsive">
<table class="table table-hover mb-0">
<thead>
<tr>
<th>{{ _('User') }}</th>
<th>{{ _('Project') }}</th>
<th>{{ _('Date') }}</th>
<th>{{ _('Duration') }}</th>
<th>{{ _('Status') }}</th>
</tr>
</thead>
<tbody>
{% for entry in recent_entries %}
<tr>
<td>{{ entry.user.display_name }}</td>
<td>
<a href="{{ url_for('projects.view_project', project_id=entry.project.id) }}">
{{ entry.project.name }}
</a>
</td>
<td>{{ entry.start_time.strftime('%Y-%m-%d %H:%M') }}</td>
<td>{{ entry.duration_formatted }}</td>
<td>
{% if entry.end_time %}
<span class="badge bg-success">{{ _('Completed') }}</span>
{% else %}
<span class="badge bg-warning">{{ _('Running') }}</span>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="text-center py-5">
<div class="empty-state">
<i class="fas fa-clock fa-3x text-muted mb-3"></i>
<h5 class="text-muted">{{ _('No recent activity') }}</h5>
<p class="text-muted mb-0">{{ _('No time entries have been recorded recently.') }}</p>
</div>
</div>
{% endif %}
</div>
</div>
</div>
<div class="col-md-4">
<div class="card shadow-sm border-0 mt-3">
<div class="card-header bg-white py-3">
<h6 class="m-0 font-weight-bold text-primary">
<i class="fas fa-tools me-2"></i>{{ _('Quick Actions') }}
</h6>
</div>
<div class="card-body">
<div class="d-grid gap-2">
<a href="{{ url_for('projects.create_project') }}" class="btn btn-outline-primary">
<i class="fas fa-plus"></i> {{ _('New Project') }}
</a>
<a href="{{ url_for('reports.reports') }}" class="btn btn-outline-info">
<i class="fas fa-chart-line"></i> {{ _('View Reports') }}
</a>
<a href="{{ url_for('admin.system_info') }}" class="btn btn-outline-secondary">
<i class="fas fa-info-circle"></i> {{ _('System Info') }}
</a>
<a href="{{ url_for('admin.oidc_debug') }}" class="btn btn-outline-warning">
<i class="fas fa-shield-alt"></i> {{ _('OIDC Debug') }}
</a>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}