meshai/dashboard-frontend/src/App.tsx

36 lines
1.3 KiB
TypeScript
Raw Normal View History

import { Routes, Route } from 'react-router-dom'
import Layout from './components/Layout'
import Dashboard from './pages/Dashboard'
import Mesh from './pages/Mesh'
import Environment from './pages/Environment'
import Config from './pages/Config'
import Alerts from './pages/Alerts'
import Notifications from './pages/Notifications'
import Reference from './pages/Reference'
feat(v0.6-3c): adapter_config REST API + dashboard editor Closes the audit-doc Section A keystone (the GUI editor). Together with v0.6-3a foundation, v0.6-3a.1 trim, and v0.6-3b handler wiring, every Rule-17 CONFIG knob from the audit is now editable in the dashboard without a container restart. API (meshai/dashboard/api/adapter_config_routes.py): GET /api/adapter-config -- {adapter: [{key, value, default, type, description}]} GET /api/adapter-config/<adapter> -- one adapter list GET /api/adapter-config/<adapter>/<key> -- single row PUT /api/adapter-config/<adapter>/<key> body {value} -- typed validation int: int or whole-number float; rejects bool, fractional float, str float: int or float; rejects bool str: str only bool: bool only json: any JSON-serializable value Every PUT calls invalidate_cache() so the next handler accessor read sees the new value -- no container restart needed. POST /api/adapter-config/<adapter>/<key>/reset -- value_json = default_json, cache invalidated GET /api/adapter-meta -- {adapter: {display_name, include_in_llm_context, description}} PUT /api/adapter-meta/<adapter> partial-update body, fields: include_in_llm_context: bool, display_name: non-empty str Dashboard (dashboard-frontend/src/pages/AdapterConfig.tsx): - Per-adapter cards. Header row shows display_name, the include_in_llm_context toggle, and an expand chevron. Adapters with zero config keys (e.g. itd_511) still render so users can toggle their LLM-context inclusion. - Expanded body lists each key with a type-aware widget: bool -> checkbox, commit-on-change int/float -> number input, commit-on-blur (or Enter) str -> text input, commit-on-blur json -> textarea, commit-on-blur (JSON.parse with inline error) Each row shows the key name, type tag, description, "edited" badge when value != default, a per-key Reset button, and a save badge (spinner, check, error tooltip, or a small amber dot for unsaved local changes). - Auto-save semantics: every blur/change/reset triggers PUT immediately; no explicit Save button needed. Reset is one-click per key. Wiring: - meshai/dashboard/server.py registers the new router with prefix /api. - dashboard-frontend/src/App.tsx adds the /adapter-config route. - dashboard-frontend/src/components/Layout.tsx adds the left-nav entry (Sliders icon, label "Adapter Config", after Reference). - Vite build produces a fresh meshai/dashboard/static bundle. The Dockerfile copies meshai/ so the new bundle ships with the container image at next rebuild. Tests (tests/test_adapter_config_api.py, 30 cases): - GET grouped, per-adapter, single key - GET per-adapter returns [] for adapters with zero keys (itd_511) - PUT updates value, GET shows new value, accessor returns new value (proves cache invalidation propagates to the in-process accessor) - PUT type validation per (int, float, str, bool, json) incl. edge cases: int rejects str + fractional float + bool but accepts whole-number float; float accepts int + float, rejects bool; bool rejects int; str rejects other types; json accepts list / dict / None - PUT 404 on unknown key, 400 on missing value field - POST reset restores default + invalidates cache - GET /api/adapter-meta: include_in_llm_context defaults match registry (central / geocoder false, rest true) - PUT meta partial update: only provided fields change - PUT meta rejects non-bool include_in_llm_context, empty display_name, unknown adapter Test count: 731 -> 761 (+30 API cases, 0 regressions). Refs audit doc v0.6-phase1-audit.md Section A keystone + finding #4.
2026-06-05 18:50:30 +00:00
import AdapterConfig from './pages/AdapterConfig'
feat(v0.6-4): gauge_sites + town_anchors curation tables + GUI CRUD Closes Section A.5 (gauge_sites) and A.12 (town_anchors) of the audit doc by lifting both Python-dict curation lists into editable SQLite tables. Operators can add/edit/disable rows from the dashboard without a deploy; runtime reads go through cached accessors that invalidate when the REST API mutates state. Schema: v8.sql adds gauge_sites(site_id PK, gauge_name, lat, lon, action_ft, flood_minor_ft, flood_moderate_ft, flood_major_ft, enabled, updated_at). v9.sql adds town_anchors(anchor_id AUTOINC PK, name UNIQUE, lat, lon, state, enabled, updated_at). SCHEMA_VERSION 7 -> 9. Seed (meshai/persistence/curation.py): _GAUGE_SITES_SEED carries the original 9 Idaho rows from IDAHO_CURATED_SITES verbatim. _TOWN_ANCHORS_SEED carries the 29 Idaho-and-neighbor towns from _TOWN_COORDS verbatim. seed_gauge_sites() / seed_town_anchors() INSERT OR IGNORE -- safe to re-run; never overwrites user edits. Handler integration: - meshai/central/idaho_gauge_sites.py: IDAHO_CURATED_SITES dict deleted. lookup_site() now calls meshai.persistence.curation.lookup_gauge_site() which reads the table. THRESHOLD_RANK, normalize_site_id, and compute_threshold_state remain in this module (CODE per Matt s rule). - meshai/central/nwis_handler.py drops IDAHO_CURATED_SITES from its import list; the table-backed lookup_site() is API-compatible. - meshai/central_normalizer.py: _TOWN_COORDS dict deleted. _compute_distance_bearing() now calls meshai.persistence.curation.lookup_town_anchor() with the same lowercased-name semantics it always used. REST API (meshai/dashboard/api/curation_routes.py): /api/gauge-sites GET list, GET one, POST add, PUT update, DELETE /api/town-anchors GET list, GET one, POST add, PUT update, DELETE Every mutation calls invalidate_curation_cache() so handler reads see the new state on the next call -- no container restart. Dashboard (dashboard-frontend/src/pages/): - GaugeSites.tsx: table view with Add row / Edit row inline / Delete confirm + per-row enabled toggle. 8 columns mirror the schema. - TownAnchors.tsx: same pattern, 5 columns. Name is lowercased on save to match the lookup key. - Left-nav entries "Gauge Sites" (Droplets icon) and "Town Anchors" (MapPin icon) added to Layout.tsx; routes added to App.tsx. Tests (tests/test_curation.py, 18 cases): - v8/v9 tables exist - Seed lands every row from both dicts - Seed idempotent; never overwrites user edits - lookup_gauge_site hits/miss, disabled rows are invisible - lookup_town_anchor case-insensitive - REST API: GET list, GET one, GET 404, POST add, PUT update, DELETE, POST missing-field 400; both gauge_sites + town_anchors - Accessor reflects API mutations after invalidate_curation_cache() tests/test_nwis_handler.py back-compat: IDAHO_CURATED_SITES dict alias points at _GAUGE_SITES_SEED so the existing assertion suite still passes. tests/test_adapter_config_foundation.py schema_meta v7 -> v9 bump. Test count: 797 -> 819 (+18 curation cases + 4 maintenance updates).
2026-06-05 20:19:13 +00:00
import GaugeSites from './pages/GaugeSites'
import TownAnchors from './pages/TownAnchors'
import { ToastProvider } from './components/ToastProvider'
function App() {
return (
<ToastProvider>
<Layout>
<Routes>
<Route path="/" element={<Dashboard />} />
<Route path="/mesh" element={<Mesh />} />
<Route path="/environment" element={<Environment />} />
<Route path="/config" element={<Config />} />
<Route path="/alerts" element={<Alerts />} />
<Route path="/notifications" element={<Notifications />} />
<Route path="/reference" element={<Reference />} />
feat(v0.6-3c): adapter_config REST API + dashboard editor Closes the audit-doc Section A keystone (the GUI editor). Together with v0.6-3a foundation, v0.6-3a.1 trim, and v0.6-3b handler wiring, every Rule-17 CONFIG knob from the audit is now editable in the dashboard without a container restart. API (meshai/dashboard/api/adapter_config_routes.py): GET /api/adapter-config -- {adapter: [{key, value, default, type, description}]} GET /api/adapter-config/<adapter> -- one adapter list GET /api/adapter-config/<adapter>/<key> -- single row PUT /api/adapter-config/<adapter>/<key> body {value} -- typed validation int: int or whole-number float; rejects bool, fractional float, str float: int or float; rejects bool str: str only bool: bool only json: any JSON-serializable value Every PUT calls invalidate_cache() so the next handler accessor read sees the new value -- no container restart needed. POST /api/adapter-config/<adapter>/<key>/reset -- value_json = default_json, cache invalidated GET /api/adapter-meta -- {adapter: {display_name, include_in_llm_context, description}} PUT /api/adapter-meta/<adapter> partial-update body, fields: include_in_llm_context: bool, display_name: non-empty str Dashboard (dashboard-frontend/src/pages/AdapterConfig.tsx): - Per-adapter cards. Header row shows display_name, the include_in_llm_context toggle, and an expand chevron. Adapters with zero config keys (e.g. itd_511) still render so users can toggle their LLM-context inclusion. - Expanded body lists each key with a type-aware widget: bool -> checkbox, commit-on-change int/float -> number input, commit-on-blur (or Enter) str -> text input, commit-on-blur json -> textarea, commit-on-blur (JSON.parse with inline error) Each row shows the key name, type tag, description, "edited" badge when value != default, a per-key Reset button, and a save badge (spinner, check, error tooltip, or a small amber dot for unsaved local changes). - Auto-save semantics: every blur/change/reset triggers PUT immediately; no explicit Save button needed. Reset is one-click per key. Wiring: - meshai/dashboard/server.py registers the new router with prefix /api. - dashboard-frontend/src/App.tsx adds the /adapter-config route. - dashboard-frontend/src/components/Layout.tsx adds the left-nav entry (Sliders icon, label "Adapter Config", after Reference). - Vite build produces a fresh meshai/dashboard/static bundle. The Dockerfile copies meshai/ so the new bundle ships with the container image at next rebuild. Tests (tests/test_adapter_config_api.py, 30 cases): - GET grouped, per-adapter, single key - GET per-adapter returns [] for adapters with zero keys (itd_511) - PUT updates value, GET shows new value, accessor returns new value (proves cache invalidation propagates to the in-process accessor) - PUT type validation per (int, float, str, bool, json) incl. edge cases: int rejects str + fractional float + bool but accepts whole-number float; float accepts int + float, rejects bool; bool rejects int; str rejects other types; json accepts list / dict / None - PUT 404 on unknown key, 400 on missing value field - POST reset restores default + invalidates cache - GET /api/adapter-meta: include_in_llm_context defaults match registry (central / geocoder false, rest true) - PUT meta partial update: only provided fields change - PUT meta rejects non-bool include_in_llm_context, empty display_name, unknown adapter Test count: 731 -> 761 (+30 API cases, 0 regressions). Refs audit doc v0.6-phase1-audit.md Section A keystone + finding #4.
2026-06-05 18:50:30 +00:00
<Route path="/adapter-config" element={<AdapterConfig />} />
feat(v0.6-4): gauge_sites + town_anchors curation tables + GUI CRUD Closes Section A.5 (gauge_sites) and A.12 (town_anchors) of the audit doc by lifting both Python-dict curation lists into editable SQLite tables. Operators can add/edit/disable rows from the dashboard without a deploy; runtime reads go through cached accessors that invalidate when the REST API mutates state. Schema: v8.sql adds gauge_sites(site_id PK, gauge_name, lat, lon, action_ft, flood_minor_ft, flood_moderate_ft, flood_major_ft, enabled, updated_at). v9.sql adds town_anchors(anchor_id AUTOINC PK, name UNIQUE, lat, lon, state, enabled, updated_at). SCHEMA_VERSION 7 -> 9. Seed (meshai/persistence/curation.py): _GAUGE_SITES_SEED carries the original 9 Idaho rows from IDAHO_CURATED_SITES verbatim. _TOWN_ANCHORS_SEED carries the 29 Idaho-and-neighbor towns from _TOWN_COORDS verbatim. seed_gauge_sites() / seed_town_anchors() INSERT OR IGNORE -- safe to re-run; never overwrites user edits. Handler integration: - meshai/central/idaho_gauge_sites.py: IDAHO_CURATED_SITES dict deleted. lookup_site() now calls meshai.persistence.curation.lookup_gauge_site() which reads the table. THRESHOLD_RANK, normalize_site_id, and compute_threshold_state remain in this module (CODE per Matt s rule). - meshai/central/nwis_handler.py drops IDAHO_CURATED_SITES from its import list; the table-backed lookup_site() is API-compatible. - meshai/central_normalizer.py: _TOWN_COORDS dict deleted. _compute_distance_bearing() now calls meshai.persistence.curation.lookup_town_anchor() with the same lowercased-name semantics it always used. REST API (meshai/dashboard/api/curation_routes.py): /api/gauge-sites GET list, GET one, POST add, PUT update, DELETE /api/town-anchors GET list, GET one, POST add, PUT update, DELETE Every mutation calls invalidate_curation_cache() so handler reads see the new state on the next call -- no container restart. Dashboard (dashboard-frontend/src/pages/): - GaugeSites.tsx: table view with Add row / Edit row inline / Delete confirm + per-row enabled toggle. 8 columns mirror the schema. - TownAnchors.tsx: same pattern, 5 columns. Name is lowercased on save to match the lookup key. - Left-nav entries "Gauge Sites" (Droplets icon) and "Town Anchors" (MapPin icon) added to Layout.tsx; routes added to App.tsx. Tests (tests/test_curation.py, 18 cases): - v8/v9 tables exist - Seed lands every row from both dicts - Seed idempotent; never overwrites user edits - lookup_gauge_site hits/miss, disabled rows are invisible - lookup_town_anchor case-insensitive - REST API: GET list, GET one, GET 404, POST add, PUT update, DELETE, POST missing-field 400; both gauge_sites + town_anchors - Accessor reflects API mutations after invalidate_curation_cache() tests/test_nwis_handler.py back-compat: IDAHO_CURATED_SITES dict alias points at _GAUGE_SITES_SEED so the existing assertion suite still passes. tests/test_adapter_config_foundation.py schema_meta v7 -> v9 bump. Test count: 797 -> 819 (+18 curation cases + 4 maintenance updates).
2026-06-05 20:19:13 +00:00
<Route path="/gauge-sites" element={<GaugeSites />} />
<Route path="/town-anchors" element={<TownAnchors />} />
</Routes>
</Layout>
</ToastProvider>
)
}
export default App