meshai/dashboard-frontend/src/pages/Environment.tsx

480 lines
28 KiB
TypeScript
Raw Normal View History

feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle Restructures the environmental config UI into the 7-family taxonomy on the /environment page (Matt-approved Option C: unified families = config + live status per adapter). The editable adapter config moves out of the Config page's "Environmental" tab (now deprecated) onto /environment, where each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings) together with its live status (feed health + active events). Frontend (dashboard-frontend/src): - pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family adapter sub-tab strip -> AdapterPanel. * AdapterPanel: header row = on/off Toggle + feed_source toggle (native|central). When OFF, feed_source + all settings grey out (disabled, not hidden). * Native-only adapters (ducting, avalanche, roads511 -- no Central stream per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central' disabled + a 'Central not available for this adapter' tooltip/label. * Missing-key adapters (firms, roads511) show an 'API key not configured -- contact admin' notice; toggles still operate. * Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes are planned for v0.5.'). Mesh Health = no env adapters; a disabled feed_source toggle with 'central' greyed for future migration. * All existing per-adapter settings preserved verbatim (NWS zones/user_agent/ severity, ducting lat/lon, fires state, avalanche centers/season, USGS sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS map_key/source/confidence/bbox). ALSO adds a usgs_quake panel (tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in the old GUI. feed_source field (C.1) now surfaced per adapter. * Per-adapter live status: reuses /api/env/status feed health + /api/env/active events (filtered by source; fires->nifc mapping). Refreshes every 30s. - pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS entry + render case + EnvironmentalSection function + unused Thermometer import); exported the shared form primitives (Toggle, TextInput, NumberInput, SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx. - Reuses the existing restart_required banner pattern. - Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js, index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle). Rule 17 / no backend change: config is wired to the existing schema-driven GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake fields ride the generic dataclass coercion). No backend edited this phase. Verification: (A) `npm run build` clean -- tsc strict + vite, only the pre-existing >500kB single-chunk advisory (not introduced here). (B) static committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell (index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards, RF Propagation, ADS-B, 'Central not available', 'API key not configured'). (D) GET /api/config/environmental returns all adapters with feed_source=native, usgs_quake present, central{enabled:false} -- toggles bind to real data; all native, nothing flipped. Rebuilt prod healthy. *** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 *** The save half of gate D fails: PUT /api/config/environmental returns {"detail":"could not determine a constructor for the tag '!include' ..."}. Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py) calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a loader lacking the !include constructor) instead of the multi-file-aware meshai/config_loader.py::save_section that exists for exactly the !include layout. This breaks ALL GUI config saves in prod (every section, not just environmental) and predates C.2 -- the old Config 'Environmental' tab had the same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind works; only persistence is blocked. Verified disk pristine (idempotent PUT errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup). FIX (one line, but prod-wide blast radius -> wants its own phase + verification): config_routes PUT should call config_loader.save_section(section, data, config_dir) instead of config.save_config(...). Recommend C.2.1 backend fix before C.3. C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save, since flipping feed_source from the GUI is the whole point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
import { useEffect, useState, type ReactNode } from 'react'
import {
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle Restructures the environmental config UI into the 7-family taxonomy on the /environment page (Matt-approved Option C: unified families = config + live status per adapter). The editable adapter config moves out of the Config page's "Environmental" tab (now deprecated) onto /environment, where each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings) together with its live status (feed health + active events). Frontend (dashboard-frontend/src): - pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family adapter sub-tab strip -> AdapterPanel. * AdapterPanel: header row = on/off Toggle + feed_source toggle (native|central). When OFF, feed_source + all settings grey out (disabled, not hidden). * Native-only adapters (ducting, avalanche, roads511 -- no Central stream per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central' disabled + a 'Central not available for this adapter' tooltip/label. * Missing-key adapters (firms, roads511) show an 'API key not configured -- contact admin' notice; toggles still operate. * Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes are planned for v0.5.'). Mesh Health = no env adapters; a disabled feed_source toggle with 'central' greyed for future migration. * All existing per-adapter settings preserved verbatim (NWS zones/user_agent/ severity, ducting lat/lon, fires state, avalanche centers/season, USGS sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS map_key/source/confidence/bbox). ALSO adds a usgs_quake panel (tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in the old GUI. feed_source field (C.1) now surfaced per adapter. * Per-adapter live status: reuses /api/env/status feed health + /api/env/active events (filtered by source; fires->nifc mapping). Refreshes every 30s. - pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS entry + render case + EnvironmentalSection function + unused Thermometer import); exported the shared form primitives (Toggle, TextInput, NumberInput, SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx. - Reuses the existing restart_required banner pattern. - Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js, index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle). Rule 17 / no backend change: config is wired to the existing schema-driven GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake fields ride the generic dataclass coercion). No backend edited this phase. Verification: (A) `npm run build` clean -- tsc strict + vite, only the pre-existing >500kB single-chunk advisory (not introduced here). (B) static committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell (index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards, RF Propagation, ADS-B, 'Central not available', 'API key not configured'). (D) GET /api/config/environmental returns all adapters with feed_source=native, usgs_quake present, central{enabled:false} -- toggles bind to real data; all native, nothing flipped. Rebuilt prod healthy. *** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 *** The save half of gate D fails: PUT /api/config/environmental returns {"detail":"could not determine a constructor for the tag '!include' ..."}. Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py) calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a loader lacking the !include constructor) instead of the multi-file-aware meshai/config_loader.py::save_section that exists for exactly the !include layout. This breaks ALL GUI config saves in prod (every section, not just environmental) and predates C.2 -- the old Config 'Environmental' tab had the same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind works; only persistence is blocked. Verified disk pristine (idempotent PUT errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup). FIX (one line, but prod-wide blast radius -> wants its own phase + verification): config_routes PUT should call config_loader.save_section(section, data, config_dir) instead of config.save_config(...). Recommend C.2.1 backend fix before C.3. C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save, since flipping feed_source from the GUI is the whole point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
Cloud, Flame, Radio, Car, Mountain, Satellite, Activity,
Save, RotateCcw, RefreshCw, AlertCircle, AlertTriangle, Info,
} from 'lucide-react'
import {
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle Restructures the environmental config UI into the 7-family taxonomy on the /environment page (Matt-approved Option C: unified families = config + live status per adapter). The editable adapter config moves out of the Config page's "Environmental" tab (now deprecated) onto /environment, where each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings) together with its live status (feed health + active events). Frontend (dashboard-frontend/src): - pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family adapter sub-tab strip -> AdapterPanel. * AdapterPanel: header row = on/off Toggle + feed_source toggle (native|central). When OFF, feed_source + all settings grey out (disabled, not hidden). * Native-only adapters (ducting, avalanche, roads511 -- no Central stream per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central' disabled + a 'Central not available for this adapter' tooltip/label. * Missing-key adapters (firms, roads511) show an 'API key not configured -- contact admin' notice; toggles still operate. * Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes are planned for v0.5.'). Mesh Health = no env adapters; a disabled feed_source toggle with 'central' greyed for future migration. * All existing per-adapter settings preserved verbatim (NWS zones/user_agent/ severity, ducting lat/lon, fires state, avalanche centers/season, USGS sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS map_key/source/confidence/bbox). ALSO adds a usgs_quake panel (tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in the old GUI. feed_source field (C.1) now surfaced per adapter. * Per-adapter live status: reuses /api/env/status feed health + /api/env/active events (filtered by source; fires->nifc mapping). Refreshes every 30s. - pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS entry + render case + EnvironmentalSection function + unused Thermometer import); exported the shared form primitives (Toggle, TextInput, NumberInput, SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx. - Reuses the existing restart_required banner pattern. - Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js, index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle). Rule 17 / no backend change: config is wired to the existing schema-driven GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake fields ride the generic dataclass coercion). No backend edited this phase. Verification: (A) `npm run build` clean -- tsc strict + vite, only the pre-existing >500kB single-chunk advisory (not introduced here). (B) static committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell (index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards, RF Propagation, ADS-B, 'Central not available', 'API key not configured'). (D) GET /api/config/environmental returns all adapters with feed_source=native, usgs_quake present, central{enabled:false} -- toggles bind to real data; all native, nothing flipped. Rebuilt prod healthy. *** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 *** The save half of gate D fails: PUT /api/config/environmental returns {"detail":"could not determine a constructor for the tag '!include' ..."}. Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py) calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a loader lacking the !include constructor) instead of the multi-file-aware meshai/config_loader.py::save_section that exists for exactly the !include layout. This breaks ALL GUI config saves in prod (every section, not just environmental) and predates C.2 -- the old Config 'Environmental' tab had the same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind works; only persistence is blocked. Verified disk pristine (idempotent PUT errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup). FIX (one line, but prod-wide blast radius -> wants its own phase + verification): config_routes PUT should call config_loader.save_section(section, data, config_dir) instead of config.save_config(...). Recommend C.2.1 backend fix before C.3. C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save, since flipping feed_source from the GUI is the whole point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
Toggle, TextInput, NumberInput, SelectInput, ListInput, NumberListInput,
US_STATES,
} from './Config'
import {
fetchEnvStatus, fetchEnvActive,
type EnvStatus, type EnvEvent,
} from '@/lib/api'
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle Restructures the environmental config UI into the 7-family taxonomy on the /environment page (Matt-approved Option C: unified families = config + live status per adapter). The editable adapter config moves out of the Config page's "Environmental" tab (now deprecated) onto /environment, where each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings) together with its live status (feed health + active events). Frontend (dashboard-frontend/src): - pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family adapter sub-tab strip -> AdapterPanel. * AdapterPanel: header row = on/off Toggle + feed_source toggle (native|central). When OFF, feed_source + all settings grey out (disabled, not hidden). * Native-only adapters (ducting, avalanche, roads511 -- no Central stream per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central' disabled + a 'Central not available for this adapter' tooltip/label. * Missing-key adapters (firms, roads511) show an 'API key not configured -- contact admin' notice; toggles still operate. * Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes are planned for v0.5.'). Mesh Health = no env adapters; a disabled feed_source toggle with 'central' greyed for future migration. * All existing per-adapter settings preserved verbatim (NWS zones/user_agent/ severity, ducting lat/lon, fires state, avalanche centers/season, USGS sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS map_key/source/confidence/bbox). ALSO adds a usgs_quake panel (tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in the old GUI. feed_source field (C.1) now surfaced per adapter. * Per-adapter live status: reuses /api/env/status feed health + /api/env/active events (filtered by source; fires->nifc mapping). Refreshes every 30s. - pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS entry + render case + EnvironmentalSection function + unused Thermometer import); exported the shared form primitives (Toggle, TextInput, NumberInput, SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx. - Reuses the existing restart_required banner pattern. - Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js, index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle). Rule 17 / no backend change: config is wired to the existing schema-driven GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake fields ride the generic dataclass coercion). No backend edited this phase. Verification: (A) `npm run build` clean -- tsc strict + vite, only the pre-existing >500kB single-chunk advisory (not introduced here). (B) static committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell (index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards, RF Propagation, ADS-B, 'Central not available', 'API key not configured'). (D) GET /api/config/environmental returns all adapters with feed_source=native, usgs_quake present, central{enabled:false} -- toggles bind to real data; all native, nothing flipped. Rebuilt prod healthy. *** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 *** The save half of gate D fails: PUT /api/config/environmental returns {"detail":"could not determine a constructor for the tag '!include' ..."}. Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py) calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a loader lacking the !include constructor) instead of the multi-file-aware meshai/config_loader.py::save_section that exists for exactly the !include layout. This breaks ALL GUI config saves in prod (every section, not just environmental) and predates C.2 -- the old Config 'Environmental' tab had the same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind works; only persistence is blocked. Verified disk pristine (idempotent PUT errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup). FIX (one line, but prod-wide blast radius -> wants its own phase + verification): config_routes PUT should call config_loader.save_section(section, data, config_dir) instead of config.save_config(...). Recommend C.2.1 backend fix before C.3. C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save, since flipping feed_source from the GUI is the whole point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
type FeedSource = 'native' | 'central'
interface EnvConfig {
enabled: boolean
nws_zones: string[]
nws: { enabled: boolean; user_agent: string; tick_seconds: number; severity_min: string; feed_source?: FeedSource }
swpc: { enabled: boolean; feed_source?: FeedSource }
ducting: { enabled: boolean; tick_seconds: number; latitude: number; longitude: number; feed_source?: FeedSource }
fires: { enabled: boolean; tick_seconds: number; state: string; feed_source?: FeedSource }
avalanche: { enabled: boolean; tick_seconds: number; center_ids: string[]; season_months: number[]; feed_source?: FeedSource }
usgs: { enabled: boolean; tick_seconds: number; sites: string[]; feed_source?: FeedSource }
usgs_quake: { enabled: boolean; tick_seconds: number; feed_url: string; min_magnitude: number; bbox: number[]; region: string; feed_source?: FeedSource }
traffic: { enabled: boolean; tick_seconds: number; api_key: string; corridors: { name: string; lat: number; lon: number }[]; feed_source?: FeedSource }
roads511: { enabled: boolean; tick_seconds: number; api_key: string; base_url: string; endpoints: string[]; bbox: number[]; feed_source?: FeedSource }
firms: { enabled: boolean; tick_seconds: number; map_key: string; source: string; bbox: number[]; day_range: number; confidence_min: string; proximity_km: number; feed_source?: FeedSource }
central?: { enabled: boolean; url: string; durable: string }
}
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle Restructures the environmental config UI into the 7-family taxonomy on the /environment page (Matt-approved Option C: unified families = config + live status per adapter). The editable adapter config moves out of the Config page's "Environmental" tab (now deprecated) onto /environment, where each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings) together with its live status (feed health + active events). Frontend (dashboard-frontend/src): - pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family adapter sub-tab strip -> AdapterPanel. * AdapterPanel: header row = on/off Toggle + feed_source toggle (native|central). When OFF, feed_source + all settings grey out (disabled, not hidden). * Native-only adapters (ducting, avalanche, roads511 -- no Central stream per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central' disabled + a 'Central not available for this adapter' tooltip/label. * Missing-key adapters (firms, roads511) show an 'API key not configured -- contact admin' notice; toggles still operate. * Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes are planned for v0.5.'). Mesh Health = no env adapters; a disabled feed_source toggle with 'central' greyed for future migration. * All existing per-adapter settings preserved verbatim (NWS zones/user_agent/ severity, ducting lat/lon, fires state, avalanche centers/season, USGS sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS map_key/source/confidence/bbox). ALSO adds a usgs_quake panel (tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in the old GUI. feed_source field (C.1) now surfaced per adapter. * Per-adapter live status: reuses /api/env/status feed health + /api/env/active events (filtered by source; fires->nifc mapping). Refreshes every 30s. - pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS entry + render case + EnvironmentalSection function + unused Thermometer import); exported the shared form primitives (Toggle, TextInput, NumberInput, SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx. - Reuses the existing restart_required banner pattern. - Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js, index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle). Rule 17 / no backend change: config is wired to the existing schema-driven GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake fields ride the generic dataclass coercion). No backend edited this phase. Verification: (A) `npm run build` clean -- tsc strict + vite, only the pre-existing >500kB single-chunk advisory (not introduced here). (B) static committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell (index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards, RF Propagation, ADS-B, 'Central not available', 'API key not configured'). (D) GET /api/config/environmental returns all adapters with feed_source=native, usgs_quake present, central{enabled:false} -- toggles bind to real data; all native, nothing flipped. Rebuilt prod healthy. *** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 *** The save half of gate D fails: PUT /api/config/environmental returns {"detail":"could not determine a constructor for the tag '!include' ..."}. Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py) calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a loader lacking the !include constructor) instead of the multi-file-aware meshai/config_loader.py::save_section that exists for exactly the !include layout. This breaks ALL GUI config saves in prod (every section, not just environmental) and predates C.2 -- the old Config 'Environmental' tab had the same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind works; only persistence is blocked. Verified disk pristine (idempotent PUT errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup). FIX (one line, but prod-wide blast radius -> wants its own phase + verification): config_routes PUT should call config_loader.save_section(section, data, config_dir) instead of config.save_config(...). Recommend C.2.1 backend fix before C.3. C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save, since flipping feed_source from the GUI is the whole point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
type FeedHealth = EnvStatus['feeds'][number]
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle Restructures the environmental config UI into the 7-family taxonomy on the /environment page (Matt-approved Option C: unified families = config + live status per adapter). The editable adapter config moves out of the Config page's "Environmental" tab (now deprecated) onto /environment, where each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings) together with its live status (feed health + active events). Frontend (dashboard-frontend/src): - pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family adapter sub-tab strip -> AdapterPanel. * AdapterPanel: header row = on/off Toggle + feed_source toggle (native|central). When OFF, feed_source + all settings grey out (disabled, not hidden). * Native-only adapters (ducting, avalanche, roads511 -- no Central stream per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central' disabled + a 'Central not available for this adapter' tooltip/label. * Missing-key adapters (firms, roads511) show an 'API key not configured -- contact admin' notice; toggles still operate. * Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes are planned for v0.5.'). Mesh Health = no env adapters; a disabled feed_source toggle with 'central' greyed for future migration. * All existing per-adapter settings preserved verbatim (NWS zones/user_agent/ severity, ducting lat/lon, fires state, avalanche centers/season, USGS sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS map_key/source/confidence/bbox). ALSO adds a usgs_quake panel (tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in the old GUI. feed_source field (C.1) now surfaced per adapter. * Per-adapter live status: reuses /api/env/status feed health + /api/env/active events (filtered by source; fires->nifc mapping). Refreshes every 30s. - pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS entry + render case + EnvironmentalSection function + unused Thermometer import); exported the shared form primitives (Toggle, TextInput, NumberInput, SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx. - Reuses the existing restart_required banner pattern. - Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js, index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle). Rule 17 / no backend change: config is wired to the existing schema-driven GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake fields ride the generic dataclass coercion). No backend edited this phase. Verification: (A) `npm run build` clean -- tsc strict + vite, only the pre-existing >500kB single-chunk advisory (not introduced here). (B) static committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell (index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards, RF Propagation, ADS-B, 'Central not available', 'API key not configured'). (D) GET /api/config/environmental returns all adapters with feed_source=native, usgs_quake present, central{enabled:false} -- toggles bind to real data; all native, nothing flipped. Rebuilt prod healthy. *** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 *** The save half of gate D fails: PUT /api/config/environmental returns {"detail":"could not determine a constructor for the tag '!include' ..."}. Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py) calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a loader lacking the !include constructor) instead of the multi-file-aware meshai/config_loader.py::save_section that exists for exactly the !include layout. This breaks ALL GUI config saves in prod (every section, not just environmental) and predates C.2 -- the old Config 'Environmental' tab had the same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind works; only persistence is blocked. Verified disk pristine (idempotent PUT errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup). FIX (one line, but prod-wide blast radius -> wants its own phase + verification): config_routes PUT should call config_loader.save_section(section, data, config_dir) instead of config.save_config(...). Recommend C.2.1 backend fix before C.3. C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save, since flipping feed_source from the GUI is the whole point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
// ---------------------------------------------------------------- status cards
function FeedStatusCard({ feed }: { feed: FeedHealth }) {
const color = !feed.is_loaded ? 'bg-red-500' : feed.consecutive_errors > 0 ? 'bg-amber-500' : 'bg-green-500'
const text = !feed.is_loaded ? 'Not loaded' : feed.consecutive_errors > 0 ? `${feed.consecutive_errors} errors` : 'Healthy'
const lastFetch = feed.last_fetch ? new Date(feed.last_fetch * 1000).toLocaleTimeString() : 'Never'
return (
<div className="bg-bg-hover rounded-lg p-4">
<div className="flex items-center justify-between mb-2">
<div className="flex items-center gap-2">
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle Restructures the environmental config UI into the 7-family taxonomy on the /environment page (Matt-approved Option C: unified families = config + live status per adapter). The editable adapter config moves out of the Config page's "Environmental" tab (now deprecated) onto /environment, where each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings) together with its live status (feed health + active events). Frontend (dashboard-frontend/src): - pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family adapter sub-tab strip -> AdapterPanel. * AdapterPanel: header row = on/off Toggle + feed_source toggle (native|central). When OFF, feed_source + all settings grey out (disabled, not hidden). * Native-only adapters (ducting, avalanche, roads511 -- no Central stream per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central' disabled + a 'Central not available for this adapter' tooltip/label. * Missing-key adapters (firms, roads511) show an 'API key not configured -- contact admin' notice; toggles still operate. * Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes are planned for v0.5.'). Mesh Health = no env adapters; a disabled feed_source toggle with 'central' greyed for future migration. * All existing per-adapter settings preserved verbatim (NWS zones/user_agent/ severity, ducting lat/lon, fires state, avalanche centers/season, USGS sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS map_key/source/confidence/bbox). ALSO adds a usgs_quake panel (tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in the old GUI. feed_source field (C.1) now surfaced per adapter. * Per-adapter live status: reuses /api/env/status feed health + /api/env/active events (filtered by source; fires->nifc mapping). Refreshes every 30s. - pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS entry + render case + EnvironmentalSection function + unused Thermometer import); exported the shared form primitives (Toggle, TextInput, NumberInput, SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx. - Reuses the existing restart_required banner pattern. - Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js, index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle). Rule 17 / no backend change: config is wired to the existing schema-driven GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake fields ride the generic dataclass coercion). No backend edited this phase. Verification: (A) `npm run build` clean -- tsc strict + vite, only the pre-existing >500kB single-chunk advisory (not introduced here). (B) static committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell (index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards, RF Propagation, ADS-B, 'Central not available', 'API key not configured'). (D) GET /api/config/environmental returns all adapters with feed_source=native, usgs_quake present, central{enabled:false} -- toggles bind to real data; all native, nothing flipped. Rebuilt prod healthy. *** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 *** The save half of gate D fails: PUT /api/config/environmental returns {"detail":"could not determine a constructor for the tag '!include' ..."}. Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py) calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a loader lacking the !include constructor) instead of the multi-file-aware meshai/config_loader.py::save_section that exists for exactly the !include layout. This breaks ALL GUI config saves in prod (every section, not just environmental) and predates C.2 -- the old Config 'Environmental' tab had the same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind works; only persistence is blocked. Verified disk pristine (idempotent PUT errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup). FIX (one line, but prod-wide blast radius -> wants its own phase + verification): config_routes PUT should call config_loader.save_section(section, data, config_dir) instead of config.save_config(...). Recommend C.2.1 backend fix before C.3. C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save, since flipping feed_source from the GUI is the whole point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
<div className={`w-2 h-2 rounded-full ${color}`} />
<span className="text-sm font-medium text-slate-200 uppercase">{feed.source}</span>
</div>
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle Restructures the environmental config UI into the 7-family taxonomy on the /environment page (Matt-approved Option C: unified families = config + live status per adapter). The editable adapter config moves out of the Config page's "Environmental" tab (now deprecated) onto /environment, where each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings) together with its live status (feed health + active events). Frontend (dashboard-frontend/src): - pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family adapter sub-tab strip -> AdapterPanel. * AdapterPanel: header row = on/off Toggle + feed_source toggle (native|central). When OFF, feed_source + all settings grey out (disabled, not hidden). * Native-only adapters (ducting, avalanche, roads511 -- no Central stream per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central' disabled + a 'Central not available for this adapter' tooltip/label. * Missing-key adapters (firms, roads511) show an 'API key not configured -- contact admin' notice; toggles still operate. * Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes are planned for v0.5.'). Mesh Health = no env adapters; a disabled feed_source toggle with 'central' greyed for future migration. * All existing per-adapter settings preserved verbatim (NWS zones/user_agent/ severity, ducting lat/lon, fires state, avalanche centers/season, USGS sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS map_key/source/confidence/bbox). ALSO adds a usgs_quake panel (tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in the old GUI. feed_source field (C.1) now surfaced per adapter. * Per-adapter live status: reuses /api/env/status feed health + /api/env/active events (filtered by source; fires->nifc mapping). Refreshes every 30s. - pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS entry + render case + EnvironmentalSection function + unused Thermometer import); exported the shared form primitives (Toggle, TextInput, NumberInput, SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx. - Reuses the existing restart_required banner pattern. - Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js, index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle). Rule 17 / no backend change: config is wired to the existing schema-driven GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake fields ride the generic dataclass coercion). No backend edited this phase. Verification: (A) `npm run build` clean -- tsc strict + vite, only the pre-existing >500kB single-chunk advisory (not introduced here). (B) static committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell (index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards, RF Propagation, ADS-B, 'Central not available', 'API key not configured'). (D) GET /api/config/environmental returns all adapters with feed_source=native, usgs_quake present, central{enabled:false} -- toggles bind to real data; all native, nothing flipped. Rebuilt prod healthy. *** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 *** The save half of gate D fails: PUT /api/config/environmental returns {"detail":"could not determine a constructor for the tag '!include' ..."}. Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py) calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a loader lacking the !include constructor) instead of the multi-file-aware meshai/config_loader.py::save_section that exists for exactly the !include layout. This breaks ALL GUI config saves in prod (every section, not just environmental) and predates C.2 -- the old Config 'Environmental' tab had the same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind works; only persistence is blocked. Verified disk pristine (idempotent PUT errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup). FIX (one line, but prod-wide blast radius -> wants its own phase + verification): config_routes PUT should call config_loader.save_section(section, data, config_dir) instead of config.save_config(...). Recommend C.2.1 backend fix before C.3. C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save, since flipping feed_source from the GUI is the whole point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
<span className="text-xs text-slate-400">{text}</span>
</div>
<div className="text-xs text-slate-500 space-y-1">
<div>Events: {feed.event_count}</div>
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle Restructures the environmental config UI into the 7-family taxonomy on the /environment page (Matt-approved Option C: unified families = config + live status per adapter). The editable adapter config moves out of the Config page's "Environmental" tab (now deprecated) onto /environment, where each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings) together with its live status (feed health + active events). Frontend (dashboard-frontend/src): - pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family adapter sub-tab strip -> AdapterPanel. * AdapterPanel: header row = on/off Toggle + feed_source toggle (native|central). When OFF, feed_source + all settings grey out (disabled, not hidden). * Native-only adapters (ducting, avalanche, roads511 -- no Central stream per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central' disabled + a 'Central not available for this adapter' tooltip/label. * Missing-key adapters (firms, roads511) show an 'API key not configured -- contact admin' notice; toggles still operate. * Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes are planned for v0.5.'). Mesh Health = no env adapters; a disabled feed_source toggle with 'central' greyed for future migration. * All existing per-adapter settings preserved verbatim (NWS zones/user_agent/ severity, ducting lat/lon, fires state, avalanche centers/season, USGS sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS map_key/source/confidence/bbox). ALSO adds a usgs_quake panel (tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in the old GUI. feed_source field (C.1) now surfaced per adapter. * Per-adapter live status: reuses /api/env/status feed health + /api/env/active events (filtered by source; fires->nifc mapping). Refreshes every 30s. - pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS entry + render case + EnvironmentalSection function + unused Thermometer import); exported the shared form primitives (Toggle, TextInput, NumberInput, SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx. - Reuses the existing restart_required banner pattern. - Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js, index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle). Rule 17 / no backend change: config is wired to the existing schema-driven GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake fields ride the generic dataclass coercion). No backend edited this phase. Verification: (A) `npm run build` clean -- tsc strict + vite, only the pre-existing >500kB single-chunk advisory (not introduced here). (B) static committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell (index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards, RF Propagation, ADS-B, 'Central not available', 'API key not configured'). (D) GET /api/config/environmental returns all adapters with feed_source=native, usgs_quake present, central{enabled:false} -- toggles bind to real data; all native, nothing flipped. Rebuilt prod healthy. *** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 *** The save half of gate D fails: PUT /api/config/environmental returns {"detail":"could not determine a constructor for the tag '!include' ..."}. Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py) calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a loader lacking the !include constructor) instead of the multi-file-aware meshai/config_loader.py::save_section that exists for exactly the !include layout. This breaks ALL GUI config saves in prod (every section, not just environmental) and predates C.2 -- the old Config 'Environmental' tab had the same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind works; only persistence is blocked. Verified disk pristine (idempotent PUT errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup). FIX (one line, but prod-wide blast radius -> wants its own phase + verification): config_routes PUT should call config_loader.save_section(section, data, config_dir) instead of config.save_config(...). Recommend C.2.1 backend fix before C.3. C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save, since flipping feed_source from the GUI is the whole point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
<div>Last fetch: {lastFetch}</div>
{feed.last_error && <div className="text-amber-500 truncate">{feed.last_error}</div>}
</div>
</div>
)
}
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle Restructures the environmental config UI into the 7-family taxonomy on the /environment page (Matt-approved Option C: unified families = config + live status per adapter). The editable adapter config moves out of the Config page's "Environmental" tab (now deprecated) onto /environment, where each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings) together with its live status (feed health + active events). Frontend (dashboard-frontend/src): - pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family adapter sub-tab strip -> AdapterPanel. * AdapterPanel: header row = on/off Toggle + feed_source toggle (native|central). When OFF, feed_source + all settings grey out (disabled, not hidden). * Native-only adapters (ducting, avalanche, roads511 -- no Central stream per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central' disabled + a 'Central not available for this adapter' tooltip/label. * Missing-key adapters (firms, roads511) show an 'API key not configured -- contact admin' notice; toggles still operate. * Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes are planned for v0.5.'). Mesh Health = no env adapters; a disabled feed_source toggle with 'central' greyed for future migration. * All existing per-adapter settings preserved verbatim (NWS zones/user_agent/ severity, ducting lat/lon, fires state, avalanche centers/season, USGS sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS map_key/source/confidence/bbox). ALSO adds a usgs_quake panel (tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in the old GUI. feed_source field (C.1) now surfaced per adapter. * Per-adapter live status: reuses /api/env/status feed health + /api/env/active events (filtered by source; fires->nifc mapping). Refreshes every 30s. - pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS entry + render case + EnvironmentalSection function + unused Thermometer import); exported the shared form primitives (Toggle, TextInput, NumberInput, SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx. - Reuses the existing restart_required banner pattern. - Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js, index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle). Rule 17 / no backend change: config is wired to the existing schema-driven GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake fields ride the generic dataclass coercion). No backend edited this phase. Verification: (A) `npm run build` clean -- tsc strict + vite, only the pre-existing >500kB single-chunk advisory (not introduced here). (B) static committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell (index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards, RF Propagation, ADS-B, 'Central not available', 'API key not configured'). (D) GET /api/config/environmental returns all adapters with feed_source=native, usgs_quake present, central{enabled:false} -- toggles bind to real data; all native, nothing flipped. Rebuilt prod healthy. *** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 *** The save half of gate D fails: PUT /api/config/environmental returns {"detail":"could not determine a constructor for the tag '!include' ..."}. Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py) calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a loader lacking the !include constructor) instead of the multi-file-aware meshai/config_loader.py::save_section that exists for exactly the !include layout. This breaks ALL GUI config saves in prod (every section, not just environmental) and predates C.2 -- the old Config 'Environmental' tab had the same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind works; only persistence is blocked. Verified disk pristine (idempotent PUT errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup). FIX (one line, but prod-wide blast radius -> wants its own phase + verification): config_routes PUT should call config_loader.save_section(section, data, config_dir) instead of config.save_config(...). Recommend C.2.1 backend fix before C.3. C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save, since flipping feed_source from the GUI is the whole point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
function EventCard({ event }: { event: EnvEvent }) {
const sev = event.severity.toLowerCase()
const styles = (sev === 'extreme' || sev === 'severe' || sev === 'immediate')
? { bg: 'bg-red-500/10', border: 'border-red-500', Icon: AlertCircle, color: 'text-red-500' }
: (sev === 'moderate' || sev === 'warning' || sev === 'priority')
? { bg: 'bg-amber-500/10', border: 'border-amber-500', Icon: AlertTriangle, color: 'text-amber-500' }
: { bg: 'bg-blue-500/10', border: 'border-blue-500', Icon: Info, color: 'text-blue-500' }
const Icon = styles.Icon
return (
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle Restructures the environmental config UI into the 7-family taxonomy on the /environment page (Matt-approved Option C: unified families = config + live status per adapter). The editable adapter config moves out of the Config page's "Environmental" tab (now deprecated) onto /environment, where each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings) together with its live status (feed health + active events). Frontend (dashboard-frontend/src): - pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family adapter sub-tab strip -> AdapterPanel. * AdapterPanel: header row = on/off Toggle + feed_source toggle (native|central). When OFF, feed_source + all settings grey out (disabled, not hidden). * Native-only adapters (ducting, avalanche, roads511 -- no Central stream per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central' disabled + a 'Central not available for this adapter' tooltip/label. * Missing-key adapters (firms, roads511) show an 'API key not configured -- contact admin' notice; toggles still operate. * Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes are planned for v0.5.'). Mesh Health = no env adapters; a disabled feed_source toggle with 'central' greyed for future migration. * All existing per-adapter settings preserved verbatim (NWS zones/user_agent/ severity, ducting lat/lon, fires state, avalanche centers/season, USGS sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS map_key/source/confidence/bbox). ALSO adds a usgs_quake panel (tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in the old GUI. feed_source field (C.1) now surfaced per adapter. * Per-adapter live status: reuses /api/env/status feed health + /api/env/active events (filtered by source; fires->nifc mapping). Refreshes every 30s. - pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS entry + render case + EnvironmentalSection function + unused Thermometer import); exported the shared form primitives (Toggle, TextInput, NumberInput, SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx. - Reuses the existing restart_required banner pattern. - Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js, index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle). Rule 17 / no backend change: config is wired to the existing schema-driven GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake fields ride the generic dataclass coercion). No backend edited this phase. Verification: (A) `npm run build` clean -- tsc strict + vite, only the pre-existing >500kB single-chunk advisory (not introduced here). (B) static committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell (index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards, RF Propagation, ADS-B, 'Central not available', 'API key not configured'). (D) GET /api/config/environmental returns all adapters with feed_source=native, usgs_quake present, central{enabled:false} -- toggles bind to real data; all native, nothing flipped. Rebuilt prod healthy. *** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 *** The save half of gate D fails: PUT /api/config/environmental returns {"detail":"could not determine a constructor for the tag '!include' ..."}. Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py) calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a loader lacking the !include constructor) instead of the multi-file-aware meshai/config_loader.py::save_section that exists for exactly the !include layout. This breaks ALL GUI config saves in prod (every section, not just environmental) and predates C.2 -- the old Config 'Environmental' tab had the same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind works; only persistence is blocked. Verified disk pristine (idempotent PUT errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup). FIX (one line, but prod-wide blast radius -> wants its own phase + verification): config_routes PUT should call config_loader.save_section(section, data, config_dir) instead of config.save_config(...). Recommend C.2.1 backend fix before C.3. C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save, since flipping feed_source from the GUI is the whole point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
<div className={`p-3 rounded-lg ${styles.bg} border-l-2 ${styles.border}`}>
<div className="flex items-start gap-3">
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle Restructures the environmental config UI into the 7-family taxonomy on the /environment page (Matt-approved Option C: unified families = config + live status per adapter). The editable adapter config moves out of the Config page's "Environmental" tab (now deprecated) onto /environment, where each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings) together with its live status (feed health + active events). Frontend (dashboard-frontend/src): - pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family adapter sub-tab strip -> AdapterPanel. * AdapterPanel: header row = on/off Toggle + feed_source toggle (native|central). When OFF, feed_source + all settings grey out (disabled, not hidden). * Native-only adapters (ducting, avalanche, roads511 -- no Central stream per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central' disabled + a 'Central not available for this adapter' tooltip/label. * Missing-key adapters (firms, roads511) show an 'API key not configured -- contact admin' notice; toggles still operate. * Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes are planned for v0.5.'). Mesh Health = no env adapters; a disabled feed_source toggle with 'central' greyed for future migration. * All existing per-adapter settings preserved verbatim (NWS zones/user_agent/ severity, ducting lat/lon, fires state, avalanche centers/season, USGS sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS map_key/source/confidence/bbox). ALSO adds a usgs_quake panel (tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in the old GUI. feed_source field (C.1) now surfaced per adapter. * Per-adapter live status: reuses /api/env/status feed health + /api/env/active events (filtered by source; fires->nifc mapping). Refreshes every 30s. - pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS entry + render case + EnvironmentalSection function + unused Thermometer import); exported the shared form primitives (Toggle, TextInput, NumberInput, SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx. - Reuses the existing restart_required banner pattern. - Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js, index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle). Rule 17 / no backend change: config is wired to the existing schema-driven GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake fields ride the generic dataclass coercion). No backend edited this phase. Verification: (A) `npm run build` clean -- tsc strict + vite, only the pre-existing >500kB single-chunk advisory (not introduced here). (B) static committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell (index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards, RF Propagation, ADS-B, 'Central not available', 'API key not configured'). (D) GET /api/config/environmental returns all adapters with feed_source=native, usgs_quake present, central{enabled:false} -- toggles bind to real data; all native, nothing flipped. Rebuilt prod healthy. *** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 *** The save half of gate D fails: PUT /api/config/environmental returns {"detail":"could not determine a constructor for the tag '!include' ..."}. Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py) calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a loader lacking the !include constructor) instead of the multi-file-aware meshai/config_loader.py::save_section that exists for exactly the !include layout. This breaks ALL GUI config saves in prod (every section, not just environmental) and predates C.2 -- the old Config 'Environmental' tab had the same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind works; only persistence is blocked. Verified disk pristine (idempotent PUT errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup). FIX (one line, but prod-wide blast radius -> wants its own phase + verification): config_routes PUT should call config_loader.save_section(section, data, config_dir) instead of config.save_config(...). Recommend C.2.1 backend fix before C.3. C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save, since flipping feed_source from the GUI is the whole point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
<Icon size={16} className={styles.color} />
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2 mb-1">
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle Restructures the environmental config UI into the 7-family taxonomy on the /environment page (Matt-approved Option C: unified families = config + live status per adapter). The editable adapter config moves out of the Config page's "Environmental" tab (now deprecated) onto /environment, where each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings) together with its live status (feed health + active events). Frontend (dashboard-frontend/src): - pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family adapter sub-tab strip -> AdapterPanel. * AdapterPanel: header row = on/off Toggle + feed_source toggle (native|central). When OFF, feed_source + all settings grey out (disabled, not hidden). * Native-only adapters (ducting, avalanche, roads511 -- no Central stream per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central' disabled + a 'Central not available for this adapter' tooltip/label. * Missing-key adapters (firms, roads511) show an 'API key not configured -- contact admin' notice; toggles still operate. * Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes are planned for v0.5.'). Mesh Health = no env adapters; a disabled feed_source toggle with 'central' greyed for future migration. * All existing per-adapter settings preserved verbatim (NWS zones/user_agent/ severity, ducting lat/lon, fires state, avalanche centers/season, USGS sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS map_key/source/confidence/bbox). ALSO adds a usgs_quake panel (tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in the old GUI. feed_source field (C.1) now surfaced per adapter. * Per-adapter live status: reuses /api/env/status feed health + /api/env/active events (filtered by source; fires->nifc mapping). Refreshes every 30s. - pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS entry + render case + EnvironmentalSection function + unused Thermometer import); exported the shared form primitives (Toggle, TextInput, NumberInput, SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx. - Reuses the existing restart_required banner pattern. - Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js, index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle). Rule 17 / no backend change: config is wired to the existing schema-driven GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake fields ride the generic dataclass coercion). No backend edited this phase. Verification: (A) `npm run build` clean -- tsc strict + vite, only the pre-existing >500kB single-chunk advisory (not introduced here). (B) static committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell (index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards, RF Propagation, ADS-B, 'Central not available', 'API key not configured'). (D) GET /api/config/environmental returns all adapters with feed_source=native, usgs_quake present, central{enabled:false} -- toggles bind to real data; all native, nothing flipped. Rebuilt prod healthy. *** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 *** The save half of gate D fails: PUT /api/config/environmental returns {"detail":"could not determine a constructor for the tag '!include' ..."}. Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py) calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a loader lacking the !include constructor) instead of the multi-file-aware meshai/config_loader.py::save_section that exists for exactly the !include layout. This breaks ALL GUI config saves in prod (every section, not just environmental) and predates C.2 -- the old Config 'Environmental' tab had the same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind works; only persistence is blocked. Verified disk pristine (idempotent PUT errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup). FIX (one line, but prod-wide blast radius -> wants its own phase + verification): config_routes PUT should call config_loader.save_section(section, data, config_dir) instead of config.save_config(...). Recommend C.2.1 backend fix before C.3. C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save, since flipping feed_source from the GUI is the whole point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
<span className="text-sm font-medium text-slate-200">{event.event_type}</span>
<span className={`text-xs px-1.5 py-0.5 rounded ${styles.bg} ${styles.color}`}>{event.severity}</span>
</div>
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle Restructures the environmental config UI into the 7-family taxonomy on the /environment page (Matt-approved Option C: unified families = config + live status per adapter). The editable adapter config moves out of the Config page's "Environmental" tab (now deprecated) onto /environment, where each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings) together with its live status (feed health + active events). Frontend (dashboard-frontend/src): - pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family adapter sub-tab strip -> AdapterPanel. * AdapterPanel: header row = on/off Toggle + feed_source toggle (native|central). When OFF, feed_source + all settings grey out (disabled, not hidden). * Native-only adapters (ducting, avalanche, roads511 -- no Central stream per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central' disabled + a 'Central not available for this adapter' tooltip/label. * Missing-key adapters (firms, roads511) show an 'API key not configured -- contact admin' notice; toggles still operate. * Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes are planned for v0.5.'). Mesh Health = no env adapters; a disabled feed_source toggle with 'central' greyed for future migration. * All existing per-adapter settings preserved verbatim (NWS zones/user_agent/ severity, ducting lat/lon, fires state, avalanche centers/season, USGS sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS map_key/source/confidence/bbox). ALSO adds a usgs_quake panel (tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in the old GUI. feed_source field (C.1) now surfaced per adapter. * Per-adapter live status: reuses /api/env/status feed health + /api/env/active events (filtered by source; fires->nifc mapping). Refreshes every 30s. - pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS entry + render case + EnvironmentalSection function + unused Thermometer import); exported the shared form primitives (Toggle, TextInput, NumberInput, SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx. - Reuses the existing restart_required banner pattern. - Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js, index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle). Rule 17 / no backend change: config is wired to the existing schema-driven GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake fields ride the generic dataclass coercion). No backend edited this phase. Verification: (A) `npm run build` clean -- tsc strict + vite, only the pre-existing >500kB single-chunk advisory (not introduced here). (B) static committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell (index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards, RF Propagation, ADS-B, 'Central not available', 'API key not configured'). (D) GET /api/config/environmental returns all adapters with feed_source=native, usgs_quake present, central{enabled:false} -- toggles bind to real data; all native, nothing flipped. Rebuilt prod healthy. *** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 *** The save half of gate D fails: PUT /api/config/environmental returns {"detail":"could not determine a constructor for the tag '!include' ..."}. Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py) calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a loader lacking the !include constructor) instead of the multi-file-aware meshai/config_loader.py::save_section that exists for exactly the !include layout. This breaks ALL GUI config saves in prod (every section, not just environmental) and predates C.2 -- the old Config 'Environmental' tab had the same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind works; only persistence is blocked. Verified disk pristine (idempotent PUT errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup). FIX (one line, but prod-wide blast radius -> wants its own phase + verification): config_routes PUT should call config_loader.save_section(section, data, config_dir) instead of config.save_config(...). Recommend C.2.1 backend fix before C.3. C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save, since flipping feed_source from the GUI is the whole point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
<div className="text-sm text-slate-300">{event.headline}</div>
</div>
</div>
</div>
)
}
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle Restructures the environmental config UI into the 7-family taxonomy on the /environment page (Matt-approved Option C: unified families = config + live status per adapter). The editable adapter config moves out of the Config page's "Environmental" tab (now deprecated) onto /environment, where each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings) together with its live status (feed health + active events). Frontend (dashboard-frontend/src): - pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family adapter sub-tab strip -> AdapterPanel. * AdapterPanel: header row = on/off Toggle + feed_source toggle (native|central). When OFF, feed_source + all settings grey out (disabled, not hidden). * Native-only adapters (ducting, avalanche, roads511 -- no Central stream per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central' disabled + a 'Central not available for this adapter' tooltip/label. * Missing-key adapters (firms, roads511) show an 'API key not configured -- contact admin' notice; toggles still operate. * Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes are planned for v0.5.'). Mesh Health = no env adapters; a disabled feed_source toggle with 'central' greyed for future migration. * All existing per-adapter settings preserved verbatim (NWS zones/user_agent/ severity, ducting lat/lon, fires state, avalanche centers/season, USGS sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS map_key/source/confidence/bbox). ALSO adds a usgs_quake panel (tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in the old GUI. feed_source field (C.1) now surfaced per adapter. * Per-adapter live status: reuses /api/env/status feed health + /api/env/active events (filtered by source; fires->nifc mapping). Refreshes every 30s. - pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS entry + render case + EnvironmentalSection function + unused Thermometer import); exported the shared form primitives (Toggle, TextInput, NumberInput, SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx. - Reuses the existing restart_required banner pattern. - Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js, index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle). Rule 17 / no backend change: config is wired to the existing schema-driven GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake fields ride the generic dataclass coercion). No backend edited this phase. Verification: (A) `npm run build` clean -- tsc strict + vite, only the pre-existing >500kB single-chunk advisory (not introduced here). (B) static committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell (index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards, RF Propagation, ADS-B, 'Central not available', 'API key not configured'). (D) GET /api/config/environmental returns all adapters with feed_source=native, usgs_quake present, central{enabled:false} -- toggles bind to real data; all native, nothing flipped. Rebuilt prod healthy. *** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 *** The save half of gate D fails: PUT /api/config/environmental returns {"detail":"could not determine a constructor for the tag '!include' ..."}. Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py) calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a loader lacking the !include constructor) instead of the multi-file-aware meshai/config_loader.py::save_section that exists for exactly the !include layout. This breaks ALL GUI config saves in prod (every section, not just environmental) and predates C.2 -- the old Config 'Environmental' tab had the same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind works; only persistence is blocked. Verified disk pristine (idempotent PUT errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup). FIX (one line, but prod-wide blast radius -> wants its own phase + verification): config_routes PUT should call config_loader.save_section(section, data, config_dir) instead of config.save_config(...). Recommend C.2.1 backend fix before C.3. C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save, since flipping feed_source from the GUI is the whole point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
// ---------------------------------------------------------------- feed_source toggle
function FeedSourceToggle({ value, onChange, disabled, centralDisabled }: {
value: FeedSource; onChange: (v: FeedSource) => void; disabled: boolean; centralDisabled: boolean
}) {
const base = 'px-2 py-1 text-xs transition-colors'
return (
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle Restructures the environmental config UI into the 7-family taxonomy on the /environment page (Matt-approved Option C: unified families = config + live status per adapter). The editable adapter config moves out of the Config page's "Environmental" tab (now deprecated) onto /environment, where each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings) together with its live status (feed health + active events). Frontend (dashboard-frontend/src): - pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family adapter sub-tab strip -> AdapterPanel. * AdapterPanel: header row = on/off Toggle + feed_source toggle (native|central). When OFF, feed_source + all settings grey out (disabled, not hidden). * Native-only adapters (ducting, avalanche, roads511 -- no Central stream per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central' disabled + a 'Central not available for this adapter' tooltip/label. * Missing-key adapters (firms, roads511) show an 'API key not configured -- contact admin' notice; toggles still operate. * Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes are planned for v0.5.'). Mesh Health = no env adapters; a disabled feed_source toggle with 'central' greyed for future migration. * All existing per-adapter settings preserved verbatim (NWS zones/user_agent/ severity, ducting lat/lon, fires state, avalanche centers/season, USGS sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS map_key/source/confidence/bbox). ALSO adds a usgs_quake panel (tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in the old GUI. feed_source field (C.1) now surfaced per adapter. * Per-adapter live status: reuses /api/env/status feed health + /api/env/active events (filtered by source; fires->nifc mapping). Refreshes every 30s. - pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS entry + render case + EnvironmentalSection function + unused Thermometer import); exported the shared form primitives (Toggle, TextInput, NumberInput, SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx. - Reuses the existing restart_required banner pattern. - Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js, index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle). Rule 17 / no backend change: config is wired to the existing schema-driven GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake fields ride the generic dataclass coercion). No backend edited this phase. Verification: (A) `npm run build` clean -- tsc strict + vite, only the pre-existing >500kB single-chunk advisory (not introduced here). (B) static committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell (index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards, RF Propagation, ADS-B, 'Central not available', 'API key not configured'). (D) GET /api/config/environmental returns all adapters with feed_source=native, usgs_quake present, central{enabled:false} -- toggles bind to real data; all native, nothing flipped. Rebuilt prod healthy. *** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 *** The save half of gate D fails: PUT /api/config/environmental returns {"detail":"could not determine a constructor for the tag '!include' ..."}. Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py) calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a loader lacking the !include constructor) instead of the multi-file-aware meshai/config_loader.py::save_section that exists for exactly the !include layout. This breaks ALL GUI config saves in prod (every section, not just environmental) and predates C.2 -- the old Config 'Environmental' tab had the same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind works; only persistence is blocked. Verified disk pristine (idempotent PUT errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup). FIX (one line, but prod-wide blast radius -> wants its own phase + verification): config_routes PUT should call config_loader.save_section(section, data, config_dir) instead of config.save_config(...). Recommend C.2.1 backend fix before C.3. C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save, since flipping feed_source from the GUI is the whole point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
<div className={`flex rounded border border-[#1e2a3a] overflow-hidden ${disabled ? 'opacity-40' : ''}`}>
<button
type="button"
disabled={disabled}
onClick={() => onChange('native')}
className={`${base} ${value === 'native' ? 'bg-accent text-white' : 'text-slate-400 hover:text-slate-200'}`}
>native</button>
<button
type="button"
disabled={disabled || centralDisabled}
title={centralDisabled ? 'Central not available for this adapter' : ''}
onClick={() => { if (!centralDisabled) onChange('central') }}
className={`${base} ${centralDisabled ? 'text-slate-600 cursor-not-allowed' : value === 'central' ? 'bg-accent text-white' : 'text-slate-400 hover:text-slate-200'}`}
>central</button>
</div>
)
}
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle Restructures the environmental config UI into the 7-family taxonomy on the /environment page (Matt-approved Option C: unified families = config + live status per adapter). The editable adapter config moves out of the Config page's "Environmental" tab (now deprecated) onto /environment, where each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings) together with its live status (feed health + active events). Frontend (dashboard-frontend/src): - pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family adapter sub-tab strip -> AdapterPanel. * AdapterPanel: header row = on/off Toggle + feed_source toggle (native|central). When OFF, feed_source + all settings grey out (disabled, not hidden). * Native-only adapters (ducting, avalanche, roads511 -- no Central stream per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central' disabled + a 'Central not available for this adapter' tooltip/label. * Missing-key adapters (firms, roads511) show an 'API key not configured -- contact admin' notice; toggles still operate. * Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes are planned for v0.5.'). Mesh Health = no env adapters; a disabled feed_source toggle with 'central' greyed for future migration. * All existing per-adapter settings preserved verbatim (NWS zones/user_agent/ severity, ducting lat/lon, fires state, avalanche centers/season, USGS sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS map_key/source/confidence/bbox). ALSO adds a usgs_quake panel (tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in the old GUI. feed_source field (C.1) now surfaced per adapter. * Per-adapter live status: reuses /api/env/status feed health + /api/env/active events (filtered by source; fires->nifc mapping). Refreshes every 30s. - pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS entry + render case + EnvironmentalSection function + unused Thermometer import); exported the shared form primitives (Toggle, TextInput, NumberInput, SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx. - Reuses the existing restart_required banner pattern. - Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js, index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle). Rule 17 / no backend change: config is wired to the existing schema-driven GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake fields ride the generic dataclass coercion). No backend edited this phase. Verification: (A) `npm run build` clean -- tsc strict + vite, only the pre-existing >500kB single-chunk advisory (not introduced here). (B) static committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell (index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards, RF Propagation, ADS-B, 'Central not available', 'API key not configured'). (D) GET /api/config/environmental returns all adapters with feed_source=native, usgs_quake present, central{enabled:false} -- toggles bind to real data; all native, nothing flipped. Rebuilt prod healthy. *** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 *** The save half of gate D fails: PUT /api/config/environmental returns {"detail":"could not determine a constructor for the tag '!include' ..."}. Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py) calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a loader lacking the !include constructor) instead of the multi-file-aware meshai/config_loader.py::save_section that exists for exactly the !include layout. This breaks ALL GUI config saves in prod (every section, not just environmental) and predates C.2 -- the old Config 'Environmental' tab had the same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind works; only persistence is blocked. Verified disk pristine (idempotent PUT errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup). FIX (one line, but prod-wide blast radius -> wants its own phase + verification): config_routes PUT should call config_loader.save_section(section, data, config_dir) instead of config.save_config(...). Recommend C.2.1 backend fix before C.3. C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save, since flipping feed_source from the GUI is the whole point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
// ---------------------------------------------------------------- adapter panel
function AdapterPanel({ title, subtitle, enabled, onEnabled, feedSource, onFeedSource, hasCentral, nativeOnly, hasKey, health, events, children }: {
title: string; subtitle?: string
enabled: boolean; onEnabled: (v: boolean) => void
feedSource: FeedSource; onFeedSource: (v: FeedSource) => void
hasCentral: boolean; nativeOnly: boolean; hasKey: boolean
health?: FeedHealth; events?: EnvEvent[]; children?: ReactNode
}) {
const centralDisabled = nativeOnly || !hasCentral
return (
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle Restructures the environmental config UI into the 7-family taxonomy on the /environment page (Matt-approved Option C: unified families = config + live status per adapter). The editable adapter config moves out of the Config page's "Environmental" tab (now deprecated) onto /environment, where each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings) together with its live status (feed health + active events). Frontend (dashboard-frontend/src): - pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family adapter sub-tab strip -> AdapterPanel. * AdapterPanel: header row = on/off Toggle + feed_source toggle (native|central). When OFF, feed_source + all settings grey out (disabled, not hidden). * Native-only adapters (ducting, avalanche, roads511 -- no Central stream per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central' disabled + a 'Central not available for this adapter' tooltip/label. * Missing-key adapters (firms, roads511) show an 'API key not configured -- contact admin' notice; toggles still operate. * Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes are planned for v0.5.'). Mesh Health = no env adapters; a disabled feed_source toggle with 'central' greyed for future migration. * All existing per-adapter settings preserved verbatim (NWS zones/user_agent/ severity, ducting lat/lon, fires state, avalanche centers/season, USGS sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS map_key/source/confidence/bbox). ALSO adds a usgs_quake panel (tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in the old GUI. feed_source field (C.1) now surfaced per adapter. * Per-adapter live status: reuses /api/env/status feed health + /api/env/active events (filtered by source; fires->nifc mapping). Refreshes every 30s. - pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS entry + render case + EnvironmentalSection function + unused Thermometer import); exported the shared form primitives (Toggle, TextInput, NumberInput, SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx. - Reuses the existing restart_required banner pattern. - Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js, index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle). Rule 17 / no backend change: config is wired to the existing schema-driven GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake fields ride the generic dataclass coercion). No backend edited this phase. Verification: (A) `npm run build` clean -- tsc strict + vite, only the pre-existing >500kB single-chunk advisory (not introduced here). (B) static committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell (index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards, RF Propagation, ADS-B, 'Central not available', 'API key not configured'). (D) GET /api/config/environmental returns all adapters with feed_source=native, usgs_quake present, central{enabled:false} -- toggles bind to real data; all native, nothing flipped. Rebuilt prod healthy. *** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 *** The save half of gate D fails: PUT /api/config/environmental returns {"detail":"could not determine a constructor for the tag '!include' ..."}. Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py) calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a loader lacking the !include constructor) instead of the multi-file-aware meshai/config_loader.py::save_section that exists for exactly the !include layout. This breaks ALL GUI config saves in prod (every section, not just environmental) and predates C.2 -- the old Config 'Environmental' tab had the same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind works; only persistence is blocked. Verified disk pristine (idempotent PUT errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup). FIX (one line, but prod-wide blast radius -> wants its own phase + verification): config_routes PUT should call config_loader.save_section(section, data, config_dir) instead of config.save_config(...). Recommend C.2.1 backend fix before C.3. C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save, since flipping feed_source from the GUI is the whole point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
<div className="border border-[#1e2a3a] rounded-lg p-4 space-y-3">
<div className="flex items-center justify-between">
<div>
<span className="text-sm font-medium text-slate-300">{title}</span>
{subtitle && <p className="text-xs text-slate-600">{subtitle}</p>}
</div>
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle Restructures the environmental config UI into the 7-family taxonomy on the /environment page (Matt-approved Option C: unified families = config + live status per adapter). The editable adapter config moves out of the Config page's "Environmental" tab (now deprecated) onto /environment, where each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings) together with its live status (feed health + active events). Frontend (dashboard-frontend/src): - pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family adapter sub-tab strip -> AdapterPanel. * AdapterPanel: header row = on/off Toggle + feed_source toggle (native|central). When OFF, feed_source + all settings grey out (disabled, not hidden). * Native-only adapters (ducting, avalanche, roads511 -- no Central stream per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central' disabled + a 'Central not available for this adapter' tooltip/label. * Missing-key adapters (firms, roads511) show an 'API key not configured -- contact admin' notice; toggles still operate. * Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes are planned for v0.5.'). Mesh Health = no env adapters; a disabled feed_source toggle with 'central' greyed for future migration. * All existing per-adapter settings preserved verbatim (NWS zones/user_agent/ severity, ducting lat/lon, fires state, avalanche centers/season, USGS sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS map_key/source/confidence/bbox). ALSO adds a usgs_quake panel (tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in the old GUI. feed_source field (C.1) now surfaced per adapter. * Per-adapter live status: reuses /api/env/status feed health + /api/env/active events (filtered by source; fires->nifc mapping). Refreshes every 30s. - pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS entry + render case + EnvironmentalSection function + unused Thermometer import); exported the shared form primitives (Toggle, TextInput, NumberInput, SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx. - Reuses the existing restart_required banner pattern. - Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js, index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle). Rule 17 / no backend change: config is wired to the existing schema-driven GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake fields ride the generic dataclass coercion). No backend edited this phase. Verification: (A) `npm run build` clean -- tsc strict + vite, only the pre-existing >500kB single-chunk advisory (not introduced here). (B) static committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell (index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards, RF Propagation, ADS-B, 'Central not available', 'API key not configured'). (D) GET /api/config/environmental returns all adapters with feed_source=native, usgs_quake present, central{enabled:false} -- toggles bind to real data; all native, nothing flipped. Rebuilt prod healthy. *** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 *** The save half of gate D fails: PUT /api/config/environmental returns {"detail":"could not determine a constructor for the tag '!include' ..."}. Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py) calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a loader lacking the !include constructor) instead of the multi-file-aware meshai/config_loader.py::save_section that exists for exactly the !include layout. This breaks ALL GUI config saves in prod (every section, not just environmental) and predates C.2 -- the old Config 'Environmental' tab had the same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind works; only persistence is blocked. Verified disk pristine (idempotent PUT errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup). FIX (one line, but prod-wide blast radius -> wants its own phase + verification): config_routes PUT should call config_loader.save_section(section, data, config_dir) instead of config.save_config(...). Recommend C.2.1 backend fix before C.3. C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save, since flipping feed_source from the GUI is the whole point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
<div className="flex items-center gap-4">
<div className="flex items-center gap-1">
<span className="text-[10px] uppercase tracking-wide text-slate-600">source</span>
<FeedSourceToggle value={feedSource} onChange={onFeedSource} disabled={!enabled} centralDisabled={centralDisabled} />
</div>
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle Restructures the environmental config UI into the 7-family taxonomy on the /environment page (Matt-approved Option C: unified families = config + live status per adapter). The editable adapter config moves out of the Config page's "Environmental" tab (now deprecated) onto /environment, where each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings) together with its live status (feed health + active events). Frontend (dashboard-frontend/src): - pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family adapter sub-tab strip -> AdapterPanel. * AdapterPanel: header row = on/off Toggle + feed_source toggle (native|central). When OFF, feed_source + all settings grey out (disabled, not hidden). * Native-only adapters (ducting, avalanche, roads511 -- no Central stream per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central' disabled + a 'Central not available for this adapter' tooltip/label. * Missing-key adapters (firms, roads511) show an 'API key not configured -- contact admin' notice; toggles still operate. * Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes are planned for v0.5.'). Mesh Health = no env adapters; a disabled feed_source toggle with 'central' greyed for future migration. * All existing per-adapter settings preserved verbatim (NWS zones/user_agent/ severity, ducting lat/lon, fires state, avalanche centers/season, USGS sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS map_key/source/confidence/bbox). ALSO adds a usgs_quake panel (tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in the old GUI. feed_source field (C.1) now surfaced per adapter. * Per-adapter live status: reuses /api/env/status feed health + /api/env/active events (filtered by source; fires->nifc mapping). Refreshes every 30s. - pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS entry + render case + EnvironmentalSection function + unused Thermometer import); exported the shared form primitives (Toggle, TextInput, NumberInput, SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx. - Reuses the existing restart_required banner pattern. - Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js, index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle). Rule 17 / no backend change: config is wired to the existing schema-driven GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake fields ride the generic dataclass coercion). No backend edited this phase. Verification: (A) `npm run build` clean -- tsc strict + vite, only the pre-existing >500kB single-chunk advisory (not introduced here). (B) static committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell (index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards, RF Propagation, ADS-B, 'Central not available', 'API key not configured'). (D) GET /api/config/environmental returns all adapters with feed_source=native, usgs_quake present, central{enabled:false} -- toggles bind to real data; all native, nothing flipped. Rebuilt prod healthy. *** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 *** The save half of gate D fails: PUT /api/config/environmental returns {"detail":"could not determine a constructor for the tag '!include' ..."}. Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py) calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a loader lacking the !include constructor) instead of the multi-file-aware meshai/config_loader.py::save_section that exists for exactly the !include layout. This breaks ALL GUI config saves in prod (every section, not just environmental) and predates C.2 -- the old Config 'Environmental' tab had the same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind works; only persistence is blocked. Verified disk pristine (idempotent PUT errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup). FIX (one line, but prod-wide blast radius -> wants its own phase + verification): config_routes PUT should call config_loader.save_section(section, data, config_dir) instead of config.save_config(...). Recommend C.2.1 backend fix before C.3. C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save, since flipping feed_source from the GUI is the whole point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
<Toggle label="" checked={enabled} onChange={onEnabled} />
</div>
</div>
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle Restructures the environmental config UI into the 7-family taxonomy on the /environment page (Matt-approved Option C: unified families = config + live status per adapter). The editable adapter config moves out of the Config page's "Environmental" tab (now deprecated) onto /environment, where each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings) together with its live status (feed health + active events). Frontend (dashboard-frontend/src): - pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family adapter sub-tab strip -> AdapterPanel. * AdapterPanel: header row = on/off Toggle + feed_source toggle (native|central). When OFF, feed_source + all settings grey out (disabled, not hidden). * Native-only adapters (ducting, avalanche, roads511 -- no Central stream per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central' disabled + a 'Central not available for this adapter' tooltip/label. * Missing-key adapters (firms, roads511) show an 'API key not configured -- contact admin' notice; toggles still operate. * Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes are planned for v0.5.'). Mesh Health = no env adapters; a disabled feed_source toggle with 'central' greyed for future migration. * All existing per-adapter settings preserved verbatim (NWS zones/user_agent/ severity, ducting lat/lon, fires state, avalanche centers/season, USGS sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS map_key/source/confidence/bbox). ALSO adds a usgs_quake panel (tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in the old GUI. feed_source field (C.1) now surfaced per adapter. * Per-adapter live status: reuses /api/env/status feed health + /api/env/active events (filtered by source; fires->nifc mapping). Refreshes every 30s. - pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS entry + render case + EnvironmentalSection function + unused Thermometer import); exported the shared form primitives (Toggle, TextInput, NumberInput, SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx. - Reuses the existing restart_required banner pattern. - Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js, index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle). Rule 17 / no backend change: config is wired to the existing schema-driven GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake fields ride the generic dataclass coercion). No backend edited this phase. Verification: (A) `npm run build` clean -- tsc strict + vite, only the pre-existing >500kB single-chunk advisory (not introduced here). (B) static committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell (index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards, RF Propagation, ADS-B, 'Central not available', 'API key not configured'). (D) GET /api/config/environmental returns all adapters with feed_source=native, usgs_quake present, central{enabled:false} -- toggles bind to real data; all native, nothing flipped. Rebuilt prod healthy. *** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 *** The save half of gate D fails: PUT /api/config/environmental returns {"detail":"could not determine a constructor for the tag '!include' ..."}. Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py) calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a loader lacking the !include constructor) instead of the multi-file-aware meshai/config_loader.py::save_section that exists for exactly the !include layout. This breaks ALL GUI config saves in prod (every section, not just environmental) and predates C.2 -- the old Config 'Environmental' tab had the same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind works; only persistence is blocked. Verified disk pristine (idempotent PUT errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup). FIX (one line, but prod-wide blast radius -> wants its own phase + verification): config_routes PUT should call config_loader.save_section(section, data, config_dir) instead of config.save_config(...). Recommend C.2.1 backend fix before C.3. C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save, since flipping feed_source from the GUI is the whole point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
{!hasKey && (
<div className="text-xs text-amber-400 bg-amber-500/10 rounded p-2">
API key not configured contact admin
</div>
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle Restructures the environmental config UI into the 7-family taxonomy on the /environment page (Matt-approved Option C: unified families = config + live status per adapter). The editable adapter config moves out of the Config page's "Environmental" tab (now deprecated) onto /environment, where each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings) together with its live status (feed health + active events). Frontend (dashboard-frontend/src): - pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family adapter sub-tab strip -> AdapterPanel. * AdapterPanel: header row = on/off Toggle + feed_source toggle (native|central). When OFF, feed_source + all settings grey out (disabled, not hidden). * Native-only adapters (ducting, avalanche, roads511 -- no Central stream per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central' disabled + a 'Central not available for this adapter' tooltip/label. * Missing-key adapters (firms, roads511) show an 'API key not configured -- contact admin' notice; toggles still operate. * Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes are planned for v0.5.'). Mesh Health = no env adapters; a disabled feed_source toggle with 'central' greyed for future migration. * All existing per-adapter settings preserved verbatim (NWS zones/user_agent/ severity, ducting lat/lon, fires state, avalanche centers/season, USGS sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS map_key/source/confidence/bbox). ALSO adds a usgs_quake panel (tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in the old GUI. feed_source field (C.1) now surfaced per adapter. * Per-adapter live status: reuses /api/env/status feed health + /api/env/active events (filtered by source; fires->nifc mapping). Refreshes every 30s. - pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS entry + render case + EnvironmentalSection function + unused Thermometer import); exported the shared form primitives (Toggle, TextInput, NumberInput, SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx. - Reuses the existing restart_required banner pattern. - Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js, index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle). Rule 17 / no backend change: config is wired to the existing schema-driven GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake fields ride the generic dataclass coercion). No backend edited this phase. Verification: (A) `npm run build` clean -- tsc strict + vite, only the pre-existing >500kB single-chunk advisory (not introduced here). (B) static committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell (index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards, RF Propagation, ADS-B, 'Central not available', 'API key not configured'). (D) GET /api/config/environmental returns all adapters with feed_source=native, usgs_quake present, central{enabled:false} -- toggles bind to real data; all native, nothing flipped. Rebuilt prod healthy. *** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 *** The save half of gate D fails: PUT /api/config/environmental returns {"detail":"could not determine a constructor for the tag '!include' ..."}. Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py) calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a loader lacking the !include constructor) instead of the multi-file-aware meshai/config_loader.py::save_section that exists for exactly the !include layout. This breaks ALL GUI config saves in prod (every section, not just environmental) and predates C.2 -- the old Config 'Environmental' tab had the same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind works; only persistence is blocked. Verified disk pristine (idempotent PUT errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup). FIX (one line, but prod-wide blast radius -> wants its own phase + verification): config_routes PUT should call config_loader.save_section(section, data, config_dir) instead of config.save_config(...). Recommend C.2.1 backend fix before C.3. C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save, since flipping feed_source from the GUI is the whole point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
)}
{nativeOnly && (
<div className="text-[11px] text-slate-600">Central not available for this adapter native only</div>
)}
<div className={enabled ? 'space-y-3' : 'space-y-3 opacity-40 pointer-events-none select-none'}>
{children}
</div>
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle Restructures the environmental config UI into the 7-family taxonomy on the /environment page (Matt-approved Option C: unified families = config + live status per adapter). The editable adapter config moves out of the Config page's "Environmental" tab (now deprecated) onto /environment, where each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings) together with its live status (feed health + active events). Frontend (dashboard-frontend/src): - pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family adapter sub-tab strip -> AdapterPanel. * AdapterPanel: header row = on/off Toggle + feed_source toggle (native|central). When OFF, feed_source + all settings grey out (disabled, not hidden). * Native-only adapters (ducting, avalanche, roads511 -- no Central stream per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central' disabled + a 'Central not available for this adapter' tooltip/label. * Missing-key adapters (firms, roads511) show an 'API key not configured -- contact admin' notice; toggles still operate. * Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes are planned for v0.5.'). Mesh Health = no env adapters; a disabled feed_source toggle with 'central' greyed for future migration. * All existing per-adapter settings preserved verbatim (NWS zones/user_agent/ severity, ducting lat/lon, fires state, avalanche centers/season, USGS sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS map_key/source/confidence/bbox). ALSO adds a usgs_quake panel (tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in the old GUI. feed_source field (C.1) now surfaced per adapter. * Per-adapter live status: reuses /api/env/status feed health + /api/env/active events (filtered by source; fires->nifc mapping). Refreshes every 30s. - pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS entry + render case + EnvironmentalSection function + unused Thermometer import); exported the shared form primitives (Toggle, TextInput, NumberInput, SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx. - Reuses the existing restart_required banner pattern. - Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js, index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle). Rule 17 / no backend change: config is wired to the existing schema-driven GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake fields ride the generic dataclass coercion). No backend edited this phase. Verification: (A) `npm run build` clean -- tsc strict + vite, only the pre-existing >500kB single-chunk advisory (not introduced here). (B) static committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell (index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards, RF Propagation, ADS-B, 'Central not available', 'API key not configured'). (D) GET /api/config/environmental returns all adapters with feed_source=native, usgs_quake present, central{enabled:false} -- toggles bind to real data; all native, nothing flipped. Rebuilt prod healthy. *** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 *** The save half of gate D fails: PUT /api/config/environmental returns {"detail":"could not determine a constructor for the tag '!include' ..."}. Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py) calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a loader lacking the !include constructor) instead of the multi-file-aware meshai/config_loader.py::save_section that exists for exactly the !include layout. This breaks ALL GUI config saves in prod (every section, not just environmental) and predates C.2 -- the old Config 'Environmental' tab had the same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind works; only persistence is blocked. Verified disk pristine (idempotent PUT errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup). FIX (one line, but prod-wide blast radius -> wants its own phase + verification): config_routes PUT should call config_loader.save_section(section, data, config_dir) instead of config.save_config(...). Recommend C.2.1 backend fix before C.3. C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save, since flipping feed_source from the GUI is the whole point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
{(health || (events && events.length > 0)) && (
<div className="pt-2 border-t border-[#1e2a3a] space-y-3">
<div className="text-[10px] uppercase tracking-wide text-slate-600">Live status</div>
{health ? <FeedStatusCard feed={health} /> : <div className="text-xs text-slate-600">No status reported.</div>}
{events && events.length > 0 && (
<div className="space-y-2">
{events.slice(0, 5).map((e, i) => <EventCard key={i} event={e} />)}
</div>
)}
</div>
)}
</div>
)
}
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle Restructures the environmental config UI into the 7-family taxonomy on the /environment page (Matt-approved Option C: unified families = config + live status per adapter). The editable adapter config moves out of the Config page's "Environmental" tab (now deprecated) onto /environment, where each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings) together with its live status (feed health + active events). Frontend (dashboard-frontend/src): - pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family adapter sub-tab strip -> AdapterPanel. * AdapterPanel: header row = on/off Toggle + feed_source toggle (native|central). When OFF, feed_source + all settings grey out (disabled, not hidden). * Native-only adapters (ducting, avalanche, roads511 -- no Central stream per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central' disabled + a 'Central not available for this adapter' tooltip/label. * Missing-key adapters (firms, roads511) show an 'API key not configured -- contact admin' notice; toggles still operate. * Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes are planned for v0.5.'). Mesh Health = no env adapters; a disabled feed_source toggle with 'central' greyed for future migration. * All existing per-adapter settings preserved verbatim (NWS zones/user_agent/ severity, ducting lat/lon, fires state, avalanche centers/season, USGS sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS map_key/source/confidence/bbox). ALSO adds a usgs_quake panel (tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in the old GUI. feed_source field (C.1) now surfaced per adapter. * Per-adapter live status: reuses /api/env/status feed health + /api/env/active events (filtered by source; fires->nifc mapping). Refreshes every 30s. - pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS entry + render case + EnvironmentalSection function + unused Thermometer import); exported the shared form primitives (Toggle, TextInput, NumberInput, SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx. - Reuses the existing restart_required banner pattern. - Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js, index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle). Rule 17 / no backend change: config is wired to the existing schema-driven GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake fields ride the generic dataclass coercion). No backend edited this phase. Verification: (A) `npm run build` clean -- tsc strict + vite, only the pre-existing >500kB single-chunk advisory (not introduced here). (B) static committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell (index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards, RF Propagation, ADS-B, 'Central not available', 'API key not configured'). (D) GET /api/config/environmental returns all adapters with feed_source=native, usgs_quake present, central{enabled:false} -- toggles bind to real data; all native, nothing flipped. Rebuilt prod healthy. *** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 *** The save half of gate D fails: PUT /api/config/environmental returns {"detail":"could not determine a constructor for the tag '!include' ..."}. Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py) calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a loader lacking the !include constructor) instead of the multi-file-aware meshai/config_loader.py::save_section that exists for exactly the !include layout. This breaks ALL GUI config saves in prod (every section, not just environmental) and predates C.2 -- the old Config 'Environmental' tab had the same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind works; only persistence is blocked. Verified disk pristine (idempotent PUT errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup). FIX (one line, but prod-wide blast radius -> wants its own phase + verification): config_routes PUT should call config_loader.save_section(section, data, config_dir) instead of config.save_config(...). Recommend C.2.1 backend fix before C.3. C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save, since flipping feed_source from the GUI is the whole point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
// ---------------------------------------------------------------- families
type AdapterKey = 'nws' | 'fires' | 'firms' | 'swpc' | 'ducting' | 'traffic' | 'roads511' | 'usgs_quake' | 'usgs' | 'avalanche'
interface AdapterMeta { label: string; subtitle: string; health: string; hasCentral: boolean; nativeOnly: boolean; hasKey: boolean }
const META: Record<AdapterKey, AdapterMeta> = {
nws: { label: 'NWS Weather Alerts', subtitle: 'National Weather Service alerts', health: 'nws', hasCentral: true, nativeOnly: false, hasKey: true },
fires: { label: 'NIFC Fire Perimeters', subtitle: 'Active wildfires (National Interagency Fire Center)', health: 'nifc', hasCentral: true, nativeOnly: false, hasKey: true },
firms: { label: 'NASA FIRMS Hotspots', subtitle: 'Satellite thermal-anomaly detections', health: 'firms', hasCentral: true, nativeOnly: false, hasKey: false },
swpc: { label: 'NOAA Space Weather (SWPC)', subtitle: 'Solar indices, geomagnetic storms', health: 'swpc', hasCentral: true, nativeOnly: false, hasKey: true },
ducting: { label: 'Tropospheric Ducting', subtitle: 'VHF/UHF extended-range conditions', health: 'ducting', hasCentral: false, nativeOnly: true, hasKey: true },
traffic: { label: 'TomTom Traffic', subtitle: 'Traffic flow on monitored corridors', health: 'traffic', hasCentral: true, nativeOnly: false, hasKey: true },
feat(central): v0.5.3 -- roads511 + FIRMS central feeds with sub-adapter routing (recover stashed v0.5.1) C.2 (v0.4) marked roads511 and FIRMS native-only in the dashboard despite the Central event bus shipping both stream families. This recovers the v0.5.1 work paused before the v0.5.2 spam fix and lands it cleanly on top of v0.5.2. Backend (meshai/central/consumer.py): - ADAPTER_SUBJECTS: roads511 now subscribes to central.traffic.> (shared with the existing traffic adapter); firms widened from central.fire.hotspot.> to central.fire.>. - CENTRAL_ADAPTER_TO_SOURCE: three new sub-adapter remaps so the inner envelope.adapter routes correctly inside a shared subject -- tomtom_incidents -> traffic, state_511_atis -> roads511, firms -> firms. - New _subject_owned() -> dict[subject_filter, set[meshai_source]]: builds the per-subject ownership set so a single central.traffic.> subscription can be owned by {traffic, roads511} simultaneously. - subjects() now derives from _subject_owned().keys(). - _make_cb(owned) binds the owned set per-subscription; _on_message forwards it. - _handle(subject, raw, owned=None) drops events whose remapped source isn't in the owned set (silent debug log). Enabling roads511 alone no longer accidentally consumes wzdx; enabling traffic alone no longer consumes state_511_atis. - start() subscribes per (subject, owned) tuple; per-subject log line records the owned sources at startup. - Removed roads511 from the "no Central mapping" warning loop now that it has one. Frontend (dashboard-frontend/src/pages/Environment.tsx): - roads511 META entry: hasCentral false -> true, nativeOnly true -> false (the FIRMS entry was already correct). - Static bundle rebuilt via npm run build; old index-CfYlhn4e.js dropped, new index-DCFmSeOM.js + index-DjhQa8Mv.css land under static/assets; index.html updated to the new bundle hash. Tests (tests/test_central_sub_adapter_routing.py, 8 new): - roads511-only drops wzdx; emits state_511_atis as source=roads511. - traffic+roads511 both central: wzdx -> traffic, state_511_atis -> roads511. - firms-only drops wfigs_incidents; emits hotspots as source=firms. - tomtom_incidents remaps to traffic. - _subject_owned() shares central.traffic.> across {traffic, roads511}. Orthogonal to v0.5.2's dispatcher guards: cooldown / dedup / staleness still apply downstream of the consumer; the owned-sources filter operates one layer up at message ingest. No changes to the dispatcher path. Verified: pytest 318 passed (310 prior + 8 new routing tests); py_compile clean. Safe-mode preserved -- no toggle enabled, no master enabled, no central enabled. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-04 02:16:54 +00:00
roads511: { label: '511 Road Conditions', subtitle: 'State DOT road events and closures', health: 'roads511', hasCentral: true, nativeOnly: false, hasKey: false },
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle Restructures the environmental config UI into the 7-family taxonomy on the /environment page (Matt-approved Option C: unified families = config + live status per adapter). The editable adapter config moves out of the Config page's "Environmental" tab (now deprecated) onto /environment, where each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings) together with its live status (feed health + active events). Frontend (dashboard-frontend/src): - pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family adapter sub-tab strip -> AdapterPanel. * AdapterPanel: header row = on/off Toggle + feed_source toggle (native|central). When OFF, feed_source + all settings grey out (disabled, not hidden). * Native-only adapters (ducting, avalanche, roads511 -- no Central stream per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central' disabled + a 'Central not available for this adapter' tooltip/label. * Missing-key adapters (firms, roads511) show an 'API key not configured -- contact admin' notice; toggles still operate. * Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes are planned for v0.5.'). Mesh Health = no env adapters; a disabled feed_source toggle with 'central' greyed for future migration. * All existing per-adapter settings preserved verbatim (NWS zones/user_agent/ severity, ducting lat/lon, fires state, avalanche centers/season, USGS sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS map_key/source/confidence/bbox). ALSO adds a usgs_quake panel (tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in the old GUI. feed_source field (C.1) now surfaced per adapter. * Per-adapter live status: reuses /api/env/status feed health + /api/env/active events (filtered by source; fires->nifc mapping). Refreshes every 30s. - pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS entry + render case + EnvironmentalSection function + unused Thermometer import); exported the shared form primitives (Toggle, TextInput, NumberInput, SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx. - Reuses the existing restart_required banner pattern. - Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js, index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle). Rule 17 / no backend change: config is wired to the existing schema-driven GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake fields ride the generic dataclass coercion). No backend edited this phase. Verification: (A) `npm run build` clean -- tsc strict + vite, only the pre-existing >500kB single-chunk advisory (not introduced here). (B) static committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell (index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards, RF Propagation, ADS-B, 'Central not available', 'API key not configured'). (D) GET /api/config/environmental returns all adapters with feed_source=native, usgs_quake present, central{enabled:false} -- toggles bind to real data; all native, nothing flipped. Rebuilt prod healthy. *** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 *** The save half of gate D fails: PUT /api/config/environmental returns {"detail":"could not determine a constructor for the tag '!include' ..."}. Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py) calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a loader lacking the !include constructor) instead of the multi-file-aware meshai/config_loader.py::save_section that exists for exactly the !include layout. This breaks ALL GUI config saves in prod (every section, not just environmental) and predates C.2 -- the old Config 'Environmental' tab had the same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind works; only persistence is blocked. Verified disk pristine (idempotent PUT errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup). FIX (one line, but prod-wide blast radius -> wants its own phase + verification): config_routes PUT should call config_loader.save_section(section, data, config_dir) instead of config.save_config(...). Recommend C.2.1 backend fix before C.3. C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save, since flipping feed_source from the GUI is the whole point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
usgs_quake: { label: 'USGS Earthquakes', subtitle: 'Seismic events from the USGS feed', health: 'usgs_quake', hasCentral: true, nativeOnly: false, hasKey: true },
usgs: { label: 'USGS Stream Gauges', subtitle: 'River and stream water levels', health: 'usgs', hasCentral: true, nativeOnly: false, hasKey: true },
avalanche: { label: 'Avalanche Advisories', subtitle: 'Backcountry avalanche danger ratings', health: 'avalanche', hasCentral: false, nativeOnly: true, hasKey: true },
}
const FAMILIES: { key: string; label: string; icon: typeof Cloud; adapters: AdapterKey[] }[] = [
{ key: 'weather', label: 'Weather', icon: Cloud, adapters: ['nws'] },
{ key: 'fire', label: 'Fire', icon: Flame, adapters: ['fires', 'firms'] },
{ key: 'rf', label: 'RF Propagation', icon: Radio, adapters: ['swpc', 'ducting'] },
{ key: 'roads', label: 'Roads', icon: Car, adapters: ['traffic', 'roads511'] },
{ key: 'geohazards', label: 'Geohazards', icon: Mountain, adapters: ['usgs_quake', 'usgs', 'avalanche'] },
{ key: 'tracking', label: 'Tracking', icon: Satellite, adapters: [] },
{ key: 'mesh', label: 'Mesh Health', icon: Activity, adapters: [] },
]
// ---------------------------------------------------------------- main page
export default function Environment() {
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle Restructures the environmental config UI into the 7-family taxonomy on the /environment page (Matt-approved Option C: unified families = config + live status per adapter). The editable adapter config moves out of the Config page's "Environmental" tab (now deprecated) onto /environment, where each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings) together with its live status (feed health + active events). Frontend (dashboard-frontend/src): - pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family adapter sub-tab strip -> AdapterPanel. * AdapterPanel: header row = on/off Toggle + feed_source toggle (native|central). When OFF, feed_source + all settings grey out (disabled, not hidden). * Native-only adapters (ducting, avalanche, roads511 -- no Central stream per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central' disabled + a 'Central not available for this adapter' tooltip/label. * Missing-key adapters (firms, roads511) show an 'API key not configured -- contact admin' notice; toggles still operate. * Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes are planned for v0.5.'). Mesh Health = no env adapters; a disabled feed_source toggle with 'central' greyed for future migration. * All existing per-adapter settings preserved verbatim (NWS zones/user_agent/ severity, ducting lat/lon, fires state, avalanche centers/season, USGS sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS map_key/source/confidence/bbox). ALSO adds a usgs_quake panel (tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in the old GUI. feed_source field (C.1) now surfaced per adapter. * Per-adapter live status: reuses /api/env/status feed health + /api/env/active events (filtered by source; fires->nifc mapping). Refreshes every 30s. - pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS entry + render case + EnvironmentalSection function + unused Thermometer import); exported the shared form primitives (Toggle, TextInput, NumberInput, SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx. - Reuses the existing restart_required banner pattern. - Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js, index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle). Rule 17 / no backend change: config is wired to the existing schema-driven GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake fields ride the generic dataclass coercion). No backend edited this phase. Verification: (A) `npm run build` clean -- tsc strict + vite, only the pre-existing >500kB single-chunk advisory (not introduced here). (B) static committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell (index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards, RF Propagation, ADS-B, 'Central not available', 'API key not configured'). (D) GET /api/config/environmental returns all adapters with feed_source=native, usgs_quake present, central{enabled:false} -- toggles bind to real data; all native, nothing flipped. Rebuilt prod healthy. *** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 *** The save half of gate D fails: PUT /api/config/environmental returns {"detail":"could not determine a constructor for the tag '!include' ..."}. Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py) calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a loader lacking the !include constructor) instead of the multi-file-aware meshai/config_loader.py::save_section that exists for exactly the !include layout. This breaks ALL GUI config saves in prod (every section, not just environmental) and predates C.2 -- the old Config 'Environmental' tab had the same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind works; only persistence is blocked. Verified disk pristine (idempotent PUT errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup). FIX (one line, but prod-wide blast radius -> wants its own phase + verification): config_routes PUT should call config_loader.save_section(section, data, config_dir) instead of config.save_config(...). Recommend C.2.1 backend fix before C.3. C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save, since flipping feed_source from the GUI is the whole point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
const [env, setEnv] = useState<EnvConfig | null>(null)
const [original, setOriginal] = useState<string>('')
const [status, setStatus] = useState<EnvStatus | null>(null)
const [events, setEvents] = useState<EnvEvent[]>([])
const [loading, setLoading] = useState(true)
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle Restructures the environmental config UI into the 7-family taxonomy on the /environment page (Matt-approved Option C: unified families = config + live status per adapter). The editable adapter config moves out of the Config page's "Environmental" tab (now deprecated) onto /environment, where each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings) together with its live status (feed health + active events). Frontend (dashboard-frontend/src): - pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family adapter sub-tab strip -> AdapterPanel. * AdapterPanel: header row = on/off Toggle + feed_source toggle (native|central). When OFF, feed_source + all settings grey out (disabled, not hidden). * Native-only adapters (ducting, avalanche, roads511 -- no Central stream per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central' disabled + a 'Central not available for this adapter' tooltip/label. * Missing-key adapters (firms, roads511) show an 'API key not configured -- contact admin' notice; toggles still operate. * Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes are planned for v0.5.'). Mesh Health = no env adapters; a disabled feed_source toggle with 'central' greyed for future migration. * All existing per-adapter settings preserved verbatim (NWS zones/user_agent/ severity, ducting lat/lon, fires state, avalanche centers/season, USGS sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS map_key/source/confidence/bbox). ALSO adds a usgs_quake panel (tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in the old GUI. feed_source field (C.1) now surfaced per adapter. * Per-adapter live status: reuses /api/env/status feed health + /api/env/active events (filtered by source; fires->nifc mapping). Refreshes every 30s. - pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS entry + render case + EnvironmentalSection function + unused Thermometer import); exported the shared form primitives (Toggle, TextInput, NumberInput, SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx. - Reuses the existing restart_required banner pattern. - Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js, index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle). Rule 17 / no backend change: config is wired to the existing schema-driven GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake fields ride the generic dataclass coercion). No backend edited this phase. Verification: (A) `npm run build` clean -- tsc strict + vite, only the pre-existing >500kB single-chunk advisory (not introduced here). (B) static committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell (index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards, RF Propagation, ADS-B, 'Central not available', 'API key not configured'). (D) GET /api/config/environmental returns all adapters with feed_source=native, usgs_quake present, central{enabled:false} -- toggles bind to real data; all native, nothing flipped. Rebuilt prod healthy. *** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 *** The save half of gate D fails: PUT /api/config/environmental returns {"detail":"could not determine a constructor for the tag '!include' ..."}. Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py) calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a loader lacking the !include constructor) instead of the multi-file-aware meshai/config_loader.py::save_section that exists for exactly the !include layout. This breaks ALL GUI config saves in prod (every section, not just environmental) and predates C.2 -- the old Config 'Environmental' tab had the same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind works; only persistence is blocked. Verified disk pristine (idempotent PUT errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup). FIX (one line, but prod-wide blast radius -> wants its own phase + verification): config_routes PUT should call config_loader.save_section(section, data, config_dir) instead of config.save_config(...). Recommend C.2.1 backend fix before C.3. C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save, since flipping feed_source from the GUI is the whole point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
const [saving, setSaving] = useState(false)
const [error, setError] = useState<string | null>(null)
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle Restructures the environmental config UI into the 7-family taxonomy on the /environment page (Matt-approved Option C: unified families = config + live status per adapter). The editable adapter config moves out of the Config page's "Environmental" tab (now deprecated) onto /environment, where each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings) together with its live status (feed health + active events). Frontend (dashboard-frontend/src): - pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family adapter sub-tab strip -> AdapterPanel. * AdapterPanel: header row = on/off Toggle + feed_source toggle (native|central). When OFF, feed_source + all settings grey out (disabled, not hidden). * Native-only adapters (ducting, avalanche, roads511 -- no Central stream per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central' disabled + a 'Central not available for this adapter' tooltip/label. * Missing-key adapters (firms, roads511) show an 'API key not configured -- contact admin' notice; toggles still operate. * Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes are planned for v0.5.'). Mesh Health = no env adapters; a disabled feed_source toggle with 'central' greyed for future migration. * All existing per-adapter settings preserved verbatim (NWS zones/user_agent/ severity, ducting lat/lon, fires state, avalanche centers/season, USGS sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS map_key/source/confidence/bbox). ALSO adds a usgs_quake panel (tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in the old GUI. feed_source field (C.1) now surfaced per adapter. * Per-adapter live status: reuses /api/env/status feed health + /api/env/active events (filtered by source; fires->nifc mapping). Refreshes every 30s. - pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS entry + render case + EnvironmentalSection function + unused Thermometer import); exported the shared form primitives (Toggle, TextInput, NumberInput, SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx. - Reuses the existing restart_required banner pattern. - Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js, index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle). Rule 17 / no backend change: config is wired to the existing schema-driven GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake fields ride the generic dataclass coercion). No backend edited this phase. Verification: (A) `npm run build` clean -- tsc strict + vite, only the pre-existing >500kB single-chunk advisory (not introduced here). (B) static committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell (index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards, RF Propagation, ADS-B, 'Central not available', 'API key not configured'). (D) GET /api/config/environmental returns all adapters with feed_source=native, usgs_quake present, central{enabled:false} -- toggles bind to real data; all native, nothing flipped. Rebuilt prod healthy. *** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 *** The save half of gate D fails: PUT /api/config/environmental returns {"detail":"could not determine a constructor for the tag '!include' ..."}. Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py) calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a loader lacking the !include constructor) instead of the multi-file-aware meshai/config_loader.py::save_section that exists for exactly the !include layout. This breaks ALL GUI config saves in prod (every section, not just environmental) and predates C.2 -- the old Config 'Environmental' tab had the same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind works; only persistence is blocked. Verified disk pristine (idempotent PUT errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup). FIX (one line, but prod-wide blast radius -> wants its own phase + verification): config_routes PUT should call config_loader.save_section(section, data, config_dir) instead of config.save_config(...). Recommend C.2.1 backend fix before C.3. C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save, since flipping feed_source from the GUI is the whole point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
const [success, setSuccess] = useState<string | null>(null)
const [restartRequired, setRestartRequired] = useState(false)
const [family, setFamily] = useState('weather')
const [adapter, setAdapter] = useState<AdapterKey | null>('nws')
useEffect(() => {
document.title = 'Environment — MeshAI'
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle Restructures the environmental config UI into the 7-family taxonomy on the /environment page (Matt-approved Option C: unified families = config + live status per adapter). The editable adapter config moves out of the Config page's "Environmental" tab (now deprecated) onto /environment, where each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings) together with its live status (feed health + active events). Frontend (dashboard-frontend/src): - pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family adapter sub-tab strip -> AdapterPanel. * AdapterPanel: header row = on/off Toggle + feed_source toggle (native|central). When OFF, feed_source + all settings grey out (disabled, not hidden). * Native-only adapters (ducting, avalanche, roads511 -- no Central stream per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central' disabled + a 'Central not available for this adapter' tooltip/label. * Missing-key adapters (firms, roads511) show an 'API key not configured -- contact admin' notice; toggles still operate. * Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes are planned for v0.5.'). Mesh Health = no env adapters; a disabled feed_source toggle with 'central' greyed for future migration. * All existing per-adapter settings preserved verbatim (NWS zones/user_agent/ severity, ducting lat/lon, fires state, avalanche centers/season, USGS sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS map_key/source/confidence/bbox). ALSO adds a usgs_quake panel (tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in the old GUI. feed_source field (C.1) now surfaced per adapter. * Per-adapter live status: reuses /api/env/status feed health + /api/env/active events (filtered by source; fires->nifc mapping). Refreshes every 30s. - pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS entry + render case + EnvironmentalSection function + unused Thermometer import); exported the shared form primitives (Toggle, TextInput, NumberInput, SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx. - Reuses the existing restart_required banner pattern. - Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js, index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle). Rule 17 / no backend change: config is wired to the existing schema-driven GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake fields ride the generic dataclass coercion). No backend edited this phase. Verification: (A) `npm run build` clean -- tsc strict + vite, only the pre-existing >500kB single-chunk advisory (not introduced here). (B) static committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell (index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards, RF Propagation, ADS-B, 'Central not available', 'API key not configured'). (D) GET /api/config/environmental returns all adapters with feed_source=native, usgs_quake present, central{enabled:false} -- toggles bind to real data; all native, nothing flipped. Rebuilt prod healthy. *** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 *** The save half of gate D fails: PUT /api/config/environmental returns {"detail":"could not determine a constructor for the tag '!include' ..."}. Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py) calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a loader lacking the !include constructor) instead of the multi-file-aware meshai/config_loader.py::save_section that exists for exactly the !include layout. This breaks ALL GUI config saves in prod (every section, not just environmental) and predates C.2 -- the old Config 'Environmental' tab had the same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind works; only persistence is blocked. Verified disk pristine (idempotent PUT errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup). FIX (one line, but prod-wide blast radius -> wants its own phase + verification): config_routes PUT should call config_loader.save_section(section, data, config_dir) instead of config.save_config(...). Recommend C.2.1 backend fix before C.3. C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save, since flipping feed_source from the GUI is the whole point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
;(async () => {
try {
const res = await fetch('/api/config/environmental')
const data = await res.json()
setEnv(data)
setOriginal(JSON.stringify(data))
} catch (e) {
setError(e instanceof Error ? e.message : 'Failed to load config')
} finally {
setLoading(false)
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle Restructures the environmental config UI into the 7-family taxonomy on the /environment page (Matt-approved Option C: unified families = config + live status per adapter). The editable adapter config moves out of the Config page's "Environmental" tab (now deprecated) onto /environment, where each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings) together with its live status (feed health + active events). Frontend (dashboard-frontend/src): - pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family adapter sub-tab strip -> AdapterPanel. * AdapterPanel: header row = on/off Toggle + feed_source toggle (native|central). When OFF, feed_source + all settings grey out (disabled, not hidden). * Native-only adapters (ducting, avalanche, roads511 -- no Central stream per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central' disabled + a 'Central not available for this adapter' tooltip/label. * Missing-key adapters (firms, roads511) show an 'API key not configured -- contact admin' notice; toggles still operate. * Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes are planned for v0.5.'). Mesh Health = no env adapters; a disabled feed_source toggle with 'central' greyed for future migration. * All existing per-adapter settings preserved verbatim (NWS zones/user_agent/ severity, ducting lat/lon, fires state, avalanche centers/season, USGS sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS map_key/source/confidence/bbox). ALSO adds a usgs_quake panel (tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in the old GUI. feed_source field (C.1) now surfaced per adapter. * Per-adapter live status: reuses /api/env/status feed health + /api/env/active events (filtered by source; fires->nifc mapping). Refreshes every 30s. - pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS entry + render case + EnvironmentalSection function + unused Thermometer import); exported the shared form primitives (Toggle, TextInput, NumberInput, SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx. - Reuses the existing restart_required banner pattern. - Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js, index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle). Rule 17 / no backend change: config is wired to the existing schema-driven GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake fields ride the generic dataclass coercion). No backend edited this phase. Verification: (A) `npm run build` clean -- tsc strict + vite, only the pre-existing >500kB single-chunk advisory (not introduced here). (B) static committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell (index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards, RF Propagation, ADS-B, 'Central not available', 'API key not configured'). (D) GET /api/config/environmental returns all adapters with feed_source=native, usgs_quake present, central{enabled:false} -- toggles bind to real data; all native, nothing flipped. Rebuilt prod healthy. *** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 *** The save half of gate D fails: PUT /api/config/environmental returns {"detail":"could not determine a constructor for the tag '!include' ..."}. Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py) calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a loader lacking the !include constructor) instead of the multi-file-aware meshai/config_loader.py::save_section that exists for exactly the !include layout. This breaks ALL GUI config saves in prod (every section, not just environmental) and predates C.2 -- the old Config 'Environmental' tab had the same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind works; only persistence is blocked. Verified disk pristine (idempotent PUT errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup). FIX (one line, but prod-wide blast radius -> wants its own phase + verification): config_routes PUT should call config_loader.save_section(section, data, config_dir) instead of config.save_config(...). Recommend C.2.1 backend fix before C.3. C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save, since flipping feed_source from the GUI is the whole point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
}
})()
}, [])
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle Restructures the environmental config UI into the 7-family taxonomy on the /environment page (Matt-approved Option C: unified families = config + live status per adapter). The editable adapter config moves out of the Config page's "Environmental" tab (now deprecated) onto /environment, where each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings) together with its live status (feed health + active events). Frontend (dashboard-frontend/src): - pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family adapter sub-tab strip -> AdapterPanel. * AdapterPanel: header row = on/off Toggle + feed_source toggle (native|central). When OFF, feed_source + all settings grey out (disabled, not hidden). * Native-only adapters (ducting, avalanche, roads511 -- no Central stream per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central' disabled + a 'Central not available for this adapter' tooltip/label. * Missing-key adapters (firms, roads511) show an 'API key not configured -- contact admin' notice; toggles still operate. * Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes are planned for v0.5.'). Mesh Health = no env adapters; a disabled feed_source toggle with 'central' greyed for future migration. * All existing per-adapter settings preserved verbatim (NWS zones/user_agent/ severity, ducting lat/lon, fires state, avalanche centers/season, USGS sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS map_key/source/confidence/bbox). ALSO adds a usgs_quake panel (tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in the old GUI. feed_source field (C.1) now surfaced per adapter. * Per-adapter live status: reuses /api/env/status feed health + /api/env/active events (filtered by source; fires->nifc mapping). Refreshes every 30s. - pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS entry + render case + EnvironmentalSection function + unused Thermometer import); exported the shared form primitives (Toggle, TextInput, NumberInput, SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx. - Reuses the existing restart_required banner pattern. - Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js, index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle). Rule 17 / no backend change: config is wired to the existing schema-driven GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake fields ride the generic dataclass coercion). No backend edited this phase. Verification: (A) `npm run build` clean -- tsc strict + vite, only the pre-existing >500kB single-chunk advisory (not introduced here). (B) static committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell (index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards, RF Propagation, ADS-B, 'Central not available', 'API key not configured'). (D) GET /api/config/environmental returns all adapters with feed_source=native, usgs_quake present, central{enabled:false} -- toggles bind to real data; all native, nothing flipped. Rebuilt prod healthy. *** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 *** The save half of gate D fails: PUT /api/config/environmental returns {"detail":"could not determine a constructor for the tag '!include' ..."}. Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py) calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a loader lacking the !include constructor) instead of the multi-file-aware meshai/config_loader.py::save_section that exists for exactly the !include layout. This breaks ALL GUI config saves in prod (every section, not just environmental) and predates C.2 -- the old Config 'Environmental' tab had the same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind works; only persistence is blocked. Verified disk pristine (idempotent PUT errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup). FIX (one line, but prod-wide blast radius -> wants its own phase + verification): config_routes PUT should call config_loader.save_section(section, data, config_dir) instead of config.save_config(...). Recommend C.2.1 backend fix before C.3. C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save, since flipping feed_source from the GUI is the whole point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
useEffect(() => {
const load = async () => {
try {
setStatus(await fetchEnvStatus())
setEvents(await fetchEnvActive())
} catch { /* status is best-effort */ }
}
load()
const t = setInterval(load, 30000)
return () => clearInterval(t)
}, [])
const hasChanges = env !== null && JSON.stringify(env) !== original
const save = async () => {
if (!env) return
setSaving(true); setError(null); setSuccess(null)
try {
const res = await fetch('/api/config/environmental', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(env),
})
const result = await res.json()
if (!res.ok) throw new Error(result.detail || 'Save failed')
setOriginal(JSON.stringify(env))
setSuccess('Environmental config saved')
if (result.restart_required) setRestartRequired(true)
setTimeout(() => setSuccess(null), 3000)
} catch (e) {
setError(e instanceof Error ? e.message : 'Save failed')
} finally {
setSaving(false)
}
}
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle Restructures the environmental config UI into the 7-family taxonomy on the /environment page (Matt-approved Option C: unified families = config + live status per adapter). The editable adapter config moves out of the Config page's "Environmental" tab (now deprecated) onto /environment, where each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings) together with its live status (feed health + active events). Frontend (dashboard-frontend/src): - pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family adapter sub-tab strip -> AdapterPanel. * AdapterPanel: header row = on/off Toggle + feed_source toggle (native|central). When OFF, feed_source + all settings grey out (disabled, not hidden). * Native-only adapters (ducting, avalanche, roads511 -- no Central stream per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central' disabled + a 'Central not available for this adapter' tooltip/label. * Missing-key adapters (firms, roads511) show an 'API key not configured -- contact admin' notice; toggles still operate. * Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes are planned for v0.5.'). Mesh Health = no env adapters; a disabled feed_source toggle with 'central' greyed for future migration. * All existing per-adapter settings preserved verbatim (NWS zones/user_agent/ severity, ducting lat/lon, fires state, avalanche centers/season, USGS sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS map_key/source/confidence/bbox). ALSO adds a usgs_quake panel (tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in the old GUI. feed_source field (C.1) now surfaced per adapter. * Per-adapter live status: reuses /api/env/status feed health + /api/env/active events (filtered by source; fires->nifc mapping). Refreshes every 30s. - pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS entry + render case + EnvironmentalSection function + unused Thermometer import); exported the shared form primitives (Toggle, TextInput, NumberInput, SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx. - Reuses the existing restart_required banner pattern. - Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js, index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle). Rule 17 / no backend change: config is wired to the existing schema-driven GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake fields ride the generic dataclass coercion). No backend edited this phase. Verification: (A) `npm run build` clean -- tsc strict + vite, only the pre-existing >500kB single-chunk advisory (not introduced here). (B) static committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell (index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards, RF Propagation, ADS-B, 'Central not available', 'API key not configured'). (D) GET /api/config/environmental returns all adapters with feed_source=native, usgs_quake present, central{enabled:false} -- toggles bind to real data; all native, nothing flipped. Rebuilt prod healthy. *** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 *** The save half of gate D fails: PUT /api/config/environmental returns {"detail":"could not determine a constructor for the tag '!include' ..."}. Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py) calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a loader lacking the !include constructor) instead of the multi-file-aware meshai/config_loader.py::save_section that exists for exactly the !include layout. This breaks ALL GUI config saves in prod (every section, not just environmental) and predates C.2 -- the old Config 'Environmental' tab had the same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind works; only persistence is blocked. Verified disk pristine (idempotent PUT errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup). FIX (one line, but prod-wide blast radius -> wants its own phase + verification): config_routes PUT should call config_loader.save_section(section, data, config_dir) instead of config.save_config(...). Recommend C.2.1 backend fix before C.3. C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save, since flipping feed_source from the GUI is the whole point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
const discard = () => { if (env) setEnv(JSON.parse(original)) }
const restart = async () => {
try { await fetch('/api/restart', { method: 'POST' }); setRestartRequired(false); setSuccess('Restart initiated') }
catch { setError('Restart failed') }
}
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle Restructures the environmental config UI into the 7-family taxonomy on the /environment page (Matt-approved Option C: unified families = config + live status per adapter). The editable adapter config moves out of the Config page's "Environmental" tab (now deprecated) onto /environment, where each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings) together with its live status (feed health + active events). Frontend (dashboard-frontend/src): - pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family adapter sub-tab strip -> AdapterPanel. * AdapterPanel: header row = on/off Toggle + feed_source toggle (native|central). When OFF, feed_source + all settings grey out (disabled, not hidden). * Native-only adapters (ducting, avalanche, roads511 -- no Central stream per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central' disabled + a 'Central not available for this adapter' tooltip/label. * Missing-key adapters (firms, roads511) show an 'API key not configured -- contact admin' notice; toggles still operate. * Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes are planned for v0.5.'). Mesh Health = no env adapters; a disabled feed_source toggle with 'central' greyed for future migration. * All existing per-adapter settings preserved verbatim (NWS zones/user_agent/ severity, ducting lat/lon, fires state, avalanche centers/season, USGS sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS map_key/source/confidence/bbox). ALSO adds a usgs_quake panel (tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in the old GUI. feed_source field (C.1) now surfaced per adapter. * Per-adapter live status: reuses /api/env/status feed health + /api/env/active events (filtered by source; fires->nifc mapping). Refreshes every 30s. - pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS entry + render case + EnvironmentalSection function + unused Thermometer import); exported the shared form primitives (Toggle, TextInput, NumberInput, SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx. - Reuses the existing restart_required banner pattern. - Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js, index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle). Rule 17 / no backend change: config is wired to the existing schema-driven GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake fields ride the generic dataclass coercion). No backend edited this phase. Verification: (A) `npm run build` clean -- tsc strict + vite, only the pre-existing >500kB single-chunk advisory (not introduced here). (B) static committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell (index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards, RF Propagation, ADS-B, 'Central not available', 'API key not configured'). (D) GET /api/config/environmental returns all adapters with feed_source=native, usgs_quake present, central{enabled:false} -- toggles bind to real data; all native, nothing flipped. Rebuilt prod healthy. *** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 *** The save half of gate D fails: PUT /api/config/environmental returns {"detail":"could not determine a constructor for the tag '!include' ..."}. Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py) calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a loader lacking the !include constructor) instead of the multi-file-aware meshai/config_loader.py::save_section that exists for exactly the !include layout. This breaks ALL GUI config saves in prod (every section, not just environmental) and predates C.2 -- the old Config 'Environmental' tab had the same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind works; only persistence is blocked. Verified disk pristine (idempotent PUT errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup). FIX (one line, but prod-wide blast radius -> wants its own phase + verification): config_routes PUT should call config_loader.save_section(section, data, config_dir) instead of config.save_config(...). Recommend C.2.1 backend fix before C.3. C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save, since flipping feed_source from the GUI is the whole point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
const up = (patch: Partial<EnvConfig>) => env && setEnv({ ...env, ...patch })
if (loading) return <div className="flex items-center justify-center h-64 text-slate-400">Loading environmental config</div>
if (!env) return <div className="flex items-center justify-center h-64 text-red-400">{error || 'No config'}</div>
const healthFor = (key: AdapterKey): FeedHealth | undefined =>
status?.feeds.find((f) => f.source === META[key].health)
const eventsFor = (key: AdapterKey): EnvEvent[] =>
events.filter((e) => e.source === META[key].health)
const fam = FAMILIES.find((f) => f.key === family)!
const activeAdapter: AdapterKey | null =
fam.adapters.length === 0 ? null : (adapter && fam.adapters.includes(adapter) ? adapter : fam.adapters[0])
// -- per-adapter settings forms (preserve all existing settings) --
const renderSettings = (key: AdapterKey) => {
switch (key) {
case 'nws': return (<>
<ListInput label="NWS Zones" value={env.nws_zones} onChange={(v) => up({ nws_zones: v })} helper="Zone IDs like IDZ016, IDZ030" infoLink="https://www.weather.gov/pimar/PubZone" />
<TextInput label="User Agent" value={env.nws.user_agent} onChange={(v) => up({ nws: { ...env.nws, user_agent: v } })} placeholder="(MeshAI, you@email.com)" helper="Format: (app_name, contact_email)" />
<div className="grid grid-cols-2 gap-4">
<NumberInput label="Tick Seconds" value={env.nws.tick_seconds} onChange={(v) => up({ nws: { ...env.nws, tick_seconds: v } })} min={30} />
<SelectInput label="Min Severity" value={env.nws.severity_min} onChange={(v) => up({ nws: { ...env.nws, severity_min: v } })} options={[{ value: 'minor', label: 'Minor' }, { value: 'moderate', label: 'Moderate' }, { value: 'severe', label: 'Severe' }, { value: 'extreme', label: 'Extreme' }]} />
</div>
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle Restructures the environmental config UI into the 7-family taxonomy on the /environment page (Matt-approved Option C: unified families = config + live status per adapter). The editable adapter config moves out of the Config page's "Environmental" tab (now deprecated) onto /environment, where each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings) together with its live status (feed health + active events). Frontend (dashboard-frontend/src): - pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family adapter sub-tab strip -> AdapterPanel. * AdapterPanel: header row = on/off Toggle + feed_source toggle (native|central). When OFF, feed_source + all settings grey out (disabled, not hidden). * Native-only adapters (ducting, avalanche, roads511 -- no Central stream per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central' disabled + a 'Central not available for this adapter' tooltip/label. * Missing-key adapters (firms, roads511) show an 'API key not configured -- contact admin' notice; toggles still operate. * Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes are planned for v0.5.'). Mesh Health = no env adapters; a disabled feed_source toggle with 'central' greyed for future migration. * All existing per-adapter settings preserved verbatim (NWS zones/user_agent/ severity, ducting lat/lon, fires state, avalanche centers/season, USGS sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS map_key/source/confidence/bbox). ALSO adds a usgs_quake panel (tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in the old GUI. feed_source field (C.1) now surfaced per adapter. * Per-adapter live status: reuses /api/env/status feed health + /api/env/active events (filtered by source; fires->nifc mapping). Refreshes every 30s. - pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS entry + render case + EnvironmentalSection function + unused Thermometer import); exported the shared form primitives (Toggle, TextInput, NumberInput, SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx. - Reuses the existing restart_required banner pattern. - Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js, index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle). Rule 17 / no backend change: config is wired to the existing schema-driven GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake fields ride the generic dataclass coercion). No backend edited this phase. Verification: (A) `npm run build` clean -- tsc strict + vite, only the pre-existing >500kB single-chunk advisory (not introduced here). (B) static committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell (index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards, RF Propagation, ADS-B, 'Central not available', 'API key not configured'). (D) GET /api/config/environmental returns all adapters with feed_source=native, usgs_quake present, central{enabled:false} -- toggles bind to real data; all native, nothing flipped. Rebuilt prod healthy. *** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 *** The save half of gate D fails: PUT /api/config/environmental returns {"detail":"could not determine a constructor for the tag '!include' ..."}. Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py) calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a loader lacking the !include constructor) instead of the multi-file-aware meshai/config_loader.py::save_section that exists for exactly the !include layout. This breaks ALL GUI config saves in prod (every section, not just environmental) and predates C.2 -- the old Config 'Environmental' tab had the same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind works; only persistence is blocked. Verified disk pristine (idempotent PUT errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup). FIX (one line, but prod-wide blast radius -> wants its own phase + verification): config_routes PUT should call config_loader.save_section(section, data, config_dir) instead of config.save_config(...). Recommend C.2.1 backend fix before C.3. C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save, since flipping feed_source from the GUI is the whole point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
</>)
case 'swpc': return <div className="text-xs text-slate-500">No additional settings.</div>
case 'ducting': return (
<div className="grid grid-cols-3 gap-4">
<NumberInput label="Tick Seconds" value={env.ducting.tick_seconds} onChange={(v) => up({ ducting: { ...env.ducting, tick_seconds: v } })} min={60} />
<NumberInput label="Latitude" value={env.ducting.latitude} onChange={(v) => up({ ducting: { ...env.ducting, latitude: v } })} step={0.01} />
<NumberInput label="Longitude" value={env.ducting.longitude} onChange={(v) => up({ ducting: { ...env.ducting, longitude: v } })} step={0.01} />
</div>)
case 'fires': return (
<div className="grid grid-cols-2 gap-4">
<NumberInput label="Tick Seconds" value={env.fires.tick_seconds} onChange={(v) => up({ fires: { ...env.fires, tick_seconds: v } })} min={60} />
<SelectInput label="State" value={env.fires.state} onChange={(v) => up({ fires: { ...env.fires, state: v } })} options={US_STATES} />
</div>)
case 'avalanche': return (<>
<NumberInput label="Tick Seconds" value={env.avalanche.tick_seconds} onChange={(v) => up({ avalanche: { ...env.avalanche, tick_seconds: v } })} min={60} />
<ListInput label="Center IDs" value={env.avalanche.center_ids} onChange={(v) => up({ avalanche: { ...env.avalanche, center_ids: v } })} helper="e.g., SNFAC" infoLink="https://avalanche.org/avalanche-centers/" />
<NumberListInput label="Season Months" value={env.avalanche.season_months} onChange={(v) => up({ avalanche: { ...env.avalanche, season_months: v } })} helper="e.g., 12, 1, 2, 3, 4" />
</>)
case 'usgs': return (<>
<NumberInput label="Tick Seconds" value={env.usgs.tick_seconds} onChange={(v) => up({ usgs: { ...env.usgs, tick_seconds: v } })} min={900} helper="Minimum 15 min (900s)" />
<ListInput label="Site IDs" value={env.usgs.sites} onChange={(v) => up({ usgs: { ...env.usgs, sites: v } })} helper="USGS gauge site numbers" infoLink="https://waterdata.usgs.gov/nwis" />
</>)
case 'usgs_quake': return (<>
<NumberInput label="Tick Seconds" value={env.usgs_quake.tick_seconds} onChange={(v) => up({ usgs_quake: { ...env.usgs_quake, tick_seconds: v } })} min={60} />
<NumberInput label="Min Magnitude" value={env.usgs_quake.min_magnitude} onChange={(v) => up({ usgs_quake: { ...env.usgs_quake, min_magnitude: v } })} step={0.1} min={0} />
<TextInput label="Region Tag" value={env.usgs_quake.region} onChange={(v) => up({ usgs_quake: { ...env.usgs_quake, region: v } })} />
<div className="grid grid-cols-4 gap-2">
{(['West', 'South', 'East', 'North'] as const).map((lbl, i) => (
<NumberInput key={lbl} label={lbl} value={env.usgs_quake.bbox?.[i] ?? 0} onChange={(v) => { const b = [...(env.usgs_quake.bbox || [0, 0, 0, 0])]; b[i] = v; up({ usgs_quake: { ...env.usgs_quake, bbox: b } }) }} step={0.01} />
))}
</div>
<div className="text-xs text-slate-500">Bounding box [W,S,E,N] geographic filter</div>
</>)
case 'traffic': return (<>
<TextInput label="API Key" value={env.traffic.api_key} onChange={(v) => up({ traffic: { ...env.traffic, api_key: v } })} type="password" helper="developer.tomtom.com" />
<NumberInput label="Tick Seconds" value={env.traffic.tick_seconds} onChange={(v) => up({ traffic: { ...env.traffic, tick_seconds: v } })} min={60} />
<div className="text-xs text-slate-500 mt-2">Corridors:</div>
{(env.traffic.corridors || []).map((c, i) => (
<div key={i} className="grid grid-cols-4 gap-2 items-end">
<TextInput label="Name" value={c.name} onChange={(v) => { const n = [...env.traffic.corridors]; n[i] = { ...c, name: v }; up({ traffic: { ...env.traffic, corridors: n } }) }} />
<NumberInput label="Lat" value={c.lat} onChange={(v) => { const n = [...env.traffic.corridors]; n[i] = { ...c, lat: v }; up({ traffic: { ...env.traffic, corridors: n } }) }} step={0.01} />
<NumberInput label="Lon" value={c.lon} onChange={(v) => { const n = [...env.traffic.corridors]; n[i] = { ...c, lon: v }; up({ traffic: { ...env.traffic, corridors: n } }) }} step={0.01} />
<button onClick={() => up({ traffic: { ...env.traffic, corridors: env.traffic.corridors.filter((_, j) => j !== i) } })} className="px-2 py-2 text-xs text-red-400 hover:text-red-300 border border-red-400/30 rounded">Remove</button>
</div>
))}
<button onClick={() => up({ traffic: { ...env.traffic, corridors: [...(env.traffic.corridors || []), { name: '', lat: 0, lon: 0 }] } })} className="text-xs text-accent hover:underline">+ Add Corridor</button>
</>)
case 'roads511': return (<>
<TextInput label="Base URL" value={env.roads511.base_url} onChange={(v) => up({ roads511: { ...env.roads511, base_url: v } })} placeholder="https://511.yourstate.gov/api/v2" />
<TextInput label="API Key" value={env.roads511.api_key} onChange={(v) => up({ roads511: { ...env.roads511, api_key: v } })} type="password" helper="Leave empty if not required" />
<NumberInput label="Tick Seconds" value={env.roads511.tick_seconds} onChange={(v) => up({ roads511: { ...env.roads511, tick_seconds: v } })} min={60} />
<ListInput label="Endpoints" value={env.roads511.endpoints} onChange={(v) => up({ roads511: { ...env.roads511, endpoints: v } })} helper="e.g., /get/event" />
<div className="grid grid-cols-4 gap-2">
{(['West', 'South', 'East', 'North'] as const).map((lbl, i) => (
<NumberInput key={lbl} label={lbl} value={env.roads511.bbox?.[i] ?? 0} onChange={(v) => { const b = [...(env.roads511.bbox || [0, 0, 0, 0])]; b[i] = v; up({ roads511: { ...env.roads511, bbox: b } }) }} step={0.01} />
))}
</div>
</>)
case 'firms': return (<>
<TextInput label="MAP Key" value={env.firms.map_key} onChange={(v) => up({ firms: { ...env.firms, map_key: v } })} type="password" helper="firms.modaps.eosdis.nasa.gov/api/area/" infoLink="https://firms.modaps.eosdis.nasa.gov/api/area/" />
<NumberInput label="Tick Seconds" value={env.firms.tick_seconds} onChange={(v) => up({ firms: { ...env.firms, tick_seconds: v } })} min={300} />
<SelectInput label="Satellite Source" value={env.firms.source} onChange={(v) => up({ firms: { ...env.firms, source: v } })} options={[{ value: 'VIIRS_SNPP_NRT', label: 'VIIRS SNPP (NRT)' }, { value: 'VIIRS_NOAA20_NRT', label: 'VIIRS NOAA-20 (NRT)' }, { value: 'MODIS_NRT', label: 'MODIS (NRT)' }]} />
<div className="grid grid-cols-3 gap-4">
<NumberInput label="Day Range" value={env.firms.day_range} onChange={(v) => up({ firms: { ...env.firms, day_range: v } })} min={1} max={10} />
<SelectInput label="Min Confidence" value={env.firms.confidence_min} onChange={(v) => up({ firms: { ...env.firms, confidence_min: v } })} options={[{ value: 'low', label: 'Low' }, { value: 'nominal', label: 'Nominal' }, { value: 'high', label: 'High' }]} />
<NumberInput label="Proximity (km)" value={env.firms.proximity_km} onChange={(v) => up({ firms: { ...env.firms, proximity_km: v } })} step={0.5} />
</div>
<div className="grid grid-cols-4 gap-2">
{(['West', 'South', 'East', 'North'] as const).map((lbl, i) => (
<NumberInput key={lbl} label={lbl} value={env.firms.bbox?.[i] ?? 0} onChange={(v) => { const b = [...(env.firms.bbox || [0, 0, 0, 0])]; b[i] = v; up({ firms: { ...env.firms, bbox: b } }) }} step={0.01} />
))}
</div>
</>)
}
}
const a = env as unknown as Record<AdapterKey, { enabled: boolean; feed_source?: FeedSource }>
const setAdapterField = (key: AdapterKey, patch: { enabled?: boolean; feed_source?: FeedSource }) => {
const cur = (env as any)[key] || {}
up({ [key]: { ...cur, ...patch } } as unknown as Partial<EnvConfig>)
}
return (
<div className="space-y-6">
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle Restructures the environmental config UI into the 7-family taxonomy on the /environment page (Matt-approved Option C: unified families = config + live status per adapter). The editable adapter config moves out of the Config page's "Environmental" tab (now deprecated) onto /environment, where each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings) together with its live status (feed health + active events). Frontend (dashboard-frontend/src): - pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family adapter sub-tab strip -> AdapterPanel. * AdapterPanel: header row = on/off Toggle + feed_source toggle (native|central). When OFF, feed_source + all settings grey out (disabled, not hidden). * Native-only adapters (ducting, avalanche, roads511 -- no Central stream per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central' disabled + a 'Central not available for this adapter' tooltip/label. * Missing-key adapters (firms, roads511) show an 'API key not configured -- contact admin' notice; toggles still operate. * Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes are planned for v0.5.'). Mesh Health = no env adapters; a disabled feed_source toggle with 'central' greyed for future migration. * All existing per-adapter settings preserved verbatim (NWS zones/user_agent/ severity, ducting lat/lon, fires state, avalanche centers/season, USGS sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS map_key/source/confidence/bbox). ALSO adds a usgs_quake panel (tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in the old GUI. feed_source field (C.1) now surfaced per adapter. * Per-adapter live status: reuses /api/env/status feed health + /api/env/active events (filtered by source; fires->nifc mapping). Refreshes every 30s. - pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS entry + render case + EnvironmentalSection function + unused Thermometer import); exported the shared form primitives (Toggle, TextInput, NumberInput, SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx. - Reuses the existing restart_required banner pattern. - Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js, index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle). Rule 17 / no backend change: config is wired to the existing schema-driven GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake fields ride the generic dataclass coercion). No backend edited this phase. Verification: (A) `npm run build` clean -- tsc strict + vite, only the pre-existing >500kB single-chunk advisory (not introduced here). (B) static committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell (index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards, RF Propagation, ADS-B, 'Central not available', 'API key not configured'). (D) GET /api/config/environmental returns all adapters with feed_source=native, usgs_quake present, central{enabled:false} -- toggles bind to real data; all native, nothing flipped. Rebuilt prod healthy. *** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 *** The save half of gate D fails: PUT /api/config/environmental returns {"detail":"could not determine a constructor for the tag '!include' ..."}. Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py) calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a loader lacking the !include constructor) instead of the multi-file-aware meshai/config_loader.py::save_section that exists for exactly the !include layout. This breaks ALL GUI config saves in prod (every section, not just environmental) and predates C.2 -- the old Config 'Environmental' tab had the same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind works; only persistence is blocked. Verified disk pristine (idempotent PUT errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup). FIX (one line, but prod-wide blast radius -> wants its own phase + verification): config_routes PUT should call config_loader.save_section(section, data, config_dir) instead of config.save_config(...). Recommend C.2.1 backend fix before C.3. C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save, since flipping feed_source from the GUI is the whole point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
{/* Header + master enable + save bar */}
<div className="flex items-center justify-between">
<h1 className="text-xl font-semibold text-slate-200">Environment</h1>
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle Restructures the environmental config UI into the 7-family taxonomy on the /environment page (Matt-approved Option C: unified families = config + live status per adapter). The editable adapter config moves out of the Config page's "Environmental" tab (now deprecated) onto /environment, where each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings) together with its live status (feed health + active events). Frontend (dashboard-frontend/src): - pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family adapter sub-tab strip -> AdapterPanel. * AdapterPanel: header row = on/off Toggle + feed_source toggle (native|central). When OFF, feed_source + all settings grey out (disabled, not hidden). * Native-only adapters (ducting, avalanche, roads511 -- no Central stream per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central' disabled + a 'Central not available for this adapter' tooltip/label. * Missing-key adapters (firms, roads511) show an 'API key not configured -- contact admin' notice; toggles still operate. * Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes are planned for v0.5.'). Mesh Health = no env adapters; a disabled feed_source toggle with 'central' greyed for future migration. * All existing per-adapter settings preserved verbatim (NWS zones/user_agent/ severity, ducting lat/lon, fires state, avalanche centers/season, USGS sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS map_key/source/confidence/bbox). ALSO adds a usgs_quake panel (tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in the old GUI. feed_source field (C.1) now surfaced per adapter. * Per-adapter live status: reuses /api/env/status feed health + /api/env/active events (filtered by source; fires->nifc mapping). Refreshes every 30s. - pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS entry + render case + EnvironmentalSection function + unused Thermometer import); exported the shared form primitives (Toggle, TextInput, NumberInput, SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx. - Reuses the existing restart_required banner pattern. - Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js, index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle). Rule 17 / no backend change: config is wired to the existing schema-driven GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake fields ride the generic dataclass coercion). No backend edited this phase. Verification: (A) `npm run build` clean -- tsc strict + vite, only the pre-existing >500kB single-chunk advisory (not introduced here). (B) static committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell (index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards, RF Propagation, ADS-B, 'Central not available', 'API key not configured'). (D) GET /api/config/environmental returns all adapters with feed_source=native, usgs_quake present, central{enabled:false} -- toggles bind to real data; all native, nothing flipped. Rebuilt prod healthy. *** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 *** The save half of gate D fails: PUT /api/config/environmental returns {"detail":"could not determine a constructor for the tag '!include' ..."}. Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py) calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a loader lacking the !include constructor) instead of the multi-file-aware meshai/config_loader.py::save_section that exists for exactly the !include layout. This breaks ALL GUI config saves in prod (every section, not just environmental) and predates C.2 -- the old Config 'Environmental' tab had the same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind works; only persistence is blocked. Verified disk pristine (idempotent PUT errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup). FIX (one line, but prod-wide blast radius -> wants its own phase + verification): config_routes PUT should call config_loader.save_section(section, data, config_dir) instead of config.save_config(...). Recommend C.2.1 backend fix before C.3. C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save, since flipping feed_source from the GUI is the whole point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
<div className="flex items-center gap-3">
<Toggle label="Feeds Enabled" checked={env.enabled} onChange={(v) => up({ enabled: v })} />
{hasChanges && (
<>
<button onClick={discard} className="flex items-center gap-1 px-3 py-1.5 text-sm text-slate-400 hover:text-slate-200 border border-border rounded">
<RotateCcw size={14} /> Discard
</button>
<button onClick={save} disabled={saving} className="flex items-center gap-1 px-3 py-1.5 text-sm bg-accent text-white rounded disabled:opacity-50">
<Save size={14} /> {saving ? 'Saving…' : 'Save'}
</button>
</>
)}
</div>
</div>
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle Restructures the environmental config UI into the 7-family taxonomy on the /environment page (Matt-approved Option C: unified families = config + live status per adapter). The editable adapter config moves out of the Config page's "Environmental" tab (now deprecated) onto /environment, where each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings) together with its live status (feed health + active events). Frontend (dashboard-frontend/src): - pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family adapter sub-tab strip -> AdapterPanel. * AdapterPanel: header row = on/off Toggle + feed_source toggle (native|central). When OFF, feed_source + all settings grey out (disabled, not hidden). * Native-only adapters (ducting, avalanche, roads511 -- no Central stream per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central' disabled + a 'Central not available for this adapter' tooltip/label. * Missing-key adapters (firms, roads511) show an 'API key not configured -- contact admin' notice; toggles still operate. * Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes are planned for v0.5.'). Mesh Health = no env adapters; a disabled feed_source toggle with 'central' greyed for future migration. * All existing per-adapter settings preserved verbatim (NWS zones/user_agent/ severity, ducting lat/lon, fires state, avalanche centers/season, USGS sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS map_key/source/confidence/bbox). ALSO adds a usgs_quake panel (tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in the old GUI. feed_source field (C.1) now surfaced per adapter. * Per-adapter live status: reuses /api/env/status feed health + /api/env/active events (filtered by source; fires->nifc mapping). Refreshes every 30s. - pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS entry + render case + EnvironmentalSection function + unused Thermometer import); exported the shared form primitives (Toggle, TextInput, NumberInput, SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx. - Reuses the existing restart_required banner pattern. - Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js, index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle). Rule 17 / no backend change: config is wired to the existing schema-driven GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake fields ride the generic dataclass coercion). No backend edited this phase. Verification: (A) `npm run build` clean -- tsc strict + vite, only the pre-existing >500kB single-chunk advisory (not introduced here). (B) static committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell (index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards, RF Propagation, ADS-B, 'Central not available', 'API key not configured'). (D) GET /api/config/environmental returns all adapters with feed_source=native, usgs_quake present, central{enabled:false} -- toggles bind to real data; all native, nothing flipped. Rebuilt prod healthy. *** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 *** The save half of gate D fails: PUT /api/config/environmental returns {"detail":"could not determine a constructor for the tag '!include' ..."}. Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py) calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a loader lacking the !include constructor) instead of the multi-file-aware meshai/config_loader.py::save_section that exists for exactly the !include layout. This breaks ALL GUI config saves in prod (every section, not just environmental) and predates C.2 -- the old Config 'Environmental' tab had the same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind works; only persistence is blocked. Verified disk pristine (idempotent PUT errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup). FIX (one line, but prod-wide blast radius -> wants its own phase + verification): config_routes PUT should call config_loader.save_section(section, data, config_dir) instead of config.save_config(...). Recommend C.2.1 backend fix before C.3. C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save, since flipping feed_source from the GUI is the whole point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
{error && <div className="text-sm text-red-400 bg-red-500/10 rounded p-3">{error}</div>}
{success && <div className="text-sm text-green-400 bg-green-500/10 rounded p-3">{success}</div>}
{restartRequired && (
<div className="flex items-center justify-between text-sm text-amber-400 bg-amber-500/10 border border-amber-500/30 rounded p-3">
<span className="flex items-center gap-2"><RefreshCw size={14} /> A restart is required for some changes to take effect.</span>
<button onClick={restart} className="px-3 py-1 bg-amber-500/20 hover:bg-amber-500/30 rounded">Restart now</button>
</div>
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle Restructures the environmental config UI into the 7-family taxonomy on the /environment page (Matt-approved Option C: unified families = config + live status per adapter). The editable adapter config moves out of the Config page's "Environmental" tab (now deprecated) onto /environment, where each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings) together with its live status (feed health + active events). Frontend (dashboard-frontend/src): - pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family adapter sub-tab strip -> AdapterPanel. * AdapterPanel: header row = on/off Toggle + feed_source toggle (native|central). When OFF, feed_source + all settings grey out (disabled, not hidden). * Native-only adapters (ducting, avalanche, roads511 -- no Central stream per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central' disabled + a 'Central not available for this adapter' tooltip/label. * Missing-key adapters (firms, roads511) show an 'API key not configured -- contact admin' notice; toggles still operate. * Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes are planned for v0.5.'). Mesh Health = no env adapters; a disabled feed_source toggle with 'central' greyed for future migration. * All existing per-adapter settings preserved verbatim (NWS zones/user_agent/ severity, ducting lat/lon, fires state, avalanche centers/season, USGS sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS map_key/source/confidence/bbox). ALSO adds a usgs_quake panel (tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in the old GUI. feed_source field (C.1) now surfaced per adapter. * Per-adapter live status: reuses /api/env/status feed health + /api/env/active events (filtered by source; fires->nifc mapping). Refreshes every 30s. - pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS entry + render case + EnvironmentalSection function + unused Thermometer import); exported the shared form primitives (Toggle, TextInput, NumberInput, SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx. - Reuses the existing restart_required banner pattern. - Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js, index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle). Rule 17 / no backend change: config is wired to the existing schema-driven GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake fields ride the generic dataclass coercion). No backend edited this phase. Verification: (A) `npm run build` clean -- tsc strict + vite, only the pre-existing >500kB single-chunk advisory (not introduced here). (B) static committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell (index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards, RF Propagation, ADS-B, 'Central not available', 'API key not configured'). (D) GET /api/config/environmental returns all adapters with feed_source=native, usgs_quake present, central{enabled:false} -- toggles bind to real data; all native, nothing flipped. Rebuilt prod healthy. *** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 *** The save half of gate D fails: PUT /api/config/environmental returns {"detail":"could not determine a constructor for the tag '!include' ..."}. Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py) calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a loader lacking the !include constructor) instead of the multi-file-aware meshai/config_loader.py::save_section that exists for exactly the !include layout. This breaks ALL GUI config saves in prod (every section, not just environmental) and predates C.2 -- the old Config 'Environmental' tab had the same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind works; only persistence is blocked. Verified disk pristine (idempotent PUT errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup). FIX (one line, but prod-wide blast radius -> wants its own phase + verification): config_routes PUT should call config_loader.save_section(section, data, config_dir) instead of config.save_config(...). Recommend C.2.1 backend fix before C.3. C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save, since flipping feed_source from the GUI is the whole point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
)}
feat(notifications): v0.5.0 -- Master Toggles UX redesign + Central Connection GUI + grouped categories + region scoping Per-family notification policy (PagerDuty/Grafana-style): each family gets a severity threshold + region scope + a severity->channel routing matrix, so an operator opts in per family rather than hand-writing rules. SECTION 1 -- BACKEND - config.py: new NotificationToggle dataclass (enabled, min_severity, regions, severity_channels{severity->[channel types]}, quiet_hours_override, + per-channel delivery config: broadcast_channel/node_ids/smtp_*/recipients/webhook_*). notifications.toggles is now a dict[family]->NotificationToggle with 8 family defaults (mesh_health, weather, fire, rf_propagation, roads, avalanche, seismic, tracking), all enabled=false (opt-in), min_severity=priority, severity_channels={priority:[mesh_broadcast], immediate:[mesh_broadcast, mesh_dm]}, quiet_hours_override=true. (Old TogglesConfig.enabled was only read by build_pipeline via getattr -> degrades to ToggleFilter no-op, so the pipeline filter is unchanged; toggles now drive the Dispatcher instead.) - region_scope:list added to NotificationRuleConfig; _matching_rules filters by event.region/regions ([] = all). - Dispatcher: _dispatch_toggles runs IN PARALLEL to rule matching -- looks up get_toggle(event.category), checks enabled + region scope + severity threshold, then for each channel in severity_channels[event.severity] builds a synthetic rule (override_quiet set only for immediate when quiet_hours_override) and delivers. 'digest' channel is skipped in live dispatch (handled by accumulator). - categories.py: get_toggle() prefix fallback maps the live phases-2.7-2.14 categories (weather_warning, wildfire_incident, earthquake_event, traffic_congestion, geomagnetic/rf_*, stream_*, ...) to their family, fixing the v0.4 "category -> other" gap. - config_loader.py: SECRET_FIELDS += notifications.toggles.*.smtp_password. - _dataclass_to_dict now recurses dict-of-dataclasses, and the loader coerces the toggles dict -> NotificationToggle on both the full-load and section-PUT paths (so GUI save round-trips correctly). - tests/test_notification_toggles.py (11): enabled/disabled, region filter (empty+populated+regions-list), severity threshold, per-severity channel routing, digest-skipped-live, quiet-hours-override immediate-only, category->family, rules+toggles both fire. Full suite: 294 passed (283 + 11). SECTION 2 -- FRONTEND - Notifications.tsx: MasterToggles component above the rules section -- 8 family cards (icon + enable toggle; collapsed summary 'OFF' or 'N regions, M channels at <sev>+'; expanded: severity threshold, severity x channel checkbox matrix, region list, quiet-hours-override toggle, per-channel config: broadcast_channel/DM node IDs/recipients/SMTP host+port/webhook URL). - Environment.tsx: CentralConnectionPanel above the family tabs (url, durable, enabled) wired to environmental.central. - npm run build clean (tsc strict); rebuilt static committed (index-CfYlhn4e.js). SECTION 3 -- VERIFICATION - py_compile + tsc strict clean; pytest 294 passed. - Rebuilt prod: /notifications serves Master Toggles, /environment serves Central Connection (strings confirmed in the served bundle); 8 adapters, pipeline started, no tracebacks, healthy. - GUI round-trip: enable weather toggle (min_severity=priority, regions=[Magic Valley], severity_channels.priority=[mesh_broadcast]) -> PUT {saved:true} -> notifications.yaml reflects it; env_feeds traffic.api_key stayed ${TOMTOM_API_KEY} (C.3.1 secret preservation holds). Restored to clean opt-in baseline. - Synthetic NWS weather_warning/priority/Magic Valley -> routes through the weather toggle to mesh_broadcast; out-of-region and below-threshold events correctly dropped. DEFERRED (noted for a follow-up, not blocking Matt's morning config): Section 2B rules-editor polish -- grouped-by-family category checkboxes, region_scope multi-select in the rule editor (backend field + filtering ARE in), tooltips, and the fire-count Active/No-activity badge -- were not built tonight to keep the build shippable and verified; the Advanced Rules section is otherwise unchanged and still functional. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 07:00:10 +00:00
{/* Central Connection (v0.5) -- NATS source for adapters set to central */}
{env.central && (
<div className="border border-[#1e2a3a] rounded-lg p-4 space-y-3">
<div className="flex items-center justify-between">
<div>
<span className="text-sm font-medium text-slate-300">Central Connection</span>
<p className="text-xs text-slate-600">NATS JetStream source for any adapter set to "central"</p>
</div>
<Toggle label="" checked={!!env.central.enabled}
onChange={(v) => up({ central: { ...env.central!, enabled: v } })} />
</div>
<div className={env.central.enabled ? 'space-y-3' : 'space-y-3 opacity-40 pointer-events-none select-none'}>
<TextInput label="URL" value={env.central.url || ''}
onChange={(v) => up({ central: { ...env.central!, url: v } })}
placeholder="nats://central.echo6.mesh:4222" />
<TextInput label="Durable" value={env.central.durable || ''}
onChange={(v) => up({ central: { ...env.central!, durable: v } })}
placeholder="meshai-v04" />
</div>
</div>
)}
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle Restructures the environmental config UI into the 7-family taxonomy on the /environment page (Matt-approved Option C: unified families = config + live status per adapter). The editable adapter config moves out of the Config page's "Environmental" tab (now deprecated) onto /environment, where each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings) together with its live status (feed health + active events). Frontend (dashboard-frontend/src): - pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family adapter sub-tab strip -> AdapterPanel. * AdapterPanel: header row = on/off Toggle + feed_source toggle (native|central). When OFF, feed_source + all settings grey out (disabled, not hidden). * Native-only adapters (ducting, avalanche, roads511 -- no Central stream per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central' disabled + a 'Central not available for this adapter' tooltip/label. * Missing-key adapters (firms, roads511) show an 'API key not configured -- contact admin' notice; toggles still operate. * Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes are planned for v0.5.'). Mesh Health = no env adapters; a disabled feed_source toggle with 'central' greyed for future migration. * All existing per-adapter settings preserved verbatim (NWS zones/user_agent/ severity, ducting lat/lon, fires state, avalanche centers/season, USGS sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS map_key/source/confidence/bbox). ALSO adds a usgs_quake panel (tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in the old GUI. feed_source field (C.1) now surfaced per adapter. * Per-adapter live status: reuses /api/env/status feed health + /api/env/active events (filtered by source; fires->nifc mapping). Refreshes every 30s. - pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS entry + render case + EnvironmentalSection function + unused Thermometer import); exported the shared form primitives (Toggle, TextInput, NumberInput, SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx. - Reuses the existing restart_required banner pattern. - Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js, index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle). Rule 17 / no backend change: config is wired to the existing schema-driven GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake fields ride the generic dataclass coercion). No backend edited this phase. Verification: (A) `npm run build` clean -- tsc strict + vite, only the pre-existing >500kB single-chunk advisory (not introduced here). (B) static committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell (index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards, RF Propagation, ADS-B, 'Central not available', 'API key not configured'). (D) GET /api/config/environmental returns all adapters with feed_source=native, usgs_quake present, central{enabled:false} -- toggles bind to real data; all native, nothing flipped. Rebuilt prod healthy. *** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 *** The save half of gate D fails: PUT /api/config/environmental returns {"detail":"could not determine a constructor for the tag '!include' ..."}. Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py) calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a loader lacking the !include constructor) instead of the multi-file-aware meshai/config_loader.py::save_section that exists for exactly the !include layout. This breaks ALL GUI config saves in prod (every section, not just environmental) and predates C.2 -- the old Config 'Environmental' tab had the same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind works; only persistence is blocked. Verified disk pristine (idempotent PUT errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup). FIX (one line, but prod-wide blast radius -> wants its own phase + verification): config_routes PUT should call config_loader.save_section(section, data, config_dir) instead of config.save_config(...). Recommend C.2.1 backend fix before C.3. C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save, since flipping feed_source from the GUI is the whole point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
{/* Family tabs */}
<div className="flex gap-1 border-b border-border overflow-x-auto">
{FAMILIES.map(({ key, label, icon: Icon }) => (
<button key={key} onClick={() => { setFamily(key); const f = FAMILIES.find((x) => x.key === key)!; setAdapter(f.adapters[0] ?? null) }}
className={`flex items-center gap-2 px-4 py-2 text-sm whitespace-nowrap border-b-2 -mb-px transition-colors ${family === key ? 'border-accent text-accent' : 'border-transparent text-slate-400 hover:text-slate-200'}`}>
<Icon size={15} /> {label}
</button>
))}
</div>
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle Restructures the environmental config UI into the 7-family taxonomy on the /environment page (Matt-approved Option C: unified families = config + live status per adapter). The editable adapter config moves out of the Config page's "Environmental" tab (now deprecated) onto /environment, where each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings) together with its live status (feed health + active events). Frontend (dashboard-frontend/src): - pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family adapter sub-tab strip -> AdapterPanel. * AdapterPanel: header row = on/off Toggle + feed_source toggle (native|central). When OFF, feed_source + all settings grey out (disabled, not hidden). * Native-only adapters (ducting, avalanche, roads511 -- no Central stream per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central' disabled + a 'Central not available for this adapter' tooltip/label. * Missing-key adapters (firms, roads511) show an 'API key not configured -- contact admin' notice; toggles still operate. * Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes are planned for v0.5.'). Mesh Health = no env adapters; a disabled feed_source toggle with 'central' greyed for future migration. * All existing per-adapter settings preserved verbatim (NWS zones/user_agent/ severity, ducting lat/lon, fires state, avalanche centers/season, USGS sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS map_key/source/confidence/bbox). ALSO adds a usgs_quake panel (tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in the old GUI. feed_source field (C.1) now surfaced per adapter. * Per-adapter live status: reuses /api/env/status feed health + /api/env/active events (filtered by source; fires->nifc mapping). Refreshes every 30s. - pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS entry + render case + EnvironmentalSection function + unused Thermometer import); exported the shared form primitives (Toggle, TextInput, NumberInput, SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx. - Reuses the existing restart_required banner pattern. - Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js, index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle). Rule 17 / no backend change: config is wired to the existing schema-driven GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake fields ride the generic dataclass coercion). No backend edited this phase. Verification: (A) `npm run build` clean -- tsc strict + vite, only the pre-existing >500kB single-chunk advisory (not introduced here). (B) static committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell (index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards, RF Propagation, ADS-B, 'Central not available', 'API key not configured'). (D) GET /api/config/environmental returns all adapters with feed_source=native, usgs_quake present, central{enabled:false} -- toggles bind to real data; all native, nothing flipped. Rebuilt prod healthy. *** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 *** The save half of gate D fails: PUT /api/config/environmental returns {"detail":"could not determine a constructor for the tag '!include' ..."}. Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py) calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a loader lacking the !include constructor) instead of the multi-file-aware meshai/config_loader.py::save_section that exists for exactly the !include layout. This breaks ALL GUI config saves in prod (every section, not just environmental) and predates C.2 -- the old Config 'Environmental' tab had the same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind works; only persistence is blocked. Verified disk pristine (idempotent PUT errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup). FIX (one line, but prod-wide blast radius -> wants its own phase + verification): config_routes PUT should call config_loader.save_section(section, data, config_dir) instead of config.save_config(...). Recommend C.2.1 backend fix before C.3. C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save, since flipping feed_source from the GUI is the whole point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
{/* Tracking placeholder */}
{family === 'tracking' && (
<div className="flex flex-col items-center justify-center h-[40vh] text-center">
<Satellite size={32} className="text-slate-600 mb-4" />
<p className="text-slate-500 max-w-md">No adapters yet. ADS-B / AIS / satellite passes are planned for v0.5.</p>
</div>
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle Restructures the environmental config UI into the 7-family taxonomy on the /environment page (Matt-approved Option C: unified families = config + live status per adapter). The editable adapter config moves out of the Config page's "Environmental" tab (now deprecated) onto /environment, where each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings) together with its live status (feed health + active events). Frontend (dashboard-frontend/src): - pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family adapter sub-tab strip -> AdapterPanel. * AdapterPanel: header row = on/off Toggle + feed_source toggle (native|central). When OFF, feed_source + all settings grey out (disabled, not hidden). * Native-only adapters (ducting, avalanche, roads511 -- no Central stream per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central' disabled + a 'Central not available for this adapter' tooltip/label. * Missing-key adapters (firms, roads511) show an 'API key not configured -- contact admin' notice; toggles still operate. * Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes are planned for v0.5.'). Mesh Health = no env adapters; a disabled feed_source toggle with 'central' greyed for future migration. * All existing per-adapter settings preserved verbatim (NWS zones/user_agent/ severity, ducting lat/lon, fires state, avalanche centers/season, USGS sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS map_key/source/confidence/bbox). ALSO adds a usgs_quake panel (tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in the old GUI. feed_source field (C.1) now surfaced per adapter. * Per-adapter live status: reuses /api/env/status feed health + /api/env/active events (filtered by source; fires->nifc mapping). Refreshes every 30s. - pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS entry + render case + EnvironmentalSection function + unused Thermometer import); exported the shared form primitives (Toggle, TextInput, NumberInput, SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx. - Reuses the existing restart_required banner pattern. - Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js, index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle). Rule 17 / no backend change: config is wired to the existing schema-driven GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake fields ride the generic dataclass coercion). No backend edited this phase. Verification: (A) `npm run build` clean -- tsc strict + vite, only the pre-existing >500kB single-chunk advisory (not introduced here). (B) static committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell (index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards, RF Propagation, ADS-B, 'Central not available', 'API key not configured'). (D) GET /api/config/environmental returns all adapters with feed_source=native, usgs_quake present, central{enabled:false} -- toggles bind to real data; all native, nothing flipped. Rebuilt prod healthy. *** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 *** The save half of gate D fails: PUT /api/config/environmental returns {"detail":"could not determine a constructor for the tag '!include' ..."}. Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py) calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a loader lacking the !include constructor) instead of the multi-file-aware meshai/config_loader.py::save_section that exists for exactly the !include layout. This breaks ALL GUI config saves in prod (every section, not just environmental) and predates C.2 -- the old Config 'Environmental' tab had the same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind works; only persistence is blocked. Verified disk pristine (idempotent PUT errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup). FIX (one line, but prod-wide blast radius -> wants its own phase + verification): config_routes PUT should call config_loader.save_section(section, data, config_dir) instead of config.save_config(...). Recommend C.2.1 backend fix before C.3. C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save, since flipping feed_source from the GUI is the whole point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
)}
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle Restructures the environmental config UI into the 7-family taxonomy on the /environment page (Matt-approved Option C: unified families = config + live status per adapter). The editable adapter config moves out of the Config page's "Environmental" tab (now deprecated) onto /environment, where each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings) together with its live status (feed health + active events). Frontend (dashboard-frontend/src): - pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family adapter sub-tab strip -> AdapterPanel. * AdapterPanel: header row = on/off Toggle + feed_source toggle (native|central). When OFF, feed_source + all settings grey out (disabled, not hidden). * Native-only adapters (ducting, avalanche, roads511 -- no Central stream per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central' disabled + a 'Central not available for this adapter' tooltip/label. * Missing-key adapters (firms, roads511) show an 'API key not configured -- contact admin' notice; toggles still operate. * Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes are planned for v0.5.'). Mesh Health = no env adapters; a disabled feed_source toggle with 'central' greyed for future migration. * All existing per-adapter settings preserved verbatim (NWS zones/user_agent/ severity, ducting lat/lon, fires state, avalanche centers/season, USGS sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS map_key/source/confidence/bbox). ALSO adds a usgs_quake panel (tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in the old GUI. feed_source field (C.1) now surfaced per adapter. * Per-adapter live status: reuses /api/env/status feed health + /api/env/active events (filtered by source; fires->nifc mapping). Refreshes every 30s. - pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS entry + render case + EnvironmentalSection function + unused Thermometer import); exported the shared form primitives (Toggle, TextInput, NumberInput, SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx. - Reuses the existing restart_required banner pattern. - Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js, index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle). Rule 17 / no backend change: config is wired to the existing schema-driven GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake fields ride the generic dataclass coercion). No backend edited this phase. Verification: (A) `npm run build` clean -- tsc strict + vite, only the pre-existing >500kB single-chunk advisory (not introduced here). (B) static committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell (index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards, RF Propagation, ADS-B, 'Central not available', 'API key not configured'). (D) GET /api/config/environmental returns all adapters with feed_source=native, usgs_quake present, central{enabled:false} -- toggles bind to real data; all native, nothing flipped. Rebuilt prod healthy. *** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 *** The save half of gate D fails: PUT /api/config/environmental returns {"detail":"could not determine a constructor for the tag '!include' ..."}. Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py) calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a loader lacking the !include constructor) instead of the multi-file-aware meshai/config_loader.py::save_section that exists for exactly the !include layout. This breaks ALL GUI config saves in prod (every section, not just environmental) and predates C.2 -- the old Config 'Environmental' tab had the same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind works; only persistence is blocked. Verified disk pristine (idempotent PUT errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup). FIX (one line, but prod-wide blast radius -> wants its own phase + verification): config_routes PUT should call config_loader.save_section(section, data, config_dir) instead of config.save_config(...). Recommend C.2.1 backend fix before C.3. C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save, since flipping feed_source from the GUI is the whole point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
{/* Mesh Health (no env adapters; central greyed for future migration) */}
{family === 'mesh' && (
<div className="border border-[#1e2a3a] rounded-lg p-4 space-y-3">
<div className="flex items-center justify-between">
<div>
<span className="text-sm font-medium text-slate-300">Mesh Health</span>
<p className="text-xs text-slate-600">Node/infra telemetry sourced from the mesh, not an environmental feed.</p>
</div>
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle Restructures the environmental config UI into the 7-family taxonomy on the /environment page (Matt-approved Option C: unified families = config + live status per adapter). The editable adapter config moves out of the Config page's "Environmental" tab (now deprecated) onto /environment, where each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings) together with its live status (feed health + active events). Frontend (dashboard-frontend/src): - pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family adapter sub-tab strip -> AdapterPanel. * AdapterPanel: header row = on/off Toggle + feed_source toggle (native|central). When OFF, feed_source + all settings grey out (disabled, not hidden). * Native-only adapters (ducting, avalanche, roads511 -- no Central stream per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central' disabled + a 'Central not available for this adapter' tooltip/label. * Missing-key adapters (firms, roads511) show an 'API key not configured -- contact admin' notice; toggles still operate. * Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes are planned for v0.5.'). Mesh Health = no env adapters; a disabled feed_source toggle with 'central' greyed for future migration. * All existing per-adapter settings preserved verbatim (NWS zones/user_agent/ severity, ducting lat/lon, fires state, avalanche centers/season, USGS sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS map_key/source/confidence/bbox). ALSO adds a usgs_quake panel (tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in the old GUI. feed_source field (C.1) now surfaced per adapter. * Per-adapter live status: reuses /api/env/status feed health + /api/env/active events (filtered by source; fires->nifc mapping). Refreshes every 30s. - pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS entry + render case + EnvironmentalSection function + unused Thermometer import); exported the shared form primitives (Toggle, TextInput, NumberInput, SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx. - Reuses the existing restart_required banner pattern. - Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js, index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle). Rule 17 / no backend change: config is wired to the existing schema-driven GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake fields ride the generic dataclass coercion). No backend edited this phase. Verification: (A) `npm run build` clean -- tsc strict + vite, only the pre-existing >500kB single-chunk advisory (not introduced here). (B) static committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell (index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards, RF Propagation, ADS-B, 'Central not available', 'API key not configured'). (D) GET /api/config/environmental returns all adapters with feed_source=native, usgs_quake present, central{enabled:false} -- toggles bind to real data; all native, nothing flipped. Rebuilt prod healthy. *** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 *** The save half of gate D fails: PUT /api/config/environmental returns {"detail":"could not determine a constructor for the tag '!include' ..."}. Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py) calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a loader lacking the !include constructor) instead of the multi-file-aware meshai/config_loader.py::save_section that exists for exactly the !include layout. This breaks ALL GUI config saves in prod (every section, not just environmental) and predates C.2 -- the old Config 'Environmental' tab had the same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind works; only persistence is blocked. Verified disk pristine (idempotent PUT errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup). FIX (one line, but prod-wide blast radius -> wants its own phase + verification): config_routes PUT should call config_loader.save_section(section, data, config_dir) instead of config.save_config(...). Recommend C.2.1 backend fix before C.3. C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save, since flipping feed_source from the GUI is the whole point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
<div className="flex items-center gap-1">
<span className="text-[10px] uppercase tracking-wide text-slate-600">source</span>
<FeedSourceToggle value="native" onChange={() => {}} disabled={false} centralDisabled={true} />
</div>
</div>
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle Restructures the environmental config UI into the 7-family taxonomy on the /environment page (Matt-approved Option C: unified families = config + live status per adapter). The editable adapter config moves out of the Config page's "Environmental" tab (now deprecated) onto /environment, where each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings) together with its live status (feed health + active events). Frontend (dashboard-frontend/src): - pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family adapter sub-tab strip -> AdapterPanel. * AdapterPanel: header row = on/off Toggle + feed_source toggle (native|central). When OFF, feed_source + all settings grey out (disabled, not hidden). * Native-only adapters (ducting, avalanche, roads511 -- no Central stream per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central' disabled + a 'Central not available for this adapter' tooltip/label. * Missing-key adapters (firms, roads511) show an 'API key not configured -- contact admin' notice; toggles still operate. * Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes are planned for v0.5.'). Mesh Health = no env adapters; a disabled feed_source toggle with 'central' greyed for future migration. * All existing per-adapter settings preserved verbatim (NWS zones/user_agent/ severity, ducting lat/lon, fires state, avalanche centers/season, USGS sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS map_key/source/confidence/bbox). ALSO adds a usgs_quake panel (tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in the old GUI. feed_source field (C.1) now surfaced per adapter. * Per-adapter live status: reuses /api/env/status feed health + /api/env/active events (filtered by source; fires->nifc mapping). Refreshes every 30s. - pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS entry + render case + EnvironmentalSection function + unused Thermometer import); exported the shared form primitives (Toggle, TextInput, NumberInput, SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx. - Reuses the existing restart_required banner pattern. - Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js, index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle). Rule 17 / no backend change: config is wired to the existing schema-driven GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake fields ride the generic dataclass coercion). No backend edited this phase. Verification: (A) `npm run build` clean -- tsc strict + vite, only the pre-existing >500kB single-chunk advisory (not introduced here). (B) static committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell (index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards, RF Propagation, ADS-B, 'Central not available', 'API key not configured'). (D) GET /api/config/environmental returns all adapters with feed_source=native, usgs_quake present, central{enabled:false} -- toggles bind to real data; all native, nothing flipped. Rebuilt prod healthy. *** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 *** The save half of gate D fails: PUT /api/config/environmental returns {"detail":"could not determine a constructor for the tag '!include' ..."}. Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py) calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a loader lacking the !include constructor) instead of the multi-file-aware meshai/config_loader.py::save_section that exists for exactly the !include layout. This breaks ALL GUI config saves in prod (every section, not just environmental) and predates C.2 -- the old Config 'Environmental' tab had the same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind works; only persistence is blocked. Verified disk pristine (idempotent PUT errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup). FIX (one line, but prod-wide blast radius -> wants its own phase + verification): config_routes PUT should call config_loader.save_section(section, data, config_dir) instead of config.save_config(...). Recommend C.2.1 backend fix before C.3. C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save, since flipping feed_source from the GUI is the whole point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
<div className="text-[11px] text-slate-600">Central not available reserved for a future migration.</div>
</div>
)}
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle Restructures the environmental config UI into the 7-family taxonomy on the /environment page (Matt-approved Option C: unified families = config + live status per adapter). The editable adapter config moves out of the Config page's "Environmental" tab (now deprecated) onto /environment, where each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings) together with its live status (feed health + active events). Frontend (dashboard-frontend/src): - pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family adapter sub-tab strip -> AdapterPanel. * AdapterPanel: header row = on/off Toggle + feed_source toggle (native|central). When OFF, feed_source + all settings grey out (disabled, not hidden). * Native-only adapters (ducting, avalanche, roads511 -- no Central stream per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central' disabled + a 'Central not available for this adapter' tooltip/label. * Missing-key adapters (firms, roads511) show an 'API key not configured -- contact admin' notice; toggles still operate. * Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes are planned for v0.5.'). Mesh Health = no env adapters; a disabled feed_source toggle with 'central' greyed for future migration. * All existing per-adapter settings preserved verbatim (NWS zones/user_agent/ severity, ducting lat/lon, fires state, avalanche centers/season, USGS sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS map_key/source/confidence/bbox). ALSO adds a usgs_quake panel (tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in the old GUI. feed_source field (C.1) now surfaced per adapter. * Per-adapter live status: reuses /api/env/status feed health + /api/env/active events (filtered by source; fires->nifc mapping). Refreshes every 30s. - pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS entry + render case + EnvironmentalSection function + unused Thermometer import); exported the shared form primitives (Toggle, TextInput, NumberInput, SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx. - Reuses the existing restart_required banner pattern. - Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js, index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle). Rule 17 / no backend change: config is wired to the existing schema-driven GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake fields ride the generic dataclass coercion). No backend edited this phase. Verification: (A) `npm run build` clean -- tsc strict + vite, only the pre-existing >500kB single-chunk advisory (not introduced here). (B) static committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell (index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards, RF Propagation, ADS-B, 'Central not available', 'API key not configured'). (D) GET /api/config/environmental returns all adapters with feed_source=native, usgs_quake present, central{enabled:false} -- toggles bind to real data; all native, nothing flipped. Rebuilt prod healthy. *** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 *** The save half of gate D fails: PUT /api/config/environmental returns {"detail":"could not determine a constructor for the tag '!include' ..."}. Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py) calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a loader lacking the !include constructor) instead of the multi-file-aware meshai/config_loader.py::save_section that exists for exactly the !include layout. This breaks ALL GUI config saves in prod (every section, not just environmental) and predates C.2 -- the old Config 'Environmental' tab had the same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind works; only persistence is blocked. Verified disk pristine (idempotent PUT errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup). FIX (one line, but prod-wide blast radius -> wants its own phase + verification): config_routes PUT should call config_loader.save_section(section, data, config_dir) instead of config.save_config(...). Recommend C.2.1 backend fix before C.3. C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save, since flipping feed_source from the GUI is the whole point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
{/* Adapter sub-tabs + panel */}
{fam.adapters.length > 0 && activeAdapter && (
<>
{fam.adapters.length > 1 && (
<div className="flex gap-1">
{fam.adapters.map((k) => (
<button key={k} onClick={() => setAdapter(k)}
className={`px-3 py-1.5 text-sm rounded ${activeAdapter === k ? 'bg-bg-hover text-slate-100' : 'text-slate-400 hover:text-slate-200'}`}>
{META[k].label}
</button>
))}
</div>
)}
feat(dashboard): v0.4 C.2 family-tab restructure -- 7 families x per-adapter feed_source toggle Restructures the environmental config UI into the 7-family taxonomy on the /environment page (Matt-approved Option C: unified families = config + live status per adapter). The editable adapter config moves out of the Config page's "Environmental" tab (now deprecated) onto /environment, where each adapter sub-tab shows its AdapterPanel (on/off + feed_source + settings) together with its live status (feed health + active events). Frontend (dashboard-frontend/src): - pages/Environment.tsx rewritten: 7 family tabs (Weather, Fire, RF Propagation, Roads, Geohazards, Tracking, Mesh Health) -> per-family adapter sub-tab strip -> AdapterPanel. * AdapterPanel: header row = on/off Toggle + feed_source toggle (native|central). When OFF, feed_source + all settings grey out (disabled, not hidden). * Native-only adapters (ducting, avalanche, roads511 -- no Central stream per C.1's ADAPTER_SUBJECTS) show the feed_source toggle with 'central' disabled + a 'Central not available for this adapter' tooltip/label. * Missing-key adapters (firms, roads511) show an 'API key not configured -- contact admin' notice; toggles still operate. * Tracking = placeholder ('No adapters yet. ADS-B / AIS / satellite passes are planned for v0.5.'). Mesh Health = no env adapters; a disabled feed_source toggle with 'central' greyed for future migration. * All existing per-adapter settings preserved verbatim (NWS zones/user_agent/ severity, ducting lat/lon, fires state, avalanche centers/season, USGS sites, traffic corridors+key, roads511 base_url/key/endpoints/bbox, FIRMS map_key/source/confidence/bbox). ALSO adds a usgs_quake panel (tick/min_magnitude/region/bbox) -- usgs_quake (2.14) was never exposed in the old GUI. feed_source field (C.1) now surfaced per adapter. * Per-adapter live status: reuses /api/env/status feed health + /api/env/active events (filtered by source; fires->nifc mapping). Refreshes every 30s. - pages/Config.tsx: removed the now-duplicate 'Environmental' tab (SECTIONS entry + render case + EnvironmentalSection function + unused Thermometer import); exported the shared form primitives (Toggle, TextInput, NumberInput, SelectInput, ListInput, NumberListInput, US_STATES) for reuse by Environment.tsx. - Reuses the existing restart_required banner pattern. - Rebuilt static: meshai/dashboard/static/{index.html, assets/index-9OZ6ZqzI.js, index-B_J_Z7c8.css} (vite emptyOutDir replaced the old hashed bundle). Rule 17 / no backend change: config is wired to the existing schema-driven GET/PUT /api/config/environmental (the C.1 feed_source + central + usgs_quake fields ride the generic dataclass coercion). No backend edited this phase. Verification: (A) `npm run build` clean -- tsc strict + vite, only the pre-existing >500kB single-chunk advisory (not introduced here). (B) static committed; prod rebuilt picks it up. (C) GET / returns the new SPA shell (index-9OZ6ZqzI.js); the bundle contains the new family strings (Geohazards, RF Propagation, ADS-B, 'Central not available', 'API key not configured'). (D) GET /api/config/environmental returns all adapters with feed_source=native, usgs_quake present, central{enabled:false} -- toggles bind to real data; all native, nothing flipped. Rebuilt prod healthy. *** BLOCKER FOUND (pre-existing, NOT introduced by C.2) -- flagged for C.2.1 *** The save half of gate D fails: PUT /api/config/environmental returns {"detail":"could not determine a constructor for the tag '!include' ..."}. Root cause: the dashboard PUT handler (meshai/dashboard/api/config_routes.py) calls meshai/config.py::save_config (monolithic; re-parses config.yaml with a loader lacking the !include constructor) instead of the multi-file-aware meshai/config_loader.py::save_section that exists for exactly the !include layout. This breaks ALL GUI config saves in prod (every section, not just environmental) and predates C.2 -- the old Config 'Environmental' tab had the same broken save; C.1 did not touch save_config. The C.2 GET/render/toggle-bind works; only persistence is blocked. Verified disk pristine (idempotent PUT errored, wrote nothing; env_feeds.yaml md5 unchanged, restored from backup). FIX (one line, but prod-wide blast radius -> wants its own phase + verification): config_routes PUT should call config_loader.save_section(section, data, config_dir) instead of config.save_config(...). Recommend C.2.1 backend fix before C.3. C.3 (quake-to-central flip) should not proceed until C.2.1 unblocks GUI save, since flipping feed_source from the GUI is the whole point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 03:09:16 +00:00
<AdapterPanel
title={META[activeAdapter].label}
subtitle={META[activeAdapter].subtitle}
enabled={a[activeAdapter]?.enabled ?? false}
onEnabled={(v) => setAdapterField(activeAdapter, { enabled: v })}
feedSource={(a[activeAdapter]?.feed_source ?? 'native')}
onFeedSource={(v) => setAdapterField(activeAdapter, { feed_source: v })}
hasCentral={META[activeAdapter].hasCentral}
nativeOnly={META[activeAdapter].nativeOnly}
hasKey={META[activeAdapter].hasKey}
health={healthFor(activeAdapter)}
events={eventsFor(activeAdapter)}
>
{renderSettings(activeAdapter)}
</AdapterPanel>
</>
)}
</div>
)
}