mirror of
https://github.com/DRYTRIX/TimeTracker.git
synced 2026-05-12 23:39:17 -05:00
77aec94b86
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)
277 lines
14 KiB
HTML
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 %}
|