@oneuptime/common 12.0.13 → 12.0.14

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 (376) hide show
  1. package/Models/DatabaseModels/OnCallDutyPolicySchedule.ts +14 -0
  2. package/Models/DatabaseModels/UserCall.ts +94 -3
  3. package/Models/DatabaseModels/UserEmail.ts +94 -3
  4. package/Models/DatabaseModels/UserIncomingCallNumber.ts +94 -3
  5. package/Models/DatabaseModels/UserPush.ts +37 -0
  6. package/Models/DatabaseModels/UserSMS.ts +94 -3
  7. package/Models/DatabaseModels/UserWhatsApp.ts +93 -2
  8. package/Server/API/BaseAPI.ts +79 -14
  9. package/Server/API/UserCallAPI.ts +53 -45
  10. package/Server/API/UserEmailAPI.ts +53 -44
  11. package/Server/API/UserIncomingCallNumberAPI.ts +48 -38
  12. package/Server/API/UserNotificationMethodAdminAPI.ts +481 -0
  13. package/Server/API/UserPushAPI.ts +126 -2
  14. package/Server/API/UserSmsAPI.ts +53 -44
  15. package/Server/API/UserWhatsAppAPI.ts +53 -50
  16. package/Server/Infrastructure/Postgres/SchemaMigrations/1787142779538-MigrationName.ts +154 -0
  17. package/Server/Infrastructure/Postgres/SchemaMigrations/1787156982416-MigrationName.ts +17 -0
  18. package/Server/Infrastructure/Postgres/SchemaMigrations/1787700000000-FixTotpOtpUrlAlgorithm.ts +45 -0
  19. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
  20. package/Server/Middleware/VerificationCodeRateLimit.ts +586 -0
  21. package/Server/Services/AnalyticsDatabaseService.ts +169 -0
  22. package/Server/Services/DatabaseService.ts +101 -0
  23. package/Server/Services/LogAggregationService.ts +5 -0
  24. package/Server/Services/PushNotificationService.ts +127 -12
  25. package/Server/Services/TeamMemberService.ts +53 -13
  26. package/Server/Services/UserCallService.ts +55 -24
  27. package/Server/Services/UserEmailService.ts +83 -17
  28. package/Server/Services/UserIncomingCallNumberService.ts +49 -85
  29. package/Server/Services/UserNotificationMethodAdminService.ts +1527 -0
  30. package/Server/Services/UserNotificationRuleService.ts +26 -0
  31. package/Server/Services/UserPushService.ts +89 -0
  32. package/Server/Services/UserSmsService.ts +50 -17
  33. package/Server/Services/UserWhatsAppService.ts +48 -15
  34. package/Server/Types/Database/QueryHelper.ts +30 -0
  35. package/Server/Utils/AnalyticsDatabase/QuerySettingsHelper.ts +59 -0
  36. package/Server/Utils/ChannelVerification.ts +510 -0
  37. package/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.ts +13 -16
  38. package/Server/Utils/Monitor/MonitorAlert.ts +24 -21
  39. package/Server/Utils/Monitor/MonitorIncident.ts +21 -18
  40. package/Server/Utils/Monitor/MonitorResourceContext.ts +107 -0
  41. package/Server/Utils/Monitor/MonitorStepResourceIdentity.ts +442 -0
  42. package/Server/Utils/Monitor/SeriesResourceLabels.ts +3 -3
  43. package/Server/Utils/Monitor/SeriesResourceLinker.ts +96 -28
  44. package/Server/Utils/TotpAuth.ts +137 -9
  45. package/Server/Utils/UserRegistrationToken.ts +182 -0
  46. package/Server/Utils/VerificationCode.ts +134 -0
  47. package/Tests/App/Dashboard/AdminNotificationRulesPage.test.tsx +173 -94
  48. package/Tests/App/Dashboard/AdminUserNotificationMethodsPage.test.tsx +1151 -0
  49. package/Tests/App/Dashboard/AdminUserOnCallPages.test.tsx +905 -0
  50. package/Tests/App/Dashboard/DashboardCommandPalette.test.tsx +338 -0
  51. package/Tests/App/Dashboard/InvestigationPanel.test.tsx +1 -1
  52. package/Tests/App/Dashboard/MonitorTypePicker.test.tsx +28 -0
  53. package/Tests/Models/UserPushCriticalAlertColumn.test.ts +172 -0
  54. package/Tests/Server/API/BaseAPIGetListParallel.test.ts +519 -0
  55. package/Tests/Server/API/NotificationChannelVerificationAPI.test.ts +543 -0
  56. package/Tests/Server/API/UserIncomingCallNumberApi.test.ts +108 -18
  57. package/Tests/Server/API/UserNotificationMethodAdminAPI.test.ts +963 -0
  58. package/Tests/Server/API/UserPushCriticalAlertsApi.test.ts +114 -0
  59. package/Tests/Server/API/UserSmsApi.test.ts +107 -14
  60. package/Tests/Server/API/UserTotpAuthAPI.test.ts +427 -0
  61. package/Tests/Server/Infrastructure/Postgres/FixTotpOtpUrlAlgorithmMigration.test.ts +315 -0
  62. package/Tests/Server/Middleware/VerificationCodeRateLimit.test.ts +775 -0
  63. package/Tests/Server/Services/AddNetworkDeviceReachabilityColumnsMigration.test.ts +10 -62
  64. package/Tests/Server/Services/AnalyticsDatabasePaginationStability.test.ts +33 -13
  65. package/Tests/Server/Services/AnalyticsDatabaseSortKeyBoundary.test.ts +658 -0
  66. package/Tests/Server/Services/CriticalOnCallAlertDelivery.test.ts +458 -0
  67. package/Tests/Server/Services/CriticalPushAlertPayload.test.ts +301 -0
  68. package/Tests/Server/Services/DatabaseServiceAtomicIncrement.test.ts +143 -0
  69. package/Tests/Server/Services/DeliverNotificationForRuleExtraction.test.ts +1 -0
  70. package/Tests/Server/Services/LogAggregationScanMemory.test.ts +421 -0
  71. package/Tests/Server/Services/LogAggregationService.test.ts +2 -2
  72. package/Tests/Server/Services/NotificationChannelCodeIssuance.test.ts +401 -0
  73. package/Tests/Server/Services/PushNotificationDeliveryOptions.test.ts +204 -0
  74. package/Tests/Server/Services/TeamMemberAutoAcceptInvitation.test.ts +21 -1
  75. package/Tests/Server/Services/TeamMemberInviteRegistrationToken.test.ts +290 -0
  76. package/Tests/Server/Services/UserNotificationMethodAdminService.test.ts +948 -0
  77. package/Tests/Server/Services/UserNotificationRuleExecuteItem.test.ts +1 -0
  78. package/Tests/Server/Services/UserPushCriticalAlertToggle.test.ts +255 -0
  79. package/Tests/Server/Services/UserTotpAuthEnrolment.test.ts +218 -0
  80. package/Tests/Server/TestingUtils/AuthenticatorApp.ts +248 -0
  81. package/Tests/Server/Types/Database/Permissions/NotificationChannelVerificationColumns.test.ts +135 -0
  82. package/Tests/Server/Types/Database/Permissions/OnCallScheduleRelationSelect.test.ts +504 -0
  83. package/Tests/Server/Types/Database/QueryHelperFindWithSameTextAnyOf.test.ts +91 -0
  84. package/Tests/Server/Types/Workflow/Components/JsonToText.test.ts +384 -0
  85. package/Tests/Server/Types/Workflow/Components/MergeJson.test.ts +403 -0
  86. package/Tests/Server/Utils/Browser.test.ts +162 -0
  87. package/Tests/Server/Utils/ChannelVerification.test.ts +689 -0
  88. package/Tests/Server/Utils/Express.test.ts +371 -0
  89. package/Tests/Server/Utils/Monitor/Criteria/CustomCodeMonitorCriteria.test.ts +707 -0
  90. package/Tests/Server/Utils/Monitor/Criteria/DnsMonitorCriteria.test.ts +698 -0
  91. package/Tests/Server/Utils/Monitor/Criteria/IncomingEmailBodyCriteria.test.ts +243 -0
  92. package/Tests/Server/Utils/Monitor/Criteria/IncomingRequestBodyCriteria.test.ts +278 -0
  93. package/Tests/Server/Utils/Monitor/MonitorAlertResourceLinking.test.ts +160 -20
  94. package/Tests/Server/Utils/Monitor/MonitorDependencySuppressionCreatorSkip.test.ts +13 -8
  95. package/Tests/Server/Utils/Monitor/MonitorIncidentResourceLinking.test.ts +162 -18
  96. package/Tests/Server/Utils/Monitor/MonitorResourceContext.test.ts +349 -0
  97. package/Tests/Server/Utils/Monitor/MonitorStepResourceIdentity.test.ts +690 -0
  98. package/Tests/Server/Utils/Monitor/MonitorSummaryPersistence.test.ts +7 -2
  99. package/Tests/Server/Utils/Monitor/SeriesResourceLinker.test.ts +92 -18
  100. package/Tests/Server/Utils/PushNotificationUtilCreators.test.ts +267 -0
  101. package/Tests/Server/Utils/Telemetry/OneuptimeLabel.test.ts +439 -0
  102. package/Tests/Server/Utils/TotpAuth.test.ts +719 -0
  103. package/Tests/Server/Utils/UserRegistrationToken.test.ts +322 -0
  104. package/Tests/Server/Utils/VerificationCode.test.ts +296 -0
  105. package/Tests/Types/AutoRemediation/AutoRemediationSuggestionStatus.test.ts +192 -0
  106. package/Tests/Types/Call/CallRequest.test.ts +189 -0
  107. package/Tests/Types/Dashboard/DashboardSize.test.ts +226 -0
  108. package/Tests/Types/Incident/IncidentSlaStatus.test.ts +191 -0
  109. package/Tests/Types/Metrics/MetricDashboardMetricType.test.ts +278 -0
  110. package/Tests/Types/Metrics/MetricPipelineRuleType.test.ts +517 -0
  111. package/Tests/Types/Monitor/IncomingMonitorDefaultCriteria.test.ts +470 -0
  112. package/Tests/Types/Monitor/MonitorCriteriaInstance.test.ts +6 -4
  113. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCatalog.test.ts +262 -5
  114. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCoverage.test.ts +1 -1
  115. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationNotificationMode.test.ts +1 -1
  116. package/Tests/Types/Monitor/ServiceAlertTemplates.test.ts +905 -0
  117. package/Tests/Types/Service/ServiceLanguage.test.ts +255 -0
  118. package/Tests/Types/TextRandomGeneration.test.ts +211 -0
  119. package/Tests/UI/Components/Button.test.tsx +175 -1
  120. package/Tests/UI/Components/CodeBlockLanguages.test.tsx +236 -0
  121. package/Tests/UI/Components/CommandPalette/CommandPalette.test.tsx +568 -0
  122. package/Tests/UI/Components/CommandPalette/CommandPaletteKeyboard.test.tsx +239 -0
  123. package/Tests/UI/Components/CommandPalette/CommandPaletteProviders.test.tsx +400 -0
  124. package/Tests/UI/Components/CommandPalette/PaletteFilter.test.ts +234 -0
  125. package/Tests/UI/Components/CommandPalette/RecentCommands.test.ts +97 -0
  126. package/Tests/UI/Components/CountModelSideMenuItemGuard.test.tsx +198 -0
  127. package/Tests/UI/Components/FeedItemSafeMode.test.tsx +1 -1
  128. package/Tests/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.test.ts +267 -0
  129. package/Tests/UI/Components/List.test.tsx +9 -2
  130. package/Tests/UI/Components/ListLoadingStates.test.tsx +173 -0
  131. package/Tests/UI/Components/MarkdownLazy.test.tsx +113 -0
  132. package/Tests/UI/Components/MarkdownMermaidRetry.test.tsx +110 -0
  133. package/Tests/UI/Components/ModelTable/useCustomFieldColumns.test.tsx +8 -0
  134. package/Tests/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.test.ts +692 -0
  135. package/Tests/UI/Components/MoreMenuMotion.test.tsx +118 -0
  136. package/Tests/UI/Components/NavBarCommandKGating.test.tsx +178 -0
  137. package/Tests/UI/Components/OrderedStatesList.test.tsx +19 -2
  138. package/Tests/UI/Components/SideOverMotion.test.tsx +109 -0
  139. package/Tests/UI/Components/Skeleton.test.tsx +71 -0
  140. package/Tests/UI/Components/TableLoadingStates.test.tsx +279 -0
  141. package/Tests/UI/Components/Tabs.test.tsx +24 -0
  142. package/Tests/UI/Components/Toast.test.tsx +130 -7
  143. package/Tests/UI/Components/ToastStacking.test.tsx +198 -0
  144. package/Tests/UI/Utils/Dropdown.test.ts +423 -0
  145. package/Tests/UI/Utils/ModelAPITenantHeader.test.ts +122 -0
  146. package/Tests/UI/Utils/ModelListCache.test.ts +338 -0
  147. package/Tests/UI/Utils/ProjectListCacheInvalidation.test.ts +225 -0
  148. package/Tests/Utils/API.test.ts +16 -2
  149. package/Tests/Utils/Dashboard/Components/DashboardKubernetesResourceListShared.test.ts +347 -0
  150. package/Tests/Utils/Dashboard/Components/DashboardListSharedArgs.test.ts +259 -0
  151. package/Tests/Utils/Dashboard/Components/DashboardMonitorListComponent.test.ts +466 -0
  152. package/Tests/Utils/Rum/UrlScrubberSegments.test.ts +362 -0
  153. package/Types/Email/EmailTemplateType.ts +1 -0
  154. package/Types/Monitor/MonitorCriteriaInstance.ts +41 -20
  155. package/Types/Monitor/Recommendation/MonitorRecommendationCatalog.ts +131 -8
  156. package/Types/Monitor/Recommendation/MonitorRecommendationTypes.ts +48 -8
  157. package/Types/Monitor/ServiceAlertTemplates.ts +1352 -0
  158. package/Types/Permission.ts +35 -0
  159. package/Types/PushNotification/AndroidNotificationChannel.ts +26 -0
  160. package/Types/PushNotification/PushNotificationMessage.ts +15 -0
  161. package/Types/PushNotification/PushNotificationRequest.ts +8 -16
  162. package/Types/Service/ServiceLanguage.ts +144 -0
  163. package/Types/Text.ts +111 -19
  164. package/UI/Components/Button/Button.tsx +27 -14
  165. package/UI/Components/CodeBlock/CodeBlock.tsx +54 -7
  166. package/UI/Components/CodeBlock/LanguageRegistry.ts +103 -0
  167. package/UI/Components/CommandPalette/CommandPalette.tsx +790 -0
  168. package/UI/Components/CommandPalette/PaletteFilter.ts +196 -0
  169. package/UI/Components/CommandPalette/PaletteRow.tsx +173 -0
  170. package/UI/Components/CommandPalette/RecentCommands.ts +57 -0
  171. package/UI/Components/CommandPalette/Types.ts +49 -0
  172. package/UI/Components/CommandPalette/UseProviderSearch.ts +129 -0
  173. package/UI/Components/Dropdown/Dropdown.tsx +6 -1
  174. package/UI/Components/Feed/FeedItem.tsx +1 -1
  175. package/UI/Components/List/List.tsx +37 -5
  176. package/UI/Components/List/ListSkeleton.tsx +54 -0
  177. package/UI/Components/Markdown.tsx/LazyMarkdownViewer.tsx +6 -1
  178. package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +71 -26
  179. package/UI/Components/Modal/Modal.tsx +2 -2
  180. package/UI/Components/ModelTable/BaseModelTable.tsx +1 -1
  181. package/UI/Components/ModelTable/useCustomFieldColumns.ts +11 -5
  182. package/UI/Components/MoreMenu/MoreMenu.tsx +39 -1
  183. package/UI/Components/Navbar/NavBar.tsx +14 -1
  184. package/UI/Components/OrderedStatesList/OrderedStatesList.tsx +50 -5
  185. package/UI/Components/SideMenu/CountModelSideMenuItem.tsx +40 -1
  186. package/UI/Components/SideOver/SideOver.tsx +37 -2
  187. package/UI/Components/Skeleton/Skeleton.tsx +53 -0
  188. package/UI/Components/Table/Table.tsx +74 -44
  189. package/UI/Components/Table/TableSkeletonRows.tsx +141 -0
  190. package/UI/Components/Tabs/Tab.tsx +1 -1
  191. package/UI/Components/Toast/Toast.tsx +118 -53
  192. package/UI/Components/Toast/ToastInit.tsx +82 -32
  193. package/UI/Styles/Theme.css +40 -0
  194. package/UI/Utils/ModelAPI/ModelAPI.ts +20 -10
  195. package/UI/Utils/ModelListCache.ts +188 -0
  196. package/UI/Utils/Project.ts +29 -0
  197. package/Utils/API.ts +6 -1
  198. package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js +14 -0
  199. package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js.map +1 -1
  200. package/build/dist/Models/DatabaseModels/UserCall.js +101 -3
  201. package/build/dist/Models/DatabaseModels/UserCall.js.map +1 -1
  202. package/build/dist/Models/DatabaseModels/UserEmail.js +101 -3
  203. package/build/dist/Models/DatabaseModels/UserEmail.js.map +1 -1
  204. package/build/dist/Models/DatabaseModels/UserIncomingCallNumber.js +101 -3
  205. package/build/dist/Models/DatabaseModels/UserIncomingCallNumber.js.map +1 -1
  206. package/build/dist/Models/DatabaseModels/UserPush.js +38 -0
  207. package/build/dist/Models/DatabaseModels/UserPush.js.map +1 -1
  208. package/build/dist/Models/DatabaseModels/UserSMS.js +101 -3
  209. package/build/dist/Models/DatabaseModels/UserSMS.js.map +1 -1
  210. package/build/dist/Models/DatabaseModels/UserWhatsApp.js +100 -2
  211. package/build/dist/Models/DatabaseModels/UserWhatsApp.js.map +1 -1
  212. package/build/dist/Server/API/BaseAPI.js +69 -13
  213. package/build/dist/Server/API/BaseAPI.js.map +1 -1
  214. package/build/dist/Server/API/UserCallAPI.js +42 -38
  215. package/build/dist/Server/API/UserCallAPI.js.map +1 -1
  216. package/build/dist/Server/API/UserEmailAPI.js +42 -38
  217. package/build/dist/Server/API/UserEmailAPI.js.map +1 -1
  218. package/build/dist/Server/API/UserIncomingCallNumberAPI.js +37 -32
  219. package/build/dist/Server/API/UserIncomingCallNumberAPI.js.map +1 -1
  220. package/build/dist/Server/API/UserNotificationMethodAdminAPI.js +341 -0
  221. package/build/dist/Server/API/UserNotificationMethodAdminAPI.js.map +1 -0
  222. package/build/dist/Server/API/UserPushAPI.js +95 -2
  223. package/build/dist/Server/API/UserPushAPI.js.map +1 -1
  224. package/build/dist/Server/API/UserSmsAPI.js +42 -38
  225. package/build/dist/Server/API/UserSmsAPI.js.map +1 -1
  226. package/build/dist/Server/API/UserWhatsAppAPI.js +42 -40
  227. package/build/dist/Server/API/UserWhatsAppAPI.js.map +1 -1
  228. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787142779538-MigrationName.js +89 -0
  229. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787142779538-MigrationName.js.map +1 -0
  230. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787156982416-MigrationName.js +12 -0
  231. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787156982416-MigrationName.js.map +1 -0
  232. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787700000000-FixTotpOtpUrlAlgorithm.js +40 -0
  233. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787700000000-FixTotpOtpUrlAlgorithm.js.map +1 -0
  234. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
  235. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  236. package/build/dist/Server/Middleware/VerificationCodeRateLimit.js +393 -0
  237. package/build/dist/Server/Middleware/VerificationCodeRateLimit.js.map +1 -0
  238. package/build/dist/Server/Services/AnalyticsDatabaseService.js +136 -0
  239. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  240. package/build/dist/Server/Services/DatabaseService.js +74 -0
  241. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  242. package/build/dist/Server/Services/LogAggregationService.js +5 -0
  243. package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
  244. package/build/dist/Server/Services/PushNotificationService.js +69 -30
  245. package/build/dist/Server/Services/PushNotificationService.js.map +1 -1
  246. package/build/dist/Server/Services/TeamMemberService.js +43 -7
  247. package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
  248. package/build/dist/Server/Services/UserCallService.js +54 -22
  249. package/build/dist/Server/Services/UserCallService.js.map +1 -1
  250. package/build/dist/Server/Services/UserEmailService.js +78 -17
  251. package/build/dist/Server/Services/UserEmailService.js.map +1 -1
  252. package/build/dist/Server/Services/UserIncomingCallNumberService.js +45 -74
  253. package/build/dist/Server/Services/UserIncomingCallNumberService.js.map +1 -1
  254. package/build/dist/Server/Services/UserNotificationMethodAdminService.js +1117 -0
  255. package/build/dist/Server/Services/UserNotificationMethodAdminService.js.map +1 -0
  256. package/build/dist/Server/Services/UserNotificationRuleService.js +20 -0
  257. package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
  258. package/build/dist/Server/Services/UserPushService.js +77 -0
  259. package/build/dist/Server/Services/UserPushService.js.map +1 -1
  260. package/build/dist/Server/Services/UserSmsService.js +50 -17
  261. package/build/dist/Server/Services/UserSmsService.js.map +1 -1
  262. package/build/dist/Server/Services/UserWhatsAppService.js +48 -15
  263. package/build/dist/Server/Services/UserWhatsAppService.js.map +1 -1
  264. package/build/dist/Server/Types/Database/QueryHelper.js +27 -0
  265. package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
  266. package/build/dist/Server/Utils/AnalyticsDatabase/QuerySettingsHelper.js +46 -0
  267. package/build/dist/Server/Utils/AnalyticsDatabase/QuerySettingsHelper.js.map +1 -1
  268. package/build/dist/Server/Utils/ChannelVerification.js +348 -0
  269. package/build/dist/Server/Utils/ChannelVerification.js.map +1 -0
  270. package/build/dist/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.js +13 -12
  271. package/build/dist/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.js.map +1 -1
  272. package/build/dist/Server/Utils/Monitor/MonitorAlert.js +20 -17
  273. package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
  274. package/build/dist/Server/Utils/Monitor/MonitorIncident.js +17 -14
  275. package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
  276. package/build/dist/Server/Utils/Monitor/MonitorResourceContext.js +95 -0
  277. package/build/dist/Server/Utils/Monitor/MonitorResourceContext.js.map +1 -0
  278. package/build/dist/Server/Utils/Monitor/MonitorStepResourceIdentity.js +313 -0
  279. package/build/dist/Server/Utils/Monitor/MonitorStepResourceIdentity.js.map +1 -0
  280. package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js +3 -3
  281. package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js +67 -26
  282. package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js.map +1 -1
  283. package/build/dist/Server/Utils/TotpAuth.js +127 -8
  284. package/build/dist/Server/Utils/TotpAuth.js.map +1 -1
  285. package/build/dist/Server/Utils/UserRegistrationToken.js +170 -0
  286. package/build/dist/Server/Utils/UserRegistrationToken.js.map +1 -0
  287. package/build/dist/Server/Utils/VerificationCode.js +120 -0
  288. package/build/dist/Server/Utils/VerificationCode.js.map +1 -0
  289. package/build/dist/Types/Email/EmailTemplateType.js +1 -0
  290. package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
  291. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +42 -21
  292. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
  293. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js +77 -5
  294. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js.map +1 -1
  295. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js +18 -6
  296. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js.map +1 -1
  297. package/build/dist/Types/Monitor/ServiceAlertTemplates.js +1028 -0
  298. package/build/dist/Types/Monitor/ServiceAlertTemplates.js.map +1 -0
  299. package/build/dist/Types/Permission.js +33 -0
  300. package/build/dist/Types/Permission.js.map +1 -1
  301. package/build/dist/Types/PushNotification/AndroidNotificationChannel.js +27 -0
  302. package/build/dist/Types/PushNotification/AndroidNotificationChannel.js.map +1 -0
  303. package/build/dist/Types/Service/ServiceLanguage.js +96 -0
  304. package/build/dist/Types/Service/ServiceLanguage.js.map +1 -0
  305. package/build/dist/Types/Text.js +82 -17
  306. package/build/dist/Types/Text.js.map +1 -1
  307. package/build/dist/UI/Components/Button/Button.js +22 -14
  308. package/build/dist/UI/Components/Button/Button.js.map +1 -1
  309. package/build/dist/UI/Components/CodeBlock/CodeBlock.js +35 -3
  310. package/build/dist/UI/Components/CodeBlock/CodeBlock.js.map +1 -1
  311. package/build/dist/UI/Components/CodeBlock/LanguageRegistry.js +57 -0
  312. package/build/dist/UI/Components/CodeBlock/LanguageRegistry.js.map +1 -0
  313. package/build/dist/UI/Components/CommandPalette/CommandPalette.js +439 -0
  314. package/build/dist/UI/Components/CommandPalette/CommandPalette.js.map +1 -0
  315. package/build/dist/UI/Components/CommandPalette/PaletteFilter.js +135 -0
  316. package/build/dist/UI/Components/CommandPalette/PaletteFilter.js.map +1 -0
  317. package/build/dist/UI/Components/CommandPalette/PaletteRow.js +83 -0
  318. package/build/dist/UI/Components/CommandPalette/PaletteRow.js.map +1 -0
  319. package/build/dist/UI/Components/CommandPalette/RecentCommands.js +47 -0
  320. package/build/dist/UI/Components/CommandPalette/RecentCommands.js.map +1 -0
  321. package/build/dist/UI/Components/CommandPalette/Types.js +2 -0
  322. package/build/dist/UI/Components/CommandPalette/Types.js.map +1 -0
  323. package/build/dist/UI/Components/CommandPalette/UseProviderSearch.js +88 -0
  324. package/build/dist/UI/Components/CommandPalette/UseProviderSearch.js.map +1 -0
  325. package/build/dist/UI/Components/Dropdown/Dropdown.js +6 -1
  326. package/build/dist/UI/Components/Dropdown/Dropdown.js.map +1 -1
  327. package/build/dist/UI/Components/Feed/FeedItem.js +1 -1
  328. package/build/dist/UI/Components/Feed/FeedItem.js.map +1 -1
  329. package/build/dist/UI/Components/List/List.js +19 -5
  330. package/build/dist/UI/Components/List/List.js.map +1 -1
  331. package/build/dist/UI/Components/List/ListSkeleton.js +21 -0
  332. package/build/dist/UI/Components/List/ListSkeleton.js.map +1 -0
  333. package/build/dist/UI/Components/Markdown.tsx/LazyMarkdownViewer.js.map +1 -1
  334. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +52 -25
  335. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
  336. package/build/dist/UI/Components/Modal/Modal.js +2 -2
  337. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +1 -1
  338. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  339. package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js +9 -2
  340. package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js.map +1 -1
  341. package/build/dist/UI/Components/MoreMenu/MoreMenu.js +32 -1
  342. package/build/dist/UI/Components/MoreMenu/MoreMenu.js.map +1 -1
  343. package/build/dist/UI/Components/Navbar/NavBar.js +5 -1
  344. package/build/dist/UI/Components/Navbar/NavBar.js.map +1 -1
  345. package/build/dist/UI/Components/OrderedStatesList/OrderedStatesList.js +29 -5
  346. package/build/dist/UI/Components/OrderedStatesList/OrderedStatesList.js.map +1 -1
  347. package/build/dist/UI/Components/SideMenu/CountModelSideMenuItem.js +23 -1
  348. package/build/dist/UI/Components/SideMenu/CountModelSideMenuItem.js.map +1 -1
  349. package/build/dist/UI/Components/SideOver/SideOver.js +23 -2
  350. package/build/dist/UI/Components/SideOver/SideOver.js.map +1 -1
  351. package/build/dist/UI/Components/Skeleton/Skeleton.js +23 -0
  352. package/build/dist/UI/Components/Skeleton/Skeleton.js.map +1 -0
  353. package/build/dist/UI/Components/Table/Table.js +35 -24
  354. package/build/dist/UI/Components/Table/Table.js.map +1 -1
  355. package/build/dist/UI/Components/Table/TableSkeletonRows.js +52 -0
  356. package/build/dist/UI/Components/Table/TableSkeletonRows.js.map +1 -0
  357. package/build/dist/UI/Components/Tabs/Tab.js +1 -1
  358. package/build/dist/UI/Components/Tabs/Tab.js.map +1 -1
  359. package/build/dist/UI/Components/Toast/Toast.js +77 -23
  360. package/build/dist/UI/Components/Toast/Toast.js.map +1 -1
  361. package/build/dist/UI/Components/Toast/ToastInit.js +37 -15
  362. package/build/dist/UI/Components/Toast/ToastInit.js.map +1 -1
  363. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +18 -9
  364. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
  365. package/build/dist/UI/Utils/ModelListCache.js +126 -0
  366. package/build/dist/UI/Utils/ModelListCache.js.map +1 -0
  367. package/build/dist/UI/Utils/Project.js +24 -1
  368. package/build/dist/UI/Utils/Project.js.map +1 -1
  369. package/build/dist/Utils/API.js +6 -1
  370. package/build/dist/Utils/API.js.map +1 -1
  371. package/jest.config.json +1 -1
  372. package/package.json +2 -3
  373. package/Server/Utils/Monitor/MonitorClusterContext.ts +0 -182
  374. package/Tests/__mocks__/otpauth.js +0 -30
  375. package/build/dist/Server/Utils/Monitor/MonitorClusterContext.js +0 -141
  376. package/build/dist/Server/Utils/Monitor/MonitorClusterContext.js.map +0 -1
