@oneuptime/common 13.0.8 → 14.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (514) hide show
  1. package/Models/AnalyticsModels/AuditLog.ts +23 -15
  2. package/Models/DatabaseModels/GlobalConfig.ts +24 -0
  3. package/Models/DatabaseModels/Index.ts +4 -0
  4. package/Models/DatabaseModels/Monitor.ts +3 -2
  5. package/Models/DatabaseModels/MonitorTemplate.ts +2 -1
  6. package/Models/DatabaseModels/ServiceLevelObjectiveFeed.ts +2 -0
  7. package/Models/DatabaseModels/ServiceLevelObjectiveLabelRule.ts +522 -0
  8. package/Models/DatabaseModels/ServiceLevelObjectiveMonitorRule.ts +38 -4
  9. package/Models/DatabaseModels/ServiceLevelObjectiveOwnerRule.ts +603 -0
  10. package/Scripts/generate-service-worker.js +65 -4
  11. package/Server/API/BaseAPI.ts +26 -5
  12. package/Server/API/GlobalConfigAPI.ts +228 -462
  13. package/Server/API/ProjectOIDC.ts +25 -16
  14. package/Server/API/ProjectSSO.ts +24 -16
  15. package/Server/API/StatusPageAPI.ts +100 -42
  16. package/Server/API/TeamMemberAPI.ts +10 -1
  17. package/Server/Enterprise/EnterpriseEdition.ts +649 -0
  18. package/Server/Enterprise/EnterpriseFeature.ts +48 -0
  19. package/Server/Enterprise/EnterpriseLicenseSnapshot.ts +192 -0
  20. package/Server/Enterprise/EnterpriseServerModule.ts +233 -0
  21. package/Server/EnvironmentConfig.ts +147 -2
  22. package/Server/Infrastructure/Postgres/SchemaMigrations/1793900000000-AddEnterpriseEditionFirstSeenAt.ts +25 -0
  23. package/Server/Infrastructure/Postgres/SchemaMigrations/1794100000000-AddSloLabelAndOwnerRules.ts +248 -0
  24. package/Server/Infrastructure/Postgres/SchemaMigrations/1794200000000-AddSloMonitorRuleMonitorType.ts +19 -0
  25. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
  26. package/Server/Middleware/UserAuthorization.ts +38 -16
  27. package/Server/Services/AuditLogService.ts +84 -1079
  28. package/Server/Services/DatabaseService.ts +10 -0
  29. package/Server/Services/EnterpriseLicenseService.ts +38 -0
  30. package/Server/Services/GlobalConfigService.ts +80 -1
  31. package/Server/Services/Index.ts +4 -0
  32. package/Server/Services/MonitorProbeService.ts +116 -20
  33. package/Server/Services/MonitorService.ts +59 -6
  34. package/Server/Services/MonitorStatusTimelineService.ts +284 -0
  35. package/Server/Services/MonitorTemplateService.ts +26 -0
  36. package/Server/Services/ProjectService.ts +284 -0
  37. package/Server/Services/RumSessionPinService.ts +25 -6
  38. package/Server/Services/ServiceLevelObjectiveLabelRuleEngineService.ts +385 -0
  39. package/Server/Services/ServiceLevelObjectiveLabelRuleService.ts +51 -0
  40. package/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.ts +92 -16
  41. package/Server/Services/ServiceLevelObjectiveMonitorRuleService.ts +36 -1
  42. package/Server/Services/ServiceLevelObjectiveOwnerRuleEngineService.ts +521 -0
  43. package/Server/Services/ServiceLevelObjectiveOwnerRuleService.ts +51 -0
  44. package/Server/Services/ServiceLevelObjectiveService.ts +28 -0
  45. package/Server/Services/StatusPagePrivateUserService.ts +7 -3
  46. package/Server/Services/StatusPageService.ts +78 -1
  47. package/Server/Services/TeamMemberService.ts +11 -0
  48. package/Server/Services/TeamService.ts +10 -0
  49. package/Server/Services/UserService.ts +10 -17
  50. package/Server/Types/Database/Permissions/CreatePermission.ts +15 -0
  51. package/Server/Types/Database/Permissions/EditionPermission.ts +208 -18
  52. package/Server/Types/Database/Permissions/TablePermission.ts +10 -3
  53. package/Server/Types/Database/Permissions/UpdatePermission.ts +29 -0
  54. package/Server/Types/Markdown.ts +41 -0
  55. package/Server/Types/Workflow/Components/Telegram/SendMessageToChat.ts +27 -1
  56. package/Server/Utils/AI/Toolbox/SloTools.ts +1 -0
  57. package/Server/Utils/CommunityEditionSsoReport.ts +361 -0
  58. package/Server/Utils/EditionEnforcement.ts +233 -0
  59. package/Server/Utils/FrontendEnvironment.ts +41 -2
  60. package/Server/Utils/InstanceHealth/RedisHealth.ts +12 -277
  61. package/Server/Utils/Monitor/AffectedResourceList.ts +148 -0
  62. package/Server/Utils/Monitor/Criteria/CompareCriteria.ts +1 -1
  63. package/Server/Utils/Monitor/Criteria/EvaluateOverTime.ts +10 -1
  64. package/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.ts +1 -1
  65. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +720 -465
  66. package/Server/Utils/Monitor/MonitoringIntervalValidator.ts +116 -0
  67. package/Server/Utils/Monitor/RootCauseList.ts +139 -0
  68. package/Server/Utils/Rules/RuleRun/RuleRunRegistry.ts +28 -0
  69. package/Server/Utils/Slo/SloRulePatternValidator.ts +42 -0
  70. package/Server/Utils/Workspace/Slack/Slack.ts +423 -10
  71. package/Tests/App/Accounts/LoginPasskey.test.tsx +18 -0
  72. package/Tests/App/Accounts/LoginSsoEdition.test.tsx +616 -0
  73. package/Tests/App/AdminDashboard/AdminAuthenticationSsoEdition.test.tsx +217 -0
  74. package/Tests/App/AdminDashboard/AdminEnterprisePluginPage.test.tsx +357 -0
  75. package/Tests/App/AdminDashboard/EnterpriseLicensesShell.test.tsx +214 -0
  76. package/Tests/App/AdminDashboard/GlobalSSOEnterpriseShells.test.tsx +271 -0
  77. package/Tests/App/AdminDashboard/HealthShells.test.tsx +659 -0
  78. package/Tests/App/Dashboard/AuditLogsAndComplianceShells.test.tsx +572 -0
  79. package/Tests/App/Dashboard/EnterpriseFeatureUpgrade.test.tsx +547 -0
  80. package/Tests/App/Dashboard/EnterprisePluginPage.test.tsx +667 -0
  81. package/Tests/App/Dashboard/FeedFilterSortIntegration.test.tsx +1632 -0
  82. package/Tests/App/Dashboard/FeedOptionsLocales.test.ts +250 -0
  83. package/Tests/App/Dashboard/FeedOrdering.test.ts +279 -9
  84. package/Tests/App/Dashboard/IdentityEnterpriseShells.test.tsx +319 -0
  85. package/Tests/App/Dashboard/InvestigationFeedRefresh.test.tsx +60 -1
  86. package/Tests/App/Dashboard/InvestigationPanel.test.tsx +594 -3
  87. package/Tests/App/Dashboard/OnboardingSsoEmptyState.test.tsx +185 -0
  88. package/Tests/App/Dashboard/ProductOrientation.test.tsx +646 -0
  89. package/Tests/App/Dashboard/ResourceFeed.test.tsx +612 -2
  90. package/Tests/App/Dashboard/ResourceFeedPageWiring.test.ts +30 -0
  91. package/Tests/App/Dashboard/ResourceFeedPagesFilterSort.test.tsx +1202 -0
  92. package/Tests/App/Dashboard/ScheduledMaintenanceFeedRefresh.test.tsx +52 -1
  93. package/Tests/App/Dashboard/SecurityEventConnectionDiagnostics.test.tsx +45 -9
  94. package/Tests/App/Dashboard/SecurityEventDetailPanel.test.tsx +399 -0
  95. package/Tests/App/Dashboard/SecurityEventListRow.test.tsx +214 -0
  96. package/Tests/App/Dashboard/SecurityEventsEmptyState.test.tsx +1 -83
  97. package/Tests/App/Dashboard/SecurityEventsNavTabs.test.tsx +248 -0
  98. package/Tests/App/Dashboard/SecurityEventsViewer.test.tsx +1203 -0
  99. package/Tests/App/Dashboard/SloFeed.test.tsx +106 -1
  100. package/Tests/App/Dashboard/SloMonitorRuleCriteria.test.tsx +566 -0
  101. package/Tests/App/Dashboard/SloNavigationWiring.test.tsx +6 -4
  102. package/Tests/App/Dashboard/SloNoticeBanner.test.tsx +186 -0
  103. package/Tests/App/Dashboard/SloOverviewKpiStrip.test.tsx +388 -44
  104. package/Tests/App/Dashboard/SloOverviewLayout.test.tsx +613 -0
  105. package/Tests/App/Dashboard/UserEmailCallSites.test.tsx +11 -91
  106. package/Tests/App/StatusPage/StatusPageSsoEmptyState.test.tsx +319 -0
  107. package/Tests/Helpers/RefreshAwareApiScan.ts +757 -0
  108. package/Tests/Helpers/TenantScopedRequestScan.ts +515 -0
  109. package/Tests/Models/DatabaseModels/ResourceFeedModels.test.ts +0 -8
  110. package/Tests/Models/DatabaseModels/SloLabelAndOwnerRuleModels.test.ts +404 -0
  111. package/Tests/Models/DatabaseModels/SloOverhaulModels.test.ts +17 -1
  112. package/Tests/Models/DatabaseModels/StaticRuleCriteriaInheritance.test.ts +4 -2
  113. package/Tests/Scripts/GenerateServiceWorker.test.ts +931 -0
  114. package/Tests/Server/API/BaseAPICreateItemDoesNotMutateModel.test.ts +130 -0
  115. package/Tests/Server/API/GlobalConfigLicense.test.ts +742 -497
  116. package/Tests/Server/API/OnCallReadinessAPI.test.ts +35 -909
  117. package/Tests/Server/API/SsoProviderListingsEdition.test.ts +432 -0
  118. package/Tests/Server/API/TeamMemberLeaveScimLockEdition.test.ts +252 -0
  119. package/Tests/Server/Enterprise/EnterpriseEdition.test.ts +824 -0
  120. package/Tests/Server/Enterprise/EnterpriseFeatureActive.test.ts +643 -0
  121. package/Tests/Server/Enterprise/EnterpriseLicenseSnapshot.test.ts +294 -0
  122. package/Tests/Server/Enterprise/EnterpriseModelFeatureMap.test.ts +93 -0
  123. package/Tests/Server/Enterprise/EnterpriseOnlyModulesLiveInEe.test.ts +582 -0
  124. package/Tests/Server/Enterprise/EnterpriseServerModule.test.ts +247 -0
  125. package/Tests/Server/Enterprise/FakeEnterpriseModule.ts +502 -0
  126. package/Tests/Server/Enterprise/TestBillingFlag.ts +70 -0
  127. package/Tests/Server/EnvironmentConfigEnterpriseEdition.test.ts +314 -0
  128. package/Tests/Server/Infrastructure/ContainerBootConfiguration.test.ts +289 -0
  129. package/Tests/Server/Infrastructure/E2EClickHouseFixtureAccess.test.ts +5 -1
  130. package/Tests/Server/Infrastructure/Postgres/AddConfigurableRuleCriteriaMigration.test.ts +3 -0
  131. package/Tests/Server/Infrastructure/Postgres/AddSloLabelAndOwnerRulesMigration.test.ts +407 -0
  132. package/Tests/Server/Infrastructure/Postgres/AddSloMonitorRuleMonitorTypeMigration.test.ts +171 -0
  133. package/Tests/Server/Infrastructure/Postgres/SloProductOverhaulMigration.test.ts +88 -4
  134. package/Tests/Server/Middleware/UserAuthorization.test.ts +37 -2
  135. package/Tests/Server/Middleware/UserAuthorizationEditionEnforcement.test.ts +516 -0
  136. package/Tests/Server/Services/AuditLogService.test.ts +460 -990
  137. package/Tests/Server/Services/CreateAndReadResponsesKeepIds.test.ts +263 -0
  138. package/Tests/Server/Services/DatabaseServiceRuleCriteriaValidation.test.ts +104 -0
  139. package/Tests/Server/Services/EnterpriseLicenseServiceKeyGeneration.test.ts +151 -0
  140. package/Tests/Server/Services/GlobalConfigServiceLicenseColumns.test.ts +357 -0
  141. package/Tests/Server/Services/MonitorDestinationInfoIPv6.test.ts +101 -0
  142. package/Tests/Server/Services/MonitorProbeServiceIntervalScheduling.test.ts +333 -0
  143. package/Tests/Server/Services/MonitorStatusTimelineDailyAggregates.test.ts +303 -0
  144. package/Tests/Server/Services/ProjectServiceAuditLogLicense.test.ts +701 -0
  145. package/Tests/Server/Services/ProjectServiceAuditLogSettingsCache.test.ts +31 -35
  146. package/Tests/Server/Services/RuleRun/LabelRuleEnginesGroupBRun.test.ts +48 -1
  147. package/Tests/Server/Services/RuleRun/OwnerRuleEnginesGroupBRun.test.ts +30 -0
  148. package/Tests/Server/Services/ScimPushGroupsTeamLocksEdition.test.ts +431 -0
  149. package/Tests/Server/Services/ServiceLevelObjectiveLabelAndOwnerRuleEngines.test.ts +1170 -0
  150. package/Tests/Server/Services/ServiceLevelObjectiveLabelAndOwnerRuleServices.test.ts +172 -0
  151. package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.test.ts +323 -0
  152. package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleService.test.ts +115 -0
  153. package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleTriggers.test.ts +21 -2
  154. package/Tests/Server/Services/ServiceLevelObjectiveMonitorTypePostgres.test.ts +480 -0
  155. package/Tests/Server/Services/ServiceLevelObjectiveService.test.ts +162 -0
  156. package/Tests/Server/Services/SsoRequirementReadMasking.test.ts +419 -0
  157. package/Tests/Server/Services/SsoRequirementWriteGuard.test.ts +769 -0
  158. package/Tests/Server/Services/StaticRuleCriteriaRuntimeCoverage.test.ts +45 -6
  159. package/Tests/Server/Services/StatusPagePrivateUserInvitationPolicy.test.ts +94 -0
  160. package/Tests/Server/Services/UserAuthenticationService.test.ts +35 -0
  161. package/Tests/Server/Services/UserServiceSeatLimit.test.ts +94 -56
  162. package/Tests/Server/Types/Database/FeedEventTypeFilterQuery.test.ts +745 -0
  163. package/Tests/Server/Types/Database/Permissions/EditionPermission.test.ts +1823 -0
  164. package/Tests/Server/Types/Markdown.test.ts +183 -1
  165. package/Tests/Server/Types/Workflow/Components/ApiComponentSsrf.test.ts +49 -0
  166. package/Tests/Server/Types/Workflow/Components/TelegramSendMessageToChat.test.ts +331 -0
  167. package/Tests/Server/Utils/AI/SloTools.test.ts +10 -0
  168. package/Tests/Server/Utils/CommunityEditionSsoReport.test.ts +565 -0
  169. package/Tests/Server/Utils/EditionEnforcement.test.ts +303 -0
  170. package/Tests/Server/Utils/FrontendEnvironment.test.ts +367 -0
  171. package/Tests/Server/Utils/InstanceHealth/RedisHealth.test.ts +38 -418
  172. package/Tests/Server/Utils/Monitor/AffectedResourceList.test.ts +518 -0
  173. package/Tests/Server/Utils/Monitor/Criteria/EvaluateOverTime.test.ts +24 -4
  174. package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteriaAnomalyUnits.test.ts +1 -1
  175. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluator.test.ts +1339 -96
  176. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorBreachingSamples.test.ts +1101 -0
  177. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorMetricUnits.test.ts +106 -80
  178. package/Tests/Server/Utils/Monitor/MonitoringIntervalValidator.test.ts +188 -0
  179. package/Tests/Server/Utils/Monitor/RootCauseList.test.ts +252 -0
  180. package/Tests/Server/Utils/Rules/RuleRun/RuleRunPermission.test.ts +154 -0
  181. package/Tests/Server/Utils/SSRFProtection.test.ts +111 -0
  182. package/Tests/Server/Utils/Slo/SloRulePatternValidator.test.ts +67 -0
  183. package/Tests/Server/Utils/VM/VMRunnerSsrf.test.ts +213 -0
  184. package/Tests/Server/Utils/Workspace/Slack/SlackMarkdownSectionSplit.test.ts +1209 -0
  185. package/Tests/Types/API/HostnameFromAuthority.test.ts +20 -10
  186. package/Tests/Types/API/HostnameIPv6.test.ts +149 -0
  187. package/Tests/Types/API/Route.test.ts +56 -0
  188. package/Tests/Types/API/URLSchemeLikePath.test.ts +304 -0
  189. package/Tests/Types/Database/CanonicalModelInstance.test.ts +190 -0
  190. package/Tests/UI/Components/EditionLabelLapseCopy.ts +104 -0
  191. package/Tests/UI/Components/EditionLabelLicenseManagerWiring.test.tsx +254 -0
  192. package/Tests/UI/Components/EditionLabelLicenseRefresh.test.tsx +84 -178
  193. package/Tests/UI/Components/EditionLabelLicenseStatus.test.tsx +1586 -0
  194. package/Tests/UI/Components/EditionLabelLicenseWritesGuard.test.tsx +157 -0
  195. package/Tests/UI/Components/FeedOptions.test.ts +1404 -0
  196. package/Tests/UI/Components/FeedOptionsButton.test.tsx +2603 -0
  197. package/Tests/UI/Components/Graphs/DayUptimeGraph.test.tsx +49 -2
  198. package/Tests/UI/Components/Icon.test.tsx +26 -0
  199. package/Tests/UI/Components/MarkdownConverters.test.ts +684 -0
  200. package/Tests/UI/Components/ModelListEmptyState.test.tsx +289 -0
  201. package/Tests/UI/Components/RuleCriteria/RuleCriteriaModelTable.test.tsx +3 -3
  202. package/Tests/UI/Components/TelemetryViewer/TelemetryViewerEmptyContent.test.tsx +131 -0
  203. package/Tests/UI/Components/useFeedItemsViewKey.test.tsx +1017 -0
  204. package/Tests/UI/Components/useFeedOptions.test.tsx +1107 -0
  205. package/Tests/UI/EsbuildEnterpriseAlias.test.ts +1127 -0
  206. package/Tests/Utils/HostAddressUtil.test.ts +138 -0
  207. package/Tests/Utils/LabelRuleImportExport.test.ts +102 -0
  208. package/Tests/Utils/Monitor/MonitorDestinationUtil.test.ts +313 -0
  209. package/Tests/Utils/Monitor/MonitoringIntervalUtil.test.ts +304 -0
  210. package/Tests/Utils/Slo/SloDuration.test.ts +115 -24
  211. package/Tests/Utils/Slo/SloHealth.test.ts +93 -10
  212. package/Tests/Utils/Slo/SloMonitorRuleCriteria.test.ts +109 -0
  213. package/Tests/Utils/Slo/SloOverviewText.test.ts +64 -0
  214. package/Tests/Utils/StatusPage/UptimeDailyAggregateUtil.test.ts +248 -0
  215. package/Types/API/Hostname.ts +63 -16
  216. package/Types/API/Route.ts +19 -2
  217. package/Types/API/URL.ts +26 -2
  218. package/Types/Database/AccessControl/ColumnAccessControl.ts +19 -7
  219. package/Types/Database/AccessControl/ColumnBillingAccessControl.ts +19 -7
  220. package/Types/Database/AccessControl/OwnerOnlyColumn.ts +15 -6
  221. package/Types/Database/CanonicalModelInstance.ts +102 -0
  222. package/Types/Database/TableColumn.ts +20 -6
  223. package/Types/EnterpriseLicense/EnterpriseLicensePeriods.ts +29 -0
  224. package/Types/IP/IPv6.ts +6 -1
  225. package/Types/Monitor/MetricMonitor/MetricCriteriaContext.ts +6 -5
  226. package/Types/Monitor/MetricMonitor/MetricMonitorResponse.ts +9 -0
  227. package/Types/Monitor/MonitorStep.ts +6 -1
  228. package/Types/Monitor/ProxmoxAlertTemplates.ts +1 -1
  229. package/Types/Permission.ts +90 -0
  230. package/Types/Rules/RuleCriteriaFieldRegistry.ts +11 -0
  231. package/Types/Rules/RuleRun.ts +4 -0
  232. package/Types/StatusPage/UptimeDailyAggregate.ts +110 -0
  233. package/UI/Components/EditionLabel/EditionLabel.tsx +796 -1195
  234. package/UI/Components/EditionLabel/LicenseManager.ts +119 -0
  235. package/UI/Components/Feed/FeedOptions.ts +427 -0
  236. package/UI/Components/Feed/FeedOptionsButton.tsx +692 -0
  237. package/UI/Components/Feed/useFeedItems.ts +28 -9
  238. package/UI/Components/Feed/useFeedOptions.ts +225 -0
  239. package/UI/Components/Graphs/DayUptimeGraph.tsx +123 -15
  240. package/UI/Components/Icon/Icon.tsx +1 -1
  241. package/UI/Components/Markdown.tsx/MarkdownConverters.ts +0 -0
  242. package/UI/Components/ModelList/ModelList.tsx +11 -1
  243. package/UI/Components/MonitorGraphs/Uptime.tsx +69 -0
  244. package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +28 -12
  245. package/UI/esbuild-enterprise.js +263 -0
  246. package/Utils/EnterpriseLicense/EnterpriseLicenseUsage.ts +7 -2
  247. package/Utils/HostAddressUtil.ts +94 -0
  248. package/Utils/LabelRuleImportExport.ts +27 -9
  249. package/Utils/Monitor/MetricValueFormatter.ts +2 -2
  250. package/Utils/Monitor/MonitorDestinationUtil.ts +157 -0
  251. package/Utils/Monitor/MonitoringIntervalUtil.ts +436 -0
  252. package/Utils/Rules/MonitorTypeRuleCriteria.ts +31 -0
  253. package/Utils/Slo/SloDuration.ts +22 -6
  254. package/Utils/Slo/SloHealth.ts +8 -7
  255. package/Utils/Slo/SloMonitorRuleCriteria.ts +33 -1
  256. package/Utils/Slo/SloOverviewText.ts +33 -0
  257. package/Utils/Slo/SloProjection.ts +4 -3
  258. package/Utils/StatusPage/UptimeDailyAggregateUtil.ts +192 -0
  259. package/build/dist/Models/AnalyticsModels/AuditLog.js +23 -15
  260. package/build/dist/Models/AnalyticsModels/AuditLog.js.map +1 -1
  261. package/build/dist/Models/DatabaseModels/GlobalConfig.js +25 -0
  262. package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
  263. package/build/dist/Models/DatabaseModels/Index.js +4 -0
  264. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  265. package/build/dist/Models/DatabaseModels/Monitor.js +2 -2
  266. package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
  267. package/build/dist/Models/DatabaseModels/MonitorTemplate.js +2 -1
  268. package/build/dist/Models/DatabaseModels/MonitorTemplate.js.map +1 -1
  269. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveFeed.js +2 -0
  270. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveFeed.js.map +1 -1
  271. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveLabelRule.js +530 -0
  272. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveLabelRule.js.map +1 -0
  273. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveMonitorRule.js +39 -4
  274. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveMonitorRule.js.map +1 -1
  275. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveOwnerRule.js +612 -0
  276. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveOwnerRule.js.map +1 -0
  277. package/build/dist/Server/API/BaseAPI.js +26 -5
  278. package/build/dist/Server/API/BaseAPI.js.map +1 -1
  279. package/build/dist/Server/API/GlobalConfigAPI.js +148 -344
  280. package/build/dist/Server/API/GlobalConfigAPI.js.map +1 -1
  281. package/build/dist/Server/API/ProjectOIDC.js +24 -16
  282. package/build/dist/Server/API/ProjectOIDC.js.map +1 -1
  283. package/build/dist/Server/API/ProjectSSO.js +24 -16
  284. package/build/dist/Server/API/ProjectSSO.js.map +1 -1
  285. package/build/dist/Server/API/StatusPageAPI.js +93 -43
  286. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  287. package/build/dist/Server/API/TeamMemberAPI.js +8 -1
  288. package/build/dist/Server/API/TeamMemberAPI.js.map +1 -1
  289. package/build/dist/Server/Enterprise/EnterpriseEdition.js +466 -0
  290. package/build/dist/Server/Enterprise/EnterpriseEdition.js.map +1 -0
  291. package/build/dist/Server/Enterprise/EnterpriseFeature.js +39 -0
  292. package/build/dist/Server/Enterprise/EnterpriseFeature.js.map +1 -0
  293. package/build/dist/Server/Enterprise/EnterpriseLicenseSnapshot.js +70 -0
  294. package/build/dist/Server/Enterprise/EnterpriseLicenseSnapshot.js.map +1 -0
  295. package/build/dist/Server/Enterprise/EnterpriseServerModule.js +84 -0
  296. package/build/dist/Server/Enterprise/EnterpriseServerModule.js.map +1 -0
  297. package/build/dist/Server/EnvironmentConfig.js +84 -2
  298. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  299. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1793900000000-AddEnterpriseEditionFirstSeenAt.js +18 -0
  300. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1793900000000-AddEnterpriseEditionFirstSeenAt.js.map +1 -0
  301. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794100000000-AddSloLabelAndOwnerRules.js +93 -0
  302. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794100000000-AddSloLabelAndOwnerRules.js.map +1 -0
  303. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794200000000-AddSloMonitorRuleMonitorType.js +12 -0
  304. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794200000000-AddSloMonitorRuleMonitorType.js.map +1 -0
  305. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
  306. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  307. package/build/dist/Server/Middleware/UserAuthorization.js +30 -11
  308. package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
  309. package/build/dist/Server/Services/AuditLogService.js +71 -740
  310. package/build/dist/Server/Services/AuditLogService.js.map +1 -1
  311. package/build/dist/Server/Services/DatabaseService.js +7 -0
  312. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  313. package/build/dist/Server/Services/EnterpriseLicenseService.js +34 -0
  314. package/build/dist/Server/Services/EnterpriseLicenseService.js.map +1 -1
  315. package/build/dist/Server/Services/GlobalConfigService.js +63 -0
  316. package/build/dist/Server/Services/GlobalConfigService.js.map +1 -1
  317. package/build/dist/Server/Services/Index.js +4 -0
  318. package/build/dist/Server/Services/Index.js.map +1 -1
  319. package/build/dist/Server/Services/MonitorProbeService.js +90 -17
  320. package/build/dist/Server/Services/MonitorProbeService.js.map +1 -1
  321. package/build/dist/Server/Services/MonitorService.js +48 -6
  322. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  323. package/build/dist/Server/Services/MonitorStatusTimelineService.js +242 -0
  324. package/build/dist/Server/Services/MonitorStatusTimelineService.js.map +1 -1
  325. package/build/dist/Server/Services/MonitorTemplateService.js +16 -0
  326. package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
  327. package/build/dist/Server/Services/ProjectService.js +214 -0
  328. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  329. package/build/dist/Server/Services/RumSessionPinService.js +24 -6
  330. package/build/dist/Server/Services/RumSessionPinService.js.map +1 -1
  331. package/build/dist/Server/Services/ServiceLevelObjectiveLabelRuleEngineService.js +295 -0
  332. package/build/dist/Server/Services/ServiceLevelObjectiveLabelRuleEngineService.js.map +1 -0
  333. package/build/dist/Server/Services/ServiceLevelObjectiveLabelRuleService.js +56 -0
  334. package/build/dist/Server/Services/ServiceLevelObjectiveLabelRuleService.js.map +1 -0
  335. package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.js +59 -14
  336. package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.js.map +1 -1
  337. package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleService.js +22 -1
  338. package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleService.js.map +1 -1
  339. package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleEngineService.js +400 -0
  340. package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleEngineService.js.map +1 -0
  341. package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleService.js +56 -0
  342. package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleService.js.map +1 -0
  343. package/build/dist/Server/Services/ServiceLevelObjectiveService.js +21 -0
  344. package/build/dist/Server/Services/ServiceLevelObjectiveService.js.map +1 -1
  345. package/build/dist/Server/Services/StatusPagePrivateUserService.js +7 -3
  346. package/build/dist/Server/Services/StatusPagePrivateUserService.js.map +1 -1
  347. package/build/dist/Server/Services/StatusPageService.js +71 -0
  348. package/build/dist/Server/Services/StatusPageService.js.map +1 -1
  349. package/build/dist/Server/Services/TeamMemberService.js +10 -0
  350. package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
  351. package/build/dist/Server/Services/TeamService.js +9 -0
  352. package/build/dist/Server/Services/TeamService.js.map +1 -1
  353. package/build/dist/Server/Services/UserService.js +10 -16
  354. package/build/dist/Server/Services/UserService.js.map +1 -1
  355. package/build/dist/Server/Types/Database/Permissions/CreatePermission.js +10 -0
  356. package/build/dist/Server/Types/Database/Permissions/CreatePermission.js.map +1 -1
  357. package/build/dist/Server/Types/Database/Permissions/EditionPermission.js +154 -14
  358. package/build/dist/Server/Types/Database/Permissions/EditionPermission.js.map +1 -1
  359. package/build/dist/Server/Types/Database/Permissions/TablePermission.js +10 -3
  360. package/build/dist/Server/Types/Database/Permissions/TablePermission.js.map +1 -1
  361. package/build/dist/Server/Types/Database/Permissions/UpdatePermission.js +17 -0
  362. package/build/dist/Server/Types/Database/Permissions/UpdatePermission.js.map +1 -1
  363. package/build/dist/Server/Types/Markdown.js +31 -0
  364. package/build/dist/Server/Types/Markdown.js.map +1 -1
  365. package/build/dist/Server/Types/Workflow/Components/Telegram/SendMessageToChat.js +20 -1
  366. package/build/dist/Server/Types/Workflow/Components/Telegram/SendMessageToChat.js.map +1 -1
  367. package/build/dist/Server/Utils/AI/Toolbox/SloTools.js +1 -0
  368. package/build/dist/Server/Utils/AI/Toolbox/SloTools.js.map +1 -1
  369. package/build/dist/Server/Utils/CommunityEditionSsoReport.js +223 -0
  370. package/build/dist/Server/Utils/CommunityEditionSsoReport.js.map +1 -0
  371. package/build/dist/Server/Utils/EditionEnforcement.js +187 -0
  372. package/build/dist/Server/Utils/EditionEnforcement.js.map +1 -0
  373. package/build/dist/Server/Utils/FrontendEnvironment.js +31 -2
  374. package/build/dist/Server/Utils/FrontendEnvironment.js.map +1 -1
  375. package/build/dist/Server/Utils/InstanceHealth/RedisHealth.js +6 -193
  376. package/build/dist/Server/Utils/InstanceHealth/RedisHealth.js.map +1 -1
  377. package/build/dist/Server/Utils/Monitor/AffectedResourceList.js +67 -0
  378. package/build/dist/Server/Utils/Monitor/AffectedResourceList.js.map +1 -0
  379. package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js +1 -1
  380. package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js +8 -1
  381. package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js.map +1 -1
  382. package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js +1 -1
  383. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +585 -367
  384. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  385. package/build/dist/Server/Utils/Monitor/MonitoringIntervalValidator.js +88 -0
  386. package/build/dist/Server/Utils/Monitor/MonitoringIntervalValidator.js.map +1 -0
  387. package/build/dist/Server/Utils/Monitor/RootCauseList.js +101 -0
  388. package/build/dist/Server/Utils/Monitor/RootCauseList.js.map +1 -0
  389. package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js +28 -0
  390. package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js.map +1 -1
  391. package/build/dist/Server/Utils/Slo/SloRulePatternValidator.js +28 -0
  392. package/build/dist/Server/Utils/Slo/SloRulePatternValidator.js.map +1 -0
  393. package/build/dist/Server/Utils/Workspace/Slack/Slack.js +331 -11
  394. package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
  395. package/build/dist/Types/API/Hostname.js +59 -11
  396. package/build/dist/Types/API/Hostname.js.map +1 -1
  397. package/build/dist/Types/API/Route.js +18 -2
  398. package/build/dist/Types/API/Route.js.map +1 -1
  399. package/build/dist/Types/API/URL.js +23 -2
  400. package/build/dist/Types/API/URL.js.map +1 -1
  401. package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js +19 -7
  402. package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js.map +1 -1
  403. package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js +19 -7
  404. package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js.map +1 -1
  405. package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js +13 -6
  406. package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js.map +1 -1
  407. package/build/dist/Types/Database/CanonicalModelInstance.js +88 -0
  408. package/build/dist/Types/Database/CanonicalModelInstance.js.map +1 -0
  409. package/build/dist/Types/Database/TableColumn.js +19 -6
  410. package/build/dist/Types/Database/TableColumn.js.map +1 -1
  411. package/build/dist/Types/EnterpriseLicense/EnterpriseLicensePeriods.js +28 -0
  412. package/build/dist/Types/EnterpriseLicense/EnterpriseLicensePeriods.js.map +1 -0
  413. package/build/dist/Types/IP/IPv6.js +6 -1
  414. package/build/dist/Types/IP/IPv6.js.map +1 -1
  415. package/build/dist/Types/Monitor/MonitorStep.js +5 -0
  416. package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
  417. package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js +1 -1
  418. package/build/dist/Types/Permission.js +84 -0
  419. package/build/dist/Types/Permission.js.map +1 -1
  420. package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js +11 -0
  421. package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js.map +1 -1
  422. package/build/dist/Types/Rules/RuleRun.js +4 -0
  423. package/build/dist/Types/Rules/RuleRun.js.map +1 -1
  424. package/build/dist/Types/StatusPage/UptimeDailyAggregate.js +32 -0
  425. package/build/dist/Types/StatusPage/UptimeDailyAggregate.js.map +1 -0
  426. package/build/dist/UI/Components/EditionLabel/EditionLabel.js +414 -585
  427. package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
  428. package/build/dist/UI/Components/EditionLabel/LicenseManager.js +2 -0
  429. package/build/dist/UI/Components/EditionLabel/LicenseManager.js.map +1 -0
  430. package/build/dist/UI/Components/Feed/FeedOptions.js +235 -0
  431. package/build/dist/UI/Components/Feed/FeedOptions.js.map +1 -0
  432. package/build/dist/UI/Components/Feed/FeedOptionsButton.js +346 -0
  433. package/build/dist/UI/Components/Feed/FeedOptionsButton.js.map +1 -0
  434. package/build/dist/UI/Components/Feed/useFeedItems.js +17 -7
  435. package/build/dist/UI/Components/Feed/useFeedItems.js.map +1 -1
  436. package/build/dist/UI/Components/Feed/useFeedOptions.js +130 -0
  437. package/build/dist/UI/Components/Feed/useFeedOptions.js.map +1 -0
  438. package/build/dist/UI/Components/Graphs/DayUptimeGraph.js +90 -17
  439. package/build/dist/UI/Components/Graphs/DayUptimeGraph.js.map +1 -1
  440. package/build/dist/UI/Components/Icon/Icon.js +1 -1
  441. package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
  442. package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js +0 -0
  443. package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js.map +1 -1
  444. package/build/dist/UI/Components/ModelList/ModelList.js +4 -1
  445. package/build/dist/UI/Components/ModelList/ModelList.js.map +1 -1
  446. package/build/dist/UI/Components/MonitorGraphs/Uptime.js +42 -1
  447. package/build/dist/UI/Components/MonitorGraphs/Uptime.js.map +1 -1
  448. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +2 -2
  449. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
  450. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseUsage.js +6 -2
  451. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseUsage.js.map +1 -1
  452. package/build/dist/Utils/HostAddressUtil.js +80 -0
  453. package/build/dist/Utils/HostAddressUtil.js.map +1 -0
  454. package/build/dist/Utils/LabelRuleImportExport.js +22 -6
  455. package/build/dist/Utils/LabelRuleImportExport.js.map +1 -1
  456. package/build/dist/Utils/Monitor/MetricValueFormatter.js +2 -2
  457. package/build/dist/Utils/Monitor/MonitorDestinationUtil.js +127 -0
  458. package/build/dist/Utils/Monitor/MonitorDestinationUtil.js.map +1 -0
  459. package/build/dist/Utils/Monitor/MonitoringIntervalUtil.js +374 -0
  460. package/build/dist/Utils/Monitor/MonitoringIntervalUtil.js.map +1 -0
  461. package/build/dist/Utils/Rules/MonitorTypeRuleCriteria.js +18 -0
  462. package/build/dist/Utils/Rules/MonitorTypeRuleCriteria.js.map +1 -0
  463. package/build/dist/Utils/Slo/SloDuration.js +13 -5
  464. package/build/dist/Utils/Slo/SloDuration.js.map +1 -1
  465. package/build/dist/Utils/Slo/SloHealth.js +8 -7
  466. package/build/dist/Utils/Slo/SloHealth.js.map +1 -1
  467. package/build/dist/Utils/Slo/SloMonitorRuleCriteria.js +16 -1
  468. package/build/dist/Utils/Slo/SloMonitorRuleCriteria.js.map +1 -1
  469. package/build/dist/Utils/Slo/SloOverviewText.js +16 -0
  470. package/build/dist/Utils/Slo/SloOverviewText.js.map +1 -1
  471. package/build/dist/Utils/Slo/SloProjection.js.map +1 -1
  472. package/build/dist/Utils/StatusPage/UptimeDailyAggregateUtil.js +148 -0
  473. package/build/dist/Utils/StatusPage/UptimeDailyAggregateUtil.js.map +1 -0
  474. package/jest.config.json +3 -1
  475. package/package.json +1 -1
  476. package/test-setup.sh +103 -34
  477. package/tsconfig.json +13 -0
  478. package/Server/API/EnterpriseLicenseAPI.ts +0 -925
  479. package/Server/API/TeamComplianceAPI.ts +0 -158
  480. package/Server/Middleware/SCIMAuthorization.ts +0 -141
  481. package/Server/Services/TeamComplianceService.ts +0 -733
  482. package/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.ts +0 -186
  483. package/Server/Utils/InstanceHealth/PostgresHealth.ts +0 -286
  484. package/Tests/App/AdminDashboard/EnterpriseLicenseInstanceActivity.test.tsx +0 -451
  485. package/Tests/App/Dashboard/AuditLogsTable.test.tsx +0 -598
  486. package/Tests/App/Dashboard/SecurityEventsExplorer.test.tsx +0 -761
  487. package/Tests/App/Dashboard/SecurityEventsSideMenu.test.tsx +0 -247
  488. package/Tests/App/Dashboard/SecurityEventsTableColumns.test.tsx +0 -382
  489. package/Tests/App/Dashboard/SecurityEventsVolumeChart.test.tsx +0 -321
  490. package/Tests/Server/API/EnterpriseLicenseReportUserCount.test.ts +0 -1851
  491. package/Tests/Server/Middleware/SCIMAuthorization.test.ts +0 -525
  492. package/Tests/Server/Services/TeamComplianceServiceBehaviour.test.ts +0 -1845
  493. package/Tests/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.test.ts +0 -495
  494. package/Tests/Server/Utils/InstanceHealth/PostgresHealth.test.ts +0 -337
  495. package/Tests/Utils/EnterpriseLicenseSeats.test.ts +0 -576
  496. package/Tests/Utils/EnterpriseLicenseSync.test.ts +0 -568
  497. package/Utils/EnterpriseLicense/EnterpriseLicenseSeats.ts +0 -287
  498. package/Utils/EnterpriseLicense/EnterpriseLicenseSync.ts +0 -244
  499. package/build/dist/Server/API/EnterpriseLicenseAPI.js +0 -646
  500. package/build/dist/Server/API/EnterpriseLicenseAPI.js.map +0 -1
  501. package/build/dist/Server/API/TeamComplianceAPI.js +0 -111
  502. package/build/dist/Server/API/TeamComplianceAPI.js.map +0 -1
  503. package/build/dist/Server/Middleware/SCIMAuthorization.js +0 -124
  504. package/build/dist/Server/Middleware/SCIMAuthorization.js.map +0 -1
  505. package/build/dist/Server/Services/TeamComplianceService.js +0 -537
  506. package/build/dist/Server/Services/TeamComplianceService.js.map +0 -1
  507. package/build/dist/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.js +0 -145
  508. package/build/dist/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.js.map +0 -1
  509. package/build/dist/Server/Utils/InstanceHealth/PostgresHealth.js +0 -194
  510. package/build/dist/Server/Utils/InstanceHealth/PostgresHealth.js.map +0 -1
  511. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js +0 -168
  512. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js.map +0 -1
  513. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSync.js +0 -174
  514. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSync.js.map +0 -1
