@schandlergarcia/sf-web-components 1.9.67 → 1.9.69

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.
@@ -1,6 +1,6 @@
1
1
  # Engine Travel Command Center
2
2
 
3
- Product Requirements Document · TDX26 Demo Build
3
+ Product Requirements Document
4
4
 
5
5
  ---
6
6
 
@@ -8,28 +8,25 @@ Product Requirements Document · TDX26 Demo Build
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
- It is not Engine's consumer-facing booking app. It is a personalized command center for one travel manager at one company.
12
-
13
- **Demo context:** Built live at TDX26 using Agentforce Vibes. The arc is: PRD → scaffold → data → agent → intelligence. Each build prompt maps to one beat.
11
+ **Build approach:** Incremental PRD scaffold data agent intelligence.
14
12
 
15
13
  ---
16
14
 
17
15
  ## 2. Users
18
16
 
19
17
  **Primary — Sarah Chen, Travel Manager at Acme Corp**
20
- Manages Acme's travel program: bookings, policy enforcement, exceptions, traveler monitoring. Before this dashboard she worked across Engine's booking interface, email, and Salesforce separately.
18
+ Manages Acme's travel program: bookings, policy enforcement, exceptions, traveler monitoring.
21
19
 
22
20
  **Secondary — Eva (Agentforce Agent)**
23
- Engine's Agentforce-powered AI agent. Handles traveler requests autonomously — booking changes, policy questions, itinerary lookups. Resolves ~30% of requests without human involvement. Surfaces in the dashboard via `ChatBar` (command palette).
21
+ Engine's Agentforce-powered AI agent. Handles traveler requests autonomously — booking changes, policy questions, itinerary lookups. Resolves ~30% of requests without human involvement. Surfaces via `ChatBar` below the hero map.
24
22
 
25
23
  ---
26
24
 
27
25
  ## 3. Brand Tokens
28
26
 
29
- Update `src/styles/global.css` — replace the existing `--color-brand-*` palette inside the `@theme inline` block. Do not add new CSS variables or custom properties outside this block.
27
+ Update `src/styles/global.css` — replace the existing `--color-brand-*` palette inside the `@theme inline` block:
30
28
 
31
29
  ```css
32
- /* Engine teal brand palette */
33
30
  --color-brand-50: #ECFDF9;
34
31
  --color-brand-100: #D1FAF0;
35
32
  --color-brand-200: #A7F3E1;
@@ -41,185 +38,96 @@ Update `src/styles/global.css` — replace the existing `--color-brand-*` palett
41
38
  --color-brand-800: #115E59;
42
39
  --color-brand-900: #134E4A;
43
40
  --color-brand-950: #042F2E;
44
- ```
45
-
46
- Additionally, add a custom `--color-engine-savings` token for the green savings accent:
47
-
48
- ```css
49
41
  --color-engine-savings: #16A34A;
50
42
  ```
51
43
 
52
- All component styling uses `brand-*` Tailwind classes for primary UI and Tailwind built-in colors (green, amber, red) for status. No hardcoded hex values in components. No `bg-indigo-*` or raw color classes for brand elements.
44
+ Use `brand-*` Tailwind classes for brand elements. Tailwind built-in colors (green, amber, red) for status. No hardcoded hex in components.
53
45
 
54
- **Typography:** Inter (already configured as `--font-sans` in global.css). No changes needed.
55
-
56
- **Icons:** Heroicons 2 exclusively (`@heroicons/react`). No Lucide, Font Awesome, or other libraries inside command center pages.
46
+ **Typography:** Inter (already `--font-sans`). **Icons:** Heroicons 2 (`@heroicons/react`).
57
47
 
58
48
  ---
59
49
 
60
50
  ## 4. Architecture
61
51
 
62
- **Dashboard file:** `src/pages/EngineDashboard.tsx` (single file, `.tsx` — TypeScript project, React components MUST use `.tsx`)
63
-
64
- **Wiring:** `CommandCenter.tsx` imports and renders `EngineDashboard` inside the existing provider stack (`heroui-scope` → `AppThemeProvider` → `DataModeProvider` → `Toast.Provider`). Never recreate these providers in the dashboard.
65
-
66
- **Imports:** All components from `@/components/library` barrel. Theme access from `@/components/library/theme/AppThemeProvider`.
52
+ - **Dashboard file:** `src/pages/EngineDashboard.tsx`
53
+ - **Wiring:** `CommandCenter.tsx` imports and renders `EngineDashboard` inside the existing provider stack. Never recreate providers in the dashboard.
54
+ - **Imports:** Components from `@/components/library`. Theme from `@/components/library/theme/AppThemeProvider`.
67
55
 
