refactor(report): retire the Generate Report modal (Task 5)

Per the plan at docs/plans/2026-04-23-inline-filters-plan.md Task 5, the
now-redundant configuration modal goes away:

core/templates/core/_report_config_modal.html → deleted (160 lines)
core/templates/core/index.html:
  - Dashboard 'Generate Report' tile → plain link to
    /report/?from_month={% now 'Y-m' %}&to_month={% now 'Y-m' %} so the
    click lands on the report page with the current month pre-filled.
  - Modal {% include %} at EOF removed.
core/templates/core/report.html:
  - Both 'New Report' buttons (header + bottom action bar) deleted;
    comments updated to say the pills ARE the new-report interface.
  - {% include 'core/_report_config_modal.html' %} removed.
  - Stale 'Task 5 will delete...' comment on the Choices.js CDN block
    updated.

Konrad's exact ask (Checkpoint 1 feedback):
  'Does it make sense to have this popup window for reports? Don't you
  think clicking on generate report should just default to current month
  and open the report page where users can adjust report filters?'
  → Yes. The pills do exactly that, one click in.

Verification:
  grep -rn 'reportConfigModal\|_report_config_modal' core/ returns 0 hits.
  47/47 tests still pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Konrad du Plessis 2026-04-23 13:27:21 +02:00
parent c1937cd89d
commit 1d00a3a68f
3 changed files with 9 additions and 184 deletions

View File

