@schandlergarcia/sf-web-components 1.9.66 → 1.9.68

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,19 +1,21 @@
1
1
  ---
2
2
  name: command-center-builder
3
3
  description: >-
4
- Strict rules for building command center pages, dashboards, components, and
5
- layouts. Use when creating or editing dashboard pages, choosing components,
6
- designing layouts, styling with dark mode, or working with charts, tables,
7
- forms, filters, chat, or navigation in this project.
4
+ Conventions and patterns for building the Engine Travel Command Center
5
+ dashboard and other command center pages. Covers layout patterns
6
+ (visualization-hero, grid), component selection (MetricCard, ChartCard,
7
+ GeoMap, ListCard, ActivityCard), dark mode, brand colors, styling, and
8
+ wiring dashboards into the app. Use when scaffolding, building, or editing
9
+ any dashboard page in this project.
8
10
  ---
9
11
 
10
12
  # Command Center Builder Rules
11
13
 
12
14
  These rules apply when building dashboards rendered by `CommandCenter.tsx`. For full component API details, read the **component-library** skill.
13
15
 
14
- ## STOP — READ BEFORE WRITING ANY CODE
16
+ ## Core Conventions
15
17
 
16
- **Every time you build a dashboard, you MUST follow these non-negotiable rules. Violations are the #1 cause of rejected dashboards.**
18
+ The following conventions apply to all dashboard development in this project.
17
19
 
18
20
  Before writing any code, read `.a4drules/features/pre-code-checklist.md` and verify all requirements. The most common mistakes are:
19
21
  - Creating `.jsx` instead of `.tsx` files
@@ -21,7 +23,7 @@ Before writing any code, read `.a4drules/features/pre-code-checklist.md` and ver
21
23
  - Using forbidden colors (`text-white`, `bg-black`)
22
24
  - Hand-rolling HTML cards instead of using library components
23
25
 
24
- 1. **Actually write files to disk.** Use the file-writing tools to create `.tsx` files (NOT `.jsx`) in `src/pages/` (NOT `src/components/pages/`) and update `CommandCenter.tsx` to import your dashboard. This is a TypeScript project - all React components MUST use `.tsx` extension. If you only describe what you would build without creating the files, the dashboard will not render. Verify the files exist after writing them.
26
+ 1. **Write `.tsx` files** in `src/pages/` and update `CommandCenter.tsx` to import the dashboard. This is a TypeScript project all React components use `.tsx` extension.
25
27
 
26
28
  2. **Use ONLY library components** from `@/components/library` for all cards, charts, tables, lists, and feeds. The library has 30+ components — there is no reason to hand-roll HTML. See the component table below.
27
29
 
@@ -33,11 +35,9 @@ Before writing any code, read `.a4drules/features/pre-code-checklist.md` and ver
33
35
 
34
36
  6. **Never `npm install` Salesforce packages.** The `@salesforce/*` packages are platform-provided via broken symlinks. They are stubbed in `src/stubs/` and aliased in `vite.config.ts`. Do not try to install them — they don't exist on npm. If you see a TypeScript error about `@salesforce/vite-plugin-webapp-experimental`, ignore it — the build still succeeds via `vite build` (the `tsc` error is in `vite.config.ts` which is handled separately by `tsconfig.node.json`).
35
37
 
36
- If your output violates any of these 6 rules, it will be rejected and you will need to redo the work.
38
+ ## Use Library Components Not Hand-Rolled HTML Cards
37
39
 
38
- ## CRITICAL: Use Library Components Never Hand-Roll HTML Cards
39
-
40
- **This is the #1 mistake.** The component library (`@/components/library`) provides pre-built, themed, dark-mode-ready cards for every common dashboard need. You MUST use them instead of writing raw `<div>` cards with custom classes.
40
+ The component library (`@/components/library`) provides pre-built, themed, dark-mode-ready cards for every common dashboard need. Use them instead of writing raw `<div>` cards with custom classes.
41
41
 
42
42
  ### Before writing ANY card-like UI, check this table:
43
43
 
@@ -53,7 +53,7 @@ If your output violates any of these 6 rules, it will be rejected and you will n
53
53
  | Text summary | `NarrativeSummary` / `SectionCard` | `<div>` with headings and paragraphs |
54
54
  | Alert / callout | `CalloutCard` | Custom banner div |
55
55
 
56
- **Every visible section of a dashboard should be a library component.** If you find yourself writing `<div className="bg-white border rounded-[10px] shadow-sm p-6">` with content inside, STOP you are hand-rolling a card. Find the library component that fits.
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
57
 
58
58
  ### Correct example:
59
59
 
@@ -81,27 +81,9 @@ import { MetricCard, ListCard, ActivityCard, WidgetCard } from "@/components/lib
81
81
  </WidgetCard>