68
56
  ---
69
57
 
70
- ## 5. Layout — Visualization-Hero Pattern
58
+ ## 5. Layout
71
59
 
72
- This dashboard uses the **visualization-hero** layout. The live flight map is the signature element.
73
-
74
- ### Structure (top to bottom)
60
+ Visualization-hero layout. Full-page scroll (no `h-screen`).
75
61
 
76
62
  ```
77
- ┌─────────────────────────────────────────────────────────────┐
78
- │ HEADER BAR (h-12, sticky)
79
- │ Engine logo + "Travel Command Center" | ChatBar | actions
80
- ├─────────────────────────────────────────────────────────────┤
81
- │ HERO MAP (h-[520px] or h-[600px], GeoMap)
82
- Full-page
83
- ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ↕ scroll
84
- │ KPI │ │ KPI │ │ KPI │ │ KPI │ ← glass
85
- overlay overlay │ overlay │ │ overlay │ overlays
86
- └─────────┘ └─────────┘ └─────────┘ └─────────┘
87
-
88
- │ ┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐ ← flight │ ↕
89
- BA286 NH109 LH431 │ │ SQ 37 │ status
90
- │ └───────┘ └───────┘ └───────┘ └───────┘ strip │ ↕
91
- ├─────────────────────────────────────────────────────────────┤ ↕
92
- DATA PANELS (px-4 py-5, space-y-6, natural flow)
93
-
94
- ┌─── 1/3 ───────┐ ┌─── 2/3 ────────────────────┐
95
- │ │ Disruptions │ │ Active Travelers
96
- (custom panel) │ │ (expandable cards, scroll) │ ↕
97
- └────────────────┘ └────────────────────────────┘
98
-
99
- ┌─── 1/2 ───────┐ ┌─── 1/2 ────────────────────┐
100
- │ │ Escalations │ │ Travel Spend (Monthly)
101
- (ActivityCard) │ │ (ChartCard + D3Chart line) │ ↕
102
- │ └────────────────┘ └────────────────────────────┘ │ ↕
103
- └─────────────────────────────────────────────────────────────┘
63
+ ┌──────────────────────────────────────────────────────────────┐
64
+ │ HEADER (h-12, sticky, bg-slate-900)
65
+ │ Engine logo + "Travel Command Center" + badge | actions
66
+ ├──────────────────────────────────────────────────────────────┤
67
+ │ HERO MAP (h-[520px], GeoMap, relative)
68
+ ┌─ glass KPI overlays (absolute top-left) ───────────────┐ │
69
+ Active Travelers | Spend MTD | Compliance | Eva Resolved
70
+ └────────────────────────────────────────────────────────┘
71
+ ┌─ flight status strip (absolute bottom) ────────────────┐
72
+ BA 286 · In Air | NH 109 · In Air | LH 431 · Delayed
73
+ └────────────────────────────────────────────────────────┘
74
+ ├──────────────────────────────────────────────────────────────┤
75
+ EVA CHATBAR (px-4 pt-4, full-width command palette strip)
76
+ ├──────────────────────────────────────────────────────────────┤
77
+ │ DATA PANELS (px-4 py-5 space-y-6) │
78
+
79
+ Row 1: grid grid-cols-1 items-start gap-4 lg:grid-cols-3
80
+ ┌── 2/3 (lg:col-span-2) ──────┐ ┌── 1/3 ───────────────┐
81
+ │ │ Escalations (custom panel) │ │ Disruptions (custom)
82
+ └───────────────────────────────┘ └──────────────────────┘
83
+
84
+ Row 2: grid grid-cols-1 items-start gap-4 lg:grid-cols-3
85
+ ┌── 2/3 (lg:col-span-2) ──────┐ ┌── 1/3 ───────────────┐
86
+ │ │ Active Travelers (expandable) │ │ Travel Spend (chart)
87
+ └───────────────────────────────┘ └──────────────────────┘
88
+ └──────────────────────────────────────────────────────────────┘
104
89
  ```
105
90
 
106
- ### Layout Rules
107
-
108
- **CRITICAL: Full-page scroll, NOT viewport-locked sections.**
91
+ ### Layout classes
109
92
 
