# K-Frame SPA Handoff ## What is K-Frame IoT dashboard system. Server aggregates data from configurable sources and pushes to ESP32 display clients via TCP. The server is fully functional — SQLite config, REST API, TCP broadcasting, data source polling. ESP32 firmware works end-to-end (display renders widgets). Now needs a config UI. ## What this session should build A React SPA (config/admin UI) for the K-Frame server. The SPA is at `/mnt/drive/dev/k-frame/spa/` — fresh Vite + React 19 + shadcn/ui + TanStack Router + TanStack Query setup. Currently shows a placeholder page. ## Existing artifacts to read first - **Design spec**: `docs/superpowers/specs/2026-06-18-k-frame-design.md` - **Domain glossary**: `CONTEXT.md` - **ADRs**: `docs/adr/0001-event-driven-cqrs.md`, `0002-static-dispatch-over-trait-objects.md`, `0003-postcard-over-flatbuffers.md` - **API types (DTO definitions)**: `crates/api-types/src/` — widget.rs, data_source.rs, layout.rs, preset.rs. These define the exact JSON shapes the API accepts/returns. ## REST API (server runs on :3000) All endpoints return/accept JSON. | Method | Path | Description | |--------|------|-------------| | GET | /api/widgets | List all widgets | | POST | /api/widgets | Create widget | | GET | /api/widgets/{id} | Get widget | | PUT | /api/widgets/{id} | Update widget | | DELETE | /api/widgets/{id} | Delete widget | | GET | /api/data-sources | List all data sources | | POST | /api/data-sources | Create data source | | GET | /api/data-sources/{id} | Get data source | | PUT | /api/data-sources/{id} | Update data source | | DELETE | /api/data-sources/{id} | Delete data source | | GET | /api/layout | Get current layout (nullable) | | PUT | /api/layout | Update layout | | GET | /api/presets | List presets | | POST | /api/presets | Create preset | | GET | /api/presets/{id} | Get preset | | DELETE | /api/presets/{id} | Delete preset | | POST | /api/presets/{id}/load | Load preset as active layout | ### Key JSON shapes **Widget** (create/update): ```json { "id": 1, "name": "weather", "display_hint": "icon_value", "data_source_id": 10, "mappings": [ {"source_path": "$.main.temp", "target_key": "temperature"}, {"source_path": "$.weather[0].icon", "target_key": "icon"} ], "max_data_size": 2048 } ``` `display_hint` values: `"icon_value"`, `"text_block"`, `"key_value"` **Data Source** (create/update): ```json { "id": 10, "name": "weather", "source_type": "weather", "poll_interval_secs": 300, "url": "https://api.openweathermap.org/...", "api_key": "xxx", "headers": [] } ``` `source_type` values: `"weather"`, `"media"`, `"rss"`, `"http_json"`, `"webhook"` **Layout**: ```json { "root": { "type": "container", "direction": "row", "gap": 4, "padding": 2, "children": [ { "sizing": {"type": "flex", "value": 1}, "node": {"type": "leaf", "widget_id": 1} }, { "sizing": {"type": "fixed", "value": 80}, "node": {"type": "leaf", "widget_id": 2} } ] } } ``` Nodes are recursive — containers can nest. **Preset**: ```json {"id": 1, "name": "dashboard", "layout": { "root": { ... } }} ``` ## Pages to build 1. **Dashboard** — overview of connected clients, active data sources, current layout. Landing page. 2. **Data Sources** — CRUD list. Form: name, source_type (select), URL, API key, poll interval, headers. 3. **Widgets** — CRUD list. Form: name, display_hint (select), data_source_id (select from existing sources), key mappings (dynamic list of source_path + target_key pairs), max_data_size. 4. **Layout Builder** — visual tree editor. Add containers (row/column), nest them, place widgets as leaves, set sizing (fixed/flex), gap, padding. This is the most complex page. 5. **Presets** — save current layout as preset, load presets, delete presets. ## SPA tech stack (already set up) - React 19 + TypeScript - Vite 8 - shadcn/ui (components already installed in `src/components/ui/`) - TanStack Router (not yet configured with routes) - TanStack Query (not yet configured with provider) - Tailwind CSS 4 - Bun (lockfile is bun.lock) ## Server integration The SPA's built files need to be served by the Axum server. Two approaches: 1. **Dev**: SPA runs on Vite dev server (:5173), proxies API calls to :3000. Add proxy config to `vite.config.ts`. 2. **Prod**: `bun run build` outputs to `spa/dist/`, Axum serves these as static files. Need to add static file serving to the http-api adapter. The Vite proxy setup is needed first so development works. ## User preferences - Concise, no filler - No mocking — test against real API - Clean code, modules, no huge files - shadcn components for all UI elements - TanStack Router for routing, TanStack Query for data fetching - No Co-Authored-By in commits ## What NOT to change - No changes to Rust crates (domain, application, adapters, etc.) - No changes to the ESP32 firmware - API is stable — build against it as-is ## Suggested skills - `superpowers:brainstorming` — for designing the layout builder UX (most complex page) - `frontend-design` — for visual design direction, making it not look like a default template - `shadcn` — for component usage, composition, and styling patterns