BIG update: Update to 0.5.1. Added:
-Project management -Modem Managerment -Modem/unit pairing and more
This commit is contained in:
@@ -131,10 +131,8 @@
|
||||
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>
|
||||
<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"
|
||||
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"
|
||||
placeholder="PROJ-001">
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Project</label>
|
||||
{% include "partials/project_picker.html" with context %}
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Location</label>
|
||||
@@ -159,8 +157,8 @@
|
||||
</div>
|
||||
<div id="modemPairingField" class="hidden">
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Deployed With Modem</label>
|
||||
<input type="text" name="deployed_with_modem_id" placeholder="Modem ID"
|
||||
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">
|
||||
{% set picker_id = "-add-seismo" %}
|
||||
{% include "partials/modem_picker.html" with context %}
|
||||
<p class="text-xs text-gray-500 dark:text-gray-400 mt-1">Only needed when deployed</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -183,6 +181,21 @@
|
||||
<input type="text" name="hardware_model" placeholder="e.g., Raven XTV"
|
||||
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>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Deployment Type</label>
|
||||
<select name="deployment_type"
|
||||
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">
|
||||
<option value="">Not assigned</option>
|
||||
<option value="seismograph">Seismograph</option>
|
||||
<option value="slm">Sound Level Meter (SLM)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Deployed With Unit</label>
|
||||
{% set picker_id = "-add-modem" %}
|
||||
{% set device_type_filter = "" %}
|
||||
{% include "partials/unit_picker.html" with context %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sound Level Meter-specific fields -->
|
||||
@@ -297,9 +310,9 @@
|
||||
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>
|
||||
<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="editProjectId"
|
||||
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 = "-edit" %}
|
||||
{% include "partials/project_picker.html" with context %}
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Address</label>
|
||||
@@ -327,8 +340,8 @@
|
||||
</div>
|
||||
<div id="editModemPairingField" class="hidden">
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Deployed With Modem</label>
|
||||
<input type="text" name="deployed_with_modem_id" id="editDeployedWithModemId" placeholder="Modem ID"
|
||||
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">
|
||||
{% set picker_id = "-edit-seismo" %}
|
||||
{% include "partials/modem_picker.html" with context %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -350,6 +363,21 @@
|
||||
<input type="text" name="hardware_model" id="editHardwareModel" placeholder="e.g., Raven XTV"
|
||||
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>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Deployment Type</label>
|
||||
<select name="deployment_type" id="editDeploymentType"
|
||||
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">
|
||||
<option value="">Not assigned</option>
|
||||
<option value="seismograph">Seismograph</option>
|
||||
<option value="slm">Sound Level Meter (SLM)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Deployed With Unit</label>
|
||||
{% set picker_id = "-edit-modem" %}
|
||||
{% set device_type_filter = "" %}
|
||||
{% include "partials/unit_picker.html" with context %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sound Level Meter-specific fields -->
|
||||
@@ -419,6 +447,55 @@
|
||||
<textarea name="note" id="editNote" rows="3"
|
||||
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"></textarea>
|
||||
</div>
|
||||
|
||||
<!-- Cascade to Paired Device Section -->
|
||||
<div id="editCascadeSection" class="hidden border-t border-gray-200 dark:border-gray-700 pt-4">
|
||||
<div class="flex items-center gap-2 mb-3">
|
||||
<svg class="w-5 h-5 text-seismo-orange" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1"></path>
|
||||
</svg>
|
||||
<span class="text-sm font-semibold text-gray-700 dark:text-gray-300">
|
||||
Also update paired device: <span id="editPairedDeviceName" class="text-seismo-orange"></span>
|
||||
</span>
|
||||
</div>
|
||||
<input type="hidden" name="cascade_to_unit_id" id="editCascadeToUnitId" value="">
|
||||
<div class="grid grid-cols-2 gap-2 bg-gray-50 dark:bg-slate-700/50 rounded-lg p-3">
|
||||
<label class="flex items-center gap-2 cursor-pointer">
|
||||
<input type="checkbox" name="cascade_deployed" id="editCascadeDeployed" value="true"
|
||||
class="w-4 h-4 text-seismo-orange focus:ring-seismo-orange rounded">
|
||||
<span class="text-sm text-gray-700 dark:text-gray-300">Deployed status</span>
|
||||
</label>
|
||||
<label class="flex items-center gap-2 cursor-pointer">
|
||||
<input type="checkbox" name="cascade_retired" id="editCascadeRetired" value="true"
|
||||
class="w-4 h-4 text-seismo-orange focus:ring-seismo-orange rounded">
|
||||
<span class="text-sm text-gray-700 dark:text-gray-300">Retired status</span>
|
||||
</label>
|
||||
<label class="flex items-center gap-2 cursor-pointer">
|
||||
<input type="checkbox" name="cascade_project" id="editCascadeProject" value="true"
|
||||
class="w-4 h-4 text-seismo-orange focus:ring-seismo-orange rounded">
|
||||
<span class="text-sm text-gray-700 dark:text-gray-300">Project</span>
|
||||
</label>
|
||||
<label class="flex items-center gap-2 cursor-pointer">
|
||||
<input type="checkbox" name="cascade_location" id="editCascadeLocation" value="true"
|
||||
class="w-4 h-4 text-seismo-orange focus:ring-seismo-orange rounded">
|
||||
<span class="text-sm text-gray-700 dark:text-gray-300">Address</span>
|
||||
</label>
|
||||
<label class="flex items-center gap-2 cursor-pointer">
|
||||
<input type="checkbox" name="cascade_coordinates" id="editCascadeCoordinates" value="true"
|
||||
class="w-4 h-4 text-seismo-orange focus:ring-seismo-orange rounded">
|
||||
<span class="text-sm text-gray-700 dark:text-gray-300">Coordinates</span>
|
||||
</label>
|
||||
<label class="flex items-center gap-2 cursor-pointer">
|
||||
<input type="checkbox" name="cascade_note" id="editCascadeNote" value="true"
|
||||
class="w-4 h-4 text-seismo-orange focus:ring-seismo-orange rounded">
|
||||
<span class="text-sm text-gray-700 dark:text-gray-300">Notes</span>
|
||||
</label>
|
||||
</div>
|
||||
<p class="text-xs text-gray-500 dark:text-gray-400 mt-2">
|
||||
Check the fields you want to sync to the paired device
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-3 pt-4">
|
||||
<button type="submit" class="flex-1 px-4 py-2 bg-seismo-orange hover:bg-orange-600 text-white rounded-lg transition-colors">
|
||||
Save Changes
|
||||
@@ -650,11 +727,7 @@
|
||||
document.getElementById('addUnitForm').addEventListener('htmx:afterRequest', function(event) {
|
||||
if (event.detail.successful) {
|
||||
closeAddUnitModal();
|
||||
// Trigger roster refresh for current active tab
|
||||
htmx.ajax('GET', currentRosterEndpoint, {
|
||||
target: '#roster-content',
|
||||
swap: 'innerHTML'
|
||||
});
|
||||
refreshDeviceList();
|
||||
// Show success message
|
||||
alert('Unit added successfully!');
|
||||
} else {
|
||||
@@ -692,6 +765,33 @@
|
||||
function closeEditUnitModal() {
|
||||
document.getElementById('editUnitModal').classList.add('hidden');
|
||||
document.getElementById('editUnitForm').reset();
|
||||
// Also clear the project picker
|
||||
const projectPickerValue = document.getElementById('project-picker-value-edit');
|
||||
const projectPickerSearch = document.getElementById('project-picker-search-edit');
|
||||
const projectPickerClear = document.getElementById('project-picker-clear-edit');
|
||||
if (projectPickerValue) projectPickerValue.value = '';
|
||||
if (projectPickerSearch) projectPickerSearch.value = '';
|
||||
if (projectPickerClear) projectPickerClear.classList.add('hidden');
|
||||
}
|
||||
|
||||
// Fetch project display name for edit modal
|
||||
async function fetchProjectDisplayForEdit(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;
|
||||
}
|
||||
|
||||
// Toggle device-specific fields in edit modal
|
||||
@@ -753,7 +853,23 @@
|
||||
document.getElementById('editUnitId').value = unit.id;
|
||||
document.getElementById('editDeviceTypeSelect').value = unit.device_type;
|
||||
document.getElementById('editUnitType').value = unit.unit_type;
|
||||
document.getElementById('editProjectId').value = unit.project_id;
|
||||
|
||||
// Populate project picker (uses -edit suffix)
|
||||
const projectPickerValue = document.getElementById('project-picker-value-edit');
|
||||
const projectPickerSearch = document.getElementById('project-picker-search-edit');
|
||||
const projectPickerClear = document.getElementById('project-picker-clear-edit');
|
||||
if (projectPickerValue) projectPickerValue.value = unit.project_id || '';
|
||||
if (unit.project_id) {
|
||||
// Fetch project display name
|
||||
fetchProjectDisplayForEdit(unit.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('editAddress').value = unit.address;
|
||||
document.getElementById('editCoordinates').value = unit.coordinates;
|
||||
document.getElementById('editNote').value = unit.note;
|
||||
@@ -765,12 +881,62 @@
|
||||
// Seismograph fields
|
||||
document.getElementById('editLastCalibrated').value = unit.last_calibrated;
|
||||
document.getElementById('editNextCalibrationDue').value = unit.next_calibration_due;
|
||||
document.getElementById('editDeployedWithModemId').value = unit.deployed_with_modem_id;
|
||||
|
||||
// Populate modem picker for seismograph (uses -edit-seismo suffix)
|
||||
const modemPickerValue = document.getElementById('modem-picker-value-edit-seismo');
|
||||
const modemPickerSearch = document.getElementById('modem-picker-search-edit-seismo');
|
||||
const modemPickerClear = document.getElementById('modem-picker-clear-edit-seismo');
|
||||
if (modemPickerValue) modemPickerValue.value = unit.deployed_with_modem_id || '';
|
||||
if (unit.deployed_with_modem_id) {
|
||||
// Fetch modem display (ID + IP + note)
|
||||
fetch(`/api/roster/${unit.deployed_with_modem_id}`)
|
||||
.then(r => r.ok ? r.json() : null)
|
||||
.then(modem => {
|
||||
if (modem && modemPickerSearch) {
|
||||
let display = modem.id;
|
||||
if (modem.ip_address) display += ` - ${modem.ip_address}`;
|
||||
if (modem.note) display += ` - ${modem.note}`;
|
||||
modemPickerSearch.value = display;
|
||||
if (modemPickerClear) modemPickerClear.classList.remove('hidden');
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
if (modemPickerSearch) modemPickerSearch.value = unit.deployed_with_modem_id;
|
||||
});
|
||||
} else {
|
||||
if (modemPickerSearch) modemPickerSearch.value = '';
|
||||
if (modemPickerClear) modemPickerClear.classList.add('hidden');
|
||||
}
|
||||
|
||||
// Modem fields
|
||||
document.getElementById('editIpAddress').value = unit.ip_address;
|
||||
document.getElementById('editPhoneNumber').value = unit.phone_number;
|
||||
document.getElementById('editHardwareModel').value = unit.hardware_model;
|
||||
document.getElementById('editDeploymentType').value = unit.deployment_type || '';
|
||||
|
||||
// Populate unit picker for modem (uses -edit-modem suffix)
|
||||
const unitPickerValue = document.getElementById('unit-picker-value-edit-modem');
|
||||
const unitPickerSearch = document.getElementById('unit-picker-search-edit-modem');
|
||||
const unitPickerClear = document.getElementById('unit-picker-clear-edit-modem');
|
||||
if (unitPickerValue) unitPickerValue.value = unit.deployed_with_unit_id || '';
|
||||
if (unit.deployed_with_unit_id) {
|
||||
// Fetch unit display (ID + note)
|
||||
fetch(`/api/roster/${unit.deployed_with_unit_id}`)
|
||||
.then(r => r.ok ? r.json() : null)
|
||||
.then(linkedUnit => {
|
||||
if (linkedUnit && unitPickerSearch) {
|
||||
const display = linkedUnit.note ? `${linkedUnit.id} - ${linkedUnit.note}` : linkedUnit.id;
|
||||
unitPickerSearch.value = display;
|
||||
if (unitPickerClear) unitPickerClear.classList.remove('hidden');
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
if (unitPickerSearch) unitPickerSearch.value = unit.deployed_with_unit_id;
|
||||
});
|
||||
} else {
|
||||
if (unitPickerSearch) unitPickerSearch.value = '';
|
||||
if (unitPickerClear) unitPickerClear.classList.add('hidden');
|
||||
}
|
||||
|
||||
// SLM fields
|
||||
document.getElementById('editSlmModel').value = unit.slm_model || '';
|
||||
@@ -781,6 +947,35 @@
|
||||
document.getElementById('editSlmFrequencyWeighting').value = unit.slm_frequency_weighting || '';
|
||||
document.getElementById('editSlmTimeWeighting').value = unit.slm_time_weighting || '';
|
||||
|
||||
// Cascade section - show if there's a paired device
|
||||
const cascadeSection = document.getElementById('editCascadeSection');
|
||||
const cascadeToUnitId = document.getElementById('editCascadeToUnitId');
|
||||
const pairedDeviceName = document.getElementById('editPairedDeviceName');
|
||||
|
||||
// Determine paired device based on device type
|
||||
let pairedUnitId = null;
|
||||
if (unit.device_type === 'modem' && unit.deployed_with_unit_id) {
|
||||
pairedUnitId = unit.deployed_with_unit_id;
|
||||
} else if ((unit.device_type === 'seismograph' || unit.device_type === 'sound_level_meter') && unit.deployed_with_modem_id) {
|
||||
pairedUnitId = unit.deployed_with_modem_id;
|
||||
}
|
||||
|
||||
if (pairedUnitId) {
|
||||
cascadeToUnitId.value = pairedUnitId;
|
||||
pairedDeviceName.textContent = pairedUnitId;
|
||||
cascadeSection.classList.remove('hidden');
|
||||
// Reset checkboxes
|
||||
document.getElementById('editCascadeDeployed').checked = false;
|
||||
document.getElementById('editCascadeRetired').checked = false;
|
||||
document.getElementById('editCascadeProject').checked = false;
|
||||
document.getElementById('editCascadeLocation').checked = false;
|
||||
document.getElementById('editCascadeCoordinates').checked = false;
|
||||
document.getElementById('editCascadeNote').checked = false;
|
||||
} else {
|
||||
cascadeToUnitId.value = '';
|
||||
cascadeSection.classList.add('hidden');
|
||||
}
|
||||
|
||||
// Store unit ID for form submission
|
||||
document.getElementById('editUnitForm').dataset.unitId = unitId;
|
||||
|
||||
@@ -814,11 +1009,7 @@
|
||||
|
||||
if (response.ok) {
|
||||
closeEditUnitModal();
|
||||
// Trigger roster refresh for current active tab
|
||||
htmx.ajax('GET', currentRosterEndpoint, {
|
||||
target: '#roster-content',
|
||||
swap: 'innerHTML'
|
||||
});
|
||||
refreshDeviceList();
|
||||
alert('Unit updated successfully!');
|
||||
} else {
|
||||
const result = await response.json();
|
||||
@@ -846,11 +1037,7 @@
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
// Trigger roster refresh for current active tab
|
||||
htmx.ajax('GET', currentRosterEndpoint, {
|
||||
target: '#roster-content',
|
||||
swap: 'innerHTML'
|
||||
});
|
||||
refreshDeviceList();
|
||||
alert(`Unit ${deployed ? 'deployed' : 'benched'} successfully!`);
|
||||
} else {
|
||||
const result = await response.json();
|
||||
@@ -878,11 +1065,7 @@
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
// Trigger roster refresh for current active tab
|
||||
htmx.ajax('GET', currentRosterEndpoint, {
|
||||
target: '#roster-content',
|
||||
swap: 'innerHTML'
|
||||
});
|
||||
refreshDeviceList();
|
||||
alert(`Unit ${unitId} moved to ignore list`);
|
||||
} else {
|
||||
const result = await response.json();
|
||||
@@ -905,11 +1088,7 @@
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
// Trigger roster refresh for current active tab
|
||||
htmx.ajax('GET', currentRosterEndpoint, {
|
||||
target: '#roster-content',
|
||||
swap: 'innerHTML'
|
||||
});
|
||||
refreshDeviceList();
|
||||
alert(`Unit ${unitId} deleted successfully`);
|
||||
} else {
|
||||
const result = await response.json();
|
||||
@@ -948,11 +1127,7 @@
|
||||
`;
|
||||
resultDiv.classList.remove('hidden');
|
||||
|
||||
// Trigger roster refresh for current active tab
|
||||
htmx.ajax('GET', currentRosterEndpoint, {
|
||||
target: '#roster-content',
|
||||
swap: 'innerHTML'
|
||||
});
|
||||
refreshDeviceList();
|
||||
|
||||
// Close modal after 2 seconds
|
||||
setTimeout(() => closeImportModal(), 2000);
|
||||
@@ -968,35 +1143,26 @@
|
||||
}
|
||||
});
|
||||
|
||||
// Handle roster tab switching with auto-refresh
|
||||
let currentRosterEndpoint = '/partials/roster-deployed'; // Default to deployed tab
|
||||
// Refresh device list (applies current client-side filters after load)
|
||||
function refreshDeviceList() {
|
||||
htmx.ajax('GET', '/partials/devices-all', {
|
||||
target: '#device-content',
|
||||
swap: 'innerHTML'
|
||||
}).then(() => {
|
||||
// Re-apply filters after content loads
|
||||
setTimeout(filterDevices, 100);
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const tabButtons = document.querySelectorAll('.roster-tab-button');
|
||||
|
||||
tabButtons.forEach(button => {
|
||||
button.addEventListener('click', function() {
|
||||
// Remove active-roster-tab class from all buttons
|
||||
tabButtons.forEach(btn => btn.classList.remove('active-roster-tab'));
|
||||
// Add active-roster-tab class to clicked button
|
||||
this.classList.add('active-roster-tab');
|
||||
|
||||
// Update current endpoint for auto-refresh
|
||||
currentRosterEndpoint = this.getAttribute('data-endpoint');
|
||||
});
|
||||
});
|
||||
|
||||
// Auto-refresh the current active tab every 10 seconds
|
||||
// Auto-refresh device list every 30 seconds (increased from 10s to reduce flicker)
|
||||
setInterval(() => {
|
||||
const rosterContent = document.getElementById('roster-content');
|
||||
if (rosterContent) {
|
||||
// Use HTMX to trigger a refresh of the current endpoint
|
||||
htmx.ajax('GET', currentRosterEndpoint, {
|
||||
target: '#roster-content',
|
||||
swap: 'innerHTML'
|
||||
});
|
||||
const deviceContent = document.getElementById('device-content');
|
||||
if (deviceContent && !document.querySelector('.modal:not(.hidden)')) {
|
||||
// Only auto-refresh if no modal is open
|
||||
refreshDeviceList();
|
||||
}
|
||||
}, 10000); // 10 seconds
|
||||
}, 30000);
|
||||
});
|
||||
|
||||
// Un-ignore Unit (remove from ignored list)
|
||||
@@ -1345,4 +1511,7 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Include Project Create Modal for inline project creation -->
|
||||
{% include "partials/project_create_modal.html" %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user