@informedai/react 0.4.27 → 0.4.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -31,6 +31,7 @@ interface WidgetMessage {
31
31
  runId: string;
32
32
  query: string;
33
33
  status: string;
34
+ queryType?: string;
34
35
  };
35
36
  triages?: string[];
36
37
  };
@@ -136,6 +137,8 @@ interface InformedAssistantConfig {
136
137
  * Return the current values of all fields as they appear in your UI.
137
138
  */
138
139
  getCurrentFieldValues?: () => Record<string, unknown>;
140
+ /** Callback when an analytics run badge is clicked. If not provided, an inline panel is shown. */
141
+ onAnalyticsRunClick?: (runId: string, query: string, sessionId: string) => void;
139
142
  /** Custom theme overrides */
140
143
  theme?: Partial<WidgetTheme>;
141
144
  /** Position of the widget (for floating mode) */
@@ -160,6 +163,24 @@ interface WidgetTheme {
160
163
  /** Font family */
161
164
  fontFamily: string;
162
165
  }
166
+ interface SqlBreakdown {
167
+ baseDataset: string;
168
+ metrics: string[];
169
+ filters: string[];
170
+ grouping: string[];
171
+ sorting: string | null;
172
+ timeRange: string | null;
173
+ }
174
+ interface AnalyticsRunDetail {
175
+ id: string;
176
+ query: string;
177
+ status: string;
178
+ generatedSql: string | null;
179
+ structuredBreakdown: SqlBreakdown | null;
180
+ result: unknown[] | null;
181
+ createdAt: string;
182
+ parentRunId: string | null;
183
+ }
163
184
  interface SSEEvent {
164
185
  type: 'content' | 'done' | 'error' | 'session_update';
165
186
  content?: string;
@@ -353,6 +374,8 @@ interface InformedAIContextValue {
353
374
  startNewSession: () => Promise<void>;
354
375
  endSession: () => Promise<void>;
355
376
  clearError: () => void;
377
+ onAnalyticsRunClick?: (runId: string, query: string, sessionId: string) => void;
378
+ getAnalyticsRun: (sessionId: string, runId: string) => Promise<AnalyticsRunDetail>;
356
379
  }
357
380
  declare function useInformedAI(): InformedAIContextValue;
358
381
  interface InformedAIProviderProps {
@@ -471,7 +494,11 @@ declare class InformedAIClient {
471
494
  * Returns true if sendBeacon was used, false if fetch fallback was attempted.
472
495
  */
473
496
  endSessionBeacon(sessionId: string): boolean;
497
+ /**
498
+ * Get details of a specific analytics run.
499
+ */
500
+ getAnalyticsRun(sessionId: string, runId: string): Promise<AnalyticsRunDetail>;
474
501
  private processSSEStream;
475
502
  }
476
503
 
477
- export { AdminChatbot, type AdminChatbotConfig, type AdminChatbotTheme, type ChatMessage, type Document, type DocumentType, type DocumentTypeSchema, type FieldDefinition, InformedAIClient, InformedAIProvider, InformedAssistant, type InformedAssistantConfig, type QuickAction, type QuickActionGroup, type SSEEvent, type Session, SmartQuestionnaire, type SmartQuestionnaireConfig, type SmartQuestionnaireTheme, type TaskConfig, type TaskState, type UseSessionReturn, WebsiteChatbot, type WebsiteChatbotConfig, type WebsiteChatbotTheme, type WidgetMessage, type WidgetReadyContext, type WidgetTheme, useInformedAI, useSession };
504
+ export { AdminChatbot, type AdminChatbotConfig, type AdminChatbotTheme, type AnalyticsRunDetail, type ChatMessage, type Document, type DocumentType, type DocumentTypeSchema, type FieldDefinition, InformedAIClient, InformedAIProvider, InformedAssistant, type InformedAssistantConfig, type QuickAction, type QuickActionGroup, type SSEEvent, type Session, SmartQuestionnaire, type SmartQuestionnaireConfig, type SmartQuestionnaireTheme, type SqlBreakdown, type TaskConfig, type TaskState, type UseSessionReturn, WebsiteChatbot, type WebsiteChatbotConfig, type WebsiteChatbotTheme, type WidgetMessage, type WidgetReadyContext, type WidgetTheme, useInformedAI, useSession };
package/dist/index.d.ts CHANGED
@@ -31,6 +31,7 @@ interface WidgetMessage {
31
31
  runId: string;
32
32
  query: string;
33
33
  status: string;
34
+ queryType?: string;
34
35
  };
35
36
  triages?: string[];
36
37
  };
@@ -136,6 +137,8 @@ interface InformedAssistantConfig {
136
137
  * Return the current values of all fields as they appear in your UI.
137
138
  */
138
139
  getCurrentFieldValues?: () => Record<string, unknown>;
140
+ /** Callback when an analytics run badge is clicked. If not provided, an inline panel is shown. */
141
+ onAnalyticsRunClick?: (runId: string, query: string, sessionId: string) => void;
139
142
  /** Custom theme overrides */
140
143
  theme?: Partial<WidgetTheme>;
141
144
  /** Position of the widget (for floating mode) */
@@ -160,6 +163,24 @@ interface WidgetTheme {
160
163
  /** Font family */
161
164
  fontFamily: string;
162
165
  }
166
+ interface SqlBreakdown {
167
+ baseDataset: string;
168
+ metrics: string[];
169
+ filters: string[];
170
+ grouping: string[];
171
+ sorting: string | null;
172
+ timeRange: string | null;
173
+ }
174
+ interface AnalyticsRunDetail {
175
+ id: string;
176
+ query: string;
177
+ status: string;
178
+ generatedSql: string | null;
179
+ structuredBreakdown: SqlBreakdown | null;
180
+ result: unknown[] | null;
181
+ createdAt: string;
182
+ parentRunId: string | null;
183
+ }
163
184
  interface SSEEvent {
164
185
  type: 'content' | 'done' | 'error' | 'session_update';
165
186
  content?: string;
@@ -353,6 +374,8 @@ interface InformedAIContextValue {
353
374
  startNewSession: () => Promise<void>;
354
375
  endSession: () => Promise<void>;
355
376
  clearError: () => void;
377
+ onAnalyticsRunClick?: (runId: string, query: string, sessionId: string) => void;
378
+ getAnalyticsRun: (sessionId: string, runId: string) => Promise<AnalyticsRunDetail>;
356
379
  }
357
380
  declare function useInformedAI(): InformedAIContextValue;
358
381
  interface InformedAIProviderProps {
@@ -471,7 +494,11 @@ declare class InformedAIClient {
471
494
  * Returns true if sendBeacon was used, false if fetch fallback was attempted.
472
495
  */
473
496
  endSessionBeacon(sessionId: string): boolean;
497
+ /**
498
+ * Get details of a specific analytics run.
499
+ */
500
+ getAnalyticsRun(sessionId: string, runId: string): Promise<AnalyticsRunDetail>;
474
501
  private processSSEStream;
475
502
  }
476
503
 
477
- export { AdminChatbot, type AdminChatbotConfig, type AdminChatbotTheme, type ChatMessage, type Document, type DocumentType, type DocumentTypeSchema, type FieldDefinition, InformedAIClient, InformedAIProvider, InformedAssistant, type InformedAssistantConfig, type QuickAction, type QuickActionGroup, type SSEEvent, type Session, SmartQuestionnaire, type SmartQuestionnaireConfig, type SmartQuestionnaireTheme, type TaskConfig, type TaskState, type UseSessionReturn, WebsiteChatbot, type WebsiteChatbotConfig, type WebsiteChatbotTheme, type WidgetMessage, type WidgetReadyContext, type WidgetTheme, useInformedAI, useSession };
504
+ export { AdminChatbot, type AdminChatbotConfig, type AdminChatbotTheme, type AnalyticsRunDetail, type ChatMessage, type Document, type DocumentType, type DocumentTypeSchema, type FieldDefinition, InformedAIClient, InformedAIProvider, InformedAssistant, type InformedAssistantConfig, type QuickAction, type QuickActionGroup, type SSEEvent, type Session, SmartQuestionnaire, type SmartQuestionnaireConfig, type SmartQuestionnaireTheme, type SqlBreakdown, type TaskConfig, type TaskState, type UseSessionReturn, WebsiteChatbot, type WebsiteChatbotConfig, type WebsiteChatbotTheme, type WidgetMessage, type WidgetReadyContext, type WidgetTheme, useInformedAI, useSession };
package/dist/index.js CHANGED
@@ -216,6 +216,15 @@ var InformedAIClient = class {
216
216
  return false;
217
217
  }
218
218
  // ========================================================================
219
+ // Analytics
220
+ // ========================================================================
221
+ /**
222
+ * Get details of a specific analytics run.
223
+ */
224
+ async getAnalyticsRun(sessionId, runId) {
225
+ return this.request(`/widget/sessions/${sessionId}/analytics-runs/${runId}`);
226
+ }
227
+ // ========================================================================
219
228
  // SSE Stream Processing
220
229
  // ========================================================================
221
230
  async processSSEStream(response, onEvent) {
@@ -713,6 +722,10 @@ function InformedAIProvider({ config, children }) {
713
722
  const clearError = (0, import_react.useCallback)(() => {
714
723
  setError(null);
715
724
  }, []);
725
+ const getAnalyticsRun = (0, import_react.useCallback)(async (sessionId, runId) => {
726
+ if (!clientRef.current) throw new Error("Client not initialized");
727
+ return clientRef.current.getAnalyticsRun(sessionId, runId);
728
+ }, []);
716
729
  const value = {
717
730
  session,
718
731
  document,
@@ -727,7 +740,9 @@ function InformedAIProvider({ config, children }) {
727
740
  skipTask,
728
741
  startNewSession,
729
742
  endSession,
730
- clearError
743
+ clearError,
744
+ onAnalyticsRunClick: config.onAnalyticsRunClick,
745
+ getAnalyticsRun
731
746
  };
732
747
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InformedAIContext.Provider, { value, children });
733
748
  }
@@ -1150,6 +1165,7 @@ function MessageBubble({ message, theme, onQuickAction, isLatest = false, quickA
1150
1165
  const isUser = message.role === "user";
1151
1166
  const hasQuickActions = isLatest && message.quickActions && message.quickActions.length > 0;
1152
1167
  const hasKbSearch = message.kbSearch != null;
1168
+ const hasAnalyticsRun = message.agentContext?.analyticsRun != null;
1153
1169
  if ((message.type === "quick_actions" || !message.content) && hasQuickActions) {
1154
1170
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1155
1171
  GroupedQuickActions,
@@ -1188,6 +1204,15 @@ function MessageBubble({ message, theme, onQuickAction, isLatest = false, quickA
1188
1204
  ]
1189
1205
  }
1190
1206
  ),
1207
+ hasAnalyticsRun && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1208
+ AnalyticsBadge,
1209
+ {
1210
+ runId: message.agentContext.analyticsRun.runId,
1211
+ query: message.agentContext.analyticsRun.query,
1212
+ status: message.agentContext.analyticsRun.status,
1213
+ queryType: message.agentContext.analyticsRun.queryType
1214
+ }
1215
+ ),
1191
1216
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1192
1217
  "div",
1193
1218
  {
@@ -1323,6 +1348,185 @@ function GroupedQuickActions({ actions, groups, theme, onQuickAction }) {
1323
1348
  ] }, groupKey);
1324
1349
  }) });
1325
1350
  }
1351
+ function AnalyticsBadge({ runId, query, status, queryType }) {
1352
+ const { session, onAnalyticsRunClick, getAnalyticsRun } = useInformedAI();
1353
+ const [expanded, setExpanded] = (0, import_react2.useState)(false);
1354
+ const [runDetail, setRunDetail] = (0, import_react2.useState)(null);
1355
+ const [loading, setLoading] = (0, import_react2.useState)(false);
1356
+ const handleClick = () => {
1357
+ if (onAnalyticsRunClick && session) {
1358
+ onAnalyticsRunClick(runId, query, session.id);
1359
+ return;
1360
+ }
1361
+ setExpanded((prev) => !prev);
1362
+ };
1363
+ (0, import_react2.useEffect)(() => {
1364
+ if (expanded && !runDetail && !loading && session) {
1365
+ setLoading(true);
1366
+ getAnalyticsRun(session.id, runId).then(setRunDetail).catch((err) => console.warn("Failed to load analytics run:", err)).finally(() => setLoading(false));
1367
+ }
1368
+ }, [expanded, runDetail, loading, session, runId, getAnalyticsRun]);
1369
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { marginBottom: "4px" }, children: [
1370
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1371
+ "button",
1372
+ {
1373
+ onClick: handleClick,
1374
+ style: {
1375
+ display: "inline-flex",
1376
+ alignItems: "center",
1377
+ gap: "6px",
1378
+ padding: "4px 10px",
1379
+ backgroundColor: status === "completed" ? "#ede9fe" : "#fef3c7",
1380
+ border: "none",
1381
+ borderRadius: "12px",
1382
+ fontSize: "11px",
1383
+ color: status === "completed" ? "#6d28d9" : "#92400e",
1384
+ cursor: "pointer",
1385
+ transition: "opacity 0.15s"
1386
+ },
1387
+ onMouseOver: (e) => {
1388
+ e.currentTarget.style.opacity = "0.8";
1389
+ },
1390
+ onMouseOut: (e) => {
1391
+ e.currentTarget.style.opacity = "1";
1392
+ },
1393
+ title: `Analytics query: ${query}`,
1394
+ children: [
1395
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ChartIcon, { size: 12 }),
1396
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { children: [
1397
+ "Analytics query",
1398
+ queryType ? ` \xB7 ${queryType.charAt(0).toUpperCase() + queryType.slice(1)}` : ""
1399
+ ] }),
1400
+ !onAnalyticsRunClick && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { fontSize: "9px", marginLeft: "2px" }, children: expanded ? "\u25B2" : "\u25BC" })
1401
+ ]
1402
+ }
1403
+ ),
1404
+ expanded && !onAnalyticsRunClick && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(AnalyticsRunPanel, { detail: runDetail, loading })
1405
+ ] });
1406
+ }
1407
+ function AnalyticsRunPanel({ detail, loading }) {
1408
+ const [sqlExpanded, setSqlExpanded] = (0, import_react2.useState)(false);
1409
+ if (loading) {
1410
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { padding: "12px", fontSize: "12px", color: "#737373" }, children: "Loading analytics details..." });
1411
+ }
1412
+ if (!detail) return null;
1413
+ const breakdown = detail.structuredBreakdown;
1414
+ const rows = detail.result;
1415
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1416
+ "div",
1417
+ {
1418
+ style: {
1419
+ marginTop: "6px",
1420
+ padding: "12px",
1421
+ backgroundColor: "#faf5ff",
1422
+ borderRadius: "8px",
1423
+ fontSize: "12px",
1424
+ lineHeight: 1.5,
1425
+ border: "1px solid #e9d5ff"
1426
+ },
1427
+ children: [
1428
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { fontWeight: 600, color: "#6d28d9", marginBottom: "8px" }, children: detail.query }),
1429
+ breakdown && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: "4px", marginBottom: "8px" }, children: [
1430
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(BreakdownItem, { label: "Dataset", value: breakdown.baseDataset }),
1431
+ breakdown.metrics.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(BreakdownItem, { label: "Metrics", value: breakdown.metrics.join(", ") }),
1432
+ breakdown.filters.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(BreakdownItem, { label: "Filters", value: breakdown.filters.join(", ") }),
1433
+ breakdown.grouping.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(BreakdownItem, { label: "Grouped by", value: breakdown.grouping.join(", ") }),
1434
+ breakdown.sorting && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(BreakdownItem, { label: "Sorted", value: breakdown.sorting }),
1435
+ breakdown.timeRange && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(BreakdownItem, { label: "Time range", value: breakdown.timeRange })
1436
+ ] }),
1437
+ detail.generatedSql && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
1438
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1439
+ "button",
1440
+ {
1441
+ onClick: () => setSqlExpanded((prev) => !prev),
1442
+ style: {
1443
+ background: "none",
1444
+ border: "none",
1445
+ padding: 0,
1446
+ fontSize: "11px",
1447
+ color: "#7c3aed",
1448
+ cursor: "pointer",
1449
+ textDecoration: "underline"
1450
+ },
1451
+ children: sqlExpanded ? "Hide SQL" : "Show SQL"
1452
+ }
1453
+ ),
1454
+ sqlExpanded && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1455
+ "pre",
1456
+ {
1457
+ style: {
1458
+ marginTop: "4px",
1459
+ padding: "8px",
1460
+ backgroundColor: "#1e1b4b",
1461
+ color: "#c4b5fd",
1462
+ borderRadius: "6px",
1463
+ fontSize: "10px",
1464
+ overflowX: "auto",
1465
+ whiteSpace: "pre-wrap",
1466
+ wordBreak: "break-all"
1467
+ },
1468
+ children: detail.generatedSql
1469
+ }
1470
+ )
1471
+ ] }),
1472
+ rows && rows.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { marginTop: "8px" }, children: [
1473
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { fontSize: "11px", color: "#737373", marginBottom: "4px" }, children: [
1474
+ "Results (",
1475
+ rows.length,
1476
+ " row",
1477
+ rows.length !== 1 ? "s" : "",
1478
+ ")"
1479
+ ] }),
1480
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { overflowX: "auto" }, children: [
1481
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("table", { style: { borderCollapse: "collapse", fontSize: "10px", width: "100%" }, children: [
1482
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("tr", { children: Object.keys(rows[0]).map((key) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1483
+ "th",
1484
+ {
1485
+ style: {
1486
+ padding: "4px 8px",
1487
+ backgroundColor: "#ede9fe",
1488
+ borderBottom: "1px solid #d8b4fe",
1489
+ textAlign: "left",
1490
+ fontWeight: 600,
1491
+ whiteSpace: "nowrap"
1492
+ },
1493
+ children: key
1494
+ },
1495
+ key
1496
+ )) }) }),
1497
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("tbody", { children: rows.slice(0, 5).map((row, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("tr", { children: Object.values(row).map((val, j) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1498
+ "td",
1499
+ {
1500
+ style: {
1501
+ padding: "3px 8px",
1502
+ borderBottom: "1px solid #f3e8ff",
1503
+ whiteSpace: "nowrap"
1504
+ },
1505
+ children: String(val ?? "")
1506
+ },
1507
+ j
1508
+ )) }, i)) })
1509
+ ] }),
1510
+ rows.length > 5 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { fontSize: "10px", color: "#737373", marginTop: "4px" }, children: [
1511
+ "+ ",
1512
+ rows.length - 5,
1513
+ " more rows"
1514
+ ] })
1515
+ ] })
1516
+ ] })
1517
+ ]
1518
+ }
1519
+ );
1520
+ }
1521
+ function BreakdownItem({ label, value }) {
1522
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", gap: "6px" }, children: [
1523
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { style: { color: "#737373", minWidth: "70px" }, children: [
1524
+ label,
1525
+ ":"
1526
+ ] }),
1527
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: "#1c1917" }, children: value })
1528
+ ] });
1529
+ }
1326
1530
  function SparklesIcon({ color = "currentColor" }) {
1327
1531
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: color, strokeWidth: "2", children: [
1328
1532
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M12 3l1.5 4.5L18 9l-4.5 1.5L12 15l-1.5-4.5L6 9l4.5-1.5L12 3z" }),
@@ -1339,6 +1543,13 @@ function MinimizeIcon() {
1339
1543
  function ChevronRightIcon({ size = 16, color = "currentColor" }) {
1340
1544
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: color, strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M9 18l6-6-6-6" }) });
1341
1545
  }
1546
+ function ChartIcon({ size = 16, color = "currentColor" }) {
1547
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: color, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
1548
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "18", y1: "20", x2: "18", y2: "10" }),
1549
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "12", y1: "20", x2: "12", y2: "4" }),
1550
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "6", y1: "20", x2: "6", y2: "14" })
1551
+ ] });
1552
+ }
1342
1553
  function BookIcon({ size = 16, color = "currentColor" }) {
1343
1554
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: color, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
1344
1555
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M4 19.5A2.5 2.5 0 016.5 17H20" }),
package/dist/index.mjs CHANGED
@@ -183,6 +183,15 @@ var InformedAIClient = class {
183
183
  return false;
184
184
  }
185
185
  // ========================================================================
186
+ // Analytics
187
+ // ========================================================================
188
+ /**
189
+ * Get details of a specific analytics run.
190
+ */
191
+ async getAnalyticsRun(sessionId, runId) {
192
+ return this.request(`/widget/sessions/${sessionId}/analytics-runs/${runId}`);
193
+ }
194
+ // ========================================================================
186
195
  // SSE Stream Processing
187
196
  // ========================================================================
188
197
  async processSSEStream(response, onEvent) {
@@ -680,6 +689,10 @@ function InformedAIProvider({ config, children }) {
680
689
  const clearError = useCallback(() => {
681
690
  setError(null);
682
691
  }, []);
692
+ const getAnalyticsRun = useCallback(async (sessionId, runId) => {
693
+ if (!clientRef.current) throw new Error("Client not initialized");
694
+ return clientRef.current.getAnalyticsRun(sessionId, runId);
695
+ }, []);
683
696
  const value = {
684
697
  session,
685
698
  document,
@@ -694,7 +707,9 @@ function InformedAIProvider({ config, children }) {
694
707
  skipTask,
695
708
  startNewSession,
696
709
  endSession,
697
- clearError
710
+ clearError,
711
+ onAnalyticsRunClick: config.onAnalyticsRunClick,
712
+ getAnalyticsRun
698
713
  };
699
714
  return /* @__PURE__ */ jsx(InformedAIContext.Provider, { value, children });
700
715
  }
@@ -1117,6 +1132,7 @@ function MessageBubble({ message, theme, onQuickAction, isLatest = false, quickA
1117
1132
  const isUser = message.role === "user";
1118
1133
  const hasQuickActions = isLatest && message.quickActions && message.quickActions.length > 0;
1119
1134
  const hasKbSearch = message.kbSearch != null;
1135
+ const hasAnalyticsRun = message.agentContext?.analyticsRun != null;
1120
1136
  if ((message.type === "quick_actions" || !message.content) && hasQuickActions) {
1121
1137
  return /* @__PURE__ */ jsx2(
1122
1138
  GroupedQuickActions,
@@ -1155,6 +1171,15 @@ function MessageBubble({ message, theme, onQuickAction, isLatest = false, quickA
1155
1171
  ]
1156
1172
  }
1157
1173
  ),
1174
+ hasAnalyticsRun && /* @__PURE__ */ jsx2(
1175
+ AnalyticsBadge,
1176
+ {
1177
+ runId: message.agentContext.analyticsRun.runId,
1178
+ query: message.agentContext.analyticsRun.query,
1179
+ status: message.agentContext.analyticsRun.status,
1180
+ queryType: message.agentContext.analyticsRun.queryType
1181
+ }
1182
+ ),
1158
1183
  /* @__PURE__ */ jsx2(
1159
1184
  "div",
1160
1185
  {
@@ -1290,6 +1315,185 @@ function GroupedQuickActions({ actions, groups, theme, onQuickAction }) {
1290
1315
  ] }, groupKey);
1291
1316
  }) });
1292
1317
  }
1318
+ function AnalyticsBadge({ runId, query, status, queryType }) {
1319
+ const { session, onAnalyticsRunClick, getAnalyticsRun } = useInformedAI();
1320
+ const [expanded, setExpanded] = useState2(false);
1321
+ const [runDetail, setRunDetail] = useState2(null);
1322
+ const [loading, setLoading] = useState2(false);
1323
+ const handleClick = () => {
1324
+ if (onAnalyticsRunClick && session) {
1325
+ onAnalyticsRunClick(runId, query, session.id);
1326
+ return;
1327
+ }
1328
+ setExpanded((prev) => !prev);
1329
+ };
1330
+ useEffect2(() => {
1331
+ if (expanded && !runDetail && !loading && session) {
1332
+ setLoading(true);
1333
+ getAnalyticsRun(session.id, runId).then(setRunDetail).catch((err) => console.warn("Failed to load analytics run:", err)).finally(() => setLoading(false));
1334
+ }
1335
+ }, [expanded, runDetail, loading, session, runId, getAnalyticsRun]);
1336
+ return /* @__PURE__ */ jsxs("div", { style: { marginBottom: "4px" }, children: [
1337
+ /* @__PURE__ */ jsxs(
1338
+ "button",
1339
+ {
1340
+ onClick: handleClick,
1341
+ style: {
1342
+ display: "inline-flex",
1343
+ alignItems: "center",
1344
+ gap: "6px",
1345
+ padding: "4px 10px",
1346
+ backgroundColor: status === "completed" ? "#ede9fe" : "#fef3c7",
1347
+ border: "none",
1348
+ borderRadius: "12px",
1349
+ fontSize: "11px",
1350
+ color: status === "completed" ? "#6d28d9" : "#92400e",
1351
+ cursor: "pointer",
1352
+ transition: "opacity 0.15s"
1353
+ },
1354
+ onMouseOver: (e) => {
1355
+ e.currentTarget.style.opacity = "0.8";
1356
+ },
1357
+ onMouseOut: (e) => {
1358
+ e.currentTarget.style.opacity = "1";
1359
+ },
1360
+ title: `Analytics query: ${query}`,
1361
+ children: [
1362
+ /* @__PURE__ */ jsx2(ChartIcon, { size: 12 }),
1363
+ /* @__PURE__ */ jsxs("span", { children: [
1364
+ "Analytics query",
1365
+ queryType ? ` \xB7 ${queryType.charAt(0).toUpperCase() + queryType.slice(1)}` : ""
1366
+ ] }),
1367
+ !onAnalyticsRunClick && /* @__PURE__ */ jsx2("span", { style: { fontSize: "9px", marginLeft: "2px" }, children: expanded ? "\u25B2" : "\u25BC" })
1368
+ ]
1369
+ }
1370
+ ),
1371
+ expanded && !onAnalyticsRunClick && /* @__PURE__ */ jsx2(AnalyticsRunPanel, { detail: runDetail, loading })
1372
+ ] });
1373
+ }
1374
+ function AnalyticsRunPanel({ detail, loading }) {
1375
+ const [sqlExpanded, setSqlExpanded] = useState2(false);
1376
+ if (loading) {
1377
+ return /* @__PURE__ */ jsx2("div", { style: { padding: "12px", fontSize: "12px", color: "#737373" }, children: "Loading analytics details..." });
1378
+ }
1379
+ if (!detail) return null;
1380
+ const breakdown = detail.structuredBreakdown;
1381
+ const rows = detail.result;
1382
+ return /* @__PURE__ */ jsxs(
1383
+ "div",
1384
+ {
1385
+ style: {
1386
+ marginTop: "6px",
1387
+ padding: "12px",
1388
+ backgroundColor: "#faf5ff",
1389
+ borderRadius: "8px",
1390
+ fontSize: "12px",
1391
+ lineHeight: 1.5,
1392
+ border: "1px solid #e9d5ff"
1393
+ },
1394
+ children: [
1395
+ /* @__PURE__ */ jsx2("div", { style: { fontWeight: 600, color: "#6d28d9", marginBottom: "8px" }, children: detail.query }),
1396
+ breakdown && /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "4px", marginBottom: "8px" }, children: [
1397
+ /* @__PURE__ */ jsx2(BreakdownItem, { label: "Dataset", value: breakdown.baseDataset }),
1398
+ breakdown.metrics.length > 0 && /* @__PURE__ */ jsx2(BreakdownItem, { label: "Metrics", value: breakdown.metrics.join(", ") }),
1399
+ breakdown.filters.length > 0 && /* @__PURE__ */ jsx2(BreakdownItem, { label: "Filters", value: breakdown.filters.join(", ") }),
1400
+ breakdown.grouping.length > 0 && /* @__PURE__ */ jsx2(BreakdownItem, { label: "Grouped by", value: breakdown.grouping.join(", ") }),
1401
+ breakdown.sorting && /* @__PURE__ */ jsx2(BreakdownItem, { label: "Sorted", value: breakdown.sorting }),
1402
+ breakdown.timeRange && /* @__PURE__ */ jsx2(BreakdownItem, { label: "Time range", value: breakdown.timeRange })
1403
+ ] }),
1404
+ detail.generatedSql && /* @__PURE__ */ jsxs("div", { children: [
1405
+ /* @__PURE__ */ jsx2(
1406
+ "button",
1407
+ {
1408
+ onClick: () => setSqlExpanded((prev) => !prev),
1409
+ style: {
1410
+ background: "none",
1411
+ border: "none",
1412
+ padding: 0,
1413
+ fontSize: "11px",
1414
+ color: "#7c3aed",
1415
+ cursor: "pointer",
1416
+ textDecoration: "underline"
1417
+ },
1418
+ children: sqlExpanded ? "Hide SQL" : "Show SQL"
1419
+ }
1420
+ ),
1421
+ sqlExpanded && /* @__PURE__ */ jsx2(
1422
+ "pre",
1423
+ {
1424
+ style: {
1425
+ marginTop: "4px",
1426
+ padding: "8px",
1427
+ backgroundColor: "#1e1b4b",
1428
+ color: "#c4b5fd",
1429
+ borderRadius: "6px",
1430
+ fontSize: "10px",
1431
+ overflowX: "auto",
1432
+ whiteSpace: "pre-wrap",
1433
+ wordBreak: "break-all"
1434
+ },
1435
+ children: detail.generatedSql
1436
+ }
1437
+ )
1438
+ ] }),
1439
+ rows && rows.length > 0 && /* @__PURE__ */ jsxs("div", { style: { marginTop: "8px" }, children: [
1440
+ /* @__PURE__ */ jsxs("div", { style: { fontSize: "11px", color: "#737373", marginBottom: "4px" }, children: [
1441
+ "Results (",
1442
+ rows.length,
1443
+ " row",
1444
+ rows.length !== 1 ? "s" : "",
1445
+ ")"
1446
+ ] }),
1447
+ /* @__PURE__ */ jsxs("div", { style: { overflowX: "auto" }, children: [
1448
+ /* @__PURE__ */ jsxs("table", { style: { borderCollapse: "collapse", fontSize: "10px", width: "100%" }, children: [
1449
+ /* @__PURE__ */ jsx2("thead", { children: /* @__PURE__ */ jsx2("tr", { children: Object.keys(rows[0]).map((key) => /* @__PURE__ */ jsx2(
1450
+ "th",
1451
+ {
1452
+ style: {
1453
+ padding: "4px 8px",
1454
+ backgroundColor: "#ede9fe",
1455
+ borderBottom: "1px solid #d8b4fe",
1456
+ textAlign: "left",
1457
+ fontWeight: 600,
1458
+ whiteSpace: "nowrap"
1459
+ },
1460
+ children: key
1461
+ },
1462
+ key
1463
+ )) }) }),
1464
+ /* @__PURE__ */ jsx2("tbody", { children: rows.slice(0, 5).map((row, i) => /* @__PURE__ */ jsx2("tr", { children: Object.values(row).map((val, j) => /* @__PURE__ */ jsx2(
1465
+ "td",
1466
+ {
1467
+ style: {
1468
+ padding: "3px 8px",
1469
+ borderBottom: "1px solid #f3e8ff",
1470
+ whiteSpace: "nowrap"
1471
+ },
1472
+ children: String(val ?? "")
1473
+ },
1474
+ j
1475
+ )) }, i)) })
1476
+ ] }),
1477
+ rows.length > 5 && /* @__PURE__ */ jsxs("div", { style: { fontSize: "10px", color: "#737373", marginTop: "4px" }, children: [
1478
+ "+ ",
1479
+ rows.length - 5,
1480
+ " more rows"
1481
+ ] })
1482
+ ] })
1483
+ ] })
1484
+ ]
1485
+ }
1486
+ );
1487
+ }
1488
+ function BreakdownItem({ label, value }) {
1489
+ return /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "6px" }, children: [
1490
+ /* @__PURE__ */ jsxs("span", { style: { color: "#737373", minWidth: "70px" }, children: [
1491
+ label,
1492
+ ":"
1493
+ ] }),
1494
+ /* @__PURE__ */ jsx2("span", { style: { color: "#1c1917" }, children: value })
1495
+ ] });
1496
+ }
1293
1497
  function SparklesIcon({ color = "currentColor" }) {
1294
1498
  return /* @__PURE__ */ jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: color, strokeWidth: "2", children: [
1295
1499
  /* @__PURE__ */ jsx2("path", { d: "M12 3l1.5 4.5L18 9l-4.5 1.5L12 15l-1.5-4.5L6 9l4.5-1.5L12 3z" }),
@@ -1306,6 +1510,13 @@ function MinimizeIcon() {
1306
1510
  function ChevronRightIcon({ size = 16, color = "currentColor" }) {
1307
1511
  return /* @__PURE__ */ jsx2("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: color, strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx2("path", { d: "M9 18l6-6-6-6" }) });
1308
1512
  }
1513
+ function ChartIcon({ size = 16, color = "currentColor" }) {
1514
+ return /* @__PURE__ */ jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: color, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
1515
+ /* @__PURE__ */ jsx2("line", { x1: "18", y1: "20", x2: "18", y2: "10" }),
1516
+ /* @__PURE__ */ jsx2("line", { x1: "12", y1: "20", x2: "12", y2: "4" }),
1517
+ /* @__PURE__ */ jsx2("line", { x1: "6", y1: "20", x2: "6", y2: "14" })
1518
+ ] });
1519
+ }
1309
1520
  function BookIcon({ size = 16, color = "currentColor" }) {
1310
1521
  return /* @__PURE__ */ jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: color, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
1311
1522
  /* @__PURE__ */ jsx2("path", { d: "M4 19.5A2.5 2.5 0 016.5 17H20" }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@informedai/react",
3
- "version": "0.4.27",
3
+ "version": "0.4.29",
4
4
  "description": "React SDK for InformedAI Assistant - AI-powered content creation widget",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",