@rebasepro/plugin-insights 0.5.0 → 0.6.0

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 (303) hide show
  1. package/dist/{plugin-insights/src/components → components}/CollectionInsightsInline.d.ts +2 -1
  2. package/dist/{plugin-insights/src/components → components}/HomeCardInsightSlot.d.ts +3 -2
  3. package/dist/{plugin-insights/src/components → components}/HomeInsightsSlot.d.ts +3 -2
  4. package/dist/{plugin-insights/src/components → components}/InsightWidget.d.ts +3 -2
  5. package/dist/{plugin-insights/src/components → components}/InsightWidgetSkeleton.d.ts +3 -2
  6. package/dist/{plugin-insights/src/components → components}/InsightsScorecardView.d.ts +3 -2
  7. package/dist/{plugin-insights/src/engine → engine}/InsightsProvider.d.ts +2 -2
  8. package/dist/{plugin-insights/src/index.d.ts → index.d.ts} +1 -1
  9. package/dist/index.es.js +583 -1072
  10. package/dist/index.es.js.map +1 -1
  11. package/dist/index.umd.js +645 -1096
  12. package/dist/index.umd.js.map +1 -1
  13. package/package.json +19 -14
  14. package/src/components/CollectionInsightsInline.tsx +3 -3
  15. package/src/components/HomeCardInsightSlot.tsx +1 -1
  16. package/src/components/HomeInsightsSlot.tsx +1 -1
  17. package/src/components/InsightWidget.tsx +4 -2
  18. package/src/components/InsightWidgetSkeleton.tsx +21 -13
  19. package/src/components/InsightsScorecardView.tsx +2 -2
  20. package/src/engine/InsightsCache.test.ts +5 -3
  21. package/src/engine/InsightsCache.ts +3 -2
  22. package/src/engine/useInsightsData.ts +3 -1
  23. package/src/index.ts +1 -1
  24. package/src/useInsightsPlugin.tsx +68 -65
  25. package/dist/common/src/collections/CollectionRegistry.d.ts +0 -56
  26. package/dist/common/src/collections/default-collections.d.ts +0 -9
  27. package/dist/common/src/collections/index.d.ts +0 -2
  28. package/dist/common/src/data/buildRebaseData.d.ts +0 -14
  29. package/dist/common/src/data/query_builder.d.ts +0 -55
  30. package/dist/common/src/index.d.ts +0 -4
  31. package/dist/common/src/util/builders.d.ts +0 -57
  32. package/dist/common/src/util/callbacks.d.ts +0 -6
  33. package/dist/common/src/util/collections.d.ts +0 -11
  34. package/dist/common/src/util/common.d.ts +0 -2
  35. package/dist/common/src/util/conditions.d.ts +0 -26
  36. package/dist/common/src/util/entities.d.ts +0 -58
  37. package/dist/common/src/util/enums.d.ts +0 -3
  38. package/dist/common/src/util/index.d.ts +0 -16
  39. package/dist/common/src/util/navigation_from_path.d.ts +0 -34
  40. package/dist/common/src/util/navigation_utils.d.ts +0 -20
  41. package/dist/common/src/util/parent_references_from_path.d.ts +0 -6
  42. package/dist/common/src/util/paths.d.ts +0 -14
  43. package/dist/common/src/util/permissions.d.ts +0 -14
  44. package/dist/common/src/util/references.d.ts +0 -2
  45. package/dist/common/src/util/relations.d.ts +0 -22
  46. package/dist/common/src/util/resolutions.d.ts +0 -72
  47. package/dist/common/src/util/storage.d.ts +0 -24
  48. package/dist/core/src/components/AIIcon.d.ts +0 -16
  49. package/dist/core/src/components/BootstrapAdminBanner.d.ts +0 -4
  50. package/dist/core/src/components/ConfirmationDialog.d.ts +0 -9
  51. package/dist/core/src/components/Debug/UIReferenceView.d.ts +0 -1
  52. package/dist/core/src/components/Debug/UIStyleGuide.d.ts +0 -1
  53. package/dist/core/src/components/ErrorTooltip.d.ts +0 -2
  54. package/dist/core/src/components/ErrorView.d.ts +0 -21
  55. package/dist/core/src/components/LanguageToggle.d.ts +0 -1
  56. package/dist/core/src/components/LoginView/LoginView.d.ts +0 -109
  57. package/dist/core/src/components/LoginView/index.d.ts +0 -2
  58. package/dist/core/src/components/NotFoundPage.d.ts +0 -1
  59. package/dist/core/src/components/RebaseAuth.d.ts +0 -10
  60. package/dist/core/src/components/RebaseLogo.d.ts +0 -7
  61. package/dist/core/src/components/UnsavedChangesDialog.d.ts +0 -9
  62. package/dist/core/src/components/UserDisplay.d.ts +0 -7
  63. package/dist/core/src/components/UserSelectPopover.d.ts +0 -62
  64. package/dist/core/src/components/UserSettingsView.d.ts +0 -1
  65. package/dist/core/src/components/common/index.d.ts +0 -6
  66. package/dist/core/src/components/common/table_height.d.ts +0 -5
  67. package/dist/core/src/components/common/types.d.ts +0 -66
  68. package/dist/core/src/components/common/useColumnsIds.d.ts +0 -9
  69. package/dist/core/src/components/common/useDataTableController.d.ts +0 -45
  70. package/dist/core/src/components/common/useDebouncedData.d.ts +0 -9
  71. package/dist/core/src/components/common/useScrollRestoration.d.ts +0 -14
  72. package/dist/core/src/components/index.d.ts +0 -17
  73. package/dist/core/src/contexts/AdminModeController.d.ts +0 -4
  74. package/dist/core/src/contexts/AnalyticsContext.d.ts +0 -3
  75. package/dist/core/src/contexts/AuthControllerContext.d.ts +0 -3
  76. package/dist/core/src/contexts/CustomizationControllerContext.d.ts +0 -3
  77. package/dist/core/src/contexts/DataDriverContext.d.ts +0 -3
  78. package/dist/core/src/contexts/DatabaseAdminContext.d.ts +0 -3
  79. package/dist/core/src/contexts/DialogsProvider.d.ts +0 -4
  80. package/dist/core/src/contexts/EffectiveRoleController.d.ts +0 -4
  81. package/dist/core/src/contexts/InternalUserManagementContext.d.ts +0 -3
  82. package/dist/core/src/contexts/ModeController.d.ts +0 -4
  83. package/dist/core/src/contexts/RebaseClientInstanceContext.d.ts +0 -6
  84. package/dist/core/src/contexts/RebaseDataContext.d.ts +0 -3
  85. package/dist/core/src/contexts/SnackbarProvider.d.ts +0 -2
  86. package/dist/core/src/contexts/StorageSourceContext.d.ts +0 -3
  87. package/dist/core/src/contexts/UserConfigurationPersistenceContext.d.ts +0 -3
  88. package/dist/core/src/contexts/index.d.ts +0 -13
  89. package/dist/core/src/core/PluginLifecycleManager.d.ts +0 -17
  90. package/dist/core/src/core/PluginProviderStack.d.ts +0 -21
  91. package/dist/core/src/core/Rebase.d.ts +0 -14
  92. package/dist/core/src/core/RebaseProps.d.ts +0 -147
  93. package/dist/core/src/core/RebaseRouter.d.ts +0 -4
  94. package/dist/core/src/core/RebaseRoutes.d.ts +0 -17
  95. package/dist/core/src/core/index.d.ts +0 -4
  96. package/dist/core/src/hooks/ApiConfigContext.d.ts +0 -24
  97. package/dist/core/src/hooks/data/delete.d.ts +0 -31
  98. package/dist/core/src/hooks/data/save.d.ts +0 -34
  99. package/dist/core/src/hooks/data/useCollectionFetch.d.ts +0 -62
  100. package/dist/core/src/hooks/data/useData.d.ts +0 -13
  101. package/dist/core/src/hooks/data/useDataOrder.d.ts +0 -12
  102. package/dist/core/src/hooks/data/useEntityFetch.d.ts +0 -43
  103. package/dist/core/src/hooks/data/useRelationSelector.d.ts +0 -52
  104. package/dist/core/src/hooks/data/useUserSelector.d.ts +0 -31
  105. package/dist/core/src/hooks/index.d.ts +0 -37
  106. package/dist/core/src/hooks/useAdminModeController.d.ts +0 -19
  107. package/dist/core/src/hooks/useAnalyticsController.d.ts +0 -5
  108. package/dist/core/src/hooks/useAuthController.d.ts +0 -11
  109. package/dist/core/src/hooks/useAuthSubscription.d.ts +0 -2
  110. package/dist/core/src/hooks/useBackendStorageSource.d.ts +0 -30
  111. package/dist/core/src/hooks/useBridgeRegistration.d.ts +0 -18
  112. package/dist/core/src/hooks/useBrowserTitleAndIcon.d.ts +0 -6
  113. package/dist/core/src/hooks/useBuildAdminModeController.d.ts +0 -6
  114. package/dist/core/src/hooks/useBuildEffectiveRoleController.d.ts +0 -8
  115. package/dist/core/src/hooks/useBuildLocalConfigurationPersistence.d.ts +0 -2
  116. package/dist/core/src/hooks/useBuildModeController.d.ts +0 -6
  117. package/dist/core/src/hooks/useClipboard.d.ts +0 -57
  118. package/dist/core/src/hooks/useCollapsedGroups.d.ts +0 -27
  119. package/dist/core/src/hooks/useCustomizationController.d.ts +0 -11
  120. package/dist/core/src/hooks/useDialogsController.d.ts +0 -11
  121. package/dist/core/src/hooks/useEffectiveRoleController.d.ts +0 -7
  122. package/dist/core/src/hooks/useInternalUserManagementController.d.ts +0 -12
  123. package/dist/core/src/hooks/useLargeLayout.d.ts +0 -1
  124. package/dist/core/src/hooks/useModeController.d.ts +0 -19
  125. package/dist/core/src/hooks/usePermissions.d.ts +0 -12
  126. package/dist/core/src/hooks/useRebaseClient.d.ts +0 -5
  127. package/dist/core/src/hooks/useRebaseContext.d.ts +0 -11
  128. package/dist/core/src/hooks/useRebaseRegistry.d.ts +0 -34
  129. package/dist/core/src/hooks/useResolvedComponent.d.ts +0 -47
  130. package/dist/core/src/hooks/useSlot.d.ts +0 -18
  131. package/dist/core/src/hooks/useSnackbarController.d.ts +0 -20
  132. package/dist/core/src/hooks/useStorageSource.d.ts +0 -7
  133. package/dist/core/src/hooks/useStudioBridge.d.ts +0 -91
  134. package/dist/core/src/hooks/useTranslation.d.ts +0 -17
  135. package/dist/core/src/hooks/useUnsavedChangesDialog.d.ts +0 -12
  136. package/dist/core/src/hooks/useUserConfigurationPersistence.d.ts +0 -8
  137. package/dist/core/src/i18n/RebaseI18nProvider.d.ts +0 -33
  138. package/dist/core/src/index.d.ts +0 -15
  139. package/dist/core/src/internal/common.d.ts +0 -3
  140. package/dist/core/src/internal/useRestoreScroll.d.ts +0 -6
  141. package/dist/core/src/locales/de.d.ts +0 -2
  142. package/dist/core/src/locales/en.d.ts +0 -10
  143. package/dist/core/src/locales/es.d.ts +0 -10
  144. package/dist/core/src/locales/fr.d.ts +0 -2
  145. package/dist/core/src/locales/hi.d.ts +0 -2
  146. package/dist/core/src/locales/it.d.ts +0 -2
  147. package/dist/core/src/locales/pt.d.ts +0 -7
  148. package/dist/core/src/util/constants.d.ts +0 -1
  149. package/dist/core/src/util/createFormexStub.d.ts +0 -2
  150. package/dist/core/src/util/entity_cache.d.ts +0 -22
  151. package/dist/core/src/util/enums.d.ts +0 -5
  152. package/dist/core/src/util/icon_list.d.ts +0 -5
  153. package/dist/core/src/util/icons.d.ts +0 -20
  154. package/dist/core/src/util/index.d.ts +0 -8
  155. package/dist/core/src/util/previews.d.ts +0 -4
  156. package/dist/core/src/util/useStorageUploadController.d.ts +0 -38
  157. package/dist/formex/src/Field.d.ts +0 -52
  158. package/dist/formex/src/Formex.d.ts +0 -7
  159. package/dist/formex/src/index.d.ts +0 -5
  160. package/dist/formex/src/types.d.ts +0 -40
  161. package/dist/formex/src/useCreateFormex.d.ts +0 -14
  162. package/dist/formex/src/utils.d.ts +0 -16
  163. package/dist/types/src/controllers/analytics_controller.d.ts +0 -7
  164. package/dist/types/src/controllers/auth.d.ts +0 -104
  165. package/dist/types/src/controllers/client.d.ts +0 -168
  166. package/dist/types/src/controllers/collection_registry.d.ts +0 -46
  167. package/dist/types/src/controllers/customization_controller.d.ts +0 -60
  168. package/dist/types/src/controllers/data.d.ts +0 -207
  169. package/dist/types/src/controllers/data_driver.d.ts +0 -218
  170. package/dist/types/src/controllers/database_admin.d.ts +0 -11
  171. package/dist/types/src/controllers/dialogs_controller.d.ts +0 -36
  172. package/dist/types/src/controllers/effective_role.d.ts +0 -4
  173. package/dist/types/src/controllers/email.d.ts +0 -36
  174. package/dist/types/src/controllers/index.d.ts +0 -18
  175. package/dist/types/src/controllers/local_config_persistence.d.ts +0 -20
  176. package/dist/types/src/controllers/navigation.d.ts +0 -225
  177. package/dist/types/src/controllers/registry.d.ts +0 -63
  178. package/dist/types/src/controllers/side_dialogs_controller.d.ts +0 -67
  179. package/dist/types/src/controllers/side_entity_controller.d.ts +0 -97
  180. package/dist/types/src/controllers/snackbar.d.ts +0 -24
  181. package/dist/types/src/controllers/storage.d.ts +0 -171
  182. package/dist/types/src/index.d.ts +0 -4
  183. package/dist/types/src/rebase_context.d.ts +0 -122
  184. package/dist/types/src/types/auth_adapter.d.ts +0 -301
  185. package/dist/types/src/types/backend.d.ts +0 -571
  186. package/dist/types/src/types/backend_hooks.d.ts +0 -172
  187. package/dist/types/src/types/builders.d.ts +0 -15
  188. package/dist/types/src/types/chips.d.ts +0 -5
  189. package/dist/types/src/types/collections.d.ts +0 -961
  190. package/dist/types/src/types/component_ref.d.ts +0 -47
  191. package/dist/types/src/types/cron.d.ts +0 -102
  192. package/dist/types/src/types/data_source.d.ts +0 -64
  193. package/dist/types/src/types/database_adapter.d.ts +0 -94
  194. package/dist/types/src/types/entities.d.ts +0 -145
  195. package/dist/types/src/types/entity_actions.d.ts +0 -104
  196. package/dist/types/src/types/entity_callbacks.d.ts +0 -173
  197. package/dist/types/src/types/entity_link_builder.d.ts +0 -7
  198. package/dist/types/src/types/entity_overrides.d.ts +0 -10
  199. package/dist/types/src/types/entity_views.d.ts +0 -87
  200. package/dist/types/src/types/export_import.d.ts +0 -21
  201. package/dist/types/src/types/formex.d.ts +0 -40
  202. package/dist/types/src/types/index.d.ts +0 -28
  203. package/dist/types/src/types/locales.d.ts +0 -4
  204. package/dist/types/src/types/modify_collections.d.ts +0 -5
  205. package/dist/types/src/types/plugins.d.ts +0 -282
  206. package/dist/types/src/types/properties.d.ts +0 -1173
  207. package/dist/types/src/types/property_config.d.ts +0 -74
  208. package/dist/types/src/types/relations.d.ts +0 -336
  209. package/dist/types/src/types/slots.d.ts +0 -262
  210. package/dist/types/src/types/translations.d.ts +0 -900
  211. package/dist/types/src/types/user_management_delegate.d.ts +0 -86
  212. package/dist/types/src/types/websockets.d.ts +0 -78
  213. package/dist/types/src/users/index.d.ts +0 -1
  214. package/dist/types/src/users/user.d.ts +0 -50
  215. package/dist/ui/src/components/Alert.d.ts +0 -12
  216. package/dist/ui/src/components/Autocomplete.d.ts +0 -21
  217. package/dist/ui/src/components/Avatar.d.ts +0 -11
  218. package/dist/ui/src/components/Badge.d.ts +0 -8
  219. package/dist/ui/src/components/BooleanSwitch.d.ts +0 -14
  220. package/dist/ui/src/components/BooleanSwitchWithLabel.d.ts +0 -17
  221. package/dist/ui/src/components/Button.d.ts +0 -14
  222. package/dist/ui/src/components/Card.d.ts +0 -8
  223. package/dist/ui/src/components/CenteredView.d.ts +0 -9
  224. package/dist/ui/src/components/Checkbox.d.ts +0 -13
  225. package/dist/ui/src/components/Chip.d.ts +0 -26
  226. package/dist/ui/src/components/CircularProgress.d.ts +0 -5
  227. package/dist/ui/src/components/CircularProgressCenter.d.ts +0 -11
  228. package/dist/ui/src/components/Collapse.d.ts +0 -9
  229. package/dist/ui/src/components/ColorPicker.d.ts +0 -30
  230. package/dist/ui/src/components/Container.d.ts +0 -8
  231. package/dist/ui/src/components/DateTimeField.d.ts +0 -24
  232. package/dist/ui/src/components/DebouncedTextField.d.ts +0 -2
  233. package/dist/ui/src/components/Dialog.d.ts +0 -39
  234. package/dist/ui/src/components/DialogActions.d.ts +0 -7
  235. package/dist/ui/src/components/DialogContent.d.ts +0 -7
  236. package/dist/ui/src/components/DialogTitle.d.ts +0 -10
  237. package/dist/ui/src/components/ErrorBoundary.d.ts +0 -33
  238. package/dist/ui/src/components/ExpandablePanel.d.ts +0 -12
  239. package/dist/ui/src/components/FileUpload.d.ts +0 -23
  240. package/dist/ui/src/components/FilterChip.d.ts +0 -34
  241. package/dist/ui/src/components/IconButton.d.ts +0 -12
  242. package/dist/ui/src/components/InfoLabel.d.ts +0 -5
  243. package/dist/ui/src/components/InputLabel.d.ts +0 -11
  244. package/dist/ui/src/components/Label.d.ts +0 -7
  245. package/dist/ui/src/components/LoadingButton.d.ts +0 -7
  246. package/dist/ui/src/components/Markdown.d.ts +0 -10
  247. package/dist/ui/src/components/Menu.d.ts +0 -23
  248. package/dist/ui/src/components/Menubar.d.ts +0 -80
  249. package/dist/ui/src/components/MultiSelect.d.ts +0 -48
  250. package/dist/ui/src/components/Paper.d.ts +0 -6
  251. package/dist/ui/src/components/Popover.d.ts +0 -24
  252. package/dist/ui/src/components/RadioGroup.d.ts +0 -28
  253. package/dist/ui/src/components/ResizablePanels.d.ts +0 -18
  254. package/dist/ui/src/components/SearchBar.d.ts +0 -26
  255. package/dist/ui/src/components/Select.d.ts +0 -43
  256. package/dist/ui/src/components/Separator.d.ts +0 -5
  257. package/dist/ui/src/components/Sheet.d.ts +0 -22
  258. package/dist/ui/src/components/Skeleton.d.ts +0 -6
  259. package/dist/ui/src/components/Slider.d.ts +0 -21
  260. package/dist/ui/src/components/Table.d.ts +0 -34
  261. package/dist/ui/src/components/Tabs.d.ts +0 -19
  262. package/dist/ui/src/components/TextField.d.ts +0 -58
  263. package/dist/ui/src/components/TextareaAutosize.d.ts +0 -43
  264. package/dist/ui/src/components/ToggleButtonGroup.d.ts +0 -30
  265. package/dist/ui/src/components/Tooltip.d.ts +0 -19
  266. package/dist/ui/src/components/Typography.d.ts +0 -36
  267. package/dist/ui/src/components/VirtualTable/VirtualTable.d.ts +0 -11
  268. package/dist/ui/src/components/VirtualTable/VirtualTableCell.d.ts +0 -21
  269. package/dist/ui/src/components/VirtualTable/VirtualTableHeader.d.ts +0 -29
  270. package/dist/ui/src/components/VirtualTable/VirtualTableHeaderRow.d.ts +0 -2
  271. package/dist/ui/src/components/VirtualTable/VirtualTableProps.d.ts +0 -249
  272. package/dist/ui/src/components/VirtualTable/VirtualTableRow.d.ts +0 -3
  273. package/dist/ui/src/components/VirtualTable/index.d.ts +0 -3
  274. package/dist/ui/src/components/VirtualTable/types.d.ts +0 -38
  275. package/dist/ui/src/components/common/SelectInputLabel.d.ts +0 -5
  276. package/dist/ui/src/components/index.d.ts +0 -58
  277. package/dist/ui/src/hooks/PortalContainerContext.d.ts +0 -31
  278. package/dist/ui/src/hooks/index.d.ts +0 -6
  279. package/dist/ui/src/hooks/useDebounceCallback.d.ts +0 -1
  280. package/dist/ui/src/hooks/useDebounceValue.d.ts +0 -1
  281. package/dist/ui/src/hooks/useDebouncedCallback.d.ts +0 -1
  282. package/dist/ui/src/hooks/useInjectStyles.d.ts +0 -7
  283. package/dist/ui/src/hooks/useOutsideAlerter.d.ts +0 -5
  284. package/dist/ui/src/icons/GitHubIcon.d.ts +0 -2
  285. package/dist/ui/src/icons/HandleIcon.d.ts +0 -1
  286. package/dist/ui/src/icons/Icon.d.ts +0 -20
  287. package/dist/ui/src/icons/cool_icon_keys.d.ts +0 -1
  288. package/dist/ui/src/icons/icon_keys.d.ts +0 -1
  289. package/dist/ui/src/icons/index.d.ts +0 -8
  290. package/dist/ui/src/index.d.ts +0 -5
  291. package/dist/ui/src/styles.d.ts +0 -12
  292. package/dist/ui/src/util/chip_colors.d.ts +0 -4
  293. package/dist/ui/src/util/cls.d.ts +0 -2
  294. package/dist/ui/src/util/debounce.d.ts +0 -10
  295. package/dist/ui/src/util/hash.d.ts +0 -1
  296. package/dist/ui/src/util/index.d.ts +0 -4
  297. package/dist/ui/src/util/key_to_icon_component.d.ts +0 -1
  298. /package/dist/{plugin-insights/src/engine → engine}/InsightsCache.d.ts +0 -0
  299. /package/dist/{plugin-insights/src/engine → engine}/useInsightsData.d.ts +0 -0
  300. /package/dist/{plugin-insights/src/types → types}/engine.d.ts +0 -0
  301. /package/dist/{plugin-insights/src/types → types}/index.d.ts +0 -0
  302. /package/dist/{plugin-insights/src/types → types}/widgets.d.ts +0 -0
  303. /package/dist/{plugin-insights/src/useInsightsPlugin.d.ts → useInsightsPlugin.d.ts} +0 -0
