feat: add report_date to monitoring sessions and update related functionality

fix: chart properly renders centered
This commit is contained in:
2026-03-27 22:18:50 +00:00
parent 95fedca8c9
commit 49bc625c1a
5 changed files with 192 additions and 62 deletions

View File

@@ -40,9 +40,9 @@
<!-- Day-of-week headers -->
<div class="grid grid-cols-7 border-b border-gray-100 dark:border-gray-700">
{% for day_name in ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] %}
{% for day_name in ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'] %}
<div class="py-2 text-center text-xs font-medium text-gray-400 dark:text-gray-500 uppercase tracking-wide
{% if loop.index >= 6 %}text-amber-500 dark:text-amber-400{% endif %}">
{% if loop.index == 1 or loop.index == 7 %}text-amber-500 dark:text-amber-400{% endif %}">
{{ day_name }}
</div>
{% endfor %}

View File

@@ -77,6 +77,12 @@
<dd class="font-medium text-indigo-600 dark:text-indigo-400 text-right text-xs" id="info-effective"></dd>
</div>
{% endif %}
<div class="flex justify-between">
<dt class="text-gray-500 dark:text-gray-400">Report Date</dt>
<dd class="font-medium text-gray-900 dark:text-white" id="info-report-date">
{{ report_date or '— (auto)' }}
</dd>
</div>
{% if session.started_at %}
<div class="flex justify-between">
<dt class="text-gray-500 dark:text-gray-400">Started</dt>
@@ -118,66 +124,107 @@
<!-- Edit Card -->
<div class="bg-white dark:bg-slate-800 rounded-xl border border-gray-200 dark:border-gray-700 p-5">
<h2 class="text-sm font-semibold text-gray-700 dark:text-gray-300 uppercase tracking-wide mb-3">Edit Session</h2>
<form id="edit-form" class="space-y-3" onsubmit="saveSession(event)">
<div>
<h2 class="text-sm font-semibold text-gray-700 dark:text-gray-300 uppercase tracking-wide mb-4">Edit Session</h2>
<form id="edit-form" onsubmit="saveSession(event)">
<!-- Label -->
<div class="mb-4">
<label class="block text-xs text-gray-500 dark:text-gray-400 mb-1">Label</label>
<input type="text" id="edit-label" name="session_label"
value="{{ session.session_label or '' }}"
placeholder="e.g. NRL-1 — Mon 3/24 — Night"
class="w-full text-sm bg-gray-50 dark:bg-slate-700 border border-gray-200 dark:border-gray-600 rounded-lg px-3 py-2 text-gray-900 dark:text-white focus:outline-none focus:border-seismo-orange">
</div>
<div>
<label class="block text-xs text-gray-500 dark:text-gray-400 mb-1">Period Type</label>
<select id="edit-period-type" name="period_type"
class="w-full text-sm bg-gray-50 dark:bg-slate-700 border border-gray-200 dark:border-gray-600 rounded-lg px-3 py-2 text-gray-900 dark:text-white focus:outline-none focus:border-seismo-orange">
<option value="">— Not Set —</option>
<option value="weekday_day" {% if session.period_type == 'weekday_day' %}selected{% endif %}>Weekday Day</option>
<option value="weekday_night" {% if session.period_type == 'weekday_night' %}selected{% endif %}>Weekday Night</option>
<option value="weekend_day" {% if session.period_type == 'weekend_day' %}selected{% endif %}>Weekend Day</option>
<option value="weekend_night" {% if session.period_type == 'weekend_night' %}selected{% endif %}>Weekend Night</option>
</select>
</div>
<div class="grid grid-cols-2 gap-2">
<div>
<label class="block text-xs text-gray-500 dark:text-gray-400 mb-1">Start Hour (023)</label>
<input type="number" min="0" max="23" id="edit-start-hour" name="period_start_hour"
value="{{ session.period_start_hour if session.period_start_hour is not none else '' }}"
placeholder="e.g. 19"
class="w-full text-sm bg-gray-50 dark:bg-slate-700 border border-gray-200 dark:border-gray-600 rounded-lg px-3 py-2 text-gray-900 dark:text-white focus:outline-none focus:border-seismo-orange">
</div>
<div>
<label class="block text-xs text-gray-500 dark:text-gray-400 mb-1">End Hour (023)</label>
<input type="number" min="0" max="23" id="edit-end-hour" name="period_end_hour"
value="{{ session.period_end_hour if session.period_end_hour is not none else '' }}"
placeholder="e.g. 7"
class="w-full text-sm bg-gray-50 dark:bg-slate-700 border border-gray-200 dark:border-gray-600 rounded-lg px-3 py-2 text-gray-900 dark:text-white focus:outline-none focus:border-seismo-orange">
<!-- Section: Required Recording Window -->
<div class="mb-4 p-3 bg-indigo-50 dark:bg-indigo-900/20 rounded-lg border border-indigo-100 dark:border-indigo-800">
<p class="text-xs font-semibold text-indigo-700 dark:text-indigo-300 mb-0.5">Required Recording Window</p>
<p class="text-xs text-indigo-500 dark:text-indigo-400 mb-3">The hours that count for reports. Only data within this window is included.</p>
<div class="space-y-2">
<div>
<label class="block text-xs text-gray-500 dark:text-gray-400 mb-1">Period Type</label>
<select id="edit-period-type" name="period_type"
onchange="fillPeriodDefaults()"
class="w-full text-sm bg-white dark:bg-slate-700 border border-gray-200 dark:border-gray-600 rounded-lg px-3 py-2 text-gray-900 dark:text-white focus:outline-none focus:border-seismo-orange">
<option value="">— Not Set —</option>
<option value="weekday_day" {% if session.period_type == 'weekday_day' %}selected{% endif %}>Weekday Day</option>
<option value="weekday_night" {% if session.period_type == 'weekday_night' %}selected{% endif %}>Weekday Night</option>
<option value="weekend_day" {% if session.period_type == 'weekend_day' %}selected{% endif %}>Weekend Day</option>
<option value="weekend_night" {% if session.period_type == 'weekend_night' %}selected{% endif %}>Weekend Night</option>
</select>
</div>
<div class="grid grid-cols-2 gap-2">
<div>
<label class="block text-xs text-gray-500 dark:text-gray-400 mb-1">From (hour)</label>
<div class="relative">
<input type="number" min="0" max="23" id="edit-start-hour" name="period_start_hour"
value="{{ session.period_start_hour if session.period_start_hour is not none else '' }}"
placeholder="e.g. 19"
oninput="updateWindowPreview()"
class="w-full text-sm bg-white dark:bg-slate-700 border border-gray-200 dark:border-gray-600 rounded-lg px-3 py-2 text-gray-900 dark:text-white focus:outline-none focus:border-seismo-orange">
</div>
</div>
<div>
<label class="block text-xs text-gray-500 dark:text-gray-400 mb-1">To (hour)</label>
<input type="number" min="0" max="23" id="edit-end-hour" name="period_end_hour"
value="{{ session.period_end_hour if session.period_end_hour is not none else '' }}"
placeholder="e.g. 7"
oninput="updateWindowPreview()"
class="w-full text-sm bg-white dark:bg-slate-700 border border-gray-200 dark:border-gray-600 rounded-lg px-3 py-2 text-gray-900 dark:text-white focus:outline-none focus:border-seismo-orange">
</div>
</div>
<!-- Live preview -->
<div id="window-preview" class="text-xs font-medium text-indigo-600 dark:text-indigo-300 min-h-[1rem]">
{% if session.period_start_hour is not none and session.period_end_hour is not none %}
{% set sh = session.period_start_hour %}
{% set eh = session.period_end_hour %}
Window: {{ (sh % 12) or 12 }}:00 {{ 'AM' if sh < 12 else 'PM' }} {{ (eh % 12) or 12 }}:00 {{ 'AM' if eh < 12 else 'PM' }}{% if eh <= sh %} (crosses midnight){% endif %}
{% endif %}
</div>
<div class="mt-2">
<label class="block text-xs text-gray-500 dark:text-gray-400 mb-1">
Target Date <span class="text-gray-400">(optional — day sessions only)</span>
</label>
<input type="date" id="edit-report-date" name="report_date"
value="{{ report_date or '' }}"
class="w-full text-sm bg-white dark:bg-slate-700 border border-gray-200 dark:border-gray-600 rounded-lg px-3 py-2 text-gray-900 dark:text-white focus:outline-none focus:border-seismo-orange">
<p class="text-xs text-gray-400 dark:text-gray-500 mt-1">Leave blank to auto-select the last day with data in the window.</p>
</div>
</div>
</div>
<div>
<label class="block text-xs text-gray-500 dark:text-gray-400 mb-1">Start Time (local)</label>
<input type="datetime-local" id="edit-started-at" name="started_at"
value="{{ session.started_at|local_datetime_input if session.started_at else '' }}"
class="w-full text-sm bg-gray-50 dark:bg-slate-700 border border-gray-200 dark:border-gray-600 rounded-lg px-3 py-2 text-gray-900 dark:text-white focus:outline-none focus:border-seismo-orange">
<!-- Section: Device On/Off Times -->
<div class="mb-4 p-3 bg-gray-50 dark:bg-slate-700/40 rounded-lg border border-gray-200 dark:border-gray-600">
<p class="text-xs font-semibold text-gray-600 dark:text-gray-400 mb-0.5">Device On/Off Times</p>
<p class="text-xs text-gray-400 dark:text-gray-500 mb-3">When the meter was actually running. Usually set automatically from the data file.</p>
<div class="space-y-2">
<div>
<label class="block text-xs text-gray-500 dark:text-gray-400 mb-1">Powered on</label>
<input type="datetime-local" id="edit-started-at" name="started_at"
value="{{ session.started_at|local_datetime_input if session.started_at else '' }}"
class="w-full text-sm bg-white dark:bg-slate-700 border border-gray-200 dark:border-gray-600 rounded-lg px-3 py-2 text-gray-900 dark:text-white focus:outline-none focus:border-seismo-orange">
</div>
<div>
<label class="block text-xs text-gray-500 dark:text-gray-400 mb-1">Powered off</label>
<input type="datetime-local" id="edit-stopped-at" name="stopped_at"
value="{{ session.stopped_at|local_datetime_input if session.stopped_at else '' }}"
class="w-full text-sm bg-white dark:bg-slate-700 border border-gray-200 dark:border-gray-600 rounded-lg px-3 py-2 text-gray-900 dark:text-white focus:outline-none focus:border-seismo-orange">
</div>
</div>
</div>
<div>
<label class="block text-xs text-gray-500 dark:text-gray-400 mb-1">End Time (local)</label>
<input type="datetime-local" id="edit-stopped-at" name="stopped_at"
value="{{ session.stopped_at|local_datetime_input if session.stopped_at else '' }}"
class="w-full text-sm bg-gray-50 dark:bg-slate-700 border border-gray-200 dark:border-gray-600 rounded-lg px-3 py-2 text-gray-900 dark:text-white focus:outline-none focus:border-seismo-orange">
</div>
<div class="flex gap-2 pt-1">
<div class="flex gap-2">
<button type="submit"
class="flex-1 text-sm py-2 bg-seismo-orange text-white rounded-lg hover:bg-orange-600 transition-colors font-medium">
Save Changes
</button>
<button type="button" onclick="fillPeriodDefaults()"
class="text-sm py-2 px-3 border border-gray-200 dark:border-gray-600 rounded-lg text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-slate-700 transition-colors"
title="Fill default hours for selected period type">
Defaults
</button>
</div>
<div id="save-status" class="hidden text-xs text-center pt-1"></div>
<div id="save-status" class="hidden text-xs text-center pt-2"></div>
</form>
</div>
</div>
@@ -307,8 +354,23 @@ function fillPeriodDefaults() {
document.getElementById('edit-start-hour').value = defaults.start;
document.getElementById('edit-end-hour').value = defaults.end;
}
updateWindowPreview();
}
function updateWindowPreview() {
const sh = parseInt(document.getElementById('edit-start-hour').value, 10);
const eh = parseInt(document.getElementById('edit-end-hour').value, 10);
const el = document.getElementById('window-preview');
if (!el) return;
if (isNaN(sh) || isNaN(eh)) { el.textContent = ''; return; }
function fmt(h) { return `${h % 12 || 12}:00 ${h < 12 ? 'AM' : 'PM'}`; }
const crosses = eh <= sh;
el.textContent = `Window: ${fmt(sh)}${fmt(eh)}${crosses ? ' (crosses midnight)' : ''}`;
}
// Run once on load to populate preview if values already set
document.addEventListener('DOMContentLoaded', updateWindowPreview);
async function saveSession(e) {
e.preventDefault();
const status = document.getElementById('save-status');
@@ -330,6 +392,9 @@ async function saveSession(e) {
payload.period_start_hour = sh !== '' ? parseInt(sh, 10) : null;
payload.period_end_hour = eh !== '' ? parseInt(eh, 10) : null;
const rd = form.report_date.value;
payload.report_date = rd || null;
const sa = form.started_at.value;
if (sa) payload.started_at = sa;
@@ -354,6 +419,7 @@ async function saveSession(e) {
weekday_day: 'Weekday Day', weekday_night: 'Weekday Night',
weekend_day: 'Weekend Day', weekend_night: 'Weekend Night'
}[result.period_type] || '—';
document.getElementById('info-report-date').textContent = result.report_date || '— (auto)';
status.className = 'text-xs text-center pt-1 text-green-600 dark:text-green-400';
status.textContent = 'Saved!';