@trackunit/iris-app 1.4.9 → 1.4.10

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/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ ## 1.4.10 (2025-07-11)
2
+
3
+ ### 🧱 Updated Dependencies
4
+
5
+ - Updated iris-app-build-utilities to 1.4.10
6
+ - Updated iris-app-webpack-plugin to 1.4.10
7
+ - Updated iris-app-api to 1.4.10
8
+ - Updated react-test-setup to 1.1.10
9
+ - Updated shared-utils to 1.6.10
10
+
1
11
  ## 1.4.9 (2025-07-11)
2
12
 
3
13
  ### 🧱 Updated Dependencies
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trackunit/iris-app",
3
- "version": "1.4.9",
3
+ "version": "1.4.10",
4
4
  "license": "SEE LICENSE IN LICENSE.txt",
5
5
  "main": "src/index.js",
6
6
  "generators": "./generators.json",
@@ -32,12 +32,12 @@
32
32
  "@npmcli/arborist": "^7.3.1",
33
33
  "webpack-bundle-analyzer": "^4.8.0",
34
34
  "win-ca": "^3.5.1",
35
- "@trackunit/iris-app-build-utilities": "1.4.9",
36
- "@trackunit/shared-utils": "1.6.9",
37
- "@trackunit/iris-app-api": "1.4.9",
38
- "@trackunit/iris-app-webpack-plugin": "1.4.9",
35
+ "@trackunit/iris-app-build-utilities": "1.4.10",
36
+ "@trackunit/shared-utils": "1.6.10",
37
+ "@trackunit/iris-app-api": "1.4.10",
38
+ "@trackunit/iris-app-webpack-plugin": "1.4.10",
39
39
  "tslib": "^2.6.2",
40
- "@trackunit/react-test-setup": "1.1.9"
40
+ "@trackunit/react-test-setup": "1.1.10"
41
41
  },
42
42
  "types": "./src/index.d.ts",
43
43
  "type": "commonjs"
