@oneuptime/common 11.7.1 → 11.7.3

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 (285) hide show
  1. package/Models/AnalyticsModels/KubernetesCostAllocation.ts +103 -0
  2. package/Models/DatabaseModels/DashboardDomain.ts +1 -0
  3. package/Models/DatabaseModels/Incident.ts +2 -0
  4. package/Models/DatabaseModels/IncidentEpisode.ts +1 -0
  5. package/Models/DatabaseModels/IncidentEpisodePublicNote.ts +1 -0
  6. package/Models/DatabaseModels/IncidentEpisodeStateTimeline.ts +1 -0
  7. package/Models/DatabaseModels/IncidentPublicNote.ts +1 -0
  8. package/Models/DatabaseModels/IncidentStateTimeline.ts +1 -0
  9. package/Models/DatabaseModels/Monitor.ts +1 -0
  10. package/Models/DatabaseModels/MonitorStatusTimeline.ts +1 -0
  11. package/Models/DatabaseModels/NetworkSite.ts +1 -0
  12. package/Models/DatabaseModels/NetworkSiteStatusTimeline.ts +1 -0
  13. package/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.ts +1 -0
  14. package/Models/DatabaseModels/OnCallDutyPolicySchedule.ts +2 -0
  15. package/Models/DatabaseModels/Probe.ts +13 -13
  16. package/Models/DatabaseModels/ScheduledMaintenance.ts +1 -0
  17. package/Models/DatabaseModels/ScheduledMaintenancePublicNote.ts +1 -0
  18. package/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.ts +1 -0
  19. package/Models/DatabaseModels/ScheduledMaintenanceTemplate.ts +1 -0
  20. package/Models/DatabaseModels/StatusPage.ts +1 -0
  21. package/Models/DatabaseModels/StatusPageAnnouncement.ts +1 -0
  22. package/Models/DatabaseModels/StatusPageDomain.ts +1 -0
  23. package/Models/DatabaseModels/TableView.ts +40 -0
  24. package/Models/DatabaseModels/WorkspaceNotificationSummary.ts +1 -0
  25. package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +11 -0
  26. package/Server/API/BaseAPI.ts +17 -1
  27. package/Server/API/CommonAPI.ts +32 -0
  28. package/Server/API/MicrosoftTeamsAPI.ts +99 -2
  29. package/Server/API/ProbeAPI.ts +2 -0
  30. package/Server/API/SlackAPI.ts +62 -0
  31. package/Server/API/StatusPageAPI.ts +157 -192
  32. package/Server/Infrastructure/Postgres/SchemaMigrations/1785140242697-AddHotQueryIndexes.ts +89 -0
  33. package/Server/Infrastructure/Postgres/SchemaMigrations/1785148065137-AddHotQueryIndexesSecondPass.ts +61 -0
  34. package/Server/Infrastructure/Postgres/SchemaMigrations/1785240000000-RepairCrossProjectMonitorStatusReferences.ts +280 -0
  35. package/Server/Infrastructure/Postgres/SchemaMigrations/1785241000000-AddColumnsToTableView.ts +13 -0
  36. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +8 -0
  37. package/Server/Services/DatabaseService.ts +11 -3
  38. package/Server/Services/MonitorGroupService.ts +145 -0
  39. package/Server/Services/MonitorService.ts +231 -30
  40. package/Server/Services/ProbeService.ts +319 -16
  41. package/Server/Services/StatusPageService.ts +72 -42
  42. package/Server/Services/WorkspaceNotificationRuleService.ts +232 -2
  43. package/Server/Utils/Database/PostgresErrorTranslator.ts +131 -0
  44. package/Server/Utils/Monitor/MonitorResource.ts +88 -35
  45. package/Server/Utils/Monitor/MonitorStatusTimeline.ts +21 -1
  46. package/Server/Utils/Monitor/MonitorStepsProjectValidator.ts +158 -0
  47. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +706 -6
  48. package/Tests/Models/AnalyticsModels/KubernetesCostAllocation.test.ts +16 -0
  49. package/Tests/Models/DatabaseModels/ProbeColumnAccessControl.test.ts +103 -0
  50. package/Tests/Server/API/BaseAPIUpdateNoOp.test.ts +135 -0
  51. package/Tests/Server/API/CommonAPIAuthGuard.test.ts +317 -0
  52. package/Tests/Server/API/MicrosoftTeamsManifest.test.ts +228 -0
  53. package/Tests/Server/API/ProbeAPI.test.ts +6 -0
  54. package/Tests/Server/API/ZZVerifyChatsAuth.test.ts +167 -0
  55. package/Tests/Server/Services/AddHotQueryIndexesMigration.test.ts +255 -0
  56. package/Tests/Server/Services/AddHotQueryIndexesSecondPassMigration.test.ts +258 -0
  57. package/Tests/Server/Services/DatabaseServiceSanitizeUpdateData.test.ts +1 -1
  58. package/Tests/Server/Services/DiscoveryScanClaimHookFreeSafety.test.ts +121 -0
  59. package/Tests/Server/Services/HeartbeatWriteFastPathSafety.test.ts +200 -0
  60. package/Tests/Server/Services/HookFreeWriteSafetySecondPass.test.ts +181 -0
  61. package/Tests/Server/Services/MonitorGroupServiceBatchedStatuses.test.ts +513 -0
  62. package/Tests/Server/Services/MonitorResourceProbeAgreementReuse.test.ts +492 -0
  63. package/Tests/Server/Services/MonitorServiceProbeSelection.test.ts +401 -0
  64. package/Tests/Server/Services/MonitorStatusTimelineFastPath.test.ts +453 -0
  65. package/Tests/Server/Services/ProbeAuthKeyCacheAndHeartbeat.test.ts +679 -0
  66. package/Tests/Server/Services/ServiceLevelObjectiveService.test.ts +3 -3
  67. package/Tests/Server/Services/StatusPageDomainResolutionCache.test.ts +245 -0
  68. package/Tests/Server/Services/StatusPageOverviewTimelineWindow.test.ts +283 -0
  69. package/Tests/Server/Services/StatusPageServiceMcp.test.ts +17 -1
  70. package/Tests/Server/Services/StatusPageServiceMonitorGroupBatching.test.ts +331 -0
  71. package/Tests/Server/Services/UserOnCallLogClaimNotificationRule.test.ts +12 -12
  72. package/Tests/Server/Services/WorkspaceNotificationRuleChats.test.ts +1390 -0
  73. package/Tests/Server/Utils/AI/Insights/InsightFixRouting.test.ts +4 -4
  74. package/Tests/Server/Utils/AI/Insights/InsightScanner.test.ts +2 -2
  75. package/Tests/Server/Utils/AI/Insights/InsightStore.test.ts +1 -3
  76. package/Tests/Server/Utils/AI/Insights/InsightStoreHardening.test.ts +1 -3
  77. package/Tests/Server/Utils/AI/Insights/InsightTriage.test.ts +1 -1
  78. package/Tests/Server/Utils/AI/Insights/InsightTriageRunner.test.ts +5 -5
  79. package/Tests/Server/Utils/Alert/AlertPrivacyFilter.test.ts +187 -0
  80. package/Tests/Server/Utils/AlertEpisode/AlertEpisodePrivacyFilter.test.ts +170 -0
  81. package/Tests/Server/Utils/Database/PostgresErrorTranslator.test.ts +125 -0
  82. package/Tests/Server/Utils/Incident/IncidentPrivacyFilter.test.ts +264 -0
  83. package/Tests/Server/Utils/IncidentEpisode/IncidentEpisodePrivacyFilter.test.ts +175 -0
  84. package/Tests/Server/Utils/Monitor/MonitorStepsProjectValidator.test.ts +181 -0
  85. package/Tests/Server/Utils/Monitor/NetworkDeviceWalkUtil.test.ts +1 -1
  86. package/Tests/Server/Utils/Monitor/NetworkInventoryUtil.test.ts +2 -2
  87. package/Tests/Server/Utils/Workspace/MicrosoftTeamsChannelsList.test.ts +341 -0
  88. package/Tests/Server/Utils/Workspace/MicrosoftTeamsChats.test.ts +2175 -0
  89. package/Tests/Server/Utils/Workspace/SlackChannelsList.test.ts +585 -0
  90. package/Tests/Types/Billing/MeteredPlan.test.ts +46 -0
  91. package/Tests/Types/JSONFunctions.test.ts +103 -1
  92. package/Tests/Types/Workspace/NotificationRules/NotificationRuleCondition.test.ts +219 -1
  93. package/Tests/UI/Components/BulkUpdateForm.test.tsx +174 -0
  94. package/Tests/UI/Components/CardModelDetailEdit.test.tsx +315 -0
  95. package/Tests/UI/Components/ModelDetailSelect.test.tsx +229 -0
  96. package/Tests/UI/Components/ModelTable/BaseModelTableBulkSelectAll.test.tsx +1258 -0
  97. package/Tests/UI/Components/ModelTable/BaseModelTableColumnCustomization.test.tsx +679 -0
  98. package/Tests/UI/Components/ModelTable/ColumnCustomizationModal.test.tsx +572 -0
  99. package/Tests/UI/Components/ModelTable/ColumnPreference.test.ts +1678 -0
  100. package/Tests/UI/Components/ModelTable/CustomFieldColumns.test.tsx +1094 -0
  101. package/Tests/UI/Components/ModelTableExportFromColumns.test.ts +153 -0
  102. package/Tests/UI/Components/MonacoLoader.test.ts +67 -0
  103. package/Tests/UI/Components/MonacoRuntime.test.ts +121 -0
  104. package/Tests/UI/Components/TableBulkCsvExport.test.tsx +212 -3
  105. package/Tests/UI/Utils/Project.test.ts +809 -0
  106. package/Tests/UI/Utils/TableColumnsToCsv.test.ts +360 -0
  107. package/Tests/Utils/Alerts/AlertMetricType.test.ts +143 -0
  108. package/Tests/Utils/Dashboard/VariableUrlState.test.ts +219 -0
  109. package/Tests/Utils/Incident/IncidentMetricType.test.ts +174 -0
  110. package/Tests/Utils/Monitor/MonitorProbeSelectionUtil.test.ts +145 -0
  111. package/Tests/Utils/StatusPage/ResourceUptime.test.ts +263 -0
  112. package/Tests/Utils/UserPreferences.test.ts +563 -0
  113. package/Types/JSONFunctions.ts +7 -2
  114. package/Types/Kubernetes/KubernetesCostIngest.ts +37 -1
  115. package/Types/Workspace/NotificationRules/BaseNotificationRule.ts +4 -0
  116. package/Types/Workspace/NotificationRules/NotificationRuleCondition.ts +25 -10
  117. package/Types/Workspace/WorkspaceMessagePayload.ts +1 -0
  118. package/UI/Components/BulkUpdate/BulkUpdateForm.tsx +63 -8
  119. package/UI/Components/CodeEditor/CodeEditor.tsx +3 -0
  120. package/UI/Components/CodeEditor/MonacoLoader.ts +36 -0
  121. package/UI/Components/Forms/ModelForm.tsx +41 -18
  122. package/UI/Components/MasterPage/MasterPage.tsx +65 -0
  123. package/UI/Components/ModelDetail/CardModelDetail.tsx +23 -1
  124. package/UI/Components/ModelDetail/ModelDetail.tsx +46 -1
  125. package/UI/Components/ModelTable/BaseModelTable.tsx +687 -34
  126. package/UI/Components/ModelTable/Column.ts +29 -0
  127. package/UI/Components/ModelTable/ColumnCustomizationModal.tsx +420 -0
  128. package/UI/Components/ModelTable/ColumnPreference.ts +482 -0
  129. package/UI/Components/ModelTable/CustomFieldColumns.tsx +326 -0
  130. package/UI/Components/ModelTable/ExportFromColumns.ts +57 -0
  131. package/UI/Components/ModelTable/TableView.tsx +24 -2
  132. package/UI/Components/ModelTable/useCustomFieldColumns.ts +150 -0
  133. package/UI/Components/Navbar/NavBar.tsx +66 -1
  134. package/UI/Components/SideMenu/SideMenu.tsx +24 -4
  135. package/UI/Components/Table/Table.tsx +66 -14
  136. package/UI/Components/Table/TableRow.tsx +180 -175
  137. package/UI/Components/Table/Types/Column.ts +20 -0
  138. package/UI/Utils/Project.ts +214 -18
  139. package/UI/Utils/TableColumnsToCsv.ts +86 -6
  140. package/UI/esbuild-config.js +34 -0
  141. package/Utils/Monitor/MonitorProbeSelectionUtil.ts +72 -0
  142. package/Utils/UserPreferences.ts +53 -0
  143. package/build/dist/Models/AnalyticsModels/KubernetesCostAllocation.js +86 -0
  144. package/build/dist/Models/AnalyticsModels/KubernetesCostAllocation.js.map +1 -1
  145. package/build/dist/Models/DatabaseModels/DashboardDomain.js +1 -0
  146. package/build/dist/Models/DatabaseModels/DashboardDomain.js.map +1 -1
  147. package/build/dist/Models/DatabaseModels/Incident.js +2 -0
  148. package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
  149. package/build/dist/Models/DatabaseModels/IncidentEpisode.js +1 -0
  150. package/build/dist/Models/DatabaseModels/IncidentEpisode.js.map +1 -1
  151. package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js +1 -0
  152. package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js.map +1 -1
  153. package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js +1 -0
  154. package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js.map +1 -1
  155. package/build/dist/Models/DatabaseModels/IncidentPublicNote.js +1 -0
  156. package/build/dist/Models/DatabaseModels/IncidentPublicNote.js.map +1 -1
  157. package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js +1 -0
  158. package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js.map +1 -1
  159. package/build/dist/Models/DatabaseModels/Monitor.js +1 -0
  160. package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
  161. package/build/dist/Models/DatabaseModels/MonitorStatusTimeline.js +1 -0
  162. package/build/dist/Models/DatabaseModels/MonitorStatusTimeline.js.map +1 -1
  163. package/build/dist/Models/DatabaseModels/NetworkSite.js +1 -0
  164. package/build/dist/Models/DatabaseModels/NetworkSite.js.map +1 -1
  165. package/build/dist/Models/DatabaseModels/NetworkSiteStatusTimeline.js +1 -0
  166. package/build/dist/Models/DatabaseModels/NetworkSiteStatusTimeline.js.map +1 -1
  167. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.js +1 -0
  168. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.js.map +1 -1
  169. package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js +2 -0
  170. package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js.map +1 -1
  171. package/build/dist/Models/DatabaseModels/Probe.js +13 -13
  172. package/build/dist/Models/DatabaseModels/Probe.js.map +1 -1
  173. package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js +1 -0
  174. package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js.map +1 -1
  175. package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js +1 -0
  176. package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js.map +1 -1
  177. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js +1 -0
  178. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js.map +1 -1
  179. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceTemplate.js +1 -0
  180. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceTemplate.js.map +1 -1
  181. package/build/dist/Models/DatabaseModels/StatusPage.js +1 -0
  182. package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
  183. package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js +1 -0
  184. package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js.map +1 -1
  185. package/build/dist/Models/DatabaseModels/StatusPageDomain.js +1 -0
  186. package/build/dist/Models/DatabaseModels/StatusPageDomain.js.map +1 -1
  187. package/build/dist/Models/DatabaseModels/TableView.js +40 -0
  188. package/build/dist/Models/DatabaseModels/TableView.js.map +1 -1
  189. package/build/dist/Models/DatabaseModels/WorkspaceNotificationSummary.js +1 -0
  190. package/build/dist/Models/DatabaseModels/WorkspaceNotificationSummary.js.map +1 -1
  191. package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
  192. package/build/dist/Server/API/BaseAPI.js +12 -1
  193. package/build/dist/Server/API/BaseAPI.js.map +1 -1
  194. package/build/dist/Server/API/CommonAPI.js +21 -0
  195. package/build/dist/Server/API/CommonAPI.js.map +1 -1
  196. package/build/dist/Server/API/MicrosoftTeamsAPI.js +72 -2
  197. package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
  198. package/build/dist/Server/API/ProbeAPI.js +2 -0
  199. package/build/dist/Server/API/ProbeAPI.js.map +1 -1
  200. package/build/dist/Server/API/SlackAPI.js +44 -0
  201. package/build/dist/Server/API/SlackAPI.js.map +1 -1
  202. package/build/dist/Server/API/StatusPageAPI.js +98 -152
  203. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  204. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785140242697-AddHotQueryIndexes.js +36 -0
  205. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785140242697-AddHotQueryIndexes.js.map +1 -0
  206. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785148065137-AddHotQueryIndexesSecondPass.js +26 -0
  207. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785148065137-AddHotQueryIndexesSecondPass.js.map +1 -0
  208. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785240000000-RepairCrossProjectMonitorStatusReferences.js +254 -0
  209. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785240000000-RepairCrossProjectMonitorStatusReferences.js.map +1 -0
  210. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785241000000-AddColumnsToTableView.js +12 -0
  211. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785241000000-AddColumnsToTableView.js.map +1 -0
  212. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +8 -0
  213. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  214. package/build/dist/Server/Services/DatabaseService.js +10 -2
  215. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  216. package/build/dist/Server/Services/MonitorGroupService.js +118 -0
  217. package/build/dist/Server/Services/MonitorGroupService.js.map +1 -1
  218. package/build/dist/Server/Services/MonitorService.js +198 -24
  219. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  220. package/build/dist/Server/Services/ProbeService.js +238 -9
  221. package/build/dist/Server/Services/ProbeService.js.map +1 -1
  222. package/build/dist/Server/Services/StatusPageService.js +46 -39
  223. package/build/dist/Server/Services/StatusPageService.js.map +1 -1
  224. package/build/dist/Server/Services/WorkspaceNotificationRuleService.js +178 -3
  225. package/build/dist/Server/Services/WorkspaceNotificationRuleService.js.map +1 -1
  226. package/build/dist/Server/Utils/Database/PostgresErrorTranslator.js +80 -0
  227. package/build/dist/Server/Utils/Database/PostgresErrorTranslator.js.map +1 -0
  228. package/build/dist/Server/Utils/Monitor/MonitorResource.js +67 -29
  229. package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
  230. package/build/dist/Server/Utils/Monitor/MonitorStatusTimeline.js +31 -14
  231. package/build/dist/Server/Utils/Monitor/MonitorStatusTimeline.js.map +1 -1
  232. package/build/dist/Server/Utils/Monitor/MonitorStepsProjectValidator.js +117 -0
  233. package/build/dist/Server/Utils/Monitor/MonitorStepsProjectValidator.js.map +1 -0
  234. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +549 -7
  235. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  236. package/build/dist/Types/JSONFunctions.js +8 -3
  237. package/build/dist/Types/JSONFunctions.js.map +1 -1
  238. package/build/dist/Types/Kubernetes/KubernetesCostIngest.js.map +1 -1
  239. package/build/dist/Types/Workspace/NotificationRules/NotificationRuleCondition.js +17 -7
  240. package/build/dist/Types/Workspace/NotificationRules/NotificationRuleCondition.js.map +1 -1
  241. package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js +37 -11
  242. package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js.map +1 -1
  243. package/build/dist/UI/Components/CodeEditor/CodeEditor.js +2 -0
  244. package/build/dist/UI/Components/CodeEditor/CodeEditor.js.map +1 -1
  245. package/build/dist/UI/Components/CodeEditor/MonacoLoader.js +34 -0
  246. package/build/dist/UI/Components/CodeEditor/MonacoLoader.js.map +1 -0
  247. package/build/dist/UI/Components/Forms/ModelForm.js +37 -17
  248. package/build/dist/UI/Components/Forms/ModelForm.js.map +1 -1
  249. package/build/dist/UI/Components/MasterPage/MasterPage.js +50 -1
  250. package/build/dist/UI/Components/MasterPage/MasterPage.js.map +1 -1
  251. package/build/dist/UI/Components/ModelDetail/CardModelDetail.js +22 -1
  252. package/build/dist/UI/Components/ModelDetail/CardModelDetail.js.map +1 -1
  253. package/build/dist/UI/Components/ModelDetail/ModelDetail.js +31 -1
  254. package/build/dist/UI/Components/ModelDetail/ModelDetail.js.map +1 -1
  255. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +472 -27
  256. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  257. package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js +189 -0
  258. package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js.map +1 -0
  259. package/build/dist/UI/Components/ModelTable/ColumnPreference.js +258 -0
  260. package/build/dist/UI/Components/ModelTable/ColumnPreference.js.map +1 -0
  261. package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js +168 -0
  262. package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js.map +1 -0
  263. package/build/dist/UI/Components/ModelTable/ExportFromColumns.js +30 -0
  264. package/build/dist/UI/Components/ModelTable/ExportFromColumns.js.map +1 -0
  265. package/build/dist/UI/Components/ModelTable/TableView.js +13 -2
  266. package/build/dist/UI/Components/ModelTable/TableView.js.map +1 -1
  267. package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js +84 -0
  268. package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js.map +1 -0
  269. package/build/dist/UI/Components/Navbar/NavBar.js +39 -1
  270. package/build/dist/UI/Components/Navbar/NavBar.js.map +1 -1
  271. package/build/dist/UI/Components/SideMenu/SideMenu.js +15 -5
  272. package/build/dist/UI/Components/SideMenu/SideMenu.js.map +1 -1
  273. package/build/dist/UI/Components/Table/Table.js +46 -13
  274. package/build/dist/UI/Components/Table/Table.js.map +1 -1
  275. package/build/dist/UI/Components/Table/TableRow.js +11 -6
  276. package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
  277. package/build/dist/UI/Utils/Project.js +161 -15
  278. package/build/dist/UI/Utils/Project.js.map +1 -1
  279. package/build/dist/UI/Utils/TableColumnsToCsv.js +67 -6
  280. package/build/dist/UI/Utils/TableColumnsToCsv.js.map +1 -1
  281. package/build/dist/Utils/Monitor/MonitorProbeSelectionUtil.js +52 -0
  282. package/build/dist/Utils/Monitor/MonitorProbeSelectionUtil.js.map +1 -0
  283. package/build/dist/Utils/UserPreferences.js +33 -0
  284. package/build/dist/Utils/UserPreferences.js.map +1 -1
  285. package/package.json +4 -3
