@burdenoff/microfe-bigconsole 2026.622.1 → 2026.624.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 (153) hide show
  1. package/dist/bigconsole/BigConsoleRoutes.js +1 -1
  2. package/dist/bigconsole/BigConsoleRoutes.js.map +1 -1
  3. package/dist/bigconsole/components/DrilldownModal.js +4 -4
  4. package/dist/bigconsole/components/DrilldownModal.js.map +1 -1
  5. package/dist/bigconsole/components/dashboard/DashboardCanvas.js +1 -1
  6. package/dist/bigconsole/components/dashboard/DashboardCanvas.js.map +1 -1
  7. package/dist/bigconsole/components/dashboard/DashboardToolbar.js +2 -2
  8. package/dist/bigconsole/components/dashboard/DashboardToolbar.js.map +1 -1
  9. package/dist/bigconsole/components/dashboard/DrilldownBreadcrumb.js +1 -1
  10. package/dist/bigconsole/components/dashboard/DrilldownBreadcrumb.js.map +1 -1
  11. package/dist/bigconsole/components/dashboard/ImportDashboardDialog.js +28 -28
  12. package/dist/bigconsole/components/dashboard/ImportDashboardDialog.js.map +1 -1
  13. package/dist/bigconsole/components/datasink/CacheTTLSelector.js +1 -1
  14. package/dist/bigconsole/components/datasink/CacheTTLSelector.js.map +1 -1
  15. package/dist/bigconsole/components/datasink/DataSinkCard.js +4 -4
  16. package/dist/bigconsole/components/datasink/DataSinkCard.js.map +1 -1
  17. package/dist/bigconsole/components/datasink/DataSinkDataViewer.js +2 -2
  18. package/dist/bigconsole/components/datasink/DataSinkDataViewer.js.map +1 -1
  19. package/dist/bigconsole/components/datasink/DataSinkDeleteDialog.js +2 -2
  20. package/dist/bigconsole/components/datasink/DataSinkDeleteDialog.js.map +1 -1
  21. package/dist/bigconsole/components/datasink/DataSinkRefreshDialog.js +2 -2
  22. package/dist/bigconsole/components/datasink/DataSinkRefreshDialog.js.map +1 -1
  23. package/dist/bigconsole/components/datasink/DataSinkTableViewer.js +2 -2
  24. package/dist/bigconsole/components/datasink/DataSinkTableViewer.js.map +1 -1
  25. package/dist/bigconsole/components/datasink/ImportDataSinkDialog.js +25 -25
  26. package/dist/bigconsole/components/datasink/ImportDataSinkDialog.js.map +1 -1
  27. package/dist/bigconsole/components/datasink/LinkedParsersPanel.js +1 -1
  28. package/dist/bigconsole/components/datasink/LinkedParsersPanel.js.map +1 -1
  29. package/dist/bigconsole/components/datasink/LinkedWorkflowsPanel.js +3 -3
  30. package/dist/bigconsole/components/datasink/LinkedWorkflowsPanel.js.map +1 -1
  31. package/dist/bigconsole/components/datasink/RefreshTypeSelector.js +2 -2
  32. package/dist/bigconsole/components/datasink/RefreshTypeSelector.js.map +1 -1
  33. package/dist/bigconsole/components/datasink/WorkflowStatusPanel.js +2 -2
  34. package/dist/bigconsole/components/datasink/WorkflowStatusPanel.js.map +1 -1
  35. package/dist/bigconsole/components/parser/DataSinkSelector.js +3 -3
  36. package/dist/bigconsole/components/parser/DataSinkSelector.js.map +1 -1
  37. package/dist/bigconsole/components/parser/ImportParserDialog.js +26 -26
  38. package/dist/bigconsole/components/parser/ImportParserDialog.js.map +1 -1
  39. package/dist/bigconsole/components/parser/ParserCard.js +5 -5
  40. package/dist/bigconsole/components/parser/ParserCard.js.map +1 -1
  41. package/dist/bigconsole/components/parser/ParserSelector.js +6 -6
  42. package/dist/bigconsole/components/parser/ParserSelector.js.map +1 -1
  43. package/dist/bigconsole/components/parser/ParserStatusBadge.js +1 -1
  44. package/dist/bigconsole/components/parser/ParserStatusBadge.js.map +1 -1
  45. package/dist/bigconsole/components/parser/ParserTypeBadge.js +1 -1
  46. package/dist/bigconsole/components/parser/ParserTypeBadge.js.map +1 -1
  47. package/dist/bigconsole/components/pipeline/PipelineCard.js +2 -2
  48. package/dist/bigconsole/components/pipeline/PipelineCard.js.map +1 -1
  49. package/dist/bigconsole/components/pipeline/PipelineDeleteDialog.js +1 -1
  50. package/dist/bigconsole/components/pipeline/PipelineDeleteDialog.js.map +1 -1
  51. package/dist/bigconsole/components/pipeline/PipelineStatusBadge.js +1 -1
  52. package/dist/bigconsole/components/pipeline/PipelineStatusBadge.js.map +1 -1
  53. package/dist/bigconsole/components/widgets/DrilldownConfigPanel.js +11 -11
  54. package/dist/bigconsole/components/widgets/DrilldownConfigPanel.js.map +1 -1
  55. package/dist/bigconsole/components/widgets/PropertiesPanel.js +17 -17
  56. package/dist/bigconsole/components/widgets/PropertiesPanel.js.map +1 -1
  57. package/dist/bigconsole/components/widgets/WidgetConfigDialog.js +14 -14
  58. package/dist/bigconsole/components/widgets/WidgetConfigDialog.js.map +1 -1
  59. package/dist/bigconsole/components/widgets/WidgetHeader.js +4 -4
  60. package/dist/bigconsole/components/widgets/WidgetHeader.js.map +1 -1
  61. package/dist/bigconsole/components/widgets/WidgetMenu.js +3 -3
  62. package/dist/bigconsole/components/widgets/WidgetMenu.js.map +1 -1
  63. package/dist/bigconsole/components/widgets/WidgetWrapper.js +266 -238
  64. package/dist/bigconsole/components/widgets/WidgetWrapper.js.map +1 -1
  65. package/dist/bigconsole/components/widgets/calendar/CalendarWidget.js +1 -1
  66. package/dist/bigconsole/components/widgets/calendar/CalendarWidget.js.map +1 -1
  67. package/dist/bigconsole/components/widgets/comments/WidgetComments.js +2 -2
  68. package/dist/bigconsole/components/widgets/comments/WidgetComments.js.map +1 -1
  69. package/dist/bigconsole/components/widgets/custom/CustomWidget.js +5 -5
  70. package/dist/bigconsole/components/widgets/custom/CustomWidget.js.map +1 -1
  71. package/dist/bigconsole/components/widgets/gauge/GaugeWidget.js +1 -1
  72. package/dist/bigconsole/components/widgets/gauge/GaugeWidget.js.map +1 -1
  73. package/dist/bigconsole/components/widgets/gauge/LinearGauge.js +1 -1
  74. package/dist/bigconsole/components/widgets/gauge/LinearGauge.js.map +1 -1
  75. package/dist/bigconsole/components/widgets/kanban/KanbanWidget.js +2 -2
  76. package/dist/bigconsole/components/widgets/kanban/KanbanWidget.js.map +1 -1
  77. package/dist/bigconsole/components/widgets/kpi/KPIWidget.js +2 -2
  78. package/dist/bigconsole/components/widgets/kpi/KPIWidget.js.map +1 -1
  79. package/dist/bigconsole/components/widgets/kpi-comparison/KPIComparisonWidget.js +2 -2
  80. package/dist/bigconsole/components/widgets/kpi-comparison/KPIComparisonWidget.js.map +1 -1
  81. package/dist/bigconsole/components/widgets/list/ListItem.js +3 -3
  82. package/dist/bigconsole/components/widgets/list/ListItem.js.map +1 -1
  83. package/dist/bigconsole/components/widgets/map-widget/MapWidget.js +3 -3
  84. package/dist/bigconsole/components/widgets/map-widget/MapWidget.js.map +1 -1
  85. package/dist/bigconsole/components/widgets/metric-card/ComparisonBadge.js +1 -1
  86. package/dist/bigconsole/components/widgets/metric-card/ComparisonBadge.js.map +1 -1
  87. package/dist/bigconsole/components/widgets/metric-card/MetricCardWidget.js +2 -2
  88. package/dist/bigconsole/components/widgets/metric-card/MetricCardWidget.js.map +1 -1
  89. package/dist/bigconsole/components/widgets/pivot-table/PivotTableWidget.js +12 -12
  90. package/dist/bigconsole/components/widgets/pivot-table/PivotTableWidget.js.map +1 -1
  91. package/dist/bigconsole/components/widgets/progress/ProgressBar.js +2 -2
  92. package/dist/bigconsole/components/widgets/progress/ProgressBar.js.map +1 -1
  93. package/dist/bigconsole/components/widgets/progress/ProgressConfig.js +1 -1
  94. package/dist/bigconsole/components/widgets/progress/ProgressConfig.js.map +1 -1
  95. package/dist/bigconsole/components/widgets/progress/ProgressWidget.js +1 -1
  96. package/dist/bigconsole/components/widgets/progress/ProgressWidget.js.map +1 -1
  97. package/dist/bigconsole/components/widgets/retention/RetentionWidget.js +2 -2
  98. package/dist/bigconsole/components/widgets/retention/RetentionWidget.js.map +1 -1
  99. package/dist/bigconsole/components/widgets/states/EmptyState.js +1 -1
  100. package/dist/bigconsole/components/widgets/states/EmptyState.js.map +1 -1
  101. package/dist/bigconsole/components/widgets/table/TableCell.js +1 -1
  102. package/dist/bigconsole/components/widgets/table/TableCell.js.map +1 -1
  103. package/dist/bigconsole/components/widgets/table/TableConfig.js +1 -1
  104. package/dist/bigconsole/components/widgets/table/TableConfig.js.map +1 -1
  105. package/dist/bigconsole/components/widgets/table/TableHeader.js +2 -2
  106. package/dist/bigconsole/components/widgets/table/TableHeader.js.map +1 -1
  107. package/dist/bigconsole/components/widgets/table/TablePagination.js +4 -4
  108. package/dist/bigconsole/components/widgets/table/TablePagination.js.map +1 -1
  109. package/dist/bigconsole/components/widgets/table/TableWidget.js +5 -5
  110. package/dist/bigconsole/components/widgets/table/TableWidget.js.map +1 -1
  111. package/dist/bigconsole/components/widgets/text-widget/TextWidget.js +1 -1
  112. package/dist/bigconsole/components/widgets/text-widget/TextWidget.js.map +1 -1
  113. package/dist/bigconsole/components/widgets/timeline/TimelineWidget.js +3 -3
  114. package/dist/bigconsole/components/widgets/timeline/TimelineWidget.js.map +1 -1
  115. package/dist/bigconsole/components/widgets/utils/conditionalFormat.js +2 -2
  116. package/dist/bigconsole/components/widgets/utils/conditionalFormat.js.map +1 -1
  117. package/dist/bigconsole/components/widgets/widget-actions/WidgetActions.js +3 -3
  118. package/dist/bigconsole/components/widgets/widget-actions/WidgetActions.js.map +1 -1
  119. package/dist/bigconsole/components/workflow/WorkflowCard.js +2 -2
  120. package/dist/bigconsole/components/workflow/WorkflowCard.js.map +1 -1
  121. package/dist/bigconsole/components/workflow/WorkflowDeleteDialog.js +1 -1
  122. package/dist/bigconsole/components/workflow/WorkflowDeleteDialog.js.map +1 -1
  123. package/dist/bigconsole/pages/DashboardBuilderPage.js +2 -2
  124. package/dist/bigconsole/pages/DashboardBuilderPage.js.map +1 -1
  125. package/dist/bigconsole/pages/DashboardViewPage.js +1 -1
  126. package/dist/bigconsole/pages/DashboardViewPage.js.map +1 -1
  127. package/dist/bigconsole/pages/DashboardsPage.js +2 -2
  128. package/dist/bigconsole/pages/DashboardsPage.js.map +1 -1
  129. package/dist/bigconsole/pages/DataSinkBuilderPage.js +3 -3
  130. package/dist/bigconsole/pages/DataSinkBuilderPage.js.map +1 -1
  131. package/dist/bigconsole/pages/DataSinkViewPage.js +2 -2
  132. package/dist/bigconsole/pages/DataSinkViewPage.js.map +1 -1
  133. package/dist/bigconsole/pages/ParserBuilderPage.js +5 -5
  134. package/dist/bigconsole/pages/ParserBuilderPage.js.map +1 -1
  135. package/dist/bigconsole/pages/ParserViewPage.js +1 -1
  136. package/dist/bigconsole/pages/ParserViewPage.js.map +1 -1
  137. package/dist/bigconsole/pages/PipelineBuilderPage.js +4 -4
  138. package/dist/bigconsole/pages/PipelineBuilderPage.js.map +1 -1
  139. package/dist/bigconsole/pages/PipelineViewPage.js +9 -9
  140. package/dist/bigconsole/pages/PipelineViewPage.js.map +1 -1
  141. package/dist/bigconsole/pages/WidgetDemoPage.js +2 -2
  142. package/dist/bigconsole/pages/WidgetDemoPage.js.map +1 -1
  143. package/dist/bigconsole/pages/WorkflowViewPage.js +5 -5
  144. package/dist/bigconsole/pages/WorkflowViewPage.js.map +1 -1
  145. package/dist/bigconsole/renderers/adapters/AdaptiveCardRenderer.js +12 -12
  146. package/dist/bigconsole/renderers/adapters/AdaptiveCardRenderer.js.map +1 -1
  147. package/dist/bigconsole/renderers/adapters/BigConsoleRenderer.js +2 -2
  148. package/dist/bigconsole/renderers/adapters/BigConsoleRenderer.js.map +1 -1
  149. package/dist/bigconsole/renderers/adapters/BlockKitRenderer.js +5 -5
  150. package/dist/bigconsole/renderers/adapters/BlockKitRenderer.js.map +1 -1
  151. package/dist/bigconsole/renderers/adapters/CustomRenderer.js +2 -2
  152. package/dist/bigconsole/renderers/adapters/CustomRenderer.js.map +1 -1
  153. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"ProgressBar.js","names":[],"sources":["../../../../../src/bigconsole/components/widgets/progress/ProgressBar.tsx"],"sourcesContent":["/**\n * ProgressBar Component\n *\n * Horizontal progress bar with optional milestones and target.\n */\n\nimport { type FC, memo, useMemo } from 'react';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface Milestone {\n /** Position as percentage (0-100) */\n position: number;\n /** Optional label */\n label?: string;\n /** Whether milestone is reached */\n reached?: boolean;\n}\n\nexport interface ProgressBarProps {\n /** Current progress value (0-100) */\n value: number;\n /** Target value to show as marker */\n target?: number;\n /** Milestones to display */\n milestones?: Milestone[];\n /** Height of the bar */\n height?: number;\n /** Color based on progress */\n color?: 'default' | 'success' | 'warning' | 'error' | 'dynamic';\n /** Whether to show percentage label */\n showPercentage?: boolean;\n /** Whether to animate */\n animated?: boolean;\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction getProgressColor(value: number, color: ProgressBarProps['color']): string {\n if (color === 'dynamic') {\n if (value >= 75) return 'bg-state-success';\n if (value >= 50) return 'bg-state-warning';\n return 'bg-state-error';\n }\n\n switch (color) {\n case 'success':\n return 'bg-state-success';\n case 'warning':\n return 'bg-state-warning';\n case 'error':\n return 'bg-state-error';\n case 'default':\n default:\n return 'bg-action-primary-bg';\n }\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const ProgressBar: FC<ProgressBarProps> = memo(function ProgressBar({\n value,\n target,\n milestones = [],\n height = 8,\n color = 'default',\n showPercentage = false,\n animated = true,\n}) {\n const clampedValue = useMemo(() => Math.max(0, Math.min(100, value)), [value]);\n\n const progressColor = getProgressColor(clampedValue, color);\n\n return (\n <div className=\"w-full\">\n {/* Progress bar container */}\n <div className=\"relative w-full rounded-full bg-bg-muted overflow-visible\" style={{ height }}>\n {/* Progress fill */}\n <div\n className={`\n absolute inset-y-0 left-0 rounded-full\n ${progressColor}\n ${animated ? 'transition-all duration-500 ease-out' : ''}\n `}\n style={{ width: `${clampedValue}%` }}\n role=\"progressbar\"\n aria-valuenow={clampedValue}\n aria-valuemin={0}\n aria-valuemax={100}\n />\n\n {/* Target marker */}\n {target !== undefined && target >= 0 && target <= 100 && (\n <div\n className=\"absolute top-1/2 -translate-y-1/2 w-0.5 h-[150%] bg-text-primary z-10\"\n style={{ left: `${target}%` }}\n title={`Target: ${target}%`}\n >\n {/* Target flag */}\n <div className=\"absolute -top-3 left-1/2 -translate-x-1/2\">\n <svg className=\"w-3 h-3 text-text-primary\" fill=\"currentColor\" viewBox=\"0 0 24 24\">\n <path d=\"M5 21V3h14l-3 9 3 9H5z\" />\n </svg>\n </div>\n </div>\n )}\n\n {/* Milestones */}\n {milestones.map((milestone, index) => (\n <div\n key={index}\n className={`\n absolute top-1/2 -translate-y-1/2\n w-3 h-3 rounded-full border-2\n ${\n milestone.reached || clampedValue >= milestone.position\n ? 'bg-bg-surface border-action-primary-bg'\n : 'bg-bg-muted border-border-default'\n }\n z-10\n `}\n style={{ left: `${milestone.position}%`, transform: 'translate(-50%, -50%)' }}\n title={milestone.label || `${milestone.position}%`}\n />\n ))}\n </div>\n\n {/* Milestone labels */}\n {milestones.some((m) => m.label) && (\n <div className=\"relative mt-2\">\n {milestones.map((milestone, index) =>\n milestone.label ? (\n <span\n key={index}\n className=\"absolute text-xs text-text-secondary -translate-x-1/2 whitespace-nowrap\"\n style={{ left: `${milestone.position}%` }}\n >\n {milestone.label}\n </span>\n ) : null\n )}\n </div>\n )}\n\n {/* Percentage label */}\n {showPercentage && (\n <div className=\"flex justify-between items-center mt-1\">\n <span className=\"text-xs text-text-secondary\">0%</span>\n <span className=\"text-sm font-medium text-text-primary\">{clampedValue.toFixed(0)}%</span>\n <span className=\"text-xs text-text-secondary\">100%</span>\n </div>\n )}\n </div>\n );\n});\n\nexport default ProgressBar;\n"],"mappings":";;;AA0CA,SAAS,EAAiB,GAAe,GAA0C;AACjF,KAAI,MAAU,UAGZ,QAFI,KAAS,KAAW,qBACpB,KAAS,KAAW,qBACjB;AAGT,SAAQ,GAAR;EACE,KAAK,UACH,QAAO;EACT,KAAK,UACH,QAAO;EACT,KAAK,QACH,QAAO;EAET,QACE,QAAO;;;AAQb,IAAa,IAAoC,EAAK,SAAqB,EACzE,UACA,WACA,gBAAa,EAAE,EACf,YAAS,GACT,WAAQ,WACR,oBAAiB,IACjB,cAAW,MACV;CACD,IAAM,IAAe,QAAc,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,EAAM,CAAC,EAAE,CAAC,EAAM,CAAC,EAExE,IAAgB,EAAiB,GAAc,EAAM;AAE3D,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GAEE,kBAAC,OAAD;IAAK,WAAU;IAA4D,OAAO,EAAE,WAAQ;cAA5F;KAEE,kBAAC,OAAD;MACE,WAAW;;cAEP,EAAc;cACd,IAAW,yCAAyC,GAAG;;MAE3D,OAAO,EAAE,OAAO,GAAG,EAAa,IAAI;MACpC,MAAK;MACL,iBAAe;MACf,iBAAe;MACf,iBAAe;MACf,CAAA;KAGD,MAAW,KAAA,KAAa,KAAU,KAAK,KAAU,OAChD,kBAAC,OAAD;MACE,WAAU;MACV,OAAO,EAAE,MAAM,GAAG,EAAO,IAAI;MAC7B,OAAO,WAAW,EAAO;gBAGzB,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,OAAD;QAAK,WAAU;QAA4B,MAAK;QAAe,SAAQ;kBACrE,kBAAC,QAAD,EAAM,GAAE,0BAA2B,CAAA;QAC/B,CAAA;OACF,CAAA;MACF,CAAA;KAIP,EAAW,KAAK,GAAW,MAC1B,kBAAC,OAAD;MAEE,WAAW;;;gBAIP,EAAU,WAAW,KAAgB,EAAU,WAC3C,2CACA,oCACL;;;MAGH,OAAO;OAAE,MAAM,GAAG,EAAU,SAAS;OAAI,WAAW;OAAyB;MAC7E,OAAO,EAAU,SAAS,GAAG,EAAU,SAAS;MAChD,EAbK,EAaL,CACF;KACE;;GAGL,EAAW,MAAM,MAAM,EAAE,MAAM,IAC9B,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAW,KAAK,GAAW,MAC1B,EAAU,QACR,kBAAC,QAAD;KAEE,WAAU;KACV,OAAO,EAAE,MAAM,GAAG,EAAU,SAAS,IAAI;eAExC,EAAU;KACN,EALA,EAKA,GACL,KACL;IACG,CAAA;GAIP,KACC,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,QAAD;MAAM,WAAU;gBAA8B;MAAS,CAAA;KACvD,kBAAC,QAAD;MAAM,WAAU;gBAAhB,CAAyD,EAAa,QAAQ,EAAE,EAAC,IAAQ;;KACzF,kBAAC,QAAD;MAAM,WAAU;gBAA8B;MAAW,CAAA;KACrD;;GAEJ;;EAER"}
