fix(absences): multi-line {# #} comments rendering as text + add Resources menu entry

CLAUDE.md gotcha #5: multi-line {# ... #} blocks render as literal text
in Django templates. Converted to {% comment %} blocks in edit.html
and list.html (also scanned log.html / log_confirm.html for safety).

Adds an 'Absences' entry to the Resources dropdown in base.html so the
feature is discoverable from the topbar.
This commit is contained in:
Konrad du Plessis 2026-05-14 21:13:53 +02:00
parent 37268801a1
commit ea94c46cb6
3 changed files with 41 additions and 30 deletions

View File

@ -70,9 +70,9 @@
<i class="fas fa-clock"></i><span>History</span> <i class="fas fa-clock"></i><span>History</span>
</a> </a>
{% if user.is_staff %} {% if user.is_staff %}
<!-- Resources dropdown: Workers, Teams, Projects --> <!-- Resources dropdown: Workers, Teams, Projects, Absences -->
<div class="dropdown"> <div class="dropdown">
<a href="#" class="topbar-nav__link dropdown-toggle {% if 'worker' in request.resolver_match.url_name|default:'' or 'team' in request.resolver_match.url_name|default:'' or 'project' in request.resolver_match.url_name|default:'' %}active{% endif %}" <a href="#" class="topbar-nav__link dropdown-toggle {% if 'worker' in request.resolver_match.url_name|default:'' or 'team' in request.resolver_match.url_name|default:'' or 'project' in request.resolver_match.url_name|default:'' or 'absence' in request.resolver_match.url_name|default:'' %}active{% endif %}"
data-bs-toggle="dropdown" aria-expanded="false" role="button"> data-bs-toggle="dropdown" aria-expanded="false" role="button">
<i class="fas fa-hard-hat"></i><span>Resources</span> <i class="fas fa-hard-hat"></i><span>Resources</span>
</a> </a>
@ -80,6 +80,7 @@
<li><a class="dropdown-item" href="{% url 'worker_list' %}"><i class="fas fa-hard-hat me-2" style="color: var(--accent);"></i>Workers</a></li> <li><a class="dropdown-item" href="{% url 'worker_list' %}"><i class="fas fa-hard-hat me-2" style="color: var(--accent);"></i>Workers</a></li>
<li><a class="dropdown-item" href="{% url 'team_list' %}"><i class="fas fa-users me-2" style="color: var(--accent);"></i>Teams</a></li> <li><a class="dropdown-item" href="{% url 'team_list' %}"><i class="fas fa-users me-2" style="color: var(--accent);"></i>Teams</a></li>
<li><a class="dropdown-item" href="{% url 'project_list' %}"><i class="fas fa-project-diagram me-2" style="color: var(--accent);"></i>Projects</a></li> <li><a class="dropdown-item" href="{% url 'project_list' %}"><i class="fas fa-project-diagram me-2" style="color: var(--accent);"></i>Projects</a></li>
<li><a class="dropdown-item" href="{% url 'absence_list' %}"><i class="fas fa-user-clock me-2" style="color: var(--accent);"></i>Absences</a></li>
</ul> </ul>
</div> </div>
{% endif %} {% endif %}
@ -148,6 +149,9 @@
<a href="{% url 'project_list' %}" class="mobile-menu__link"> <a href="{% url 'project_list' %}" class="mobile-menu__link">
<i class="fas fa-project-diagram"></i><span>Projects</span> <i class="fas fa-project-diagram"></i><span>Projects</span>
</a> </a>
<a href="{% url 'absence_list' %}" class="mobile-menu__link">
<i class="fas fa-user-clock"></i><span>Absences</span>
</a>
{% endif %} {% endif %}
<a href="{% url 'create_receipt' %}" class="mobile-menu__link {% if request.resolver_match.url_name == 'create_receipt' %}active{% endif %}"> <a href="{% url 'create_receipt' %}" class="mobile-menu__link {% if request.resolver_match.url_name == 'create_receipt' %}active{% endif %}">
<i class="fas fa-receipt"></i><span>Receipts</span> <i class="fas fa-receipt"></i><span>Receipts</span>

View File

@ -3,20 +3,21 @@
{% block title %}Edit Absence | FoxFitt{% endblock %} {% block title %}Edit Absence | FoxFitt{% endblock %}
{% block content %} {% block content %}
{# === EDIT ABSENCE PAGE === {% comment %}
Single form for editing one Absence row. The is_paid checkbox is === EDIT ABSENCE PAGE ===
the magic field — toggling it on creates a Bonus PayrollAdjustment Single form for editing one Absence row. The is_paid checkbox is
at the worker's daily rate; toggling it off deletes the adjustment the magic field — toggling it on creates a Bonus PayrollAdjustment
(UNLESS it's already been paid, in which case the view surfaces an at the worker's daily rate; toggling it off deletes the adjustment
error). (UNLESS it's already been paid, in which case the view surfaces an
error).
Why two forms? HTML doesn't allow nested <form> tags. The delete Why two forms? HTML doesn't allow nested <form> tags. The delete
action needs its own form to POST to /absences/<id>/delete/. So action needs its own form to POST to /absences/<id>/delete/. So
the delete form lives OUTSIDE the edit form (hidden), and the the delete form lives OUTSIDE the edit form (hidden), and the
Delete button inside the edit form uses the HTML5 `form="..."` Delete button inside the edit form uses the HTML5 `form="..."`
attribute to submit the delete form instead of its parent edit attribute to submit the delete form instead of its parent edit
form. form.
#} {% endcomment %}
<div class="container py-4"> <div class="container py-4">
<h1 class="page-title mb-3"><i class="fas fa-pen me-2"></i>Edit Absence</h1> <h1 class="page-title mb-3"><i class="fas fa-pen me-2"></i>Edit Absence</h1>
@ -24,8 +25,10 @@
{% for m in messages %}<div class="alert alert-{{ m.tags }}">{{ m }}</div>{% endfor %} {% for m in messages %}<div class="alert alert-{{ m.tags }}">{{ m }}</div>{% endfor %}
{% endif %} {% endif %}
{# Hidden sibling form so the Delete button can submit to its own URL. {% comment %}
Hidden via inline style — only the button inside the edit form is visible. #} Hidden sibling form so the Delete button can submit to its own URL.
Hidden via inline style — only the button inside the edit form is visible.
{% endcomment %}
<form method="post" action="{% url 'absence_delete' absence.id %}" <form method="post" action="{% url 'absence_delete' absence.id %}"
onsubmit="return confirm('Delete this absence?');" onsubmit="return confirm('Delete this absence?');"
id="absence-delete-form" style="display: none;"> id="absence-delete-form" style="display: none;">
@ -71,8 +74,10 @@
{% endif %} {% endif %}
<div class="d-flex justify-content-between mt-3"> <div class="d-flex justify-content-between mt-3">
{# `form="absence-delete-form"` makes this button submit the {% comment %}
hidden delete form rather than its enclosing edit form. #} `form="absence-delete-form"` makes this button submit the
hidden delete form rather than its enclosing edit form.
{% endcomment %}
<button type="submit" form="absence-delete-form" class="btn btn-outline-danger"> <button type="submit" form="absence-delete-form" class="btn btn-outline-danger">
<i class="fas fa-trash me-1"></i>Delete <i class="fas fa-trash me-1"></i>Delete
</button> </button>

View File

@ -4,10 +4,11 @@
{% block title %}Absences | FoxFitt{% endblock %} {% block title %}Absences | FoxFitt{% endblock %}
{% block content %} {% block content %}
{# === ABSENCES LIST PAGE === {% comment %}
Filtered, paginated table of absences. Each row links to edit and === ABSENCES LIST PAGE ===
has an inline delete form. CSV export button only shows for admin. Filtered, paginated table of absences. Each row links to edit and
#} has an inline delete form. CSV export button only shows for admin.
{% endcomment %}
<div class="container-fluid py-3"> <div class="container-fluid py-3">
{# === Page header — title + log/export action buttons === #} {# === Page header — title + log/export action buttons === #}
@ -162,13 +163,14 @@
{% endif %} {% endif %}
</div> </div>
{# === Reason badge colours === {% comment %}
Reuses the existing semantic badge palette from custom.css so dark/ === Reason badge colours ===
light theme switching works out of the box. Green-ish for "valid" Reuses the existing semantic badge palette from custom.css so dark/
leave (sick/family/annual), neutral for unpaid/other, amber for IOD, light theme switching works out of the box. Green-ish for "valid"
purple-ish (deduction) for the disciplinary reasons (suspension, leave (sick/family/annual), neutral for unpaid/other, amber for IOD,
absconded). purple-ish (deduction) for the disciplinary reasons (suspension,
#} absconded).
{% endcomment %}
<style> <style>
.badge-absence-sick { background: var(--badge-bonus-bg); color: var(--badge-bonus-fg); } .badge-absence-sick { background: var(--badge-bonus-bg); color: var(--badge-bonus-fg); }
.badge-absence-family { background: var(--badge-bonus-bg); color: var(--badge-bonus-fg); } .badge-absence-family { background: var(--badge-bonus-bg); color: var(--badge-bonus-fg); }