@@ -238,7 +238,7 @@ async function downloadCombinedReport() {
const btn = document.getElementById('download-btn');
const originalText = btn.innerHTML;
btn.disabled = true;
- btn.innerHTML = ' Generating...';
+ btn.innerHTML = ' Generating ZIP...';
try {
const locations = allLocationData.map(function(loc) {
@@ -268,7 +268,7 @@ async function downloadCombinedReport() {
a.href = url;
const contentDisposition = response.headers.get('Content-Disposition');
- let filename = 'combined_report.xlsx';
+ let filename = 'combined_reports.zip';
if (contentDisposition) {
const match = contentDisposition.match(/filename="(.+)"/);
if (match) filename = match[1];
diff --git a/templates/nrl_detail.html b/templates/nrl_detail.html
index edebe16..cafc12a 100644
--- a/templates/nrl_detail.html
+++ b/templates/nrl_detail.html
@@ -385,16 +385,27 @@
file:text-sm file:font-medium file:bg-seismo-orange file:text-white
hover:file:bg-seismo-navy file:cursor-pointer" />
-
-
+
+
{
+ if (e.lengthComputable) {
+ const pct = Math.round((e.loaded / e.total) * 100);
+ progressBar.style.width = pct + '%';
+ progressLabel.textContent = `Uploading ${fileCount} file${fileCount !== 1 ? 's' : ''}\u2026 ${pct}%`;
+ }
+ });
+
+ xhr.upload.addEventListener('load', () => {
+ progressBar.style.width = '100%';
+ progressLabel.textContent = 'Processing files on server\u2026';
+ });
+
+ xhr.addEventListener('load', () => {
+ progressWrap.classList.add('hidden');
+ btn.disabled = false;
+ btn.textContent = 'Import Files';
+ btn.classList.remove('opacity-60', 'cursor-not-allowed');
+ cancelBtn.disabled = false;
+ cancelBtn.classList.remove('opacity-40', 'cursor-not-allowed');
+
+ try {
+ const data = JSON.parse(xhr.responseText);
+ if (xhr.status >= 200 && xhr.status < 300) {
+ 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 = '';
+ 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';
}
- 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'}`;
+ } catch {
+ status.textContent = 'Error: Unexpected server response';
status.className = 'text-sm text-red-600 dark:text-red-400';
}
- } catch (err) {
- status.textContent = `Error: ${err.message}`;
+ });
+
+ xhr.addEventListener('error', () => {
+ progressWrap.classList.add('hidden');
+ btn.disabled = false;
+ btn.textContent = 'Import Files';
+ btn.classList.remove('opacity-60', 'cursor-not-allowed');
+ cancelBtn.disabled = false;
+ cancelBtn.classList.remove('opacity-40', 'cursor-not-allowed');
+ status.textContent = 'Error: Network error during upload';
status.className = 'text-sm text-red-600 dark:text-red-400';
- }
+ });
+
+ xhr.open('POST', `/api/projects/${projectId}/nrl/${locationId}/upload-data`);
+ xhr.send(formData);
}
{% endblock %}
diff --git a/templates/projects/detail.html b/templates/projects/detail.html
index 40110ad..784cd55 100644
--- a/templates/projects/detail.html
+++ b/templates/projects/detail.html
@@ -264,16 +264,28 @@
file:mr-3 file:py-1.5 file:px-3 file:rounded-lg file:border-0
file:text-sm file:font-medium file:bg-seismo-orange file:text-white
hover:file:bg-seismo-navy file:cursor-pointer" />
-
+
+
@@ -1642,75 +1654,148 @@ function toggleUploadAll() {
document.getElementById('upload-all-results').classList.add('hidden');
document.getElementById('upload-all-results').innerHTML = '';
document.getElementById('upload-all-input').value = '';
+ document.getElementById('upload-all-file-count').classList.add('hidden');
+ document.getElementById('upload-all-progress-wrap').classList.add('hidden');
+ document.getElementById('upload-all-progress-bar').style.width = '0%';
}
}
-async function submitUploadAll() {
+// Show file count and filter info when folder is selected
+document.getElementById('upload-all-input').addEventListener('change', function() {
+ const countEl = document.getElementById('upload-all-file-count');
+ const total = this.files.length;
+ if (!total) { countEl.classList.add('hidden'); return; }
+ const wanted = Array.from(this.files).filter(_isWantedFile).length;
+ countEl.textContent = `${wanted} of ${total} files will be uploaded (Leq + .rnh only)`;
+ countEl.classList.remove('hidden');
+});
+
+function _isWantedFile(f) {
+ const n = (f.webkitRelativePath || f.name).toLowerCase();
+ const base = n.split('/').pop();
+ if (base.endsWith('.rnh')) return true;
+ if (base.endsWith('.rnd')) {
+ if (base.includes('_leq_')) return true; // NL-43 Leq
+ if (base.startsWith('au2_')) return true; // AU2/NL-23 format
+ if (!base.includes('_lp')) return true; // unknown format — keep
+ }
+ return false;
+}
+
+function submitUploadAll() {
const input = document.getElementById('upload-all-input');
const status = document.getElementById('upload-all-status');
const resultsEl = document.getElementById('upload-all-results');
+ const btn = document.getElementById('upload-all-btn');
+ const cancelBtn = document.getElementById('upload-all-cancel-btn');
+ const progressWrap = document.getElementById('upload-all-progress-wrap');
+ const progressBar = document.getElementById('upload-all-progress-bar');
+ const progressLabel = document.getElementById('upload-all-progress-label');
if (!input.files.length) {
alert('Please select a folder to upload.');
return;
}
+ // Filter client-side — only send Leq .rnd and .rnh files
+ const filesToSend = Array.from(input.files).filter(_isWantedFile);
+ if (!filesToSend.length) {
+ alert('No Leq .rnd or .rnh files found in selected folder.');
+ return;
+ }
+
const formData = new FormData();
- for (const f of input.files) {
- // webkitRelativePath gives the path relative to the selected folder root
+ for (const f of filesToSend) {
formData.append('files', f);
formData.append('paths', f.webkitRelativePath || f.name);
}
- status.textContent = `Uploading ${input.files.length} files\u2026`;
- status.className = 'text-sm text-gray-500';
+ // Disable controls and show progress
+ btn.disabled = true;
+ btn.textContent = 'Uploading\u2026';
+ btn.classList.add('opacity-60', 'cursor-not-allowed');
+ cancelBtn.disabled = true;
+ cancelBtn.classList.add('opacity-40', 'cursor-not-allowed');
+ status.className = 'text-sm hidden';
resultsEl.classList.add('hidden');
+ progressWrap.classList.remove('hidden');
+ progressBar.style.width = '0%';
+ progressLabel.textContent = `Uploading ${filesToSend.length} files\u2026`;
- try {
- const response = await fetch(
- `/api/projects/{{ project_id }}/upload-all`,
- { method: 'POST', body: formData }
- );
- const data = await response.json();
+ const xhr = new XMLHttpRequest();
- if (response.ok) {
- const s = data.sessions_created;
- const f = data.files_imported;
- status.textContent = `\u2713 Imported ${f} file${f !== 1 ? 's' : ''} across ${s} session${s !== 1 ? 's' : ''}`;
- status.className = 'text-sm text-green-600 dark:text-green-400';
- input.value = '';
+ xhr.upload.addEventListener('progress', (e) => {
+ if (e.lengthComputable) {
+ const pct = Math.round((e.loaded / e.total) * 100);
+ progressBar.style.width = pct + '%';
+ progressLabel.textContent = `Uploading ${filesToSend.length} files\u2026 ${pct}%`;
+ }
+ });
- // Build results summary
- let html = '';
- if (data.sessions && data.sessions.length) {
- html += 'Sessions created:
';
- html += '';
- for (const sess of data.sessions) {
- html += `- \u2022 ${sess.location_name} — ${sess.files} files`;
- if (sess.leq_files || sess.lp_files) html += ` (${sess.leq_files} Leq, ${sess.lp_files} Lp)`;
- if (sess.store_name) html += ` — ${sess.store_name}`;
- html += '
';
+ xhr.upload.addEventListener('load', () => {
+ progressBar.style.width = '100%';
+ progressLabel.textContent = 'Processing files on server\u2026';
+ });
+
+ function _resetControls() {
+ progressWrap.classList.add('hidden');
+ btn.disabled = false;
+ btn.textContent = 'Import';
+ btn.classList.remove('opacity-60', 'cursor-not-allowed');
+ cancelBtn.disabled = false;
+ cancelBtn.classList.remove('opacity-40', 'cursor-not-allowed');
+ }
+
+ xhr.addEventListener('load', () => {
+ _resetControls();
+ try {
+ const data = JSON.parse(xhr.responseText);
+ if (xhr.status >= 200 && xhr.status < 300) {
+ const s = data.sessions_created;
+ const f = data.files_imported;
+ status.textContent = `\u2713 Imported ${f} file${f !== 1 ? 's' : ''} across ${s} session${s !== 1 ? 's' : ''}`;
+ status.className = 'text-sm text-green-600 dark:text-green-400';
+ input.value = '';
+ document.getElementById('upload-all-file-count').classList.add('hidden');
+
+ let html = '';
+ if (data.sessions && data.sessions.length) {
+ html += 'Sessions created:
';
+ html += '';
+ for (const sess of data.sessions) {
+ html += `- \u2022 ${sess.location_name} — ${sess.files} files`;
+ if (sess.leq_files || sess.lp_files) html += ` (${sess.leq_files} Leq, ${sess.lp_files} Lp)`;
+ if (sess.store_name) html += ` — ${sess.store_name}`;
+ html += '
';
+ }
+ html += '
';
}
- html += '
';
+ if (data.unmatched_folders && data.unmatched_folders.length) {
+ html += `\u26a0 Unmatched folders (no NRL location found): ${data.unmatched_folders.join(', ')}
`;
+ }
+ if (html) {
+ resultsEl.innerHTML = html;
+ resultsEl.classList.remove('hidden');
+ }
+ htmx.trigger(document.getElementById('unified-files'), 'refresh');
+ } else {
+ status.textContent = `Error: ${data.detail || 'Upload failed'}`;
+ status.className = 'text-sm text-red-600 dark:text-red-400';
}
- if (data.unmatched_folders && data.unmatched_folders.length) {
- html += `\u26a0 Unmatched folders (no NRL location found): ${data.unmatched_folders.join(', ')}
`;
- }
- if (html) {
- resultsEl.innerHTML = html;
- resultsEl.classList.remove('hidden');
- }
-
- // Refresh the unified files view
- htmx.trigger(document.getElementById('unified-files'), 'refresh');
- } else {
- status.textContent = `Error: ${data.detail || 'Upload failed'}`;
+ } catch {
+ status.textContent = 'Error: Unexpected server response';
status.className = 'text-sm text-red-600 dark:text-red-400';
}
- } catch (err) {
- status.textContent = `Error: ${err.message}`;
+ });
+
+ xhr.addEventListener('error', () => {
+ _resetControls();
+ status.textContent = 'Error: Network error during upload';
status.className = 'text-sm text-red-600 dark:text-red-400';
- }
+ });
+
+ xhr.open('POST', `/api/projects/{{ project_id }}/upload-all`);
+ xhr.send(formData);
}
// Load project details on page load and restore active tab from URL hash