Files
TimeTracker/app/templates/reports/task_report.html
T
Dries Peeters c93a37f126 feat: add overtime tracking support with configurable working hours
Implement comprehensive overtime tracking feature that allows users to
set their standard working hours per day and automatically calculates
overtime for hours worked beyond that threshold.

Core Features:
- Add standard_hours_per_day field to User model (default: 8.0 hours)
- Create Alembic migration (031_add_standard_hours_per_day.py)
- Implement overtime calculation utilities (app/utils/overtime.py)
  * calculate_daily_overtime: per-day overtime calculation
  * calculate_period_overtime: multi-day overtime aggregation
  * get_daily_breakdown: detailed day-by-day analysis
  * get_weekly_overtime_summary: weekly overtime statistics
  * get_overtime_statistics: comprehensive overtime metrics

User Interface:
- Add "Overtime Settings" section to user settings page
- Display overtime data in user reports (regular vs overtime hours)
- Show "Days with Overtime" badge in reports
- Add overtime analytics API endpoint (/api/analytics/overtime)
- Improve input field styling with cleaner appearance (no spinners)

Reports Enhancement:
- Standardize form input styling across all report pages
- Replace inline Tailwind classes with consistent form-input class
- Add FontAwesome icons to form labels for better UX
- Improve button hover states and transitions

Testing:
- Add comprehensive unit tests (tests/test_overtime.py)
- Add smoke tests for quick validation (tests/test_overtime_smoke.py)
- Test coverage for models, utilities, and various overtime scenarios

Documentation:
- OVERTIME_FEATURE_DOCUMENTATION.md: complete feature guide
- OVERTIME_IMPLEMENTATION_SUMMARY.md: technical implementation details
- docs/features/OVERTIME_TRACKING.md: quick start guide

This change enables organizations to track employee overtime accurately
based on individual working hour configurations, providing better
insights into work patterns and resource allocation.
2025-10-27 08:44:04 +01:00

79 lines
3.3 KiB
HTML

{% extends "base.html" %}
{% block content %}
<div class="flex justify-between items-center mb-6">
<h1 class="text-2xl font-bold">Task Report</h1>
</div>
<div class="bg-card-light dark:bg-card-dark p-6 rounded-lg shadow mb-6">
<form method="GET" class="grid grid-cols-1 md:grid-cols-4 gap-4">
<div>
<label for="project_id" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
<i class="fas fa-project-diagram mr-1"></i>Project
</label>
<select name="project_id" id="project_id" class="form-input">
<option value="">All Projects</option>
{% for project in projects %}
<option value="{{ project.id }}" {% if selected_project == project.id %}selected{% endif %}>{{ project.name }}</option>
{% endfor %}
</select>
</div>
<div>
<label for="user_id" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
<i class="fas fa-user mr-1"></i>User
</label>
<select name="user_id" id="user_id" class="form-input">
<option value="">All Users</option>
{% for user in users %}
<option value="{{ user.id }}" {% if selected_user == user.id %}selected{% endif %}>{{ user.display_name }}</option>
{% endfor %}
</select>
</div>
<div>
<label for="start_date" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
<i class="fas fa-calendar mr-1"></i>Start Date
</label>
<input type="date" name="start_date" id="start_date" value="{{ start_date }}" class="form-input">
</div>
<div>
<label for="end_date" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
<i class="fas fa-calendar mr-1"></i>End Date
</label>
<input type="date" name="end_date" id="end_date" value="{{ end_date }}" class="form-input">
</div>
<div class="self-end">
<button type="submit" class="bg-primary text-white px-4 py-2 rounded-lg hover:bg-primary/90 transition">
<i class="fas fa-filter mr-2"></i>Filter
</button>
</div>
</form>
</div>
<div class="bg-card-light dark:bg-card-dark p-6 rounded-lg shadow">
<table class="w-full text-left">
<thead>
<tr>
<th class="p-2">Task</th>
<th class="p-2">Project</th>
<th class="p-2">Completed At</th>
<th class="p-2">Hours</th>
</tr>
</thead>
<tbody>
{% for task_row in tasks %}
<tr class="border-b border-border-light dark:border-border-dark">
<td class="p-2">{{ task_row.task.name }}</td>
<td class="p-2">{{ task_row.project.name }}</td>
<td class="p-2">{{ task_row.completed_at.strftime('%Y-%m-%d') }}</td>
<td class="p-2">{{ "%.2f"|format(task_row.hours) }}</td>
</tr>
{% else %}
<tr>
<td colspan="4" class="p-4 text-center">No completed tasks for the selected period.</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}