@schandlergarcia/sf-web-components 1.9.68 → 1.9.70

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.
@@ -45,7 +45,8 @@ The component library (`@/components/library`) provides pre-built, themed, dark-
45
45
  |------|----------|----------|
46
46
  | KPI / stat | `MetricCard` | `<div className="bg-white border ..."><h3>Total Spend</h3><span>$4,903</span></div>` |
47
47
  | List of items | `ListCard` | `<div className="bg-white ..."><div className="space-y-3">` with custom item rows |
48
- | Activity feed | `ActivityCard` or `FeedPanel` | `<div>` with hand-rolled interaction rows |
48
+ | Activity feed (display only) | `ActivityCard` or `FeedPanel` | `<div>` with hand-rolled interaction rows |
49
+ | **Activity feed (with per-item actions like "Assign to Agent")** | **Custom markup inside `BaseCard`** | **`ActivityCard` (doesn't support per-item buttons — see Engine PRD Section 8a)** |
49
50
  | Data table | `TableCard` | `<table>` or custom grid of rows |
50
51
  | Stats panel | `WidgetCard` or `SectionCard` | `<div className="bg-white ... p-6">` with label/value pairs |
51
52
  | Custom interactive content (expand/collapse, inline details) | `WidgetCard` (sections accept arbitrary JSX) or `ListCard` (with `onItemClick` + `itemActions`) | Hand-rolled `<div>` card with custom expand/collapse logic |
@@ -53,7 +54,7 @@ The component library (`@/components/library`) provides pre-built, themed, dark-
53
54
  | Text summary | `NarrativeSummary` / `SectionCard` | `<div>` with headings and paragraphs |
54
55
  | Alert / callout | `CalloutCard` | Custom banner div |
55
56
 
56
- Every visible section of a dashboard should be a library component. If you're writing `<div className="bg-white border rounded-[10px] shadow-sm p-6">` with content inside, that's a hand-rolled card — use a library component instead.
57
+ Every visible section of a dashboard should be a library component. If you're writing `<div className="bg-white border rounded-[10px] shadow-sm p-6">` with content inside, that's a hand-rolled card — use a library component instead. Exception: when a panel needs per-item action buttons (like "Assign to Agent"), use custom markup inside `BaseCard`.
57
58
 
58
59
  ### Correct example:
59
60
 
@@ -103,7 +104,7 @@ No `<nav>`, header bar, tab bar, or content-swapping via `useState("activeTab")`
103
104
 
104
105
  - Dashboard page files: `src/pages/` (e.g. `EngineDashboard.tsx`, `FleetDashboard.tsx`) — **NOT** `src/components/pages/`
105
106
  - File format: `.tsx` (MUST be TypeScript) — this is a TypeScript project, all React components use `.tsx`, NEVER `.jsx`.
106
- - `CommandCenter.tsx` wraps with `AppThemeProvider` + `DataModeProvider` + `Toaster`. **Never recreate these providers in dashboard pages.**
107
+ - `CommandCenter.tsx` wraps with `heroui-scope` div + `AppThemeProvider` + `DataModeProvider` + `Toast.Provider` from `@heroui/react`. **Never recreate these providers in dashboard pages.** When wiring a new dashboard, only change the import — preserve the `heroui-scope` wrapper and `Toast.Provider` exactly as-is. For toasts inside dashboards, use `toast` from `sonner` (not `addToast` from HeroUI).
107
108
 
108
109
  ### Wiring a new dashboard (REQUIRED STEPS)
109
110
 
@@ -124,22 +125,24 @@ export default function MyDashboard() {
124
125
  }
125
126
  ```
126
127
 
127
- **Step 2:** Update `CommandCenter.tsx` to import and render it:
128
+ **Step 2:** Update `CommandCenter.tsx` to import and render it. Only change the import line — preserve `heroui-scope` and `Toast.Provider`:
128
129
  ```tsx
129
130
  // src/components/workspace/CommandCenter.tsx
130
131
  import AppThemeProvider from "@/components/library/theme/AppThemeProvider";
131
132
  import DataModeProvider from "@/components/library/data/DataModeProvider";
132
- import { Toaster } from "sonner";
133
- import MyDashboard from "../../pages/MyDashboard"; // ← change this import
133
+ import { Toast } from "@heroui/react";
134
+ import MyDashboard from "../../pages/MyDashboard"; // ← change ONLY this import
134
135
 
135
136
  export default function CommandCenter() {
136
137
  return (
137
- <AppThemeProvider initialMode="light">
138
- <DataModeProvider initialMode="sample">
139
- <MyDashboard /> {/* ← change this */}
140
- <Toaster position="bottom-right" />
141
- </DataModeProvider>
142
- </AppThemeProvider>
138
+ <div className="heroui-scope"> {/* ← preserve this wrapper */}
139
+ <AppThemeProvider initialMode="light">
140
+ <DataModeProvider initialMode="sample">
141
+ <MyDashboard /> {/* ← change this */}
142
+ <Toast.Provider placement="bottom end" /> {/* ← preserve this */}
143
+ </DataModeProvider>
144
+ </AppThemeProvider>
145
+ </div>
143
146
  );
144
147
  }
145
148
  ```
@@ -224,7 +227,8 @@ Key rules:
224
227
  | System health | `StatusCard` |
225
228
  | Feed / item list | `ListCard` |
226
229
  | Scrollable side panel | `FeedPanel` |
227
- | Activity feed | `ActivityCard` |
230
+ | Activity feed (display only) | `ActivityCard` |
231
+ | Activity feed (per-item actions) | Custom markup in `BaseCard` |
228
232
  | User avatar | `Avatar` |
229
233
  | Text summary | `NarrativeSummary` (schema) / `SectionCard` |
230
234
  | Actions | `ActionList` (schema) / `UIButton` group |
@@ -341,8 +345,9 @@ Define schemas outside component body. Always `required: true` on mandatory fiel
341
345
 
342
346
  ## AI Chat & Agent
343
347
 
344
- Use `ChatBar` (command palette, ⌘K) for dashboards. `ChatPanel` for full-page chat.
348
+ Use `ChatBar` for dashboards, `ChatPanel` for full-page chat.
345
349
 
350
+ - Place `ChatBar` between the hero map and data panels (full-width, `px-4 pt-4`), NOT in the header
346
351
  - Always provide 3–5 `suggestions` for starter prompts
347
352
  - Return `components` for structured data — never markdown tables in text
348
353
  - Always set height on `ChatPanel`
@@ -466,7 +471,7 @@ Every data component handles loading. Error messages are human-readable. Empty s
466
471
 
467
472
  Before finishing a dashboard page, verify ALL of these:
468
473
 
469
- - [ ] **Every card/panel is a library component** (MetricCard, ListCard, ActivityCard, WidgetCard, TableCard, etc.) — no hand-rolled `<div className="bg-white border ...">` cards
474
+ - [ ] **Every card/panel is a library component** (MetricCard, ListCard, ActivityCard, WidgetCard, TableCard, etc.) — no hand-rolled `<div className="bg-white border ...">` cards. Exception: panels that need per-item action buttons (e.g. "Assign to Agent") can use custom markup inside `BaseCard`.
470
475
  - [ ] **Every chart/visualization is a library component** (ChartCard + D3Chart, GeoMap) — no hand-rolled SVG/canvas
471
476
  - [ ] **No `<nav>`, header bar, or sticky tab bar** — navigation is handled by appLayout.tsx
472
477
  - [ ] **Single scrollable page** — no multi-tab layout that swaps content
@@ -498,7 +503,8 @@ Instead, verify correctness by reviewing the code against the Pre-Completion Che
498
503
  - Using `template` prop on D3Chart — use `renderChart={D3ChartTemplates.lineChart}`
499
504
  - Passing D3Chart as children to ChartCard — use `chart={<D3Chart ... />}` prop
500
505
  - Adding `<nav>` or tab-swapping (`useState("activeTab")`) inside dashboards
501
- - Recreating providers — `CommandCenter.tsx` already provides them
506
+ - Recreating providers — `CommandCenter.tsx` already provides them. Do not replace `Toast.Provider` with `Toaster` from sonner, and do not remove the `heroui-scope` div
507
+ - Using `addToast` from `@heroui/react` — use `toast` / `toast.success` from `sonner` instead
502
508
  - Using `text-black`, `text-white`, `bg-black` — use slate scale
503
509
  - Hardcoded data without `useDataSource` — always support sample/live switching
504
510
  - `position: fixed` without `createPortal`
package/CHANGELOG.md CHANGED
@@ -5,7 +5,25 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
- ## [1.9.68] - 2026-04-01
8
+ ## [1.9.70] - 2026-04-02
9
+
10
+ ### Updated
11
+ - **PRD (engine-command-center-prd.md)** - 5 edits for phasing clarity and toast API:
12
+ - Section 4 (Architecture): Added explicit rules to preserve `heroui-scope` wrapper and `Toast.Provider` in CommandCenter. Added `toast` from `sonner` as the correct toast API
13
+ - Section 6 (Header): Added "(Phase 4)" tag to "Powered by Agentforce" badge
14
+ - Section 8a (Escalations): Added "(Phase 4)" tag to "Assign to Agent" button, noted `toast.success` comes via `sonner`, added instruction to render button with empty `onClick` in Phase 2 and wire toast in Phase 4
15
+ - Section 10 (Signals): Changed `toast()` and `toast.success()` to include full call examples with "via sonner" annotation
16
+ - Section 13, Phase 3 prompt: Added file paths for queries and hook, code example showing how to wire `useEngineLiveData()` into `useDataSource({ sample: ..., live: live.xxx })`, clarified hook should match sample data shape
17
+
18
+ - **Builder skill (SKILL.md)** - 3 edits for toast API and wiring:
19
+ - Wiring section: Changed description from Toaster to `heroui-scope` + `Toast.Provider` from `@heroui/react`. Added rule to use `toast` from `sonner` for dashboard toasts
20
+ - Wiring example (Step 2): Replaced sonner Toaster code with actual pattern: `heroui-scope` div, `Toast.Provider` from `@heroui/react`, with comments marking "preserve this" vs "change this"
21
+ - Component table: Bolded "per-item actions" row and added "see Engine PRD Section 8a" cross-reference
22
+ - Anti-patterns: Added two entries: (1) don't replace `Toast.Provider` with sonner's `Toaster` or remove `heroui-scope`, (2) don't use `addToast` from HeroUI — use `toast` from `sonner`
23
+
24
+ **Context:** Clarified phasing tags for Phase 4 features (badge, agent buttons) and documented correct toast API pattern (HeroUI provider + sonner toast function).
25
+
26
+ ## [1.9.69] - 2026-04-01
9
27
 
10
28
  ### Fixed
11
29
  - **6 data bugs in engine-sample-data.js**:
@@ -17,39 +35,50 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
17
35
  6. Updated TXL → BER throughout (Berlin airport code change)
18
36
 
19
37
  ### Updated
20
- - **PRD (engine-command-center-prd.md)** - Major rewrite (481 290 lines, -39%):
38
+ - **Global styles** - Added `src/styles/global.css` with Engine brand tokens and command center styling
39
+
40
+ - **PRD (engine-command-center-prd.md)** - Updated to match actual built dashboard:
41
+ - Layout diagram now shows 4-section structure: Header → Hero Map → Eva ChatBar → Data Panels (was missing ChatBar section)
42
+ - Layout classes added Eva: `<div className="px-4 pt-4">` as distinct section
43
+ - Section 6 (Header): Explicitly says "No ChatBar in the header — Eva lives below the map." Added "Powered by Agentforce" badge description
44
+ - Section 7 (Hero Map): Added explicit KPI label styling (`text-[10px] font-medium uppercase tracking-wide text-slate-300`) and value/pill/icon specs
45
+ - Section 8a (Escalations): Replaced ActivityCard with custom inline panel in BaseCard. Added full code example with status icons (CheckCircleIcon/ArrowPathIcon/ClockIcon), conditional "Assign to Agent" buttons, and toast.success
46
+ - Section 8c (Active Travelers): Added "View in Salesforce" link in expanded detail
47
+ - Section 9 (Eva): Completely rewritten - ChatBar is now between map and data panels, full-width, with phasing notes (Phase 1 = skip, Phase 4 = add)
48
+ - Section 10 (Signals): Added "Assigned to Eva" toast row for Assign to Agent button
49
+ - Phase 4 build prompt: Updated to say "Add Eva below the map as a ChatBar" and mention "Assign to Agent" buttons on open escalations
21
50
  - Applied Option A layout: Escalations + Disruptions in Row 1 (above fold), Active Travelers + Spend in Row 2
22
- - Both rows now use `lg:grid-cols-3` with left panel at 2/3 width
51
+ - Both rows use `lg:grid-cols-3` with left panel at 2/3 width
23
52
  - Removed all "TDX26" and "demo" references
24
- - Cut Section 14 "What NOT to Do" entirely (covered by builder rules)
25
- - Cut all "Why" explanations and duplicate wrong/right examples
26
- - Condensed Phase 1 placeholder rules from 75 lines to 8
27
- - Sharpened every code example to be copy-ready
28
- - Fixed "Sarah Chen" → "Priya Patel" in section 8c escalation example
29
- - Removed "Required skills:" line from header
30
-
31
- - **Builder skill sub-files** - Replaced with single-line redirects (100% duplicates):
32
- - `skills/command-center-builder/getting-started.md` - 125 1 line
33
- - `skills/command-center-builder/page-layout.md` - 1451 line
34
- - `skills/command-center-builder/components-styling.md` - 98 → 1 line
35
- - `skills/command-center-builder/charts-visualization.md` - 136 1 line
36
- - `skills/command-center-builder/completion-checklist.md` - 62 1 line
37
- - `skills/command-center-builder/data-forms-ai.md` - 44 → 1 line
38
- - `skills/command-center-builder/improved-build-process.md` - 20 → 1 line
39
-
40
- - **Builder skill main file** - Trimmed (629 → 505 lines, -20%):
41
- - `skills/command-center-builder/SKILL.md` - Removed wrong-example code blocks, condensed navigation section from 40 to 2 lines, trimmed map layout section, removed custom renderChart examples, cut anti-patterns list from 33 to 12 items
53
+ - Cut Section 14 "What NOT to Do" entirely
54
+ - Condensed Phase 1 placeholder rules from 75 to 8 lines
55
+
56
+ - **Builder skill (SKILL.md)** - Fixed 4 contradictions:
57
+ - Component table: Split "Activity feed" into two rows: "display only" ActivityCard, "with per-item actions" custom markup in BaseCard
58
+ - Component selection table: Same split applied
59
+ - AI Chat & Agent section: Added bullet: "Place ChatBar between the hero map and data panels, NOT in the header"
60
+ - Pre-Completion Checklist: Added exception: panels needing per-item action buttons can use custom markup inside BaseCard
61
+ - General library component rule: Added exception note for per-item action buttons
62
+ - Removed wrong-example code blocks, condensed navigation (40 → 2 lines), removed custom renderChart examples, cut anti-patterns (3312 items)
63
+
64
+ - **Builder skill sub-files** - Replaced with single-line redirects:
65
+ - 7 files reduced from 630 total lines to 7 lines (100% duplicates eliminated)
42
66
 
43
67
  - **Simplified .a4drules documentation** - Removed redundancy, contradictions, and AI coaching language:
44
- - `features/engine-dashboard-rule.md` - Rewritten as "Project Conventions" (reduced from 310 to ~65 lines)
68
+ - `features/engine-dashboard-rule.md` - Rewritten as "Project Conventions" (~65 lines)
45
69
  - `features/command-center-dashboard-rule.md` - Rewritten as slim conventions (~30 lines)
46
- - `features/pre-code-checklist.md` - Rewritten as "Quick Reference" (~25 lines, removed auto-load)
70
+ - `features/pre-code-checklist.md` - Rewritten as "Quick Reference" (~25 lines)
47
71
  - `features/phase2-data-pattern.md` - Rewritten as "Sample Data" documentation (~20 lines)
48
- - `skills/command-center-project/SKILL.md` - Fixed file path contradiction (src/components/pages/ → src/pages/). Description now includes "Engine Travel Command Center", tech stack keywords
49
- - `skills/command-center-guide/SKILL.md` - Reduced from 270 to ~45 lines (simplified routing guide). Description improved for better matching
50
- - `skills/component-library/SKILL.md` - Description now lists all key component names (MetricCard, ChartCard, GeoMap, etc.) for better matching
72
+ - `skills/command-center-project/SKILL.md` - Fixed file path contradiction
73
+ - `skills/command-center-guide/SKILL.md` - Reduced from 270 to ~45 lines
74
+ - `skills/component-library/SKILL.md` - Description now lists all key component names
75
+
76
+ **Context:** PRD now accurately reflects the actual built dashboard with Eva ChatBar between map and data panels. Eliminated contradictions about ActivityCard vs custom markup for action buttons.
51
77
 
52
- **Context:** Major documentation consolidation. PRD is now 39% shorter with copy-ready code. Builder skill sub-files eliminated duplication by redirecting to SKILL.md. Overall .a4drules reduced from ~2000 to ~800 lines while improving clarity.
78
+ ## [1.9.68] - 2026-04-01
79
+
80
+ ### Updated
81
+ - **Major documentation consolidation** - PRD reduced 39%, builder skill sub-files eliminated duplication, overall .a4drules reduced from ~2000 to ~800 lines
53
82
 
54
83
  ## [1.9.67] - 2026-04-01
55
84
 
@@ -18,7 +18,7 @@ The Engine Travel Command Center is a real-time operations dashboard for corpora
18
18
  Manages Acme's travel program: bookings, policy enforcement, exceptions, traveler monitoring.
19
19
 
20
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`.
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
22
 
23
23
  ---
24
24
 
@@ -50,7 +50,8 @@ Use `brand-*` Tailwind classes for brand elements. Tailwind built-in colors (gre
50
50
  ## 4. Architecture
51
51
 
52
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.
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 `sonner` 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.
54
55
  - **Imports:** Components from `@/components/library`. Theme from `@/components/library/theme/AppThemeProvider`.
55
56
 
56
57
  ---
@@ -62,7 +63,7 @@ Visualization-hero layout. Full-page scroll (no `h-screen`).
62
63
  ```
63
64
  ┌──────────────────────────────────────────────────────────────┐
64
65
  │ HEADER (h-12, sticky, bg-slate-900) │
65
- │ Engine logo + "Travel Command Center" | actions + toggle
66
+ │ Engine logo + "Travel Command Center" + badge | actions
66
67
  ├──────────────────────────────────────────────────────────────┤
67
68
  │ HERO MAP (h-[520px], GeoMap, relative) │
68
69
  │ ┌─ glass KPI overlays (absolute top-left) ───────────────┐ │
@@ -72,11 +73,13 @@ Visualization-hero layout. Full-page scroll (no `h-screen`).
72
73
  │ │ BA 286 · In Air | NH 109 · In Air | LH 431 · Delayed │ │
73
74
  │ └────────────────────────────────────────────────────────┘ │
74
75
  ├──────────────────────────────────────────────────────────────┤
76
+ │ EVA CHATBAR (px-4 pt-4, full-width command palette strip) │
77
+ ├──────────────────────────────────────────────────────────────┤
75
78
  │ DATA PANELS (px-4 py-5 space-y-6) │
76
79
  │ │
77
80
  │ Row 1: grid grid-cols-1 items-start gap-4 lg:grid-cols-3 │
78
81
  │ ┌── 2/3 (lg:col-span-2) ──────┐ ┌── 1/3 ───────────────┐ │
79
- │ │ Escalations (ActivityCard) │ │ Disruptions (custom) │ │
82
+ │ │ Escalations (custom panel) │ │ Disruptions (custom) │ │
80
83
  │ └───────────────────────────────┘ └──────────────────────┘ │
81
84
  │ │
82
85
  │ Row 2: grid grid-cols-1 items-start gap-4 lg:grid-cols-3 │
@@ -90,13 +93,14 @@ Visualization-hero layout. Full-page scroll (no `h-screen`).
90
93
 
91
94
  - **Outer:** `<div className="flex flex-col bg-slate-50 dark:bg-slate-950">`
92
95
  - **Map:** `<div className="relative h-[520px]">`
96
+ - **Eva:** `<div className="px-4 pt-4"><ChatBar ... /></div>`
93
97
  - **Panels:** `<div className="px-4 py-5"><div className="space-y-6">...</div></div>`
94
98
  - **Row 1:** `grid grid-cols-1 items-start gap-4 lg:grid-cols-3` — Escalations `lg:col-span-2`, Disruptions auto
95
99
  - **Row 2:** `grid grid-cols-1 items-start gap-4 lg:grid-cols-3` — Travelers `lg:col-span-2`, Spend auto
96
100
 
97
101
  ### Phase 1 placeholders
98
102
 
99
- Phase 1 builds the header, map, glass overlays, and flight strip as real components. Only the 4 data panels below the map are placeholders:
103
+ Phase 1 builds the header, map, glass overlays, and flight strip as real components. Only the ChatBar and 4 data panels below the map are placeholders:
100
104
 
101
105
  ```tsx
102
106
  <BaseCard>
@@ -112,11 +116,11 @@ The GeoMap renders the base world map with no data props. Glass overlays and fli
112
116
 
113
117
  ## 6. Header
114
118
 
115
- Compact `h-12` sticky header (`bg-slate-900 dark:bg-slate-950`).
119
+ Compact `h-12` sticky header (`bg-slate-900 dark:bg-slate-950`). No ChatBar in the header — Eva lives below the map.
116
120
 
117
121
  | Left | Right |
118
122
  |------|-------|
119
- | Engine logo (`GlobeAltIcon` in `bg-brand-600` square) + "ENGINE" wordmark + separator + "Travel Command Center" | ChatBar (Phase 4) + notification bell (badge count) + theme toggle (sun/moon) |
123
+ | Engine logo (`GlobeAltIcon` in `bg-brand-600` square) + "ENGINE" wordmark + separator + "Travel Command Center" + "Powered by Agentforce" badge (Phase 4: `bg-brand-900/40 text-brand-400 rounded-full text-[10px]`) | Notification bell (badge count) + theme toggle (sun/moon) |
120
124
 
121
125
  ---
122
126
 
@@ -145,9 +149,13 @@ Compact `h-12` sticky header (`bg-slate-900 dark:bg-slate-950`).
145
149
 
146
150
  **Glass KPI overlays** — 4 pills, `absolute left-3 top-3`:
147
151
  Active Travelers (count), Spend MTD (`fmtK()`), Compliance %, Eva Resolved (count + "resolved").
148
- Style: `bg-black/40 backdrop-blur-md border border-white/10 rounded-lg px-3 py-1.5`
149
152
 
150
- **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, contextual=Boarding). Same glass styling.
153
+ Label styling: `text-[10px] font-medium uppercase tracking-wide text-slate-300`
154
+ Value styling: `text-sm font-semibold text-slate-50`
155
+ Pill styling: `bg-black/40 backdrop-blur-md border border-white/10 rounded-lg px-3 py-1.5`
156
+ Icon: `h-4 w-4 text-brand-400`
157
+
158
+ **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.
151
159
 
152
160
  ---
153
161
 
@@ -157,18 +165,48 @@ Style: `bg-black/40 backdrop-blur-md border border-white/10 rounded-lg px-3 py-1
157
165
 
158
166
  ### 8a. Escalations (Row 1, 2/3 width)
159
167
 
160
- ActivityCard wrapped in BaseCard:
168
+ Custom inline panel in BaseCard. Do NOT use ActivityCard — this panel has per-item "Assign to Agent" buttons.
169
+
170
+ Each escalation row:
171
+ - Status icon: `CheckCircleIcon` (complete, green), `ArrowPathIcon` (working, brand), `ClockIcon` (pending, amber)
172
+ - Title + subtitle + timestamp
173
+ - **"Assign to Agent" button** (Phase 4) on items where status is NOT `"complete"`: teal button (`bg-brand-600`) with `SparklesIcon` + "Assign to Agent" text. On click: `toast.success("Assigned to Eva")` via sonner. In Phase 2, render the button but leave the `onClick` empty — wire the toast in Phase 4.
161
174
 
162
175
  ```tsx
163
176
  <BaseCard>
164
- <ActivityCard
165
- title="Escalations"
166
- actions={useDataSource({ sample: ESCALATION_CARDS, live: [] })}
167
- />
177
+ <div className="p-4">
178
+ <h3 className="mb-3 text-sm font-semibold text-slate-900 dark:text-slate-50">
179
+ Escalations
180
+ </h3>
181
+ <div className="divide-y divide-slate-100 dark:divide-slate-800">
182
+ {escalations.map((e) => {
183
+ const isOpen = e.status !== "complete";
184
+ return (
185
+ <div key={e.id} className="flex items-start gap-3 py-3">
186
+ <StatusIcon className={`mt-0.5 h-5 w-5 shrink-0 ${statusColor}`} />
187
+ <div className="min-w-0 flex-1">
188
+ <div className="text-sm font-medium text-slate-900 dark:text-slate-50">{e.title}</div>
189
+ <div className="mt-0.5 text-xs text-slate-500 dark:text-slate-400">
190
+ {e.subtitle} · {e.timestamp}
191
+ </div>
192
+ </div>
193
+ {isOpen && (
194
+ <button onClick={() => handleAssignToAgent(e)}
195
+ className="shrink-0 rounded-md bg-brand-600 px-2.5 py-1 text-xs font-medium text-slate-50 hover:bg-brand-700">
196
+ <span className="flex items-center gap-1">
197
+ <SparklesIcon className="h-3 w-3" /> Assign to Agent
198
+ </span>
199
+ </button>
200
+ )}
201
+ </div>
202
+ );
203
+ })}
204
+ </div>
205
+ </div>
168
206
  </BaseCard>
169
207
  ```
170
208
 
171
- Each escalation: title, subtitle, status (`"working"` | `"pending"` | `"complete"`), timestamp.
209
+ Data: `ESCALATION_CARDS` from sample data.
172
210
 
173
211
  ### 8b. Disruptions (Row 1, 1/3 width)
174
212
 
@@ -186,8 +224,8 @@ Data: `DISRUPTION_CARDS` from sample data.
186
224
 
187
225
  BaseCard with `maxBodyHeight={420}`. Expandable traveler cards.
188
226
 
189
- Each row: `Avatar` (initials) + name + department + origin → destination + `UIChip` (compliant/exception).
190
- Expand: 4-col grid — Flight, Hotel, Return, Destination.
227
+ Each row: `Avatar` (initials) + name + department + origin → destination + policy chip (compliant=green, exception=amber).
228
+ Expand: 4-col grid — Flight, Hotel, Return, Destination + "View in Salesforce" link.
191
229
 
192
230
  Data: `TRAVELER_CARDS` from sample data.
193
231
 
@@ -223,23 +261,28 @@ const spendChartData = useDataSource({
223
261
 
224
262
  ---
225
263
 
226
- ## 9. Eva — ChatBar (Phase 4)
264
+ ## 9. Eva — ChatBar
227
265
 
228
- ChatBar in the header. Not a FAB or sliding panel.
266
+ Eva renders as a full-width `ChatBar` below the hero map, between the map and the data panels. NOT in the header. NOT a FAB or sliding panel.
229
267
 
230
268
  ```tsx
231
- <ChatBar
232
- title="Eva"
233
- placeholder="Ask about travelers, bookings, policy, spend…"
234
- suggestions={CHAT_SUGGESTIONS}
235
- onSend={handleChat}
236
- />
269
+ <div className="px-4 pt-4">
270
+ <ChatBar
271
+ title="Eva"
272
+ placeholder="Ask Eva anything about travelers, bookings, policy, or spend…"
273
+ suggestions={CHAT_SUGGESTIONS}
274
+ onSend={handleChat}
275
+ />
276
+ </div>
237
277
  ```
238
278
 
239
279
  Suggestions: "Who's traveling internationally right now?", "Show bookings pending approval", "What has Eva resolved today?", "Travelers returning this week"
240
280
 
241
281
  Define `handleChat` and `CHAT_SUGGESTIONS` at module scope.
242
282
 
283
+ **Phase 1:** Placeholder — do NOT add ChatBar. Just leave the space empty.
284
+ **Phase 4:** Add the ChatBar between the map and data panels.
285
+
243
286
  ---
244
287
 
245
288
  ## 10. Salesforce Signals (Phase 4)
@@ -248,9 +291,10 @@ Subtle indicators, not banners.
248
291
 
249
292
  | Signal | Location |
250
293
  |--------|----------|
251
- | "View in Salesforce" | Traveler expanded view, booking rows (micro-link, muted text) |
252
- | "Salesforce updated" | After rebook/approve actions (`toast()`) |
253
- | "Powered by Agentforce" | ChatBar subtitle area (small badge) |
294
+ | "Powered by Agentforce" | Header, next to "Travel Command Center" (small rounded badge) |
295
+ | "View in Salesforce" | Traveler expanded view (micro-link, muted text) |
296
+ | "Salesforce updated" | After rebook/approve actions — `toast("Opening in Salesforce…")` via sonner |
297
+ | "Assigned to Eva" | After "Assign to Agent" button click — `toast.success("Assigned to Eva")` via sonner |
254
298
 
255
299
  ---
256
300
 
@@ -285,8 +329,15 @@ Build in 4 incremental phases.
285
329
 
286
330
  ### Phase 3: Real Data Integration
287
331
 
288
- > Connect the dashboard to live Salesforce data using GraphQL. Read the sample data file to see which fields are needed, then build queries and hooks for each object. The UI should stay identical just swap the data source.
332
+ > Connect the dashboard to live Salesforce data using GraphQL. Read the sample data file to see which fields are needed, then build queries in `src/api/engine/queries.ts` and a hook in `src/hooks/useEngineLiveData.ts` for each object. The hook should return the same data shape as the sample data exports. Wire it into the dashboard by populating the `live` prop on each `useDataSource` call:
333
+ >
334
+ > ```tsx
335
+ > const live = useEngineLiveData();
336
+ > const markers = useDataSource({ sample: MAP_MARKERS, live: live.mapMarkers });
337
+ > ```
338
+ >
339
+ > The UI should stay identical — only the data source changes.
289
340
 
290
341
  ### Phase 4: Agentforce Integration
291
342
 
292
- > Add Eva to the header as a ChatBar. Add subtle Salesforce signals throughout — "View in Salesforce" links, success toasts, "Powered by Agentforce" badge.
343
+ > Add Eva below the map as a ChatBar. Add subtle Salesforce signals throughout — "Powered by Agentforce" badge in the header, "View in Salesforce" links in traveler details, success toasts, and "Assign to Agent" buttons on open escalations.
@@ -0,0 +1,238 @@
1
+ @import '@heroui/styles';
2
+
3
+ @layer base {
4
+ html,
5
+ body,
6
+ #root {
7
+ @apply min-h-screen;
8
+ }
9
+
10
+ body {
11
+ @apply antialiased bg-white;
12
+ }
13
+ }
14
+
15
+ @import 'tw-animate-css';
16
+ @import 'shadcn/tailwind.css';
17
+
18
+ @custom-variant dark (&:is(.dark *));
19
+
20
+ @source "../components/library";
21
+ @source "../components/pages";
22
+
23
+ @theme inline {
24
+ --color-background: var(--background);
25
+ --color-foreground: var(--foreground);
26
+ --color-card: var(--card);
27
+ --color-card-foreground: var(--card-foreground);
28
+ --color-popover: var(--popover);
29
+ --color-popover-foreground: var(--popover-foreground);
30
+ --color-primary: var(--primary);
31
+ --color-primary-foreground: var(--primary-foreground);
32
+ --color-secondary: var(--secondary);
33
+ --color-secondary-foreground: var(--secondary-foreground);
34
+ --color-muted: var(--muted);
35
+ --color-muted-foreground: var(--muted-foreground);
36
+ --color-accent: var(--accent);
37
+ --color-accent-foreground: var(--accent-foreground);
38
+ --color-destructive: var(--destructive);
39
+ --color-destructive-foreground: var(--destructive-foreground);
40
+ --color-border: var(--border);
41
+ --color-input: var(--input);
42
+ --color-ring: var(--ring);
43
+ --color-chart-1: var(--chart-1);
44
+ --color-chart-2: var(--chart-2);
45
+ --color-chart-3: var(--chart-3);
46
+ --color-chart-4: var(--chart-4);
47
+ --color-chart-5: var(--chart-5);
48
+ --radius-sm: calc(var(--radius) - 4px);
49
+ --radius-md: calc(var(--radius) - 2px);
50
+ --radius-lg: var(--radius);
51
+ --radius-xl: calc(var(--radius) + 4px);
52
+ --color-sidebar: var(--sidebar);
53
+ --color-sidebar-foreground: var(--sidebar-foreground);
54
+ --color-sidebar-primary: var(--sidebar-primary);
55
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
56
+ --color-sidebar-accent: var(--sidebar-accent);
57
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
58
+ --color-sidebar-border: var(--sidebar-border);
59
+ --color-sidebar-ring: var(--sidebar-ring);
60
+
61
+ /* Engine brand palette */
62
+ --color-engine-black: #111111;
63
+ --color-engine-teal: #5BC8C8;
64
+ --color-engine-green: #3DAB5F;
65
+ --color-engine-coral: #FF5722;
66
+ --color-engine-orange: #F59E0B;
67
+ --color-engine-savings: #16A34A;
68
+ --color-engine-bg: #F7F8FA;
69
+ --color-engine-border: #E5E7EB;
70
+ --color-engine-text: #111111;
71
+ --color-engine-muted: #6B7280;
72
+ --color-engine-label: #9CA3AF;
73
+
74
+ /* Engine teal brand palette */
75
+ --color-brand-50: #ECFDF9;
76
+ --color-brand-100: #D1FAF0;
77
+ --color-brand-200: #A7F3E1;
78
+ --color-brand-300: #6EE7C8;
79
+ --color-brand-400: #34D3AB;
80
+ --color-brand-500: #5BC8C8;
81
+ --color-brand-600: #0D9488;
82
+ --color-brand-700: #0F766E;
83
+ --color-brand-800: #115E59;
84
+ --color-brand-900: #134E4A;
85
+ --color-brand-950: #042F2E;
86
+
87
+ --font-sans: 'Inter', ui-sans-serif, system-ui, -apple-system, sans-serif;
88
+ --font-mono: 'JetBrains Mono', ui-monospace, monospace;
89
+
90
+ /* Engine border radius tokens */
91
+ --radius-pill: 9999px;
92
+ --radius-card: 10px;
93
+ }
94
+
95
+ :root {
96
+ --radius: 0.625rem;
97
+ --background: oklch(1 0 0);
98
+ --foreground: oklch(0.145 0 0);
99
+ --card: oklch(1 0 0);
100
+ --card-foreground: oklch(0.145 0 0);
101
+ --popover: oklch(1 0 0);
102
+ --popover-foreground: oklch(0.145 0 0);
103
+ --primary: oklch(0.205 0 0);
104
+ --primary-foreground: oklch(0.985 0 0);
105
+ --secondary: oklch(0.97 0 0);
106
+ --secondary-foreground: oklch(0.205 0 0);
107
+ --muted: oklch(0.97 0 0);
108
+ --muted-foreground: oklch(0.556 0 0);
109
+ --accent: oklch(0.97 0 0);
110
+ --accent-foreground: oklch(0.205 0 0);
111
+ --destructive: oklch(0.577 0.245 27.325);
112
+ --border: oklch(0.922 0 0);
113
+ --input: oklch(0.922 0 0);
114
+ --ring: oklch(0.708 0 0);
115
+ --chart-1: oklch(0.646 0.222 41.116);
116
+ --chart-2: oklch(0.6 0.118 184.704);
117
+ --chart-3: oklch(0.398 0.07 227.392);
118
+ --chart-4: oklch(0.828 0.189 84.429);
119
+ --chart-5: oklch(0.769 0.188 70.08);
120
+ --sidebar: oklch(0.985 0 0);
121
+ --sidebar-foreground: oklch(0.145 0 0);
122
+ --sidebar-primary: oklch(0.205 0 0);
123
+ --sidebar-primary-foreground: oklch(0.985 0 0);
124
+ --sidebar-accent: oklch(0.97 0 0);
125
+ --sidebar-accent-foreground: oklch(0.205 0 0);
126
+ --sidebar-border: oklch(0.922 0 0);
127
+ --sidebar-ring: oklch(0.708 0 0);
128
+ }
129
+
130
+ .dark {
131
+ --background: oklch(0.145 0 0);
132
+ --foreground: oklch(0.985 0 0);
133
+ --card: oklch(0.205 0 0);
134
+ --card-foreground: oklch(0.985 0 0);
135
+ --popover: oklch(0.205 0 0);
136
+ --popover-foreground: oklch(0.985 0 0);
137
+ --primary: oklch(0.922 0 0);
138
+ --primary-foreground: oklch(0.205 0 0);
139
+ --secondary: oklch(0.269 0 0);
140
+ --secondary-foreground: oklch(0.985 0 0);
141
+ --muted: oklch(0.269 0 0);
142
+ --muted-foreground: oklch(0.708 0 0);
143
+ --accent: oklch(0.269 0 0);
144
+ --accent-foreground: oklch(0.985 0 0);
145
+ --destructive: oklch(0.704 0.191 22.216);
146
+ --border: oklch(1 0 0 / 10%);
147
+ --input: oklch(1 0 0 / 15%);
148
+ --ring: oklch(0.556 0 0);
149
+ --chart-1: oklch(0.488 0.243 264.376);
150
+ --chart-2: oklch(0.696 0.17 162.48);
151
+ --chart-3: oklch(0.769 0.188 70.08);
152
+ --chart-4: oklch(0.627 0.265 303.9);
153
+ --chart-5: oklch(0.645 0.246 16.439);
154
+ --sidebar: oklch(0.205 0 0);
155
+ --sidebar-foreground: oklch(0.985 0 0);
156
+ --sidebar-primary: oklch(0.488 0.243 264.376);
157
+ --sidebar-primary-foreground: oklch(0.985 0 0);
158
+ --sidebar-accent: oklch(0.269 0 0);
159
+ --sidebar-accent-foreground: oklch(0.985 0 0);
160
+ --sidebar-border: oklch(1 0 0 / 10%);
161
+ --sidebar-ring: oklch(0.556 0 0);
162
+ }
163
+
164
+ @layer base {
165
+ * {
166
+ @apply border-border outline-ring/50;
167
+ }
168
+ body {
169
+ @apply bg-background text-foreground;
170
+ }
171
+ }
172
+
173
+ /*
174
+ * Restore HeroUI theme variables inside the Command Center scope.
175
+ * shadcn redefines --muted, --accent, --accent-foreground with different
176
+ * semantics (bg colors vs text colors). This scope restores HeroUI's values
177
+ * so HeroUI components render correctly.
178
+ */
179
+ .heroui-scope {
180
+ /* Engine HeroUI theme overrides */
181
+ --primary: #000000;
182
+ --primary-foreground: oklch(0.9911 0 0);
183
+ --secondary: #5BC8C8;
184
+ --secondary-foreground: oklch(0.2103 0.0059 285.89);
185
+ --success: #16A34A;
186
+ --success-foreground: oklch(0.9911 0 0);
187
+ --warning: #F59E0B;
188
+ --warning-foreground: oklch(0.2103 0.0059 285.89);
189
+ --danger: #FF5722;
190
+ --danger-foreground: oklch(0.9911 0 0);
191
+
192
+ --muted: oklch(0.5517 0.0138 285.94);
193
+ --accent: oklch(0.6204 0.195 253.83);
194
+ --accent-foreground: oklch(0.9911 0 0);
195
+ --background: oklch(0.9702 0 0);
196
+ --foreground: oklch(0.2103 0.0059 285.89);
197
+ --default: oklch(94% 0.001 286.375);
198
+ --default-foreground: oklch(0.2103 0.0059 285.89);
199
+ --border: oklch(90% 0.004 286.32);
200
+ --separator: oklch(92% 0.004 286.32);
201
+ --segment: oklch(100% 0 0);
202
+ --segment-foreground: oklch(0.2103 0.0059 285.89);
203
+ --surface: oklch(100% 0 0);
204
+ --surface-foreground: oklch(0.2103 0.0059 285.89);
205
+ --overlay: oklch(100% 0 0);
206
+ --overlay-foreground: oklch(0.2103 0.0059 285.89);
207
+ --focus: oklch(0.6204 0.195 253.83);
208
+ --link: oklch(0.2103 0.0059 285.89);
209
+ }
210
+
211
+ .dark .heroui-scope,
212
+ .heroui-scope.dark {
213
+ --muted: oklch(70.5% 0.015 286.067);
214
+ --background: oklch(12% 0.005 285.823);
215
+ --foreground: oklch(0.9911 0 0);
216
+ --default: oklch(27.4% 0.006 286.033);
217
+ --default-foreground: oklch(0.9911 0 0);
218
+ --border: oklch(28% 0.006 286.033);
219
+ --separator: oklch(25% 0.006 286.033);
220
+ --segment: oklch(0.3964 0.01 285.93);
221
+ --segment-foreground: oklch(0.9911 0 0);
222
+ --surface: oklch(0.2103 0.0059 285.89);
223
+ --surface-foreground: oklch(0.9911 0 0);
224
+ --overlay: oklch(0.2103 0.0059 285.89);
225
+ --overlay-foreground: oklch(0.9911 0 0);
226
+ --warning: oklch(0.8203 0.1388 76.34);
227
+ --warning-foreground: oklch(0.2103 0.0059 285.89);
228
+ --danger: oklch(0.594 0.1967 24.63);
229
+ --danger-foreground: oklch(0.9911 0 0);
230
+ --focus: oklch(0.6204 0.195 253.83);
231
+ --link: oklch(0.9911 0 0);
232
+ }
233
+
234
+ /* ChatBar overlay — add horizontal padding so it doesn't hit window edges */
235
+ body > .fixed.inset-x-0.rounded-2xl {
236
+ left: 1.5rem !important;
237
+ right: 1.5rem !important;
238
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@schandlergarcia/sf-web-components",
3
- "version": "1.9.68",
3
+ "version": "1.9.70",
4
4
  "description": "Reusable Salesforce web components library with Tailwind CSS v4 and shadcn/ui",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",