package/dist/index.es.js CHANGED
@@ -1,1098 +1,609 @@
1
+ import React, { createContext, useContext, useEffect, useMemo, useRef, useState } from "react";
1
2
  import { jsx, jsxs } from "react/jsx-runtime";
2
- import { c } from "react-compiler-runtime";
3
- import React, { createContext, useContext, useState, useEffect, useRef } from "react";
4
- import { useAuthController, getIcon } from "@rebasepro/core";
3
+ import { getIcon, useAuthController } from "@rebasepro/core";
5
4
  import { cls, defaultBorderMixin } from "@rebasepro/ui";
6
- class InsightsCache {
7
- constructor(ttl = 6e4) {
8
- this.ttl = ttl;
9
- }
10
- ttl;
11
- cache = /* @__PURE__ */ new Map();
12
- inflight = /* @__PURE__ */ new Map();
13
- get(key) {
14
- const entry = this.cache.get(key);
15
- if (!entry) return null;
16
- if (Date.now() - entry.timestamp > this.ttl) {
17
- this.cache.delete(key);
18
- return null;
19
- }
20
- return entry.data;
21
- }
22
- set(key, data) {
23
- this.cache.set(key, {
24
- data,
25
- timestamp: Date.now()
26
- });
27
- this.inflight.delete(key);
28
- }
29
- getInflight(key) {
30
- return this.inflight.get(key) ?? null;
31
- }
32
- setInflight(key, promise) {
33
- this.inflight.set(key, promise);
34
- }
35
- invalidate(key) {
36
- if (key) {
37
- this.cache.delete(key);
38
- this.inflight.delete(key);
39
- } else {
40
- this.cache.clear();
41
- this.inflight.clear();
42
- }
43
- }
44
- }
45
- const InsightsContext = createContext(null);
46
- function InsightsProvider(t0) {
47
- const $ = c(7);
48
- const {
49
- cacheTTL,
50
- children
51
- } = t0;
52
- let t1;
53
- let t2;
54
- if ($[0] !== cacheTTL) {
55
- t2 = new InsightsCache(cacheTTL);
56
- $[0] = cacheTTL;
57
- $[1] = t2;
58
- } else {
59
- t2 = $[1];
60
- }
61
- t1 = t2;
62
- const cache = t1;
63
- let t3;
64
- let t4;
65
- if ($[2] !== cache) {
66
- t4 = {
67
- cache
68
- };
69
- $[2] = cache;
70
- $[3] = t4;
71
- } else {
72
- t4 = $[3];
73
- }
74
- t3 = t4;
75
- const value = t3;
76
- let t5;
77
- if ($[4] !== children || $[5] !== value) {
78
- t5 = /* @__PURE__ */ jsx(InsightsContext.Provider, { value, children });
79
- $[4] = children;
80
- $[5] = value;
81
- $[6] = t5;
82
- } else {
83
- t5 = $[6];
84
- }
85
- return t5;
5
+ //#region src/engine/InsightsCache.ts
6
+ /**
7
+ * In-memory cache for insight query results.
8
+ * Supports TTL-based expiry and inflight request deduplication
9
+ * to prevent redundant network requests when multiple widgets
10
+ * share the same query.
11
+ */
12
+ var InsightsCache = class {
13
+ ttl;
14
+ cache = /* @__PURE__ */ new Map();
15
+ inflight = /* @__PURE__ */ new Map();
16
+ constructor(ttl = 6e4) {
17
+ this.ttl = ttl;
18
+ }
19
+ get(key) {
20
+ const entry = this.cache.get(key);
21
+ if (!entry) return null;
22
+ if (Date.now() - entry.timestamp > this.ttl) {
23
+ this.cache.delete(key);
24
+ return null;
25
+ }
26
+ return entry.data;
27
+ }
28
+ set(key, data) {
29
+ this.cache.set(key, {
30
+ data,
31
+ timestamp: Date.now()
32
+ });
33
+ this.inflight.delete(key);
34
+ }
35
+ getInflight(key) {
36
+ return this.inflight.get(key) ?? null;
37
+ }
38
+ setInflight(key, promise) {
39
+ this.inflight.set(key, promise);
40
+ }
41
+ invalidate(key) {
42
+ if (key) {
43
+ this.cache.delete(key);
44
+ this.inflight.delete(key);
45
+ } else {
46
+ this.cache.clear();
47
+ this.inflight.clear();
48
+ }
49
+ }
50
+ };
51
+ //#endregion
52
+ //#region src/engine/InsightsProvider.tsx
53
+ var InsightsContext = createContext(null);
54
+ /**
55
+ * Root-level provider for the insights data engine.
56
+ * Injected automatically by the plugin via `providers: [{ scope: "root" }]`.
57
+ *
58
+ * Manages a single `InsightsCache` instance shared by all insight widgets
59
+ * for TTL-based caching and inflight request deduplication.
60
+ */
61
+ function InsightsProvider({ cacheTTL, children }) {
62
+ const cache = useMemo(() => new InsightsCache(cacheTTL), [cacheTTL]);
63
+ const value = useMemo(() => ({ cache }), [cache]);
64
+ return /* @__PURE__ */ jsx(InsightsContext.Provider, {
65
+ value,
66
+ children
67
+ });
86
68
  }
69
+ /**
70
+ * Access the insights cache (for advanced usage).
71
+ * Returns null when called outside of an `InsightsProvider`
72
+ * (e.g. during auth-loading phase before plugin providers mount).
73
+ */
87
74
  function useInsightsEngine() {
88
- return useContext(InsightsContext);
75
+ return useContext(InsightsContext);
89
76
  }
77
+ //#endregion
78
+ //#region src/engine/useInsightsData.ts
79
+ /**
80
+ * Hook that fetches and caches data for a single insight definition.
81
+ *
82
+ * Calls the definition's own `data()` callback and manages:
83
+ * - TTL-based caching via InsightsCache
84
+ * - Inflight request deduplication (multiple mounts of the same widget)
85
+ * - Loading and error state management
86
+ *
87
+ * @param definition - The insight to fetch data for
88
+ * @param collectionSlug - Optional collection context for cache key scoping
89
+ */
90
90
  function useInsightsData(definition, context) {
91
- const $ = c(18);
92
- const engine = useInsightsEngine();
93
- const cache = engine?.cache ?? null;
94
- const {
95
- initialLoading,
96
- authLoading,
97
- user,
98
- loginSkipped
99
- } = useAuthController();
100
- const authReady = !initialLoading && !authLoading && (Boolean(user) || loginSkipped);
101
- const [data, setData] = useState(null);
102
- const [loading, setLoading] = useState(true);
103
- const [error, setError] = useState(null);
104
- const cacheKey = `${definition.id}:${context.path ?? context.collectionSlug ?? "global"}`;
105
- let t0;
106
- if ($[0] !== authReady || $[1] !== cache || $[2] !== cacheKey || $[3] !== context || $[4] !== definition) {
107
- t0 = () => {
108
- if (!authReady || !cache) {
109
- return;
110
- }
111
- let cancelled;
112
- cancelled = false;
113
- const cached = cache.get(cacheKey);
114
- if (cached) {
115
- setData(cached);
116
- setLoading(false);
117
- return;
118
- }
119
- const inflight = cache.getInflight(cacheKey);
120
- if (inflight) {
121
- setLoading(true);
122
- inflight.then((result) => {
123
- if (!cancelled) {
124
- setData(result);
125
- }
126
- }).catch((err) => {
127
- if (!cancelled) {
128
- setError(err instanceof Error ? err : new Error(String(err)));
129
- }
130
- }).finally(() => {
131
- if (!cancelled) {
132
- setLoading(false);
133
- }
134
- });
135
- return;
136
- }
137
- setLoading(true);
138
- setError(null);
139
- const promise = definition.data(context);
140
- cache.setInflight(cacheKey, promise);
141
- promise.then((result_0) => {
142
- cache.set(cacheKey, result_0);
143
- if (!cancelled) {
144
- setData(result_0);
145
- }
146
- }).catch((err_0) => {
147
- cache.invalidate(cacheKey);
148
- if (!cancelled) {
149
- setError(err_0 instanceof Error ? err_0 : new Error(String(err_0)));
150
- }
151
- }).finally(() => {
152
- if (!cancelled) {
153
- setLoading(false);
154
- }
155
- });
156
- return () => {
157
- cancelled = true;
158
- };
159
- };
160
- $[0] = authReady;
161
- $[1] = cache;
162
- $[2] = cacheKey;
163
- $[3] = context;
164
- $[4] = definition;
165
- $[5] = t0;
166
- } else {
167
- t0 = $[5];
168
- }
169
- let t1;
170
- if ($[6] !== authReady || $[7] !== cache || $[8] !== cacheKey || $[9] !== context.collectionSlug || $[10] !== context.path || $[11] !== definition.data || $[12] !== definition.id) {
171
- t1 = [definition.id, definition.data, context.path, context.collectionSlug, cacheKey, cache, authReady];
172
- $[6] = authReady;
173
- $[7] = cache;
174
- $[8] = cacheKey;
175
- $[9] = context.collectionSlug;
176
- $[10] = context.path;
177
- $[11] = definition.data;
178
- $[12] = definition.id;
179
- $[13] = t1;
180
- } else {
181
- t1 = $[13];
182
- }
183
- useEffect(t0, t1);
184
- let t2;
185
- if ($[14] !== data || $[15] !== error || $[16] !== loading) {
186
- t2 = {
187
- data,
188
- loading,
189
- error
190
- };
191
- $[14] = data;
192
- $[15] = error;
193
- $[16] = loading;
194
- $[17] = t2;
195
- } else {
196
- t2 = $[17];
197
- }
198
- return t2;
91
+ const cache = useInsightsEngine()?.cache ?? null;
92
+ const { initialLoading, authLoading, user, loginSkipped } = useAuthController();
93
+ const authReady = !initialLoading && !authLoading && (Boolean(user) || loginSkipped);
94
+ const [data, setData] = useState(null);
95
+ const [loading, setLoading] = useState(true);
96
+ const [error, setError] = useState(null);
97
+ const cacheKey = `${definition.id}:${context.path ?? context.collectionSlug ?? "global"}`;
98
+ useEffect(() => {
99
+ if (!authReady || !cache) return;
100
+ let cancelled = false;
101
+ const cached = cache.get(cacheKey);
102
+ if (cached) {
103
+ setData(cached);
104
+ setLoading(false);
105
+ return;
106
+ }
107
+ const inflight = cache.getInflight(cacheKey);
108
+ if (inflight) {
109
+ setLoading(true);
110
+ inflight.then((result) => {
111
+ if (!cancelled) setData(result);
112
+ }).catch((err) => {
113
+ if (!cancelled) setError(err instanceof Error ? err : new Error(String(err)));
114
+ }).finally(() => {
115
+ if (!cancelled) setLoading(false);
116
+ });
117
+ return;
118
+ }
119
+ setLoading(true);
120
+ setError(null);
121
+ const promise = definition.data(context);
122
+ cache.setInflight(cacheKey, promise);
123
+ promise.then((result) => {
124
+ cache.set(cacheKey, result);
125
+ if (!cancelled) setData(result);
126
+ }).catch((err) => {
127
+ cache.invalidate(cacheKey);
128
+ if (!cancelled) setError(err instanceof Error ? err : new Error(String(err)));
129
+ }).finally(() => {
130
+ if (!cancelled) setLoading(false);
131
+ });
132
+ return () => {
133
+ cancelled = true;
134
+ };
135
+ }, [
136
+ definition.id,
137
+ definition.data,
138
+ context.path,
139
+ context.collectionSlug,
140
+ cacheKey,
141
+ cache,
142
+ authReady
143
+ ]);
144
+ return {
145
+ data,
146
+ loading,
147
+ error
148
+ };
199
149
  }
150
+ //#endregion
151
+ //#region src/components/InsightsScorecardView.tsx
200
152
  function formatNumber(value, format) {
201
- if (value === null || value === void 0) return "N/A";
202
- const options = {
203
- style: format?.style ?? "decimal",
204
- notation: format?.notation ?? "standard",
205
- maximumFractionDigits: format?.decimals ?? 1,
206
- minimumFractionDigits: format?.decimals ?? 1
207
- };
208
- if (format?.style === "currency") {
209
- options.currency = format.currency ?? "USD";
210
- }
211
- let formatted = new Intl.NumberFormat("en-US", options).format(value);
212
- if (format?.showSign && value > 0) {
213
- formatted = "+" + formatted;
214
- }
215
- return formatted;
153
+ if (value === null || value === void 0) return "N/A";
154
+ const options = {
155
+ style: format?.style ?? "decimal",
156
+ notation: format?.notation ?? "standard",
157
+ maximumFractionDigits: format?.decimals ?? 1,
158
+ minimumFractionDigits: format?.decimals ?? 1
159
+ };
160
+ if (format?.style === "currency") options.currency = format.currency ?? "USD";
161
+ let formatted = new Intl.NumberFormat("en-US", options).format(value);
162
+ if (format?.showSign && value > 0) formatted = "+" + formatted;
163
+ return formatted;
216
164
  }
217
- function InsightsScorecardView(t0) {
218
- const $ = c(59);
219
- const {
220
- config,
221
- data,
222
- title,
223
- compact: t1,
224
- embedded: t2,
225
- fixedHeight
226
- } = t0;
227
- const compact = t1 === void 0 ? false : t1;
228
- const embedded = t2 === void 0 ? false : t2;
229
- const containerRef = useRef(null);
230
- const [containerWidth, setContainerWidth] = useState(null);
231
- let t3;
232
- let t4;
233
- if ($[0] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
234
- t3 = () => {
235
- if (!containerRef.current) {
236
- return;
237
- }
238
- setContainerWidth(containerRef.current.offsetWidth);
239
- const observer = new ResizeObserver((entries) => {
240
- for (const entry of entries) {
241
- setContainerWidth(entry.contentRect.width);
242
- }
243
- });
244
- observer.observe(containerRef.current);
245
- return () => observer.disconnect();
246
- };
247
- t4 = [];
248
- $[0] = t3;
249
- $[1] = t4;
250
- } else {
251
- t3 = $[0];
252
- t4 = $[1];
253
- }
254
- React.useLayoutEffect(t3, t4);
255
- const mainValue = data[config.value.field];
256
- let t5;
257
- if ($[2] !== config.value.format || $[3] !== mainValue) {
258
- t5 = typeof mainValue === "number" ? formatNumber(mainValue, config.value.format) : String(mainValue ?? "N/A");
259
- $[2] = config.value.format;
260
- $[3] = mainValue;
261
- $[4] = t5;
262
- } else {
263
- t5 = $[4];
264
- }
265
- const formattedValue = t5;
266
- let comparisonElement = null;
267
- if (config.comparison) {
268
- const comparisonValue = data[config.comparison.field];
269
- if (typeof comparisonValue === "number") {
270
- let t62;
271
- if ($[5] !== comparisonValue || $[6] !== config.comparison.format) {
272
- t62 = formatNumber(comparisonValue, config.comparison.format);
273
- $[5] = comparisonValue;
274
- $[6] = config.comparison.format;
275
- $[7] = t62;
276
- } else {
277
- t62 = $[7];
278
- }
279
- const formattedComparison = t62;
280
- const isPositive = comparisonValue > 0;
281
- const isNegative = comparisonValue < 0;
282
- let colorClass = "text-surface-500 dark:text-surface-400";
283
- if (config.comparison.intent === "increase_is_good") {
284
- if (isPositive) {
285
- colorClass = "text-emerald-500";
286
- }
287
- if (isNegative) {
288
- colorClass = "text-red-500";
289
- }
290
- } else {
291
- if (config.comparison.intent === "decrease_is_good") {
292
- if (isPositive) {
293
- colorClass = "text-red-500";
294
- }
295
- if (isNegative) {
296
- colorClass = "text-emerald-500";
297
- }
298
- }
299
- }
300
- const t72 = `font-medium ${compact ? "text-[10px]" : "text-xs"} ${colorClass}`;
301
- let t82;
302
- if ($[8] !== formattedComparison || $[9] !== t72) {
303
- t82 = /* @__PURE__ */ jsx("span", { className: t72, children: formattedComparison });
304
- $[8] = formattedComparison;
305
- $[9] = t72;
306
- $[10] = t82;
307
- } else {
308
- t82 = $[10];
309
- }
310
- comparisonElement = t82;
311
- }
312
- }
313
- const isSmall = compact || containerWidth !== null && containerWidth < 200;
314
- let t6;
315
- if ($[11] !== config.icon || $[12] !== isSmall) {
316
- t6 = config.icon ? getIcon(config.icon, "text-surface-400 dark:text-surface-500", void 0, isSmall ? 14 : 18) : null;
317
- $[11] = config.icon;
318
- $[12] = isSmall;
319
- $[13] = t6;
320
- } else {
321
- t6 = $[13];
322
- }
323
- const iconElement = t6;
324
- if (compact) {
325
- let t72;
326
- if ($[14] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
327
- t72 = cls("flex flex-col gap-0.5 px-2.5 py-2 rounded-md bg-transparent border min-w-0", defaultBorderMixin);
328
- $[14] = t72;
329
- } else {
330
- t72 = $[14];
331
- }
332
- let t82;
333
- if ($[15] !== title) {
334
- t82 = /* @__PURE__ */ jsx("span", { className: "text-[10px] uppercase tracking-wider text-surface-400 dark:text-surface-500 truncate", children: title });
335
- $[15] = title;
336
- $[16] = t82;
337
- } else {
338
- t82 = $[16];
339
- }
340
- let t92;
341
- if ($[17] !== formattedValue) {
342
- t92 = /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold tabular-nums text-surface-800 dark:text-surface-100", children: formattedValue });
343
- $[17] = formattedValue;
344
- $[18] = t92;
345
- } else {
346
- t92 = $[18];
347
- }
348
- let t102;
349
- if ($[19] !== comparisonElement || $[20] !== t92) {
350
- t102 = /* @__PURE__ */ jsxs("div", { className: "flex items-baseline gap-1.5", children: [
351
- t92,
352
- comparisonElement
353
- ] });
354
- $[19] = comparisonElement;
355
- $[20] = t92;
356
- $[21] = t102;
357
- } else {
358
- t102 = $[21];
359
- }
360
- let t112;
361
- if ($[22] !== t102 || $[23] !== t82) {
362
- t112 = /* @__PURE__ */ jsxs("div", { className: t72, children: [
363
- t82,
364
- t102
365
- ] });
366
- $[22] = t102;
367
- $[23] = t82;
368
- $[24] = t112;
369
- } else {
370
- t112 = $[24];
371
- }
372
- return t112;
373
- }
374
- let t7;
375
- if ($[25] !== embedded || $[26] !== isSmall) {
376
- t7 = embedded ? `flex flex-col min-w-0 h-full ${isSmall ? "px-3.5 py-3" : "px-5 py-4"}` : cls("rounded-lg flex flex-col min-w-0 bg-transparent border", defaultBorderMixin, isSmall ? "px-3.5 py-3" : "px-5 py-4");
377
- $[25] = embedded;
378
- $[26] = isSmall;
379
- $[27] = t7;
380
- } else {
381
- t7 = $[27];
382
- }
383
- const baseClass = t7;
384
- let t8;
385
- if ($[28] !== embedded || $[29] !== fixedHeight || $[30] !== isSmall) {
386
- t8 = embedded ? void 0 : fixedHeight ? {
387
- height: fixedHeight
388
- } : {
389
- minHeight: isSmall ? 68 : 92
390
- };
391
- $[28] = embedded;
392
- $[29] = fixedHeight;
393
- $[30] = isSmall;
394
- $[31] = t8;
395
- } else {
396
- t8 = $[31];
397
- }
398
- const t9 = `flex items-center justify-between ${isSmall ? "mb-1" : "mb-2"}`;
399
- const t10 = `font-medium leading-snug truncate text-surface-500 dark:text-surface-400 ${isSmall ? "text-[11px]" : "text-xs"}`;
400
- let t11;
401
- if ($[32] !== t10 || $[33] !== title) {
402
- t11 = /* @__PURE__ */ jsx("span", { className: t10, children: title });
403
- $[32] = t10;
404
- $[33] = title;
405
- $[34] = t11;
406
- } else {
407
- t11 = $[34];
408
- }
409
- let t12;
410
- if ($[35] !== config.dateRange || $[36] !== isSmall) {
411
- t12 = config.dateRange && !isSmall && /* @__PURE__ */ jsx("span", { className: "text-[10px] text-surface-400 dark:text-surface-500 truncate mt-0.5", children: config.dateRange });
412
- $[35] = config.dateRange;
413
- $[36] = isSmall;
414
- $[37] = t12;
415
- } else {
416
- t12 = $[37];
417
- }
418
- let t13;
419
- if ($[38] !== t11 || $[39] !== t12) {
420
- t13 = /* @__PURE__ */ jsxs("div", { className: "flex flex-col min-w-0", children: [
421
- t11,
422
- t12
423
- ] });
424
- $[38] = t11;
425
- $[39] = t12;
426
- $[40] = t13;
427
- } else {
428
- t13 = $[40];
429
- }
430
- let t14;
431
- if ($[41] !== iconElement) {
432
- t14 = iconElement && /* @__PURE__ */ jsx("span", { className: "ml-2 shrink-0", children: iconElement });
433
- $[41] = iconElement;
434
- $[42] = t14;
435
- } else {
436
- t14 = $[42];
437
- }
438
- let t15;
439
- if ($[43] !== t13 || $[44] !== t14 || $[45] !== t9) {
440
- t15 = /* @__PURE__ */ jsxs("div", { className: t9, children: [
441
- t13,
442
- t14
443
- ] });
444
- $[43] = t13;
445
- $[44] = t14;
446
- $[45] = t9;
447
- $[46] = t15;
448
- } else {
449
- t15 = $[46];
450
- }
451
- const t16 = `font-semibold leading-tight tracking-tight break-all text-surface-800 dark:text-surface-100 ${isSmall ? "text-lg" : containerWidth !== null && containerWidth < 300 ? "text-xl" : "text-2xl"}`;
452
- let t17;
453
- if ($[47] !== formattedValue || $[48] !== t16) {
454
- t17 = /* @__PURE__ */ jsx("div", { className: t16, children: formattedValue });
455
- $[47] = formattedValue;
456
- $[48] = t16;
457
- $[49] = t17;
458
- } else {
459
- t17 = $[49];
460
- }
461
- let t18;
462
- if ($[50] !== comparisonElement || $[51] !== isSmall) {
463
- t18 = comparisonElement && /* @__PURE__ */ jsx("div", { className: isSmall ? "mt-0.5" : "mt-1", children: comparisonElement });
464
- $[50] = comparisonElement;
465
- $[51] = isSmall;
466
- $[52] = t18;
467
- } else {
468
- t18 = $[52];
469
- }
470
- let t19;
471
- if ($[53] !== baseClass || $[54] !== t15 || $[55] !== t17 || $[56] !== t18 || $[57] !== t8) {
472
- t19 = /* @__PURE__ */ jsxs("div", { ref: containerRef, className: baseClass, style: t8, children: [
473
- t15,
474
- t17,
475
- t18
476
- ] });
477
- $[53] = baseClass;
478
- $[54] = t15;
479
- $[55] = t17;
480
- $[56] = t18;
481
- $[57] = t8;
482
- $[58] = t19;
483
- } else {
484
- t19 = $[58];
485
- }
486
- return t19;
165
+ /**
166
+ * Scorecard widget for the Rebase design system.
167
+ *
168
+ * Renders a single KPI metric with optional comparison value and icon.
169
+ * Uses Tailwind `dark:` classes — no JS dark mode detection.
170
+ * Icons are resolved via `getIcon` from `@rebasepro/core`.
171
+ */
172
+ function InsightsScorecardView({ config, data, title, compact = false, embedded = false, fixedHeight }) {
173
+ const containerRef = useRef(null);
174
+ const [containerWidth, setContainerWidth] = useState(null);
175
+ React.useLayoutEffect(() => {
176
+ if (!containerRef.current) return;
177
+ setContainerWidth(containerRef.current.offsetWidth);
178
+ const observer = new ResizeObserver((entries) => {
179
+ for (const entry of entries) setContainerWidth(entry.contentRect.width);
180
+ });
181
+ observer.observe(containerRef.current);
182
+ return () => observer.disconnect();
183
+ }, []);
184
+ const mainValue = data[config.value.field];
185
+ const formattedValue = typeof mainValue === "number" ? formatNumber(mainValue, config.value.format) : String(mainValue ?? "N/A");
186
+ let comparisonElement = null;
187
+ if (config.comparison) {
188
+ const comparisonValue = data[config.comparison.field];
189
+ if (typeof comparisonValue === "number") {
190
+ const formattedComparison = formatNumber(comparisonValue, config.comparison.format);
191
+ const isPositive = comparisonValue > 0;
192
+ const isNegative = comparisonValue < 0;
193
+ let colorClass = "text-surface-500 dark:text-surface-400";
194
+ if (config.comparison.intent === "increase_is_good") {
195
+ if (isPositive) colorClass = "text-emerald-500";
196
+ if (isNegative) colorClass = "text-red-500";
197
+ } else if (config.comparison.intent === "decrease_is_good") {
198
+ if (isPositive) colorClass = "text-red-500";
199
+ if (isNegative) colorClass = "text-emerald-500";
200
+ }
201
+ comparisonElement = /* @__PURE__ */ jsx("span", {
202
+ className: `font-medium ${compact ? "text-[10px]" : "text-xs"} ${colorClass}`,
203
+ children: formattedComparison
204
+ });
205
+ }
206
+ }
207
+ const isSmall = compact || containerWidth !== null && containerWidth < 200;
208
+ const iconElement = config.icon ? getIcon(config.icon, "text-surface-400 dark:text-surface-500", void 0, isSmall ? 14 : 18) : null;
209
+ if (compact) return /* @__PURE__ */ jsxs("div", {
210
+ className: cls("flex flex-col gap-0.5 px-2.5 py-2 rounded-md bg-transparent border min-w-0", defaultBorderMixin),
211
+ children: [/* @__PURE__ */ jsx("span", {
212
+ className: "text-[10px] uppercase tracking-wider text-surface-400 dark:text-surface-500 truncate",
213
+ children: title
214
+ }), /* @__PURE__ */ jsxs("div", {
215
+ className: "flex items-baseline gap-1.5",
216
+ children: [/* @__PURE__ */ jsx("span", {
217
+ className: "text-sm font-semibold tabular-nums text-surface-800 dark:text-surface-100",
218
+ children: formattedValue
219
+ }), comparisonElement]
220
+ })]
221
+ });
222
+ return /* @__PURE__ */ jsxs("div", {
223
+ ref: containerRef,
224
+ className: embedded ? `flex flex-col min-w-0 h-full ${isSmall ? "px-3.5 py-3" : "px-5 py-4"}` : cls("rounded-lg flex flex-col min-w-0 bg-transparent border", defaultBorderMixin, isSmall ? "px-3.5 py-3" : "px-5 py-4"),
225
+ style: embedded ? void 0 : fixedHeight ? { height: fixedHeight } : { minHeight: isSmall ? 68 : 92 },
226
+ children: [
227
+ /* @__PURE__ */ jsxs("div", {
228
+ className: `flex items-center justify-between ${isSmall ? "mb-1" : "mb-2"}`,
229
+ children: [/* @__PURE__ */ jsxs("div", {
230
+ className: "flex flex-col min-w-0",
231
+ children: [/* @__PURE__ */ jsx("span", {
232
+ className: `font-medium leading-snug truncate text-surface-500 dark:text-surface-400 ${isSmall ? "text-[11px]" : "text-xs"}`,
233
+ children: title
234
+ }), config.dateRange && !isSmall && /* @__PURE__ */ jsx("span", {
235
+ className: "text-[10px] text-surface-400 dark:text-surface-500 truncate mt-0.5",
236
+ children: config.dateRange
237
+ })]
238
+ }), iconElement && /* @__PURE__ */ jsx("span", {
239
+ className: "ml-2 shrink-0",
240
+ children: iconElement
241
+ })]
242
+ }),
243
+ /* @__PURE__ */ jsx("div", {
244
+ className: `font-semibold leading-tight tracking-tight break-all text-surface-800 dark:text-surface-100 ${isSmall ? "text-lg" : containerWidth !== null && containerWidth < 300 ? "text-xl" : "text-2xl"}`,
245
+ children: formattedValue
246
+ }),
247
+ comparisonElement && /* @__PURE__ */ jsx("div", {
248
+ className: isSmall ? "mt-0.5" : "mt-1",
249
+ children: comparisonElement
250
+ })
251
+ ]
252
+ });
487
253
  }
488
254
  InsightsScorecardView.displayName = "InsightsScorecardView";
489
- function InsightWidgetSkeleton(t0) {
490
- const $ = c(18);
491
- const {
492
- config,
493
- compact: t1,
494
- embedded: t2,
495
- fixedHeight
496
- } = t0;
497
- const compact = t1 === void 0 ? false : t1;
498
- const embedded = t2 === void 0 ? false : t2;
499
- const hasComparison = Boolean(config.comparison);
500
- const hasIcon = Boolean(config.icon);
501
- const hasDateRange = Boolean(config.dateRange);
502
- if (compact) {
503
- const t32 = embedded ? "h-full px-2.5 py-2" : "flex flex-col gap-0.5 rounded-md bg-transparent border min-w-0 px-2.5 py-2";
504
- const t4 = !embedded && defaultBorderMixin;
505
- let t5;
506
- if ($[0] !== t32 || $[1] !== t4) {
507
- t5 = cls("animate-pulse", t32, t4);
508
- $[0] = t32;
509
- $[1] = t4;
510
- $[2] = t5;
511
- } else {
512
- t5 = $[2];
513
- }
514
- let t6;
515
- if ($[3] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
516
- t6 = /* @__PURE__ */ jsx("div", { className: "bg-surface-200 dark:bg-surface-700 rounded-sm", style: {
517
- height: 14,
518
- width: 48
519
- } });
520
- $[3] = t6;
521
- } else {
522
- t6 = $[3];
523
- }
524
- let t7;
525
- if ($[4] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
526
- t7 = /* @__PURE__ */ jsx("div", { className: "bg-surface-200 dark:bg-surface-700 rounded-sm", style: {
527
- height: 20,
528
- width: 40
529
- } });
530
- $[4] = t7;
531
- } else {
532
- t7 = $[4];
533
- }
534
- let t8;
535
- if ($[5] !== hasComparison) {
536
- t8 = hasComparison && /* @__PURE__ */ jsx("div", { className: "bg-surface-200/60 dark:bg-surface-700/60 rounded-sm", style: {
537
- height: 14,
538
- width: 28
539
- } });
540
- $[5] = hasComparison;
541
- $[6] = t8;
542
- } else {
543
- t8 = $[6];
544
- }
545
- let t9;
546
- if ($[7] !== t8) {
547
- t9 = /* @__PURE__ */ jsxs("div", { className: "flex items-baseline gap-1.5", children: [
548
- t7,
549
- t8
550
- ] });
551
- $[7] = t8;
552
- $[8] = t9;
553
- } else {
554
- t9 = $[8];
555
- }
556
- let t10;
557
- if ($[9] !== t5 || $[10] !== t9) {
558
- t10 = /* @__PURE__ */ jsxs("div", { className: t5, children: [
559
- t6,
560
- t9
561
- ] });
562
- $[9] = t5;
563
- $[10] = t9;
564
- $[11] = t10;
565
- } else {
566
- t10 = $[11];
567
- }
568
- return t10;
569
- }
570
- let t3;
571
- if ($[12] !== embedded || $[13] !== fixedHeight || $[14] !== hasComparison || $[15] !== hasDateRange || $[16] !== hasIcon) {
572
- t3 = /* @__PURE__ */ jsx(StandardSkeleton, { hasComparison, hasIcon, hasDateRange, embedded, fixedHeight });
573
- $[12] = embedded;
574
- $[13] = fixedHeight;
575
- $[14] = hasComparison;
576
- $[15] = hasDateRange;
577
- $[16] = hasIcon;
578
- $[17] = t3;
579
- } else {
580
- t3 = $[17];
581
- }
582
- return t3;
255
+ //#endregion
256
+ //#region src/components/InsightWidgetSkeleton.tsx
257
+ /**
258
+ * Skeleton loader for scorecard insight widgets — displays animated
259
+ * shimmer placeholders that exactly match the final rendered layout
260
+ * of InsightsScorecardView for a given config, preventing layout shift.
261
+ *
262
+ * The skeleton receives the scorecard config so it can conditionally
263
+ * render placeholder lines for comparison, dateRange, and icon
264
+ * only when the loaded view will also render them.
265
+ *
266
+ * The standard skeleton mirrors InsightsScorecardView's responsive
267
+ * container-width breakpoints (ResizeObserver → isSmall / isMedium)
268
+ * and uses placeholder heights that exactly match the **computed**
269
+ * Tailwind line-heights (accounting for `leading-*` overrides).
270
+ * This guarantees a pixel-perfect skeleton → loaded transition.
271
+ */
272
+ function InsightWidgetSkeleton({ config, compact = false, embedded = false, fixedHeight }) {
273
+ const hasComparison = Boolean(config.comparison);
274
+ const hasIcon = Boolean(config.icon);
275
+ const hasDateRange = Boolean(config.dateRange);
276
+ if (compact) return /* @__PURE__ */ jsxs("div", {
277
+ className: cls("animate-pulse", embedded ? "h-full px-2.5 py-2" : "flex flex-col gap-0.5 rounded-md bg-transparent border min-w-0 px-2.5 py-2", !embedded && defaultBorderMixin),
278
+ children: [/* @__PURE__ */ jsx("div", {
279
+ className: "bg-surface-200 dark:bg-surface-700 rounded-sm",
280
+ style: {
281
+ height: 14,
282
+ width: 48
283
+ }
284
+ }), /* @__PURE__ */ jsxs("div", {
285
+ className: "flex items-baseline gap-1.5",
286
+ children: [/* @__PURE__ */ jsx("div", {
287
+ className: "bg-surface-200 dark:bg-surface-700 rounded-sm",
288
+ style: {
289
+ height: 20,
290
+ width: 40
291
+ }
292
+ }), hasComparison && /* @__PURE__ */ jsx("div", {
293
+ className: "bg-surface-200/60 dark:bg-surface-700/60 rounded-sm",
294
+ style: {
295
+ height: 14,
296
+ width: 28
297
+ }
298
+ })]
299
+ })]
300
+ });
301
+ return /* @__PURE__ */ jsx(StandardSkeleton, {
302
+ hasComparison,
303
+ hasIcon,
304
+ hasDateRange,
305
+ embedded,
306
+ fixedHeight
307
+ });
583
308
  }
584
- function StandardSkeleton(t0) {
585
- const $ = c(37);
586
- const {
587
- hasComparison,
588
- hasIcon,
589
- hasDateRange,
590
- embedded,
591
- fixedHeight
592
- } = t0;
593
- const containerRef = useRef(null);
594
- const [containerWidth, setContainerWidth] = useState(null);
595
- let t1;
596
- let t2;
597
- if ($[0] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
598
- t1 = () => {
599
- if (!containerRef.current) {
600
- return;
601
- }
602
- setContainerWidth(containerRef.current.offsetWidth);
603
- const observer = new ResizeObserver((entries) => {
604
- for (const entry of entries) {
605
- setContainerWidth(entry.contentRect.width);
606
- }
607
- });
608
- observer.observe(containerRef.current);
609
- return () => observer.disconnect();
610
- };
611
- t2 = [];
612
- $[0] = t1;
613
- $[1] = t2;
614
- } else {
615
- t1 = $[0];
616
- t2 = $[1];
617
- }
618
- React.useLayoutEffect(t1, t2);
619
- const isSmall = containerWidth !== null && containerWidth < 200;
620
- const titleHeight = isSmall ? 15 : 16.5;
621
- const valueHeight = isSmall ? 22.5 : containerWidth !== null && containerWidth < 300 ? 25 : 30;
622
- const iconSize = isSmall ? 14 : 18;
623
- let t3;
624
- let t4;
625
- if ($[2] !== embedded || $[3] !== isSmall) {
626
- const baseClass = embedded ? `flex flex-col min-w-0 h-full ${isSmall ? "px-3.5 py-3" : "px-5 py-4"}` : cls("rounded-lg flex flex-col min-w-0 bg-transparent border", defaultBorderMixin, isSmall ? "px-3.5 py-3" : "px-5 py-4");
627
- t3 = containerRef;
628
- t4 = cls("animate-pulse", baseClass);
629
- $[2] = embedded;
630
- $[3] = isSmall;
631
- $[4] = t3;
632
- $[5] = t4;
633
- } else {
634
- t3 = $[4];
635
- t4 = $[5];
636
- }
637
- let t5;
638
- if ($[6] !== embedded || $[7] !== fixedHeight || $[8] !== isSmall) {
639
- t5 = embedded ? void 0 : fixedHeight ? {
640
- height: fixedHeight
641
- } : {
642
- minHeight: isSmall ? 68 : 92
643
- };
644
- $[6] = embedded;
645
- $[7] = fixedHeight;
646
- $[8] = isSmall;
647
- $[9] = t5;
648
- } else {
649
- t5 = $[9];
650
- }
651
- const t6 = `flex items-center justify-between ${isSmall ? "mb-1" : "mb-2"}`;
652
- let t7;
653
- if ($[10] !== titleHeight) {
654
- t7 = /* @__PURE__ */ jsx("div", { className: "bg-surface-200 dark:bg-surface-700 rounded", style: {
655
- height: titleHeight,
656
- width: "60%"
657
- } });
658
- $[10] = titleHeight;
659
- $[11] = t7;
660
- } else {
661
- t7 = $[11];
662
- }
663
- let t8;
664
- if ($[12] !== hasDateRange || $[13] !== isSmall) {
665
- t8 = hasDateRange && !isSmall && /* @__PURE__ */ jsx("div", { className: "bg-surface-200/60 dark:bg-surface-700/60 rounded mt-0.5", style: {
666
- height: 14,
667
- width: "40%"
668
- } });
669
- $[12] = hasDateRange;
670
- $[13] = isSmall;
671
- $[14] = t8;
672
- } else {
673
- t8 = $[14];
674
- }
675
- let t9;
676
- if ($[15] !== t7 || $[16] !== t8) {
677
- t9 = /* @__PURE__ */ jsxs("div", { className: "flex flex-col min-w-0", children: [
678
- t7,
679
- t8
680
- ] });
681
- $[15] = t7;
682
- $[16] = t8;
683
- $[17] = t9;
684
- } else {
685
- t9 = $[17];
686
- }
687
- let t10;
688
- if ($[18] !== hasIcon || $[19] !== iconSize) {
689
- t10 = hasIcon && /* @__PURE__ */ jsx("span", { className: "ml-2 shrink-0", children: /* @__PURE__ */ jsx("div", { className: "bg-surface-200 dark:bg-surface-700 rounded", style: {
690
- height: iconSize,
691
- width: iconSize
692
- } }) });
693
- $[18] = hasIcon;
694
- $[19] = iconSize;
695
- $[20] = t10;
696
- } else {
697
- t10 = $[20];
698
- }
699
- let t11;
700
- if ($[21] !== t10 || $[22] !== t6 || $[23] !== t9) {
701
- t11 = /* @__PURE__ */ jsxs("div", { className: t6, children: [
702
- t9,
703
- t10
704
- ] });
705
- $[21] = t10;
706
- $[22] = t6;
707
- $[23] = t9;
708
- $[24] = t11;
709
- } else {
710
- t11 = $[24];
711
- }
712
- let t12;
713
- if ($[25] !== valueHeight) {
714
- t12 = /* @__PURE__ */ jsx("div", { className: "bg-surface-200 dark:bg-surface-700 rounded", style: {
715
- height: valueHeight,
716
- width: "40%"
717
- } });
718
- $[25] = valueHeight;
719
- $[26] = t12;
720
- } else {
721
- t12 = $[26];
722
- }
723
- let t13;
724
- if ($[27] !== hasComparison || $[28] !== isSmall) {
725
- t13 = hasComparison && /* @__PURE__ */ jsx("div", { className: isSmall ? "mt-0.5" : "mt-1", children: /* @__PURE__ */ jsx("div", { className: "bg-surface-200/60 dark:bg-surface-700/60 rounded", style: {
726
- height: 16,
727
- width: "25%"
728
- } }) });
729
- $[27] = hasComparison;
730
- $[28] = isSmall;
731
- $[29] = t13;
732
- } else {
733
- t13 = $[29];
734
- }
735
- let t14;
736
- if ($[30] !== t11 || $[31] !== t12 || $[32] !== t13 || $[33] !== t3 || $[34] !== t4 || $[35] !== t5) {
737
- t14 = /* @__PURE__ */ jsxs("div", { ref: t3, className: t4, style: t5, children: [
738
- t11,
739
- t12,
740
- t13
741
- ] });
742
- $[30] = t11;
743
- $[31] = t12;
744
- $[32] = t13;
745
- $[33] = t3;
746
- $[34] = t4;
747
- $[35] = t5;
748
- $[36] = t14;
749
- } else {
750
- t14 = $[36];
751
- }
752
- return t14;
309
+ /**
310
+ * Inner component for the standard scorecard skeleton.
311
+ *
312
+ * Mirrors InsightsScorecardView's layout by:
313
+ * 1. Using the same ResizeObserver + containerWidth pattern for
314
+ * responsive breakpoints (isSmall < 200px, isMedium < 300px).
315
+ * 2. Using placeholder heights derived from the exact computed
316
+ * Tailwind line-heights that InsightsScorecardView renders.
317
+ * 3. Matching all container classes, margins, paddings, and flex
318
+ * layout properties identically.
319
+ */
320
+ function StandardSkeleton({ hasComparison, hasIcon, hasDateRange, embedded, fixedHeight }) {
321
+ const containerRef = useRef(null);
322
+ const [containerWidth, setContainerWidth] = useState(null);
323
+ React.useLayoutEffect(() => {
324
+ if (!containerRef.current) return;
325
+ setContainerWidth(containerRef.current.offsetWidth);
326
+ const observer = new ResizeObserver((entries) => {
327
+ for (const entry of entries) setContainerWidth(entry.contentRect.width);
328
+ });
329
+ observer.observe(containerRef.current);
330
+ return () => observer.disconnect();
331
+ }, []);
332
+ const isSmall = containerWidth !== null && containerWidth < 200;
333
+ const titleHeight = isSmall ? 15 : 16.5;
334
+ const valueHeight = isSmall ? 22.5 : containerWidth !== null && containerWidth < 300 ? 25 : 30;
335
+ const comparisonHeight = 16;
336
+ const iconSize = isSmall ? 14 : 18;
337
+ return /* @__PURE__ */ jsxs("div", {
338
+ ref: containerRef,
339
+ className: cls("animate-pulse", embedded ? `flex flex-col min-w-0 h-full ${isSmall ? "px-3.5 py-3" : "px-5 py-4"}` : cls("rounded-lg flex flex-col min-w-0 bg-transparent border", defaultBorderMixin, isSmall ? "px-3.5 py-3" : "px-5 py-4")),
340
+ style: embedded ? void 0 : fixedHeight ? { height: fixedHeight } : { minHeight: isSmall ? 68 : 92 },
341
+ children: [
342
+ /* @__PURE__ */ jsxs("div", {
343
+ className: `flex items-center justify-between ${isSmall ? "mb-1" : "mb-2"}`,
344
+ children: [/* @__PURE__ */ jsxs("div", {
345
+ className: "flex flex-col min-w-0",
346
+ children: [/* @__PURE__ */ jsx("div", {
347
+ className: "bg-surface-200 dark:bg-surface-700 rounded",
348
+ style: {
349
+ height: titleHeight,
350
+ width: "60%"
351
+ }
352
+ }), hasDateRange && !isSmall && /* @__PURE__ */ jsx("div", {
353
+ className: "bg-surface-200/60 dark:bg-surface-700/60 rounded mt-0.5",
354
+ style: {
355
+ height: 14,
356
+ width: "40%"
357
+ }
358
+ })]
359
+ }), hasIcon && /* @__PURE__ */ jsx("span", {
360
+ className: "ml-2 shrink-0",
361
+ children: /* @__PURE__ */ jsx("div", {
362
+ className: "bg-surface-200 dark:bg-surface-700 rounded",
363
+ style: {
364
+ height: iconSize,
365
+ width: iconSize
366
+ }
367
+ })
368
+ })]
369
+ }),
370
+ /* @__PURE__ */ jsx("div", {
371
+ className: "bg-surface-200 dark:bg-surface-700 rounded",
372
+ style: {
373
+ height: valueHeight,
374
+ width: "40%"
375
+ }
376
+ }),
377
+ hasComparison && /* @__PURE__ */ jsx("div", {
378
+ className: isSmall ? "mt-0.5" : "mt-1",
379
+ children: /* @__PURE__ */ jsx("div", {
380
+ className: "bg-surface-200/60 dark:bg-surface-700/60 rounded",
381
+ style: {
382
+ height: comparisonHeight,
383
+ width: "25%"
384
+ }
385
+ })
386
+ })
387
+ ]
388
+ });
753
389
  }
754
390
  InsightWidgetSkeleton.displayName = "InsightWidgetSkeleton";
391
+ //#endregion
392
+ //#region src/components/InsightWidget.tsx
393
+ /**
394
+ * Compute a deterministic fixed height for a standard scorecard based
395
+ * on which optional elements the config declares. This eliminates
396
+ * layout shift between skeleton and loaded states.
397
+ *
398
+ * Breakdown (non-compact, non-small):
399
+ * py-4 padding: 16 + 16 = 32
400
+ * title row: 16.5 (text-xs leading-snug)
401
+ * mb-2 margin: 8
402
+ * value: 30 (text-2xl leading-tight)
403
+ * ---
404
+ * base: 86.5
405
+ * + dateRange: +16 (14px text + 2px mt-0.5)
406
+ * + comparison: +20 (16px text + 4px mt-1)
407
+ */
755
408
  function computeFixedHeight(config) {
756
- let h = 86.5;
757
- if (config.dateRange) h += 16;
758
- if (config.comparison) h += 20;
759
- return Math.ceil(h);
409
+ let h = 86.5;
410
+ if (config.dateRange) h += 16;
411
+ if (config.comparison) h += 20;
412
+ return Math.ceil(h);
760
413
  }
761
- function InsightWidget(t0) {
762
- const $ = c(37);
763
- const {
764
- definition,
765
- collectionSlug,
766
- path,
767
- parentCollectionSlugs,
768
- compact: t1,
769
- embedded: t2
770
- } = t0;
771
- const compact = t1 === void 0 ? false : t1;
772
- const embedded = t2 === void 0 ? false : t2;
773
- let t3;
774
- if ($[0] !== collectionSlug || $[1] !== parentCollectionSlugs || $[2] !== path) {
775
- t3 = {
776
- path,
777
- collectionSlug,
778
- parentCollectionSlugs
779
- };
780
- $[0] = collectionSlug;
781
- $[1] = parentCollectionSlugs;
782
- $[2] = path;
783
- $[3] = t3;
784
- } else {
785
- t3 = $[3];
786
- }
787
- const {
788
- data,
789
- loading,
790
- error
791
- } = useInsightsData(definition, t3);
792
- let t4;
793
- if ($[4] !== compact || $[5] !== definition.scorecard || $[6] !== embedded) {
794
- t4 = !compact && !embedded ? computeFixedHeight(definition.scorecard) : void 0;
795
- $[4] = compact;
796
- $[5] = definition.scorecard;
797
- $[6] = embedded;
798
- $[7] = t4;
799
- } else {
800
- t4 = $[7];
801
- }
802
- const fixedHeight = t4;
803
- if (loading) {
804
- let t52;
805
- if ($[8] !== compact || $[9] !== definition.scorecard || $[10] !== embedded || $[11] !== fixedHeight) {
806
- t52 = /* @__PURE__ */ jsx(InsightWidgetSkeleton, { config: definition.scorecard, compact, embedded, fixedHeight });
807
- $[8] = compact;
808
- $[9] = definition.scorecard;
809
- $[10] = embedded;
810
- $[11] = fixedHeight;
811
- $[12] = t52;
812
- } else {
813
- t52 = $[12];
814
- }
815
- return t52;
816
- }
817
- if (error) {
818
- const t52 = `text-red-500/70 dark:text-red-400/70 text-[0.8125rem] ${embedded ? "px-5 py-4 h-full" : `rounded-lg bg-red-500/5 dark:bg-red-400/5 border border-red-500/10 dark:border-red-400/10 ${compact ? "px-3.5 py-3" : "px-5 py-4"}`}`;
819
- let t62;
820
- if ($[13] !== fixedHeight) {
821
- t62 = fixedHeight ? {
822
- height: fixedHeight
823
- } : void 0;
824
- $[13] = fixedHeight;
825
- $[14] = t62;
826
- } else {
827
- t62 = $[14];
828
- }
829
- let t7;
830
- if ($[15] !== definition.title) {
831
- t7 = /* @__PURE__ */ jsx("div", { className: "font-semibold mb-1", children: definition.title });
832
- $[15] = definition.title;
833
- $[16] = t7;
834
- } else {
835
- t7 = $[16];
836
- }
837
- let t8;
838
- if ($[17] !== error.message) {
839
- t8 = /* @__PURE__ */ jsx("div", { children: error.message });
840
- $[17] = error.message;
841
- $[18] = t8;
842
- } else {
843
- t8 = $[18];
844
- }
845
- let t9;
846
- if ($[19] !== t52 || $[20] !== t62 || $[21] !== t7 || $[22] !== t8) {
847
- t9 = /* @__PURE__ */ jsxs("div", { className: t52, style: t62, children: [
848
- t7,
849
- t8
850
- ] });
851
- $[19] = t52;
852
- $[20] = t62;
853
- $[21] = t7;
854
- $[22] = t8;
855
- $[23] = t9;
856
- } else {
857
- t9 = $[23];
858
- }
859
- return t9;
860
- }
861
- if (!data || data.rows.length === 0) {
862
- const t52 = `text-surface-400 dark:text-surface-500 text-[0.8125rem] ${embedded ? "px-5 py-4 h-full" : `rounded-lg bg-surface-100 dark:bg-surface-800 border border-surface-200 dark:border-surface-700 ${compact ? "px-3.5 py-3" : "px-5 py-4"}`}`;
863
- let t62;
864
- if ($[24] !== fixedHeight) {
865
- t62 = fixedHeight ? {
866
- height: fixedHeight
867
- } : void 0;
868
- $[24] = fixedHeight;
869
- $[25] = t62;
870
- } else {
871
- t62 = $[25];
872
- }
873
- let t7;
874
- if ($[26] !== definition.title || $[27] !== t52 || $[28] !== t62) {
875
- t7 = /* @__PURE__ */ jsxs("div", { className: t52, style: t62, children: [
876
- definition.title,
877
- " — No data"
878
- ] });
879
- $[26] = definition.title;
880
- $[27] = t52;
881
- $[28] = t62;
882
- $[29] = t7;
883
- } else {
884
- t7 = $[29];
885
- }
886
- return t7;
887
- }
888
- const t5 = data.rows[0];
889
- let t6;
890
- if ($[30] !== compact || $[31] !== definition.scorecard || $[32] !== definition.title || $[33] !== embedded || $[34] !== fixedHeight || $[35] !== t5) {
891
- t6 = /* @__PURE__ */ jsx(InsightsScorecardView, { config: definition.scorecard, data: t5, title: definition.title, compact, embedded, fixedHeight });
892
- $[30] = compact;
893
- $[31] = definition.scorecard;
894
- $[32] = definition.title;
895
- $[33] = embedded;
896
- $[34] = fixedHeight;
897
- $[35] = t5;
898
- $[36] = t6;
899
- } else {
900
- t6 = $[36];
901
- }
902
- return t6;
414
+ /**
415
+ * Single insight widget orchestrator.
416
+ *
417
+ * Wraps skeleton and loaded states in a fixed-height container
418
+ * (computed from the scorecard config) to prevent layout shift.
419
+ *
420
+ * All theme-awareness is handled via Tailwind `dark:` classes.
421
+ */
422
+ function InsightWidget({ definition, collectionSlug, path, parentCollectionSlugs, parentEntityIds, compact = false, embedded = false }) {
423
+ const { data, loading, error } = useInsightsData(definition, {
424
+ path,
425
+ collectionSlug,
426
+ parentCollectionSlugs
427
+ });
428
+ const fixedHeight = !compact && !embedded ? computeFixedHeight(definition.scorecard) : void 0;
429
+ if (loading) return /* @__PURE__ */ jsx(InsightWidgetSkeleton, {
430
+ config: definition.scorecard,
431
+ compact,
432
+ embedded,
433
+ fixedHeight
434
+ });
435
+ if (error) return /* @__PURE__ */ jsxs("div", {
436
+ className: `text-red-500/70 dark:text-red-400/70 text-[0.8125rem] ${embedded ? "px-5 py-4 h-full" : `rounded-lg bg-red-500/5 dark:bg-red-400/5 border border-red-500/10 dark:border-red-400/10 ${compact ? "px-3.5 py-3" : "px-5 py-4"}`}`,
437
+ style: fixedHeight ? { height: fixedHeight } : void 0,
438
+ children: [/* @__PURE__ */ jsx("div", {
439
+ className: "font-semibold mb-1",
440
+ children: definition.title
441
+ }), /* @__PURE__ */ jsx("div", { children: error.message })]
442
+ });
443
+ if (!data || data.rows.length === 0) return /* @__PURE__ */ jsxs("div", {
444
+ className: `text-surface-400 dark:text-surface-500 text-[0.8125rem] ${embedded ? "px-5 py-4 h-full" : `rounded-lg bg-surface-100 dark:bg-surface-800 border border-surface-200 dark:border-surface-700 ${compact ? "px-3.5 py-3" : "px-5 py-4"}`}`,
445
+ style: fixedHeight ? { height: fixedHeight } : void 0,
446
+ children: [definition.title, " No data"]
447
+ });
448
+ return /* @__PURE__ */ jsx(InsightsScorecardView, {
449
+ config: definition.scorecard,
450
+ data: data.rows[0],
451
+ title: definition.title,
452
+ compact,
453
+ embedded,
454
+ fixedHeight
455
+ });
903
456
  }
904
457
  InsightWidget.displayName = "InsightWidget";
905
- function HomeCardInsightSlot(t0) {
906
- const $ = c(10);
907
- const {
908
- slug,
909
- insights
910
- } = t0;
911
- if (!insights || insights.length === 0) {
912
- return null;
913
- }
914
- const estimatedRows = Math.ceil(insights.length / 2);
915
- const minHeight = estimatedRows * 42 + (estimatedRows - 1) * 6;
916
- let t1;
917
- if ($[0] !== minHeight) {
918
- t1 = {
919
- minHeight
920
- };
921
- $[0] = minHeight;
922
- $[1] = t1;
923
- } else {
924
- t1 = $[1];
925
- }
926
- let t2;
927
- if ($[2] !== insights || $[3] !== slug) {
928
- let t32;
929
- if ($[5] !== slug) {
930
- t32 = (def) => /* @__PURE__ */ jsx(InsightWidget, { definition: def, collectionSlug: slug, compact: true }, def.id);
931
- $[5] = slug;
932
- $[6] = t32;
933
- } else {
934
- t32 = $[6];
935
- }
936
- t2 = insights.map(t32);
937
- $[2] = insights;
938
- $[3] = slug;
939
- $[4] = t2;
940
- } else {
941
- t2 = $[4];
942
- }
943
- let t3;
944
- if ($[7] !== t1 || $[8] !== t2) {
945
- t3 = /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-1.5 mt-2", style: t1, children: t2 });
946
- $[7] = t1;
947
- $[8] = t2;
948
- $[9] = t3;
949
- } else {
950
- t3 = $[9];
951
- }
952
- return t3;
458
+ //#endregion
459
+ //#region src/components/HomeCardInsightSlot.tsx
460
+ /**
461
+ * Renders compact insight widgets inline within a home page collection card.
462
+ * Injected via the `home.card.insight` slot.
463
+ *
464
+ * Uses a horizontal flex layout so multiple cards sit side by side.
465
+ */
466
+ function HomeCardInsightSlot({ slug, insights }) {
467
+ if (!insights || insights.length === 0) return null;
468
+ const estimatedRows = Math.ceil(insights.length / 2);
469
+ return /* @__PURE__ */ jsx("div", {
470
+ className: "flex flex-wrap items-center gap-1.5 mt-2",
471
+ style: { minHeight: estimatedRows * 42 + (estimatedRows - 1) * 6 },
472
+ children: insights.map((def) => /* @__PURE__ */ jsx(InsightWidget, {
473
+ definition: def,
474
+ collectionSlug: slug,
475
+ compact: true
476
+ }, def.id))
477
+ });
953
478
  }
954
479
  HomeCardInsightSlot.displayName = "HomeCardInsightSlot";
955
- function HomeInsightsSlot(t0) {
956
- const $ = c(5);
957
- const {
958
- insights
959
- } = t0;
960
- if (!insights || insights.length === 0) {
961
- return null;
962
- }
963
- let t1;
964
- if ($[0] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
965
- t1 = {
966
- minHeight: 92
967
- };
968
- $[0] = t1;
969
- } else {
970
- t1 = $[0];
971
- }
972
- let t2;
973
- if ($[1] !== insights) {
974
- t2 = insights.map(_temp);
975
- $[1] = insights;
976
- $[2] = t2;
977
- } else {
978
- t2 = $[2];
979
- }
980
- let t3;
981
- if ($[3] !== t2) {
982
- t3 = /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-3 pb-6", style: t1, children: t2 });
983
- $[3] = t2;
984
- $[4] = t3;
985
- } else {
986
- t3 = $[4];
987
- }
988
- return t3;
989
- }
990
- function _temp(def) {
991
- return /* @__PURE__ */ jsx(InsightWidget, { definition: def }, def.id);
480
+ //#endregion
481
+ //#region src/components/HomeInsightsSlot.tsx
482
+ /**
483
+ * Scorecard insights panel rendered at the top of the home page.
484
+ * Injected via the `home.children.start` slot.
485
+ *
486
+ * Renders scorecards in a responsive grid (up to 4 columns).
487
+ */
488
+ function HomeInsightsSlot({ insights }) {
489
+ if (!insights || insights.length === 0) return null;
490
+ return /* @__PURE__ */ jsx("div", {
491
+ className: "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-3 pb-6",
492
+ style: { minHeight: 92 },
493
+ children: insights.map((def) => /* @__PURE__ */ jsx(InsightWidget, { definition: def }, def.id))
494
+ });
992
495
  }
993
496
  HomeInsightsSlot.displayName = "HomeInsightsSlot";
994
- function CollectionInsightsInline(t0) {
995
- const $ = c(11);
996
- const {
997
- insights,
998
- path,
999
- parentCollectionSlugs,
1000
- parentEntityIds
1001
- } = t0;
1002
- if (!insights || insights.length === 0) {
1003
- return null;
1004
- }
1005
- let t1;
1006
- if ($[0] !== insights || $[1] !== parentCollectionSlugs || $[2] !== parentEntityIds || $[3] !== path) {
1007
- let t22;
1008
- if ($[5] !== parentCollectionSlugs || $[6] !== parentEntityIds || $[7] !== path) {
1009
- t22 = (def) => /* @__PURE__ */ jsx(InsightWidget, { definition: def, path, parentCollectionSlugs, parentEntityIds }, def.id);
1010
- $[5] = parentCollectionSlugs;
1011
- $[6] = parentEntityIds;
1012
- $[7] = path;
1013
- $[8] = t22;
1014
- } else {
1015
- t22 = $[8];
1016
- }
1017
- t1 = insights.map(t22);
1018
- $[0] = insights;
1019
- $[1] = parentCollectionSlugs;
1020
- $[2] = parentEntityIds;
1021
- $[3] = path;
1022
- $[4] = t1;
1023
- } else {
1024
- t1 = $[4];
1025
- }
1026
- let t2;
1027
- if ($[9] !== t1) {
1028
- t2 = /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-3 pb-4", children: t1 });
1029
- $[9] = t1;
1030
- $[10] = t2;
1031
- } else {
1032
- t2 = $[10];
1033
- }
1034
- return t2;
497
+ //#endregion
498
+ //#region src/components/CollectionInsightsInline.tsx
499
+ /**
500
+ * Renders scorecard insight widgets inline within a collection's list view,
501
+ * positioned below the title and above the main data list.
502
+ *
503
+ * Injected via the `collection.insights` slot.
504
+ */
505
+ function CollectionInsightsInline({ insights, path, parentCollectionSlugs, parentEntityIds }) {
506
+ if (!insights || insights.length === 0) return null;
507
+ return /* @__PURE__ */ jsx("div", {
508
+ className: "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-3 pb-4",
509
+ children: insights.map((def) => /* @__PURE__ */ jsx(InsightWidget, {
510
+ definition: def,
511
+ path,
512
+ parentCollectionSlugs,
513
+ parentEntityIds
514
+ }, def.id))
515
+ });
1035
516
  }
1036
517
  CollectionInsightsInline.displayName = "CollectionInsightsInline";
518
+ //#endregion
519
+ //#region src/useInsightsPlugin.tsx
520
+ /**
521
+ * Creates the Insights plugin for Rebase.
522
+ *
523
+ * This plugin injects scorecard widgets into key UI locations:
524
+ * - **Home page header**: KPI overview via `home.children.start` slot
525
+ * - **Collection list view**: Scorecards inline (below title, above list) via `collection.insights` slot
526
+ * - **Home page cards**: Compact scorecard metrics auto-extracted from collection insights via `home.card.insight` slot
527
+ *
528
+ * Collection-level insights (`collections.<slug>`) are the single source of truth:
529
+ * scorecards render in the collection list view and are automatically extracted
530
+ * to show as compact widgets on the corresponding home page card.
531
+ *
532
+ * Each insight owns its own `data()` callback — use the Rebase client SDK,
533
+ * call a custom function, or hit any external API. Full flexibility, zero new endpoints.
534
+ *
535
+ * @example
536
+ * ```typescript
537
+ * import { useInsightsPlugin } from "@rebasepro/plugin-insights";
538
+ *
539
+ * const insightsPlugin = useInsightsPlugin({
540
+ * cacheTTL: 120_000,
541
+ * insights: {
542
+ * home: [
543
+ * { id: "revenue", title: "Revenue", data: async () => ..., scorecard: { ... } },
544
+ * ],
545
+ * collections: {
546
+ * orders: [
547
+ * { id: "total", title: "Total Orders", data: async () => ..., scorecard: { ... } },
548
+ * ],
549
+ * },
550
+ * },
551
+ * });
552
+ * ```
553
+ */
1037
554
  function useInsightsPlugin(config) {
1038
- const {
1039
- insights,
1040
- cacheTTL
1041
- } = config;
1042
- const slots = [];
1043
- if (insights.home && insights.home.length > 0) {
1044
- const homeInsights = insights.home;
1045
- slots.push({
1046
- slot: "home.children.start",
1047
- Component: (props) => /* @__PURE__ */ jsx(HomeInsightsSlot, { ...props, insights: homeInsights }),
1048
- order: 10
1049
- });
1050
- }
1051
- if (insights.collections) {
1052
- for (const [slug, defs] of Object.entries(insights.collections)) {
1053
- if (defs.length === 0) continue;
1054
- const collectionInsights = defs;
1055
- slots.push({
1056
- slot: "collection.insights",
1057
- Component: (props) => {
1058
- const path = props.path;
1059
- const collectionSlug = path?.split("/").filter(Boolean).pop() ?? "";
1060
- if (collectionSlug !== slug) return null;
1061
- return /* @__PURE__ */ jsx(CollectionInsightsInline, { ...props, insights: collectionInsights });
1062
- },
1063
- order: 10
1064
- });
1065
- slots.push({
1066
- slot: "home.card.insight",
1067
- Component: (props) => {
1068
- const cardSlug = props.slug;
1069
- if (cardSlug !== slug) return null;
1070
- return /* @__PURE__ */ jsx(HomeCardInsightSlot, { ...props, insights: collectionInsights });
1071
- },
1072
- order: 10
1073
- });
1074
- }
1075
- }
1076
- return {
1077
- key: "plugin-insights",
1078
- slots,
1079
- providers: [{
1080
- scope: "root",
1081
- Component: InsightsProvider,
1082
- props: {
1083
- cacheTTL
1084
- }
1085
- }]
1086
- };
555
+ const { insights, cacheTTL } = config;
556
+ return React.useMemo(() => {
557
+ const slots = [];
558
+ if (insights.home && insights.home.length > 0) {
559
+ const homeInsights = insights.home;
560
+ slots.push({
561
+ slot: "home.children.start",
562
+ Component: (props) => /* @__PURE__ */ jsx(HomeInsightsSlot, {
563
+ ...props,
564
+ insights: homeInsights
565
+ }),
566
+ order: 10
567
+ });
568
+ }
569
+ if (insights.collections) for (const [slug, defs] of Object.entries(insights.collections)) {
570
+ if (defs.length === 0) continue;
571
+ const collectionInsights = defs;
572
+ slots.push({
573
+ slot: "collection.insights",
574
+ Component: (props) => {
575
+ if ((props.path?.split("/").filter(Boolean).pop() ?? "") !== slug) return null;
576
+ return /* @__PURE__ */ jsx(CollectionInsightsInline, {
577
+ ...props,
578
+ insights: collectionInsights
579
+ });
580
+ },
581
+ order: 10
582
+ });
583
+ slots.push({
584
+ slot: "home.card.insight",
585
+ Component: (props) => {
586
+ if (props.slug !== slug) return null;
587
+ return /* @__PURE__ */ jsx(HomeCardInsightSlot, {
588
+ ...props,
589
+ insights: collectionInsights
590
+ });
591
+ },
592
+ order: 10
593
+ });
594
+ }
595
+ return {
596
+ key: "plugin-insights",
597
+ slots,
598
+ providers: [{
599
+ scope: "root",
600
+ Component: InsightsProvider,
601
+ props: { cacheTTL }
602
+ }]
603
+ };
604
+ }, [insights, cacheTTL]);
1087
605
  }
1088
- export {
1089
- InsightWidget,
1090
- InsightWidgetSkeleton,
1091
- InsightsCache,
1092
- InsightsProvider,
1093
- InsightsScorecardView,
1094
- useInsightsData,
1095
- useInsightsEngine,
1096
- useInsightsPlugin
1097
- };
1098
- //# sourceMappingURL=index.es.js.map
606
+ //#endregion
607
+ export { InsightWidget, InsightWidgetSkeleton, InsightsCache, InsightsProvider, InsightsScorecardView, useInsightsData, useInsightsEngine, useInsightsPlugin };
608
+
609
+ //# sourceMappingURL=index.es.js.map