@salesforce/webapp-template-feature-react-file-upload-experimental 1.55.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/LICENSE.txt +82 -0
  2. package/README.md +102 -0
  3. package/dist/.a4drules/README.md +35 -0
  4. package/dist/.a4drules/a4d-webapp-generate.md +27 -0
  5. package/dist/.a4drules/build-validation.md +78 -0
  6. package/dist/.a4drules/code-quality.md +137 -0
  7. package/dist/.a4drules/graphql/tools/knowledge/lds-explore-graphql-schema.md +227 -0
  8. package/dist/.a4drules/graphql/tools/knowledge/lds-generate-graphql-mutationquery.md +212 -0
  9. package/dist/.a4drules/graphql/tools/knowledge/lds-generate-graphql-readquery.md +185 -0
  10. package/dist/.a4drules/graphql/tools/knowledge/lds-guide-graphql.md +205 -0
  11. package/dist/.a4drules/graphql/tools/schemas/shared.graphqls +1150 -0
  12. package/dist/.a4drules/graphql.md +409 -0
  13. package/dist/.a4drules/images.md +13 -0
  14. package/dist/.a4drules/react.md +387 -0
  15. package/dist/.a4drules/react_image_processing.md +45 -0
  16. package/dist/.a4drules/typescript.md +224 -0
  17. package/dist/.a4drules/ui-layout.md +23 -0
  18. package/dist/.a4drules/webapp-nav-and-placeholders.md +33 -0
  19. package/dist/.a4drules/webapp-no-node-e.md +25 -0
  20. package/dist/.a4drules/webapp-ui-first.md +32 -0
  21. package/dist/.a4drules/webapp.md +75 -0
  22. package/dist/.forceignore +15 -0
  23. package/dist/.husky/pre-commit +4 -0
  24. package/dist/.prettierignore +11 -0
  25. package/dist/.prettierrc +17 -0
  26. package/dist/AGENT.md +75 -0
  27. package/dist/CHANGELOG.md +803 -0
  28. package/dist/README.md +18 -0
  29. package/dist/config/project-scratch-def.json +13 -0
  30. package/dist/force-app/main/default/webapplications/feature-react-file-upload/.graphqlrc.yml +2 -0
  31. package/dist/force-app/main/default/webapplications/feature-react-file-upload/.prettierignore +9 -0
  32. package/dist/force-app/main/default/webapplications/feature-react-file-upload/.prettierrc +11 -0
  33. package/dist/force-app/main/default/webapplications/feature-react-file-upload/build/vite.config.d.ts +2 -0
  34. package/dist/force-app/main/default/webapplications/feature-react-file-upload/build/vite.config.js +93 -0
  35. package/dist/force-app/main/default/webapplications/feature-react-file-upload/codegen.yml +94 -0
  36. package/dist/force-app/main/default/webapplications/feature-react-file-upload/e2e/app.spec.ts +17 -0
  37. package/dist/force-app/main/default/webapplications/feature-react-file-upload/eslint.config.js +141 -0
  38. package/dist/force-app/main/default/webapplications/feature-react-file-upload/feature-react-file-upload.webapplication-meta.xml +7 -0
  39. package/dist/force-app/main/default/webapplications/feature-react-file-upload/index.html +13 -0
  40. package/dist/force-app/main/default/webapplications/feature-react-file-upload/package-lock.json +18396 -0
  41. package/dist/force-app/main/default/webapplications/feature-react-file-upload/package.json +66 -0
  42. package/dist/force-app/main/default/webapplications/feature-react-file-upload/playwright.config.ts +24 -0
  43. package/dist/force-app/main/default/webapplications/feature-react-file-upload/scripts/get-graphql-schema.mjs +68 -0
  44. package/dist/force-app/main/default/webapplications/feature-react-file-upload/scripts/rewrite-e2e-assets.mjs +23 -0
  45. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/api/fileUpload.ts +154 -0
  46. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/api/graphql-operations-types.ts +116 -0
  47. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/api/utils/accounts.ts +41 -0
  48. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/api/utils/query/highRevenueAccountsQuery.graphql +29 -0
  49. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/app.tsx +22 -0
  50. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/appLayout.tsx +9 -0
  51. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/assets/icons/book.svg +3 -0
  52. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/assets/icons/copy.svg +4 -0
  53. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/assets/icons/rocket.svg +3 -0
  54. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/assets/icons/star.svg +3 -0
  55. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/assets/images/codey-1.png +0 -0
  56. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/assets/images/codey-2.png +0 -0
  57. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/assets/images/codey-3.png +0 -0
  58. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/assets/images/vibe-codey.svg +194 -0
  59. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/assets/symbols.svg +1 -0
  60. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/assets/utility.svg +1 -0
  61. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/components/FileUpload.tsx +83 -0
  62. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/components/FileUploadDialog.tsx +79 -0
  63. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/components/FileUploadDropZone.tsx +82 -0
  64. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/components/FileUploadFileItem.tsx +99 -0
  65. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/components/FileUploadIcons.tsx +58 -0
  66. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/components/ui/alert.tsx +69 -0
  67. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/components/ui/button.tsx +67 -0
  68. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/components/ui/card.tsx +92 -0
  69. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/components/ui/dialog.tsx +143 -0
  70. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/components/ui/field.tsx +222 -0
  71. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/components/ui/index.ts +84 -0
  72. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/components/ui/input.tsx +19 -0
  73. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/components/ui/label.tsx +19 -0
  74. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/components/ui/pagination.tsx +112 -0
  75. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/components/ui/select.tsx +183 -0
  76. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/components/ui/separator.tsx +26 -0
  77. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/components/ui/skeleton.tsx +14 -0
  78. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/components/ui/spinner.tsx +15 -0
  79. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/components/ui/table.tsx +87 -0
  80. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/components/ui/tabs.tsx +78 -0
  81. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/components.json +18 -0
  82. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/hooks/useFileUpload.ts +299 -0
  83. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/hooks/useFileUploadDialog.ts +70 -0
  84. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/index.ts +56 -0
  85. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/lib/utils.ts +6 -0
  86. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/navigationMenu.tsx +80 -0
  87. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/pages/Home.tsx +25 -0
  88. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/pages/NotFound.tsx +18 -0
  89. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/router-utils.tsx +35 -0
  90. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/routes.tsx +22 -0
  91. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/styles/global.css +135 -0
  92. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/types/fileUpload.ts +26 -0
  93. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/utils/fileUploadUtils.ts +44 -0
  94. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/utils/labels.ts +21 -0
  95. package/dist/force-app/main/default/webapplications/feature-react-file-upload/tsconfig.json +36 -0
  96. package/dist/force-app/main/default/webapplications/feature-react-file-upload/tsconfig.node.json +13 -0
  97. package/dist/force-app/main/default/webapplications/feature-react-file-upload/vite-env.d.ts +1 -0
  98. package/dist/force-app/main/default/webapplications/feature-react-file-upload/vite.config.ts +43 -0
  99. package/dist/force-app/main/default/webapplications/feature-react-file-upload/vitest-env.d.ts +2 -0
  100. package/dist/force-app/main/default/webapplications/feature-react-file-upload/vitest.config.ts +11 -0
  101. package/dist/force-app/main/default/webapplications/feature-react-file-upload/vitest.setup.ts +1 -0
  102. package/dist/force-app/main/default/webapplications/feature-react-file-upload/webapplication.json +7 -0
  103. package/dist/jest.config.js +6 -0
  104. package/dist/package.json +38 -0
  105. package/dist/scripts/apex/hello.apex +10 -0
  106. package/dist/scripts/soql/account.soql +6 -0
  107. package/dist/sfdx-project.json +12 -0
  108. package/package.json +53 -0
  109. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/api/fileUpload.ts +154 -0
  110. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/appLayout.tsx +9 -0
  111. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/assets/symbols.svg +1 -0
  112. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/assets/utility.svg +1 -0
  113. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/components/FileUpload.tsx +83 -0
  114. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/components/FileUploadDialog.tsx +79 -0
  115. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/components/FileUploadDropZone.tsx +82 -0
  116. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/components/FileUploadFileItem.tsx +99 -0
  117. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/components/FileUploadIcons.tsx +58 -0
  118. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/hooks/useFileUpload.ts +299 -0
  119. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/hooks/useFileUploadDialog.ts +70 -0
  120. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/index.ts +56 -0
  121. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/pages/Home.tsx +25 -0
  122. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/routes.tsx +17 -0
  123. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/types/fileUpload.ts +26 -0
  124. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/utils/fileUploadUtils.ts +44 -0
  125. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/utils/labels.ts +21 -0
  126. package/src/force-app/main/default/webapplications/feature-react-file-upload/vite.config.ts +43 -0
