@schandlergarcia/sf-web-components 1.9.72 → 1.9.74

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.
@@ -104,7 +104,7 @@ No `<nav>`, header bar, tab bar, or content-swapping via `useState("activeTab")`
104
104
 
105
105
  - Dashboard page files: `src/pages/` (e.g. `EngineDashboard.tsx`, `FleetDashboard.tsx`) — **NOT** `src/components/pages/`
106
106
  - File format: `.tsx` (MUST be TypeScript) — this is a TypeScript project, all React components use `.tsx`, NEVER `.jsx`.
107
- - `CommandCenter.tsx` wraps with `heroui-scope` div + `AppThemeProvider` + `DataModeProvider` + `Toast.Provider` from `@heroui/react`. **Never recreate these providers in dashboard pages.** When wiring a new dashboard, only change the import — preserve the `heroui-scope` wrapper and `Toast.Provider` exactly as-is. For toasts inside dashboards, use `toast` from `sonner` (not `addToast` from HeroUI).
107
+ - `CommandCenter.tsx` wraps with `heroui-scope` div + `AppThemeProvider` + `DataModeProvider` + `Toast.Provider` from `@heroui/react`. **Never recreate these providers in dashboard pages.** When wiring a new dashboard, only change the import — preserve the `heroui-scope` wrapper and `Toast.Provider` exactly as-is. For toasts inside dashboards, import `toast` from `@heroui/react` (or `@/components/library`) — NOT from `sonner`.
108
108
 
109
109
  ### Wiring a new dashboard (REQUIRED STEPS)
110
110
 
@@ -321,6 +321,7 @@ Three layers: `filterUtils.js` (pure) → `usePageFilters` hook (state) → `Fil
321
321
  - Same layout for both modes — only data changes.
322
322
  - Sample data: 10–50 items, realistic, deterministic (no `Math.random()`).
323
323
  - Define sample data as constants outside the component body.
324
+ - **Live data simulation:** When GraphQL is unavailable, use a separate live data file (`engine-live-data.js`) with a visibly different dataset. Create a hook that simulates async loading via `setTimeout`, then calls `setMode("live")` to flip all `useDataSource` calls at once. This makes the data transition visible — components refresh with new values after a loading period.
324
325
 
325
326
  ```jsx
326
327
  // ✅ Correct: data defined outside, selected by mode
@@ -490,7 +491,7 @@ Before finishing a dashboard page, verify ALL of these:
490
491
 
491
492
  ## Post-Completion Verification
492
493
 
493
- **DO NOT run `npm run validate:dashboard`, `npm run dev`, `npm run build`, or `tsc` during builds.** These waste time and are not required for phase completion.
494
+ **DO NOT run `npm run validate:dashboard`, `npm run dev`, `npm run build`, or `tsc` during builds.** These waste time and are not required for completion.
494
495
 
495
496
  Instead, verify correctness by reviewing the code against the Pre-Completion Checklist above. Confirm that all three wiring files are updated (CommandCenter.tsx, Home.tsx, routes.tsx).
496
497
 
@@ -504,8 +505,42 @@ Instead, verify correctness by reviewing the code against the Pre-Completion Che
504
505
  - Passing D3Chart as children to ChartCard — use `chart={<D3Chart ... />}` prop
505
506
  - Adding `<nav>` or tab-swapping (`useState("activeTab")`) inside dashboards
506
507
  - Recreating providers — `CommandCenter.tsx` already provides them. Do not replace `Toast.Provider` with `Toaster` from sonner, and do not remove the `heroui-scope` div
507
- - Using `addToast` from `@heroui/react` — use `toast` / `toast.success` from `sonner` instead
508
+ - Importing `toast` from `sonner` — use `toast` / `toast.success` from `@heroui/react` (or `@/components/library`) instead. The `Toast.Provider` is HeroUI's, so only HeroUI's `toast` renders through it
508
509
  - Using `text-black`, `text-white`, `bg-black` — use slate scale
509
510
  - Hardcoded data without `useDataSource` — always support sample/live switching
510
511
  - `position: fixed` without `createPortal`
511
512
  - `Math.random()` in sample data
513
+
514
+ ## Salesforce Metadata Patterns
515
+
516
+ This is an SFDX project (`force-app/main/default/`). When the build requires metadata changes — permission sets, custom fields, flows, record types — write standard SFDX XML files and deploy with the Salesforce CLI.
517
+
518
+ ### File locations
519
+
520
+ | Metadata type | Path pattern |
521
+ |---|---|
522
+ | Permission Set | `force-app/main/default/permissionsets/{Name}.permissionset-meta.xml` |
523
+ | Custom Field | `force-app/main/default/objects/{Object}/fields/{Field}.field-meta.xml` |
524
+ | Flow | `force-app/main/default/flows/{Name}.flow-meta.xml` |
525
+ | Record Type | `force-app/main/default/objects/{Object}/recordTypes/{Name}.recordType-meta.xml` |
526
+ | Email Template | `force-app/main/default/email/{Folder}/{Name}.email-meta.xml` |
527
+
528
+ ### Deploy command
529
+
530
+ ```bash
531
+ sf project deploy start --source-dir force-app/main/default/permissionsets/MyPermSet.permissionset-meta.xml
532
+ ```
533
+
534
+ Use `--source-dir` to deploy individual files. For multiple files, point to the parent directory.
535
+
536
+ ### When to create metadata
537
+
538
+ During the build, proactively check whether the Salesforce org has the metadata needed for the features you're building. If something is missing — a field isn't accessible, a record type doesn't exist, a flow would automate a manual process — tell the user what's missing and ask before creating it. The PRD's build prompts include specific scripted moments for these interactions.
539
+
540
+ ### Key rules
541
+
542
+ - Always use API version `62.0` or higher
543
+ - Permission set field permissions: set `readable` to `true`, `editable` to `false` for read-only dashboard fields
544
+ - Flows: set `status` to `Draft` — the user will activate after review
545
+ - Never deploy destructive changes without explicit confirmation
546
+ - Never modify standard Salesforce objects' core fields — only add custom fields
package/CHANGELOG.md CHANGED
@@ -5,6 +5,29 @@ 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.74] - 2026-04-02
9
+
10
+ ### Added
11
+ - **Engine logo asset** - `assets/images/engine_logo.png` restored from internal-app-standard-config
12
+ - Postinstall script copies to `src/assets/images/engine_logo.png`
13
+ - Reset script restores to baseline
14
+
15
+ ### Updated
16
+ - **PRD (engine-command-center-prd.md)** - Latest rewritten version from react-cursor-1
17
+ - **Live data files**:
18
+ - `data/engine-live-data.js` - Updated with Midwest storm dataset
19
+ - `data/useEngineLiveData.ts` - Updated loading hook
20
+ - **Global styles (src/styles/global.css)** - Added ChatBar overlay padding rule
21
+ - **Builder skill (SKILL.md)** - Updated with metadata patterns
22
+ - **package.json** - Added "assets" to files array
23
+
24
+ **Context:** Restored Engine logo asset that was previously missing. All Engine branding assets now included in package.
25
+
26
+ ## [1.9.73] - 2026-04-02
27
+
28
+ ### Updated
29
+ - **PRD, live data, global styles, and SKILL.md** - Latest updates from react-cursor-1
30
+
8
31
  ## [1.9.72] - 2026-04-02
