@schandlergarcia/sf-web-components 1.9.62 → 1.9.64

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,215 +1,26 @@
1
- ---
2
- paths:
3
- - "**/pages/*.tsx"
4
- - "**/components/pages/*.tsx"
5
- ---
1
+ # Pre-Code Checklist
6
2
 
7
- # Pre-Code Checklist (MANDATORY)
3
+ This is a reference checklist. For build instructions, use the phase files in `.a4drules/phases/`.
8
4
 
9
- Before writing any code for command center dashboards, verify these requirements from the PRD and skills.
5
+ ## Before creating a dashboard file
10
6
 
11
- ## FIRST: Read These Files
7
+ - [ ] File path: `src/pages/DashboardName.tsx` (NOT `src/components/pages/`)
8
+ - [ ] File extension: `.tsx` (NOT `.jsx`)
9
+ - [ ] Imports: `@/components/library` only (NOT `@/components/ui/`, NOT `lucide-react`)
10
+ - [ ] Theme hook: `import { useThemeMode } from "@/components/library/theme/AppThemeProvider"`
11
+ - [ ] Data hook: `import useDataSource from "@/components/library/data/useDataSource"` (default export)
12
12
 
13
- **Before writing a single line of code, read these files:**
14
- 1. `.a4drules/skills/component-library/SKILL.md` (lines 15-40) - Import Pattern section
15
- 2. `engine-command-center-prd.md` - Phase 1 Placeholder Rules section
16
- 3. This file - Phase 1 Code Template section
13
+ ## Color Reference
17
14
 
