mirror of
https://github.com/DRYTRIX/TimeTracker.git
synced 2026-01-20 19:39:59 -06:00
- Clients: add model, routes, and templates
- app/models/client.py
- app/routes/clients.py
- templates/clients/{create,edit,list,view}.html
- docs/CLIENT_MANAGEMENT_README.md
- Database: add enhanced init/verify scripts, migrations, and docs
- docker/{init-database-enhanced.py,start-enhanced.py,verify-database.py}
- docs/ENHANCED_DATABASE_STARTUP.md
- migrations/{add_analytics_column.sql,add_analytics_setting.py,migrate_to_client_model.py}
- Scripts: add version manager and docker network test helpers
- scripts/version-manager.{bat,ps1,py,sh}
- scripts/test-docker-network.{bat,sh}
- docs/VERSION_MANAGEMENT.md
- UI: tweak base stylesheet
- app/static/base.css
- Tests: add client system test
- test_client_system.py
256 lines
11 KiB
HTML
256 lines
11 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}Admin Dashboard - {{ app_name }}{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container-fluid">
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<h1 class="h3 mb-0">
|
|
<i class="fas fa-cogs text-primary"></i> Admin Dashboard
|
|
</h1>
|
|
<div>
|
|
<a href="{{ url_for('admin.system_info') }}" class="btn btn-outline-info">
|
|
<i class="fas fa-info-circle"></i> System Info
|
|
</a>
|
|
<a href="{{ url_for('admin.backup') }}" class="btn btn-outline-warning">
|
|
<i class="fas fa-download"></i> Backup
|
|
</a>
|
|
<a href="{{ url_for('admin.license_status') }}" class="btn btn-outline-info">
|
|
<i class="fas fa-key"></i> License Status
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- System Statistics -->
|
|
<div class="row mb-4">
|
|
<div class="col-md-3">
|
|
<div class="card text-center">
|
|
<div class="card-body">
|
|
<i class="fas fa-users fa-2x text-primary mb-2"></i>
|
|
<h4 class="text-primary">{{ stats.total_users }}</h4>
|
|
<p class="text-muted mb-0">Total Users</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="card text-center">
|
|
<div class="card-body">
|
|
<i class="fas fa-project-diagram fa-2x text-success mb-2"></i>
|
|
<h4 class="text-success">{{ stats.total_projects }}</h4>
|
|
<p class="text-muted mb-0">Total Projects</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="card text-center">
|
|
<div class="card-body">
|
|
<i class="fas fa-clock fa-2x text-info mb-2"></i>
|
|
<h4 class="text-info">{{ stats.total_entries }}</h4>
|
|
<p class="text-muted mb-0">Time Entries</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="card text-center">
|
|
<div class="card-body">
|
|
<i class="fas fa-dollar-sign fa-2x text-warning mb-2"></i>
|
|
<h4 class="text-warning">{{ "%.1f"|format(stats.total_hours) }}h</h4>
|
|
<p class="text-muted mb-0">Total Hours</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Quick Actions -->
|
|
<div class="row mb-4">
|
|
<div class="col-md-6">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-user-cog"></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-outline-primary">
|
|
<i class="fas fa-users"></i> Manage Users
|
|
</a>
|
|
<a href="{{ url_for('admin.create_user') }}" class="btn btn-outline-success">
|
|
<i class="fas fa-user-plus"></i> Create New User
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-md-6">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-cog"></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-secondary">
|
|
<i class="fas fa-sliders-h"></i> Configure Settings
|
|
</a>
|
|
<a href="{{ url_for('admin.backup') }}" class="btn btn-outline-warning">
|
|
<i class="fas fa-download"></i> Create Backup
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Recent Activity -->
|
|
<div class="row">
|
|
<div class="col-md-8">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-history"></i> Recent Activity
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
{% if recent_entries %}
|
|
<div class="table-responsive">
|
|
<table class="table table-hover">
|
|
<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.username }}</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>
|
|
<strong>{{ entry.duration_formatted }}</strong>
|
|
</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-4">
|
|
<i class="fas fa-clock fa-2x text-muted mb-3"></i>
|
|
<h5 class="text-muted">No Recent Activity</h5>
|
|
<p class="text-muted">No time entries have been recorded recently.</p>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-md-4">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-chart-pie"></i> System Overview
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="mb-3">
|
|
<h6>Active Users</h6>
|
|
<div class="progress mb-2">
|
|
<div class="progress-bar" role="progressbar"
|
|
style="width: {{ (stats.active_users / stats.total_users * 100) if stats.total_users > 0 else 0 }}%">
|
|
{{ stats.active_users }}/{{ stats.total_users }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<h6>Active Projects</h6>
|
|
<div class="progress mb-2">
|
|
<div class="progress-bar bg-success" role="progressbar"
|
|
style="width: {{ (stats.active_projects / stats.total_projects * 100) if stats.total_projects > 0 else 0 }}%">
|
|
{{ stats.active_projects }}/{{ stats.total_projects }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<h6>Billable Hours</h6>
|
|
<div class="progress mb-2">
|
|
<div class="progress-bar bg-warning" role="progressbar"
|
|
style="width: {{ (stats.billable_hours / stats.total_hours * 100) if stats.total_hours > 0 else 0 }}%">
|
|
{{ "%.1f"|format(stats.billable_hours) }}h/{{ "%.1f"|format(stats.total_hours) }}h
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-4">
|
|
<h6>System Health</h6>
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<span>Database</span>
|
|
<span class="badge bg-success">Healthy</span>
|
|
</div>
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<span>Backup Status</span>
|
|
{% if stats.last_backup %}
|
|
<span class="badge bg-success">{{ stats.last_backup.strftime('%Y-%m-%d') }}</span>
|
|
{% else %}
|
|
<span class="badge bg-warning">No Backup</span>
|
|
{% endif %}
|
|
</div>
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<span>License Server</span>
|
|
<a href="{{ url_for('admin.license_status') }}" class="badge bg-info text-decoration-none">View Status</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card mt-3">
|
|
<div class="card-header">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-tools"></i> Quick Actions
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="d-grid gap-2">
|
|
<a href="{{ url_for('projects.create_project') }}" class="btn btn-sm btn-outline-primary">
|
|
<i class="fas fa-plus"></i> New Project
|
|
</a>
|
|
<a href="{{ url_for('reports.reports') }}" class="btn btn-sm btn-outline-info">
|
|
<i class="fas fa-chart-line"></i> View Reports
|
|
</a>
|
|
<a href="{{ url_for('admin.system_info') }}" class="btn btn-sm btn-outline-secondary">
|
|
<i class="fas fa-info-circle"></i> System Info
|
|
</a>
|
|
<a href="{{ url_for('admin.license_status') }}" class="btn btn-sm btn-outline-info">
|
|
<i class="fas fa-key"></i> License Status
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|