110
- The entire dashboard scrolls as one page. Do NOT use `h-screen` or flex height constraints that lock sections to viewport.
93
+ - **Outer:** `<div className="flex flex-col bg-slate-50 dark:bg-slate-950">`
94
+ - **Map:** `<div className="relative h-[520px]">`
95
+ - **Eva:** `<div className="px-4 pt-4"><ChatBar ... /></div>`
96
+ - **Panels:** `<div className="px-4 py-5"><div className="space-y-6">...</div></div>`
97
+ - **Row 1:** `grid grid-cols-1 items-start gap-4 lg:grid-cols-3` — Escalations `lg:col-span-2`, Disruptions auto
98
+ - **Row 2:** `grid grid-cols-1 items-start gap-4 lg:grid-cols-3` — Travelers `lg:col-span-2`, Spend auto
111
99
 
112
- - **Outer container:** `<div className="flex flex-col bg-slate-50 dark:bg-slate-950">` (NO `h-screen`)
113
- - **Hero map:** Fixed height `h-[520px]` or `h-[600px]` with `relative` positioning for glass overlays
114
- - **Data panels:** Natural flow with `px-4 py-5 space-y-6` (NO `overflow-y-auto`, NO flex height constraints)
115
- - **Row 1** - Disruptions + Travelers: `grid grid-cols-1 items-start gap-4 lg:grid-cols-3` (disruptions 1/3, travelers `lg:col-span-2`)
116
- - **Row 2** - Escalations + Chart: `grid grid-cols-1 items-start gap-4 lg:grid-cols-2`
117
- - All grids use `items-start` for different-height cards
100
+ ### Phase 1 placeholders
118
101
 
119
- **Why full-page scroll:** Users need to see the map in context while scrolling through data. A fixed-height map that stays visible while data scrolls below creates a cramped, split-screen feeling. Full-page scroll is more natural and gives each section breathing room.
120
-
121
- ### Phase 1 Placeholder Rules
122
-
123
- **Phase 1 builds the complete layout structure** including header bar, hero map, glass KPI overlays, flight status strip, and data panels. The only "placeholders" are the **data panels** below the map.
124
-
125
- **What to build in Phase 1:**
126
- - ✅ Header bar (with logo, title, theme toggle) - **use slate scale colors, NOT text-white**
127
- - ✅ Hero map (GeoMap component with no markers/arcs/overlays props)
128
- - ✅ Glass KPI overlays (4 KPIs, hardcoded values, positioned absolutely over map)
129
- - ✅ Flight status strip (4 flights, hardcoded values, positioned at map bottom)
130
- - ✅ Data panels (BaseCard placeholders with centered text) - **these are the only placeholders**
131
-
132
- **Data panel placeholders** use `BaseCard` with centered text:
102
+ 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:
133
103
 
