@burdenoff/microfe-bigconsole 2026.729.1 → 2026.729.2

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.
Files changed (109) hide show
  1. package/dist/bigconsole/components/DrilldownDashboardRenderer.js +1 -1
  2. package/dist/bigconsole/components/DrilldownDashboardRenderer.js.map +1 -1
  3. package/dist/bigconsole/components/builder/inspector/DataBindingSection.js +4 -4
  4. package/dist/bigconsole/components/builder/inspector/DataBindingSection.js.map +1 -1
  5. package/dist/bigconsole/components/datasink/CacheTTLSelector.js +5 -5
  6. package/dist/bigconsole/components/datasink/CacheTTLSelector.js.map +1 -1
  7. package/dist/bigconsole/components/datasink/DataSinkCard.js +3 -3
  8. package/dist/bigconsole/components/datasink/DataSinkCard.js.map +1 -1
  9. package/dist/bigconsole/components/datasink/DataSinkDeleteDialog.js +4 -4
  10. package/dist/bigconsole/components/datasink/DataSinkDeleteDialog.js.map +1 -1
  11. package/dist/bigconsole/components/datasink/DataSinkStatusBadge.js +6 -6
  12. package/dist/bigconsole/components/datasink/DataSinkStatusBadge.js.map +1 -1
  13. package/dist/bigconsole/components/datasink/LinkedWorkflowsPanel.js +9 -9
  14. package/dist/bigconsole/components/datasink/LinkedWorkflowsPanel.js.map +1 -1
  15. package/dist/bigconsole/components/datasink/RefreshTypeSelector.js +2 -2
  16. package/dist/bigconsole/components/datasink/RefreshTypeSelector.js.map +1 -1
  17. package/dist/bigconsole/components/datasink/SetupStateBadge.js +8 -8
  18. package/dist/bigconsole/components/datasink/SetupStateBadge.js.map +1 -1
  19. package/dist/bigconsole/components/datasink/WorkflowStatusPanel.js +13 -13
  20. package/dist/bigconsole/components/datasink/WorkflowStatusPanel.js.map +1 -1
  21. package/dist/bigconsole/components/parser/DataSinkSelector.js +2 -2
  22. package/dist/bigconsole/components/parser/DataSinkSelector.js.map +1 -1
  23. package/dist/bigconsole/components/parser/ExpressionEditor.js +1 -1
  24. package/dist/bigconsole/components/parser/ExpressionEditor.js.map +1 -1
  25. package/dist/bigconsole/components/parser/ParserCard.js +1 -1
  26. package/dist/bigconsole/components/parser/ParserCard.js.map +1 -1
  27. package/dist/bigconsole/components/parser/ParserSelector.js +2 -2
  28. package/dist/bigconsole/components/parser/ParserSelector.js.map +1 -1
  29. package/dist/bigconsole/components/parser/ParserStatusBadge.js +2 -2
  30. package/dist/bigconsole/components/parser/ParserStatusBadge.js.map +1 -1
  31. package/dist/bigconsole/components/pipeline/PipelineCard.js +2 -2
  32. package/dist/bigconsole/components/pipeline/PipelineCard.js.map +1 -1
  33. package/dist/bigconsole/components/pipeline/PipelineDeleteDialog.js +5 -5
  34. package/dist/bigconsole/components/pipeline/PipelineDeleteDialog.js.map +1 -1
  35. package/dist/bigconsole/components/pipeline/PipelineStatusBadge.js +3 -3
  36. package/dist/bigconsole/components/pipeline/PipelineStatusBadge.js.map +1 -1
  37. package/dist/bigconsole/components/widgets/DrilldownConfigPanel.js +1 -1
  38. package/dist/bigconsole/components/widgets/DrilldownConfigPanel.js.map +1 -1
  39. package/dist/bigconsole/components/widgets/PropertiesPanel.js +3 -3
  40. package/dist/bigconsole/components/widgets/PropertiesPanel.js.map +1 -1
  41. package/dist/bigconsole/components/widgets/WidgetErrorBoundary.js +2 -2
  42. package/dist/bigconsole/components/widgets/WidgetErrorBoundary.js.map +1 -1
  43. package/dist/bigconsole/components/widgets/WidgetMenu.js +1 -1
  44. package/dist/bigconsole/components/widgets/WidgetMenu.js.map +1 -1
  45. package/dist/bigconsole/components/widgets/adaptive-card/AdaptiveCardWidget.js +3 -3
  46. package/dist/bigconsole/components/widgets/adaptive-card/AdaptiveCardWidget.js.map +1 -1
  47. package/dist/bigconsole/components/widgets/calendar/CalendarWidget.js +1 -1
  48. package/dist/bigconsole/components/widgets/calendar/CalendarWidget.js.map +1 -1
  49. package/dist/bigconsole/components/widgets/gauge/GaugeConfig.js +3 -3
  50. package/dist/bigconsole/components/widgets/gauge/GaugeConfig.js.map +1 -1
  51. package/dist/bigconsole/components/widgets/heatmap/HeatmapWidget.js +52 -32
  52. package/dist/bigconsole/components/widgets/heatmap/HeatmapWidget.js.map +1 -1
  53. package/dist/bigconsole/components/widgets/kanban/KanbanWidget.js +3 -3
  54. package/dist/bigconsole/components/widgets/kanban/KanbanWidget.js.map +1 -1
  55. package/dist/bigconsole/components/widgets/kpi/KPIWidget.js +1 -1
  56. package/dist/bigconsole/components/widgets/kpi/KPIWidget.js.map +1 -1
  57. package/dist/bigconsole/components/widgets/kpi-comparison/KPIComparisonWidget.js +2 -2
  58. package/dist/bigconsole/components/widgets/kpi-comparison/KPIComparisonWidget.js.map +1 -1
  59. package/dist/bigconsole/components/widgets/list/ListItem.js +3 -3
  60. package/dist/bigconsole/components/widgets/list/ListItem.js.map +1 -1
  61. package/dist/bigconsole/components/widgets/metric-card/ComparisonBadge.js +1 -1
  62. package/dist/bigconsole/components/widgets/metric-card/ComparisonBadge.js.map +1 -1
  63. package/dist/bigconsole/components/widgets/metric-card/MetricCardWidget.js +1 -1
  64. package/dist/bigconsole/components/widgets/metric-card/MetricCardWidget.js.map +1 -1
  65. package/dist/bigconsole/components/widgets/metric-card/SparklineChart.js +6 -6
  66. package/dist/bigconsole/components/widgets/metric-card/SparklineChart.js.map +1 -1
  67. package/dist/bigconsole/components/widgets/metric-card/TrendIndicator.js +1 -1
  68. package/dist/bigconsole/components/widgets/metric-card/TrendIndicator.js.map +1 -1
  69. package/dist/bigconsole/components/widgets/progress/ProgressBar.js +4 -4
  70. package/dist/bigconsole/components/widgets/progress/ProgressBar.js.map +1 -1
  71. package/dist/bigconsole/components/widgets/progress/ProgressConfig.js +1 -1
  72. package/dist/bigconsole/components/widgets/progress/ProgressConfig.js.map +1 -1
  73. package/dist/bigconsole/components/widgets/progress/ProgressWidget.js +1 -1
  74. package/dist/bigconsole/components/widgets/progress/ProgressWidget.js.map +1 -1
  75. package/dist/bigconsole/components/widgets/retention/RetentionWidget.js +2 -2
  76. package/dist/bigconsole/components/widgets/retention/RetentionWidget.js.map +1 -1
  77. package/dist/bigconsole/components/widgets/states/ErrorState.js +2 -2
  78. package/dist/bigconsole/components/widgets/states/ErrorState.js.map +1 -1
  79. package/dist/bigconsole/components/widgets/table/TableConfig.js +2 -2
  80. package/dist/bigconsole/components/widgets/table/TableConfig.js.map +1 -1
  81. package/dist/bigconsole/components/widgets/timeline/TimelineWidget.js +1 -1
  82. package/dist/bigconsole/components/widgets/timeline/TimelineWidget.js.map +1 -1
  83. package/dist/bigconsole/components/widgets/widget-actions/WidgetActions.js +6 -6
  84. package/dist/bigconsole/components/widgets/widget-actions/WidgetActions.js.map +1 -1
  85. package/dist/bigconsole/components/workflow/WorkflowCard.js +1 -1
  86. package/dist/bigconsole/components/workflow/WorkflowCard.js.map +1 -1
  87. package/dist/bigconsole/components/workflow/WorkflowDeleteDialog.js +5 -5
  88. package/dist/bigconsole/components/workflow/WorkflowDeleteDialog.js.map +1 -1
  89. package/dist/bigconsole/components/workflow/WorkflowStatusBadge.js +6 -6
  90. package/dist/bigconsole/components/workflow/WorkflowStatusBadge.js.map +1 -1
  91. package/dist/bigconsole/pages/DataSinkBuilderPage.js +17 -17
  92. package/dist/bigconsole/pages/DataSinkBuilderPage.js.map +1 -1
  93. package/dist/bigconsole/pages/DataSinkViewPage.js +2 -2
  94. package/dist/bigconsole/pages/DataSinkViewPage.js.map +1 -1
  95. package/dist/bigconsole/pages/ParserBuilderPage.js +24 -24
  96. package/dist/bigconsole/pages/ParserBuilderPage.js.map +1 -1
  97. package/dist/bigconsole/pages/ParserViewPage.js +8 -8
  98. package/dist/bigconsole/pages/ParserViewPage.js.map +1 -1
  99. package/dist/bigconsole/pages/PipelineBuilderPage.js +9 -9
  100. package/dist/bigconsole/pages/PipelineBuilderPage.js.map +1 -1
  101. package/dist/bigconsole/pages/PipelineViewPage.js +12 -12
  102. package/dist/bigconsole/pages/PipelineViewPage.js.map +1 -1
  103. package/dist/bigconsole/pages/WorkflowViewPage.js +6 -6
  104. package/dist/bigconsole/pages/WorkflowViewPage.js.map +1 -1
  105. package/dist/bigconsole/renderers/adapters/AdaptiveCardRenderer.js +1 -1
  106. package/dist/bigconsole/renderers/adapters/AdaptiveCardRenderer.js.map +1 -1
  107. package/dist/bigconsole/renderers/adapters/BlockKitRenderer.js +2 -2
  108. package/dist/bigconsole/renderers/adapters/BlockKitRenderer.js.map +1 -1
  109. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"WidgetMenu.js","names":[],"sources":["../../../../src/bigconsole/components/widgets/WidgetMenu.tsx"],"sourcesContent":["/**\n * WidgetMenu Component\n *\n * Dropdown menu with widget actions (edit, duplicate, delete, etc.)\n */\n\nimport { type FC, type ReactElement, useState, useRef, useEffect, useCallback, memo } from 'react';\nimport { createPortal } from 'react-dom';\nimport type { Widget } from '../../types';\nimport { useWidgetStore } from '../../store';\nimport { useWidgetOperations } from '../../hooks';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface WidgetMenuProps {\n /** The widget data */\n widget: Widget;\n /** Whether in edit mode */\n isEditMode?: boolean;\n /** Callback to refresh widget */\n onRefresh?: () => void;\n /** Callback to export as CSV */\n onExportCSV?: () => void;\n /** Callback to export as PNG */\n onExportPNG?: () => void;\n}\n\ninterface MenuItem {\n id: string;\n label: string;\n icon: ReactElement;\n action: () => void;\n disabled?: boolean;\n destructive?: boolean;\n divider?: boolean;\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const WidgetMenu: FC<WidgetMenuProps> = memo(function WidgetMenu({\n widget,\n isEditMode,\n onRefresh,\n onExportCSV,\n onExportPNG,\n}) {\n const [isOpen, setIsOpen] = useState(false);\n const [menuPosition, setMenuPosition] = useState({ top: 0, left: 0 });\n const menuRef = useRef<HTMLDivElement>(null);\n const buttonRef = useRef<HTMLButtonElement>(null);\n\n // Store actions\n const selectWidget = useWidgetStore((state) => state.selectWidget);\n const copyWidget = useWidgetStore((state) => state.copyWidget);\n const cutWidget = useWidgetStore((state) => state.cutWidget);\n const removeWidget = useWidgetStore((state) => state.removeWidget);\n const openConfigPanel = useWidgetStore((state) => state.openConfigPanel);\n\n // API operations - skipFetch since parent page fetches\n const {\n duplicateWidget: duplicateWidgetApi,\n deleteWidget: deleteWidgetApi,\n refreshWidgetData,\n } = useWidgetOperations(widget.pageId, widget.dashboardId, { skipFetch: true });\n\n // Close menu when clicking outside\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (\n menuRef.current &&\n !menuRef.current.contains(event.target as Node) &&\n buttonRef.current &&\n !buttonRef.current.contains(event.target as Node)\n ) {\n setIsOpen(false);\n }\n };\n\n if (isOpen) {\n document.addEventListener('mousedown', handleClickOutside);\n }\n\n return () => {\n document.removeEventListener('mousedown', handleClickOutside);\n };\n }, [isOpen]);\n\n // Close menu on escape\n useEffect(() => {\n const handleEscape = (event: KeyboardEvent) => {\n if (event.key === 'Escape') {\n setIsOpen(false);\n buttonRef.current?.focus();\n }\n };\n\n if (isOpen) {\n document.addEventListener('keydown', handleEscape);\n }\n\n return () => {\n document.removeEventListener('keydown', handleEscape);\n };\n }, [isOpen]);\n\n // Toggle menu with collision detection\n const toggleMenu = useCallback((event: React.MouseEvent) => {\n event.stopPropagation();\n setIsOpen((prev) => {\n if (!prev && buttonRef.current) {\n // Calculate position when opening with collision detection\n const rect = buttonRef.current.getBoundingClientRect();\n const menuWidth = 160;\n const menuHeight = 320; // Approximate max height of menu\n const collisionPadding = 10;\n\n // Calculate initial position\n let top = rect.bottom + 4;\n let left = rect.right - menuWidth;\n\n // Check right overflow - ensure menu doesn't go past viewport right edge\n if (left + menuWidth > window.innerWidth - collisionPadding) {\n left = window.innerWidth - menuWidth - collisionPadding;\n }\n\n // Check left overflow - ensure menu doesn't go past viewport left edge\n if (left < collisionPadding) {\n left = collisionPadding;\n }\n\n // Check bottom overflow - flip to top if needed\n if (top + menuHeight > window.innerHeight - collisionPadding) {\n top = rect.top - menuHeight - 4;\n // If still overflows at top, position at top of viewport\n if (top < collisionPadding) {\n top = collisionPadding;\n }\n }\n\n setMenuPosition({\n top: top + window.scrollY,\n left: left + window.scrollX,\n });\n }\n return !prev;\n });\n }, []);\n\n // Menu items\n const menuItems: MenuItem[] = [\n {\n id: 'edit',\n label: 'Edit Widget',\n icon: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className=\"w-4 h-4\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z\"\n />\n </svg>\n ),\n action: () => {\n selectWidget(widget.id);\n openConfigPanel();\n setIsOpen(false);\n },\n disabled: !isEditMode,\n },\n {\n id: 'refresh',\n label: 'Refresh Data',\n icon: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className=\"w-4 h-4\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15\"\n />\n </svg>\n ),\n action: async () => {\n setIsOpen(false);\n if (onRefresh) {\n onRefresh();\n } else {\n await refreshWidgetData(widget.id);\n }\n },\n disabled: false,\n },\n {\n id: 'exportCSV',\n label: 'Export CSV',\n icon: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className=\"w-4 h-4\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z\"\n />\n </svg>\n ),\n action: () => {\n setIsOpen(false);\n onExportCSV?.();\n },\n disabled: !onExportCSV,\n },\n {\n id: 'exportPNG',\n label: 'Export PNG',\n icon: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className=\"w-4 h-4\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z\"\n />\n </svg>\n ),\n action: () => {\n setIsOpen(false);\n onExportPNG?.();\n },\n disabled: !onExportPNG,\n },\n {\n id: 'divider1',\n label: '',\n icon: <></>,\n action: () => {},\n divider: true,\n },\n {\n id: 'copy',\n label: 'Copy',\n icon: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className=\"w-4 h-4\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"\n />\n </svg>\n ),\n action: () => {\n copyWidget(widget.id);\n setIsOpen(false);\n },\n disabled: !isEditMode,\n },\n {\n id: 'cut',\n label: 'Cut',\n icon: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className=\"w-4 h-4\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M14.121 14.121L19 19m-7-7l7-7m-7 7l-2.879 2.879M12 12L9.121 9.121m0 5.758a3 3 0 10-4.243 4.243 3 3 0 004.243-4.243zm0-5.758a3 3 0 10-4.243-4.243 3 3 0 004.243 4.243z\"\n />\n </svg>\n ),\n action: () => {\n cutWidget(widget.id);\n setIsOpen(false);\n },\n disabled: !isEditMode,\n },\n {\n id: 'duplicate',\n label: 'Duplicate',\n icon: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className=\"w-4 h-4\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M8 7v8a2 2 0 002 2h6M8 7V5a2 2 0 012-2h4.586a1 1 0 01.707.293l4.414 4.414a1 1 0 01.293.707V15a2 2 0 01-2 2h-2M8 7H6a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2v-2\"\n />\n </svg>\n ),\n action: async () => {\n setIsOpen(false);\n // Calculate new position (offset from original)\n const newPosition = {\n x: (widget.positionX ?? 0) + 1,\n y: (widget.positionY ?? 0) + 1,\n width: widget.positionWidth ?? 4,\n height: widget.positionHeight ?? 4,\n };\n await duplicateWidgetApi(widget.id, newPosition, `${widget.title} (Copy)`);\n },\n disabled: !isEditMode,\n },\n {\n id: 'divider2',\n label: '',\n icon: <></>,\n action: () => {},\n divider: true,\n },\n {\n id: 'delete',\n label: 'Delete',\n icon: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className=\"w-4 h-4\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16\"\n />\n </svg>\n ),\n action: async () => {\n if (window.confirm('Are you sure you want to delete this widget?')) {\n setIsOpen(false);\n const success = await deleteWidgetApi(widget.id);\n if (!success) {\n // Fallback to local store removal if API fails\n removeWidget(widget.id);\n }\n } else {\n setIsOpen(false);\n }\n },\n disabled: !isEditMode,\n destructive: true,\n },\n ];\n\n return (\n <div className=\"relative\">\n {/* Menu Button */}\n <button\n ref={buttonRef}\n type=\"button\"\n onClick={toggleMenu}\n className={`\n p-1.5 rounded-md\n text-text-secondary\n hover:text-text-primary\n hover:bg-bg-sunken\n transition-colors\n ${isOpen ? 'bg-bg-sunken' : ''}\n `}\n title=\"Widget options\"\n aria-haspopup=\"menu\"\n aria-expanded={isOpen}\n >\n <svg className=\"w-4 h-4\" fill=\"currentColor\" viewBox=\"0 0 24 24\">\n <circle cx=\"12\" cy=\"5\" r=\"2\" />\n <circle cx=\"12\" cy=\"12\" r=\"2\" />\n <circle cx=\"12\" cy=\"19\" r=\"2\" />\n </svg>\n </button>\n\n {/* Dropdown Menu - Rendered via Portal to avoid overflow clipping */}\n {isOpen &&\n createPortal(\n <div\n ref={menuRef}\n style={{\n position: 'fixed',\n top: menuPosition.top,\n left: menuPosition.left,\n zIndex: 99999,\n }}\n className=\"\n min-w-[160px]\n py-1\n bg-bg-surface\n border border-border-default\n rounded-lg shadow-lg\n \"\n role=\"menu\"\n aria-orientation=\"vertical\"\n >\n {menuItems.map((item) => {\n if (item.divider) {\n return <div key={item.id} className=\"my-1 border-t border-border-default\" />;\n }\n\n return (\n <button\n key={item.id}\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n item.action();\n }}\n disabled={item.disabled}\n className={`\n w-full flex items-center gap-2\n px-3 py-2 text-left text-sm\n transition-colors\n ${item.disabled ? 'opacity-50 cursor-not-allowed' : 'hover:bg-bg-sunken'}\n ${item.destructive ? 'text-state-error hover:text-state-error' : 'text-text-primary'}\n `}\n role=\"menuitem\"\n >\n {item.icon}\n <span>{item.label}</span>\n </button>\n );\n })}\n </div>,\n document.body\n )}\n </div>\n );\n});\n\nexport default WidgetMenu;\n"],"mappings":"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"}