@@ -25,6 +25,14 @@ export interface FeedPage<TFeedModel> {
25
25
 
26
26
  export interface UseFeedItemsProps<TFeedModel> {
27
27
  resourceKey: string;
28
+ /*
29
+ * What the reader has chosen to see of this resource's feed - its sort
30
+ * order and event type filter (see useFeedOptions). A change re-reads the
31
+ * feed from its first window, exactly like moving to another resource:
32
+ * rows already loaded were read for the old view, so keeping them, or the
33
+ * size of the window they filled, would mix two different feeds.
34
+ */
35
+ viewKey?: string | undefined;
28
36
  refreshToken?: number | undefined;
29
37
  getItems: (limit: number) => Promise<FeedPage<TFeedModel>>;
30
38
  mapItems: (items: Array<TFeedModel>) => Array<FeedItemProps>;
@@ -50,7 +58,8 @@ interface FetchOptions {
50
58
 
51
59
  /*
52
60
  * Every dashboard activity feed follows the same paging contract: read the
53
- * newest top N rows and replace that window when the reader asks for more.
61
+ * first N rows in the reader's chosen order (newest first unless they asked
62
+ * for oldest first) and replace that window when the reader asks for more.
54
63
  * Re-reading from zero matters for a live feed. Offset-appending can duplicate
55
64
  * or skip a row when a new event arrives between page requests.
56
65
  */
@@ -71,10 +80,20 @@ const useFeedItems: <TFeedModel>(
71
80
  null,
72
81
  );
73
82
 
74
- const activeResourceKeyRef: MutableRefObject<string> = useRef<string>(
75
- props.resourceKey,
76
- );
77
- activeResourceKeyRef.current = props.resourceKey;
83
+ /*
84
+ * The resource and the view together name the feed being shown. Everything
85
+ * below that guards against a stale response or resets the window keys on
86
+ * this, so a response for the previous view is dropped the same way as a
87
+ * response for the previous resource.
88
+ */
89
+ const feedKey: string =
90
+ props.viewKey === undefined
91
+ ? props.resourceKey
92
+ : `${props.resourceKey}\u0000${props.viewKey}`;
93
+
94
+ const activeResourceKeyRef: MutableRefObject<string> =
95
+ useRef<string>(feedKey);
96
+ activeResourceKeyRef.current = feedKey;
78
97
  const previousResourceKeyRef: MutableRefObject<string | null> = useRef<
79
98
  string | null
80
99
  >(null);
@@ -184,8 +203,8 @@ const useFeedItems: <TFeedModel>(
184
203
 
185
204
  useEffect(() => {
186
205
  const hasResourceChanged: boolean =
187
- previousResourceKeyRef.current !== props.resourceKey;
188
- previousResourceKeyRef.current = props.resourceKey;
206
+ previousResourceKeyRef.current !== feedKey;
207
+ previousResourceKeyRef.current = feedKey;
189
208
 
190
209
  if (hasResourceChanged) {
191
210
  currentLimitRef.current = DEFAULT_LIMIT;
@@ -200,7 +219,7 @@ const useFeedItems: <TFeedModel>(
200
219
  }).catch(() => {
201
220
  // fetchItems converts request failures into the appropriate UI state.
202
221
  });
203
- }, [fetchItems, props.resourceKey, props.refreshToken]);
222
+ }, [fetchItems, feedKey, props.refreshToken]);
204
223
 
205
224
  useEffect(() => {
206
225
  return () => {
@@ -208,7 +227,7 @@ const useFeedItems: <TFeedModel>(
208
227
  };
209
228
  }, []);
210
229
 
211
- const isCurrentFeedLoaded: boolean = loadedResourceKey === props.resourceKey;
230
+ const isCurrentFeedLoaded: boolean = loadedResourceKey === feedKey;
212
231
 
213
232
  return {
214
233
  feedItems,
@@ -0,0 +1,225 @@
1
+ import {
2
+ MutableRefObject,
3
+ useCallback,
4
+ useEffect,
5
+ useMemo,
6
+ useRef,
7
+ useState,
8
+ } from "react";
9
+ import SortOrder from "../../../Types/BaseDatabase/SortOrder";
10
+ import LocalStorage from "../../Utils/LocalStorage";
11
+ import {
12
+ DEFAULT_FEED_SORT_ORDER,
13
+ FeedEventTypeOption,
14
+ FeedOptions,
15
+ GetFeedEventTypeIconFunction,
16
+ GetFeedEventTypeLabelFunction,
17
+ getFeedEventTypeOptions,
18
+ getFeedOptionsKey,
19
+ isFeedFiltered,
20
+ isFeedSortOrder,
21
+ sanitizeFeedEventTypes,
22
+ } from "./FeedOptions";
23
+
24
+ export interface UseFeedOptionsProps {
25
+ // Every event type the feed model can record, e.g. Object.values(the enum).
26
+ eventTypes: Array<string>;
27
+ getEventTypeLabel?: GetFeedEventTypeLabelFunction | undefined;
28
+ getEventTypeIcon?: GetFeedEventTypeIconFunction | undefined;
29
+ /*
30
+ * Names this kind of feed ("incident", "monitor", ...) so the reader's sort
31
+ * order is remembered across pages and visits. The event type filter is
32
+ * deliberately not remembered: it narrows one investigation, and a filter
33
+ * that silently followed the reader to the next incident would read as a
34
+ * feed with missing events.
35
+ */
36
+ storageKey?: string | undefined;
37
+ /*
38
+ * The resource whose feed this is (the incident id, the cluster id, ...).
39
+ * The dashboard moves between resources without remounting the feed, and
40
+ * the event type filter belongs to the resource it was chosen on, so a new
41
+ * resource starts unfiltered. The sort order is a preference and stays.
42
+ */
43
+ resetKey?: string | undefined;
44
+ }
45
+
46
+ export interface UseFeedOptionsResult {
47
+ options: FeedOptions;
48
+ // Pass to useFeedItems as viewKey: a change re-reads the feed.
49
+ optionsKey: string;
50
+ eventTypeOptions: Array<FeedEventTypeOption>;
51
+ isFiltered: boolean;
52
+ setOptions: (options: FeedOptions) => void;
53
+ }
54
+
55
+ const SORT_ORDER_STORAGE_KEY_PREFIX: string = "feed-sort-order:";
56
+
57
+ // One shared empty selection, so an unfiltered render keeps its identity.
58
+ const NO_EVENT_TYPES: Array<string> = [];
59
+
60
+ type GetSortOrderStorageKey = (storageKey: string) => string;
61
+
62
+ export const getSortOrderStorageKey: GetSortOrderStorageKey = (
63
+ storageKey: string,
64
+ ): string => {
65
+ return `${SORT_ORDER_STORAGE_KEY_PREFIX}${storageKey}`;
66
+ };
67
+
68
+ type ReadStoredSortOrder = (storageKey: string | undefined) => SortOrder;
69
+
70
+ /*
71
+ * Storage can be missing, blocked or hold anything, so every read falls back
72
+ * to the default order.
73
+ */
74
+ const readStoredSortOrder: ReadStoredSortOrder = (
75
+ storageKey: string | undefined,
76
+ ): SortOrder => {
77
+ if (!storageKey) {
78
+ return DEFAULT_FEED_SORT_ORDER;
79
+ }
80
+
81
+ try {
82
+ const storedValue: unknown = LocalStorage.getItem(
83
+ getSortOrderStorageKey(storageKey),
84
+ );
85
+
86
+ return isFeedSortOrder(storedValue) ? storedValue : DEFAULT_FEED_SORT_ORDER;
87
+ } catch {
88
+ return DEFAULT_FEED_SORT_ORDER;
89
+ }
90
+ };
91
+
92
+ type WriteStoredSortOrder = (
93
+ storageKey: string | undefined,
94
+ sortOrder: SortOrder,
95
+ ) => void;
96
+
97
+ const writeStoredSortOrder: WriteStoredSortOrder = (
98
+ storageKey: string | undefined,
99
+ sortOrder: SortOrder,
100
+ ): void => {
101
+ if (!storageKey) {
102
+ return;
103
+ }
104
+
105
+ try {
106
+ if (sortOrder === DEFAULT_FEED_SORT_ORDER) {
107
+ LocalStorage.removeItem(getSortOrderStorageKey(storageKey));
108
+ } else {
109
+ LocalStorage.setItem(getSortOrderStorageKey(storageKey), sortOrder);
110
+ }
111
+ } catch {
112
+ // A preference that cannot be saved still applies to this page.
113
+ }
114
+ };
115
+
116
+ const useFeedOptions: (props: UseFeedOptionsProps) => UseFeedOptionsResult = (
117
+ props: UseFeedOptionsProps,
118
+ ): UseFeedOptionsResult => {
119
+ /*
120
+ * Callers pass Object.values(SomeEnum), a new array on every render, so the
121
+ * list is keyed by its contents.
122
+ */
123
+ const eventTypesKey: string = props.eventTypes.join("\u0000");
124
+ const knownEventTypes: Array<string> = useMemo(() => {
125
+ return props.eventTypes;
126
+ }, [eventTypesKey]);
127
+
128
+ const [sortOrder, setSortOrder] = useState<SortOrder>(() => {
129
+ return readStoredSortOrder(props.storageKey);
130
+ });
131
+ /*
132
+ * The selection remembers which resource it was made on. It is read through
133
+ * that, during render rather than in an effect, so the first render for a
134
+ * new resource is already unfiltered and never asks the API for the old
135
+ * resource's filter. The stale selection is also dropped (React's "adjust
136
+ * state when a prop changes" pattern), so going back to the first resource
137
+ * does not bring its old filter back either.
138
+ */
139
+ const [selection, setSelection] = useState<{
140
+ resetKey: string | undefined;
141
+ eventTypes: Array<string>;
142
+ }>(() => {
143
+ return {
144
+ resetKey: props.resetKey,
145
+ eventTypes: [],
146
+ };
147
+ });
148
+ const isSelectionForThisResource: boolean =
149
+ selection.resetKey === props.resetKey;
150
+
151
+ if (!isSelectionForThisResource) {
152
+ setSelection({
153
+ resetKey: props.resetKey,
154
+ eventTypes: NO_EVENT_TYPES,
155
+ });
156
+ }
157
+
158
+ const selectedEventTypes: Array<string> = isSelectionForThisResource
159
+ ? selection.eventTypes
160
+ : NO_EVENT_TYPES;
161
+
162
+ const options: FeedOptions = useMemo(() => {
163
+ return {
164
+ sortOrder,
165
+ eventTypes: sanitizeFeedEventTypes({
166
+ selectedEventTypes,
167
+ knownEventTypes,
168
+ }),
169
+ };
170
+ }, [sortOrder, selectedEventTypes, knownEventTypes]);
171
+
172
+ const eventTypeOptions: Array<FeedEventTypeOption> = useMemo(() => {
173
+ return getFeedEventTypeOptions({
174
+ eventTypes: knownEventTypes,
175
+ getEventTypeLabel: props.getEventTypeLabel,
176
+ getEventTypeIcon: props.getEventTypeIcon,
177
+ });
178
+ }, [knownEventTypes, props.getEventTypeLabel, props.getEventTypeIcon]);
179
+
180
+ const setOptions: (nextOptions: FeedOptions) => void = useCallback(
181
+ (nextOptions: FeedOptions): void => {
182
+ const nextSortOrder: SortOrder = isFeedSortOrder(nextOptions.sortOrder)
183
+ ? nextOptions.sortOrder
184
+ : DEFAULT_FEED_SORT_ORDER;
185
+
186
+ setSortOrder(nextSortOrder);
187
+ setSelection({
188
+ resetKey: props.resetKey,
189
+ eventTypes: sanitizeFeedEventTypes({
190
+ selectedEventTypes: nextOptions.eventTypes,
191
+ knownEventTypes,
192
+ }),
193
+ });
194
+ },
195
+ [knownEventTypes, props.resetKey],
196
+ );
197
+
198
+ /*
199
+ * The stored order follows the committed state, not each setOptions call:
200
+ * two changes batched into one render would otherwise leave storage holding
201
+ * the first while the page shows the second. The first run is the value
202
+ * just read from storage, so there is nothing to write.
203
+ */
204
+ const hasCommittedSortOrderRef: MutableRefObject<boolean> =
205
+ useRef<boolean>(false);
206
+
207
+ useEffect(() => {
208
+ if (!hasCommittedSortOrderRef.current) {
209
+ hasCommittedSortOrderRef.current = true;
210
+ return;
211
+ }
212
+
213
+ writeStoredSortOrder(props.storageKey, sortOrder);
214
+ }, [sortOrder]);
215
+
216
+ return {
217
+ options,
218
+ optionsKey: getFeedOptionsKey(options),
219
+ eventTypeOptions,
220
+ isFiltered: isFeedFiltered(options),
221
+ setOptions,
222
+ };
223
+ };
224
+
225
+ export default useFeedOptions;
@@ -39,10 +39,52 @@ export interface UptimeBarDaySummary {
39
39
  incidents: Array<UptimeBarTooltipIncident>;
40
40
  }
41
41
 
42
+ /*
43
+ * One day's reading, measured server-side.
44
+ *
45
+ * The bars used to be derived purely from the timeline rows the browser
46
+ * happened to receive, and those rows arrive under a cap that silently drops
47
+ * history (see MonitorStatusTimelineService.getDailyUptimeAggregate). A day
48
+ * whose rows were dropped is indistinguishable, client-side, from a day that
49
+ * was genuinely quiet - so the graph painted both as the status page's
50
+ * `defaultBarColor`, which is GREEN on the overwhelming majority of pages.
51
+ * The page then asserted uptime for periods it had no data for.
52
+ *
53
+ * A reading carries how much of the day was actually COVERED by data, which
54
+ * is the thing the browser cannot infer for itself.
55
+ */
56
+ export interface DayReading {
57
+ dayStart: Date;
58
+ daySeconds: number;
59
+ /* Zero means nothing was ever recorded for this day. */
60
+ coveredSeconds: number;
61
+ statusDurations: Array<StatusDuration>;
62
+ }
63
+
64
+ /*
65
+ * A day nobody measured is not a colour the operator gets to choose.
66
+ *
67
+ * `defaultBarColor` is branding for an ordinary good day; letting it also
68
+ * stand for "we have no idea" is exactly the conflation that made a page with
69
+ * a green default claim uptime it never recorded, and a page with a grey
70
+ * default look unmonitored while it was passing every check. This is a fixed
71
+ * neutral, drawn with a hatch so it is still distinguishable in greyscale and
72
+ * to a colour-blind reader.
73
+ */
74
+ export const NO_DATA_BAR_COLOR: Color = new Color("#9CA3AF");
75
+
42
76
  export interface ComponentProps {
43
77
  startDate: Date;
44
78
  endDate: Date;
45
79
  events: Array<Event>;
80
+ /*
81
+ * Server-measured coverage per day. When a day has a reading, it decides
82
+ * whether that day has data and what its durations are; the `events` array
83
+ * is then only used for incident overlays. When absent - the dashboard
84
+ * monitor page, which does not fetch the aggregate - the old event-derived
85
+ * behaviour applies unchanged.
86
+ */
87
+ dayReadings?: Array<DayReading> | undefined;
46
88
  height?: number | undefined;
47
89
  barColorRules?: Array<BarChartRule> | undefined;
48
90
  downtimeEventStatusIds?: Array<ObjectID> | undefined;
@@ -173,6 +215,20 @@ const DayUptimeGraph: FunctionComponent<ComponentProps> = (
173
215
  const startOfTheDay: Date = OneUptimeDate.getStartOfDay(todaysDay);
174
216
  const endOfTheDay: Date = OneUptimeDate.getEndOfDay(todaysDay);
175
217
 
218
+ /*
219
+ * The server's reading for this day, when the caller supplied one. It is
220
+ * matched by instant rather than by index: the window's first bucket is
221
+ * clipped to the window start, so bucket i is not necessarily day i.
222
+ */
223
+ const todaysReading: DayReading | undefined = (
224
+ props.dayReadings || []
225
+ ).find((reading: DayReading) => {
226
+ return (
227
+ reading.dayStart.getTime() >= startOfTheDay.getTime() &&
228
+ reading.dayStart.getTime() <= endOfTheDay.getTime()
229
+ );
230
+ });
231
+
176
232
  const todaysEvents: Array<Event> = props.events.filter((event: Event) => {
177
233
  let doesEventBelongsToToday: boolean = false;
178
234
 
@@ -274,32 +330,75 @@ const DayUptimeGraph: FunctionComponent<ComponentProps> = (
274
330
  }
275
331
  }
276
332
 
333
+ /*
334
+ * Does this day have data at all?
335
+ *
336
+ * With a server reading, coverage is the answer and it is authoritative -
337
+ * the browser cannot tell a quiet day from a day whose rows were dropped
338
+ * by the fetch cap. Without one, fall back to the old test.
339
+ */
340
+ const hasDataForTheDay: boolean = todaysReading
341
+ ? todaysReading.coveredSeconds > 0
342
+ : todaysEvents.length > 0;
343
+
277
344
  // now check bar rules and finalize the color of the bar
278
345
 
279
- const uptimePercentForTheDay: number =
280
- totalUptimeInSeconds + totalDowntimeInSeconds > 0
346
+ /*
347
+ * The denominator is what was MEASURED, not the length of the day. A day
348
+ * only half covered - the day a monitor was created - reports the
349
+ * percentage of the half we watched rather than being marked down for the
350
+ * hours nobody was looking.
351
+ *
352
+ * The old expression ended in `: 100`, which said "no readings at all
353
+ * means a perfect day". That is the single line that let a status page
354
+ * assert uptime for a period it had no data for, so it is gone rather
355
+ * than adjusted: with no data there is no percentage.
356
+ */
357
+ const uptimePercentForTheDay: number = !hasDataForTheDay
358
+ ? 0
359
+ : totalUptimeInSeconds + totalDowntimeInSeconds > 0
281
360
  ? (totalUptimeInSeconds /
282
361
  (totalDowntimeInSeconds + totalUptimeInSeconds)) *
283
362
  100
284
363
  : 100;
285
364
 
286
- for (const rules of props.barColorRules || []) {
287
- if (uptimePercentForTheDay >= rules.uptimePercentGreaterThanOrEqualTo) {
288
- color = rules.barColor;
289
- break;
365
+ /*
366
+ * Rules are skipped entirely on a day with no data. Otherwise a page
367
+ * configuring ">= 99% is green" would have that rule matched by a day
368
+ * nobody measured, which is the very claim this change removes. No-data
369
+ * outranks operator rules on purpose.
370
+ */
371
+ if (hasDataForTheDay) {
372
+ for (const rules of props.barColorRules || []) {
373
+ if (uptimePercentForTheDay >= rules.uptimePercentGreaterThanOrEqualTo) {
374
+ color = rules.barColor;
375
+ break;
376
+ }
290
377
  }
291
378
  }
292
379
 
293
- if (todaysEvents.length === 1 && !hasEvents) {
380
+ if (todaysEvents.length >= 1) {
294
381
  hasEvents = true;
295
382
  }
296
383
 
297
- if (todaysEvents.length === 1) {
298
- hasEvents = true;
299
- }
384
+ /*
385
+ * THE FIX.
386
+ *
387
+ * This used to read: no events today, so paint `defaultBarColor`. That
388
+ * single line is how a status page claimed uptime for days it had no data
389
+ * for - `defaultBarColor` is green on the overwhelming majority of pages,
390
+ * and "no events" was true both for a quiet day and for a day whose rows
391
+ * the fetch cap had thrown away.
392
+ *
393
+ * Now the two are separated. A day with data but no events of its own is
394
+ * an ordinary good day and still gets the operator's colour. A day with
395
+ * no data gets the fixed no-data treatment and says so.
396
+ */
397
+ hasEvents = hasDataForTheDay;
300
398
 
301
- if (todaysEvents.length === 0) {
302
- hasEvents = false;
399
+ if (!hasDataForTheDay) {
400
+ color = NO_DATA_BAR_COLOR;
401
+ } else if (todaysEvents.length === 0) {
303
402
  color = props.defaultBarColor || Green;
304
403
  }
305
404
 
@@ -315,9 +414,18 @@ const DayUptimeGraph: FunctionComponent<ComponentProps> = (
315
414
  className = "w-20 h-" + props.height;
316
415
  }
317
416
 
318
- // Build status durations for tooltip
319
- const statusDurations: Array<StatusDuration> = [];
320
- for (const key in secondsOfEvent) {
417
+ /*
418
+ * Build status durations for the tooltip and the day dialog.
419
+ *
420
+ * A server reading wins: its durations are measured over the whole day
421
+ * server-side, while `secondsOfEvent` can only describe the rows this
422
+ * browser happened to receive.
423
+ */
424
+ const statusDurations: Array<StatusDuration> = todaysReading
425
+ ? todaysReading.statusDurations
426
+ : [];
427
+
428
+ for (const key in todaysReading ? {} : secondsOfEvent) {
321
429
  statusDurations.push({
322
430
  label: eventLabels[key] || "Unknown",
323
431
  seconds: secondsOfEvent[key] || 0,
@@ -800,7 +800,7 @@ const Icon: FunctionComponent<ComponentProps> = ({
800
800
  <path
801
801
  strokeLinecap="round"
802
802
  strokeLinejoin="round"
803
- d="M3 4.5h14.25M3 9h9.75M3 13.5h9.75m4.5-4.5v12m0 0l-3.75-3.75M17.25 21L21 17.25"
803
+ d="M3 4.5h14.25M3 9h9.75M3 13.5h5.25m5.25-.75L17.25 9m0 0L21 12.75M17.25 9v12"
804
804
  />,
805
805
  );
806
806
  } else if (icon === IconProp.CheckCircle) {
@@ -37,6 +37,13 @@ export interface ComponentProps<TBaseModel extends BaseModel> {
37
37
  fetchRequestOptions?: RequestOptions | undefined;
38
38
  customElement?: ((item: TBaseModel) => ReactElement) | undefined;
39
39
  noItemsMessage: string;
40
+ /*
41
+ * Render nothing, rather than noItemsMessage (or its "No items found."
42
+ * fallback), when the list loads empty. For a caller that explains an empty
43
+ * list itself, e.g. from onListLoaded. A search that matches nothing still
44
+ * says so.
45
+ */
46
+ hideEmptyState?: boolean | undefined;
40
47
  headerField?: string | ((item: TBaseModel) => ReactElement) | undefined;
41
48
  onSelectChange?: ((list: Array<TBaseModel>) => void) | undefined;
42
49
  refreshToggle?: string | undefined;
@@ -260,7 +267,10 @@ const ModelList: <TBaseModel extends BaseModel>(
260
267
  {error ? <ErrorMessage message={error} /> : <></>}
261
268
  {isLoading ? <ComponentLoader /> : <></>}
262
269
 
263
- {!isLoading && !error && searchedList.length === 0 ? (
270
+ {!isLoading &&
271
+ !error &&
272
+ searchedList.length === 0 &&
273
+ (Boolean(searchText) || !props.hideEmptyState) ? (
264
274
  <ErrorMessage
265
275
  message={
266
276
  searchText
@@ -2,9 +2,16 @@ import ComponentLoader from "../ComponentLoader/ComponentLoader";
2
2
  import ErrorMessage from "../ErrorMessage/ErrorMessage";
3
3
  import DayUptimeGraph, {
4
4
  BarChartRule,
5
+ DayReading,
5
6
  Event,
6
7
  UptimeBarDaySummary,
7
8
  } from "../Graphs/DayUptimeGraph";
9
+ import { StatusDuration } from "../Graphs/UptimeDaySummary";
10
+ import Dictionary from "../../../Types/Dictionary";
11
+ import {
12
+ UptimeDayBucket,
13
+ UptimeStatusDuration,
14
+ } from "../../../Types/StatusPage/UptimeDailyAggregate";
8
15
  import UptimeUtil from "./UptimeUtil";
9
16
  import Color from "../../../Types/Color";
10
17
  import CommonMonitorEvent from "../../../Utils/Uptime/MonitorEvent";
@@ -27,6 +34,17 @@ export interface ComponentProps {
27
34
  height?: number | undefined;
28
35
  barColorRules?: Array<StatusPageHistoryChartBarColorRule> | undefined;
29
36
  downtimeMonitorStatuses: Array<MonitorStatus> | undefined;
37
+ /*
38
+ * Server-measured per-day coverage for THIS monitor.
39
+ *
40
+ * The `items` array above arrives under a fetch cap that silently drops
41
+ * history, so it cannot answer "did we have data for this day". These
42
+ * buckets can. When absent the graph keeps its old event-derived
43
+ * behaviour, which is what the dashboard monitor page still uses.
44
+ */
45
+ uptimeBuckets?: Array<UptimeDayBucket> | undefined;
46
+ /* Statuses by id, so a bucket's durations can be given a name and colour. */
47
+ monitorStatuses?: Array<MonitorStatus> | undefined;
30
48
  defaultBarColor: Color;
31
49
  incidents?: Array<UptimeBarTooltipIncident> | undefined;
32
50
  onBarClick?: (
@@ -62,6 +80,56 @@ const MonitorUptimeGraph: FunctionComponent<ComponentProps> = (
62
80
  );
63
81
  }, [props.barColorRules]);
64
82
 
83
+ /*
84
+ * Turn the server's buckets into the graph's per-day readings, resolving
85
+ * each status id to the name and colour the page already knows.
86
+ */
87
+ const dayReadings: Array<DayReading> | undefined = useMemo(() => {
88
+ if (!props.uptimeBuckets) {
89
+ return undefined;
90
+ }
91
+
92
+ const statusById: Dictionary<MonitorStatus> = {};
93
+
94
+ for (const status of props.monitorStatuses || []) {
95
+ if (status.id) {
96
+ statusById[status.id.toString()] = status;
97
+ }
98
+ }
99
+
100
+ const downtimeIds: Array<string> = (
101
+ props.downtimeMonitorStatuses || []
102
+ ).map((status: MonitorStatus) => {
103
+ return status.id?.toString() || "";
104
+ });
105
+
106
+ return props.uptimeBuckets.map((bucket: UptimeDayBucket): DayReading => {
107
+ return {
108
+ dayStart: bucket.bucketStart,
109
+ daySeconds: bucket.daySeconds,
110
+ coveredSeconds: bucket.coveredSeconds,
111
+ statusDurations: bucket.statusDurations.map(
112
+ (duration: UptimeStatusDuration): StatusDuration => {
113
+ const statusId: string = duration.monitorStatusId.toString();
114
+ const status: MonitorStatus | undefined = statusById[statusId];
115
+
116
+ return {
117
+ label: status?.name || "Unknown",
118
+ seconds: duration.seconds,
119
+ color: status?.color || props.defaultBarColor,
120
+ isDowntime: downtimeIds.includes(statusId),
121
+ };
122
+ },
123
+ ),
124
+ };
125
+ });
126
+ }, [
127
+ props.uptimeBuckets,
128
+ props.monitorStatuses,
129
+ props.downtimeMonitorStatuses,
130
+ props.defaultBarColor,
131
+ ]);
132
+
65
133
  if (props.isLoading) {
66
134
  return <ComponentLoader />;
67
135
  }
@@ -80,6 +148,7 @@ const MonitorUptimeGraph: FunctionComponent<ComponentProps> = (
80
148
  startDate={props.startDate}
81
149
  endDate={props.endDate}
82
150
  events={events}
151
+ dayReadings={dayReadings}
83
152
  defaultBarColor={props.defaultBarColor}
84
153
  height={props.height}
85
154
  barColorRules={barColorRules}
@@ -33,6 +33,18 @@ export interface TelemetryViewerProps<T> {
33
33
  error?: string | undefined;
34
34
  onRefresh?: (() => void) | undefined;
35
35
  emptyMessage?: string | undefined;
36
+ /*
37
+ * Replaces the default "No results / try adjusting filters" block when the
38
+ * list comes back empty.
39
+ *
40
+ * For most signals the default is right: an empty list means the filters
41
+ * are too narrow. For a signal that a project may not be SENDING at all,
42
+ * it is usually wrong — the reader needs the setup guide, not an
43
+ * invitation to widen a time range over data that does not exist. Takes
44
+ * precedence over `emptyMessage`; nothing is rendered around it, so the
45
+ * caller owns the whole empty area.
46
+ */
47
+ emptyContent?: ReactNode;
36
48
 
37
49
  // -- Layout --
38
50
  /** Render one item row in the main list. */
@@ -296,18 +308,22 @@ function TelemetryViewerInner<T>(props: TelemetryViewerProps<T>): ReactElement {
296
308
  <ComponentLoader />
297
309
  </div>
298
310
  ) : props.items.length === 0 ? (
299
- <div className="flex h-48 flex-col items-center justify-center gap-2 px-6 text-center">
300
- <Icon
301
- icon={IconProp.Search}
302
- className="h-8 w-8 text-gray-300"
303
- />
304
- <p className="text-sm font-medium text-gray-500">
305
- {props.emptyMessage || "No results"}
306
- </p>
307
- <p className="text-xs text-gray-400">
308
- Try adjusting filters or time range.
309
- </p>
310
- </div>
311
+ props.emptyContent !== undefined ? (
312
+ <>{props.emptyContent}</>
313
+ ) : (
314
+ <div className="flex h-48 flex-col items-center justify-center gap-2 px-6 text-center">
315
+ <Icon
316
+ icon={IconProp.Search}
317
+ className="h-8 w-8 text-gray-300"
318
+ />
319
+ <p className="text-sm font-medium text-gray-500">
320
+ {props.emptyMessage || "No results"}
321
+ </p>
322
+ <p className="text-xs text-gray-400">
323
+ Try adjusting filters or time range.
324
+ </p>
325
+ </div>
326
+ )
311
327
  ) : (
312
328
  <ul className="divide-y divide-gray-100">
313
329
  {props.items.map((item: T, index: number) => {