Fix work history filter — add validation, explicit form action, and visual feedback
- Add explicit action="{% url 'work_history' %}" to filter form (prevents
potential URL mismatch on Flatlogic proxy)
- Add numeric validation for worker/project GET params (prevents 500 errors)
- Add results counter: "Showing X of Y work logs" when filters are active
- Add active filter badges showing worker name, project name, and status
- Add green left border indicator on filter card when filters are active
- Make Clear button conditional (red, only appears with active filters)
- Add SQLite dev toggle in settings.py for local testing without MariaDB
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
b837932bb4
commit
0b3ef5395f
@ -199,4 +199,20 @@ LOGOUT_REDIRECT_URL = 'login'
|
||||
from django.contrib.messages import constants as message_constants
|
||||
MESSAGE_TAGS = {
|
||||
message_constants.ERROR: 'danger',
|
||||
}
|
||||
}
|
||||
|
||||
# === LOCAL DEVELOPMENT: SQLite override ===
|
||||
# Set USE_SQLITE=true in environment to use SQLite instead of MariaDB.
|
||||
# This lets you test locally without a MySQL/MariaDB server.
|
||||
if os.getenv('USE_SQLITE', 'false').lower() == 'true':
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': BASE_DIR / 'db.sqlite3',
|
||||
}
|
||||
}
|
||||
# Disable secure cookies for local http:// testing
|
||||
SESSION_COOKIE_SECURE = False
|
||||
CSRF_COOKIE_SECURE = False
|
||||
SESSION_COOKIE_SAMESITE = 'Lax'
|
||||
CSRF_COOKIE_SAMESITE = 'Lax'
|
||||
@ -40,9 +40,9 @@
|
||||
</div>
|
||||
|
||||
{# === FILTER BAR === #}
|
||||
<div class="card shadow-sm border-0 mb-4">
|
||||
<div class="card shadow-sm border-0 mb-4{% if has_active_filters %} border-start border-3{% endif %}" {% if has_active_filters %}style="border-left-color: var(--accent, #10b981) !important;"{% endif %}>
|
||||
<div class="card-body py-3">
|
||||
<form method="GET" class="row g-2 align-items-end">
|
||||
<form method="GET" action="{% url 'work_history' %}" class="row g-2 align-items-end">
|
||||
{# Preserve current view mode when filtering #}
|
||||
<input type="hidden" name="view" value="{{ view_mode }}">
|
||||
{% if view_mode == 'calendar' %}
|
||||
@ -87,16 +87,48 @@
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{# Filter Button #}
|
||||
{# Filter + Clear Buttons #}
|
||||
<div class="col-md-3 d-flex gap-2">
|
||||
<button type="submit" class="btn btn-sm btn-accent">
|
||||
<i class="fas fa-filter me-1"></i> Filter
|
||||
</button>
|
||||
<a href="{% url 'work_history' %}?view={{ view_mode }}" class="btn btn-sm btn-outline-secondary">
|
||||
{% if has_active_filters %}
|
||||
<a href="{% url 'work_history' %}?view={{ view_mode }}" class="btn btn-sm btn-outline-danger">
|
||||
<i class="fas fa-times me-1"></i> Clear
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{# === Active Filter Feedback === #}
|
||||
{# Shows a results counter when filters are active so the user can see the filter is working #}
|
||||
{% if has_active_filters %}
|
||||
<div class="mt-2 d-flex align-items-center flex-wrap gap-2">
|
||||
<small class="text-muted">
|
||||
<i class="fas fa-info-circle me-1"></i>
|
||||
Showing <strong>{{ filtered_log_count }}</strong> of {{ total_log_count }} work log{{ total_log_count|pluralize }}
|
||||
</small>
|
||||
{# Show which filters are active as small badges #}
|
||||
{% if selected_worker %}
|
||||
<span class="badge bg-primary bg-opacity-10 text-primary border border-primary border-opacity-25">
|
||||
<i class="fas fa-user fa-xs me-1"></i>
|
||||
{% for w in filter_workers %}{% if w.id|stringformat:"d" == selected_worker %}{{ w.name }}{% endif %}{% endfor %}
|
||||
</span>
|
||||
{% endif %}
|
||||
{% if selected_project %}
|
||||
<span class="badge bg-success bg-opacity-10 text-success border border-success border-opacity-25">
|
||||
<i class="fas fa-project-diagram fa-xs me-1"></i>
|
||||
{% for p in filter_projects %}{% if p.id|stringformat:"d" == selected_project %}{{ p.name }}{% endif %}{% endfor %}
|
||||
</span>
|
||||
{% endif %}
|
||||
{% if selected_status %}
|
||||
<span class="badge bg-warning bg-opacity-10 text-dark border border-warning border-opacity-25">
|
||||
<i class="fas fa-tag fa-xs me-1"></i>
|
||||
{{ selected_status|capfirst }}
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@ -375,11 +375,25 @@ def work_history(request):
|
||||
).distinct()
|
||||
|
||||
# --- Filters ---
|
||||
# Read filter values from the URL query string
|
||||
# Read filter values from the URL query string.
|
||||
# Validate numeric params to prevent 500 errors from bad/malformed URLs.
|
||||
worker_filter = request.GET.get('worker', '')
|
||||
project_filter = request.GET.get('project', '')
|
||||
status_filter = request.GET.get('status', '')
|
||||
|
||||
# Validate: worker and project must be numeric IDs (or empty)
|
||||
try:
|
||||
worker_filter = str(int(worker_filter)) if worker_filter else ''
|
||||
except (ValueError, TypeError):
|
||||
worker_filter = ''
|
||||
try:
|
||||
project_filter = str(int(project_filter)) if project_filter else ''
|
||||
except (ValueError, TypeError):
|
||||
project_filter = ''
|
||||
|
||||
# Count total logs BEFORE filtering (so we can show "X of Y" to the user)
|
||||
total_log_count = logs.count()
|
||||
|
||||
if worker_filter:
|
||||
logs = logs.filter(workers__id=worker_filter).distinct()
|
||||
|
||||
@ -393,6 +407,12 @@ def work_history(request):
|
||||
# "Unpaid" = has no PayrollRecord linked
|
||||
logs = logs.filter(payroll_records__isnull=True)
|
||||
|
||||
# Track whether any filter is active (for showing feedback in the template)
|
||||
has_active_filters = bool(worker_filter or project_filter or status_filter)
|
||||
|
||||
# Count filtered results BEFORE adding joins (more efficient SQL)
|
||||
filtered_log_count = logs.count() if has_active_filters else 0
|
||||
|
||||
# Add related data and order by date (newest first)
|
||||
logs = logs.select_related(
|
||||
'project', 'supervisor'
|
||||
@ -435,6 +455,9 @@ def work_history(request):
|
||||
'is_admin': is_admin(user),
|
||||
'view_mode': view_mode,
|
||||
'filter_params': filter_params,
|
||||
'has_active_filters': has_active_filters,
|
||||
'total_log_count': total_log_count,
|
||||
'filtered_log_count': filtered_log_count,
|
||||
}
|
||||
|
||||
# === CALENDAR MODE ===
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user