@e-llm-studio/federated-dashboard-components
v1.0.1
Published
A React component library for building **config-driven executive dashboards**. The flagship export, `ExecutiveDashboardWrapper`, renders a complete dashboard — date filters, a multi-tier line-chart, KPI metric cards, a filter sidebar, and data tables — fr
Maintainers
Keywords
Readme
@e-llm-studio/federated-dashboard-components
A React component library for building config-driven executive dashboards. The
flagship export, ExecutiveDashboardWrapper, renders a complete dashboard —
date filters, a multi-tier line-chart, KPI metric cards, a filter sidebar, and
data tables — from a single configId. The wrapper fetches its own layout and
data from the backend, so the host application only has to mount one component
and (optionally) describe how its tables should render.
The package also ships the underlying building blocks (Graph, MetricCard,
FTable, TableCard, DashboardCard, DashboardDateFilter, FiltersSidebar,
chart primitives, and KPI formatting helpers) for teams that want to compose a
dashboard by hand.
Table of contents
- Installation
- Quick start
- How it works (architecture)
ExecutiveDashboardWrapperprops- The
tablesprop in depth - Backend API contract
- Exported building blocks
- KPI formatting utilities
- TypeScript types reference
- Troubleshooting
1. Installation
npm install @e-llm-studio/federated-dashboard-componentsPeer dependencies
These must be installed in the host application (they are intentionally not bundled, so a single copy is shared):
npm install react@^18.3.1 react-dom@^18.3.1 \
primereact@^10.9.7 primeicons@^7.0.0 \
lucide-react@^0.476.0 recharts@^2.13.0 \
react-router-dom@^7.6.2| Peer dependency | Version | Used for |
| ------------------ | ---------- | ----------------------------------------- |
| react / react-dom | ^18.3.1 | Core runtime |
| primereact | ^10.9.7 | Underlying DataTable, sidebar, dropdowns |
| primeicons | ^7.0.0 | Icon font used by PrimeReact widgets |
| lucide-react | ^0.476.0 | Iconography (chart/KPI/table icons) |
| recharts | ^2.13.0 | Line-chart rendering |
| react-router-dom | ^7.6.2 | Router context expected by the demo shell |
Import the stylesheet once
The library ships a single extracted stylesheet. Import it once at your app root (it is side-effecting and required for correct layout):
import "@e-llm-studio/federated-dashboard-components/dist/styles.css";You will typically also want PrimeReact's own base CSS in your app shell:
import "primereact/resources/themes/lara-light-indigo/theme.css"; // or your theme
import "primereact/resources/primereact.min.css";
import "primeicons/primeicons.css";2. Quick start
The wrapper is fully self-contained. Give it a configId and a baseUrl; it
does the rest.
import "@e-llm-studio/federated-dashboard-components/dist/styles.css";
import { ExecutiveDashboardWrapper } from "@e-llm-studio/federated-dashboard-components";
export function ClaimsDashboard() {
return (
<ExecutiveDashboardWrapper
configId="6a579569dc07b01447bd9797"
baseUrl="https://dev.appmod.ai"
/>
);
}That single mount will:
- Call the UI config API once to learn the dashboard's structure (graph tiers, plot options, KPI cards, sidebar filters, date presets, table layout).
- Seed the date range from the config's default preset.
- Call the data API to load graph series + KPI values, and re-call it every time the date range or applied filters change.
- Render a skeleton while loading and an inline error + Retry button on failure.
Exports:
ExecutiveDashboardWrapperis exported from the package root (src/index.ts), alongside the presentationalExecutiveDashboard, the built-in API fetchers (fetchUIConfig,fetchDashboardData,DashboardApiError), and the full set of container / config / data types.
With custom table rendering
To control how the dashboard's tables look (links, badges, currency, impact
chips), pass a tables config keyed by each table's key from the UI config.
See §5 for the full walkthrough.
import type { DashboardTablesConfig } from "@e-llm-studio/federated-dashboard-components";
const TABLES: DashboardTablesConfig = {
top_plant_locations_by_claim_cost: {
tableProps: { showGridlines: true },
card: {
title: { fontSize: 16, color: "#111827" },
icon: { size: 18, color: "#059669" },
},
columns: [
{
key: "plant_location",
header: "Plant Location",
body: (row) => <a href="#">{row.plant_location}</a>,
},
{
key: "claim_count",
header: "Claim Count",
body: (row) => <span>{row.claim_count}</span>,
},
],
},
};
<ExecutiveDashboardWrapper
configId="6a579569dc07b01447bd9797"
baseUrl="https://dev.appmod.ai"
tables={TABLES}
/>;A full worked example (money/impact/status cell helpers, a custom app header) lives in src/App.tsx.
3. How it works (architecture)
┌──────────────────────────────┐
configId, baseUrl → │ ExecutiveDashboardWrapper │
│ (state + data orchestration) │
└──────────────┬───────────────┘
│
┌───────────────────────────┼───────────────────────────┐
│ 1. UI config API (once) │ 2. Data API (on range/ │
│ /federated/filters/ui │ filter change) │
│ + .../distinct-value │ /federated/filters/ │
│ │ get-data │
▼ ▼ │
structure & labels numeric series + KPI values │
└───────────────┬───────────────────────────────────────┘
▼
┌────────────────────┐
│ ExecutiveDashboard │ (presentational)
│ • DashboardDateFilter
│ • Graph (tiers + plot selectors)
│ • MetricCard × N
│ • FiltersSidebar
│ • TableCard × N
└────────────────────┘- State lives in the wrapper (ExecutiveDashboardWrapper.tsx):
selected plot-by option per tier, filter values (draft vs. applied), and the
active date range. All fetches are
AbortController-guarded, so rapid filter changes cancel in-flight requests. - Two logical fetch phases. The UI config load also fetches the distinct
values for each sidebar filter and merges them in as
options. The data load is re-run wheneverdateRangeorappliedFilterschange. - The inner
ExecutiveDashboardis presentational — it receives everything as props and reports interactions back up via callbacks.
4. ExecutiveDashboardWrapper props
Defined by the DashboardContainerProps interface in
src/types/index.ts. All props below are wired and
consumed by the wrapper.
| Prop | Type | Required | Description |
| ------------------ | ------------------------------------------------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| configId | string | ✅ Yes | Identifies which dashboard config to load. Sent as config_id in the body of every API call. |
| baseUrl | string | No | API origin. Defaults to "https://dev.appmod.ai". Trailing slashes are trimmed. Override for other environments (staging, prod, localhost). |
| graphEmptyState | React.ReactNode | No | Custom node shown inside the graph area when no series have data. Falls back to a built-in empty state when omitted. |
| tables | DashboardTablesConfig | No | Per-table render config keyed by the table key from the UI config's tableBuilder. Tables without an entry auto-render as plain text. See §5. |
| tableLayout | DashboardTableLayoutConfig | No | Grid layout for the tables section — how many tables sit per row, which options the toolbar offers, and how narrow a card may get before the grid reflows. See §5.1. |
| sectionOrder | DashboardSectionKey[] | No | Vertical order of the dashboard sections, by UI config key — e.g. ["tableBuilder", "kpiCards", "graphBuilder"]. Partial lists are allowed. See §4.1. |
| hiddenSections | DashboardSectionKey[] | No | Parts to leave out entirely, e.g. ["graphBuilder", "sidebarFilters"]. Applied after sectionOrder, and unlike it, also accepts the pinned dateFilters / sidebarFilters. See §4.1. |
| fetchUIConfig | FetchUIConfig | No | Override the built-in UI-config fetcher — (config, signal?) => Promise<DashboardUIConfig>. Useful for mocks/tests or a custom transport. Defaults to the built-in /federated/filters/ui fetcher. |
| fetchDashboardData | FetchDashboardData | No | Override the built-in data fetcher — (config, params, signal?) => Promise<DashboardDataResponse>. Defaults to the built-in /federated/filters/get-data fetcher. |
| initialStartDate | Date | No | Force the initial range start instead of deriving it from the config's default preset. Must be paired with initialEndDate. |
| initialEndDate | Date | No | Force the initial range end. Must be paired with initialStartDate. |
| onDateChange | (payload: any) => void | No | Fires whenever the resolved date range changes (payload carries { startDate, endDate }). |
| onFiltersApplied | (filters: Record<string, string \| string[]>) => void | No | Fires after the sidebar's Apply is pressed, with the applied filter values. |
| renderLoading | () => React.ReactNode | No | Replace the default skeleton loader. |
| renderError | (error: Error, retry: () => void) => React.ReactNode | No | Replace the default inline error + retry UI. retry re-runs the UI-config load. |
Notes
initialStartDate/initialEndDateonly seed the first render. If only one is provided, the wrapper falls back to the config's default preset.- Fetcher overrides receive an
AbortControllersignal and are expected to honour it — the wrapper cancels in-flight requests when inputs change.
<ExecutiveDashboardWrapper
configId="6a579569dc07b01447bd9797"
initialStartDate={new Date("2026-01-01")}
initialEndDate={new Date("2026-04-18")}
onDateChange={(p) => console.log("range:", p)}
onFiltersApplied={(f) => console.log("filters:", f)}
renderLoading={() => <MySpinner />}
renderError={(err, retry) => <MyError message={err.message} onRetry={retry} />}
/>4.1 Section order (sectionOrder / hiddenSections)
Everything in the dashboard body can be re-ordered by passing the same keys the
API sends — today graphBuilder, kpiCards and tableBuilder:
// Tables on top, then the KPI cards, then the chart.
<ExecutiveDashboardWrapper
configId="6a579569dc07b01447bd9797"
sectionOrder={["tableBuilder", "kpiCards", "graphBuilder"]}
/>Two parts have a fixed position: the date filter bar stays pinned at the
top and the filters sidebar is an overlay, so its position never affects the
layout. sectionOrder therefore ignores dateFilters / sidebarFilters — handy
when you're just reordering the config's key list wholesale:
// Same result as above.
sectionOrder={["dateFilters", "sidebarFilters", "tableBuilder", "kpiCards", "graphBuilder"]}Fixed position doesn't mean permanent, though — hiddenSections can remove
them:
hiddenSections={["dateFilters", "sidebarFilters"]} // both goneNew sections work automatically. When the backend grows another section, pass
its key the same way — sectionOrder={["kpiCards", "pieChartBuilder", "graphBuilder"]}.
Nothing about ordering needs to change: the order is resolved against whatever
sections the dashboard currently renders, and the prop type accepts any string, so
a new key never trips the type checker.
Details:
- Partial lists are fine. Anything omitted keeps its default position, so
sectionOrder={["tableBuilder"]}alone means "tables first, then the rest as usual". hiddenSectionsdrops a part — separate fromsectionOrder, so nothing disappears just for being left out of the order. It's applied last, so hiding always wins, and it accepts the pinned parts as well as the body sections:
HidingsectionOrder={["kpiCards", "graphBuilder"]} hiddenSections={["tableBuilder", "sidebarFilters"]}sidebarFiltersalso removes the Filter button (and its count) from the date bar, since there'd be no panel left for it to open. HidingdateFiltersonly removes the bar — the dashboard still loads data for the config's default date range.- Key matching is forgiving — case-insensitive, and spaces/underscores/dashes
are ignored, so
"Table Builder","table_builder"and"tableBuilder"are the same key. - Unknown keys are ignored, not thrown — they'd usually come from config. In development an ignored key logs a console warning naming it and listing the available sections, so a typo is easy to spot. Duplicates collapse.
Driving the order from the backend config
The UI config payload may also carry the order as data.sectionOrder, which is
useful when the layout should travel with the dashboard config instead of being
hard-coded in the host app:
{
"data": {
"sectionOrder": ["tableBuilder", "kpiCards", "graphBuilder"],
"dateFilters": { "...": "..." },
"graphBuilder": { "...": "..." },
"kpiCards": ["..."],
"tableBuilder": ["..."]
}
}The sectionOrder prop wins over data.sectionOrder, so a host can always
override what the config asks for. Both go through the same resolution, so the
same keys work in either place.
The resolver is exported if a host needs the same behaviour in its own layout UI:
import {
DASHBOARD_SECTION_KEYS, // ["graphBuilder", "kpiCards", "tableBuilder"]
PINNED_SECTION_KEYS, // ["dateFilters", "sidebarFilters"]
resolveSectionOrder, // (order?, hidden?, configOrder?, available?) → string[]
isSectionHidden, // (hidden, "dateFilters") → boolean
} from "@e-llm-studio/federated-dashboard-components";Ordering within a section still comes from the config: KPI cards honour
displayOrder, graph tiers/series honour theirs, and table placement inside the grid is controlled bytableLayout.spans.
5. The tables prop in depth
The UI config's tableBuilder defines which tables exist and their raw
columns. The tables prop lets the host app decide how each of those tables
renders — custom cell JSX, headers, per-column styles, and the surrounding
card chrome (icon + title). It's optional and partial: any table key you don't
provide falls back to auto-generated plain-text columns.
type DashboardTablesConfig = Record<string, DashboardTableRenderConfig>;The keys must match tableBuilder[].key from the UI config.
DashboardTableRenderConfig
| Field | Type | Description |
| ------------ | --------------------------- | ------------------------------------------------------------------------------------------------------- |
| columns | FTableColumn[] | Required. Column render definitions (see below). |
| tableProps | Record<string, any> | Passthrough props merged onto the underlying PrimeReact DataTable (e.g. { showGridlines: true }). |
| card | DashboardTableCardStyles | Styling for the card wrapping the table (shell, header, title, icon). |
FTableColumn
| Field | Type | Description |
| ---------------- | ------------------------------------ | ----------------------------------------------------------------------- |
| key | string | Row field / column identity. |
| header | React.ReactNode | Header cell content (string or JSX). |
| body | (row: TableRow) => React.ReactNode | Cell renderer for each row. TableRow is Record<string, any>. |
| style | React.CSSProperties | Applied to both header and body cells of the column. |
| headerStyle | React.CSSProperties | Header-cell (<th>) only; overrides style for the header. |
| bodyStyle | React.CSSProperties | Body-cell (<td>) only; overrides style for rows. |
| headerClassName| string | Class on the header cell. |
| bodyClassName | string | Class on body cells. |
DashboardTableCardStyles
| Field | Type | Description |
| -------- | -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
| root | React.CSSProperties | The card shell. Set width here to size the whole card — the table fills it. |
| header | React.CSSProperties | The header row (icon + title + search/expand controls). |
| title | React.CSSProperties | The title text (font, colour, padding). |
| icon | { size?: number; color?: string; background?: string; style?: CSSProperties } | Glyph size/colour, chip background, and an escape hatch for other icon-container CSS. |
Sizing tip: To make a table narrower, set
card.root.width(e.g."60%"). Sizing only the table leaves the card full-width.
Full table example
import type { DashboardTablesConfig, FTableColumn, TableRow } from "@e-llm-studio/federated-dashboard-components";
const money = (v: any) => (Number.isFinite(Number(v)) ? `$${v}` : String(v ?? ""));
const linkCol = (field: string, header: string): FTableColumn => ({
key: field,
header,
body: (row) => <a href="#" style={{ color: "#7c3aed" }}>{row[field]}</a>,
});
const TABLES: DashboardTablesConfig = {
claim_cost_by_incident_type: {
tableProps: { showGridlines: true },
card: {
header: { padding: "10px" },
title: { fontSize: 16, color: "#111827" },
icon: { size: 18, color: "#059669" },
},
columns: [
linkCol("incident_type", "Incident Type"),
{ key: "total_claim_cost", header: "Total Claim Cost", body: (r) => money(r.total_claim_cost) },
{ key: "litigation_rate", header: "Litigation Rate", body: (r) => `${r.litigation_rate} %` },
],
},
};5.1 Table grid layout (tableLayout)
When the UI config has more than one table, a small toolbar appears above the tables section with one icon per layout — 1 / 2 / 3 tables per row. Picking an icon re-flows the tables into that many columns.
The layout is always responsive: minColumnWidth is the narrowest a table card
may become, and the grid drops columns automatically as the container (not the
window) gets narrower — 3-up becomes 2-up, then 1-up. The user's choice is
remembered, so widening the container restores it. An option that can't fit at
the current width is dimmed in the toolbar.
<ExecutiveDashboardWrapper
configId="6a579569dc07b01447bd9797"
tables={TABLES}
tableLayout={{
options: [1, 2, 3], // icons offered in the toolbar
defaultColumns: 2, // start 2-up
minColumnWidth: 420, // drop a column below this per-card width
gap: 16,
toggleLabel: "Layout",
}}
/>| Field | Type | Default | Description |
| ----------------- | ------------------------------------------- | ----------- | ---------------------------------------------------------------------- |
| spans | Record<string, TableSpan> | TableSpan[] | — | Per-table width — see below. |
| totalColumns | number | 12 | Grid columns the spans values are expressed in. |
| defaultSpan | TableSpan | full width | Width for tables missing from spans. |
| options | number[] | [1, 2, 3] | Column counts offered as toolbar icons (1–4 have dedicated icons). |
| defaultColumns | number \| "custom" | see note | Layout on first render. Defaults to "custom" when spans is set, else 1. |
| columns | number \| "custom" | — | Controlled layout. Pair with onColumnsChange to own the state. |
| onColumnsChange | (columns: number \| "custom") => void | — | Fires when the user picks a layout. |
| showToggle | boolean | true | Hide the toolbar and keep the configured column count. Auto-hidden for 1 table. |
| toggleLabel | string | — | Caption rendered left of the toggle. |
| minColumnWidth | number | 380 | Narrowest a card may get before a column is dropped — this drives the reflow. |
| gap | number | 12 | Gap between cards, in px. |
| equalHeights | boolean | true | Stretch every card in a row to the tallest one. false lets each card hug its own content. |
Cards in the same row are height-matched by default, so a 5-row table beside a
10-row one no longer renders short — the shorter table's card fills the row
height. To cap how tall a row can get, give the table a scroll height via
tables[key].tableProps, e.g. { scrollHeight: "360px" }.
Per-table widths (spans)
Uniform 1/2/3-up is the user's control. spans is the host's control: it
sets the width of each table individually, so table 1 can own a full row while
tables 2 and 3 sit side by side and table 4 goes full width again.
tableLayout={{
spans: {
revenue_summary: "full", // row 1 — on its own
claims_by_type: "1/2", // row 2 — side by side
claims_by_plant: "1/2", //
litigation_log: "full", // row 3 — on its own
},
}}Keys are the table's key from the UI config's tableBuilder. An array works
too and is read in table order — handy when the layout is positional:
tableLayout={{ spans: ["full", "1/2", "1/2", "full"] }}Accepted values (TableSpan): "full", any fraction string ("1/2", "1/3",
"2/3", "1/4", "3/4", …), or a raw column count against totalColumns
(6 is half of the default 12-column grid). Rows are formed by CSS grid
auto-placement, so spans that overflow a row simply wrap.
This stays responsive. No table is ever rendered narrower than
minColumnWidth; spans widen instead. With the four-table layout above at
minColumnWidth: 380:
| Container width | Result |
| --------------- | --------------------------------------------------- |
| ≥ 772px | [T1] / [T2 + T3] / [T4] — the configured shape |
| < 772px | [T1] / [T2] / [T3] / [T4] — all full width |
(772px = two 380px cards plus the 12px gap.) Raise minColumnWidth to make it
break to one column earlier, lower it to hold two columns longer.
When spans is set, the toolbar grows an extra icon for the configured layout
and starts on it. The user can still switch to a uniform 1/2/3-up and click
back to the configured one. Pass showToggle: false to lock the layout down.
The same grid is exported standalone as TableGrid if you're composing table
cards yourself:
import { TableGrid, TableCard } from "@e-llm-studio/federated-dashboard-components";
<TableGrid defaultColumns={2} minColumnWidth={400}>
<TableCard title="Claims" table={{ rows, columns }} />
<TableCard title="Providers" table={{ rows: rows2, columns: columns2 }} />
</TableGrid>;6. Backend API contract
The wrapper (via src/api/dashboardApi.ts) calls three
POST JSON endpoints, all relative to baseUrl. Each responds with an envelope:
{
"success": true,
"request_id": "…",
"data": { /* … */ },
"error": null | { "errorMessage": "…", "errorType": "…" }
}A non-2xx status, or success: false, throws a DashboardApiError (surfaced in
the wrapper's error UI).
6.1 POST /federated/filters/ui — structure & labels
Request: { "config_id": "<configId>" }
Returns the dashboard layout: dateFilters, sidebarFilters, graphBuilder
(tiers → plot options → series), kpiCards, and optional tableBuilder. See
DashboardUIData in §9.
6.2 POST /federated/filters/distinct-value — filter options
Request: { "config_id": "<configId>" }
Returns { "sidebarFilters": { "<schemaFilterName>": string[] } }. The wrapper
merges these arrays into each sidebar filter definition as its options.
UI config (6.1) and distinct values (6.2) are fetched in parallel and merged into a single
DashboardUIConfig.
6.3 POST /federated/filters/get-data — the numbers
Request:
{
"config_id": "<configId>",
"dateFilters": { "startDate": "<ISO>", "endDate": "<ISO>" },
"sidebarFilters": { "<schemaFilterName>": ["value", …] }
}Filter normalisation: single-select values become one-element arrays; empty / unset filters are dropped from the payload.
Response data shape (DashboardDataResponse):
{
"graph": {
"<graphKey>": [
{ "key": "<tierKey>", "plotbyoptions": { "<plotOptionKey>": { "<seriesKey>": [ { "x": "D1", "y": 58 }, … ] } } }
]
},
"kpiCards": {
// either a bare value…
"Fill Rate Impact": "2.8k",
// …or an object carrying a baseline for indicator colouring:
"Total Claim Cost": { "currentValue": 8421316, "baselineValue": 7900000 }
},
"table": {
"<tableKey>": [ { /* row */ }, … ]
}
}7. Exported building blocks
If you're composing a dashboard by hand rather than using the wrapper, the following are exported from the package root (see src/index.ts):
Components
| Export | Notes |
| --------------------- | -------------------------------------------------------- |
| Graph | Multi-pane line chart. Props: GraphProps. |
| LineChart | Single line chart primitive. |
| PlotBySelector | Dropdown used to switch a tier's plotted metric. |
| DashboardDateFilter | Date-range picker with presets + refresh/filter buttons. |
| MetricCard | KPI card. Props: MetricCardProps. |
| FiltersSidebar | Slide-out filter panel. Props: FiltersSidebarProps. |
| FTable | Low-level table. Props: FTableProps. |
| TableCard | FTable wrapped in a titled/iconed card. Props: TableCardProps. |
| TableGrid | Responsive N-per-row grid + layout toggle for table cards. Props: TableGridProps. |
| TableLayoutToggle | The 1/2/3-per-row icon group on its own. Props: TableLayoutToggleProps. |
| useResponsiveColumns| Hook behind the grid — caps a desired column count to what the container fits. |
| DashboardCard | Rich content card (sections, tags, events, news, grid). |
Chart primitives (shadcn/Recharts port)
ChartContainer, ChartTooltip, ChartTooltipContent, ChartLegend,
ChartLegendContent, useChart, plus the ChartConfig type and the
DEFAULT_COLORS / colorForIndex palette helpers.
Date presets
DATE_PRESETS and related exports from datePreset — e.g. DAYS_7, DAYS_15,
DAYS_30, THIS_MONTH, CUSTOM. Each preset with a getRange() returns
{ startDate, endDate }.
8. KPI formatting utilities
Exported from the package root for formatting/colouring KPI values consistently (see src/utils/index.ts):
| Export | Signature / purpose |
| --------------------- | ----------------------------------------------------------------------------------------------------------- |
| formatKpiValue | Format a raw KPI value using a format string (compacts numbers, preserves suffixes like %). |
| getKpiDelta | Compute the delta (absolute + %) and sentiment between a current and baseline value. Returns KpiDelta. |
| getKpiSentiment | (current, baseline, indicatorLogic) → "positive" \| "negative" \| "neutral". |
| getKpiValueColor | Resolve a display colour from sentiment, with optional per-sentiment overrides. |
| isKpiValueObject | Type guard: is a raw value the { currentValue, baselineValue } object form? |
| KPI_SENTIMENT_COLORS| Default positive/negative colour map. |
| compactNumber | 8421316 → "8.4M" style compaction. |
IndicatorLogic is one of "higher_better" | "lower_better" | "neutral" — it
tells the sentiment helpers whether an increase is good or bad.
import {
getKpiDelta,
getKpiValueColor,
} from "@e-llm-studio/federated-dashboard-components";
const delta = getKpiDelta(8421316, 7900000, "higher_better");
// → { …, sentiment: "positive" }
const color = getKpiValueColor(8421316, 7900000, "higher_better");9. TypeScript types reference
All types below are exported (or defined) in src/types/index.ts and re-exported from the root where noted.
- Container:
DashboardContainerProps— the wrapper's prop bag (§4). - UI config:
DashboardUIConfig→DashboardUIData→{ dateFilters, sidebarFilters, graphBuilder, kpiCards, tableBuilder, sectionOrder? }.DateFiltersConfig—datepresets,defaultPreset,showFilterButton,showFilterCount,showLiveButton,liveDurationMs.SidebarFilterConfig—label,description,schemaFilterName,type.GraphBuilderConfig—title,subtitle,icon,axis,tiers[],legend.GraphTierConfig—key,title,displayOrder,plotOptions[],yAxis,series[].PlotOptionConfig,SeriesConfig,LegendConfig,KpiCardConfig.
- Data:
DashboardDataResponse,TierData,PlotByOptions,SeriesDataMap,DataPoint. - Query:
DashboardQueryParams—{ startDate?, endDate?, filters? }. - Tables:
DashboardTablesConfig,DashboardTableRenderConfig,DashboardTableCardStyles,DashboardTableLayoutConfig,TableBuilderConfig,TableColumnConfig,FTableColumn,TableRow. - Layout:
DashboardSectionKey— a section named by its UI config key ("graphBuilder" | "kpiCards" | "tableBuilder", plus any future one, since any string is accepted). Used bysectionOrder/hiddenSections(§4.1). - KPI:
KpiValueObject,KpiDelta,KpiSentiment,IndicatorLogic. - Fetchers:
FetchUIConfig,FetchDashboardData(function types for the pluggable fetchers).
10. Troubleshooting
| Symptom | Likely cause / fix |
| --------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| ExecutiveDashboardWrapper is undefined on import | Ensure you're on a build that exports it from the root; it is exported from src/index.ts. |
| Blank / unstyled layout | You didn't import dist/styles.css (and/or PrimeReact's theme CSS). See §1. |
| Requests hit the wrong host / 404 | baseUrl defaults to https://dev.appmod.ai. For another environment pass an explicit absolute baseUrl. |
| "Failed to load dashboard" with a Retry button | An API returned non-2xx or success: false. Check the three endpoints in §6 and the config_id. |
| Tables render as plain text | No matching entry in tables for that tableBuilder[].key. Keys must match exactly. See §5. |
| Peer dependency warnings on install | Install the peers listed in §1 at compatible versions. |
Package: @e-llm-studio/federated-dashboard-components · Entry:
src/index.ts · Wrapper:
src/ExecutiveDashboardWrapper.tsx · API
layer: src/api/dashboardApi.ts