@@ -0,0 +1,409 @@
1
+ # AI Rule: GraphQL Data Access
2
+
3
+ Instructs agents to use the established GraphQL utilities for Salesforce data access.
4
+
5
+ ## Targets
6
+ - `force-app/main/default/webapplications/<appName>/**/*.ts`
7
+ - `force-app/main/default/webapplications/<appName>/**/*.tsx`
8
+
9
+ ## TypeScript Types & Code Generation
10
+
11
+ ### Generated Types File
12
+ Types are auto-generated at: `force-app/main/default/webapplications/<appName>/src/api/graphql-operations-types.ts`
13
+
14
+ ### Generation Command
15
+ Run from the web app directory (replace `<appName>` with your app folder name, e.g. `base-react-app`):
16
+ ```bash
17
+ cd force-app/main/default/webapplications/<appName> && npm run graphql:codegen
18
+ ```
19
+
20
+ The codegen configuration is located at `scripts/graphql/codegen.js` (or the package's codegen config) and generates types from:
21
+ - Schema: `schema.graphql` (root level of the web app or as configured)
22
+ - Documents: `force-app/main/default/webapplications/<appName>/src/**/*.{graphql,ts,tsx}`
23
+
24
+ ### Type Naming Convention
25
+ For a GraphQL operation named `GetHighRevenueAccounts`:
26
+ - **Query/Mutation Response Type**: `GetHighRevenueAccountsQuery` or `GetHighRevenueAccountsMutation`
27
+ - **Input Variables Type**: `GetHighRevenueAccountsQueryVariables` or `GetHighRevenueAccountsMutationVariables`
28
+
29
+ ## Core Types & Function Signatures
30
+
31
+ ### executeGraphQL Function
32
+ Located in `force-app/main/default/webapplications/<appName>/src/api/graphql.ts`:
33
+
34
+ ```typescript
35
+ function executeGraphQL<T, InputVariables = Record<string, unknown>>(
36
+ query: string,
37
+ variables?: InputVariables
38
+ ): Promise<T>
39
+ ```
40
+
41
+ - `T` - The response type (e.g., `GetHighRevenueAccountsQuery`)
42
+ - `InputVariables` - The variables type (e.g., `GetHighRevenueAccountsQueryVariables`)
43
+
44
+ ### gql Template Tag
45
+ Also exported from `graphql.ts` for inline query definitions:
46
+
47
+ ```typescript
48
+ import { gql } from '../api/graphql';
49
+
50
+ const MY_QUERY = gql`
51
+ query MyQuery {
52
+ uiapi {
53
+ ...
54
+ }
55
+ }
56
+ `;
57
+ ```
58
+
59
+ The `gql` tag is a template literal that allows defining GraphQL queries inline while maintaining syntax highlighting in most editors.
60
+
61
+ ### GraphQLResponse Shape
62
+ The raw response wrapper (handled internally by `executeGraphQL`):
63
+
64
+ ```typescript
65
+ interface GraphQLResponse<T> {
66
+ data: T;
67
+ errors?: Array<{
68
+ message: string;
69
+ locations?: Array<{ line: number; column: number }>;
70
+ path?: string[];
71
+ }>;
72
+ }
73
+ ```
74
+
75
+ ### NodeOfConnection Utility Type
76
+ Extract the node type from a connection (edges/node pattern):
77
+
78
+ ```typescript
79
+ import { type NodeOfConnection } from '../api/graphql';
80
+
81
+ // Extract Account node type from the query response
82
+ type AccountNode = NodeOfConnection<
83
+ GetHighRevenueAccountsQuery['uiapi']['query']['Account']
84
+ >;
85
+ ```
86
+
87
+ ### UIAPI Response Shape
88
+ All Salesforce GraphQL queries follow this structure:
89
+
90
+ ```typescript
91
+ interface UIAPIQueryResponse {
92
+ uiapi: {
93
+ query: {
94
+ [ObjectName]: {
95
+ edges?: Array<{
96
+ node?: {
97
+ Id: string;
98
+ [FieldName]?: { value?: FieldType | null } | null;
99
+ // Reference fields include the related record
100
+ [ReferenceField]?: {
101
+ value?: string | null; // The ID
102
+ [RelatedField]?: { value?: RelatedType | null } | null;
103
+ } | null;
104
+ } | null;
105
+ } | null> | null;
106
+ } | null;
107
+ };
108
+ };
109
+ }
110
+ ```
111
+
112
+ ## Required Workflow
113
+
114
+ There are **two acceptable patterns** for defining GraphQL queries:
115
+
116
+ ### Pattern 1: External .graphql File (Recommended for complex queries)
117
+
118
+ #### Step 1: Create .graphql File
119
+ Store queries in `.graphql` files for codegen to process:
120
+
121
+ ```graphql
122
+ # force-app/main/default/webapplications/<appName>/src/api/utils/query/myQuery.graphql
123
+ query GetMyData($myVariable: String) {
124
+ uiapi {
125
+ query {
126
+ MyObject(first: 10, where: { Field: { eq: $myVariable } }) {
127
+ edges {
128
+ node {
129
+ Id
130
+ Name { value }
131
+ }
132
+ }
133
+ }
134
+ }
135
+ }
136
+ }
137
+ ```
138
+
139
+ #### Step 2: Run Code Generation
140
+ ```bash
141
+ cd force-app/main/default/webapplications/<appName> && npm run graphql:codegen
142
+ ```
143
+
144
+ This generates types in `graphql-operations-types.ts`:
145
+ - `GetMyDataQuery` - response type
146
+ - `GetMyDataQueryVariables` - variables type
147
+
148
+ #### Step 3: Import and Use
149
+ ```typescript
150
+ import { executeGraphQL, type NodeOfConnection } from '../api/graphql';
151
+ import MY_QUERY from './query/myQuery.graphql?raw';
152
+ import type {
153
+ GetMyDataQuery,
154
+ GetMyDataQueryVariables,
155
+ } from '../graphql-operations-types';
156
+
157
+ // Extract node type for cleaner return types
158
+ type MyNode = NodeOfConnection<GetMyDataQuery['uiapi']['query']['MyObject']>;
159
+
160
+ export async function getMyData(
161
+ variables: GetMyDataQueryVariables
162
+ ): Promise<MyNode[]> {
163
+ const response = await executeGraphQL<GetMyDataQuery>(MY_QUERY, variables);
164
+ return response.uiapi?.query?.MyObject?.edges?.map(edge => edge?.node) || [];
165
+ }
166
+ ```
167
+
168
+ **Key imports for Pattern 1:**
169
+ - `executeGraphQL` - Execute the query
170
+ - `NodeOfConnection` - Extract node types from connection responses
171
+ - Query from `.graphql` file with `?raw` suffix
172
+ - Generated types from `graphql-operations-types.ts`
173
+
174
+ **Pattern 1 Benefits:**
175
+ - Full codegen support with automatic type generation
176
+ - Syntax highlighting and validation in `.graphql` files
177
+ - Easier to share queries across multiple files/components
178
+ - Better for complex queries with fragments and multiple variables
179
+ - IDE support for GraphQL (autocomplete, validation)
180
+ - Queries can be tested independently
181
+ - Clear separation of concerns between query definition and usage
182
+
183
+ **Pattern 1 Limitations:**
184
+ - Requires separate file management
185
+ - Extra step to run codegen after query changes
186
+ - More boilerplate (file import with `?raw`, separate file to maintain)
187
+ - Slight overhead for very simple queries
188
+ - Need to navigate between files during development
189
+ - Doesn't support dynamic queries (e.g., the set of fields changes based on runtime conditions and cannot be predetermined)
190
+
191
+ ### Pattern 2: Inline Query with gql Tag (Recommended for simple queries)
192
+
193
+ For simpler queries without variables or when colocation is preferred:
194
+
195
+ ```typescript
196
+ import { executeGraphQL, gql } from '../api/graphql';
197
+ import { type CurrentUserQuery } from '../graphql-operations-types';
198
+
199
+ const CURRENT_USER_QUERY = gql`
200
+ query CurrentUser {
201
+ uiapi {
202
+ currentUser {
203
+ Id
204
+ Name {
205
+ value
206
+ }
207
+ }
208
+ }
209
+ }
210
+ `;
211
+
212
+ interface User {
213
+ id: string;
214
+ name: string;
215
+ }
216
+
217
+ export async function getCurrentUser(): Promise<User | null> {
218
+ try {
219
+ const response = await executeGraphQL<CurrentUserQuery>(CURRENT_USER_QUERY);
220
+
221
+ const userData = response.uiapi.currentUser;
222
+
223
+ if (!userData) {
224
+ throw new Error('No user data found');
225
+ }
226
+
227
+ return {
228
+ id: userData.Id,
229
+ name: userData.Name?.value || 'User',
230
+ };
231
+ } catch (error) {
232
+ console.error('Error fetching user data:', error);
233
+ throw error;
234
+ }
235
+ }
236
+ ```
237
+
238
+ **Key imports for Pattern 2:**
239
+ - `executeGraphQL` - Execute the query
240
+ - `gql` - Template tag for inline query definition
241
+ - Generated types from `graphql-operations-types.ts`
242
+
243
+ **Pattern 2 Benefits:**
244
+ - Query is colocated with usage code
245
+ - Supports dynamic queries (e.g., the set of fields changes based on runtime conditions and cannot be predetermined)
246
+ - No separate file to maintain
247
+ - Still gets type-checked against generated types
248
+ - Simpler for straightforward queries
249
+
250
+ **Pattern 2 Limitations:**
251
+ - Inline queries without `gql` template tag are not processed by codegen
252
+ - Must manually ensure query name matches generated types
253
+ - Less suitable for complex queries with fragments
254
+
255
+ ## Reference Examples
256
+
257
+ ### Pattern 1 Example: accounts.ts
258
+ See `force-app/main/default/webapplications/<appName>/src/api/utils/accounts.ts` for Pattern 1:
259
+ 1. Importing query from `.graphql` file with `?raw` suffix
260
+ 2. Importing generated types from `graphql-operations-types.ts`
261
+ 3. Using `NodeOfConnection` to extract node types
262
+ 4. Proper typing with `executeGraphQL<ResponseType>(query, variables)`
263
+ 5. Safe data extraction from the nested response
264
+
265
+ ### Pattern 2 Example: user.ts
266
+ See `force-app/main/default/webapplications/<appName>/src/api/utils/user.ts` for Pattern 2:
267
+ 1. Using `gql` template tag for inline query definition
268
+ 2. Importing generated types from `graphql-operations-types.ts`
269
+ 3. Simple query without variables
270
+ 4. Error handling with try/catch
271
+ 5. Direct access to `uiapi.currentUser` (non-connection response)
272
+
273
+ ## Conditional Field Selection with Directives
274
+
275
+ For dynamic fieldsets with known fields that should be conditionally included, use GraphQL directives instead of building queries dynamically. This preserves type generation while allowing runtime control.
276
+
277
+ ### Directives
278
+ - **`@include(if: $condition)`** - include field/fragment when `$condition` is `true`
279
+ - **`@skip(if: $condition)`** - skip field/fragment when `$condition` is `true`
280
+
281
+ ### Example with Fragments
282
+ ```graphql
283
+ # <appName>/src/api/utils/query/getAccountDetails.graphql
284
+ query GetAccountDetails(
285
+ $id: ID!
286
+ $includeFinancials: Boolean!
287
+ $includeContacts: Boolean!
288
+ ) {
289
+ uiapi {
290
+ query {
291
+ Account(where: { Id: { eq: $id } }) {
292
+ edges {
293
+ node {
294
+ Id
295
+ Name { value }
296
+ ...FinancialFields @include(if: $includeFinancials)
297
+ ...ContactFields @include(if: $includeContacts)
298
+ }
299
+ }
300
+ }
301
+ }
302
+ }
303
+ }
304
+
305
+ fragment FinancialFields on Account {
306
+ AnnualRevenue { value }
307
+ NumberOfEmployees { value }
308
+ }
309
+
310
+ fragment ContactFields on Account {
311
+ Phone { value }
312
+ Website { value }
313
+ }
314
+ ```
315
+
316
+ ### Usage
317
+ ```typescript
318
+ import { executeGraphQL } from '../api/graphql';
319
+ import QUERY from './query/getAccountDetails.graphql?raw';
320
+ import type {
321
+ GetAccountDetailsQuery,
322
+ GetAccountDetailsQueryVariables,
323
+ } from '../graphql-operations-types';
324
+
325
+ const data = await executeGraphQL<GetAccountDetailsQuery>(QUERY, {
326
+ id: accountId,
327
+ includeFinancials: userWantsFinancials,
328
+ includeContacts: userWantsContacts,
329
+ });
330
+ ```
331
+
332
+ ### Benefits
333
+ - Query stays in `.graphql` file (codegen works)
334
+ - Generated types include all possible fields as optional
335
+ - Runtime control over which fields are fetched
336
+ - Better performance when fields aren't needed
337
+ - Type safety is preserved
338
+
339
+ ## Anti-Patterns (Not Recommended)
340
+
341
+ ### Direct API Calls
342
+ ```typescript
343
+ // NOT RECOMMENDED: Direct axios/fetch calls for GraphQL
344
+ const response = await axios.post('/graphql', { query });
345
+
346
+ // PREFERRED: Use executeGraphQL
347
+ const data = await executeGraphQL<ResponseType>(query, variables);
348
+ ```
349
+
350
+ ### Missing Type Definitions
351
+ ```typescript
352
+ // NOT RECOMMENDED: Untyped GraphQL calls
353
+ const data = await executeGraphQL(query);
354
+
355
+ // PREFERRED: Provide response type
356
+ const data = await executeGraphQL<GetMyDataQuery>(query);
357
+ ```
358
+
359
+ ### Plain String Queries (Without gql Tag)
360
+ ```typescript
361
+ // NOT RECOMMENDED: Plain string queries without gql tag
362
+ const query = `query { ... }`;
363
+ const data = await executeGraphQL(query);
364
+
365
+ // PREFERRED: Use gql tag for inline queries
366
+ const QUERY = gql`query { ... }`;
367
+ const data = await executeGraphQL<ResponseType>(QUERY);
368
+
369
+ // OR: Use .graphql file for complex queries
370
+ import QUERY from './query/myQuery.graphql?raw';
371
+ const data = await executeGraphQL<ResponseType>(QUERY);
372
+ ```
373
+
374
+ **Why avoid plain strings:**
375
+ - No syntax highlighting or validation
376
+ - Harder to maintain and refactor
377
+ - More error-prone
378
+
379
+ ## Benefits of executeGraphQL
380
+ - Centralized error handling for GraphQL errors
381
+ - Consistent typing with `GraphQLResponse<T>` interface
382
+ - Uses the configured `baseDataClient` with proper authentication
383
+ - Automatic extraction of `data` from response envelope
384
+
385
+ ## Quality Checklist
386
+ Before completing GraphQL data access code:
387
+
388
+ ### For Pattern 1 (.graphql files):
389
+ 1. [ ] Create `.graphql` file for the query/mutation
390
+ 2. [ ] Run `npm run graphql:codegen` to generate types
391
+ 3. [ ] Import query with `?raw` suffix
392
+ 4. [ ] Import generated types from `graphql-operations-types.ts`
393
+ 5. [ ] Use `executeGraphQL<ResponseType>()` with proper generic
394
+ 6. [ ] Use `NodeOfConnection` for cleaner node types when needed
395
+ 7. [ ] Handle optional chaining for nested response data
396
+ 8. [ ] Follow the pattern in `accounts.ts`
397
+
398
+ ### For Pattern 2 (inline with gql):
399
+ 1. [ ] Define query using `gql` template tag
400
+ 2. [ ] Ensure query name matches generated types in `graphql-operations-types.ts`
401
+ 3. [ ] Import generated types for the query
402
+ 4. [ ] Use `executeGraphQL<ResponseType>()` with proper generic
403
+ 5. [ ] Handle errors with try/catch when appropriate
404
+ 6. [ ] Handle optional chaining for nested response data
405
+ 7. [ ] Follow the pattern in `user.ts`
406
+
407
+ ### General:
408
+ - [ ] Choose Pattern 1 for complex queries with variables, fragments, or when shared
409
+ - [ ] Choose Pattern 2 for simple, colocated queries without complex requirements
@@ -0,0 +1,13 @@
1
+ # Rule: Images
2
+
3
+ **Description:** Image rules for React web apps (SFDX): default to Unsplash, CSP compliance, and accessibility. Ensures Unsplash is used as the default image source unless the developer provides their own.
4
+
5
+ **Applies to:** `force-app/main/default/webapplications/*/**/*.{js,jsx,ts,tsx,css,html}`
6
+
7
+ **Guidelines:**
8
+ - Default to Unsplash when the user does not specify an image source; it is pre-configured in CSP and works in Salesforce without extra setup.
9
+ - Use URL format: `https://images.unsplash.com/photo-{PHOTO_ID}?w={WIDTH}&h={HEIGHT}&fit=crop&q=80&auto=format` (e.g. photo ID `1557683316-973673baf926`).
10
+ - If the user requests a different source, use it and inform: "Add CSP Trusted Site in Setup → Security → CSP Trusted Sites".
11
+ - Always add descriptive `alt` text; use `alt=""` only for decorative images.
12
+ - Avoid `placeholder.com`, `picsum.photos`, `via.placeholder.com` unless requested.
13
+ - CSP-approved domains: `images.unsplash.com` (primary), `source.unsplash.com`, `images.pexels.com`.