134
104
  ```tsx
135
- // ✅ Correct data panel placeholder
136
105
  <BaseCard>
137
106
  <div className="h-48 flex items-center justify-center text-slate-400">
138
- Disruptions Panel
107
+ Escalations Panel
139
108
  </div>
140
109
  </BaseCard>
141
-
142
- // ❌ Wrong - title causes text to run together
143
- <BaseCard title="Disruptions">
144
- <div className="text-slate-400">No disruptions detected</div>
145
- </BaseCard>
146
- ```
147
-
148
- **Hero map** (render actual GeoMap component in Phase 1):
149
-
150
- ```tsx
151
- // ✅ Correct - render actual GeoMap component (empty, no data)
152
- import { GeoMap } from "@/components/library";
153
- import { useThemeMode } from "@/components/library/theme/AppThemeProvider";
154
-
155
- export default function EngineDashboard() {
156
- const { mode } = useThemeMode();
157
-
158
- return (
159
- <div className="relative h-[520px]">
160
- <GeoMap
161
- width={960}
162
- height={520}
163
- theme={mode === "dark" ? "dark" : "light"}
164
- className="h-full w-full"
165
- />
166
- </div>
167
- );
168
- }
169
- ```
170
-
171
- In Phase 1:
172
- - GeoMap component renders with NO `markers`, `arcs`, or `overlays` props (these are GeoMap data props for Phase 2+)
173
- - GeoMap will render the base world map (land, sphere, graticule) even without data - this is NOT a placeholder, it's the actual map component
174
- - Glass KPI overlays and flight status strip ARE built in Phase 1 (as separate `<div>` elements positioned absolutely over the map)
175
- - Uses `useThemeMode()` to get dark/light mode for theme prop
176
- - **Verify:** You should see a blue world map (dark theme) or gray world map (light theme) with glass overlays on top
177
-
178
- // ❌ Wrong - wrapped in BaseCard
179
- <div className="relative h-[520px]">
180
- <BaseCard>
181
- <GeoMap ... />
182
- </BaseCard>
183
- </div>
184
-
185
- // ❌ Wrong - text placeholder instead of GeoMap component
186
- <div className="relative h-[520px]">
187
- <div className="flex h-full items-center justify-center bg-slate-100 dark:bg-slate-900">
188
- <span className="text-slate-400">Hero Map (GeoMap)</span>
189
- </div>
190
- </div>
191
110
  ```
192
111
 
193
- All data panel placeholders use: BaseCard, no title, centered text, `h-48`, `text-slate-400`.
194
-
195
- Hero map uses: actual GeoMap component (no markers/arcs/overlays in Phase 1), direct render, fills hero container.
112
+ The GeoMap renders the base world map with no data props. Glass overlays and flight strip use hardcoded values.
196
113
 
197
114
  ---
198
115
 
199
116
  ## 6. Header
200
117
 
201
- Compact `h-12` sticky header with dark background (`bg-slate-900 dark:bg-slate-950`).
118
+ Compact `h-12` sticky header (`bg-slate-900 dark:bg-slate-950`). No ChatBar in the header — Eva lives below the map.
202
119
 
203
- | Position | Content |
204
- |----------|---------|
205
- | Left | Engine logo (small square icon with `GlobeAltIcon` in `bg-brand-600`) + "ENGINE" wordmark + separator + "Travel Command Center" |
206
- | Right | `ChatBar` (Eva) + notification bell (with pending count badge) + theme toggle (sun/moon) |
207
-
208
- The `ChatBar` replaces any FAB or sliding panel. It opens inline as a command palette overlay.
120
+ | Left | Right |
121
+ |------|-------|
122
+ | 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) |
209
123
 
210
124
  ---
211
125
 
212
126
  ## 7. Hero Map
213
127
 
214
- **Component:** `GeoMap` (from `@/components/library`)
215
-
216
- **Phase 1:** Render GeoMap with NO data props (no markers/arcs/overlays)
217
- **Phase 2:** Add markers, arcs, and overlays from sample data to show where travelers are
128
+ **Component:** `GeoMap` from `@/components/library`
218
129
 
219
130
  ```tsx
220
- import { MAP_MARKERS, MAP_ARCS, MAP_OVERLAYS } from "@/data/engine-sample-data";
221
-
222
- // Phase 2 - with map data
223
131
  <GeoMap
224
132
  width={960}
225
133
  height={520}
@@ -232,86 +140,100 @@ import { MAP_MARKERS, MAP_ARCS, MAP_OVERLAYS } from "@/data/engine-sample-data";
232
140
  />