82
82
  ```
83
83
 
84
- ### Wrong example (hand-rolled HTML):
85
-
86
- ```jsx
87
- // ❌ NEVER DO THIS — this is a hand-rolled card
88
- <div className="bg-white border border-engine-border rounded-[10px] shadow-sm">
89
- <div className="p-6 border-b border-engine-border">
90
- <h2 className="text-lg font-semibold">Trip Activity</h2>
91
- </div>
92
- <div className="p-6 space-y-4">
93
- {trips.map(trip => (
94
- <div key={trip.id} className="flex gap-4 p-4 rounded-lg border ...">
95
- ...
96
- </div>
97
- ))}
98
- </div>
99
- </div>
100
- ```
101
-
102
- ## Images (MANDATORY)
84
+ ## Images
103
85
 
104
- The only image allowed in dashboards is the **company logo**: `src/assets/images/engine_logo.png`. Import it as a module and use it where a logo is needed (nav header, branding, etc.).
86
+ The company logo is at `src/assets/images/engine_logo.png`. Import it as a module and use it where a logo is needed (nav header, branding, etc.).
105
87
 
106
88
  ```jsx
107
89
  import engineLogo from "@/assets/images/engine_logo.png";
@@ -113,36 +95,9 @@ import engineLogo from "@/assets/images/engine_logo.png";
113
95
  - **Do not use other asset images** (codey-*.png, etc.) unless the user asks for them.
114
96
  - **Preserve aspect ratio** — always use `w-auto` with a fixed height, or `h-auto` with a fixed width. Never set both `w-*` and `h-*` to fixed values on the logo or any image, as this distorts the aspect ratio.
115
97
 
116
- ```jsx
117
- // ✅ Correct — aspect ratio preserved
118
- <img src={engineLogo} alt="Engine" className="h-8 w-auto" />
119
- <img src={engineLogo} alt="Engine" className="w-24 h-auto" />
120
-
121
- // ❌ Wrong — aspect ratio distorted
122
- <img src={engineLogo} alt="Engine" className="w-8 h-8" />
123
- <div className="w-8 h-8 bg-black rounded" /> // placeholder box instead of logo
124
- ```
125
-
126
- ## No Dashboard-Level Navigation (MANDATORY)
127
-
128
- Dashboard pages must NOT include their own `<nav>`, header bar, or top navigation of any kind. Navigation is handled by `appLayout.tsx`. The dashboard renders inside the app shell — adding a nav inside the dashboard creates a duplicate header, which is the most visually obvious mistake.
129
-
130
- - ❌ `<nav className="bg-white border-b ...">` inside a dashboard page
131
- - ❌ A header bar with logo + nav links + user avatar inside the dashboard
132
- - ❌ Tab bars that act as top-level navigation (Overview, Travelers, Spend, etc.)
133
- - ❌ `useState("overview")` / `useState("travelers")` to toggle between views — this IS tab navigation even without a `<nav>` element
134
- - ❌ `{activeView === "overview" && (...)}` / `{activeView === "travelers" && (...)}` — content-swapping IS multi-tab navigation
135
- - ✅ Dashboard starts directly with content (metrics, then primary content)
136
- - ✅ If you need sub-sections, use the library `Tabs` component **inside a card** — not as a full-width page-level switcher
137
- - ✅ Build a single-page dashboard — all content visible by scrolling. Do NOT split into multiple tab "pages" that swap content.
138
-
139
- **Self-check:** If your dashboard has `useState` for an "active tab" or "active view" that conditionally renders different page sections, you are building multi-tab navigation. Delete it and put all content on one scrollable page.
140
-
141
- **Self-check:** If your dashboard JSX starts with `<nav>`, a sticky tab bar, or a `<div>` that spans the full width with nav links, you are violating this rule. Delete it.
142
-
143
- ### Why no multi-tab dashboards?
98
+ ## No Dashboard-Level Navigation
144
99
 
145
- Splitting a dashboard into 5 tab "pages" (Overview, Travelers, Spend, Policy, Eva) means each tab is a separate mini-app. This violates the single-page dashboard pattern. Instead, put **all sections on one scrollable page** using the vertical page structure (metrics → primary content secondary content). If content is too long, prioritize the most important sections and use cards with `maxBodyHeight` to constrain tall sections.
100
+ No `<nav>`, header bar, tab bar, or content-swapping via `useState("activeTab")` inside dashboard pages. The app shell handles navigation. Dashboards are single scrollable pages all content visible by scrolling.
146
101
 
147
102
  ## Where Dashboards Live & How to Wire Them
148
103
 
