refactor: Clean checkin override parsing and require node type in frontend configuration modal
Build & Deploy / Build & push images (push) Successful in 4m0s
Build & Deploy / Deploy to VM (push) Failing after 2m12s

This commit is contained in:
Logan Cusano
2026-07-12 23:20:39 -04:00
parent c42bd1902c
commit 2e3fde2448
2 changed files with 13 additions and 9 deletions
+8 -6
View File
@@ -1,6 +1,6 @@
import asyncio import asyncio
import json import json
from datetime import datetime, timezone from datetime import datetime, timezone, timedelta
from typing import Optional from typing import Optional
import paho.mqtt.client as mqtt import paho.mqtt.client as mqtt
from app.config import settings from app.config import settings
@@ -117,14 +117,17 @@ class MQTTHandler:
# Approved but not yet configured — restore reachable status after reboot # Approved but not yet configured — restore reachable status after reboot
updates["status"] = "unconfigured" updates["status"] = "unconfigured"
node_type = payload.get("node_type", existing.get("node_type", "fixed")) node_type = payload.get("node_type") or existing.get("node_type") or "fixed"
enforce_timeout = payload.get("enforce_override_timeout", existing.get("enforce_override_timeout", True)) enforce_timeout = payload.get("enforce_override_timeout")
is_overridden = payload.get("is_overridden", False) if enforce_timeout is None:
override_system_id = payload.get("override_system_id") enforce_timeout = existing.get("enforce_override_timeout", True)
updates["node_type"] = node_type updates["node_type"] = node_type
updates["enforce_override_timeout"] = enforce_timeout updates["enforce_override_timeout"] = enforce_timeout
is_overridden = payload.get("is_overridden", False)
override_system_id = payload.get("override_system_id")
if node_type == "portable": if node_type == "portable":
updates["is_overridden"] = False updates["is_overridden"] = False
updates["override_system_id"] = None updates["override_system_id"] = None
@@ -138,7 +141,6 @@ class MQTTHandler:
existing_override_id = existing.get("override_system_id") existing_override_id = existing.get("override_system_id")
if enforce_timeout: if enforce_timeout:
if not existing_timeout or existing_override_id != override_system_id: if not existing_timeout or existing_override_id != override_system_id:
from datetime import timedelta
updates["override_timeout_at"] = (now + timedelta(hours=24)).isoformat() updates["override_timeout_at"] = (now + timedelta(hours=24)).isoformat()
else: else:
updates["override_timeout_at"] = None updates["override_timeout_at"] = None
+5 -3
View File
@@ -20,7 +20,7 @@ export function NodeConfigModal({ node, systems, onClose }: Props) {
const [systemId, setSystemId] = useState(node.assigned_system_id ?? ""); const [systemId, setSystemId] = useState(node.assigned_system_id ?? "");
const [preset, setPreset] = useState(node.hardware_preset ?? "rtl-sdr-v3"); const [preset, setPreset] = useState(node.hardware_preset ?? "rtl-sdr-v3");
const [ppm, setPpm] = useState(node.ppm_override ? String(node.ppm_override) : "0"); const [ppm, setPpm] = useState(node.ppm_override ? String(node.ppm_override) : "0");
const [nodeType, setNodeType] = useState(node.node_type ?? "fixed"); const [nodeType, setNodeType] = useState(node.node_type ?? "");
const [enforceTimeout, setEnforceTimeout] = useState(node.enforce_override_timeout ?? true); const [enforceTimeout, setEnforceTimeout] = useState(node.enforce_override_timeout ?? true);
const [saving, setSaving] = useState(false); const [saving, setSaving] = useState(false);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
@@ -107,12 +107,14 @@ export function NodeConfigModal({ node, systems, onClose }: Props) {
</div> </div>
<div> <div>
<label className="block text-xs text-gray-400 mb-1">Node Type</label> <label className="block text-xs text-gray-400 mb-1">Node Type *</label>
<select <select
value={nodeType} value={nodeType}
onChange={(e) => setNodeType(e.target.value)} onChange={(e) => setNodeType(e.target.value)}
className="w-full bg-gray-800 border border-gray-700 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-indigo-500" className="w-full bg-gray-800 border border-gray-700 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-indigo-500"
required
> >
<option value="">Select node type...</option>
<option value="fixed">Fixed Node (Standard)</option> <option value="fixed">Fixed Node (Standard)</option>
<option value="portable">Portable Node (Handheld)</option> <option value="portable">Portable Node (Handheld)</option>
</select> </select>
@@ -138,7 +140,7 @@ export function NodeConfigModal({ node, systems, onClose }: Props) {
<div className="flex gap-3 pt-1"> <div className="flex gap-3 pt-1">
<button <button
type="submit" type="submit"
disabled={saving || !systemId} disabled={saving || !systemId || !nodeType}
className="flex-1 bg-indigo-600 hover:bg-indigo-500 disabled:opacity-50 text-white rounded-lg py-2 text-sm font-semibold transition-colors" className="flex-1 bg-indigo-600 hover:bg-indigo-500 disabled:opacity-50 text-white rounded-lg py-2 text-sm font-semibold transition-colors"
> >
{saving ? "Saving…" : "Assign & Configure"} {saving ? "Saving…" : "Assign & Configure"}