233
141
  ```
234
142
 
235
- **Markers:** One per city (origin and destination). Each marker: `{ id, lon, lat, label, active: true }`. Shows where travelers are located.
236
-
237
- **Arcs:** One per active flight route. Each arc: `{ id, from: [lon, lat], to: [lon, lat], progress: 0–1 }`. Set `danger: true` on disrupted routes (delayed 60+ min or grounded). The `progress` value places an animated pulsing dot showing the flight's current position along the great circle arc.
238
-
239
- **Overlays:** Weather/disruption zones as pulsing circles. Each overlay: `{ id, center: [lon, lat], radius: number }`.
143
+ - **Markers:** One per city. `{ id, lon, lat, label, active: true }`
144
+ - **Arcs:** One per flight route. `{ id, from: [lon, lat], to: [lon, lat], progress: 0–1, danger: boolean }`. `danger: true` on disrupted routes.
145
+ - **Overlays:** Weather zones. `{ id, center: [lon, lat], radius: number }`
146
+ - **Phase 1:** Render GeoMap with no data props (map renders base world automatically)
147
+ - **Phase 2:** Add `MAP_MARKERS`, `MAP_ARCS`, `MAP_OVERLAYS` from `@/data/engine-sample-data`
240
148
 
241
- **Glass KPI overlays:** Four pills floating `absolute left-3 top-3` on the map:
242
- - Active Travelers (count)
243
- - Spend MTD (formatted with `fmtK()`)
244
- - Compliance %
245
- - Eva Resolved (count + "resolved")
149
+ **Glass KPI overlays** 4 pills, `absolute left-3 top-3`:
150
+ Active Travelers (count), Spend MTD (`fmtK()`), Compliance %, Eva Resolved (count + "resolved").
246
151
 
247
- Each pill: `bg-black/40 backdrop-blur-md border border-white/10 rounded-lg px-3 py-1.5` with white text and icon.
152
+ Label styling: `text-[10px] font-medium uppercase tracking-wide text-slate-300`
153
+ Value styling: `text-sm font-semibold text-slate-50`
154
+ Pill styling: `bg-black/40 backdrop-blur-md border border-white/10 rounded-lg px-3 py-1.5`
155
+ Icon: `h-4 w-4 text-brand-400`
248
156
 
249
- **Flight status strip:** Floating `absolute bottom-3 left-3 right-3`, horizontal scroll. One pill per active flight showing flight number + status chip (On Time = green, Delayed = amber, Grounded/Boarding = contextual). Same glass styling as KPI overlays.
157
+ **Flight status strip** `absolute bottom-3 left-3 right-3`, horizontal scroll. One pill per flight: flight number + status chip (green=On Time, amber=Delayed, sky=Boarding). Same glass styling.
250
158
 
251
159
  ---
252
160
 
253
161
  ## 8. Data Panels
254
162
 
255
- **CRITICAL: Panel titles MUST match PRD exactly, even if data export has different name.**
256
-
257
- For example: Panel titled "Escalations" uses data exported as `EVA_ACTIONS` or `ESCALATION_CARDS` (both are aliases). The PRD panel title takes precedence over data export names.
258
-
259
- ### 8a. Disruptions Panel (1/3 width)
260
-
261
- Custom inline component inside a `BaseCard`. Shows only flights that are delayed 60+ min or grounded.
262
-
263
- Each disruption card:
264
- - Severity dot (rose for grounded, amber for delayed) + flight number + severity label + delay minutes
265
- - Route + affected traveler name
266
- - Reason text
267
- - Eva action line with `SparklesIcon` showing what Eva is doing about it
268
-
269
- ### 8b. Active Travelers (2/3 width)
163
+ 4 panels in 2 rows. Sample data imported from `src/data/engine-sample-data.js`.
270
164
 
271
- `BaseCard` with `maxBodyHeight={420}` for internal scroll. Expandable traveler cards.
165
+ ### 8a. Escalations (Row 1, 2/3 width)
272
166
 
273
- Each traveler row:
274
- - `Avatar` (initials) + name + department + origin → destination
275
- - `UIChip` for policy status (success = compliant, warning = exception)
276
- - Click to expand: 4-col grid showing Flight, Hotel, Return date, Destination
167
+ Custom inline panel in BaseCard. Do NOT use ActivityCard — this panel has per-item "Assign to Agent" buttons.
277
168
 
278
- ### 8c. Escalations (1/2 width)
279
-
280
- **CRITICAL: ActivityCard has no card styling by default. Must wrap in BaseCard for visible card background.**
169
+ Each escalation row:
170
+ - Status icon: `CheckCircleIcon` (complete, green), `ArrowPathIcon` (working, brand), `ClockIcon` (pending, amber)
171
+ - Title + subtitle + timestamp
172
+ - **"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")`
281
173
 
282
174
  ```tsx
283
175
  <BaseCard>
284
- <ActivityCard
285
- title="Escalations"
286
- actions={useDataSource({ sample: ESCALATION_CARDS, live: [] })}
287
- />
176
+ <div className="p-4">
177
+ <h3 className="mb-3 text-sm font-semibold text-slate-900 dark:text-slate-50">
178
+ Escalations
179
+ </h3>
180
+ <div className="divide-y divide-slate-100 dark:divide-slate-800">
181
+ {escalations.map((e) => {
182
+ const isOpen = e.status !== "complete";
183
+ return (
184
+ <div key={e.id} className="flex items-start gap-3 py-3">
185
+ <StatusIcon className={`mt-0.5 h-5 w-5 shrink-0 ${statusColor}`} />
186
+ <div className="min-w-0 flex-1">
187
+ <div className="text-sm font-medium text-slate-900 dark:text-slate-50">{e.title}</div>
188
+ <div className="mt-0.5 text-xs text-slate-500 dark:text-slate-400">
189
+ {e.subtitle} · {e.timestamp}
190
+ </div>
191
+ </div>
192
+ {isOpen && (
193
+ <button onClick={() => handleAssignToAgent(e)}
194
+ className="shrink-0 rounded-md bg-brand-600 px-2.5 py-1 text-xs font-medium text-slate-50 hover:bg-brand-700">
195
+ <span className="flex items-center gap-1">
196
+ <SparklesIcon className="h-3 w-3" /> Assign to Agent
197
+ </span>
198
+ </button>
199
+ )}
200
+ </div>
201
+ );
202
+ })}
203
+ </div>
204
+ </div>
288
205
  </BaseCard>
289
206
  ```