@@ -152,7 +107,7 @@ Splitting a dashboard into 5 tab "pages" (Overview, Travelers, Spend, Policy, Ev
152
107
 
153
108
  ### Wiring a new dashboard (REQUIRED STEPS)
154
109
 
155
- You must do ALL of these or the dashboard will not render correctly:
110
+ All three steps are required for the dashboard to render:
156
111
 
157
112
  **Step 1:** Create the dashboard file in `src/pages/` (NOT `src/components/pages/`):
158
113
  ```tsx
@@ -248,46 +203,14 @@ Only these grid patterns — do not invent new ones:
248
203
 
249
204
  **Max 4 KPIs per row — NEVER 5 or more.** If you have 5+ metrics, split into two rows (e.g. 4 + 1, or 3 + 2). Always `items-start` on grids pairing different-height cards. Use `maxBodyHeight={px}` for long card bodies. Actions go in card `actions` slot. Place `FilterBar` near the data it controls.
250
205
 
251
- ## Layout Differentiation
252
-
253
- Every dashboard needs a unique layout structure and at least one domain-specific "signature element" that couldn't exist in another dashboard. Vary section ordering, grid proportions, density, and visual rhythm.
254
-
255
206
  ## Map Layout
256
207
 
257
- **Default pattern:** GeoMap in a **wide/narrow grid** (`lg:grid-cols-3` with `lg:col-span-2`). This works well for dashboards where the map is one of several equal-weight sections.
258
-
259
- **If the PRD specifies a "visualization-hero" layout**, the map is full-width hero with glass overlays. The PRD always overrides this default pattern.
260
-
261
- **Pattern: Map + sidebar at matched height**
262
-
263
- ```jsx
264
- <div className="grid grid-cols-1 gap-4 lg:grid-cols-3">
265
- <div className="lg:col-span-2 relative overflow-hidden rounded-xl h-[300px]">
266
- <GeoMap
267
- markers={locations}
268
- arcs={arcs}
269
- overlays={overlays}
270
- initialBounds={{ sw: [-130, 24], ne: [-65, 50], padding: 30 }}
271
- theme="dark"
272
- width={960}
273
- height={480}
274
- zoomable
275
- className="h-full w-full"
276
- />
277
- </div>
278
- {/* Sidebar card — use maxBodyHeight to match map height */}
279
- {/* Map h-[300px] ≈ ListCard maxBodyHeight={236} + ~64px header */}
280
- <ListCard title="Activity" items={items} maxBodyHeight={236} showStatus />
281
- </div>
282
- ```
208
+ **Default:** GeoMap in a wide/narrow grid (`lg:grid-cols-3` with `lg:col-span-2`). **If the PRD specifies "visualization-hero"**, the map is full-width hero PRD overrides this default.
283
209
 
284
210
  Key rules:
285
- - **Do NOT wrap GeoMap in ChartCard** — GeoMap has its own background/borders
286
- - **Match heights** — set map `h-[Xpx]` and sidebar `maxBodyHeight` so they align. Account for ~64px card header padding.
287
- - **Use `initialBounds`** to auto-zoom to the region with data: `{ sw: [lonMin, latMin], ne: [lonMax, latMax], padding: 30 }`
288
- - **Always pass `arcs`** for flight routes and `overlays` for disruption zones — not just markers
289
- - **Use ListCard** (not ActivityCard) for the sidebar — ListCard supports `maxBodyHeight` for scroll, ActivityCard does not
290
- - Keep map height at 280–320px — not 400+
211
+ - Do NOT wrap GeoMap in ChartCard — GeoMap has its own styling
212
+ - Always pass `arcs` and `overlays`, not just markers
213
+ - Use `initialBounds` to auto-zoom: `{ sw: [lonMin, latMin], ne: [lonMax, latMax], padding: 30 }`
291
214
 
292
215
  ## Component Selection
293
216
 
@@ -346,7 +269,7 @@ Defined in `global.css` (`--color-brand-*` and `--color-engine-*`), available as
346
269
 
347
270
  Canonical: `"operational"`, `"degraded"`, `"outage"`, `"maintenance"`. Always show color + text label.
348
271
 
349
- ## Dark Mode (MANDATORY)
272
+ ## Dark Mode
350
273
 
351
274
  Every visible element needs both light and dark styles. Library components handle this automatically — which is another reason to use them.
352
275
 
@@ -429,9 +352,9 @@ Use `ChatBar` (command palette, ⌘K) for dashboards. `ChatPanel` for full-page
429
352
 
430
353
  `searchable` when 10+ rows (unless `FilterBar` handles search). `sortable` on comparable columns. `paginated` when 10+ rows. Use built-in column `type` before custom `render`.
431
354
 
432
- ## Charts & Visualizations (MANDATORY)
355
+ ## Charts & Visualizations
433
356
 
434
- **All charts, graphs, maps, and visualizations MUST use library components.** Never use Recharts, Chart.js, Nivo, or any third-party charting library. Never hand-roll SVG, canvas, or custom chart markup. The ONLY charting tools allowed are `D3Chart` (via `D3ChartTemplates` or a custom `renderChart` function) and `GeoMap`.
357
+ All charts, graphs, maps, and visualizations use the library's charting components. Never use Recharts, Chart.js, Nivo, or any third-party charting library. Never hand-roll SVG, canvas, or custom chart markup. The ONLY charting tools allowed are `D3Chart` (via `D3ChartTemplates` or a custom `renderChart` function) and `GeoMap`.
435
358
 
436
359
  | Visualization | MUST use | NEVER do |
437
360
  |--------------|----------|----------|
@@ -496,14 +419,6 @@ const monthlyData = [
496
419
  }
497
420
  />
498
421
 
499
- // ✅ Custom renderChart function (for donut, horizontal bar, etc.)
500
- function renderDonut(svgEl, data, dims, opts) {
501
- // Use d3 to draw into svgEl
502
- const d3svg = d3.select(svgEl);
503
- d3svg.selectAll("*").remove();
504
- // ... d3 drawing code
505
- }
506
- <ChartCard title="By Department" chart={<D3Chart data={deptData} renderChart={renderDonut} responsive height={280} />} />
507
422
  ```
508
423
 
509
424
  ### Available D3ChartTemplates
@@ -515,27 +430,9 @@ function renderDonut(svgEl, data, dims, opts) {
515
430
 
516
431
  ### GeoMap API
517
432
 
518
- ```jsx
519
- import { GeoMap } from "@/components/library";
520
-
521
- // markers: array of { id, lon, lat, active?, label? }
522
- const markers = [
523
- { id: "sf", lon: -122.4, lat: 37.8, active: true, label: "San Francisco" },
524
- { id: "nyc", lon: -74.0, lat: 40.7, active: true, label: "New York" },
525
- ];
526
-
527
- <ChartCard title="Active Locations" chart={
528
- <GeoMap
529
- markers={markers}
530
- theme="dark"
531
- height={400}
532
- width={800}
533
- zoomable
534
- />
535
- } />
536
- ```
433
+ Props: `width`, `height`, `projection` (`"naturalEarth"` | `"mercator"` | `"equirectangular"`), `theme` (`"dark"` | `"light"`), `markers`, `arcs`, `overlays`, `zoomable`, `minZoom`, `maxZoom`, `onMarkerClick`.
537
434
 
538
- GeoMap props: `width`, `height`, `projection` (`"naturalEarth"` | `"mercator"` | `"equirectangular"`), `theme` (`"dark"` | `"light"`), `markers`, `arcs`, `overlays`, `zoomable`, `minZoom`, `maxZoom`, `onMarkerClick`.
435
+ Marker shape: `{ id, lon, lat, active?, label? }`. Arc shape: `{ id, from: [lon, lat], to: [lon, lat], progress?, danger? }`. Overlay shape: `{ id, center: [lon, lat], radius }`.
539
436
 
540
437
  ### Common mistakes
541
438
 
@@ -592,37 +489,17 @@ Before finishing a dashboard page, verify ALL of these:
592
489
 
593
490
  Instead, verify correctness by reviewing the code against the Pre-Completion Checklist above. Confirm that all three wiring files are updated (CommandCenter.tsx, Home.tsx, routes.tsx).
594
491
 
595
- ## Anti-Patterns (never do these)
492
+ ## Anti-Patterns
596
493
 
597
- - Hand-rolling HTML cards instead of using library components (MetricCard, ListCard, etc.)
598
- - Using Recharts, Chart.js, Nivo, or any chart library other than D3Chart/GeoMap
599
- - Hand-rolling SVG/canvas charts instead of using ChartCard + D3Chart or GeoMap
600
- - Adding a `<nav>`, header bar, sticky tab bar, or top navigation inside a dashboard page
601
- - Multi-tab layouts that swap content (Overview/Travelers/Spend/etc tabs) build one scrollable page
602
- - More than 4 KPIs in a single row (max is `lg:grid-cols-4`)
603
- - Using `template` prop on D3Chart (doesn't exist) — use `renderChart={D3ChartTemplates.lineChart}`
494
+ - Importing from `src/components/ui/` (shadcn) or `lucide-react` use `@/components/library` and `@heroicons/react`
495
+ - Using `cn()` helper use Tailwind classes directly
496
+ - Hand-rolling HTML cards (`<div className="bg-white border ...">`) use library components
497
+ - Using Recharts, Chart.js, or any third-party charts use `D3Chart` + `D3ChartTemplates` or `GeoMap`
498
+ - Using `template` prop on D3Chartuse `renderChart={D3ChartTemplates.lineChart}`
604
499
  - Passing D3Chart as children to ChartCard — use `chart={<D3Chart ... />}` prop
605
- - CSS Modules, styled-components, or inline `style={{}}` for layout
606
- - `<style>` tags with keyframes or custom CSS inside components
607
- - Inline `<svg>` elements when a Heroicon exists
608
- - Importing shadcn components (`src/components/ui/`) into command center pages `Button`, `Card`, `Input`, `Select`, `Alert`, `Skeleton`, `Label`, `Spinner`, etc. are ALL forbidden. Use the library equivalents from `@/components/library` (`UIButton`, `BaseCard`/`WidgetCard`, `UIInput`, `Select`, `Alert`, `Skeleton`, etc.)
609
- - Importing Lucide icons (`lucide-react`) into command center pages — use Heroicons (`@heroicons/react`) instead
610
- - Using `cn()` helper in command center pages (that's shadcn's utility — use Tailwind classes directly)
611
- - `useEffect` for data in schema components
612
- - `bg-indigo-*` or raw colors for brand — use `brand-*` or `engine-*`
613
- - Recreating providers (`AppThemeProvider`, `DataModeProvider`) in dashboard pages — `CommandCenter.tsx` already provides them
614
- - Tables without search on 10+ rows, charts without tooltips, empty states without messages
615
- - Hard-coded pixel widths or skipping dark mode
616
- - `text-black`, `text-white`, `bg-black` — use slate scale
617
- - `lg:grid-cols-6` for metrics (max 4), manually appending K/M/B
618
- - `mb-6` between sections instead of `space-y-6` wrapper
619
- - Same layout across dashboards, no signature element
620
- - `ChatPanel` in dashboard grid — use `ChatBar`
500
+ - Adding `<nav>` or tab-swapping (`useState("activeTab")`) inside dashboards
501
+ - Recreating providers `CommandCenter.tsx` already provides them
502
+ - Using `text-black`, `text-white`, `bg-black` use slate scale
503
+ - Hardcoded data without `useDataSource`always support sample/live switching
621
504
  - `position: fixed` without `createPortal`
622
- - `Math.random()` in sample data, different layouts based on data mode
623
- - Re-declaring filter/form definitions inside component render
624
- - Dismissing validator errors as "acceptable" or "justified" instead of fixing them — every error has a library component fix
625
- - Hardcoded data without `useDataSource` for sample/live mode switching
626
- - External image URLs (Unsplash, placeholders) unless user explicitly requests them
627
- - Placeholder boxes (`<div className="w-8 h-8 bg-black rounded" />`) instead of the actual logo
628
- - Fixed width AND height on images — always let one dimension be `auto` to preserve aspect ratio
505
+ - `Math.random()` in sample data
@@ -1,136 +1 @@
1
- ## Charts & Visualizations
2
-
3
- **All charts, graphs, maps, and visualizations MUST use library components.** Never use Recharts, Chart.js, Nivo, or any third-party charting library. Never hand-roll SVG, canvas, or custom chart markup. The ONLY charting tools allowed are `D3Chart` (via `D3ChartTemplates` or a custom `renderChart` function) and `GeoMap`.
4
-
5
- | Visualization | MUST use | NEVER do |
6
- |--------------|----------|----------|
7
- | Line chart | `ChartCard` + `D3Chart` with `D3ChartTemplates.lineChart` | Custom `<svg>` with hand-drawn paths/circles |
8
- | Grouped bar chart | `ChartCard` + `D3Chart` with `D3ChartTemplates.groupedBarChart` | Custom `<svg>` bar elements |
9
- | Map / geographic | `GeoMap` | Custom SVG map or embedded iframe |
10
- | Custom visualization | `ChartCard` + `D3Chart` with a custom `renderChart` function | Inline SVG/canvas |
11
-
12
- ### D3Chart API (CRITICAL — read carefully)
13
-
14
- `D3Chart` does NOT use a `template` prop. It uses a `renderChart` callback function:
15
-
16
- ```jsx
17
- <D3Chart
18
- data={array} // data array
19
- renderChart={D3ChartTemplates.lineChart} // function reference, NOT a string
20
- responsive={true} // enables ResizeObserver
21
- height={280} // height in px
22
- />
23
- ```
24
-
25
- ### ChartCard + D3Chart usage
26
-
27
- `ChartCard` takes a `chart` prop (a React element). Pass the `D3Chart` as the `chart` value:
28
-
29
- ```jsx
30
- import { ChartCard, D3Chart, D3ChartTemplates } from "@/components/library";
31
-
32
- // ✅ Line chart — data must have { x, y } shape (numeric x)
33
- const spendData = [
34
- { x: 1, y: 2400 }, { x: 2, y: 3100 }, { x: 3, y: 2800 }, ...
35
- ];
36
- <ChartCard
37
- title="30-Day Spend"
38
- subtitle="Engine savings"
39
- chart={
40
- <D3Chart
41
- data={spendData}
42
- renderChart={D3ChartTemplates.lineChart}
43
- options={{ stroke: "#5BC8C8", xKey: "x", yKey: "y" }}
44
- responsive
45
- height={280}
46
- />
47
- }
48
- />
49
-
50
- // ✅ Grouped bar chart — data must have { x, group1, group2 } shape
51
- const monthlyData = [
52
- { x: "Jan", Hotels: 12000, Flights: 8000 },
53
- { x: "Feb", Hotels: 14000, Flights: 9000 }, ...
54
- ];
55
- <ChartCard
56
- title="Monthly Spend"
57
- chart={
58
- <D3Chart
59
- data={monthlyData}
60
- renderChart={D3ChartTemplates.groupedBarChart}
61
- options={{ xKey: "x", groups: ["Hotels", "Flights"], colors: ["#5BC8C8", "#6366F1"] }}
62
- responsive
63
- height={280}
64
- />
65
- }
66
- />
67
-
68
- // ✅ Custom renderChart function (for donut, horizontal bar, etc.)
69
- function renderDonut(svgEl, data, dims, opts) {
70
- // Use d3 to draw into svgEl
71
- const d3svg = d3.select(svgEl);
72
- d3svg.selectAll("*").remove();
73
- // ... d3 drawing code
74
- }
75
- <ChartCard title="By Department" chart={<D3Chart data={deptData} renderChart={renderDonut} responsive height={280} />} />
76
- ```
77
-
78
- ### Available D3ChartTemplates
79
-
80
- - `D3ChartTemplates.lineChart` — options: `{ xKey, yKey, stroke, strokeWidth, margin, showAxes, showGrid }`
81
- - `D3ChartTemplates.groupedBarChart` — options: `{ xKey, groups, colors, barRadius, margin, yFormat, showGrid }`
82
-
83
- **There is NO `D3ChartTemplates.LINE`, `.BAR`, `.DONUT`, `.AREA`.** Only `lineChart` and `groupedBarChart`. For donut/pie/horizontal bar, write a custom `renderChart` function using d3.
84
-
85
- ### GeoMap API
86
-
87
- ```jsx
88
- import { GeoMap } from "@/components/library";
89
-
90
- // markers: array of { id, lon, lat, active?, label? }
91
- const markers = [
92
- { id: "sf", lon: -122.4, lat: 37.8, active: true, label: "San Francisco" },
93
- { id: "nyc", lon: -74.0, lat: 40.7, active: true, label: "New York" },
94
- ];
95
-
96
- <ChartCard title="Active Locations" chart={
97
- <GeoMap
98
- markers={markers}
99
- theme="dark"
100
- height={400}
101
- width={800}
102
- zoomable
103
- />
104
- } />
105
- ```
106
-
107
- GeoMap props: `width`, `height`, `projection` (`"naturalEarth"` | `"mercator"` | `"equirectangular"`), `theme` (`"dark"` | `"light"`), `markers`, `arcs`, `overlays`, `zoomable`, `minZoom`, `maxZoom`, `onMarkerClick`.
108
-
109
- ### Common mistakes
110
-
111
- ```jsx
112
- // ❌ WRONG — third-party chart libraries are forbidden
113
- import { BarChart, Bar } from "recharts";
114
- <BarChart data={data}><Bar dataKey="count" /></BarChart>
115
-
116
- // ❌ WRONG — template prop doesn't exist
117
- <D3Chart template={D3ChartTemplates.LINE} data={data} />
118
-
119
- // ❌ WRONG — passing as children instead of chart prop
120
- <ChartCard title="Spend"><D3Chart ... /></ChartCard>
121
-
122
- // ❌ WRONG — non-numeric x values with lineChart (use groupedBarChart for categorical x)
123
- <D3Chart data={[{x: "Jan", y: 100}]} renderChart={D3ChartTemplates.lineChart} />
124
-
125
- // ✅ CORRECT
126
- <ChartCard title="Spend" chart={<D3Chart data={data} renderChart={D3ChartTemplates.lineChart} responsive height={280} />} />
127
- ```
128
-
129
- ## Portals
130
-
131
- Any `position: fixed` component **must** use `createPortal(... , document.body)`.
132
-
133
- ## Loading / Error / Empty States
134
-
135
- Every data component handles loading. Error messages are human-readable. Empty states explain what would appear.
136
-
1
+ See SKILL.md — Charts & Visualizations section.
@@ -1,61 +1 @@
1
- ## Pre-Completion Checklist
2
-
3
- Before finishing a dashboard page, verify ALL of these:
4
-
5
- - [ ] **Every card/panel is a library component** (MetricCard, ListCard, ActivityCard, WidgetCard, TableCard, etc.) — no hand-rolled `<div className="bg-white border ...">` cards
6
- - [ ] **Every chart/visualization is a library component** (ChartCard + D3Chart, GeoMap) — no hand-rolled SVG/canvas
7
- - [ ] **No `<nav>`, header bar, or sticky tab bar** — navigation is handled by appLayout.tsx
8
- - [ ] **Single scrollable page** — no multi-tab layout that swaps content
9
- - [ ] **Max 4 KPIs per row** — if 5+, split across rows
10
- - [ ] **Charts render** — using `renderChart` callback (not `template` prop), data format matches (numeric x for lineChart)
11
- - [ ] **`<div className="space-y-6">` wraps all content** — no `mb-*` for section spacing
12
- - [ ] **Dark mode works** — every custom element has `dark:` variants; no `text-black`, `text-white`, or `bg-black`
13
- - [ ] **No inline `style={{}}` or `<style>` tags** — use Tailwind classes or CSS in global.css
14
- - [ ] **No inline `<svg>`** — use Heroicons from `@heroicons/react`
15
- - [ ] **`position: fixed` uses `createPortal`** — FABs, modals, toasts
16
- - [ ] **Data uses `useDataSource`** — sample data defined outside the component
17
- - [ ] **Only uses company logo** (`@/assets/images/engine_logo.png`) — no external URLs, no placeholder boxes
18
- - [ ] **Images preserve aspect ratio** — one dimension auto, never both fixed
19
- - [ ] **Imports only from `@/components/library`** — no shadcn, no Lucide, no `cn()`
20
- - [ ] **Grid patterns match the approved list** — no custom grid layouts
21
-
22
- ## Post-Completion Verification
23
-
24
- **DO NOT run `npm run validate:dashboard`, `npm run dev`, `npm run build`, or `tsc` during builds.** These waste time and are not required for phase completion.
25
-
26
- Instead, verify correctness by reviewing the code against the Pre-Completion Checklist above. Confirm that all three wiring files are updated (CommandCenter.tsx, Home.tsx, routes.tsx).
27
-
28
- ## Anti-Patterns (never do these)
29
-
30
- - Hand-rolling HTML cards instead of using library components (MetricCard, ListCard, etc.)
31
- - Using Recharts, Chart.js, Nivo, or any chart library other than D3Chart/GeoMap
32
- - Hand-rolling SVG/canvas charts instead of using ChartCard + D3Chart or GeoMap
33
- - Adding a `<nav>`, header bar, sticky tab bar, or top navigation inside a dashboard page
34
- - Multi-tab layouts that swap content (Overview/Travelers/Spend/etc tabs) — build one scrollable page
35
- - More than 4 KPIs in a single row (max is `lg:grid-cols-4`)
36
- - Using `template` prop on D3Chart (doesn't exist) — use `renderChart={D3ChartTemplates.lineChart}`
37
- - Passing D3Chart as children to ChartCard — use `chart={<D3Chart ... />}` prop
38
- - CSS Modules, styled-components, or inline `style={{}}` for layout
39
- - `<style>` tags with keyframes or custom CSS inside components
40
- - Inline `<svg>` elements when a Heroicon exists
41
- - Importing shadcn components (`src/components/ui/`) into command center pages — `Button`, `Card`, `Input`, `Select`, `Alert`, `Skeleton`, `Label`, `Spinner`, etc. are ALL forbidden. Use the library equivalents from `@/components/library` (`UIButton`, `BaseCard`/`WidgetCard`, `UIInput`, `Select`, `Alert`, `Skeleton`, etc.)
42
- - Importing Lucide icons (`lucide-react`) into command center pages — use Heroicons (`@heroicons/react`) instead
43
- - Using `cn()` helper in command center pages (that's shadcn's utility — use Tailwind classes directly)
44
- - `useEffect` for data in schema components
45
- - `bg-indigo-*` or raw colors for brand — use `brand-*` or `engine-*`
46
- - Recreating providers (`AppThemeProvider`, `DataModeProvider`) in dashboard pages — `CommandCenter.tsx` already provides them
47
- - Tables without search on 10+ rows, charts without tooltips, empty states without messages
48
- - Hard-coded pixel widths or skipping dark mode
49
- - `text-black`, `text-white`, `bg-black` — use slate scale
50
- - `lg:grid-cols-6` for metrics (max 4), manually appending K/M/B
51
- - `mb-6` between sections instead of `space-y-6` wrapper
52
- - Same layout across dashboards, no signature element
53
- - `ChatPanel` in dashboard grid — use `ChatBar`
54
- - `position: fixed` without `createPortal`
55
- - `Math.random()` in sample data, different layouts based on data mode
56
- - Re-declaring filter/form definitions inside component render
57
- - Dismissing validator errors as "acceptable" or "justified" instead of fixing them — every error has a library component fix
58
- - Hardcoded data without `useDataSource` for sample/live mode switching
59
- - External image URLs (Unsplash, placeholders) unless user explicitly requests them
60
- - Placeholder boxes (`<div className="w-8 h-8 bg-black rounded" />`) instead of the actual logo
61
- - Fixed width AND height on images — always let one dimension be `auto` to preserve aspect ratio
1
+ See SKILL.md — Pre-Completion Checklist and Post-Completion Verification sections.
@@ -1,97 +1 @@
1
- ## Component Selection
2
-
3
- | Need | Component |
4
- |------|-----------|
5
- | Single KPI | `MetricCard` |
6
- | Row of 2–4 KPIs | `MetricsStrip` (schema) / `MetricCard` grid |
7
- | Tabular data | `TableCard` |
8
- | Time series / distribution | `ChartCard` + `D3Chart` |
9
- | Geographic / flight map | `GeoMap` |
10
- | System health | `StatusCard` |
11
- | Feed / item list | `ListCard` |
12
- | Scrollable side panel | `FeedPanel` |
13
- | Activity feed | `ActivityCard` |
14
- | User avatar | `Avatar` |
15
- | Text summary | `NarrativeSummary` (schema) / `SectionCard` |
16
- | Actions | `ActionList` (schema) / `UIButton` group |
17
- | Alert / callout | `CalloutCard` |
18
- | Multi-section panel | `WidgetCard` |
19
-
20
- ## Component Data Contract
21
-
22
- Common card props: `title`, `subtitle`, `actions`, `loading`, `error`, `emptyMessage`, `...cardProps`.
23
-
24
- Item shape (StatusCard, ListCard, ItemList): `{ id?, title, description?, status?, timestamp?, value?, unit?, avatar? }`. `title` is primary; `name` is alias.
25
-
26
- Never use hardcoded `indigo-*` — use `brand-*` classes.
27
-
28
- ## Metric Formatting
29
-
30
- Use `fmtK()` helper — never manually append K/M/B:
31
-
32
- ```js
33
- function fmtK(n) {
34
- if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
35
- if (n >= 1e3) return `${(n / 1e3).toFixed(1)}K`;
36
- return n.toLocaleString();
37
- }
38
- ```
39
-
40
- ## Brand & Accent Colors
41
-
42
- Defined in `global.css` (`--color-brand-*` and `--color-engine-*`), available as Tailwind classes.
43
-
44
- - `engine-*` for the Engine brand: `engine-teal`, `engine-coral`, `engine-orange`, `engine-savings`, `engine-text`, `engine-muted`, `engine-label`, `engine-border`, `engine-bg`
45
- - `brand-*` for the legacy command center palette (indigo-based)
46
- - Tailwind built-in colors for status (green/amber/red). Never use brand for status.
47
-
48
- ## Semantic Colors
49
-
50
- - `"default"` — neutral | `"primary"` — brand | `"success"` — healthy | `"warning"` — attention | `"danger"` — critical
51
-
52
- `changeType` reflects good/bad, not direction. Revenue down = `"negative"`. Incidents down = `"positive"`.
53
-
54
- ## Status Values
55
-
56
- Canonical: `"operational"`, `"degraded"`, `"outage"`, `"maintenance"`. Always show color + text label.
57
-
58
- ## Dark Mode
59
-
60
- Every visible element needs both light and dark styles. Library components handle this automatically — which is another reason to use them.
61
-
62
- For any custom markup you do write:
63
-
64
- | Role | Light | Dark |
65
- |------|-------|------|
66
- | Background | `bg-white` | `dark:bg-slate-900` |
67
- | Surface | `bg-slate-50` | `dark:bg-slate-950/30` |
68
- | Border | `border-slate-200` | `dark:border-slate-800` |
69
- | Text primary | `text-slate-900` | `dark:text-slate-50` |
70
- | Text secondary | `text-slate-600` | `dark:text-slate-300` |
71
- | Text muted | `text-slate-500` | `dark:text-slate-400` |
72
- | Brand tint | `brand-50` | `brand-950` |
73
-
74
- **Never use `text-black` or `text-white`.** Use the slate scale. Never use `bg-black` for surfaces — use `bg-slate-900` / `dark:bg-slate-950`.
75
-
76
- ## Icons (Command Center)
77
-
78
- **Heroicons 2** (`@heroicons/react`) exclusively inside command center pages. No Lucide, no Font Awesome, no inline SVGs.
79
-
80
- - `24/outline` — default | `24/solid` — status/active | `20/solid` — chips/badges
81
- - Sizes: `h-4 w-4` compact, `h-5 w-5` default, `h-6 w-6` emphasis, `h-8 w-8` hero only
82
- - Always set a color class. Pass rendered elements: `icon={<UsersIcon className="h-5 w-5 text-brand-500" />}`
83
- - ❌ Never use inline `<svg>` when a Heroicon exists for the same purpose
84
- - ❌ Never guess icon names — Heroicons uses specific names that don't always match what you'd expect. Common mistakes: `PlaneIcon` (doesn't exist → use `PaperAirplaneIcon`), `FlightIcon` (doesn't exist → use `PaperAirplaneIcon`), `HotelIcon` (doesn't exist → use `BuildingOfficeIcon`), `MoneyIcon` (doesn't exist → use `BanknotesIcon`), `AlertIcon` (doesn't exist → use `ExclamationTriangleIcon`), `PersonIcon` (doesn't exist → use `UserIcon`)
85
-
86
- ## Fonts
87
-
88
- Fonts are set in `global.css` via `@theme` block (`--font-sans`, `--font-mono`). No `next/font` in this project (that's the starter). No Google Fonts `<link>` tags.
89
-
90
- ## Filtering & Data Utilities
91
-
92
- Three layers: `filterUtils.js` (pure) → `usePageFilters` hook (state) → `FilterBar` + components (UI).
93
-
94
- - Pass `sortedData` (not `filteredData`) to components
95
- - Disable `TableCard.searchable` when `FilterBar` provides search
96
- - Declare filter definitions outside component body (or memoize)
97
-
1
+ See SKILL.md — Component Selection, Colors, Dark Mode, Icons sections.
@@ -1,43 +1 @@
1
- ## Data Mode (Sample vs Live)
2
-
3
- `useDataMode()` returns `{ mode, isSample, isLive, toggle, setMode }`. `useDataSource({ sample, live })` picks based on mode.
4
-
5
- - Every page works in both modes. Default to `"sample"`.
6
- - Same layout for both modes — only data changes.
7
- - Sample data: 10–50 items, realistic, deterministic (no `Math.random()`).
8
- - Define sample data as constants outside the component body.
9
-
10
- ```jsx
11
- // ✅ Correct: data defined outside, selected by mode
12
- const SAMPLE_TRIPS = [ ... ];
13
-
14
- export default function Dashboard() {
15
- const trips = useDataSource({ sample: SAMPLE_TRIPS, live: liveTrips });
16
- ...
17
- }
18
-
19
- // ❌ Wrong: hardcoded data inside the component with no mode switching
20
- export default function Dashboard() {
21
- const trips = [ ... ]; // no useDataSource, no mode awareness
22
- }
23
- ```
24
-
25
- ## Forms & Record Modals
26
-
27
- Schema-driven: `sections[] → fields[]`. Use `FormModal` (modal) or `FormRenderer` (inline). `useFormState` for state.
28
-
29
- Define schemas outside component body. Always `required: true` on mandatory fields. Submissions enforce 4s minimum spinner.
30
-
31
- ## AI Chat & Agent
32
-
33
- Use `ChatBar` (command palette, ⌘K) for dashboards. `ChatPanel` for full-page chat.
34
-
35
- - Always provide 3–5 `suggestions` for starter prompts
36
- - Return `components` for structured data — never markdown tables in text
37
- - Always set height on `ChatPanel`
38
- - Extract `onSend` handlers to module scope when shared
39
-
40
- ## Tables
41
-
42
- `searchable` when 10+ rows (unless `FilterBar` handles search). `sortable` on comparable columns. `paginated` when 10+ rows. Use built-in column `type` before custom `render`.
43
-
1
+ See SKILL.md — Data Mode, Forms, AI Chat, Tables sections.