18
- **Verify you know:**
19
- - [ ] Exact import statement for `useThemeMode` hook
20
- - [ ] What "Phase 1 placeholder" means (data panels ONLY, not entire dashboard)
21
- - [ ] Which colors are forbidden (`text-white`, `text-black`, `bg-black` without opacity)
22
-
23
- ## File Creation Checklist
24
-
25
- When creating a new dashboard file, confirm:
26
-
27
- 1. **File path:** `src/pages/DashboardName.tsx` (NOT `src/components/pages/`)
28
- 2. **File extension:** `.tsx` (NOT `.jsx`) — this is a TypeScript project
29
- 3. **Imports:** `@/components/library` ONLY (NOT `@/components/ui/`, NOT `lucide-react`, NOT `recharts`)
30
- 4. **Import names:** Check SKILL.md for exact names — DO NOT guess or invent import names
31
- - Theme toggle: `useThemeMode` (NOT `useAppTheme`, NOT `useTheme`)
32
- - Data source: `import useDataSource from "..."` (default export, NOT named)
33
- 5. **Colors:** Slate scale ONLY (NOT `text-white`, NOT `text-black`, NOT `bg-black`)
34
-
35
- ## Phase 1 Code Template
36
-
37
- **START WITH THIS EXACT STRUCTURE:**
38
-
39
- ```tsx
40
- import { GeoMap, BaseCard } from "@/components/library";
41
- import { useThemeMode } from "@/components/library/theme/AppThemeProvider";
42
- import { /* Heroicons */ } from "@heroicons/react/24/outline";
43
-
44
- export default function EngineDashboard() {
45
- const { mode, toggle } = useThemeMode();
46
-
47
- return (
48
- <div className="flex flex-col bg-slate-50 dark:bg-slate-950">
49
- {/* Header Bar - h-12, sticky, slate-900 bg */}
50
- <header className="sticky top-0 z-50 flex h-12 items-center justify-between border-b border-slate-800 bg-slate-900 px-4">
51
- {/* Logo + title with text-slate-50 (NOT text-white) */}
52
- </header>
53
-
54
- {/* Hero Map - fixed height h-[520px], GeoMap renders world automatically */}
55
- <div className="relative h-[520px]">
56
- <GeoMap width={960} height={520} theme={mode === "dark" ? "dark" : "light"} className="h-full w-full" />
57
-
58
- {/* Glass KPI overlays - positioned absolutely, text-slate-50 */}
59
- <div className="absolute left-3 top-3 flex gap-2">
60
- {/* 4 KPI divs with border-white/10, bg-black/40, backdrop-blur-md */}
61
- </div>
62
-
63
- {/* Flight status strip - positioned absolutely at bottom */}
64
- <div className="absolute bottom-3 left-3 right-3 flex gap-2 overflow-x-auto">
65
- {/* 4 flight divs with status badges */}
66
- </div>
67
- </div>
68
-
69
- {/* Data Panels - natural flow, full-page scroll, 4 panels total */}
70
- <div className="px-4 py-5">
71
- <div className="space-y-6">
72
- {/* Row 1: Disruptions (1/3) + Active Travelers (2/3) */}
73
- <div className="grid grid-cols-1 items-start gap-4 lg:grid-cols-3">
74
- <BaseCard>
75
- <div className="flex h-48 items-center justify-center text-slate-400">
76
- Disruptions Panel
77
- </div>
78
- </BaseCard>
79
- <div className="lg:col-span-2">
80
- <BaseCard>
81
- <div className="flex h-48 items-center justify-center text-slate-400">
82
- Active Travelers Panel
83
- </div>
84
- </BaseCard>
85
- </div>
86
- </div>
87
-
88
- {/* Row 2: Escalations (1/2) + Monthly Spend Chart (1/2) */}
89
- <div className="grid grid-cols-1 items-start gap-4 lg:grid-cols-2">
90
- <BaseCard>
91
- <div className="flex h-48 items-center justify-center text-slate-400">
92
- Escalations Panel
93
- </div>
94
- </BaseCard>
95
- <BaseCard>
96
- <div className="flex h-48 items-center justify-center text-slate-400">
97
- Travel Spend Panel
98
- </div>
99
- </BaseCard>
100
- </div>
101
- </div>
102
- </div>
103
- </div>
104
- );
105
- }
106
- ```
107
-
108
- ## During Code Generation Checklist
109
-
110
- As you write the dashboard code:
111
-
112
- 1. **Every card** is a library component (MetricCard, ListCard, WidgetCard, etc.) — NO hand-rolled `<div className="bg-white border rounded...">`
113
- 2. **Every chart** uses `ChartCard` + `D3Chart` or `GeoMap` — NO Recharts, NO Chart.js, NO custom SVG
114
- 3. **Every color** uses semantic classes or slate scale — NO `text-white`, NO `bg-black`, NO hardcoded hex
115
- 4. **Dark mode** on every element — `dark:` variants on all custom styling
116
- 5. **Phase 1 scope - Build COMPLETE layout structure:**
117
- - ✅ Outer container: `className="flex flex-col bg-slate-50 dark:bg-slate-950"` (NO `h-screen` - full-page scroll)
118
- - ✅ Header bar (logo, title, actions) with slate scale colors (NO text-white)
119
- - ✅ Header text: ONLY "ENGINE" + "Travel Command Center" (NO "Powered by..." branding in Phase 1)
120
- - ✅ Hero map container: `className="relative h-[520px]"` (fixed height, NOT flex-based)
121
- - ✅ Hero map (GeoMap component with NO markers/arcs/overlays PROPS, `className="h-full w-full"`)
122
- - ✅ Glass KPI overlays (4 hardcoded KPIs, positioned absolutely over map)
123
- - ✅ Flight status strip (4 hardcoded flights, positioned at map bottom)
124
- - ✅ Data panels container: `className="px-4 py-5"` (NO `overflow-y-auto`, NO flex constraints - natural flow)
125
- - ✅ **4 data panel placeholders** (2 rows: Disruptions/Travelers, Escalations/Destinations) - BaseCard with NO title prop, centered text
126
- - ❌ **Phase 1 EXCLUDES:** Salesforce/Data Cloud branding (Phase 4), ChatBar (Phase 4), real data (Phase 2+)
127
- - "Placeholder" means data panels below the map, NOT the header/overlays/map
128
- - **Full-page scroll:** Entire dashboard scrolls naturally (map scrolls up as user scrolls down)
129
-
130
- ## After Code Generation Checklist
131
-
132
- After writing the dashboard code:
133
-
134
- 1. **Wiring:**
135
- - [ ] `CommandCenter.tsx` imports your dashboard
136
- - [ ] `Home.tsx` renders `CommandCenter`
137
- - [ ] `routes.tsx` has correct routes
138
-
139
- 2. **Completion:**
140
- - [ ] All files wired correctly
141
- - [ ] **DO NOT run `npm run dev` or `npm run validate:dashboard`** - not required for ANY phase completion
142
-
143
- ## Color Reference (CRITICAL)
144
-
145
- **NEVER use these:**
146
-
147
- | ❌ FORBIDDEN | ✅ USE INSTEAD | Where |
148
- |-------------|---------------|-------|
15
+ | Forbidden | Use instead | Where |
16
+ |-----------|------------|-------|
149
17
  | `text-white` | `text-slate-50` | Header text, overlay text |
