feat(reports): add "Night Report" button to sound project header
Sound projects only: a Night Report button next to "Generate Combined Report" opens a small modal (pick night + optional baseline range) that opens the rendered report (/reports/nightly/view) in a new tab. Defaults the night to last night; baseline is optional. Verified the header partial renders and the button is gated to sound_monitoring (hidden on vibration-only projects); modal + JS wired. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -74,6 +74,14 @@
|
|||||||
</svg>
|
</svg>
|
||||||
Generate Combined Report
|
Generate Combined Report
|
||||||
</a>
|
</a>
|
||||||
|
<button onclick="openNightReportModal()"
|
||||||
|
title="Last night's noise vs baseline, per location (FTP report pipeline)"
|
||||||
|
class="px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 transition-colors flex items-center gap-2 text-sm">
|
||||||
|
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"></path>
|
||||||
|
</svg>
|
||||||
|
Night Report
|
||||||
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<button onclick="openMergeModal()"
|
<button onclick="openMergeModal()"
|
||||||
title="Merge this project into another (consolidates duplicates)"
|
title="Merge this project into another (consolidates duplicates)"
|
||||||
@@ -87,6 +95,65 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Night Report Modal -->
|
||||||
|
<div id="night-report-modal" class="hidden fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm">
|
||||||
|
<div class="bg-white dark:bg-slate-800 rounded-xl shadow-2xl w-full max-w-md mx-4">
|
||||||
|
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700 flex items-center justify-between">
|
||||||
|
<h3 class="text-lg font-semibold text-gray-900 dark:text-white">Night Report</h3>
|
||||||
|
<button onclick="closeNightReportModal()" class="text-gray-400 hover:text-gray-600 dark:hover:text-gray-200">
|
||||||
|
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path></svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="px-6 py-5 space-y-4">
|
||||||
|
<p class="text-xs text-gray-500 dark:text-gray-400">Last night's noise (7 PM–7 AM) vs a baseline range, per location. Opens in a new tab.</p>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Night (evening date)</label>
|
||||||
|
<input type="date" id="nr-night-date" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-slate-700 text-gray-900 dark:text-white text-sm">
|
||||||
|
</div>
|
||||||
|
<div class="grid grid-cols-2 gap-3">
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Baseline start <span class="text-gray-400 font-normal">(optional)</span></label>
|
||||||
|
<input type="date" id="nr-baseline-start" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-slate-700 text-gray-900 dark:text-white text-sm">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Baseline end</label>
|
||||||
|
<input type="date" id="nr-baseline-end" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-slate-700 text-gray-900 dark:text-white text-sm">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="px-6 py-4 border-t border-gray-200 dark:border-gray-700 flex justify-end gap-2">
|
||||||
|
<button onclick="closeNightReportModal()" class="px-4 py-2 border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors text-sm">Cancel</button>
|
||||||
|
<button onclick="viewNightReport('{{ project.id }}')" class="px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 transition-colors text-sm">View Report</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
function openNightReportModal() {
|
||||||
|
var el = document.getElementById('nr-night-date');
|
||||||
|
if (el && !el.value) { // default to last night
|
||||||
|
var d = new Date(); d.setDate(d.getDate() - 1);
|
||||||
|
el.value = d.getFullYear() + '-'
|
||||||
|
+ String(d.getMonth() + 1).padStart(2, '0') + '-'
|
||||||
|
+ String(d.getDate()).padStart(2, '0');
|
||||||
|
}
|
||||||
|
document.getElementById('night-report-modal').classList.remove('hidden');
|
||||||
|
}
|
||||||
|
function closeNightReportModal() {
|
||||||
|
document.getElementById('night-report-modal').classList.add('hidden');
|
||||||
|
}
|
||||||
|
function viewNightReport(projectId) {
|
||||||
|
var night = document.getElementById('nr-night-date').value;
|
||||||
|
var bs = document.getElementById('nr-baseline-start').value;
|
||||||
|
var be = document.getElementById('nr-baseline-end').value;
|
||||||
|
if (!night) { alert('Pick a night (evening date).'); return; }
|
||||||
|
if ((bs && !be) || (be && !bs)) { alert('Provide both baseline dates, or leave both empty.'); return; }
|
||||||
|
var url = '/api/projects/' + projectId + '/reports/nightly/view?night_date=' + night;
|
||||||
|
if (bs && be) url += '&baseline_start=' + bs + '&baseline_end=' + be;
|
||||||
|
window.open(url, '_blank');
|
||||||
|
closeNightReportModal();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<!-- Merge Modal —
|
<!-- Merge Modal —
|
||||||
min-h on the body ensures the typeahead dropdown has room to render
|
min-h on the body ensures the typeahead dropdown has room to render
|
||||||
below the input without forcing the operator to scroll inside the
|
below the input without forcing the operator to scroll inside the
|
||||||
|
|||||||
Reference in New Issue
Block a user