1
+ {"version":3,"file":"ProgressBar.js","names":[],"sources":["../../../../../src/bigconsole/components/widgets/progress/ProgressBar.tsx"],"sourcesContent":["/**\n * ProgressBar Component\n *\n * Horizontal progress bar with optional milestones and target.\n */\n\nimport { type FC, memo, useMemo } from 'react';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface Milestone {\n /** Position as percentage (0-100) */\n position: number;\n /** Optional label */\n label?: string;\n /** Whether milestone is reached */\n reached?: boolean;\n}\n\nexport interface ProgressBarProps {\n /** Current progress value (0-100) */\n value: number;\n /** Target value to show as marker */\n target?: number;\n /** Milestones to display */\n milestones?: Milestone[];\n /** Height of the bar */\n height?: number;\n /** Color based on progress */\n color?: 'default' | 'success' | 'warning' | 'error' | 'dynamic';\n /** Whether to show percentage label */\n showPercentage?: boolean;\n /** Whether to animate */\n animated?: boolean;\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction getProgressColor(value: number, color: ProgressBarProps['color']): string {\n if (color === 'dynamic') {\n if (value >= 75) return 'bg-state-success';\n if (value >= 50) return 'bg-state-warning';\n return 'bg-state-error';\n }\n\n switch (color) {\n case 'success':\n return 'bg-state-success';\n case 'warning':\n return 'bg-state-warning';\n case 'error':\n return 'bg-state-error';\n case 'default':\n default:\n return 'bg-action-primary-bg';\n }\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const ProgressBar: FC<ProgressBarProps> = memo(function ProgressBar({\n value,\n target,\n milestones = [],\n height = 8,\n color = 'default',\n showPercentage = false,\n animated = true,\n}) {\n const clampedValue = useMemo(() => Math.max(0, Math.min(100, value)), [value]);\n\n const progressColor = getProgressColor(clampedValue, color);\n\n return (\n <div className=\"w-full\">\n {/* Progress bar container */}\n <div className=\"relative w-full rounded-full bg-bg-sunken overflow-visible\" style={{ height }}>\n {/* Progress fill */}\n <div\n className={`\n absolute inset-y-0 left-0 rounded-full\n ${progressColor}\n ${animated ? 'transition-all duration-500 ease-out' : ''}\n `}\n style={{ width: `${clampedValue}%` }}\n role=\"progressbar\"\n aria-valuenow={clampedValue}\n aria-valuemin={0}\n aria-valuemax={100}\n />\n\n {/* Target marker */}\n {target !== undefined && target >= 0 && target <= 100 && (\n <div\n className=\"absolute top-1/2 -translate-y-1/2 w-0.5 h-[150%] bg-text-primary z-10\"\n style={{ left: `${target}%` }}\n title={`Target: ${target}%`}\n >\n {/* Target flag */}\n <div className=\"absolute -top-3 left-1/2 -translate-x-1/2\">\n <svg className=\"w-3 h-3 text-text-primary\" fill=\"currentColor\" viewBox=\"0 0 24 24\">\n <path d=\"M5 21V3h14l-3 9 3 9H5z\" />\n </svg>\n </div>\n </div>\n )}\n\n {/* Milestones */}\n {milestones.map((milestone, index) => (\n <div\n key={index}\n className={`\n absolute top-1/2 -translate-y-1/2\n w-3 h-3 rounded-full border-2\n ${\n milestone.reached || clampedValue >= milestone.position\n ? 'bg-bg-surface border-action-primary-bg'\n : 'bg-bg-sunken border-border-default'\n }\n z-10\n `}\n style={{ left: `${milestone.position}%`, transform: 'translate(-50%, -50%)' }}\n title={milestone.label || `${milestone.position}%`}\n />\n ))}\n </div>\n\n {/* Milestone labels */}\n {milestones.some((m) => m.label) && (\n <div className=\"relative mt-2\">\n {milestones.map((milestone, index) =>\n milestone.label ? (\n <span\n key={index}\n className=\"absolute text-xs text-text-secondary -translate-x-1/2 whitespace-nowrap\"\n style={{ left: `${milestone.position}%` }}\n >\n {milestone.label}\n </span>\n ) : null\n )}\n </div>\n )}\n\n {/* Percentage label */}\n {showPercentage && (\n <div className=\"flex justify-between items-center mt-1\">\n <span className=\"text-xs text-text-secondary\">0%</span>\n <span className=\"text-sm font-medium text-text-primary\">{clampedValue.toFixed(0)}%</span>\n <span className=\"text-xs text-text-secondary\">100%</span>\n </div>\n )}\n </div>\n );\n});\n\nexport default ProgressBar;\n"],"mappings":";;;AA0CA,SAAS,EAAiB,GAAe,GAA0C;AACjF,KAAI,MAAU,UAGZ,QAFI,KAAS,KAAW,qBACpB,KAAS,KAAW,qBACjB;AAGT,SAAQ,GAAR;EACE,KAAK,UACH,QAAO;EACT,KAAK,UACH,QAAO;EACT,KAAK,QACH,QAAO;EAET,QACE,QAAO;;;AAQb,IAAa,IAAoC,EAAK,SAAqB,EACzE,UACA,WACA,gBAAa,EAAE,EACf,YAAS,GACT,WAAQ,WACR,oBAAiB,IACjB,cAAW,MACV;CACD,IAAM,IAAe,QAAc,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,EAAM,CAAC,EAAE,CAAC,EAAM,CAAC,EAExE,IAAgB,EAAiB,GAAc,EAAM;AAE3D,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GAEE,kBAAC,OAAD;IAAK,WAAU;IAA6D,OAAO,EAAE,WAAQ;cAA7F;KAEE,kBAAC,OAAD;MACE,WAAW;;cAEP,EAAc;cACd,IAAW,yCAAyC,GAAG;;MAE3D,OAAO,EAAE,OAAO,GAAG,EAAa,IAAI;MACpC,MAAK;MACL,iBAAe;MACf,iBAAe;MACf,iBAAe;MACf,CAAA;KAGD,MAAW,KAAA,KAAa,KAAU,KAAK,KAAU,OAChD,kBAAC,OAAD;MACE,WAAU;MACV,OAAO,EAAE,MAAM,GAAG,EAAO,IAAI;MAC7B,OAAO,WAAW,EAAO;gBAGzB,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,OAAD;QAAK,WAAU;QAA4B,MAAK;QAAe,SAAQ;kBACrE,kBAAC,QAAD,EAAM,GAAE,0BAA2B,CAAA;QAC/B,CAAA;OACF,CAAA;MACF,CAAA;KAIP,EAAW,KAAK,GAAW,MAC1B,kBAAC,OAAD;MAEE,WAAW;;;gBAIP,EAAU,WAAW,KAAgB,EAAU,WAC3C,2CACA,qCACL;;;MAGH,OAAO;OAAE,MAAM,GAAG,EAAU,SAAS;OAAI,WAAW;OAAyB;MAC7E,OAAO,EAAU,SAAS,GAAG,EAAU,SAAS;MAChD,EAbK,EAaL,CACF;KACE;;GAGL,EAAW,MAAM,MAAM,EAAE,MAAM,IAC9B,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAW,KAAK,GAAW,MAC1B,EAAU,QACR,kBAAC,QAAD;KAEE,WAAU;KACV,OAAO,EAAE,MAAM,GAAG,EAAU,SAAS,IAAI;eAExC,EAAU;KACN,EALA,EAKA,GACL,KACL;IACG,CAAA;GAIP,KACC,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,QAAD;MAAM,WAAU;gBAA8B;MAAS,CAAA;KACvD,kBAAC,QAAD;MAAM,WAAU;gBAAhB,CAAyD,EAAa,QAAQ,EAAE,EAAC,IAAQ;;KACzF,kBAAC,QAAD;MAAM,WAAU;gBAA8B;MAAW,CAAA;KACrD;;GAEJ;;EAER"}
@@ -111,7 +111,7 @@ var i = e(function({ config: e, onChange: i }) {
111
111
  }), /* @__PURE__ */ r("div", {
112
112
  className: "space-y-2",
113
113
  children: [(e.milestones || []).map((e, t) => /* @__PURE__ */ r("div", {
114
- className: "\n flex items-center gap-2\n p-2\n bg-bg-muted\n rounded-md\n ",
114
+ className: "\n flex items-center gap-2\n p-2\n bg-bg-sunken\n rounded-md\n ",
115
115
  children: [
116
116
  /* @__PURE__ */ n("input", {
117
117
  type: "number",
@@ -1 +1 @@
1
- {"version":3,"file":"ProgressConfig.js","names":[],"sources":["../../../../../src/bigconsole/components/widgets/progress/ProgressConfig.tsx"],"sourcesContent":["/**\n * ProgressConfig Component\n *\n * Configuration panel for Progress widgets.\n */\n\nimport { type FC, memo, useCallback } from 'react';\nimport type { ProgressConfig as ConfigType, ProgressMilestone } from '../../../types';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface ProgressConfigProps {\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 ProgressConfig: FC<ProgressConfigProps> = memo(function ProgressConfig({ 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 const handleMilestoneChange = useCallback(\n (index: number, field: 'position' | 'label', value: string | number) => {\n const milestones = [...(config.milestones || [])];\n milestones[index] = { ...milestones[index], [field]: value };\n handleChange('milestones', milestones);\n },\n [config.milestones, handleChange]\n );\n\n const addMilestone = useCallback(() => {\n const milestones = [...(config.milestones || []), { position: 50, label: '', value: 50 }];\n handleChange('milestones', milestones as ProgressMilestone[]);\n }, [config.milestones, handleChange]);\n\n const removeMilestone = useCallback(\n (index: number) => {\n const milestones = (config.milestones || []).filter((_, i) => i !== index);\n handleChange('milestones', milestones);\n },\n [config.milestones, handleChange]\n );\n\n return (\n <div className=\"space-y-4\">\n {/* Max Value */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">Maximum Value</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\n {/* Bar Height */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">Bar Height (px)</label>\n <input\n type=\"number\"\n value={config.barHeight ?? 12}\n onChange={(e) => handleChange('barHeight', Number(e.target.value))}\n min={4}\n max={32}\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 {/* Value 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=\"currency\">Currency</option>\n <option value=\"compact\">Compact (1K, 1M)</option>\n </select>\n </div>\n\n {/* Color Mode */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">Color Mode</label>\n <select\n value={config.colorMode || 'default'}\n onChange={(e) => handleChange('colorMode', e.target.value as ConfigType['colorMode'])}\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=\"default\">Default (Blue)</option>\n <option value=\"dynamic\">Dynamic (based on progress)</option>\n <option value=\"success\">Success (Green)</option>\n <option value=\"warning\">Warning (Yellow)</option>\n <option value=\"error\">Error (Red)</option>\n </select>\n </div>\n\n {/* Milestones */}\n <div>\n <div className=\"flex items-center justify-between mb-2\">\n <label className=\"text-sm font-medium text-text-primary\">Milestones</label>\n <button\n type=\"button\"\n onClick={addMilestone}\n className=\"\n text-sm text-action-primary-bg\n hover:text-action-primary-bgHover\n \"\n >\n + Add Milestone\n </button>\n </div>\n\n <div className=\"space-y-2\">\n {(config.milestones || []).map((milestone, index) => (\n <div\n key={index}\n className=\"\n flex items-center gap-2\n p-2\n bg-bg-muted\n rounded-md\n \"\n >\n <input\n type=\"number\"\n value={milestone.position}\n onChange={(e) => handleMilestoneChange(index, 'position', Number(e.target.value))}\n min={0}\n max={100}\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 placeholder=\"%\"\n />\n <input\n type=\"text\"\n value={milestone.label || ''}\n onChange={(e) => handleMilestoneChange(index, 'label', e.target.value)}\n className=\"\n flex-1 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 placeholder=\"Label (optional)\"\n />\n <button\n type=\"button\"\n onClick={() => removeMilestone(index)}\n className=\"text-state-error hover:text-state-error/80 p-1\"\n >\n <svg className=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n </div>\n ))}\n\n {(!config.milestones || config.milestones.length === 0) && (\n <p className=\"text-sm text-text-secondary text-center py-2\">No milestones configured</p>\n )}\n </div>\n </div>\n\n {/* Toggle Options */}\n <div className=\"space-y-3\">\n <label className=\"flex items-center gap-3\">\n <input\n type=\"checkbox\"\n checked={config.showPercentage !== false}\n onChange={(e) => handleChange('showPercentage', 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 percentage labels</span>\n </label>\n\n <label className=\"flex items-center gap-3\">\n <input\n type=\"checkbox\"\n checked={config.showMax !== false}\n onChange={(e) => handleChange('showMax', 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 maximum value</span>\n </label>\n </div>\n </div>\n );\n});\n\nexport default ProgressConfig;\n"],"mappings":"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"}
1
+ {"version":3,"file":"ProgressConfig.js","names":[],"sources":["../../../../../src/bigconsole/components/widgets/progress/ProgressConfig.tsx"],"sourcesContent":["/**\n * ProgressConfig Component\n *\n * Configuration panel for Progress widgets.\n */\n\nimport { type FC, memo, useCallback } from 'react';\nimport type { ProgressConfig as ConfigType, ProgressMilestone } from '../../../types';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface ProgressConfigProps {\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 ProgressConfig: FC<ProgressConfigProps> = memo(function ProgressConfig({ 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 const handleMilestoneChange = useCallback(\n (index: number, field: 'position' | 'label', value: string | number) => {\n const milestones = [...(config.milestones || [])];\n milestones[index] = { ...milestones[index], [field]: value };\n handleChange('milestones', milestones);\n },\n [config.milestones, handleChange]\n );\n\n const addMilestone = useCallback(() => {\n const milestones = [...(config.milestones || []), { position: 50, label: '', value: 50 }];\n handleChange('milestones', milestones as ProgressMilestone[]);\n }, [config.milestones, handleChange]);\n\n const removeMilestone = useCallback(\n (index: number) => {\n const milestones = (config.milestones || []).filter((_, i) => i !== index);\n handleChange('milestones', milestones);\n },\n [config.milestones, handleChange]\n );\n\n return (\n <div className=\"space-y-4\">\n {/* Max Value */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">Maximum Value</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\n {/* Bar Height */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">Bar Height (px)</label>\n <input\n type=\"number\"\n value={config.barHeight ?? 12}\n onChange={(e) => handleChange('barHeight', Number(e.target.value))}\n min={4}\n max={32}\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 {/* Value 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=\"currency\">Currency</option>\n <option value=\"compact\">Compact (1K, 1M)</option>\n </select>\n </div>\n\n {/* Color Mode */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">Color Mode</label>\n <select\n value={config.colorMode || 'default'}\n onChange={(e) => handleChange('colorMode', e.target.value as ConfigType['colorMode'])}\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=\"default\">Default (Blue)</option>\n <option value=\"dynamic\">Dynamic (based on progress)</option>\n <option value=\"success\">Success (Green)</option>\n <option value=\"warning\">Warning (Yellow)</option>\n <option value=\"error\">Error (Red)</option>\n </select>\n </div>\n\n {/* Milestones */}\n <div>\n <div className=\"flex items-center justify-between mb-2\">\n <label className=\"text-sm font-medium text-text-primary\">Milestones</label>\n <button\n type=\"button\"\n onClick={addMilestone}\n className=\"\n text-sm text-action-primary-bg\n hover:text-action-primary-bgHover\n \"\n >\n + Add Milestone\n </button>\n </div>\n\n <div className=\"space-y-2\">\n {(config.milestones || []).map((milestone, index) => (\n <div\n key={index}\n className=\"\n flex items-center gap-2\n p-2\n bg-bg-sunken\n rounded-md\n \"\n >\n <input\n type=\"number\"\n value={milestone.position}\n onChange={(e) => handleMilestoneChange(index, 'position', Number(e.target.value))}\n min={0}\n max={100}\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 placeholder=\"%\"\n />\n <input\n type=\"text\"\n value={milestone.label || ''}\n onChange={(e) => handleMilestoneChange(index, 'label', e.target.value)}\n className=\"\n flex-1 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 placeholder=\"Label (optional)\"\n />\n <button\n type=\"button\"\n onClick={() => removeMilestone(index)}\n className=\"text-state-error hover:text-state-error/80 p-1\"\n >\n <svg className=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n </div>\n ))}\n\n {(!config.milestones || config.milestones.length === 0) && (\n <p className=\"text-sm text-text-secondary text-center py-2\">No milestones configured</p>\n )}\n </div>\n </div>\n\n {/* Toggle Options */}\n <div className=\"space-y-3\">\n <label className=\"flex items-center gap-3\">\n <input\n type=\"checkbox\"\n checked={config.showPercentage !== false}\n onChange={(e) => handleChange('showPercentage', 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 percentage labels</span>\n </label>\n\n <label className=\"flex items-center gap-3\">\n <input\n type=\"checkbox\"\n checked={config.showMax !== false}\n onChange={(e) => handleChange('showMax', 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 maximum value</span>\n </label>\n </div>\n </div>\n );\n});\n\nexport default ProgressConfig;\n"],"mappings":"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"}
@@ -112,7 +112,7 @@ var u = a(function({ widget: a, data: u, onClick: d, onDrilldown: f }) {
112
112
  return m ? /* @__PURE__ */ l("div", {
113
113
  className: `
114
114
  flex flex-col h-full p-4
115
- ${S ? "cursor-pointer hover:bg-bg-muted/50 transition-colors" : ""}
115
+ ${S ? "cursor-pointer hover:bg-bg-sunken/50 transition-colors" : ""}
116
116
  `,
117
117
  onClick: x,
118
118
  role: S ? "button" : void 0,
@@ -1 +1 @@
1
- {"version":3,"file":"ProgressWidget.js","names":[],"sources":["../../../../../src/bigconsole/components/widgets/progress/ProgressWidget.tsx"],"sourcesContent":["/**\n * ProgressWidget Component\n *\n * Progress bar widget with target, milestones, and color stages.\n */\n\nimport { type FC, memo, useMemo, useCallback } from 'react';\nimport type { Widget, ProgressConfig } from '../../../types';\nimport { ProgressBar, type Milestone } from './ProgressBar';\nimport { firstObjectRow, inferValueField, inferMaxValue, unwrapRows } from '../utils/inferFields';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface ProgressWidgetProps {\n /** Widget configuration */\n widget: Widget;\n /** Progress data */\n data?: ProgressData;\n /** Click handler for drilldown */\n onClick?: () => void;\n /** Drilldown handler - receives progress data */\n onDrilldown?: (selectedData: Record<string, unknown>) => void;\n}\n\nexport interface ProgressData {\n /** Current value */\n value: number;\n /** Maximum value (for calculating percentage) */\n max?: number;\n /** Target value */\n target?: number;\n /** Label for the progress */\n label?: string;\n /** Description text */\n description?: string;\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const ProgressWidget: FC<ProgressWidgetProps> = memo(function ProgressWidget({\n widget,\n data: rawData,\n onClick,\n onDrilldown,\n}) {\n const config = widget.config as unknown as ProgressConfig | undefined;\n\n // BOFF-2246 smart default: if rawData is an array (or envelope) rather than\n // the expected `ProgressData` single object, pull the configured valueField\n // (flat OR dotted, e.g. `metrics.revenue`) or fall back to the first\n // numeric field. Auto-compute a sensible max from the dataset.\n const normalizedData = useMemo((): ProgressData | undefined => {\n if (!rawData) return undefined;\n // Already the expected shape — pass through. Accept both number and\n // numeric-string `value`, since parsers sometimes serialize metrics as\n // strings and the pre-BOFF-2246 code path rendered those via implicit\n // JS coercion in percentage math.\n if (typeof rawData === 'object' && !Array.isArray(rawData) && 'value' in rawData) {\n const rawValue = (rawData as { value: unknown }).value;\n if (typeof rawValue === 'number') {\n return rawData as ProgressData;\n }\n if (typeof rawValue === 'string') {\n const parsed = Number(rawValue);\n if (Number.isFinite(parsed)) {\n // Coerce to number so downstream math is deterministic.\n return { ...(rawData as ProgressData), value: parsed };\n }\n }\n }\n const row = firstObjectRow(rawData);\n if (!row) return undefined;\n const configuredField = (config as Record<string, unknown> | undefined)?.valueField as string | undefined;\n\n // Supports dotted paths like `metrics.revenue` so nested valueFields\n // configured in widget config resolve on array/envelope payloads the\n // same way they do on single-object payloads.\n const getNestedValue = (obj: Record<string, unknown>, path: string): unknown =>\n path.split('.').reduce<unknown>((current, key) => {\n return current && typeof current === 'object' ? (current as Record<string, unknown>)[key] : undefined;\n }, obj);\n\n // Coerce `number` or finite numeric string to a number. Mirrors the\n // pass-through branch above so the row path doesn't silently fall back\n // to inference when a parser serializes metrics as strings.\n const toNumber = (v: unknown): number | null => {\n if (typeof v === 'number' && Number.isFinite(v)) return v;\n if (typeof v === 'string') {\n const n = Number(v);\n if (Number.isFinite(n)) return n;\n }\n return null;\n };\n\n const labelFromRow =\n typeof row.label === 'string' ? row.label : typeof row.name === 'string' ? row.name : undefined;\n\n // Configured field is authoritative — scan every row for it so sparse\n // envelopes like `{data:[{id:1},{revenue:120}]}` with\n // `valueField:\"revenue\"` still resolve. If no row carries it, return\n // undefined rather than substituting an unrelated numeric column from\n // row[0] via inference (that would silently misrepresent user config).\n if (configuredField) {\n const rows = unwrapRows(rawData) ?? [row];\n for (const r of rows) {\n if (r && typeof r === 'object') {\n const resolvedRow = r as Record<string, unknown>;\n const resolved = toNumber(getNestedValue(resolvedRow, configuredField));\n if (resolved !== null) {\n const rowLabel =\n typeof resolvedRow.label === 'string'\n ? resolvedRow.label\n : typeof resolvedRow.name === 'string'\n ? resolvedRow.name\n : labelFromRow;\n return {\n value: resolved,\n max: config?.max ?? inferMaxValue(rawData, configuredField) ?? undefined,\n label: rowLabel,\n };\n }\n }\n }\n return undefined;\n }\n\n // Fall back to inference (only when no configured field). `inferValueField`\n // already filters to numeric fields, but we still coerce here for symmetry\n // with the configured branch above.\n const inferred = inferValueField(row);\n if (!inferred) return undefined;\n const inferredValue = toNumber(row[inferred]);\n if (inferredValue === null) return undefined;\n return {\n value: inferredValue,\n max: config?.max ?? inferMaxValue(rawData, inferred) ?? undefined,\n label: labelFromRow,\n };\n }, [rawData, config]);\n\n // Downstream render uses the normalized shape. Name kept as `data` so the\n // rest of the component body (formattedValue, handleClick, header, etc.)\n // stays unchanged.\n const data = normalizedData;\n\n // Calculate percentage\n const percentage = useMemo(() => {\n if (!data) return 0;\n const max = data.max || config?.max || 100;\n return (data.value / max) * 100;\n }, [data, config?.max]);\n\n // Calculate target percentage\n const targetPercentage = useMemo(() => {\n if (!data?.target) return undefined;\n const max = data.max || config?.max || 100;\n return (data.target / max) * 100;\n }, [data, config?.max]);\n\n // Build milestones from config\n const milestones: Milestone[] = useMemo(() => {\n if (!config?.milestones?.length) return [];\n return config.milestones.map((m) => ({\n position: m.position ?? m.value ?? 0,\n label: m.label,\n reached: percentage >= (m.position ?? m.value ?? 0),\n }));\n }, [config?.milestones, percentage]);\n\n // Determine color mode\n const colorMode = useMemo(() => {\n if (config?.colorMode === 'dynamic') return 'dynamic';\n if (config?.colorMode === 'success') return 'success';\n if (config?.colorMode === 'warning') return 'warning';\n if (config?.colorMode === 'error') return 'error';\n return 'default';\n }, [config?.colorMode]);\n\n // Format value\n const formattedValue = useMemo(() => {\n if (!data) return '0';\n const format = config?.format || 'number';\n\n switch (format) {\n case 'currency':\n return new Intl.NumberFormat('en-US', {\n style: 'currency',\n currency: 'USD',\n maximumFractionDigits: 0,\n }).format(data.value);\n case 'compact':\n return new Intl.NumberFormat('en-US', {\n notation: 'compact',\n maximumFractionDigits: 1,\n }).format(data.value);\n case 'number':\n default:\n return new Intl.NumberFormat('en-US').format(data.value);\n }\n }, [data, config?.format]);\n\n const formattedMax = useMemo(() => {\n const max = data?.max || config?.max || 100;\n const format = config?.format || 'number';\n\n switch (format) {\n case 'currency':\n return new Intl.NumberFormat('en-US', {\n style: 'currency',\n currency: 'USD',\n maximumFractionDigits: 0,\n }).format(max);\n case 'compact':\n return new Intl.NumberFormat('en-US', {\n notation: 'compact',\n maximumFractionDigits: 1,\n }).format(max);\n case 'number':\n default:\n return new Intl.NumberFormat('en-US').format(max);\n }\n }, [data?.max, config?.max, config?.format]);\n\n const handleClick = useCallback(() => {\n // Support both legacy onClick and new onDrilldown\n if (onDrilldown && data) {\n onDrilldown({\n value: data.value,\n max: data.max,\n target: data.target,\n label: data.label,\n percentage,\n });\n } else if (onClick && widget.drilldowns?.length) {\n onClick();\n }\n }, [onClick, onDrilldown, widget.drilldowns, data, percentage]);\n\n const hasDrilldown = !!widget.drilldown?.enabled;\n const isClickable = Boolean(hasDrilldown || (onClick && widget.drilldowns?.length));\n\n if (!data) {\n return null;\n }\n\n return (\n <div\n className={`\n flex flex-col h-full p-4\n ${isClickable ? 'cursor-pointer hover:bg-bg-muted/50 transition-colors' : ''}\n `}\n onClick={handleClick}\n role={isClickable ? 'button' : undefined}\n tabIndex={isClickable ? 0 : undefined}\n onKeyDown={(e) => {\n if (isClickable && (e.key === 'Enter' || e.key === ' ')) {\n e.preventDefault();\n handleClick();\n }\n }}\n >\n {/* Header */}\n <div className=\"flex items-start justify-between mb-4\">\n <div className=\"flex-1\">\n {data.label && <h4 className=\"text-sm font-medium text-text-primary\">{data.label}</h4>}\n {data.description && <p className=\"text-xs text-text-secondary mt-0.5\">{data.description}</p>}\n </div>\n\n {/* Value display */}\n <div className=\"text-right\">\n <span className=\"text-2xl font-bold text-text-primary\">{formattedValue}</span>\n {config?.showMax !== false && (\n <span className=\"text-sm text-text-secondary\">\n {' / '}\n {formattedMax}\n </span>\n )}\n </div>\n </div>\n\n {/* Progress bar */}\n <ProgressBar\n value={percentage}\n target={targetPercentage}\n milestones={milestones}\n height={config?.barHeight || 12}\n color={colorMode}\n showPercentage={config?.showPercentage !== false}\n />\n\n {/* Target info */}\n {data.target !== undefined && (\n <div className=\"mt-3 flex items-center gap-2 text-xs\">\n <svg className=\"w-4 h-4 text-text-secondary\" fill=\"currentColor\" viewBox=\"0 0 24 24\">\n <path d=\"M5 21V3h14l-3 9 3 9H5z\" />\n </svg>\n <span className=\"text-text-secondary\">\n Target:{' '}\n <span className=\"font-medium text-text-primary\">{new Intl.NumberFormat('en-US').format(data.target)}</span>\n {percentage >= (targetPercentage || 0) && <span className=\"text-state-success ml-1\">Reached!</span>}\n </span>\n </div>\n )}\n\n {/* Drilldown indicator */}\n {isClickable && (\n <div className=\"mt-auto pt-3 flex items-center justify-end\">\n <span className=\"text-xs text-text-secondary flex items-center gap-1\">\n View details\n <svg className=\"w-3 h-3\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M9 5l7 7-7 7\" />\n </svg>\n </span>\n </div>\n )}\n </div>\n );\n});\n\nexport default ProgressWidget;\n"],"mappings":"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"}
1
+ {"version":3,"file":"ProgressWidget.js","names":[],"sources":["../../../../../src/bigconsole/components/widgets/progress/ProgressWidget.tsx"],"sourcesContent":["/**\n * ProgressWidget Component\n *\n * Progress bar widget with target, milestones, and color stages.\n */\n\nimport { type FC, memo, useMemo, useCallback } from 'react';\nimport type { Widget, ProgressConfig } from '../../../types';\nimport { ProgressBar, type Milestone } from './ProgressBar';\nimport { firstObjectRow, inferValueField, inferMaxValue, unwrapRows } from '../utils/inferFields';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface ProgressWidgetProps {\n /** Widget configuration */\n widget: Widget;\n /** Progress data */\n data?: ProgressData;\n /** Click handler for drilldown */\n onClick?: () => void;\n /** Drilldown handler - receives progress data */\n onDrilldown?: (selectedData: Record<string, unknown>) => void;\n}\n\nexport interface ProgressData {\n /** Current value */\n value: number;\n /** Maximum value (for calculating percentage) */\n max?: number;\n /** Target value */\n target?: number;\n /** Label for the progress */\n label?: string;\n /** Description text */\n description?: string;\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const ProgressWidget: FC<ProgressWidgetProps> = memo(function ProgressWidget({\n widget,\n data: rawData,\n onClick,\n onDrilldown,\n}) {\n const config = widget.config as unknown as ProgressConfig | undefined;\n\n // BOFF-2246 smart default: if rawData is an array (or envelope) rather than\n // the expected `ProgressData` single object, pull the configured valueField\n // (flat OR dotted, e.g. `metrics.revenue`) or fall back to the first\n // numeric field. Auto-compute a sensible max from the dataset.\n const normalizedData = useMemo((): ProgressData | undefined => {\n if (!rawData) return undefined;\n // Already the expected shape — pass through. Accept both number and\n // numeric-string `value`, since parsers sometimes serialize metrics as\n // strings and the pre-BOFF-2246 code path rendered those via implicit\n // JS coercion in percentage math.\n if (typeof rawData === 'object' && !Array.isArray(rawData) && 'value' in rawData) {\n const rawValue = (rawData as { value: unknown }).value;\n if (typeof rawValue === 'number') {\n return rawData as ProgressData;\n }\n if (typeof rawValue === 'string') {\n const parsed = Number(rawValue);\n if (Number.isFinite(parsed)) {\n // Coerce to number so downstream math is deterministic.\n return { ...(rawData as ProgressData), value: parsed };\n }\n }\n }\n const row = firstObjectRow(rawData);\n if (!row) return undefined;\n const configuredField = (config as Record<string, unknown> | undefined)?.valueField as string | undefined;\n\n // Supports dotted paths like `metrics.revenue` so nested valueFields\n // configured in widget config resolve on array/envelope payloads the\n // same way they do on single-object payloads.\n const getNestedValue = (obj: Record<string, unknown>, path: string): unknown =>\n path.split('.').reduce<unknown>((current, key) => {\n return current && typeof current === 'object' ? (current as Record<string, unknown>)[key] : undefined;\n }, obj);\n\n // Coerce `number` or finite numeric string to a number. Mirrors the\n // pass-through branch above so the row path doesn't silently fall back\n // to inference when a parser serializes metrics as strings.\n const toNumber = (v: unknown): number | null => {\n if (typeof v === 'number' && Number.isFinite(v)) return v;\n if (typeof v === 'string') {\n const n = Number(v);\n if (Number.isFinite(n)) return n;\n }\n return null;\n };\n\n const labelFromRow =\n typeof row.label === 'string' ? row.label : typeof row.name === 'string' ? row.name : undefined;\n\n // Configured field is authoritative — scan every row for it so sparse\n // envelopes like `{data:[{id:1},{revenue:120}]}` with\n // `valueField:\"revenue\"` still resolve. If no row carries it, return\n // undefined rather than substituting an unrelated numeric column from\n // row[0] via inference (that would silently misrepresent user config).\n if (configuredField) {\n const rows = unwrapRows(rawData) ?? [row];\n for (const r of rows) {\n if (r && typeof r === 'object') {\n const resolvedRow = r as Record<string, unknown>;\n const resolved = toNumber(getNestedValue(resolvedRow, configuredField));\n if (resolved !== null) {\n const rowLabel =\n typeof resolvedRow.label === 'string'\n ? resolvedRow.label\n : typeof resolvedRow.name === 'string'\n ? resolvedRow.name\n : labelFromRow;\n return {\n value: resolved,\n max: config?.max ?? inferMaxValue(rawData, configuredField) ?? undefined,\n label: rowLabel,\n };\n }\n }\n }\n return undefined;\n }\n\n // Fall back to inference (only when no configured field). `inferValueField`\n // already filters to numeric fields, but we still coerce here for symmetry\n // with the configured branch above.\n const inferred = inferValueField(row);\n if (!inferred) return undefined;\n const inferredValue = toNumber(row[inferred]);\n if (inferredValue === null) return undefined;\n return {\n value: inferredValue,\n max: config?.max ?? inferMaxValue(rawData, inferred) ?? undefined,\n label: labelFromRow,\n };\n }, [rawData, config]);\n\n // Downstream render uses the normalized shape. Name kept as `data` so the\n // rest of the component body (formattedValue, handleClick, header, etc.)\n // stays unchanged.\n const data = normalizedData;\n\n // Calculate percentage\n const percentage = useMemo(() => {\n if (!data) return 0;\n const max = data.max || config?.max || 100;\n return (data.value / max) * 100;\n }, [data, config?.max]);\n\n // Calculate target percentage\n const targetPercentage = useMemo(() => {\n if (!data?.target) return undefined;\n const max = data.max || config?.max || 100;\n return (data.target / max) * 100;\n }, [data, config?.max]);\n\n // Build milestones from config\n const milestones: Milestone[] = useMemo(() => {\n if (!config?.milestones?.length) return [];\n return config.milestones.map((m) => ({\n position: m.position ?? m.value ?? 0,\n label: m.label,\n reached: percentage >= (m.position ?? m.value ?? 0),\n }));\n }, [config?.milestones, percentage]);\n\n // Determine color mode\n const colorMode = useMemo(() => {\n if (config?.colorMode === 'dynamic') return 'dynamic';\n if (config?.colorMode === 'success') return 'success';\n if (config?.colorMode === 'warning') return 'warning';\n if (config?.colorMode === 'error') return 'error';\n return 'default';\n }, [config?.colorMode]);\n\n // Format value\n const formattedValue = useMemo(() => {\n if (!data) return '0';\n const format = config?.format || 'number';\n\n switch (format) {\n case 'currency':\n return new Intl.NumberFormat('en-US', {\n style: 'currency',\n currency: 'USD',\n maximumFractionDigits: 0,\n }).format(data.value);\n case 'compact':\n return new Intl.NumberFormat('en-US', {\n notation: 'compact',\n maximumFractionDigits: 1,\n }).format(data.value);\n case 'number':\n default:\n return new Intl.NumberFormat('en-US').format(data.value);\n }\n }, [data, config?.format]);\n\n const formattedMax = useMemo(() => {\n const max = data?.max || config?.max || 100;\n const format = config?.format || 'number';\n\n switch (format) {\n case 'currency':\n return new Intl.NumberFormat('en-US', {\n style: 'currency',\n currency: 'USD',\n maximumFractionDigits: 0,\n }).format(max);\n case 'compact':\n return new Intl.NumberFormat('en-US', {\n notation: 'compact',\n maximumFractionDigits: 1,\n }).format(max);\n case 'number':\n default:\n return new Intl.NumberFormat('en-US').format(max);\n }\n }, [data?.max, config?.max, config?.format]);\n\n const handleClick = useCallback(() => {\n // Support both legacy onClick and new onDrilldown\n if (onDrilldown && data) {\n onDrilldown({\n value: data.value,\n max: data.max,\n target: data.target,\n label: data.label,\n percentage,\n });\n } else if (onClick && widget.drilldowns?.length) {\n onClick();\n }\n }, [onClick, onDrilldown, widget.drilldowns, data, percentage]);\n\n const hasDrilldown = !!widget.drilldown?.enabled;\n const isClickable = Boolean(hasDrilldown || (onClick && widget.drilldowns?.length));\n\n if (!data) {\n return null;\n }\n\n return (\n <div\n className={`\n flex flex-col h-full p-4\n ${isClickable ? 'cursor-pointer hover:bg-bg-sunken/50 transition-colors' : ''}\n `}\n onClick={handleClick}\n role={isClickable ? 'button' : undefined}\n tabIndex={isClickable ? 0 : undefined}\n onKeyDown={(e) => {\n if (isClickable && (e.key === 'Enter' || e.key === ' ')) {\n e.preventDefault();\n handleClick();\n }\n }}\n >\n {/* Header */}\n <div className=\"flex items-start justify-between mb-4\">\n <div className=\"flex-1\">\n {data.label && <h4 className=\"text-sm font-medium text-text-primary\">{data.label}</h4>}\n {data.description && <p className=\"text-xs text-text-secondary mt-0.5\">{data.description}</p>}\n </div>\n\n {/* Value display */}\n <div className=\"text-right\">\n <span className=\"text-2xl font-bold text-text-primary\">{formattedValue}</span>\n {config?.showMax !== false && (\n <span className=\"text-sm text-text-secondary\">\n {' / '}\n {formattedMax}\n </span>\n )}\n </div>\n </div>\n\n {/* Progress bar */}\n <ProgressBar\n value={percentage}\n target={targetPercentage}\n milestones={milestones}\n height={config?.barHeight || 12}\n color={colorMode}\n showPercentage={config?.showPercentage !== false}\n />\n\n {/* Target info */}\n {data.target !== undefined && (\n <div className=\"mt-3 flex items-center gap-2 text-xs\">\n <svg className=\"w-4 h-4 text-text-secondary\" fill=\"currentColor\" viewBox=\"0 0 24 24\">\n <path d=\"M5 21V3h14l-3 9 3 9H5z\" />\n </svg>\n <span className=\"text-text-secondary\">\n Target:{' '}\n <span className=\"font-medium text-text-primary\">{new Intl.NumberFormat('en-US').format(data.target)}</span>\n {percentage >= (targetPercentage || 0) && <span className=\"text-state-success ml-1\">Reached!</span>}\n </span>\n </div>\n )}\n\n {/* Drilldown indicator */}\n {isClickable && (\n <div className=\"mt-auto pt-3 flex items-center justify-end\">\n <span className=\"text-xs text-text-secondary flex items-center gap-1\">\n View details\n <svg className=\"w-3 h-3\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M9 5l7 7-7 7\" />\n </svg>\n </span>\n </div>\n )}\n </div>\n );\n});\n\nexport default ProgressWidget;\n"],"mappings":"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"}
@@ -2,7 +2,7 @@ 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/retention/RetentionWidget.tsx
4
4
  function a(e) {
5
- return e >= 80 ? "bg-state-success/90 text-white" : e >= 60 ? "bg-state-success/70 text-white" : e >= 40 ? "bg-state-success/50 text-text-primary" : e >= 20 ? "bg-state-success/30 text-text-primary" : e > 0 ? "bg-state-success/10 text-text-secondary" : "bg-bg-muted text-text-secondary";
5
+ return e >= 80 ? "bg-state-success/90 text-white" : e >= 60 ? "bg-state-success/70 text-white" : e >= 40 ? "bg-state-success/50 text-text-primary" : e >= 20 ? "bg-state-success/30 text-text-primary" : e > 0 ? "bg-state-success/10 text-text-secondary" : "bg-bg-sunken text-text-secondary";
6
6
  }
7
7
  var o = e(function({ widget: e, data: o, onClick: s }) {
8
8
  let c = e.config, l = t((t, n, r) => {
@@ -119,7 +119,7 @@ var o = e(function({ widget: e, data: o, onClick: s }) {
119
119
  d.map((e, t) => /* @__PURE__ */ r("td", {
120
120
  className: "p-0",
121
121
  children: /* @__PURE__ */ r("div", {
122
- className: "m-0.5 p-2 rounded bg-bg-muted text-center",
122
+ className: "m-0.5 p-2 rounded bg-bg-sunken text-center",
123
123
  children: /* @__PURE__ */ i("span", {
124
124
  className: "text-xs font-semibold text-text-primary",
125
125
  children: [e.toFixed(c?.decimals ?? 0), "%"]
@@ -1 +1 @@
1
- {"version":3,"file":"RetentionWidget.js","names":[],"sources":["../../../../../src/bigconsole/components/widgets/retention/RetentionWidget.tsx"],"sourcesContent":["/**\n * RetentionWidget Component\n *\n * Retention cohort chart showing user retention over time.\n */\n\nimport { type FC, memo, useMemo, useCallback } from 'react';\nimport type { Widget, RetentionConfig } from '../../../types';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface RetentionWidgetProps {\n widget: Widget;\n data?: RetentionData;\n onClick?: (cohort: string, period: number, value: number) => void;\n}\n\nexport interface RetentionCohort {\n /** Cohort identifier (e.g., \"Week 1\", \"Jan 2024\") */\n cohort: string;\n /** Initial cohort size */\n initialSize: number;\n /** Retention values for each period (percentages) */\n values: number[];\n}\n\nexport interface RetentionData {\n /** Period labels (e.g., [\"Day 0\", \"Day 1\", ...]) */\n periods: string[];\n /** Cohort data */\n cohorts: RetentionCohort[];\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction getRetentionColor(value: number): string {\n // Green gradient based on retention percentage\n if (value >= 80) return 'bg-state-success/90 text-white';\n if (value >= 60) return 'bg-state-success/70 text-white';\n if (value >= 40) return 'bg-state-success/50 text-text-primary';\n if (value >= 20) return 'bg-state-success/30 text-text-primary';\n if (value > 0) return 'bg-state-success/10 text-text-secondary';\n return 'bg-bg-muted text-text-secondary';\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const RetentionWidget: FC<RetentionWidgetProps> = memo(function RetentionWidget({ widget, data, onClick }) {\n const config = widget.config as unknown as RetentionConfig | undefined;\n\n const handleCellClick = useCallback(\n (cohort: string, periodIndex: number, value: number) => {\n if (onClick && widget.drilldowns?.length) {\n onClick(cohort, periodIndex, value);\n }\n },\n [onClick, widget.drilldowns]\n );\n\n const isClickable = Boolean(onClick && widget.drilldowns?.length);\n\n // Calculate average retention per period\n const averages = useMemo(() => {\n if (!data?.cohorts?.length || !data?.periods?.length) return [];\n\n return data.periods.map((_, periodIndex) => {\n const values = data.cohorts\n .map((c) => c.values?.[periodIndex])\n .filter((v) => v !== undefined && v !== null) as number[];\n if (values.length === 0) return 0;\n return values.reduce((a, b) => a + b, 0) / values.length;\n });\n }, [data]);\n\n // Show retention-specific empty state when cohort data is missing.\n if (!data?.cohorts?.length || !data?.periods?.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=\"M9 17V7m0 10a2 2 0 01-2 2H5a2 2 0 01-2-2V7a2 2 0 012-2h2a2 2 0 012 2m0 10a2 2 0 002 2h2a2 2 0 002-2M9 7a2 2 0 012-2h2a2 2 0 012 2m0 10V7m0 10a2 2 0 002 2h2a2 2 0 002-2V7a2 2 0 00-2-2h-2a2 2 0 00-2 2\"\n />\n </svg>\n <p className=\"font-medium text-text-primary\">No retention cohort data</p>\n <p className=\"text-xs text-text-tertiary mt-1\">\n Connect a data source that returns <span className=\"font-mono\">periods</span> and{' '}\n <span className=\"font-mono\">cohorts</span>.\n </p>\n <p className=\"text-xs text-text-tertiary mt-2\">\n Expected shape: <span className=\"font-mono\">RetentionData</span>.\n </p>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"flex flex-col h-full p-4 overflow-auto\">\n <table className=\"w-full border-collapse\">\n <thead>\n <tr>\n <th className=\"p-2 text-xs font-medium text-text-secondary text-left sticky left-0 bg-bg-surface\">\n Cohort\n </th>\n <th className=\"p-2 text-xs font-medium text-text-secondary text-right\">Users</th>\n {data.periods.map((period, i) => (\n <th key={i} className=\"p-2 text-xs font-medium text-text-secondary text-center min-w-[60px]\">\n {period}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {data.cohorts.map((cohort, _cohortIndex) => (\n <tr key={cohort.cohort}>\n <td className=\"p-2 text-sm font-medium text-text-primary text-left sticky left-0 bg-bg-surface\">\n {cohort.cohort}\n </td>\n <td className=\"p-2 text-sm text-text-secondary text-right\">\n {new Intl.NumberFormat('en-US').format(cohort.initialSize)}\n </td>\n {cohort.values.map((value, periodIndex) => (\n <td\n key={periodIndex}\n className={`p-0 ${isClickable ? 'cursor-pointer' : ''}`}\n onClick={() => handleCellClick(cohort.cohort, periodIndex, value)}\n >\n <div\n className={`\n m-0.5 p-2 rounded text-center\n ${getRetentionColor(value)}\n ${isClickable ? 'hover:ring-2 hover:ring-action-primary-bg' : ''}\n transition-all\n `}\n title={`${cohort.cohort} - ${data.periods[periodIndex]}: ${value.toFixed(1)}%`}\n >\n <span className=\"text-xs font-medium\">{value.toFixed(config?.decimals ?? 0)}%</span>\n </div>\n </td>\n ))}\n </tr>\n ))}\n\n {/* Average row */}\n {config?.showAverages !== false && (\n <tr className=\"border-t-2 border-border-default\">\n <td className=\"p-2 text-sm font-medium text-text-primary sticky left-0 bg-bg-surface\">Average</td>\n <td className=\"p-2 text-sm text-text-secondary text-right\">—</td>\n {averages.map((avg, i) => (\n <td key={i} className=\"p-0\">\n <div className=\"m-0.5 p-2 rounded bg-bg-muted text-center\">\n <span className=\"text-xs font-semibold text-text-primary\">\n {avg.toFixed(config?.decimals ?? 0)}%\n </span>\n </div>\n </td>\n ))}\n </tr>\n )}\n </tbody>\n </table>\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\">0%</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-state-success/${opacity}`} />\n ))}\n </div>\n <span className=\"text-xs text-text-secondary\">100%</span>\n </div>\n )}\n </div>\n );\n});\n\nexport default RetentionWidget;\n"],"mappings":"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"}
1
+ {"version":3,"file":"RetentionWidget.js","names":[],"sources":["../../../../../src/bigconsole/components/widgets/retention/RetentionWidget.tsx"],"sourcesContent":["/**\n * RetentionWidget Component\n *\n * Retention cohort chart showing user retention over time.\n */\n\nimport { type FC, memo, useMemo, useCallback } from 'react';\nimport type { Widget, RetentionConfig } from '../../../types';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface RetentionWidgetProps {\n widget: Widget;\n data?: RetentionData;\n onClick?: (cohort: string, period: number, value: number) => void;\n}\n\nexport interface RetentionCohort {\n /** Cohort identifier (e.g., \"Week 1\", \"Jan 2024\") */\n cohort: string;\n /** Initial cohort size */\n initialSize: number;\n /** Retention values for each period (percentages) */\n values: number[];\n}\n\nexport interface RetentionData {\n /** Period labels (e.g., [\"Day 0\", \"Day 1\", ...]) */\n periods: string[];\n /** Cohort data */\n cohorts: RetentionCohort[];\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction getRetentionColor(value: number): string {\n // Green gradient based on retention percentage\n if (value >= 80) return 'bg-state-success/90 text-white';\n if (value >= 60) return 'bg-state-success/70 text-white';\n if (value >= 40) return 'bg-state-success/50 text-text-primary';\n if (value >= 20) return 'bg-state-success/30 text-text-primary';\n if (value > 0) return 'bg-state-success/10 text-text-secondary';\n return 'bg-bg-sunken text-text-secondary';\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const RetentionWidget: FC<RetentionWidgetProps> = memo(function RetentionWidget({ widget, data, onClick }) {\n const config = widget.config as unknown as RetentionConfig | undefined;\n\n const handleCellClick = useCallback(\n (cohort: string, periodIndex: number, value: number) => {\n if (onClick && widget.drilldowns?.length) {\n onClick(cohort, periodIndex, value);\n }\n },\n [onClick, widget.drilldowns]\n );\n\n const isClickable = Boolean(onClick && widget.drilldowns?.length);\n\n // Calculate average retention per period\n const averages = useMemo(() => {\n if (!data?.cohorts?.length || !data?.periods?.length) return [];\n\n return data.periods.map((_, periodIndex) => {\n const values = data.cohorts\n .map((c) => c.values?.[periodIndex])\n .filter((v) => v !== undefined && v !== null) as number[];\n if (values.length === 0) return 0;\n return values.reduce((a, b) => a + b, 0) / values.length;\n });\n }, [data]);\n\n // Show retention-specific empty state when cohort data is missing.\n if (!data?.cohorts?.length || !data?.periods?.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=\"M9 17V7m0 10a2 2 0 01-2 2H5a2 2 0 01-2-2V7a2 2 0 012-2h2a2 2 0 012 2m0 10a2 2 0 002 2h2a2 2 0 002-2M9 7a2 2 0 012-2h2a2 2 0 012 2m0 10V7m0 10a2 2 0 002 2h2a2 2 0 002-2V7a2 2 0 00-2-2h-2a2 2 0 00-2 2\"\n />\n </svg>\n <p className=\"font-medium text-text-primary\">No retention cohort data</p>\n <p className=\"text-xs text-text-tertiary mt-1\">\n Connect a data source that returns <span className=\"font-mono\">periods</span> and{' '}\n <span className=\"font-mono\">cohorts</span>.\n </p>\n <p className=\"text-xs text-text-tertiary mt-2\">\n Expected shape: <span className=\"font-mono\">RetentionData</span>.\n </p>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"flex flex-col h-full p-4 overflow-auto\">\n <table className=\"w-full border-collapse\">\n <thead>\n <tr>\n <th className=\"p-2 text-xs font-medium text-text-secondary text-left sticky left-0 bg-bg-surface\">\n Cohort\n </th>\n <th className=\"p-2 text-xs font-medium text-text-secondary text-right\">Users</th>\n {data.periods.map((period, i) => (\n <th key={i} className=\"p-2 text-xs font-medium text-text-secondary text-center min-w-[60px]\">\n {period}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {data.cohorts.map((cohort, _cohortIndex) => (\n <tr key={cohort.cohort}>\n <td className=\"p-2 text-sm font-medium text-text-primary text-left sticky left-0 bg-bg-surface\">\n {cohort.cohort}\n </td>\n <td className=\"p-2 text-sm text-text-secondary text-right\">\n {new Intl.NumberFormat('en-US').format(cohort.initialSize)}\n </td>\n {cohort.values.map((value, periodIndex) => (\n <td\n key={periodIndex}\n className={`p-0 ${isClickable ? 'cursor-pointer' : ''}`}\n onClick={() => handleCellClick(cohort.cohort, periodIndex, value)}\n >\n <div\n className={`\n m-0.5 p-2 rounded text-center\n ${getRetentionColor(value)}\n ${isClickable ? 'hover:ring-2 hover:ring-action-primary-bg' : ''}\n transition-all\n `}\n title={`${cohort.cohort} - ${data.periods[periodIndex]}: ${value.toFixed(1)}%`}\n >\n <span className=\"text-xs font-medium\">{value.toFixed(config?.decimals ?? 0)}%</span>\n </div>\n </td>\n ))}\n </tr>\n ))}\n\n {/* Average row */}\n {config?.showAverages !== false && (\n <tr className=\"border-t-2 border-border-default\">\n <td className=\"p-2 text-sm font-medium text-text-primary sticky left-0 bg-bg-surface\">Average</td>\n <td className=\"p-2 text-sm text-text-secondary text-right\">—</td>\n {averages.map((avg, i) => (\n <td key={i} className=\"p-0\">\n <div className=\"m-0.5 p-2 rounded bg-bg-sunken text-center\">\n <span className=\"text-xs font-semibold text-text-primary\">\n {avg.toFixed(config?.decimals ?? 0)}%\n </span>\n </div>\n </td>\n ))}\n </tr>\n )}\n </tbody>\n </table>\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\">0%</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-state-success/${opacity}`} />\n ))}\n </div>\n <span className=\"text-xs text-text-secondary\">100%</span>\n </div>\n )}\n </div>\n );\n});\n\nexport default RetentionWidget;\n"],"mappings":"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"}
@@ -7,7 +7,7 @@ var r = e(function({ widgetType: e, hasDataSource: r = !1, message: a }) {
7
7
  className: "\n flex flex-col items-center justify-center\n h-full w-full min-h-[120px]\n text-center\n px-4\n ",
8
8
  children: [
9
9
  /* @__PURE__ */ t("div", {
10
- className: "\n w-12 h-12 mb-3\n flex items-center justify-center\n rounded-full\n bg-bg-muted\n text-text-secondary\n ",
10
+ className: "\n w-12 h-12 mb-3\n flex items-center justify-center\n rounded-full\n bg-bg-sunken\n text-text-secondary\n ",
11
11
  children: /* @__PURE__ */ t("svg", {
12
12
  className: "w-6 h-6",
13
13
  fill: "none",
@@ -1 +1 @@
1
- {"version":3,"file":"EmptyState.js","names":[],"sources":["../../../../../src/bigconsole/components/widgets/states/EmptyState.tsx"],"sourcesContent":["/**\n * WidgetEmptyState Component\n *\n * Empty state displayed when widget has no data.\n */\n\nimport { type FC, memo } from 'react';\nimport type { WidgetType } from '../../../types';\n\nexport interface WidgetEmptyStateProps {\n /** Widget type for context-specific messaging */\n widgetType?: WidgetType;\n /** Whether a data source is configured */\n hasDataSource?: boolean;\n /** Custom message */\n message?: string;\n}\n\nexport const WidgetEmptyState: FC<WidgetEmptyStateProps> = memo(function WidgetEmptyState({\n widgetType,\n hasDataSource = false,\n message,\n}) {\n // Determine appropriate message\n const displayMessage =\n message || (!hasDataSource ? 'Connect a data source to display data' : getEmptyMessageForType(widgetType));\n\n return (\n <div\n className=\"\n flex flex-col items-center justify-center\n h-full w-full min-h-[120px]\n text-center\n px-4\n \"\n >\n {/* Empty Icon */}\n <div\n className=\"\n w-12 h-12 mb-3\n flex items-center justify-center\n rounded-full\n bg-bg-muted\n text-text-secondary\n \"\n >\n <svg className=\"w-6 h-6\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2.586a1 1 0 00-.707.293l-2.414 2.414a1 1 0 01-.707.293h-3.172a1 1 0 01-.707-.293l-2.414-2.414A1 1 0 006.586 13H4\"\n />\n </svg>\n </div>\n\n {/* Message */}\n <p className=\"text-sm text-text-secondary\">{displayMessage}</p>\n\n {/* Data source hint */}\n {!hasDataSource && (\n <p className=\"text-xs text-text-secondary mt-2 max-w-[200px]\">Click Edit to configure this widget</p>\n )}\n </div>\n );\n});\n\n/**\n * Get context-specific empty message for widget type (v1.0 snake_case)\n */\nfunction getEmptyMessageForType(type?: WidgetType): string {\n switch (type) {\n case 'metric_card':\n case 'kpi_card_comparison':\n return 'No metrics available';\n case 'chart':\n case 'funnel_chart':\n return 'No chart data available';\n case 'table':\n case 'pivot_table':\n return 'No rows to display';\n case 'gauge':\n case 'progress':\n return 'No value available';\n case 'list':\n return 'No items to display';\n case 'heatmap':\n return 'No heatmap data available';\n case 'calendar':\n case 'timeline':\n case 'kanban':\n return 'No schedule data available';\n case 'map':\n return 'No location data available';\n case 'form':\n return 'Form not configured';\n case 'text':\n case 'iframe':\n return 'No content configured';\n case 'custom':\n return 'Custom widget not configured';\n default:\n return 'No data available';\n }\n}\n\nexport default WidgetEmptyState;\n"],"mappings":";;;AAkBA,IAAa,IAA8C,EAAK,SAA0B,EACxF,eACA,mBAAgB,IAChB,cACC;CAED,IAAM,IACJ,MAAa,IAA0D,EAAuB,EAAW,GAA5E;AAE/B,QACE,kBAAC,OAAD;EACE,WAAU;YADZ;GASE,kBAAC,OAAD;IACE,WAAU;cAQV,kBAAC,OAAD;KAAK,WAAU;KAAU,MAAK;KAAO,QAAO;KAAe,SAAQ;eACjE,kBAAC,QAAD;MACE,eAAc;MACd,gBAAe;MACf,aAAa;MACb,GAAE;MACF,CAAA;KACE,CAAA;IACF,CAAA;GAGN,kBAAC,KAAD;IAAG,WAAU;cAA+B;IAAmB,CAAA;GAG9D,CAAC,KACA,kBAAC,KAAD;IAAG,WAAU;cAAiD;IAAuC,CAAA;GAEnG;;EAER;AAKF,SAAS,EAAuB,GAA2B;AACzD,SAAQ,GAAR;EACE,KAAK;EACL,KAAK,sBACH,QAAO;EACT,KAAK;EACL,KAAK,eACH,QAAO;EACT,KAAK;EACL,KAAK,cACH,QAAO;EACT,KAAK;EACL,KAAK,WACH,QAAO;EACT,KAAK,OACH,QAAO;EACT,KAAK,UACH,QAAO;EACT,KAAK;EACL,KAAK;EACL,KAAK,SACH,QAAO;EACT,KAAK,MACH,QAAO;EACT,KAAK,OACH,QAAO;EACT,KAAK;EACL,KAAK,SACH,QAAO;EACT,KAAK,SACH,QAAO;EACT,QACE,QAAO"}
1
+ {"version":3,"file":"EmptyState.js","names":[],"sources":["../../../../../src/bigconsole/components/widgets/states/EmptyState.tsx"],"sourcesContent":["/**\n * WidgetEmptyState Component\n *\n * Empty state displayed when widget has no data.\n */\n\nimport { type FC, memo } from 'react';\nimport type { WidgetType } from '../../../types';\n\nexport interface WidgetEmptyStateProps {\n /** Widget type for context-specific messaging */\n widgetType?: WidgetType;\n /** Whether a data source is configured */\n hasDataSource?: boolean;\n /** Custom message */\n message?: string;\n}\n\nexport const WidgetEmptyState: FC<WidgetEmptyStateProps> = memo(function WidgetEmptyState({\n widgetType,\n hasDataSource = false,\n message,\n}) {\n // Determine appropriate message\n const displayMessage =\n message || (!hasDataSource ? 'Connect a data source to display data' : getEmptyMessageForType(widgetType));\n\n return (\n <div\n className=\"\n flex flex-col items-center justify-center\n h-full w-full min-h-[120px]\n text-center\n px-4\n \"\n >\n {/* Empty Icon */}\n <div\n className=\"\n w-12 h-12 mb-3\n flex items-center justify-center\n rounded-full\n bg-bg-sunken\n text-text-secondary\n \"\n >\n <svg className=\"w-6 h-6\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2.586a1 1 0 00-.707.293l-2.414 2.414a1 1 0 01-.707.293h-3.172a1 1 0 01-.707-.293l-2.414-2.414A1 1 0 006.586 13H4\"\n />\n </svg>\n </div>\n\n {/* Message */}\n <p className=\"text-sm text-text-secondary\">{displayMessage}</p>\n\n {/* Data source hint */}\n {!hasDataSource && (\n <p className=\"text-xs text-text-secondary mt-2 max-w-[200px]\">Click Edit to configure this widget</p>\n )}\n </div>\n );\n});\n\n/**\n * Get context-specific empty message for widget type (v1.0 snake_case)\n */\nfunction getEmptyMessageForType(type?: WidgetType): string {\n switch (type) {\n case 'metric_card':\n case 'kpi_card_comparison':\n return 'No metrics available';\n case 'chart':\n case 'funnel_chart':\n return 'No chart data available';\n case 'table':\n case 'pivot_table':\n return 'No rows to display';\n case 'gauge':\n case 'progress':\n return 'No value available';\n case 'list':\n return 'No items to display';\n case 'heatmap':\n return 'No heatmap data available';\n case 'calendar':\n case 'timeline':\n case 'kanban':\n return 'No schedule data available';\n case 'map':\n return 'No location data available';\n case 'form':\n return 'Form not configured';\n case 'text':\n case 'iframe':\n return 'No content configured';\n case 'custom':\n return 'Custom widget not configured';\n default:\n return 'No data available';\n }\n}\n\nexport default WidgetEmptyState;\n"],"mappings":";;;AAkBA,IAAa,IAA8C,EAAK,SAA0B,EACxF,eACA,mBAAgB,IAChB,cACC;CAED,IAAM,IACJ,MAAa,IAA0D,EAAuB,EAAW,GAA5E;AAE/B,QACE,kBAAC,OAAD;EACE,WAAU;YADZ;GASE,kBAAC,OAAD;IACE,WAAU;cAQV,kBAAC,OAAD;KAAK,WAAU;KAAU,MAAK;KAAO,QAAO;KAAe,SAAQ;eACjE,kBAAC,QAAD;MACE,eAAc;MACd,gBAAe;MACf,aAAa;MACb,GAAE;MACF,CAAA;KACE,CAAA;IACF,CAAA;GAGN,kBAAC,KAAD;IAAG,WAAU;cAA+B;IAAmB,CAAA;GAG9D,CAAC,KACA,kBAAC,KAAD;IAAG,WAAU;cAAiD;IAAuC,CAAA;GAEnG;;EAER;AAKF,SAAS,EAAuB,GAA2B;AACzD,SAAQ,GAAR;EACE,KAAK;EACL,KAAK,sBACH,QAAO;EACT,KAAK;EACL,KAAK,eACH,QAAO;EACT,KAAK;EACL,KAAK,cACH,QAAO;EACT,KAAK;EACL,KAAK,WACH,QAAO;EACT,KAAK,OACH,QAAO;EACT,KAAK,UACH,QAAO;EACT,KAAK;EACL,KAAK;EACL,KAAK,SACH,QAAO;EACT,KAAK,MACH,QAAO;EACT,KAAK,OACH,QAAO;EACT,KAAK;EACL,KAAK,SACH,QAAO;EACT,KAAK,SACH,QAAO;EACT,QACE,QAAO"}
@@ -36,7 +36,7 @@ function l(e, t) {
36
36
  px-2 py-0.5
37
37
  text-xs font-medium
38
38
  rounded-full
39
- ${t?.badgeColors?.[n] || "bg-bg-muted text-text-primary"}
39
+ ${t?.badgeColors?.[n] || "bg-bg-sunken text-text-primary"}
40
40
  `,
41
41
  children: n
42
42
  });
@@ -1 +1 @@
1
- {"version":3,"file":"TableCell.js","names":[],"sources":["../../../../../src/bigconsole/components/widgets/table/TableCell.tsx"],"sourcesContent":["/**\n * TableCell Component\n *\n * Table cell with formatting and rendering options.\n */\n\nimport { type FC, memo, type ReactNode } from 'react';\nimport type { ColumnDefinition } from './TableHeader';\nimport {\n type ConditionalRule,\n resolveConditionalStyle,\n conditionalStyleToClassName,\n intentToCssVar,\n CONDITIONAL_ICON_PATHS,\n} from '../utils/conditionalFormat';\nimport { SparklineChart } from '../metric-card/SparklineChart';\n\n/** In-cell mini-chart kinds. */\nexport type CellChart = 'sparkline';\n\n/**\n * Extract finite numeric values from a sparkline cell value (an array of raw\n * numbers or `{value}` objects). Non-finite values (NaN/Infinity) are rejected\n * in both forms. Returns null when the value isn't a usable numeric array — so\n * the caller can decide how to render: a chart (≥2 points), a single value\n * (1 point), or fall through to the default renderer.\n */\nfunction extractSparklineValues(value: unknown): number[] | null {\n if (!Array.isArray(value) || value.length === 0) return null;\n const nums: number[] = [];\n for (const item of value) {\n if (typeof item === 'number' && Number.isFinite(item)) {\n nums.push(item);\n } else if (\n item &&\n typeof item === 'object' &&\n typeof (item as { value?: unknown }).value === 'number' &&\n Number.isFinite((item as { value: number }).value)\n ) {\n nums.push((item as { value: number }).value);\n }\n }\n return nums.length ? nums : null;\n}\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type CellFormat = 'text' | 'number' | 'currency' | 'percentage' | 'date' | 'badge' | 'link';\n\nexport interface CellFormatConfig {\n /** Format type */\n type: CellFormat;\n /** Currency code for currency format */\n currency?: string;\n /** Date format options */\n dateOptions?: Intl.DateTimeFormatOptions;\n /** Badge color mapping (value -> color) */\n badgeColors?: Record<string, string>;\n /** Link URL pattern (use {value} as placeholder) */\n linkPattern?: string;\n}\n\nexport interface TableCellProps {\n /** Cell value */\n value: unknown;\n /** Column definition */\n column: ColumnDefinition;\n /** Format configuration */\n format?: CellFormatConfig;\n /** Row data for link patterns and conditional rule field lookups */\n rowData?: Record<string, unknown>;\n /** Conditional formatting rules to evaluate against this cell */\n conditionalRules?: ConditionalRule[];\n /** Max absolute value in this column, used to scale in-cell data bars */\n dataBarMax?: number;\n /** When set, freeze this cell sticky-left at the given px offset */\n stickyLeft?: number;\n /** Render the cell as an in-cell mini chart (e.g. sparkline) instead of a value */\n cellChart?: CellChart;\n /** Click handler */\n onClick?: () => void;\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction formatValue(value: unknown, format?: CellFormatConfig): ReactNode {\n if (value === null || value === undefined) {\n return <span className=\"text-text-secondary\">—</span>;\n }\n\n const formatType = format?.type || 'text';\n\n switch (formatType) {\n case 'number':\n return typeof value === 'number' ? new Intl.NumberFormat('en-US').format(value) : String(value);\n\n case 'currency':\n return typeof value === 'number'\n ? new Intl.NumberFormat('en-US', {\n style: 'currency',\n currency: format?.currency || 'USD',\n }).format(value)\n : String(value);\n\n case 'percentage':\n return typeof value === 'number' ? `${(value * 100).toFixed(1)}%` : String(value);\n\n case 'date':\n if (value instanceof Date) {\n return new Intl.DateTimeFormat('en-US', format?.dateOptions || { dateStyle: 'medium' }).format(value);\n }\n if (typeof value === 'string' || typeof value === 'number') {\n const date = new Date(value);\n if (!isNaN(date.getTime())) {\n return new Intl.DateTimeFormat('en-US', format?.dateOptions || { dateStyle: 'medium' }).format(date);\n }\n }\n return String(value);\n\n case 'badge': {\n const stringValue = String(value);\n const badgeColor = format?.badgeColors?.[stringValue] || 'bg-bg-muted text-text-primary';\n return (\n <span\n className={`\n inline-flex items-center\n px-2 py-0.5\n text-xs font-medium\n rounded-full\n ${badgeColor}\n `}\n >\n {stringValue}\n </span>\n );\n }\n\n case 'link':\n return <span className=\"text-action-primary-bg hover:underline cursor-pointer\">{String(value)}</span>;\n\n case 'text':\n default:\n return String(value);\n }\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const TableCell: FC<TableCellProps> = memo(function TableCell({\n value,\n column,\n format,\n rowData,\n conditionalRules,\n dataBarMax,\n stickyLeft,\n cellChart,\n onClick,\n}) {\n const cf = resolveConditionalStyle(value, conditionalRules, rowData);\n const cfClass = conditionalStyleToClassName(cf);\n const isFrozen = stickyLeft != null;\n // In-cell sparkline: only when the column opts in and the value is numeric.\n const sparkValues = cellChart === 'sparkline' ? extractSparklineValues(value) : null;\n // Range for the sparkline's accessible label (reduce, not spread → no call-stack risk).\n const sparkMin = sparkValues ? sparkValues.reduce((a, b) => Math.min(a, b), Infinity) : 0;\n const sparkMax = sparkValues ? sparkValues.reduce((a, b) => Math.max(a, b), -Infinity) : 0;\n\n // In-cell data bar width (0–100%) scaled against the column max.\n const dataBarWidth =\n cf?.dataBar && typeof value === 'number' && dataBarMax && dataBarMax > 0\n ? Math.max(0, Math.min(100, (Math.abs(value) / dataBarMax) * 100))\n : null;\n\n return (\n <td\n className={`\n relative px-4 py-3\n text-sm text-text-primary\n border-b border-border-default\n ${column.align === 'center' ? 'text-center' : ''}\n ${column.align === 'right' ? 'text-right' : 'text-left'}\n ${onClick ? 'cursor-pointer hover:text-action-primary-bg' : ''}\n ${isFrozen ? 'sticky z-10 bg-bg-surface' : ''}\n ${cfClass}\n `}\n style={isFrozen ? { left: stickyLeft, width: column.width } : undefined}\n onClick={onClick}\n role={onClick ? 'button' : undefined}\n tabIndex={onClick ? 0 : undefined}\n onKeyDown={(e) => {\n if (onClick && (e.key === 'Enter' || e.key === ' ')) {\n e.preventDefault();\n onClick();\n }\n }}\n >\n {dataBarWidth != null && (\n <span\n aria-hidden=\"true\"\n className=\"absolute inset-y-1 left-1 rounded-sm opacity-20 pointer-events-none\"\n style={{\n width: `calc(${dataBarWidth}% - 0.5rem)`,\n backgroundColor: intentToCssVar(cf?.dataBarColor ?? 'info'),\n }}\n />\n )}\n {sparkValues && sparkValues.length >= 2 ? (\n // ≥2 points → draw the sparkline (SparklineChart needs >1 to avoid a\n // divide-by-zero). Labelled for screen readers since the SVG is decorative.\n <span\n className=\"relative inline-flex items-center\"\n role=\"img\"\n aria-label={`Sparkline, ${sparkValues.length} points, ${sparkMin.toLocaleString()} to ${sparkMax.toLocaleString()}`}\n >\n <SparklineChart data={sparkValues.map((v) => ({ value: v }))} width={96} height={28} color=\"primary\" />\n </span>\n ) : sparkValues && sparkValues.length === 1 ? (\n // A single numeric point can't be charted — render the value itself\n // (not the raw array, which would stringify to \"[object Object]\").\n <span className=\"relative inline-flex items-center\">{formatValue(sparkValues[0], format)}</span>\n ) : (\n <span className=\"relative inline-flex items-center gap-1.5\">\n {cf?.icon && (\n <svg className=\"w-3.5 h-3.5 shrink-0\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d={CONDITIONAL_ICON_PATHS[cf.icon]} />\n </svg>\n )}\n {formatValue(value, format)}\n </span>\n )}\n </td>\n );\n});\n\nexport default TableCell;\n"],"mappings":"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"}
1
+ {"version":3,"file":"TableCell.js","names":[],"sources":["../../../../../src/bigconsole/components/widgets/table/TableCell.tsx"],"sourcesContent":["/**\n * TableCell Component\n *\n * Table cell with formatting and rendering options.\n */\n\nimport { type FC, memo, type ReactNode } from 'react';\nimport type { ColumnDefinition } from './TableHeader';\nimport {\n type ConditionalRule,\n resolveConditionalStyle,\n conditionalStyleToClassName,\n intentToCssVar,\n CONDITIONAL_ICON_PATHS,\n} from '../utils/conditionalFormat';\nimport { SparklineChart } from '../metric-card/SparklineChart';\n\n/** In-cell mini-chart kinds. */\nexport type CellChart = 'sparkline';\n\n/**\n * Extract finite numeric values from a sparkline cell value (an array of raw\n * numbers or `{value}` objects). Non-finite values (NaN/Infinity) are rejected\n * in both forms. Returns null when the value isn't a usable numeric array — so\n * the caller can decide how to render: a chart (≥2 points), a single value\n * (1 point), or fall through to the default renderer.\n */\nfunction extractSparklineValues(value: unknown): number[] | null {\n if (!Array.isArray(value) || value.length === 0) return null;\n const nums: number[] = [];\n for (const item of value) {\n if (typeof item === 'number' && Number.isFinite(item)) {\n nums.push(item);\n } else if (\n item &&\n typeof item === 'object' &&\n typeof (item as { value?: unknown }).value === 'number' &&\n Number.isFinite((item as { value: number }).value)\n ) {\n nums.push((item as { value: number }).value);\n }\n }\n return nums.length ? nums : null;\n}\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type CellFormat = 'text' | 'number' | 'currency' | 'percentage' | 'date' | 'badge' | 'link';\n\nexport interface CellFormatConfig {\n /** Format type */\n type: CellFormat;\n /** Currency code for currency format */\n currency?: string;\n /** Date format options */\n dateOptions?: Intl.DateTimeFormatOptions;\n /** Badge color mapping (value -> color) */\n badgeColors?: Record<string, string>;\n /** Link URL pattern (use {value} as placeholder) */\n linkPattern?: string;\n}\n\nexport interface TableCellProps {\n /** Cell value */\n value: unknown;\n /** Column definition */\n column: ColumnDefinition;\n /** Format configuration */\n format?: CellFormatConfig;\n /** Row data for link patterns and conditional rule field lookups */\n rowData?: Record<string, unknown>;\n /** Conditional formatting rules to evaluate against this cell */\n conditionalRules?: ConditionalRule[];\n /** Max absolute value in this column, used to scale in-cell data bars */\n dataBarMax?: number;\n /** When set, freeze this cell sticky-left at the given px offset */\n stickyLeft?: number;\n /** Render the cell as an in-cell mini chart (e.g. sparkline) instead of a value */\n cellChart?: CellChart;\n /** Click handler */\n onClick?: () => void;\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction formatValue(value: unknown, format?: CellFormatConfig): ReactNode {\n if (value === null || value === undefined) {\n return <span className=\"text-text-secondary\">—</span>;\n }\n\n const formatType = format?.type || 'text';\n\n switch (formatType) {\n case 'number':\n return typeof value === 'number' ? new Intl.NumberFormat('en-US').format(value) : String(value);\n\n case 'currency':\n return typeof value === 'number'\n ? new Intl.NumberFormat('en-US', {\n style: 'currency',\n currency: format?.currency || 'USD',\n }).format(value)\n : String(value);\n\n case 'percentage':\n return typeof value === 'number' ? `${(value * 100).toFixed(1)}%` : String(value);\n\n case 'date':\n if (value instanceof Date) {\n return new Intl.DateTimeFormat('en-US', format?.dateOptions || { dateStyle: 'medium' }).format(value);\n }\n if (typeof value === 'string' || typeof value === 'number') {\n const date = new Date(value);\n if (!isNaN(date.getTime())) {\n return new Intl.DateTimeFormat('en-US', format?.dateOptions || { dateStyle: 'medium' }).format(date);\n }\n }\n return String(value);\n\n case 'badge': {\n const stringValue = String(value);\n const badgeColor = format?.badgeColors?.[stringValue] || 'bg-bg-sunken text-text-primary';\n return (\n <span\n className={`\n inline-flex items-center\n px-2 py-0.5\n text-xs font-medium\n rounded-full\n ${badgeColor}\n `}\n >\n {stringValue}\n </span>\n );\n }\n\n case 'link':\n return <span className=\"text-action-primary-bg hover:underline cursor-pointer\">{String(value)}</span>;\n\n case 'text':\n default:\n return String(value);\n }\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const TableCell: FC<TableCellProps> = memo(function TableCell({\n value,\n column,\n format,\n rowData,\n conditionalRules,\n dataBarMax,\n stickyLeft,\n cellChart,\n onClick,\n}) {\n const cf = resolveConditionalStyle(value, conditionalRules, rowData);\n const cfClass = conditionalStyleToClassName(cf);\n const isFrozen = stickyLeft != null;\n // In-cell sparkline: only when the column opts in and the value is numeric.\n const sparkValues = cellChart === 'sparkline' ? extractSparklineValues(value) : null;\n // Range for the sparkline's accessible label (reduce, not spread → no call-stack risk).\n const sparkMin = sparkValues ? sparkValues.reduce((a, b) => Math.min(a, b), Infinity) : 0;\n const sparkMax = sparkValues ? sparkValues.reduce((a, b) => Math.max(a, b), -Infinity) : 0;\n\n // In-cell data bar width (0–100%) scaled against the column max.\n const dataBarWidth =\n cf?.dataBar && typeof value === 'number' && dataBarMax && dataBarMax > 0\n ? Math.max(0, Math.min(100, (Math.abs(value) / dataBarMax) * 100))\n : null;\n\n return (\n <td\n className={`\n relative px-4 py-3\n text-sm text-text-primary\n border-b border-border-default\n ${column.align === 'center' ? 'text-center' : ''}\n ${column.align === 'right' ? 'text-right' : 'text-left'}\n ${onClick ? 'cursor-pointer hover:text-action-primary-bg' : ''}\n ${isFrozen ? 'sticky z-10 bg-bg-surface' : ''}\n ${cfClass}\n `}\n style={isFrozen ? { left: stickyLeft, width: column.width } : undefined}\n onClick={onClick}\n role={onClick ? 'button' : undefined}\n tabIndex={onClick ? 0 : undefined}\n onKeyDown={(e) => {\n if (onClick && (e.key === 'Enter' || e.key === ' ')) {\n e.preventDefault();\n onClick();\n }\n }}\n >\n {dataBarWidth != null && (\n <span\n aria-hidden=\"true\"\n className=\"absolute inset-y-1 left-1 rounded-sm opacity-20 pointer-events-none\"\n style={{\n width: `calc(${dataBarWidth}% - 0.5rem)`,\n backgroundColor: intentToCssVar(cf?.dataBarColor ?? 'info'),\n }}\n />\n )}\n {sparkValues && sparkValues.length >= 2 ? (\n // ≥2 points → draw the sparkline (SparklineChart needs >1 to avoid a\n // divide-by-zero). Labelled for screen readers since the SVG is decorative.\n <span\n className=\"relative inline-flex items-center\"\n role=\"img\"\n aria-label={`Sparkline, ${sparkValues.length} points, ${sparkMin.toLocaleString()} to ${sparkMax.toLocaleString()}`}\n >\n <SparklineChart data={sparkValues.map((v) => ({ value: v }))} width={96} height={28} color=\"primary\" />\n </span>\n ) : sparkValues && sparkValues.length === 1 ? (\n // A single numeric point can't be charted — render the value itself\n // (not the raw array, which would stringify to \"[object Object]\").\n <span className=\"relative inline-flex items-center\">{formatValue(sparkValues[0], format)}</span>\n ) : (\n <span className=\"relative inline-flex items-center gap-1.5\">\n {cf?.icon && (\n <svg className=\"w-3.5 h-3.5 shrink-0\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d={CONDITIONAL_ICON_PATHS[cf.icon]} />\n </svg>\n )}\n {formatValue(value, format)}\n </span>\n )}\n </td>\n );\n});\n\nexport default TableCell;\n"],"mappings":"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"}
@@ -81,7 +81,7 @@ var s = e(function({ config: e, onChange: s }) {
81
81
  }), /* @__PURE__ */ o("div", {
82
82
  className: "space-y-3",
83
83
  children: [c.map((e, t) => /* @__PURE__ */ o("div", {
84
- className: "\n p-3\n bg-bg-muted\n rounded-md\n space-y-2\n ",
84
+ className: "\n p-3\n bg-bg-sunken\n rounded-md\n space-y-2\n ",
85
85
  children: [
86
86
  /* @__PURE__ */ o("div", {
87
87
  className: "flex items-center justify-between",
@@ -1 +1 @@
1
- {"version":3,"file":"TableConfig.js","names":[],"sources":["../../../../../src/bigconsole/components/widgets/table/TableConfig.tsx"],"sourcesContent":["/**\n * TableConfig Component\n *\n * Configuration panel for Table widgets.\n */\n\nimport { type FC, memo, useCallback, useMemo, useEffect, useState } from 'react';\nimport type { TableConfig as ConfigType, TableColumnConfig } from '../../../types';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface TableConfigProps {\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 TableConfig: FC<TableConfigProps> = memo(function TableConfig({ config, onChange }) {\n // Normalize columns - convert string[] to TableColumnConfig[]\n const initialColumns = useMemo(() => {\n return ((config.columns || []) as (string | TableColumnConfig)[]).map((col) => {\n if (typeof col === 'string') {\n return {\n field: col,\n header: col.charAt(0).toUpperCase() + col.slice(1).replace(/_/g, ' '),\n };\n }\n return col as TableColumnConfig;\n });\n }, []); // Only run once on mount\n\n // Local state for columns to make them editable\n const [columns, setColumns] = useState<TableColumnConfig[]>(initialColumns);\n\n // Sync with parent when columns change\n useEffect(() => {\n // Only update if columns actually changed (deep compare)\n const currentStr = JSON.stringify(config.columns);\n const newStr = JSON.stringify(columns);\n if (currentStr !== newStr && columns.length > 0) {\n onChange({ ...config, columns });\n }\n }, [columns]);\n\n // Reset columns when config changes from outside\n useEffect(() => {\n const normalized = ((config.columns || []) as (string | TableColumnConfig)[]).map((col) => {\n if (typeof col === 'string') {\n return {\n field: col,\n header: col.charAt(0).toUpperCase() + col.slice(1).replace(/_/g, ' '),\n };\n }\n return col as TableColumnConfig;\n });\n setColumns(normalized);\n }, [config.columns]);\n\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 const handleColumnChange = useCallback((index: number, field: keyof TableColumnConfig, value: string) => {\n setColumns((prev) => {\n const newColumns = [...prev];\n newColumns[index] = { ...newColumns[index], [field]: value };\n return newColumns;\n });\n }, []);\n\n const addColumn = useCallback(() => {\n setColumns((prev) => [...prev, { field: '', header: '' }]);\n }, []);\n\n const removeColumn = useCallback((index: number) => {\n setColumns((prev) => prev.filter((_, i) => i !== index));\n }, []);\n\n return (\n <div className=\"space-y-4\">\n {/* Page Size */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">Page Size</label>\n <select\n value={config.pageSize || 10}\n onChange={(e) => handleChange('pageSize', 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 <option value={10}>10 rows</option>\n <option value={25}>25 rows</option>\n <option value={50}>50 rows</option>\n <option value={100}>100 rows</option>\n </select>\n </div>\n\n {/* Columns Configuration */}\n <div>\n <div className=\"flex items-center justify-between mb-2\">\n <label className=\"text-sm font-medium text-text-primary\">Columns</label>\n <button\n type=\"button\"\n onClick={addColumn}\n className=\"\n text-sm text-action-primary-bg\n hover:text-action-primary-bgHover\n \"\n >\n + Add Column\n </button>\n </div>\n\n <div className=\"space-y-3\">\n {columns.map((col, index) => (\n <div\n key={index}\n className=\"\n p-3\n bg-bg-muted\n rounded-md\n space-y-2\n \"\n >\n <div className=\"flex items-center justify-between\">\n <span className=\"text-xs text-text-secondary\">Column {index + 1}</span>\n <button\n type=\"button\"\n onClick={() => removeColumn(index)}\n className=\"text-state-error hover:text-state-error/80\"\n >\n <svg className=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n </div>\n\n <div className=\"grid grid-cols-2 gap-2\">\n <input\n type=\"text\"\n value={col.field || ''}\n onChange={(e) => handleColumnChange(index, 'field', e.target.value)}\n placeholder=\"Field name\"\n className=\"\n 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 <input\n type=\"text\"\n value={col.header || ''}\n onChange={(e) => handleColumnChange(index, 'header', e.target.value)}\n placeholder=\"Header label\"\n className=\"\n 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 <div className=\"grid grid-cols-2 gap-2\">\n <select\n value={col.format || 'text'}\n onChange={(e) => handleColumnChange(index, 'format', e.target.value)}\n className=\"\n 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 <option value=\"text\">Text</option>\n <option value=\"number\">Number</option>\n <option value=\"currency\">Currency</option>\n <option value=\"percentage\">Percentage</option>\n <option value=\"date\">Date</option>\n <option value=\"badge\">Badge</option>\n </select>\n <select\n value={col.align || 'left'}\n onChange={(e) => handleColumnChange(index, 'align', e.target.value)}\n className=\"\n 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 <option value=\"left\">Left</option>\n <option value=\"center\">Center</option>\n <option value=\"right\">Right</option>\n </select>\n </div>\n </div>\n ))}\n\n {columns.length === 0 && (\n <p className=\"text-sm text-text-secondary text-center py-4\">\n No columns configured. Columns will be auto-generated from data.\n </p>\n )}\n </div>\n </div>\n\n {/* Toggle Options */}\n <div className=\"space-y-3\">\n <label className=\"flex items-center gap-3\">\n <input\n type=\"checkbox\"\n checked={config.showSearch !== false}\n onChange={(e) => handleChange('showSearch', 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 search box</span>\n </label>\n\n <label className=\"flex items-center gap-3\">\n <input\n type=\"checkbox\"\n checked={config.showPagination !== false}\n onChange={(e) => handleChange('showPagination', 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 pagination</span>\n </label>\n\n <label className=\"flex items-center gap-3\">\n <input\n type=\"checkbox\"\n checked={config.showExport !== false}\n onChange={(e) => handleChange('showExport', 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 export button</span>\n </label>\n\n <label className=\"flex items-center gap-3\">\n <input\n type=\"checkbox\"\n checked={config.stickyHeader === true}\n onChange={(e) => handleChange('stickyHeader', 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\">Sticky header</span>\n </label>\n\n <label className=\"flex items-center gap-3\">\n <input\n type=\"checkbox\"\n checked={config.stripedRows === true}\n onChange={(e) => handleChange('stripedRows', 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\">Striped rows</span>\n </label>\n </div>\n </div>\n );\n});\n\nexport default TableConfig;\n"],"mappings":"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"}
1
+ {"version":3,"file":"TableConfig.js","names":[],"sources":["../../../../../src/bigconsole/components/widgets/table/TableConfig.tsx"],"sourcesContent":["/**\n * TableConfig Component\n *\n * Configuration panel for Table widgets.\n */\n\nimport { type FC, memo, useCallback, useMemo, useEffect, useState } from 'react';\nimport type { TableConfig as ConfigType, TableColumnConfig } from '../../../types';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface TableConfigProps {\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 TableConfig: FC<TableConfigProps> = memo(function TableConfig({ config, onChange }) {\n // Normalize columns - convert string[] to TableColumnConfig[]\n const initialColumns = useMemo(() => {\n return ((config.columns || []) as (string | TableColumnConfig)[]).map((col) => {\n if (typeof col === 'string') {\n return {\n field: col,\n header: col.charAt(0).toUpperCase() + col.slice(1).replace(/_/g, ' '),\n };\n }\n return col as TableColumnConfig;\n });\n }, []); // Only run once on mount\n\n // Local state for columns to make them editable\n const [columns, setColumns] = useState<TableColumnConfig[]>(initialColumns);\n\n // Sync with parent when columns change\n useEffect(() => {\n // Only update if columns actually changed (deep compare)\n const currentStr = JSON.stringify(config.columns);\n const newStr = JSON.stringify(columns);\n if (currentStr !== newStr && columns.length > 0) {\n onChange({ ...config, columns });\n }\n }, [columns]);\n\n // Reset columns when config changes from outside\n useEffect(() => {\n const normalized = ((config.columns || []) as (string | TableColumnConfig)[]).map((col) => {\n if (typeof col === 'string') {\n return {\n field: col,\n header: col.charAt(0).toUpperCase() + col.slice(1).replace(/_/g, ' '),\n };\n }\n return col as TableColumnConfig;\n });\n setColumns(normalized);\n }, [config.columns]);\n\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 const handleColumnChange = useCallback((index: number, field: keyof TableColumnConfig, value: string) => {\n setColumns((prev) => {\n const newColumns = [...prev];\n newColumns[index] = { ...newColumns[index], [field]: value };\n return newColumns;\n });\n }, []);\n\n const addColumn = useCallback(() => {\n setColumns((prev) => [...prev, { field: '', header: '' }]);\n }, []);\n\n const removeColumn = useCallback((index: number) => {\n setColumns((prev) => prev.filter((_, i) => i !== index));\n }, []);\n\n return (\n <div className=\"space-y-4\">\n {/* Page Size */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">Page Size</label>\n <select\n value={config.pageSize || 10}\n onChange={(e) => handleChange('pageSize', 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 <option value={10}>10 rows</option>\n <option value={25}>25 rows</option>\n <option value={50}>50 rows</option>\n <option value={100}>100 rows</option>\n </select>\n </div>\n\n {/* Columns Configuration */}\n <div>\n <div className=\"flex items-center justify-between mb-2\">\n <label className=\"text-sm font-medium text-text-primary\">Columns</label>\n <button\n type=\"button\"\n onClick={addColumn}\n className=\"\n text-sm text-action-primary-bg\n hover:text-action-primary-bgHover\n \"\n >\n + Add Column\n </button>\n </div>\n\n <div className=\"space-y-3\">\n {columns.map((col, index) => (\n <div\n key={index}\n className=\"\n p-3\n bg-bg-sunken\n rounded-md\n space-y-2\n \"\n >\n <div className=\"flex items-center justify-between\">\n <span className=\"text-xs text-text-secondary\">Column {index + 1}</span>\n <button\n type=\"button\"\n onClick={() => removeColumn(index)}\n className=\"text-state-error hover:text-state-error/80\"\n >\n <svg className=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n </div>\n\n <div className=\"grid grid-cols-2 gap-2\">\n <input\n type=\"text\"\n value={col.field || ''}\n onChange={(e) => handleColumnChange(index, 'field', e.target.value)}\n placeholder=\"Field name\"\n className=\"\n 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 <input\n type=\"text\"\n value={col.header || ''}\n onChange={(e) => handleColumnChange(index, 'header', e.target.value)}\n placeholder=\"Header label\"\n className=\"\n 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 <div className=\"grid grid-cols-2 gap-2\">\n <select\n value={col.format || 'text'}\n onChange={(e) => handleColumnChange(index, 'format', e.target.value)}\n className=\"\n 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 <option value=\"text\">Text</option>\n <option value=\"number\">Number</option>\n <option value=\"currency\">Currency</option>\n <option value=\"percentage\">Percentage</option>\n <option value=\"date\">Date</option>\n <option value=\"badge\">Badge</option>\n </select>\n <select\n value={col.align || 'left'}\n onChange={(e) => handleColumnChange(index, 'align', e.target.value)}\n className=\"\n 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 <option value=\"left\">Left</option>\n <option value=\"center\">Center</option>\n <option value=\"right\">Right</option>\n </select>\n </div>\n </div>\n ))}\n\n {columns.length === 0 && (\n <p className=\"text-sm text-text-secondary text-center py-4\">\n No columns configured. Columns will be auto-generated from data.\n </p>\n )}\n </div>\n </div>\n\n {/* Toggle Options */}\n <div className=\"space-y-3\">\n <label className=\"flex items-center gap-3\">\n <input\n type=\"checkbox\"\n checked={config.showSearch !== false}\n onChange={(e) => handleChange('showSearch', 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 search box</span>\n </label>\n\n <label className=\"flex items-center gap-3\">\n <input\n type=\"checkbox\"\n checked={config.showPagination !== false}\n onChange={(e) => handleChange('showPagination', 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 pagination</span>\n </label>\n\n <label className=\"flex items-center gap-3\">\n <input\n type=\"checkbox\"\n checked={config.showExport !== false}\n onChange={(e) => handleChange('showExport', 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 export button</span>\n </label>\n\n <label className=\"flex items-center gap-3\">\n <input\n type=\"checkbox\"\n checked={config.stickyHeader === true}\n onChange={(e) => handleChange('stickyHeader', 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\">Sticky header</span>\n </label>\n\n <label className=\"flex items-center gap-3\">\n <input\n type=\"checkbox\"\n checked={config.stripedRows === true}\n onChange={(e) => handleChange('stripedRows', 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\">Striped rows</span>\n </label>\n </div>\n </div>\n );\n});\n\nexport default TableConfig;\n"],"mappings":"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"}
@@ -66,7 +66,7 @@ var c = 60, l = e(function({ columns: e, sortState: l, onSort: u, sticky: d = !0
66
66
  };
67
67
  return /* @__PURE__ */ o("thead", {
68
68
  className: `
69
- bg-bg-muted
69
+ bg-bg-sunken
70
70
  ${d ? "sticky top-0 z-10" : ""}
71
71
  `,
72
72
  children: /* @__PURE__ */ o("tr", { children: e.map((e) => {
@@ -101,7 +101,7 @@ var c = 60, l = e(function({ columns: e, sortState: l, onSort: u, sticky: d = !0
101
101
  ${e.sortable === !1 ? "" : "cursor-pointer select-none group hover:bg-bg-surface"}
102
102
  ${e.align === "center" ? "text-center" : ""}
103
103
  ${e.align === "right" ? "text-right" : "text-left"}
104
- ${t == null ? "" : "sticky z-20 bg-bg-muted"}
104
+ ${t == null ? "" : "sticky z-20 bg-bg-sunken"}
105
105
  ${i ? "border-l-2 border-l-action-primary-bg" : ""}
106
106
  `,
107
107
  style: t == null ? { width: n } : {