@schandlergarcia/sf-web-components 1.9.87 → 2.0.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/.a4drules/features/command-center-dashboard-rule.md +1 -1
- package/.a4drules/skills/command-center-builder/SKILL.md +33 -36
- package/.a4drules/skills/command-center-builder/getting-started.md +64 -104
- package/.a4drules/skills/command-center-builder/improved-build-process.md +28 -34
- package/.a4drules/skills/command-center-guide/SKILL.md +9 -9
- package/.a4drules/skills/command-center-project/SKILL.md +4 -5
- package/.a4drules/skills/component-library/SKILL.md +8 -10
- package/.a4drules/skills/component-library/card-components.md +3 -3
- package/.a4drules/skills/component-library/chat-data.md +4 -6
- package/.a4drules/troubleshooting/codegen-overwrites-types.md +21 -162
- package/.a4drules/troubleshooting/graphql-introspection-failure.md +13 -264
- package/.a4drules/validation-requirements.md +3 -5
- package/.a4drules/webapp-data.md +1 -1
- package/CHANGELOG.md +30 -0
- package/CLAUDE.md +19 -39
- package/dist/components/library/cards/ActivityCard.js +9 -9
- package/dist/components/library/cards/ActivityCard.js.map +1 -1
- package/dist/styles/base.css +112 -27
- package/dist/styles/global.css +15 -30
- package/package.json +2 -3
- package/scripts/postinstall.mjs +39 -178
- package/scripts/reset-command-center.sh +67 -406
- package/scripts/validate-dashboard.sh +4 -4
- package/src/components/library/cards/ActivityCard.jsx +2 -2
- package/src/styles/base.css +223 -0
- package/src/styles/global.css +223 -0
- package/src/templates/config/vite.config.ts.template +0 -18
- package/.a4drules/features/engine-dashboard-rule.md +0 -63
- package/.a4drules/features/phase2-data-pattern.md +0 -15
- package/assets/images/engine_logo.png +0 -0
- package/data/README.md +0 -202
- package/data/USAGE.md +0 -81
- package/data/agentApiConfig.ts +0 -36
- package/data/copy-to-webapp.sh +0 -61
- package/data/engine-command-center-prd.md +0 -575
- package/data/engine-live-data.js +0 -135
- package/data/engine-sample-data.js +0 -378
- package/data/schema.graphql +0 -292
- package/data/useEngineLiveData.ts +0 -49
- package/data/useEvaAgent.ts +0 -288
- package/scripts/generate-schema-from-sample.mjs +0 -370
|
@@ -13,274 +13,23 @@ This is an **org-level bug** where at least one field in the Salesforce org has
|
|
|
13
13
|
|
|
14
14
|
## What's Broken
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
- ❌ Full schema with all org objects and fields
|
|
18
|
-
- ❌ Some IDE autocomplete features that query the org directly
|
|
16
|
+
The GraphQL server attempts to enumerate every object and field in the org. If ANY field across any object has a null `getDataType()`, the entire introspection fails. This prevents `codegen` from generating types.
|
|
19
17
|
|
|
20
|
-
##
|
|
18
|
+
## Workaround
|
|
21
19
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
20
|
+
1. **Use a pre-built `schema.graphql`** — If you have one checked into the repo, keep using it
|
|
21
|
+
2. **Query specific objects** — Instead of full introspection, query only the objects you need:
|
|
22
|
+
```graphql
|
|
23
|
+
{ uiapi { query { Account { edges { node { Id Name { value } } } } } } }
|
|
24
|
+
```
|
|
25
|
+
3. **Run `get-graphql-schema.mjs`** — This script uses the Salesforce SDK to attempt introspection. If it fails, use the pre-built schema.
|
|
27
26
|
|
|
28
|
-
|
|
27
|
+
## Key Point
|
|
29
28
|
|
|
30
|
-
|
|
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.
|
|
29
|
+
The introspection failure does NOT affect runtime queries. You can still query any object via GraphQL — only the schema generation step is broken.
|
|
268
30
|
|
|
269
31
|
## Related Files
|
|
270
32
|
|
|
271
|
-
-
|
|
272
|
-
- `
|
|
273
|
-
- `
|
|
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.
|
|
33
|
+
- `scripts/get-graphql-schema.mjs` — The script that runs introspection
|
|
34
|
+
- `schema.graphql` — Pre-built schema (if available)
|
|
35
|
+
- `vite.config.ts` — Codegen plugin configuration (disabled by default to prevent issues)
|
|
@@ -176,12 +176,10 @@ Any other output means there are errors that MUST be fixed.
|
|
|
176
176
|
|
|
177
177
|
## Integration with Build Phases
|
|
178
178
|
|
|
179
|
-
|
|
179
|
+
Run the validator at the end of each build phase before proceeding to the next:
|
|
180
180
|
|
|
181
|
-
- **End of
|
|
182
|
-
- **
|
|
183
|
-
- **End of Phase 3:** Run validator, fix errors → proceed to Phase 4
|
|
184
|
-
- **End of Phase 4:** Run validator, fix errors → DONE
|
|
181
|
+
- **End of each phase:** Run validator, fix all errors → proceed to next phase
|
|
182
|
+
- **Final phase:** Run validator, fix all errors → DONE
|
|
185
183
|
|
|
186
184
|
## Do Not Run These Instead
|
|
187
185
|
|
package/.a4drules/webapp-data.md
CHANGED
|
@@ -64,7 +64,7 @@ const res = await sdk.fetch?.("/services/apexrest/my-resource");
|
|
|
64
64
|
|
|
65
65
|
The `schema.graphql` file is **already checked into the repo** at the SFDX project root. **Never open or parse it directly.**
|
|
66
66
|
|
|
67
|
-
- Pre-built from sample data, contains
|
|
67
|
+
- Pre-built from sample data, contains standard and custom objects
|
|
68
68
|
- No generation needed — just use it
|
|
69
69
|
- If you ever need to regenerate (after adding fields to sample data): `npm run graphql:schema:sample` from webapp dir
|
|
70
70
|
|
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,36 @@ 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
|
+
## [2.0.0] - 2026-04-04
|
|
9
|
+
|
|
10
|
+
### Changed — Generic framework release
|
|
11
|
+
Stripped all Engine Travel-specific content to make the package a generic component framework anyone can use.
|
|
12
|
+
|
|
13
|
+
- **Postinstall** — removed Engine sample data, Engine PRD, Engine logo, useEvaAgent, agentApiConfig, useEngineLiveData, schema.graphql, and generate-schema-from-sample copies. Now only installs the generic component library, templates, config files, utility scripts, and .a4drules.
|
|
14
|
+
- **Reset script** — removed Engine brand CSS inline write (now copies neutral `global.css` from package), removed Engine data/hook/schema/PRD/logo restores, removed Engine-specific `KEEP_CLASSES` (TCC_TravelMetricsController) from Apex cleanup.
|
|
15
|
+
- **global.css** — replaced Engine brand palette (`--color-engine-*`, teal #5BC8C8) with neutral sky-blue brand tokens (`--color-brand-*`, #0EA5E9). Replaced Engine coral (#FF5722) with standard red (#EF4444) in `.heroui-scope`.
|
|
16
|
+
- **vite.config.ts.template** — removed hardcoded Salesforce org proxy rules (`/sf-oauth`, `/sf-agent` with tdx26-keynote-org URL).
|
|
17
|
+
- **package.json** — removed `data` and `assets` from `files` array. Added `src/styles`.
|
|
18
|
+
- **.a4drules** — deleted `engine-dashboard-rule.md` and `phase2-data-pattern.md`. Generalized all remaining files: removed Engine PRD/data references from SKILL.md, getting-started.md, improved-build-process.md, chat-data.md, webapp-data.md, validation-requirements.md, and command-center-dashboard-rule.md.
|
|
19
|
+
- **CLAUDE.md** — rewrote to describe generic framework without Engine references.
|
|
20
|
+
- **ActivityCard.jsx** — renamed `action.traveler` fallback to `action.description`.
|
|
21
|
+
- **validate-dashboard.sh** — renamed `TRAVEL_DIR` to `CUSTOM_DIR`.
|
|
22
|
+
|
|
23
|
+
### Removed
|
|
24
|
+
- `scripts/generate-schema-from-sample.mjs` (Engine-specific schema generator)
|
|
25
|
+
- `.a4drules/features/engine-dashboard-rule.md`
|
|
26
|
+
- `.a4drules/features/phase2-data-pattern.md`
|
|
27
|
+
|
|
28
|
+
### Note
|
|
29
|
+
The `data/` directory still exists in the repository for reference but is no longer published or copied by postinstall.
|
|
30
|
+
|
|
31
|
+
## [1.9.88] - 2026-04-07
|
|
32
|
+
|
|
33
|
+
### Fixed
|
|
34
|
+
- **Postinstall `.a4drules` copy** — now copies all root-level `.md` files (`webapp-ui.md`, `webapp-data.md`, `validation-requirements.md`, `RULES.md`) and all subdirectories (`skills/`, `features/`, `troubleshooting/`), not just `skills/` and `features/`. Ensures the full build guide reaches consuming projects.
|
|
35
|
+
- **Postinstall vite proxy injection** — when `vite.config.ts` already exists, the postinstall now checks for and injects the Agent API proxy rules (`/sf-oauth`, `/sf-agent`) if they're missing, rather than silently skipping.
|
|
36
|
+
- **Reset script `.a4drules` restore** — the reset script now force-copies every `.md` file from the package's `.a4drules` to the project root, preventing stale stubs from persisting across resets.
|
|
37
|
+
|
|
8
38
|
## [1.9.87] - 2026-04-07
|
|
9
39
|
|
|
10
40
|
### Changed
|
package/CLAUDE.md
CHANGED
|
@@ -10,8 +10,7 @@ This file provides guidance to Claude Code when working with the sf-web-componen
|
|
|
10
10
|
2. **Templates** - Page templates (Home, Search, NotFound, BlankDashboard) and config templates (routes.tsx, vite.config.ts, dataStrategy.ts)
|
|
11
11
|
3. **Automation** - Postinstall script that sets up consuming projects automatically
|
|
12
12
|
4. **Documentation** - `.a4drules` skills and features for AI-assisted development
|
|
13
|
-
5. **
|
|
14
|
-
6. **Scripts** - Reset scripts, validation scripts, schema generation
|
|
13
|
+
5. **Scripts** - Reset scripts, validation scripts
|
|
15
14
|
|
|
16
15
|
**Primary use case:** Building Salesforce Web Applications with React, specifically for:
|
|
17
16
|
- Outer app pages (search, navigation) using shadcn/ui + Tailwind
|
|
@@ -31,18 +30,18 @@ The package supports **two distinct UI contexts** that must NOT be mixed:
|
|
|
31
30
|
- `Input` from `@/components/ui/input`
|
|
32
31
|
- etc.
|
|
33
32
|
- **Icons:** Lucide React (`lucide-react`)
|
|
34
|
-
- **Styling:** Neutral shadcn theme (slate colors
|
|
33
|
+
- **Styling:** Neutral shadcn theme (slate colors)
|
|
35
34
|
- **File type:** `.tsx`
|
|
36
35
|
|
|
37
36
|
#### 2. Command Center Context
|
|
38
|
-
- **Files:** Dashboard pages in `src/pages/` (e.g., BlankDashboard,
|
|
37
|
+
- **Files:** Dashboard pages in `src/pages/` (e.g., BlankDashboard, custom dashboards)
|
|
39
38
|
- **Rendered in:** CommandCenter wrapper with `.heroui-scope` class
|
|
40
39
|
- **Components:** Library components from `@/components/library`
|
|
41
40
|
- `UIButton` from `@/components/library/ui/UIButton`
|
|
42
41
|
- `UIInput` from `@/components/library/ui/UIInput`
|
|
43
42
|
- `MetricCard`, `ChartCard`, `ListCard`, `TableCard`, etc.
|
|
44
43
|
- **Icons:** Heroicons (`@heroicons/react`)
|
|
45
|
-
- **Styling:**
|
|
44
|
+
- **Styling:** Brand colors defined in `global.css` inside `.heroui-scope`
|
|
46
45
|
- **File type:** `.jsx` or `.tsx`
|
|
47
46
|
|
|
48
47
|
**Common mistake:** Using UIButton/UIInput in outer app pages or shadcn Button/Input in command center dashboards. Check `.a4drules/features/pre-code-checklist.md` for validation rules.
|
|
@@ -67,18 +66,14 @@ sf-web-components/
|
|
|
67
66
|
│ │ ├── lib/ # dataStrategy.ts.template
|
|
68
67
|
│ │ └── workspace/ # CommandCenter.tsx.template
|
|
69
68
|
│ ├── lib/ # Utilities (utils.ts)
|
|
70
|
-
│ └── styles/ # global.css with
|
|
69
|
+
│ └── styles/ # global.css with neutral brand tokens
|
|
71
70
|
├── scripts/
|
|
72
71
|
│ ├── postinstall.mjs # Runs after npm install in consuming projects
|
|
73
72
|
│ ├── reset-command-center.sh
|
|
74
73
|
│ ├── validate-dashboard.sh
|
|
75
74
|
│ └── verify-consistency.sh
|
|
76
|
-
├── data/
|
|
77
|
-
│ ├── engine-sample-data.js
|
|
78
|
-
│ ├── schema.graphql
|
|
79
|
-
│ └── engine-command-center-prd.md
|
|
80
75
|
└── .a4drules/
|
|
81
|
-
├── features/ # Dashboard rules,
|
|
76
|
+
├── features/ # Dashboard rules, pre-code checklist
|
|
82
77
|
├── skills/ # component-library, command-center-builder, etc.
|
|
83
78
|
├── troubleshooting/ # Common issues and solutions
|
|
84
79
|
├── validation-requirements.md
|
|
@@ -103,9 +98,9 @@ sf-web-components/
|
|
|
103
98
|
```javascript
|
|
104
99
|
import useDataSource from "@/components/library/data/useDataSource";
|
|
105
100
|
|
|
106
|
-
const
|
|
107
|
-
sample:
|
|
108
|
-
live:
|
|
101
|
+
const items = useDataSource({
|
|
102
|
+
sample: SAMPLE_DATA,
|
|
103
|
+
live: liveData ?? []
|
|
109
104
|
});
|
|
110
105
|
```
|
|
111
106
|
- **Configuration:** `src/lib/dataStrategy.ts` controls `ENABLE_SAMPLE_DATA_CACHE`
|
|
@@ -149,12 +144,9 @@ When a project installs this package, `scripts/postinstall.mjs` automatically:
|
|
|
149
144
|
4. **Copies workspace files** to `src/components/workspace/`
|
|
150
145
|
5. **Installs page templates** to `src/pages/` (Home.tsx, Search.tsx, NotFound.tsx, BlankDashboard.tsx)
|
|
151
146
|
6. **Updates imports** in existing files (changes package imports to local `@/components/library` imports)
|
|
152
|
-
7. **
|
|
153
|
-
8. **
|
|
154
|
-
9. **Copies
|
|
155
|
-
10. **Installs config files** (routes.tsx, vite.config.ts, dataStrategy.ts) if they don't exist
|
|
156
|
-
11. **Adds npm scripts** to package.json (reset:command-center, validate:dashboard, graphql:schema:sample)
|
|
157
|
-
12. **Copies .a4drules** to project root for AI assistant discoverability
|
|
147
|
+
7. **Installs config files** (routes.tsx, vite.config.ts, dataStrategy.ts) if they don't exist
|
|
148
|
+
8. **Adds npm scripts** to package.json (reset:command-center, validate:dashboard)
|
|
149
|
+
9. **Copies .a4drules** to project root for AI assistant discoverability
|
|
158
150
|
|
|
159
151
|
**Important:** Templates are **always overwritten** to ensure latest versions, except for config files (vite.config.ts, dataStrategy.ts) which only install if missing.
|
|
160
152
|
|
|
@@ -170,8 +162,10 @@ The `scripts/reset-command-center.sh` provides a clean baseline for projects:
|
|
|
170
162
|
- Updates routes.tsx to include Home, /accounts, and /accounts/:recordId
|
|
171
163
|
- Updates appLayout.tsx with nav bar
|
|
172
164
|
- Clears Vite cache
|
|
173
|
-
- Restores
|
|
174
|
-
- Restores
|
|
165
|
+
- Restores neutral theme (global.css from package)
|
|
166
|
+
- Restores dataStrategy.ts
|
|
167
|
+
- Restores .a4drules
|
|
168
|
+
- Cleans Apex classes and platform events (if SFDX project)
|
|
175
169
|
|
|
176
170
|
## Important Files
|
|
177
171
|
|
|
@@ -184,22 +178,14 @@ Complete history of all changes. ALWAYS update when making changes. Include:
|
|
|
184
178
|
|
|
185
179
|
### .a4drules/
|
|
186
180
|
AI assistant documentation. Key files:
|
|
187
|
-
- `features/engine-dashboard-rule.md` - How to build Engine Travel Command Center
|
|
188
181
|
- `features/command-center-dashboard-rule.md` - Dashboard development rules
|
|
189
182
|
- `features/pre-code-checklist.md` - Pre-implementation validation
|
|
190
183
|
- `skills/component-library/` - Component API reference
|
|
184
|
+
- `skills/command-center-builder/` - How to build Command Center dashboards
|
|
191
185
|
- `validation-requirements.md` - Validation rules
|
|
192
186
|
- `webapp-data.md` - Data access patterns (GraphQL, REST, SDK usage)
|
|
193
187
|
- `webapp-ui.md` - UI platform rules
|
|
194
188
|
|
|
195
|
-
### data/engine-command-center-prd.md
|
|
196
|
-
Product Requirements Document for Engine Travel Command Center. Defines:
|
|
197
|
-
- Product overview and users
|
|
198
|
-
- Brand tokens (Engine teal #5BC8C8, etc.)
|
|
199
|
-
- Layout structure (visualization-hero pattern)
|
|
200
|
-
- Component sections and data model
|
|
201
|
-
- Phase-by-phase build instructions
|
|
202
|
-
|
|
203
189
|
### src/components/library/theme/AppThemeProvider.jsx
|
|
204
190
|
Theme provider with dark mode toggle. Exports:
|
|
205
191
|
- **Default:** `AppThemeProvider` component
|
|
@@ -235,7 +221,7 @@ Routes need `handle: { showInNavigation: true, showNavBar: true }` for nav bar t
|
|
|
235
221
|
## Testing
|
|
236
222
|
|
|
237
223
|
Before publishing:
|
|
238
|
-
1. Test in a real project
|
|
224
|
+
1. Test in a real project
|
|
239
225
|
2. Run `npm install @schandlergarcia/sf-web-components@latest`
|
|
240
226
|
3. Verify postinstall behavior
|
|
241
227
|
4. Check templates are correct
|
|
@@ -245,13 +231,7 @@ Before publishing:
|
|
|
245
231
|
|
|
246
232
|
## Version History
|
|
247
233
|
|
|
248
|
-
See CHANGELOG.md for complete history.
|
|
249
|
-
- v1.9.55 - Fixed UIButton/Button confusion in templates
|
|
250
|
-
- v1.9.56 - Fixed reset script 404 errors
|
|
251
|
-
- v1.9.57 - Restored "Browse All Accounts" button
|
|
252
|
-
- v1.9.58 - Fixed reset routes pointing to wrong Home file
|
|
253
|
-
- v1.9.59 - Restored navigation bar (showNavBar flag)
|
|
254
|
-
- v1.9.60-61 - Synced .a4drules and PRD from reactapp4
|
|
234
|
+
See CHANGELOG.md for complete history.
|
|
255
235
|
|
|
256
236
|
## Current Version
|
|
257
237
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsxs as
|
|
1
|
+
import { jsxs as r, jsx as t } from "react/jsx-runtime";
|
|
2
2
|
import "react";
|
|
3
3
|
import { AnimatePresence as n, motion as o } from "framer-motion";
|
|
4
4
|
import { ExclamationCircleIcon as c, CheckCircleIcon as d, ClockIcon as m, ArrowPathIcon as p } from "@heroicons/react/24/outline";
|
|
@@ -10,7 +10,7 @@ const l = {
|
|
|
10
10
|
error: { Icon: c, color: "text-red-500", spin: !1 }
|
|
11
11
|
};
|
|
12
12
|
function u({ action: e }) {
|
|
13
|
-
const
|
|
13
|
+
const i = l[e.status] ?? l.pending;
|
|
14
14
|
return /* @__PURE__ */ t(
|
|
15
15
|
o.div,
|
|
16
16
|
{
|
|
@@ -18,20 +18,20 @@ function u({ action: e }) {
|
|
|
18
18
|
animate: { y: 0, opacity: 1 },
|
|
19
19
|
exit: { y: -12, opacity: 0 },
|
|
20
20
|
className: "rounded-lg border border-slate-100 bg-slate-50 p-3 dark:border-slate-800 dark:bg-slate-950/40",
|
|
21
|
-
children: /* @__PURE__ */
|
|
22
|
-
/* @__PURE__ */ t(
|
|
23
|
-
/* @__PURE__ */
|
|
21
|
+
children: /* @__PURE__ */ r("div", { className: "flex items-start gap-2", children: [
|
|
22
|
+
/* @__PURE__ */ t(i.Icon, { className: `mt-0.5 h-4 w-4 shrink-0 ${i.color} ${i.spin ? "animate-spin" : ""}` }),
|
|
23
|
+
/* @__PURE__ */ r("div", { className: "min-w-0", children: [
|
|
24
24
|
/* @__PURE__ */ t("div", { className: "text-xs font-medium text-slate-700 dark:text-slate-200", children: e.title ?? e.action }),
|
|
25
|
-
(e.subtitle ?? e.
|
|
25
|
+
(e.subtitle ?? e.description ?? e.timestamp ?? e.startedAt) && /* @__PURE__ */ t("div", { className: "mt-0.5 text-[10px] text-slate-400", children: [e.subtitle, e.description, e.timestamp ?? e.startedAt].filter(Boolean).join(" · ") })
|
|
26
26
|
] })
|
|
27
27
|
] })
|
|
28
28
|
}
|
|
29
29
|
);
|
|
30
30
|
}
|
|
31
|
-
function b({ title: e = "Activity", actions:
|
|
32
|
-
return
|
|
31
|
+
function b({ title: e = "Activity", actions: i = [], className: a = "" }) {
|
|
32
|
+
return i.length === 0 ? null : /* @__PURE__ */ r("div", { className: a, children: [
|
|
33
33
|
e && /* @__PURE__ */ t(x, { as: "div", size: "xs", weight: "semibold", muted: !0, className: "mb-2 uppercase tracking-wider", children: e }),
|
|
34
|
-
/* @__PURE__ */ t("div", { className: "space-y-2", children: /* @__PURE__ */ t(n, { children:
|
|
34
|
+
/* @__PURE__ */ t("div", { className: "space-y-2", children: /* @__PURE__ */ t(n, { children: i.map((s) => /* @__PURE__ */ t(u, { action: s }, s.id)) }) })
|
|
35
35
|
] });
|
|
36
36
|
}
|
|
37
37
|
export {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ActivityCard.js","sources":["../../../../src/components/library/cards/ActivityCard.jsx"],"sourcesContent":["import React from \"react\";\nimport { motion, AnimatePresence } from \"framer-motion\";\nimport { ArrowPathIcon, CheckCircleIcon, ExclamationCircleIcon, ClockIcon } from \"@heroicons/react/24/outline\";\nimport UIText from \"../ui/Text\";\n\nconst STATUS_ICON = {\n working: { Icon: ArrowPathIcon, color: \"text-indigo-500\", spin: true },\n pending: { Icon: ClockIcon, color: \"text-slate-400\", spin: false },\n complete: { Icon: CheckCircleIcon, color: \"text-emerald-500\", spin: false },\n error: { Icon: ExclamationCircleIcon, color: \"text-red-500\", spin: false },\n};\n\nfunction ActionItem({ action }) {\n const s = STATUS_ICON[action.status] ?? STATUS_ICON.pending;\n return (\n <motion.div\n initial={{ y: 12, opacity: 0 }}\n animate={{ y: 0, opacity: 1 }}\n exit={{ y: -12, opacity: 0 }}\n className=\"rounded-lg border border-slate-100 bg-slate-50 p-3 dark:border-slate-800 dark:bg-slate-950/40\"\n >\n <div className=\"flex items-start gap-2\">\n <s.Icon className={`mt-0.5 h-4 w-4 shrink-0 ${s.color} ${s.spin ? \"animate-spin\" : \"\"}`} />\n <div className=\"min-w-0\">\n <div className=\"text-xs font-medium text-slate-700 dark:text-slate-200\">{action.title ?? action.action}</div>\n {(action.subtitle ?? action.
|
|
1
|
+
{"version":3,"file":"ActivityCard.js","sources":["../../../../src/components/library/cards/ActivityCard.jsx"],"sourcesContent":["import React from \"react\";\nimport { motion, AnimatePresence } from \"framer-motion\";\nimport { ArrowPathIcon, CheckCircleIcon, ExclamationCircleIcon, ClockIcon } from \"@heroicons/react/24/outline\";\nimport UIText from \"../ui/Text\";\n\nconst STATUS_ICON = {\n working: { Icon: ArrowPathIcon, color: \"text-indigo-500\", spin: true },\n pending: { Icon: ClockIcon, color: \"text-slate-400\", spin: false },\n complete: { Icon: CheckCircleIcon, color: \"text-emerald-500\", spin: false },\n error: { Icon: ExclamationCircleIcon, color: \"text-red-500\", spin: false },\n};\n\nfunction ActionItem({ action }) {\n const s = STATUS_ICON[action.status] ?? STATUS_ICON.pending;\n return (\n <motion.div\n initial={{ y: 12, opacity: 0 }}\n animate={{ y: 0, opacity: 1 }}\n exit={{ y: -12, opacity: 0 }}\n className=\"rounded-lg border border-slate-100 bg-slate-50 p-3 dark:border-slate-800 dark:bg-slate-950/40\"\n >\n <div className=\"flex items-start gap-2\">\n <s.Icon className={`mt-0.5 h-4 w-4 shrink-0 ${s.color} ${s.spin ? \"animate-spin\" : \"\"}`} />\n <div className=\"min-w-0\">\n <div className=\"text-xs font-medium text-slate-700 dark:text-slate-200\">{action.title ?? action.action}</div>\n {(action.subtitle ?? action.description ?? action.timestamp ?? action.startedAt) && (\n <div className=\"mt-0.5 text-[10px] text-slate-400\">\n {[action.subtitle, action.description, action.timestamp ?? action.startedAt].filter(Boolean).join(\" · \")}\n </div>\n )}\n </div>\n </div>\n </motion.div>\n );\n}\n\nexport default function ActivityCard({ title = \"Activity\", actions = [], className = \"\" }) {\n if (actions.length === 0) return null;\n\n return (\n <div className={className}>\n {title && (\n <UIText as=\"div\" size=\"xs\" weight=\"semibold\" muted className=\"mb-2 uppercase tracking-wider\">\n {title}\n </UIText>\n )}\n <div className=\"space-y-2\">\n <AnimatePresence>\n {actions.map(a => (\n <ActionItem key={a.id} action={a} />\n ))}\n </AnimatePresence>\n </div>\n </div>\n );\n}\n"],"names":["STATUS_ICON","ArrowPathIcon","ClockIcon","CheckCircleIcon","ExclamationCircleIcon","ActionItem","action","s","jsx","motion","jsxs","ActivityCard","title","actions","className","UIText","AnimatePresence","a"],"mappings":";;;;;AAKA,MAAMA,IAAc;AAAA,EAClB,SAAU,EAAE,MAAMC,GAAsB,OAAO,mBAAoB,MAAM,GAAA;AAAA,EACzE,SAAU,EAAE,MAAMC,GAAsB,OAAO,kBAAoB,MAAM,GAAA;AAAA,EACzE,UAAU,EAAE,MAAMC,GAAsB,OAAO,oBAAoB,MAAM,GAAA;AAAA,EACzE,OAAU,EAAE,MAAMC,GAAuB,OAAO,gBAAoB,MAAM,GAAA;AAC5E;AAEA,SAASC,EAAW,EAAE,QAAAC,KAAU;AAC9B,QAAMC,IAAIP,EAAYM,EAAO,MAAM,KAAKN,EAAY;AACpD,SACE,gBAAAQ;AAAA,IAACC,EAAO;AAAA,IAAP;AAAA,MACC,SAAS,EAAE,GAAG,IAAI,SAAS,EAAA;AAAA,MAC3B,SAAS,EAAE,GAAG,GAAG,SAAS,EAAA;AAAA,MAC1B,MAAM,EAAE,GAAG,KAAK,SAAS,EAAA;AAAA,MACzB,WAAU;AAAA,MAEV,UAAA,gBAAAC,EAAC,OAAA,EAAI,WAAU,0BACb,UAAA;AAAA,QAAA,gBAAAF,EAACD,EAAE,MAAF,EAAO,WAAW,2BAA2BA,EAAE,KAAK,IAAIA,EAAE,OAAO,iBAAiB,EAAE,GAAA,CAAI;AAAA,QACzF,gBAAAG,EAAC,OAAA,EAAI,WAAU,WACb,UAAA;AAAA,UAAA,gBAAAF,EAAC,SAAI,WAAU,0DAA0D,UAAAF,EAAO,SAASA,EAAO,QAAO;AAAA,WACrGA,EAAO,YAAYA,EAAO,eAAeA,EAAO,aAAaA,EAAO,cACpE,gBAAAE,EAAC,OAAA,EAAI,WAAU,qCACZ,UAAA,CAACF,EAAO,UAAUA,EAAO,aAAaA,EAAO,aAAaA,EAAO,SAAS,EAAE,OAAO,OAAO,EAAE,KAAK,KAAK,EAAA,CACzG;AAAA,QAAA,EAAA,CAEJ;AAAA,MAAA,EAAA,CACF;AAAA,IAAA;AAAA,EAAA;AAGN;AAEA,SAAwBK,EAAa,EAAE,OAAAC,IAAQ,YAAY,SAAAC,IAAU,IAAI,WAAAC,IAAY,MAAM;AACzF,SAAID,EAAQ,WAAW,IAAU,OAG/B,gBAAAH,EAAC,SAAI,WAAAI,GACF,UAAA;AAAA,IAAAF,KACC,gBAAAJ,EAACO,GAAA,EAAO,IAAG,OAAM,MAAK,MAAK,QAAO,YAAW,OAAK,IAAC,WAAU,iCAC1D,UAAAH,GACH;AAAA,sBAED,OAAA,EAAI,WAAU,aACb,UAAA,gBAAAJ,EAACQ,KACE,UAAAH,EAAQ,IAAI,CAAAI,MACX,gBAAAT,EAACH,KAAsB,QAAQY,EAAA,GAAdA,EAAE,EAAe,CACnC,GACH,EAAA,CACF;AAAA,EAAA,GACF;AAEJ;"}
|