@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.
@@ -0,0 +1,286 @@
1
+ # GraphQL Schema Introspection Failure
2
+
3
+ ## Problem
4
+
5
+ When running `npm run graphql:schema`, the introspection query fails with an error like:
6
+
7
+ ```
8
+ Cannot invoke FieldDataType.equals(Object) because
9
+ the return value of Field.getDataType() is null
10
+ ```
11
+
12
+ This is an **org-level bug** where at least one field in the Salesforce org has a null data type, which breaks the `__schema` introspection query.
13
+
14
+ ## What's Broken
15
+
16
+ - ❌ Generating `schema.graphql` from org introspection
17
+ - ❌ Full schema with all org objects and fields
18
+ - ❌ Some IDE autocomplete features that query the org directly
19
+
20
+ ## What Still Works
21
+
22
+ - ✅ **Regular GraphQL queries** — Queries execute normally despite introspection failure
23
+ - ✅ All CRUD operations via GraphQL
24
+ - ✅ The Data SDK (`createDataSDK()`)
25
+ - ✅ UI API GraphQL endpoint (`/services/data/v{ver}/graphql`)
26
+ - ✅ **Schema generation from sample data** (see Solution below)
27
+
28
+ **Key insight:** Introspection is just metadata about the schema. It doesn't affect actual query execution.
29
+
30
+ ## Solution: Pre-built Schema (Already in Repo)
31
+
32
+ The `schema.graphql` file is **already checked into the repo** at the project root. It was generated from the sample data file and includes all Engine objects.
33
+
34
+ **You don't need to generate anything** — the schema is ready to use.
35
+
36
+ **What this provides:**
37
+ - ✅ `exploring-webapp-graphql-schema` skill works normally
38
+ - ✅ `generating-webapp-graphql-read-query` skill works normally
39
+ - ✅ `using-webapp-graphql` skill works normally
40
+ - ✅ IDE autocomplete and validation
41
+ - ✅ GraphQL codegen
42
+ - ✅ Normal Phase 3 workflow without manual workarounds
43
+ - ✅ Works in CI/CD without org access
44
+
45
+ **Schema contents:**
46
+ - Contact (travelers with custom fields)
47
+ - Trip__c, Flight__c, Booking__c, Disruption__c, Rebooking_Action__c, Travel_Policy__c
48
+ - All fields from `src/data/engine-sample-data.js`
49
+
50
+ ## Regenerating the Schema (Optional)
51
+
52
+ If you need to update the schema (e.g., after adding new fields to sample data):
53
+
54
+ ```bash
55
+ # From the webapp directory
56
+ npm run graphql:schema:sample
57
+ ```
58
+
59
+ This regenerates `schema.graphql` from the current sample data file. Then commit the updated schema to git.
60
+
61
+ ## Fallback: Copy Working Query Patterns (Manual)
62
+
63
+ Since introspection fails but queries work, you can implement GraphQL features by copying working patterns from elsewhere in the codebase.
64
+
65
+ ### Step 1: Find a Working Query
66
+
67
+ Look for existing GraphQL implementations (e.g., Account search, Contact queries in other features):
68
+
69
+ ```bash
70
+ cd force-app/main/default/webapplications/reactapp4
71
+ grep -r "uiapi {" src/
72
+ ```
73
+
74
+ ### Step 2: Copy the Pattern
75
+
76
+ The working pattern for UI API GraphQL queries:
77
+
78
+ ```typescript
79
+ import { createDataSDK, gql } from "@salesforce/sdk-data";
80
+
81
+ const GET_TRIPS = gql`
82
+ query GetTrips {
83
+ uiapi {
84
+ query {
85
+ Trip__c(first: 50) {
86
+ edges {
87
+ node {
88
+ Id
89
+ Trip_Name__c @optional { value }
90
+ Origin_City__c @optional { value }
91
+ Destination_City__c @optional { value }
92
+ Contact__c @optional { value }
93
+ Start_Date__c @optional { value }
94
+ End_Date__c @optional { value }
95
+ Status__c @optional { value }
96
+ Total_Cost__c @optional { value }
97
+ In_Policy__c @optional { value }
98
+ Has_Disruption__c @optional { value }
99
+ }
100
+ }
101
+ }
102
+ }
103
+ }
104
+ }
105
+ `;
106
+ ```
107
+
108
+ **Critical elements:**
109
+ - `uiapi { query { ... } }` wrapper
110
+ - Object name matches Salesforce API name (e.g., `Trip__c`, `Contact`)
111
+ - `@optional` directive on ALL fields except `Id` (for FLS resilience)
112
+ - Nested field access: `FieldName { value }` for most fields
113
+ - Lookup fields: `RelatedObject__c { Name { value } }`
114
+
115
+ ### Step 3: Implement Hooks
116
+
117
+ Create custom hooks using the Data SDK pattern:
118
+
119
+ ```typescript
120
+ import { useState, useEffect } from "react";
121
+ import { createDataSDK } from "@salesforce/sdk-data";
122
+
123
+ export function useTrips() {
124
+ const [data, setData] = useState<any[] | null>(null);
125
+ const [loading, setLoading] = useState(true);
126
+ const [error, setError] = useState<Error | null>(null);
127
+
128
+ useEffect(() => {
129
+ let mounted = true;
130
+
131
+ async function fetchData() {
132
+ try {
133
+ const sdk = await createDataSDK();
134
+ const response = await sdk.graphql?.(GET_TRIPS);
135
+
136
+ if (!mounted) return;
137
+
138
+ if (response?.errors?.length) {
139
+ throw new Error(response.errors.map((e: any) => e.message).join("; "));
140
+ }
141
+
142
+ const edges = response?.data?.uiapi?.query?.Trip__c?.edges ?? [];
143
+ const trips = edges.map((edge: any) => ({
144
+ Id: edge.node.Id,
145
+ Trip_Name__c: edge.node.Trip_Name__c?.value ?? null,
146
+ Origin_City__c: edge.node.Origin_City__c?.value ?? null,
147
+ // ... map all fields with optional chaining
148
+ }));
149
+
150
+ setData(trips);
151
+ setError(null);
152
+ } catch (err) {
153
+ if (mounted) {
154
+ setError(err instanceof Error ? err : new Error(String(err)));
155
+ }
156
+ } finally {
157
+ if (mounted) {
158
+ setLoading(false);
159
+ }
160
+ }
161
+ }
162
+
163
+ fetchData();
164
+
165
+ return () => {
166
+ mounted = false;
167
+ };
168
+ }, []);
169
+
170
+ return { data, loading, error };
171
+ }
172
+ ```
173
+
174
+ ### Step 4: Get Field Names from Sample Data
175
+
176
+ The sample data file already has real Salesforce field names. Read `src/data/engine-sample-data.js` to see which fields exist:
177
+
178
+ ```javascript
179
+ // File: src/data/engine-sample-data.js
180
+ export const TRIPS = [
181
+ {
182
+ Id: "a00xx000001",
183
+ Trip_Name__c: "Q1 Sales Conference - SF",
184
+ Origin_City__c: "New York",
185
+ Destination_City__c: "San Francisco",
186
+ // ... etc
187
+ }
188
+ ];
189
+ ```
190
+
191
+ Copy these exact field names into your GraphQL query.
192
+
193
+ ### Step 5: Test Your Queries
194
+
195
+ Create a test script to verify queries work:
196
+
197
+ ```javascript
198
+ // scripts/test-engine-query.mjs
199
+ import { getOrgInfo } from '@salesforce/webapp-experimental/app';
200
+
201
+ async function testQuery(query, objectName) {
202
+ const { rawInstanceUrl, apiVersion, accessToken } = await getOrgInfo();
203
+
204
+ const response = await fetch(
205
+ `${rawInstanceUrl}/services/data/v${apiVersion}/graphql`,
206
+ {
207
+ method: 'POST',
208
+ headers: {
209
+ Authorization: `Bearer ${accessToken}`,
210
+ 'Content-Type': 'application/json',
211
+ 'X-Chatter-Entity-Encoding': 'false',
212
+ },
213
+ body: JSON.stringify({ query }),
214
+ }
215
+ );
216
+
217
+ const result = await response.json();
218
+
219
+ if (result.errors) {
220
+ console.error(`❌ ${objectName} query failed:`, result.errors);
221
+ return false;
222
+ }
223
+
224
+ console.log(`✅ ${objectName} query succeeded`);
225
+ console.log(` Records found: ${result.data?.uiapi?.query?.[objectName]?.edges?.length}`);
226
+ return true;
227
+ }
228
+
229
+ // Test your queries
230
+ await testQuery(`query { uiapi { query { Trip__c(first: 1) { edges { node { Id } } } } } }`, 'Trip__c');
231
+ ```
232
+
233
+ Run: `node scripts/test-engine-query.mjs`
234
+
235
+ ## For Agents Building the Dashboard
236
+
237
+ **The schema.graphql file is already in the repo.** Just use the GraphQL skills normally:
238
+
239
+ 1. `exploring-webapp-graphql-schema` — Looks up field names
240
+ 2. `generating-webapp-graphql-read-query` — Creates queries
241
+ 3. `using-webapp-graphql` — Implements hooks
242
+
243
+ No schema generation needed during Phase 3.
244
+
245
+ ## For Developers Adding Fields
246
+
247
+ If you add new fields to `src/data/engine-sample-data.js`, regenerate the schema:
248
+
249
+ ```bash
250
+ cd force-app/main/default/webapplications/reactapp4
251
+ npm run graphql:schema:sample
252
+ git add ../../../../../../schema.graphql
253
+ git commit -m "Update schema with new fields"
254
+ ```
255
+
256
+ ## Decision Tree for Schema Updates
257
+
258
+ **Do you need to update schema.graphql?**
259
+
260
+ - ❌ Building dashboard from existing sample data? → No, use existing schema
261
+ - ✅ Added new fields to sample data? → Run `npm run graphql:schema:sample`
262
+ - ✅ Added new custom objects? → Update generation script first, then regenerate
263
+ - ✅ Want to try org introspection? → Run `npm run graphql:schema` (may fail)
264
+
265
+ ## Prevention
266
+
267
+ This is an org-level bug that can't be prevented from the web app side. The org admin would need to identify and fix the field(s) with null data types. However, this workaround allows development to continue without waiting for org fixes.
268
+
269
+ ## Related Files
270
+
271
+ - `.a4drules/features/engine-dashboard-rule.md` — Phase 3 implementation with workaround steps
272
+ - `engine-command-center-prd.md` — Phase 3 prompt updated to include fallback approach
273
+ - `scripts/get-graphql-schema.mjs` — The script that runs introspection (will fail with this bug)
274
+ - `scripts/test-engine-query.mjs` — Test script to verify queries work despite introspection failure
275
+
276
+ ## Success Indicators
277
+
278
+ You've successfully worked around the issue when:
279
+
280
+ - ✅ All custom hooks return data correctly (`{ data, loading, error }`)
281
+ - ✅ Dashboard displays live Salesforce data
282
+ - ✅ No GraphQL-related errors in browser console
283
+ - ✅ Test script shows all queries succeed
284
+ - ✅ Loading and error states work properly
285
+
286
+ The fact that schema.graphql doesn't exist is fine — it's only needed for introspection-based tooling, not for runtime queries.
@@ -0,0 +1,211 @@
1
+ ---
2
+ paths:
3
+ - "**/pages/*.tsx"
4
+ - "**/components/workspace/*.tsx"
5
+ ---
6
+
7
+ # Validation Requirements (MANDATORY)
8
+
9
+ This file defines the mandatory validation process for all command center dashboards.
10
+
11
+ ## The Validator
12
+
13
+ The validator is a shell script at `node_modules/@schandlergarcia/sf-web-components/scripts/validate-dashboard.sh` that automatically checks for common mistakes.
14
+
15
+ **Location to run:** Always run from the webapp directory:
16
+
17
+ ```bash
18
+ cd force-app/main/default/webapplications/reactapp4
19
+ npm run validate:dashboard
20
+ ```
21
+
22
+ ## When to Run
23
+
24
+ **Run the validator after EVERY significant change:**
25
+ - After creating a new dashboard page
26
+ - After adding components
27
+ - After updating styles
28
+ - After changing imports
29
+ - Before committing code
30
+ - Before reporting a phase/task as complete
31
+
32
+ **Frequency:** If in doubt, run it. It takes <1 second.
33
+
34
+ ## Zero Errors Required
35
+
36
+ The validator MUST show zero errors before:
37
+ - Proceeding to the next build phase
38
+ - Reporting a task as complete
39
+ - Committing code
40
+ - Deploying
41
+
42
+ **Do NOT:**
43
+ - Dismiss errors as "justified" or "acceptable"
44
+ - Skip validation "because you're sure it's correct"
45
+ - Report work as complete with validator errors
46
+ - Proceed to the next phase with errors
47
+
48
+ **Every error has a fix.** The fix is usually switching to a library component.
49
+
50
+ ## What the Validator Checks
51
+
52
+ 1. **Hand-rolled HTML cards** — Detects `<div className="bg-white border rounded-[10px]">` patterns that should be library cards
53
+ 2. **Forbidden colors** — Flags `text-black`, `text-white`, `bg-black` (use slate scale)
54
+ 3. **Inline styles** — Detects `style={{}}` and `<style>` tags (use Tailwind)
55
+ 4. **Missing useDataSource** — Checks that data uses sample/live mode pattern
56
+ 5. **Grid layouts** — Flags `grid-cols-5` or higher (max is 4 KPIs per row)
57
+ 6. **Position fixed** — Checks that `position: fixed` uses `createPortal`
58
+ 7. **shadcn imports** — Detects imports from `@/components/ui/` (use library)
59
+ 8. **Lucide imports** — Detects imports from `lucide-react` (use Heroicons)
60
+ 9. **File extension** — Ensures `.tsx` not `.jsx` (TypeScript project)
61
+ 10. **Dashboard navigation** — Detects `<nav>` elements (handled by AppLayout)
62
+ 11. **Missing space-y** — Checks for proper section spacing
63
+
64
+ ## Common Errors and Fixes
65
+
66
+ ### "Hand-rolled card detected"
67
+
68
+ **Error:** `bg-white border rounded-[10px] shadow-sm` pattern found
69
+ **Fix:** Wrap content in a library card component
70
+
71
+ ```tsx
72
+ // ❌ Wrong
73
+ <div className="bg-white border rounded-[10px] shadow-sm p-6">
74
+ <h2>Active Travelers</h2>
75
+ {travelers.map(...)}
76
+ </div>
77
+
78
+ // ✅ Correct
79
+ <ListCard title="Active Travelers" items={travelers} />
80
+
81
+ // ✅ Also correct (for custom content)
82
+ <WidgetCard
83
+ sections={[{
84
+ id: "travelers",
85
+ content: <div>{travelers.map(...)}</div>
86
+ }]}
87
+ />
88
+ ```
89
+
90
+ ### "Forbidden color"
91
+
92
+ **Error:** `text-white`, `text-black`, or `bg-black` detected
93
+ **Fix:** Use slate scale
94
+
95
+ ```tsx
96
+ // ❌ Wrong
97
+ className="text-white bg-black"
98
+
99
+ // ✅ Correct
100
+ className="text-slate-50 dark:text-slate-900 bg-slate-900 dark:bg-slate-950"
101
+ ```
102
+
103
+ ### "shadcn import detected"
104
+
105
+ **Error:** `from '@/components/ui/button'` or similar
106
+ **Fix:** Use library equivalent
107
+
108
+ ```tsx
109
+ // ❌ Wrong
110
+ import { Button } from '@/components/ui/button';
111
+
112
+ // ✅ Correct
113
+ import { UIButton } from '@/components/library';
114
+ ```
115
+
116
+ ### "Missing useDataSource"
117
+
118
+ **Error:** Hardcoded data array without mode switching
119
+ **Fix:** Use `useDataSource` pattern
120
+
121
+ ```tsx
122
+ // ❌ Wrong
123
+ const travelers = SAMPLE_TRAVELERS; // always sample
124
+
125
+ // ✅ Correct
126
+ const travelers = useDataSource({ sample: SAMPLE_TRAVELERS, live: liveTravelers });
127
+ ```
128
+
129
+ ### "Inline style detected"
130
+
131
+ **Error:** `style={{}}` or `<style>` tag found
132
+ **Fix:** Use Tailwind classes or global.css
133
+
134
+ ```tsx
135
+ // ❌ Wrong
136
+ <div style={{ padding: '20px', backgroundColor: '#fff' }}>
137
+
138
+ // ✅ Correct
139
+ <div className="p-5 bg-white dark:bg-slate-900">
140
+ ```
141
+
142
+ ### "Grid exceeds 4 columns"
143
+
144
+ **Error:** `grid-cols-5` or higher detected
145
+ **Fix:** Max 4 KPIs per row — split across multiple rows
146
+
147
+ ```tsx
148
+ // ❌ Wrong
149
+ <div className="grid grid-cols-5 gap-3"> // 5 KPIs
150
+
151
+ // ✅ Correct
152
+ <div className="grid grid-cols-4 gap-3"> // 4 KPIs
153
+ <div className="grid grid-cols-1 gap-3 sm:grid-cols-1"> // 1 KPI on second row
154
+ ```
155
+
156
+ ## Error Priority
157
+
158
+ If the validator reports multiple errors, fix them in this order:
159
+
160
+ 1. **File extension** (`.jsx` → `.tsx`) — affects everything
161
+ 2. **Imports** (shadcn/Lucide → library/Heroicons) — structural
162
+ 3. **Hand-rolled cards** (→ library components) — most common
163
+ 4. **Forbidden colors** (→ slate scale) — visual
164
+ 5. **Other errors** (useDataSource, inline styles, etc.)
165
+
166
+ ## Success Criteria
167
+
168
+ The validator is successful when it outputs:
169
+
170
+ ```
171
+ ✅ All checks passed
172
+ Dashboard is ready
173
+ ```
174
+
175
+ Any other output means there are errors that MUST be fixed.
176
+
177
+ ## Integration with Build Phases
178
+
179
+ For the Engine Travel Command Center (and all 4-phase dashboards):
180
+
181
+ - **End of Phase 1:** Run validator, fix errors → proceed to Phase 2
182
+ - **End of Phase 2:** Run validator, fix errors → proceed to Phase 3
183
+ - **End of Phase 3:** Run validator, fix errors → proceed to Phase 4
184
+ - **End of Phase 4:** Run validator, fix errors → DONE
185
+
186
+ ## Do Not Run These Instead
187
+
188
+ **Do NOT run:**
189
+ - `npm run build` — This runs TypeScript checking which has known issues with platform-only Salesforce packages. The validator is sufficient for dashboard validation.
190
+ - `tsc` or `tsc -b` — Same issue as above
191
+ - `npm run lint` — This is for ESLint, not dashboard validation
192
+
193
+ **Only run:**
194
+ - `npm run validate:dashboard` — The correct validator for dashboards
195
+ - `npm run dev` — To test the dashboard in the browser after validation passes
196
+
197
+ ## When to Ask for Help
198
+
199
+ If you see a validator error you don't understand:
200
+ 1. Read the error message carefully
201
+ 2. Check this file for the fix pattern
202
+ 3. Check `.a4drules/features/command-center-dashboard-rule.md` for the rule
203
+ 4. Check the component-library skill for the correct component API
204
+
205
+ If still stuck, ask: "The validator reports [error]. I tried [fix]. What's the correct approach?"
206
+
207
+ ## The Golden Rule
208
+
209
+ **Validator errors are not suggestions — they are requirements.**
210
+
211
+ Zero errors is not optional. Zero errors is the definition of "done".