@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.
- package/LICENSE.txt +82 -0
- package/dist/.a4drules/build-validation.md +81 -0
- package/dist/.a4drules/code-quality.md +150 -0
- package/dist/.a4drules/graphql/tools/knowledge/lds-explore-graphql-schema.md +227 -0
- package/dist/.a4drules/graphql/tools/knowledge/lds-generate-graphql-mutationquery.md +212 -0
- package/dist/.a4drules/graphql/tools/knowledge/lds-generate-graphql-readquery.md +185 -0
- package/dist/.a4drules/graphql/tools/knowledge/lds-guide-graphql.md +205 -0
- package/dist/.a4drules/graphql/tools/schemas/shared.graphqls +1150 -0
- package/dist/.a4drules/graphql.md +408 -0
- package/dist/.a4drules/images.md +13 -0
- package/dist/.a4drules/react.md +361 -0
- package/dist/.a4drules/react_image_processing.md +45 -0
- package/dist/.a4drules/skills/install-feature/SKILL.md +66 -0
- package/dist/.a4drules/skills/install-feature/scripts/copy-feature-assets.sh +36 -0
- package/dist/.a4drules/typescript.md +224 -0
- package/dist/.forceignore +15 -0
- package/dist/.husky/pre-commit +4 -0
- package/dist/.prettierignore +11 -0
- package/dist/.prettierrc +17 -0
- package/dist/CHANGELOG.md +364 -0
- package/dist/README.md +18 -0
- package/dist/config/project-scratch-def.json +13 -0
- package/dist/force-app/main/default/digitalExperienceConfigs/appreactb2csample1.digitalExperienceConfig +8 -0
- package/dist/force-app/main/default/digitalExperiences/site/appreactb2csample1/appreactb2csample1.digitalExperience-meta.xml +11 -0
- package/dist/force-app/main/default/digitalExperiences/site/appreactb2csample1/sfdc_cms__site/appreactb2csample1/_meta.json +5 -0
- package/dist/force-app/main/default/digitalExperiences/site/appreactb2csample1/sfdc_cms__site/appreactb2csample1/content.json +10 -0
- package/dist/force-app/main/default/networks/appreactb2csample.network +60 -0
- package/dist/force-app/main/default/package.xml +20 -0
- package/dist/force-app/main/default/sites/appreactb2csample.site +31 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/.prettierignore +9 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/.prettierrc +11 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/appreactb2csample.webapplication-meta.xml +7 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/build/vite.config.d.ts +2 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/build/vite.config.js +74 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/e2e/app.spec.ts +24 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/eslint.config.js +113 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/index.html +13 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/package-lock.json +7157 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/package.json +45 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/playwright.config.ts +24 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/scripts/rewrite-e2e-assets.mjs +23 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/src/api/graphql-operations-types.ts +127 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/src/api/utils/query/highRevenueAccountsQuery.graphql +29 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/src/app.tsx +41 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/src/appLayout.tsx +161 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/src/assets/icons/book.svg +3 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/src/assets/icons/copy.svg +4 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/src/assets/icons/rocket.svg +3 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/src/assets/icons/star.svg +3 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/src/assets/images/codey-1.png +0 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/src/assets/images/codey-2.png +0 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/src/assets/images/codey-3.png +0 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/src/assets/images/vibe-codey.svg +194 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/src/components/ui/alert.tsx +65 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/src/components/ui/button.tsx +54 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/src/components/ui/card.tsx +77 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/src/components/ui/field.tsx +111 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/src/components/ui/index.ts +71 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/src/components/ui/input.tsx +19 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/src/components/ui/label.tsx +19 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/src/components/ui/pagination.tsx +99 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/src/components/ui/select.tsx +151 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/src/components/ui/skeleton.tsx +7 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/src/components/ui/spinner.tsx +26 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/src/components/ui/table.tsx +114 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/src/components/ui/tabs.tsx +115 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/src/lib/utils.ts +6 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/src/pages/About.tsx +8 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/src/pages/Application.tsx +101 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/src/pages/Dashboard.tsx +101 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/src/pages/HelpCenter.tsx +29 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/src/pages/Home.tsx +30 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/src/pages/Maintenance.tsx +132 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/src/pages/NotFound.tsx +14 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/src/pages/PropertyDetails.tsx +68 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/src/pages/PropertyListings.tsx +84 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/src/routes.tsx +62 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/src/styles/global.css +80 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/tsconfig.json +36 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/tsconfig.node.json +13 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/vite-env.d.ts +1 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/vite.config.ts +83 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/vitest-env.d.ts +2 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/vitest.config.ts +11 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/vitest.setup.ts +1 -0
- package/dist/force-app/main/default/webapplications/appreactb2csample/webapplication.json +7 -0
- package/dist/jest.config.js +6 -0
- package/dist/package.json +37 -0
- package/dist/scripts/apex/hello.apex +10 -0
- package/dist/scripts/soql/account.soql +6 -0
- package/dist/sfdx-project.json +12 -0
- 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`.
|