BIG update: Update to 0.5.1. Added:

-Project management
-Modem Managerment
-Modem/unit pairing

and more
This commit is contained in:
serversdwn
2026-01-28 03:27:50 +00:00
parent 44d7841852
commit 6492fdff82
24 changed files with 2459 additions and 90 deletions

View File

@@ -121,8 +121,13 @@
<p id="viewUnitType" class="mt-1 text-gray-900 dark:text-white font-medium">--</p>
</div>
<div>
<label class="text-sm font-medium text-gray-500 dark:text-gray-400">Project ID</label>
<p id="viewProjectId" class="mt-1 text-gray-900 dark:text-white font-medium">--</p>
<label class="text-sm font-medium text-gray-500 dark:text-gray-400">Project</label>
<p id="viewProjectContainer" class="mt-1">
<a id="viewProjectLink" href="#" class="text-seismo-orange hover:text-orange-600 font-medium hover:underline hidden">
<span id="viewProjectText">--</span>
</a>
<span id="viewProjectNoLink" class="text-gray-900 dark:text-white font-medium">Not assigned</span>
</p>
</div>
<div>
<label class="text-sm font-medium text-gray-500 dark:text-gray-400">Address</label>
@@ -172,6 +177,48 @@
</div>
</div>
<!-- Paired Device (for modems only) -->
<div id="viewPairedDeviceSection" class="hidden border-t border-gray-200 dark:border-gray-700 pt-6">
<h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-4">Paired Device</h3>
<div id="pairedDeviceInfo">
<p class="text-sm text-gray-500 dark:text-gray-400">Loading...</p>
</div>
</div>
<!-- Connectivity (for modems only) -->
<div id="viewConnectivitySection" class="hidden border-t border-gray-200 dark:border-gray-700 pt-6">
<h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-4">Connectivity</h3>
<div class="flex items-center gap-4 mb-4">
<button onclick="pingModem()" id="modemPingBtn"
class="px-4 py-2 bg-blue-100 hover:bg-blue-200 text-blue-700 dark:bg-blue-900/30 dark:hover:bg-blue-900/50 dark:text-blue-300 rounded-lg flex items-center gap-2 transition-colors">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.111 16.404a5.5 5.5 0 017.778 0M12 20h.01m-7.08-7.071c3.904-3.905 10.236-3.905 14.141 0M1.394 9.393c5.857-5.857 15.355-5.857 21.213 0"></path>
</svg>
Ping Test
</button>
<span id="modemPingResult" class="text-sm text-gray-500 dark:text-gray-400">--</span>
</div>
<!-- Future Diagnostics Placeholders -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 opacity-60">
<div class="bg-gray-100 dark:bg-gray-700 rounded-lg p-4">
<p class="text-xs text-gray-500 dark:text-gray-400">Signal Strength</p>
<p class="text-xl font-semibold text-gray-400 dark:text-gray-500">-- dBm</p>
<p class="text-xs text-gray-400 dark:text-gray-500 mt-1">ModemManager pending</p>
</div>
<div class="bg-gray-100 dark:bg-gray-700 rounded-lg p-4">
<p class="text-xs text-gray-500 dark:text-gray-400">Data Usage</p>
<p class="text-xl font-semibold text-gray-400 dark:text-gray-500">-- MB</p>
<p class="text-xs text-gray-400 dark:text-gray-500 mt-1">ModemManager pending</p>
</div>
<div class="bg-gray-100 dark:bg-gray-700 rounded-lg p-4">
<p class="text-xs text-gray-500 dark:text-gray-400">Uptime</p>
<p class="text-xl font-semibold text-gray-400 dark:text-gray-500">--</p>
<p class="text-xs text-gray-400 dark:text-gray-500 mt-1">ModemManager pending</p>
</div>
</div>
</div>
<!-- Notes -->
<div class="border-t border-gray-200 dark:border-gray-700 pt-6">
<label class="text-sm font-medium text-gray-500 dark:text-gray-400">Notes</label>
@@ -251,11 +298,11 @@
class="w-full px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-seismo-orange">
</div>
<!-- Project ID -->
<!-- Project -->
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Project ID</label>
<input type="text" name="project_id" id="projectId"
class="w-full px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-seismo-orange">
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Project</label>
{% set picker_id = "-detail" %}
{% include "partials/project_picker.html" with context %}
</div>
<!-- Address -->
@@ -415,6 +462,26 @@ let currentSnapshot = null;
let unitMap = null;
let mapMarker = null;
// Fetch project display name (combines project_number, client_name, name)
async function fetchProjectDisplay(projectId) {
if (!projectId) return '';
try {
const response = await fetch(`/api/projects/${projectId}`);
if (response.ok) {
const project = await response.json();
const parts = [
project.project_number,
project.client_name,
project.name
].filter(Boolean);
return parts.join(' - ') || projectId;
}
} catch (e) {
console.error('Failed to fetch project:', e);
}
return projectId;
}
// Load unit data on page load
async function loadUnitData() {
try {
@@ -536,7 +603,31 @@ function populateViewMode() {
// Basic info
document.getElementById('viewDeviceType').textContent = currentUnit.device_type || '--';
document.getElementById('viewUnitType').textContent = currentUnit.unit_type || '--';
document.getElementById('viewProjectId').textContent = currentUnit.project_id || '--';
// Project display with clickable link
const projectId = currentUnit.project_id;
const projectLink = document.getElementById('viewProjectLink');
const projectNoLink = document.getElementById('viewProjectNoLink');
const projectText = document.getElementById('viewProjectText');
if (projectId) {
// Fetch project display name and show link
fetchProjectDisplay(projectId).then(displayText => {
if (projectText) projectText.textContent = displayText;
if (projectLink) {
projectLink.href = `/projects/${projectId}`;
projectLink.classList.remove('hidden');
}
if (projectNoLink) projectNoLink.classList.add('hidden');
});
} else {
if (projectNoLink) {
projectNoLink.textContent = 'Not assigned';
projectNoLink.classList.remove('hidden');
}
if (projectLink) projectLink.classList.add('hidden');
}
document.getElementById('viewAddress').textContent = currentUnit.address || '--';
document.getElementById('viewCoordinates').textContent = currentUnit.coordinates || '--';
@@ -557,9 +648,15 @@ function populateViewMode() {
if (currentUnit.device_type === 'modem') {
document.getElementById('viewSeismographFields').classList.add('hidden');
document.getElementById('viewModemFields').classList.remove('hidden');
document.getElementById('viewPairedDeviceSection').classList.remove('hidden');
document.getElementById('viewConnectivitySection').classList.remove('hidden');
// Load paired device info
loadPairedDevice();
} else {
document.getElementById('viewSeismographFields').classList.remove('hidden');
document.getElementById('viewModemFields').classList.add('hidden');
document.getElementById('viewPairedDeviceSection').classList.add('hidden');
document.getElementById('viewConnectivitySection').classList.add('hidden');
}
}
@@ -567,7 +664,22 @@ function populateViewMode() {
function populateEditForm() {
document.getElementById('deviceType').value = currentUnit.device_type || 'seismograph';
document.getElementById('unitType').value = currentUnit.unit_type || '';
document.getElementById('projectId').value = currentUnit.project_id || '';
// Populate project picker (uses -detail suffix)
const projectPickerValue = document.getElementById('project-picker-value-detail');
const projectPickerSearch = document.getElementById('project-picker-search-detail');
const projectPickerClear = document.getElementById('project-picker-clear-detail');
if (projectPickerValue) projectPickerValue.value = currentUnit.project_id || '';
if (currentUnit.project_id) {
fetchProjectDisplay(currentUnit.project_id).then(displayText => {
if (projectPickerSearch) projectPickerSearch.value = displayText;
if (projectPickerClear) projectPickerClear.classList.remove('hidden');
});
} else {
if (projectPickerSearch) projectPickerSearch.value = '';
if (projectPickerClear) projectPickerClear.classList.add('hidden');
}
document.getElementById('address').value = currentUnit.address || '';
document.getElementById('coordinates').value = currentUnit.coordinates || '';
document.getElementById('deployed').checked = currentUnit.deployed;
@@ -999,10 +1111,66 @@ async function deleteHistoryEntry(historyId) {
}
}
// Load paired device info for modems
async function loadPairedDevice() {
try {
const response = await fetch(`/api/modem-dashboard/${unitId}/paired-device-html`);
if (response.ok) {
const html = await response.text();
document.getElementById('pairedDeviceInfo').innerHTML = html;
}
} catch (error) {
console.error('Error loading paired device:', error);
document.getElementById('pairedDeviceInfo').innerHTML = '<p class="text-red-500 text-sm">Failed to load paired device info</p>';
}
}
// Ping modem and show result
async function pingModem() {
const btn = document.getElementById('modemPingBtn');
const resultSpan = document.getElementById('modemPingResult');
// Show loading state
const originalText = btn.innerHTML;
btn.innerHTML = `
<svg class="w-5 h-5 animate-spin" 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>
</svg>
Pinging...
`;
btn.disabled = true;
resultSpan.textContent = 'Testing connection...';
resultSpan.className = 'text-sm text-gray-500 dark:text-gray-400';
try {
const response = await fetch(`/api/modem-dashboard/${unitId}/ping`);
const data = await response.json();
if (data.status === 'success') {
resultSpan.innerHTML = `<span class="inline-block w-2 h-2 bg-green-500 rounded-full mr-1"></span>Online (${data.response_time_ms}ms)`;
resultSpan.className = 'text-sm text-green-600 dark:text-green-400';
} else {
resultSpan.innerHTML = `<span class="inline-block w-2 h-2 bg-red-500 rounded-full mr-1"></span>${data.detail || 'Offline'}`;
resultSpan.className = 'text-sm text-red-600 dark:text-red-400';
}
} catch (error) {
resultSpan.textContent = 'Error: ' + error.message;
resultSpan.className = 'text-sm text-red-600 dark:text-red-400';
}
// Restore button
btn.innerHTML = originalText;
btn.disabled = false;
}
// Load data when page loads
loadUnitData().then(() => {
loadPhotos();
loadUnitHistory();
});
</script>
<!-- Include Project Create Modal for inline project creation -->
{% include "partials/project_create_modal.html" %}
{% endblock %}