@@ -0,0 +1,311 @@
1
+ ---
2
+ description: CustomFields are the primary mechanism for storing data from IrisX applications. They allow IrisX App developers to extend the Trackunit data model by storing additional information on entities like assets, accounts, groups, and sites.
3
+ globs:
4
+ alwaysApply: false
5
+ ---
6
+
7
+ # CustomFields Rule for Trackunit IrisX App SDK
8
+
9
+ ## Overview
10
+ **CustomFields are the primary mechanism for storing data from IrisX applications.** They allow IrisX App developers to extend the Trackunit data model by storing additional information on entities like assets, accounts, groups, and sites.
11
+
12
+ ### Key Use Cases:
13
+ - **Extending the data model**: Add new fields and attributes to existing Trackunit entities
14
+ - **Storing user input**: Capture and persist form data, user preferences, and settings
15
+ - **Storing metadata**: Keep application-specific information, configuration data, and operational metrics
16
+ - **Fleet-specific data**: Custom attributes unique to specific fleet management needs
17
+ - **Performance metrics**: Custom KPIs and measurement data not available in standard Trackunit data
18
+ - **Business logic data**: Information required for app-specific workflows and processes
19
+
20
+ **Important**: CustomFields are the recommended and supported way to persist any data your IrisX app needs to store. Do not attempt to use external databases or storage solutions - use CustomFields for all persistent data requirements.
21
+
22
+ ## 1. Defining Custom Fields
23
+
24
+ ### Manifest Configuration
25
+
26
+ Custom fields must be defined in your IrisX App manifest using the `customFieldDefinitions` array:
27
+
28
+ ```typescript
29
+ // In your app manifest
30
+ customFieldDefinitions: [
31
+ {
32
+ type: 'STRING',
33
+ entityType: 'ASSET',
34
+ key: 'myCustomField',
35
+ translations: [{
36
+ language: 'en',
37
+ title: 'My Custom Field',
38
+ description: 'Description of what this field stores',
39
+ }],
40
+ uiEditable: true,
41
+ uiVisible: true,
42
+ scopeType: ScopeType.ACCOUNT
43
+ }
44
+ ]
45
+ ```
46
+
47
+ ### Required Properties
48
+
49
+ - **`type`**: Field data type (see supported types below)
50
+ - **`entityType`**: Entity the field applies to (`ACCOUNT`, `ASSET`, `GROUP`, `SITE`)
51
+ - **`key`**: Programmatic identifier (cannot be changed after creation)
52
+ - **`translations`**: UI labels and descriptions for each supported language
53
+ - **`scopeType`**: Data access scope (see scope types below)
54
+
55
+ ### Optional Properties
56
+
57
+ - **`uiEditable`**: Whether field can be edited in Manager UI (default: true)
58
+ - **`uiVisible`**: Whether field is visible in Manager UI (default: true)
59
+
60
+ ## 2. Supported Field Types
61
+
62
+ | Type | Description | Extra Properties |
63
+ |------|-------------|------------------|
64
+ | `BOOLEAN` | True/false values | - |
65
+ | `DATE` | ISO8601 formatted dates | - |
66
+ | `DROPDOWN` | Predefined option lists | `allValues`, `multiSelect`, `dropDownValueReplacements` |
67
+ | `EMAIL` | Email addresses with mailto links | - |
68
+ | `NUMBER` | Numeric values | `minimum`, `maximum`, `isInteger`, `unitSi`, `unitUs` |
69
+ | `PHONE_NUMBER` | E.164 formatted phone numbers | - |
70
+ | `STRING` | Free text | `minimumLength`, `maximumLength`, `pattern` |
71
+ | `WEB_ADDRESS` | URLs as clickable links | - |
72
+ | `JSON` | JSON objects | - |
73
+ | `MONETARY` | Currency values (ISO 4217) | `minimum`, `maximum`, `currency` |
74
+
75
+ ### Example Field Definitions
76
+
77
+ ```typescript
78
+ // String field with validation
79
+ {
80
+ type: 'STRING',
81
+ entityType: 'ASSET',
82
+ key: 'serialNumber',
83
+ translations: [{ language: 'en', title: 'Serial Number', description: 'Asset serial number' }],
84
+ minimumLength: 5,
85
+ maximumLength: 20,
86
+ pattern: '^[A-Z0-9]+$'
87
+ }
88
+
89
+ // Number field with units
90
+ {
91
+ type: 'NUMBER',
92
+ entityType: 'ASSET',
93
+ key: 'fuelCapacity',
94
+ translations: [{ language: 'en', title: 'Fuel Capacity', description: 'Tank capacity' }],
95
+ minimum: 0,
96
+ maximum: 1000,
97
+ unitSi: 'L',
98
+ unitUs: 'gal'
99
+ }
100
+
101
+ // Dropdown with multiple selection
102
+ {
103
+ type: 'DROPDOWN',
104
+ entityType: 'ASSET',
105
+ key: 'features',
106
+ translations: [{ language: 'en', title: 'Features', description: 'Available features' }],
107
+ allValues: ['GPS', 'Camera', 'Bluetooth', 'WiFi'],
108
+ multiSelect: true
109
+ }
110
+ ```
111
+
112
+ ## 3. Scope Types
113
+
114
+ | Scope | Description |
115
+ |-------|-------------|
116
+ | `ACCOUNT` | Values shared within a single account only |
117
+ | `ACCOUNT_WRITE_GLOBAL_READ` | Account can write, all accounts can read |
118
+ | `GLOBAL` | Values shared between all accounts with entity access |
119
+
120
+ ## 4. Using Custom Fields in React Components
121
+
122
+ ### Required Hooks
123
+
124
+ Use these hooks to interact with custom fields:
125
+
126
+ ```typescript
127
+ import {
128
+ useCustomFieldsValueAndDefinition,
129
+ useUpdateCustomFieldValues,
130
+ useCurrentUserSystemOfMeasurement
131
+ } from '@trackunit/react-core-contexts-host';
132
+ ```
133
+
134
+ ### Basic Implementation Pattern
135
+
136
+ ```typescript
137
+ import React from 'react';
138
+ import { useForm } from 'react-hook-form';
139
+ import { Card, CardBody, CardHeader, CardFooter, Button, Spinner } from '@trackunit/react-components';
140
+ import { CustomField } from '@trackunit/react-core-contexts-host';
141
+
142
+ const CustomFieldsForm = ({ assetId }: { assetId: string }) => {
143
+ const { register, handleSubmit, formState, setValue, control } = useForm({
144
+ shouldUnregister: false,
145
+ });
146
+
147
+ const { systemOfMeasurement } = useCurrentUserSystemOfMeasurement();
148
+
149
+ const { fields, loading } = useCustomFieldsValueAndDefinition({
150
+ entityType: "ASSET",
151
+ entityId: assetId,
152
+ systemOfMeasurement: systemOfMeasurement ?? "SI",
153
+ });
154
+
155
+ const { updateCustomFields, inProgress } = useUpdateCustomFieldValues(
156
+ assetId,
157
+ "ASSET",
158
+ fields,
159
+ systemOfMeasurement ?? "SI"
160
+ );
161
+
162
+ if (loading) {
163
+ return <Spinner />;
164
+ }
165
+
166
+ return (
167
+ <Card>
168
+ <CardHeader heading="Custom Fields" />
169
+ <CardBody>
170
+ {fields.map(field => {
171
+ const isEditable = field.valueEditable && (field.definition.uiEditable ?? true);
172
+ return (
173
+ <CustomField
174
+ key={field.definition.key}
175
+ control={control}
176
+ field={field}
177
+ formState={formState}
178
+ isEditable={isEditable}
179
+ register={register}
180
+ setValue={setValue}
181
+ unitPreference={systemOfMeasurement}
182
+ />
183
+ );
184
+ })}
185
+ </CardBody>
186
+ <CardFooter>
187
+ <Button
188
+ loading={inProgress}
189
+ onClick={handleSubmit(updateCustomFields)}
190
+ >
191
+ {inProgress ? "Saving..." : "Save Changes"}
192
+ </Button>
193
+ </CardFooter>
194
+ </Card>
195
+ );
196
+ };
197
+ ```
198
+
199
+ ## 5. Best Practices
200
+
201
+ ### Field Design
202
+ - **Use descriptive keys**: Choose clear, descriptive field keys that won't need changing
203
+ - **Add proper translations**: Always provide meaningful titles and descriptions
204
+ - **Set appropriate constraints**: Use min/max values, length limits, and patterns for validation
205
+ - **Consider data types**: Choose the most appropriate type for your data
206
+
207
+ ### Performance
208
+ - **Batch updates**: Use `useUpdateCustomFieldValues` to update multiple fields at once
209
+ - **Handle loading states**: Always show loading indicators while fetching data
210
+ - **Error handling**: Implement proper error handling for failed updates
211
+
212
+ ### Data Management
213
+ - **Plan for migration**: Consider existing data when changing field definitions
214
+ - **Use appropriate scopes**: Choose the right scope type for your data sharing needs
215
+ - **Validate user input**: Implement client-side validation matching your field constraints
216
+
217
+ ## 6. Common Use Cases
218
+
219
+ ### Fleet Management
220
+ ```typescript
221
+ // Store vehicle-specific information
222
+ {
223
+ type: 'NUMBER',
224
+ entityType: 'ASSET',
225
+ key: 'fuelEfficiency',
226
+ translations: [{ language: 'en', title: 'Fuel Efficiency', description: 'Miles per gallon' }],
227
+ unitSi: 'L/100km',
228
+ unitUs: 'mpg'
229
+ }
230
+ ```
231
+
232
+ ### Asset Utilization
233
+ ```typescript
234
+ // Track custom performance metrics
235
+ {
236
+ type: 'JSON',
237
+ entityType: 'ASSET',
238
+ key: 'performanceMetrics',
239
+ translations: [{ language: 'en', title: 'Performance Data', description: 'Custom metrics' }]
240
+ }
241
+ ```
242
+
243
+ ### Driver Safety
244
+ ```typescript
245
+ // Store safety-related information
246
+ {
247
+ type: 'DROPDOWN',
248
+ entityType: 'ASSET',
249
+ key: 'safetyRating',
250
+ translations: [{ language: 'en', title: 'Safety Rating', description: 'Driver safety score' }],
251
+ allValues: ['Excellent', 'Good', 'Fair', 'Poor']
252
+ }
253
+ ```
254
+
255
+ ## 7. Local Development
256
+
257
+ - Custom fields are available immediately in local development
258
+ - Changes to field definitions require app redeployment
259
+ - Test with various data types and edge cases
260
+ - Verify validation rules work as expected
261
+
262
+ ## 8. Deployment Considerations
263
+
264
+ - **Field keys are immutable**: Cannot be changed after creation
265
+ - **Definition updates**: Changing field definitions affects all existing data
266
+ - **Migration planning**: Consider data migration when modifying existing fields
267
+ - **Approval process**: Custom field changes require app approval and marketplace submission
268
+
269
+ ## 9. Error Handling
270
+
271
+ ```typescript
272
+ const CustomFieldsComponent = () => {
273
+ const { fields, loading, error } = useCustomFieldsValueAndDefinition({
274
+ entityType: "ASSET",
275
+ entityId: assetId,
276
+ systemOfMeasurement: systemOfMeasurement ?? "SI",
277
+ });
278
+
279
+ if (loading) return <Spinner />;
280
+ if (error) return <ErrorState message="Failed to load custom fields" />;
281
+
282
+ // Render fields...
283
+ };
284
+ ```
285
+
286
+ ## 10. Integration with Trackunit Components
287
+
288
+ Always use the provided `CustomField` component for consistent UI:
289
+
290
+ ```typescript
291
+ import { CustomField } from '@trackunit/react-core-contexts-host';
292
+
293
+ // This handles all field types automatically
294
+ <CustomField
295
+ control={control}
296
+ field={field}
297
+ formState={formState}
298
+ isEditable={isEditable}
299
+ register={register}
300
+ setValue={setValue}
301
+ unitPreference={systemOfMeasurement}
302
+ />
303
+ ```
304
+
305
+ ## References
306
+
307
+ - [Save Data from Your App](https://developers.trackunit.com/docs/save-data-from-your-app)
308
+ - [Defining a Custom Field](https://developers.trackunit.com/docs/defining-a-custom-field)
309
+ - [Using a Custom Field](https://developers.trackunit.com/docs/using-a-custom-field)
310
+ - [Local Development](https://developers.trackunit.com/docs/local-development)
311
+ - [Deploying a New Custom Field](https://developers.trackunit.com/docs/deploying-a-new-custom-field)
@@ -5,6 +5,23 @@ alwaysApply: true
5
5
  ---
6
6
  You are currently in an IrisX App workspace. Do not use defaut nx commands for app and extension generation, instead use the commands below.
7
7
 
8
+ ## Building Real Applications
9
+
10
+ You are building real applications that work with actual Trackunit data. Key principles:
11
+
12
+ - Never mock data unless explicitly requested by the user
13
+ - Always use Trackunit's GraphQL API to fetch real data
14
+ - If data is not available via GraphQL, ask the user how they want to obtain the data
15
+ - Only use mock data if the user explicitly requests it for testing purposes
16
+
17
+ When building any feature:
18
+ 1. Identify what data you need
19
+ 2. Check if it's available via Trackunit's GraphQL API
20
+ 3. If available, use the GraphQL API (see GraphQL rules for implementation)
21
+ 4. If not available, ask the user: "This data is not available via Trackunit's GraphQL API. How would you like to obtain [specific data description]?"
22
+
23
+ ## IrisX App Development Guidelines
24
+
8
25
  Key Points:
9
26
  1. An IrisX App is a container that packages multiple extensions with marketplace metadata and app manifest
10
27
  2. Extensions are React applications that integrate into specific points in Trackunit Manager
@@ -48,6 +65,16 @@ cspHeader: {
48
65
 
49
66
  For more information: [Embedding IrisX Dashboards in Apps](mdc:https:/developers.trackunit.com/docs/analytics-dashboards-in-apps#troubleshooting)
50
67
 
68
+
69
+ ## Runtime Hooks Available
70
+
71
+ IrisX Apps have access to runtime hooks from `@trackunit/react-core-hooks` that provide context-specific data:
72
+ - `useAssetRuntime()` for asset information in asset-scoped extensions
73
+ - `useCustomerRuntime()` for customer data
74
+ - `useEventRuntime()` for event details
75
+ - `useSiteRuntime()` for site information
76
+ - Plus navigation, user context, and utility hooks (see react-core-hooks rule for full list)
77
+
51
78
  DO NOT use generic React/Node.js commands for project creation or management - always use the Trackunit IrisX App SDK commands.
52
79
 
53
80
 
@@ -0,0 +1,160 @@
1
+ ---
2
+ description: Use these rules whne building App SDK specific React Hook, Runtime Hooks
3
+ alwaysApply: false
4
+ ---
5
+ ## React Core Hooks Library
6
+
7
+ Available hooks from `@trackunit/react-core-hooks` for IrisX App development.
8
+
9
+ ### Runtime Hooks
10
+
11
+ **`useAssetRuntime()`** - Access asset information in asset-scoped extensions
12
+ ```typescript
13
+ const { assetInfo, loading, error } = useAssetRuntime();
14
+ // assetInfo.assetId, assetInfo.name, etc.
15
+ ```
16
+
17
+ **`useCustomerRuntime()`** - Access customer information
18
+ ```typescript
19
+ const { customerInfo, loading, error } = useCustomerRuntime();
20
+ // customerInfo.customerId, customerInfo.name, etc.
21
+ ```
22
+
23
+ **`useEventRuntime()`** - Access event information in event-scoped extensions
24
+ ```typescript
25
+ const { eventInfo, loading, error } = useEventRuntime();
26
+ // eventInfo.eventId, eventInfo.type, etc.
27
+ ```
28
+
29
+ **`useSiteRuntime()`** - Access site information in site-scoped extensions
30
+ ```typescript
31
+ const { siteInfo, loading, error } = useSiteRuntime();
32
+ // siteInfo.siteId, siteInfo.name, etc.
33
+ ```
34
+
35
+ **`useIrisAppName()`** - Get current app name
36
+ ```typescript
37
+ const { appName, loading, error } = useIrisAppName();
38
+ ```
39
+
40
+ **`useIrisAppId()`** - Get current app ID
41
+ ```typescript
42
+ const { irisAppId, loading, error } = useIrisAppId();
43
+ ```
44
+
45
+ ### Navigation & User Context
46
+
47
+ **`useNavigateInHost()`** - Navigate within Trackunit Manager
48
+ ```typescript
49
+ const { navigateToAsset, navigateToSite } = useNavigateInHost();
50
+ navigateToAsset({ assetId: "123" });
51
+ ```
52
+
53
+ **`useHasAccessTo()`** - Check user permissions
54
+ ```typescript
55
+ const hasAccess = useHasAccessTo({ permission: "assets.read" });
56
+ ```
57
+
58
+ **`useCurrentUser()`** - Access current user information
59
+ ```typescript
60
+ const { user } = useCurrentUser();
61
+ // user.id, user.email, etc.
62
+ ```
63
+
64
+ **`useCurrentUserLanguage()`** - Get/set user language preference
65
+ ```typescript
66
+ const { language, setLanguage } = useCurrentUserLanguage();
67
+ ```
68
+
69
+ **`useCurrentUserTimeZonePreference()`** - Get user timezone
70
+ ```typescript
71
+ const { timeZone } = useCurrentUserTimeZonePreference();
72
+ ```
73
+
74
+ **`useToken()`** - Access authentication token
75
+ ```typescript
76
+ const { token } = useToken();
77
+ ```
78
+
79
+ ### UI & Interaction
80
+
81
+ **`useToast()`** - Show toast notifications
82
+ ```typescript
83
+ const { showToast } = useToast();
84
+ showToast({ message: "Success!", type: "success" });
85
+ ```
86
+
87
+ **`useConfirmationDialog()`** - Show confirmation dialogs
88
+ ```typescript
89
+ const { showConfirmationDialog } = useConfirmationDialog();
90
+ const confirmed = await showConfirmationDialog({ message: "Are you sure?" });
91
+ ```
92
+
93
+ **`useModalDialogContext()`** - Manage modal dialogs
94
+ ```typescript
95
+ const { openModal, closeModal } = useModalDialogContext();
96
+ ```
97
+
98
+ **`useErrorHandler()`** - Handle errors consistently
99
+ ```typescript
100
+ const { handleError } = useErrorHandler();
101
+ handleError(new Error("Something went wrong"));
102
+ ```
103
+
104
+ ### Data & State Management
105
+
106
+ **`useLocalStorage()`** - Persist state in localStorage
107
+ ```typescript
108
+ const [value, setValue, reset] = useLocalStorage({
109
+ key: "myKey",
110
+ defaultState: { count: 0 }
111
+ });
112
+ ```
113
+
114
+ **`useLocalStorageReducer()`** - localStorage with reducer pattern
115
+ ```typescript
116
+ const [state, dispatch] = useLocalStorageReducer({
117
+ key: "counterState",
118
+ defaultState: { count: 0 },
119
+ reducer: (state, action) => { /* reducer logic */ }
120
+ });
121
+ ```
122
+
123
+ **`useAssetSorting()`** - Asset sorting functionality
124
+ ```typescript
125
+ const { sortField, sortDirection, setSorting } = useAssetSorting();
126
+ ```
127
+
128
+ **`useFilterBarContext()`** - Filter bar state management
129
+ ```typescript
130
+ const { filters, setFilters, clearFilters } = useFilterBarContext();
131
+ ```
132
+
133
+ **`useTimeRange()`** - Time range selection
134
+ ```typescript
135
+ const { timeRange, setTimeRange } = useTimeRange();
136
+ ```
137
+
138
+
139
+
140
+ ### Widget Configuration
141
+
142
+ **`useWidgetConfig()`** - Access widget configuration (synchronous)
143
+ ```typescript
144
+ const config = useWidgetConfig();
145
+ ```
146
+
147
+ **`useWidgetConfigAsync()`** - Access widget configuration (asynchronous)
148
+ ```typescript
149
+ const { getConfig, setConfig } = useWidgetConfigAsync();
150
+ const config = await getConfig();
151
+ ```
152
+
153
+
154
+
155
+
156
+
157
+ All hooks require their respective providers to be set up in your app's component tree.
158
+
159
+ ```
160
+
@@ -0,0 +1,325 @@
1
+ ---
2
+ description: Use these rules when building Widget Extensions for an Iris App.
3
+ globs:
4
+ alwaysApply: false
5
+ ---
6
+
7
+ # Widget Extensions
8
+
9
+ Widget extensions are specialized components that integrate into Trackunit Manager's dashboard systems, providing customizable UI elements for data visualization and user interaction.
10
+
11
+ ## Key Concepts
12
+
13
+ 1. **Widget Extensions** are React applications that render within widget containers on various pages
14
+ 2. **Supported Locations** define where widgets can be placed in Trackunit Manager
15
+ 3. **Widget Types** categorize widgets by their primary purpose and visual style
16
+ 4. **Filter Support** enables widgets to respond to user-applied filters
17
+ 5. **Configuration** allows widgets to store and manage their own settings
18
+
19
+ ## Creating Widget Extensions
20
+
21
+ ### Generate a new widget extension:
22
+ ```bash
23
+ npx nx g @trackunit/iris-app:extend --name=[my-widget-name] --directory=[feature] --type=WIDGET_EXTENSION
24
+ ```
25
+
26
+ This creates:
27
+ - `extension-manifest.ts` - Widget configuration and metadata
28
+ - `src/app.tsx` - Main widget component
29
+ - `src/editDialog.tsx` - Configuration dialog (if `hasEditDialog: true`)
30
+
31
+ ## Widget Extension Manifest
32
+
33
+ The manifest defines widget metadata, appearance, and behavior:
34
+
35
+ ```typescript
36
+ import { WidgetExtensionManifest } from '@trackunit/iris-app-api';
37
+
38
+ const widgetExtensionManifest: WidgetExtensionManifest = {
39
+ id: "my-widget-id",
40
+ type: "WIDGET_EXTENSION",
41
+ sourceRoot: "libs/[feature]/[widget-name]/src",
42
+
43
+ widgetType: "CHART", // Required: Widget classification
44
+
45
+ size: {
46
+ default: {
47
+ width: 2, // 1-6 grid units
48
+ height: 2, // 1-6 grid units
49
+ },
50
+ allowFullWidth: false, // Optional: Allow full-width expansion
51
+ },
52
+
53
+ header: {
54
+ name: "My Widget", // Display name
55
+ image: { name: "ChartBar" }, // Icon (IconByName or IconByPath)
56
+ hasEditDialog: true, // Optional: Enable edit button
57
+ },
58
+
59
+ footer: { // Optional: Footer with link
60
+ linkDescription: "View details",
61
+ link: "/my-app/details",
62
+ poweredByImage: { path: "/assets/logo.svg" },
63
+ },
64
+
65
+ preview: { // Optional: Description for widget drawer
66
+ description: "Displays chart data for analysis",
67
+ },
68
+
69
+ supportedLocations: ["MY_HOME", "SITE_HOME"],
70
+
71
+ supportedFilters: { // Optional: Enable filter support
72
+ TIME_RANGE: { include: ["ALL"] },
73
+ ASSETS: { include: ["assetType", "brands", "models"] },
74
+ CUSTOMERS: { include: ["customerType", "criticality"] },
75
+ SITES: { include: ["siteType", "location"] },
76
+ },
77
+ };
78
+
79
+ export default widgetExtensionManifest;
80
+ ```
81
+
82
+ ## Widget Types
83
+
84
+ Available widget types that determine visual presentation:
85
+
86
+ - **`"KPI"`** - Single metric displays (typically 1x1 size)
87
+ - **`"CHART"`** - Data visualization widgets (charts, graphs)
88
+ - **`"LIST"`** - Tabular or list-based data presentation
89
+ - **`"MAP"`** - Geographic or spatial data visualization
90
+ - **`"OTHER"`** - Custom or specialized widgets
91
+
92
+ ## Supported Locations
93
+
94
+ Widgets can be placed in these locations:
95
+
96
+ - **`"MY_HOME"`** - User's personal dashboard
97
+ - **`"SITE_HOME"`** - Site-specific dashboards
98
+
99
+ ## Widget Development
100
+
101
+ ### Basic Widget Component
102
+
103
+ ```typescript
104
+ import { useWidgetConfig } from '@trackunit/react-core-hooks';
105
+
106
+ export const App = () => {
107
+ const {
108
+ data,
109
+ setData,
110
+ dataVersion,
111
+ title,
112
+ setTitle,
113
+ filters,
114
+ timeRange,
115
+ isEditMode,
116
+ openEditMode,
117
+ closeEditMode
118
+ } = useWidgetConfig();
119
+
120
+ if (isEditMode) {
121
+ return <EditDialog />;
122
+ }
123
+
124
+ return (
125
+ <div>
126
+ <h2>{title}</h2>
127
+ {/* Widget content */}
128
+ </div>
129
+ );
130
+ };
131
+ ```
132
+
133
+ ### Widget Configuration Hook
134
+
135
+ The `useWidgetConfig()` hook provides:
136
+
137
+ **Data Management:**
138
+ - `data: Record<string, unknown>` - Widget configuration data
139
+ - `setData: (data: Record<string, unknown>, version: number) => Promise<void>` - Update widget data
140
+ - `dataVersion: number` - Current data version for migrations
141
+
142
+ **Title Management:**
143
+ - `title: string` - Widget title
144
+ - `setTitle: (title: string) => Promise<void>` - Update widget title
145
+
146
+ **Context Information:**
147
+ - `filters: { assetsFilterBarValues, customersFilterBarValues, sitesFilterBarValues }` - Applied filters
148
+ - `timeRange: { startMsInEpoch, endMsInEpoch }` - Selected time range
149
+ - `isEditMode: boolean` - Whether widget is in edit mode
150
+
151
+ **Edit Mode Control:**
152
+ - `openEditMode: () => Promise<void>` - Enter edit mode
153
+ - `closeEditMode: () => Promise<void>` - Exit edit mode
154
+
155
+ ### Widget with Edit Dialog
156
+
157
+ ```typescript
158
+ import { useWidgetConfig } from '@trackunit/react-core-hooks';
159
+ import { WidgetEditBody } from '@trackunit/react-widgets';
160
+
161
+ export const App = () => {
162
+ const { isEditMode, data, setData, closeEditMode } = useWidgetConfig();
163
+
164
+ if (isEditMode) {
165
+ return (
166
+ <WidgetEditBody
167
+ onSave={(newData) => {
168
+ setData(newData, 1);
169
+ closeEditMode();
170
+ }}
171
+ onCancel={closeEditMode}
172
+ initialData={data}
173
+ />
174
+ );
175
+ }
176
+
177
+ return <MainWidgetContent />;
178
+ };
179
+ ```
180
+
181
+ ## Filter Support
182
+
183
+ ### Supported Filter Types
184
+
185
+ Configure which filters your widget responds to:
186
+
187
+ ```typescript
188
+ supportedFilters: {
189
+ // Time range filtering
190
+ TIME_RANGE: { include: ["ALL"] },
191
+
192
+ // Asset filtering
193
+ ASSETS: {
194
+ include: [
195
+ "assetType", "brands", "models", "types",
196
+ "activity", "criticality", "lastSeen", "groups",
197
+ "siteIds", "customerIds", "ownerAccountIds",
198
+ // Add custom fields support
199
+ ...allAssetFilterKeysWithCustomFields
200
+ ]
201
+ },
202
+
203
+ // Customer filtering
204
+ CUSTOMERS: {
205
+ include: [
206
+ "customerType", "criticality", "servicePlan",
207
+ ...allCustomerFilterKeysWithCustomFields
208
+ ]
209
+ },
210
+
211
+ // Site filtering
212
+ SITES: {
213
+ include: [
214
+ "siteType", "location", "area",
215
+ ...allSiteFilterKeysWithCustomFields
216
+ ]
217
+ },
218
+ }
219
+ ```
220
+
221
+ ### Using Filters in Widgets
222
+
223
+ ```typescript
224
+ const { filters, timeRange } = useWidgetConfig();
225
+
226
+ // Access filter values
227
+ const selectedAssets = filters.assetsFilterBarValues?.assetType;
228
+ const selectedCustomers = filters.customersFilterBarValues?.customerType;
229
+ const selectedSites = filters.sitesFilterBarValues?.siteType;
230
+
231
+ // Use time range
232
+ const startTime = timeRange?.startMsInEpoch;
233
+ const endTime = timeRange?.endMsInEpoch;
234
+ ```
235
+
236
+ ## Widget Lifecycle
237
+
238
+ ### 1. Widget Creation
239
+ - Generated via `@trackunit/iris-app:extend` command
240
+ - Manifest defines widget metadata and capabilities
241
+ - React component handles rendering and user interaction
242
+
243
+ ### 2. Widget Registration
244
+ - Widget appears in the widget drawer based on `supportedLocations`
245
+ - Users can add widgets to their dashboards
246
+ - Widget configuration is persisted automatically
247
+
248
+ ### 3. Widget Runtime
249
+ - Widget receives context (filters, time range, etc.)
250
+ - Widget can store and retrieve configuration data
251
+ - Widget responds to user interactions and filter changes
252
+
253
+ ## Best Practices
254
+
255
+ ### Widget Design
256
+ 1. **Responsive Layout** - Design for different grid sizes
257
+ 2. **Loading States** - Show loading indicators for async operations
258
+ 3. **Error Handling** - Gracefully handle data loading errors
259
+ 4. **Empty States** - Provide meaningful empty state messages
260
+
261
+ ### Data Management
262
+ 1. **Use Real Data** - Always fetch from Trackunit GraphQL API
263
+ 2. **Respect Filters** - Apply user-selected filters to your data queries
264
+ 3. **Efficient Updates** - Only update widget data when necessary
265
+ 4. **Version Control** - Use `dataVersion` for configuration migrations
266
+
267
+ ### Configuration
268
+ 1. **Meaningful Defaults** - Provide sensible default configurations
269
+ 2. **Validation** - Validate user input in edit dialogs
270
+ 3. **Persistence** - Use `setData()` to save configuration changes
271
+ 4. **User Experience** - Keep configuration UI simple and intuitive
272
+
273
+ ## Example: KPI Widget
274
+
275
+ ```typescript
276
+ import { useWidgetConfig } from '@trackunit/react-core-hooks';
277
+ import { useQuery } from '@apollo/client';
278
+
279
+ export const App = () => {
280
+ const { filters, timeRange, data } = useWidgetConfig();
281
+
282
+ const { data: kpiData, loading } = useQuery(MY_KPI_QUERY, {
283
+ variables: {
284
+ filters: {
285
+ assetIds: filters.assetsFilterBarValues?.assetIds,
286
+ timeRange: {
287
+ start: timeRange?.startMsInEpoch,
288
+ end: timeRange?.endMsInEpoch,
289
+ },
290
+ },
291
+ },
292
+ });
293
+
294
+ if (loading) return <Spinner />;
295
+
296
+ return (
297
+ <div className="kpi-widget">
298
+ <div className="kpi-value">{kpiData?.totalCount}</div>
299
+ <div className="kpi-label">Total Assets</div>
300
+ </div>
301
+ );
302
+ };
303
+ ```
304
+
305
+ ## Common Patterns
306
+
307
+ ### Chart Widget
308
+ - Use `widgetType: "CHART"`
309
+ - Implement responsive chart sizing
310
+ - Support time range filtering
311
+ - Show data trends and comparisons
312
+
313
+ ### List Widget
314
+ - Use `widgetType: "LIST"`
315
+ - Implement pagination for large datasets
316
+ - Support asset/customer filtering
317
+ - Provide links to detailed views
318
+
319
+ ### Map Widget
320
+ - Use `widgetType: "MAP"`
321
+ - Set `allowFullWidth: true` for better visibility
322
+ - Support location-based filtering
323
+ - Show asset positions and movement
324
+
325
+ DO NOT use generic React widget libraries - always use the Trackunit IrisX App SDK widget patterns and components.