@sensolus/create-snt-agent-app 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,153 @@
1
+ # @sensolus/create-snt-agent-app
2
+
3
+ Scaffold a new Sensolus agent app: a React (Vite) frontend wired to
4
+ [`@sensolus/snt-agent-kit`](https://www.npmjs.com/package/@sensolus/snt-agent-kit)
5
+ plus a Flask backend that proxies the Sensolus public API, with PostgreSQL,
6
+ migrations, Docker, and Jenkins CI included.
7
+
8
+ ## Quick start — create an agent app
9
+
10
+ ```bash
11
+ npm create @sensolus/snt-agent-app my-app
12
+ cd my-app
13
+ cp .env.example .env # fill in MAPBOX_KEY + LOCATIONIQ_KEY (required for SntMap)
14
+ npm install
15
+ pip install -r backend/requirements.txt
16
+ npm run dev # frontend on :3000
17
+ python backend/app.py # backend on :5000 (separate terminal)
18
+ ```
19
+
20
+ Open http://localhost:3000. The Vite dev server proxies `/api/*` to Flask;
21
+ Flask serves runtime config (map keys) at `/api/config` and proxies the
22
+ Sensolus API with your session cookie or an API key entered in the app.
23
+
24
+ The generated README covers the rest: PostgreSQL setup, Docker build,
25
+ deployment, and environment variables.
26
+
27
+ ### Rules of the road
28
+
29
+ Widgets, theme, and the i18n framework come from `@sensolus/snt-agent-kit` —
30
+ **import them, don't copy them**. ESLint in the generated app enforces no deep
31
+ imports and no `Snt*` re-declarations. App-specific translation keys live in
32
+ your app (`src/i18n/messages.js`) and are merged via
33
+ `<LocaleProvider messages={...}>`.
34
+
35
+ ## The widget kit
36
+
37
+ The generated app comes pre-wired, but everything below is plain
38
+ `@sensolus/snt-agent-kit` API — useful when extending the app.
39
+
40
+ ### Default wiring (generated-app convention)
41
+
42
+ The app proxies the Sensolus backend at `/api/*`, so the kit's defaults just
43
+ work:
44
+
45
+ ```jsx
46
+ import { SntUiProvider, LocaleProvider, SntButton, SntCard } from '@sensolus/snt-agent-kit'
47
+ import '@sensolus/snt-agent-kit/theme.css'
48
+ import { messages } from './i18n' // your app's translation keys
49
+
50
+ function App() {
51
+ return (
52
+ <SntUiProvider>
53
+ <LocaleProvider messages={messages}>
54
+ <SntCard title="Hello">
55
+ <SntButton variant="primary">Click me</SntButton>
56
+ </SntCard>
57
+ </LocaleProvider>
58
+ </SntUiProvider>
59
+ )
60
+ }
61
+ ```
62
+
63
+ ### Custom wiring (non-`/api/*` backends)
64
+
65
+ `SntUiProvider` accepts overrides for any of the four backend calls and for
66
+ navigation. Anything you omit falls back to the same-origin `/api/*` default.
67
+
68
+ ```jsx
69
+ import { useNavigate } from 'react-router-dom'
70
+ import { SntUiProvider } from '@sensolus/snt-agent-kit'
71
+
72
+ function Shell({ children }) {
73
+ const navigate = useNavigate()
74
+ return (
75
+ <SntUiProvider
76
+ api={{
77
+ // Call the Sensolus public API directly with your own API key
78
+ fetchLoginInfo: () => mySensolus.getLoginInfo(),
79
+ fetchGeozones: (orgId) => mySensolus.getGeozones(orgId),
80
+ geocode: (q) => mySensolus.geocode(q),
81
+ reverseGeocode: (lat, lng) => mySensolus.reverseGeocode(lat, lng),
82
+ }}
83
+ navigate={navigate} // optional: wire to react-router, Next, etc.
84
+ >
85
+ {children}
86
+ </SntUiProvider>
87
+ )
88
+ }
89
+ ```
90
+
91
+ #### `api` contract
92
+
93
+ | Method | Signature | Used by |
94
+ |--------|-----------|---------|
95
+ | `fetchLoginInfo()` | `() => Promise<{ language, timezone, firstDayOfWeek, unitDistance, unitTemperature } \| null>` | `LocaleProvider` |
96
+ | `fetchGeozones(orgId)` | `(string) => Promise<Geozone[]>` | `SntMap` (when `orgId` prop set) |
97
+ | `geocode(query)` | `(string) => Promise<Array<{ name, lat, lng, boundingbox }>>` | `SntMap` (search box) |
98
+ | `reverseGeocode(lat, lng)` | `(number, number) => Promise<{ displayName, address, lat, lng } \| null>` | reserved for future widgets |
99
+
100
+ #### `navigate` contract
101
+
102
+ `(to: string \| number) => void` — strings are routes (`'/orgs/123'`), `-1`
103
+ means "back". Wire to `useNavigate()` from react-router-dom, the Next.js
104
+ router, or omit to fall back to `window.history`.
105
+
106
+ ### SntMap
107
+
108
+ Tile-provider keys are **required props** — the kit ships no keys. In a
109
+ generated app they come from the backend's `/api/config` at runtime (via
110
+ `AppConfigContext`), so one Docker image deploys across environments:
111
+
112
+ ```jsx
113
+ const config = useAppConfig()
114
+
115
+ <SntMap
116
+ mapboxKey={config.mapboxKey}
117
+ locationiqKey={config.locationiqKey}
118
+ devices={devices}
119
+ orgId={id} // triggers api.fetchGeozones(orgId)
120
+ />
121
+ ```
122
+
123
+ ### Components
124
+
125
+ | Component | Description |
126
+ |-----------|-------------|
127
+ | `SntUiProvider`, `useSntUi` | Inject backend `api` + `navigate` adapters |
128
+ | `LocaleProvider`, `useLocale` | i18n / locale context (reads `api.fetchLoginInfo`, merges app `messages`) |
129
+ | `formatShortDate`, `formatDateTime`, `formatNumber` | Locale-aware formatters |
130
+ | `SntButton` | Primary button with variants |
131
+ | `SntInput` | Text input (onChange receives value directly) |
132
+ | `SntBadge` | Status badge with color variants |
133
+ | `SntCard` | Card container |
134
+ | `SntTable` | Sortable, paginated data table |
135
+ | `SntSpinner`, `SntLoadingOverlay` | Loading indicators |
136
+ | `SntToolbar`, `SntToolbarSpacer` | Toolbar layout |
137
+ | `SntButtonGroup` | Segmented control |
138
+ | `SntSelect` | Native select with Sensolus styling |
139
+ | `SntPageHeader` | Page header (uses `navigate` from `SntUiProvider`) |
140
+ | `SntTabs`, `SntTabPanel` | Tabbed views |
141
+ | `SntDialog` | Modal dialog |
142
+ | `SntSidepanel`, `SntFilterSection` | Slide-out side panel |
143
+ | `SntSwitch` | Toggle switch |
144
+ | `SntGrid`, `SntGridItem` | Responsive grid |
145
+ | `SntSummaryStat` | Big-number summary tile |
146
+ | `SntHistogram` | Time-bucketed histogram |
147
+ | `SntCheckboxList` | Multi-select checkbox list |
148
+ | `SntDateRangePicker` | Date range picker (locale-aware) |
149
+ | `SntMap` | Leaflet map (Street/Satellite, geozones, device markers) |
150
+ | `SntColors` | JavaScript color constants |
151
+
152
+ The generated app includes a **Widgets** tab (`WidgetShowcase`) demonstrating
153
+ every component live.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sensolus/create-snt-agent-app",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Scaffold a new Sensolus agent app: React frontend wired to @sensolus/snt-agent-kit + Flask API proxy backend.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -15,7 +15,7 @@
15
15
  "url": "git+ssh://git@bitbucket.org/sensolus/{{APP_NAME}}.git"
