@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.
- package/.a4drules/features/command-center-dashboard-rule.md +20 -46
- package/.a4drules/features/engine-dashboard-rule.md +36 -288
- package/.a4drules/features/phase2-data-pattern.md +8 -159
- package/.a4drules/features/pre-code-checklist.md +16 -205
- package/.a4drules/phases/phase-1-layout.md +187 -0
- package/.a4drules/phases/phase-2-components.md +176 -0
- package/.a4drules/phases/phase-3-live-data.md +85 -0
- package/.a4drules/phases/phase-4-agent.md +73 -0
- package/.a4drules/skills/command-center-builder/SKILL.md +6 -30
- package/.a4drules/skills/command-center-builder/completion-checklist.md +3 -29
- package/.a4drules/skills/command-center-builder/improved-build-process.md +20 -331
- package/.a4drules/skills/command-center-builder/page-layout.md +4 -2
- package/.a4drules/skills/command-center-guide/SKILL.md +23 -257
- package/.a4drules/skills/command-center-project/SKILL.md +1 -1
- package/CHANGELOG.md +47 -0
- package/data/engine-command-center-prd.md +31 -33
- package/data/engine-sample-data.js +20 -8
- package/package.json +1 -1
|
@@ -1,215 +1,26 @@
|
|
|
1
|
-
|
|
2
|
-
paths:
|
|
3
|
-
- "**/pages/*.tsx"
|
|
4
|
-
- "**/components/pages/*.tsx"
|
|
5
|
-
---
|
|
1
|
+
# Pre-Code Checklist
|
|
6
2
|
|
|
7
|
-
|
|
3
|
+
This is a reference checklist. For build instructions, use the phase files in `.a4drules/phases/`.
|
|
8
4
|
|
|
9
|
-
Before
|
|
5
|
+
## Before creating a dashboard file
|
|
10
6
|
|
|
11
|
-
|
|
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
|
-
|
|
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
|
-
|
|
19
|
-
|
|
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
|
-
##
|
|
21
|
+
## After creating the dashboard
|
|
214
22
|
|
|
215
|
-
|
|
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
|