@burdenoff/microfe-bigconsole 2026.710.2 → 2026.711.1

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.
@@ -56,7 +56,7 @@ function l({ dataSinkId: l, workspaceId: u, enabled: d = !0, onUpdate: f, onWork
56
56
  type: e,
57
57
  dataSink: i,
58
58
  timestamp: n,
59
- userId: r
59
+ actorId: r
60
60
  };
61
61
  switch (h(new Date(n)), e) {
62
62
  case "CREATED":
@@ -96,7 +96,7 @@ function l({ dataSinkId: l, workspaceId: u, enabled: d = !0, onUpdate: f, onWork
96
96
  onData: ({ data: e }) => {
97
97
  if (e.data?.dataSinkUpdated) {
98
98
  let t = e.data.dataSinkUpdated;
99
- S(t.type, t.dataSink, t.timestamp, t.userId);
99
+ S(t.type, t.dataSink, t.timestamp, t.actorId);
100
100
  }
101
101
  },
102
102
  onError: (e) => {
@@ -1 +1 @@
1
- {"version":3,"file":"useDataSinkSubscription.js","names":[],"sources":["../../../src/bigconsole/hooks/useDataSinkSubscription.ts"],"sourcesContent":["/**\n * DataSink Subscription Hook\n *\n * Custom hook for subscribing to DataSink updates via GraphQL subscriptions.\n * Supports both sink-level (individual) and workspace-level (all sinks) subscriptions.\n *\n * Note: Subscriptions require WebSocket transport in Apollo Client.\n * In standalone dev mode (HTTP-only), subscriptions are gracefully skipped.\n */\n\nimport { useState, useCallback, useEffect } from 'react';\nimport { useSubscription } from '@apollo/client/react';\nimport { useDataSinkStore } from '../store/dataSinkStore';\nimport { DataSinkDataUpdatedDocument, DataSinkUpdatedDocument } from '../../generated/wspace-operations';\nimport type { DataSink } from '../types';\n\n// ============================================================================\n// Types\n// ============================================================================\n\ninterface UseDataSinkSubscriptionOptions {\n /** Specific DataSink ID to subscribe to (sink-level updates) */\n dataSinkId?: string;\n /** Workspace ID for workspace-level events */\n workspaceId?: string;\n /** Whether subscriptions are enabled */\n enabled?: boolean;\n /** Callback when sink-level update received */\n onUpdate?: (dataSink: DataSink) => void;\n /** Callback when workspace-level event received */\n onWorkspaceEvent?: (event: DataSinkWorkspaceEvent) => void;\n}\n\ninterface DataSinkWorkspaceEvent {\n type: 'CREATED' | 'UPDATED' | 'REFRESHED' | 'DELETED';\n dataSink: DataSink;\n timestamp: string;\n userId?: string;\n}\n\ninterface UseDataSinkSubscriptionResult {\n /** Whether subscriptions are active */\n isSubscribed: boolean;\n /** Last update timestamp */\n lastUpdate: Date | null;\n /** Subscription error */\n error: Error | null;\n}\n\n// Subscription Result Types\ninterface DataSinkDataUpdatedResult {\n dataSinkDataUpdated?: Record<string, unknown>;\n}\n\ninterface DataSinkUpdatedResult {\n dataSinkUpdated?: {\n type: string;\n dataSink: Record<string, unknown>;\n timestamp: string;\n userId?: string;\n };\n}\n\n// ============================================================================\n// Helper: Check if error is a known subscription transport issue\n// ============================================================================\n\n/**\n * Detects errors caused by missing WebSocket transport (e.g., standalone dev mode).\n * These are expected and should not be treated as application errors.\n */\nfunction isSubscriptionTransportError(error: unknown): boolean {\n const message = error instanceof Error ? error.message : String(error);\n return (\n message.includes('Async Iterable') ||\n message.includes('Observable') ||\n message.includes('WebSocket') ||\n message.includes('Subscription field must return')\n );\n}\n\n// ============================================================================\n// Helper: Map API response to DataSink type\n// ============================================================================\n\nfunction mapToDataSink(data: Record<string, unknown>): DataSink {\n return {\n id: data.id as string,\n workspaceId: data.workspaceId as string,\n tenantId: data.tenantId as string,\n name: (data.name as string) || null,\n description: (data.description as string) || null,\n key: data.key as string,\n fluidGridsWorkflowId: (data.fluidGridsWorkflowId as string) || null,\n fluidGridsWorkflowExecutionId: (data.fluidGridsWorkflowExecutionId as string) || null,\n config: (data.config as Record<string, unknown>) || null,\n data: (data.data as Record<string, unknown>) || {},\n dataStats: (data.dataStats as DataSink['dataStats']) || null,\n lastRefreshedAt: (data.lastRefreshedAt as string) || new Date().toISOString(),\n lastRefreshedBy: (data.lastRefreshedBy as string) || null,\n expiresAt: (data.expiresAt as string) || null,\n nextRefreshAt: (data.nextRefreshAt as string) || null,\n ttl: (data.ttl as number) ?? 0,\n version: (data.version as number) ?? 1,\n sizeBytes: (data.sizeBytes as number) ?? 0,\n isActive: (data.isActive as boolean) ?? true,\n fieldMaskingEnabled: (data.fieldMaskingEnabled as boolean) ?? false,\n fieldMaskingRules: (data.fieldMaskingRules as Record<string, unknown>) || null,\n status: (data.status as DataSink['status']) || 'ready',\n statusChangedAt: (data.statusChangedAt as string) || null,\n statusChangedBy: (data.statusChangedBy as string) || null,\n setupState: (data.setupState as DataSink['setupState']) || 'not_started',\n refresh: (data.refresh as DataSink['refresh']) || null,\n exports: (data.exports as DataSink['exports']) || null,\n alerts: (data.alerts as DataSink['alerts']) || null,\n quota: (data.quota as DataSink['quota']) || null,\n metadata: (data.metadata as Record<string, unknown>) || null,\n createdAt: data.createdAt as string,\n createdBy: (data.createdBy as string) || '',\n updatedAt: data.updatedAt as string,\n updatedBy: (data.updatedBy as string) || '',\n deletedAt: (data.deletedAt as string) || null,\n deletedBy: (data.deletedBy as string) || null,\n };\n}\n\n// ============================================================================\n// Hook\n// ============================================================================\n\nexport function useDataSinkSubscription({\n dataSinkId,\n workspaceId,\n enabled = true,\n onUpdate,\n onWorkspaceEvent,\n}: UseDataSinkSubscriptionOptions): UseDataSinkSubscriptionResult {\n const [lastUpdate, setLastUpdate] = useState<Date | null>(null);\n const [subscriptionError, setSubscriptionError] = useState<Error | null>(null);\n\n // Store actions\n const updateDataSink = useDataSinkStore((state) => state.updateDataSink);\n const addDataSink = useDataSinkStore((state) => state.addDataSink);\n const removeDataSink = useDataSinkStore((state) => state.removeDataSink);\n\n // Handle sink-level data update\n const handleSinkDataUpdate = useCallback(\n (data: Record<string, unknown>) => {\n const dataSink = mapToDataSink(data);\n updateDataSink(dataSink.id, dataSink);\n setLastUpdate(new Date());\n onUpdate?.(dataSink);\n },\n [updateDataSink, onUpdate]\n );\n\n // Handle workspace-level event\n const handleWorkspaceEvent = useCallback(\n (eventType: string, data: Record<string, unknown>, timestamp: string, userId?: string) => {\n const dataSink = mapToDataSink(data);\n const event: DataSinkWorkspaceEvent = {\n type: eventType as DataSinkWorkspaceEvent['type'],\n dataSink,\n timestamp,\n userId,\n };\n\n setLastUpdate(new Date(timestamp));\n\n switch (eventType) {\n case 'CREATED':\n addDataSink(dataSink);\n break;\n case 'UPDATED':\n case 'REFRESHED':\n updateDataSink(dataSink.id, dataSink);\n break;\n case 'DELETED':\n removeDataSink(dataSink.id);\n break;\n default:\n }\n\n onWorkspaceEvent?.(event);\n },\n [addDataSink, updateDataSink, removeDataSink, onWorkspaceEvent]\n );\n\n // Sink-level subscription (for specific DataSink updates)\n const { loading: sinkLoading, error: sinkError } = useSubscription<DataSinkDataUpdatedResult>(\n DataSinkDataUpdatedDocument,\n {\n variables: { id: dataSinkId },\n skip: !enabled || !dataSinkId,\n onData: ({ data }) => {\n if (data.data?.dataSinkDataUpdated) {\n handleSinkDataUpdate(data.data.dataSinkDataUpdated);\n }\n },\n onError: (error) => {\n if (isSubscriptionTransportError(error)) {\n console.debug('[useDataSinkSubscription] Sink-level subscription not available (no WebSocket transport)');\n return;\n }\n setSubscriptionError(error instanceof Error ? error : new Error('Subscription error'));\n },\n }\n );\n\n // Workspace-level subscription (for all DataSink events in workspace)\n const { loading: workspaceLoading, error: workspaceError } = useSubscription<DataSinkUpdatedResult>(\n DataSinkUpdatedDocument,\n {\n variables: { workspaceId: workspaceId ?? '' },\n skip: !enabled || !workspaceId,\n onData: ({ data }) => {\n if (data.data?.dataSinkUpdated) {\n const event = data.data.dataSinkUpdated;\n handleWorkspaceEvent(event.type, event.dataSink, event.timestamp, event.userId);\n }\n },\n onError: (error) => {\n if (isSubscriptionTransportError(error)) {\n console.debug(\n '[useDataSinkSubscription] Workspace-level subscription not available (no WebSocket transport)'\n );\n return;\n }\n setSubscriptionError(error instanceof Error ? error : new Error('Subscription error'));\n },\n }\n );\n\n // Log subscription status\n useEffect(() => {\n if (enabled && dataSinkId) {\n console.debug('[useDataSinkSubscription] Sink-level subscription active:', dataSinkId);\n }\n if (enabled && workspaceId) {\n console.debug('[useDataSinkSubscription] Workspace-level subscription active:', workspaceId);\n }\n }, [enabled, dataSinkId, workspaceId]);\n\n // Combined error (filter out known transport errors)\n const rawError = sinkError || workspaceError || subscriptionError;\n const error = rawError && !isSubscriptionTransportError(rawError) ? rawError : null;\n\n useEffect(() => {\n if (error) {\n }\n }, [error]);\n\n // Determine if subscriptions are active\n const isSubscribed = !!(enabled && ((dataSinkId && !sinkLoading) || (workspaceId && !workspaceLoading)));\n\n return {\n isSubscribed,\n lastUpdate,\n error,\n };\n}\n\nexport default useDataSinkSubscription;\n"],"mappings":"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"}
1
+ {"version":3,"file":"useDataSinkSubscription.js","names":[],"sources":["../../../src/bigconsole/hooks/useDataSinkSubscription.ts"],"sourcesContent":["/**\n * DataSink Subscription Hook\n *\n * Custom hook for subscribing to DataSink updates via GraphQL subscriptions.\n * Supports both sink-level (individual) and workspace-level (all sinks) subscriptions.\n *\n * Note: Subscriptions require WebSocket transport in Apollo Client.\n * In standalone dev mode (HTTP-only), subscriptions are gracefully skipped.\n */\n\nimport { useState, useCallback, useEffect } from 'react';\nimport { useSubscription } from '@apollo/client/react';\nimport { useDataSinkStore } from '../store/dataSinkStore';\nimport { DataSinkDataUpdatedDocument, DataSinkUpdatedDocument } from '../../generated/wspace-operations';\nimport type { DataSink } from '../types';\n\n// ============================================================================\n// Types\n// ============================================================================\n\ninterface UseDataSinkSubscriptionOptions {\n /** Specific DataSink ID to subscribe to (sink-level updates) */\n dataSinkId?: string;\n /** Workspace ID for workspace-level events */\n workspaceId?: string;\n /** Whether subscriptions are enabled */\n enabled?: boolean;\n /** Callback when sink-level update received */\n onUpdate?: (dataSink: DataSink) => void;\n /** Callback when workspace-level event received */\n onWorkspaceEvent?: (event: DataSinkWorkspaceEvent) => void;\n}\n\ninterface DataSinkWorkspaceEvent {\n type: 'CREATED' | 'UPDATED' | 'REFRESHED' | 'DELETED';\n dataSink: DataSink;\n timestamp: string;\n actorId?: string;\n}\n\ninterface UseDataSinkSubscriptionResult {\n /** Whether subscriptions are active */\n isSubscribed: boolean;\n /** Last update timestamp */\n lastUpdate: Date | null;\n /** Subscription error */\n error: Error | null;\n}\n\n// Subscription Result Types\ninterface DataSinkDataUpdatedResult {\n dataSinkDataUpdated?: Record<string, unknown>;\n}\n\ninterface DataSinkUpdatedResult {\n dataSinkUpdated?: {\n type: string;\n dataSink: Record<string, unknown>;\n timestamp: string;\n actorId?: string;\n };\n}\n\n// ============================================================================\n// Helper: Check if error is a known subscription transport issue\n// ============================================================================\n\n/**\n * Detects errors caused by missing WebSocket transport (e.g., standalone dev mode).\n * These are expected and should not be treated as application errors.\n */\nfunction isSubscriptionTransportError(error: unknown): boolean {\n const message = error instanceof Error ? error.message : String(error);\n return (\n message.includes('Async Iterable') ||\n message.includes('Observable') ||\n message.includes('WebSocket') ||\n message.includes('Subscription field must return')\n );\n}\n\n// ============================================================================\n// Helper: Map API response to DataSink type\n// ============================================================================\n\nfunction mapToDataSink(data: Record<string, unknown>): DataSink {\n return {\n id: data.id as string,\n workspaceId: data.workspaceId as string,\n tenantId: data.tenantId as string,\n name: (data.name as string) || null,\n description: (data.description as string) || null,\n key: data.key as string,\n fluidGridsWorkflowId: (data.fluidGridsWorkflowId as string) || null,\n fluidGridsWorkflowExecutionId: (data.fluidGridsWorkflowExecutionId as string) || null,\n config: (data.config as Record<string, unknown>) || null,\n data: (data.data as Record<string, unknown>) || {},\n dataStats: (data.dataStats as DataSink['dataStats']) || null,\n lastRefreshedAt: (data.lastRefreshedAt as string) || new Date().toISOString(),\n lastRefreshedBy: (data.lastRefreshedBy as string) || null,\n expiresAt: (data.expiresAt as string) || null,\n nextRefreshAt: (data.nextRefreshAt as string) || null,\n ttl: (data.ttl as number) ?? 0,\n version: (data.version as number) ?? 1,\n sizeBytes: (data.sizeBytes as number) ?? 0,\n isActive: (data.isActive as boolean) ?? true,\n fieldMaskingEnabled: (data.fieldMaskingEnabled as boolean) ?? false,\n fieldMaskingRules: (data.fieldMaskingRules as Record<string, unknown>) || null,\n status: (data.status as DataSink['status']) || 'ready',\n statusChangedAt: (data.statusChangedAt as string) || null,\n statusChangedBy: (data.statusChangedBy as string) || null,\n setupState: (data.setupState as DataSink['setupState']) || 'not_started',\n refresh: (data.refresh as DataSink['refresh']) || null,\n exports: (data.exports as DataSink['exports']) || null,\n alerts: (data.alerts as DataSink['alerts']) || null,\n quota: (data.quota as DataSink['quota']) || null,\n metadata: (data.metadata as Record<string, unknown>) || null,\n createdAt: data.createdAt as string,\n createdBy: (data.createdBy as string) || '',\n updatedAt: data.updatedAt as string,\n updatedBy: (data.updatedBy as string) || '',\n deletedAt: (data.deletedAt as string) || null,\n deletedBy: (data.deletedBy as string) || null,\n };\n}\n\n// ============================================================================\n// Hook\n// ============================================================================\n\nexport function useDataSinkSubscription({\n dataSinkId,\n workspaceId,\n enabled = true,\n onUpdate,\n onWorkspaceEvent,\n}: UseDataSinkSubscriptionOptions): UseDataSinkSubscriptionResult {\n const [lastUpdate, setLastUpdate] = useState<Date | null>(null);\n const [subscriptionError, setSubscriptionError] = useState<Error | null>(null);\n\n // Store actions\n const updateDataSink = useDataSinkStore((state) => state.updateDataSink);\n const addDataSink = useDataSinkStore((state) => state.addDataSink);\n const removeDataSink = useDataSinkStore((state) => state.removeDataSink);\n\n // Handle sink-level data update\n const handleSinkDataUpdate = useCallback(\n (data: Record<string, unknown>) => {\n const dataSink = mapToDataSink(data);\n updateDataSink(dataSink.id, dataSink);\n setLastUpdate(new Date());\n onUpdate?.(dataSink);\n },\n [updateDataSink, onUpdate]\n );\n\n // Handle workspace-level event\n const handleWorkspaceEvent = useCallback(\n (eventType: string, data: Record<string, unknown>, timestamp: string, actorId?: string) => {\n const dataSink = mapToDataSink(data);\n const event: DataSinkWorkspaceEvent = {\n type: eventType as DataSinkWorkspaceEvent['type'],\n dataSink,\n timestamp,\n actorId,\n };\n\n setLastUpdate(new Date(timestamp));\n\n switch (eventType) {\n case 'CREATED':\n addDataSink(dataSink);\n break;\n case 'UPDATED':\n case 'REFRESHED':\n updateDataSink(dataSink.id, dataSink);\n break;\n case 'DELETED':\n removeDataSink(dataSink.id);\n break;\n default:\n }\n\n onWorkspaceEvent?.(event);\n },\n [addDataSink, updateDataSink, removeDataSink, onWorkspaceEvent]\n );\n\n // Sink-level subscription (for specific DataSink updates)\n const { loading: sinkLoading, error: sinkError } = useSubscription<DataSinkDataUpdatedResult>(\n DataSinkDataUpdatedDocument,\n {\n variables: { id: dataSinkId },\n skip: !enabled || !dataSinkId,\n onData: ({ data }) => {\n if (data.data?.dataSinkDataUpdated) {\n handleSinkDataUpdate(data.data.dataSinkDataUpdated);\n }\n },\n onError: (error) => {\n if (isSubscriptionTransportError(error)) {\n console.debug('[useDataSinkSubscription] Sink-level subscription not available (no WebSocket transport)');\n return;\n }\n setSubscriptionError(error instanceof Error ? error : new Error('Subscription error'));\n },\n }\n );\n\n // Workspace-level subscription (for all DataSink events in workspace)\n const { loading: workspaceLoading, error: workspaceError } = useSubscription<DataSinkUpdatedResult>(\n DataSinkUpdatedDocument,\n {\n variables: { workspaceId: workspaceId ?? '' },\n skip: !enabled || !workspaceId,\n onData: ({ data }) => {\n if (data.data?.dataSinkUpdated) {\n const event = data.data.dataSinkUpdated;\n handleWorkspaceEvent(event.type, event.dataSink, event.timestamp, event.actorId);\n }\n },\n onError: (error) => {\n if (isSubscriptionTransportError(error)) {\n console.debug(\n '[useDataSinkSubscription] Workspace-level subscription not available (no WebSocket transport)'\n );\n return;\n }\n setSubscriptionError(error instanceof Error ? error : new Error('Subscription error'));\n },\n }\n );\n\n // Log subscription status\n useEffect(() => {\n if (enabled && dataSinkId) {\n console.debug('[useDataSinkSubscription] Sink-level subscription active:', dataSinkId);\n }\n if (enabled && workspaceId) {\n console.debug('[useDataSinkSubscription] Workspace-level subscription active:', workspaceId);\n }\n }, [enabled, dataSinkId, workspaceId]);\n\n // Combined error (filter out known transport errors)\n const rawError = sinkError || workspaceError || subscriptionError;\n const error = rawError && !isSubscriptionTransportError(rawError) ? rawError : null;\n\n useEffect(() => {\n if (error) {\n }\n }, [error]);\n\n // Determine if subscriptions are active\n const isSubscribed = !!(enabled && ((dataSinkId && !sinkLoading) || (workspaceId && !workspaceLoading)));\n\n return {\n isSubscribed,\n lastUpdate,\n error,\n };\n}\n\nexport default useDataSinkSubscription;\n"],"mappings":"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"}
@@ -1,17 +1,59 @@
1
1
  import { GetWidgetTypesDocument as e } from "../../generated/wspace-operations.js";
2
2
  import t, { WIDGET_CATEGORY_LABELS as n, getAllWidgetDefinitions as r, getWidgetDefinition as i } from "../components/widgets/WidgetRegistry.js";
3
- import { mapFromFederationWidgetType as a } from "../utils/widgetTypeMapping.js";
4
- import { useEffect as o, useMemo as s } from "react";
5
- import { useQuery as c } from "@tanstack/react-query";
6
- import { print as l } from "graphql";
3
+ import { isFederationAccessible as a, mapFromFederationWidgetType as o } from "../utils/widgetTypeMapping.js";
4
+ import { useEffect as s, useMemo as c } from "react";
5
+ import { useQuery as l } from "@tanstack/react-query";
6
+ import { print as u } from "graphql";
7
7
  //#region src/bigconsole/hooks/useWidgetCatalog.ts
8
- var u = new Set(Object.keys(n));
9
- function d(e) {
8
+ var d = new Set(Object.keys(n)), f = "Puzzle", p = "Installed";
9
+ function m(e) {
10
10
  let n = String(e).toLowerCase();
11
- return Object.prototype.hasOwnProperty.call(t, n) ? n : a(e);
11
+ return Object.prototype.hasOwnProperty.call(t, n) || a(e);
12
12
  }
13
- function f(e) {
14
- let t = d(e.type), n = i(t), r = u.has(e.category) ? e.category : n.category;
13
+ function h(e) {
14
+ let n = String(e).toLowerCase();
15
+ return Object.prototype.hasOwnProperty.call(t, n) ? n : o(e);
16
+ }
17
+ function g(e) {
18
+ return {
19
+ type: "custom",
20
+ name: e.label,
21
+ description: e.description,
22
+ icon: e.icon || f,
23
+ category: p,
24
+ component: null,
25
+ defaultConfig: {
26
+ ...e.defaultConfig ?? {},
27
+ widgetTypeKey: e.type,
28
+ isStoreWidget: !0
29
+ },
30
+ defaultSize: {
31
+ width: e.defaultSize.width,
32
+ height: e.defaultSize.height
33
+ },
34
+ minSize: {
35
+ width: e.minSize.width,
36
+ height: e.minSize.height
37
+ },
38
+ maxSize: {
39
+ width: e.maxSize.width,
40
+ height: e.maxSize.height
41
+ },
42
+ defaultResponsive: {
43
+ xs: 12,
44
+ sm: 6,
45
+ md: 4,
46
+ lg: 4
47
+ },
48
+ supportsDataBinding: !0,
49
+ supportsDrilldown: !0,
50
+ supportsAutoRefresh: !0,
51
+ tags: ["store", "installed"]
52
+ };
53
+ }
54
+ function _(e) {
55
+ if (!m(e.type)) return g(e);
56
+ let t = h(e.type), n = i(t), r = d.has(e.category) ? e.category : n.category;
15
57
  return {
16
58
  type: t,
17
59
  name: e.label,
@@ -39,8 +81,8 @@ function f(e) {
39
81
  tags: n.tags
40
82
  };
41
83
  }
42
- function p(t = {}) {
43
- let { workspaceId: n, apiGatewayUrl: i, authToken: a, enabled: u = !0 } = t, { data: d, isLoading: p, error: m } = c({
84
+ function v(t = {}) {
85
+ let { workspaceId: n, apiGatewayUrl: i, authToken: a, enabled: o = !0 } = t, { data: d, isLoading: f, error: p } = l({
44
86
  queryKey: ["widgetCatalog", n],
45
87
  queryFn: async () => {
46
88
  let t = { "Content-Type": "application/json" };
@@ -48,35 +90,35 @@ function p(t = {}) {
48
90
  let r = i ? `${i}/workspaces/graphql` : "/workspaces/graphql", o = await fetch(r, {
49
91
  method: "POST",
50
92
  headers: t,
51
- body: JSON.stringify({ query: l(e) })
93
+ body: JSON.stringify({ query: u(e) })
52
94
  });
53
95
  if (!o.ok) throw Error(`Failed to fetch widget catalog: ${o.status}`);
54
96
  let s = await o.json();
55
97
  if (s.errors?.length) throw Error(s.errors[0].message);
56
98
  return s.data?.getWidgetTypes ?? [];
57
99
  },
58
- enabled: u && !!n,
100
+ enabled: o && !!n,
59
101
  staleTime: 600 * 1e3
60
102
  });
61
- return o(() => {
62
- m && console.warn("Falling back to bundled widget registry:", m.message);
63
- }, [m]), s(() => d && d.length > 0 ? {
64
- definitions: d.map(f),
103
+ return s(() => {
104
+ p && console.warn("Falling back to bundled widget registry:", p.message);
105
+ }, [p]), c(() => d && d.length > 0 ? {
106
+ definitions: d.map(_),
65
107
  source: "backend",
66
- loading: p,
67
- error: m ?? void 0
108
+ loading: f,
109
+ error: p ?? void 0
68
110
  } : {
69
111
  definitions: r(),
70
112
  source: "static",
71
- loading: p,
72
- error: m ?? void 0
113
+ loading: f,
114
+ error: p ?? void 0
73
115
  }, [
74
116
  d,
75
- p,
76
- m
117
+ f,
118
+ p
77
119
  ]);
78
120
  }
79
121
  //#endregion
80
- export { p as default };
122
+ export { v as default };
81
123
 
82
124
  //# sourceMappingURL=useWidgetCatalog.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useWidgetCatalog.js","names":[],"sources":["../../../src/bigconsole/hooks/useWidgetCatalog.ts"],"sourcesContent":["/**\n * Hook to fetch the widget-type catalog from the backend (`getWidgetTypes`) and\n * merge it over the bundled static registry.\n *\n * The backend is the source of truth for which widget types exist and their\n * palette metadata (label, description, icon, category, defaultConfig, sizes).\n * A few presentational fields the backend doesn't model — defaultResponsive,\n * supports* capability flags, tags, and the (always-null) `component` — are\n * filled in from the matching static `WIDGET_DEFINITIONS` entry.\n *\n * Resilient by design: while the query is loading, or if it errors / returns\n * nothing (e.g. an older supergraph that hasn't picked up the schema yet), the\n * hook returns the full bundled registry so the palette always works.\n *\n * Follows the same fetch/auth convention as {@link useInstalledWidgets}:\n * workspaceId, apiGatewayUrl, and authToken come from BigConsoleProvider context.\n */\n\nimport { useEffect, useMemo } from 'react';\nimport { useQuery } from '@tanstack/react-query';\nimport { print } from 'graphql';\nimport { GetWidgetTypesDocument } from '../../generated/wspace-operations';\nimport type { GetWidgetTypesQuery } from '../../generated/wspace-operations';\nimport {\n WIDGET_CATEGORY_LABELS,\n WIDGET_DEFINITIONS,\n getAllWidgetDefinitions,\n getWidgetDefinition,\n type WidgetDefinition,\n} from '../components/widgets/WidgetRegistry';\nimport { mapFromFederationWidgetType } from '../utils/widgetTypeMapping';\nimport type { WidgetType, WidgetCategory } from '../types';\n\ntype BackendWidgetType = GetWidgetTypesQuery['getWidgetTypes'][number];\n\nconst KNOWN_CATEGORIES = new Set<string>(Object.keys(WIDGET_CATEGORY_LABELS));\n\n/**\n * Resolve a backend `type` value to a valid internal widget-type key.\n *\n * Handles both shapes the field can take:\n * - canonical front-end keys (`metric_card`, `chart`, `map`, …) once the\n * supergraph serves `WidgetTypeInfo.type` as String — used as-is; and\n * - federation `WidgetType` enum values (`KPI`, `LINE_CHART`, `FUNNEL`, …)\n * if an older supergraph still serves the enum — reverse-mapped to the\n * internal key via the shared widgetTypeMapping util.\n *\n * Naive lowercasing alone is wrong: `KPI` -> `kpi`, `LINE_CHART` -> `line_chart`\n * are not internal types and would render as the \"unknown\" definition.\n */\nfunction resolveInternalType(rawType: string): WidgetType {\n const lower = String(rawType).toLowerCase();\n if (Object.prototype.hasOwnProperty.call(WIDGET_DEFINITIONS, lower)) {\n return lower as WidgetType;\n }\n return mapFromFederationWidgetType(rawType);\n}\n\nexport interface UseWidgetCatalogParams {\n workspaceId?: string;\n apiGatewayUrl?: string;\n authToken?: string;\n /** When false, the query is skipped and the static registry is returned. */\n enabled?: boolean;\n}\n\nexport interface UseWidgetCatalogResult {\n /** Merged catalog (backend metadata over static), or the static registry as fallback. */\n definitions: WidgetDefinition[];\n /** Whether the catalog came from the backend (`backend`) or the bundled registry (`static`). */\n source: 'backend' | 'static';\n loading: boolean;\n error: Error | undefined;\n}\n\n/**\n * Build a WidgetDefinition from one backend catalog entry, filling presentational\n * gaps from the matching static definition.\n */\nfunction mergeBackendType(entry: BackendWidgetType): WidgetDefinition {\n // Resolve to a valid internal widget-type key, whether the backend returns a\n // canonical key (\"metric_card\") or a federation enum value (\"KPI\").\n const typeKey = resolveInternalType(entry.type);\n const staticDef = getWidgetDefinition(typeKey);\n\n const category: WidgetCategory = KNOWN_CATEGORIES.has(entry.category)\n ? (entry.category as WidgetCategory)\n : staticDef.category;\n\n return {\n type: typeKey,\n name: entry.label,\n description: entry.description,\n icon: entry.icon,\n category,\n component: null,\n defaultConfig: (entry.defaultConfig as Record<string, unknown>) ?? staticDef.defaultConfig,\n defaultSize: { width: entry.defaultSize.width, height: entry.defaultSize.height },\n minSize: { width: entry.minSize.width, height: entry.minSize.height },\n maxSize: { width: entry.maxSize.width, height: entry.maxSize.height },\n // Fields the backend WidgetTypeInfo does not model — keep the static values.\n defaultResponsive: staticDef.defaultResponsive,\n supportsDataBinding: staticDef.supportsDataBinding,\n supportsDrilldown: staticDef.supportsDrilldown,\n supportsAutoRefresh: staticDef.supportsAutoRefresh,\n tags: staticDef.tags,\n };\n}\n\nexport function useWidgetCatalog(params: UseWidgetCatalogParams = {}): UseWidgetCatalogResult {\n const { workspaceId, apiGatewayUrl, authToken, enabled = true } = params;\n\n const {\n data: backendTypes,\n isLoading: loading,\n error,\n } = useQuery<BackendWidgetType[], Error>({\n // Only workspaceId is part of the cache key; token/url are used inside queryFn\n // but should not invalidate the cache on rotation.\n queryKey: ['widgetCatalog', workspaceId],\n queryFn: async () => {\n const headers: Record<string, string> = { 'Content-Type': 'application/json' };\n if (workspaceId) headers['x-workspace-id'] = workspaceId;\n if (authToken) headers['Authorization'] = `Bearer ${authToken}`;\n\n const url = apiGatewayUrl ? `${apiGatewayUrl}/workspaces/graphql` : '/workspaces/graphql';\n\n const response = await fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify({ query: print(GetWidgetTypesDocument) }),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to fetch widget catalog: ${response.status}`);\n }\n\n const result: { data?: GetWidgetTypesQuery; errors?: { message: string }[] } = await response.json();\n\n if (result.errors?.length) {\n throw new Error(result.errors[0].message);\n }\n\n return result.data?.getWidgetTypes ?? [];\n },\n enabled: enabled && !!workspaceId,\n // The catalog is effectively static config; avoid refetching on every remount.\n staleTime: 10 * 60 * 1000,\n });\n\n useEffect(() => {\n if (error) {\n // Non-fatal: the palette falls back to the bundled registry.\n console.warn('Falling back to bundled widget registry:', error.message);\n }\n }, [error]);\n\n return useMemo<UseWidgetCatalogResult>(() => {\n if (backendTypes && backendTypes.length > 0) {\n return {\n definitions: backendTypes.map(mergeBackendType),\n source: 'backend',\n loading,\n error: error ?? undefined,\n };\n }\n return {\n definitions: getAllWidgetDefinitions(),\n source: 'static',\n loading,\n error: error ?? undefined,\n };\n }, [backendTypes, loading, error]);\n}\n\nexport default useWidgetCatalog;\n"],"mappings":";;;;;;;AAmCA,IAAM,IAAmB,IAAI,IAAY,OAAO,KAAK,EAAuB,CAAC;AAe7E,SAAS,EAAoB,GAA6B;CACxD,IAAM,IAAQ,OAAO,EAAQ,CAAC,aAAa;AAI3C,QAHI,OAAO,UAAU,eAAe,KAAK,GAAoB,EAAM,GAC1D,IAEF,EAA4B,EAAQ;;AAwB7C,SAAS,EAAiB,GAA4C;CAGpE,IAAM,IAAU,EAAoB,EAAM,KAAK,EACzC,IAAY,EAAoB,EAAQ,EAExC,IAA2B,EAAiB,IAAI,EAAM,SAAS,GAChE,EAAM,WACP,EAAU;AAEd,QAAO;EACL,MAAM;EACN,MAAM,EAAM;EACZ,aAAa,EAAM;EACnB,MAAM,EAAM;EACZ;EACA,WAAW;EACX,eAAgB,EAAM,iBAA6C,EAAU;EAC7E,aAAa;GAAE,OAAO,EAAM,YAAY;GAAO,QAAQ,EAAM,YAAY;GAAQ;EACjF,SAAS;GAAE,OAAO,EAAM,QAAQ;GAAO,QAAQ,EAAM,QAAQ;GAAQ;EACrE,SAAS;GAAE,OAAO,EAAM,QAAQ;GAAO,QAAQ,EAAM,QAAQ;GAAQ;EAErE,mBAAmB,EAAU;EAC7B,qBAAqB,EAAU;EAC/B,mBAAmB,EAAU;EAC7B,qBAAqB,EAAU;EAC/B,MAAM,EAAU;EACjB;;AAGH,SAAgB,EAAiB,IAAiC,EAAE,EAA0B;CAC5F,IAAM,EAAE,gBAAa,kBAAe,cAAW,aAAU,OAAS,GAE5D,EACJ,MAAM,GACN,WAAW,GACX,aACE,EAAqC;EAGvC,UAAU,CAAC,iBAAiB,EAAY;EACxC,SAAS,YAAY;GACnB,IAAM,IAAkC,EAAE,gBAAgB,oBAAoB;AAE9E,GADI,MAAa,EAAQ,oBAAoB,IACzC,MAAW,EAAQ,gBAAmB,UAAU;GAEpD,IAAM,IAAM,IAAgB,GAAG,EAAc,uBAAuB,uBAE9D,IAAW,MAAM,MAAM,GAAK;IAChC,QAAQ;IACR;IACA,MAAM,KAAK,UAAU,EAAE,OAAO,EAAM,EAAuB,EAAE,CAAC;IAC/D,CAAC;AAEF,OAAI,CAAC,EAAS,GACZ,OAAU,MAAM,mCAAmC,EAAS,SAAS;GAGvE,IAAM,IAAyE,MAAM,EAAS,MAAM;AAEpG,OAAI,EAAO,QAAQ,OACjB,OAAU,MAAM,EAAO,OAAO,GAAG,QAAQ;AAG3C,UAAO,EAAO,MAAM,kBAAkB,EAAE;;EAE1C,SAAS,KAAW,CAAC,CAAC;EAEtB,WAAW,MAAU;EACtB,CAAC;AASF,QAPA,QAAgB;AACd,EAAI,KAEF,QAAQ,KAAK,4CAA4C,EAAM,QAAQ;IAExE,CAAC,EAAM,CAAC,EAEJ,QACD,KAAgB,EAAa,SAAS,IACjC;EACL,aAAa,EAAa,IAAI,EAAiB;EAC/C,QAAQ;EACR;EACA,OAAO,KAAS,KAAA;EACjB,GAEI;EACL,aAAa,GAAyB;EACtC,QAAQ;EACR;EACA,OAAO,KAAS,KAAA;EACjB,EACA;EAAC;EAAc;EAAS;EAAM,CAAC"}
1
+ {"version":3,"file":"useWidgetCatalog.js","names":[],"sources":["../../../src/bigconsole/hooks/useWidgetCatalog.ts"],"sourcesContent":["/**\n * Hook to fetch the widget-type catalog from the backend (`getWidgetTypes`) and\n * merge it over the bundled static registry.\n *\n * The backend is the source of truth for which widget types exist and their\n * palette metadata (label, description, icon, category, defaultConfig, sizes).\n * A few presentational fields the backend doesn't model — defaultResponsive,\n * supports* capability flags, tags, and the (always-null) `component` — are\n * filled in from the matching static `WIDGET_DEFINITIONS` entry.\n *\n * Resilient by design: while the query is loading, or if it errors / returns\n * nothing (e.g. an older supergraph that hasn't picked up the schema yet), the\n * hook returns the full bundled registry so the palette always works.\n *\n * Follows the same fetch/auth convention as {@link useInstalledWidgets}:\n * workspaceId, apiGatewayUrl, and authToken come from BigConsoleProvider context.\n */\n\nimport { useEffect, useMemo } from 'react';\nimport { useQuery } from '@tanstack/react-query';\nimport { print } from 'graphql';\nimport { GetWidgetTypesDocument } from '../../generated/wspace-operations';\nimport type { GetWidgetTypesQuery } from '../../generated/wspace-operations';\nimport {\n WIDGET_CATEGORY_LABELS,\n WIDGET_DEFINITIONS,\n getAllWidgetDefinitions,\n getWidgetDefinition,\n type WidgetDefinition,\n} from '../components/widgets/WidgetRegistry';\nimport { isFederationAccessible, mapFromFederationWidgetType } from '../utils/widgetTypeMapping';\nimport type { WidgetType, WidgetCategory } from '../types';\n\ntype BackendWidgetType = GetWidgetTypesQuery['getWidgetTypes'][number];\n\nconst KNOWN_CATEGORIES = new Set<string>(Object.keys(WIDGET_CATEGORY_LABELS));\n\n/** Fallbacks for store/custom types whose manifest omits presentational metadata. */\nconst STORE_WIDGET_FALLBACK_ICON = 'Puzzle';\nconst STORE_WIDGET_CATEGORY: WidgetCategory = 'Installed';\n\n/**\n * Whether a backend `type` value corresponds to one of the built-in widget types\n * (either a canonical front-end key like \"metric_card\" or a federation enum value\n * like \"KPI\"). Store/custom types installed from the marketplace (BOFF-2858) use\n * namespaced keys such as \"store/stripe-mrr-card\" and match neither.\n */\nfunction isBuiltInType(rawType: string): boolean {\n const lower = String(rawType).toLowerCase();\n return Object.prototype.hasOwnProperty.call(WIDGET_DEFINITIONS, lower) || isFederationAccessible(rawType);\n}\n\n/**\n * Resolve a backend `type` value to a valid internal widget-type key.\n *\n * Handles both shapes the field can take:\n * - canonical front-end keys (`metric_card`, `chart`, `map`, …) once the\n * supergraph serves `WidgetTypeInfo.type` as String — used as-is; and\n * - federation `WidgetType` enum values (`KPI`, `LINE_CHART`, `FUNNEL`, …)\n * if an older supergraph still serves the enum — reverse-mapped to the\n * internal key via the shared widgetTypeMapping util.\n *\n * Naive lowercasing alone is wrong: `KPI` -> `kpi`, `LINE_CHART` -> `line_chart`\n * are not internal types and would render as the \"unknown\" definition.\n */\nfunction resolveInternalType(rawType: string): WidgetType {\n const lower = String(rawType).toLowerCase();\n if (Object.prototype.hasOwnProperty.call(WIDGET_DEFINITIONS, lower)) {\n return lower as WidgetType;\n }\n return mapFromFederationWidgetType(rawType);\n}\n\nexport interface UseWidgetCatalogParams {\n workspaceId?: string;\n apiGatewayUrl?: string;\n authToken?: string;\n /** When false, the query is skipped and the static registry is returned. */\n enabled?: boolean;\n}\n\nexport interface UseWidgetCatalogResult {\n /** Merged catalog (backend metadata over static), or the static registry as fallback. */\n definitions: WidgetDefinition[];\n /** Whether the catalog came from the backend (`backend`) or the bundled registry (`static`). */\n source: 'backend' | 'static';\n loading: boolean;\n error: Error | undefined;\n}\n\n/**\n * Build a WidgetDefinition for a store/custom widget type (BOFF-2858).\n *\n * Store types have no matching static definition, so everything comes from the\n * backend catalog entry. The namespaced key (e.g. \"store/stripe-mrr-card\") is\n * kept verbatim as the definition `type` and the entry is filed under the\n * \"Installed from Store\" category so the palette groups it separately.\n */\nfunction buildStoreType(entry: BackendWidgetType): WidgetDefinition {\n return {\n // Store types persist as `custom` widget instances — the namespaced key is\n // NOT a valid federated WidgetType enum value, so it cannot be sent to\n // createBigConsoleWidget directly. The real key travels in config\n // (widgetTypeKey), matching the convention useInstalledWidgets already uses\n // for store-installed widgets (isStoreWidget + custom type).\n type: 'custom' as WidgetType,\n name: entry.label,\n description: entry.description,\n icon: entry.icon || STORE_WIDGET_FALLBACK_ICON,\n category: STORE_WIDGET_CATEGORY,\n component: null,\n defaultConfig: {\n ...((entry.defaultConfig as Record<string, unknown>) ?? {}),\n // Preserve the namespaced type so the create flow can set Widget.widgetTypeKey\n // and the renderer can resolve the store type's template.\n widgetTypeKey: entry.type,\n isStoreWidget: true,\n },\n defaultSize: { width: entry.defaultSize.width, height: entry.defaultSize.height },\n minSize: { width: entry.minSize.width, height: entry.minSize.height },\n maxSize: { width: entry.maxSize.width, height: entry.maxSize.height },\n // The backend WidgetTypeInfo does not model these; store widgets support the\n // full data-binding feature set by default.\n defaultResponsive: { xs: 12, sm: 6, md: 4, lg: 4 },\n supportsDataBinding: true,\n supportsDrilldown: true,\n supportsAutoRefresh: true,\n tags: ['store', 'installed'],\n };\n}\n\n/**\n * Build a WidgetDefinition from one backend catalog entry, filling presentational\n * gaps from the matching static definition. Store/custom types (no static match)\n * are handled by {@link buildStoreType} instead of being collapsed onto a\n * built-in key.\n */\nfunction mergeBackendType(entry: BackendWidgetType): WidgetDefinition {\n if (!isBuiltInType(entry.type)) {\n return buildStoreType(entry);\n }\n\n // Resolve to a valid internal widget-type key, whether the backend returns a\n // canonical key (\"metric_card\") or a federation enum value (\"KPI\").\n const typeKey = resolveInternalType(entry.type);\n const staticDef = getWidgetDefinition(typeKey);\n\n const category: WidgetCategory = KNOWN_CATEGORIES.has(entry.category)\n ? (entry.category as WidgetCategory)\n : staticDef.category;\n\n return {\n type: typeKey,\n name: entry.label,\n description: entry.description,\n icon: entry.icon,\n category,\n component: null,\n defaultConfig: (entry.defaultConfig as Record<string, unknown>) ?? staticDef.defaultConfig,\n defaultSize: { width: entry.defaultSize.width, height: entry.defaultSize.height },\n minSize: { width: entry.minSize.width, height: entry.minSize.height },\n maxSize: { width: entry.maxSize.width, height: entry.maxSize.height },\n // Fields the backend WidgetTypeInfo does not model — keep the static values.\n defaultResponsive: staticDef.defaultResponsive,\n supportsDataBinding: staticDef.supportsDataBinding,\n supportsDrilldown: staticDef.supportsDrilldown,\n supportsAutoRefresh: staticDef.supportsAutoRefresh,\n tags: staticDef.tags,\n };\n}\n\nexport function useWidgetCatalog(params: UseWidgetCatalogParams = {}): UseWidgetCatalogResult {\n const { workspaceId, apiGatewayUrl, authToken, enabled = true } = params;\n\n const {\n data: backendTypes,\n isLoading: loading,\n error,\n } = useQuery<BackendWidgetType[], Error>({\n // Only workspaceId is part of the cache key; token/url are used inside queryFn\n // but should not invalidate the cache on rotation.\n queryKey: ['widgetCatalog', workspaceId],\n queryFn: async () => {\n const headers: Record<string, string> = { 'Content-Type': 'application/json' };\n if (workspaceId) headers['x-workspace-id'] = workspaceId;\n if (authToken) headers['Authorization'] = `Bearer ${authToken}`;\n\n const url = apiGatewayUrl ? `${apiGatewayUrl}/workspaces/graphql` : '/workspaces/graphql';\n\n const response = await fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify({ query: print(GetWidgetTypesDocument) }),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to fetch widget catalog: ${response.status}`);\n }\n\n const result: { data?: GetWidgetTypesQuery; errors?: { message: string }[] } = await response.json();\n\n if (result.errors?.length) {\n throw new Error(result.errors[0].message);\n }\n\n return result.data?.getWidgetTypes ?? [];\n },\n enabled: enabled && !!workspaceId,\n // The catalog is effectively static config; avoid refetching on every remount.\n staleTime: 10 * 60 * 1000,\n });\n\n useEffect(() => {\n if (error) {\n // Non-fatal: the palette falls back to the bundled registry.\n console.warn('Falling back to bundled widget registry:', error.message);\n }\n }, [error]);\n\n return useMemo<UseWidgetCatalogResult>(() => {\n if (backendTypes && backendTypes.length > 0) {\n return {\n definitions: backendTypes.map(mergeBackendType),\n source: 'backend',\n loading,\n error: error ?? undefined,\n };\n }\n return {\n definitions: getAllWidgetDefinitions(),\n source: 'static',\n loading,\n error: error ?? undefined,\n };\n }, [backendTypes, loading, error]);\n}\n\nexport default useWidgetCatalog;\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"WidgetDemoPage.js","names":[],"sources":["../../../src/bigconsole/pages/WidgetDemoPage.tsx"],"sourcesContent":["/**\n * WidgetDemoPage\n *\n * Demonstrates all 18 widget types.\n * Widgets show empty states when no real data is available.\n * Connect a DataSink + Parser pipeline for real data processing.\n */\n\nimport { type FC, useState, useMemo } from 'react';\nimport { IllustratedEmptyState, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport type { Widget } from '../types';\nimport { WidgetWrapper } from '../components/widgets';\n\n// Widget type string literals\ntype WidgetType =\n | 'metric_card'\n | 'kpi_card_comparison'\n | 'chart'\n | 'funnel_chart'\n | 'table'\n | 'pivot_table'\n | 'gauge'\n | 'progress'\n | 'list'\n | 'form'\n | 'text'\n | 'iframe'\n | 'map'\n | 'heatmap'\n | 'calendar'\n | 'kanban'\n | 'timeline'\n | 'custom';\n\n// ============================================================================\n// Widget Types for Demo\n// ============================================================================\n\nconst WIDGET_TYPES: { type: WidgetType; title: string; description: string }[] = [\n { type: 'metric_card', title: 'Metric Card', description: 'Single metric with trend and sparkline' },\n { type: 'kpi_card_comparison', title: 'KPI Card Comparison', description: 'Multiple KPIs with comparisons' },\n { type: 'chart', title: 'Chart (Line/Bar/Area)', description: 'Time-series data visualization' },\n { type: 'funnel_chart', title: 'Funnel Chart', description: 'Conversion funnel with stages' },\n { type: 'table', title: 'Table', description: 'Data table with sort/filter/pagination' },\n { type: 'pivot_table', title: 'Pivot Table', description: 'Multi-dimensional data analysis' },\n { type: 'gauge', title: 'Gauge', description: 'Circular/linear gauge with thresholds' },\n { type: 'progress', title: 'Progress', description: 'Progress bar with milestones' },\n { type: 'list', title: 'List', description: 'Scrollable list with items' },\n { type: 'form', title: 'Form (Adaptive Card)', description: 'Interactive form with inputs' },\n { type: 'text', title: 'Text (Markdown)', description: 'Rich text with markdown support' },\n { type: 'iframe', title: 'iFrame', description: 'Embedded external content' },\n { type: 'map', title: 'Map', description: 'Geographic data with markers' },\n { type: 'heatmap', title: 'Heatmap', description: 'Grid visualization with color intensity' },\n { type: 'calendar', title: 'Calendar', description: 'Event calendar view' },\n { type: 'kanban', title: 'Kanban', description: 'Task board with columns' },\n { type: 'timeline', title: 'Timeline (Gantt)', description: 'Project timeline with tasks' },\n { type: 'custom', title: 'Custom', description: 'User-defined widget rendering' },\n];\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const WidgetDemoPage: FC = () => {\n const [filter, setFilter] = useState('');\n const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid');\n\n // Generate demo widgets from all types\n const demoWidgets = useMemo(() => {\n const filtered = filter\n ? WIDGET_TYPES.filter(\n (w) => w.type.includes(filter.toLowerCase()) || w.title.toLowerCase().includes(filter.toLowerCase())\n )\n : WIDGET_TYPES;\n\n return filtered.map(\n (widgetDef, index): Widget => ({\n id: `demo-${widgetDef.type}`,\n pageId: 'demo-page',\n dashboardId: 'demo-dashboard',\n type: widgetDef.type,\n title: widgetDef.title,\n // No data source - widgets show empty state until connected to DataSink\n config: {},\n position: {\n x: (index % 3) * 4,\n y: Math.floor(index / 3) * 4,\n width: 4,\n height: 4,\n },\n positionX: (index % 3) * 4,\n positionY: Math.floor(index / 3) * 4,\n positionWidth: 4,\n positionHeight: 4,\n refreshInterval: undefined,\n lastRefreshedAt: undefined,\n metadata: { description: widgetDef.description },\n context: {},\n translations: [],\n createdAt: new Date().toISOString(),\n updatedAt: new Date().toISOString(),\n })\n );\n }, [filter]);\n\n return (\n <div className=\"h-full flex flex-col bg-bg-canvas\">\n {/* Header */}\n <div className=\"flex-shrink-0 border-b border-border-default bg-bg-surface px-6 py-4\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-xl font-semibold text-text-primary\">Widget Demo</h1>\n <p className=\"text-sm text-text-secondary mt-1\">All 18 widget types - connect a DataSink for real data</p>\n </div>\n <div className=\"flex items-center gap-4\">\n {/* Search Filter */}\n <div className=\"relative\">\n <input\n type=\"text\"\n placeholder=\"Filter widgets...\"\n value={filter}\n onChange={(e) => setFilter(e.target.value)}\n className=\"\n w-64 px-3 py-2 pl-[3.25rem]\n text-sm\n bg-bg-sunken\n border border-border-default\n rounded-lg\n text-text-primary\n placeholder:text-text-tertiary\n focus:outline-none focus:ring-2 focus:ring-action-primary-bg\n \"\n />\n <svg\n className=\"absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-text-secondary\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z\"\n />\n </svg>\n </div>\n\n {/* View Toggle */}\n <div className=\"flex items-center bg-bg-sunken rounded-lg p-1\">\n <button\n type=\"button\"\n onClick={() => setViewMode('grid')}\n className={`\n px-3 py-1.5 text-sm font-medium rounded\n transition-colors\n ${\n viewMode === 'grid'\n ? 'bg-bg-surface text-text-primary shadow-sm'\n : 'text-text-secondary hover:text-text-primary'\n }\n `}\n >\n Grid\n </button>\n <button\n type=\"button\"\n onClick={() => setViewMode('list')}\n className={`\n px-3 py-1.5 text-sm font-medium rounded\n transition-colors\n ${\n viewMode === 'list'\n ? 'bg-bg-surface text-text-primary shadow-sm'\n : 'text-text-secondary hover:text-text-primary'\n }\n `}\n >\n List\n </button>\n </div>\n </div>\n </div>\n </div>\n\n {/* Widget Grid */}\n <div className=\"flex-1 overflow-auto p-6\">\n <PagePurpose className=\"mb-[var(--space-sectionGap)]\">\n This gallery previews every widget type BigConsole can render on a dashboard — metrics, charts, tables, maps,\n kanban boards and more. Use it to see what each widget looks like and pick the right one before wiring it to a\n real DataSink in the dashboard builder.\n </PagePurpose>\n\n <div\n className={\n viewMode === 'grid' ? 'grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6' : 'flex flex-col gap-4'\n }\n >\n {demoWidgets.map((widget) => {\n const widgetDef = WIDGET_TYPES.find((w) => w.type === widget.type);\n return (\n <div key={widget.id} className={viewMode === 'grid' ? 'h-80' : 'h-64'}>\n <div className=\"h-full flex flex-col\">\n {/* Widget Description */}\n <div className=\"flex-shrink-0 mb-2\">\n <span className=\"text-xs text-text-tertiary\">\n Type: <code className=\"font-mono text-action-primary-text\">{widget.type}</code>\n </span>\n {widgetDef && <span className=\"text-xs text-text-tertiary ml-2\">- {widgetDef.description}</span>}\n </div>\n {/* Widget */}\n <div className=\"flex-1 min-h-0\">\n <WidgetWrapper widget={widget} />\n </div>\n </div>\n </div>\n );\n })}\n </div>\n\n {/* Empty State */}\n {demoWidgets.length === 0 && (\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title=\"No widgets match your filter\"\n description=\"Try a different search term to preview a widget type.\"\n />\n )}\n </div>\n\n {/* Footer Stats */}\n <div className=\"flex-shrink-0 border-t border-border-default bg-bg-surface px-6 py-3\">\n <div className=\"flex items-center justify-between text-sm text-text-secondary\">\n <span>\n Showing {demoWidgets.length} of {WIDGET_TYPES.length} widget types\n </span>\n <span>Widgets show empty state until connected to DataSink + Parser</span>\n </div>\n </div>\n </div>\n );\n};\n\nexport default WidgetDemoPage;\n"],"mappings":"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"}
1
+ {"version":3,"file":"WidgetDemoPage.js","names":[],"sources":["../../../src/bigconsole/pages/WidgetDemoPage.tsx"],"sourcesContent":["/**\n * WidgetDemoPage\n *\n * Demonstrates all 18 widget types.\n * Widgets show empty states when no real data is available.\n * Connect a DataSink + Parser pipeline for real data processing.\n */\n\nimport { type FC, useState, useMemo } from 'react';\nimport { IllustratedEmptyState, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport type { Widget } from '../types';\nimport { WidgetWrapper } from '../components/widgets';\n\n// Widget type string literals\ntype WidgetType =\n | 'metric_card'\n | 'kpi_card_comparison'\n | 'chart'\n | 'funnel_chart'\n | 'table'\n | 'pivot_table'\n | 'gauge'\n | 'progress'\n | 'list'\n | 'form'\n | 'text'\n | 'iframe'\n | 'map'\n | 'heatmap'\n | 'calendar'\n | 'kanban'\n | 'timeline'\n | 'custom';\n\n// ============================================================================\n// Widget Types for Demo\n// ============================================================================\n\nconst WIDGET_TYPES: { type: WidgetType; title: string; description: string }[] = [\n { type: 'metric_card', title: 'Metric Card', description: 'Single metric with trend and sparkline' },\n { type: 'kpi_card_comparison', title: 'KPI Card Comparison', description: 'Multiple KPIs with comparisons' },\n { type: 'chart', title: 'Chart (Line/Bar/Area)', description: 'Time-series data visualization' },\n { type: 'funnel_chart', title: 'Funnel Chart', description: 'Conversion funnel with stages' },\n { type: 'table', title: 'Table', description: 'Data table with sort/filter/pagination' },\n { type: 'pivot_table', title: 'Pivot Table', description: 'Multi-dimensional data analysis' },\n { type: 'gauge', title: 'Gauge', description: 'Circular/linear gauge with thresholds' },\n { type: 'progress', title: 'Progress', description: 'Progress bar with milestones' },\n { type: 'list', title: 'List', description: 'Scrollable list with items' },\n { type: 'form', title: 'Form (Adaptive Card)', description: 'Interactive form with inputs' },\n { type: 'text', title: 'Text (Markdown)', description: 'Rich text with markdown support' },\n { type: 'iframe', title: 'iFrame', description: 'Embedded external content' },\n { type: 'map', title: 'Map', description: 'Geographic data with markers' },\n { type: 'heatmap', title: 'Heatmap', description: 'Grid visualization with color intensity' },\n { type: 'calendar', title: 'Calendar', description: 'Event calendar view' },\n { type: 'kanban', title: 'Kanban', description: 'Task board with columns' },\n { type: 'timeline', title: 'Timeline (Gantt)', description: 'Project timeline with tasks' },\n { type: 'custom', title: 'Custom', description: 'User-defined widget rendering' },\n];\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const WidgetDemoPage: FC = () => {\n const [filter, setFilter] = useState('');\n const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid');\n\n // Generate demo widgets from all types\n const demoWidgets = useMemo(() => {\n const filtered = filter\n ? WIDGET_TYPES.filter(\n (w) => w.type.includes(filter.toLowerCase()) || w.title.toLowerCase().includes(filter.toLowerCase())\n )\n : WIDGET_TYPES;\n\n return filtered.map((widgetDef, index): Widget => ({\n id: `demo-${widgetDef.type}`,\n pageId: 'demo-page',\n dashboardId: 'demo-dashboard',\n type: widgetDef.type,\n title: widgetDef.title,\n // No data source - widgets show empty state until connected to DataSink\n config: {},\n position: {\n x: (index % 3) * 4,\n y: Math.floor(index / 3) * 4,\n width: 4,\n height: 4,\n },\n positionX: (index % 3) * 4,\n positionY: Math.floor(index / 3) * 4,\n positionWidth: 4,\n positionHeight: 4,\n refreshInterval: undefined,\n lastRefreshedAt: undefined,\n metadata: { description: widgetDef.description },\n context: {},\n translations: [],\n createdAt: new Date().toISOString(),\n updatedAt: new Date().toISOString(),\n }));\n }, [filter]);\n\n return (\n <div className=\"h-full flex flex-col bg-bg-canvas\">\n {/* Header */}\n <div className=\"flex-shrink-0 border-b border-border-default bg-bg-surface px-6 py-4\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-xl font-semibold text-text-primary\">Widget Demo</h1>\n <p className=\"text-sm text-text-secondary mt-1\">All 18 widget types - connect a DataSink for real data</p>\n </div>\n <div className=\"flex items-center gap-4\">\n {/* Search Filter */}\n <div className=\"relative\">\n <input\n type=\"text\"\n placeholder=\"Filter widgets...\"\n value={filter}\n onChange={(e) => setFilter(e.target.value)}\n className=\"\n w-64 px-3 py-2 pl-[3.25rem]\n text-sm\n bg-bg-sunken\n border border-border-default\n rounded-lg\n text-text-primary\n placeholder:text-text-tertiary\n focus:outline-none focus:ring-2 focus:ring-action-primary-bg\n \"\n />\n <svg\n className=\"absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-text-secondary\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z\"\n />\n </svg>\n </div>\n\n {/* View Toggle */}\n <div className=\"flex items-center bg-bg-sunken rounded-lg p-1\">\n <button\n type=\"button\"\n onClick={() => setViewMode('grid')}\n className={`\n px-3 py-1.5 text-sm font-medium rounded\n transition-colors\n ${\n viewMode === 'grid'\n ? 'bg-bg-surface text-text-primary shadow-sm'\n : 'text-text-secondary hover:text-text-primary'\n }\n `}\n >\n Grid\n </button>\n <button\n type=\"button\"\n onClick={() => setViewMode('list')}\n className={`\n px-3 py-1.5 text-sm font-medium rounded\n transition-colors\n ${\n viewMode === 'list'\n ? 'bg-bg-surface text-text-primary shadow-sm'\n : 'text-text-secondary hover:text-text-primary'\n }\n `}\n >\n List\n </button>\n </div>\n </div>\n </div>\n </div>\n\n {/* Widget Grid */}\n <div className=\"flex-1 overflow-auto p-6\">\n <PagePurpose className=\"mb-[var(--space-sectionGap)]\">\n This gallery previews every widget type BigConsole can render on a dashboard — metrics, charts, tables, maps,\n kanban boards and more. Use it to see what each widget looks like and pick the right one before wiring it to a\n real DataSink in the dashboard builder.\n </PagePurpose>\n\n <div\n className={\n viewMode === 'grid' ? 'grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6' : 'flex flex-col gap-4'\n }\n >\n {demoWidgets.map((widget) => {\n const widgetDef = WIDGET_TYPES.find((w) => w.type === widget.type);\n return (\n <div key={widget.id} className={viewMode === 'grid' ? 'h-80' : 'h-64'}>\n <div className=\"h-full flex flex-col\">\n {/* Widget Description */}\n <div className=\"flex-shrink-0 mb-2\">\n <span className=\"text-xs text-text-tertiary\">\n Type: <code className=\"font-mono text-action-primary-text\">{widget.type}</code>\n </span>\n {widgetDef && <span className=\"text-xs text-text-tertiary ml-2\">- {widgetDef.description}</span>}\n </div>\n {/* Widget */}\n <div className=\"flex-1 min-h-0\">\n <WidgetWrapper widget={widget} />\n </div>\n </div>\n </div>\n );\n })}\n </div>\n\n {/* Empty State */}\n {demoWidgets.length === 0 && (\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title=\"No widgets match your filter\"\n description=\"Try a different search term to preview a widget type.\"\n />\n )}\n </div>\n\n {/* Footer Stats */}\n <div className=\"flex-shrink-0 border-t border-border-default bg-bg-surface px-6 py-3\">\n <div className=\"flex items-center justify-between text-sm text-text-secondary\">\n <span>\n Showing {demoWidgets.length} of {WIDGET_TYPES.length} widget types\n </span>\n <span>Widgets show empty state until connected to DataSink + Parser</span>\n </div>\n </div>\n </div>\n );\n};\n\nexport default WidgetDemoPage;\n"],"mappings":"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"}
@@ -52,6 +52,6 @@ function r(t) {
52
52
  return n(t) ? t.toUpperCase() : e(t);
53
53
  }
54
54
  //#endregion
55
- export { t as mapFromFederationWidgetType, r as normalizeWidgetTypeForApi };
55
+ export { n as isFederationAccessible, t as mapFromFederationWidgetType, r as normalizeWidgetTypeForApi };
56
56
 
57
57
  //# sourceMappingURL=widgetTypeMapping.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"widgetTypeMapping.js","names":[],"sources":["../../../src/bigconsole/utils/widgetTypeMapping.ts"],"sourcesContent":["/**\n * Widget Type Mapping Utilities\n *\n * Maps internal UI widget types (snake_case) to federation-accessible GraphQL enum values (UPPER_CASE).\n * The supergraph only exposes certain WidgetType values - other values are marked @inaccessible.\n *\n * Federation-accessible values: AREA_CHART, BAR_CHART, FUNNEL, HEATMAP, KPI, LINE_CHART, PIE_CHART, RETENTION, TABLE\n */\n\nimport type { WidgetType as InternalWidgetType } from '../types';\n\n// Federation-accessible WidgetType enum values\nexport type FederationWidgetType =\n | 'AREA_CHART'\n | 'BAR_CHART'\n | 'FUNNEL'\n | 'HEATMAP'\n | 'KPI'\n | 'LINE_CHART'\n | 'PIE_CHART'\n | 'RETENTION'\n | 'TABLE';\n\n/**\n * Maps internal UI widget types to federation-accessible GraphQL enum values.\n *\n * This mapping is necessary because:\n * 1. The UI uses snake_case widget types (e.g., 'kpi_card_comparison', 'metric_card')\n * 2. The BigConsole GraphQL schema defines many widget types\n * 3. But only some are exposed via federation (@inaccessible hides the rest)\n * 4. When creating/updating widgets through the gateway, we must use accessible values\n */\nexport function mapToFederationWidgetType(internalType: InternalWidgetType): FederationWidgetType {\n const mapping: Record<InternalWidgetType, FederationWidgetType> = {\n // KPI/Metric types -> KPI\n kpi_card_comparison: 'KPI',\n metric_card: 'KPI',\n\n // Chart types -> appropriate chart enum\n chart: 'LINE_CHART',\n funnel_chart: 'FUNNEL',\n\n // Direct mappings (already federation-accessible when uppercase)\n table: 'TABLE',\n pivot_table: 'TABLE',\n\n // Gauge and progress -> KPI (closest match)\n gauge: 'KPI',\n progress: 'KPI',\n\n // List -> TABLE\n list: 'TABLE',\n\n // Form -> TABLE (data display fallback)\n form: 'TABLE',\n\n // Text/iframe -> KPI (content display fallback)\n text: 'KPI',\n iframe: 'KPI',\n\n // Spatial/temporal types\n map: 'HEATMAP',\n heatmap: 'HEATMAP',\n calendar: 'TABLE',\n kanban: 'TABLE',\n timeline: 'TABLE',\n\n // Retention / cohort grid (federation-accessible)\n retention: 'RETENTION',\n\n // Custom -> KPI (generic fallback)\n custom: 'KPI',\n };\n\n return mapping[internalType] || 'KPI';\n}\n\n/**\n * Maps federation GraphQL enum values back to internal UI widget types.\n * Used when receiving data from the API.\n *\n * Note: This is a best-effort reverse mapping since multiple internal types\n * may map to the same federation type. The actual internal type should be\n * stored in widget metadata/config if precise type preservation is needed.\n */\nexport function mapFromFederationWidgetType(federationType: string): InternalWidgetType {\n // Normalize to uppercase for comparison\n const normalizedType = federationType.toUpperCase();\n\n const reverseMapping: Record<string, InternalWidgetType> = {\n KPI: 'kpi_card_comparison',\n LINE_CHART: 'chart',\n BAR_CHART: 'chart',\n AREA_CHART: 'chart',\n PIE_CHART: 'chart',\n TABLE: 'table',\n FUNNEL: 'funnel_chart',\n HEATMAP: 'heatmap',\n RETENTION: 'table',\n };\n\n return reverseMapping[normalizedType] || 'metric_card';\n}\n\n/**\n * Checks if a widget type is directly accessible via federation.\n * If true, it can be used as-is in GraphQL mutations through the gateway.\n */\nexport function isFederationAccessible(type: string): boolean {\n const accessibleTypes = new Set([\n 'AREA_CHART',\n 'BAR_CHART',\n 'FUNNEL',\n 'HEATMAP',\n 'KPI',\n 'LINE_CHART',\n 'PIE_CHART',\n 'RETENTION',\n 'TABLE',\n ]);\n\n return accessibleTypes.has(type.toUpperCase());\n}\n\n/**\n * Normalizes a widget type for API calls.\n * If the type is already federation-accessible, returns it uppercase.\n * Otherwise, maps it to the closest federation-accessible type.\n */\nexport function normalizeWidgetTypeForApi(type: string): FederationWidgetType {\n // Check if already a federation type (case-insensitive)\n if (isFederationAccessible(type)) {\n return type.toUpperCase() as FederationWidgetType;\n }\n\n // Otherwise, map from internal type\n return mapToFederationWidgetType(type as InternalWidgetType);\n}\n"],"mappings":";AAgCA,SAAgB,EAA0B,GAAwD;AA0ChG,QAzCkE;EAEhE,qBAAqB;EACrB,aAAa;EAGb,OAAO;EACP,cAAc;EAGd,OAAO;EACP,aAAa;EAGb,OAAO;EACP,UAAU;EAGV,MAAM;EAGN,MAAM;EAGN,MAAM;EACN,QAAQ;EAGR,KAAK;EACL,SAAS;EACT,UAAU;EACV,QAAQ;EACR,UAAU;EAGV,WAAW;EAGX,QAAQ;EACT,CAEc,MAAiB;;AAWlC,SAAgB,EAA4B,GAA4C;AAgBtF,QAZ2D;EACzD,KAAK;EACL,YAAY;EACZ,WAAW;EACX,YAAY;EACZ,WAAW;EACX,OAAO;EACP,QAAQ;EACR,SAAS;EACT,WAAW;EACZ,CAZsB,EAAe,aAAa,KAcV;;AAO3C,SAAgB,EAAuB,GAAuB;AAa5D,QAZwB,IAAI,IAAI;EAC9B;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAAC,CAEqB,IAAI,EAAK,aAAa,CAAC;;AAQhD,SAAgB,EAA0B,GAAoC;AAO5E,QALI,EAAuB,EAAK,GACvB,EAAK,aAAa,GAIpB,EAA0B,EAA2B"}
1
+ {"version":3,"file":"widgetTypeMapping.js","names":[],"sources":["../../../src/bigconsole/utils/widgetTypeMapping.ts"],"sourcesContent":["/**\n * Widget Type Mapping Utilities\n *\n * Maps internal UI widget types (snake_case) to federation-accessible GraphQL enum values (UPPER_CASE).\n * The supergraph only exposes certain WidgetType values - other values are marked @inaccessible.\n *\n * Federation-accessible values: AREA_CHART, BAR_CHART, FUNNEL, HEATMAP, KPI, LINE_CHART, PIE_CHART, RETENTION, TABLE\n */\n\nimport type { WidgetType as InternalWidgetType } from '../types';\n\n// Federation-accessible WidgetType enum values\nexport type FederationWidgetType =\n 'AREA_CHART' | 'BAR_CHART' | 'FUNNEL' | 'HEATMAP' | 'KPI' | 'LINE_CHART' | 'PIE_CHART' | 'RETENTION' | 'TABLE';\n\n/**\n * Maps internal UI widget types to federation-accessible GraphQL enum values.\n *\n * This mapping is necessary because:\n * 1. The UI uses snake_case widget types (e.g., 'kpi_card_comparison', 'metric_card')\n * 2. The BigConsole GraphQL schema defines many widget types\n * 3. But only some are exposed via federation (@inaccessible hides the rest)\n * 4. When creating/updating widgets through the gateway, we must use accessible values\n */\nexport function mapToFederationWidgetType(internalType: InternalWidgetType): FederationWidgetType {\n const mapping: Record<InternalWidgetType, FederationWidgetType> = {\n // KPI/Metric types -> KPI\n kpi_card_comparison: 'KPI',\n metric_card: 'KPI',\n\n // Chart types -> appropriate chart enum\n chart: 'LINE_CHART',\n funnel_chart: 'FUNNEL',\n\n // Direct mappings (already federation-accessible when uppercase)\n table: 'TABLE',\n pivot_table: 'TABLE',\n\n // Gauge and progress -> KPI (closest match)\n gauge: 'KPI',\n progress: 'KPI',\n\n // List -> TABLE\n list: 'TABLE',\n\n // Form -> TABLE (data display fallback)\n form: 'TABLE',\n\n // Text/iframe -> KPI (content display fallback)\n text: 'KPI',\n iframe: 'KPI',\n\n // Spatial/temporal types\n map: 'HEATMAP',\n heatmap: 'HEATMAP',\n calendar: 'TABLE',\n kanban: 'TABLE',\n timeline: 'TABLE',\n\n // Retention / cohort grid (federation-accessible)\n retention: 'RETENTION',\n\n // Custom -> KPI (generic fallback)\n custom: 'KPI',\n };\n\n return mapping[internalType] || 'KPI';\n}\n\n/**\n * Maps federation GraphQL enum values back to internal UI widget types.\n * Used when receiving data from the API.\n *\n * Note: This is a best-effort reverse mapping since multiple internal types\n * may map to the same federation type. The actual internal type should be\n * stored in widget metadata/config if precise type preservation is needed.\n */\nexport function mapFromFederationWidgetType(federationType: string): InternalWidgetType {\n // Normalize to uppercase for comparison\n const normalizedType = federationType.toUpperCase();\n\n const reverseMapping: Record<string, InternalWidgetType> = {\n KPI: 'kpi_card_comparison',\n LINE_CHART: 'chart',\n BAR_CHART: 'chart',\n AREA_CHART: 'chart',\n PIE_CHART: 'chart',\n TABLE: 'table',\n FUNNEL: 'funnel_chart',\n HEATMAP: 'heatmap',\n RETENTION: 'table',\n };\n\n return reverseMapping[normalizedType] || 'metric_card';\n}\n\n/**\n * Checks if a widget type is directly accessible via federation.\n * If true, it can be used as-is in GraphQL mutations through the gateway.\n */\nexport function isFederationAccessible(type: string): boolean {\n const accessibleTypes = new Set([\n 'AREA_CHART',\n 'BAR_CHART',\n 'FUNNEL',\n 'HEATMAP',\n 'KPI',\n 'LINE_CHART',\n 'PIE_CHART',\n 'RETENTION',\n 'TABLE',\n ]);\n\n return accessibleTypes.has(type.toUpperCase());\n}\n\n/**\n * Normalizes a widget type for API calls.\n * If the type is already federation-accessible, returns it uppercase.\n * Otherwise, maps it to the closest federation-accessible type.\n */\nexport function normalizeWidgetTypeForApi(type: string): FederationWidgetType {\n // Check if already a federation type (case-insensitive)\n if (isFederationAccessible(type)) {\n return type.toUpperCase() as FederationWidgetType;\n }\n\n // Otherwise, map from internal type\n return mapToFederationWidgetType(type as InternalWidgetType);\n}\n"],"mappings":";AAwBA,SAAgB,EAA0B,GAAwD;AA0ChG,QAzCkE;EAEhE,qBAAqB;EACrB,aAAa;EAGb,OAAO;EACP,cAAc;EAGd,OAAO;EACP,aAAa;EAGb,OAAO;EACP,UAAU;EAGV,MAAM;EAGN,MAAM;EAGN,MAAM;EACN,QAAQ;EAGR,KAAK;EACL,SAAS;EACT,UAAU;EACV,QAAQ;EACR,UAAU;EAGV,WAAW;EAGX,QAAQ;EACT,CAEc,MAAiB;;AAWlC,SAAgB,EAA4B,GAA4C;AAgBtF,QAZ2D;EACzD,KAAK;EACL,YAAY;EACZ,WAAW;EACX,YAAY;EACZ,WAAW;EACX,OAAO;EACP,QAAQ;EACR,SAAS;EACT,WAAW;EACZ,CAZsB,EAAe,aAAa,KAcV;;AAO3C,SAAgB,EAAuB,GAAuB;AAa5D,QAZwB,IAAI,IAAI;EAC9B;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAAC,CAEqB,IAAI,EAAK,aAAa,CAAC;;AAQhD,SAAgB,EAA0B,GAAoC;AAO5E,QALI,EAAuB,EAAK,GACvB,EAAK,aAAa,GAIpB,EAA0B,EAA2B"}
@@ -520,7 +520,7 @@ ${e`
520
520
  fragment PromptGenerationFields on PromptGeneration {
521
521
  id
522
522
  workspaceId
523
- userId
523
+ actorId
524
524
  prompt
525
525
  status
526
526
  dashboardId
@@ -1484,7 +1484,7 @@ ${h}`, Ze = e`
1484
1484
  ...DataSinkListItem
1485
1485
  }
1486
1486
  timestamp
1487
- userId
1487
+ actorId
1488
1488
  }
1489
1489
  }
1490
1490
  ${m}`;