@ -1,160 +0,0 @@
{% comment %}
=== REPORT CONFIGURATION MODAL (shared partial) ===
Renders the "Generate Report" modal and its month-vs-custom-dates
toggle script. Included by both the Dashboard (index.html) and the
Report page (report.html) so users can launch a new report from
either place without duplicating the modal HTML or the JS.
Requires in the parent template context:
- `projects` (queryset of Project, for the project dropdown)
- `teams` (queryset of Team, for the team dropdown)
If those are missing, the dropdowns simply show "All Projects" /
"All Teams" — no crash.
{% endcomment %}
<div class="modal fade" id="reportConfigModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><i class="fas fa-file-alt me-2" style="color: var(--accent);"></i>Generate Report</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<form method="get" action="{% url 'generate_report' %}" id="reportForm">
<div class="modal-body">
<div class="row g-3">
<!-- Date Mode Toggle -->
<div class="col-12">
<label class="form-label fw-semibold">Date Selection</label>
<div class="btn-group w-100" role="group">
<input type="radio" class="btn-check" name="date_mode" id="modeMonth" value="month" checked>
<label class="btn btn-outline-secondary" for="modeMonth">
<i class="fas fa-calendar-alt me-1"></i>Month(s)
</label>
<input type="radio" class="btn-check" name="date_mode" id="modeCustom" value="custom">
<label class="btn btn-outline-secondary" for="modeCustom">
<i class="fas fa-calendar-week me-1"></i>Custom Dates
</label>
</div>
</div>
<!-- Month Range Picker (shown by default) -->
<div class="col-6" id="fromMonthGroup">
<label class="form-label fw-semibold">From</label>
<input type="month" name="from_month" class="form-control" id="reportFromMonth">
</div>
<div class="col-6" id="toMonthGroup">
<label class="form-label fw-semibold">To</label>
<input type="month" name="to_month" class="form-control" id="reportToMonth">
</div>
<!-- Custom Date Range (hidden by default) -->
<div class="col-6 d-none" id="startDateGroup">
<label class="form-label fw-semibold">Start Date</label>
<input type="date" name="start_date" class="form-control" id="reportStartDate">
</div>
<div class="col-6 d-none" id="endDateGroup">
<label class="form-label fw-semibold">End Date</label>
<input type="date" name="end_date" class="form-control" id="reportEndDate">
</div>
<!-- Project Filter (optional) -->
<div class="col-12">
<label class="form-label fw-semibold">Project <span class="text-muted fw-normal">(optional)</span></label>
<select name="project" class="form-select report-multi" multiple data-placeholder="All projects (leave empty for all)">
{% for p in projects %}
<option value="{{ p.id }}"{% if p.id|stringformat:"s" in selected_project_ids %} selected{% endif %}>{{ p.name }}</option>
{% endfor %}
</select>
</div>
<!-- Team Filter (optional) -->
<div class="col-12">
<label class="form-label fw-semibold">Team <span class="text-muted fw-normal">(optional)</span></label>
<select name="team" class="form-select report-multi" multiple data-placeholder="All teams (leave empty for all)">
{% for t in teams %}
<option value="{{ t.id }}"{% if t.id|stringformat:"s" in selected_team_ids %} selected{% endif %}>{{ t.name }}</option>
{% endfor %}
</select>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-accent"><i class="fas fa-chart-bar me-1"></i>Generate</button>
</div>
</form>
</div>
</div>
</div>
<!--
=== REPORT MODAL — toggle month range vs custom dates ===
Defaults both month pickers to the current month on page load so
clicking "Generate" without changing anything produces a
current-month report. Guarded by `if (!modeMonth)` so it's a no-op
on pages that don't include the modal.
-->
<script>
(function() {
var modeMonth = document.getElementById('modeMonth');
if (!modeMonth) return; // modal not on this page — skip
var modeCustom = document.getElementById('modeCustom');
var fromMonthGroup = document.getElementById('fromMonthGroup');
var toMonthGroup = document.getElementById('toMonthGroup');
var startGroup = document.getElementById('startDateGroup');
var endGroup = document.getElementById('endDateGroup');
var fromMonth = document.getElementById('reportFromMonth');
var toMonth = document.getElementById('reportToMonth');
// Default both month pickers to current month
var now = new Date();
var curMonth = now.getFullYear() + '-' + String(now.getMonth() + 1).padStart(2, '0');
if (fromMonth) fromMonth.value = curMonth;
if (toMonth) toMonth.value = curMonth;
function toggleMode() {
if (modeMonth.checked) {
fromMonthGroup.classList.remove('d-none');
toMonthGroup.classList.remove('d-none');
startGroup.classList.add('d-none');
endGroup.classList.add('d-none');
} else {
fromMonthGroup.classList.add('d-none');
toMonthGroup.classList.add('d-none');
startGroup.classList.remove('d-none');
endGroup.classList.remove('d-none');
}
}
modeMonth.addEventListener('change', toggleMode);
if (modeCustom) modeCustom.addEventListener('change', toggleMode);
})();
</script>
{# === CHOICES.JS — multi-select enhancement (admin-only) === #}
{# Loaded CDN-only; falls back to native <select multiple> if the CDN fails. #}
{% if user.is_staff or user.is_superuser %}
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/choices.js@10.2.0/public/assets/styles/choices.min.css"
integrity="sha384-9oHz8X4XgvL+WkhPjPTMHviP0FM/eWUHWFmAVXKJ3PnbIK8Vi2ranPMgb0LZhaeQ"
crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/choices.js@10.2.0/public/assets/scripts/choices.min.js"
integrity="sha384-9r5e85TmdjVjyjYzZAV3TG5A6tcrmD7JjNBGfT2r1wp9txUPttent/DMiMuOwRNG"
crossorigin="anonymous"
defer></script>
<script>
(function() {
document.addEventListener('DOMContentLoaded', function() {
if (typeof Choices === 'undefined') return; // graceful fallback
document.querySelectorAll('.report-multi').forEach(function(el) {
new Choices(el, {
removeItemButton: true,
shouldSort: false,
placeholder: true,
placeholderValue: el.getAttribute('data-placeholder') || '',
});
});
});
})();
</script>
{% endif %}

View File

@ -184,7 +184,11 @@
<i class="fas fa-receipt"></i>
<span>New Receipt</span>
</a>
<a href="#" class="quick-action" data-bs-toggle="modal" data-bs-target="#reportConfigModal">
{# === GENERATE REPORT — PLAIN LINK === #}
{# No modal: lands on /report/ with the current month pre-filled; #}
{# inline pill filters on the page handle any further tweaking. #}
<a href="{% url 'generate_report' %}?from_month={% now 'Y-m' %}&to_month={% now 'Y-m' %}"
class="quick-action">
<i class="fas fa-file-alt"></i>
<span>Generate Report</span>
</a>
@ -599,9 +603,4 @@ document.addEventListener('DOMContentLoaded', function() {
});
</script>
<!-- === REPORT CONFIGURATION MODAL === -->
<!-- Extracted to a shared partial so the report page can use the same
modal without duplicating the HTML or the toggle script. -->
{% include 'core/_report_config_modal.html' %}
{% endblock %}

View File

@ -17,10 +17,8 @@
</p>
</div>
<div class="d-flex gap-2 mt-3 mt-md-0">
<!-- New Report: opens the same config modal as the Dashboard -->
<button type="button" class="btn btn-primary shadow-sm" data-bs-toggle="modal" data-bs-target="#reportConfigModal">
<i class="fas fa-plus me-1"></i>New Report
</button>
{# "New Report" removed — the inline filter pills below ARE the #}
{# new-report interface now. Date/project/team pills edit in-place. #}
<a href="{% url 'generate_report_pdf' %}?{{ query_string }}" class="btn btn-accent shadow-sm">
<i class="fas fa-download me-1"></i>Download PDF
</a>
@ -525,27 +523,15 @@
<div class="d-flex justify-content-between align-items-center d-print-none">
<a href="{% url 'home' %}" class="btn btn-outline-secondary"><i class="fas fa-arrow-left me-1"></i>Back to Dashboard</a>
<div class="d-flex gap-2">
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#reportConfigModal">
<i class="fas fa-plus me-1"></i>New Report
</button>
<a href="{% url 'generate_report_pdf' %}?{{ query_string }}" class="btn btn-accent"><i class="fas fa-download me-1"></i>Download PDF</a>
</div>
</div>
</div>
<!-- === REPORT CONFIGURATION MODAL ===
Shared partial — same modal the Dashboard uses, so clicking
"New Report" here opens the familiar config screen without
navigating away. -->
{% include 'core/_report_config_modal.html' %}
{# === CHOICES.JS CDN — admin-only === #}
{# Loaded here because the pill popovers enhance their <select multiple> #}
{# elements with Choices.js. Task 5 will delete _report_config_modal.html #}
{# which currently also loads this CDN; having it here keeps the pills #}
{# functional after retirement. Falls back to native multi-select if CDN #}
{# fails. SRI hashes match the ones used in the retired modal. #}
{# The pill popovers enhance their <select multiple> elements with #}
{# Choices.js. Falls back to a native multi-select if the CDN fails. #}
{% if user.is_staff or user.is_superuser %}
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/choices.js@10.2.0/public/assets/styles/choices.min.css"