fix: separate days now are in separate .xlsx files, NRLs still 1 per sheet.
add: rebuild script for prod. fix: Improved data parsing, now filters out unneeded Lp files and .xlsx files.
This commit is contained in:
@@ -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" />
|
||||
<button onclick="submitUploadAll()"
|
||||
<span id="upload-all-file-count" class="text-xs text-gray-500 dark:text-gray-400 hidden"></span>
|
||||
<button id="upload-all-btn" onclick="submitUploadAll()"
|
||||
class="px-4 py-1.5 text-sm bg-green-600 text-white rounded-lg hover:bg-green-700 transition-colors">
|
||||
Import
|
||||
</button>
|
||||
<button onclick="toggleUploadAll()"
|
||||
<button id="upload-all-cancel-btn" onclick="toggleUploadAll()"
|
||||
class="px-4 py-1.5 text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white transition-colors">
|
||||
Cancel
|
||||
</button>
|
||||
<span id="upload-all-status" class="text-sm hidden"></span>
|
||||
</div>
|
||||
<!-- Progress bar -->
|
||||
<div id="upload-all-progress-wrap" class="hidden mt-3">
|
||||
<div class="flex justify-between text-xs text-gray-500 dark:text-gray-400 mb-1">
|
||||
<span id="upload-all-progress-label">Uploading…</span>
|
||||
</div>
|
||||
<div class="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-2">
|
||||
<div id="upload-all-progress-bar"
|
||||
class="bg-green-500 h-2 rounded-full transition-all duration-300"
|
||||
style="width: 0%"></div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Result summary -->
|
||||
<div id="upload-all-results" class="hidden mt-3 text-sm space-y-1"></div>
|
||||
</div>
|
||||
@@ -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 += '<div class="font-medium text-gray-700 dark:text-gray-300 mb-1">Sessions created:</div>';
|
||||
html += '<ul class="space-y-0.5 ml-2">';
|
||||
for (const sess of data.sessions) {
|
||||
html += `<li class="text-xs text-gray-600 dark:text-gray-400">\u2022 <span class="font-medium">${sess.location_name}</span> — ${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 += '</li>';
|
||||
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 += '<div class="font-medium text-gray-700 dark:text-gray-300 mb-1">Sessions created:</div>';
|
||||
html += '<ul class="space-y-0.5 ml-2">';
|
||||
for (const sess of data.sessions) {
|
||||
html += `<li class="text-xs text-gray-600 dark:text-gray-400">\u2022 <span class="font-medium">${sess.location_name}</span> — ${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 += '</li>';
|
||||
}
|
||||
html += '</ul>';
|
||||
}
|
||||
html += '</ul>';
|
||||
if (data.unmatched_folders && data.unmatched_folders.length) {
|
||||
html += `<div class="mt-2 text-xs text-amber-600 dark:text-amber-400">\u26a0 Unmatched folders (no NRL location found): ${data.unmatched_folders.join(', ')}</div>`;
|
||||
}
|
||||
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 += `<div class="mt-2 text-xs text-amber-600 dark:text-amber-400">\u26a0 Unmatched folders (no NRL location found): ${data.unmatched_folders.join(', ')}</div>`;
|
||||
}
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user