Appearance
Pages
Each page in the web frontend is a TypeScript module that exports a single function (e.g. dashboard_page()). Pages use HTML templates from the templates/ directory and interact with the server API via fetch with JWT auth.
Login
File: web/src/pages/login.ts
- Renders the login form with hero section
- Validates credentials against
POST /api/v1/login - Stores JWT in
localStorageon success - Redirects to dashboard
Dashboard
File: web/src/pages/dashboard.ts
The main page after login. Provides:
- System Logs: Fetches from
GET /api/v1/logsand displays recent entries - Events: Fetches from
GET /api/v1/eventsand displays beacon events (panic, SOS) - MQTT Status: Auto-checks
GET /api/v1/status/dashboardwhen the page loads (cached snapshot: MQTT, KISS, ntfy, fleet table, issues). Recheck buttons callPOST /api/v1/status/recheck. - MQTT / KISS / ntfy rows: render icon + status text from the snapshot. Recheck buttons call
POST /api/v1/status/recheck. The ntfy row also shows separate Reachability and Delivery tags plus the concrete curl error or HTTP status. In-flight dashboard polls are aborted when Recheck starts so a delayed GET cannot overwrite the Recheck result. - Navigation QR Codes: Generates QR codes for mobile app connection (location + token)
- Event Export: CSV export via
GET /api/v1/events/export - Menu Navigation: Routes to all other pages
Settings
File: web/src/shared/pages/settings.ts
Admin-only Settings page with Server Settings (nested Configuration, User interface, and System) and User Settings.
Configuration (editable, applied immediately, no restart):
server_idserver_modeserver_kiss_url_baseserver_kiss_credentials_usernameserver_kiss_credentials_passwordserver_devices_cache_sync_intervalserver_perf_memory_thresholdserver_debug_snapshot_intervalserver_debug_log_dirserver_debug_max_filesserver_ntfy_enabledserver_ntfy_ops_enabledserver_ntfy_sensors_cooldown_secondsserver_ntfy_button_dedup_secondsserver_ntfy_critical_cooldown_secondsserver_ntfy_digest_timezone/server_ntfy_digest_time- Bulk/individual mode and interval settings for batteries, sensor not-seen, calibration, and fleet health
server_log_verbosity
User interface (editable, applied immediately, no restart; chrome for simple and maintenance users):
server_ui_nav_orientationserver_ui_show_brandserver_ui_show_profileserver_ui_show_accountserver_ui_show_logoutserver_ui_logout_styleserver_ui_show_system_statusserver_ui_show_footerserver_ui_footer_show_versionserver_ui_footer_textserver_bug_tracker_enabled
System (read-only): remaining main.config.json keys plus version/build. Secrets are masked. Changing those values still requires editing the config file and restarting.
Behavior notes:
- Opened from the navbar gear icon next to logout.
- Routes:
/settings/server,/settings/ui,/settings/system,/settings/user. - Loads values from
GET /api/v1/settings(editable+readonly). - Saves via
PATCH /api/v1/settings. Changes apply to the running process immediately and do not regenerate JWT keys. - Simple-user sessions receive resolved chrome as
ui_chromeonGET /api/v1/user/session.
Simple-user app shell
Files: web/src/templates/shared/generic/navbar-user.html, web/src/shell/shell.ts, web/src/shell/handlers.ts, web/src/shell/nav.ts
Non-admin (user / maintenance) pages render inside one app shell instead of the admin navbar + shared footer:
- Horizontal default: product name on the left, template tabs, user menu on the right (Account, personal Settings, Logout).
- Optional vertical sidebar via
server_ui_nav_orientation. - System status is off by default. When enabled it is a quiet dot + tooltip from
GET /api/v1/status/dashboard, not MQTT/KISS “CHECK SERVICES” polling. - Compact footer (product + Wantok, optional version). No build-date dump.
- Account (
/account/password,/account/settings) uses the same shell. - Tab content mounts into a CSS grid. Interface templates may include optional
tabs[].columns; the editor still writescomponents[]and the renderer wraps them in one full-width column whencolumnsis absent.
Beacons
File: web/src/pages/beacons.ts
CRUD management for BLE beacons:
- List all beacons with search/filter
- Create new beacons (MAC, name, type, location)
- Edit beacon properties
- Delete beacons
- View beacon info and tracking intervals
Gateways
File: web/src/pages/gateways.ts
CRUD management for BLE gateways:
- List all gateways
- Create new gateways (MAC, name, IP, location)
- Edit gateway properties
- Delete gateways
Users
File: web/src/pages/users.ts
User account management (admin only):
- List all users
- Create new users
- Change passwords
- Delete users
Locations
File: web/src/pages/locations.ts
Location/building management:
- List all locations
- Create, edit, delete locations
- Used as foreign keys by maps, beacons, gateways
Maps
File: web/src/pages/maps.ts
Floor map image management (admin only):
- Upload map images with metadata (name, location)
- Edit map metadata or replace images
- Delete maps
- Maps are referenced by zones and the navigation system
Zones
File: web/src/pages/zones.ts
Zone management and live tracking with tabbed interface:
Overview Tab
- Tracking status (running/stopped, current algorithm)
- Start/stop tracking controls
- Algorithm selection
Track Tab
- Select a beacon to track
- Live position updates via WebSocket
- Pixi.js canvas showing beacon position on map
Manage Maps Tab
- Interactive zone editor using Pixi.js canvas
- Draw, resize, and position zone rectangles on map images
- Place and move gateway markers
- Define zone connections (adjacency graph)
Debug Tab
- Live WebSocket stream of RSSI data for a beacon
- Shows raw signal strength from each gateway
- Useful for debugging tracking accuracy
Navigation
File: web/src/pages/navigation.ts
Navigation graph editor with tabbed interface:
Overview Tab
- Floor listing and navigation token management
Manage Floors Tab
- Interactive graph editor using Pixi.js
- Tools: Select, Add Node, Connect Nodes, Ruler (distance measurement)
- Add/move/delete nodes on floor maps
- Connect nodes with weighted edges
- Assign beacons to positions on the graph
- Save graph to server via
POST /api/v1/floor/:id/graph
Manage Beacons Tab
- Assign beacons to navigation floors
- Configure POI (Point of Interest) beacons
- Set static vs mobile beacon flags
Placement Planner
File: web/src/admin/tools/placement-planner/placement-planner.ts
Client-side installation planning tool for uploaded floor plans:
- Imports image files directly in the browser.
- Imports PDF files by rendering page 1 to a bounded PNG canvas in the browser.
- Imports DWG files by posting the file to
POST /api/v1/tools/placement-planner/convert-dwg, which returns a temporary PNG. - Keeps planner state local to the browser session; imported files and converted PNGs are not saved as maps.
- Uses the existing Measure tool for scale calibration after import because vector file units are not treated as trusted planner scale.
DWG conversion is Linux-only and depends on system-installed command-line tools on the server:
dwg2SVGfrom LibreDWG converts the uploaded DWG into SVG.rsvg-convertfrom Debian/Ubuntu packagelibrsvg2-binrasterizes the SVG into PNG.
If the tools are installed outside PATH, set SAFECALL_DWG2SVG_BIN and SAFECALL_RSVG_CONVERT_BIN. Large SVGs are rasterized with a bounded maximum dimension controlled by SAFECALL_DWG_RASTER_MAX_PX (default 4096).
Sensors
File: web/src/pages/sensors.ts
Environmental sensor monitoring dashboard (admin only). Only visible when the "sensors" feature is enabled in the server configuration.
Out-of-Range Alerts
- Fetches from
GET /api/v1/sensors/alerts - Displays the 10 most recent sensor readings that fall outside their configured ranges (humidity_range / temperature_range on each beacon)
- Table columns: Beacon Name, Sensor MAC, Type, Value, Expected Range, Last Occurred
- Rows highlighted in red for visual emphasis
Sensor Charts
- Fetches from
GET /api/v1/sensors/history(past 12 hours) - One Chart.js line chart per sensor type (Temperature, Humidity, Battery)
- Each beacon is a separate line on the chart, labeled by beacon name
- X-axis: time, Y-axis: reading value with unit
External Grafana Integration
- SafeCall exposes a Grafana JSON datasource API (
/grafana/*) so Grafana can query sensor data directly. - For setup instructions, see Grafana Setup.