Add file and session lists to project dashboard

- Created a new template for displaying a list of data files in `file_list.html`, including file details and actions for downloading and viewing file details.
- Added a new template for displaying recording sessions in `session_list.html`, featuring session status, details, and action buttons for stopping recordings and viewing session details.
- Introduced a legacy dashboard template `slm_legacy_dashboard.html` for sound level meter control, including a live view panel and configuration modal with dynamic content loading.
This commit is contained in:
serversdwn
2026-01-13 01:32:03 +00:00
parent 04c66bdf9c
commit 98ee9d7cea
8 changed files with 1112 additions and 23 deletions

View File

@@ -0,0 +1,126 @@
<!-- Data Files List -->
{% if files %}
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
<thead class="bg-gray-50 dark:bg-gray-800">
<tr>
<th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
File Name
</th>
<th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
Type
</th>
<th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
Size
</th>
<th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
Created
</th>
<th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
Session
</th>
<th scope="col" class="px-4 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
Actions
</th>
</tr>
</thead>
<tbody class="bg-white dark:bg-slate-800 divide-y divide-gray-200 dark:divide-gray-700">
{% for item in files %}
<tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors">
<td class="px-4 py-3 whitespace-nowrap">
<div class="flex items-center">
<svg class="w-5 h-5 mr-2 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z"></path>
</svg>
<div>
<div class="text-sm font-medium text-gray-900 dark:text-white">
{{ item.file.file_name }}
</div>
{% if item.file.file_path %}
<div class="text-xs text-gray-500 dark:text-gray-400 font-mono">
{{ item.file.file_path }}
</div>
{% endif %}
</div>
</div>
</td>
<td class="px-4 py-3 whitespace-nowrap">
<span class="px-2 py-1 text-xs font-medium rounded-full
{% if item.file.file_type == 'audio' %}bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-300
{% elif item.file.file_type == 'data' %}bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-300
{% elif item.file.file_type == 'log' %}bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-300
{% else %}bg-purple-100 text-purple-800 dark:bg-purple-900/30 dark:text-purple-300{% endif %}">
{{ item.file.file_type or 'unknown' }}
</span>
</td>
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900 dark:text-white">
{% if item.file.file_size_bytes %}
{% if item.file.file_size_bytes < 1024 %}
{{ item.file.file_size_bytes }} B
{% elif item.file.file_size_bytes < 1048576 %}
{{ "%.1f"|format(item.file.file_size_bytes / 1024) }} KB
{% elif item.file.file_size_bytes < 1073741824 %}
{{ "%.1f"|format(item.file.file_size_bytes / 1048576) }} MB
{% else %}
{{ "%.2f"|format(item.file.file_size_bytes / 1073741824) }} GB
{% endif %}
{% else %}
-
{% endif %}
</td>
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900 dark:text-white">
{{ item.file.created_at.strftime('%Y-%m-%d %H:%M') if item.file.created_at else 'N/A' }}
</td>
<td class="px-4 py-3 whitespace-nowrap text-sm">
{% if item.session %}
<span class="text-gray-900 dark:text-white font-mono text-xs">
{{ item.session.id[:8] }}...
</span>
{% else %}
<span class="text-gray-400">-</span>
{% endif %}
</td>
<td class="px-4 py-3 whitespace-nowrap text-right text-sm">
<div class="flex items-center justify-end gap-2">
<button onclick="downloadFile('{{ item.file.id }}')"
class="px-3 py-1 text-xs bg-seismo-orange text-white rounded-lg hover:bg-seismo-navy transition-colors"
title="Download file">
<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 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"></path>
</svg>
</button>
<button onclick="viewFileDetails('{{ item.file.id }}')"
class="px-3 py-1 text-xs bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors"
title="View details">
<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="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
</button>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="text-center py-12">
<svg class="w-16 h-16 mx-auto mb-4 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z"></path>
</svg>
<p class="text-gray-500 dark:text-gray-400 mb-2">No data files yet</p>
<p class="text-sm text-gray-400 dark:text-gray-500">Files will appear here after recording sessions</p>
</div>
{% endif %}
<script>
function downloadFile(fileId) {
// TODO: Implement file download
window.location.href = `/api/projects/{{ project_id }}/files/${fileId}/download`;
}
function viewFileDetails(fileId) {
// TODO: Implement file details modal
alert('File details coming soon: ' + fileId);
}
</script>

View File