@@ -1,6 +1,5 @@
1
1
  import { GetReactElementFunction } from "../../Types/FunctionTypes";
2
2
  import ActionButtonSchema from "../ActionButton/ActionButtonSchema";
3
- import ComponentLoader from "../ComponentLoader/ComponentLoader";
4
3
  import Field from "../Detail/Field";
5
4
  import ErrorMessage from "../ErrorMessage/ErrorMessage";
6
5
  import FilterViewer from "../Filters/FilterViewer";
@@ -8,6 +7,7 @@ import FilterType from "../Filters/Types/Filter";
8
7
  import FilterData from "../Filters/Types/FilterData";
9
8
  import Pagination from "../Pagination/Pagination";
10
9
  import ListBody from "./ListBody";
10
+ import ListSkeleton from "./ListSkeleton";
11
11
  import { ListDetailProps } from "./ListRow";
12
12
  import GenericObject from "../../../Types/GenericObject";
13
13
  import React, { ReactElement } from "react";
@@ -67,12 +67,30 @@ type ListFunction = <T extends GenericObject>(
67
67
  const List: ListFunction = <T extends GenericObject>(
68
68
  props: ComponentProps<T>,
69
69
  ): ReactElement => {
70
+ /*
71
+ * A refetch with cards already on screen (pagination, sort, refresh - the
72
+ * parent never clears `data` while fetching) keeps those cards visible and
73
+ * dims them; skeletons are only for a load with nothing to show yet.
74
+ */
75
+ const isRefetchingWithData: boolean =
76
+ props.isLoading && props.data.length > 0;
77
+
70
78
  const getListbody: GetReactElementFunction = (): ReactElement => {
71
- if (props.isLoading) {
72
- return <ComponentLoader />;
79
+ if (props.isLoading && props.data.length === 0) {
80
+ return (
81
+ <ListSkeleton
82
+ fieldsCount={props.fields.length}
83
+ itemsOnPage={props.itemsOnPage}
84
+ />
85
+ );
73
86
  }
74
87
 
75
- if (props.error) {
88
+ /*
89
+ * Loading still wins over error and empty (same priority as before the
90
+ * skeletons): while a refetch is in flight the stale cards render, never
91
+ * a stale error or a premature "no items".
92
+ */
93
+ if (!props.isLoading && props.error) {
76
94
  return (
77
95
  <div className="p-6">
78
96
  <ErrorMessage
@@ -145,7 +163,21 @@ const List: ListFunction = <T extends GenericObject>(
145
163
  }
146
164
  }}
147
165
  >
148
- {getListbody()}
166
+ {/*
167
+ * Dims the stale cards while a refetch is in flight and blocks
168
+ * clicks on them - actions against data about to be replaced
169
+ * are a race.
170
+ */}
171
+ <div
172
+ data-testid="list-content"
173
+ className={
174
+ isRefetchingWithData
175
+ ? "opacity-60 pointer-events-none transition-opacity duration-150"
176
+ : "transition-opacity duration-150"
177
+ }
178
+ >
179
+ {getListbody()}
180
+ </div>
149
181
  </DragDropContext>
150
182
  {!props.disablePagination && (
151
183
  <div className="mt-5 -mb-6">
@@ -0,0 +1,54 @@
1
+ import Skeleton from "../Skeleton/Skeleton";
2
+ import React, { FunctionComponent, ReactElement } from "react";
3
+
4
+ export interface ComponentProps {
5
+ /*
6
+ * One placeholder line per field the real ListRow will render, so the
7
+ * cards are the right height before the data arrives.
8
+ */
9
+ fieldsCount: number;
10
+ itemsOnPage: number;
11
+ }
12
+
13
+ /*
14
+ * Card-shaped placeholders for the list's first load, matching ListBody's
15
+ * wrapper (space-y-6 p-6 border-t) so nothing shifts when real rows land.
16
+ */
17
+ const ListSkeleton: FunctionComponent<ComponentProps> = (
18
+ props: ComponentProps,
19
+ ): ReactElement => {
20
+ // Enough cards to look like a page without painting offscreen ones.
21
+ const cardCount: number = Math.max(1, Math.min(props.itemsOnPage || 0, 6));
22
+ const cardIndexes: Array<number> = Array.from(Array(cardCount).keys());
23
+ const lineIndexes: Array<number> = Array.from(
24
+ Array(Math.max(1, props.fieldsCount)).keys(),
25
+ );
26
+
27
+ return (
28
+ <div
29
+ data-testid="list-skeleton-loader"
30
+ role="status"
31
+ aria-live="polite"
32
+ className="space-y-6 p-6 border-t border-gray-200"
33
+ >
34
+ <span className="sr-only">Loading...</span>
35
+ {cardIndexes.map((cardIndex: number) => {
36
+ return (
37
+ <div key={cardIndex} className="space-y-3">
38
+ {lineIndexes.map((lineIndex: number) => {
39
+ return (
40
+ <Skeleton
41
+ key={lineIndex}
42
+ className="h-4"
43
+ widthVariantIndex={cardIndex + lineIndex}
44
+ />
45
+ );
46
+ })}
47
+ </div>
48
+ );
49
+ })}
50
+ </div>
51
+ );
52
+ };
53
+
54
+ export default ListSkeleton;
@@ -1,4 +1,9 @@
1
- import { ComponentProps } from "./MarkdownViewer";
1
+ /*
2
+ * Type-only on purpose: the sole static edge to MarkdownViewer must be
3
+ * erasable, so the viewer (and the mermaid chunk it lazily pulls) stays out
4
+ * of every eager bundle that imports this wrapper.
5
+ */
6
+ import type { ComponentProps } from "./MarkdownViewer";
2
7
  import React, {
3
8
  FunctionComponent,
4
9
  LazyExoticComponent,
@@ -76,7 +76,6 @@ SyntaxHighlighter.registerLanguage("ruby", ruby);
76
76
  SyntaxHighlighter.registerLanguage("php", php);
77
77
  SyntaxHighlighter.registerLanguage("graphql", graphql);
78
78
  SyntaxHighlighter.registerLanguage("http", http);
79
- import mermaid from "mermaid";
80
79
  import DOMPurify from "dompurify";
81
80
  import OneUptimeDate from "../../../Types/Date";
82
81
  import { Theme, useTheme } from "../../Utils/Theme";
@@ -90,7 +89,48 @@ import { Theme, useTheme } from "../../Utils/Theme";
90
89
  const ISO_8601_REGEX: RegExp =
91
90
  /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,6})?Z$/;
92
91
 
93
- const initializeMermaid: (theme: Theme) => void = (theme: Theme): void => {
92
+ /*
93
+ * mermaid's pre-bundled build is ~4.5MB — by far the heaviest dependency of
94
+ * this component — so it is loaded on demand the first time a mermaid fence
95
+ * actually renders. Plain markdown (the overwhelmingly common case) never
96
+ * pays for it: there is no static import and no module-scope initialization.
97
+ * The promise is cached module-wide so multiple diagrams share one load, and
98
+ * theme setup runs only after the load resolves.
99
+ */
100
+ type MermaidApi = typeof import("mermaid").default;
101
+
102
+ let mermaidLoadPromise: Promise<MermaidApi> | null = null;
103
+
104
+ const loadMermaid: () => Promise<MermaidApi> = (): Promise<MermaidApi> => {
105
+ if (!mermaidLoadPromise) {
106
+ const loadPromise: Promise<MermaidApi> = import("mermaid").then(
107
+ (mermaidModule: { default: MermaidApi }): MermaidApi => {
108
+ return mermaidModule.default;
109
+ },
110
+ );
111
+
112
+ mermaidLoadPromise = loadPromise;
113
+
114
+ /*
115
+ * A failed chunk load (flaky network, deploy rotating hashed chunk
116
+ * names) must not be cached forever — clear the slot so the next
117
+ * diagram retries the import. The reset rides a side branch of the
118
+ * promise: the original is still returned below, so the caller that
119
+ * triggered this load sees the rejection through its own error path.
120
+ */
121
+ loadPromise.catch((): void => {
122
+ if (mermaidLoadPromise === loadPromise) {
123
+ mermaidLoadPromise = null;
124
+ }
125
+ });
126
+ }
127
+ return mermaidLoadPromise;
128
+ };
129
+
130
+ const initializeMermaid: (mermaid: MermaidApi, theme: Theme) => void = (
131
+ mermaid: MermaidApi,
132
+ theme: Theme,
133
+ ): void => {
94
134
  const isDark: boolean = theme === Theme.Dark;
95
135
 
96
136
  mermaid.initialize({
@@ -120,10 +160,8 @@ const initializeMermaid: (theme: Theme) => void = (theme: Theme): void => {
120
160
  });
121
161
  };
122
162
 
123
- initializeMermaid(Theme.Light);
124
-
125
- // Mermaid diagram component
126
- const MermaidDiagram: FunctionComponent<{ chart: string }> = ({
163
+ // Mermaid diagram component (exported so tests can exercise the lazy load directly)
164
+ export const MermaidDiagram: FunctionComponent<{ chart: string }> = ({
127
165
  chart,
128
166
  }: {
129
167
  chart: string;
@@ -136,27 +174,34 @@ const MermaidDiagram: FunctionComponent<{ chart: string }> = ({
136
174
  let isCancelled: boolean = false;
137
175
 
138
176
  const renderDiagram: () => Promise<void> = async (): Promise<void> => {
139
- if (containerRef.current) {
177
+ try {
178
+ /*
179
+ * Dynamic load first; a failed chunk load surfaces through the same
180
+ * error path as a failed render. Theme setup MUST follow the load —
181
+ * there is deliberately no module-scope initialization any more.
182
+ */
183
+ const mermaid: MermaidApi = await loadMermaid();
184
+ if (!containerRef.current || isCancelled) {
185
+ return;
186
+ }
140
187
  containerRef.current.innerHTML = "";
141
- try {
142
- initializeMermaid(theme);
143
- const id: string = `mermaid-${Math.random().toString(36).substr(2, 9)}`;
144
- const { svg } = await mermaid.render(id, chart);
145
- if (containerRef.current && !isCancelled) {
146
- containerRef.current.innerHTML = DOMPurify.sanitize(svg, {
147
- USE_PROFILES: { svg: true, svgFilters: true },
148
- ADD_TAGS: ["foreignObject"],
149
- });
150
- }
151
- } catch (error) {
152
- if (containerRef.current && !isCancelled) {
153
- const errorMessage: string = String(error);
154
- const errorEl: HTMLPreElement = document.createElement("pre");
155
- errorEl.className = "text-red-500";
156
- errorEl.textContent = `Error rendering diagram: ${errorMessage}`;
157
- containerRef.current.innerHTML = "";
158
- containerRef.current.appendChild(errorEl);
159
- }
188
+ initializeMermaid(mermaid, theme);
189
+ const id: string = `mermaid-${Math.random().toString(36).substr(2, 9)}`;
190
+ const { svg } = await mermaid.render(id, chart);
191
+ if (containerRef.current && !isCancelled) {
192
+ containerRef.current.innerHTML = DOMPurify.sanitize(svg, {
193
+ USE_PROFILES: { svg: true, svgFilters: true },
194
+ ADD_TAGS: ["foreignObject"],
195
+ });
196
+ }
197
+ } catch (error) {
198
+ if (containerRef.current && !isCancelled) {
199
+ const errorMessage: string = String(error);
200
+ const errorEl: HTMLPreElement = document.createElement("pre");
201
+ errorEl.className = "text-red-500";
202
+ errorEl.textContent = `Error rendering diagram: ${errorMessage}`;
203
+ containerRef.current.innerHTML = "";
204
+ containerRef.current.appendChild(errorEl);
160
205
  }
161
206
  }
162
207
  };
@@ -413,7 +413,7 @@ const Modal: FunctionComponent<ComponentProps> = (
413
413
  return (
414
414
  <div className="relative z-50">
415
415
  <div
416
- className={`fixed inset-0 bg-gray-950/45 backdrop-blur-[2px] transition-opacity duration-200 ease-out motion-reduce:transition-none ${
416
+ className={`fixed inset-0 bg-gray-950/45 backdrop-blur-[2px] transition-opacity duration-150 ease-out motion-reduce:transition-none ${
417
417
  hasEntered ? "opacity-100" : "opacity-0"
418
418
  }`}
419
419
  data-testid="modal-backdrop"
@@ -429,7 +429,7 @@ const Modal: FunctionComponent<ComponentProps> = (
429
429
  <div className="flex min-h-full items-end justify-center p-0 text-center sm:items-center sm:p-6">
430
430
  <div
431
431
  ref={modalRef}
432
- className={`relative flex max-h-[calc(100dvh-1rem)] w-full flex-col rounded-t-2xl border border-gray-200/80 bg-white text-left shadow-2xl ring-1 ring-black/5 transition duration-200 ease-out focus:outline-none motion-reduce:transition-none sm:my-8 sm:max-h-[calc(100dvh-4rem)] sm:rounded-xl ${modalWidthClassName} ${panelTransitionClassName}`}
432
+ className={`relative flex max-h-[calc(100dvh-1rem)] w-full flex-col rounded-t-2xl border border-gray-200/80 bg-white text-left shadow-2xl ring-1 ring-black/5 transition duration-150 ease-out focus:outline-none motion-reduce:transition-none sm:my-8 sm:max-h-[calc(100dvh-4rem)] sm:rounded-xl ${modalWidthClassName} ${panelTransitionClassName}`}
433
433
  data-testid="modal"
434
434
  aria-labelledby={titleId}
435
435
  aria-describedby={translatedDescription ? descriptionId : undefined}
@@ -47,7 +47,7 @@ import List from "../List/List";
47
47
  import { ListDetailProps } from "../List/ListRow";
48
48
  import ConfirmModal from "../Modal/ConfirmModal";
49
49
  import Modal, { ModalWidth } from "../Modal/Modal";
50
- import MarkdownViewer from "../Markdown.tsx/MarkdownViewer";
50
+ import MarkdownViewer from "../Markdown.tsx/LazyMarkdownViewer";
51
51
  import Icon from "../Icon/Icon";
52
52
  import Filter from "../ModelFilter/Filter";
53
53
  import { DropdownOption, DropdownOptionLabel } from "../Dropdown/Dropdown";
@@ -4,7 +4,7 @@ import {
4
4
  getCustomFieldColumns,
5
5
  getCustomFieldDefinitions,
6
6
  } from "./CustomFieldColumns";
7
- import ModelAPI from "../../Utils/ModelAPI/ModelAPI";
7
+ import ModelListCache from "../../Utils/ModelListCache";
8
8
  import ProjectUtil from "../../Utils/Project";
9
9
  import { Logger } from "../../Utils/Logger";
10
10
  import AnalyticsBaseModel from "../../../Models/AnalyticsModels/AnalyticsBaseModel/AnalyticsBaseModel";
@@ -96,8 +96,14 @@ const useCustomFieldColumns: UseCustomFieldColumnsFunction = <
96
96
  setError("");
97
97
 
98
98
  try {
99
- const result: ListResult<BaseModel> = await ModelAPI.getList<BaseModel>(
100
- {
99
+ /*
100
+ * Through ModelListCache rather than ModelAPI: a table mounts two
101
+ * instances of this hook (the facet bar and the column set), and both
102
+ * want the same definition list - the cache turns that into one
103
+ * request, shared for the next minute.
104
+ */
105
+ const result: ListResult<BaseModel> =
106
+ await ModelListCache.getList<BaseModel>({
101
107
  modelType: customFieldsModelType,
102
108
  query: {
103
109
  projectId: projectId,
@@ -113,8 +119,8 @@ const useCustomFieldColumns: UseCustomFieldColumnsFunction = <
113
119
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
114
120
  } as any,
115
121
  sort: {},
116
- },
117
- );
122
+ projectId: projectId,
123
+ });
118
124
 
119
125
  if (isCancelled) {
120
126
  return;
@@ -114,6 +114,13 @@ const MoreMenu: React.ForwardRefExoticComponent<
114
114
 
115
115
  const [isDropdownVisible, setDropdownVisible] = useState<boolean>(false);
116
116
 
117
+ /*
118
+ * The menu is mounted in its closed state (faded, slightly shrunk towards
119
+ * origin-top-right) and flipped open one frame later so the browser has
120
+ * something to transition from — the same pattern Modal uses.
121
+ */
122
+ const [hasMenuEntered, setHasMenuEntered] = useState<boolean>(false);
123
+
117
124
  useEffect(() => {
118
125
  setDropdownVisible(isComponentVisible);
119
126
  if (isComponentVisible) {
@@ -123,6 +130,30 @@ const MoreMenu: React.ForwardRefExoticComponent<
123
130
  }
124
131
  }, [isComponentVisible]);
125
132
 
133
+ useEffect(() => {
134
+ if (!isComponentVisible) {
135
+ setHasMenuEntered(false);
136
+ return undefined;
137
+ }
138
+
139
+ const open: () => void = (): void => {
140
+ setHasMenuEntered(true);
141
+ };
142
+
143
+ const frame: number = requestAnimationFrame(open);
144
+
145
+ /*
146
+ * A hidden or throttled tab never runs an animation frame; the timer
147
+ * skips the transition rather than leaving the menu at zero opacity.
148
+ */
149
+ const fallbackTimer: ReturnType<typeof setTimeout> = setTimeout(open, 80);
150
+
151
+ return () => {
152
+ cancelAnimationFrame(frame);
153
+ clearTimeout(fallbackTimer);
154
+ };
155
+ }, [isComponentVisible]);
156
+
126
157
  useEffect(() => {
127
158
  if (props.isDisabled && isComponentVisible) {
128
159
  setIsComponentVisible(false);
@@ -427,7 +458,14 @@ const MoreMenu: React.ForwardRefExoticComponent<
427
458
  <div
428
459
  ref={ref}
429
460
  id={menuId}
430
- className="absolute right-0 z-50 mt-2 w-56 origin-top-right rounded-lg bg-white shadow-xl ring-1 ring-gray-200 focus:outline-none py-1"
461
+ /*
462
+ * Once settled the menu carries no transform class at all: a
463
+ * lingering scale would make it the containing block for any
464
+ * position:fixed descendant (the Modal invariant).
465
+ */
466
+ className={`absolute right-0 z-50 mt-2 w-56 origin-top-right rounded-lg bg-white shadow-xl ring-1 ring-gray-200 focus:outline-none py-1 transition duration-150 ease-out motion-reduce:transition-none ${
467
+ hasMenuEntered ? "opacity-100" : "opacity-0 scale-95"
468
+ }`}
431
469
  role="menu"
432
470
  aria-orientation="vertical"
433
471
  aria-labelledby={buttonId}
@@ -65,6 +65,14 @@ export interface ComponentProps {
65
65
  description: string;
66
66
  link: URL;
67
67
  };
68
+ /*
69
+ * When true, the document-level Cmd/Ctrl+K shortcut that toggles the
70
+ * products menu is not registered — for apps where another surface (e.g. a
71
+ * command palette) owns that shortcut. The menu itself stays fully usable
72
+ * via its trigger button. Defaults to false: existing consumers keep the
73
+ * shortcut.
74
+ */
75
+ disableCommandKShortcut?: boolean | undefined;
68
76
  className?: string;
69
77
  // Legacy support for children-based usage
70
78
  children?: ReactElement | Array<ReactElement>;
@@ -165,6 +173,11 @@ const Navbar: FunctionComponent<ComponentProps> = (
165
173
 
166
174
  // Open/close the products menu with Cmd/Ctrl + K from anywhere.
167
175
  useEffect(() => {
176
+ if (props.disableCommandKShortcut) {
177
+ // Another surface (e.g. a command palette) owns Cmd/Ctrl+K.
178
+ return;
179
+ }
180
+
168
181
  const handleGlobalKeyDown: (event: KeyboardEvent) => void = (
169
182
  event: KeyboardEvent,
170
183
  ): void => {
@@ -180,7 +193,7 @@ const Navbar: FunctionComponent<ComponentProps> = (
180
193
  return () => {
181
194
  document.removeEventListener("keydown", handleGlobalKeyDown);
182
195
  };
183
- }, []);
196
+ }, [props.disableCommandKShortcut]);
184
197
 
185
198
  // More menu open/close.
186
199
  const openMoreMenu: () => void = (): void => {
@@ -1,8 +1,8 @@
1
1
  import ActionButtonSchema from "../ActionButton/ActionButtonSchema";
2
- import ComponentLoader from "../ComponentLoader/ComponentLoader";
3
2
  import ErrorMessage from "../ErrorMessage/ErrorMessage";
4
3
  import Icon, { SizeProp } from "../Icon/Icon";
5
4
  import Item from "./Item";
5
+ import Skeleton from "../Skeleton/Skeleton";
6
6
  import GenericObject from "../../../Types/GenericObject";
7
7
  import IconProp from "../../../Types/Icon/IconProp";
8
8
  import React, { ReactElement } from "react";
@@ -33,11 +33,49 @@ type OrderedStatesListFunction = <T extends GenericObject>(
33
33
  const OrderedStatesList: OrderedStatesListFunction = <T extends GenericObject>(
34
34
  props: ComponentProps<T>,
35
35
  ): ReactElement => {
36
- if (props.isLoading) {
37
- return <ComponentLoader />;
36
+ /*
37
+ * A refetch with states already on screen keeps them visible and dims
38
+ * them; the skeleton stack is only for a load with nothing to show yet.
39
+ */
40
+ const isRefetchingWithData: boolean =
41
+ Boolean(props.isLoading) && props.data.length > 0;
42
+
43
+ if (props.isLoading && props.data.length === 0) {
44
+ /*
45
+ * Three card-shaped placeholders where the ordered state cards will
46
+ * land (matching Item's rounded bordered card), so the page keeps its
47
+ * shape instead of collapsing to a centered spinner.
48
+ */
49
+ return (
50
+ <div
51
+ data-testid="ordered-states-list-skeleton-loader"
52
+ role="status"
53
+ aria-live="polite"
54
+ className="m-32 text-center"
55
+ >
56
+ <span className="sr-only">Loading...</span>
57
+ {[0, 1, 2].map((index: number) => {
58
+ return (
59
+ <div key={index} className="w-fit m-auto">
60
+ <Skeleton className="h-32 w-64 rounded p-10" />
61
+ {index < 2 && (
62
+ <div className="items-center m-10">
63
+ <Skeleton className="m-auto h-5 w-5" />
64
+ </div>
65
+ )}
66
+ </div>
67
+ );
68
+ })}
69
+ </div>
70
+ );
38
71
  }
39
72
 
40
- if (props.error) {
73
+ /*
74
+ * Loading still wins over error and empty (same priority as before the
75
+ * skeletons): while a refetch is in flight the stale states render, never
76
+ * a stale error or a premature "no items".
77
+ */
78
+ if (!props.isLoading && props.error) {
41
79
  return (
42
80
  <ErrorMessage
43
81
  message={props.error}
@@ -82,7 +120,14 @@ const OrderedStatesList: OrderedStatesListFunction = <T extends GenericObject>(
82
120
  }
83
121
 
84
122
  return (
85
- <div className="m-32 text-center">
123
+ <div
124
+ data-testid="ordered-states-list-content"
125
+ className={`m-32 text-center${
126
+ isRefetchingWithData
127
+ ? " opacity-60 pointer-events-none transition-opacity duration-150"
128
+ : " transition-opacity duration-150"
129
+ }`}
130
+ >
86
131
  {props.error && <p>{props.error}</p>}
87
132
  {!props.error &&
88
133
  props.data &&
@@ -7,7 +7,14 @@ import BaseModel from "../../../Models/DatabaseModels/DatabaseBaseModel/Database
7
7
  import { PromiseVoidFunction } from "../../../Types/FunctionTypes";
8
8
  import IconProp from "../../../Types/Icon/IconProp";
9
9
  import Link from "../../../Types/Link";
10
- import React, { ReactElement, useEffect, useState, useCallback } from "react";
10
+ import React, {
11
+ MutableRefObject,
12
+ ReactElement,
13
+ useEffect,
14
+ useRef,
15
+ useState,
16
+ useCallback,
17
+ } from "react";
11
18
  import GlobalEvents from "../../Utils/GlobalEvents";
12
19
 
13
20
  export const REFRESH_SIDEBAR_COUNT_EVENT: string = "REFRESH_SIDEBAR_COUNTS";
@@ -70,7 +77,39 @@ const CountModelSideMenuItem: <TBaseModel extends BaseModel>(
70
77
  props.onCountFetchInit,
71
78
  ]);
72
79
 
80
+ /*
81
+ * Side menus rebuild `countQuery` / `requestOptions` as fresh object
82
+ * literals on every render, so their identity changes on every parent
83
+ * re-render (which happens on every in-section navigation). Guard the fetch
84
+ * with a value-level compare - the same JSON.stringify-in-a-ref pattern
85
+ * BaseModelTable uses for its `props.query` - so only a real change
86
+ * re-issues the count request. The global refresh event below still forces
87
+ * a refetch regardless of this guard.
88
+ */
89
+ const lastFetchedModelTypeRef: MutableRefObject<
90
+ { new (): TBaseModel } | undefined
91
+ > = useRef<{ new (): TBaseModel } | undefined>(undefined);
92
+ const lastFetchedQueryJsonRef: MutableRefObject<string | null> = useRef<
93
+ string | null
94
+ >(null);
95
+
73
96
  useEffect(() => {
97
+ const queryJson: string = JSON.stringify({
98
+ countQuery: props.countQuery || null,
99
+ requestOptions: props.requestOptions || null,
100
+ });
101
+
102
+ // only fetch if the query has changed by value (or on first mount).
103
+ if (
104
+ lastFetchedModelTypeRef.current === props.modelType &&
105
+ lastFetchedQueryJsonRef.current === queryJson
106
+ ) {
107
+ return;
108
+ }
109
+
110
+ lastFetchedModelTypeRef.current = props.modelType;
111
+ lastFetchedQueryJsonRef.current = queryJson;
112
+
74
113
  fetchCount().catch((err: Error) => {
75
114
  setError(API.getFriendlyMessage(err));
76
115
  });
@@ -80,6 +80,32 @@ const SideOver: FunctionComponent<ComponentProps> = (
80
80
  const titleId: string = useId();
81
81
  const descriptionId: string = useId();
82
82
 
83
+ /*
84
+ * The panel is mounted parked off the right edge and flipped into place one
85
+ * frame later so the browser has something to transition from — the same
86
+ * pattern Modal uses for its entrance.
87
+ */
88
+ const [hasEntered, setHasEntered] = useState<boolean>(false);
89
+
90
+ useEffect(() => {
91
+ const open: () => void = (): void => {
92
+ setHasEntered(true);
93
+ };
94
+
95
+ const frame: number = requestAnimationFrame(open);
96
+
97
+ /*
98
+ * A hidden or throttled tab never runs an animation frame; the timer
99
+ * skips the transition rather than leaving the panel offscreen.
100
+ */
101
+ const fallbackTimer: ReturnType<typeof setTimeout> = setTimeout(open, 80);
102
+
103
+ return () => {
104
+ cancelAnimationFrame(frame);
105
+ clearTimeout(fallbackTimer);
106
+ };
107
+ }, []);
108
+
83
109
  const [portalTarget, setPortalTarget] = useState<HTMLElement | null>(
84
110
  getPortalTarget,
85
111
  );
@@ -148,7 +174,16 @@ const SideOver: FunctionComponent<ComponentProps> = (
148
174
  className="pointer-events-none fixed inset-0 flex justify-end"
149
175
  data-testid="side-over-layer"
150
176
  >
151
- <div className={`pointer-events-auto w-full ${widthClass}`}>
177
+ {/*
178
+ * Settled state carries no transform class at all: a lingering
179
+ * translate would make the panel the containing block for
180
+ * position:fixed descendants (the Modal invariant).
181
+ */}
182
+ <div
183
+ className={`pointer-events-auto w-full ${widthClass} transition-transform duration-200 ease-out motion-reduce:transition-none ${
184
+ hasEntered ? "" : "translate-x-full"
185
+ }`}
186
+ >
152
187
  <div className="flex h-full flex-col bg-white shadow-xl">
153
188
  <div className="flex-shrink-0 flex flex-col bg-gray-50 px-4 py-6 sm:px-6">
154
189
  <div className="flex items-start justify-between space-x-3">
@@ -176,7 +211,7 @@ const SideOver: FunctionComponent<ComponentProps> = (
176
211
  type="button"
177
212
  title="Close panel"
178
213
  data-testid="close-button"
179
- className="text-gray-400 hover:text-gray-500"
214
+ className="rounded-md text-gray-400 transition-colors duration-150 ease-out hover:text-gray-500 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2"
180
215
  >
181
216
  <span className="sr-only">Close panel</span>
182
217