@trackunit/iris-app 1.12.7 → 1.12.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 +30 -0
- package/package.json +3 -3
- package/src/generators/ai-agent-sync/README.md +5 -2
- package/src/generators/ai-agent-sync/generator.d.ts +1 -1
- package/src/generators/ai-agent-sync/generator.js +29 -4
- package/src/generators/ai-agent-sync/generator.js.map +1 -1
- package/src/generators/preset/files/.agents/skills/browser-testing/SKILL.md +193 -0
- package/src/generators/preset/files/.agents/skills/create-app/SKILL.md +191 -0
- package/src/generators/preset/files/.agents/skills/customfields/SKILL.md +239 -0
- package/src/generators/preset/files/.agents/skills/graphql/SKILL.md +147 -0
- package/src/generators/preset/files/.agents/skills/graphql-timeseries/SKILL.md +193 -0
- package/src/generators/preset/files/.agents/skills/irisx-app-sdk/SKILL.md +116 -0
- package/src/generators/preset/files/.agents/skills/react-core-hooks/SKILL.md +215 -0
- package/src/generators/preset/files/.agents/skills/tables-and-sorting/SKILL.md +122 -0
- package/src/generators/preset/files/.agents/skills/widget-extensions/SKILL.md +245 -0
- package/src/generators/preset/files/.cursor/mcp.json +4 -0
- package/src/generators/preset/root-files/AGENTS.md +43 -0
- package/src/generators/preset/root-files/CLAUDE.md +17 -0
- package/src/generators/preset/files/.cursor/commands/create-app.md +0 -226
- package/src/generators/preset/files/.cursor/rules/browser-irisx-development.mdc +0 -246
- package/src/generators/preset/files/.cursor/rules/graphql-timeseries.md +0 -260
- package/src/generators/preset/files/.cursor/rules/irisx-app-sdk-customfields.md +0 -305
- package/src/generators/preset/files/.cursor/rules/irisx-app-sdk-graphql.md +0 -30
- package/src/generators/preset/files/.cursor/rules/irisx-app-sdk.mdc +0 -82
- package/src/generators/preset/files/.cursor/rules/react-core-hooks.md +0 -155
- package/src/generators/preset/files/.cursor/rules/rules-index.mdc +0 -10
- package/src/generators/preset/files/.cursor/rules/structured-development.mdc +0 -86
- package/src/generators/preset/files/.cursor/rules/tables-and-sorting.mdc +0 -126
- package/src/generators/preset/files/.cursor/rules/widget-extensions.md +0 -323
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
## React Core Hooks Library
|
|
2
|
-
|
|
3
|
-
Available hooks from `@trackunit/react-core-hooks` for IrisX App development.
|
|
4
|
-
|
|
5
|
-
### Runtime Hooks
|
|
6
|
-
|
|
7
|
-
**`useAssetRuntime()`** - Access asset information in asset-scoped extensions
|
|
8
|
-
```typescript
|
|
9
|
-
const { assetInfo, loading, error } = useAssetRuntime();
|
|
10
|
-
// assetInfo.assetId, assetInfo.name, etc.
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
**`useCustomerRuntime()`** - Access customer information
|
|
14
|
-
```typescript
|
|
15
|
-
const { customerInfo, loading, error } = useCustomerRuntime();
|
|
16
|
-
// customerInfo.customerId, customerInfo.name, etc.
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
**`useEventRuntime()`** - Access event information in event-scoped extensions
|
|
20
|
-
```typescript
|
|
21
|
-
const { eventInfo, loading, error } = useEventRuntime();
|
|
22
|
-
// eventInfo.eventId, eventInfo.type, etc.
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
**`useSiteRuntime()`** - Access site information in site-scoped extensions
|
|
26
|
-
```typescript
|
|
27
|
-
const { siteInfo, loading, error } = useSiteRuntime();
|
|
28
|
-
// siteInfo.siteId, siteInfo.name, etc.
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
**`useIrisAppName()`** - Get current app name
|
|
32
|
-
```typescript
|
|
33
|
-
const { appName, loading, error } = useIrisAppName();
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
**`useIrisAppId()`** - Get current app ID
|
|
37
|
-
```typescript
|
|
38
|
-
const { irisAppId, loading, error } = useIrisAppId();
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
### Navigation & User Context
|
|
42
|
-
|
|
43
|
-
**`useNavigateInHost()`** - Navigate within Trackunit Manager
|
|
44
|
-
```typescript
|
|
45
|
-
const { navigateToAsset, navigateToSite } = useNavigateInHost();
|
|
46
|
-
navigateToAsset({ assetId: "123" });
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
**`useHasAccessTo()`** - Check user permissions
|
|
50
|
-
```typescript
|
|
51
|
-
const hasAccess = useHasAccessTo({ permission: "assets.read" });
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
**`useCurrentUser()`** - Access current user information
|
|
55
|
-
```typescript
|
|
56
|
-
const { user } = useCurrentUser();
|
|
57
|
-
// user.id, user.email, etc.
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
**`useCurrentUserLanguage()`** - Get/set user language preference
|
|
61
|
-
```typescript
|
|
62
|
-
const { language, setLanguage } = useCurrentUserLanguage();
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
**`useCurrentUserTimeZonePreference()`** - Get user timezone
|
|
66
|
-
```typescript
|
|
67
|
-
const { timeZone } = useCurrentUserTimeZonePreference();
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
**`useToken()`** - Access authentication token
|
|
71
|
-
```typescript
|
|
72
|
-
const { token } = useToken();
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
### UI & Interaction
|
|
76
|
-
|
|
77
|
-
**`useToast()`** - Show toast notifications
|
|
78
|
-
```typescript
|
|
79
|
-
const { showToast } = useToast();
|
|
80
|
-
showToast({ message: "Success!", type: "success" });
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
**`useConfirmationDialog()`** - Show confirmation dialogs
|
|
84
|
-
```typescript
|
|
85
|
-
const { showConfirmationDialog } = useConfirmationDialog();
|
|
86
|
-
const confirmed = await showConfirmationDialog({ message: "Are you sure?" });
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
**`useModalDialogContext()`** - Manage modal dialogs
|
|
90
|
-
```typescript
|
|
91
|
-
const { openModal, closeModal } = useModalDialogContext();
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
**`useErrorHandler()`** - Handle errors consistently
|
|
95
|
-
```typescript
|
|
96
|
-
const { handleError } = useErrorHandler();
|
|
97
|
-
handleError(new Error("Something went wrong"));
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
### Data & State Management
|
|
101
|
-
|
|
102
|
-
**`useLocalStorage()`** - Persist state in localStorage
|
|
103
|
-
```typescript
|
|
104
|
-
const [value, setValue, reset] = useLocalStorage({
|
|
105
|
-
key: "myKey",
|
|
106
|
-
defaultState: { count: 0 }
|
|
107
|
-
});
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
**`useLocalStorageReducer()`** - localStorage with reducer pattern
|
|
111
|
-
```typescript
|
|
112
|
-
const [state, dispatch] = useLocalStorageReducer({
|
|
113
|
-
key: "counterState",
|
|
114
|
-
defaultState: { count: 0 },
|
|
115
|
-
reducer: (state, action) => { /* reducer logic */ }
|
|
116
|
-
});
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
**`useAssetSorting()`** - Asset sorting functionality
|
|
120
|
-
```typescript
|
|
121
|
-
const { sortField, sortDirection, setSorting } = useAssetSorting();
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
**`useFilterBarContext()`** - Filter bar state management
|
|
125
|
-
```typescript
|
|
126
|
-
const { filters, setFilters, clearFilters } = useFilterBarContext();
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
**`useTimeRange()`** - Time range selection
|
|
130
|
-
```typescript
|
|
131
|
-
const { timeRange, setTimeRange } = useTimeRange();
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
### Widget Configuration
|
|
137
|
-
|
|
138
|
-
**`useWidgetConfig()`** - Access widget configuration (synchronous)
|
|
139
|
-
```typescript
|
|
140
|
-
const config = useWidgetConfig();
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
**`useWidgetConfigAsync()`** - Access widget configuration (asynchronous)
|
|
144
|
-
```typescript
|
|
145
|
-
const { getData, setData } = useWidgetConfigAsync();
|
|
146
|
-
const config = await getConfig();
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
All hooks require their respective providers to be set up in your app's component tree.
|
|
154
|
-
|
|
155
|
-
```
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
alwaysApply: true
|
|
3
|
-
---
|
|
4
|
-
This file contains a list of helpful information and context that the agent can reference when working in this codebase. Each entry provides specific guidance or rules for different aspects of the project. You can read these files using the readFile tool if the users prompt seems related.
|
|
5
|
-
|
|
6
|
-
- [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.](./irisx-app-sdk-customfields.md)
|
|
7
|
-
- [This rule covers GraphQL API integration in Trackunit IrisX App SDK workspace. All GraphQL development MUST follow the Trackunit GraphQL API patterns and commands.](./irisx-app-sdk-graphql.md)
|
|
8
|
-
- [Time series API, get time series data from GraphQL. Machine insights data over time.](./graphql-timeseries.md)
|
|
9
|
-
- [Use these rules when building Widget Extensions for an Iris App.](./widget-extensions.md)
|
|
10
|
-
- [Use these rules whne building App SDK specific React Hook, Runtime Hooks](./react-core-hooks.md)
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
alwaysApply: true
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
# Structured Feature Development Documentation
|
|
6
|
-
|
|
7
|
-
## Overview
|
|
8
|
-
When developing new features, maintain concise documentation to track progress, decisions, and implementation details.
|
|
9
|
-
|
|
10
|
-
## Process
|
|
11
|
-
1. **Check for existing documentation**: Look in `/docs` folder for a markdown file named after the feature (e.g., `user-authentication.md`, `shopping-cart.md`)
|
|
12
|
-
2. **Create or update**:
|
|
13
|
-
- If documentation exists: Update it with new requirements and progress
|
|
14
|
-
- If it doesn't exist: Create a new file with the feature name as filename
|
|
15
|
-
3. **Use visual aids**: Include mermaid diagrams for complex flows, architecture, or data relationships
|
|
16
|
-
4. **Keep it current**: Update the documentation as development progresses
|
|
17
|
-
|
|
18
|
-
## Important Guidelines
|
|
19
|
-
- **Be concise**: Keep descriptions brief and to the point
|
|
20
|
-
- **Only document what was requested**: Do not make up future steps or tasks that weren't part of the user's suggestions, unless explitly asked.
|
|
21
|
-
- **Focus on implementation**: Document what was actually built, not theoretical features
|
|
22
|
-
|
|
23
|
-
## Task Analysis Process
|
|
24
|
-
1. **Analyze the user's request**: Understand exactly what the user asked for
|
|
25
|
-
2. **Break down into tasks**: Split the request into specific, actionable tasks
|
|
26
|
-
3. **Document only requested work**: Only include tasks that directly relate to the user's request
|
|
27
|
-
4. **Optional sections**: Todo and In Progress sections are OPTIONAL - only include them if there are actual remaining tasks from the user's request
|
|
28
|
-
|
|
29
|
-
## Required File Structure
|
|
30
|
-
|
|
31
|
-
### File Naming
|
|
32
|
-
Use kebab-case naming convention: `feature-name.md`
|
|
33
|
-
|
|
34
|
-
### Template Structure
|
|
35
|
-
|
|
36
|
-
```markdown
|
|
37
|
-
# Feature Name
|
|
38
|
-
|
|
39
|
-
## Description
|
|
40
|
-
Brief overview of what the feature does and why it's needed.
|
|
41
|
-
|
|
42
|
-
## Architecture
|
|
43
|
-
Technical approach and key design decisions.
|
|
44
|
-
|
|
45
|
-
## Progress Tracking
|
|
46
|
-
|
|
47
|
-
### ✅ Completed Steps
|
|
48
|
-
- [x] Task 1: Description
|
|
49
|
-
- [x] Task 2: Description
|
|
50
|
-
|
|
51
|
-
### 🔄 In Progress
|
|
52
|
-
*OPTIONAL: Only include if there are actual tasks from the user's request that are currently being worked on*
|
|
53
|
-
- [ ] Task 3: Description
|
|
54
|
-
- [ ] Task 4: Description
|
|
55
|
-
|
|
56
|
-
### 📝 Todo
|
|
57
|
-
*OPTIONAL: Only include if there are remaining tasks from the user's request that haven't been started*
|
|
58
|
-
- [ ] Task 5: Description
|
|
59
|
-
- [ ] Task 6: Description
|
|
60
|
-
|
|
61
|
-
## Technologies Used
|
|
62
|
-
- **Technology Name**: Brief description of usage
|
|
63
|
-
- **Library Name**: Version and purpose
|
|
64
|
-
|
|
65
|
-
## Implementation Details
|
|
66
|
-
Key technical specifics:
|
|
67
|
-
- API endpoints used
|
|
68
|
-
- Configuration requirements
|
|
69
|
-
- Performance considerations
|
|
70
|
-
|
|
71
|
-
## Good To Know
|
|
72
|
-
Important findings, decisions, and gotchas:
|
|
73
|
-
- Quirks or workarounds implemented
|
|
74
|
-
- Alternative approaches considered
|
|
75
|
-
- Performance implications
|
|
76
|
-
- Security considerations
|
|
77
|
-
- Testing strategies
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
## Best Practices
|
|
81
|
-
- Keep descriptions brief and focused
|
|
82
|
-
- Only document requested features and tasks
|
|
83
|
-
- Include mermaid diagrams for complex flows
|
|
84
|
-
- Document actual implementation details
|
|
85
|
-
- Update progress as development happens
|
|
86
|
-
- **CRITICAL**: Never add tasks or features that weren't explicitly mentioned in the user's request
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
alwaysApply: false
|
|
3
|
-
---
|
|
4
|
-
# 📊 Trackunit Table with Filtering & Sorting
|
|
5
|
-
|
|
6
|
-
Create interactive data tables with server-side filtering, sorting, and pagination using Trackunit components and GraphQL.
|
|
7
|
-
|
|
8
|
-
## 🎯 When to Apply
|
|
9
|
-
- Building asset management interfaces
|
|
10
|
-
- Creating user administration tables
|
|
11
|
-
- Developing dashboard data grids
|
|
12
|
-
- Implementing reporting interfaces with large datasets
|
|
13
|
-
- Any UI requiring interactive table features (filter/sort/paginate)
|
|
14
|
-
|
|
15
|
-
## 🔧 Required Imports
|
|
16
|
-
```typescript
|
|
17
|
-
import { Table, useTable, TextCell, DateCell } from '@trackunit/react-table';
|
|
18
|
-
import { FilterBar, useFilterBar } from '@trackunit/filters-filter-bar';
|
|
19
|
-
import { usePaginationQuery } from '@trackunit/react-graphql-hooks';
|
|
20
|
-
import { createColumnHelper, SortingState } from '@tanstack/react-table';
|
|
21
|
-
import { useBrandFilter, useModelFilter } from '@trackunit/filters-asset-filters';
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
## ✅ Valid Implementation
|
|
25
|
-
|
|
26
|
-
```typescript
|
|
27
|
-
const AssetTable = () => {
|
|
28
|
-
// Setup filters with proper naming
|
|
29
|
-
const filterBar = useFilterBar({
|
|
30
|
-
name: "assetFilters",
|
|
31
|
-
filterBarDefinition: {
|
|
32
|
-
brand: useBrandFilter(),
|
|
33
|
-
model: useModelFilter()
|
|
34
|
-
}
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
// Server-side sorting state
|
|
38
|
-
const [sorting, setSorting] = useState<SortingState>([]);
|
|
39
|
-
|
|
40
|
-
// GraphQL query with filters
|
|
41
|
-
const { data, loading, error, pagination } = usePaginationQuery(GetAssetsDocument, {
|
|
42
|
-
variables: { first: 50, ...filterBar.selectedFilters },
|
|
43
|
-
pageSize: 50,
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
// Transform data with useMemo
|
|
47
|
-
const assets = useMemo(() =>
|
|
48
|
-
data?.assets.edges.map(edge => ({ ...edge.node, id: edge.node.id })) ?? [],
|
|
49
|
-
[data]
|
|
50
|
-
);
|
|
51
|
-
|
|
52
|
-
// Define columns with proper cell components
|
|
53
|
-
const columns = useMemo(() => [
|
|
54
|
-
columnHelper.accessor('name', {
|
|
55
|
-
cell: ({ row: { original } }) => <TextCell text={original.name} />,
|
|
56
|
-
header: "Asset Name",
|
|
57
|
-
enableSorting: true,
|
|
58
|
-
}),
|
|
59
|
-
columnHelper.accessor('createdAt', {
|
|
60
|
-
cell: ({ row: { original } }) => <DateCell date={original.createdAt} />,
|
|
61
|
-
header: "Created",
|
|
62
|
-
enableSorting: true,
|
|
63
|
-
}),
|
|
64
|
-
], []);
|
|
65
|
-
|
|
66
|
-
// Setup table with manual sorting
|
|
67
|
-
const { table } = useTable({
|
|
68
|
-
data: assets,
|
|
69
|
-
columns,
|
|
70
|
-
enableSorting: true,
|
|
71
|
-
manualSorting: true,
|
|
72
|
-
state: { sorting },
|
|
73
|
-
onSortingChange: setSorting,
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
return (
|
|
77
|
-
<>
|
|
78
|
-
<FilterBar filterBar={filterBar} />
|
|
79
|
-
<Table
|
|
80
|
-
table={table}
|
|
81
|
-
loading={loading}
|
|
82
|
-
error={error}
|
|
83
|
-
pagination={pagination}
|
|
84
|
-
onRowClick={(row) => navigateToAsset(row.original.id)}
|
|
85
|
-
/>
|
|
86
|
-
</>
|
|
87
|
-
);
|
|
88
|
-
};
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
## ❌ Invalid Implementation
|
|
92
|
-
|
|
93
|
-
```typescript
|
|
94
|
-
// DON'T: Missing useMemo optimization
|
|
95
|
-
const columns = [
|
|
96
|
-
columnHelper.accessor('name', {
|
|
97
|
-
cell: ({ getValue }) => getValue(), // Wrong: Use TextCell component
|
|
98
|
-
header: "Name"
|
|
99
|
-
})
|
|
100
|
-
];
|
|
101
|
-
|
|
102
|
-
// DON'T: Client-side sorting on large datasets
|
|
103
|
-
const { table } = useTable({
|
|
104
|
-
data: assets,
|
|
105
|
-
columns,
|
|
106
|
-
enableSorting: true,
|
|
107
|
-
manualSorting: false, // Wrong: Should be true for server-side
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
// DON'T: Missing filter integration
|
|
111
|
-
const { data } = usePaginationQuery(GetAssetsDocument, {
|
|
112
|
-
variables: { first: 50 }, // Wrong: Missing filter variables
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
// DON'T: No row click handler
|
|
116
|
-
<Table table={table} loading={loading} />
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
## 🎯 Key Requirements
|
|
120
|
-
|
|
121
|
-
- **Always use `useMemo`** for data transformation and column definitions
|
|
122
|
-
- **Enable `manualSorting: true`** for datasets > 1000 items
|
|
123
|
-
- **Integrate filter state** with GraphQL variables using spread operator
|
|
124
|
-
- **Use Trackunit cell components** (`TextCell`, `DateCell`) for consistency
|
|
125
|
-
- **Implement `onRowClick`** for navigation or detail views
|
|
126
|
-
- **Sync state properly** between filters, sorting, and GraphQL queries
|
|
@@ -1,323 +0,0 @@
|
|
|
1
|
-
# Widget Extensions
|
|
2
|
-
|
|
3
|
-
Widget extensions are specialized components that integrate into Trackunit Manager's dashboard systems, providing customizable UI elements for data visualization and user interaction.
|
|
4
|
-
|
|
5
|
-
## Key Concepts
|
|
6
|
-
|
|
7
|
-
1. **Widget Extensions** are React applications that render within widget containers on various pages
|
|
8
|
-
2. **Supported Locations** define where widgets can be placed in Trackunit Manager
|
|
9
|
-
3. **Widget Types** categorize widgets by their primary purpose and visual style
|
|
10
|
-
4. **Filter Support** enables widgets to respond to user-applied filters
|
|
11
|
-
5. **Configuration** allows widgets to store and manage their own settings
|
|
12
|
-
|
|
13
|
-
## Creating Widget Extensions
|
|
14
|
-
|
|
15
|
-
### Generate a new widget extension:
|
|
16
|
-
```bash
|
|
17
|
-
npx nx g @trackunit/iris-app:extend --name=[my-widget-name] --app=[app-name] --directory=[feature] --type=WIDGET_EXTENSION
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
This creates:
|
|
21
|
-
- `extension-manifest.ts` - Widget configuration and metadata
|
|
22
|
-
- `src/app.tsx` - Main widget component
|
|
23
|
-
- `src/editDialog.tsx` - Configuration dialog (if `hasEditDialog: true`)
|
|
24
|
-
|
|
25
|
-
## Widget Extension Manifest
|
|
26
|
-
|
|
27
|
-
The manifest defines widget metadata, appearance, and behavior:
|
|
28
|
-
|
|
29
|
-
```typescript
|
|
30
|
-
import { WidgetExtensionManifest } from '@trackunit/iris-app-api';
|
|
31
|
-
|
|
32
|
-
const widgetExtensionManifest: WidgetExtensionManifest = {
|
|
33
|
-
id: "my-widget-id",
|
|
34
|
-
type: "WIDGET_EXTENSION",
|
|
35
|
-
sourceRoot: "libs/[feature]/[widget-name]/src",
|
|
36
|
-
|
|
37
|
-
widgetType: "CHART", // Required: Widget classification
|
|
38
|
-
|
|
39
|
-
size: {
|
|
40
|
-
default: {
|
|
41
|
-
width: 2, // 1-6 grid units
|
|
42
|
-
height: 2, // 1-6 grid units
|
|
43
|
-
},
|
|
44
|
-
allowFullWidth: false, // Optional: Allow full-width expansion
|
|
45
|
-
},
|
|
46
|
-
|
|
47
|
-
header: {
|
|
48
|
-
name: "My Widget", // Display name
|
|
49
|
-
image: { name: "ChartBar" }, // Icon (IconByName or IconByPath)
|
|
50
|
-
hasEditDialog: true, // Optional: Enable edit button
|
|
51
|
-
},
|
|
52
|
-
|
|
53
|
-
footer: { // Optional: Footer with link
|
|
54
|
-
linkDescription: "View details",
|
|
55
|
-
link: "/my-app/details",
|
|
56
|
-
poweredByImage: { path: "/assets/logo.svg" },
|
|
57
|
-
},
|
|
58
|
-
|
|
59
|
-
preview: { // Optional: Description for widget drawer
|
|
60
|
-
description: "Displays chart data for analysis",
|
|
61
|
-
},
|
|
62
|
-
|
|
63
|
-
supportedLocations: ["MY_HOME", "PLAYGROUND"],
|
|
64
|
-
|
|
65
|
-
supportedFilters: { // Optional: Enable filter support
|
|
66
|
-
TIME_RANGE: { include: ["ALL"] },
|
|
67
|
-
ASSETS: { include: ["assetType", "brands", "models"] },
|
|
68
|
-
CUSTOMERS: { include: ["customerType", "criticality"] },
|
|
69
|
-
SITES: { include: ["siteType", "location"] },
|
|
70
|
-
},
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
export default widgetExtensionManifest;
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
## Widget Types
|
|
77
|
-
|
|
78
|
-
Available widget types that determine visual presentation:
|
|
79
|
-
|
|
80
|
-
- **`"KPI"`** - Single metric displays (typically 1x1 size)
|
|
81
|
-
- **`"CHART"`** - Data visualization widgets (charts, graphs)
|
|
82
|
-
- **`"LIST"`** - Tabular or list-based data presentation
|
|
83
|
-
- **`"MAP"`** - Geographic or spatial data visualization
|
|
84
|
-
- **`"OTHER"`** - Custom or specialized widgets
|
|
85
|
-
|
|
86
|
-
## Supported Locations
|
|
87
|
-
|
|
88
|
-
Widgets can be placed in these locations:
|
|
89
|
-
|
|
90
|
-
- **`"MY_HOME"`** - User's personal dashboard
|
|
91
|
-
- **`"SITE_HOME"`** - Site-specific dashboards
|
|
92
|
-
|
|
93
|
-
## Widget Development
|
|
94
|
-
|
|
95
|
-
### Basic Widget Component
|
|
96
|
-
|
|
97
|
-
```typescript
|
|
98
|
-
import { useWidgetConfig } from '@trackunit/react-core-hooks';
|
|
99
|
-
|
|
100
|
-
export const App = () => {
|
|
101
|
-
const {
|
|
102
|
-
data,
|
|
103
|
-
setData,
|
|
104
|
-
dataVersion,
|
|
105
|
-
title,
|
|
106
|
-
setTitle,
|
|
107
|
-
filters,
|
|
108
|
-
timeRange,
|
|
109
|
-
isEditMode,
|
|
110
|
-
openEditMode,
|
|
111
|
-
closeEditMode
|
|
112
|
-
pollInterval,
|
|
113
|
-
setLoadingState
|
|
114
|
-
} = useWidgetConfig();
|
|
115
|
-
|
|
116
|
-
if (isEditMode) {
|
|
117
|
-
return <EditDialog />;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
return (
|
|
121
|
-
<WidgetBody>
|
|
122
|
-
<h2>{title}</h2>
|
|
123
|
-
{/* Widget content */}
|
|
124
|
-
</WidgetBody>
|
|
125
|
-
);
|
|
126
|
-
};
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
### Widget Configuration Hook
|
|
130
|
-
|
|
131
|
-
The `useWidgetConfig()` hook provides:
|
|
132
|
-
|
|
133
|
-
**Data Management:**
|
|
134
|
-
- `data: Record<string, unknown>` - Widget configuration data
|
|
135
|
-
- `setData: (data: Record<string, unknown>, version: number) => Promise<void>` - Update widget data
|
|
136
|
-
- `dataVersion: number` - Current data version for migrations
|
|
137
|
-
|
|
138
|
-
**Title Management:**
|
|
139
|
-
- `title: string` - Widget title
|
|
140
|
-
- `setTitle: (title: string) => Promise<void>` - Update widget title
|
|
141
|
-
|
|
142
|
-
**Context Information:**
|
|
143
|
-
- `filters: { assetsFilterBarValues, customersFilterBarValues, sitesFilterBarValues }` - Applied filters
|
|
144
|
-
- `timeRange: { startMsInEpoch, endMsInEpoch }` - Selected time range
|
|
145
|
-
- `isEditMode: boolean` - Whether widget is in edit mode
|
|
146
|
-
|
|
147
|
-
**Edit Mode Control:**
|
|
148
|
-
- `openEditMode: () => Promise<void>` - Enter edit mode
|
|
149
|
-
- `closeEditMode: () => Promise<void>` - Exit edit mode
|
|
150
|
-
|
|
151
|
-
### Widget with Edit Dialog
|
|
152
|
-
|
|
153
|
-
```typescript
|
|
154
|
-
import { useWidgetConfig } from '@trackunit/react-core-hooks';
|
|
155
|
-
import { WidgetEditBody } from '@trackunit/react-widgets';
|
|
156
|
-
|
|
157
|
-
export const App = () => {
|
|
158
|
-
const { isEditMode, data, closeEditMode } = useWidgetConfig();
|
|
159
|
-
|
|
160
|
-
if (isEditMode) {
|
|
161
|
-
return (
|
|
162
|
-
<WidgetEditBody
|
|
163
|
-
onSave={async () => {
|
|
164
|
-
await closeEditMode({ newData: { data: { demo: "youDataHere"}, dataVersion: 1 });
|
|
165
|
-
}}
|
|
166
|
-
onCancel={closeEditMode}
|
|
167
|
-
initialData={data}
|
|
168
|
-
/>
|
|
169
|
-
);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
return <MainWidgetContent />;
|
|
173
|
-
};
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
## Filter Support
|
|
177
|
-
|
|
178
|
-
### Supported Filter Types
|
|
179
|
-
|
|
180
|
-
Configure which filters your widget responds to:
|
|
181
|
-
|
|
182
|
-
```typescript
|
|
183
|
-
supportedFilters: {
|
|
184
|
-
// Time range filtering
|
|
185
|
-
TIME_RANGE: { include: ["ALL"] },
|
|
186
|
-
|
|
187
|
-
// Asset filtering
|
|
188
|
-
ASSETS: {
|
|
189
|
-
include: [
|
|
190
|
-
"assetType", "brands", "models", "types",
|
|
191
|
-
"activity", "criticality", "lastSeen", "groups",
|
|
192
|
-
"siteIds", "customerIds", "ownerAccountIds",
|
|
193
|
-
// Add custom fields support
|
|
194
|
-
...allAssetFilterKeysWithCustomFields
|
|
195
|
-
]
|
|
196
|
-
},
|
|
197
|
-
|
|
198
|
-
// Customer filtering
|
|
199
|
-
CUSTOMERS: {
|
|
200
|
-
include: [
|
|
201
|
-
"customerType", "criticality", "servicePlan",
|
|
202
|
-
...allCustomerFilterKeysWithCustomFields
|
|
203
|
-
]
|
|
204
|
-
},
|
|
205
|
-
|
|
206
|
-
// Site filtering
|
|
207
|
-
SITES: {
|
|
208
|
-
include: [
|
|
209
|
-
"siteType", "location", "area",
|
|
210
|
-
...allSiteFilterKeysWithCustomFields
|
|
211
|
-
]
|
|
212
|
-
},
|
|
213
|
-
}
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
### Using Filters in Widgets
|
|
217
|
-
|
|
218
|
-
```typescript
|
|
219
|
-
const { filters, timeRange } = useWidgetConfig();
|
|
220
|
-
|
|
221
|
-
// Access filter values
|
|
222
|
-
const selectedAssets = filters.assetsFilterBarValues?.assetType;
|
|
223
|
-
const selectedCustomers = filters.customersFilterBarValues?.customerType;
|
|
224
|
-
const selectedSites = filters.sitesFilterBarValues?.siteType;
|
|
225
|
-
|
|
226
|
-
// Use time range
|
|
227
|
-
const startTime = timeRange?.startMsInEpoch;
|
|
228
|
-
const endTime = timeRange?.endMsInEpoch;
|
|
229
|
-
```
|
|
230
|
-
|
|
231
|
-
## Widget Lifecycle
|
|
232
|
-
|
|
233
|
-
### 1. Widget Creation
|
|
234
|
-
- Generated via `@trackunit/iris-app:extend` command
|
|
235
|
-
- Manifest defines widget metadata and capabilities
|
|
236
|
-
- React component handles rendering and user interaction
|
|
237
|
-
|
|
238
|
-
### 2. Widget Registration
|
|
239
|
-
- Widget appears in the widget drawer based on `supportedLocations`
|
|
240
|
-
- Users can add widgets to their dashboards
|
|
241
|
-
- Widget configuration is persisted automatically
|
|
242
|
-
|
|
243
|
-
### 3. Widget Runtime
|
|
244
|
-
- Widget receives context (filters, time range, etc.)
|
|
245
|
-
- Widget can store and retrieve configuration data
|
|
246
|
-
- Widget responds to user interactions and filter changes
|
|
247
|
-
|
|
248
|
-
## Best Practices
|
|
249
|
-
|
|
250
|
-
### Widget Design
|
|
251
|
-
1. **Responsive Layout** - Design for different grid sizes
|
|
252
|
-
2. **Loading States** - Show loading indicators for async operations
|
|
253
|
-
3. **Error Handling** - Gracefully handle data loading errors
|
|
254
|
-
4. **Empty States** - Provide meaningful empty state messages
|
|
255
|
-
|
|
256
|
-
### Data Management
|
|
257
|
-
1. **Use Real Data** - Always fetch from Trackunit GraphQL API
|
|
258
|
-
2. **Respect Filters** - Apply user-selected filters to your data queries
|
|
259
|
-
3. **Efficient Updates** - Only update widget data when necessary
|
|
260
|
-
4. **Version Control** - Use `dataVersion` for configuration migrations
|
|
261
|
-
|
|
262
|
-
### Configuration
|
|
263
|
-
1. **Meaningful Defaults** - Provide sensible default configurations
|
|
264
|
-
2. **Validation** - Validate user input in edit dialogs
|
|
265
|
-
3. **Persistence** - Use `setData()` to save configuration changes
|
|
266
|
-
4. **User Experience** - Keep configuration UI simple and intuitive
|
|
267
|
-
|
|
268
|
-
## Example: KPI Widget
|
|
269
|
-
|
|
270
|
-
```typescript
|
|
271
|
-
import { useWidgetConfig } from '@trackunit/react-core-hooks';
|
|
272
|
-
import { useQuery } from '@trackunit/react-graphql-hooks';
|
|
273
|
-
|
|
274
|
-
export const App = () => {
|
|
275
|
-
const { filters, timeRange, data, pollInterval, setLoadingState} = useWidgetConfig();
|
|
276
|
-
|
|
277
|
-
const { data: kpiData, loading } = useQuery(MY_KPI_QUERY, {
|
|
278
|
-
variables: {
|
|
279
|
-
filters: {
|
|
280
|
-
assetIds: filters.assetsFilterBarValues?.assetIds,
|
|
281
|
-
timeRange: {
|
|
282
|
-
start: timeRange?.startMsInEpoch,
|
|
283
|
-
end: timeRange?.endMsInEpoch,
|
|
284
|
-
},
|
|
285
|
-
},
|
|
286
|
-
},
|
|
287
|
-
pollInterval
|
|
288
|
-
});
|
|
289
|
-
|
|
290
|
-
useEffect(() => {
|
|
291
|
-
void setLoadingState({ hasData: !!kpiData, isLoading: loading });
|
|
292
|
-
}, [data, loading, setLoadingState]);
|
|
293
|
-
|
|
294
|
-
return (
|
|
295
|
-
<div className="kpi-widget">
|
|
296
|
-
<div className="kpi-value">{kpiData?.totalCount}</div>
|
|
297
|
-
<div className="kpi-label">Total Assets</div>
|
|
298
|
-
</div>
|
|
299
|
-
);
|
|
300
|
-
};
|
|
301
|
-
```
|
|
302
|
-
|
|
303
|
-
## Common Patterns
|
|
304
|
-
|
|
305
|
-
### Chart Widget
|
|
306
|
-
- Use `widgetType: "CHART"`
|
|
307
|
-
- Implement responsive chart sizing
|
|
308
|
-
- Support time range filtering
|
|
309
|
-
- Show data trends and comparisons
|
|
310
|
-
|
|
311
|
-
### List Widget
|
|
312
|
-
- Use `widgetType: "LIST"`
|
|
313
|
-
- Implement pagination for large datasets
|
|
314
|
-
- Support asset/customer filtering
|
|
315
|
-
- Provide links to detailed views
|
|
316
|
-
|
|
317
|
-
### Map Widget
|
|
318
|
-
- Use `widgetType: "MAP"`
|
|
319
|
-
- Set `allowFullWidth: true` for better visibility
|
|
320
|
-
- Support location-based filtering
|
|
321
|
-
- Show asset positions and movement
|
|
322
|
-
|
|
323
|
-
DO NOT use generic React widget libraries - always use the Trackunit IrisX App SDK widget patterns and components.
|