290
207
 
291
- **Data source:** Import `ESCALATION_CARDS` from sample data (alias of `EVA_ACTIONS`)
208
+ Data: `ESCALATION_CARDS` from sample data.
209
+
210
+ ### 8b. Disruptions (Row 1, 1/3 width)
211
+
212
+ Custom inline component in BaseCard. Shows flights delayed 60+ min or grounded.
213
+
214
+ Each disruption card:
215
+ - Severity dot (rose=grounded, amber=delayed) + flight number + delay minutes
216
+ - Route + traveler name
217
+ - Reason text
218
+ - Eva action line with `SparklesIcon`
219
+
220
+ Data: `DISRUPTION_CARDS` from sample data.
292
221
 
293
- Each escalation has:
294
- - Title (issue description)
295
- - Subtitle (details + context)
296
- - Status: `"working"` (Eva is handling), `"pending"` (needs review), or `"complete"` (resolved)
297
- - Timestamp
222
+ ### 8c. Active Travelers (Row 2, 2/3 width)
298
223
 
299
- Examples from sample data:
300
- - "Processing approval for Omar Hassan" - subtitle: "Paris trip over budget · escalated to manager" (status: pending)
301
- - "Monitoring weather delay for Elena Rodriguez" - subtitle: "AF 99 · CDG arrival delayed 45 min · No action yet" (status: working)
302
- - "Rebooked cancelled flight for Priya Patel" - subtitle: "LH 431 → LH 433 · Same fare class" (status: complete)
224
+ BaseCard with `maxBodyHeight={420}`. Expandable traveler cards.
303
225
 
304
- ### 8d. Monthly Spend Trend (1/2 width)
226
+ Each row: `Avatar` (initials) + name + department + origin → destination + policy chip (compliant=green, exception=amber).
227
+ Expand: 4-col grid — Flight, Hotel, Return, Destination + "View in Salesforce" link.
305
228
 
