Jul 26 - Update scheduled inspection settings (weekly/monthly)

This commit is contained in:
2026-07-26 13:47:17 -04:00
parent 0c63c45b21
commit 16858108b9
10 changed files with 488 additions and 23 deletions
+9 -1
View File
@@ -48,7 +48,7 @@
<div class="table-responsive">
<table class="table table-sm table-hover mb-0 align-middle">
<thead class="table-light">
<tr><th>Facility</th><th>Template</th><th>Inspector</th><th>Due</th><th></th></tr>
<tr><th>Facility</th><th>Template</th><th>Inspector</th><th>Repeats</th><th>Due</th><th></th></tr>
</thead>
<tbody>
{% for s in sched_upcoming %}
@@ -56,13 +56,21 @@
<td>{{ s.facility.name if s.facility else '—' }}</td>
<td class="small">{{ s.template.name if s.template else '—' }}</td>
<td class="small">{{ s.inspector.display_name if s.inspector else '—' }}</td>
<td class="small text-muted">{{ s.recurrence_label }}</td>
<td class="small">{{ s.next_due_date.strftime('%b %d') }}</td>
<td class="text-end">
{# Start is shown only to the assignee — the inspection is theirs to do. #}
{% if s.inspector_id and s.inspector_id == current_user.id %}
{% set open_id = sched_open_inspections.get(s.id) %}
{% if open_id %}
<a href="{{ url_for('inspections.execute', inspection_id=open_id) }}"
class="btn btn-sm btn-warning py-0" title="You already started this — resume it">
<i class="bi bi-pencil-square"></i> Continue</a>
{% else %}
<a href="{{ url_for('scheduled_inspections.start', schedule_id=s.id) }}"
class="btn btn-sm btn-success py-0"><i class="bi bi-play-fill"></i> Start</a>
{% endif %}
{% endif %}
</td>
</tr>
{% endfor %}
@@ -49,6 +49,64 @@
{{ form.next_due_date.label(class="form-label fw-semibold") }}
{{ form.next_due_date(class="form-control", type="date") }}
{% for e in form.next_due_date.errors %}<div class="text-danger small">{{ e }}</div>{% endfor %}
<div class="form-text">Snapped forward to the first matching day.</div>
</div>
</div>
{# ── Weekly: which days of the week ──────────────────────────── #}
<div class="mb-3 p-3 rounded bg-light border" id="weekly_block" hidden>
<label class="form-label fw-semibold d-block">{{ form.weekdays.label.text }}</label>
<div class="d-flex flex-wrap gap-3">
{% for value, label in form.weekdays.choices %}
<div class="form-check">
<input class="form-check-input" type="checkbox" name="weekdays"
id="weekday_{{ value }}" value="{{ value }}"
{% if form.weekdays.data and value in form.weekdays.data %}checked{% endif %}>
<label class="form-check-label" for="weekday_{{ value }}">{{ label[:3] }}</label>
</div>
{% endfor %}
</div>
{% for e in form.weekdays.errors %}<div class="text-danger small">{{ e }}</div>{% endfor %}
<div class="form-text mb-0">
Pick every day the inspection recurs — e.g. Mon, Wed, Fri gives three
inspections a week. The due date rolls to the next selected day each
time one is submitted.
</div>
</div>
{# ── Monthly: day-of-month OR nth weekday ────────────────────── #}
<div class="mb-3 p-3 rounded bg-light border" id="monthly_block" hidden>
<label class="form-label fw-semibold d-block">{{ form.month_mode.label.text }}</label>
<div class="form-check">
<input class="form-check-input" type="radio" name="month_mode"
id="month_mode_day" value="day_of_month"
{% if form.month_mode.data != 'nth_weekday' %}checked{% endif %}>
<label class="form-check-label" for="month_mode_day">On a day of the month</label>
</div>
<div class="ms-4 mb-2" id="dom_row">
<div class="input-group input-group-sm" style="max-width:16rem;">
<span class="input-group-text">Day</span>
{{ form.day_of_month(class="form-control", type="number", min=1, max=31,
placeholder="15") }}
</div>
{% for e in form.day_of_month.errors %}<div class="text-danger small">{{ e }}</div>{% endfor %}
<div class="form-text mb-0">Months without that day use their last day.</div>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="month_mode"
id="month_mode_nth" value="nth_weekday"
{% if form.month_mode.data == 'nth_weekday' %}checked{% endif %}>
<label class="form-check-label" for="month_mode_nth">On a weekday of the month</label>
</div>
<div class="ms-4" id="nth_row">
<div class="d-flex gap-2 flex-wrap" style="max-width:24rem;">
{{ form.nth_week(class="form-select form-select-sm", style="max-width:7rem;") }}
{{ form.nth_weekday(class="form-select form-select-sm", style="max-width:11rem;") }}
</div>
{% for e in form.nth_week.errors %}<div class="text-danger small">{{ e }}</div>{% endfor %}
<div class="form-text mb-0">e.g. the 2nd Tuesday of every month.</div>
</div>
</div>
@@ -129,5 +187,40 @@
setPlaceholder();
}
}());
// Recurrence blocks: only the one matching the chosen frequency is shown.
// The server clears the columns for the hidden blocks on save, so stale values
// left in the DOM never take effect.
(function () {
'use strict';
var freq = document.getElementById('frequency') ||
document.querySelector('[name="frequency"]');
var weekly = document.getElementById('weekly_block');
var monthly = document.getElementById('monthly_block');
if (!freq || !weekly || !monthly) { return; }
var domRadio = document.getElementById('month_mode_day');
var nthRadio = document.getElementById('month_mode_nth');
var domRow = document.getElementById('dom_row');
var nthRow = document.getElementById('nth_row');
function syncMonthMode() {
var useNth = nthRadio && nthRadio.checked;
domRow.style.opacity = useNth ? '.45' : '1';
nthRow.style.opacity = useNth ? '1' : '.45';
}
function syncFrequency() {
weekly.hidden = freq.value !== 'weekly';
monthly.hidden = freq.value !== 'monthly';
syncMonthMode();
}
freq.addEventListener('change', syncFrequency);
[domRadio, nthRadio].forEach(function (r) {
if (r) { r.addEventListener('change', syncMonthMode); }
});
syncFrequency();
}());
</script>
{% endblock %}
@@ -43,7 +43,7 @@
<td><strong>{{ s.facility.name if s.facility else '—' }}</strong></td>
<td>{{ s.template.name if s.template else '—' }}</td>
<td>{{ s.inspector.display_name if s.inspector else '— Unassigned —' }}</td>
<td><span class="badge bg-secondary">{{ s.frequency_label }}</span></td>
<td><span class="badge bg-secondary">{{ s.recurrence_label }}</span></td>
<td>
{{ s.next_due_date.strftime('%b %d, %Y') }}
{% if overdue %}
@@ -62,11 +62,19 @@
<td class="text-end text-nowrap">
{# Start is shown only to the assignee — the inspection is theirs to do. #}
{% if s.active and s.inspector_id and s.inspector_id == current_user.id %}
{% set open_id = open_inspections.get(s.id) %}
{% if open_id %}
<a href="{{ url_for('inspections.execute', inspection_id=open_id) }}"
class="btn btn-sm btn-warning" title="You already started this — resume it">
<i class="bi bi-pencil-square"></i> Continue
</a>
{% else %}
<a href="{{ url_for('scheduled_inspections.start', schedule_id=s.id) }}"
class="btn btn-sm btn-success" title="Start this inspection">
<i class="bi bi-play-fill"></i> Start
</a>
{% endif %}
{% endif %}
{% if current_user.role in ['admin','director','project_manager','auditor'] %}
<a href="{{ url_for('scheduled_inspections.edit', schedule_id=s.id) }}"
class="btn btn-sm btn-outline-primary"><i class="bi bi-pencil"></i></a>