Ver 9.1 Calender added

This commit is contained in:
Flatlogic Bot 2026-02-04 00:53:46 +00:00
parent af006bef5f
commit 06d5101e39
3 changed files with 175 additions and 5 deletions

View File

@ -11,7 +11,17 @@
<h1 class="display-5 mb-2">Work Log History</h1>
<p class="lead opacity-75">Filter and review historical daily work logs.</p>
</div>
<div class="d-flex gap-2">
<div class="d-flex gap-2 align-items-center">
<!-- View Switcher -->
<div class="btn-group me-2 shadow-sm" role="group">
<a href="?view=list&worker={{ selected_worker|default:'' }}&team={{ selected_team|default:'' }}&project={{ selected_project|default:'' }}&payment_status={{ selected_payment_status|default:'' }}" class="btn btn-outline-secondary bg-white {% if view_mode != 'calendar' %}active fw-bold{% endif %}">
<i class="bi bi-list-ul"></i> List
</a>
<a href="?view=calendar&worker={{ selected_worker|default:'' }}&team={{ selected_team|default:'' }}&project={{ selected_project|default:'' }}&payment_status={{ selected_payment_status|default:'' }}" class="btn btn-outline-secondary bg-white {% if view_mode == 'calendar' %}active fw-bold{% endif %}">
<i class="bi bi-calendar3"></i> Calendar
</a>
</div>
<a href="{% url 'export_work_log_csv' %}?{{ request.GET.urlencode }}" class="btn btn-outline-secondary shadow-sm border-0 bg-white text-dark">
<i class="bi bi-download me-1"></i> Export CSV
</a>
@ -25,6 +35,12 @@
<div class="card border-0 shadow-sm">
<div class="card-body p-4">
<form method="get" class="row g-3">
<input type="hidden" name="view" value="{{ view_mode }}">
{% if view_mode == 'calendar' %}
<input type="hidden" name="month" value="{{ curr_month }}">
<input type="hidden" name="year" value="{{ curr_year }}">
{% endif %}
<div class="col-md-3">
<label class="form-label small text-muted text-uppercase fw-bold">Worker</label>
<select name="worker" class="form-select">
@ -66,7 +82,7 @@
</form>
{% if selected_worker or selected_team or selected_project or selected_payment_status and selected_payment_status != 'all' %}
<div class="mt-3">
<a href="{% url 'work_log_list' %}" class="btn btn-sm btn-link text-decoration-none text-muted">
<a href="{% url 'work_log_list' %}?view={{ view_mode }}" class="btn btn-sm btn-link text-decoration-none text-muted">
<i class="bi bi-x-circle"></i> Clear all filters
</a>
</div>
@ -77,6 +93,84 @@
</div>
<div class="container mb-5 mt-n4">
{% if view_mode == 'calendar' %}
<!-- CALENDAR VIEW -->
<div class="d-flex justify-content-between align-items-center mb-3">
<h3 class="mb-0 fw-bold">{{ month_name }} {{ curr_year }}</h3>
<div class="btn-group shadow-sm">
<a href="?view=calendar&month={{ prev_month }}&year={{ prev_year }}&worker={{ selected_worker|default:'' }}&team={{ selected_team|default:'' }}&project={{ selected_project|default:'' }}&payment_status={{ selected_payment_status|default:'' }}" class="btn btn-light border bg-white">
<i class="bi bi-chevron-left"></i> Previous
</a>
<a href="?view=calendar&month={{ next_month }}&year={{ next_year }}&worker={{ selected_worker|default:'' }}&team={{ selected_team|default:'' }}&project={{ selected_project|default:'' }}&payment_status={{ selected_payment_status|default:'' }}" class="btn btn-light border bg-white">
Next <i class="bi bi-chevron-right"></i>
</a>
</div>
</div>
<div class="card shadow-sm border-0">
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-bordered mb-0 calendar-table" style="table-layout: fixed;">
<thead class="bg-light text-center text-uppercase text-muted small">
<tr>
<th style="width: 14.28%">Mon</th>
<th style="width: 14.28%">Tue</th>
<th style="width: 14.28%">Wed</th>
<th style="width: 14.28%">Thu</th>
<th style="width: 14.28%">Fri</th>
<th style="width: 14.28%">Sat</th>
<th style="width: 14.28%">Sun</th>
</tr>
</thead>
<tbody>
{% for week in calendar_weeks %}
<tr>
{% for day_info in week %}
<td class="{% if not day_info.is_current_month %}bg-light text-muted bg-opacity-10{% endif %}" style="height: 140px; vertical-align: top; padding: 10px;">
<div class="d-flex justify-content-between align-items-start mb-2">
<span class="fw-bold {% if day_info.date == current_time.date %}text-primary bg-primary bg-opacity-10 px-2 rounded-circle{% endif %}">{{ day_info.day }}</span>
{% if day_info.logs %}
<span class="badge bg-secondary opacity-50">{{ day_info.logs|length }}</span>
{% endif %}
</div>
<div class="calendar-events" style="max-height: 100px; overflow-y: auto;">
{% for log in day_info.logs %}
<div class="mb-1 p-1 rounded border small bg-white shadow-sm" style="font-size: 0.75rem; line-height: 1.2; border-left: 3px solid var(--bs-primary) !important;">
<div class="fw-bold text-truncate">{{ log.project.name }}</div>
{% with team=log.workers.first.teams.first %}
{% if team %}
<div class="text-muted small text-truncate" style="font-size: 0.7rem;">
<i class="bi bi-people-fill me-1"></i>{{ team.name }}
</div>
{% endif %}
{% endwith %}
<div class="text-muted text-truncate" title="{{ log.workers.count }} workers">
{% if selected_worker %}
{{ log.workers.first.name }}
{% elif log.workers.count == 1 %}
{{ log.workers.first.name }}
{% else %}
{{ log.workers.count }} workers
{% endif %}
</div>
{% if log.notes %}
<!-- Optional: indicator for notes -->
{% endif %}
</div>
{% endfor %}
</div>
</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% else %}
<!-- LIST VIEW -->
<div class="card shadow-sm border-0">
<div class="card-body p-0">
{% if logs %}
@ -184,6 +278,7 @@
{% endif %}
</div>
</div>
{% endif %}
</div>
<style>
@ -191,5 +286,14 @@
.bg-accent { background-color: var(--bs-accent); }
.btn-accent { background-color: #ffde59; color: #000; border: none; font-weight: 600; }
.btn-accent:hover { background-color: #e6c850; }
/* Calendar Scrollbar custom */
.calendar-events::-webkit-scrollbar {
width: 4px;
}
.calendar-events::-webkit-scrollbar-thumb {
background-color: #ccc;
border-radius: 4px;
}
</style>
{% endblock %}

View File

@ -2,6 +2,8 @@ import os
import platform
import json
import csv
import calendar
import datetime
from django.shortcuts import render, redirect, get_object_or_404
from django.utils import timezone
from django.contrib.auth.decorators import login_required
@ -198,8 +200,9 @@ def work_log_list(request):
team_id = request.GET.get('team')
project_id = request.GET.get('project')
payment_status = request.GET.get('payment_status') # 'paid', 'unpaid', 'all'
view_mode = request.GET.get('view', 'list')
logs = WorkLog.objects.all().prefetch_related('workers', 'project', 'supervisor', 'paid_in').order_by('-date', '-id')
logs = WorkLog.objects.all().prefetch_related('workers', 'workers__teams', 'project', 'supervisor', 'paid_in').order_by('-date', '-id')
target_worker = None
if worker_id:
@ -232,6 +235,28 @@ def work_log_list(request):
# Convert to list to attach attributes
final_logs = []
total_amount = 0
# If Calendar View: Filter logs by Month BEFORE iterating to prevent fetching ALL history
if view_mode == 'calendar':
today = timezone.now().date()
try:
curr_year = int(request.GET.get('year', today.year))
curr_month = int(request.GET.get('month', today.month))
except ValueError:
curr_year = today.year
curr_month = today.month
# Bounds safety
if curr_month < 1: curr_month = 1;
if curr_month > 12: curr_month = 12;
# Get range
_, num_days = calendar.monthrange(curr_year, curr_month)
start_date = datetime.date(curr_year, curr_month, 1)
end_date = datetime.date(curr_year, curr_month, num_days)
logs = logs.filter(date__range=(start_date, end_date))
for log in logs:
if target_worker:
log.display_amount = target_worker.day_rate
@ -243,7 +268,6 @@ def work_log_list(request):
# Context for filters
context = {
'logs': final_logs,
'total_amount': total_amount,
'workers': Worker.objects.filter(is_active=True).order_by('name'),
'teams': Team.objects.filter(is_active=True).order_by('name'),
@ -253,8 +277,50 @@ def work_log_list(request):
'selected_project': int(project_id) if project_id else None,
'selected_payment_status': payment_status,
'target_worker': target_worker,
'view_mode': view_mode,
}
if view_mode == 'calendar':
# Group by date for easy lookup in template
logs_map = {}
for log in final_logs:
if log.date not in logs_map:
logs_map[log.date] = []
logs_map[log.date].append(log)
cal = calendar.Calendar(firstweekday=0) # Monday is 0
month_dates = cal.monthdatescalendar(curr_year, curr_month)
# Prepare structured data for template
calendar_weeks = []
for week in month_dates:
week_data = []
for d in week:
week_data.append({
'date': d,
'day': d.day,
'is_current_month': d.month == curr_month,
'logs': logs_map.get(d, [])
})
calendar_weeks.append(week_data)
# Nav Links
prev_month_date = start_date - datetime.timedelta(days=1)
next_month_date = end_date + datetime.timedelta(days=1)
context.update({
'calendar_weeks': calendar_weeks,
'curr_month': curr_month,
'curr_year': curr_year,
'month_name': calendar.month_name[curr_month],
'prev_month': prev_month_date.month,
'prev_year': prev_month_date.year,
'next_month': next_month_date.month,
'next_year': next_month_date.year,
})
else:
context['logs'] = final_logs
return render(request, 'core/work_log_list.html', context)
def export_work_log_csv(request):
@ -264,7 +330,7 @@ def export_work_log_csv(request):
project_id = request.GET.get('project')
payment_status = request.GET.get('payment_status')
logs = WorkLog.objects.all().prefetch_related('workers', 'project', 'supervisor', 'paid_in').order_by('-date', '-id')
logs = WorkLog.objects.all().prefetch_related('workers', 'workers__teams', 'project', 'supervisor', 'paid_in').order_by('-date', '-id')
target_worker = None
if worker_id: