@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.
Files changed (41) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/package.json +1 -1
  3. package/src/executors/submit/executor.js.map +1 -0
  4. package/src/executors/unpublish/executor.js.map +1 -0
  5. package/src/executors/utils/authentication.js.map +1 -0
  6. package/src/executors/utils/irisAppServerSettings.js.map +1 -0
  7. package/src/executors/utils/src/index.js.map +1 -0
  8. package/src/generators/ai-agent-sync/README.md +5 -2
  9. package/src/generators/ai-agent-sync/generator.d.ts +1 -1
  10. package/src/generators/ai-agent-sync/generator.js +29 -4
  11. package/src/generators/ai-agent-sync/generator.js.map +1 -0
  12. package/src/generators/create/generator.js.map +1 -0
  13. package/src/generators/extend/dependencies.js.map +1 -0
  14. package/src/generators/extend/generator.js.map +1 -0
  15. package/src/generators/preset/files/.agents/skills/browser-testing/SKILL.md +193 -0
  16. package/src/generators/preset/files/.agents/skills/create-app/SKILL.md +191 -0
  17. package/src/generators/preset/files/.agents/skills/customfields/SKILL.md +239 -0
  18. package/src/generators/preset/files/.agents/skills/graphql/SKILL.md +147 -0
  19. package/src/generators/preset/files/.agents/skills/graphql-timeseries/SKILL.md +193 -0
  20. package/src/generators/preset/files/.agents/skills/irisx-app-sdk/SKILL.md +116 -0
  21. package/src/generators/preset/files/.agents/skills/react-core-hooks/SKILL.md +215 -0
  22. package/src/generators/preset/files/.agents/skills/tables-and-sorting/SKILL.md +122 -0
  23. package/src/generators/preset/files/.agents/skills/widget-extensions/SKILL.md +245 -0
  24. package/src/generators/preset/files/.cursor/mcp.json +4 -0
  25. package/src/generators/preset/generator.js.map +1 -0
  26. package/src/generators/preset/root-files/AGENTS.md +43 -0
  27. package/src/generators/preset/root-files/CLAUDE.md +17 -0
  28. package/src/index.js.map +1 -0
  29. package/src/utils/ast/astUtils.js.map +1 -0
  30. package/src/utils/fileUpdater.js.map +1 -0
  31. package/src/generators/preset/files/.cursor/commands/create-app.md +0 -226
  32. package/src/generators/preset/files/.cursor/rules/browser-irisx-development.mdc +0 -246
  33. package/src/generators/preset/files/.cursor/rules/graphql-timeseries.md +0 -260
  34. package/src/generators/preset/files/.cursor/rules/irisx-app-sdk-customfields.md +0 -305
  35. package/src/generators/preset/files/.cursor/rules/irisx-app-sdk-graphql.md +0 -30
  36. package/src/generators/preset/files/.cursor/rules/irisx-app-sdk.mdc +0 -82
  37. package/src/generators/preset/files/.cursor/rules/react-core-hooks.md +0 -155
  38. package/src/generators/preset/files/.cursor/rules/rules-index.mdc +0 -10
  39. package/src/generators/preset/files/.cursor/rules/structured-development.mdc +0 -86
  40. package/src/generators/preset/files/.cursor/rules/tables-and-sorting.mdc +0 -126
  41. package/src/generators/preset/files/.cursor/rules/widget-extensions.md +0 -323
@@ -0,0 +1,191 @@
1
+ ---
2
+ name: create-app
3
+ description: Use when building a new IrisX App from scratch, user asks to create an app, or starting a new extension project.
4
+ ---
5
+
6
+ # Create IrisX App
7
+
8
+ ## Overview
9
+
10
+ Create IrisX Apps using browser-driven iterative development. Real data first (GraphQL API), verify each feature in browser before proceeding.
11
+
12
+ ## When to Use
13
+
14
+ - User asks to create a new IrisX App
15
+ - Starting a new extension project
16
+ - Building a complete app from requirements
17
+
18
+ **Not for:** Modifying existing extensions (use domain-specific skills).
19
+
20
+ ## Core Principles
21
+
22
+ | Principle | Implementation |
23
+ |-----------|----------------|
24
+ | Real Data First | Use GraphQL API, never mock unless requested |
25
+ | Browser Testing | Verify each change at `manager.trackunit.com/goto/iris-app-dev` |
26
+ | Chrome DevTools MCP | Required for iFrame-based extension testing |
27
+ | One Feature at a Time | Complete and verify before moving on |
28
+ | Zero Tolerance for Errors | Fix all lint/TypeScript errors immediately |
29
+
30
+ ## Chrome DevTools MCP Requirement
31
+
32
+ **IrisX Apps render inside iFrames.** Standard browser automation cannot interact with iFrame content.
33
+
34
+ Before starting browser testing:
35
+ 1. Verify Chrome DevTools MCP is available (check for `user-chrome-devtools` server)
36
+ 2. If not available, see `browser-testing` skill for setup instructions
37
+ 3. Use Chrome DevTools tools (`take_screenshot`, `click`, `evaluate_script`, etc.) for all browser interactions
38
+
39
+ ## Development Workflow
40
+
41
+ ### Phase 1: Planning
42
+
43
+ **1. Analyze Requirements**
44
+
45
+ If not already described, ask: "What functionality do you want to build?"
46
+
47
+ **2. Create Implementation Plan**
48
+
49
+ Structure the plan as:
50
+
51
+ - **Phase 1: Project Setup**
52
+ - Create app with `@trackunit/iris-app:create`
53
+ - Create extensions with `@trackunit/iris-app:extend`
54
+ - Start dev server in background
55
+
56
+ - **Phase 2: Browser Verification (CRITICAL GATE)**
57
+ - Verify Chrome DevTools MCP is available (required for iFrame testing)
58
+ - Open browser to `https://new.manager.trackunit.com/goto/iris-app-dev`
59
+ - Wait for user login (2-4 minutes)
60
+ - Enable "Use Local Apps" toggle
61
+ - Navigate to extension location
62
+ - Use Chrome DevTools `take_screenshot` to verify extension renders
63
+ - **STOP - Must confirm extension working before Phase 3**
64
+
65
+ - **Phase 3: Feature Implementation (One at a Time)**
66
+ - For each feature:
67
+ - Prerequisites (GraphQL setup/scopes needed)
68
+ - Implementation
69
+ - Validation: Check lints, browser test
70
+ - Browser test: What to verify
71
+
72
+ - **Phase 4: Final Verification**
73
+ - Zero linter/TypeScript errors
74
+ - End-to-end browser testing
75
+ - Documentation completion
76
+
77
+ **3. Present Plan to User**
78
+
79
+ Confirm before proceeding.
80
+
81
+ ### Phase 2: Project Setup
82
+
83
+ **4. Create App and Extensions**
84
+
85
+ ```bash
86
+ npx nx generate @trackunit/iris-app:create [app-name]
87
+ npx nx g @trackunit/iris-app:extend --name=[extension-name] --app=[app-name] --directory=[feature] --type=[EXTENSION_TYPE]
88
+ ```
89
+
90
+ **5. Start Development Server**
91
+
92
+ ```bash
93
+ npx nx run [app-name]:serve
94
+ ```
95
+
96
+ **6. Open Browser and Verify**
97
+
98
+ **CRITICAL: Do this BEFORE any GraphQL setup or UI implementation**
99
+
100
+ 1. Navigate to `https://new.manager.trackunit.com/goto/iris-app-dev`
101
+ 2. Wait for user login
102
+ 3. Enable "Use Local Apps" toggle
103
+ 4. Navigate to extension based on type
104
+ 5. Take snapshot to verify extension loads
105
+ 6. Check console for errors
106
+
107
+ **STOP HERE: Do not proceed until extension is confirmed working**
108
+
109
+ ### Phase 3: Iterative Feature Development
110
+
111
+ **7. Create Documentation**
112
+
113
+ Create `/docs/[app-name].md` with the plan and architecture.
114
+
115
+ **8. For Each Feature (ONE AT A TIME)**
116
+
117
+ **a) Setup prerequisites for this feature ONLY:**
118
+ ```bash
119
+ # If feature needs GraphQL:
120
+ npm install @trackunit/react-graphql-tools
121
+ npx nx generate @trackunit/react-graphql-tools:add-graphql --project=[project-name]
122
+ ```
123
+
124
+ **b) Implement the feature:**
125
+ - Write code changes for ONE feature only
126
+ - Keep changes small and focused
127
+
128
+ **c) Check for lint and TypeScript errors:**
129
+ - Run `read_lints` on modified files
130
+ - Fix ALL errors immediately
131
+ - Zero tolerance: Do not proceed with any errors
132
+
133
+ **d) Reload browser to test:**
134
+ - Check browser console and network tab for errors
135
+ - Verify the feature renders correctly
136
+ - Take screenshot if visual changes
137
+
138
+ **e) Debug if needed:**
139
+ - Read console errors
140
+ - Check network requests
141
+ - Adjust code and repeat
142
+ - **DO NOT** move forward until feature works
143
+
144
+ **f) Update documentation:**
145
+ - Mark feature as completed
146
+
147
+ **g) Move to next feature:**
148
+ - Only proceed when current feature is verified working
149
+
150
+ ### Phase 4: Completion
151
+
152
+ **9. Final Verification**
153
+
154
+ - Run `read_lints` on all modified files
155
+ - Test all features end-to-end in browser
156
+ - Verify data loads correctly
157
+
158
+ **10. Summary**
159
+
160
+ Inform the user:
161
+ - All features implemented and verified
162
+ - No linter or TypeScript errors remaining
163
+ - Documentation complete
164
+ - **Important**: User must deploy via Trackunit marketplace (never auto-submit)
165
+
166
+ ## Critical Rules
167
+
168
+ ### Feature Implementation
169
+ - **One feature at a time** - even for similar components
170
+ - **Prerequisites on-demand** - setup GraphQL/scopes only when needed
171
+ - **Zero tolerance for errors** - fix all lint/TypeScript errors immediately
172
+ - **Test completely** - verify working in browser before moving on
173
+
174
+ ### Validation Checklist (After Every Code Change)
175
+ 1. Run `read_lints` on modified files
176
+ 2. Fix ALL linter and TypeScript errors
177
+ 3. Reload browser
178
+ 4. Check browser console and network tab
179
+ 5. Verify UI renders correctly
180
+ 6. Fix any issues before proceeding
181
+
182
+ ## Common Mistakes
183
+
184
+ | Mistake | Fix |
185
+ |---------|-----|
186
+ | Using default browser automation | Use Chrome DevTools MCP (iFrames require it) |
187
+ | Starting implementation before browser verification | Complete Phase 2 (browser gate) first |
188
+ | Implementing multiple features at once | One feature, fully verified, then next |
189
+ | Using mock data without asking | Always use real GraphQL API |
190
+ | Skipping lint check | Zero tolerance - run `read_lints` after every change |
191
+ | Auto-submitting to marketplace | Never auto-submit; instruct user to do so |
@@ -0,0 +1,239 @@
1
+ ---
2
+ name: customfields
3
+ description: Use when storing app data on entities, persisting user input, defining custom fields in manifest, or reading/writing entity-specific data.
4
+ ---
5
+
6
+ # CustomFields
7
+
8
+ ## Overview
9
+
10
+ CustomFields extend the Trackunit data model by storing additional information on entities (assets, accounts, groups, sites). This is the primary mechanism for persisting IrisX app data.
11
+
12
+ ## When to Use
13
+
14
+ - Storing user input or preferences
15
+ - Adding metadata to assets/accounts/groups/sites
16
+ - Persisting app-specific configuration
17
+ - Creating custom KPIs or fleet attributes
18
+
19
+ **Not for:** External databases or storage solutions (use CustomFields instead).
20
+
21
+ ## Defining Custom Fields
22
+
23
+ ### Manifest Configuration
24
+
25
+ ```typescript
26
+ customFieldDefinitions: [
27
+ {
28
+ type: 'STRING',
29
+ entityType: 'ASSET',
30
+ key: 'myCustomField',
31
+ translations: [{
32
+ language: 'en',
33
+ title: 'My Custom Field',
34
+ description: 'Description of what this field stores',
35
+ }],
36
+ uiEditable: true,
37
+ uiVisible: true,
38
+ scopeType: 'ACCOUNT' // or 'ACCOUNT_WRITE_GLOBAL_READ' or 'GLOBAL'
39
+ }
40
+ ]
41
+ ```
42
+
43
+ ### Required Properties
44
+
45
+ | Property | Description |
46
+ |----------|-------------|
47
+ | `type` | Field data type |
48
+ | `entityType` | `ACCOUNT`, `ASSET`, `GROUP`, or `SITE` |
49
+ | `key` | Programmatic identifier (immutable after creation) |
50
+ | `translations` | UI labels and descriptions |
51
+ | `scopeType` | Data access scope |
52
+
53
+ ### Optional Properties
54
+
55
+ | Property | Default | Description |
56
+ |----------|---------|-------------|
57
+ | `uiEditable` | `true` | Can be edited in Manager UI |
58
+ | `uiVisible` | `true` | Visible in Manager UI |
59
+
60
+ ## 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`, `valueReplacements` |
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
+ | `STRING_LIST` | Array of strings | - |
72
+ | `WEB_ADDRESS` | URLs as clickable links | - |
73
+ | `JSON` | JSON objects | - |
74
+ | `FILE` | File attachments | - |
75
+ | `MONETARY` | Currency values (ISO 4217) | `currency` |
76
+
77
+ ## Example Field Definitions
78
+
79
+ ### String with Validation
80
+
81
+ ```typescript
82
+ {
83
+ type: 'STRING',
84
+ entityType: 'ASSET',
85
+ key: 'serialNumber',
86
+ translations: [{ language: 'en', title: 'Serial Number', description: 'Asset serial number' }],
87
+ minimumLength: 5,
88
+ maximumLength: 20,
89
+ pattern: '^[A-Z0-9]+$'
90
+ }
91
+ ```
92
+
93
+ ### Number with Units
94
+
95
+ ```typescript
96
+ {
97
+ type: 'NUMBER',
98
+ entityType: 'ASSET',
99
+ key: 'fuelCapacity',
100
+ translations: [{ language: 'en', title: 'Fuel Capacity', description: 'Tank capacity' }],
101
+ minimum: 0,
102
+ maximum: 1000,
103
+ unitSi: 'L',
104
+ unitUs: 'gal'
105
+ }
106
+ ```
107
+
108
+ ### Dropdown with Multiple Selection
109
+
110
+ ```typescript
111
+ {
112
+ type: 'DROPDOWN',
113
+ entityType: 'ASSET',
114
+ key: 'features',
115
+ translations: [{ language: 'en', title: 'Features', description: 'Available features' }],
116
+ allValues: ['GPS', 'Camera', 'Bluetooth', 'WiFi'],
117
+ multiSelect: true
118
+ }
119
+ ```
120
+
121
+ ## Scope Types
122
+
123
+ | Scope | Description |
124
+ |-------|-------------|
125
+ | `ACCOUNT` | Values shared within a single account only |
126
+ | `ACCOUNT_WRITE_GLOBAL_READ` | Account can write, all accounts can read |
127
+ | `GLOBAL` | Values shared between all accounts with entity access |
128
+
129
+ ## Using CustomFields in React
130
+
131
+ ### Required Imports
132
+
133
+ ```typescript
134
+ import {
135
+ useCustomFieldsValueAndDefinition,
136
+ useUpdateCustomFieldValues
137
+ } from '@trackunit/custom-field-api';
138
+ import { CustomField } from '@trackunit/custom-field-components';
139
+ import { useCurrentUserSystemOfMeasurement } from '@trackunit/react-core-hooks';
140
+ ```
141
+
142
+ ### Basic Implementation
143
+
144
+ ```typescript
145
+ import React from 'react';
146
+ import { useForm } from 'react-hook-form';
147
+ import { Card, CardBody, CardHeader, CardFooter, Button, Spinner } from '@trackunit/react-components';
148
+ import { useCustomFieldsValueAndDefinition, useUpdateCustomFieldValues } from '@trackunit/custom-field-api';
149
+ import { CustomField } from '@trackunit/custom-field-components';
150
+ import { useCurrentUserSystemOfMeasurement } from '@trackunit/react-core-hooks';
151
+
152
+ const CustomFieldsForm = ({ assetId }: { assetId: string }) => {
153
+ const { register, handleSubmit, formState, setValue, control } = useForm({
154
+ shouldUnregister: false,
155
+ });
156
+
157
+ const { systemOfMeasurement } = useCurrentUserSystemOfMeasurement();
158
+
159
+ const { fields, loading } = useCustomFieldsValueAndDefinition({
160
+ entityType: "ASSET",
161
+ entityId: assetId,
162
+ systemOfMeasurement: systemOfMeasurement ?? "SI",
163
+ });
164
+
165
+ const { updateCustomFields, inProgress } = useUpdateCustomFieldValues(
166
+ assetId,
167
+ "ASSET",
168
+ fields,
169
+ systemOfMeasurement ?? "SI"
170
+ );
171
+
172
+ if (loading) return <Spinner />;
173
+
174
+ return (
175
+ <Card>
176
+ <CardHeader heading="Custom Fields" />
177
+ <CardBody>
178
+ {fields.map(field => {
179
+ const isEditable = field.valueEditable && (field.definition.uiEditable ?? true);
180
+ return (
181
+ <CustomField
182
+ key={field.definition.key}
183
+ control={control}
184
+ field={field}
185
+ formState={formState}
186
+ isEditable={isEditable}
187
+ register={register}
188
+ setValue={setValue}
189
+ unitPreference={systemOfMeasurement}
190
+ />
191
+ );
192
+ })}
193
+ </CardBody>
194
+ <CardFooter>
195
+ <Button
196
+ loading={inProgress}
197
+ onClick={handleSubmit(updateCustomFields)}
198
+ >
199
+ {inProgress ? "Saving..." : "Save Changes"}
200
+ </Button>
201
+ </CardFooter>
202
+ </Card>
203
+ );
204
+ };
205
+ ```
206
+
207
+ ## Best Practices
208
+
209
+ ### Field Design
210
+ - **Use descriptive keys**: Choose clear, descriptive field keys that won't need changing
211
+ - **Add proper translations**: Always provide meaningful titles and descriptions
212
+ - **Set appropriate constraints**: Use min/max values, length limits, and patterns for validation
213
+
214
+ ### Performance
215
+ - **Batch updates**: Use `useUpdateCustomFieldValues` to update multiple fields at once
216
+ - **Handle loading states**: Always show loading indicators while fetching data
217
+ - **Error handling**: Implement proper error handling for failed updates
218
+
219
+ ### Deployment
220
+ - **Field keys are immutable**: Cannot be changed after creation
221
+ - **Definition updates**: Changing field definitions affects all existing data
222
+ - **Approval process**: Custom field changes require app approval and marketplace submission
223
+
224
+ ## Common Mistakes
225
+
226
+ | Mistake | Fix |
227
+ |---------|-----|
228
+ | Changing field key after creation | Keys are immutable; plan carefully upfront |
229
+ | Missing translations | Always provide titles and descriptions for UI |
230
+ | Using external storage | CustomFields are the recommended persistence mechanism |
231
+ | No loading states | Show `<Spinner />` while fetching data |
232
+ | Skipping error handling | Handle failed updates gracefully |
233
+
234
+ ## References
235
+
236
+ - [Save Data from Your App](https://developers.trackunit.com/docs/save-data-from-your-app)
237
+ - [Defining a Custom Field](https://developers.trackunit.com/docs/defining-a-custom-field)
238
+ - [Using a Custom Field](https://developers.trackunit.com/docs/using-a-custom-field)
239
+ - [Deploying a New Custom Field](https://developers.trackunit.com/docs/deploying-a-new-custom-field)
@@ -0,0 +1,147 @@
1
+ ---
2
+ name: graphql
3
+ description: Use when setting up GraphQL tooling, writing queries/mutations, generating hooks, or fetching data from Trackunit API.
4
+ ---
5
+
6
+ # GraphQL API Integration
7
+
8
+ ## Overview
9
+
10
+ Trackunit exposes a GraphQL API with NX executors for code generation. Use `@trackunit/react-graphql-tools` to generate typed React hooks from `.graphql` files.
11
+
12
+ ## When to Use
13
+
14
+ - Setting up GraphQL in an extension
15
+ - Writing queries or mutations
16
+ - Generating React hooks from `.graphql` files
17
+ - Fetching data from Trackunit API
18
+
19
+ **Not for:** Time series data (use `graphql-timeseries` skill).
20
+
21
+ ## Setup
22
+
23
+ ### Install GraphQL Tools
24
+
25
+ ```bash
26
+ npm install @trackunit/react-graphql-tools
27
+ ```
28
+
29
+ ### Set Up GraphQL Tooling
30
+
31
+ ```bash
32
+ npx nx generate @trackunit/react-graphql-tools:add-graphql --project=[project-name]
33
+ ```
34
+
35
+ Where `[project-name]` is found in the library's `project.json` file (format: `[subdir-name]-[extension-name]` for extensions in subdirectories).
36
+
37
+ ### Generate React Hooks
38
+
39
+ ```bash
40
+ npx nx run [feature-name]-[extension-name]:graphql-hooks
41
+ ```
42
+
43
+ This generates React hooks from `.graphql` files in the `src` folder.
44
+
45
+ ## GraphQL File Structure
46
+
47
+ Create `.graphql` files in `libs/[feature-name]/[extension-name]/src/` directory.
48
+
49
+ ### Query Syntax
50
+
51
+ ```graphql
52
+ query QueryName($variable: Type) {
53
+ fieldName {
54
+ subField
55
+ }
56
+ }
57
+ ```
58
+
59
+ ### Mutation Syntax
60
+
61
+ ```graphql
62
+ mutation MutationName($variable: Type!) {
63
+ mutationField(input: $variable) {
64
+ result
65
+ }
66
+ }
67
+ ```
68
+
69
+ ## React Integration
70
+
71
+ ### Import Generated Hooks
72
+
73
+ ```typescript
74
+ import { QueryNameDocument } from "./generated/graphql-api/graphql";
75
+ ```
76
+
77
+ ### Use Queries
78
+
79
+ ```typescript
80
+ import { useQuery } from '@trackunit/react-graphql-hooks';
81
+
82
+ const { data, loading, error } = useQuery(QueryNameDocument, {
83
+ variables: { /* your variables */ }
84
+ });
85
+ ```
86
+
87
+ ### Use Mutations
88
+
89
+ ```typescript
90
+ import { useMutation } from '@apollo/client';
91
+
92
+ const [mutationName, { data, loading, error }] = useMutation(MutationNameDocument);
93
+ ```
94
+
95
+ ## Common Patterns
96
+
97
+ ### Query with Variables
98
+
99
+ ```typescript
100
+ const { data, loading, error } = useQuery(GetAssetDocument, {
101
+ variables: { assetId: "123" }
102
+ });
103
+
104
+ if (loading) return <Spinner />;
105
+ if (error) return <ErrorMessage error={error} />;
106
+
107
+ return <AssetDisplay asset={data?.asset} />;
108
+ ```
109
+
110
+ ### Mutation with Refetch
111
+
112
+ ```typescript
113
+ import { useMutation } from '@apollo/client';
114
+
115
+ const [updateAsset, { loading }] = useMutation(UpdateAssetDocument, {
116
+ refetchQueries: [{ query: GetAssetDocument, variables: { assetId } }]
117
+ });
118
+
119
+ const handleUpdate = async (input) => {
120
+ await updateAsset({ variables: { input } });
121
+ };
122
+ ```
123
+
124
+ ### Polling
125
+
126
+ ```typescript
127
+ const { data } = useQuery(GetAssetsDocument, {
128
+ pollInterval: 5000, // Poll every 5 seconds
129
+ });
130
+ ```
131
+
132
+ ## References
133
+
134
+ - [Calling Trackunit GraphQL API](https://developers.trackunit.com/docs/graphql-api)
135
+ - [Apollo useQuery documentation](https://www.apollographql.com/docs/react/data/queries)
136
+ - [Apollo useMutation documentation](https://www.apollographql.com/docs/react/data/mutations)
137
+ - [GraphQL Code Generator](https://the-guild.dev/graphql/codegen)
138
+
139
+ ## Common Mistakes
140
+
141
+ | Mistake | Fix |
142
+ |---------|-----|
143
+ | Using generic GraphQL libraries | Use `@trackunit/react-graphql-tools` only |
144
+ | Missing hook generation | Run `nx run [project]:graphql-hooks` after adding `.graphql` files |
145
+ | Wrong project name format | Use `[subdir-name]-[extension-name]` from `project.json` |
146
+ | No loading/error states | Always handle `loading` and `error` from hooks |
147
+ | Importing from wrong path | Import from `./generated/graphql-api/graphql` |