@oneuptime/common 12.0.23 → 12.0.24

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 (294) hide show
  1. package/Models/DatabaseModels/DetectionRule.ts +55 -0
  2. package/Models/DatabaseModels/NetworkDeviceAutoImportRule.ts +27 -7
  3. package/Models/DatabaseModels/NetworkDeviceDiscoveryScan.ts +67 -0
  4. package/Models/DatabaseModels/Project.ts +68 -0
  5. package/Models/DatabaseModels/User.ts +68 -0
  6. package/Server/API/TelemetryAPI.ts +29 -0
  7. package/Server/Infrastructure/Postgres/SchemaMigrations/1789400000000-AddNameToNetworkDeviceDiscoveryScan.ts +28 -0
  8. package/Server/Infrastructure/Postgres/SchemaMigrations/1789500000000-AddCampaignUtmFields.ts +49 -0
  9. package/Server/Infrastructure/Postgres/SchemaMigrations/1789512000000-AddDetectionRuleDistinctCountColumns.ts +33 -0
  10. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
  11. package/Server/Middleware/GzipRequestBody.ts +311 -0
  12. package/Server/Middleware/MultipartFormData.ts +87 -3
  13. package/Server/Services/DetectionRuleService.ts +73 -6
  14. package/Server/Services/Index.ts +205 -0
  15. package/Server/Services/NetworkDeviceDiscoveryScanService.ts +59 -0
  16. package/Server/Services/NetworkDeviceService.ts +148 -0
  17. package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +65 -0
  18. package/Server/Services/OnCallDutyPolicyFeedService.ts +1 -1
  19. package/Server/Services/ProjectService.ts +29 -19
  20. package/Server/Services/SecurityEventService.ts +90 -13
  21. package/Server/Services/StatusPageResourceService.ts +223 -0
  22. package/Server/Services/TelemetryAttributeService.ts +17 -0
  23. package/Server/Services/UserService.ts +4 -6
  24. package/Server/Types/Markdown.ts +161 -67
  25. package/Server/Utils/AI/Toolbox/OnCallTools.ts +12 -4
  26. package/Server/Utils/AnalyticsDatabase/Statement.ts +13 -1
  27. package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +297 -6
  28. package/Server/Utils/Attribution.ts +4 -0
  29. package/Server/Utils/Marketing/MarketingEventUtil.ts +45 -7
  30. package/Server/Utils/SecurityEvent/DetectionRuleEvaluator.ts +55 -3
  31. package/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.ts +21 -0
  32. package/Server/Utils/StartServer.ts +12 -32
  33. package/Server/Utils/Telemetry/LlmCostBudgetEvaluator.ts +101 -4
  34. package/Server/Utils/Telemetry/LlmMetricSpend.ts +130 -0
  35. package/Server/Views/Partials/AnalyticsConsent.ejs +159 -102
  36. package/Server/Views/Partials/Head.ejs +2 -1
  37. package/Tests/App/Dashboard/AdminUserNotificationMethodsPage.test.tsx +776 -38
  38. package/Tests/App/Dashboard/CorrelateFilterBuilder.test.tsx +359 -0
  39. package/Tests/App/Dashboard/CorrelateGraph.test.tsx +865 -0
  40. package/Tests/App/Dashboard/CorrelationGraph.test.ts +346 -0
  41. package/Tests/App/Dashboard/CriteriaFilterConditionDropdown.test.tsx +256 -0
  42. package/Tests/App/Dashboard/CriteriaFilterDefaults.test.ts +738 -0
  43. package/Tests/App/Dashboard/CriteriaFilterMonitorTypeChange.test.ts +1122 -0
  44. package/Tests/App/Dashboard/DiscoveryScanWizardValidation.test.tsx +283 -0
  45. package/Tests/App/Dashboard/ExternalStatusPageCriteriaFilter.test.ts +407 -0
  46. package/Tests/App/Dashboard/MetricSeriesInvestigateMenu.test.tsx +5 -14
  47. package/Tests/App/Dashboard/MonitorCriteriaActionToggles.test.tsx +426 -0
  48. package/Tests/App/Dashboard/MonitorStepsCriteriaAlignmentWiring.test.tsx +352 -0
  49. package/Tests/App/Dashboard/OnCallLayerCardOverrides.test.tsx +278 -0
  50. package/Tests/App/Dashboard/OnCallLayerShiftPreviewOverrides.test.ts +237 -0
  51. package/Tests/App/Dashboard/OnCallReadinessSurfaces.test.tsx +2 -2
  52. package/Tests/App/Dashboard/OnCallSchedulePreviewOverrides.test.tsx +460 -0
  53. package/Tests/App/Dashboard/SecurityEventAttributeUtil.test.ts +171 -0
  54. package/Tests/App/Dashboard/SecurityEventCorrelation.test.ts +857 -0
  55. package/Tests/App/Dashboard/SecurityEventDetailObservables.test.tsx +194 -0
  56. package/Tests/App/Dashboard/SecurityEventsDetectionRulesPage.test.tsx +124 -0
  57. package/Tests/App/Dashboard/SecurityEventsMonitorStepForm.test.tsx +79 -19
  58. package/Tests/App/Dashboard/SecurityEventsTableColumns.test.tsx +382 -0
  59. package/Tests/App/StatusPage/BulkAddStatusPageMonitors.test.ts +216 -0
  60. package/Tests/App/StatusPage/BulkAddStatusPageMonitorsModal.test.tsx +590 -11
  61. package/Tests/App/StatusPage/StatusPageResourceExplorer.test.tsx +757 -0
  62. package/Tests/Models/DiscoveryScanNameColumn.test.ts +293 -0
  63. package/Tests/Server/API/SecurityEventAttributesAPI.test.ts +564 -0
  64. package/Tests/Server/Infrastructure/Postgres/SchemaMigrationsOrdering.test.ts +120 -4
  65. package/Tests/Server/Middleware/GzipRequestBody.test.ts +725 -0
  66. package/Tests/Server/Middleware/MultipartFormData.test.ts +298 -0
  67. package/Tests/Server/Services/AddDetectionRuleDistinctCountColumnsMigration.test.ts +311 -0
  68. package/Tests/Server/Services/DetectionRuleService.test.ts +134 -2
  69. package/Tests/Server/Services/DiscoveryScanClaimHookFreeSafety.test.ts +7 -1
  70. package/Tests/Server/Services/FeedRetentionConsistency.test.ts +131 -0
  71. package/Tests/Server/Services/NetworkDeviceDiscoveryScanService.test.ts +194 -0
  72. package/Tests/Server/Services/NetworkDeviceMonitorStatusStamp.test.ts +800 -0
  73. package/Tests/Server/Services/OnCallScheduleAttachRosterRefresh.test.ts +244 -0
  74. package/Tests/Server/Services/SecurityEventService.test.ts +376 -0
  75. package/Tests/Server/Services/StatusPageResourceDuplicates.test.ts +703 -0
  76. package/Tests/Server/Services/TelemetryAttributeServiceSecurityEvent.test.ts +256 -0
  77. package/Tests/Server/Types/Workflow/Components/ComponentRegistryParity.test.ts +417 -0
  78. package/Tests/Server/Utils/AnalyticsDatabase/StatementGeneratorWhereOperators.test.ts +677 -0
  79. package/Tests/Server/Utils/Marketing/MarketingEventUtil.test.ts +43 -0
  80. package/Tests/Server/Utils/Monitor/Criteria/ProfileMonitorCriteria.test.ts +15 -11
  81. package/Tests/Server/Utils/SecurityEvent/DetectionRuleEvaluator.test.ts +369 -8
  82. package/Tests/Server/Utils/SecurityEvent/SigmaClickhouseCompiler.test.ts +35 -0
  83. package/Tests/Server/Utils/Telemetry/LlmCostBudgetEvaluator.test.ts +368 -4
  84. package/Tests/Server/Utils/Telemetry/LlmMetricSpend.test.ts +245 -0
  85. package/Tests/Server/Views/AnalyticsConsentPartial.test.ts +48 -5
  86. package/Tests/Types/Code/YamlSyntax.test.ts +396 -0
  87. package/Tests/Types/Monitor/MetricAndProfileDefaultCriteria.test.ts +957 -0
  88. package/Tests/Types/Monitor/MonitorCriteriaActionValidation.test.ts +598 -0
  89. package/Tests/Types/Monitor/MonitorCriteriaInstance.test.ts +49 -0
  90. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationNotificationMode.test.ts +6 -6
  91. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.test.ts +4 -4
  92. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationUtil.test.ts +4 -3
  93. package/Tests/Types/Telemetry/LlmMetricConventions.test.ts +193 -0
  94. package/Tests/UI/Components/AiInvestigationSettingsCard.test.tsx +0 -12
  95. package/Tests/UI/Components/CardModelDetailEdit.test.tsx +1 -17
  96. package/Tests/UI/Components/CodeEditor.test.tsx +474 -10
  97. package/Tests/UI/Components/Detail/YamlField.test.tsx +105 -0
  98. package/Tests/UI/Components/EntityDropdownLabelsBulkAdd.test.tsx +319 -0
  99. package/Tests/UI/Components/Forms/FormFieldYaml.test.tsx +231 -0
  100. package/Tests/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.test.ts +1 -0
  101. package/Tests/UI/Components/Forms/ValidationYAML.test.ts +234 -0
  102. package/Tests/UI/Components/ModelTable/AttributeColumns.test.tsx +573 -0
  103. package/Tests/UI/Components/ModelTable/BaseModelTableAttributeColumns.test.tsx +824 -0
  104. package/Tests/UI/Components/ModelTable/ColumnCustomizationModalAddableColumns.test.tsx +707 -0
  105. package/Tests/UI/Components/SideOver.test.tsx +1 -1
  106. package/Tests/UI/Components/StatusPage/ResourceGridSelection.test.tsx +294 -0
  107. package/Tests/UI/Components/StatusPage/ResourceListSelection.test.tsx +436 -0
  108. package/Tests/UI/Components/TableBulkCsvExport.test.tsx +6 -2
  109. package/Tests/UI/Components/Toggle.test.tsx +25 -0
  110. package/Tests/UI/Components/YamlEditor.test.tsx +847 -0
  111. package/Tests/Utils/NetworkDevice/DeviceReachabilityUtil.test.ts +252 -0
  112. package/Tests/Utils/NetworkDiscovery/DiscoveredHostUtil.test.ts +221 -0
  113. package/Tests/Utils/NetworkDiscovery/ScanNameUtil.test.ts +312 -0
  114. package/Tests/Utils/StatusPage/ResourceExplorer.test.ts +468 -0
  115. package/Tests/Utils/Telemetry/LlmMetricQuery.test.ts +472 -0
  116. package/Types/Code/YamlSyntax.ts +396 -0
  117. package/Types/Exception/ExceptionCode.ts +1 -0
  118. package/Types/Exception/PayloadTooLargeException.ts +8 -0
  119. package/Types/Marketing/Attribution.ts +10 -0
  120. package/Types/Marketing/MarketingEvent.ts +4 -0
  121. package/Types/Monitor/MonitorCriteriaInstance.ts +116 -56
  122. package/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.ts +2 -1
  123. package/Types/SecurityEvent/DetectionFindingConstants.ts +11 -0
  124. package/Types/Telemetry/LlmMetricConventions.ts +124 -0
  125. package/UI/Components/AutocompleteTextInput/AutocompleteTextInput.tsx +3 -0
  126. package/UI/Components/BulkUpdate/BulkUpdateForm.tsx +10 -1
  127. package/UI/Components/CodeEditor/CodeEditor.tsx +241 -79
  128. package/UI/Components/CodeEditor/YamlEditor.tsx +369 -0
  129. package/UI/Components/Detail/Detail.tsx +10 -0
  130. package/UI/Components/EntityDropdown/EntityDropdown.tsx +33 -0
  131. package/UI/Components/Forms/Fields/FormField.tsx +31 -0
  132. package/UI/Components/Forms/Types/Field.ts +8 -0
  133. package/UI/Components/Forms/Types/FormFieldSchemaType.ts +1 -0
  134. package/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.ts +2 -0
  135. package/UI/Components/Forms/Validation.ts +46 -0
  136. package/UI/Components/Input/Input.tsx +3 -0
  137. package/UI/Components/ModelTable/AttributeColumns.tsx +321 -0
  138. package/UI/Components/ModelTable/BaseModelTable.tsx +263 -14
  139. package/UI/Components/ModelTable/Column.ts +8 -0
  140. package/UI/Components/ModelTable/ColumnCustomizationModal.tsx +310 -0
  141. package/UI/Components/ModelTable/ColumnPreference.ts +13 -1
  142. package/UI/Components/MonitorTemplateVariables/TemplateVariablesModal.tsx +2 -2
  143. package/UI/Components/SideOver/SideOver.tsx +5 -5
  144. package/UI/Components/StatusPage/ResourceGrid.tsx +82 -2
  145. package/UI/Components/StatusPage/ResourceList.tsx +197 -38
  146. package/UI/Components/Toggle/Toggle.tsx +9 -1
  147. package/UI/Components/Types/FieldType.ts +1 -0
  148. package/Utils/NetworkDevice/DeviceReachabilityUtil.ts +73 -1
  149. package/Utils/NetworkDiscovery/ScanNameUtil.ts +181 -0
  150. package/Utils/StatusPage/ResourceExplorer.ts +258 -0
  151. package/Utils/Telemetry/LlmMetricQuery.ts +204 -0
  152. package/build/dist/Models/DatabaseModels/DetectionRule.js +57 -0
  153. package/build/dist/Models/DatabaseModels/DetectionRule.js.map +1 -1
  154. package/build/dist/Models/DatabaseModels/NetworkDeviceAutoImportRule.js +27 -7
  155. package/build/dist/Models/DatabaseModels/NetworkDeviceAutoImportRule.js.map +1 -1
  156. package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js +69 -0
  157. package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js.map +1 -1
  158. package/build/dist/Models/DatabaseModels/Project.js +76 -0
  159. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  160. package/build/dist/Models/DatabaseModels/User.js +76 -0
  161. package/build/dist/Models/DatabaseModels/User.js.map +1 -1
  162. package/build/dist/Server/API/TelemetryAPI.js +17 -0
  163. package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
  164. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789400000000-AddNameToNetworkDeviceDiscoveryScan.js +21 -0
  165. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789400000000-AddNameToNetworkDeviceDiscoveryScan.js.map +1 -0
  166. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789500000000-AddCampaignUtmFields.js +42 -0
  167. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789500000000-AddCampaignUtmFields.js.map +1 -0
  168. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789512000000-AddDetectionRuleDistinctCountColumns.js +22 -0
  169. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789512000000-AddDetectionRuleDistinctCountColumns.js.map +1 -0
  170. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
  171. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  172. package/build/dist/Server/Middleware/GzipRequestBody.js +224 -0
  173. package/build/dist/Server/Middleware/GzipRequestBody.js.map +1 -0
  174. package/build/dist/Server/Middleware/MultipartFormData.js +71 -2
  175. package/build/dist/Server/Middleware/MultipartFormData.js.map +1 -1
  176. package/build/dist/Server/Services/DetectionRuleService.js +49 -4
  177. package/build/dist/Server/Services/DetectionRuleService.js.map +1 -1
  178. package/build/dist/Server/Services/Index.js +204 -0
  179. package/build/dist/Server/Services/Index.js.map +1 -1
  180. package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js +50 -0
  181. package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js.map +1 -1
  182. package/build/dist/Server/Services/NetworkDeviceService.js +124 -0
  183. package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
  184. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +68 -13
  185. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
  186. package/build/dist/Server/Services/OnCallDutyPolicyFeedService.js +1 -1
  187. package/build/dist/Server/Services/OnCallDutyPolicyFeedService.js.map +1 -1
  188. package/build/dist/Server/Services/ProjectService.js +25 -45
  189. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  190. package/build/dist/Server/Services/SecurityEventService.js +57 -5
  191. package/build/dist/Server/Services/SecurityEventService.js.map +1 -1
  192. package/build/dist/Server/Services/StatusPageResourceService.js +140 -0
  193. package/build/dist/Server/Services/StatusPageResourceService.js.map +1 -1
  194. package/build/dist/Server/Services/TelemetryAttributeService.js +17 -0
  195. package/build/dist/Server/Services/TelemetryAttributeService.js.map +1 -1
  196. package/build/dist/Server/Services/UserService.js +2 -12
  197. package/build/dist/Server/Services/UserService.js.map +1 -1
  198. package/build/dist/Server/Types/Markdown.js +147 -61
  199. package/build/dist/Server/Types/Markdown.js.map +1 -1
  200. package/build/dist/Server/Utils/AI/Toolbox/OnCallTools.js +12 -4
  201. package/build/dist/Server/Utils/AI/Toolbox/OnCallTools.js.map +1 -1
  202. package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js +12 -1
  203. package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js.map +1 -1
  204. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +246 -9
  205. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
  206. package/build/dist/Server/Utils/Attribution.js.map +1 -1
  207. package/build/dist/Server/Utils/Marketing/MarketingEventUtil.js +27 -7
  208. package/build/dist/Server/Utils/Marketing/MarketingEventUtil.js.map +1 -1
  209. package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js +42 -7
  210. package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js.map +1 -1
  211. package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js +18 -0
  212. package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js.map +1 -1
  213. package/build/dist/Server/Utils/StartServer.js +12 -21
  214. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  215. package/build/dist/Server/Utils/Telemetry/LlmCostBudgetEvaluator.js +76 -5
  216. package/build/dist/Server/Utils/Telemetry/LlmCostBudgetEvaluator.js.map +1 -1
  217. package/build/dist/Server/Utils/Telemetry/LlmMetricSpend.js +102 -0
  218. package/build/dist/Server/Utils/Telemetry/LlmMetricSpend.js.map +1 -0
  219. package/build/dist/Types/Code/YamlSyntax.js +212 -0
  220. package/build/dist/Types/Code/YamlSyntax.js.map +1 -0
  221. package/build/dist/Types/Exception/ExceptionCode.js +1 -0
  222. package/build/dist/Types/Exception/ExceptionCode.js.map +1 -1
  223. package/build/dist/Types/Exception/PayloadTooLargeException.js +8 -0
  224. package/build/dist/Types/Exception/PayloadTooLargeException.js.map +1 -0
  225. package/build/dist/Types/Marketing/Attribution.js +10 -0
  226. package/build/dist/Types/Marketing/Attribution.js.map +1 -1
  227. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +112 -51
  228. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
  229. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.js +2 -1
  230. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.js.map +1 -1
  231. package/build/dist/Types/SecurityEvent/DetectionFindingConstants.js +9 -0
  232. package/build/dist/Types/SecurityEvent/DetectionFindingConstants.js.map +1 -1
  233. package/build/dist/Types/Telemetry/LlmMetricConventions.js +98 -0
  234. package/build/dist/Types/Telemetry/LlmMetricConventions.js.map +1 -0
  235. package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js +1 -1
  236. package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js.map +1 -1
  237. package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js +5 -2
  238. package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js.map +1 -1
  239. package/build/dist/UI/Components/CodeEditor/CodeEditor.js +206 -76
  240. package/build/dist/UI/Components/CodeEditor/CodeEditor.js.map +1 -1
  241. package/build/dist/UI/Components/CodeEditor/YamlEditor.js +201 -0
  242. package/build/dist/UI/Components/CodeEditor/YamlEditor.js.map +1 -0
  243. package/build/dist/UI/Components/Detail/Detail.js +9 -0
  244. package/build/dist/UI/Components/Detail/Detail.js.map +1 -1
  245. package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js +16 -1
  246. package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js.map +1 -1
  247. package/build/dist/UI/Components/Forms/Fields/FormField.js +14 -1
  248. package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
  249. package/build/dist/UI/Components/Forms/Types/Field.js.map +1 -1
  250. package/build/dist/UI/Components/Forms/Types/FormFieldSchemaType.js +1 -0
  251. package/build/dist/UI/Components/Forms/Types/FormFieldSchemaType.js.map +1 -1
  252. package/build/dist/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.js +2 -0
  253. package/build/dist/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.js.map +1 -1
  254. package/build/dist/UI/Components/Forms/Validation.js +27 -0
  255. package/build/dist/UI/Components/Forms/Validation.js.map +1 -1
  256. package/build/dist/UI/Components/Input/Input.js +1 -1
  257. package/build/dist/UI/Components/Input/Input.js.map +1 -1
  258. package/build/dist/UI/Components/ModelTable/AttributeColumns.js +159 -0
  259. package/build/dist/UI/Components/ModelTable/AttributeColumns.js.map +1 -0
  260. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +179 -17
  261. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  262. package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js +133 -2
  263. package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js.map +1 -1
  264. package/build/dist/UI/Components/ModelTable/ColumnPreference.js +7 -1
  265. package/build/dist/UI/Components/ModelTable/ColumnPreference.js.map +1 -1
  266. package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesModal.js +2 -2
  267. package/build/dist/UI/Components/SideOver/SideOver.js +5 -5
  268. package/build/dist/UI/Components/StatusPage/ResourceGrid.js +28 -2
  269. package/build/dist/UI/Components/StatusPage/ResourceGrid.js.map +1 -1
  270. package/build/dist/UI/Components/StatusPage/ResourceList.js +87 -20
  271. package/build/dist/UI/Components/StatusPage/ResourceList.js.map +1 -1
  272. package/build/dist/UI/Components/Toggle/Toggle.js +9 -1
  273. package/build/dist/UI/Components/Toggle/Toggle.js.map +1 -1
  274. package/build/dist/UI/Components/Types/FieldType.js +1 -0
  275. package/build/dist/UI/Components/Types/FieldType.js.map +1 -1
  276. package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js +37 -1
  277. package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js.map +1 -1
  278. package/build/dist/Utils/NetworkDiscovery/ScanNameUtil.js +120 -0
  279. package/build/dist/Utils/NetworkDiscovery/ScanNameUtil.js.map +1 -0
  280. package/build/dist/Utils/StatusPage/ResourceExplorer.js +171 -0
  281. package/build/dist/Utils/StatusPage/ResourceExplorer.js.map +1 -1
  282. package/build/dist/Utils/Telemetry/LlmMetricQuery.js +119 -0
  283. package/build/dist/Utils/Telemetry/LlmMetricQuery.js.map +1 -0
  284. package/jest.config.json +2 -0
  285. package/package.json +1 -1
  286. package/tsconfig.json +2 -1
  287. package/Tests/UI/Components/Toast.test.tsx +0 -197
  288. package/Tests/UI/Components/ToastStacking.test.tsx +0 -198
  289. package/UI/Components/Toast/Toast.tsx +0 -189
  290. package/UI/Components/Toast/ToastInit.tsx +0 -110
  291. package/build/dist/UI/Components/Toast/Toast.js +0 -123
  292. package/build/dist/UI/Components/Toast/Toast.js.map +0 -1
  293. package/build/dist/UI/Components/Toast/ToastInit.js +0 -53
  294. package/build/dist/UI/Components/Toast/ToastInit.js.map +0 -1
