diff --git a/work/dashboard-frontend/src/pages/MeshCoreRouting.tsx b/work/dashboard-frontend/src/pages/MeshCoreRouting.tsx
index 95874a1..1709997 100644
--- a/work/dashboard-frontend/src/pages/MeshCoreRouting.tsx
+++ b/work/dashboard-frontend/src/pages/MeshCoreRouting.tsx
@@ -1,5 +1,6 @@
import { useState, useEffect, useCallback } from 'react'
import { Link } from 'react-router-dom'
+import { useDirty } from '@/context/DirtyContext'
import { Save, RotateCcw, RefreshCw, Check, MessageSquare, ExternalLink } from 'lucide-react'
import {
SeverityChannelMatrix,
@@ -40,6 +41,7 @@ function mergeMeshcoreFields(
}
export default function MeshCoreRouting() {
+ const { setDirty } = useDirty()
const [config, setConfig] = useState
(null)
const [originalConfig, setOriginalConfig] = useState(null)
const [loading, setLoading] = useState(true)
@@ -75,6 +77,11 @@ export default function MeshCoreRouting() {
}
}, [config, originalConfig])
+ useEffect(() => {
+ setDirty(hasChanges)
+ return () => setDirty(false)
+ }, [hasChanges, setDirty])
+
const upd = (fam: string, patch: Partial) => {
if (!config) return
const toggles = config.toggles || {}
@@ -118,6 +125,7 @@ export default function MeshCoreRouting() {
setConfig(merged)
setOriginalConfig(JSON.parse(JSON.stringify(merged)))
setHasChanges(false)
+ setDirty(false)
setSuccess('MeshCore routing saved successfully')
setTimeout(() => setSuccess(null), 3000)
} catch (err) {
diff --git a/work/dashboard-frontend/src/pages/MeshtasticConnection.tsx b/work/dashboard-frontend/src/pages/MeshtasticConnection.tsx
index f52e8ab..3e54582 100644
--- a/work/dashboard-frontend/src/pages/MeshtasticConnection.tsx
+++ b/work/dashboard-frontend/src/pages/MeshtasticConnection.tsx
@@ -3,8 +3,10 @@ import { Save, RotateCcw, RefreshCw, Check } from 'lucide-react'
import { ConnectionSection, type ConnectionConfig } from './Config'
import { notifyRestartRequired } from '@/components/RestartBanner'
import { fetchConfig as apiFetchConfig, updateConfig as apiUpdateConfig } from '@/lib/api'
+import { useDirty } from '@/context/DirtyContext'
export default function MeshtasticConnection() {
+ const { setDirty } = useDirty()
const [config, setConfig] = useState(null)
const [originalConfig, setOriginalConfig] = useState(null)
const [loading, setLoading] = useState(true)
@@ -39,6 +41,11 @@ export default function MeshtasticConnection() {
}
}, [config, originalConfig])
+ useEffect(() => {
+ setDirty(hasChanges)
+ return () => setDirty(false)
+ }, [hasChanges, setDirty])
+
const saveConfig = async () => {
if (!config) return
setSaving(true)
@@ -49,6 +56,7 @@ export default function MeshtasticConnection() {
const result = await apiUpdateConfig('connection', config)
setOriginalConfig(JSON.parse(JSON.stringify(config)))
setHasChanges(false)
+ setDirty(false)
setSuccess('Meshtastic connection saved successfully')
if (result.restart_required) {
notifyRestartRequired([])
diff --git a/work/dashboard-frontend/src/pages/MeshtasticSources.tsx b/work/dashboard-frontend/src/pages/MeshtasticSources.tsx
index 3583a49..d0e69d4 100644
--- a/work/dashboard-frontend/src/pages/MeshtasticSources.tsx
+++ b/work/dashboard-frontend/src/pages/MeshtasticSources.tsx
@@ -8,8 +8,10 @@ import {
} from './Config'
import { notifyRestartRequired } from '@/components/RestartBanner'
import { fetchConfig as apiFetchConfig, updateConfig as apiUpdateConfig } from '@/lib/api'
+import { useDirty } from '@/context/DirtyContext'
export default function MeshtasticSources() {
+ const { setDirty } = useDirty()
const [meshmonitor, setMeshmonitor] = useState(null)
const [originalMeshmonitor, setOriginalMeshmonitor] = useState(null)
const [meshSources, setMeshSources] = useState(null)
@@ -54,6 +56,11 @@ export default function MeshtasticSources() {
}
}, [meshmonitor, originalMeshmonitor, meshSources, originalMeshSources])
+ useEffect(() => {
+ setDirty(hasChanges)
+ return () => setDirty(false)
+ }, [hasChanges, setDirty])
+
const saveConfig = async () => {
if (!meshmonitor || !meshSources) return
setSaving(true)
@@ -67,6 +74,7 @@ export default function MeshtasticSources() {
setOriginalMeshmonitor(JSON.parse(JSON.stringify(meshmonitor)))
setOriginalMeshSources(JSON.parse(JSON.stringify(meshSources)))
setHasChanges(false)
+ setDirty(false)
setSuccess('Meshtastic sources saved successfully')
if (mmResult.restart_required || msResult.restart_required) {
notifyRestartRequired([])
diff --git a/work/dashboard-frontend/src/pages/Notifications.tsx b/work/dashboard-frontend/src/pages/Notifications.tsx
index 7bdab66..5cf9195 100644
--- a/work/dashboard-frontend/src/pages/Notifications.tsx
+++ b/work/dashboard-frontend/src/pages/Notifications.tsx
@@ -9,6 +9,7 @@ import {
import ChannelPicker from '@/components/ChannelPicker'
import NodePicker from '@/components/NodePicker'
import { fetchConfig as apiFetchConfig, updateConfig as apiUpdateConfig } from '@/lib/api'
+import { useDirty } from '@/context/DirtyContext'
// Types
interface NotificationRuleConfig {
@@ -1762,6 +1763,7 @@ function MasterToggles({ toggles, onChange }: {
export default function Notifications() {
+ const { setDirty } = useDirty()
const [config, setConfig] = useState(null)
const [originalConfig, setOriginalConfig] = useState(null)
const [categories, setCategories] = useState([])
@@ -1811,6 +1813,11 @@ export default function Notifications() {
}
}, [config, originalConfig])
+ useEffect(() => {
+ setDirty(hasChanges)
+ return () => setDirty(false)
+ }, [hasChanges, setDirty])
+
const saveConfig = async () => {
if (!config) return
@@ -1834,6 +1841,7 @@ export default function Notifications() {
setSuccess('Notifications config saved successfully')
setOriginalConfig(JSON.parse(JSON.stringify(config)))
setHasChanges(false)
+ setDirty(false)
setTimeout(() => setSuccess(null), 3000)
} catch (err) {
setError(err instanceof Error ? err.message : 'Save failed')