150
18
  | `text-black` | `text-slate-900` | Body text (light mode) |
151
19
  | `bg-black` | `bg-slate-900` or `bg-black/40` (with opacity) | Backgrounds |
152
- | `bg-white` | `bg-slate-50` | Page background (light mode) |
153
-
154
- **Exception:** `bg-black/40`, `bg-white/10`, `border-white/10` are allowed (opacity variants for glass effect)
155
-
156
- ## Verification Checklist
157
-
158
- After building, you should see:
159
- - [ ] Header bar with Engine logo and "Travel Command Center" title
160
- - [ ] **World map visible** (blue/teal land on dark background OR gray land on light background)
161
- - [ ] 4 glass KPI boxes in top-left of map (semi-transparent with numbers)
162
- - [ ] 4 flight status boxes at bottom of map (with colored badges)
163
- - [ ] **4 white/dark cards below map** (2 rows: row 1 has 1/3 + 2/3 split, row 2 has 1/2 + 1/2 split)
164
- - [ ] Page scrolls when you scroll the data panels section
165
- - [ ] Theme toggle works (sun/moon icon in header)
166
-
167
- ## Quick Reference
168
-
169
- | Question | Answer |
170
- |----------|--------|
171
- | File path? | `src/pages/EngineDashboard.tsx` |
172
- | Extension? | `.tsx` (TypeScript) |
173
- | Import from? | `@/components/library` |
174
- | Hook import? | `import { useThemeMode } from "@/components/library/theme/AppThemeProvider"` |
175
- | Colors? | Slate scale (`text-slate-50`, `bg-slate-900`) - NEVER `text-white` |
176
- | Cards? | Library components (MetricCard, ListCard, etc.) |
177
- | Charts? | `ChartCard` + `D3Chart` or `GeoMap` |
178
- | After building? | Wire into CommandCenter/Home/routes - DO NOT run dev server |
179
-
180
- ## Common Mistakes to Avoid
181
-
182
- ❌ `src/pages/EngineDashboard.tsx` → ✅ `src/pages/EngineDashboard.tsx`
183
- ❌ `EngineDashboard.jsx` → ✅ `EngineDashboard.tsx`
184
- ❌ `text-white` → ✅ `text-slate-50`
185
- ❌ `bg-black` → ✅ `bg-slate-900`
186
- ❌ `<div className="bg-white border rounded-lg p-4">` → ✅ `<BaseCard>` or `<WidgetCard>`
187
- ❌ `import { Button } from '@/components/ui/button'` → ✅ `import { UIButton } from '@/components/library'`
188
- ❌ `import { useAppTheme } from '@/components/library'` → ✅ `import { useThemeMode } from '@/components/library'`
189
- ❌ `"Powered by Salesforce Data Cloud"` in Phase 1 → ✅ This is Phase 4 only (see PRD Section 10)
190
- ❌ Running `npm run dev` after building → ✅ Not needed - validation is sufficient
191
-
192
- ## Troubleshooting
193
-
194
- **If you get TypeScript error about imports:**
195
- 1. ❌ DO NOT follow "did you mean default import?" suggestion
196
- 2. ✅ READ `.a4drules/skills/component-library/SKILL.md` Import Pattern section
197
- 3. ✅ USE the exact import shown in the docs
198
-
199
- **If map doesn't render:**
200
- 1. ✅ Verify container has `style={{ flex: "1.15 1 0", minHeight: 0 }}`
201
- 2. ✅ Verify GeoMap has `className="h-full w-full"`
202
- 3. ✅ GeoMap shows world map automatically - no props needed
203
-
204
- **If colors look wrong:**
205
- 1. ✅ Search your code for `text-white` - should be `text-slate-50`
206
- 2. ✅ Search your code for `text-black` - should be `text-slate-900`
207
- 3. ✅ Exception: `bg-black/40` and `border-white/10` (with opacity) are OK
208
-
209
- **If data panels don't scroll:**
210
- 1. ✅ Verify data panels container has `overflow-y-auto` className
211
- 2. ✅ Verify it has `style={{ flex: "1 1 0" }}`
212
20
 
