@sensolus/create-snt-agent-app
v0.3.0
Published
Scaffold a new Sensolus agent app: React frontend wired to @sensolus/snt-agent-kit + Flask API proxy backend.
Downloads
234
Readme
@sensolus/create-snt-agent-app
Scaffold a new Sensolus agent app: a React (Vite) frontend wired to
@sensolus/snt-agent-kit
plus a Flask backend that proxies the Sensolus public API, with PostgreSQL,
migrations, Docker, and Jenkins CI included.
Quick start — create an agent app
npm create @sensolus/snt-agent-app my-app
cd my-app
cp .env.example .env # fill in MAPBOX_KEY + LOCATIONIQ_KEY (required for SntMap)
npm install
pip install -r backend/requirements.txt
npm run dev # frontend on :3000
python backend/app.py # backend on :5000 (separate terminal)Open http://localhost:3000. The Vite dev server proxies /api/* to Flask;
Flask serves runtime config (map keys) at /api/config and proxies the
Sensolus API with your session cookie or an API key entered in the app.
The generated README covers the rest: PostgreSQL setup, Docker build, deployment, and environment variables.
Rules of the road
Widgets, theme, and the i18n framework come from @sensolus/snt-agent-kit —
import them, don't copy them. ESLint in the generated app enforces no deep
imports and no Snt* re-declarations. App-specific translation keys live in
your app (src/i18n/messages.js) and are merged via
<LocaleProvider messages={...}>.
The widget kit
The generated app comes pre-wired, but everything below is plain
@sensolus/snt-agent-kit API — useful when extending the app.
Default wiring (generated-app convention)
The app proxies the Sensolus backend at /api/*, so the kit's defaults just
work:
import { SntUiProvider, LocaleProvider, SntButton, SntCard } from '@sensolus/snt-agent-kit'
import '@sensolus/snt-agent-kit/theme.css'
import { messages } from './i18n' // your app's translation keys
function App() {
return (
<SntUiProvider>
<LocaleProvider messages={messages}>
<SntCard title="Hello">
<SntButton variant="primary">Click me</SntButton>
</SntCard>
</LocaleProvider>
</SntUiProvider>
)
}Custom wiring (non-/api/* backends)
SntUiProvider accepts overrides for any of the four backend calls and for
navigation. Anything you omit falls back to the same-origin /api/* default.
import { useNavigate } from 'react-router-dom'
import { SntUiProvider } from '@sensolus/snt-agent-kit'
function Shell({ children }) {
const navigate = useNavigate()
return (
<SntUiProvider
api={{
// Call the Sensolus public API directly with your own API key
fetchLoginInfo: () => mySensolus.getLoginInfo(),
fetchGeozones: (orgId) => mySensolus.getGeozones(orgId),
geocode: (q) => mySensolus.geocode(q),
reverseGeocode: (lat, lng) => mySensolus.reverseGeocode(lat, lng),
}}
navigate={navigate} // optional: wire to react-router, Next, etc.
>
{children}
</SntUiProvider>
)
}api contract
| Method | Signature | Used by |
|--------|-----------|---------|
| fetchLoginInfo() | () => Promise<{ language, timezone, firstDayOfWeek, unitDistance, unitTemperature } \| null> | LocaleProvider |
| fetchGeozones(orgId) | (string) => Promise<Geozone[]> | SntMap (when orgId prop set) |
| geocode(query) | (string) => Promise<Array<{ name, lat, lng, boundingbox }>> | SntMap (search box) |
| reverseGeocode(lat, lng) | (number, number) => Promise<{ displayName, address, lat, lng } \| null> | reserved for future widgets |
navigate contract
(to: string \| number) => void — strings are routes ('/orgs/123'), -1
means "back". Wire to useNavigate() from react-router-dom, the Next.js
router, or omit to fall back to window.history.
SntMap
Tile-provider keys are required props — the kit ships no keys. In a
generated app they come from the backend's /api/config at runtime (via
AppConfigContext), so one Docker image deploys across environments:
const config = useAppConfig()
<SntMap
mapboxKey={config.mapboxKey}
locationiqKey={config.locationiqKey}
devices={devices}
orgId={id} // triggers api.fetchGeozones(orgId)
/>Components
| Component | Description |
|-----------|-------------|
| SntUiProvider, useSntUi | Inject backend api + navigate adapters |
| LocaleProvider, useLocale | i18n / locale context (reads api.fetchLoginInfo, merges app messages) |
| formatShortDate, formatDateTime, formatNumber | Locale-aware formatters |
| SntButton | Primary button with variants |
| SntInput | Text input (onChange receives value directly) |
| SntBadge | Status badge with color variants |
| SntCard | Card container |
| SntTable | Sortable, paginated data table |
| SntSpinner, SntLoadingOverlay | Loading indicators |
| SntToolbar, SntToolbarSpacer | Toolbar layout |
| SntButtonGroup | Segmented control |
| SntSelect | Native select with Sensolus styling |
| SntPageHeader | Page header (uses navigate from SntUiProvider) |
| SntTabs, SntTabPanel | Tabbed views |
| SntDialog | Modal dialog |
| SntSidepanel, SntFilterSection | Slide-out side panel |
| SntSwitch | Toggle switch |
| SntGrid, SntGridItem | Responsive grid |
| SntSummaryStat | Big-number summary tile |
| SntHistogram | Time-bucketed histogram |
| SntCheckboxList | Multi-select checkbox list |
| SntDateRangePicker | Date range picker (locale-aware) |
| SntMap | Leaflet map (Street/Satellite, geozones, device markers) |
| SntColors | JavaScript color constants |
The generated app includes a Widgets tab (WidgetShowcase) demonstrating
every component live.
