feat: add data collection mode to projects with UI updates and migration script
This commit is contained in:
@@ -75,6 +75,32 @@ Include this modal in pages that use the project picker.
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
Data Collection <span class="text-red-500">*</span>
|
||||
</label>
|
||||
<div class="grid grid-cols-2 gap-3">
|
||||
<label class="flex items-start gap-3 p-3 border-2 border-seismo-orange bg-orange-50 dark:bg-orange-900/20 rounded-lg cursor-pointer" id="qcp-mode-manual-label">
|
||||
<input type="radio" name="data_collection_mode" value="manual" checked
|
||||
onchange="qcpUpdateModeStyles()"
|
||||
class="mt-0.5 accent-seismo-orange shrink-0">
|
||||
<div>
|
||||
<p class="text-sm font-medium text-gray-900 dark:text-white">Manual</p>
|
||||
<p class="text-xs text-gray-500 dark:text-gray-400">SD card retrieved daily</p>
|
||||
</div>
|
||||
</label>
|
||||
<label class="flex items-start gap-3 p-3 border-2 border-gray-300 dark:border-gray-600 rounded-lg cursor-pointer" id="qcp-mode-remote-label">
|
||||
<input type="radio" name="data_collection_mode" value="remote"
|
||||
onchange="qcpUpdateModeStyles()"
|
||||
class="mt-0.5 accent-seismo-orange shrink-0">
|
||||
<div>
|
||||
<p class="text-sm font-medium text-gray-900 dark:text-white">Remote</p>
|
||||
<p class="text-xs text-gray-500 dark:text-gray-400">Modem, data pulled via FTP</p>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="qcp-error" class="hidden p-3 bg-red-100 dark:bg-red-900/30 text-red-700 dark:text-red-300 rounded-lg text-sm">
|
||||
</div>
|
||||
|
||||
@@ -98,6 +124,24 @@ Include this modal in pages that use the project picker.
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function qcpUpdateModeStyles() {
|
||||
const manualChecked = document.querySelector('input[name="data_collection_mode"][value="manual"]')?.checked;
|
||||
const manualLabel = document.getElementById('qcp-mode-manual-label');
|
||||
const remoteLabel = document.getElementById('qcp-mode-remote-label');
|
||||
if (!manualLabel || !remoteLabel) return;
|
||||
if (manualChecked) {
|
||||
manualLabel.classList.add('border-seismo-orange', 'bg-orange-50', 'dark:bg-orange-900/20');
|
||||
manualLabel.classList.remove('border-gray-300', 'dark:border-gray-600');
|
||||
remoteLabel.classList.remove('border-seismo-orange', 'bg-orange-50', 'dark:bg-orange-900/20');
|
||||
remoteLabel.classList.add('border-gray-300', 'dark:border-gray-600');
|
||||
} else {
|
||||
remoteLabel.classList.add('border-seismo-orange', 'bg-orange-50', 'dark:bg-orange-900/20');
|
||||
remoteLabel.classList.remove('border-gray-300', 'dark:border-gray-600');
|
||||
manualLabel.classList.remove('border-seismo-orange', 'bg-orange-50', 'dark:bg-orange-900/20');
|
||||
manualLabel.classList.add('border-gray-300', 'dark:border-gray-600');
|
||||
}
|
||||
}
|
||||
|
||||
// Quick create project modal functions
|
||||
if (typeof openCreateProjectModal === 'undefined') {
|
||||
function openCreateProjectModal(searchQuery, pickerId = '') {
|
||||
@@ -113,6 +157,7 @@ if (typeof openCreateProjectModal === 'undefined') {
|
||||
|
||||
// Reset form
|
||||
document.getElementById('quickCreateProjectForm').reset();
|
||||
qcpUpdateModeStyles();
|
||||
if (errorDiv) errorDiv.classList.add('hidden');
|
||||
|
||||
// Try to parse the search query intelligently
|
||||
|
||||
@@ -12,6 +12,21 @@
|
||||
{% if project_type %}
|
||||
<span class="text-gray-500 dark:text-gray-400">{{ project_type.name }}</span>
|
||||
{% endif %}
|
||||
{% if project.data_collection_mode == 'remote' %}
|
||||
<span class="inline-flex items-center gap-1 px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-800 dark:bg-blue-900/40 dark:text-blue-300">
|
||||
<svg class="w-3 h-3" 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>
|
||||
Remote
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="inline-flex items-center gap-1 px-2.5 py-0.5 rounded-full text-xs font-medium bg-amber-100 text-amber-800 dark:bg-amber-900/40 dark:text-amber-300">
|
||||
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 8h14M5 8a2 2 0 110-4h14a2 2 0 110 4M5 8v10a2 2 0 002 2h10a2 2 0 002-2V8m-9 4h4"></path>
|
||||
</svg>
|
||||
Manual
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<!-- Project Actions -->
|
||||
|
||||
@@ -40,12 +40,12 @@
|
||||
class="tab-button px-4 py-3 border-b-2 border-transparent font-medium text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white hover:border-gray-300 dark:hover:border-gray-600 transition-colors whitespace-nowrap">
|
||||
<span id="locations-tab-label">Locations</span>
|
||||
</button>
|
||||
<button onclick="switchTab('units')"
|
||||
<button id="units-tab-btn" onclick="switchTab('units')"
|
||||
data-tab="units"
|
||||
class="tab-button px-4 py-3 border-b-2 border-transparent font-medium text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white hover:border-gray-300 dark:hover:border-gray-600 transition-colors whitespace-nowrap">
|
||||
Assigned Units
|
||||
</button>
|
||||
<button onclick="switchTab('schedules')"
|
||||
<button id="schedules-tab-btn" onclick="switchTab('schedules')"
|
||||
data-tab="schedules"
|
||||
class="tab-button px-4 py-3 border-b-2 border-transparent font-medium text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white hover:border-gray-300 dark:hover:border-gray-600 transition-colors whitespace-nowrap">
|
||||
Schedules
|
||||
@@ -324,6 +324,30 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Data Collection</label>
|
||||
<div class="grid grid-cols-2 gap-3">
|
||||
<label class="flex items-start gap-3 p-3 border-2 rounded-lg cursor-pointer" id="settings-mode-manual-label">
|
||||
<input type="radio" name="data_collection_mode" id="settings-mode-manual" value="manual"
|
||||
onchange="settingsUpdateModeStyles()"
|
||||
class="mt-0.5 accent-seismo-orange shrink-0">
|
||||
<div>
|
||||
<p class="text-sm font-medium text-gray-900 dark:text-white">Manual</p>
|
||||
<p class="text-xs text-gray-500 dark:text-gray-400">SD card retrieved daily</p>
|
||||
</div>
|
||||
</label>
|
||||
<label class="flex items-start gap-3 p-3 border-2 rounded-lg cursor-pointer" id="settings-mode-remote-label">
|
||||
<input type="radio" name="data_collection_mode" id="settings-mode-remote" value="remote"
|
||||
onchange="settingsUpdateModeStyles()"
|
||||
class="mt-0.5 accent-seismo-orange shrink-0">
|
||||
<div>
|
||||
<p class="text-sm font-medium text-gray-900 dark:text-white">Remote</p>
|
||||
<p class="text-xs text-gray-500 dark:text-gray-400">Modem, data pulled via FTP</p>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Site Address</label>
|
||||
<input type="text" name="site_address" id="settings-site-address"
|
||||
@@ -775,6 +799,12 @@ async function loadProjectDetails() {
|
||||
document.getElementById('settings-start-date').value = formatDate(data.start_date);
|
||||
document.getElementById('settings-end-date').value = formatDate(data.end_date);
|
||||
|
||||
// Update data collection mode radio
|
||||
const mode = data.data_collection_mode || 'manual';
|
||||
const modeRadio = document.getElementById('settings-mode-' + mode);
|
||||
if (modeRadio) modeRadio.checked = true;
|
||||
settingsUpdateModeStyles();
|
||||
|
||||
// Update tab labels and visibility based on project type
|
||||
const isSoundProject = projectTypeId === 'sound_monitoring';
|
||||
if (isSoundProject) {
|
||||
@@ -782,9 +812,16 @@ async function loadProjectDetails() {
|
||||
document.getElementById('locations-header').textContent = 'Noise Recording Locations';
|
||||
document.getElementById('add-location-label').textContent = 'Add NRL';
|
||||
}
|
||||
// Monitoring Sessions and Data Files tabs are SLM-only
|
||||
// Monitoring Sessions and Data Files tabs are sound-only
|
||||
// Data Files also hides the FTP browser section for manual projects
|
||||
const isRemote = mode === 'remote';
|
||||
document.getElementById('sessions-tab-btn').classList.toggle('hidden', !isSoundProject);
|
||||
document.getElementById('data-tab-btn').classList.toggle('hidden', !isSoundProject);
|
||||
// Schedules and Assigned Units are remote-only (manual projects collect data by hand)
|
||||
document.getElementById('schedules-tab-btn')?.classList.toggle('hidden', isSoundProject && !isRemote);
|
||||
document.getElementById('units-tab-btn')?.classList.toggle('hidden', isSoundProject && !isRemote);
|
||||
// FTP browser within Data Files tab
|
||||
document.getElementById('ftp-browser')?.classList.toggle('hidden', !isRemote);
|
||||
|
||||
document.getElementById('settings-error').classList.add('hidden');
|
||||
updateDangerZone();
|
||||
@@ -800,6 +837,24 @@ function formatDate(value) {
|
||||
return date.toISOString().slice(0, 10);
|
||||
}
|
||||
|
||||
function settingsUpdateModeStyles() {
|
||||
const manualChecked = document.getElementById('settings-mode-manual')?.checked;
|
||||
const manualLabel = document.getElementById('settings-mode-manual-label');
|
||||
const remoteLabel = document.getElementById('settings-mode-remote-label');
|
||||
if (!manualLabel || !remoteLabel) return;
|
||||
if (manualChecked) {
|
||||
manualLabel.classList.add('border-seismo-orange', 'bg-orange-50', 'dark:bg-orange-900/20');
|
||||
manualLabel.classList.remove('border-gray-300', 'dark:border-gray-600');
|
||||
remoteLabel.classList.remove('border-seismo-orange', 'bg-orange-50', 'dark:bg-orange-900/20');
|
||||
remoteLabel.classList.add('border-gray-300', 'dark:border-gray-600');
|
||||
} else {
|
||||
remoteLabel.classList.add('border-seismo-orange', 'bg-orange-50', 'dark:bg-orange-900/20');
|
||||
remoteLabel.classList.remove('border-gray-300', 'dark:border-gray-600');
|
||||
manualLabel.classList.remove('border-seismo-orange', 'bg-orange-50', 'dark:bg-orange-900/20');
|
||||
manualLabel.classList.add('border-gray-300', 'dark:border-gray-600');
|
||||
}
|
||||
}
|
||||
|
||||
// Project settings form submission
|
||||
document.getElementById('project-settings-form').addEventListener('submit', async function(e) {
|
||||
e.preventDefault();
|
||||
@@ -812,7 +867,8 @@ document.getElementById('project-settings-form').addEventListener('submit', asyn
|
||||
site_address: document.getElementById('settings-site-address').value.trim() || null,
|
||||
site_coordinates: document.getElementById('settings-site-coordinates').value.trim() || null,
|
||||
start_date: document.getElementById('settings-start-date').value || null,
|
||||
end_date: document.getElementById('settings-end-date').value || null
|
||||
end_date: document.getElementById('settings-end-date').value || null,
|
||||
data_collection_mode: document.querySelector('input[name="data_collection_mode"]:checked')?.value || 'manual'
|
||||
};
|
||||
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user