16
16
  },
17
17
  "dependencies": {
18
- "@sensolus/snt-agent-kit": "^0.2.0",
18
+ "@sensolus/snt-agent-kit": "^0.4.0",
19
19
  "react": "^19.2.3",
20
20
  "react-dom": "^19.2.3",
21
21
  "react-router-dom": "^7.1.1"
@@ -5,7 +5,7 @@ import {
5
5
  SntBadge,
6
6
  SntProgressBar,
7
7
  SntCard,
8
- SntMap,
8
+ SntDeviceMap,
9
9
  SntSpinner,
10
10
  } from '@sensolus/snt-agent-kit'
11
11
  import { useLocale, formatNumber } from '../i18n'
@@ -214,7 +214,7 @@ export function OrganisationDetail() {
214
214
  <SntSpinner size="medium" />
215
215
  </div>
216
216
  ) : (
217
- <SntMap
217
+ <SntDeviceMap
218
218
  mapboxKey={config.mapboxKey}
219
219
  locationiqKey={config.locationiqKey}
220
220
  height="500px"
@@ -14,6 +14,7 @@ import {
14
14
  SntInput,
15
15
  SntLoadingOverlay,
16
16
  SntMap,
17
+ SntDeviceLayer,
17
18
  SntProgressBar,
18
19
  SntSelect,
19
20
  SntSidepanel,
@@ -482,7 +483,7 @@ export function WidgetShowcase() {
482
483
  {/* ------------------------------------------------------------------ */}
483
484
  <Section
484
485
  title="SntMap"
485
- description="Leaflet map with Street/Satellite layer toggle. Optionally renders geozones (by orgId or array) and device markers."
486
+ description="Base Leaflet map (street/satellite toggle, geocoder, zoom/scale). Compose with layer components here, <SntDeviceLayer>."
486
487
  >
487
488
  <SntMap
488
489
  mapboxKey={config.mapboxKey}
@@ -490,14 +491,15 @@ export function WidgetShowcase() {
490
491
  height="320px"
491
492
  center={[50.85, 4.35]}
492
493
  zoom={6}
493
- showGeozones={false}
494
- showGeozoneSelector={false}
495
- devices={[
496
- { id: 'd1', name: 'Demo tracker A', lastLat: 50.8503, lastLng: 4.3517 },
497
- { id: 'd2', name: 'Demo tracker B', lastLat: 51.2194, lastLng: 4.4025 },
498
- { id: 'd3', name: 'Demo tracker C', lastLat: 51.0543, lastLng: 3.7174 },
499
- ]}
500
- />
494
+ >
495
+ <SntDeviceLayer
496
+ devices={[
497
+ { id: 'd1', name: 'Demo tracker A', lastLat: 50.8503, lastLng: 4.3517 },
498
+ { id: 'd2', name: 'Demo tracker B', lastLat: 51.2194, lastLng: 4.4025 },
499
+ { id: 'd3', name: 'Demo tracker C', lastLat: 51.0543, lastLng: 3.7174 },
500
+ ]}
501
+ />
502
+ </SntMap>
501
503
  </Section>
502
504
 
503
505
  {/* ------------------------------------------------------------------ */}