@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,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".
|