@schandlergarcia/sf-web-components 2.3.17 → 2.4.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/CHANGELOG.md +16 -0
- package/CLAUDE.md +12 -13
- package/README.md +0 -15
- package/dist/styles/global.css +44 -57
- package/package.json +1 -2
- package/scripts/apply-brand.mjs +47 -30
- package/scripts/postinstall.mjs +1 -11
- package/src/styles/global.css +44 -57
- package/brands/engine/PARTNER_HUB_PRD.md +0 -584
- package/brands/engine/agentApiConfig.ts +0 -36
- package/brands/engine/app/api/graphql-operations-types.ts +0 -11260
- package/brands/engine/app/api/graphqlClient.ts +0 -25
- package/brands/engine/app/api/partnerQueries.ts +0 -212
- package/brands/engine/app/appLayout.tsx +0 -5
- package/brands/engine/app/components/AgentPanel.tsx +0 -541
- package/brands/engine/app/components/AgentforceConversationClient.tsx +0 -201
- package/brands/engine/app/components/Data360Widget.tsx +0 -301
- package/brands/engine/app/components/__inherit_AgentforceConversationClient.tsx +0 -3
- package/brands/engine/app/components/alerts/status-alert.tsx +0 -49
- package/brands/engine/app/components/layouts/card-layout.tsx +0 -29
- package/brands/engine/app/components/workspace/CommandCenter.tsx +0 -16
- package/brands/engine/app/config/agentApi.ts +0 -36
- package/brands/engine/app/data/partner-hub-sample-data.js +0 -297
- package/brands/engine/app/features/object-search/__examples__/api/accountSearchService.ts +0 -46
- package/brands/engine/app/features/object-search/__examples__/api/query/distinctAccountIndustries.graphql +0 -19
- package/brands/engine/app/features/object-search/__examples__/api/query/distinctAccountTypes.graphql +0 -19
- package/brands/engine/app/features/object-search/__examples__/api/query/getAccountDetail.graphql +0 -121
- package/brands/engine/app/features/object-search/__examples__/api/query/searchAccounts.graphql +0 -51
- package/brands/engine/app/features/object-search/__examples__/pages/AccountObjectDetailPage.tsx +0 -357
- package/brands/engine/app/features/object-search/__examples__/pages/AccountSearch.tsx +0 -312
- package/brands/engine/app/features/object-search/__examples__/pages/Home.tsx +0 -34
- package/brands/engine/app/features/object-search/api/objectSearchService.ts +0 -84
- package/brands/engine/app/features/object-search/components/ActiveFilters.tsx +0 -89
- package/brands/engine/app/features/object-search/components/FilterContext.tsx +0 -83
- package/brands/engine/app/features/object-search/components/ObjectBreadcrumb.tsx +0 -66
- package/brands/engine/app/features/object-search/components/PaginationControls.tsx +0 -109
- package/brands/engine/app/features/object-search/components/SearchBar.tsx +0 -41
- package/brands/engine/app/features/object-search/components/SortControl.tsx +0 -143
- package/brands/engine/app/features/object-search/components/filters/BooleanFilter.tsx +0 -78
- package/brands/engine/app/features/object-search/components/filters/DateFilter.tsx +0 -128
- package/brands/engine/app/features/object-search/components/filters/DateRangeFilter.tsx +0 -70
- package/brands/engine/app/features/object-search/components/filters/FilterFieldWrapper.tsx +0 -33
- package/brands/engine/app/features/object-search/components/filters/MultiSelectFilter.tsx +0 -97
- package/brands/engine/app/features/object-search/components/filters/NumericRangeFilter.tsx +0 -163
- package/brands/engine/app/features/object-search/components/filters/SearchFilter.tsx +0 -50
- package/brands/engine/app/features/object-search/components/filters/SelectFilter.tsx +0 -97
- package/brands/engine/app/features/object-search/components/filters/TextFilter.tsx +0 -91
- package/brands/engine/app/features/object-search/hooks/useAsyncData.ts +0 -54
- package/brands/engine/app/features/object-search/hooks/useCachedAsyncData.ts +0 -184
- package/brands/engine/app/features/object-search/hooks/useDebouncedCallback.ts +0 -34
- package/brands/engine/app/features/object-search/hooks/useObjectSearchParams.ts +0 -252
- package/brands/engine/app/features/object-search/utils/debounce.ts +0 -25
- package/brands/engine/app/features/object-search/utils/fieldUtils.ts +0 -29
- package/brands/engine/app/features/object-search/utils/filterUtils.ts +0 -404
- package/brands/engine/app/features/object-search/utils/sortUtils.ts +0 -38
- package/brands/engine/app/hooks/useEngineLiveData.ts +0 -49
- package/brands/engine/app/hooks/useEvaAgent.ts +0 -288
- package/brands/engine/app/hooks/usePartnerDashboardData.ts +0 -141
- package/brands/engine/app/navigationMenu.tsx +0 -80
- package/brands/engine/app/pages/AccountObjectDetailPage.tsx +0 -361
- package/brands/engine/app/pages/AccountSearch.tsx +0 -305
- package/brands/engine/app/pages/BlankDashboard.tsx +0 -15
- package/brands/engine/app/pages/DataTest.tsx +0 -78
- package/brands/engine/app/pages/Home.tsx +0 -5
- package/brands/engine/app/pages/NotFound.tsx +0 -19
- package/brands/engine/app/pages/PartnerHubDashboard.tsx +0 -2760
- package/brands/engine/app/pages/Search.tsx +0 -13
- package/brands/engine/app/router-utils.tsx +0 -35
- package/brands/engine/app/routes.tsx +0 -39
- package/brands/engine/app/styles/global.css +0 -269
- package/brands/engine/brand.css +0 -40
- package/brands/engine/engine-command-center-prd.md +0 -575
- package/brands/engine/engine-live-data.js +0 -135
- package/brands/engine/engine-sample-data.js +0 -378
- package/brands/engine/engine_logo.png +0 -0
- package/brands/engine/global.css +0 -269
- package/brands/engine/partner-hub-sample-data.js +0 -281
- package/brands/engine/schema.graphql +0 -292
- package/brands/engine/useEngineLiveData.ts +0 -49
- package/brands/engine/useEvaAgent.ts +0 -288
|
@@ -1,575 +0,0 @@
|
|
|
1
|
-
# Engine Travel Command Center
|
|
2
|
-
|
|
3
|
-
Product Requirements Document
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## 1. Product Overview
|
|
8
|
-
|
|
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
|
-
|
|
11
|
-
**Build approach:** Incremental — PRD → dashboard → data → agent.
|
|
12
|
-
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
## 2. Users
|
|
16
|
-
|
|
17
|
-
**Primary — Sarah Chen, Travel Manager at Acme Corp**
|
|
18
|
-
Manages Acme's travel program: bookings, policy enforcement, exceptions, traveler monitoring.
|
|
19
|
-
|
|
20
|
-
**Secondary — Eva (Agentforce Agent)**
|
|
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.
|
|
22
|
-
|
|
23
|
-
---
|
|
24
|
-
|
|
25
|
-
## 3. Brand Tokens
|
|
26
|
-
|
|
27
|
-
Update `src/styles/global.css` — replace the existing `--color-brand-*` palette inside the `@theme inline` block:
|
|
28
|
-
|
|
29
|
-
```css
|
|
30
|
-
--color-brand-50: #ECFDF9;
|
|
31
|
-
--color-brand-100: #D1FAF0;
|
|
32
|
-
--color-brand-200: #A7F3E1;
|
|
33
|
-
--color-brand-300: #6EE7C8;
|
|
34
|
-
--color-brand-400: #34D3AB;
|
|
35
|
-
--color-brand-500: #5BC8C8;
|
|
36
|
-
--color-brand-600: #0D9488;
|
|
37
|
-
--color-brand-700: #0F766E;
|
|
38
|
-
--color-brand-800: #115E59;
|
|
39
|
-
--color-brand-900: #134E4A;
|
|
40
|
-
--color-brand-950: #042F2E;
|
|
41
|
-
--color-engine-savings: #16A34A;
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
Use `brand-*` Tailwind classes for brand elements. Tailwind built-in colors (green, amber, red) for status. No hardcoded hex in components.
|
|
45
|
-
|
|
46
|
-
**Typography:** Inter (already `--font-sans`). **Icons:** Heroicons 2 (`@heroicons/react`).
|
|
47
|
-
|
|
48
|
-
---
|
|
49
|
-
|
|
50
|
-
## 4. Architecture
|
|
51
|
-
|
|
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. 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
|
-
- **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
|
-
- **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`.
|
|
57
|
-
|
|
58
|
-
---
|
|
59
|
-
|
|
60
|
-
## 5. Layout
|
|
61
|
-
|
|
62
|
-
Visualization-hero layout. Full-page scroll (no `h-screen`).
|
|
63
|
-
|
|
64
|
-
```
|
|
65
|
-
┌──────────────────────────────────────────────────────────────┐
|
|
66
|
-
│ HEADER (h-12, sticky, bg-slate-900) │
|
|
67
|
-
│ Engine logo + "Travel Command Center" + badge | actions │
|
|
68
|
-
├──────────────────────────────────────────────────────────────┤
|
|
69
|
-
│ HERO MAP (h-[520px], GeoMap, relative) │
|
|
70
|
-
│ ┌─ glass KPI overlays (absolute top-left) ───────────────┐ │
|
|
71
|
-
│ │ Active Travelers | Spend MTD | Compliance | Eva Resolved│ │
|
|
72
|
-
│ └────────────────────────────────────────────────────────┘ │
|
|
73
|
-
│ ┌─ flight status strip (absolute bottom) ────────────────┐ │
|
|
74
|
-
│ │ BA 286 · In Air | NH 109 · In Air | LH 431 · Delayed │ │
|
|
75
|
-
│ └────────────────────────────────────────────────────────┘ │
|
|
76
|
-
├──────────────────────────────────────────────────────────────┤
|
|
77
|
-
│ EVA CHATBAR (px-4 pt-4, full-width command palette strip) │
|
|
78
|
-
├──────────────────────────────────────────────────────────────┤
|
|
79
|
-
│ DATA PANELS (px-4 py-5 space-y-6) │
|
|
80
|
-
│ │
|
|
81
|
-
│ Row 1: grid grid-cols-1 items-start gap-4 lg:grid-cols-3 │
|
|
82
|
-
│ ┌── 2/3 (lg:col-span-2) ──────┐ ┌── 1/3 ───────────────┐ │
|
|
83
|
-
│ │ Escalations (custom panel) │ │ Disruptions (custom) │ │
|
|
84
|
-
│ └───────────────────────────────┘ └──────────────────────┘ │
|
|
85
|
-
│ │
|
|
86
|
-
│ Row 2: grid grid-cols-1 items-start gap-4 lg:grid-cols-3 │
|
|
87
|
-
│ ┌── 2/3 (lg:col-span-2) ──────┐ ┌── 1/3 ───────────────┐ │
|
|
88
|
-
│ │ Active Travelers (expandable) │ │ Travel Spend (chart) │ │
|
|
89
|
-
│ └───────────────────────────────┘ └──────────────────────┘ │
|
|
90
|
-
└──────────────────────────────────────────────────────────────┘
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
### Layout classes
|
|
94
|
-
|
|
95
|
-
- **Outer:** `<div className="flex flex-col bg-slate-50 dark:bg-slate-950">`
|
|
96
|
-
- **Map:** `<div className="relative h-[520px]">`
|
|
97
|
-
- **Eva:** `<div className="px-4 pt-4"><ChatBar ... /></div>`
|
|
98
|
-
- **Panels:** `<div className="px-4 py-5"><div className="space-y-6">...</div></div>`
|
|
99
|
-
- **Row 1:** `grid grid-cols-1 items-start gap-4 lg:grid-cols-3` — Escalations `lg:col-span-2`, Disruptions auto
|
|
100
|
-
- **Row 2:** `grid grid-cols-1 items-start gap-4 lg:grid-cols-3` — Travelers `lg:col-span-2`, Spend auto
|
|
101
|
-
|
|
102
|
-
---
|
|
103
|
-
|
|
104
|
-
## 6. Header
|
|
105
|
-
|
|
106
|
-
Compact `h-12` sticky header (`bg-slate-900 dark:bg-slate-950`). No ChatBar in the header — Eva lives below the map.
|
|
107
|
-
|
|
108
|
-
| Left | Right |
|
|
109
|
-
|------|-------|
|
|
110
|
-
| Engine logo (`engine_logo.png`, `h-5 w-auto brightness-0 invert` to make it white on the dark header) + "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) |
|
|
111
|
-
|
|
112
|
-
```tsx
|
|
113
|
-
import engineLogo from "@/assets/images/engine_logo.png";
|
|
114
|
-
|
|
115
|
-
<img src={engineLogo} alt="Engine" className="h-5 w-auto brightness-0 invert" />
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
---
|
|
119
|
-
|
|
120
|
-
## 7. Hero Map
|
|
121
|
-
|
|
122
|
-
**Component:** `GeoMap` from `@/components/library`
|
|
123
|
-
|
|
124
|
-
```tsx
|
|
125
|
-
<GeoMap
|
|
126
|
-
width={960}
|
|
127
|
-
height={520}
|
|
128
|
-
theme={mode === "dark" ? "dark" : "light"}
|
|
129
|
-
markers={useDataSource({ sample: MAP_MARKERS, live: live.mapMarkers })}
|
|
130
|
-
arcs={useDataSource({ sample: MAP_ARCS, live: live.mapArcs })}
|
|
131
|
-
overlays={useDataSource({ sample: MAP_OVERLAYS, live: live.mapOverlays })}
|
|
132
|
-
zoomable
|
|
133
|
-
className="h-full w-full"
|
|
134
|
-
/>
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
- **Markers:** One per city. `{ id, lon, lat, label, active: true }`
|
|
138
|
-
- **Arcs:** One per flight route. `{ id, from: [lon, lat], to: [lon, lat], progress: 0–1, danger: boolean }`. `danger: true` on disrupted routes.
|
|
139
|
-
- **Overlays:** Weather zones. `{ id, center: [lon, lat], radius: number }`
|
|
140
|
-
|
|
141
|
-
**Glass KPI overlays** — 4 pills, `absolute left-3 top-3`:
|
|
142
|
-
Active Travelers (count), Spend MTD (`fmtK()`), Compliance %, Eva Resolved (count + "resolved").
|
|
143
|
-
|
|
144
|
-
Label styling: `text-[10px] font-medium uppercase tracking-wide text-slate-300`
|
|
145
|
-
Value styling: `text-sm font-semibold text-slate-50`
|
|
146
|
-
Pill styling: `bg-black/40 backdrop-blur-md border border-white/10 rounded-lg px-3 py-1.5`
|
|
147
|
-
Icon: `h-4 w-4 text-brand-400`
|
|
148
|
-
|
|
149
|
-
**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.
|
|
150
|
-
|
|
151
|
-
---
|
|
152
|
-
|
|
153
|
-
## 8. Data Panels
|
|
154
|
-
|
|
155
|
-
4 panels in 2 rows. Sample data imported from `src/data/engine-sample-data.js`.
|
|
156
|
-
|
|
157
|
-
### 8a. Escalations (Row 1, 2/3 width)
|
|
158
|
-
|
|
159
|
-
Custom inline panel in BaseCard. Do NOT use ActivityCard — this panel has per-item "Assign to Agent" buttons.
|
|
160
|
-
|
|
161
|
-
Each escalation row:
|
|
162
|
-
- Status icon: `CheckCircleIcon` (complete, green), `ArrowPathIcon` (working, brand), `ClockIcon` (pending, amber)
|
|
163
|
-
- Title + subtitle + timestamp
|
|
164
|
-
- **"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`.
|
|
165
|
-
|
|
166
|
-
```tsx
|
|
167
|
-
<BaseCard>
|
|
168
|
-
<div className="p-4">
|
|
169
|
-
<h3 className="mb-3 text-sm font-semibold text-slate-900 dark:text-slate-50">
|
|
170
|
-
Escalations
|
|
171
|
-
</h3>
|
|
172
|
-
<div className="divide-y divide-slate-100 dark:divide-slate-800">
|
|
173
|
-
{escalations.map((e) => {
|
|
174
|
-
const isOpen = e.status !== "complete";
|
|
175
|
-
return (
|
|
176
|
-
<div key={e.id} className="flex items-start gap-3 py-3">
|
|
177
|
-
<StatusIcon className={`mt-0.5 h-5 w-5 shrink-0 ${statusColor}`} />
|
|
178
|
-
<div className="min-w-0 flex-1">
|
|
179
|
-
<div className="text-sm font-medium text-slate-900 dark:text-slate-50">{e.title}</div>
|
|
180
|
-
<div className="mt-0.5 text-xs text-slate-500 dark:text-slate-400">
|
|
181
|
-
{e.subtitle} · {e.timestamp}
|
|
182
|
-
</div>
|
|
183
|
-
</div>
|
|
184
|
-
{isOpen && (
|
|
185
|
-
<button onClick={() => toast.success("Assigned to Eva")}
|
|
186
|
-
className="shrink-0 rounded-md bg-brand-600 px-2.5 py-1 text-xs font-medium text-slate-50 hover:bg-brand-700">
|
|
187
|
-
<span className="flex items-center gap-1">
|
|
188
|
-
<SparklesIcon className="h-3 w-3" /> Assign to Agent
|
|
189
|
-
</span>
|
|
190
|
-
</button>
|
|
191
|
-
)}
|
|
192
|
-
</div>
|
|
193
|
-
);
|
|
194
|
-
})}
|
|
195
|
-
</div>
|
|
196
|
-
</div>
|
|
197
|
-
</BaseCard>
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
Data: `ESCALATION_CARDS` from sample data.
|
|
201
|
-
|
|
202
|
-
### 8b. Disruptions (Row 1, 1/3 width)
|
|
203
|
-
|
|
204
|
-
Custom inline component in BaseCard. Shows flights delayed 60+ min or grounded.
|
|
205
|
-
|
|
206
|
-
Each disruption card:
|
|
207
|
-
- Severity dot (rose=grounded, amber=delayed) + flight number + delay minutes
|
|
208
|
-
- Route + traveler name
|
|
209
|
-
- Reason text
|
|
210
|
-
- Eva action line with `SparklesIcon`
|
|
211
|
-
|
|
212
|
-
Data: `DISRUPTION_CARDS` from sample data.
|
|
213
|
-
|
|
214
|
-
### 8c. Active Travelers (Row 2, 2/3 width)
|
|
215
|
-
|
|
216
|
-
BaseCard with `maxBodyHeight={420}`. Expandable traveler cards.
|
|
217
|
-
|
|
218
|
-
Each row: `Avatar` (initials) + name + department + origin → destination + policy chip (compliant=green, exception=amber).
|
|
219
|
-
Expand: 4-col grid — Flight, Hotel, Return, Destination + "View in Salesforce" link.
|
|
220
|
-
|
|
221
|
-
Data: `TRAVELER_CARDS` from sample data.
|
|
222
|
-
|
|
223
|
-
### 8d. Monthly Spend Trend (Row 2, 1/3 width)
|
|
224
|
-
|
|
225
|
-
```tsx
|
|
226
|
-
const spendChartData = useDataSource({
|
|
227
|
-
sample: MONTHLY_SPEND.map(m => ({ x: m.month, spend: m.amount })),
|
|
228
|
-
live: live.monthlySpend.map(m => ({ x: m.month, spend: m.amount })),
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
<ChartCard
|
|
232
|
-
title="Travel Spend"
|
|
233
|
-
subtitle="Monthly trend (6 months)"
|
|
234
|
-
chart={
|
|
235
|
-
<D3Chart
|
|
236
|
-
data={spendChartData}
|
|
237
|
-
renderChart={D3ChartTemplates.groupedBarChart}
|
|
238
|
-
options={{
|
|
239
|
-
xKey: "x",
|
|
240
|
-
groups: ["spend"],
|
|
241
|
-
colors: ["#5BC8C8"],
|
|
242
|
-
barRadius: 6,
|
|
243
|
-
margin: { top: 20, right: 20, bottom: 40, left: 60 },
|
|
244
|
-
showGrid: true,
|
|
245
|
-
}}
|
|
246
|
-
responsive
|
|
247
|
-
height={320}
|
|
248
|
-
/>
|
|
249
|
-
}
|
|
250
|
-
/>
|
|
251
|
-
```
|
|
252
|
-
|
|
253
|
-
---
|
|
254
|
-
|
|
255
|
-
## 9. Eva — Agentforce Integration
|
|
256
|
-
|
|
257
|
-
Two pieces work together: a **ChatBar** for user input and the **`useEvaAgent` hook** which talks to the Agentforce Agent REST API.
|
|
258
|
-
|
|
259
|
-
> **Important:** We do NOT use `AgentforceConversationClient` (the iframe widget). All agent communication goes through the REST-based Agent API via the `useEvaAgent` hook. The Vite dev server proxies the requests to avoid CORS issues.
|
|
260
|
-
|
|
261
|
-
### ChatBar
|
|
262
|
-
|
|
263
|
-
A command-palette strip below the hero map. NOT in the header. NOT a FAB or sliding panel.
|
|
264
|
-
|
|
265
|
-
```tsx
|
|
266
|
-
<div className="px-4 pt-4">
|
|
267
|
-
<ChatBar
|
|
268
|
-
title="Eva"
|
|
269
|
-
placeholder="Ask Eva anything about travelers, bookings, policy, or spend…"
|
|
270
|
-
suggestions={CHAT_SUGGESTIONS}
|
|
271
|
-
onSend={handleChat}
|
|
272
|
-
/>
|
|
273
|
-
</div>
|
|
274
|
-
```
|
|
275
|
-
|
|
276
|
-
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"
|
|
277
|
-
|
|
278
|
-
### useEvaAgent Hook
|
|
279
|
-
|
|
280
|
-
The hook manages the full Agent API lifecycle — OAuth, session, messaging, cleanup.
|
|
281
|
-
|
|
282
|
-
```tsx
|
|
283
|
-
import useEvaAgent from "@/hooks/useEvaAgent";
|
|
284
|
-
|
|
285
|
-
// Inside the component:
|
|
286
|
-
const { messages, isReady, isSending, connect, sendMessage } = useEvaAgent();
|
|
287
|
-
|
|
288
|
-
// Connect on mount (or on first ChatBar focus):
|
|
289
|
-
useEffect(() => { connect(); }, [connect]);
|
|
290
|
-
|
|
291
|
-
// Wire to ChatBar:
|
|
292
|
-
const handleChat = (text: string) => {
|
|
293
|
-
sendMessage(text);
|
|
294
|
-
return { text: `Looking into: "${text}"…` };
|
|
295
|
-
};
|
|
296
|
-
```
|
|
297
|
-
|
|
298
|
-
The hook:
|
|
299
|
-
1. **Authenticates** via OAuth client-credentials (`POST /sf-oauth/services/oauth2/token`)
|
|
300
|
-
2. **Creates a session** (`POST /sf-agent/einstein/ai-agent/v1/agents/{agentId}/sessions`)
|
|
301
|
-
3. **Sends messages** (`POST /sf-agent/…/messages`) — returns agent response text
|
|
302
|
-
4. **Ends the session** (`DELETE /sf-agent/…`) on unmount
|
|
303
|
-
|
|
304
|
-
Config is in `src/config/agentApi.ts`. Proxy rules are in `vite.config.ts` (`/sf-oauth` and `/sf-agent`).
|
|
305
|
-
|
|
306
|
-
Define `CHAT_SUGGESTIONS` at module scope.
|
|
307
|
-
|
|
308
|
-
---
|
|
309
|
-
|
|
310
|
-
## 10. Salesforce Signals
|
|
311
|
-
|
|
312
|
-
Subtle indicators throughout the dashboard — not banners.
|
|
313
|
-
|
|
314
|
-
| Signal | Location |
|
|
315
|
-
|--------|----------|
|
|
316
|
-
| "Powered by Agentforce" | Header, next to "Travel Command Center" (small rounded badge) |
|
|
317
|
-
| "View in Salesforce" | Traveler expanded view (micro-link, muted text) |
|
|
318
|
-
| "Salesforce updated" | After rebook/approve actions — `toast("Opening in Salesforce…")` via `@heroui/react` |
|
|
319
|
-
| "Assigned to Eva" | After "Assign to Agent" button click — `toast.success("Assigned to Eva")` via `@heroui/react` |
|
|
320
|
-
|
|
321
|
-
---
|
|
322
|
-
|
|
323
|
-
## 11. Dark Mode
|
|
324
|
-
|
|
325
|
-
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.
|
|
326
|
-
|
|
327
|
-
---
|
|
328
|
-
|
|
329
|
-
## 12. Data Model
|
|
330
|
-
|
|
331
|
-
- **Traveler** — `id`, `name`, `department`, `destination`, `origin`, `hotel`, `flight`, `return`, `policyStatus` (compliant | exception)
|
|
332
|
-
- **Flight** — `id`, `flight`, `route`, `dep`, `status` (On Time | Delayed | Boarding | In Air | Landed), `traveler`, `delayMin`
|
|
333
|
-
- **Disruption** — `id`, `flight`, `route`, `traveler`, `severity` (grounded | delayed), `delayMin`, `reason`, `evaAction`
|
|
334
|
-
- **Escalation** — `id`, `title`, `subtitle`, `status` (complete | working | pending), `timestamp`
|
|
335
|
-
- **Monthly Spend** — `{ month, amount }` array, 6 months (Oct–Mar), $98K–$156K range
|
|
336
|
-
- **Sample data:** Pre-built in `src/data/engine-sample-data.js`. 8 travelers, 8 flights, 4 disruptions, 8 escalations, 6 months spend. All deterministic.
|
|
337
|
-
|
|
338
|
-
---
|
|
339
|
-
|
|
340
|
-
## 13. Build Prompts
|
|
341
|
-
|
|
342
|
-
Build incrementally in 3 prompts. Each prompt builds on the previous result. The user prompt (blockquote) is what you copy-paste. The agent instructions below each prompt are guidance for the AI — not shown to the user.
|
|
343
|
-
|
|
344
|
-
> **CRITICAL — Rules for ALL prompts:**
|
|
345
|
-
> - Do NOT read any file inside `src/components/library/` (GeoMap.jsx, ChatBar.jsx, BaseCard.jsx, useDataSource.jsx, DataModeProvider.jsx, etc.) — all props/patterns are documented here and in the build guide.
|
|
346
|
-
> - Do NOT load unnecessary skills — only `command-center-builder` is needed.
|
|
347
|
-
> - Do NOT run `npm run build` during the build — Vite handles it.
|
|
348
|
-
> - Do NOT create files not listed in the instructions below.
|
|
349
|
-
|
|
350
|
-
---
|
|
351
|
-
|
|
352
|
-
### Prompt 1 — Build the Dashboard
|
|
353
|
-
|
|
354
|
-
> Build me a travel command center for Engine. There's a PRD with the full spec. I want the big hero map showing where all our travelers are with flight arcs, glass KPI overlays on top of the map, a flight status ticker, and then below that the main panels — escalations that need attention, active disruptions, our traveler roster with expandable details, and a spend trend chart. Hook it all up to the sample data so it looks real. Don't worry about the AI assistant or live data yet — we'll layer those on after. Make it the home page when you're done — I want to see it when I open the app.
|
|
355
|
-
|
|
356
|
-
**What the agent must do:**
|
|
357
|
-
|
|
358
|
-
1. Load ONLY the `command-center-builder` skill (no other skills)
|
|
359
|
-
2. Read ONLY the PRD and `src/data/engine-sample-data.js` — no other files before writing
|
|
360
|
-
3. Create `src/pages/EngineDashboard.tsx` with the full dashboard in ONE write
|
|
361
|
-
4. Update `src/components/workspace/CommandCenter.tsx` to import EngineDashboard
|
|
362
|
-
5. **Rewrite `src/pages/Home.tsx`** to render CommandCenter (it ships as Account Search — must be replaced with a 3-line wrapper)
|
|
363
|
-
6. **Update `src/routes.tsx`** to set Home as the index route with label "Dashboard"
|
|
364
|
-
|
|
365
|
-
**FORBIDDEN in Prompt 1:**
|
|
366
|
-
- ❌ ChatBar, Eva, or any Agentforce integration (Prompt 3)
|
|
367
|
-
- ❌ `useEngineLiveData` or live data wiring (Prompt 2)
|
|
368
|
-
- ❌ Salesforce metadata — custom fields, platform events, Apex (Prompts 2-3)
|
|
369
|
-
- ❌ Reading library component source files (GeoMap.jsx, BaseCard.jsx, etc.)
|
|
370
|
-
- ❌ Loading `outer-app`, `component-library`, `command-center-project`, or other skills
|
|
371
|
-
|
|
372
|
-
**Common failure:** The agent creates the dashboard file but forgets to update `Home.tsx` and `routes.tsx`, so the user still sees the Account Search page.
|
|
373
|
-
|
|
374
|
-
---
|
|
375
|
-
|
|
376
|
-
### Prompt 2 — Connect to Salesforce Data
|
|
377
|
-
|
|
378
|
-
> Now let's connect this to real data. There's a second dataset with different travelers and metrics — hook that up as the "live" data source so the dashboard can pull from either dataset depending on the backend config. After that, see if there are any data model improvements we should make for tracking weather disruptions.
|
|
379
|
-
|
|
380
|
-
**What the agent must do (exactly 5 edits + 1 suggestion):**
|
|
381
|
-
|
|
382
|
-
1. Read ONLY `EngineDashboard.tsx` and `src/hooks/useEngineLiveData.ts` — no other files
|
|
383
|
-
2. **Edit 1:** Add `useEngineLiveData` import to EngineDashboard.tsx
|
|
384
|
-
3. **Edit 2:** Add `const live = useEngineLiveData()` hook call
|
|
385
|
-
4. **Edit 3:** Update every `useDataSource({ sample, live: [] })` to pass `live: live.xxx`
|
|
386
|
-
5. **Edit 4:** Update KPI derivations to use reactive metrics
|
|
387
|
-
6. **Edit 5:** ⚠️ **CRITICAL — Flip `ENABLE_SAMPLE_DATA_CACHE` from `true` to `false` in `src/lib/dataStrategy.ts`** — without this, the data never switches and the demo fails
|
|
388
|
-
7. After all edits, **STOP and suggest** ONLY `Weather_Impact__c` — do NOT create it until the user says yes
|
|
389
|
-
|
|
390
|
-
**FORBIDDEN in Prompt 2:**
|
|
391
|
-
- ❌ **`DataModeToggle`** — do NOT add it to the UI. There is NO UI toggle. The switch is `dataStrategy.ts`. This is the most common agent mistake.
|
|
392
|
-
- ❌ Rewriting `EngineDashboard.tsx` from scratch (make 5 targeted edits)
|
|
393
|
-
- ❌ Renaming existing imports with `SAMPLE_` prefix (leave variable names alone, just fill in `live:` props)
|
|
394
|
-
- ❌ Reading library source files (`useDataSource.jsx`, `DataModeProvider.jsx`, `DataModeToggle.jsx`)
|
|
395
|
-
- ❌ ChatBar, Eva, or Agentforce (Prompt 3)
|
|
396
|
-
- ❌ Running `npm run build` between edits
|
|
397
|
-
- ❌ Suggesting more than one custom field (only `Weather_Impact__c`)
|
|
398
|
-
- ❌ Creating extra files not listed above
|
|
399
|
-
|
|
400
|
-
**Reference — what the agent should say after wiring:**
|
|
401
|
-
|
|
402
|
-
*"I notice `Flight__c` doesn't have a field to track weather impact on individual flights. A `Weather_Impact__c` picklist would help the dashboard filter storm-affected flights. Want me to create it?"* — Only create the custom field XML from section 14a AFTER the user confirms.
|
|
403
|
-
|
|
404
|
-
---
|
|
405
|
-
|
|
406
|
-
### Prompt 3 — Add Agentforce
|
|
407
|
-
|
|
408
|
-
> Last piece — let's add Eva, the AI travel assistant. I want a chat bar right below the map where people can ask her questions about travelers and disruptions. Also hook up the real Agentforce agent so she can actually do things. Then let's set up the backend so disruption alerts can flow through the platform in real time.
|
|
409
|
-
|
|
410
|
-
**Pre-installed files (already in the project — do NOT recreate):**
|
|
411
|
-
- `src/hooks/useEvaAgent.ts` — Agent API hook (OAuth → session → messages → cleanup)
|
|
412
|
-
- `src/config/agentApi.ts` — hardcoded credentials and endpoints
|
|
413
|
-
- `vite.config.ts` — already has `/sf-oauth` and `/sf-agent` proxy rules
|
|
414
|
-
|
|
415
|
-
**What the agent must do (exactly 4 edits + 2 suggestions):**
|
|
416
|
-
|
|
417
|
-
1. Read ONLY `src/pages/EngineDashboard.tsx` — no other files
|
|
418
|
-
2. **Edit 1:** Add `ChatBar` and `useEvaAgent` imports
|
|
419
|
-
3. **Edit 2:** Add `CHAT_SUGGESTIONS` constant at module scope
|
|
420
|
-
4. **Edit 3:** Add `useEvaAgent` hook call + `handleChat` handler inside component
|
|
421
|
-
5. **Edit 4:** Insert `<ChatBar />` JSX between map and data panels
|
|
422
|
-
6. After adding Eva, **STOP and suggest** the platform event — wait for user confirmation
|
|
423
|
-
7. After platform event, **STOP and suggest** the Apex class — wait for user confirmation
|
|
424
|
-
|
|
425
|
-
**FORBIDDEN in Prompt 3:**
|
|
426
|
-
- ❌ **`AgentforceConversationClient`** — do NOT use the iframe widget. We use the Agent REST API via `useEvaAgent`.
|
|
427
|
-
- ❌ **Wrapper hooks** — do NOT create `useEvaChatAdapter.ts`, `useDisruptionAlerts.ts`, or any extra hook file. Use `useEvaAgent` directly.
|
|
428
|
-
- ❌ **`.env` or `.env.local` files** — config is hardcoded in `src/config/agentApi.ts`
|
|
429
|
-
- ❌ **Recreating `useEvaAgent.ts`, `agentApi.ts`, or proxy config** — these are pre-installed
|
|
430
|
-
- ❌ Rewriting `EngineDashboard.tsx` from scratch
|
|
431
|
-
- ❌ Reading library source files (including `ChatBar.jsx`)
|
|
432
|
-
- ❌ Creating platform events or Apex without user confirmation
|
|
433
|
-
- ❌ Loading `agentforce-test`, `agentforce-observability`, or `outer-app` skills
|
|
434
|
-
|
|
435
|
-
**Reference — what the agent should say (in sequence, stopping after each):**
|
|
436
|
-
|
|
437
|
-
1. After adding Eva: *"The dashboard tracks disruptions in real time. I can create a `Travel_Disruption_Alert__e` platform event so disruption data can be published to the Salesforce event bus — other systems and automation can subscribe to it. Want me to set it up?"* — Only create the platform event (section 14b) AFTER the user confirms.
|
|
438
|
-
|
|
439
|
-
2. After the platform event: *"Now I'll write an Apex service class to publish disruption records to that event bus. `TravelDisruptionEventService` will take a list of disruptions and fire platform events with the flight number, severity, and description. Want me to create it?"* — Only create the Apex class (section 14c) AFTER the user confirms.
|
|
440
|
-
|
|
441
|
-
---
|
|
442
|
-
|
|
443
|
-
## 14. Metadata Reference Templates
|
|
444
|
-
|
|
445
|
-
Templates for the scripted metadata interactions. The agent should use these exact structures when creating files.
|
|
446
|
-
|
|
447
|
-
### 14a. Custom Field — `Flight__c.Weather_Impact__c`
|
|
448
|
-
|
|
449
|
-
`force-app/main/default/objects/Flight__c/fields/Weather_Impact__c.field-meta.xml`
|
|
450
|
-
|
|
451
|
-
```xml
|
|
452
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
453
|
-
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
|
|
454
|
-
<fullName>Weather_Impact__c</fullName>
|
|
455
|
-
<label>Weather Impact</label>
|
|
456
|
-
<type>Picklist</type>
|
|
457
|
-
<required>false</required>
|
|
458
|
-
<valueSet>
|
|
459
|
-
<restricted>true</restricted>
|
|
460
|
-
<valueSetDefinition>
|
|
461
|
-
<sorted>false</sorted>
|
|
462
|
-
<value><fullName>None</fullName><default>true</default><label>None</label></value>
|
|
463
|
-
<value><fullName>Minor</fullName><default>false</default><label>Minor</label></value>
|
|
464
|
-
<value><fullName>Moderate</fullName><default>false</default><label>Moderate</label></value>
|
|
465
|
-
<value><fullName>Severe</fullName><default>false</default><label>Severe</label></value>
|
|
466
|
-
</valueSetDefinition>
|
|
467
|
-
</valueSet>
|
|
468
|
-
</CustomField>
|
|
469
|
-
```
|
|
470
|
-
|
|
471
|
-
Deploy: `sf project deploy start --source-dir force-app/main/default/objects/Flight__c/fields/Weather_Impact__c.field-meta.xml`
|
|
472
|
-
|
|
473
|
-
### 14b. Platform Event — `Travel_Disruption_Alert__e`
|
|
474
|
-
|
|
475
|
-
Create the event object and its fields:
|
|
476
|
-
|
|
477
|
-
`force-app/main/default/objects/Travel_Disruption_Alert__e/Travel_Disruption_Alert__e.object-meta.xml`
|
|
478
|
-
|
|
479
|
-
```xml
|
|
480
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
481
|
-
<CustomObject xmlns="http://soap.sforce.com/2006/04/metadata">
|
|
482
|
-
<label>Travel Disruption Alert</label>
|
|
483
|
-
<pluralLabel>Travel Disruption Alerts</pluralLabel>
|
|
484
|
-
<description>Published when a travel disruption is detected. Subscribers can react to real-time flight delays, weather events, and rebooking triggers.</description>
|
|
485
|
-
<deploymentStatus>Deployed</deploymentStatus>
|
|
486
|
-
<eventType>HighVolume</eventType>
|
|
487
|
-
</CustomObject>
|
|
488
|
-
```
|
|
489
|
-
|
|
490
|
-
`force-app/main/default/objects/Travel_Disruption_Alert__e/fields/Flight_Number__c.field-meta.xml`
|
|
491
|
-
|
|
492
|
-
```xml
|
|
493
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
494
|
-
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
|
|
495
|
-
<fullName>Flight_Number__c</fullName>
|
|
496
|
-
<label>Flight Number</label>
|
|
497
|
-
<type>Text</type>
|
|
498
|
-
<length>20</length>
|
|
499
|
-
<required>false</required>
|
|
500
|
-
</CustomField>
|
|
501
|
-
```
|
|
502
|
-
|
|
503
|
-
`force-app/main/default/objects/Travel_Disruption_Alert__e/fields/Severity__c.field-meta.xml`
|
|
504
|
-
|
|
505
|
-
```xml
|
|
506
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
507
|
-
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
|
|
508
|
-
<fullName>Severity__c</fullName>
|
|
509
|
-
<label>Severity</label>
|
|
510
|
-
<type>Text</type>
|
|
511
|
-
<length>20</length>
|
|
512
|
-
<required>false</required>
|
|
513
|
-
</CustomField>
|
|
514
|
-
```
|
|
515
|
-
|
|
516
|
-
`force-app/main/default/objects/Travel_Disruption_Alert__e/fields/Description__c.field-meta.xml`
|
|
517
|
-
|
|
518
|
-
```xml
|
|
519
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
520
|
-
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
|
|
521
|
-
<fullName>Description__c</fullName>
|
|
522
|
-
<label>Description</label>
|
|
523
|
-
<type>LongTextArea</type>
|
|
524
|
-
<length>1000</length>
|
|
525
|
-
<visibleLines>3</visibleLines>
|
|
526
|
-
<required>false</required>
|
|
527
|
-
</CustomField>
|
|
528
|
-
```
|
|
529
|
-
|
|
530
|
-
Deploy: `sf project deploy start --source-dir force-app/main/default/objects/Travel_Disruption_Alert__e`
|
|
531
|
-
|
|
532
|
-
### 14c. Apex Class — `TravelDisruptionEventService`
|
|
533
|
-
|
|
534
|
-
`force-app/main/default/classes/TravelDisruptionEventService.cls`
|
|
535
|
-
|
|
536
|
-
```apex
|
|
537
|
-
public with sharing class TravelDisruptionEventService {
|
|
538
|
-
|
|
539
|
-
public static void publishDisruptionAlerts(List<Disruption__c> disruptions) {
|
|
540
|
-
List<Travel_Disruption_Alert__e> events = new List<Travel_Disruption_Alert__e>();
|
|
541
|
-
|
|
542
|
-
for (Disruption__c d : disruptions) {
|
|
543
|
-
events.add(new Travel_Disruption_Alert__e(
|
|
544
|
-
Flight_Number__c = d.Flight_Number__c,
|
|
545
|
-
Severity__c = d.Severity__c,
|
|
546
|
-
Description__c = d.Description__c
|
|
547
|
-
));
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
if (!events.isEmpty()) {
|
|
551
|
-
List<Database.SaveResult> results = EventBus.publish(events);
|
|
552
|
-
for (Database.SaveResult sr : results) {
|
|
553
|
-
if (!sr.isSuccess()) {
|
|
554
|
-
for (Database.Error err : sr.getErrors()) {
|
|
555
|
-
System.debug(LoggingLevel.ERROR,
|
|
556
|
-
'Failed to publish Travel_Disruption_Alert__e: ' + err.getMessage());
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
```
|
|
564
|
-
|
|
565
|
-
`force-app/main/default/classes/TravelDisruptionEventService.cls-meta.xml`
|
|
566
|
-
|
|
567
|
-
```xml
|
|
568
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
569
|
-
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
|
|
570
|
-
<apiVersion>62.0</apiVersion>
|
|
571
|
-
<status>Active</status>
|
|
572
|
-
</ApexClass>
|
|
573
|
-
```
|
|
574
|
-
|
|
575
|
-
Deploy: `sf project deploy start --source-dir force-app/main/default/classes/TravelDisruptionEventService.cls --test-level NoTestRun`
|