Data Files
-
-
{{ file_count }} files
+
+
{{ file_count }} files
+
+
+
+
+
+
+
Upload SD Card Data
+
+ Select a ZIP file, or select all files from inside an Auto_#### folder. File types (.rnd, .rnh) are auto-detected.
+
+
+
+
+
+
@@ -559,5 +591,64 @@ document.getElementById('assign-modal')?.addEventListener('click', function(e) {
closeAssignModal();
}
});
+
+// ── Upload Data ─────────────────────────────────────────────────────────────
+
+function toggleUploadPanel() {
+ const panel = document.getElementById('upload-panel');
+ const status = document.getElementById('upload-status');
+ panel.classList.toggle('hidden');
+ // Reset status when reopening
+ if (!panel.classList.contains('hidden')) {
+ status.textContent = '';
+ status.className = 'text-sm hidden';
+ document.getElementById('upload-input').value = '';
+ }
+}
+
+async function submitUpload() {
+ const input = document.getElementById('upload-input');
+ const status = document.getElementById('upload-status');
+
+ if (!input.files.length) {
+ alert('Please select files to upload.');
+ return;
+ }
+
+ const formData = new FormData();
+ for (const file of input.files) {
+ formData.append('files', file);
+ }
+
+ status.textContent = 'Uploading\u2026';
+ status.className = 'text-sm text-gray-500';
+
+ try {
+ const response = await fetch(
+ `/api/projects/${projectId}/nrl/${locationId}/upload-data`,
+ { method: 'POST', body: formData }
+ );
+ const data = await response.json();
+
+ if (response.ok) {
+ const parts = [`Imported ${data.files_imported} file${data.files_imported !== 1 ? 's' : ''}`];
+ if (data.leq_files || data.lp_files) {
+ parts.push(`(${data.leq_files} Leq, ${data.lp_files} Lp)`);
+ }
+ if (data.store_name) parts.push(`\u2014 ${data.store_name}`);
+ status.textContent = parts.join(' ');
+ status.className = 'text-sm text-green-600 dark:text-green-400';
+ input.value = '';
+ // Refresh the file list
+ htmx.trigger(document.getElementById('data-files-list'), 'load');
+ } else {
+ status.textContent = `Error: ${data.detail || 'Upload failed'}`;
+ status.className = 'text-sm text-red-600 dark:text-red-400';
+ }
+ } catch (err) {
+ status.textContent = `Error: ${err.message}`;
+ status.className = 'text-sm text-red-600 dark:text-red-400';
+ }
+}
{% endblock %}
diff --git a/templates/partials/projects/file_list.html b/templates/partials/projects/file_list.html
index 979a8ed..3b5b382 100644
--- a/templates/partials/projects/file_list.html
+++ b/templates/partials/projects/file_list.html
@@ -151,9 +151,9 @@
-
No files downloaded yet
+
No data files yet
- Files will appear here once they are downloaded from the sound level meter
+ Files appear here after an FTP download from a connected meter, or after uploading SD card data manually.
{% endif %}
diff --git a/templates/partials/projects/schedule_oneoff.html b/templates/partials/projects/schedule_oneoff.html
index c3e4e4d..4b8dba4 100644
--- a/templates/partials/projects/schedule_oneoff.html
+++ b/templates/partials/projects/schedule_oneoff.html
@@ -5,7 +5,7 @@
One-Off Recording
- Schedule a single recording session with a specific start and end time.
+ Schedule a single monitoring session with a specific start and end time.
Duration can be between 15 minutes and 24 hours.
diff --git a/templates/partials/projects/session_list.html b/templates/partials/projects/session_list.html
index 51af907..957f431 100644
--- a/templates/partials/projects/session_list.html
+++ b/templates/partials/projects/session_list.html
@@ -1,4 +1,4 @@
-
+
{% if sessions %}
{% for item in sessions %}
@@ -87,7 +87,7 @@
-
No recording sessions yet
+
No monitoring sessions yet
Schedule a session to get started
{% endif %}
@@ -99,7 +99,7 @@ function viewSession(sessionId) {
}
function stopRecording(sessionId) {
- if (!confirm('Stop this recording session?')) return;
+ if (!confirm('Stop this monitoring session?')) return;
// TODO: Implement stop recording API call
alert('Stop recording API coming soon for session: ' + sessionId);
diff --git a/templates/projects/detail.html b/templates/projects/detail.html
index 8fcb927..dc33a09 100644
--- a/templates/projects/detail.html
+++ b/templates/projects/detail.html
@@ -53,7 +53,7 @@
-
+
-
Recording Sessions
+
Monitoring Sessions
- Single recording session with a specific start and end date/time (15 min - 24 hrs).
+ Single monitoring session with a specific start and end date/time (15 min - 24 hrs).
@@ -721,7 +721,7 @@ async function loadProjectDetails() {
document.getElementById('locations-header').textContent = 'Noise Recording Locations';
document.getElementById('add-location-label').textContent = 'Add NRL';
}
- // Recording Sessions and Data Files tabs are SLM-only
+ // Monitoring Sessions and Data Files tabs are SLM-only
document.getElementById('sessions-tab-btn').classList.toggle('hidden', !isSoundProject);
document.getElementById('data-tab-btn').classList.toggle('hidden', !isSoundProject);
@@ -808,6 +808,10 @@ function openLocationModal(defaultType) {
locationTypeSelect.value = 'sound';
locationTypeSelect.disabled = true;
if (locationTypeWrapper) locationTypeWrapper.classList.add('hidden');
+ } else if (projectTypeId === 'vibration_monitoring') {
+ locationTypeSelect.value = 'vibration';
+ locationTypeSelect.disabled = true;
+ if (locationTypeWrapper) locationTypeWrapper.classList.add('hidden');
} else {
locationTypeSelect.disabled = false;
if (locationTypeWrapper) locationTypeWrapper.classList.remove('hidden');
@@ -832,6 +836,10 @@ function openEditLocationModal(button) {
locationTypeSelect.value = 'sound';
locationTypeSelect.disabled = true;
if (locationTypeWrapper) locationTypeWrapper.classList.add('hidden');
+ } else if (projectTypeId === 'vibration_monitoring') {
+ locationTypeSelect.value = 'vibration';
+ locationTypeSelect.disabled = true;
+ if (locationTypeWrapper) locationTypeWrapper.classList.add('hidden');
} else {
locationTypeSelect.disabled = false;
if (locationTypeWrapper) locationTypeWrapper.classList.remove('hidden');
@@ -855,6 +863,8 @@ document.getElementById('location-form').addEventListener('submit', async functi
let locationType = document.getElementById('location-type').value;
if (projectTypeId === 'sound_monitoring') {
locationType = 'sound';
+ } else if (projectTypeId === 'vibration_monitoring') {
+ locationType = 'vibration';
}
try {
diff --git a/templates/projects/overview.html b/templates/projects/overview.html
index ef1d50a..6287445 100644
--- a/templates/projects/overview.html
+++ b/templates/projects/overview.html
@@ -78,9 +78,16 @@
-
-
Create New Project
-
Select a project type and configure settings
+
+
+
Create New Project
+
Select a project type and configure settings
+
+
+
+
+
diff --git a/templates/roster.html b/templates/roster.html
index 0ebc69d..0db9605 100644
--- a/templates/roster.html
+++ b/templates/roster.html
@@ -1504,7 +1504,7 @@
`• Unit roster entry\n` +
`• All history records\n` +
`• Project assignments\n` +
- `• Recording sessions\n` +
+ `• Monitoring sessions\n` +
`• Modem references\n\n` +
`This action cannot be undone.`
);