@@ -2,11 +2,14 @@
{% if locations %}
<div class="space-y-3">
{% for item in locations %}
<div class="border border-gray-200 dark:border-gray-700 rounded-lg p-4">
<div class="border border-gray-200 dark:border-gray-700 rounded-lg p-4 hover:border-seismo-orange transition-colors">
<div class="flex items-start justify-between gap-3">
<div class="min-w-0">
<div class="min-w-0 flex-1">
<div class="flex items-center gap-2">
<p class="font-semibold text-gray-900 dark:text-white truncate">{{ item.location.name }}</p>
<a href="/projects/{{ project.id }}/nrl/{{ item.location.id }}"
class="font-semibold text-gray-900 dark:text-white hover:text-seismo-orange truncate">
{{ item.location.name }}
</a>
{% if item.location.location_type %}
<span class="text-xs px-2 py-0.5 rounded-full bg-gray-100 text-gray-700 dark:bg-gray-800 dark:text-gray-300">
{{ item.location.location_type|capitalize }}

View File

@@ -0,0 +1,107 @@
<!-- Recording Sessions List -->
{% if sessions %}
<div class="space-y-4">
{% for item in sessions %}
<div class="border border-gray-200 dark:border-gray-700 rounded-lg p-4 hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors">
<div class="flex items-start justify-between gap-3">
<div class="min-w-0 flex-1">
<div class="flex items-center gap-3 mb-2">
<h4 class="font-semibold text-gray-900 dark:text-white">
Session {{ item.session.id[:8] }}...
</h4>
{% if item.session.status == 'recording' %}
<span class="px-2 py-1 text-xs font-medium bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-300 rounded-full flex items-center">
<span class="w-2 h-2 bg-red-500 rounded-full mr-1.5 animate-pulse"></span>
Recording
</span>
{% elif item.session.status == 'completed' %}
<span class="px-2 py-1 text-xs font-medium bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-300 rounded-full">
Completed
</span>
{% elif item.session.status == 'paused' %}
<span class="px-2 py-1 text-xs font-medium bg-yellow-100 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-300 rounded-full">
Paused
</span>
{% elif item.session.status == 'failed' %}
<span class="px-2 py-1 text-xs font-medium bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-300 rounded-full">
Failed
</span>
{% endif %}
</div>
<div class="grid grid-cols-2 gap-3 text-sm text-gray-600 dark:text-gray-400">
{% if item.unit %}
<div>
<span class="text-xs text-gray-500 dark:text-gray-500">Unit:</span>
<a href="/slm/{{ item.unit.id }}" class="text-seismo-orange hover:text-seismo-navy font-medium ml-1">
{{ item.unit.id }}
</a>
</div>
{% endif %}
<div>
<span class="text-xs text-gray-500">Started:</span>
<span class="ml-1">{{ item.session.started_at.strftime('%Y-%m-%d %H:%M') if item.session.started_at else 'N/A' }}</span>
</div>
{% if item.session.ended_at %}
<div>
<span class="text-xs text-gray-500">Ended:</span>
<span class="ml-1">{{ item.session.ended_at.strftime('%Y-%m-%d %H:%M') }}</span>
</div>
{% endif %}
{% if item.session.duration_seconds %}
<div>
<span class="text-xs text-gray-500">Duration:</span>
<span class="ml-1">{{ (item.session.duration_seconds // 3600) }}h {{ ((item.session.duration_seconds % 3600) // 60) }}m</span>
</div>
{% endif %}
</div>
{% if item.session.notes %}
<p class="text-xs text-gray-500 dark:text-gray-400 mt-2">
{{ item.session.notes }}
</p>
{% endif %}
</div>
<div class="flex items-center gap-2">
{% if item.session.status == 'recording' %}
<button onclick="stopRecording('{{ item.session.id }}')"
class="px-3 py-1 text-xs bg-red-600 text-white rounded-lg hover:bg-red-700 transition-colors">
Stop
</button>
{% endif %}
<button onclick="viewSession('{{ item.session.id }}')"
class="px-3 py-1 text-xs bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors">
Details
</button>
</div>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="text-center py-12">
<svg class="w-16 h-16 mx-auto mb-4 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19V6l12-3v13M9 19c0 1.105-1.343 2-3 2s-3-.895-3-2 1.343-2 3-2 3 .895 3 2zm12-3c0 1.105-1.343 2-3 2s-3-.895-3-2 1.343-2 3-2 3 .895 3 2zM9 10l12-3"></path>
</svg>
<p class="text-gray-500 dark:text-gray-400 mb-2">No recording sessions yet</p>
<p class="text-sm text-gray-400 dark:text-gray-500">Schedule a session to get started</p>
</div>
{% endif %}
<script>
function viewSession(sessionId) {
// TODO: Implement session detail modal or page
alert('Session details coming soon: ' + sessionId);
}
function stopRecording(sessionId) {
if (!confirm('Stop this recording session?')) return;
// TODO: Implement stop recording API call
alert('Stop recording API coming soon for session: ' + sessionId);
}
</script>