9
32
 
10
33
  ### Added
Binary file
@@ -8,7 +8,7 @@ Product Requirements Document
8
8
 
9
9
  The Engine Travel Command Center is a real-time operations dashboard for corporate travel managers who use Engine (engine.com). It gives a single-screen view of every active traveler, booking, flight, policy flag, and agent interaction — built on Salesforce Data Cloud.
10
10
 
11
- **Build approach:** Incremental — PRD → scaffold → data → agent → intelligence.
11
+ **Build approach:** Incremental — PRD → dashboard → data → agent.
12
12
 
13
13
  ---
14
14
 
@@ -53,6 +53,7 @@ Use `brand-*` Tailwind classes for brand elements. Tailwind built-in colors (gre
53
53
  - **Wiring:** `CommandCenter.tsx` imports and renders `EngineDashboard` inside the existing provider stack. Never recreate providers in the dashboard. When updating `CommandCenter.tsx`, only change the dashboard import line — preserve the `heroui-scope` wrapper div and `Toast.Provider` from `@heroui/react` exactly as-is.
54
54
  - **Toasts:** Import `toast` from `@heroui/react` in dashboard pages. Use `toast.success("message")` for success toasts and `toast("message")` for informational ones. The `Toast.Provider` in CommandCenter handles rendering — do not add a second `Toaster` or replace the existing provider.
55
55
  - **Imports:** Components from `@/components/library`. Theme from `@/components/library/theme/AppThemeProvider`.
56
+ - **Salesforce metadata:** This is an SFDX project. Metadata lives in `force-app/main/default/`. When creating permission sets, custom fields, flows, or other metadata, write standard SFDX XML files and deploy with `sf project deploy start`.
56
57
 
57
58
  ---
58
59
 
@@ -98,20 +99,6 @@ Visualization-hero layout. Full-page scroll (no `h-screen`).
98
99
  - **Row 1:** `grid grid-cols-1 items-start gap-4 lg:grid-cols-3` — Escalations `lg:col-span-2`, Disruptions auto
99
100
  - **Row 2:** `grid grid-cols-1 items-start gap-4 lg:grid-cols-3` — Travelers `lg:col-span-2`, Spend auto
100
101
 
101
- ### Phase 1 placeholders
102
-
103
- Phase 1 builds the header, map, glass overlays, and flight strip as real components. Only the ChatBar and 4 data panels below the map are placeholders:
104
-
105
- ```tsx
106
- <BaseCard>
107
- <div className="h-48 flex items-center justify-center text-slate-400">
108
- Escalations Panel
109
- </div>
110
- </BaseCard>
111
- ```
112
-
113
- The GeoMap renders the base world map with no data props. Glass overlays and flight strip use hardcoded values.
114
-
115
102
  ---
116
103
 
117
104
  ## 6. Header
@@ -120,7 +107,7 @@ Compact `h-12` sticky header (`bg-slate-900 dark:bg-slate-950`). No ChatBar in t
120
107
 
121
108
  | Left | Right |
122
109
  |------|-------|
123
- | Engine logo (`GlobeAltIcon` in `bg-brand-600` square) + "ENGINE" wordmark + separator + "Travel Command Center" + "Powered by Agentforce" badge (Phase 4: `bg-brand-900/40 text-brand-400 rounded-full text-[10px]`) | Notification bell (badge count) + theme toggle (sun/moon) |
110
+ | Engine logo (`GlobeAltIcon` in `bg-brand-600` square) + "ENGINE" wordmark + separator + "Travel Command Center" + "Powered by Agentforce" badge (`bg-brand-900/40 text-brand-400 rounded-full text-[10px]`) | Notification bell (badge count) + theme toggle (sun/moon) |
124
111
 
125
112
  ---
126
113
 
@@ -133,9 +120,9 @@ Compact `h-12` sticky header (`bg-slate-900 dark:bg-slate-950`). No ChatBar in t
133
120
  width={960}
134
121
  height={520}
135
122
  theme={mode === "dark" ? "dark" : "light"}
136
- markers={useDataSource({ sample: MAP_MARKERS, live: [] })}
137
- arcs={useDataSource({ sample: MAP_ARCS, live: [] })}
138
- overlays={useDataSource({ sample: MAP_OVERLAYS, live: [] })}
123
+ markers={useDataSource({ sample: MAP_MARKERS, live: live.mapMarkers })}
124
+ arcs={useDataSource({ sample: MAP_ARCS, live: live.mapArcs })}
125
+ overlays={useDataSource({ sample: MAP_OVERLAYS, live: live.mapOverlays })}
139
126
  zoomable
140
127
  className="h-full w-full"
141
128
  />
@@ -144,8 +131,6 @@ Compact `h-12` sticky header (`bg-slate-900 dark:bg-slate-950`). No ChatBar in t
144
131
  - **Markers:** One per city. `{ id, lon, lat, label, active: true }`
145
132
  - **Arcs:** One per flight route. `{ id, from: [lon, lat], to: [lon, lat], progress: 0–1, danger: boolean }`. `danger: true` on disrupted routes.
146
133
  - **Overlays:** Weather zones. `{ id, center: [lon, lat], radius: number }`
147
- - **Phase 1:** Render GeoMap with no data props (map renders base world automatically)
148
- - **Phase 2:** Add `MAP_MARKERS`, `MAP_ARCS`, `MAP_OVERLAYS` from `@/data/engine-sample-data`
149
134
 
150
135
  **Glass KPI overlays** — 4 pills, `absolute left-3 top-3`:
151
136
  Active Travelers (count), Spend MTD (`fmtK()`), Compliance %, Eva Resolved (count + "resolved").
@@ -170,7 +155,7 @@ Custom inline panel in BaseCard. Do NOT use ActivityCard — this panel has per-
170
155
  Each escalation row:
171
156
  - Status icon: `CheckCircleIcon` (complete, green), `ArrowPathIcon` (working, brand), `ClockIcon` (pending, amber)
172
157
  - Title + subtitle + timestamp
173
- - **"Assign to Agent" button** (Phase 4) on items where status is NOT `"complete"`: teal button (`bg-brand-600`) with `SparklesIcon` + "Assign to Agent" text. On click: `toast.success("Assigned to Eva")` via `@heroui/react`. In Phase 2, render the button but leave the `onClick` empty — wire the toast in Phase 4.
158
+ - **"Assign to Agent" button** on items where status is NOT `"complete"`: teal button (`bg-brand-600`) with `SparklesIcon` + "Assign to Agent" text. On click: `toast.success("Assigned to Eva")` via `@heroui/react`.
174
159
 
175
160
  ```tsx
176
161
  <BaseCard>
@@ -191,7 +176,7 @@ Each escalation row:
191
176
  </div>
192
177
  </div>
193
178
  {isOpen && (
194
- <button onClick={() => handleAssignToAgent(e)}
179
+ <button onClick={() => toast.success("Assigned to Eva")}
195
180
  className="shrink-0 rounded-md bg-brand-600 px-2.5 py-1 text-xs font-medium text-slate-50 hover:bg-brand-700">
196
181
  <span className="flex items-center gap-1">
197
182
  <SparklesIcon className="h-3 w-3" /> Assign to Agent
@@ -234,7 +219,7 @@ Data: `TRAVELER_CARDS` from sample data.
234
219
  ```tsx
235
220
  const spendChartData = useDataSource({
236
221
  sample: MONTHLY_SPEND.map(m => ({ x: m.month, spend: m.amount })),
237
- live: [],
222
+ live: live.monthlySpend.map(m => ({ x: m.month, spend: m.amount })),
238
223
  });
239
224
 
240
225
  <ChartCard
@@ -276,18 +261,15 @@ Eva renders as a full-width `ChatBar` below the hero map, between the map and th
276
261
  </div>
277
262
  ```
278
263
 
279
- Suggestions: "Who's traveling internationally right now?", "Show bookings pending approval", "What has Eva resolved today?", "Travelers returning this week"
264
+ Suggestions: "Storm warning in the Midwest — which travelers are affected?", "What's our severe weather rebooking policy?", "Notify Anna and Sofia about the Chicago delays", "Create a support case for Anna Johansson's flight"
280
265
 
281
266
  Define `handleChat` and `CHAT_SUGGESTIONS` at module scope.
282
267
 
283
- **Phase 1:** Placeholder — do NOT add ChatBar. Just leave the space empty.
284
- **Phase 4:** Add the ChatBar between the map and data panels.
285
-
286
268
  ---
287
269
 
288
- ## 10. Salesforce Signals (Phase 4)
270
+ ## 10. Salesforce Signals
289
271
 
290
- Subtle indicators, not banners.
272
+ Subtle indicators throughout the dashboard — not banners.
291
273
 
292
274
  | Signal | Location |
293
275
  |--------|----------|
@@ -317,27 +299,206 @@ Every element supports light and dark. GeoMap: `theme={mode === "dark" ? "dark"
317
299
 
318
300
  ## 13. Build Prompts
319
301
 
320
- Build in 4 incremental phases.
302
+ Build incrementally in 3 prompts. Each prompt builds on the previous result.
321
303
 
322
- ### Phase 1: Layout & Structure
304
+ ---
323
305
 
324
- > Scaffold the Engine Travel Command Center skeleton. Build the visualization-hero layout from section 5 with the header, hero map, glass KPI overlays, flight status strip, and placeholder cards for the data panels.
306
+ ### Build the Dashboard
325
307
 
326
- ### Phase 2: Components & Sample Data
308
+ > Build the Engine Travel Command Center dashboard. Read the PRD for the full spec. Create the visualization-hero layout with the sticky header, hero GeoMap, glass KPI overlays, flight status strip, and all 4 data panels — Escalations, Disruptions, Active Travelers, and Monthly Spend Trend. Wire up the sample data from `src/data/engine-sample-data.js` so we can see real travelers, flights, and map markers. Include the "Assign to Agent" buttons on open escalations, the "View in Salesforce" links in traveler details, the "Powered by Agentforce" badge in the header, and the Salesforce signal toasts. Skip the Eva ChatBar for now — we'll add that after we connect to live data.
327
309
 
328
- > Replace the placeholders with real components and wire up the sample data. Build the 4 data panels from section 8 — Escalations, Disruptions, Active Travelers, and Monthly Spend Trend. Add map markers, arcs, and overlays from the sample data so we can see where travelers are.
310
+ ---
329
311
 
330
- ### Phase 3: Real Data Integration
312
+ ### Connect to Salesforce Data
331
313
 
332
- > Connect the dashboard to live Salesforce data using GraphQL. Read the sample data file to see which fields are needed, then build queries in `src/api/engine/queries.ts` and a hook in `src/hooks/useEngineLiveData.ts` for each object. The hook should return the same data shape as the sample data exports. Wire it into the dashboard by populating the `live` prop on each `useDataSource` call:
314
+ > Connect the dashboard to live Salesforce data. A second dataset already exists in `src/data/engine-live-data.js` with 12 travelers across global routes and different KPIs (distinct from the 8-traveler sample data). A hook already exists at `src/hooks/useEngineLiveData.ts` it imports the live data, simulates a 2-second loading delay, then calls `setMode("live")` to flip the global data mode.
315
+ >
316
+ > Import `useEngineLiveData` in the dashboard and populate the `live` prop on each `useDataSource` call:
333
317
  >
334
318
  > ```tsx
335
319
  > const live = useEngineLiveData();
336
320
  > const markers = useDataSource({ sample: MAP_MARKERS, live: live.mapMarkers });
321
+ > const arcs = useDataSource({ sample: MAP_ARCS, live: live.mapArcs });
322
+ > ```
323
+ >
324
+ > Also update the KPI overlays to re-derive from whichever metrics are active:
325
+ >
326
+ > ```tsx
327
+ > const activeMetrics = live.loading ? METRICS : live.metrics;
337
328
  > ```
338
329
  >
339
- > The UI should stay identical — only the data source changes.
330
+ > When the page loads, the audience will see: sample data renders immediately → after 2 seconds components refresh with different travelers, cities, and numbers → proving the data integration works. The layout stays identical — only the data changes.
331
+
332
+ **Scripted metadata interaction — Permission Set:**
333
+ After wiring the live data, check the field-level security for the objects being queried. The `External_Travel_Manager` permission set does not have read access to `Disruption__c.Severity__c`. Tell the user:
334
+
335
+ > "I notice the `External_Travel_Manager` permission set doesn't have read access to `Disruption__c.Severity__c`. This field is needed to display disruption severity in the dashboard. Would you like me to update the permission set?"
336
+
337
+ When the user confirms, create:
338
+
339
+ `force-app/main/default/permissionsets/External_Travel_Manager.permissionset-meta.xml`
340
+
341
+ ```xml
342
+ <?xml version="1.0" encoding="UTF-8"?>
343
+ <PermissionSet xmlns="http://soap.sforce.com/2006/04/metadata">
344
+ <label>External Travel Manager</label>
345
+ <description>Field access for the Engine Travel Command Center dashboard</description>
346
+ <fieldPermissions>
347
+ <editable>false</editable>
348
+ <field>Disruption__c.Severity__c</field>
349
+ <readable>true</readable>
350
+ </fieldPermissions>
351
+ <fieldPermissions>
352
+ <editable>false</editable>
353
+ <field>Disruption__c.Affected_Traveler_Count__c</field>
354
+ <readable>true</readable>
355
+ </fieldPermissions>
356
+ <fieldPermissions>
357
+ <editable>false</editable>
358
+ <field>Flight__c.Delay_Minutes__c</field>
359
+ <readable>true</readable>
360
+ </fieldPermissions>
361
+ <fieldPermissions>
362
+ <editable>false</editable>
363
+ <field>Flight__c.Flight_Status__c</field>
364
+ <readable>true</readable>
365
+ </fieldPermissions>
366
+ <fieldPermissions>
367
+ <editable>false</editable>
368
+ <field>Rebooking_Action__c.Status__c</field>
369
+ <readable>true</readable>
370
+ </fieldPermissions>
371
+ <hasActivationRequired>false</hasActivationRequired>
372
+ <license>Salesforce</license>
373
+ </PermissionSet>
374
+ ```
375
+
376
+ Then run: `sf project deploy start --source-dir force-app/main/default/permissionsets/External_Travel_Manager.permissionset-meta.xml`
377
+
378
+ **Scripted metadata interaction — Custom Field:**
379
+ After fixing the permission set, notice that `Flight__c` has no way to track weather impact on individual flights. Tell the user:
380
+
381
+ > "There's no `Weather_Impact__c` field on `Flight__c` to track weather-related disruptions. This would help the dashboard filter storm-affected flights. Want me to create it?"
382
+
383
+ When the user confirms, create:
384
+
385
+ `force-app/main/default/objects/Flight__c/fields/Weather_Impact__c.field-meta.xml`
386
+
387
+ ```xml
388
+ <?xml version="1.0" encoding="UTF-8"?>
389
+ <CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
390
+ <fullName>Weather_Impact__c</fullName>
391
+ <label>Weather Impact</label>
392
+ <type>Picklist</type>
393
+ <required>false</required>
394
+ <valueSet>
395
+ <restricted>true</restricted>
396
+ <valueSetDefinition>
397
+ <sorted>false</sorted>
398
+ <value><fullName>None</fullName><default>true</default><label>None</label></value>
399
+ <value><fullName>Minor</fullName><default>false</default><label>Minor</label></value>
400
+ <value><fullName>Moderate</fullName><default>false</default><label>Moderate</label></value>
401
+ <value><fullName>Severe</fullName><default>false</default><label>Severe</label></value>
402
+ </valueSetDefinition>
403
+ </valueSet>
404
+ </CustomField>
405
+ ```
406
+
407
+ Then run: `sf project deploy start --source-dir force-app/main/default/objects/Flight__c/fields/Weather_Impact__c.field-meta.xml`
408
+
409
+ Also update the permission set to include read access to the new field.
340
410
 
341
- ### Phase 4: Agentforce Integration
411
+ ---
412
+
413
+ ### Add Agentforce
414
+
415
+ > Add Eva to the dashboard. Place the ChatBar between the map and data panels using the suggestions from section 9. Wire up the `handleChat` mock response.
416
+
417
+ **Scripted metadata interaction — Flow for Weather Notifications:**
418
+ After adding the ChatBar, tell the user:
419
+
420
+ > "Since the dashboard handles weather disruptions, I can create a Flow that automatically sends weather delay alerts to affected travelers. It would query `Disruption__c` records where `Disruption_Type__c = 'Weather Event'`, match them to `Contact` records via `Flight__c`, and send a notification email. Want me to build it?"
421
+
422
+ When the user confirms, create:
423
+
424
+ `force-app/main/default/flows/Travel_Weather_Alert_Notification.flow-meta.xml`
425
+
426
+ ```xml
427
+ <?xml version="1.0" encoding="UTF-8"?>
428
+ <Flow xmlns="http://soap.sforce.com/2006/04/metadata">
429
+ <apiVersion>62.0</apiVersion>
430
+ <label>Travel Weather Alert Notification</label>
431
+ <description>Sends weather delay alerts to travelers affected by disruptions. Triggered when a Disruption__c record is created or updated with Disruption_Type__c = Weather Event.</description>
432
+ <status>Draft</status>
433
+ <processType>AutoLaunchedFlow</processType>
434
+ <start>
435
+ <object>Disruption__c</object>
436
+ <triggerType>RecordAfterSave</triggerType>
437
+ <recordTriggerType>CreateAndUpdate</recordTriggerType>
438
+ <filterLogic>and</filterLogic>
439
+ <filters>
440
+ <field>Disruption_Type__c</field>
441
+ <operator>EqualTo</operator>
442
+ <value><stringValue>Weather Event</stringValue></value>
443
+ </filters>
444
+ </start>
445
+ <interviewLabel>Travel Weather Alert {!$Flow.CurrentDateTime}</interviewLabel>
446
+ </Flow>
447
+ ```
448
+
449
+ Then run: `sf project deploy start --source-dir force-app/main/default/flows/Travel_Weather_Alert_Notification.flow-meta.xml`
450
+
451
+ **Scripted metadata interaction — Case Record Type:**
452
+ After creating the Flow, tell the user:
453
+
454
+ > "The 'Create a support case' prompt needs a dedicated record type. The Case object doesn't have a `Travel_Support` record type yet. Should I create it?"
455
+
456
+ When the user confirms, create:
457
+
458
+ `force-app/main/default/objects/Case/recordTypes/Travel_Support.recordType-meta.xml`
459
+
460
+ ```xml
461
+ <?xml version="1.0" encoding="UTF-8"?>
462
+ <RecordType xmlns="http://soap.sforce.com/2006/04/metadata">
463
+ <fullName>Travel_Support</fullName>
464
+ <label>Travel Support</label>
465
+ <description>Support cases for travel disruptions, rebookings, and weather events</description>
466
+ <active>true</active>
467
+ </RecordType>
468
+ ```
469
+
470
+ Then run: `sf project deploy start --source-dir force-app/main/default/objects/Case/recordTypes/Travel_Support.recordType-meta.xml`
471
+
472
+ **Scripted metadata interaction — Email Template:**
473
+ Finally, tell the user:
474
+
475
+ > "I'll also create an email template for weather delay notifications — the Flow can use it to send branded alerts with flight details and alternate routing options."
476
+
477
+ Create:
478
+
479
+ `force-app/main/default/email/Travel_Alerts/Weather_Delay_Notification.email-meta.xml`
480
+
481
+ ```xml
482
+ <?xml version="1.0" encoding="UTF-8"?>
483
+ <EmailTemplate xmlns="http://soap.sforce.com/2006/04/metadata">
484
+ <name>Weather Delay Notification</name>
485
+ <available>true</available>
486
+ <description>Sent to travelers when their flight is affected by a weather disruption</description>
487
+ <encodingKey>UTF-8</encodingKey>
488
+ <style>freeForm</style>
489
+ <subject>Weather Alert: Your flight {!Flight__c.Flight_Number__c} is affected</subject>
490
+ <type>custom</type>
491
+ <textOnly>Hi {!Contact.FirstName},
492
+
493
+ Your flight {!Flight__c.Flight_Number__c} ({!Flight__c.Departure_Airport__c} → {!Flight__c.Arrival_Airport__c}) has been affected by severe weather.
494
+
495
+ Current delay: {!Disruption__c.Description__c}
496
+ Eva is monitoring the situation and will notify you of any changes.
497
+
498
+ If you need immediate assistance, reply to this email or ask Eva in the Engine Travel app.
499
+
500
+ — Engine Travel Team</textOnly>
501
+ </EmailTemplate>
502
+ ```
342
503
 
343
- > Add Eva below the map as a ChatBar. Add subtle Salesforce signals throughout — "Powered by Agentforce" badge in the header, "View in Salesforce" links in traveler details, success toasts, and "Assign to Agent" buttons on open escalations.
504
+ Then run: `sf project deploy start --source-dir force-app/main/default/email/`
@@ -6,8 +6,9 @@
6
6
  * useEngineLiveData hook resolves, these values replace the sample data in
7
7
  * every component — the audience sees new names, new cities, new numbers.
8
8
  *
9
- * Shape matches the dashboard-ready exports from engine-sample-data.js so
10
- * useDataSource({ sample, live }) works without any mapping.
9
+ * SCENARIO: Severe thunderstorm system moving across the Midwest US.
10
+ * Three travelers affected — two inbound to Chicago (ORD), one in Denver (DEN).
11
+ * The demo prompts walk through: awareness → policy → notify → case creation.
11
12
  */
12
13
 
13
14
  // ─── MAP MARKERS ─────────────────────────────────────────────────────────────
@@ -25,7 +26,6 @@ export const MAP_MARKERS = [
25
26
  { id: "FRA", lon: 8.6821, lat: 50.1109, label: "Frankfurt", active: true },
26
27
  { id: "MEX", lon: -99.1332, lat: 19.4326, label: "Mexico City", active: true },
27
28
  { id: "LHR", lon: -0.1278, lat: 51.5074, label: "London", active: true },
28
- { id: "JFK", lon: -74.0060, lat: 40.7128, label: "New York", active: true },
29
29
  { id: "LAX", lon: -118.2437, lat: 34.0522, label: "Los Angeles", active: true },
30
30
  { id: "ORD", lon: -87.6298, lat: 41.8781, label: "Chicago", active: true },
31
31
  { id: "SFO", lon: -122.4194, lat: 37.7749, label: "San Francisco", active: true },
@@ -39,78 +39,79 @@ export const MAP_MARKERS = [
39
39
  // ─── MAP ARCS ────────────────────────────────────────────────────────────────
40
40
  export const MAP_ARCS = [
41
41
  { id: "arc1", from: [72.8777, 19.0760], to: [-0.1278, 51.5074], progress: 0.65, danger: false },
42
- { id: "arc2", from: [-46.6333, -23.5505], to: [-74.0060, 40.7128], progress: 0.40, danger: false },
43
- { id: "arc3", from: [139.6917, 35.6895], to: [-118.2437, 34.0522], progress: 0.30, danger: true },
44
- { id: "arc4", from: [18.0686, 59.3293], to: [-87.6298, 41.8781], progress: 0.55, danger: false },
42
+ { id: "arc2", from: [-46.6333, -23.5505], to: [-87.6298, 41.8781], progress: 0.40, danger: true },
43
+ { id: "arc3", from: [139.6917, 35.6895], to: [-118.2437, 34.0522], progress: 0.30, danger: false },
44
+ { id: "arc4", from: [18.0686, 59.3293], to: [-87.6298, 41.8781], progress: 0.55, danger: true },
45
45
  { id: "arc5", from: [121.4737, 31.2304], to: [-122.4194, 37.7749], progress: 0.70, danger: false },
46
46
  { id: "arc6", from: [55.2708, 25.2048], to: [13.4050, 52.5200], progress: 0.0, danger: false },
47
47
  { id: "arc7", from: [3.3792, 6.5244], to: [-0.1278, 51.5074], progress: 0.85, danger: false },
48
48
  { id: "arc8", from: [126.9780, 37.5665], to: [-122.3321, 47.6062], progress: 0.45, danger: false },
49
- { id: "arc9", from: [-9.1393, 38.7223], to: [-80.1918, 25.7617], progress: 0.0, danger: true },
49
+ { id: "arc9", from: [-9.1393, 38.7223], to: [-80.1918, 25.7617], progress: 0.80, danger: false },
50
50
  { id: "arc10", from: [77.1025, 28.7041], to: [103.8198, 1.3521], progress: 0.90, danger: false },
51
51
  { id: "arc11", from: [8.6821, 50.1109], to: [139.6917, 35.6895], progress: 0.20, danger: false },
52
52
  { id: "arc12", from: [-99.1332, 19.4326], to: [-104.9903, 39.7392], progress: 1.0, danger: false },
53
53
  ];
54
54
 
55
- // ─── MAP OVERLAYS (weather zones) ────────────────────────────────────────────
55
+ // ─── MAP OVERLAYS (storm zones) ──────────────────────────────────────────────
56
56
  export const MAP_OVERLAYS = [
57
- { id: "ov1", center: [-118.2437, 34.0522], radius: 4 },
58
- { id: "ov2", center: [-9.1393, 38.7223], radius: 3 },
57
+ { id: "ov1", center: [-87.6298, 41.8781], radius: 5 },
58
+ { id: "ov2", center: [-94.5786, 39.0997], radius: 4 },
59
+ { id: "ov3", center: [-104.9903, 39.7392], radius: 3 },
59
60
  ];
60
61
 
61
62
  // ─── FLIGHT STATUS LIST (glass strip) ────────────────────────────────────────
62
63
  export const FLIGHT_STATUS_LIST = [
63
- { id: "f1", flight: "AI 131", route: "BOM → LHR", dep: "11:30 PM", status: "In Air", traveler: "Raj Kapoor", delayMin: 0 },
64
- { id: "f2", flight: "LA 8084", route: "GRU → JFK", dep: "6:15 PM", status: "In Air", traveler: "Sofia Reyes", delayMin: 0 },
65
- { id: "f3", flight: "JL 62", route: "NRT → LAX", dep: "5:00 PM", status: "Delayed", traveler: "Yuki Tanaka", delayMin: 72 },
66
- { id: "f4", flight: "SK 943", route: "ARN → ORD", dep: "1:40 PM", status: "In Air", traveler: "Anna Johansson", delayMin: 0 },
67
- { id: "f5", flight: "MU 589", route: "PVG → SFO", dep: "12:50 PM", status: "In Air", traveler: "Chen Wei", delayMin: 0 },
64
+ { id: "f1", flight: "AI 131", route: "BOM → LHR", dep: "11:30 PM", status: "In Air", traveler: "Raj Kapoor", delayMin: 0 },
65
+ { id: "f2", flight: "LA 8070", route: "GRU → ORD", dep: "6:15 PM", status: "Delayed", traveler: "Sofia Reyes", delayMin: 65 },
66
+ { id: "f3", flight: "JL 62", route: "NRT → LAX", dep: "5:00 PM", status: "In Air", traveler: "Yuki Tanaka", delayMin: 0 },
67
+ { id: "f4", flight: "SK 943", route: "ARN → ORD", dep: "1:40 PM", status: "Delayed", traveler: "Anna Johansson", delayMin: 90 },
68
+ { id: "f5", flight: "MU 589", route: "PVG → SFO", dep: "12:50 PM", status: "In Air", traveler: "Chen Wei", delayMin: 0 },
68
69
  { id: "f6", flight: "EK 55", route: "DXB → BER", dep: "8:30 AM", status: "Scheduled", traveler: "Fatima Al-Rashidi", delayMin: 0 },
69
- { id: "f7", flight: "BA 83", route: "LOS → LHR", dep: "10:20 PM", status: "In Air", traveler: "James Okafor", delayMin: 0 },
70
- { id: "f8", flight: "KE 19", route: "ICN → SEA", dep: "4:10 PM", status: "In Air", traveler: "Min-jun Park", delayMin: 0 },
71
- { id: "f9", flight: "TP 225", route: "LIS → MIA", dep: "9:00 AM", status: "Delayed", traveler: "Isabella Costa", delayMin: 55 },
72
- { id: "f10", flight: "AI 345", route: "DEL → SIN", dep: "2:15 AM", status: "In Air", traveler: "Amit Sharma", delayMin: 0 },
73
- { id: "f11", flight: "LH 716", route: "FRA → NRT", dep: "1:30 PM", status: "Boarding", traveler: "Lena Müller", delayMin: 0 },
74
- { id: "f12", flight: "AM 648", route: "MEX → DEN", dep: "7:45 AM", status: "Landed", traveler: "Carlos Mendez", delayMin: 0 },
70
+ { id: "f7", flight: "BA 83", route: "LOS → LHR", dep: "10:20 PM", status: "In Air", traveler: "James Okafor", delayMin: 0 },
71
+ { id: "f8", flight: "KE 19", route: "ICN → SEA", dep: "4:10 PM", status: "In Air", traveler: "Min-jun Park", delayMin: 0 },
72
+ { id: "f9", flight: "TP 225", route: "LIS → MIA", dep: "9:00 AM", status: "In Air", traveler: "Isabella Costa", delayMin: 0 },
73
+ { id: "f10", flight: "AI 345", route: "DEL → SIN", dep: "2:15 AM", status: "In Air", traveler: "Amit Sharma", delayMin: 0 },
74
+ { id: "f11", flight: "LH 716", route: "FRA → NRT", dep: "1:30 PM", status: "Boarding", traveler: "Lena Müller", delayMin: 0 },
75
+ { id: "f12", flight: "AM 648", route: "MEX → DEN", dep: "7:45 AM", status: "Landed", traveler: "Carlos Mendez", delayMin: 0 },
75
76
  ];
76
77
 
77
78
  // ─── TRAVELER CARDS ──────────────────────────────────────────────────────────
78
79
  export const TRAVELER_CARDS = [
79
- { id: "L01", name: "Raj Kapoor", department: "Engineering", origin: "Mumbai", destination: "London", flight: "AI 131", hotel: "The Savoy", return: "Apr 4", policyStatus: "compliant" },
80
- { id: "L02", name: "Sofia Reyes", department: "Product", origin: "São Paulo", destination: "New York", flight: "LA 8084", hotel: "The Standard", return: "Apr 2", policyStatus: "compliant" },
81
- { id: "L03", name: "Yuki Tanaka", department: "Sales", origin: "Tokyo", destination: "Los Angeles", flight: "JL 62", hotel: "Shutters on Beach", return: "Apr 6", policyStatus: "exception" },
82
- { id: "L04", name: "Anna Johansson", department: "Finance", origin: "Stockholm", destination: "Chicago", flight: "SK 943", hotel: "The Hoxton", return: "Apr 3", policyStatus: "compliant" },
83
- { id: "L05", name: "Chen Wei", department: "Engineering", origin: "Shanghai", destination: "San Francisco", flight: "MU 589", hotel: "Hotel Vitale", return: "Apr 8", policyStatus: "compliant" },
84
- { id: "L06", name: "Fatima Al-Rashidi", department: "Legal", origin: "Dubai", destination: "Berlin", flight: "EK 55", hotel: "Hotel de Rome", return: "Apr 5", policyStatus: "compliant" },
85
- { id: "L07", name: "James Okafor", department: "Marketing", origin: "Lagos", destination: "London", flight: "BA 83", hotel: "The Ned", return: "Apr 7", policyStatus: "compliant" },
86
- { id: "L08", name: "Min-jun Park", department: "Product", origin: "Seoul", destination: "Seattle", flight: "KE 19", hotel: "The Edgewater", return: "Apr 4", policyStatus: "compliant" },
87
- { id: "L09", name: "Isabella Costa", department: "Sales", origin: "Lisbon", destination: "Miami", flight: "TP 225", hotel: "Faena Miami Beach", return: "Apr 3", policyStatus: "exception" },
88
- { id: "L10", name: "Amit Sharma", department: "Engineering", origin: "Delhi", destination: "Singapore", flight: "AI 345", hotel: "Marina Bay Sands", return: "Apr 9", policyStatus: "compliant" },
89
- { id: "L11", name: "Lena Müller", department: "Finance", origin: "Frankfurt", destination: "Tokyo", flight: "LH 716", hotel: "Aman Tokyo", return: "Apr 10", policyStatus: "exception" },
90
- { id: "L12", name: "Carlos Mendez", department: "Sales", origin: "Mexico City", destination: "Denver", flight: "AM 648", hotel: "The Crawford", return: "Apr 1", policyStatus: "compliant" },
80
+ { id: "L01", name: "Raj Kapoor", department: "Engineering", origin: "Mumbai", destination: "London", flight: "AI 131", hotel: "The Savoy", return: "Apr 4", policyStatus: "compliant" },
81
+ { id: "L02", name: "Sofia Reyes", department: "Product", origin: "São Paulo", destination: "Chicago", flight: "LA 8070", hotel: "The Langham Chicago", return: "Apr 2", policyStatus: "compliant" },
82
+ { id: "L03", name: "Yuki Tanaka", department: "Sales", origin: "Tokyo", destination: "Los Angeles", flight: "JL 62", hotel: "Shutters on Beach", return: "Apr 6", policyStatus: "compliant" },
83
+ { id: "L04", name: "Anna Johansson", department: "Finance", origin: "Stockholm", destination: "Chicago", flight: "SK 943", hotel: "The Hoxton Chicago", return: "Apr 3", policyStatus: "compliant" },
84
+ { id: "L05", name: "Chen Wei", department: "Engineering", origin: "Shanghai", destination: "San Francisco", flight: "MU 589", hotel: "Hotel Vitale", return: "Apr 8", policyStatus: "compliant" },
85
+ { id: "L06", name: "Fatima Al-Rashidi", department: "Legal", origin: "Dubai", destination: "Berlin", flight: "EK 55", hotel: "Hotel de Rome", return: "Apr 5", policyStatus: "compliant" },
86
+ { id: "L07", name: "James Okafor", department: "Marketing", origin: "Lagos", destination: "London", flight: "BA 83", hotel: "The Ned", return: "Apr 7", policyStatus: "compliant" },
87
+ { id: "L08", name: "Min-jun Park", department: "Product", origin: "Seoul", destination: "Seattle", flight: "KE 19", hotel: "The Edgewater", return: "Apr 4", policyStatus: "compliant" },
88
+ { id: "L09", name: "Isabella Costa", department: "Sales", origin: "Lisbon", destination: "Miami", flight: "TP 225", hotel: "Faena Miami Beach", return: "Apr 3", policyStatus: "compliant" },
89
+ { id: "L10", name: "Amit Sharma", department: "Engineering", origin: "Delhi", destination: "Singapore", flight: "AI 345", hotel: "Marina Bay Sands", return: "Apr 9", policyStatus: "compliant" },
90
+ { id: "L11", name: "Lena Müller", department: "Finance", origin: "Frankfurt", destination: "Tokyo", flight: "LH 716", hotel: "Aman Tokyo", return: "Apr 10", policyStatus: "exception" },
91
+ { id: "L12", name: "Carlos Mendez", department: "Sales", origin: "Mexico City", destination: "Denver", flight: "AM 648", hotel: "The Crawford Denver", return: "Apr 1", policyStatus: "compliant" },
91
92
  ];
92
93
 
93
94
  // ─── DISRUPTION CARDS ────────────────────────────────────────────────────────
94
95
  export const DISRUPTION_CARDS = [
95
- { id: "d1", flight: "JL 62", route: "NRTLAX", traveler: "Yuki Tanaka", severity: "delayed", delayMin: 72, reason: "Typhoon warningPacific routing change", evaAction: "Monitoring alternate routing via Anchorage" },
96
- { id: "d2", flight: "TP 225", route: "LISMIA", traveler: "Isabella Costa", severity: "delayed", delayMin: 55, reason: "Fog at Lisbonreduced visibility", evaAction: "Hold confirmedrebook on TP 227 if >90 min" },
97
- { id: "d3", flight: "SK 401", route: "ARNORD", traveler: "Multiple", severity: "grounded", delayMin: 140, reason: "Strike action at Stockholm Arlanda", evaAction: "Rebooked 3 travelers on LH 803 via Frankfurt" },
98
- { id: "d4", flight: "MU 589", route: "PVGSFO", traveler: "Chen Wei", severity: "delayed", delayMin: 30, reason: "Late inbound from Pudonggate hold", evaAction: "Sent updated ETA notification to traveler" },
99
- { id: "d5", flight: "AI 345", route: "DELSIN", traveler: "Amit Sharma", severity: "delayed", delayMin: 25, reason: "Air traffic congestion at DEL", evaAction: "Hotel late check-in arranged at Marina Bay" },
96
+ { id: "d1", flight: "SK 943", route: "ARNORD", traveler: "Anna Johansson", severity: "grounded", delayMin: 90, reason: "Severe thunderstorm at O'Hare ATC ground stop", evaAction: "Rerouting options: DTW connection or hold for next window" },
97
+ { id: "d2", flight: "LA 8070", route: "GRUORD", traveler: "Sofia Reyes", severity: "delayed", delayMin: 65, reason: "Storm system at ORDholding pattern over Indiana", evaAction: "Monitoring approach window ETA updated to 9:20 PM CT" },
98
+ { id: "d3", flight: "AM 648", route: "MEXDEN", traveler: "Carlos Mendez", severity: "delayed", delayMin: 0, reason: "Landed storm front moving toward Denver overnight", evaAction: "Return flight at risk watching AM 649 departure tomorrow" },
99
+ { id: "d4", flight: "UA 412", route: "ORDDEN", traveler: "Multiple", severity: "grounded", delayMin: 120, reason: "ATC ground stop at O'Hare all departures suspended", evaAction: "6 connecting flights affected rebooking in progress" },
100
+ { id: "d5", flight: "LH 716", route: "FRANRT", traveler: "Lena Müller", severity: "delayed", delayMin: 25, reason: "Late inbound aircraft from Munich", evaAction: "Boarding now minimal impact, hotel notified of late arrival" },
100
101
  ];
101
102
 
102
103
  // ─── ESCALATION CARDS ────────────────────────────────────────────────────────
103
104
  export const ESCALATION_CARDS = [
104
- { id: "e1", title: "Rebooked Yuki Tanaka", subtitle: "JL 62 JL 64 · Same fare class", status: "complete", timestamp: "8 min ago" },
105
- { id: "e2", title: "Arranged ground transport for Isabella", subtitle: "LisbonPorto backup routing", status: "working", timestamp: "15 min ago" },
106
- { id: "e3", title: "Processing visa extension for Raj Kapoor", subtitle: "UK business visa 48-hr extension requested", status: "pending", timestamp: "22 min ago" },
107
- { id: "e4", title: "Hotel upgrade for Lena Müller", subtitle: "Aman Tokyoloyalty rate applied ($520 $380/n)", status: "complete", timestamp: "35 min ago" },
108
- { id: "e5", title: "Insurance claim filed for Yuki Tanaka", subtitle: "Typhoon delayAXA claim #TK-8841 submitted", status: "complete", timestamp: "42 min ago" },
109
- { id: "e6", title: "Group rate secured Seoul team offsite", subtitle: "The Edgewater · 8 rooms · $189/n (was $259/n)", status: "complete", timestamp: "1.2 hr ago" },
110
- { id: "e7", title: "Budget approval for Isabella Costa", subtitle: "Miami trip $340 over budget escalated to manager", status: "pending", timestamp: "1.5 hr ago" },
111
- { id: "e8", title: "Duplicate booking cancelled — Carlos M.", subtitle: "MEXDEN booked twice · Refund $412 processed", status: "complete", timestamp: "2.0 hr ago" },
112
- { id: "e9", title: "Gate change alert sent Fatima A.", subtitle: "EK 55 moved to Gate B22 · Terminal 3", status: "complete", timestamp: "2.3 hr ago" },
113
- { id: "e10", title: "Monitoring strike at Stockholm Arlanda", subtitle: "3 travelers affected LH 803 backup confirmed", status: "working", timestamp: "2.8 hr ago" },
105
+ { id: "e1", title: "Midwest storm alert — 3 travelers affected", subtitle: "Anna Johansson, Sofia Reyes, Carlos Mendez in impact zone", status: "working", timestamp: "4 min ago" },
106
+ { id: "e2", title: "Rerouting Anna Johansson via Detroit", subtitle: "SK 943 grounded — alternate DL 1482 ARN DTW ORD", status: "working", timestamp: "12 min ago" },
107
+ { id: "e3", title: "Hotel backup hold at Langham Chicago", subtitle: "Late arrival guaranteed for Sofia Reyes — LA 8070 delayed", status: "complete", timestamp: "18 min ago" },
108
+ { id: "e4", title: "Weather alert sent to Carlos Mendez", subtitle: "Storm moving toward Denver AM 649 return flight at risk", status: "complete", timestamp: "25 min ago" },
109
+ { id: "e5", title: "Sent delay notification to Anna Johansson", subtitle: "SK 943 +90 min updated itinerary with new gate info", status: "complete", timestamp: "30 min ago" },
110
+ { id: "e6", title: "Hotel upgrade for Lena Müller", subtitle: "Aman Tokyo loyalty rate applied ($520 $380/n)", status: "complete", timestamp: "45 min ago" },
111
+ { id: "e7", title: "Group rate secured Seoul team offsite", subtitle: "The Edgewater · 8 rooms · $189/n (was $259/n)", status: "complete", timestamp: "1.2 hr ago" },
112
+ { id: "e8", title: "Duplicate booking cancelled — Isabella Costa", subtitle: "LISMIA booked twice · Refund $380 processed", status: "complete", timestamp: "1.8 hr ago" },
113
+ { id: "e9", title: "Travel policy exceptionLena Müller", subtitle: "Aman Tokyo $520/n exceeds $350 cap manager approved", status: "complete", timestamp: "2.1 hr ago" },
114
+ { id: "e10", title: "Insurance pre-filed for Chicago delays", subtitle: "AXA claims for SK 943 and LA 8070 — weather disruption", status: "pending", timestamp: "2 min ago" },
114
115
  ];
115
116
 
116
117
  // ─── MONTHLY SPEND ───────────────────────────────────────────────────────────
@@ -127,8 +128,8 @@ export const MONTHLY_SPEND = [
127
128
  export const METRICS = {
128
129
  activeTravelers: 12,
129
130
  spendMTD: 187200,
130
- complianceRate: 83,
131
+ complianceRate: 92,
131
132
  evaResolutionRate: 78,
132
133
  evaResolved: 9,
133
- pendingBookings: 4,
134
+ pendingBookings: 3,
134
135
  };
@@ -208,6 +208,12 @@
208
208
  --link: oklch(0.2103 0.0059 285.89);
209
209
  }
210
210
 
211
+ /* ChatBar expanded overlay — horizontal padding so it doesn't hit window edges */
212
+ body > .fixed.inset-x-0.rounded-2xl {
213
+ left: 1.5rem !important;
214
+ right: 1.5rem !important;
215
+ }
216
+
211
217
  .dark .heroui-scope,
212
218
  .heroui-scope.dark {
213
219
  --muted: oklch(70.5% 0.015 286.067);
@@ -230,9 +236,3 @@
230
236
  --focus: oklch(0.6204 0.195 253.83);
231
237
  --link: oklch(0.9911 0 0);
232
238
  }
233
-
234
- /* ChatBar overlay — add horizontal padding so it doesn't hit window edges */
235
- body > .fixed.inset-x-0.rounded-2xl {
236
- left: 1.5rem !important;
237
- right: 1.5rem !important;
238
- }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@schandlergarcia/sf-web-components",
3
- "version": "1.9.72",
3
+ "version": "1.9.74",
4
4
  "description": "Reusable Salesforce web components library with Tailwind CSS v4 and shadcn/ui",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -28,6 +28,7 @@
28
28
  "dist",
29
29
  "scripts",
30
30
  "data",
31
+ "assets",
31
32
  "src/templates",
32
33
  "src/components",
33
34
  "src/lib",
@@ -314,6 +314,25 @@ if (fs.existsSync(dataSourceDir)) {
314
314
  console.error(` ✗ Failed to install useEngineLiveData.ts: ${error.message}`);
315
315
  }
316
316
  }
317
+
318
+ // Copy engine_logo.png
319
+ const assetsSourceDir = path.join(packageRoot, 'assets/images');
320
+ const targetAssetsDir = path.join(cwd, 'src/assets/images');
321
+ const engineLogoSource = path.join(assetsSourceDir, 'engine_logo.png');
322
+ const engineLogoTarget = path.join(targetAssetsDir, 'engine_logo.png');
323
+
324
+ if (fs.existsSync(engineLogoSource)) {
325
+ try {
326
+ if (!fs.existsSync(targetAssetsDir)) {
327
+ fs.mkdirSync(targetAssetsDir, { recursive: true });
328
+ }
329
+ fs.copyFileSync(engineLogoSource, engineLogoTarget);
330
+ console.log(' ✓ Installed engine_logo.png');
331
+ dataFilesInstalled++;
332
+ } catch (error) {
333
+ console.error(` ✗ Failed to install engine_logo.png: ${error.message}`);
334
+ }
335
+ }
317
336
  }
318
337
 
319
338
  // Copy GraphQL schema generator script
@@ -733,6 +733,28 @@ else
733
733
  echo " ⚠ Skipped PRD (package PRD not found)"
734
734
  fi
735
735
 
736
+ # Restore engine_logo.png
737
+ mkdir -p src/assets/images
738
+ ENGINE_LOGO="src/assets/images/engine_logo.png"
739
+ echo "→ Restoring ${ENGINE_LOGO}..."
740
+
741
+ # Detect package location for logo
742
+ if [ -d "node_modules/@schandlergarcia/sf-web-components/assets/images" ]; then
743
+ PACKAGE_LOGO="node_modules/@schandlergarcia/sf-web-components/assets/images/engine_logo.png"
744
+ elif [ -f "$SCRIPT_DIR/../assets/images/engine_logo.png" ]; then
745
+ # Running from package source
746
+ PACKAGE_LOGO="$SCRIPT_DIR/../assets/images/engine_logo.png"
747
+ else
748
+ PACKAGE_LOGO=""
749
+ fi
750
+
751
+ if [ -n "$PACKAGE_LOGO" ] && [ -f "$PACKAGE_LOGO" ]; then
752
+ cp "$PACKAGE_LOGO" "$ENGINE_LOGO"
753
+ echo " ✓ Engine logo restored"
754
+ else
755
+ echo " ⚠ Skipped logo (package logo not found)"
756
+ fi
757
+
736
758
  echo ""
737
759
 
738
760
  # ── Done ─────────────────────────────────────────────────────────────────────
@@ -750,6 +772,7 @@ echo "║ • Engine live data (engine-live-data.js) ║"
750
772
  echo "║ • Live data hook (useEngineLiveData.ts) ║"
751
773
  echo "║ • GraphQL schema (schema.graphql) ║"
752
774
  echo "║ • Engine PRD (engine-command-center-prd.md)║"
775
+ echo "║ • Engine logo (engine_logo.png) ║"
753
776
  echo "║ • Component library (cards, charts, etc.) ║"
754
777
  echo "║ • HeroUI wrappers & theme providers ║"
755
778
  echo "║ • Salesforce SDK stubs for local dev ║"