213
- ## Enforcement
21
+ ## After creating the dashboard
214
22
 
215
- This checklist is MANDATORY. Do not skip any step. Verify each item before moving to the next phase.
23
+ - [ ] `CommandCenter.tsx` imports your dashboard
24
+ - [ ] `Home.tsx` renders `CommandCenter`
25
+ - [ ] `routes.tsx` has correct routes
26
+ - [ ] DO NOT run `npm run dev` or `npm run validate:dashboard`
@@ -0,0 +1,187 @@
1
+ ---
2
+ name: engine-phase-1-layout
3
+ description: >-
4
+ Phase 1 of the Engine Travel Command Center build. Creates the complete layout
5
+ skeleton with header, hero map, glass overlays, flight strip, and placeholder
6
+ data panels. Use this when the build prompt says "Phase 1" or "scaffold".
7
+ ---
8
+
9
+ # Phase 1: Layout & Structure
10
+
11
+ **Source of truth:** `engine-command-center-prd.md` (sections 3–6)
12
+
13
+ **Goal:** Build the complete layout skeleton in one file. The hero map, glass KPI overlays, and flight status strip are real — only the 4 data panels below are placeholders.
14
+
15
+ ## File Setup
16
+
17
+ Create `src/pages/EngineDashboard.tsx` (.tsx, NOT .jsx).
18
+
19
+ ## Code Template
20
+
21
+ Copy this template, then fill in the `{/* TODO */}` sections using the PRD:
22
+
23
+ ```tsx
24
+ import { GeoMap, BaseCard } from "@/components/library";
25
+ import { useThemeMode } from "@/components/library/theme/AppThemeProvider";
26
+ import {
27
+ GlobeAltIcon,
28
+ SunIcon,
29
+ MoonIcon,
30
+ BellIcon,
31
+ UsersIcon,
32
+ BanknotesIcon,
33
+ ShieldCheckIcon,
34
+ SparklesIcon,
35
+ } from "@heroicons/react/24/outline";
36
+
37
+ export default function EngineDashboard() {
38
+ const { mode, toggle } = useThemeMode();
39
+
40
+ return (
41
+ <div className="flex flex-col bg-slate-50 dark:bg-slate-950">
42
+ {/* ── HEADER BAR ─────────────────────────────────────────── */}
43
+ <header className="sticky top-0 z-50 flex h-12 items-center justify-between border-b border-slate-800 bg-slate-900 dark:bg-slate-950 px-4">
44
+ {/* Left: logo + title */}
45
+ <div className="flex items-center gap-2">
46
+ <div className="flex h-7 w-7 items-center justify-center rounded-md bg-brand-600">
47
+ <GlobeAltIcon className="h-4 w-4 text-slate-50" />
48
+ </div>
49
+ <span className="text-sm font-bold tracking-wider text-slate-50">ENGINE</span>
50
+ <span className="mx-2 h-4 w-px bg-slate-700" />
51
+ <span className="text-sm font-medium text-slate-300">Travel Command Center</span>
52
+ </div>
53
+ {/* Right: actions */}
54
+ <div className="flex items-center gap-3">
55
+ {/* ChatBar goes here in Phase 4 */}
56
+ <button className="relative text-slate-400 hover:text-slate-200">
57
+ <BellIcon className="h-5 w-5" />
58
+ <span className="absolute -right-1 -top-1 flex h-4 w-4 items-center justify-center rounded-full bg-rose-500 text-[10px] font-bold text-slate-50">3</span>
59
+ </button>
60
+ <button onClick={toggle} className="text-slate-400 hover:text-slate-200">
61
+ {mode === "dark" ? <SunIcon className="h-5 w-5" /> : <MoonIcon className="h-5 w-5" />}
62
+ </button>
63
+ </div>
64
+ </header>
65
+
66
+ {/* ── HERO MAP ───────────────────────────────────────────── */}
67
+ <div className="relative h-[520px]">
68
+ <GeoMap
69
+ width={960}
70
+ height={520}
71
+ theme={mode === "dark" ? "dark" : "light"}
72
+ className="h-full w-full"
73
+ />
74
+
75
+ {/* Glass KPI overlays — 4 pills, absolute top-left */}
76
+ <div className="absolute left-3 top-3 flex flex-wrap gap-2">
77
+ {[
78
+ { icon: UsersIcon, label: "Active Travelers", value: "8" },
79
+ { icon: BanknotesIcon, label: "Spend MTD", value: "$128.4K" },
80
+ { icon: ShieldCheckIcon, label: "Compliance", value: "75%" },
81
+ { icon: SparklesIcon, label: "Eva Resolved", value: "6 resolved" },
82
+ ].map((kpi) => (
83
+ <div
84
+ key={kpi.label}
85
+ className="flex items-center gap-2 rounded-lg border border-white/10 bg-black/40 px-3 py-1.5 backdrop-blur-md"
86
+ >
87
+ <kpi.icon className="h-4 w-4 text-brand-400" />
88
+ <div>
89
+ <div className="text-[10px] uppercase tracking-wider text-slate-300">{kpi.label}</div>
90
+ <div className="text-sm font-semibold text-slate-50">{kpi.value}</div>
91
+ </div>
92
+ </div>
93
+ ))}
94
+ </div>
95
+
96
+ {/* Flight status strip — absolute bottom */}
97
+ <div className="absolute bottom-3 left-3 right-3 flex gap-2 overflow-x-auto">
98
+ {[
99
+ { flight: "BA 286", route: "SFO → LHR", status: "In Air" },
100
+ { flight: "NH 109", route: "JFK → NRT", status: "In Air" },
101
+ { flight: "LH 431", route: "ORD → BER", status: "Delayed" },
102
+ { flight: "SQ 37", route: "LAX → SIN", status: "Boarding" },
103
+ ].map((f) => (
104
+ <div
105
+ key={f.flight}
106
+ className="flex shrink-0 items-center gap-2 rounded-lg border border-white/10 bg-black/40 px-3 py-1.5 backdrop-blur-md"
107
+ >
108
+ <span className="text-sm font-medium text-slate-50">{f.flight}</span>
109
+ <span className="text-xs text-slate-400">{f.route}</span>
110
+ <span
111
+ className={`rounded-full px-2 py-0.5 text-[10px] font-semibold ${
112
+ f.status === "In Air" ? "bg-green-500/20 text-green-400" :
113
+ f.status === "Delayed" ? "bg-amber-500/20 text-amber-400" :
114
+ f.status === "Boarding" ? "bg-sky-500/20 text-sky-400" :
115
+ "bg-slate-500/20 text-slate-400"
116
+ }`}
117
+ >
118
+ {f.status}
119
+ </span>
120
+ </div>
121
+ ))}
122
+ </div>
123
+ </div>
124
+
125
+ {/* ── DATA PANELS (placeholders) ─────────────────────────── */}
126
+ <div className="px-4 py-5">
127
+ <div className="space-y-6">
128
+ {/* Row 1: Disruptions (1/3) + Active Travelers (2/3) */}
129
+ <div className="grid grid-cols-1 items-start gap-4 lg:grid-cols-3">
130
+ <BaseCard>
131
+ <div className="flex h-48 items-center justify-center text-slate-400">
132
+ Disruptions Panel
133
+ </div>
134
+ </BaseCard>
135
+ <div className="lg:col-span-2">
136
+ <BaseCard>
137
+ <div className="flex h-48 items-center justify-center text-slate-400">
138
+ Active Travelers Panel
139
+ </div>
140
+ </BaseCard>
141
+ </div>
142
+ </div>
143
+
144
+ {/* Row 2: Escalations (1/2) + Monthly Spend Chart (1/2) */}
145
+ <div className="grid grid-cols-1 items-start gap-4 lg:grid-cols-2">
146
+ <BaseCard>
147
+ <div className="flex h-48 items-center justify-center text-slate-400">
148
+ Escalations Panel
149
+ </div>
150
+ </BaseCard>
151
+ <BaseCard>
152
+ <div className="flex h-48 items-center justify-center text-slate-400">
153
+ Travel Spend Panel
154
+ </div>
155
+ </BaseCard>
156
+ </div>
157
+ </div>
158
+ </div>
159
+ </div>
160
+ );
161
+ }
162
+ ```
163
+
164
+ ## Brand Tokens
165
+
166
+ Update `src/styles/global.css` — replace the `--color-brand-*` palette inside the `@theme inline` block with the Engine teal palette from PRD section 3. Also add `--color-engine-savings: #16A34A`.
167
+
168
+ ## Wiring (all 3 required)
169
+
170
+ 1. **CommandCenter.tsx** — import `EngineDashboard` from `"../../pages/EngineDashboard"` and render it
171
+ 2. **Home.tsx** — import `CommandCenter` and render it
172
+ 3. **routes.tsx** — set `Home` as the index route, move `Search` to `/search`
173
+
174
+ ## Constraints (5 only)
175
+
176
+ 1. File MUST be `.tsx` in `src/pages/`
177
+ 2. Import ONLY from `@/components/library` and `@heroicons/react`
178
+ 3. Colors: slate scale only — NO `text-white`, `text-black`, `bg-black` (exception: `bg-black/40` with opacity is OK for glass)
179
+ 4. Full-page scroll — NO `h-screen` on outer container
180
+ 5. DO NOT run `npm run dev` or `npm run validate:dashboard` — phase is complete after wiring
181
+
182
+ ## Phase 1 does NOT include
183
+
184
+ - Sample data (Phase 2)
185
+ - ChatBar / Eva (Phase 4)
186
+ - Salesforce signals like "Powered by..." (Phase 4)
187
+ - Any `npm install` commands
@@ -0,0 +1,176 @@
1
+ ---
2
+ name: engine-phase-2-components
3
+ description: >-
4
+ Phase 2 of the Engine Travel Command Center build. Replaces placeholder panels
5
+ with library components wired to pre-existing sample data. Adds map data.
6
+ Use this when the build prompt says "Phase 2" or "components".
7
+ ---
8
+
9
+ # Phase 2: Components & Sample Data
10
+
11
+ **Source of truth:** `engine-command-center-prd.md` (sections 7–8)
12
+
13
+ **Goal:** Replace the 4 placeholder data panels with real library components. Add map data (markers, arcs, overlays). The sample data file already exists — just import from it.
14
+
15
+ ## Critical: Data file already exists
16
+
17
+ `src/data/engine-sample-data.js` is pre-built with ~370 lines of sample data. **DO NOT create a new data file.** Just import from the existing one.
18
+
19
+ ## Imports to add to EngineDashboard.tsx
20
+
21
+ ```tsx
22
+ import {
23
+ GeoMap,
24
+ BaseCard,
25
+ ActivityCard,
26
+ ChartCard,
27
+ D3Chart,
28
+ D3ChartTemplates,
29
+ Avatar,
30
+ UIChip,
31
+ } from "@/components/library";
32
+ import useDataSource from "@/components/library/data/useDataSource";
33
+ import {
34
+ MAP_MARKERS,
35
+ MAP_ARCS,
36
+ MAP_OVERLAYS,
37
+ FLIGHT_STATUS_LIST,
38
+ TRAVELER_CARDS,
39
+ DISRUPTION_CARDS,
40
+ ESCALATION_CARDS,
41
+ MONTHLY_SPEND,
42
+ METRICS,
43
+ } from "@/data/engine-sample-data";
44
+ ```
45
+
46
+ ## Changes to make
47
+
48
+ ### 1. Hero Map — add data props
49
+
50
+ ```tsx
51
+ <GeoMap
52
+ width={960}
53
+ height={520}
54
+ theme={mode === "dark" ? "dark" : "light"}
55
+ markers={useDataSource({ sample: MAP_MARKERS, live: [] })}
56
+ arcs={useDataSource({ sample: MAP_ARCS, live: [] })}
57
+ overlays={useDataSource({ sample: MAP_OVERLAYS, live: [] })}
58
+ zoomable
59
+ className="h-full w-full"
60
+ />
61
+ ```
62
+
63
+ ### 2. Glass KPI overlays — use METRICS
64
+
65
+ Replace hardcoded KPI values with `METRICS.activeTravelers`, `METRICS.spendMTD`, `METRICS.complianceRate`, `METRICS.evaResolved`.
66
+
67
+ Use `fmtK()` helper for spend formatting:
68
+
69
+ ```tsx
70
+ function fmtK(n: number) {
71
+ if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
72
+ if (n >= 1e3) return `${(n / 1e3).toFixed(1)}K`;
73
+ return n.toLocaleString();
74
+ }
75
+ ```
76
+
77
+ ### 3. Flight status strip — use FLIGHT_STATUS_LIST
78
+
79
+ Replace hardcoded flights with `FLIGHT_STATUS_LIST.map(...)`.
80
+
81
+ ### 4. Disruptions Panel (1/3 width) — custom inline component
82
+
83
+ Replace the placeholder BaseCard. Build a custom panel inside a BaseCard showing only disrupted flights. Each disruption card shows severity dot, flight number, route, traveler, reason, and Eva action.
84
+
85
+ ```tsx
86
+ <BaseCard>
87
+ <div className="space-y-3 p-1">
88
+ <h3 className="text-sm font-semibold text-slate-900 dark:text-slate-50">Disruptions</h3>
89
+ {useDataSource({ sample: DISRUPTION_CARDS, live: [] }).map((d) => (
90
+ <div key={d.id} className="rounded-lg border border-slate-200 p-3 dark:border-slate-800">
91
+ <div className="flex items-center gap-2">
92
+ <span className={`h-2 w-2 rounded-full ${d.severity === "grounded" ? "bg-rose-500" : "bg-amber-500"}`} />
93
+ <span className="text-sm font-medium text-slate-900 dark:text-slate-50">{d.flight}</span>
94
+ <span className={`text-xs ${d.severity === "grounded" ? "text-rose-500" : "text-amber-500"}`}>
95
+ {d.severity} · {d.delayMin}min
96
+ </span>
97
+ </div>
98
+ <div className="mt-1 text-xs text-slate-500 dark:text-slate-400">{d.route} · {d.traveler}</div>
99
+ <div className="mt-1 text-xs text-slate-500 dark:text-slate-400">{d.reason}</div>
100
+ <div className="mt-2 flex items-center gap-1 text-xs text-brand-600 dark:text-brand-400">
101
+ <SparklesIcon className="h-3 w-3" />
102
+ <span>{d.evaAction}</span>
103
+ </div>
104
+ </div>
105
+ ))}
106
+ </div>
107
+ </BaseCard>
108
+ ```
109
+
110
+ ### 5. Active Travelers (2/3 width) — expandable cards
111
+
112
+ Replace the placeholder. Use BaseCard with `maxBodyHeight={420}` for internal scroll. Build expandable traveler rows using `useState` for the expanded ID.
113
+
114
+ Each row: Avatar (initials) + name + department + origin → destination + UIChip for policy status. Click to expand shows 4-col detail grid (Flight, Hotel, Return, Destination).
115
+
116
+ ### 6. Escalations (1/2 width) — ActivityCard in BaseCard
117
+
118
+ **Copy this exactly from the PRD:**
119
+
120
+ ```tsx
121
+ <BaseCard>
122
+ <ActivityCard
123
+ title="Escalations"
124
+ actions={useDataSource({ sample: ESCALATION_CARDS, live: [] })}
125
+ />
126
+ </BaseCard>
127
+ ```
128
+
129
+ ActivityCard MUST be wrapped in BaseCard for visible card styling.
130
+
131
+ ### 7. Monthly Spend Trend (1/2 width) — ChartCard + D3Chart
132
+
133
+ **Copy this exactly from the PRD:**
134
+
135
+ ```tsx
136
+ const spendChartData = useDataSource({
137
+ sample: MONTHLY_SPEND.map(m => ({ x: m.month, spend: m.amount })),
138
+ live: [],
139
+ });
140
+
141
+ <ChartCard
142
+ title="Travel Spend"
143
+ subtitle="Monthly trend (6 months)"
144
+ chart={
145
+ <D3Chart
146
+ data={spendChartData}
147
+ renderChart={D3ChartTemplates.groupedBarChart}
148
+ options={{
149
+ xKey: "x",
150
+ groups: ["spend"],
151
+ colors: ["#5BC8C8"],
152
+ barRadius: 6,
153
+ margin: { top: 20, right: 20, bottom: 40, left: 60 },
154
+ showGrid: true,
155
+ }}
156
+ responsive
157
+ height={320}
158
+ />
159
+ }
160
+ />
161
+ ```
162
+
163
+ ## Constraints (5 only)
164
+
165
+ 1. **DO NOT create a new data file** — import from the existing `src/data/engine-sample-data.js`
166
+ 2. Import ONLY from `@/components/library` and `@heroicons/react` — no shadcn, no Lucide, no Recharts
167
+ 3. Wrap `ActivityCard` in `BaseCard` (ActivityCard has no card styling by default)
168
+ 4. Use `useDataSource({ sample: X, live: [] })` for all data — enables Phase 3 swap
169
+ 5. DO NOT run `npm run dev` or `npm run validate:dashboard`
170
+
171
+ ## Phase 2 does NOT include
172
+
173
+ - Live Salesforce data (Phase 3)
174
+ - ChatBar / Eva (Phase 4)
175
+ - Salesforce signals (Phase 4)
176
+ - Any `npm install` commands
@@ -0,0 +1,85 @@
1
+ ---
2
+ name: engine-phase-3-live-data
3
+ description: >-
4
+ Phase 3 of the Engine Travel Command Center build. Connects the dashboard to
5
+ live Salesforce Data Cloud via GraphQL. UI stays identical to Phase 2.
6
+ Use this when the build prompt says "Phase 3" or "real data".
7
+ ---
8
+
9
+ # Phase 3: Real Data Integration
10
+
11
+ **Source of truth:** `engine-command-center-prd.md` (section 13)
12
+
13
+ **Goal:** Connect the dashboard to live Salesforce data via GraphQL. The UI stays identical to Phase 2 — only the data source changes.
14
+
15
+ ## Approach
16
+
17
+ The sample data file (`src/data/engine-sample-data.js`) already uses real Salesforce field names (Trip__c, Flight__c, Contact, etc.). Read the file header to understand the schema.
18
+
19
+ ## Custom objects
20
+
21
+ | Object | Purpose |
22
+ |--------|---------|
23
+ | Contact | Travelers (with Home_Airport__c, Travel_Policy_Tier__c, etc.) |
24
+ | Trip__c | Origin, destination, dates, cost, policy status |
25
+ | Flight__c | Flight number, airports, status, delay, traveler lookup |
26
+ | Booking__c | Upcoming reservations |
27
+ | Disruption__c | Delays, cancellations, severity |
28
+ | Rebooking_Action__c | Eva activity log |
29
+ | Travel_Policy__c | Policy rules and status |
30
+
31
+ ## Implementation steps
32
+
33
+ ### Step 1: Read the sample data file
34
+
35
+ Read `src/data/engine-sample-data.js` to understand which Salesforce fields are needed for each object.
36
+
37
+ ### Step 2: Use GraphQL skills to create queries and hooks
38
+
39
+ For each object, use the Salesforce GraphQL skills:
40
+
41
+ 1. Use the `exploring-webapp-graphql-schema` skill to look up the object in `schema.graphql`
42
+ 2. Use the `generating-webapp-graphql-read-query` skill to create a typed query
43
+ 3. Use the `using-webapp-graphql` skill to create a data hook (e.g., `useTravelers`, `useFlights`)
44
+
45
+ Each hook returns `{ data, loading, error }`.
46
+
47
+ ### Step 3: Update useDataSource calls
48
+
49
+ Change each `useDataSource` call to include live data:
50
+
51
+ ```tsx
52
+ // Phase 2 (sample only)
53
+ const travelers = useDataSource({ sample: TRAVELER_CARDS, live: [] });
54
+
55
+ // Phase 3 (live with sample fallback)
56
+ const { data: liveTravelers, loading, error } = useTravelers();
57
+ const travelers = useDataSource({
58
+ sample: TRAVELER_CARDS,
59
+ live: liveTravelers ?? [],
60
+ });
61
+ ```
62
+
63
+ The `useDataSource` hook automatically uses `live` when available, falling back to `sample` on error.
64
+
65
+ ### Step 4: Add loading and error states
66
+
67
+ Add `loading` and `error` handling to each section. Library card components accept `loading` and `error` props.
68
+
69
+ ## Data strategy
70
+
71
+ The app uses a sample data cache (`ENABLE_SAMPLE_DATA_CACHE = true` in `src/lib/dataStrategy.ts`) for instant loading and offline capability. This is production-appropriate — build all GraphQL queries and hooks as normal.
72
+
73
+ ## Constraints (5 only)
74
+
75
+ 1. **DO NOT modify UI components** — only change data sources
76
+ 2. **DO NOT add DataModeToggle** — live data only (sample is fallback for errors)
77
+ 3. Use exact Salesforce field names from the sample data file
78
+ 4. Schema file (`schema.graphql`) is pre-built in the repo — no generation needed
79
+ 5. DO NOT run `npm run dev` or `npm run validate:dashboard`
80
+
81
+ ## Phase 3 does NOT include
82
+
83
+ - ChatBar / Eva (Phase 4)
84
+ - Salesforce signals (Phase 4)
85
+ - UI changes of any kind