@schandlergarcia/sf-web-components 1.9.48 → 1.9.52
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/.a4drules/features/command-center-dashboard-rule.md +20 -7
- package/.a4drules/features/engine-dashboard-rule.md +302 -0
- package/.a4drules/features/phase2-data-pattern.md +166 -0
- package/.a4drules/features/pre-code-checklist.md +72 -0
- package/.a4drules/skills/command-center-builder/SKILL.md +635 -29
- package/.a4drules/skills/command-center-project/SKILL.md +4 -4
- package/.a4drules/skills/component-library/SKILL.md +1000 -27
- package/.a4drules/troubleshooting/graphql-introspection-failure.md +286 -0
- package/.a4drules/validation-requirements.md +211 -0
- package/.a4drules/webapp-data.md +353 -0
- package/.a4drules/webapp-ui.md +16 -0
- package/CHANGELOG.md +258 -0
- package/data/README.md +80 -17
- package/data/engine-command-center-prd.md +436 -0
- package/data/schema.graphql +292 -0
- package/package.json +1 -1
- package/scripts/generate-schema-from-sample.mjs +370 -0
- package/scripts/postinstall.mjs +94 -0
- package/scripts/reset-command-center.sh +317 -3
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
# Salesforce Data Access
|
|
2
|
+
|
|
3
|
+
- **Fetch or display Salesforce data** — Query records (Account, Contact, Opportunity, custom objects) to show in a component
|
|
4
|
+
- **Create, update, or delete records** — Perform mutations on Salesforce data
|
|
5
|
+
- **Add data fetching to a component** — Wire up a React component to Salesforce data
|
|
6
|
+
- **Call REST APIs** — Use Connect REST, Apex REST, or UI API endpoints
|
|
7
|
+
- **Explore the org schema** — Discover available objects, fields, or relationships
|
|
8
|
+
|
|
9
|
+
## Data SDK Requirement
|
|
10
|
+
|
|
11
|
+
> **All Salesforce data access MUST use the Data SDK** (`@salesforce/sdk-data`). The SDK handles authentication, CSRF, and base URL resolution. Never use `fetch()` or `axios` directly.
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { createDataSDK, gql } from "@salesforce/sdk-data";
|
|
15
|
+
|
|
16
|
+
const sdk = await createDataSDK();
|
|
17
|
+
|
|
18
|
+
// GraphQL for record queries/mutations (PREFERRED)
|
|
19
|
+
const response = await sdk.graphql?.<ResponseType>(query, variables);
|
|
20
|
+
|
|
21
|
+
// REST for Connect REST, Apex REST, UI API (when GraphQL insufficient)
|
|
22
|
+
const res = await sdk.fetch?.("/services/apexrest/my-resource");
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**Always use optional chaining** (`sdk.graphql?.()`, `sdk.fetch?.()`) — these methods may be undefined in some surfaces.
|
|
26
|
+
|
|
27
|
+
## Supported APIs
|
|
28
|
+
|
|
29
|
+
**Only the following APIs are permitted.** Any endpoint not listed here must not be used.
|
|
30
|
+
|
|
31
|
+
| API | Method | Endpoints / Use Case |
|
|
32
|
+
|-----|--------|----------------------|
|
|
33
|
+
| GraphQL | `sdk.graphql` | All record queries and mutations via `uiapi { }` namespace |
|
|
34
|
+
| UI API REST | `sdk.fetch` | `/services/data/v{ver}/ui-api/records/{id}` — record metadata when GraphQL is insufficient |
|
|
35
|
+
| Apex REST | `sdk.fetch` | `/services/apexrest/{resource}` — custom server-side logic, aggregates, multi-step transactions |
|
|
36
|
+
| Connect REST | `sdk.fetch` | `/services/data/v{ver}/connect/file/upload/config` — file upload config |
|
|
37
|
+
| Einstein LLM | `sdk.fetch` | `/services/data/v{ver}/einstein/llm/prompt/generations` — AI text generation |
|
|
38
|
+
|
|
39
|
+
**Not supported:**
|
|
40
|
+
|
|
41
|
+
- **Enterprise REST query endpoint** (`/services/data/v*/query` with SOQL) — blocked at the proxy level. Use GraphQL for record reads; use Apex REST if server-side SOQL aggregates are required.
|
|
42
|
+
- **Aura-enabled Apex** (`@AuraEnabled`) — an LWC/Aura pattern with no invocation path from React webapps.
|
|
43
|
+
- **Chatter API** (`/chatter/users/me`) — use `uiapi { currentUser { ... } }` in a GraphQL query instead.
|
|
44
|
+
- **Any other Salesforce REST endpoint** not listed in the supported table above.
|
|
45
|
+
|
|
46
|
+
## Decision: GraphQL vs REST
|
|
47
|
+
|
|
48
|
+
| Need | Method | Example |
|
|
49
|
+
|------|--------|---------|
|
|
50
|
+
| Query/mutate records | `sdk.graphql` | Account, Contact, custom objects |
|
|
51
|
+
| Current user info | `sdk.graphql` | `uiapi { currentUser { Id Name { value } } }` |
|
|
52
|
+
| UI API record metadata | `sdk.fetch` | `/ui-api/records/{id}` |
|
|
53
|
+
| Connect REST | `sdk.fetch` | `/connect/file/upload/config` |
|
|
54
|
+
| Apex REST | `sdk.fetch` | `/services/apexrest/auth/login` |
|
|
55
|
+
| Einstein LLM | `sdk.fetch` | `/einstein/llm/prompt/generations` |
|
|
56
|
+
|
|
57
|
+
**GraphQL is preferred** for record operations. Use REST only when GraphQL doesn't cover the use case.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## GraphQL Workflow
|
|
62
|
+
|
|
63
|
+
### Step 1: Acquire Schema
|
|
64
|
+
|
|
65
|
+
The `schema.graphql` file is **already checked into the repo** at the SFDX project root. **Never open or parse it directly.**
|
|
66
|
+
|
|
67
|
+
- Pre-built from sample data, contains Engine objects (Contact, Trip__c, Flight__c, etc.)
|
|
68
|
+
- No generation needed — just use it
|
|
69
|
+
- If you ever need to regenerate (after adding fields to sample data): `npm run graphql:schema:sample` from webapp dir
|
|
70
|
+
|
|
71
|
+
### Step 2: Look Up Entity Schema
|
|
72
|
+
|
|
73
|
+
Map user intent to PascalCase names ("accounts" → `Account`), then **run the search script from the project root**:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
# From project root — look up all relevant schema info for one or more entities
|
|
77
|
+
bash scripts/graphql-search.sh Account
|
|
78
|
+
|
|
79
|
+
# Multiple entities at once
|
|
80
|
+
bash scripts/graphql-search.sh Account Contact Opportunity
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The script outputs five sections per entity:
|
|
84
|
+
1. **Type definition** — all queryable fields and relationships
|
|
85
|
+
2. **Filter options** — available fields for `where:` conditions
|
|
86
|
+
3. **Sort options** — available fields for `orderBy:`
|
|
87
|
+
4. **Create input** — fields accepted by create mutations
|
|
88
|
+
5. **Update input** — fields accepted by update mutations
|
|
89
|
+
|
|
90
|
+
Use this output to determine exact field names before writing any query or mutation. **Maximum 2 script runs.** If the entity still can't be found, ask the user — the object may not be deployed.
|
|
91
|
+
|
|
92
|
+
### Step 3: Generate Query
|
|
93
|
+
|
|
94
|
+
Use the templates below. Every field name **must** be verified from the script output in Step 2.
|
|
95
|
+
|
|
96
|
+
#### Read Query Template
|
|
97
|
+
|
|
98
|
+
```graphql
|
|
99
|
+
query GetAccounts {
|
|
100
|
+
uiapi {
|
|
101
|
+
query {
|
|
102
|
+
Account(where: { Industry: { eq: "Technology" } }, first: 10) {
|
|
103
|
+
edges {
|
|
104
|
+
node {
|
|
105
|
+
Id
|
|
106
|
+
Name @optional { value }
|
|
107
|
+
Industry @optional { value }
|
|
108
|
+
# Parent relationship
|
|
109
|
+
Owner @optional { Name { value } }
|
|
110
|
+
# Child relationship
|
|
111
|
+
Contacts @optional {
|
|
112
|
+
edges { node { Name @optional { value } } }
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
**FLS Resilience**: Apply `@optional` to all record fields. The server omits inaccessible fields instead of failing. Consuming code must use optional chaining:
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
const name = node.Name?.value ?? "";
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
#### Mutation Template
|
|
129
|
+
|
|
130
|
+
```graphql
|
|
131
|
+
mutation CreateAccount($input: AccountCreateInput!) {
|
|
132
|
+
uiapi {
|
|
133
|
+
AccountCreate(input: $input) {
|
|
134
|
+
Record { Id Name { value } }
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
**Mutation constraints:**
|
|
141
|
+
- Create: Include required fields, only `createable` fields, no child relationships
|
|
142
|
+
- Update: Include `Id`, only `updateable` fields
|
|
143
|
+
- Delete: Include `Id` only
|
|
144
|
+
|
|
145
|
+
#### Object Metadata & Picklist Values
|
|
146
|
+
|
|
147
|
+
Use `uiapi { objectInfos(...) }` to fetch field metadata or picklist values. Pass **either** `apiNames` or `objectInfoInputs` — never both in the same query.
|
|
148
|
+
|
|
149
|
+
**Object metadata** (field labels, data types, CRUD flags):
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
const GET_OBJECT_INFO = gql`
|
|
153
|
+
query GetObjectInfo($apiNames: [String!]!) {
|
|
154
|
+
uiapi {
|
|
155
|
+
objectInfos(apiNames: $apiNames) {
|
|
156
|
+
ApiName
|
|
157
|
+
label
|
|
158
|
+
labelPlural
|
|
159
|
+
fields {
|
|
160
|
+
ApiName
|
|
161
|
+
label
|
|
162
|
+
dataType
|
|
163
|
+
updateable
|
|
164
|
+
createable
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
`;
|
|
170
|
+
|
|
171
|
+
const sdk = await createDataSDK();
|
|
172
|
+
const response = await sdk.graphql?.(GET_OBJECT_INFO, { apiNames: ["Account"] });
|
|
173
|
+
const objectInfos = response?.data?.uiapi?.objectInfos ?? [];
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
**Picklist values** (use `objectInfoInputs` + `... on PicklistField` inline fragment):
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
const GET_PICKLIST_VALUES = gql`
|
|
180
|
+
query GetPicklistValues($objectInfoInputs: [ObjectInfoInput!]!) {
|
|
181
|
+
uiapi {
|
|
182
|
+
objectInfos(objectInfoInputs: $objectInfoInputs) {
|
|
183
|
+
ApiName
|
|
184
|
+
fields {
|
|
185
|
+
ApiName
|
|
186
|
+
... on PicklistField {
|
|
187
|
+
picklistValuesByRecordTypeIDs {
|
|
188
|
+
recordTypeID
|
|
189
|
+
picklistValues {
|
|
190
|
+
label
|
|
191
|
+
value
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
`;
|
|
200
|
+
|
|
201
|
+
const response = await sdk.graphql?.(GET_PICKLIST_VALUES, {
|
|
202
|
+
objectInfoInputs: [{ objectApiName: "Account" }],
|
|
203
|
+
});
|
|
204
|
+
const fields = response?.data?.uiapi?.objectInfos?.[0]?.fields ?? [];
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Step 4: Validate & Test
|
|
208
|
+
|
|
209
|
+
1. **Lint**: `npx eslint <file>` from webapp dir
|
|
210
|
+
2. **Test**: Ask user before testing. For mutations, request input values — never fabricate data.
|
|
211
|
+
|
|
212
|
+
**If ESLint reports a GraphQL error** (e.g. `Cannot query field`, `Unknown type`, `Unknown argument`), the field or type name is wrong. Re-run the schema search script to find the correct name — do not guess:
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
# From project root — re-check the entity that caused the error
|
|
216
|
+
bash scripts/graphql-search.sh <EntityName>
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Then fix the query using the exact names from the script output.
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## Webapp Integration (React)
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
import { createDataSDK, gql } from "@salesforce/sdk-data";
|
|
227
|
+
|
|
228
|
+
const GET_ACCOUNTS = gql`
|
|
229
|
+
query GetAccounts {
|
|
230
|
+
uiapi {
|
|
231
|
+
query {
|
|
232
|
+
Account(first: 10) {
|
|
233
|
+
edges {
|
|
234
|
+
node {
|
|
235
|
+
Id
|
|
236
|
+
Name @optional { value }
|
|
237
|
+
Industry @optional { value }
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
`;
|
|
245
|
+
|
|
246
|
+
const sdk = await createDataSDK();
|
|
247
|
+
const response = await sdk.graphql?.(GET_ACCOUNTS);
|
|
248
|
+
|
|
249
|
+
if (response?.errors?.length) {
|
|
250
|
+
throw new Error(response.errors.map(e => e.message).join("; "));
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const accounts = response?.data?.uiapi?.query?.Account?.edges?.map(e => e.node) ?? [];
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## REST API Patterns
|
|
258
|
+
|
|
259
|
+
Use `sdk.fetch` when GraphQL is insufficient. See the [Supported APIs](#supported-apis) table for the full allowlist.
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
declare const __SF_API_VERSION__: string;
|
|
263
|
+
const API_VERSION = typeof __SF_API_VERSION__ !== "undefined" ? __SF_API_VERSION__ : "65.0";
|
|
264
|
+
|
|
265
|
+
// Connect — file upload config
|
|
266
|
+
const res = await sdk.fetch?.(`/services/data/v${API_VERSION}/connect/file/upload/config`);
|
|
267
|
+
|
|
268
|
+
// Apex REST (no version in path)
|
|
269
|
+
const res = await sdk.fetch?.("/services/apexrest/auth/login", {
|
|
270
|
+
method: "POST",
|
|
271
|
+
body: JSON.stringify({ email, password }),
|
|
272
|
+
headers: { "Content-Type": "application/json" },
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
// UI API — record with metadata (prefer GraphQL for simple reads)
|
|
276
|
+
const res = await sdk.fetch?.(`/services/data/v${API_VERSION}/ui-api/records/${recordId}`);
|
|
277
|
+
|
|
278
|
+
// Einstein LLM
|
|
279
|
+
const res = await sdk.fetch?.(`/services/data/v${API_VERSION}/einstein/llm/prompt/generations`, {
|
|
280
|
+
method: "POST",
|
|
281
|
+
body: JSON.stringify({ promptTextorId: prompt }),
|
|
282
|
+
});
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
**Current user**: Do not use Chatter (`/chatter/users/me`). Use GraphQL instead:
|
|
286
|
+
|
|
287
|
+
```typescript
|
|
288
|
+
const GET_CURRENT_USER = gql`
|
|
289
|
+
query CurrentUser {
|
|
290
|
+
uiapi { currentUser { Id Name { value } } }
|
|
291
|
+
}
|
|
292
|
+
`;
|
|
293
|
+
const response = await sdk.graphql?.(GET_CURRENT_USER);
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
## Directory Structure
|
|
299
|
+
|
|
300
|
+
```
|
|
301
|
+
<project-root>/ ← SFDX project root
|
|
302
|
+
├── schema.graphql ← grep target (lives here)
|
|
303
|
+
├── sfdx-project.json
|
|
304
|
+
└── force-app/main/default/webapplications/<app-name>/ ← webapp dir
|
|
305
|
+
├── package.json ← npm scripts
|
|
306
|
+
└── src/
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
| Command | Run From | Why |
|
|
310
|
+
|---------|----------|-----|
|
|
311
|
+
| `npm run graphql:schema:sample` | webapp dir | Regenerate schema (rarely needed) |
|
|
312
|
+
| `npx eslint <file>` | webapp dir | Reads eslint.config.js |
|
|
313
|
+
| `bash scripts/graphql-search.sh <Entity>` | project root | Schema lookup |
|
|
314
|
+
| `sf api request rest` | project root | Needs sfdx-project.json |
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
## Quick Reference
|
|
319
|
+
|
|
320
|
+
### Schema Lookup (from project root)
|
|
321
|
+
|
|
322
|
+
Run the search script to get all relevant schema info in one step:
|
|
323
|
+
|
|
324
|
+
```bash
|
|
325
|
+
bash scripts/graphql-search.sh <EntityName>
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
| Script Output Section | Used For |
|
|
329
|
+
|-----------------------|----------|
|
|
330
|
+
| Type definition | Field names, parent/child relationships |
|
|
331
|
+
| Filter options | `where:` conditions |
|
|
332
|
+
| Sort options | `orderBy:` |
|
|
333
|
+
| CreateRepresentation | Create mutation field list |
|
|
334
|
+
| UpdateRepresentation | Update mutation field list |
|
|
335
|
+
|
|
336
|
+
### Error Categories
|
|
337
|
+
|
|
338
|
+
| Error Contains | Resolution |
|
|
339
|
+
|----------------|------------|
|
|
340
|
+
| `Cannot query field` | Field name is wrong — run `graphql-search.sh <Entity>` and use the exact name from the Type definition section |
|
|
341
|
+
| `Unknown type` | Type name is wrong — run `graphql-search.sh <Entity>` to confirm the correct PascalCase entity name |
|
|
342
|
+
| `Unknown argument` | Argument name is wrong — run `graphql-search.sh <Entity>` and check Filter or OrderBy sections |
|
|
343
|
+
| `invalid syntax` | Fix syntax per error message |
|
|
344
|
+
| `validation error` | Field name is wrong — run `graphql-search.sh <Entity>` to verify |
|
|
345
|
+
| `VariableTypeMismatch` | Correct argument type from schema |
|
|
346
|
+
| `invalid cross reference id` | Entity deleted — ask for valid Id |
|
|
347
|
+
|
|
348
|
+
### Checklist
|
|
349
|
+
|
|
350
|
+
- [ ] All field names verified via search script (Step 2)
|
|
351
|
+
- [ ] `@optional` applied to record fields (reads)
|
|
352
|
+
- [ ] Optional chaining in consuming code
|
|
353
|
+
- [ ] Lint passes: `npx eslint <file>`
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# UI Platform Rule
|
|
2
|
+
|
|
3
|
+
**All new UI must be built as a Salesforce Web Application.**
|
|
4
|
+
|
|
5
|
+
When any task involves creating a new UI, frontend, page, dashboard, form, or user-facing feature:
|
|
6
|
+
|
|
7
|
+
1. Use `sf webapp generate` to scaffold the web app inside the SFDX project — do not use `create-react-app`, standalone Vite, or any other scaffold.
|
|
8
|
+
2. The app must live under `<sfdx-source>/webapplications/<AppName>/` as a WebApplication bundle.
|
|
9
|
+
3. Do not build new UIs as LWC components, Aura components, or Visualforce pages.
|
|
10
|
+
|
|
11
|
+
For setup from the project root, use `node scripts/setup-cli.mjs --help` and the options documented there.
|
|
12
|
+
|
|
13
|
+
## Data Access (MUST FOLLOW)
|
|
14
|
+
|
|
15
|
+
- **Never hardcode data in the app.** All data displayed in the UI must come from live Salesforce data fetching — no static arrays, mock objects, or placeholder values in production code.
|
|
16
|
+
- **Follow `.a4drules/webapp-data.md`** before writing any data access code. All implementation must match those rules (Data SDK, supported APIs, GraphQL workflow).
|
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,264 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.9.52] - 2026-04-01
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **data/engine-command-center-prd.md** - Complete PRD for Engine Travel Command Center
|
|
12
|
+
- Issue: PRD was external documentation, not part of package
|
|
13
|
+
- Solution: Include PRD in package data, install to webapp root automatically
|
|
14
|
+
- Enables agents to reference PRD directly without external files
|
|
15
|
+
- Location after install: `engine-command-center-prd.md` (webapp root)
|
|
16
|
+
- Sections: Product overview, users, brand tokens, architecture, layout, components, data model, build prompts, anti-patterns
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
- **scripts/postinstall.mjs** - Now installs Engine PRD to webapp root
|
|
20
|
+
- Copies `engine-command-center-prd.md` from package data/
|
|
21
|
+
- Accessible at same level as schema.graphql for easy reference
|
|
22
|
+
- Updated summary count to include PRD
|
|
23
|
+
|
|
24
|
+
- **scripts/reset-command-center.sh** - Now restores Engine PRD during reset
|
|
25
|
+
- Added restoration of `engine-command-center-prd.md`
|
|
26
|
+
- Updated success message to mention PRD restoration
|
|
27
|
+
|
|
28
|
+
- **.a4drules/** - Synced all skill files from reactapp4
|
|
29
|
+
- Updated existing skills: command-center-builder, command-center-project, component-library, outer-app
|
|
30
|
+
- Added new feature rules:
|
|
31
|
+
- `features/phase2-data-pattern.md` - Data patterns for Phase 2
|
|
32
|
+
- `features/pre-code-checklist.md` - Pre-code verification checklist
|
|
33
|
+
- Added root-level documentation:
|
|
34
|
+
- `validation-requirements.md` - Validation rules
|
|
35
|
+
- `webapp-data.md` - Data access patterns
|
|
36
|
+
- `webapp-ui.md` - UI component patterns
|
|
37
|
+
- Updated troubleshooting: `troubleshooting/graphql-introspection-failure.md`
|
|
38
|
+
- Updated dashboard rules: `features/engine-dashboard-rule.md`, `features/command-center-dashboard-rule.md`
|
|
39
|
+
|
|
40
|
+
### Rationale
|
|
41
|
+
**The Problem:** Engine PRD was maintained separately (in docs/ or at repo root in other projects). Agents had to search for it, and different projects had different versions.
|
|
42
|
+
|
|
43
|
+
**The Solution:** Include PRD in the package just like sample data and schema:
|
|
44
|
+
1. PRD ships with package in `data/`
|
|
45
|
+
2. Postinstall copies to webapp root
|
|
46
|
+
3. Reset restores it if modified
|
|
47
|
+
4. All projects get the canonical PRD version
|
|
48
|
+
|
|
49
|
+
**Benefits:**
|
|
50
|
+
- ✅ PRD is always available at `engine-command-center-prd.md`
|
|
51
|
+
- ✅ Agents can reference it without searching
|
|
52
|
+
- ✅ Single source of truth for Engine dashboard specs
|
|
53
|
+
- ✅ Phase 1-4 build prompts included
|
|
54
|
+
- ✅ Anti-patterns documented
|
|
55
|
+
- ✅ Consistent across all projects using the package
|
|
56
|
+
|
|
57
|
+
**What Gets Installed:**
|
|
58
|
+
```
|
|
59
|
+
webapp-root/
|
|
60
|
+
├── schema.graphql (6KB - GraphQL schema)
|
|
61
|
+
├── engine-command-center-prd.md (20KB - Complete PRD)
|
|
62
|
+
├── src/
|
|
63
|
+
│ └── data/
|
|
64
|
+
│ └── engine-sample-data.js (31KB - Sample data)
|
|
65
|
+
└── scripts/
|
|
66
|
+
└── generate-schema-from-sample.mjs (Generator)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Files Synced from reactapp4
|
|
70
|
+
All .a4drules files updated to latest versions:
|
|
71
|
+
- `skills/command-center-builder/SKILL.md`
|
|
72
|
+
- `skills/command-center-project/SKILL.md`
|
|
73
|
+
- `skills/component-library/SKILL.md`
|
|
74
|
+
- `skills/outer-app/SKILL.md`
|
|
75
|
+
- `features/command-center-dashboard-rule.md`
|
|
76
|
+
- `features/engine-dashboard-rule.md`
|
|
77
|
+
- `features/phase2-data-pattern.md` (new)
|
|
78
|
+
- `features/pre-code-checklist.md` (new)
|
|
79
|
+
- `troubleshooting/graphql-introspection-failure.md`
|
|
80
|
+
- `validation-requirements.md` (new)
|
|
81
|
+
- `webapp-data.md` (new)
|
|
82
|
+
- `webapp-ui.md` (new)
|
|
83
|
+
|
|
84
|
+
### Migration
|
|
85
|
+
No breaking changes. When users update to v1.9.52:
|
|
86
|
+
- Postinstall automatically copies PRD to webapp root
|
|
87
|
+
- Reset script now restores PRD
|
|
88
|
+
- All .a4drules skills updated to latest versions
|
|
89
|
+
- Existing projects continue working
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## [1.9.51] - 2026-04-01
|
|
94
|
+
|
|
95
|
+
### Added
|
|
96
|
+
- **data/schema.graphql** - Pre-built GraphQL schema for Engine Travel Command Center
|
|
97
|
+
- Issue: Org introspection fails due to null data types in some fields
|
|
98
|
+
- Solution: Pre-generate schema from sample data field names
|
|
99
|
+
- Enables GraphQL skills to work without org access
|
|
100
|
+
- Schema includes: Contact, Trip__c, Flight__c, Booking__c, Disruption__c, Rebooking_Action__c, Travel_Policy__c
|
|
101
|
+
- All field names match sample data for consistency
|
|
102
|
+
- Works in CI/CD, no org dependency
|
|
103
|
+
- 6KB pre-built schema with proper UIAPI structure
|
|
104
|
+
|
|
105
|
+
- **scripts/generate-schema-from-sample.mjs** - Schema generator script
|
|
106
|
+
- Creates minimal GraphQL schema from sample data field names
|
|
107
|
+
- Infers types from field names (String, Float, Boolean, ID)
|
|
108
|
+
- Generates proper UIAPI structure with connections, filters, pagination
|
|
109
|
+
- Writes to webapp root: `schema.graphql`
|
|
110
|
+
- Enables regeneration when sample data fields change
|
|
111
|
+
|
|
112
|
+
- **.a4drules/troubleshooting/graphql-introspection-failure.md** - Comprehensive troubleshooting guide
|
|
113
|
+
- Documents org introspection bug and workaround
|
|
114
|
+
- Explains what still works (queries) vs what's broken (introspection)
|
|
115
|
+
- Step-by-step manual workaround if needed
|
|
116
|
+
- Decision tree for when to regenerate schema
|
|
117
|
+
|
|
118
|
+
- **.a4drules/features/engine-dashboard-rule.md** - Phase 3 implementation rules
|
|
119
|
+
- Updated to use pre-built schema (no generation needed)
|
|
120
|
+
- GraphQL skills work out of the box
|
|
121
|
+
- Sample data as fallback pattern documented
|
|
122
|
+
|
|
123
|
+
### Changed
|
|
124
|
+
- **scripts/postinstall.mjs** - Now installs GraphQL schema and generator
|
|
125
|
+
- Copies `schema.graphql` to webapp root
|
|
126
|
+
- Copies `generate-schema-from-sample.mjs` to webapp scripts/
|
|
127
|
+
- Adds `graphql:schema:sample` npm script to package.json
|
|
128
|
+
- Updated summary to show scripts installed
|
|
129
|
+
|
|
130
|
+
- **scripts/reset-command-center.sh** - Now restores GraphQL schema
|
|
131
|
+
- Added step 10: restore schema.graphql from package
|
|
132
|
+
- Handles both installed package and source package locations
|
|
133
|
+
- Updated success message to mention schema restoration
|
|
134
|
+
|
|
135
|
+
- **engine-command-center-prd.md** - Simplified Phase 3 prompt
|
|
136
|
+
- Before: Long explanation about schema generation and fallbacks
|
|
137
|
+
- After: "Schema is already pre-built - use GraphQL skills normally"
|
|
138
|
+
- 4-step process instead of complex troubleshooting
|
|
139
|
+
- Removed DataModeToggle requirement (automatic fallback)
|
|
140
|
+
|
|
141
|
+
### Rationale
|
|
142
|
+
**The Problem:** Org introspection fails with "cannot invoke FieldDataType.equals(Object) because the return value of Field.getDataType() is null". This breaks:
|
|
143
|
+
- `npm run graphql:schema` (introspection query)
|
|
144
|
+
- IDE autocomplete that queries org directly
|
|
145
|
+
- GraphQL codegen from org schema
|
|
146
|
+
|
|
147
|
+
But **queries still work** - introspection is just metadata.
|
|
148
|
+
|
|
149
|
+
**The Solution:** Pre-generate schema from sample data field names:
|
|
150
|
+
1. Sample data already has correct Salesforce field names
|
|
151
|
+
2. Generate schema.graphql from those field names
|
|
152
|
+
3. Check schema into repo (or package)
|
|
153
|
+
4. GraphQL skills use the pre-built schema
|
|
154
|
+
5. Queries execute normally using the same field names
|
|
155
|
+
|
|
156
|
+
**Benefits:**
|
|
157
|
+
- ✅ Agents don't troubleshoot - schema is ready
|
|
158
|
+
- ✅ Works without org access (CI/CD, offline dev)
|
|
159
|
+
- ✅ GraphQL skills work normally (exploring, generating, using)
|
|
160
|
+
- ✅ IDE tooling works (autocomplete, validation)
|
|
161
|
+
- ✅ Consistent with sample-first approach in Phase 2
|
|
162
|
+
- ✅ Simpler Phase 3 experience
|
|
163
|
+
|
|
164
|
+
**When to Regenerate:**
|
|
165
|
+
Only when adding new fields to sample data:
|
|
166
|
+
```bash
|
|
167
|
+
npm run graphql:schema:sample # Updates schema.graphql
|
|
168
|
+
git commit -m "Add new fields to schema"
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Files Included in Package
|
|
172
|
+
New files that ship with sf-web-components:
|
|
173
|
+
- `data/schema.graphql` (6KB)
|
|
174
|
+
- `scripts/generate-schema-from-sample.mjs`
|
|
175
|
+
- `.a4drules/troubleshooting/graphql-introspection-failure.md`
|
|
176
|
+
- `.a4drules/features/engine-dashboard-rule.md`
|
|
177
|
+
|
|
178
|
+
### Migration
|
|
179
|
+
No breaking changes. When users update to v1.9.51:
|
|
180
|
+
- Postinstall automatically copies schema.graphql and generator script
|
|
181
|
+
- Adds `graphql:schema:sample` to package.json scripts
|
|
182
|
+
- Existing projects continue working
|
|
183
|
+
- Phase 3 builds become simpler (no schema troubleshooting)
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## [1.9.50] - 2026-04-01
|
|
188
|
+
|
|
189
|
+
### Added
|
|
190
|
+
- **scripts/postinstall.mjs** - Now copies engine-sample-data.js to src/data/ on installation
|
|
191
|
+
- Issue: Phase 2 of Engine PRD required manually creating sample data file
|
|
192
|
+
- Solution: Automatically install pre-built sample data during package setup
|
|
193
|
+
- Sample data includes all Salesforce-shaped records and dashboard-ready derivatives
|
|
194
|
+
- File location: `src/data/engine-sample-data.js` (31KB of ready-to-use data)
|
|
195
|
+
- Eliminates manual data creation step from Phase 2 build process
|
|
196
|
+
|
|
197
|
+
- **scripts/reset-command-center.sh** - Now restores engine-sample-data.js during reset
|
|
198
|
+
- Issue: Reset could leave app with modified or missing sample data
|
|
199
|
+
- Solution: Added step 10 that copies fresh engine-sample-data.js from package
|
|
200
|
+
- Handles both installed package and source package locations
|
|
201
|
+
- Ensures consistent data state across resets
|
|
202
|
+
|
|
203
|
+
### Changed
|
|
204
|
+
- **engine-command-center-prd.md** - Updated Phase 2 prompt to use pre-installed data
|
|
205
|
+
- Before: "Create `src/data/engine-sample-data.js` with travelers, flights..."
|
|
206
|
+
- After: "Build components using the pre-installed sample data at `src/data/engine-sample-data.js`"
|
|
207
|
+
- Simplified Phase 2: just import and use, no data creation needed
|
|
208
|
+
|
|
209
|
+
- **Postinstall summary** - Now reports data files installed
|
|
210
|
+
- Added line: "Installed X sample data files"
|
|
211
|
+
|
|
212
|
+
- **Reset success message** - Now highlights sample data restoration
|
|
213
|
+
- Added: "Engine sample data (engine-sample-data.js)"
|
|
214
|
+
|
|
215
|
+
### Rationale
|
|
216
|
+
The engine-sample-data.js file (31KB) contains complete, dashboard-ready data for all Engine Travel Command Center components:
|
|
217
|
+
- Raw Salesforce records (Contact, Trip__c, Flight__c, Booking__c, Disruption__c, etc.)
|
|
218
|
+
- Pre-computed dashboard derivatives (MAP_MARKERS, MAP_ARCS, TRAVELER_CARDS, etc.)
|
|
219
|
+
- All field names match the org schema for easy live-data swap in Phase 3
|
|
220
|
+
|
|
221
|
+
Installing this automatically:
|
|
222
|
+
- Eliminates a 30+ minute manual data-creation step from Phase 2
|
|
223
|
+
- Ensures all developers use the same sample data
|
|
224
|
+
- Makes the Phase 2 prompt simpler and faster
|
|
225
|
+
- Consistent with how we handle other templates (Home.tsx, routes.tsx, etc.)
|
|
226
|
+
|
|
227
|
+
Restoring on reset:
|
|
228
|
+
- Prevents issues where modified sample data breaks demo builds
|
|
229
|
+
- Keeps data in sync with global.css, templates, and structure
|
|
230
|
+
- Allows developers to experiment with data changes and easily revert
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## [1.9.49] - 2026-04-01
|
|
235
|
+
|
|
236
|
+
### Added
|
|
237
|
+
- **scripts/reset-command-center.sh** - Now restores Engine-branded global.css during reset
|
|
238
|
+
- Issue: Reset didn't touch global.css, leaving inconsistent styling state
|
|
239
|
+
- Solution: Added step 9 that writes complete Engine-branded global.css template
|
|
240
|
+
- Includes all Engine brand tokens:
|
|
241
|
+
- `--color-engine-*` tokens (teal, coral, green, savings, etc.)
|
|
242
|
+
- `--color-brand-*` palette (12 teal shades from 50-950)
|
|
243
|
+
- Inter font stack (`--font-sans`)
|
|
244
|
+
- JetBrains Mono (`--font-mono`)
|
|
245
|
+
- Engine border radius tokens (`--radius-pill`, `--radius-card`)
|
|
246
|
+
- Complete `.heroui-scope` theme overrides with Engine colors
|
|
247
|
+
- Ensures consistent Engine branding across all reset operations
|
|
248
|
+
- Template sourced from enginewebexperience production configuration
|
|
249
|
+
|
|
250
|
+
### Changed
|
|
251
|
+
- **Reset success message** - Updated to highlight Engine branding restoration
|
|
252
|
+
- Before: "Everything preserved: Theme (global.css + initialMode)"
|
|
253
|
+
- After: "Restored: Engine brand theme (global.css)" with brand details
|
|
254
|
+
- Clarifies that reset actively restores Engine branding, not just preserves it
|
|
255
|
+
|
|
256
|
+
### Rationale
|
|
257
|
+
The reset script is tooling specifically for Engine Travel Command Center development. Making it restore Engine branding by default is consistent with:
|
|
258
|
+
- The script's existing opinionated behavior (specific templates for Home, Search, routes)
|
|
259
|
+
- The engine-command-center-prd.md PRD section 3 which specifies Engine brand tokens
|
|
260
|
+
- The fact that the script lives in sf-web-components package used for Engine dashboards
|
|
261
|
+
|
|
262
|
+
This eliminates a common source of confusion where developers would reset the app structure but be left with mismatched or baseline styling.
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
8
266
|
## Critical History: Component Naming Crisis (v1.9.29 - v1.9.42)
|
|
9
267
|
|
|
10
268
|
### The Problem
|