Full PWA mobile version added, bug fixes on deployment status, navigation links added
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
<div class="rounded-xl shadow-lg bg-white dark:bg-slate-800 overflow-hidden">
|
||||
<!-- Desktop Table View -->
|
||||
<div class="hidden md:block rounded-xl shadow-lg bg-white dark:bg-slate-800 overflow-hidden">
|
||||
<table id="roster-table" class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<thead class="bg-gray-50 dark:bg-gray-700">
|
||||
<tr>
|
||||
@@ -183,6 +184,160 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Mobile Card View -->
|
||||
<div class="md:hidden space-y-3">
|
||||
{% for unit in units %}
|
||||
<div class="unit-card"
|
||||
onclick="openUnitModal('{{ unit.id }}', '{{ unit.status }}', '{{ unit.age }}')"
|
||||
data-unit-id="{{ unit.id }}"
|
||||
data-status="{{ unit.status }}"
|
||||
data-age="{{ unit.age }}">
|
||||
<!-- Header: Status Dot + Unit ID + Status Badge -->
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<div class="flex items-center gap-2">
|
||||
{% if unit.status == 'OK' %}
|
||||
<span class="w-4 h-4 rounded-full bg-green-500" title="OK"></span>
|
||||
{% elif unit.status == 'Pending' %}
|
||||
<span class="w-4 h-4 rounded-full bg-yellow-500" title="Pending"></span>
|
||||
{% elif unit.status == 'Missing' %}
|
||||
<span class="w-4 h-4 rounded-full bg-red-500" title="Missing"></span>
|
||||
{% else %}
|
||||
<span class="w-4 h-4 rounded-full bg-gray-400" title="No Data"></span>
|
||||
{% endif %}
|
||||
<span class="font-bold text-lg text-seismo-orange dark:text-seismo-orange">{{ unit.id }}</span>
|
||||
</div>
|
||||
<span class="px-3 py-1 rounded-full text-xs font-medium
|
||||
{% if unit.status == 'OK' %}bg-green-100 dark:bg-green-900/30 text-green-800 dark:text-green-300
|
||||
{% elif unit.status == 'Pending' %}bg-yellow-100 dark:bg-yellow-900/30 text-yellow-800 dark:text-yellow-300
|
||||
{% elif unit.status == 'Missing' %}bg-red-100 dark:bg-red-900/30 text-red-800 dark:text-red-300
|
||||
{% else %}bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-400
|
||||
{% endif %}">
|
||||
{% if unit.status in ['N/A', 'Unknown'] %}Benched{% else %}{{ unit.status }}{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Type Badge -->
|
||||
<div class="mb-2">
|
||||
{% if unit.device_type == 'modem' %}
|
||||
<span class="px-2 py-1 rounded-full bg-purple-100 dark:bg-purple-900/30 text-purple-800 dark:text-purple-300 text-xs font-medium">
|
||||
Modem
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="px-2 py-1 rounded-full bg-blue-100 dark:bg-blue-900/30 text-blue-800 dark:text-blue-300 text-xs font-medium">
|
||||
Seismograph
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Location (Tap to Navigate) -->
|
||||
{% if unit.address %}
|
||||
<div class="text-sm mb-1">
|
||||
<a href="https://www.google.com/maps/search/?api=1&query={{ unit.address | urlencode }}"
|
||||
target="_blank"
|
||||
onclick="event.stopPropagation()"
|
||||
class="flex items-center gap-1 text-seismo-orange hover:text-orange-600 dark:text-seismo-orange dark:hover:text-orange-400">
|
||||
<svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"></path>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"></path>
|
||||
</svg>
|
||||
<span class="underline">{{ unit.address }}</span>
|
||||
</a>
|
||||
</div>
|
||||
{% elif unit.coordinates %}
|
||||
<div class="text-sm mb-1">
|
||||
<a href="https://www.google.com/maps/search/?api=1&query={{ unit.coordinates | urlencode }}"
|
||||
target="_blank"
|
||||
onclick="event.stopPropagation()"
|
||||
class="flex items-center gap-1 text-seismo-orange hover:text-orange-600 dark:text-seismo-orange dark:hover:text-orange-400">
|
||||
<svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"></path>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"></path>
|
||||
</svg>
|
||||
<span class="font-mono underline">{{ unit.coordinates }}</span>
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Project ID -->
|
||||
{% if unit.project_id %}
|
||||
<div class="text-sm text-gray-600 dark:text-gray-400 mb-1">
|
||||
🏗️ {{ unit.project_id }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Last Seen -->
|
||||
<div class="text-sm text-gray-500 dark:text-gray-500 mt-2">
|
||||
🕐 {{ unit.age }}
|
||||
</div>
|
||||
|
||||
<!-- Deployed/Benched Indicator -->
|
||||
<div class="mt-2">
|
||||
{% if unit.deployed %}
|
||||
<span class="text-xs text-blue-600 dark:text-blue-400">
|
||||
⚡ Deployed
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="text-xs text-gray-500 dark:text-gray-500">
|
||||
📦 Benched
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Tap Hint -->
|
||||
<div class="text-xs text-gray-400 mt-2 text-center border-t border-gray-200 dark:border-gray-700 pt-2">
|
||||
Tap for details
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
<!-- Mobile Last Updated -->
|
||||
<div class="text-xs text-gray-500 dark:text-gray-400 text-center py-2">
|
||||
Last updated: <span id="last-updated-mobile">{{ timestamp }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Unit Detail Modal -->
|
||||
<div id="unitModal" class="unit-modal">
|
||||
<!-- Backdrop -->
|
||||
<div class="unit-modal-backdrop" onclick="closeUnitModal(event)"></div>
|
||||
|
||||
<!-- Modal Content -->
|
||||
<div class="unit-modal-content">
|
||||
<!-- Handle Bar (Mobile Only) -->
|
||||
<div class="modal-handle"></div>
|
||||
|
||||
<!-- Header -->
|
||||
<div class="flex items-center justify-between px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<h3 id="modalUnitId" class="text-xl font-bold text-gray-900 dark:text-white"></h3>
|
||||
<button onclick="closeUnitModal(event)" data-close-modal class="w-10 h-10 flex items-center justify-center text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300">
|
||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Content -->
|
||||
<div id="modalContent" class="p-6">
|
||||
<!-- Content will be populated by JavaScript -->
|
||||
</div>
|
||||
|
||||
<!-- Actions -->
|
||||
<div class="p-6 border-t border-gray-200 dark:border-gray-700 space-y-3">
|
||||
<button id="modalEditBtn" class="w-full h-12 bg-seismo-orange hover:bg-orange-600 text-white rounded-lg font-medium transition-colors">
|
||||
Edit Unit
|
||||
</button>
|
||||
<div class="grid grid-cols-2 gap-3">
|
||||
<button id="modalDeployBtn" class="h-12 border border-gray-300 dark:border-gray-600 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors">
|
||||
Deploy/Bench
|
||||
</button>
|
||||
<button id="modalDeleteBtn" class="h-12 border border-red-300 dark:border-red-600 text-red-600 dark:text-red-400 rounded-lg hover:bg-red-50 dark:hover:bg-red-900/20 transition-colors">
|
||||
Delete
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.sort-indicator::after {
|
||||
content: '⇅';
|
||||
@@ -201,7 +356,14 @@
|
||||
|
||||
<script>
|
||||
// Update timestamp
|
||||
document.getElementById('last-updated').textContent = new Date().toLocaleTimeString();
|
||||
const timestampElement = document.getElementById('last-updated');
|
||||
if (timestampElement) {
|
||||
timestampElement.textContent = new Date().toLocaleTimeString();
|
||||
}
|
||||
const timestampMobileElement = document.getElementById('last-updated-mobile');
|
||||
if (timestampMobileElement) {
|
||||
timestampMobileElement.textContent = new Date().toLocaleTimeString();
|
||||
}
|
||||
|
||||
// Sorting state
|
||||
let currentSort = { column: null, direction: 'asc' };
|
||||
|
||||
Reference in New Issue
Block a user