Aug 4 - Update code to follow up - MT11
This commit is contained in:
@@ -29,16 +29,109 @@
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">Frequency</label>
|
||||
<select name="frequency" class="form-select" required>
|
||||
<select name="frequency" id="frequencySelect" class="form-select" required>
|
||||
{% for f in frequencies %}
|
||||
<option value="{{ f }}"
|
||||
{{ 'selected' if (schedule and schedule.frequency == f) or (not schedule and f == 'weekly') }}>
|
||||
{{ f|title }}</option>
|
||||
{{ frequency_labels.get(f, f|title) }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# ── Recurrence detail (phase46) ────────────────────────────────────
|
||||
Only the block matching the chosen frequency is shown; the server
|
||||
validates the same block and clears the others on save. #}
|
||||
<div class="row" id="weeklyBlock" style="display:none;">
|
||||
<div class="col-12 mb-3">
|
||||
<label class="form-label">Days of the Week</label>
|
||||
<div class="d-flex flex-wrap gap-3">
|
||||
{% set picked = schedule.weekday_list if schedule else [] %}
|
||||
{% for i, day in [(0,'Mon'),(1,'Tue'),(2,'Wed'),(3,'Thu'),(4,'Fri'),(5,'Sat'),(6,'Sun')] %}
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="weekdays"
|
||||
value="{{ i }}" id="wd{{ i }}" {{ 'checked' if i in picked }}>
|
||||
<label class="form-check-label" for="wd{{ i }}">{{ day }}</label>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="form-text">
|
||||
Leave the start date on any day — it snaps forward to the first
|
||||
day you pick here.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" id="monthlyBlock" style="display:none;">
|
||||
<div class="col-md-4 mb-3">
|
||||
<label class="form-label">Rule</label>
|
||||
<select name="month_mode" id="monthModeSelect" class="form-select">
|
||||
<option value="day_of_month"
|
||||
{{ 'selected' if not schedule or schedule.month_mode != 'nth_weekday' }}>
|
||||
On a day of the month</option>
|
||||
<option value="nth_weekday"
|
||||
{{ 'selected' if schedule and schedule.month_mode == 'nth_weekday' }}>
|
||||
On a weekday of the month</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4 mb-3" id="dayOfMonthField">
|
||||
<label class="form-label">Day of Month</label>
|
||||
<input type="number" name="day_of_month" class="form-control"
|
||||
min="1" max="31"
|
||||
value="{{ schedule.day_of_month if schedule and schedule.day_of_month else '' }}">
|
||||
<div class="form-text">Clamped to the last day in shorter months.</div>
|
||||
</div>
|
||||
<div class="col-md-4 mb-3" id="nthWeekdayField">
|
||||
<div class="row g-2">
|
||||
<div class="col-6">
|
||||
<label class="form-label">Week</label>
|
||||
<select name="nth_week" class="form-select">
|
||||
{% for v, lbl in [(1,'1st'),(2,'2nd'),(3,'3rd'),(4,'4th'),(5,'5th'),(-1,'Last')] %}
|
||||
<option value="{{ v }}"
|
||||
{{ 'selected' if schedule and schedule.nth_week == v }}>{{ lbl }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<label class="form-label">Weekday</label>
|
||||
<select name="nth_weekday" class="form-select">
|
||||
{% for i, day in [(0,'Monday'),(1,'Tuesday'),(2,'Wednesday'),(3,'Thursday'),(4,'Friday'),(5,'Saturday'),(6,'Sunday')] %}
|
||||
<option value="{{ i }}"
|
||||
{{ 'selected' if schedule and schedule.nth_weekday == i }}>{{ day }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label" id="dueDateLabel">
|
||||
{{ 'Next Due Date' if schedule else 'Start Date' }}
|
||||
</label>
|
||||
<input type="date" name="next_due_date" class="form-control"
|
||||
value="{{ schedule.due_date.isoformat() if schedule and schedule.due_date else '' }}">
|
||||
<div class="form-text">
|
||||
{% if schedule %}
|
||||
When the next occurrence is due. Leave unchanged and saving will
|
||||
not move it.
|
||||
{% else %}
|
||||
Leave blank to start one full period from now.
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3" id="endDateBlock">
|
||||
<label class="form-label">End Date <span class="text-muted small">(optional)</span></label>
|
||||
<input type="date" name="end_date" class="form-control"
|
||||
value="{{ schedule.end_date.isoformat() if schedule and schedule.end_date else '' }}">
|
||||
<div class="form-text">
|
||||
Last date this schedule may produce an occurrence. Blank = repeats
|
||||
indefinitely.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">Mode</label>
|
||||
@@ -103,12 +196,15 @@
|
||||
<label class="form-check-label" for="activeSwitch">Active</label>
|
||||
</div>
|
||||
<p class="text-muted small">
|
||||
Saving recomputes the next {{ 'due date' if schedule.mode == 'plan' else 'run' }}
|
||||
from now, and resets this occurrence's reminders. Currently:
|
||||
Current {{ 'due date' if schedule.mode == 'plan' else 'run' }}:
|
||||
{{ schedule.next_run_at.strftime('%Y-%m-%d %H:%M') if schedule.next_run_at else '—' }}
|
||||
{% if schedule.last_completed_at %}
|
||||
· last completed {{ schedule.last_completed_at.strftime('%Y-%m-%d %H:%M') }}
|
||||
{% endif %}
|
||||
{% if schedule.end_date %}· ends {{ schedule.end_date.strftime('%Y-%m-%d') }}{% endif %}
|
||||
<br>
|
||||
Reminders for this occurrence are only reset if the due date actually
|
||||
moves, so renaming or re-noting a schedule will not re-send them.
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
@@ -124,6 +220,46 @@
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Recurrence blocks follow the chosen frequency (phase46/47).
|
||||
// Display only — the server re-validates and clears the unused blocks on save.
|
||||
(function () {
|
||||
var freq = document.getElementById('frequencySelect');
|
||||
var weekly = document.getElementById('weeklyBlock');
|
||||
var monthly = document.getElementById('monthlyBlock');
|
||||
var monthMode = document.getElementById('monthModeSelect');
|
||||
var domField = document.getElementById('dayOfMonthField');
|
||||
var nthField = document.getElementById('nthWeekdayField');
|
||||
var endBlock = document.getElementById('endDateBlock');
|
||||
var dueLabel = document.getElementById('dueDateLabel');
|
||||
var isEdit = {{ 'true' if schedule else 'false' }};
|
||||
var MONTHLY = ['monthly', 'quarterly', 'bi-annually', 'annually'];
|
||||
|
||||
function syncMonthMode() {
|
||||
if (!monthMode || !domField || !nthField) return;
|
||||
var nth = monthMode.value === 'nth_weekday';
|
||||
domField.style.display = nth ? 'none' : '';
|
||||
nthField.style.display = nth ? '' : 'none';
|
||||
}
|
||||
|
||||
function sync() {
|
||||
if (!freq) return;
|
||||
var v = freq.value;
|
||||
if (weekly) weekly.style.display = (v === 'weekly') ? '' : 'none';
|
||||
if (monthly) monthly.style.display = (MONTHLY.indexOf(v) !== -1) ? '' : 'none';
|
||||
// A one-time schedule has no end date — it closes when it is completed.
|
||||
if (endBlock) endBlock.style.display = (v === 'once') ? 'none' : '';
|
||||
if (dueLabel) {
|
||||
dueLabel.textContent = isEdit ? 'Next Due Date'
|
||||
: (v === 'once' ? 'Date' : 'Start Date');
|
||||
}
|
||||
syncMonthMode();
|
||||
}
|
||||
|
||||
if (freq) freq.addEventListener('change', sync);
|
||||
if (monthMode) monthMode.addEventListener('change', syncMonthMode);
|
||||
sync();
|
||||
})();
|
||||
|
||||
// Facility → Area cascade, reusing the existing inspections AJAX endpoint.
|
||||
(function () {
|
||||
var facilitySelect = document.getElementById('facilitySelect');
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
<tr>
|
||||
<th>Name</th><th>Template</th><th>Facility / Area</th>
|
||||
<th>Inspector</th><th>Frequency</th><th>Mode</th><th>Next Due</th>
|
||||
<th>Last Run</th><th>Status</th><th width="190"></th>
|
||||
<th>Ends</th><th>Last Run</th><th>Status</th><th width="190"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -45,7 +45,7 @@
|
||||
{% if s.area %}<span class="text-muted small">/ {{ s.area.name }}</span>{% endif %}
|
||||
</td>
|
||||
<td>{{ s.inspector.display_name if s.inspector else '—' }}</td>
|
||||
<td><span class="badge bg-secondary">{{ s.frequency|title }}</span></td>
|
||||
<td><span class="badge bg-secondary">{{ s.recurrence_label }}</span></td>
|
||||
<td>
|
||||
{% if s.mode == 'plan' %}
|
||||
<span class="badge bg-info text-dark" title="Inspector presses Start">Plan</span>
|
||||
@@ -59,11 +59,19 @@
|
||||
<span class="badge bg-danger ms-1">Overdue</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="small text-muted">
|
||||
{% if s.frequency == 'once' %}—
|
||||
{% elif s.end_date %}{{ s.end_date.strftime('%Y-%m-%d') }}
|
||||
{% else %}No end{% endif %}
|
||||
</td>
|
||||
<td class="small text-muted">
|
||||
{{ s.last_run_at.strftime('%Y-%m-%d %H:%M') if s.last_run_at else 'Never' }}
|
||||
</td>
|
||||
<td>
|
||||
{# "Ended" separates a schedule that reached its end date from one a
|
||||
manager switched off — both are inactive, for different reasons. #}
|
||||
{% if s.active %}<span class="badge bg-success">Active</span>
|
||||
{% elif s.is_expired %}<span class="badge bg-dark">Ended</span>
|
||||
{% else %}<span class="badge bg-secondary">Paused</span>{% endif %}
|
||||
</td>
|
||||
<td class="text-end">
|
||||
|
||||
Reference in New Issue
Block a user