update ftp browser, enable folder downloads (local), reimplemented timer. Enhanced project view
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
<!-- FTP File Browser for SLMs -->
|
||||
<!-- FTP File Browser for SLMs v2.0 - Folder Download Support -->
|
||||
<div class="bg-white dark:bg-slate-800 rounded-xl shadow-lg p-6 mb-6">
|
||||
<h2 class="text-xl font-semibold text-gray-900 dark:text-white mb-4">Download Files from SLMs</h2>
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
disabled>
|
||||
Disable FTP
|
||||
</button>
|
||||
<button onclick="loadFTPFiles('{{ unit_item.unit.id }}', '/NL43_DATA')"
|
||||
<button onclick="loadFTPFiles('{{ unit_item.unit.id }}', '/NL-43')"
|
||||
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"
|
||||
disabled>
|
||||
@@ -50,8 +50,8 @@
|
||||
<svg class="w-5 h-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<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>
|
||||
<span id="current-path-{{ unit_item.unit.id }}" class="text-sm font-mono text-gray-600 dark:text-gray-400">/NL43_DATA</span>
|
||||
<button onclick="loadFTPFiles('{{ unit_item.unit.id }}', '/NL43_DATA')"
|
||||
<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')"
|
||||
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">
|
||||
<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>
|
||||
@@ -127,7 +127,7 @@ async function enableFTP(unitId) {
|
||||
if (response.ok) {
|
||||
await checkFTPStatus(unitId);
|
||||
// Auto-load files after enabling
|
||||
setTimeout(() => loadFTPFiles(unitId, '/NL43_DATA'), 1000);
|
||||
setTimeout(() => loadFTPFiles(unitId, '/NL-43'), 1000);
|
||||
} else {
|
||||
alert('Failed to enable FTP');
|
||||
}
|
||||
@@ -200,18 +200,34 @@ async function loadFTPFiles(unitId, path) {
|
||||
: '<svg class="w-5 h-5 text-gray-400" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M4 4a2 2 0 012-2h4.586A2 2 0 0112 2.586L15.414 6A2 2 0 0116 7.414V16a2 2 0 01-2 2H6a2 2 0 01-2-2V4z" clip-rule="evenodd"></path></svg>';
|
||||
|
||||
const sizeStr = file.is_dir ? '' : formatFileSize(file.size);
|
||||
const clickAction = file.is_dir
|
||||
? `onclick="loadFTPFiles('${unitId}', '${file.path}')"`
|
||||
: '';
|
||||
|
||||
html += `
|
||||
<div class="flex items-center gap-3 px-3 py-2 hover:bg-gray-50 dark:hover:bg-gray-800 rounded ${file.is_dir ? 'cursor-pointer' : ''}" ${clickAction}>
|
||||
<div class="flex items-center gap-3 px-3 py-2 hover:bg-gray-50 dark:hover:bg-gray-800 rounded">
|
||||
${icon}
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="flex-1 min-w-0 ${file.is_dir ? 'cursor-pointer' : ''}" ${file.is_dir ? `onclick="loadFTPFiles('${unitId}', '${file.path}')"` : ''}>
|
||||
<div class="text-sm font-medium text-gray-900 dark:text-white truncate">${file.name}</div>
|
||||
<div class="text-xs text-gray-500 dark:text-gray-400">${file.modified}${sizeStr ? ' • ' + sizeStr : ''}</div>
|
||||
</div>
|
||||
${!file.is_dir ? `
|
||||
${file.is_dir ? `
|
||||
<div class="flex items-center gap-2">
|
||||
<button onclick="event.stopPropagation(); downloadFolderToServer('${unitId}', '${file.path}', '${file.name}')"
|
||||
class="px-3 py-1 text-xs bg-green-600 text-white rounded hover:bg-green-700 transition-colors"
|
||||
title="Download entire folder to server and add to database">
|
||||
<svg class="w-3 h-3 inline mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01"></path>
|
||||
</svg>
|
||||
To Server (ZIP)
|
||||
</button>
|
||||
<button onclick="event.stopPropagation(); downloadFTPFolder('${unitId}', '${file.path}', '${file.name}')"
|
||||
class="px-3 py-1 text-xs bg-blue-600 text-white rounded hover:bg-blue-700 transition-colors"
|
||||
title="Download entire folder as ZIP to your computer">
|
||||
<svg class="w-3 h-3 inline mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"></path>
|
||||
</svg>
|
||||
To Browser (ZIP)
|
||||
</button>
|
||||
</div>
|
||||
` : `
|
||||
<div class="flex items-center gap-2">
|
||||
<button onclick="downloadToServer('${unitId}', '${file.path}', '${file.name}')"
|
||||
class="px-3 py-1 text-xs bg-green-600 text-white rounded hover:bg-green-700 transition-colors"
|
||||
@@ -230,7 +246,7 @@ async function loadFTPFiles(unitId, path) {
|
||||
To Browser
|
||||
</button>
|
||||
</div>
|
||||
` : ''}
|
||||
`}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@@ -280,6 +296,90 @@ async function downloadFTPFile(unitId, remotePath, fileName) {
|
||||
}
|
||||
}
|
||||
|
||||
async function downloadFTPFolder(unitId, remotePath, folderName) {
|
||||
const btn = event.target;
|
||||
const originalHTML = btn.innerHTML;
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = '<svg class="w-3 h-3 inline mr-1 animate-spin" fill="none" stroke="currentColor" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>Downloading...';
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/slmm/${unitId}/ftp/download-folder`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
remote_path: remotePath
|
||||
})
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const blob = await response.blob();
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = `${folderName}.zip`;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
window.URL.revokeObjectURL(url);
|
||||
document.body.removeChild(a);
|
||||
|
||||
// Show success message
|
||||
alert(`✓ Folder "${folderName}" downloaded successfully as ZIP file!`);
|
||||
} else {
|
||||
const errorData = await response.json();
|
||||
alert('Folder download failed: ' + (errorData.detail || 'Unknown error'));
|
||||
}
|
||||
} catch (error) {
|
||||
alert('Error downloading folder: ' + error);
|
||||
} finally {
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = originalHTML;
|
||||
}
|
||||
}
|
||||
|
||||
async function downloadFolderToServer(unitId, remotePath, folderName) {
|
||||
const btn = event.target;
|
||||
const originalHTML = btn.innerHTML;
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = '<svg class="w-3 h-3 inline mr-1 animate-spin" fill="none" stroke="currentColor" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>Downloading...';
|
||||
|
||||
// Get location_id from the unit's data attribute
|
||||
const unitContainer = btn.closest('[id^="ftp-files-"]');
|
||||
const locationId = unitContainer.dataset.locationId;
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/projects/{{ project_id }}/ftp-download-folder-to-server`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
unit_id: unitId,
|
||||
remote_path: remotePath,
|
||||
location_id: locationId
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
// Show success message
|
||||
alert(`✓ Folder "${folderName}" downloaded to server successfully as ZIP!\n\nFile ID: ${data.file_id}\nSize: ${formatFileSize(data.file_size)}`);
|
||||
|
||||
// Refresh the downloaded files list
|
||||
htmx.trigger('#project-files', 'refresh');
|
||||
} else {
|
||||
alert('Folder download to server failed: ' + (data.detail || 'Unknown error'));
|
||||
}
|
||||
} catch (error) {
|
||||
alert('Error downloading folder to server: ' + error);
|
||||
} finally {
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = originalHTML;
|
||||
}
|
||||
}
|
||||
|
||||
async function downloadToServer(unitId, remotePath, fileName) {
|
||||
const btn = event.target;
|
||||
const originalText = btn.innerHTML;
|
||||
|
||||
Reference in New Issue
Block a user