Files
terra-view/templates/partials/seismo_unit_list.html
serversdwn 89662d2fa5 Change: user sets date of previous calibration, not upcoming expire dates.
- seismograph list page enhanced with better visabilty, filtering, sorting, and calibration dates color coded.
2026-02-06 21:17:14 +00:00

201 lines
13 KiB
HTML

{% if units is defined %}
<div class="overflow-x-auto">
<table class="w-full">
<thead class="bg-gray-50 dark:bg-slate-700 border-b border-gray-200 dark:border-gray-600">
<tr>
{% set next_order = 'desc' if (sort == 'id' and order == 'asc') else 'asc' %}
<th class="px-4 py-3 text-left text-xs font-medium text-gray-700 dark:text-gray-300 uppercase tracking-wider cursor-pointer hover:bg-gray-100 dark:hover:bg-slate-600"
hx-get="/api/seismo-dashboard/units?sort=id&order={{ next_order }}&search={{ search }}&status={{ status }}&modem={{ modem }}"
hx-target="#seismo-units-list"
hx-swap="innerHTML">
<span class="flex items-center gap-1">
Unit ID
{% if sort == 'id' %}
<svg class="w-4 h-4 {% if order == 'desc' %}rotate-180{% endif %}" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 15l7-7 7 7"></path>
</svg>
{% endif %}
</span>
</th>
{% set next_order = 'desc' if (sort == 'status' and order == 'asc') else 'asc' %}
<th class="px-4 py-3 text-left text-xs font-medium text-gray-700 dark:text-gray-300 uppercase tracking-wider cursor-pointer hover:bg-gray-100 dark:hover:bg-slate-600"
hx-get="/api/seismo-dashboard/units?sort=status&order={{ next_order }}&search={{ search }}&status={{ status }}&modem={{ modem }}"
hx-target="#seismo-units-list"
hx-swap="innerHTML">
<span class="flex items-center gap-1">
Status
{% if sort == 'status' %}
<svg class="w-4 h-4 {% if order == 'desc' %}rotate-180{% endif %}" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 15l7-7 7 7"></path>
</svg>
{% endif %}
</span>
</th>
{% set next_order = 'desc' if (sort == 'modem' and order == 'asc') else 'asc' %}
<th class="px-4 py-3 text-left text-xs font-medium text-gray-700 dark:text-gray-300 uppercase tracking-wider cursor-pointer hover:bg-gray-100 dark:hover:bg-slate-600"
hx-get="/api/seismo-dashboard/units?sort=modem&order={{ next_order }}&search={{ search }}&status={{ status }}&modem={{ modem }}"
hx-target="#seismo-units-list"
hx-swap="innerHTML">
<span class="flex items-center gap-1">
Modem
{% if sort == 'modem' %}
<svg class="w-4 h-4 {% if order == 'desc' %}rotate-180{% endif %}" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 15l7-7 7 7"></path>
</svg>
{% endif %}
</span>
</th>
{% set next_order = 'desc' if (sort == 'location' and order == 'asc') else 'asc' %}
<th class="px-4 py-3 text-left text-xs font-medium text-gray-700 dark:text-gray-300 uppercase tracking-wider cursor-pointer hover:bg-gray-100 dark:hover:bg-slate-600"
hx-get="/api/seismo-dashboard/units?sort=location&order={{ next_order }}&search={{ search }}&status={{ status }}&modem={{ modem }}"
hx-target="#seismo-units-list"
hx-swap="innerHTML">
<span class="flex items-center gap-1">
Location
{% if sort == 'location' %}
<svg class="w-4 h-4 {% if order == 'desc' %}rotate-180{% endif %}" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 15l7-7 7 7"></path>
</svg>
{% endif %}
</span>
</th>
{% set next_order = 'desc' if (sort == 'last_calibrated' and order == 'asc') else 'asc' %}
<th class="px-4 py-3 text-left text-xs font-medium text-gray-700 dark:text-gray-300 uppercase tracking-wider cursor-pointer hover:bg-gray-100 dark:hover:bg-slate-600"
hx-get="/api/seismo-dashboard/units?sort=last_calibrated&order={{ next_order }}&search={{ search }}&status={{ status }}&modem={{ modem }}"
hx-target="#seismo-units-list"
hx-swap="innerHTML">
<span class="flex items-center gap-1">
Last Calibrated
{% if sort == 'last_calibrated' %}
<svg class="w-4 h-4 {% if order == 'desc' %}rotate-180{% endif %}" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 15l7-7 7 7"></path>
</svg>
{% endif %}
</span>
</th>
{% set next_order = 'desc' if (sort == 'notes' and order == 'asc') else 'asc' %}
<th class="px-4 py-3 text-left text-xs font-medium text-gray-700 dark:text-gray-300 uppercase tracking-wider cursor-pointer hover:bg-gray-100 dark:hover:bg-slate-600"
hx-get="/api/seismo-dashboard/units?sort=notes&order={{ next_order }}&search={{ search }}&status={{ status }}&modem={{ modem }}"
hx-target="#seismo-units-list"
hx-swap="innerHTML">
<span class="flex items-center gap-1">
Notes
{% if sort == 'notes' %}
<svg class="w-4 h-4 {% if order == 'desc' %}rotate-180{% endif %}" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 15l7-7 7 7"></path>
</svg>
{% endif %}
</span>
</th>
<th class="px-4 py-3 text-right text-xs font-medium text-gray-700 dark:text-gray-300 uppercase tracking-wider">Actions</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200 dark:divide-gray-700">
{% for unit in units %}
<tr class="hover:bg-gray-50 dark:hover:bg-slate-700 transition-colors">
<td class="px-4 py-3 whitespace-nowrap">
<a href="/unit/{{ unit.id }}" class="font-medium text-blue-600 dark:text-blue-400 hover:underline">
{{ unit.id }}
</a>
</td>
<td class="px-4 py-3 whitespace-nowrap">
{% if unit.deployed %}
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200">
<svg class="w-3 h-3 mr-1" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path>
</svg>
Deployed
</span>
{% else %}
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-300">
<svg class="w-3 h-3 mr-1" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8 7a1 1 0 00-1 1v4a1 1 0 001 1h4a1 1 0 001-1V8a1 1 0 00-1-1H8z" clip-rule="evenodd"></path>
</svg>
Benched
</span>
{% endif %}
</td>
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900 dark:text-gray-300">
{% if unit.deployed_with_modem_id %}
<a href="/unit/{{ unit.deployed_with_modem_id }}" class="text-blue-600 dark:text-blue-400 hover:underline">
{{ unit.deployed_with_modem_id }}
</a>
{% else %}
<span class="text-gray-400 dark:text-gray-600">None</span>
{% endif %}
</td>
<td class="px-4 py-3 text-sm text-gray-900 dark:text-gray-300">
{% if unit.address %}
<span class="truncate max-w-xs inline-block" title="{{ unit.address }}">{{ unit.address }}</span>
{% elif unit.coordinates %}
<span class="text-gray-500 dark:text-gray-400">{{ unit.coordinates }}</span>
{% else %}
<span class="text-gray-400 dark:text-gray-600"></span>
{% endif %}
</td>
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900 dark:text-gray-300">
{% if unit.last_calibrated %}
<span class="inline-flex items-center gap-1.5">
{% if unit.next_calibration_due and today %}
{% set days_until = (unit.next_calibration_due - today).days %}
{% if days_until < 0 %}
<span class="w-2 h-2 rounded-full bg-red-500" title="Calibration expired {{ -days_until }} days ago"></span>
{% elif days_until <= 14 %}
<span class="w-2 h-2 rounded-full bg-yellow-500" title="Calibration expires in {{ days_until }} days"></span>
{% else %}
<span class="w-2 h-2 rounded-full bg-green-500" title="Calibration valid ({{ days_until }} days remaining)"></span>
{% endif %}
{% else %}
<span class="w-2 h-2 rounded-full bg-gray-400" title="No expiry date set"></span>
{% endif %}
{{ unit.last_calibrated.strftime('%Y-%m-%d') }}
</span>
{% else %}
<span class="text-gray-400 dark:text-gray-600"></span>
{% endif %}
</td>
<td class="px-4 py-3 text-sm text-gray-700 dark:text-gray-400">
{% if unit.note %}
<span class="truncate max-w-xs inline-block" title="{{ unit.note }}">{{ unit.note }}</span>
{% else %}
<span class="text-gray-400 dark:text-gray-600"></span>
{% endif %}
</td>
<td class="px-4 py-3 whitespace-nowrap text-right text-sm">
<a href="/unit/{{ unit.id }}" class="text-blue-600 dark:text-blue-400 hover:underline">
View Details →
</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% if search or status or modem %}
<div class="mt-4 text-sm text-gray-600 dark:text-gray-400">
Found {{ units|length }} seismograph(s)
{% if search %} matching "{{ search }}"{% endif %}
{% if status %} ({{ status }}){% endif %}
{% if modem %} ({{ 'with modem' if modem == 'with' else 'without modem' }}){% endif %}
</div>
{% endif %}
{% else %}
<div class="text-center py-12">
<svg class="mx-auto h-12 w-12 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"></path>
</svg>
<h3 class="mt-2 text-sm font-medium text-gray-900 dark:text-white">No seismographs found</h3>
{% if search %}
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">No seismographs match "{{ search }}"</p>
<button onclick="document.getElementById('seismo-search').value = ''; htmx.trigger('#seismo-search', 'keyup');"
class="mt-3 text-blue-600 dark:text-blue-400 hover:underline">
Clear search
</button>
{% else %}
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">Get started by adding a seismograph unit from the roster page.</p>
{% endif %}
</div>
{% endif %}