306
- `ChartCard` wrapping a `D3Chart` with `D3ChartTemplates.groupedBarChart` (lineChart doesn't support categorical x-axis).
229
+ Data: `TRAVELER_CARDS` from sample data.
307
230
 
308
- **CRITICAL: Use this exact code structure:**
231
+ ### 8d. Monthly Spend Trend (Row 2, 1/3 width)
309
232
 
310
233
  ```tsx
311
- // Transform data to chart format
312
- const spendChartData = useDataSource({
313
- sample: MONTHLY_SPEND.map(m => ({ x: m.month, spend: m.amount })),
314
- live: []
234
+ const spendChartData = useDataSource({
235
+ sample: MONTHLY_SPEND.map(m => ({ x: m.month, spend: m.amount })),
236
+ live: [],
315
237
  });
316
238
 
317
239
  <ChartCard
@@ -336,145 +258,78 @@ const spendChartData = useDataSource({
336
258
  />
337
259
  ```
338
260
 
339
- **Data source:** Import `MONTHLY_SPEND` from sample data, transform to chart format
340
-
341
- **Requirements:**
342
- - X-axis: month names (Oct through Mar) - categorical
343
- - Y-axis: spend amount in dollars (will show as thousands: 98000, 128000, etc.)
344
- - Height: 320px
345
- - Bar color: Engine teal (#5BC8C8)
346
- - Bar radius: 6 for rounded corners
347
- - **Data transformation:** map `{ month, amount }` to `{ x, spend }` format inline
348
- - responsive={true}
349
-
350
- **Why this chart:** Shows spend trends over time so travel managers can spot anomalies, budget overruns, or seasonal patterns. Using bars instead of line because D3 lineChart requires numeric x-axis.
351
-
352
- **That's it!** Only 4 panels total: Disruptions, Active Travelers, Escalations, and Monthly Spend Trend.
353
-
354
261
  ---
355
262
 
356
263
  ## 9. Eva — ChatBar
357
264
 
358
- Eva surfaces as a `ChatBar` in the header. Not a FAB. Not a sliding panel. Not a `ChatPanel` in the grid.
265
+ Eva renders as a full-width `ChatBar` below the hero map, between the map and the data panels. NOT in the header. NOT a FAB or sliding panel.
359
266
 
360
- ```jsx
361
- <ChatBar
362
- title="Eva"
363
- placeholder="Ask about travelers, bookings, policy, spend…"
364
- suggestions={suggestions}
365
- onSend={handleChat}
366
- />
267
+ ```tsx
268
+ <div className="px-4 pt-4">
269
+ <ChatBar
270
+ title="Eva"
271
+ placeholder="Ask Eva anything about travelers, bookings, policy, or spend…"
272
+ suggestions={CHAT_SUGGESTIONS}
273
+ onSend={handleChat}
274
+ />
275
+ </div>
367
276
  ```
368
277
 
369
- **Starter suggestions (3–5):**
370
- - "Who's traveling internationally right now?"
371
- - "Show bookings pending approval"
372
- - "What has Eva resolved today?"
373
- - "Travelers returning this week"
278
+ Suggestions: "Who's traveling internationally right now?", "Show bookings pending approval", "What has Eva resolved today?", "Travelers returning this week"
374
279
 
375
- **Agent responses:** Return structured `components` arrays for data (MetricCard, DataTable, StatusCard) — never markdown tables in text content.
280
+ Define `handleChat` and `CHAT_SUGGESTIONS` at module scope.
376
281
 
377
- **Handler:** Define `handleChat` and `CHAT_SUGGESTIONS` at module scope (outside the component body) so they can be shared if an "open in tab" feature is added later.
282
+ **Phase 1:** Placeholder do NOT add ChatBar. Just leave the space empty.
283
+ **Phase 4:** Add the ChatBar between the map and data panels.
378
284
 
379
285
  ---
380
286
 
381
- ## 10. Salesforce Signals (Phase 4 Only)
287
+ ## 10. Salesforce Signals (Phase 4)
382
288
 
383
- **⚠️ These are added in Phase 4, NOT Phase 1.** Phase 1 has only the Engine logo and "Travel Command Center" title.
289
+ Subtle indicators, not banners.
384
290
 
385
- Subtle, contextual indicators like "Sent from iPhone", not promotional banners.
386
-
387
- | Signal | Location | Implementation | Phase |
388
- |--------|----------|----------------|-------|
389
- | " View in Salesforce" | Traveler expanded view, booking rows | Micro-link, muted text | Phase 4 |
390
- | " Salesforce updated" | After rebook/approve actions | `toast()` with success tone | Phase 4 |
391
- | "Powered by Agentforce" | ChatBar title or subtitle area | Small badge/chip | Phase 4 |
291
+ | Signal | Location |
292
+ |--------|----------|
293
+ | "Powered by Agentforce" | Header, next to "Travel Command Center" (small rounded badge) |
294
+ | "View in Salesforce" | Traveler expanded view (micro-link, muted text) |
295
+ | "Salesforce updated" | After rebook/approve actions (`toast()`) |
296
+ | "Assigned to Eva" | After "Assign to Agent" button click (`toast.success()`) |
392
297
 
393
298
  ---
394
299
 
395
300
  ## 11. Dark Mode
396
301
 
397
- Mandatory on every element. The dashboard must work in both light and dark mode with no visual bugs.
398
-
399
- - Use the standard dark mode pairs from the builder rules (bg-white/dark:bg-slate-900, etc.)
400
- - The GeoMap has built-in dark/light themes — pass `theme={mode === "dark" ? "dark" : "light"}`
401
- - Glass overlays use `bg-black/40 backdrop-blur-md` which works in both modes
402
- - Custom inline components (disruption cards, flight chips) must include `dark:` variants on every color class
403
- - Never use `text-black` or `text-white` directly — use the slate scale
404
- - Theme toggle button in the header (sun/moon icon)
302
+ Every element supports light and dark. GeoMap: `theme={mode === "dark" ? "dark" : "light"}`. Glass overlays work in both modes. Use slate scale (no `text-white` or `text-black`). Theme toggle in header.
405
303
 
406
304
  ---
407
305
 
408
306
  ## 12. Data Model
409
307
 
410
- **Traveler** — `id`, `name`, `department`, `destination`, `origin`, `hotel`, `flight`, `return`, `status`, `policyStatus` (compliant | exception)
411
-
412
- **Flight** — `id`, `flight` (number), `route`, `dep` (time), `status` (On Time | Delayed | Boarding | In Air | Landed), `traveler`, `delayMin`
413
-
414
- **Disruption** — `id`, `flight`, `route`, `traveler`, `severity` (grounded | delayed), `delayMin`, `reason`, `evaAction`
415
-
416
- **Map Coordinates** — City name → `[lon, lat]` lookup object. Markers derived from coordinates. Arcs derived from flight origin/destination pairs.
417
-
418
- **Booking** — `id`, `traveler`, `type`, `destination`, `dates`, `cost`, `status` (Confirmed | Pending approval), `policy` (In policy | Over budget | Exception granted)
419
-
420
- **Policy Item** — `id`, `title`, `description`, `status` (operational | degraded), `timestamp`
421
-
422
- **Escalation** — `id`, `title`, `subtitle`, `status` (complete | working | pending), `timestamp`
423
-
424
- **Monthly Spend** — `{ month: string, amount: number }` array. Six months of travel spend data (Oct through Mar). Amounts in dollars, typically $98K-$156K range.
425
-
426
- **Sample data:** 8–10 travelers, 8 flights, 4 disruptions, 8 bookings, 5 policy items, 8 escalations, 6 months of spend. All deterministic (no `Math.random()`). Include realistic names, routes, dollar amounts, and a mix of all status types.
308
+ - **Traveler** — `id`, `name`, `department`, `destination`, `origin`, `hotel`, `flight`, `return`, `policyStatus` (compliant | exception)
309
+ - **Flight** — `id`, `flight`, `route`, `dep`, `status` (On Time | Delayed | Boarding | In Air | Landed), `traveler`, `delayMin`
310
+ - **Disruption** — `id`, `flight`, `route`, `traveler`, `severity` (grounded | delayed), `delayMin`, `reason`, `evaAction`
311
+ - **Escalation** — `id`, `title`, `subtitle`, `status` (complete | working | pending), `timestamp`
312
+ - **Monthly Spend** — `{ month, amount }` array, 6 months (Oct–Mar), $98K–$156K range
313
+ - **Sample data:** Pre-built in `src/data/engine-sample-data.js`. 8 travelers, 8 flights, 4 disruptions, 8 escalations, 6 months spend. All deterministic.
427
314
 
428
315
  ---
429
316
 
430
317
  ## 13. Build Prompts
431
318
 
432
- Build in 4 incremental phases. Each prompt maps to one beat of the demo arc.
433
-
434
- ---
319
+ Build in 4 incremental phases.
435
320
 
436
321
  ### Phase 1: Layout & Structure
437
322
 
438
323
  > 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.
439
324
 
440
- ---
441
-
442
325
  ### Phase 2: Components & Sample Data
443
326
 
444
- > Replace the placeholders with real components and wire up the sample data. Build the 4 data panels from section 8 — Disruptions, Active Travelers, Escalations, and Monthly Spend Trend. Add map markers, arcs, and overlays from the sample data so we can see where travelers are.
445
-
446
- ---
327
+ > 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.
447
328
 
448
329
  ### Phase 3: Real Data Integration
449
330
 
450
331
  > Connect the dashboard to live Salesforce data using GraphQL. Read the sample data file to see which fields are needed, then build queries and hooks for each object. The UI should stay identical — just swap the data source.
451
332
 
452
- ---
453
-
454
333
  ### Phase 4: Agentforce Integration
455
334
 
456
- > Add Eva to the header as a ChatBar. Add subtle Salesforce signals throughout — "View in Salesforce" links, success toasts, "Powered by Agentforce" badge.
457
-
458
- ---
459
-
460
- ## 14. What NOT to Do
461
-
462
- These are explicit anti-patterns. If any prompt output includes these, it must be corrected:
463
-
464
- - Do not use `react-simple-maps`, Recharts, or any charting library other than the built-in `D3Chart` + `D3ChartTemplates`
465
- - Do not use Lucide icons, Font Awesome, or any icon library other than Heroicons 2
466
- - Do not import from `src/components/ui/` (shadcn) — use `@/components/library` exclusively
467
- - Do not use `cn()` helper — that belongs to the outer app
468
- - Do not recreate `AppThemeProvider`, `DataModeProvider`, or `Toast.Provider` — `CommandCenter.tsx` provides them
469
- - Do not build a FAB or sliding chat panel — use `ChatBar` in the header
470
- - Do not hardcode hex color values in component JSX — use `brand-*` Tailwind classes and Tailwind built-in colors for status
471
- - Do not use `bg-indigo-*` for brand — use `brand-*`
472
- - Do not use `text-black` or `text-white` — use the slate scale
473
- - Do not put more than 4 KPIs in a row
474
- - Do not squeeze the map into a grid column — it must be full-width hero
475
- - Do not wrap GeoMap in ChartCard — render it directly
476
- - Do not use `Math.random()` in sample data
477
- - Do not skip dark mode on any visible element
478
- - Do not use inline `style={{}}` for layout except the flex properties on the hero/panel containers
479
- - Do not put `ChatPanel` in the dashboard grid
480
- - Do not use `position: fixed` without `createPortal` to `document.body`
335
+ > 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.