fix(reports): resolve loadFTPFiles collision breaking Browse Files on the NRL tab

ftp_browser.html and slm_live_view.html are both loaded on the per-NRL detail page (Data Files + Command Center tabs) and each defined loadFTPFiles / downloadToServer / downloadFTPFile / enableFTP / formatFileSize as globals — last to load won. 'Browse Files' then called slm_live_view's loadFTPFiles, which renders into the hidden Command Center's #ftp-files-list, so the FTP request fired but nothing appeared. Prefix ftp_browser's five colliding functions with fb* so each partial keeps its own. (Element IDs don't collide: per-unit vs fixed.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-15 18:58:34 +00:00
parent aa21c81c2e
commit 1801d4eb74
+14 -14
View File
@@ -32,7 +32,7 @@
</svg> </svg>
Settings Settings
</button> </button>
<button onclick="enableFTP('{{ unit_item.unit.id }}')" <button onclick="fbEnableFTP('{{ unit_item.unit.id }}')"
id="enable-ftp-{{ unit_item.unit.id }}" id="enable-ftp-{{ unit_item.unit.id }}"
class="px-3 py-1 text-xs bg-green-600 text-white rounded-lg hover:bg-green-700 transition-colors" class="px-3 py-1 text-xs bg-green-600 text-white rounded-lg hover:bg-green-700 transition-colors"
disabled> disabled>
@@ -44,7 +44,7 @@
disabled> disabled>
Disable FTP Disable FTP
</button> </button>
<button onclick="loadFTPFiles('{{ unit_item.unit.id }}', '/NL-43')" <button onclick="fbLoadFTPFiles('{{ unit_item.unit.id }}', '/NL-43')"
id="browse-ftp-{{ unit_item.unit.id }}" id="browse-ftp-{{ unit_item.unit.id }}"
class="px-3 py-1 text-xs bg-seismo-orange text-white rounded-lg hover:bg-seismo-navy transition-colors" class="px-3 py-1 text-xs bg-seismo-orange text-white rounded-lg hover:bg-seismo-navy transition-colors"
disabled> disabled>
@@ -61,7 +61,7 @@
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z"></path> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z"></path>
</svg> </svg>
<span id="current-path-{{ unit_item.unit.id }}" class="text-sm font-mono text-gray-600 dark:text-gray-400">/NL-43</span> <span id="current-path-{{ unit_item.unit.id }}" class="text-sm font-mono text-gray-600 dark:text-gray-400">/NL-43</span>
<button onclick="loadFTPFiles('{{ unit_item.unit.id }}', '/NL-43')" <button onclick="fbLoadFTPFiles('{{ unit_item.unit.id }}', '/NL-43')"
class="ml-auto text-xs px-2 py-1 bg-gray-100 dark:bg-gray-700 rounded hover:bg-gray-200 dark:hover:bg-gray-600"> class="ml-auto text-xs px-2 py-1 bg-gray-100 dark:bg-gray-700 rounded hover:bg-gray-200 dark:hover:bg-gray-600">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
@@ -117,7 +117,7 @@ async function checkFTPStatus(unitId) {
} }
} }
async function enableFTP(unitId) { async function fbEnableFTP(unitId) {
const enableBtn = document.getElementById(`enable-ftp-${unitId}`); const enableBtn = document.getElementById(`enable-ftp-${unitId}`);
enableBtn.disabled = true; enableBtn.disabled = true;
enableBtn.textContent = 'Enabling...'; enableBtn.textContent = 'Enabling...';
@@ -130,7 +130,7 @@ async function enableFTP(unitId) {
if (response.ok) { if (response.ok) {
await checkFTPStatus(unitId); await checkFTPStatus(unitId);
// Auto-load files after enabling // Auto-load files after enabling
setTimeout(() => loadFTPFiles(unitId, '/NL-43'), 1000); setTimeout(() => fbLoadFTPFiles(unitId, '/NL-43'), 1000);
} else { } else {
alert('Failed to enable FTP'); alert('Failed to enable FTP');
} }
@@ -169,7 +169,7 @@ async function disableFTP(unitId) {
} }
} }
async function loadFTPFiles(unitId, path) { async function fbLoadFTPFiles(unitId, path) {
const fileListDiv = document.getElementById(`ftp-file-list-${unitId}`); const fileListDiv = document.getElementById(`ftp-file-list-${unitId}`);
const filesContainer = document.getElementById(`ftp-files-${unitId}`); const filesContainer = document.getElementById(`ftp-files-${unitId}`);
const currentPathSpan = document.getElementById(`current-path-${unitId}`); const currentPathSpan = document.getElementById(`current-path-${unitId}`);
@@ -220,7 +220,7 @@ async function loadFTPFiles(unitId, path) {
let html = '<div class="space-y-1">'; let html = '<div class="space-y-1">';
for (const file of sorted) { for (const file of sorted) {
const icon = getFileIcon(file); const icon = getFileIcon(file);
const sizeStr = file.is_dir ? '' : formatFileSize(file.size); const sizeStr = file.is_dir ? '' : fbFormatFileSize(file.size);
const folderId = 'proj-folder-' + file.path.replace(/[^a-zA-Z0-9]/g, '-'); const folderId = 'proj-folder-' + file.path.replace(/[^a-zA-Z0-9]/g, '-');
if (file.is_dir) { if (file.is_dir) {
@@ -264,7 +264,7 @@ async function loadFTPFiles(unitId, path) {
<div class="flex items-center gap-3 flex-shrink-0 ml-4"> <div class="flex items-center gap-3 flex-shrink-0 ml-4">
<span class="text-xs text-gray-500 hidden sm:inline">${sizeStr}</span> <span class="text-xs text-gray-500 hidden sm:inline">${sizeStr}</span>
<span class="text-xs text-gray-500 hidden md:inline">${file.modified || ''}</span> <span class="text-xs text-gray-500 hidden md:inline">${file.modified || ''}</span>
<button onclick="downloadToServer('${unitId}', '${escapeForAttribute(file.path)}', '${escapeForAttribute(file.name)}')" <button onclick="fbDownloadToServer('${unitId}', '${escapeForAttribute(file.path)}', '${escapeForAttribute(file.name)}')"
class="px-3 py-1 text-xs bg-seismo-orange text-white rounded hover:bg-seismo-navy transition-colors flex items-center" class="px-3 py-1 text-xs bg-seismo-orange text-white rounded hover:bg-seismo-navy transition-colors flex items-center"
title="Download file from device to server and add to project database"> title="Download file from device to server and add to project database">
<svg class="w-3 h-3 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-3 h-3 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@@ -371,7 +371,7 @@ async function toggleFTPFolderProject(unitId, folderPath, folderId, headerElemen
files.forEach(file => { files.forEach(file => {
const fullPath = file.path || `${folderPath}/${file.name}`; const fullPath = file.path || `${folderPath}/${file.name}`;
const icon = getFileIcon(file); const icon = getFileIcon(file);
const sizeText = file.size ? formatFileSize(file.size) : ''; const sizeText = file.size ? fbFormatFileSize(file.size) : '';
const subFolderId = 'proj-folder-' + fullPath.replace(/[^a-zA-Z0-9]/g, '-'); const subFolderId = 'proj-folder-' + fullPath.replace(/[^a-zA-Z0-9]/g, '-');
if (file.is_dir) { if (file.is_dir) {
@@ -412,7 +412,7 @@ async function toggleFTPFolderProject(unitId, folderPath, folderId, headerElemen
</div> </div>
<div class="flex items-center gap-2 flex-shrink-0 ml-2"> <div class="flex items-center gap-2 flex-shrink-0 ml-2">
<span class="text-xs text-gray-500 hidden sm:inline">${sizeText}</span> <span class="text-xs text-gray-500 hidden sm:inline">${sizeText}</span>
<button onclick="downloadToServer('${unitId}', '${escapeForAttribute(fullPath)}', '${escapeForAttribute(file.name)}')" <button onclick="fbDownloadToServer('${unitId}', '${escapeForAttribute(fullPath)}', '${escapeForAttribute(file.name)}')"
class="px-2 py-1 bg-seismo-orange hover:bg-seismo-navy text-white text-xs rounded transition-colors" class="px-2 py-1 bg-seismo-orange hover:bg-seismo-navy text-white text-xs rounded transition-colors"
title="Download to server"> title="Download to server">
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@@ -436,7 +436,7 @@ async function toggleFTPFolderProject(unitId, folderPath, folderId, headerElemen
} }
} }
async function downloadFTPFile(unitId, remotePath, fileName) { async function fbDownloadFTPFile(unitId, remotePath, fileName) {
const btn = event.target; const btn = event.target;
btn.disabled = true; btn.disabled = true;
btn.textContent = 'Downloading...'; btn.textContent = 'Downloading...';
@@ -561,7 +561,7 @@ async function downloadFolderToServer(unitId, remotePath, folderName) {
} }
} }
async function downloadToServer(unitId, remotePath, fileName) { async function fbDownloadToServer(unitId, remotePath, fileName) {
const btn = event.target; const btn = event.target;
const originalText = btn.innerHTML; const originalText = btn.innerHTML;
btn.disabled = true; btn.disabled = true;
@@ -588,7 +588,7 @@ async function downloadToServer(unitId, remotePath, fileName) {
if (response.ok) { if (response.ok) {
// Show success message // Show success message
const sizeLine = `\nSize: ${formatFileSize(data.file_size)}`; const sizeLine = `\nSize: ${fbFormatFileSize(data.file_size)}`;
const msg = data.ingested const msg = data.ingested
? `${fileName} imported as measurement data!` + formatRunLength(data) + sizeLine ? `${fileName} imported as measurement data!` + formatRunLength(data) + sizeLine
: `${fileName} downloaded to server successfully!\n\nFile ID: ${data.file_id}` + sizeLine; : `${fileName} downloaded to server successfully!\n\nFile ID: ${data.file_id}` + sizeLine;
@@ -607,7 +607,7 @@ async function downloadToServer(unitId, remotePath, fileName) {
} }
} }
function formatFileSize(bytes) { function fbFormatFileSize(bytes) {
if (bytes < 1024) return bytes + ' B'; if (bytes < 1024) return bytes + ' B';
if (bytes < 1048576) return (bytes / 1024).toFixed(1) + ' KB'; if (bytes < 1048576) return (bytes / 1024).toFixed(1) + ' KB';
if (bytes < 1073741824) return (bytes / 1048576).toFixed(1) + ' MB'; if (bytes < 1073741824) return (bytes / 1048576).toFixed(1) + ' MB';