@@ -415,30 +415,50 @@ const SideMenu: FunctionComponent<ComponentProps> = (props: ComponentProps) => {
415
415
  role="navigation"
416
416
  aria-label="Main navigation"
417
417
  >
418
- <div className="sticky top-6">
418
+ {/*
419
+ * Stick below the app header, not at the top of the viewport. The header
420
+ * is sticky too, so a plain `top-6` slides the first menu items behind it
421
+ * as soon as the page scrolls. --app-header-height is measured and
422
+ * published by MasterPage; the 0px fallback keeps this correct on pages
423
+ * that render without a sticky header.
424
+ */}
425
+ <div
426
+ className="sticky"
427
+ style={{ top: "calc(var(--app-header-height, 0px) + 1.5rem)" }}
428
+ >
419
429
  <div
420
430
  className={`
431
+ flex flex-col
421
432
  bg-white rounded-2xl
422
433
  border border-gray-200/80
423
434
  shadow-sm
424
435
  overflow-hidden
425
436
  `}
437
+ /*
438
+ * Menus longer than the viewport (the Kubernetes cluster menu, for
439
+ * one) would otherwise get pushed back up behind the header near the
440
+ * bottom of a long page. Cap the card to the space left under the
441
+ * header and let the item list scroll inside it instead.
442
+ */
443
+ style={{
444
+ maxHeight: "calc(100vh - var(--app-header-height, 0px) - 3rem)",
445
+ }}
426
446
  >
427
447
  {/* Optional Header */}
428
448
  {props.header && (
429
- <div className="px-3 py-3 border-b border-gray-100 bg-gradient-to-b from-gray-50 to-white">
449
+ <div className="flex-shrink-0 px-3 py-3 border-b border-gray-100 bg-gradient-to-b from-gray-50 to-white">
430
450
  {props.header}
431
451
  </div>
432
452
  )}
433
453
 
434
454
  {/* Menu Content */}
435
- <nav className="p-2">
455
+ <nav className="p-2 min-h-0 overflow-y-auto">
436
456
  <div className="space-y-0.5">{renderMenuContent()}</div>
437
457
  </nav>
438
458
 
439
459
  {/* Optional Footer */}
440
460
  {props.footer && (
441
- <div className="px-3 py-2 border-t border-gray-100 bg-gray-50/50">
461
+ <div className="flex-shrink-0 px-3 py-2 border-t border-gray-100 bg-gray-50/50">
442
462
  {props.footer}
443
463
  </div>
444
464
  )}
@@ -78,9 +78,19 @@ export interface ComponentProps<T extends GenericObject> {
78
78
  bulkSelectedItems?: Array<T> | undefined;
79
79
  onBulkSelectedItemAdded?: ((item: T) => void) | undefined;
80
80
  onBulkSelectedItemRemoved?: ((item: T) => void) | undefined;
81
- onBulkSelectAllItems?: (() => void) | undefined;
81
+ /*
82
+ * Resolves to whether every matching row really got selected. The table
83
+ * only claims "all items selected" when it did - otherwise a failed
84
+ * select-all would hide the Select All button while leaving just the
85
+ * current page selected, with no way to retry.
86
+ */
87
+ onBulkSelectAllItems?: (() => Promise<boolean>) | undefined;
82
88
  onBulkSelectItemsOnCurrentPage?: (() => void) | undefined;
83
89
  onBulkClearAllItems?: (() => void) | undefined;
90
+ bulkSelectionError?: string | undefined;
91
+ isBulkSelectAllLoading?: boolean | undefined;
92
+ isBulkSelectionTruncated?: boolean | undefined;
93
+ bulkSelectionTotalCount?: number | undefined;
84
94
  matchBulkSelectedItemByField?: keyof T | undefined; // which field to use to match selected items. For exmaple this could be '_id'
85
95
  onBulkActionEnd?: (() => void) | undefined;
86
96
  onBulkActionStart?: (() => void) | undefined;
@@ -156,13 +166,38 @@ const Table: TableFunction = <T extends GenericObject>(
156
166
  useEffect(() => {
157
167
  if (props.bulkSelectedItems) {
158
168
  setBulkSelectedItems(props.bulkSelectedItems);
169
+
170
+ /*
171
+ * "All items selected" is a claim about a selection that exists, so it
172
+ * cannot outlive one. The parent drops the selection whenever the query
173
+ * changes underneath it (and after a bulk action completes), and this
174
+ * state is local to the table - without this, the next single row the
175
+ * user ticked would re-open the bulk bar with the "Select All" button
176
+ * still hidden, as though everything matching were already selected.
177
+ */
178
+ if (props.bulkSelectedItems.length === 0) {
179
+ setIsAllItemsSelected(false);
180
+ }
159
181
  }
160
182
  }, [props.bulkSelectedItems]);
161
183
 
184
+ /*
185
+ * Width of the loading / error / "no items" row, in cells. It has to match
186
+ * what TableHeader actually renders or those messages sit under part of the
187
+ * table instead of spanning it: the Actions column is already one of
188
+ * `props.columns`, and the drag-handle and bulk-select cells are extra
189
+ * leading columns that only exist when those features are on.
190
+ */
162
191
  let colspan: number = props.columns.length || 0;
163
- if (props.actionButtons && props.actionButtons?.length > 0) {
192
+ if (props.enableDragAndDrop) {
164
193
  colspan++;
165
194
  }
195
+ if (isBulkActionsEnabled) {
196
+ colspan++;
197
+ }
198
+ if (colspan === 0) {
199
+ colspan = 1;
200
+ }
166
201
 
167
202
  const getTablebody: GetReactElementFunction = (): ReactElement => {
168
203
  if (props.isLoading) {
@@ -238,16 +273,20 @@ const Table: TableFunction = <T extends GenericObject>(
238
273
  if (props.matchBulkSelectedItemByField === undefined) {
239
274
  return;
240
275
  }
241
- const index: number = bulkSelectedItems.findIndex((x: T) => {
242
- return (
243
- x[props.matchBulkSelectedItemByField!]?.toString() ===
244
- item[props.matchBulkSelectedItemByField!]?.toString()
245
- );
246
- });
247
276
 
248
- if (index > -1) {
249
- bulkSelectedItems.splice(index, 1);
250
- }
277
+ /*
278
+ * Rebuild rather than splice: this array is the parent's state
279
+ * array by reference (see the sync effect above), so mutating it
280
+ * in place edits React state behind React's back.
281
+ */
282
+ setBulkSelectedItems(
283
+ bulkSelectedItems.filter((x: T) => {
284
+ return (
285
+ x[props.matchBulkSelectedItemByField!]?.toString() !==
286
+ item[props.matchBulkSelectedItemByField!]?.toString()
287
+ );
288
+ }),
289
+ );
251
290
 
252
291
  props.onBulkSelectedItemRemoved?.(item);
253
292
  }}
@@ -301,14 +340,27 @@ const Table: TableFunction = <T extends GenericObject>(
301
340
  props.onBulkClearAllItems?.();
302
341
  setIsAllItemsSelected(false);
303
342
  }}
304
- onSelectAllClick={() => {
305
- props.onBulkSelectAllItems?.();
306
- setIsAllItemsSelected(true);
343
+ onSelectAllClick={async () => {
344
+ const didSelectAllItems: boolean =
345
+ (await props.onBulkSelectAllItems?.()) ?? false;
346
+
347
+ /*
348
+ * Only on success. Otherwise the bulk bar would hide the Select
349
+ * All button and claim everything was selected while the
350
+ * selection is still just the current page.
351
+ */
352
+ if (didSelectAllItems) {
353
+ setIsAllItemsSelected(true);
354
+ }
307
355
  }}
308
356
  selectedItems={bulkSelectedItems}
309
357
  singularLabel={translatedSingularLabel}
310
358
  pluralLabel={translatedPluralLabel}
311
359
  isAllItemsSelected={isAllItemsSelected}
360
+ errorMessage={props.bulkSelectionError}
361
+ isSelectingAllItems={props.isBulkSelectAllLoading}
362
+ isSelectionTruncated={props.isBulkSelectionTruncated}
363
+ totalMatchingItemsCount={props.bulkSelectionTotalCount}
312
364
  onActionStart={props.onBulkActionStart}
313
365
  onActionEnd={() => {
314
366
  setIsAllItemsSelected(false);
@@ -77,6 +77,13 @@ const TableRow: TableRowFunction = <T extends GenericObject>(
77
77
  };
78
78
  }, []);
79
79
 
80
+ // The columns this row will actually put on screen.
81
+ const renderedColumns: Array<Column<T>> = (props.columns || []).filter(
82
+ (column: Column<T>) => {
83
+ return !(column.hideOnMobile && isMobile);
84
+ },
85
+ );
86
+
80
87
  type GetRowFunction = (provided?: DraggableProvided) => ReactElement;
81
88
 
82
89
  const getRow: GetRowFunction = (
@@ -339,186 +346,184 @@ const TableRow: TableRowFunction = <T extends GenericObject>(
339
346
  </td>
340
347
  )}
341
348
  {props.columns &&
342
- props.columns
343
- .filter((column: Column<T>) => {
344
- return !(column.hideOnMobile && isMobile);
345
- })
346
- .map((column: Column<T>, i: number) => {
347
- let className: string =
348
- "whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-500 sm:pl-6 align-top";
349
- if (i === props.columns.length - 1) {
350
- className =
351
- "whitespace-nowrap py-4 pl-4 pr-6 text-sm font-medium text-gray-500 sm:pl-6 align-top";
352
- }
353
-
354
- let columnContent: React.ReactNode = null;
355
-
356
- if (column.key && !column.getElement) {
357
- columnContent =
358
- column.type === FieldType.Date ? (
359
- props.item[column.key] ? (
360
- OneUptimeDate.getDateAsUserFriendlyLocalFormattedString(
361
- props.item[column.key] as string,
362
- true,
363
- )
364
- ) : (
365
- column.noValueMessage || ""
366
- )
367
- ) : column.type === FieldType.DateTime ? (
368
- props.item[column.key] ? (
369
- OneUptimeDate.getDateAsUserFriendlyLocalFormattedString(
370
- props.item[column.key] as string,
371
- false,
372
- )
373
- ) : (
374
- column.noValueMessage || ""
375
- )
376
- ) : column.type === FieldType.USDCents ? (
377
- props.item[column.key] ? (
378
- ((props.item[column.key] as number) || 0) / 100 + " USD"
379
- ) : (
380
- column.noValueMessage || "0 USD"
381
- )
382
- ) : column.type === FieldType.Percent ? (
383
- props.item[column.key] ? (
384
- props.item[column.key] + "%"
385
- ) : (
386
- column.noValueMessage || "0%"
349
+ renderedColumns.map((column: Column<T>, i: number) => {
350
+ let className: string =
351
+ "whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-500 sm:pl-6 align-top";
352
+ /*
353
+ * Compared against the rendered count, not the declared one:
354
+ * with any column filtered out the two differ, and the extra
355
+ * right padding would land on the wrong cell - or on none.
356
+ */
357
+ if (i === renderedColumns.length - 1) {
358
+ className =
359
+ "whitespace-nowrap py-4 pl-4 pr-6 text-sm font-medium text-gray-500 sm:pl-6 align-top";
360
+ }
361
+
362
+ let columnContent: React.ReactNode = null;
363
+
364
+ if (column.key && !column.getElement) {
365
+ columnContent =
366
+ column.type === FieldType.Date ? (
367
+ props.item[column.key] ? (
368
+ OneUptimeDate.getDateAsUserFriendlyLocalFormattedString(
369
+ props.item[column.key] as string,
370
+ true,
387
371
  )
388
- ) : column.type === FieldType.Color ? (
389
- props.item[column.key] ? (
390
- <ColorInput value={props.item[column.key] as Color} />
391
- ) : (
392
- column.noValueMessage || "0%"
393
- )
394
- ) : column.type === FieldType.LongText ? (
395
- props.item[column.key] ? (
396
- <LongTextViewer
397
- text={props.item[column.key] as string}
398
- />
399
- ) : (
400
- column.noValueMessage || ""
401
- )
402
- ) : column.type === FieldType.Boolean ? (
403
- props.item[column.key] ? (
404
- <Icon
405
- icon={IconProp.Check}
406
- className={"h-5 w-5 text-gray-500"}
407
- thick={ThickProp.Thick}
408
- />
409
- ) : (
410
- <Icon
411
- icon={IconProp.False}
412
- className={"h-5 w-5 text-gray-500"}
413
- thick={ThickProp.Thick}
414
- />
372
+ ) : (
373
+ column.noValueMessage || ""
374
+ )
375
+ ) : column.type === FieldType.DateTime ? (
376
+ props.item[column.key] ? (
377
+ OneUptimeDate.getDateAsUserFriendlyLocalFormattedString(
378
+ props.item[column.key] as string,
379
+ false,
415
380
  )
416
381
  ) : (
417
- getNestedValue(
418
- props.item,
419
- String(column.key),
420
- )?.toString() ||
421
- column.noValueMessage ||
422
- ""
423
- );
424
- } else if (column.key && column.getElement) {
425
- columnContent = column.getElement(props.item);
426
- }
427
-
428
- const contentWrapperClassName: string = column.contentClassName
429
- ? column.contentClassName
430
- : "";
431
-
432
- const actionsContainerClassName: string =
433
- column.contentClassName
434
- ? `flex justify-end ${column.contentClassName}`
435
- : "flex justify-end";
382
+ column.noValueMessage || ""
383
+ )
384
+ ) : column.type === FieldType.USDCents ? (
385
+ props.item[column.key] ? (
386
+ ((props.item[column.key] as number) || 0) / 100 + " USD"
387
+ ) : (
388
+ column.noValueMessage || "0 USD"
389
+ )
390
+ ) : column.type === FieldType.Percent ? (
391
+ props.item[column.key] ? (
392
+ props.item[column.key] + "%"
393
+ ) : (
394
+ column.noValueMessage || "0%"
395
+ )
396
+ ) : column.type === FieldType.Color ? (
397
+ props.item[column.key] ? (
398
+ <ColorInput value={props.item[column.key] as Color} />
399
+ ) : (
400
+ column.noValueMessage || "0%"
401
+ )
402
+ ) : column.type === FieldType.LongText ? (
403
+ props.item[column.key] ? (
404
+ <LongTextViewer text={props.item[column.key] as string} />
405
+ ) : (
406
+ column.noValueMessage || ""
407
+ )
408
+ ) : column.type === FieldType.Boolean ? (
409
+ props.item[column.key] ? (
410
+ <Icon
411
+ icon={IconProp.Check}
412
+ className={"h-5 w-5 text-gray-500"}
413
+ thick={ThickProp.Thick}
414
+ />
415
+ ) : (
416
+ <Icon
417
+ icon={IconProp.False}
418
+ className={"h-5 w-5 text-gray-500"}
419
+ thick={ThickProp.Thick}
420
+ />
421
+ )
422
+ ) : (
423
+ getNestedValue(
424
+ props.item,
425
+ String(column.key),
426
+ )?.toString() ||
427
+ column.noValueMessage ||
428
+ ""
429
+ );
430
+ } else if (column.key && column.getElement) {
431
+ columnContent = column.getElement(props.item);
432
+ }
433
+
434
+ const contentWrapperClassName: string = column.contentClassName
435
+ ? column.contentClassName
436
+ : "";
437
+
438
+ const actionsContainerClassName: string = column.contentClassName
439
+ ? `flex justify-end ${column.contentClassName}`
440
+ : "flex justify-end";
441
+
442
+ return (
443
+ <td
444
+ key={i}
445
+ className={className}
446
+ style={{
447
+ textAlign:
448
+ column.type === FieldType.Actions ? "right" : "left",
449
+ }}
450
+ onClick={() => {
451
+ if (column.tooltipText) {
452
+ setTooltipModalText(column.tooltipText(props.item));
453
+ }
454
+ }}
455
+ >
456
+ {columnContent !== null && columnContent !== undefined && (
457
+ <div className={contentWrapperClassName}>
458
+ {columnContent}
459
+ </div>
460
+ )}
461
+ {column.type === FieldType.Actions && (
462
+ <div className={actionsContainerClassName}>
463
+ {error && (
464
+ <div className="text-align-left">
465
+ <ConfirmModal
466
+ title={`Error`}
467
+ description={error}
468
+ submitButtonText={"Close"}
469
+ onSubmit={() => {
470
+ return setError("");
471
+ }}
472
+ />
473
+ </div>
474
+ )}
475
+ {props.actionButtons?.map(
476
+ (button: ActionButtonSchema<T>, i: number) => {
477
+ if (
478
+ button.isVisible &&
479
+ !button.isVisible(props.item)
480
+ ) {
481
+ return <div key={i}></div>;
482
+ }
436
483
 
437
- return (
438
- <td
439
- key={i}
440
- className={className}
441
- style={{
442
- textAlign:
443
- column.type === FieldType.Actions ? "right" : "left",
444
- }}
445
- onClick={() => {
446
- if (column.tooltipText) {
447
- setTooltipModalText(column.tooltipText(props.item));
448
- }
449
- }}
450
- >
451
- {columnContent !== null && columnContent !== undefined && (
452
- <div className={contentWrapperClassName}>
453
- {columnContent}
454
- </div>
455
- )}
456
- {column.type === FieldType.Actions && (
457
- <div className={actionsContainerClassName}>
458
- {error && (
459
- <div className="text-align-left">
460
- <ConfirmModal
461
- title={`Error`}
462
- description={error}
463
- submitButtonText={"Close"}
464
- onSubmit={() => {
465
- return setError("");
466
- }}
467
- />
468
- </div>
469
- )}
470
- {props.actionButtons?.map(
471
- (button: ActionButtonSchema<T>, i: number) => {
472
- if (
473
- button.isVisible &&
474
- !button.isVisible(props.item)
475
- ) {
476
- return <div key={i}></div>;
477
- }
478
-
479
- // Hide button on mobile if hideOnMobile is true
480
- if (button.hideOnMobile && isMobile) {
481
- return <div key={i}></div>;
482
- }
483
-
484
- return (
485
- <div key={i}>
486
- <Button
487
- buttonSize={ButtonSize.Small}
488
- title={button.title}
489
- icon={button.icon}
490
- buttonStyle={button.buttonStyleType}
491
- isLoading={isButtonLoading[i]}
492
- onClick={() => {
493
- if (button.onClick) {
494
- isButtonLoading[i] = true;
495
- setIsButtonLoading(isButtonLoading);
484
+ // Hide button on mobile if hideOnMobile is true
485
+ if (button.hideOnMobile && isMobile) {
486
+ return <div key={i}></div>;
487
+ }
496
488
 
497
- button.onClick(
498
- props.item,
499
- () => {
500
- // on action complete
501
- isButtonLoading[i] = false;
502
- setIsButtonLoading(isButtonLoading);
503
- },
504
- (err: Error) => {
505
- isButtonLoading[i] = false;
506
- setIsButtonLoading(isButtonLoading);
507
- setError((err as Error).message);
508
- },
509
- );
510
- }
511
- }}
512
- />
513
- </div>
514
- );
515
- },
516
- )}
517
- </div>
518
- )}
519
- </td>
520
- );
521
- })}
489
+ return (
490
+ <div key={i}>
491
+ <Button
492
+ buttonSize={ButtonSize.Small}
493
+ title={button.title}
494
+ icon={button.icon}
495
+ buttonStyle={button.buttonStyleType}
496
+ isLoading={isButtonLoading[i]}
497
+ onClick={() => {
498
+ if (button.onClick) {
499
+ isButtonLoading[i] = true;
500
+ setIsButtonLoading(isButtonLoading);
501
+
502
+ button.onClick(
503
+ props.item,
504
+ () => {
505
+ // on action complete
506
+ isButtonLoading[i] = false;
507
+ setIsButtonLoading(isButtonLoading);
508
+ },
509
+ (err: Error) => {
510
+ isButtonLoading[i] = false;
511
+ setIsButtonLoading(isButtonLoading);
512
+ setError((err as Error).message);
513
+ },
514
+ );
515
+ }
516
+ }}
517
+ />
518
+ </div>
519
+ );
520
+ },
521
+ )}
522
+ </div>
523
+ )}
524
+ </td>
525
+ );
526
+ })}
522
527
  </tr>
523
528
  {tooltipModalText && (
524
529
  <ConfirmModal
@@ -5,6 +5,8 @@ import { ReactElement } from "react";
5
5
 
6
6
  export default interface Column<T extends GenericObject> {
7
7
  title: string;
8
+ // Stable identity carried over from the ModelTable column, if it had one.
9
+ id?: string | undefined;
8
10
  description?: string | undefined;
9
11
  disableSort?: boolean | undefined;
10
12
  tooltipText?: ((item: T) => string) | undefined;
@@ -15,6 +17,24 @@ export default interface Column<T extends GenericObject> {
15
17
  alignItem?: AlignItem | undefined;
16
18
  key?: keyof T | null; //can be null because actions column does not have a key.
17
19
  hideOnMobile?: boolean | undefined; // Hide column on mobile devices
20
+ /*
21
+ * Every field that backs this cell, in declaration order. A ModelTable
22
+ * column may declare more than one - e.g. the alert "Affected Resources"
23
+ * cell spans hosts / kubernetesClusters / dockerHosts / podmanHosts /
24
+ * services - while `key` only ever holds the first one, because that is the
25
+ * one used for sorting and for the default renderer. CSV export reads all
26
+ * of them so it does not silently drop the rest. Defaults to [key].
27
+ */
28
+ exportKeys?: Array<string> | undefined;
29
+ /*
30
+ * Exact text for this column's CSV cell, overriding the value read off the
31
+ * row. Use it when the rendered cell is built from something the row does
32
+ * not carry under the column's own field (data fetched alongside the table,
33
+ * a computed summary, several fields combined into one phrase).
34
+ */
35
+ getExportValue?: ((item: T) => string) | undefined;
36
+ // Leave this column out of the CSV export entirely.
37
+ disableCsvExport?: boolean | undefined;
18
38
  getElement?:
19
39
  | ((item: T, onBeforeFetchData?: T | undefined) => ReactElement)
20
40
  | undefined;