@trackunit/iris-app 1.12.9 → 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 +12 -0
- package/package.json +1 -1
- package/src/executors/submit/executor.js.map +1 -0
- package/src/executors/unpublish/executor.js.map +1 -0
- package/src/executors/utils/authentication.js.map +1 -0
- package/src/executors/utils/irisAppServerSettings.js.map +1 -0
- package/src/executors/utils/src/index.js.map +1 -0
- 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 -0
- package/src/generators/create/generator.js.map +1 -0
- package/src/generators/extend/dependencies.js.map +1 -0
- package/src/generators/extend/generator.js.map +1 -0
- 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/generator.js.map +1 -0
- package/src/generators/preset/root-files/AGENTS.md +43 -0
- package/src/generators/preset/root-files/CLAUDE.md +17 -0
- package/src/index.js.map +1 -0
- package/src/utils/ast/astUtils.js.map +1 -0
- package/src/utils/fileUpdater.js.map +1 -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
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: graphql-timeseries
|
|
3
|
+
description: Use when querying machine insights, engine metrics, fuel data, operating hours, or building charts with historical time-based data.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# GraphQL Time Series API
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
Query time series data through GraphQL using PromQL (Prometheus Query Language). Use `rangeQuery` for time series charts, `instantQuery` for current values.
|
|
11
|
+
|
|
12
|
+
## When to Use
|
|
13
|
+
|
|
14
|
+
- Querying engine metrics (speed, temperature, fuel rate)
|
|
15
|
+
- Building historical data charts
|
|
16
|
+
- Calculating increases over time periods (daily fuel usage, operating hours)
|
|
17
|
+
- Getting current sensor values
|
|
18
|
+
|
|
19
|
+
**Not for:** Static entity data (use `graphql` skill).
|
|
20
|
+
|
|
21
|
+
## Query Structure
|
|
22
|
+
|
|
23
|
+
### Range Query (Time Series Data)
|
|
24
|
+
|
|
25
|
+
```graphql
|
|
26
|
+
query GetTimeSeriesData($assetId: ID!, $start: DateTime!, $end: DateTime!, $step: Duration!) {
|
|
27
|
+
asset(id: $assetId) {
|
|
28
|
+
timeSeries {
|
|
29
|
+
rangeQuery(query: "metric_name", start: $start, end: $end, step: $step) {
|
|
30
|
+
data {
|
|
31
|
+
... on TimeSeriesMatrixData {
|
|
32
|
+
result {
|
|
33
|
+
values { timestamp value }
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Instant Query (Single Point in Time)
|
|
44
|
+
|
|
45
|
+
```graphql
|
|
46
|
+
query GetInstantData($assetId: ID!, $time: DateTime!) {
|
|
47
|
+
asset(id: $assetId) {
|
|
48
|
+
timeSeries {
|
|
49
|
+
instantQuery(query: "metric_name", time: $time) {
|
|
50
|
+
data {
|
|
51
|
+
... on TimeSeriesVectorData {
|
|
52
|
+
result { value { timestamp value } }
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Common Metrics
|
|
62
|
+
|
|
63
|
+
### Machine Insights
|
|
64
|
+
|
|
65
|
+
Not all assets have all insights. Common metrics include:
|
|
66
|
+
|
|
67
|
+
**Engine Metrics:**
|
|
68
|
+
- `machine_insight_engine_speed`
|
|
69
|
+
- `machine_insight_engine_coolant_temperature`
|
|
70
|
+
- `machine_insight_engine_oil_pressure`
|
|
71
|
+
- `machine_insight_engine_oil_temperature`
|
|
72
|
+
- `machine_insight_engine_fuel_rate`
|
|
73
|
+
- `machine_insight_engine_total_fuel_used`
|
|
74
|
+
- `machine_insight_engine_percent_load_at_current_speed`
|
|
75
|
+
- `machine_insight_cumulative_engine_hours`
|
|
76
|
+
|
|
77
|
+
**Fuel & Emissions:**
|
|
78
|
+
- `machine_insight_fuel_level`
|
|
79
|
+
- `machine_insight_fuel_tank_capacity`
|
|
80
|
+
- `machine_insight_fuel_used_last_24`
|
|
81
|
+
- `machine_insight_cumulative_co2_emissions`
|
|
82
|
+
|
|
83
|
+
**Operating Hours:**
|
|
84
|
+
- `machine_insight_cumulative_operating_hours`
|
|
85
|
+
- `machine_insight_cumulative_idle_hours`
|
|
86
|
+
- `machine_insight_cumulative_moving_hours`
|
|
87
|
+
- `machine_insight_cumulative_productive_hours`
|
|
88
|
+
|
|
89
|
+
**Battery & Electric:**
|
|
90
|
+
- `machine_insight_battery_state_of_charge_percent`
|
|
91
|
+
- `machine_insight_battery_state_of_health_percent`
|
|
92
|
+
- `machine_insight_battery_temperature`
|
|
93
|
+
- `machine_insight_battery_current`
|
|
94
|
+
- `machine_insight_battery_potential`
|
|
95
|
+
|
|
96
|
+
**Location & Movement:**
|
|
97
|
+
- `machine_insight_speed`
|
|
98
|
+
- `machine_insight_altitude`
|
|
99
|
+
- `machine_insight_total_vehicle_distance`
|
|
100
|
+
|
|
101
|
+
**Environmental:**
|
|
102
|
+
- `machine_insight_ambient_air_temperature`
|
|
103
|
+
- `machine_insight_barometric_pressure`
|
|
104
|
+
|
|
105
|
+
## PromQL Examples
|
|
106
|
+
|
|
107
|
+
### Calculate Daily Increases
|
|
108
|
+
|
|
109
|
+
```graphql
|
|
110
|
+
query IncreaseOperatingHours($assetId: ID!, $start: DateTime!, $end: DateTime!, $step: Duration!) {
|
|
111
|
+
asset(id: $assetId) {
|
|
112
|
+
timeSeries {
|
|
113
|
+
rangeQuery(
|
|
114
|
+
query: "increase(machine_insight_cumulative_operating_hours[1d])",
|
|
115
|
+
start: $start,
|
|
116
|
+
end: $end,
|
|
117
|
+
step: $step
|
|
118
|
+
) {
|
|
119
|
+
data {
|
|
120
|
+
... on TimeSeriesMatrixData {
|
|
121
|
+
result { values { timestamp value } }
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Filter with Conditions
|
|
131
|
+
|
|
132
|
+
```graphql
|
|
133
|
+
query HighFuelConsumption($assetId: ID!, $start: DateTime!, $end: DateTime!, $step: Duration!) {
|
|
134
|
+
asset(id: $assetId) {
|
|
135
|
+
timeSeries {
|
|
136
|
+
rangeQuery(
|
|
137
|
+
query: "increase(machine_insight_engine_total_fuel_used[1d]) > 13",
|
|
138
|
+
start: $start,
|
|
139
|
+
end: $end,
|
|
140
|
+
step: $step
|
|
141
|
+
) {
|
|
142
|
+
data {
|
|
143
|
+
... on TimeSeriesMatrixData {
|
|
144
|
+
result { values { timestamp value } }
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Combine Metrics
|
|
154
|
+
|
|
155
|
+
```graphql
|
|
156
|
+
query FuelWithHighIdle($assetId: ID!, $start: DateTime!, $end: DateTime!, $step: Duration!) {
|
|
157
|
+
asset(id: $assetId) {
|
|
158
|
+
timeSeries {
|
|
159
|
+
rangeQuery(
|
|
160
|
+
query: "(increase(machine_insight_engine_total_fuel_used[1d]) > 13) and (increase(machine_insight_cumulative_idle_hours[1d]) > 4)",
|
|
161
|
+
start: $start,
|
|
162
|
+
end: $end,
|
|
163
|
+
step: $step
|
|
164
|
+
) {
|
|
165
|
+
data {
|
|
166
|
+
... on TimeSeriesMatrixData {
|
|
167
|
+
result { values { timestamp value } }
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Key Points
|
|
177
|
+
|
|
178
|
+
- **Always use fragments** for both `TimeSeriesMatrixData` and `TimeSeriesVectorData`
|
|
179
|
+
- **Use PromQL functions**: `increase()`, `rate()`, `avg_over_time()`
|
|
180
|
+
- **Filter at query level** with operators: `>`, `<`, `>=`, `<=`, `==`, `!=`
|
|
181
|
+
- **Time windows** use brackets: `[1d]`, `[1h]`, `[15m]`
|
|
182
|
+
- **Step intervals**: Choose based on data granularity needed
|
|
183
|
+
- **Timestamps** are Unix timestamps in seconds
|
|
184
|
+
- **Values** are returned as strings - convert to numbers as needed
|
|
185
|
+
|
|
186
|
+
## Common Mistakes
|
|
187
|
+
|
|
188
|
+
| Mistake | Fix |
|
|
189
|
+
|---------|-----|
|
|
190
|
+
| Missing fragment for data type | Use `... on TimeSeriesMatrixData` or `TimeSeriesVectorData` |
|
|
191
|
+
| Not handling missing metrics | Not all assets have all insights; handle empty results |
|
|
192
|
+
| Using values as numbers directly | Values are strings; convert with `parseFloat()` |
|
|
193
|
+
| Wrong step interval | Match step to desired chart granularity |
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: irisx-app-sdk
|
|
3
|
+
description: Use when creating apps, adding extensions, configuring CSP/scopes, or needing SDK command reference. Core SDK documentation.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# IrisX App SDK Development
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
IrisX Apps are React applications that integrate into Trackunit Manager at defined extension points. Use SDK commands (not generic React/Node.js) for all project operations.
|
|
11
|
+
|
|
12
|
+
## When to Use
|
|
13
|
+
|
|
14
|
+
- Creating new apps or extensions
|
|
15
|
+
- Configuring security (CSP headers, scopes)
|
|
16
|
+
- Looking up extension types or SDK commands
|
|
17
|
+
- Understanding runtime hooks availability
|
|
18
|
+
|
|
19
|
+
**Not for:** GraphQL setup (use `graphql` skill), browser testing (use `browser-testing` skill).
|
|
20
|
+
|
|
21
|
+
## Extension Points
|
|
22
|
+
|
|
23
|
+
Extensions are React applications that integrate into specific points in Trackunit Manager:
|
|
24
|
+
|
|
25
|
+
| Extension Type | Location |
|
|
26
|
+
|----------------|----------|
|
|
27
|
+
| `ASSET_HOME_EXTENSION` | New tabs in Assets Home screen |
|
|
28
|
+
| `SITE_HOME_EXTENSION` | New tabs in Site Home screen |
|
|
29
|
+
| `FLEET_EXTENSION` | Menu items in Main Menu |
|
|
30
|
+
| `REPORT_EXTENSION` | New reports in Reports screen |
|
|
31
|
+
| `WIDGET_EXTENSION` | Dashboard widgets |
|
|
32
|
+
| `IRIS_APP_SETTINGS_EXTENSION` | Configuration UI in App library |
|
|
33
|
+
| `ADMIN_EXTENSION` | Admin UI tabs (admin-only) |
|
|
34
|
+
| `CUSTOMER_HOME_EXTENSION` | UI within Customer Home |
|
|
35
|
+
| `ASSET_EVENTS_ACTIONS_EXTENSION` | UI in Asset Home Events |
|
|
36
|
+
| `LIFECYCLE_EXTENSION` | Handles app install/uninstall events |
|
|
37
|
+
| `SERVERLESS_FUNCTION_EXTENSION` | Serverless backend functions |
|
|
38
|
+
|
|
39
|
+
## Commands
|
|
40
|
+
|
|
41
|
+
### Create New App
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npx nx generate @trackunit/iris-app:create [name-of-your-app]
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
See: [Creating a new app](https://developers.trackunit.com/docs/creating-a-new-app)
|
|
48
|
+
|
|
49
|
+
### Create New Extension
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
npx nx g @trackunit/iris-app:extend --name=[my-extension-name] --app=[app-name] --directory=[feature] --type=[extension-type]
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
See: [Creating a new extension](https://developers.trackunit.com/docs/creating-a-new-extension)
|
|
56
|
+
|
|
57
|
+
### Run App
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
npx nx run [name-of-your-app]:serve
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
See: [Running the Iris App SDK](https://developers.trackunit.com/docs/running-the-iris-app-sdk)
|
|
64
|
+
|
|
65
|
+
## Security Configuration
|
|
66
|
+
|
|
67
|
+
IrisX Apps are locked down by default. External API requests and embedded content require proper Content Security Policy (CSP) configuration in the app manifest.
|
|
68
|
+
|
|
69
|
+
### CSP Headers Example
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
cspHeader: {
|
|
73
|
+
'frame-src': ['https://api.example.com', 'https://dashboard.example.com'],
|
|
74
|
+
'script-src': ['https://api.example.com'],
|
|
75
|
+
'connect-src': ['https://api.example.com'],
|
|
76
|
+
},
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Scopes Example
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
scopes: [
|
|
83
|
+
{scope: "required.scope.1", optional: false},
|
|
84
|
+
{scope: "required.scope.2", optional: true},
|
|
85
|
+
],
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Apps requiring API access need scopes configured in the manifest, and the app must be deployed with those scopes to work locally. Never submit an app automatically - instruct the user to do so.
|
|
89
|
+
|
|
90
|
+
See: [Embedding IrisX Dashboards in Apps](https://developers.trackunit.com/docs/analytics-dashboards-in-apps#troubleshooting)
|
|
91
|
+
|
|
92
|
+
## Runtime Hooks
|
|
93
|
+
|
|
94
|
+
IrisX Apps have access to runtime hooks from `@trackunit/react-core-hooks`:
|
|
95
|
+
|
|
96
|
+
- `useAssetRuntime()` - Asset information in asset-scoped extensions
|
|
97
|
+
- `useCustomerRuntime()` - Customer data
|
|
98
|
+
- `useEventRuntime()` - Event details
|
|
99
|
+
- `useSiteRuntime()` - Site information
|
|
100
|
+
- Plus navigation, user context, and utility hooks
|
|
101
|
+
|
|
102
|
+
See the `react-core-hooks` skill for full details.
|
|
103
|
+
|
|
104
|
+
## Quality Assurance
|
|
105
|
+
|
|
106
|
+
Before completing any task: Always use `read_lints` to check for and fix all ESLint and TypeScript errors in modified files.
|
|
107
|
+
|
|
108
|
+
## Common Mistakes
|
|
109
|
+
|
|
110
|
+
| Mistake | Fix |
|
|
111
|
+
|---------|-----|
|
|
112
|
+
| Using `create-react-app` or `npm init` | Use `nx generate @trackunit/iris-app:create` |
|
|
113
|
+
| Mocking data without asking | Always use real GraphQL API |
|
|
114
|
+
| Missing scopes in manifest | Add required scopes; deploy to enable locally |
|
|
115
|
+
| External API blocked | Add domain to `cspHeader` in manifest |
|
|
116
|
+
| Auto-submitting app | Never auto-submit; instruct user to deploy |
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: react-core-hooks
|
|
3
|
+
description: Use when needing asset/site/customer runtime data, user context, navigation, toasts, or confirmation dialogs in IrisX Apps.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# React Core Hooks
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
Reference for `@trackunit/react-core-hooks` - runtime context, navigation, user information, and UI interaction hooks for IrisX Apps.
|
|
11
|
+
|
|
12
|
+
## When to Use
|
|
13
|
+
|
|
14
|
+
- Accessing current asset/site/customer context
|
|
15
|
+
- Getting user information or preferences
|
|
16
|
+
- Navigating within Trackunit Manager
|
|
17
|
+
- Showing toasts or confirmation dialogs
|
|
18
|
+
- Managing widget configuration
|
|
19
|
+
|
|
20
|
+
## Runtime Hooks
|
|
21
|
+
|
|
22
|
+
### useAssetRuntime
|
|
23
|
+
|
|
24
|
+
Access asset information in asset-scoped extensions.
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
const { assetInfo, loading, error } = useAssetRuntime();
|
|
28
|
+
// assetInfo.assetId, assetInfo.name, etc.
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### useCustomerRuntime
|
|
32
|
+
|
|
33
|
+
Access customer information.
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
const { customerInfo, loading, error } = useCustomerRuntime();
|
|
37
|
+
// customerInfo.customerId, customerInfo.name, etc.
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### useEventRuntime
|
|
41
|
+
|
|
42
|
+
Access event information in event-scoped extensions.
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
const { eventInfo, loading, error } = useEventRuntime();
|
|
46
|
+
// eventInfo.eventId, eventInfo.type, etc.
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### useSiteRuntime
|
|
50
|
+
|
|
51
|
+
Access site information in site-scoped extensions.
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
const { siteInfo, loading, error } = useSiteRuntime();
|
|
55
|
+
// siteInfo.siteId, siteInfo.name, etc.
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### useIrisAppName
|
|
59
|
+
|
|
60
|
+
Get current app name.
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
const { appName, loading, error } = useIrisAppName();
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### useIrisAppId
|
|
67
|
+
|
|
68
|
+
Get current app ID.
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
const { irisAppId, loading, error } = useIrisAppId();
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Navigation & User Context
|
|
75
|
+
|
|
76
|
+
### useNavigateInHost
|
|
77
|
+
|
|
78
|
+
Navigate within Trackunit Manager.
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
const { gotoAssetHome, gotoSiteHome } = useNavigateInHost();
|
|
82
|
+
gotoAssetHome(assetId);
|
|
83
|
+
gotoSiteHome(siteId);
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### useHasAccessTo
|
|
87
|
+
|
|
88
|
+
Check user access to pages or asset-scoped pages.
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
const hasAccess = useHasAccessTo({ page: "sites" });
|
|
92
|
+
const hasAssetAccess = useHasAccessTo({ assetId, page: "asset-home" });
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### useCurrentUser
|
|
96
|
+
|
|
97
|
+
Access current user information. Returns flat properties directly.
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
const { email, name, accountId, userId } = useCurrentUser();
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### useCurrentUserLanguage
|
|
104
|
+
|
|
105
|
+
Get/set user language preference.
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
const { language, setLanguage } = useCurrentUserLanguage();
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### useCurrentUserTimeZonePreference
|
|
112
|
+
|
|
113
|
+
Get/set user timezone preference.
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
const { timeZonePreference, setTimeZonePreference } = useCurrentUserTimeZonePreference();
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### useToken
|
|
120
|
+
|
|
121
|
+
Access authentication token.
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
const { token } = useToken();
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## UI & Interaction
|
|
128
|
+
|
|
129
|
+
### useToast
|
|
130
|
+
|
|
131
|
+
Show toast notifications.
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
const { addToast } = useToast();
|
|
135
|
+
addToast({ intent: "success", title: "Operation completed" });
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### useConfirmationDialog
|
|
139
|
+
|
|
140
|
+
Show confirmation dialogs.
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
const { confirm } = useConfirmationDialog();
|
|
144
|
+
const confirmed = await confirm({
|
|
145
|
+
title: "Confirm Action",
|
|
146
|
+
message: "Are you sure?",
|
|
147
|
+
primaryActionType: "danger",
|
|
148
|
+
primaryActionLabel: "Delete",
|
|
149
|
+
secondaryActionLabel: "Cancel"
|
|
150
|
+
});
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### useModalDialogContext
|
|
154
|
+
|
|
155
|
+
Manage modal dialogs.
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
const { openModal, closeModal } = useModalDialogContext();
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### useErrorHandler
|
|
162
|
+
|
|
163
|
+
Handle errors consistently.
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
const { logError } = useErrorHandler();
|
|
167
|
+
logError(new Error("Something went wrong"));
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Data & State Management
|
|
171
|
+
|
|
172
|
+
### useAssetSorting
|
|
173
|
+
|
|
174
|
+
Asset sorting functionality.
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
const { sortingState, setSortBy } = useAssetSorting();
|
|
178
|
+
// sortingState.sortBy, sortingState.order
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### useFilterBarContext
|
|
182
|
+
|
|
183
|
+
Filter bar state management. Provides domain-specific filter values.
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
const { assetsFilterBarValues, customersFilterBarValues } = useFilterBarContext();
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### useTimeRange
|
|
190
|
+
|
|
191
|
+
Time range context (read-only).
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
const { timeRange, temporalPeriod } = useTimeRange();
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Widget Configuration
|
|
198
|
+
|
|
199
|
+
### useWidgetConfig
|
|
200
|
+
|
|
201
|
+
Access widget configuration, filters, time range, and edit mode state.
|
|
202
|
+
|
|
203
|
+
```typescript
|
|
204
|
+
const {
|
|
205
|
+
data, setData, dataVersion, loadingData,
|
|
206
|
+
title, setTitle,
|
|
207
|
+
filters, timeRange,
|
|
208
|
+
isEditMode, openEditMode, closeEditMode,
|
|
209
|
+
pollInterval, setLoadingState
|
|
210
|
+
} = useWidgetConfig();
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## Note
|
|
214
|
+
|
|
215
|
+
All hooks require their respective providers to be set up in your app's component tree. IrisX App SDK handles this automatically for extensions.
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: tables-and-sorting
|
|
3
|
+
description: Use when building data tables with filtering, sorting, pagination, or displaying lists of assets, users, or other entities.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Tables with Filtering & Sorting
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
Create interactive data tables using `@trackunit/react-table`, `@trackunit/filters-filter-bar`, and server-side pagination with GraphQL. Always use `useMemo` and `manualSorting: true` for large datasets.
|
|
11
|
+
|
|
12
|
+
## When to Use
|
|
13
|
+
|
|
14
|
+
- Building asset management interfaces
|
|
15
|
+
- Creating user administration tables
|
|
16
|
+
- Developing dashboard data grids
|
|
17
|
+
- Implementing reporting interfaces with large datasets
|
|
18
|
+
|
|
19
|
+
**Not for:** Simple lists without filtering (use standard components).
|
|
20
|
+
|
|
21
|
+
## Required Imports
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import { Table, useTable } from '@trackunit/react-table';
|
|
25
|
+
import { TextCell, DateTimeCell } from '@trackunit/react-table-base-components';
|
|
26
|
+
import { FilterBar, useFilterBar } from '@trackunit/filters-filter-bar';
|
|
27
|
+
import { useAssetQueryFilters } from '@trackunit/filters-filter-bar-asset-graphql';
|
|
28
|
+
import { usePaginationQuery } from '@trackunit/react-graphql-hooks';
|
|
29
|
+
import { createColumnHelper, SortingState } from '@tanstack/react-table';
|
|
30
|
+
import { useBrandFilter, useModelsFilter } from '@trackunit/filters-asset-filter-definitions';
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Valid Implementation
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
type Asset = { id: string; name: string; createdAt: string };
|
|
37
|
+
const columnHelper = createColumnHelper<Asset>();
|
|
38
|
+
|
|
39
|
+
const AssetTable = () => {
|
|
40
|
+
// Setup filters with proper naming
|
|
41
|
+
const filterBar = useFilterBar({
|
|
42
|
+
name: "assetFilters",
|
|
43
|
+
filterBarDefinition: {
|
|
44
|
+
brand: useBrandFilter(),
|
|
45
|
+
model: useModelsFilter()
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// Convert filter values to GraphQL variables
|
|
50
|
+
const filterVariables = useAssetQueryFilters(filterBar.filterBarConfig.values);
|
|
51
|
+
|
|
52
|
+
// Server-side sorting state
|
|
53
|
+
const [sorting, setSorting] = useState<SortingState>([]);
|
|
54
|
+
|
|
55
|
+
// GraphQL query with filters
|
|
56
|
+
const { data, loading, pagination } = usePaginationQuery(GetAssetsDocument, {
|
|
57
|
+
variables: { first: 50, ...filterVariables },
|
|
58
|
+
pageSize: 50,
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// Transform data with useMemo
|
|
62
|
+
const assets = useMemo(() =>
|
|
63
|
+
data?.assets.edges.map(edge => ({ ...edge.node, id: edge.node.id })) ?? [],
|
|
64
|
+
[data]
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
// Define columns with proper cell components
|
|
68
|
+
const columns = useMemo(() => [
|
|
69
|
+
columnHelper.accessor('name', {
|
|
70
|
+
cell: ({ row: { original } }) => <TextCell content={original.name} />,
|
|
71
|
+
header: "Asset Name",
|
|
72
|
+
enableSorting: true,
|
|
73
|
+
}),
|
|
74
|
+
columnHelper.accessor('createdAt', {
|
|
75
|
+
cell: ({ row: { original } }) => <DateTimeCell date={new Date(original.createdAt)} />,
|
|
76
|
+
header: "Created",
|
|
77
|
+
enableSorting: true,
|
|
78
|
+
}),
|
|
79
|
+
], []);
|
|
80
|
+
|
|
81
|
+
// Setup table with manual sorting
|
|
82
|
+
const table = useTable({
|
|
83
|
+
data: assets,
|
|
84
|
+
columns,
|
|
85
|
+
enableSorting: true,
|
|
86
|
+
manualSorting: true,
|
|
87
|
+
state: { sorting },
|
|
88
|
+
onSortingChange: setSorting,
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
return (
|
|
92
|
+
<>
|
|
93
|
+
<FilterBar {...filterBar} />
|
|
94
|
+
<Table
|
|
95
|
+
{...table}
|
|
96
|
+
loading={loading}
|
|
97
|
+
pagination={pagination}
|
|
98
|
+
onRowClick={(row) => navigateToAsset(row.original.id)}
|
|
99
|
+
/>
|
|
100
|
+
</>
|
|
101
|
+
);
|
|
102
|
+
};
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Common Mistakes
|
|
106
|
+
|
|
107
|
+
| Mistake | Fix |
|
|
108
|
+
|---------|-----|
|
|
109
|
+
| Missing `useMemo` for columns/data | Wrap in `useMemo` to prevent re-renders |
|
|
110
|
+
| `manualSorting: false` on large datasets | Use `manualSorting: true` for server-side sorting |
|
|
111
|
+
| Missing filter variables in query | Spread `filterVariables` into GraphQL variables |
|
|
112
|
+
| Using `getValue()` in cells | Use Trackunit cell components (`TextCell`, `DateTimeCell`) |
|
|
113
|
+
| No `onRowClick` handler | Add navigation or detail view on row click |
|
|
114
|
+
|
|
115
|
+
## Key Requirements
|
|
116
|
+
|
|
117
|
+
- **Always use `useMemo`** for data transformation and column definitions
|
|
118
|
+
- **Enable `manualSorting: true`** for datasets > 1000 items
|
|
119
|
+
- **Integrate filter state** with GraphQL variables using spread operator
|
|
120
|
+
- **Use Trackunit cell components** (`TextCell`, `DateTimeCell`, `PlainDateCell`) for consistency
|
|
121
|
+
- **Implement `onRowClick`** for navigation or detail views
|
|
122
|
+
- **Sync state properly** between filters, sorting, and GraphQL queries
|