1
+ {"version":3,"file":"WidgetMenu.js","names":[],"sources":["../../../../src/bigconsole/components/widgets/WidgetMenu.tsx"],"sourcesContent":["/**\n * WidgetMenu Component\n *\n * Dropdown menu with widget actions (edit, duplicate, delete, etc.)\n */\n\nimport { type FC, type ReactElement, useState, useRef, useEffect, useCallback, memo } from 'react';\nimport { createPortal } from 'react-dom';\nimport type { Widget } from '../../types';\nimport { useWidgetStore } from '../../store';\nimport { useWidgetOperations } from '../../hooks';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface WidgetMenuProps {\n /** The widget data */\n widget: Widget;\n /** Whether in edit mode */\n isEditMode?: boolean;\n /** Callback to refresh widget */\n onRefresh?: () => void;\n /** Callback to export as CSV */\n onExportCSV?: () => void;\n /** Callback to export as PNG */\n onExportPNG?: () => void;\n}\n\ninterface MenuItem {\n id: string;\n label: string;\n icon: ReactElement;\n action: () => void;\n disabled?: boolean;\n destructive?: boolean;\n divider?: boolean;\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const WidgetMenu: FC<WidgetMenuProps> = memo(function WidgetMenu({\n widget,\n isEditMode,\n onRefresh,\n onExportCSV,\n onExportPNG,\n}) {\n const [isOpen, setIsOpen] = useState(false);\n const [menuPosition, setMenuPosition] = useState({ top: 0, left: 0 });\n const menuRef = useRef<HTMLDivElement>(null);\n const buttonRef = useRef<HTMLButtonElement>(null);\n\n // Store actions\n const selectWidget = useWidgetStore((state) => state.selectWidget);\n const copyWidget = useWidgetStore((state) => state.copyWidget);\n const cutWidget = useWidgetStore((state) => state.cutWidget);\n const removeWidget = useWidgetStore((state) => state.removeWidget);\n const openConfigPanel = useWidgetStore((state) => state.openConfigPanel);\n\n // API operations - skipFetch since parent page fetches\n const {\n duplicateWidget: duplicateWidgetApi,\n deleteWidget: deleteWidgetApi,\n refreshWidgetData,\n } = useWidgetOperations(widget.pageId, widget.dashboardId, { skipFetch: true });\n\n // Close menu when clicking outside\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (\n menuRef.current &&\n !menuRef.current.contains(event.target as Node) &&\n buttonRef.current &&\n !buttonRef.current.contains(event.target as Node)\n ) {\n setIsOpen(false);\n }\n };\n\n if (isOpen) {\n document.addEventListener('mousedown', handleClickOutside);\n }\n\n return () => {\n document.removeEventListener('mousedown', handleClickOutside);\n };\n }, [isOpen]);\n\n // Close menu on escape\n useEffect(() => {\n const handleEscape = (event: KeyboardEvent) => {\n if (event.key === 'Escape') {\n setIsOpen(false);\n buttonRef.current?.focus();\n }\n };\n\n if (isOpen) {\n document.addEventListener('keydown', handleEscape);\n }\n\n return () => {\n document.removeEventListener('keydown', handleEscape);\n };\n }, [isOpen]);\n\n // Toggle menu with collision detection\n const toggleMenu = useCallback((event: React.MouseEvent) => {\n event.stopPropagation();\n setIsOpen((prev) => {\n if (!prev && buttonRef.current) {\n // Calculate position when opening with collision detection\n const rect = buttonRef.current.getBoundingClientRect();\n const menuWidth = 160;\n const menuHeight = 320; // Approximate max height of menu\n const collisionPadding = 10;\n\n // Calculate initial position\n let top = rect.bottom + 4;\n let left = rect.right - menuWidth;\n\n // Check right overflow - ensure menu doesn't go past viewport right edge\n if (left + menuWidth > window.innerWidth - collisionPadding) {\n left = window.innerWidth - menuWidth - collisionPadding;\n }\n\n // Check left overflow - ensure menu doesn't go past viewport left edge\n if (left < collisionPadding) {\n left = collisionPadding;\n }\n\n // Check bottom overflow - flip to top if needed\n if (top + menuHeight > window.innerHeight - collisionPadding) {\n top = rect.top - menuHeight - 4;\n // If still overflows at top, position at top of viewport\n if (top < collisionPadding) {\n top = collisionPadding;\n }\n }\n\n setMenuPosition({\n top: top + window.scrollY,\n left: left + window.scrollX,\n });\n }\n return !prev;\n });\n }, []);\n\n // Menu items\n const menuItems: MenuItem[] = [\n {\n id: 'edit',\n label: 'Edit Widget',\n icon: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className=\"w-4 h-4\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z\"\n />\n </svg>\n ),\n action: () => {\n selectWidget(widget.id);\n openConfigPanel();\n setIsOpen(false);\n },\n disabled: !isEditMode,\n },\n {\n id: 'refresh',\n label: 'Refresh Data',\n icon: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className=\"w-4 h-4\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15\"\n />\n </svg>\n ),\n action: async () => {\n setIsOpen(false);\n if (onRefresh) {\n onRefresh();\n } else {\n await refreshWidgetData(widget.id);\n }\n },\n disabled: false,\n },\n {\n id: 'exportCSV',\n label: 'Export CSV',\n icon: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className=\"w-4 h-4\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z\"\n />\n </svg>\n ),\n action: () => {\n setIsOpen(false);\n onExportCSV?.();\n },\n disabled: !onExportCSV,\n },\n {\n id: 'exportPNG',\n label: 'Export PNG',\n icon: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className=\"w-4 h-4\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z\"\n />\n </svg>\n ),\n action: () => {\n setIsOpen(false);\n onExportPNG?.();\n },\n disabled: !onExportPNG,\n },\n {\n id: 'divider1',\n label: '',\n icon: <></>,\n action: () => {},\n divider: true,\n },\n {\n id: 'copy',\n label: 'Copy',\n icon: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className=\"w-4 h-4\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"\n />\n </svg>\n ),\n action: () => {\n copyWidget(widget.id);\n setIsOpen(false);\n },\n disabled: !isEditMode,\n },\n {\n id: 'cut',\n label: 'Cut',\n icon: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className=\"w-4 h-4\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M14.121 14.121L19 19m-7-7l7-7m-7 7l-2.879 2.879M12 12L9.121 9.121m0 5.758a3 3 0 10-4.243 4.243 3 3 0 004.243-4.243zm0-5.758a3 3 0 10-4.243-4.243 3 3 0 004.243 4.243z\"\n />\n </svg>\n ),\n action: () => {\n cutWidget(widget.id);\n setIsOpen(false);\n },\n disabled: !isEditMode,\n },\n {\n id: 'duplicate',\n label: 'Duplicate',\n icon: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className=\"w-4 h-4\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M8 7v8a2 2 0 002 2h6M8 7V5a2 2 0 012-2h4.586a1 1 0 01.707.293l4.414 4.414a1 1 0 01.293.707V15a2 2 0 01-2 2h-2M8 7H6a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2v-2\"\n />\n </svg>\n ),\n action: async () => {\n setIsOpen(false);\n // Calculate new position (offset from original)\n const newPosition = {\n x: (widget.positionX ?? 0) + 1,\n y: (widget.positionY ?? 0) + 1,\n width: widget.positionWidth ?? 4,\n height: widget.positionHeight ?? 4,\n };\n await duplicateWidgetApi(widget.id, newPosition, `${widget.title} (Copy)`);\n },\n disabled: !isEditMode,\n },\n {\n id: 'divider2',\n label: '',\n icon: <></>,\n action: () => {},\n divider: true,\n },\n {\n id: 'delete',\n label: 'Delete',\n icon: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className=\"w-4 h-4\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16\"\n />\n </svg>\n ),\n action: async () => {\n if (window.confirm('Are you sure you want to delete this widget?')) {\n setIsOpen(false);\n const success = await deleteWidgetApi(widget.id);\n if (!success) {\n // Fallback to local store removal if API fails\n removeWidget(widget.id);\n }\n } else {\n setIsOpen(false);\n }\n },\n disabled: !isEditMode,\n destructive: true,\n },\n ];\n\n return (\n <div className=\"relative\">\n {/* Menu Button */}\n <button\n ref={buttonRef}\n type=\"button\"\n onClick={toggleMenu}\n className={`\n p-1.5 rounded-md\n text-text-secondary\n hover:text-text-primary\n hover:bg-bg-sunken\n transition-colors\n ${isOpen ? 'bg-bg-sunken' : ''}\n `}\n title=\"Widget options\"\n aria-haspopup=\"menu\"\n aria-expanded={isOpen}\n >\n <svg className=\"w-4 h-4\" fill=\"currentColor\" viewBox=\"0 0 24 24\">\n <circle cx=\"12\" cy=\"5\" r=\"2\" />\n <circle cx=\"12\" cy=\"12\" r=\"2\" />\n <circle cx=\"12\" cy=\"19\" r=\"2\" />\n </svg>\n </button>\n\n {/* Dropdown Menu - Rendered via Portal to avoid overflow clipping */}\n {isOpen &&\n createPortal(\n <div\n ref={menuRef}\n style={{\n position: 'fixed',\n top: menuPosition.top,\n left: menuPosition.left,\n zIndex: 99999,\n }}\n className=\"\n min-w-[160px]\n py-1\n bg-bg-surface\n border border-border-default\n rounded-lg shadow-lg\n \"\n role=\"menu\"\n aria-orientation=\"vertical\"\n >\n {menuItems.map((item) => {\n if (item.divider) {\n return <div key={item.id} className=\"my-1 border-t border-border-default\" />;\n }\n\n return (\n <button\n key={item.id}\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n item.action();\n }}\n disabled={item.disabled}\n className={`\n w-full flex items-center gap-2\n px-3 py-2 text-left text-sm\n transition-colors\n ${item.disabled ? 'opacity-50 cursor-not-allowed' : 'hover:bg-bg-sunken'}\n ${item.destructive ? 'text-status-error-text hover:text-status-error-text' : 'text-text-primary'}\n `}\n role=\"menuitem\"\n >\n {item.icon}\n <span>{item.label}</span>\n </button>\n );\n })}\n </div>,\n document.body\n )}\n </div>\n );\n});\n\nexport default WidgetMenu;\n"],"mappings":"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"}
@@ -19,9 +19,9 @@ function i(e) {
19
19
  function a(e) {
20
20
  switch (e) {
21
21
  case "accent": return "text-action-primary-bg";
22
- case "attention": return "text-state-error";
23
- case "good": return "text-state-success";
24
- case "warning": return "text-state-warning";
22
+ case "attention": return "text-status-error-text";
23
+ case "good": return "text-status-success-text";
24
+ case "warning": return "text-status-warning-text";
25
25
  default: return "text-text-primary";
26
26
  }
27
27
  }
@@ -1 +1 @@
1
- {"version":3,"file":"AdaptiveCardWidget.js","names":[],"sources":["../../../../../src/bigconsole/components/widgets/adaptive-card/AdaptiveCardWidget.tsx"],"sourcesContent":["/**\n * AdaptiveCardWidget Component\n *\n * JSON-based adaptive cards renderer.\n */\n\nimport { type FC, memo, type ReactNode } from 'react';\nimport type { Widget } from '../../../types';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface AdaptiveCardWidgetProps {\n widget: Widget;\n data?: AdaptiveCardData;\n onClick?: (action: AdaptiveCardAction) => void;\n}\n\nexport interface AdaptiveCardData {\n body?: AdaptiveCardElement[];\n actions?: AdaptiveCardAction[];\n}\n\nexport interface AdaptiveCardElement {\n type: 'TextBlock' | 'Image' | 'Container' | 'ColumnSet' | 'Column' | 'FactSet' | 'ActionSet';\n text?: string;\n url?: string;\n size?: 'small' | 'medium' | 'large' | 'extraLarge';\n weight?: 'lighter' | 'default' | 'bolder';\n color?: 'default' | 'accent' | 'attention' | 'good' | 'warning';\n wrap?: boolean;\n items?: AdaptiveCardElement[];\n columns?: AdaptiveCardElement[];\n width?: string;\n facts?: Array<{ title: string; value: string }>;\n actions?: AdaptiveCardAction[];\n}\n\nexport interface AdaptiveCardAction {\n type: 'Action.OpenUrl' | 'Action.Submit' | 'Action.ShowCard';\n title: string;\n url?: string;\n data?: Record<string, unknown>;\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction getTextSizeClass(size?: string): string {\n switch (size) {\n case 'small':\n return 'text-xs';\n case 'large':\n return 'text-lg';\n case 'extraLarge':\n return 'text-2xl';\n default:\n return 'text-sm';\n }\n}\n\nfunction getTextWeightClass(weight?: string): string {\n switch (weight) {\n case 'lighter':\n return 'font-light';\n case 'bolder':\n return 'font-bold';\n default:\n return 'font-normal';\n }\n}\n\nfunction getTextColorClass(color?: string): string {\n switch (color) {\n case 'accent':\n return 'text-action-primary-bg';\n case 'attention':\n return 'text-state-error';\n case 'good':\n return 'text-state-success';\n case 'warning':\n return 'text-state-warning';\n default:\n return 'text-text-primary';\n }\n}\n\n// ============================================================================\n// Element Renderer\n// ============================================================================\n\nfunction renderElement(\n element: AdaptiveCardElement,\n index: number,\n onAction?: (action: AdaptiveCardAction) => void\n): ReactNode {\n switch (element.type) {\n case 'TextBlock':\n return (\n <p\n key={index}\n className={`\n ${getTextSizeClass(element.size)}\n ${getTextWeightClass(element.weight)}\n ${getTextColorClass(element.color)}\n ${element.wrap ? '' : 'truncate'}\n mb-1\n `}\n >\n {element.text}\n </p>\n );\n\n case 'Image':\n return <img key={index} src={element.url} alt=\"\" className=\"max-w-full h-auto rounded mb-2\" />;\n\n case 'Container':\n return (\n <div key={index} className=\"mb-2\">\n {element.items?.map((item, i) => renderElement(item, i, onAction))}\n </div>\n );\n\n case 'ColumnSet':\n return (\n <div key={index} className=\"flex gap-2 mb-2\">\n {element.columns?.map((col, i) => renderElement(col, i, onAction))}\n </div>\n );\n\n case 'Column':\n return (\n <div key={index} className=\"flex-1\" style={{ flexBasis: element.width }}>\n {element.items?.map((item, i) => renderElement(item, i, onAction))}\n </div>\n );\n\n case 'FactSet':\n return (\n <div key={index} className=\"mb-2\">\n {element.facts?.map((fact, i) => (\n <div key={i} className=\"flex gap-2 text-sm py-1\">\n <span className=\"text-text-secondary flex-shrink-0\">{fact.title}:</span>\n <span className=\"text-text-primary font-medium\">{fact.value}</span>\n </div>\n ))}\n </div>\n );\n\n case 'ActionSet':\n return (\n <div key={index} className=\"flex gap-2 mt-3\">\n {element.actions?.map((action, i) => (\n <button\n key={i}\n type=\"button\"\n onClick={() => onAction?.(action)}\n className=\"\n px-3 py-1.5\n text-sm font-medium\n text-action-primary-fg\n bg-action-primary-bg\n hover:bg-action-primary-bgHover\n rounded-md\n transition-colors\n \"\n >\n {action.title}\n </button>\n ))}\n </div>\n );\n\n default:\n return null;\n }\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const AdaptiveCardWidget: FC<AdaptiveCardWidgetProps> = memo(function AdaptiveCardWidget({\n widget: _widget,\n data,\n onClick,\n}) {\n if (!data?.body?.length && !data?.actions?.length) {\n return <div className=\"flex items-center justify-center h-full p-4 text-text-secondary\">No content to display</div>;\n }\n\n return (\n <div className=\"flex flex-col h-full p-4\">\n {/* Body */}\n <div className=\"flex-1\">{data.body?.map((element, i) => renderElement(element, i, onClick))}</div>\n\n {/* Actions */}\n {data.actions && data.actions.length > 0 && (\n <div className=\"flex gap-2 mt-4 pt-4 border-t border-border-default\">\n {data.actions.map((action, i) => (\n <button\n key={i}\n type=\"button\"\n onClick={() => onClick?.(action)}\n className=\"\n px-4 py-2\n text-sm font-medium\n text-action-primary-fg\n bg-action-primary-bg\n hover:bg-action-primary-bgHover\n rounded-md\n transition-colors\n \"\n >\n {action.title}\n </button>\n ))}\n </div>\n )}\n </div>\n );\n});\n\nexport default AdaptiveCardWidget;\n"],"mappings":";;;AAkDA,SAAS,EAAiB,GAAuB;AAC/C,SAAQ,GAAR;EACE,KAAK,QACH,QAAO;EACT,KAAK,QACH,QAAO;EACT,KAAK,aACH,QAAO;EACT,QACE,QAAO;;;AAIb,SAAS,EAAmB,GAAyB;AACnD,SAAQ,GAAR;EACE,KAAK,UACH,QAAO;EACT,KAAK,SACH,QAAO;EACT,QACE,QAAO;;;AAIb,SAAS,EAAkB,GAAwB;AACjD,SAAQ,GAAR;EACE,KAAK,SACH,QAAO;EACT,KAAK,YACH,QAAO;EACT,KAAK,OACH,QAAO;EACT,KAAK,UACH,QAAO;EACT,QACE,QAAO;;;AAQb,SAAS,EACP,GACA,GACA,GACW;AACX,SAAQ,EAAQ,MAAhB;EACE,KAAK,YACH,QACE,kBAAC,KAAD;GAEE,WAAW;cACP,EAAiB,EAAQ,KAAK,CAAC;cAC/B,EAAmB,EAAQ,OAAO,CAAC;cACnC,EAAkB,EAAQ,MAAM,CAAC;cACjC,EAAQ,OAAO,KAAK,WAAW;;;aAIlC,EAAQ;GACP,EAVG,EAUH;EAGR,KAAK,QACH,QAAO,kBAAC,OAAD;GAAiB,KAAK,EAAQ;GAAK,KAAI;GAAG,WAAU;GAAmC,EAA7E,EAA6E;EAEhG,KAAK,YACH,QACE,kBAAC,OAAD;GAAiB,WAAU;aACxB,EAAQ,OAAO,KAAK,GAAM,MAAM,EAAc,GAAM,GAAG,EAAS,CAAC;GAC9D,EAFI,EAEJ;EAGV,KAAK,YACH,QACE,kBAAC,OAAD;GAAiB,WAAU;aACxB,EAAQ,SAAS,KAAK,GAAK,MAAM,EAAc,GAAK,GAAG,EAAS,CAAC;GAC9D,EAFI,EAEJ;EAGV,KAAK,SACH,QACE,kBAAC,OAAD;GAAiB,WAAU;GAAS,OAAO,EAAE,WAAW,EAAQ,OAAO;aACpE,EAAQ,OAAO,KAAK,GAAM,MAAM,EAAc,GAAM,GAAG,EAAS,CAAC;GAC9D,EAFI,EAEJ;EAGV,KAAK,UACH,QACE,kBAAC,OAAD;GAAiB,WAAU;aACxB,EAAQ,OAAO,KAAK,GAAM,MACzB,kBAAC,OAAD;IAAa,WAAU;cAAvB,CACE,kBAAC,QAAD;KAAM,WAAU;eAAhB,CAAqD,EAAK,OAAM,IAAQ;QACxE,kBAAC,QAAD;KAAM,WAAU;eAAiC,EAAK;KAAa,CAAA,CAC/D;MAHI,EAGJ,CACN;GACE,EAPI,EAOJ;EAGV,KAAK,YACH,QACE,kBAAC,OAAD;GAAiB,WAAU;aACxB,EAAQ,SAAS,KAAK,GAAQ,MAC7B,kBAAC,UAAD;IAEE,MAAK;IACL,eAAe,IAAW,EAAO;IACjC,WAAU;cAUT,EAAO;IACD,EAdF,EAcE,CACT;GACE,EAnBI,EAmBJ;EAGV,QACE,QAAO;;;AAQb,IAAa,IAAkD,EAAK,SAA4B,EAC9F,QAAQ,GACR,SACA,cACC;AAKD,QAJI,CAAC,GAAM,MAAM,UAAU,CAAC,GAAM,SAAS,SAClC,kBAAC,OAAD;EAAK,WAAU;YAAkE;EAA2B,CAAA,GAInH,kBAAC,OAAD;EAAK,WAAU;YAAf,CAEE,kBAAC,OAAD;GAAK,WAAU;aAAU,EAAK,MAAM,KAAK,GAAS,MAAM,EAAc,GAAS,GAAG,EAAQ,CAAC;GAAO,CAAA,EAGjG,EAAK,WAAW,EAAK,QAAQ,SAAS,KACrC,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAK,QAAQ,KAAK,GAAQ,MACzB,kBAAC,UAAD;IAEE,MAAK;IACL,eAAe,IAAU,EAAO;IAChC,WAAU;cAUT,EAAO;IACD,EAdF,EAcE,CACT;GACE,CAAA,CAEJ;;EAER"}
1
+ {"version":3,"file":"AdaptiveCardWidget.js","names":[],"sources":["../../../../../src/bigconsole/components/widgets/adaptive-card/AdaptiveCardWidget.tsx"],"sourcesContent":["/**\n * AdaptiveCardWidget Component\n *\n * JSON-based adaptive cards renderer.\n */\n\nimport { type FC, memo, type ReactNode } from 'react';\nimport type { Widget } from '../../../types';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface AdaptiveCardWidgetProps {\n widget: Widget;\n data?: AdaptiveCardData;\n onClick?: (action: AdaptiveCardAction) => void;\n}\n\nexport interface AdaptiveCardData {\n body?: AdaptiveCardElement[];\n actions?: AdaptiveCardAction[];\n}\n\nexport interface AdaptiveCardElement {\n type: 'TextBlock' | 'Image' | 'Container' | 'ColumnSet' | 'Column' | 'FactSet' | 'ActionSet';\n text?: string;\n url?: string;\n size?: 'small' | 'medium' | 'large' | 'extraLarge';\n weight?: 'lighter' | 'default' | 'bolder';\n color?: 'default' | 'accent' | 'attention' | 'good' | 'warning';\n wrap?: boolean;\n items?: AdaptiveCardElement[];\n columns?: AdaptiveCardElement[];\n width?: string;\n facts?: Array<{ title: string; value: string }>;\n actions?: AdaptiveCardAction[];\n}\n\nexport interface AdaptiveCardAction {\n type: 'Action.OpenUrl' | 'Action.Submit' | 'Action.ShowCard';\n title: string;\n url?: string;\n data?: Record<string, unknown>;\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction getTextSizeClass(size?: string): string {\n switch (size) {\n case 'small':\n return 'text-xs';\n case 'large':\n return 'text-lg';\n case 'extraLarge':\n return 'text-2xl';\n default:\n return 'text-sm';\n }\n}\n\nfunction getTextWeightClass(weight?: string): string {\n switch (weight) {\n case 'lighter':\n return 'font-light';\n case 'bolder':\n return 'font-bold';\n default:\n return 'font-normal';\n }\n}\n\nfunction getTextColorClass(color?: string): string {\n switch (color) {\n case 'accent':\n return 'text-action-primary-bg';\n case 'attention':\n return 'text-status-error-text';\n case 'good':\n return 'text-status-success-text';\n case 'warning':\n return 'text-status-warning-text';\n default:\n return 'text-text-primary';\n }\n}\n\n// ============================================================================\n// Element Renderer\n// ============================================================================\n\nfunction renderElement(\n element: AdaptiveCardElement,\n index: number,\n onAction?: (action: AdaptiveCardAction) => void\n): ReactNode {\n switch (element.type) {\n case 'TextBlock':\n return (\n <p\n key={index}\n className={`\n ${getTextSizeClass(element.size)}\n ${getTextWeightClass(element.weight)}\n ${getTextColorClass(element.color)}\n ${element.wrap ? '' : 'truncate'}\n mb-1\n `}\n >\n {element.text}\n </p>\n );\n\n case 'Image':\n return <img key={index} src={element.url} alt=\"\" className=\"max-w-full h-auto rounded mb-2\" />;\n\n case 'Container':\n return (\n <div key={index} className=\"mb-2\">\n {element.items?.map((item, i) => renderElement(item, i, onAction))}\n </div>\n );\n\n case 'ColumnSet':\n return (\n <div key={index} className=\"flex gap-2 mb-2\">\n {element.columns?.map((col, i) => renderElement(col, i, onAction))}\n </div>\n );\n\n case 'Column':\n return (\n <div key={index} className=\"flex-1\" style={{ flexBasis: element.width }}>\n {element.items?.map((item, i) => renderElement(item, i, onAction))}\n </div>\n );\n\n case 'FactSet':\n return (\n <div key={index} className=\"mb-2\">\n {element.facts?.map((fact, i) => (\n <div key={i} className=\"flex gap-2 text-sm py-1\">\n <span className=\"text-text-secondary flex-shrink-0\">{fact.title}:</span>\n <span className=\"text-text-primary font-medium\">{fact.value}</span>\n </div>\n ))}\n </div>\n );\n\n case 'ActionSet':\n return (\n <div key={index} className=\"flex gap-2 mt-3\">\n {element.actions?.map((action, i) => (\n <button\n key={i}\n type=\"button\"\n onClick={() => onAction?.(action)}\n className=\"\n px-3 py-1.5\n text-sm font-medium\n text-action-primary-fg\n bg-action-primary-bg\n hover:bg-action-primary-bgHover\n rounded-md\n transition-colors\n \"\n >\n {action.title}\n </button>\n ))}\n </div>\n );\n\n default:\n return null;\n }\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const AdaptiveCardWidget: FC<AdaptiveCardWidgetProps> = memo(function AdaptiveCardWidget({\n widget: _widget,\n data,\n onClick,\n}) {\n if (!data?.body?.length && !data?.actions?.length) {\n return <div className=\"flex items-center justify-center h-full p-4 text-text-secondary\">No content to display</div>;\n }\n\n return (\n <div className=\"flex flex-col h-full p-4\">\n {/* Body */}\n <div className=\"flex-1\">{data.body?.map((element, i) => renderElement(element, i, onClick))}</div>\n\n {/* Actions */}\n {data.actions && data.actions.length > 0 && (\n <div className=\"flex gap-2 mt-4 pt-4 border-t border-border-default\">\n {data.actions.map((action, i) => (\n <button\n key={i}\n type=\"button\"\n onClick={() => onClick?.(action)}\n className=\"\n px-4 py-2\n text-sm font-medium\n text-action-primary-fg\n bg-action-primary-bg\n hover:bg-action-primary-bgHover\n rounded-md\n transition-colors\n \"\n >\n {action.title}\n </button>\n ))}\n </div>\n )}\n </div>\n );\n});\n\nexport default AdaptiveCardWidget;\n"],"mappings":";;;AAkDA,SAAS,EAAiB,GAAuB;AAC/C,SAAQ,GAAR;EACE,KAAK,QACH,QAAO;EACT,KAAK,QACH,QAAO;EACT,KAAK,aACH,QAAO;EACT,QACE,QAAO;;;AAIb,SAAS,EAAmB,GAAyB;AACnD,SAAQ,GAAR;EACE,KAAK,UACH,QAAO;EACT,KAAK,SACH,QAAO;EACT,QACE,QAAO;;;AAIb,SAAS,EAAkB,GAAwB;AACjD,SAAQ,GAAR;EACE,KAAK,SACH,QAAO;EACT,KAAK,YACH,QAAO;EACT,KAAK,OACH,QAAO;EACT,KAAK,UACH,QAAO;EACT,QACE,QAAO;;;AAQb,SAAS,EACP,GACA,GACA,GACW;AACX,SAAQ,EAAQ,MAAhB;EACE,KAAK,YACH,QACE,kBAAC,KAAD;GAEE,WAAW;cACP,EAAiB,EAAQ,KAAK,CAAC;cAC/B,EAAmB,EAAQ,OAAO,CAAC;cACnC,EAAkB,EAAQ,MAAM,CAAC;cACjC,EAAQ,OAAO,KAAK,WAAW;;;aAIlC,EAAQ;GACP,EAVG,EAUH;EAGR,KAAK,QACH,QAAO,kBAAC,OAAD;GAAiB,KAAK,EAAQ;GAAK,KAAI;GAAG,WAAU;GAAmC,EAA7E,EAA6E;EAEhG,KAAK,YACH,QACE,kBAAC,OAAD;GAAiB,WAAU;aACxB,EAAQ,OAAO,KAAK,GAAM,MAAM,EAAc,GAAM,GAAG,EAAS,CAAC;GAC9D,EAFI,EAEJ;EAGV,KAAK,YACH,QACE,kBAAC,OAAD;GAAiB,WAAU;aACxB,EAAQ,SAAS,KAAK,GAAK,MAAM,EAAc,GAAK,GAAG,EAAS,CAAC;GAC9D,EAFI,EAEJ;EAGV,KAAK,SACH,QACE,kBAAC,OAAD;GAAiB,WAAU;GAAS,OAAO,EAAE,WAAW,EAAQ,OAAO;aACpE,EAAQ,OAAO,KAAK,GAAM,MAAM,EAAc,GAAM,GAAG,EAAS,CAAC;GAC9D,EAFI,EAEJ;EAGV,KAAK,UACH,QACE,kBAAC,OAAD;GAAiB,WAAU;aACxB,EAAQ,OAAO,KAAK,GAAM,MACzB,kBAAC,OAAD;IAAa,WAAU;cAAvB,CACE,kBAAC,QAAD;KAAM,WAAU;eAAhB,CAAqD,EAAK,OAAM,IAAQ;QACxE,kBAAC,QAAD;KAAM,WAAU;eAAiC,EAAK;KAAa,CAAA,CAC/D;MAHI,EAGJ,CACN;GACE,EAPI,EAOJ;EAGV,KAAK,YACH,QACE,kBAAC,OAAD;GAAiB,WAAU;aACxB,EAAQ,SAAS,KAAK,GAAQ,MAC7B,kBAAC,UAAD;IAEE,MAAK;IACL,eAAe,IAAW,EAAO;IACjC,WAAU;cAUT,EAAO;IACD,EAdF,EAcE,CACT;GACE,EAnBI,EAmBJ;EAGV,QACE,QAAO;;;AAQb,IAAa,IAAkD,EAAK,SAA4B,EAC9F,QAAQ,GACR,SACA,cACC;AAKD,QAJI,CAAC,GAAM,MAAM,UAAU,CAAC,GAAM,SAAS,SAClC,kBAAC,OAAD;EAAK,WAAU;YAAkE;EAA2B,CAAA,GAInH,kBAAC,OAAD;EAAK,WAAU;YAAf,CAEE,kBAAC,OAAD;GAAK,WAAU;aAAU,EAAK,MAAM,KAAK,GAAS,MAAM,EAAc,GAAS,GAAG,EAAQ,CAAC;GAAO,CAAA,EAGjG,EAAK,WAAW,EAAK,QAAQ,SAAS,KACrC,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAK,QAAQ,KAAK,GAAQ,MACzB,kBAAC,UAAD;IAEE,MAAK;IACL,eAAe,IAAU,EAAO;IAChC,WAAU;cAUT,EAAO;IACD,EAdF,EAcE,CACT;GACE,CAAA,CAEJ;;EAER"}
@@ -92,7 +92,7 @@ var o = e(function({ widget: e, data: i, onClick: o }) {
92
92
  className: "text-xs text-text-secondary mt-1",
93
93
  children: [
94
94
  i ? /* @__PURE__ */ n("span", {
95
- className: "text-state-success font-medium",
95
+ className: "text-status-success-text font-medium",
96
96
  children: "Today"
97
97
  }) : t.toLocaleDateString("en-US", {
98
98
  weekday: "short",
@@ -1 +1 @@
1
- {"version":3,"file":"CalendarWidget.js","names":[],"sources":["../../../../../src/bigconsole/components/widgets/calendar/CalendarWidget.tsx"],"sourcesContent":["/**\n * CalendarWidget Component\n *\n * Simple calendar view showing events.\n */\n\nimport { type FC, memo, useMemo } from 'react';\nimport type { Widget } from '../../../types';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface CalendarWidgetProps {\n widget: Widget;\n data?: CalendarData;\n onClick?: (event: CalendarEvent) => void;\n}\n\nexport interface CalendarEvent {\n id: string;\n title: string;\n start: string;\n end: string;\n color?: string;\n allDay?: boolean;\n category?: string;\n}\n\nexport interface CalendarData {\n events?: CalendarEvent[];\n currentDate?: string;\n view?: 'month' | 'week' | 'day';\n}\n\n// ============================================================================\n// Normalization\n// ============================================================================\n\nfunction firstDefined<T = unknown>(obj: Record<string, unknown>, keys: string[]): T | undefined {\n for (const key of keys) {\n if (obj[key] !== undefined && obj[key] !== null) return obj[key] as T;\n }\n return undefined;\n}\n\n/**\n * Normalize whatever the widget is bound to into `CalendarEvent[]`. Accepts the\n * documented `{ events, ... }` shape, an events array under a different common\n * key, or a bare array of event-like records — a DataSink parser naturally\n * outputs a bare array, and WidgetWrapper has no calendar-specific reshaping\n * step (unlike table/list/chart), so this widget has to tolerate it itself, the\n * same way MapWidget tolerates a bare marker array instead of requiring\n * `{ markers }`. Field names are matched against common aliases so a parser\n * doesn't have to emit the exact `title`/`start`/`end` keys.\n */\nexport function normalizeCalendarEvents(\n data: CalendarData | CalendarEvent[] | Record<string, unknown> | undefined\n): CalendarEvent[] {\n const rawEvents: unknown = Array.isArray(data)\n ? data\n : Array.isArray((data as CalendarData | undefined)?.events)\n ? (data as CalendarData).events\n : Array.isArray((data as Record<string, unknown> | undefined)?.items)\n ? (data as Record<string, unknown>).items\n : Array.isArray((data as Record<string, unknown> | undefined)?.data)\n ? (data as Record<string, unknown>).data\n : undefined;\n\n if (!Array.isArray(rawEvents)) return [];\n\n return rawEvents\n .map((raw, i): CalendarEvent | null => {\n if (!raw || typeof raw !== 'object') return null;\n const r = raw as Record<string, unknown>;\n const start = firstDefined<string>(r, ['start', 'startDate', 'date', 'startTime']);\n if (!start) return null;\n const end = firstDefined<string>(r, ['end', 'endDate', 'endTime']) ?? start;\n const title = firstDefined<string>(r, ['title', 'name', 'label', 'summary']) ?? 'Untitled event';\n const id = firstDefined<string>(r, ['id', 'eventId', '_id']) ?? `${title}-${start}-${i}`;\n return {\n id: String(id),\n title: String(title),\n start: String(start),\n end: String(end),\n color: typeof r.color === 'string' ? r.color : undefined,\n allDay: typeof r.allDay === 'boolean' ? r.allDay : undefined,\n category: firstDefined<string>(r, ['category', 'group', 'type']),\n };\n })\n .filter((e): e is CalendarEvent => e !== null);\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const CalendarWidget: FC<CalendarWidgetProps> = memo(function CalendarWidget({\n widget: _widget,\n data,\n onClick,\n}) {\n const events = useMemo(() => normalizeCalendarEvents(data), [data]);\n\n // Get upcoming events (next 7 days)\n const upcomingEvents = useMemo(() => {\n const now = new Date();\n const weekFromNow = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000);\n return events\n .filter((e) => {\n const eventDate = new Date(e.start);\n return eventDate >= now && eventDate <= weekFromNow;\n })\n .sort((a, b) => new Date(a.start).getTime() - new Date(b.start).getTime())\n .slice(0, 8);\n }, [events]);\n\n if (!events.length) {\n return (\n <div className=\"flex items-center justify-center h-full text-text-secondary text-sm\">\n <div className=\"text-center\">\n <svg\n className=\"w-12 h-12 mx-auto mb-2 text-text-tertiary\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={1.5}\n d=\"M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z\"\n />\n </svg>\n <p>No events to display</p>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"flex flex-col h-full p-4 overflow-auto\">\n {/* Header */}\n <div className=\"flex items-center justify-between mb-4\">\n <h3 className=\"text-sm font-medium text-text-primary\">Upcoming Events</h3>\n <span className=\"text-xs text-text-secondary\">{events.length} events</span>\n </div>\n\n {/* Event List */}\n <div className=\"flex-1 space-y-2 overflow-auto\">\n {upcomingEvents.map((event) => {\n const eventDate = new Date(event.start);\n const isToday = eventDate.toDateString() === new Date().toDateString();\n\n return (\n <div\n key={event.id}\n className={`\n p-3 rounded-lg border border-border-default\n ${onClick ? 'cursor-pointer hover:border-action-primary-bg' : ''}\n transition-colors\n `}\n onClick={() => onClick?.(event)}\n >\n <div className=\"flex items-start gap-3\">\n {/* Color indicator */}\n <div\n className=\"w-1 h-full min-h-[40px] rounded-full flex-shrink-0\"\n style={{ backgroundColor: event.color || 'var(--color-action-primary-bg)' }}\n />\n\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-sm font-medium text-text-primary truncate\">{event.title}</p>\n <p className=\"text-xs text-text-secondary mt-1\">\n {isToday ? (\n <span className=\"text-state-success font-medium\">Today</span>\n ) : (\n eventDate.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric' })\n )}\n {!event.allDay && (\n <span className=\"ml-2\">\n {eventDate.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' })}\n </span>\n )}\n {event.allDay && <span className=\"ml-2\">All day</span>}\n </p>\n {event.category && (\n <span className=\"inline-block mt-1 px-2 py-0.5 text-[10px] rounded bg-bg-sunken text-text-secondary\">\n {event.category}\n </span>\n )}\n </div>\n </div>\n </div>\n );\n })}\n </div>\n\n {events.length > upcomingEvents.length && (\n <p className=\"text-xs text-text-tertiary text-center mt-3\">\n +{events.length - upcomingEvents.length} more events\n </p>\n )}\n </div>\n );\n});\n\nexport default CalendarWidget;\n"],"mappings":"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"}
1
+ {"version":3,"file":"CalendarWidget.js","names":[],"sources":["../../../../../src/bigconsole/components/widgets/calendar/CalendarWidget.tsx"],"sourcesContent":["/**\n * CalendarWidget Component\n *\n * Simple calendar view showing events.\n */\n\nimport { type FC, memo, useMemo } from 'react';\nimport type { Widget } from '../../../types';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface CalendarWidgetProps {\n widget: Widget;\n data?: CalendarData;\n onClick?: (event: CalendarEvent) => void;\n}\n\nexport interface CalendarEvent {\n id: string;\n title: string;\n start: string;\n end: string;\n color?: string;\n allDay?: boolean;\n category?: string;\n}\n\nexport interface CalendarData {\n events?: CalendarEvent[];\n currentDate?: string;\n view?: 'month' | 'week' | 'day';\n}\n\n// ============================================================================\n// Normalization\n// ============================================================================\n\nfunction firstDefined<T = unknown>(obj: Record<string, unknown>, keys: string[]): T | undefined {\n for (const key of keys) {\n if (obj[key] !== undefined && obj[key] !== null) return obj[key] as T;\n }\n return undefined;\n}\n\n/**\n * Normalize whatever the widget is bound to into `CalendarEvent[]`. Accepts the\n * documented `{ events, ... }` shape, an events array under a different common\n * key, or a bare array of event-like records — a DataSink parser naturally\n * outputs a bare array, and WidgetWrapper has no calendar-specific reshaping\n * step (unlike table/list/chart), so this widget has to tolerate it itself, the\n * same way MapWidget tolerates a bare marker array instead of requiring\n * `{ markers }`. Field names are matched against common aliases so a parser\n * doesn't have to emit the exact `title`/`start`/`end` keys.\n */\nexport function normalizeCalendarEvents(\n data: CalendarData | CalendarEvent[] | Record<string, unknown> | undefined\n): CalendarEvent[] {\n const rawEvents: unknown = Array.isArray(data)\n ? data\n : Array.isArray((data as CalendarData | undefined)?.events)\n ? (data as CalendarData).events\n : Array.isArray((data as Record<string, unknown> | undefined)?.items)\n ? (data as Record<string, unknown>).items\n : Array.isArray((data as Record<string, unknown> | undefined)?.data)\n ? (data as Record<string, unknown>).data\n : undefined;\n\n if (!Array.isArray(rawEvents)) return [];\n\n return rawEvents\n .map((raw, i): CalendarEvent | null => {\n if (!raw || typeof raw !== 'object') return null;\n const r = raw as Record<string, unknown>;\n const start = firstDefined<string>(r, ['start', 'startDate', 'date', 'startTime']);\n if (!start) return null;\n const end = firstDefined<string>(r, ['end', 'endDate', 'endTime']) ?? start;\n const title = firstDefined<string>(r, ['title', 'name', 'label', 'summary']) ?? 'Untitled event';\n const id = firstDefined<string>(r, ['id', 'eventId', '_id']) ?? `${title}-${start}-${i}`;\n return {\n id: String(id),\n title: String(title),\n start: String(start),\n end: String(end),\n color: typeof r.color === 'string' ? r.color : undefined,\n allDay: typeof r.allDay === 'boolean' ? r.allDay : undefined,\n category: firstDefined<string>(r, ['category', 'group', 'type']),\n };\n })\n .filter((e): e is CalendarEvent => e !== null);\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const CalendarWidget: FC<CalendarWidgetProps> = memo(function CalendarWidget({\n widget: _widget,\n data,\n onClick,\n}) {\n const events = useMemo(() => normalizeCalendarEvents(data), [data]);\n\n // Get upcoming events (next 7 days)\n const upcomingEvents = useMemo(() => {\n const now = new Date();\n const weekFromNow = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000);\n return events\n .filter((e) => {\n const eventDate = new Date(e.start);\n return eventDate >= now && eventDate <= weekFromNow;\n })\n .sort((a, b) => new Date(a.start).getTime() - new Date(b.start).getTime())\n .slice(0, 8);\n }, [events]);\n\n if (!events.length) {\n return (\n <div className=\"flex items-center justify-center h-full text-text-secondary text-sm\">\n <div className=\"text-center\">\n <svg\n className=\"w-12 h-12 mx-auto mb-2 text-text-tertiary\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={1.5}\n d=\"M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z\"\n />\n </svg>\n <p>No events to display</p>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"flex flex-col h-full p-4 overflow-auto\">\n {/* Header */}\n <div className=\"flex items-center justify-between mb-4\">\n <h3 className=\"text-sm font-medium text-text-primary\">Upcoming Events</h3>\n <span className=\"text-xs text-text-secondary\">{events.length} events</span>\n </div>\n\n {/* Event List */}\n <div className=\"flex-1 space-y-2 overflow-auto\">\n {upcomingEvents.map((event) => {\n const eventDate = new Date(event.start);\n const isToday = eventDate.toDateString() === new Date().toDateString();\n\n return (\n <div\n key={event.id}\n className={`\n p-3 rounded-lg border border-border-default\n ${onClick ? 'cursor-pointer hover:border-action-primary-bg' : ''}\n transition-colors\n `}\n onClick={() => onClick?.(event)}\n >\n <div className=\"flex items-start gap-3\">\n {/* Color indicator */}\n <div\n className=\"w-1 h-full min-h-[40px] rounded-full flex-shrink-0\"\n style={{ backgroundColor: event.color || 'var(--color-action-primary-bg)' }}\n />\n\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-sm font-medium text-text-primary truncate\">{event.title}</p>\n <p className=\"text-xs text-text-secondary mt-1\">\n {isToday ? (\n <span className=\"text-status-success-text font-medium\">Today</span>\n ) : (\n eventDate.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric' })\n )}\n {!event.allDay && (\n <span className=\"ml-2\">\n {eventDate.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' })}\n </span>\n )}\n {event.allDay && <span className=\"ml-2\">All day</span>}\n </p>\n {event.category && (\n <span className=\"inline-block mt-1 px-2 py-0.5 text-[10px] rounded bg-bg-sunken text-text-secondary\">\n {event.category}\n </span>\n )}\n </div>\n </div>\n </div>\n );\n })}\n </div>\n\n {events.length > upcomingEvents.length && (\n <p className=\"text-xs text-text-tertiary text-center mt-3\">\n +{events.length - upcomingEvents.length} more events\n </p>\n )}\n </div>\n );\n});\n\nexport default CalendarWidget;\n"],"mappings":"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"}
@@ -93,7 +93,7 @@ var i = e(function({ config: e, onChange: i }) {
93
93
  /* @__PURE__ */ r("div", {
94
94
  className: "flex items-center gap-2",
95
95
  children: [
96
- /* @__PURE__ */ n("div", { className: "w-4 h-4 rounded bg-state-error" }),
96
+ /* @__PURE__ */ n("div", { className: "w-4 h-4 rounded bg-status-error-bg" }),
97
97
  /* @__PURE__ */ n("span", {
98
98
  className: "text-sm text-text-primary flex-1",
99
99
  children: "Danger (Red)"
@@ -115,7 +115,7 @@ var i = e(function({ config: e, onChange: i }) {
115
115
  /* @__PURE__ */ r("div", {
116
116
  className: "flex items-center gap-2",
117
117
  children: [
118
- /* @__PURE__ */ n("div", { className: "w-4 h-4 rounded bg-state-warning" }),
118
+ /* @__PURE__ */ n("div", { className: "w-4 h-4 rounded bg-status-warning-bg" }),
119
119
  /* @__PURE__ */ n("span", {
120
120
  className: "text-sm text-text-primary flex-1",
121
121
  children: "Warning (Yellow)"
@@ -137,7 +137,7 @@ var i = e(function({ config: e, onChange: i }) {
137
137
  /* @__PURE__ */ r("div", {
138
138
  className: "flex items-center gap-2",
139
139
  children: [
140
- /* @__PURE__ */ n("div", { className: "w-4 h-4 rounded bg-state-success" }),
140
+ /* @__PURE__ */ n("div", { className: "w-4 h-4 rounded bg-status-success-bg" }),
141
141
  /* @__PURE__ */ n("span", {
142
142
  className: "text-sm text-text-primary flex-1",
143
143
  children: "Good (Green)"
@@ -1 +1 @@
1
- {"version":3,"file":"GaugeConfig.js","names":[],"sources":["../../../../../src/bigconsole/components/widgets/gauge/GaugeConfig.tsx"],"sourcesContent":["/**\n * GaugeConfig Component\n *\n * Configuration panel for Gauge widgets.\n */\n\nimport { type FC, memo, useCallback } from 'react';\nimport type { GaugeConfig as ConfigType } from '../../../types';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface GaugeConfigProps {\n /** Current configuration */\n config: ConfigType;\n /** Callback when config changes */\n onChange: (config: ConfigType) => void;\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const GaugeConfig: FC<GaugeConfigProps> = memo(function GaugeConfig({ config, onChange }) {\n const handleChange = useCallback(\n <K extends keyof ConfigType>(key: K, value: ConfigType[K]) => {\n onChange({ ...config, [key]: value });\n },\n [config, onChange]\n );\n\n return (\n <div className=\"space-y-4\">\n {/* Variant */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">Gauge Type</label>\n <select\n value={config.variant || 'circular'}\n onChange={(e) => handleChange('variant', e.target.value as ConfigType['variant'])}\n className=\"\n w-full px-3 py-2\n text-sm text-text-primary\n bg-bg-surface\n border border-border-default\n rounded-md\n focus:outline-none focus:ring-2 focus:ring-action-primary-bg\n \"\n >\n <option value=\"circular\">Circular</option>\n <option value=\"linear\">Linear</option>\n </select>\n </div>\n\n {/* Min/Max Range */}\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">Minimum</label>\n <input\n type=\"number\"\n value={config.min ?? 0}\n onChange={(e) => handleChange('min', Number(e.target.value))}\n className=\"\n w-full px-3 py-2\n text-sm text-text-primary\n bg-bg-surface\n border border-border-default\n rounded-md\n focus:outline-none focus:ring-2 focus:ring-action-primary-bg\n \"\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">Maximum</label>\n <input\n type=\"number\"\n value={config.max ?? 100}\n onChange={(e) => handleChange('max', Number(e.target.value))}\n className=\"\n w-full px-3 py-2\n text-sm text-text-primary\n bg-bg-surface\n border border-border-default\n rounded-md\n focus:outline-none focus:ring-2 focus:ring-action-primary-bg\n \"\n />\n </div>\n </div>\n\n {/* Unit */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">Unit Label</label>\n <input\n type=\"text\"\n value={config.unit || ''}\n onChange={(e) => handleChange('unit', e.target.value)}\n placeholder=\"e.g., %, ms, users\"\n className=\"\n w-full px-3 py-2\n text-sm text-text-primary\n bg-bg-surface\n border border-border-default\n rounded-md\n focus:outline-none focus:ring-2 focus:ring-action-primary-bg\n \"\n />\n </div>\n\n {/* Format */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">Value Format</label>\n <select\n value={config.format || 'number'}\n onChange={(e) => handleChange('format', e.target.value as ConfigType['format'])}\n className=\"\n w-full px-3 py-2\n text-sm text-text-primary\n bg-bg-surface\n border border-border-default\n rounded-md\n focus:outline-none focus:ring-2 focus:ring-action-primary-bg\n \"\n >\n <option value=\"number\">Number</option>\n <option value=\"percentage\">Percentage</option>\n <option value=\"currency\">Currency</option>\n </select>\n </div>\n\n {/* Thresholds Section */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-2\">Color Thresholds</label>\n <p className=\"text-xs text-text-secondary mb-3\">\n Define color zones based on value ranges. Values up to each threshold will use that color.\n </p>\n\n <div className=\"space-y-2\">\n {/* Red zone */}\n <div className=\"flex items-center gap-2\">\n <div className=\"w-4 h-4 rounded bg-state-error\" />\n <span className=\"text-sm text-text-primary flex-1\">Danger (Red)</span>\n <input\n type=\"number\"\n value={config.thresholds?.[0]?.value ?? 33}\n onChange={(e) => {\n const thresholds = [...(config.thresholds || [])];\n thresholds[0] = {\n value: Number(e.target.value),\n color: 'error',\n };\n handleChange('thresholds', thresholds);\n }}\n className=\"\n w-20 px-2 py-1\n text-sm text-text-primary\n bg-bg-surface\n border border-border-default\n rounded-md\n focus:outline-none focus:ring-2 focus:ring-action-primary-bg\n \"\n />\n </div>\n\n {/* Yellow zone */}\n <div className=\"flex items-center gap-2\">\n <div className=\"w-4 h-4 rounded bg-state-warning\" />\n <span className=\"text-sm text-text-primary flex-1\">Warning (Yellow)</span>\n <input\n type=\"number\"\n value={config.thresholds?.[1]?.value ?? 66}\n onChange={(e) => {\n const thresholds = [...(config.thresholds || [])];\n thresholds[1] = {\n value: Number(e.target.value),\n color: 'warning',\n };\n handleChange('thresholds', thresholds);\n }}\n className=\"\n w-20 px-2 py-1\n text-sm text-text-primary\n bg-bg-surface\n border border-border-default\n rounded-md\n focus:outline-none focus:ring-2 focus:ring-action-primary-bg\n \"\n />\n </div>\n\n {/* Green zone */}\n <div className=\"flex items-center gap-2\">\n <div className=\"w-4 h-4 rounded bg-state-success\" />\n <span className=\"text-sm text-text-primary flex-1\">Good (Green)</span>\n <input\n type=\"number\"\n value={config.thresholds?.[2]?.value ?? 100}\n onChange={(e) => {\n const thresholds = [...(config.thresholds || [])];\n thresholds[2] = {\n value: Number(e.target.value),\n color: 'success',\n };\n handleChange('thresholds', thresholds);\n }}\n className=\"\n w-20 px-2 py-1\n text-sm text-text-primary\n bg-bg-surface\n border border-border-default\n rounded-md\n focus:outline-none focus:ring-2 focus:ring-action-primary-bg\n \"\n />\n </div>\n </div>\n </div>\n\n {/* Show zones (for linear gauge) */}\n {config.variant === 'linear' && (\n <label className=\"flex items-center gap-3\">\n <input\n type=\"checkbox\"\n checked={config.showZones === true}\n onChange={(e) => handleChange('showZones', e.target.checked)}\n className=\"\n w-4 h-4\n text-action-primary-bg\n bg-bg-surface\n border-border-default\n rounded\n focus:ring-action-primary-bg\n \"\n />\n <span className=\"text-sm text-text-primary\">Show color zones in background</span>\n </label>\n )}\n </div>\n );\n});\n\nexport default GaugeConfig;\n"],"mappings":"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"}
1
+ {"version":3,"file":"GaugeConfig.js","names":[],"sources":["../../../../../src/bigconsole/components/widgets/gauge/GaugeConfig.tsx"],"sourcesContent":["/**\n * GaugeConfig Component\n *\n * Configuration panel for Gauge widgets.\n */\n\nimport { type FC, memo, useCallback } from 'react';\nimport type { GaugeConfig as ConfigType } from '../../../types';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface GaugeConfigProps {\n /** Current configuration */\n config: ConfigType;\n /** Callback when config changes */\n onChange: (config: ConfigType) => void;\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const GaugeConfig: FC<GaugeConfigProps> = memo(function GaugeConfig({ config, onChange }) {\n const handleChange = useCallback(\n <K extends keyof ConfigType>(key: K, value: ConfigType[K]) => {\n onChange({ ...config, [key]: value });\n },\n [config, onChange]\n );\n\n return (\n <div className=\"space-y-4\">\n {/* Variant */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">Gauge Type</label>\n <select\n value={config.variant || 'circular'}\n onChange={(e) => handleChange('variant', e.target.value as ConfigType['variant'])}\n className=\"\n w-full px-3 py-2\n text-sm text-text-primary\n bg-bg-surface\n border border-border-default\n rounded-md\n focus:outline-none focus:ring-2 focus:ring-action-primary-bg\n \"\n >\n <option value=\"circular\">Circular</option>\n <option value=\"linear\">Linear</option>\n </select>\n </div>\n\n {/* Min/Max Range */}\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">Minimum</label>\n <input\n type=\"number\"\n value={config.min ?? 0}\n onChange={(e) => handleChange('min', Number(e.target.value))}\n className=\"\n w-full px-3 py-2\n text-sm text-text-primary\n bg-bg-surface\n border border-border-default\n rounded-md\n focus:outline-none focus:ring-2 focus:ring-action-primary-bg\n \"\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">Maximum</label>\n <input\n type=\"number\"\n value={config.max ?? 100}\n onChange={(e) => handleChange('max', Number(e.target.value))}\n className=\"\n w-full px-3 py-2\n text-sm text-text-primary\n bg-bg-surface\n border border-border-default\n rounded-md\n focus:outline-none focus:ring-2 focus:ring-action-primary-bg\n \"\n />\n </div>\n </div>\n\n {/* Unit */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">Unit Label</label>\n <input\n type=\"text\"\n value={config.unit || ''}\n onChange={(e) => handleChange('unit', e.target.value)}\n placeholder=\"e.g., %, ms, users\"\n className=\"\n w-full px-3 py-2\n text-sm text-text-primary\n bg-bg-surface\n border border-border-default\n rounded-md\n focus:outline-none focus:ring-2 focus:ring-action-primary-bg\n \"\n />\n </div>\n\n {/* Format */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">Value Format</label>\n <select\n value={config.format || 'number'}\n onChange={(e) => handleChange('format', e.target.value as ConfigType['format'])}\n className=\"\n w-full px-3 py-2\n text-sm text-text-primary\n bg-bg-surface\n border border-border-default\n rounded-md\n focus:outline-none focus:ring-2 focus:ring-action-primary-bg\n \"\n >\n <option value=\"number\">Number</option>\n <option value=\"percentage\">Percentage</option>\n <option value=\"currency\">Currency</option>\n </select>\n </div>\n\n {/* Thresholds Section */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-2\">Color Thresholds</label>\n <p className=\"text-xs text-text-secondary mb-3\">\n Define color zones based on value ranges. Values up to each threshold will use that color.\n </p>\n\n <div className=\"space-y-2\">\n {/* Red zone */}\n <div className=\"flex items-center gap-2\">\n <div className=\"w-4 h-4 rounded bg-status-error-bg\" />\n <span className=\"text-sm text-text-primary flex-1\">Danger (Red)</span>\n <input\n type=\"number\"\n value={config.thresholds?.[0]?.value ?? 33}\n onChange={(e) => {\n const thresholds = [...(config.thresholds || [])];\n thresholds[0] = {\n value: Number(e.target.value),\n color: 'error',\n };\n handleChange('thresholds', thresholds);\n }}\n className=\"\n w-20 px-2 py-1\n text-sm text-text-primary\n bg-bg-surface\n border border-border-default\n rounded-md\n focus:outline-none focus:ring-2 focus:ring-action-primary-bg\n \"\n />\n </div>\n\n {/* Yellow zone */}\n <div className=\"flex items-center gap-2\">\n <div className=\"w-4 h-4 rounded bg-status-warning-bg\" />\n <span className=\"text-sm text-text-primary flex-1\">Warning (Yellow)</span>\n <input\n type=\"number\"\n value={config.thresholds?.[1]?.value ?? 66}\n onChange={(e) => {\n const thresholds = [...(config.thresholds || [])];\n thresholds[1] = {\n value: Number(e.target.value),\n color: 'warning',\n };\n handleChange('thresholds', thresholds);\n }}\n className=\"\n w-20 px-2 py-1\n text-sm text-text-primary\n bg-bg-surface\n border border-border-default\n rounded-md\n focus:outline-none focus:ring-2 focus:ring-action-primary-bg\n \"\n />\n </div>\n\n {/* Green zone */}\n <div className=\"flex items-center gap-2\">\n <div className=\"w-4 h-4 rounded bg-status-success-bg\" />\n <span className=\"text-sm text-text-primary flex-1\">Good (Green)</span>\n <input\n type=\"number\"\n value={config.thresholds?.[2]?.value ?? 100}\n onChange={(e) => {\n const thresholds = [...(config.thresholds || [])];\n thresholds[2] = {\n value: Number(e.target.value),\n color: 'success',\n };\n handleChange('thresholds', thresholds);\n }}\n className=\"\n w-20 px-2 py-1\n text-sm text-text-primary\n bg-bg-surface\n border border-border-default\n rounded-md\n focus:outline-none focus:ring-2 focus:ring-action-primary-bg\n \"\n />\n </div>\n </div>\n </div>\n\n {/* Show zones (for linear gauge) */}\n {config.variant === 'linear' && (\n <label className=\"flex items-center gap-3\">\n <input\n type=\"checkbox\"\n checked={config.showZones === true}\n onChange={(e) => handleChange('showZones', e.target.checked)}\n className=\"\n w-4 h-4\n text-action-primary-bg\n bg-bg-surface\n border-border-default\n rounded\n focus:ring-action-primary-bg\n \"\n />\n <span className=\"text-sm text-text-primary\">Show color zones in background</span>\n </label>\n )}\n </div>\n );\n});\n\nexport default GaugeConfig;\n"],"mappings":"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"}
@@ -1,34 +1,60 @@
1
1
  import { memo as e, useCallback as t, useMemo as n } from "react";
2
2
  import { jsx as r, jsxs as i } from "react/jsx-runtime";
3
3
  //#region src/bigconsole/components/widgets/heatmap/HeatmapWidget.tsx
4
- function a(e, t, n, r = "blue") {
4
+ var a = {
5
+ blue: [
6
+ "bg-action-primary-bg/10",
7
+ "bg-action-primary-bg/30",
8
+ "bg-action-primary-bg/50",
9
+ "bg-action-primary-bg/70",
10
+ "bg-action-primary-bg/90"
11
+ ],
12
+ green: [
13
+ "bg-status-success-bg/10",
14
+ "bg-status-success-bg/30",
15
+ "bg-status-success-bg/50",
16
+ "bg-status-success-bg/70",
17
+ "bg-status-success-bg/90"
18
+ ],
19
+ red: [
20
+ "bg-status-error-bg/10",
21
+ "bg-status-error-bg/30",
22
+ "bg-status-error-bg/50",
23
+ "bg-status-error-bg/70",
24
+ "bg-status-error-bg/90"
25
+ ]
26
+ };
27
+ function o(e) {
28
+ return a[e ?? "blue"] ?? a.blue;
29
+ }
30
+ function s(e, t, n, r = "blue") {
5
31
  let i = Math.max(0, Math.min(1, (e - t) / (n - t || 1)));
6
- return r === "green" ? i < .2 ? "bg-state-success/10" : i < .4 ? "bg-state-success/30" : i < .6 ? "bg-state-success/50" : i < .8 ? "bg-state-success/70" : "bg-state-success/90" : r === "red" ? i < .2 ? "bg-state-error/10" : i < .4 ? "bg-state-error/30" : i < .6 ? "bg-state-error/50" : i < .8 ? "bg-state-error/70" : "bg-state-error/90" : i < .2 ? "bg-action-primary-bg/10" : i < .4 ? "bg-action-primary-bg/30" : i < .6 ? "bg-action-primary-bg/50" : i < .8 ? "bg-action-primary-bg/70" : "bg-action-primary-bg/90";
32
+ return o(r)[i < .2 ? 0 : i < .4 ? 1 : i < .6 ? 2 : i < .8 ? 3 : 4];
7
33
  }
8
- var o = e(function({ widget: e, data: o, onClick: s }) {
9
- let c = e.config, l = n(() => {
34
+ var c = e(function({ widget: e, data: a, onClick: c }) {
35
+ let l = e.config, u = n(() => {
10
36
  let e = /* @__PURE__ */ new Map();
11
- return o?.cells && Array.isArray(o.cells) && o.cells.forEach((t) => {
37
+ return a?.cells && Array.isArray(a.cells) && a.cells.forEach((t) => {
12
38
  e.set(`${t.row}:${t.column}`, t.value);
13
39
  }), e;
14
- }, [o?.cells]), { minValue: u, maxValue: d } = n(() => {
15
- if (!o?.cells || !Array.isArray(o.cells) || !o.cells.length) return {
40
+ }, [a?.cells]), { minValue: d, maxValue: f } = n(() => {
41
+ if (!a?.cells || !Array.isArray(a.cells) || !a.cells.length) return {
16
42
  minValue: 0,
17
43
  maxValue: 100
18
44
  };
19
- let e = o.cells.map((e) => e.value);
45
+ let e = a.cells.map((e) => e.value);
20
46
  return {
21
- minValue: o.minValue ?? Math.min(...e),
22
- maxValue: o.maxValue ?? Math.max(...e)
47
+ minValue: a.minValue ?? Math.min(...e),
48
+ maxValue: a.maxValue ?? Math.max(...e)
23
49
  };
24
- }, [o]), f = t((t, n, r) => {
25
- s && e.drilldowns?.length && s({
50
+ }, [a]), p = t((t, n, r) => {
51
+ c && e.drilldowns?.length && c({
26
52
  row: t,
27
53
  column: n,
28
54
  value: r
29
55
  });
30
- }, [s, e.drilldowns]), p = !!(s && e.drilldowns?.length);
31
- return !o || !Array.isArray(o.rows) || !Array.isArray(o.columns) || !o.rows.length || !o.columns.length ? /* @__PURE__ */ r("div", {
56
+ }, [c, e.drilldowns]), m = !!(c && e.drilldowns?.length);
57
+ return !a || !Array.isArray(a.rows) || !Array.isArray(a.columns) || !a.rows.length || !a.columns.length ? /* @__PURE__ */ r("div", {
32
58
  className: "flex items-center justify-center h-full text-text-secondary text-sm",
33
59
  children: "No heatmap data available"
34
60
  }) : /* @__PURE__ */ i("div", {
@@ -37,37 +63,37 @@ var o = e(function({ widget: e, data: o, onClick: s }) {
37
63
  className: "min-h-0 flex-1 overflow-auto",
38
64
  children: /* @__PURE__ */ i("table", {
39
65
  className: "w-full border-collapse",
40
- children: [/* @__PURE__ */ r("thead", { children: /* @__PURE__ */ i("tr", { children: [/* @__PURE__ */ r("th", { className: "p-2 text-xs text-text-secondary text-left" }), o.columns.map((e) => /* @__PURE__ */ r("th", {
66
+ children: [/* @__PURE__ */ r("thead", { children: /* @__PURE__ */ i("tr", { children: [/* @__PURE__ */ r("th", { className: "p-2 text-xs text-text-secondary text-left" }), a.columns.map((e) => /* @__PURE__ */ r("th", {
41
67
  className: "p-2 text-xs font-medium text-text-secondary text-center min-w-[60px]",
42
68
  children: e
43
- }, e))] }) }), /* @__PURE__ */ r("tbody", { children: o.rows.map((e) => /* @__PURE__ */ i("tr", { children: [/* @__PURE__ */ r("td", {
69
+ }, e))] }) }), /* @__PURE__ */ r("tbody", { children: a.rows.map((e) => /* @__PURE__ */ i("tr", { children: [/* @__PURE__ */ r("td", {
44
70
  className: "p-2 text-xs font-medium text-text-secondary text-left",
45
71
  children: e
46
- }), o.columns.map((t) => {
47
- let n = l.get(`${e}:${t}`) ?? 0, i = a(n, u, d, c?.colorScheme);
72
+ }), a.columns.map((t) => {
73
+ let n = u.get(`${e}:${t}`) ?? 0, i = s(n, d, f, l?.colorScheme);
48
74
  return /* @__PURE__ */ r("td", {
49
75
  className: `
50
76
  p-0 text-center
51
- ${p ? "cursor-pointer" : ""}
77
+ ${m ? "cursor-pointer" : ""}
52
78
  `,
53
- onClick: () => f(e, t, n),
79
+ onClick: () => p(e, t, n),
54
80
  children: /* @__PURE__ */ r("div", {
55
81
  className: `
56
82
  m-0.5 p-2 rounded
57
83
  ${i}
58
- ${p ? "hover:ring-2 hover:ring-action-primary-bg" : ""}
84
+ ${m ? "hover:ring-2 hover:ring-action-primary-bg" : ""}
59
85
  transition-all
60
86
  `,
61
87
  title: `${e} / ${t}: ${n}`,
62
- children: c?.showValues !== !1 && /* @__PURE__ */ r("span", {
88
+ children: l?.showValues !== !1 && /* @__PURE__ */ r("span", {
63
89
  className: "text-xs font-medium text-text-primary",
64
- children: typeof n == "number" ? n.toFixed(c?.decimals || 0) : n
90
+ children: typeof n == "number" ? n.toFixed(l?.decimals || 0) : n
65
91
  })
66
92
  })
67
93
  }, t);
68
94
  })] }, e)) })]
69
95
  })
70
- }), c?.showLegend !== !1 && /* @__PURE__ */ i("div", {
96
+ }), l?.showLegend !== !1 && /* @__PURE__ */ i("div", {
71
97
  className: "flex items-center justify-center gap-2 mt-4",
72
98
  children: [
73
99
  /* @__PURE__ */ r("span", {
@@ -76,13 +102,7 @@ var o = e(function({ widget: e, data: o, onClick: s }) {
76
102
  }),
77
103
  /* @__PURE__ */ r("div", {
78
104
  className: "flex gap-0.5",
79
- children: [
80
- 10,
81
- 30,
82
- 50,
83
- 70,
84
- 90
85
- ].map((e) => /* @__PURE__ */ r("div", { className: `w-6 h-4 rounded bg-action-primary-bg/${e}` }, e))
105
+ children: o(l?.colorScheme).map((e) => /* @__PURE__ */ r("div", { className: `w-6 h-4 rounded ${e}` }, e))
86
106
  }),
87
107
  /* @__PURE__ */ r("span", {
88
108
  className: "text-xs text-text-secondary",
@@ -93,6 +113,6 @@ var o = e(function({ widget: e, data: o, onClick: s }) {
93
113
  });
94
114
  });
95
115
  //#endregion
96
- export { o as default };
116
+ export { c as default };
97
117
 
98
118
  //# sourceMappingURL=HeatmapWidget.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"HeatmapWidget.js","names":[],"sources":["../../../../../src/bigconsole/components/widgets/heatmap/HeatmapWidget.tsx"],"sourcesContent":["/**\n * HeatmapWidget Component\n *\n * Heatmap grid visualization with color intensity based on values.\n */\n\nimport { type FC, memo, useMemo, useCallback } from 'react';\nimport type { Widget, HeatmapConfig } from '../../../types';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface HeatmapWidgetProps {\n widget: Widget;\n data?: HeatmapData;\n onClick?: (cell: HeatmapCell) => void;\n}\n\nexport interface HeatmapCell {\n row: string;\n column: string;\n value: number;\n}\n\nexport interface HeatmapData {\n rows: string[];\n columns: string[];\n cells: HeatmapCell[];\n minValue?: number;\n maxValue?: number;\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction getColorIntensity(value: number, min: number, max: number, colorScheme: string = 'blue'): string {\n const normalized = Math.max(0, Math.min(1, (value - min) / (max - min || 1)));\n\n // Color schemes using CSS variables\n if (colorScheme === 'green') {\n if (normalized < 0.2) return 'bg-state-success/10';\n if (normalized < 0.4) return 'bg-state-success/30';\n if (normalized < 0.6) return 'bg-state-success/50';\n if (normalized < 0.8) return 'bg-state-success/70';\n return 'bg-state-success/90';\n }\n\n if (colorScheme === 'red') {\n if (normalized < 0.2) return 'bg-state-error/10';\n if (normalized < 0.4) return 'bg-state-error/30';\n if (normalized < 0.6) return 'bg-state-error/50';\n if (normalized < 0.8) return 'bg-state-error/70';\n return 'bg-state-error/90';\n }\n\n // Default blue\n if (normalized < 0.2) return 'bg-action-primary-bg/10';\n if (normalized < 0.4) return 'bg-action-primary-bg/30';\n if (normalized < 0.6) return 'bg-action-primary-bg/50';\n if (normalized < 0.8) return 'bg-action-primary-bg/70';\n return 'bg-action-primary-bg/90';\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const HeatmapWidget: FC<HeatmapWidgetProps> = memo(function HeatmapWidget({ widget, data, onClick }) {\n const config = widget.config as unknown as HeatmapConfig | undefined;\n\n // Build cell lookup map\n const cellMap = useMemo(() => {\n const map = new Map<string, number>();\n if (data?.cells && Array.isArray(data.cells)) {\n data.cells.forEach((cell) => {\n map.set(`${cell.row}:${cell.column}`, cell.value);\n });\n }\n return map;\n }, [data?.cells]);\n\n // Calculate min/max\n const { minValue, maxValue } = useMemo(() => {\n if (!data?.cells || !Array.isArray(data.cells) || !data.cells.length) {\n return { minValue: 0, maxValue: 100 };\n }\n\n const values = data.cells.map((c) => c.value);\n return {\n minValue: data.minValue ?? Math.min(...values),\n maxValue: data.maxValue ?? Math.max(...values),\n };\n }, [data]);\n\n const handleCellClick = useCallback(\n (row: string, column: string, value: number) => {\n if (onClick && widget.drilldowns?.length) {\n onClick({ row, column, value });\n }\n },\n [onClick, widget.drilldowns]\n );\n\n const isClickable = Boolean(onClick && widget.drilldowns?.length);\n\n // Early return if data is not properly structured\n if (!data || !Array.isArray(data.rows) || !Array.isArray(data.columns) || !data.rows.length || !data.columns.length) {\n return (\n <div className=\"flex items-center justify-center h-full text-text-secondary text-sm\">\n No heatmap data available\n </div>\n );\n }\n\n return (\n <div className=\"flex flex-col h-full p-4\">\n {/* Grid — `min-h-0` lets this flex child shrink so it scrolls instead of\n clipping its last rows when the widget is shorter than the grid. */}\n <div className=\"min-h-0 flex-1 overflow-auto\">\n <table className=\"w-full border-collapse\">\n <thead>\n <tr>\n <th className=\"p-2 text-xs text-text-secondary text-left\" />\n {data.columns.map((col) => (\n <th key={col} className=\"p-2 text-xs font-medium text-text-secondary text-center min-w-[60px]\">\n {col}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {data.rows.map((row) => (\n <tr key={row}>\n <td className=\"p-2 text-xs font-medium text-text-secondary text-left\">{row}</td>\n {data.columns.map((col) => {\n const value = cellMap.get(`${row}:${col}`) ?? 0;\n const colorClass = getColorIntensity(value, minValue, maxValue, config?.colorScheme);\n\n return (\n <td\n key={col}\n className={`\n p-0 text-center\n ${isClickable ? 'cursor-pointer' : ''}\n `}\n onClick={() => handleCellClick(row, col, value)}\n >\n <div\n className={`\n m-0.5 p-2 rounded\n ${colorClass}\n ${isClickable ? 'hover:ring-2 hover:ring-action-primary-bg' : ''}\n transition-all\n `}\n title={`${row} / ${col}: ${value}`}\n >\n {config?.showValues !== false && (\n <span className=\"text-xs font-medium text-text-primary\">\n {typeof value === 'number' ? value.toFixed(config?.decimals || 0) : value}\n </span>\n )}\n </div>\n </td>\n );\n })}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n\n {/* Legend */}\n {config?.showLegend !== false && (\n <div className=\"flex items-center justify-center gap-2 mt-4\">\n <span className=\"text-xs text-text-secondary\">Low</span>\n <div className=\"flex gap-0.5\">\n {[10, 30, 50, 70, 90].map((opacity) => (\n <div key={opacity} className={`w-6 h-4 rounded bg-action-primary-bg/${opacity}`} />\n ))}\n </div>\n <span className=\"text-xs text-text-secondary\">High</span>\n </div>\n )}\n </div>\n );\n});\n\nexport default HeatmapWidget;\n"],"mappings":"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"}
1
+ {"version":3,"file":"HeatmapWidget.js","names":[],"sources":["../../../../../src/bigconsole/components/widgets/heatmap/HeatmapWidget.tsx"],"sourcesContent":["/**\n * HeatmapWidget Component\n *\n * Heatmap grid visualization with color intensity based on values.\n */\n\nimport { type FC, memo, useMemo, useCallback } from 'react';\nimport type { Widget, HeatmapConfig } from '../../../types';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface HeatmapWidgetProps {\n widget: Widget;\n data?: HeatmapData;\n onClick?: (cell: HeatmapCell) => void;\n}\n\nexport interface HeatmapCell {\n row: string;\n column: string;\n value: number;\n}\n\nexport interface HeatmapData {\n rows: string[];\n columns: string[];\n cells: HeatmapCell[];\n minValue?: number;\n maxValue?: number;\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/**\n * Intensity ramps, five steps from lowest to highest.\n *\n * Every class is written out in full and never assembled at runtime: Tailwind\n * only emits utilities it can find as literal strings, so a template-built\n * class like `bg-…/${opacity}` produces no CSS unless something else happens\n * to declare it. Keeping the ramps here means the cells and the legend share\n * one source of truth and both are statically discoverable.\n */\nconst COLOR_RAMPS = {\n blue: [\n 'bg-action-primary-bg/10',\n 'bg-action-primary-bg/30',\n 'bg-action-primary-bg/50',\n 'bg-action-primary-bg/70',\n 'bg-action-primary-bg/90',\n ],\n green: [\n 'bg-status-success-bg/10',\n 'bg-status-success-bg/30',\n 'bg-status-success-bg/50',\n 'bg-status-success-bg/70',\n 'bg-status-success-bg/90',\n ],\n red: [\n 'bg-status-error-bg/10',\n 'bg-status-error-bg/30',\n 'bg-status-error-bg/50',\n 'bg-status-error-bg/70',\n 'bg-status-error-bg/90',\n ],\n} as const satisfies Record<string, readonly string[]>;\n\nexport type HeatmapColorScheme = keyof typeof COLOR_RAMPS;\n\n/** The ramp for a scheme, falling back to blue for anything unrecognised. */\nfunction rampFor(colorScheme?: string): readonly string[] {\n return COLOR_RAMPS[(colorScheme ?? 'blue') as HeatmapColorScheme] ?? COLOR_RAMPS.blue;\n}\n\nfunction getColorIntensity(value: number, min: number, max: number, colorScheme: string = 'blue'): string {\n const normalized = Math.max(0, Math.min(1, (value - min) / (max - min || 1)));\n const ramp = rampFor(colorScheme);\n const step = normalized < 0.2 ? 0 : normalized < 0.4 ? 1 : normalized < 0.6 ? 2 : normalized < 0.8 ? 3 : 4;\n return ramp[step] as string;\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const HeatmapWidget: FC<HeatmapWidgetProps> = memo(function HeatmapWidget({ widget, data, onClick }) {\n const config = widget.config as unknown as HeatmapConfig | undefined;\n\n // Build cell lookup map\n const cellMap = useMemo(() => {\n const map = new Map<string, number>();\n if (data?.cells && Array.isArray(data.cells)) {\n data.cells.forEach((cell) => {\n map.set(`${cell.row}:${cell.column}`, cell.value);\n });\n }\n return map;\n }, [data?.cells]);\n\n // Calculate min/max\n const { minValue, maxValue } = useMemo(() => {\n if (!data?.cells || !Array.isArray(data.cells) || !data.cells.length) {\n return { minValue: 0, maxValue: 100 };\n }\n\n const values = data.cells.map((c) => c.value);\n return {\n minValue: data.minValue ?? Math.min(...values),\n maxValue: data.maxValue ?? Math.max(...values),\n };\n }, [data]);\n\n const handleCellClick = useCallback(\n (row: string, column: string, value: number) => {\n if (onClick && widget.drilldowns?.length) {\n onClick({ row, column, value });\n }\n },\n [onClick, widget.drilldowns]\n );\n\n const isClickable = Boolean(onClick && widget.drilldowns?.length);\n\n // Early return if data is not properly structured\n if (!data || !Array.isArray(data.rows) || !Array.isArray(data.columns) || !data.rows.length || !data.columns.length) {\n return (\n <div className=\"flex items-center justify-center h-full text-text-secondary text-sm\">\n No heatmap data available\n </div>\n );\n }\n\n return (\n <div className=\"flex flex-col h-full p-4\">\n {/* Grid — `min-h-0` lets this flex child shrink so it scrolls instead of\n clipping its last rows when the widget is shorter than the grid. */}\n <div className=\"min-h-0 flex-1 overflow-auto\">\n <table className=\"w-full border-collapse\">\n <thead>\n <tr>\n <th className=\"p-2 text-xs text-text-secondary text-left\" />\n {data.columns.map((col) => (\n <th key={col} className=\"p-2 text-xs font-medium text-text-secondary text-center min-w-[60px]\">\n {col}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {data.rows.map((row) => (\n <tr key={row}>\n <td className=\"p-2 text-xs font-medium text-text-secondary text-left\">{row}</td>\n {data.columns.map((col) => {\n const value = cellMap.get(`${row}:${col}`) ?? 0;\n const colorClass = getColorIntensity(value, minValue, maxValue, config?.colorScheme);\n\n return (\n <td\n key={col}\n className={`\n p-0 text-center\n ${isClickable ? 'cursor-pointer' : ''}\n `}\n onClick={() => handleCellClick(row, col, value)}\n >\n <div\n className={`\n m-0.5 p-2 rounded\n ${colorClass}\n ${isClickable ? 'hover:ring-2 hover:ring-action-primary-bg' : ''}\n transition-all\n `}\n title={`${row} / ${col}: ${value}`}\n >\n {config?.showValues !== false && (\n <span className=\"text-xs font-medium text-text-primary\">\n {typeof value === 'number' ? value.toFixed(config?.decimals || 0) : value}\n </span>\n )}\n </div>\n </td>\n );\n })}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n\n {/* Legend */}\n {config?.showLegend !== false && (\n <div className=\"flex items-center justify-center gap-2 mt-4\">\n <span className=\"text-xs text-text-secondary\">Low</span>\n <div className=\"flex gap-0.5\">\n {rampFor(config?.colorScheme).map((swatch) => (\n <div key={swatch} className={`w-6 h-4 rounded ${swatch}`} />\n ))}\n </div>\n <span className=\"text-xs text-text-secondary\">High</span>\n </div>\n )}\n </div>\n );\n});\n\nexport default HeatmapWidget;\n"],"mappings":"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"}
@@ -3,9 +3,9 @@ import { jsx as n, jsxs as r } from "react/jsx-runtime";
3
3
  //#region src/bigconsole/components/widgets/kanban/KanbanWidget.tsx
4
4
  function i(e) {
5
5
  switch (e) {
6
- case "high": return "bg-state-error/20 text-state-error";
7
- case "medium": return "bg-state-warning/20 text-state-warning";
8
- case "low": return "bg-state-success/20 text-state-success";
6
+ case "high": return "bg-status-error-bg/20 text-status-error-text";
7
+ case "medium": return "bg-status-warning-bg/20 text-status-warning-text";
8
+ case "low": return "bg-status-success-bg/20 text-status-success-text";
9
9
  default: return "bg-bg-sunken text-text-secondary";
10
10
  }
11
11
  }