@@ -1,197 +0,0 @@
1
- import Toast, { ToastType } from "../../../UI/Components/Toast/Toast";
2
- import "@testing-library/jest-dom";
3
- import {
4
- act,
5
- fireEvent,
6
- render,
7
- screen,
8
- waitFor,
9
- } from "@testing-library/react";
10
- import userEvent from "@testing-library/user-event";
11
- import { UserEvent } from "@testing-library/user-event/dist/types/setup/setup";
12
- import * as React from "react";
13
- import { describe, expect, jest, test } from "@jest/globals";
14
-
15
- /*
16
- * The toast mounts closed (faded, nudged down) and opens on the next
17
- * animation frame, so anything asserting the settled look has to let that
18
- * frame run first — the same choreography Modal.test.tsx flushes.
19
- */
20
- type FlushEntranceFrameFunction = () => Promise<void>;
21
-
22
- const flushEntranceFrame: FlushEntranceFrameFunction =
23
- async (): Promise<void> => {
24
- await act(async () => {
25
- await new Promise<void>((resolve: () => void) => {
26
- requestAnimationFrame(() => {
27
- resolve();
28
- });
29
- });
30
- });
31
- };
32
-
33
- describe("Test for Toast.tsx", () => {
34
- test("should render the toast card", () => {
35
- render(
36
- <Toast type={ToastType.SUCCESS} title="Spread" description="Love" />,
37
- );
38
-
39
- /*
40
- * The card itself, not a fixed strip: positioning now belongs to the ONE
41
- * region container ToastInit renders (see ToastStacking.test.tsx), so
42
- * stacked toasts no longer paint on top of each other at top-0.
43
- */
44
- const toast: HTMLElement = screen.getByTestId("toast");
45
-
46
- expect(toast).toHaveClass(
47
- "pointer-events-auto",
48
- "w-full",
49
- "max-w-sm",
50
- "rounded-lg",
51
- "bg-white",
52
- "shadow-lg",
53
- );
54
- expect(toast).not.toHaveClass("fixed");
55
- });
56
-
57
- test("enters by fading and lifting in from a closed state", async () => {
58
- render(
59
- <Toast type={ToastType.SUCCESS} title="Spread" description="Love" />,
60
- );
61
-
62
- const toast: HTMLElement = screen.getByTestId("toast");
63
-
64
- expect(toast).toHaveClass("opacity-0", "translate-y-2");
65
-
66
- await flushEntranceFrame();
67
-
68
- expect(toast).toHaveClass("opacity-100");
69
- /*
70
- * A settled toast carries no transform class — a lingering translate
71
- * would make the card the containing block for position:fixed
72
- * descendants.
73
- */
74
- expect(toast.className).not.toMatch(/(^|\s)(scale|translate)-/);
75
- });
76
-
77
- test("should have close button", () => {
78
- render(
79
- <Toast type={ToastType.SUCCESS} title="Spread" description="Love" />,
80
- );
81
- expect(screen.getByTestId("close-button")).toBeInTheDocument();
82
- });
83
-
84
- test("Checking Title", () => {
85
- render(
86
- <Toast type={ToastType.SUCCESS} title="Spread" description="Love" />,
87
- );
88
- expect(screen.getByTestId("title")).toHaveTextContent("Spread");
89
- });
90
-
91
- test("Checking Description", () => {
92
- render(
93
- <Toast type={ToastType.SUCCESS} title="Spread" description="Love" />,
94
- );
95
- expect(screen.getByTestId("description")).toHaveTextContent("Love");
96
- });
97
-
98
- test("Checking if Toast is for SUCCESS", () => {
99
- render(
100
- <Toast type={ToastType.SUCCESS} title="Spread" description="Love" />,
101
- );
102
- expect(screen.getByTestId("toast-icon")).toHaveClass("text-green-400");
103
- });
104
- test("Checking if Toast is for INFO", () => {
105
- render(<Toast type={ToastType.INFO} title="Spread" description="Love" />);
106
- expect(screen.getByTestId("toast-icon")).toHaveClass("text-blue-400");
107
- });
108
-
109
- test("Checking if Toast is for Warning", () => {
110
- render(
111
- <Toast type={ToastType.WARNING} title="Spread" description="Love" />,
112
- );
113
- expect(screen.getByTestId("toast-icon")).toHaveClass("text-yellow-400");
114
- });
115
-
116
- test("Checking if Toast is for Normal", () => {
117
- render(<Toast type={ToastType.NORMAL} title="Spread" description="Love" />);
118
- expect(screen.getByTestId("toast-icon")).toHaveClass("text-gray-400");
119
- });
120
-
121
- test("clicking close fades the toast out and only then unmounts it", async () => {
122
- const user: UserEvent = userEvent.setup();
123
-
124
- render(
125
- <Toast type={ToastType.SUCCESS} title="Spread" description="Love" />,
126
- );
127
-
128
- await flushEntranceFrame();
129
-
130
- const closeButton: HTMLButtonElement = screen.getByTestId("close-button");
131
- await user.click(closeButton);
132
-
133
- /*
134
- * The exit runs as a fade with a delayed unmount, so immediately after
135
- * the click the card is still mounted, at zero opacity.
136
- */
137
- expect(screen.getByTestId("toast")).toHaveClass("opacity-0");
138
-
139
- await waitFor(() => {
140
- expect(screen.queryByTestId("toast")).toBeFalsy();
141
- });
142
- });
143
-
144
- /*
145
- * Owners (ToastInit) may hand the toast a NEW onClose identity on any
146
- * re-render. The exit timer must not restart because of that — it keys off
147
- * isLeaving alone and reads the latest onClose through a ref — otherwise a
148
- * re-render mid-fade would leave an invisible pointer-events-auto card
149
- * intercepting clicks past the original deadline.
150
- */
151
- test("a re-render with a new onClose mid-exit neither restarts the timer nor calls a stale handler", () => {
152
- jest.useFakeTimers();
153
-
154
- try {
155
- const staleOnClose: () => void = jest.fn();
156
- const latestOnClose: () => void = jest.fn();
157
-
158
- const { rerender } = render(
159
- <Toast
160
- type={ToastType.SUCCESS}
161
- title="Spread"
162
- description="Love"
163
- onClose={staleOnClose}
164
- />,
165
- );
166
-
167
- fireEvent.click(screen.getByTestId("close-button"));
168
-
169
- // 100ms into the 150ms exit fade the owner re-renders the toast…
170
- act(() => {
171
- jest.advanceTimersByTime(100);
172
- });
173
- rerender(
174
- <Toast
175
- type={ToastType.SUCCESS}
176
- title="Spread"
177
- description="Love"
178
- onClose={latestOnClose}
179
- />,
180
- );
181
-
182
- // …the card is still mid-fade…
183
- expect(screen.getByTestId("toast")).toHaveClass("opacity-0");
184
-
185
- // …and the ORIGINAL 150ms deadline still unmounts it 50ms later.
186
- act(() => {
187
- jest.advanceTimersByTime(50);
188
- });
189
-
190
- expect(screen.queryByTestId("toast")).toBeFalsy();
191
- expect(latestOnClose).toHaveBeenCalledTimes(1);
192
- expect(staleOnClose).not.toHaveBeenCalled();
193
- } finally {
194
- jest.useRealTimers();
195
- }
196
- });
197
- });
@@ -1,198 +0,0 @@
1
- import ToastLayout, {
2
- ShowToastNotification,
3
- } from "../../../UI/Components/Toast/ToastInit";
4
- import { ToastType } from "../../../UI/Components/Toast/Toast";
5
- import "@testing-library/jest-dom";
6
- import {
7
- act,
8
- cleanup,
9
- fireEvent,
10
- render,
11
- screen,
12
- waitFor,
13
- } from "@testing-library/react";
14
- import React from "react";
15
- import { afterEach, describe, expect, jest, test } from "@jest/globals";
16
-
17
- /*
18
- * ToastInit used to render every toast as its own fixed strip at top-0 (the
19
- * index prop that was supposed to offset them was never passed), so stacked
20
- * toasts painted on top of each other and only the last one was readable.
21
- * The contract pinned here: ONE fixed region, cards stacked in a flex column
22
- * in insertion order.
23
- */
24
-
25
- type ShowToastFunction = (title: string, onClose?: () => void) => void;
26
-
27
- const showToast: ShowToastFunction = (
28
- title: string,
29
- onClose?: () => void,
30
- ): void => {
31
- act(() => {
32
- ShowToastNotification({
33
- title,
34
- description: `${title} description`,
35
- type: ToastType.SUCCESS,
36
- onClose,
37
- });
38
- });
39
- };
40
-
41
- describe("Toast stacking", () => {
42
- afterEach(() => {
43
- cleanup();
44
- jest.clearAllMocks();
45
- });
46
-
47
- test("renders one fixed region that keeps its slot in the z-index ladder", () => {
48
- render(<ToastLayout />);
49
-
50
- const region: HTMLElement = screen.getByTestId("toast-region");
51
-
52
- /*
53
- * z-40 is deliberate: above SideOver (z-30), below Modal (z-50). The
54
- * region itself takes no pointer events; only the cards inside do.
55
- */
56
- expect(region).toHaveClass(
57
- "pointer-events-none",
58
- "fixed",
59
- "z-40",
60
- "top-0",
61
- "left-0",
62
- "right-0",
63
- );
64
- expect(region).toHaveClass("sm:items-start", "sm:p-6");
65
- expect(region).toHaveAttribute("aria-live", "assertive");
66
- });
67
-
68
- test("stacks multiple toasts in one region, in insertion order", () => {
69
- render(<ToastLayout />);
70
-
71
- showToast("First");
72
- showToast("Second");
73
- showToast("Third");
74
-
75
- const toasts: Array<HTMLElement> = screen.getAllByTestId("toast");
76
-
77
- expect(toasts).toHaveLength(3);
78
-
79
- // All three cards live inside the ONE region, not three fixed strips.
80
- const region: HTMLElement = screen.getByTestId("toast-region");
81
-
82
- for (const toast of toasts) {
83
- expect(region).toContainElement(toast);
84
- expect(toast).not.toHaveClass("fixed");
85
- }
86
-
87
- // The stack is a flex column with a gap, so cards never overlap.
88
- const stack: HTMLElement = toasts[0]?.parentElement as HTMLElement;
89
-
90
- expect(stack).toHaveClass("flex", "flex-col", "gap-4");
91
- expect(toasts[1]?.parentElement).toBe(stack);
92
- expect(toasts[2]?.parentElement).toBe(stack);
93
-
94
- const titles: Array<string | null> = screen
95
- .getAllByTestId("title")
96
- .map((title: HTMLElement) => {
97
- return title.textContent;
98
- });
99
-
100
- expect(titles).toEqual(["First", "Second", "Third"]);
101
- });
102
-
103
- test("dismissing one toast removes only that toast and calls its onClose", async () => {
104
- const onCloseFirst: () => void = jest.fn();
105
-
106
- render(<ToastLayout />);
107
-
108
- showToast("First", onCloseFirst);
109
- showToast("Second");
110
-
111
- const closeButtons: Array<HTMLElement> =
112
- screen.getAllByTestId("close-button");
113
-
114
- expect(closeButtons).toHaveLength(2);
115
-
116
- fireEvent.click(closeButtons[0] as HTMLElement);
117
-
118
- /*
119
- * The exit fade delays the unmount, so removal is awaited rather than
120
- * asserted synchronously.
121
- */
122
- await waitFor(() => {
123
- expect(screen.getAllByTestId("toast")).toHaveLength(1);
124
- });
125
-
126
- expect(onCloseFirst).toHaveBeenCalledTimes(1);
127
- expect(screen.getByTestId("title")).toHaveTextContent("Second");
128
- });
129
-
130
- test("a toast dispatched after a dismissal still stacks below the survivors", async () => {
131
- render(<ToastLayout />);
132
-
133
- showToast("First");
134
- showToast("Second");
135
-
136
- fireEvent.click(screen.getAllByTestId("close-button")[0] as HTMLElement);
137
-
138
- await waitFor(() => {
139
- expect(screen.getAllByTestId("toast")).toHaveLength(1);
140
- });
141
-
142
- showToast("Third");
143
-
144
- const titles: Array<string | null> = screen
145
- .getAllByTestId("title")
146
- .map((title: HTMLElement) => {
147
- return title.textContent;
148
- });
149
-
150
- expect(titles).toEqual(["Second", "Third"]);
151
- });
152
-
153
- /*
154
- * Regression: dispatching a toast re-renders the whole stack, and the
155
- * layout used to hand every card a freshly created onClose on each render.
156
- * A dismissed card mid-exit-fade saw the new prop, cleared its unmount
157
- * timer, and re-armed it — so a burst of toasts could keep an invisible
158
- * pointer-events-auto card alive indefinitely, intercepting clicks.
159
- */
160
- test("a toast dispatched during another toast's exit fade does not restart its unmount timer", () => {
161
- jest.useFakeTimers();
162
-
163
- try {
164
- const onCloseFirst: () => void = jest.fn();
165
-
166
- render(<ToastLayout />);
167
-
168
- showToast("First", onCloseFirst);
169
-
170
- fireEvent.click(screen.getByTestId("close-button"));
171
-
172
- // 100ms into the 150ms exit fade a second toast re-renders the stack.
173
- act(() => {
174
- jest.advanceTimersByTime(100);
175
- });
176
- showToast("Second");
177
-
178
- expect(screen.getAllByTestId("toast")).toHaveLength(2);
179
-
180
- // The ORIGINAL 150ms deadline holds: 50ms later the first card is gone.
181
- act(() => {
182
- jest.advanceTimersByTime(50);
183
- });
184
-
185
- expect(onCloseFirst).toHaveBeenCalledTimes(1);
186
-
187
- const titles: Array<string | null> = screen
188
- .getAllByTestId("title")
189
- .map((title: HTMLElement) => {
190
- return title.textContent;
191
- });
192
-
193
- expect(titles).toEqual(["Second"]);
194
- } finally {
195
- jest.useRealTimers();
196
- }
197
- });
198
- });
@@ -1,189 +0,0 @@
1
- import React, {
2
- FunctionComponent,
3
- ReactElement,
4
- useEffect,
5
- useRef,
6
- useState,
7
- } from "react";
8
- import Icon from "../Icon/Icon";
9
- import IconProp from "../../../Types/Icon/IconProp";
10
- import useTranslateValue from "../../Utils/Translation";
11
-
12
- export enum ToastType {
13
- DANGER,
14
- SUCCESS,
15
- INFO,
16
- WARNING,
17
- NORMAL,
18
- }
19
-
20
- export interface ComponentProps {
21
- title: string;
22
- description: string;
23
- onClose?: undefined | (() => void);
24
- type?: undefined | ToastType;
25
- icon?: IconProp | undefined;
26
- }
27
-
28
- /* How long the exit fade runs before the toast actually unmounts. */
29
- const EXIT_DURATION_IN_MS: number = 150;
30
-
31
- const Component: FunctionComponent<ComponentProps> = (
32
- props: ComponentProps,
33
- ): ReactElement => {
34
- const { translateString } = useTranslateValue();
35
- const [show, setShow] = useState<boolean>(true);
36
-
37
- /*
38
- * The toast is mounted in its closed state (faded, nudged down) and flipped
39
- * open one frame later so the browser has something to transition from —
40
- * the same pattern Modal uses for its entrance.
41
- */
42
- const [hasEntered, setHasEntered] = useState<boolean>(false);
43
-
44
- /*
45
- * Dismissal fades the card out first and only unmounts (and notifies the
46
- * owner) once the fade has finished, so the toast never blinks away.
47
- */
48
- const [isLeaving, setIsLeaving] = useState<boolean>(false);
49
-
50
- useEffect(() => {
51
- const open: () => void = (): void => {
52
- setHasEntered(true);
53
- };
54
-
55
- const frame: number = requestAnimationFrame(open);
56
-
57
- /*
58
- * A hidden or throttled tab never runs an animation frame; the timer
59
- * skips the transition rather than leaving the toast at zero opacity.
60
- */
61
- const fallbackTimer: ReturnType<typeof setTimeout> = setTimeout(open, 80);
62
-
63
- return () => {
64
- cancelAnimationFrame(frame);
65
- clearTimeout(fallbackTimer);
66
- };
67
- }, []);
68
-
69
- /*
70
- * The latest onClose lives in a ref so the exit effect can depend on
71
- * isLeaving alone. Owners may recreate onClose every render, and a dep on
72
- * it would restart the unmount timer whenever a sibling toast re-renders
73
- * the stack mid-exit — leaving an invisible pointer-events-auto card
74
- * intercepting clicks past its original deadline.
75
- */
76
- const onCloseRef: React.MutableRefObject<(() => void) | undefined> = useRef<
77
- (() => void) | undefined
78
- >(props.onClose);
79
- onCloseRef.current = props.onClose;
80
-
81
- useEffect(() => {
82
- if (!isLeaving) {
83
- return undefined;
84
- }
85
-
86
- const exitTimer: ReturnType<typeof setTimeout> = setTimeout(() => {
87
- setShow(false);
88
- if (onCloseRef.current) {
89
- onCloseRef.current();
90
- }
91
- }, EXIT_DURATION_IN_MS);
92
-
93
- return () => {
94
- clearTimeout(exitTimer);
95
- };
96
- }, [isLeaving]);
97
-
98
- let typeCssClass: string = "text-gray-400";
99
- let iconType: IconProp = IconProp.Info;
100
-
101
- if (props.type === ToastType.NORMAL) {
102
- typeCssClass = "text-gray-400";
103
- iconType = IconProp.Info;
104
- }
105
- if (props.type === ToastType.DANGER) {
106
- typeCssClass = "text-red-400";
107
- iconType = IconProp.Error;
108
- }
109
- if (props.type === ToastType.WARNING) {
110
- typeCssClass = "text-yellow-400";
111
- iconType = IconProp.Alert;
112
- }
113
- if (props.type === ToastType.SUCCESS) {
114
- typeCssClass = "text-green-400";
115
- iconType = IconProp.CheckCircle;
116
- }
117
- if (props.type === ToastType.INFO) {
118
- typeCssClass = "text-blue-400";
119
- iconType = IconProp.Info;
120
- }
121
-
122
- if (props.icon) {
123
- iconType = props.icon;
124
- }
125
-
126
- /*
127
- * Settled state carries no transform class at all — leaving a translate on
128
- * the card would make it the containing block for position:fixed
129
- * descendants (the Modal invariant).
130
- */
131
- let motionClassName: string = "translate-y-2 opacity-0";
132
-
133
- if (isLeaving) {
134
- motionClassName = "opacity-0";
135
- } else if (hasEntered) {
136
- motionClassName = "opacity-100";
137
- }
138
-
139
- if (show) {
140
- return (
141
- <div
142
- data-testid="toast"
143
- className={`pointer-events-auto w-full max-w-sm overflow-hidden rounded-lg bg-white shadow-lg ring-1 ring-black ring-opacity-5 transition duration-150 ease-out motion-reduce:transition-none ${motionClassName}`}
144
- >
145
- <div className="p-4">
146
- <div className="flex items-start">
147
- <div className="flex-shrink-0">
148
- <Icon
149
- className={`h-6 w-6 ${typeCssClass}`}
150
- data-testid="toast-icon"
151
- icon={iconType}
152
- />
153
- </div>
154
- <div className="ml-3 w-0 flex-1 pt-0.5">
155
- <p
156
- data-testid="title"
157
- className="text-sm font-medium text-gray-900"
158
- >
159
- {translateString(props.title)}
160
- </p>
161
- <p
162
- data-testid="description"
163
- className="mt-1 text-sm text-gray-500"
164
- >
165
- {translateString(props.description)}
166
- </p>
167
- </div>
168
- <div className="ml-4 flex flex-shrink-0">
169
- <button
170
- onClick={() => {
171
- setIsLeaving(true);
172
- }}
173
- data-testid="close-button"
174
- type="button"
175
- className="inline-flex rounded-md bg-white text-gray-400 transition-colors duration-150 ease-out hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
176
- >
177
- <span className="sr-only">Close</span>
178
- <Icon className={`h-5 w-5`} icon={IconProp.Close} />
179
- </button>
180
- </div>
181
- </div>
182
- </div>
183
- </div>
184
- );
185
- }
186
- return <></>;
187
- };
188
-
189
- export default Component;
@@ -1,110 +0,0 @@
1
- import React, {
2
- FunctionComponent,
3
- ReactElement,
4
- useEffect,
5
- useRef,
6
- } from "react";
7
- import Toast, { ComponentProps as ToastComponentProps } from "./Toast";
8
- import GlobalEvents from "../../Utils/GlobalEvents";
9
-
10
- type ShowToastNotificationFunction = (toast: ToastComponentProps) => void;
11
-
12
- export const ShowToastNotification: ShowToastNotificationFunction = (
13
- toast: ToastComponentProps,
14
- ): void => {
15
- GlobalEvents.dispatchEvent("toast", toast as any);
16
- };
17
-
18
- /*
19
- * Each queued toast gets a stable id so removals key correctly — keying by
20
- * array index would re-key (and so re-mount and re-animate) every toast below
21
- * the one that was dismissed.
22
- */
23
- interface ToastEntry {
24
- id: number;
25
- toast: ToastComponentProps;
26
- /*
27
- * Built once at dispatch and stable for the toast's lifetime: recreating
28
- * onClose per render would hand every Toast a fresh prop on each stack
29
- * re-render, which used to restart the exit-unmount timer of any toast
30
- * mid-fade.
31
- */
32
- onClose: () => void;
33
- }
34
-
35
- const ToastLayout: FunctionComponent = (): ReactElement => {
36
- const [currentToasts, setCurrentToasts] = React.useState<Array<ToastEntry>>(
37
- [],
38
- );
39
- const nextToastIdRef: React.MutableRefObject<number> = useRef<number>(0);
40
-
41
- useEffect(() => {
42
- const addToastNotification: (event: CustomEvent) => void = (
43
- event: CustomEvent,
44
- ): void => {
45
- const toast: ToastComponentProps = event.detail;
46
- const id: number = nextToastIdRef.current;
47
- nextToastIdRef.current += 1;
48
-
49
- // setCurrentToasts is stable, so this closure never goes stale.
50
- const onClose: () => void = (): void => {
51
- if (toast.onClose) {
52
- toast.onClose();
53
- }
54
-
55
- setCurrentToasts((toasts: Array<ToastEntry>) => {
56
- return toasts.filter((candidate: ToastEntry) => {
57
- return candidate.id !== id;
58
- });
59
- });
60
- };
61
-
62
- /*
63
- * Functional update: this listener is registered once, so reading
64
- * currentToasts from its closure would always see the mount-time empty
65
- * array and every new toast would clobber the ones before it.
66
- */
67
- setCurrentToasts((toasts: Array<ToastEntry>) => {
68
- return [...toasts, { id, toast, onClose }];
69
- });
70
- };
71
-
72
- GlobalEvents.addEventListener("toast", addToastNotification);
73
-
74
- return () => {
75
- GlobalEvents.removeEventListener("toast", addToastNotification);
76
- };
77
- }, []);
78
-
79
- /*
80
- * ONE fixed container for every toast. Each toast used to render its own
81
- * fixed strip at top-0 (the index prop that offset them was never passed),
82
- * so stacked toasts painted on top of each other. A single region stacking
83
- * cards with flex-col gap makes the order self-evident and keeps the
84
- * z-index ladder in exactly one place (Toast z-40, under Modal z-50).
85
- */
86
- return (
87
- <div
88
- data-testid="toast-region"
89
- aria-live="assertive"
90
- className="pointer-events-none fixed z-40 top-0 left-0 right-0 flex items-end px-4 py-6 sm:items-start sm:p-6"
91
- >
92
- <div className="flex w-full flex-col items-center gap-4 sm:items-end">
93
- {currentToasts.map((entry: ToastEntry) => {
94
- return (
95
- <Toast
96
- key={entry.id}
97
- title={entry.toast.title}
98
- description={entry.toast.description}
99
- type={entry.toast.type}
100
- icon={entry.toast.icon}
101
- onClose={entry.onClose}
102
- />
103
- );
104
- })}
105
- </div>
106
- </div>
107
- );
108
- };
109
-
110
- export default ToastLayout;