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