@oneuptime/common 12.0.9 → 12.0.11

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 (262) hide show
  1. package/Models/DatabaseModels/NetworkDevice.ts +44 -0
  2. package/Models/DatabaseModels/NetworkDeviceLink.ts +116 -0
  3. package/Models/DatabaseModels/RunnerJob.ts +17 -2
  4. package/Server/API/LlmProviderAPI.ts +8 -0
  5. package/Server/Infrastructure/Postgres/SchemaMigrations/1787400000000-AddDeviceRoleAndDeclaredLinkParent.ts +52 -0
  6. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
  7. package/Server/Services/NetworkDeviceLabelRuleEngineService.ts +240 -0
  8. package/Server/Services/NetworkDeviceLinkService.ts +338 -2
  9. package/Server/Services/NetworkDeviceService.ts +265 -12
  10. package/Server/Services/RunnerJobService.ts +24 -1
  11. package/Server/Types/Billing/MeteredPlan/AllMeteredPlans.ts +19 -5
  12. package/Server/Types/Database/JSONColumnQuery.ts +42 -4
  13. package/Server/Utils/LLM/LLMService.ts +108 -19
  14. package/Server/Utils/Monitor/MonitorAlert.ts +22 -115
  15. package/Server/Utils/Monitor/MonitorIncident.ts +7 -337
  16. package/Server/Utils/Monitor/MonitorMaintenanceSuppression.ts +27 -2
  17. package/Server/Utils/Monitor/SeriesResourceLabels.ts +32 -0
  18. package/Server/Utils/Monitor/SeriesResourceLinker.ts +469 -0
  19. package/Server/Utils/SSRFProtection.ts +5 -4
  20. package/Server/Utils/Telemetry/InventoryEntityRegistry.ts +186 -38
  21. package/Tests/App/AdminDashboard/AdminUsersBulkAddToProject.test.tsx +955 -0
  22. package/Tests/App/AdminDashboard/BulkAddUsersToProject.test.ts +778 -0
  23. package/Tests/App/AdminDashboard/BulkAddUsersToProjectModal.test.tsx +1051 -0
  24. package/Tests/App/Dashboard/AdminNotificationRulesPage.test.tsx +420 -112
  25. package/Tests/App/Dashboard/OnCallReadinessSurfaces.test.tsx +81 -6
  26. package/Tests/App/Dashboard/PayAsYouGoConsentGate.test.tsx +543 -0
  27. package/Tests/App/Dashboard/PayAsYouGoFreePlan.test.ts +125 -0
  28. package/Tests/App/Dashboard/PayAsYouGoNotices.test.tsx +612 -0
  29. package/Tests/App/Dashboard/SloWidgetChartRendering.test.tsx +536 -0
  30. package/Tests/App/Dashboard/UserElementEmail.test.tsx +447 -0
  31. package/Tests/App/Dashboard/UserEmailCallSites.test.tsx +284 -0
  32. package/Tests/Models/AnalyticsModels/CommonModel.test.ts +312 -0
  33. package/Tests/Server/Infrastructure/Postgres/DeviceRoleAndDeclaredLinkParentMigration.test.ts +250 -0
  34. package/Tests/Server/Infrastructure/Postgres/EpisodeMemberNotifyIndexesMigration.test.ts +46 -18
  35. package/Tests/Server/Services/NetworkDeviceLabelRuleRun.test.ts +618 -0
  36. package/Tests/Server/Services/NetworkDeviceLinkParentValidation.test.ts +1043 -0
  37. package/Tests/Server/Services/NetworkSiteAssignmentRuleRun.test.ts +706 -0
  38. package/Tests/Server/Services/RunnerJobEnqueue.test.ts +412 -0
  39. package/Tests/Server/Types/Billing/PayAsYouGoMeteredPricing.test.ts +125 -0
  40. package/Tests/Server/Types/Database/JSONColumnQuery.test.ts +508 -0
  41. package/Tests/Server/Utils/AI/LLMServiceRequestPolicy.test.ts +2 -2
  42. package/Tests/Server/Utils/AI/LLMServiceRetryPolicy.test.ts +450 -0
  43. package/Tests/Server/Utils/Monitor/MonitorAlertResourceLinking.test.ts +556 -0
  44. package/Tests/Server/Utils/Monitor/MonitorIncidentResourceLinking.test.ts +547 -0
  45. package/Tests/Server/Utils/Monitor/MonitorMaintenanceSuppression.test.ts +30 -0
  46. package/Tests/Server/Utils/Monitor/SeriesResourceLinker.test.ts +699 -0
  47. package/Tests/Server/Utils/SeriesResourceLabels.test.ts +55 -0
  48. package/Tests/Server/Utils/Telemetry/InventoryEntityRegistry.test.ts +668 -0
  49. package/Tests/Types/API/URLMalformedTolerance.test.ts +301 -0
  50. package/Tests/Types/API/URLTelScheme.test.ts +261 -0
  51. package/Tests/Types/API/URLUnsupportedScheme.test.ts +148 -0
  52. package/Tests/Types/Billing/PayAsYouGoPricing.test.ts +64 -0
  53. package/Tests/Types/CustomField/CustomFieldTypeCorrespondence.test.ts +107 -0
  54. package/Tests/Types/Monitor/MonitorType.test.ts +62 -0
  55. package/Tests/Types/NetworkAutomation/RuleRunResult.test.ts +184 -0
  56. package/Tests/Types/Runbook/RunbookStepType.test.ts +124 -0
  57. package/Tests/Types/Workflow/BaseModelComponents.test.ts +17 -0
  58. package/Tests/UI/Components/Button.test.tsx +279 -0
  59. package/Tests/UI/Components/Charts/ChartXAxisDataKey.test.tsx +256 -0
  60. package/Tests/UI/Components/Charts/DataPointUtil.test.ts +323 -0
  61. package/Tests/UI/Components/DictionaryValuePlaceholderAndSuggestions.test.tsx +595 -0
  62. package/Tests/UI/Components/Icon.test.tsx +57 -0
  63. package/Tests/UI/Components/List.test.tsx +20 -3
  64. package/Tests/UI/Components/LogsSavedViewsDropdown.test.tsx +304 -0
  65. package/Tests/UI/Components/LogsViewerClearSavedView.test.tsx +179 -0
  66. package/Tests/UI/Components/ModelTable/CustomFieldColumns.test.tsx +349 -1
  67. package/Tests/UI/Components/Pagination.test.tsx +721 -146
  68. package/Tests/UI/Components/PaginationIntegration.test.tsx +386 -0
  69. package/Tests/UI/Components/PaginationUtil.test.ts +417 -0
  70. package/Tests/UI/Components/TelemetrySavedViewsDropdown.test.tsx +343 -0
  71. package/Tests/UI/Components/Workflow/ColumnEditor/ColumnControl.test.ts +289 -0
  72. package/Tests/UI/Components/Workflow/ColumnEditor/ColumnOperators.test.ts +271 -0
  73. package/Tests/UI/Components/Workflow/ColumnEditor/ColumnRow.test.ts +119 -0
  74. package/Tests/UI/Components/Workflow/ColumnEditor/ColumnRowSerialization.test.ts +968 -0
  75. package/Tests/UI/Components/Workflow/ModelColumnEditor.render.test.tsx +766 -0
  76. package/Tests/UI/Utils/RunNetworkRule.test.ts +320 -0
  77. package/Tests/Utils/APIRetryPolicy.test.ts +823 -0
  78. package/Tests/Utils/Monitor/NetworkDeviceRoleUtil.test.ts +502 -0
  79. package/Tests/Utils/Monitor/NetworkTopologyDeclaredHierarchy.test.ts +759 -0
  80. package/Tests/Utils/NetworkAutomation/RuleRunSummary.test.ts +286 -0
  81. package/Tests/Utils/Schema/BaseSchema.test.ts +651 -0
  82. package/Types/API/Protocol.ts +9 -0
  83. package/Types/API/URL.ts +286 -20
  84. package/Types/Billing/PayAsYouGoPricing.ts +47 -0
  85. package/Types/CustomField/CustomFieldType.ts +28 -0
  86. package/Types/Monitor/MonitorType.ts +11 -0
  87. package/Types/Monitor/SnmpMonitor/NetworkTopology.ts +13 -0
  88. package/Types/NetworkAutomation/RuleRunResult.ts +121 -0
  89. package/Types/Runbook/RunbookStepType.ts +23 -0
  90. package/UI/Components/AutocompleteTextInput/AutocompleteTextInput.tsx +7 -0
  91. package/UI/Components/Button/Button.tsx +14 -22
  92. package/UI/Components/Charts/Area/AreaChart.tsx +4 -2
  93. package/UI/Components/Charts/Bar/BarChart.tsx +4 -2
  94. package/UI/Components/Charts/ChartLibrary/Types/ChartDataPoint.ts +13 -0
  95. package/UI/Components/Charts/Line/LineChart.tsx +4 -2
  96. package/UI/Components/Charts/Types/XAxis/XAxis.ts +6 -0
  97. package/UI/Components/Charts/Utils/DataPoint.ts +15 -12
  98. package/UI/Components/Dictionary/Dictionary.tsx +4 -39
  99. package/UI/Components/Icon/Icon.tsx +1 -1
  100. package/UI/Components/LogsViewer/LogsViewer.tsx +5 -1
  101. package/UI/Components/LogsViewer/components/LogsFacetSidebar.tsx +8 -0
  102. package/UI/Components/LogsViewer/components/LogsPagination.tsx +29 -91
  103. package/UI/Components/LogsViewer/components/LogsViewerToolbar.tsx +2 -0
  104. package/UI/Components/LogsViewer/components/SavedViewsDropdown.tsx +66 -1
  105. package/UI/Components/ModelTable/CustomFieldColumns.tsx +55 -0
  106. package/UI/Components/Pagination/Pagination.tsx +373 -328
  107. package/UI/Components/Pagination/PaginationUtil.ts +263 -0
  108. package/UI/Components/Pagination/Screenshots/README.md +15 -0
  109. package/UI/Components/Pagination/Screenshots/pagination-before-after.png +0 -0
  110. package/UI/Components/Pagination/Screenshots/pagination-go-to-page-modal.png +0 -0
  111. package/UI/Components/Pagination/Screenshots/pagination-long-list.png +0 -0
  112. package/UI/Components/Pagination/Screenshots/pagination-mobile.png +0 -0
  113. package/UI/Components/Pagination/Screenshots/pagination-telemetry.png +0 -0
  114. package/UI/Components/TelemetryViewer/components/SavedViewsDropdown.tsx +66 -1
  115. package/UI/Components/TelemetryViewer/components/TelemetryPagination.tsx +38 -92
  116. package/UI/Components/TextArea/TextArea.tsx +3 -0
  117. package/UI/Components/Workflow/ArgumentsForm.tsx +18 -1
  118. package/UI/Components/Workflow/ColumnEditor/AddColumnDropdown.tsx +177 -0
  119. package/UI/Components/Workflow/ColumnEditor/ColumnConditionRow.tsx +243 -0
  120. package/UI/Components/Workflow/ColumnEditor/ColumnControl.ts +255 -0
  121. package/UI/Components/Workflow/ColumnEditor/ColumnFieldRow.tsx +178 -0
  122. package/UI/Components/Workflow/ColumnEditor/ColumnOperators.ts +206 -0
  123. package/UI/Components/Workflow/ColumnEditor/ColumnRow.ts +157 -0
  124. package/UI/Components/Workflow/ColumnEditor/ColumnRowSerialization.ts +517 -0
  125. package/UI/Components/Workflow/ColumnEditor/ColumnValueInput.tsx +482 -0
  126. package/UI/Components/Workflow/ColumnEditor/ModelQueryBuilder.tsx +131 -0
  127. package/UI/Components/Workflow/ColumnEditor/ModelRecordForm.tsx +163 -0
  128. package/UI/Components/Workflow/ModelColumnEditor.tsx +284 -100
  129. package/UI/Utils/NetworkAutomation/RunNetworkRule.ts +122 -0
  130. package/Utils/API.ts +243 -36
  131. package/Utils/Monitor/NetworkDeviceRoleUtil.ts +45 -0
  132. package/Utils/Monitor/NetworkTopologyUtil.ts +42 -2
  133. package/Utils/NetworkAutomation/RuleRunSummary.ts +147 -0
  134. package/build/dist/Models/DatabaseModels/NetworkDevice.js +45 -0
  135. package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
  136. package/build/dist/Models/DatabaseModels/NetworkDeviceLink.js +115 -0
  137. package/build/dist/Models/DatabaseModels/NetworkDeviceLink.js.map +1 -1
  138. package/build/dist/Models/DatabaseModels/RunnerJob.js +17 -2
  139. package/build/dist/Models/DatabaseModels/RunnerJob.js.map +1 -1
  140. package/build/dist/Server/API/LlmProviderAPI.js +9 -1
  141. package/build/dist/Server/API/LlmProviderAPI.js.map +1 -1
  142. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787400000000-AddDeviceRoleAndDeclaredLinkParent.js +33 -0
  143. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787400000000-AddDeviceRoleAndDeclaredLinkParent.js.map +1 -0
  144. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
  145. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  146. package/build/dist/Server/Services/NetworkDeviceLabelRuleEngineService.js +202 -0
  147. package/build/dist/Server/Services/NetworkDeviceLabelRuleEngineService.js.map +1 -1
  148. package/build/dist/Server/Services/NetworkDeviceLinkService.js +253 -1
  149. package/build/dist/Server/Services/NetworkDeviceLinkService.js.map +1 -1
  150. package/build/dist/Server/Services/NetworkDeviceService.js +223 -12
  151. package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
  152. package/build/dist/Server/Services/RunnerJobService.js +20 -2
  153. package/build/dist/Server/Services/RunnerJobService.js.map +1 -1
  154. package/build/dist/Server/Types/Billing/MeteredPlan/AllMeteredPlans.js +12 -5
  155. package/build/dist/Server/Types/Billing/MeteredPlan/AllMeteredPlans.js.map +1 -1
  156. package/build/dist/Server/Types/Database/JSONColumnQuery.js +40 -4
  157. package/build/dist/Server/Types/Database/JSONColumnQuery.js.map +1 -1
  158. package/build/dist/Server/Utils/LLM/LLMService.js +78 -22
  159. package/build/dist/Server/Utils/LLM/LLMService.js.map +1 -1
  160. package/build/dist/Server/Utils/Monitor/MonitorAlert.js +22 -98
  161. package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
  162. package/build/dist/Server/Utils/Monitor/MonitorIncident.js +7 -254
  163. package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
  164. package/build/dist/Server/Utils/Monitor/MonitorMaintenanceSuppression.js +14 -2
  165. package/build/dist/Server/Utils/Monitor/MonitorMaintenanceSuppression.js.map +1 -1
  166. package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js +27 -0
  167. package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js.map +1 -1
  168. package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js +285 -0
  169. package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js.map +1 -0
  170. package/build/dist/Server/Utils/SSRFProtection.js +5 -4
  171. package/build/dist/Server/Utils/SSRFProtection.js.map +1 -1
  172. package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js +128 -35
  173. package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js.map +1 -1
  174. package/build/dist/Types/API/Protocol.js +9 -0
  175. package/build/dist/Types/API/Protocol.js.map +1 -1
  176. package/build/dist/Types/API/URL.js +241 -18
  177. package/build/dist/Types/API/URL.js.map +1 -1
  178. package/build/dist/Types/Billing/PayAsYouGoPricing.js +41 -0
  179. package/build/dist/Types/Billing/PayAsYouGoPricing.js.map +1 -0
  180. package/build/dist/Types/CustomField/CustomFieldType.js +26 -0
  181. package/build/dist/Types/CustomField/CustomFieldType.js.map +1 -1
  182. package/build/dist/Types/Monitor/MonitorType.js +10 -0
  183. package/build/dist/Types/Monitor/MonitorType.js.map +1 -1
  184. package/build/dist/Types/NetworkAutomation/RuleRunResult.js +39 -0
  185. package/build/dist/Types/NetworkAutomation/RuleRunResult.js.map +1 -0
  186. package/build/dist/Types/Runbook/RunbookStepType.js +21 -0
  187. package/build/dist/Types/Runbook/RunbookStepType.js.map +1 -1
  188. package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js +1 -1
  189. package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js.map +1 -1
  190. package/build/dist/UI/Components/Button/Button.js +10 -16
  191. package/build/dist/UI/Components/Button/Button.js.map +1 -1
  192. package/build/dist/UI/Components/Charts/Area/AreaChart.js +2 -1
  193. package/build/dist/UI/Components/Charts/Area/AreaChart.js.map +1 -1
  194. package/build/dist/UI/Components/Charts/Bar/BarChart.js +2 -1
  195. package/build/dist/UI/Components/Charts/Bar/BarChart.js.map +1 -1
  196. package/build/dist/UI/Components/Charts/ChartLibrary/Types/ChartDataPoint.js +12 -0
  197. package/build/dist/UI/Components/Charts/ChartLibrary/Types/ChartDataPoint.js.map +1 -1
  198. package/build/dist/UI/Components/Charts/Line/LineChart.js +2 -1
  199. package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
  200. package/build/dist/UI/Components/Charts/Utils/DataPoint.js +17 -10
  201. package/build/dist/UI/Components/Charts/Utils/DataPoint.js.map +1 -1
  202. package/build/dist/UI/Components/Dictionary/Dictionary.js +4 -18
  203. package/build/dist/UI/Components/Dictionary/Dictionary.js.map +1 -1
  204. package/build/dist/UI/Components/Icon/Icon.js +1 -1
  205. package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
  206. package/build/dist/UI/Components/LogsViewer/LogsViewer.js +3 -3
  207. package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
  208. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js +5 -1
  209. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js.map +1 -1
  210. package/build/dist/UI/Components/LogsViewer/components/LogsPagination.js +14 -44
  211. package/build/dist/UI/Components/LogsViewer/components/LogsPagination.js.map +1 -1
  212. package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js +1 -1
  213. package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js.map +1 -1
  214. package/build/dist/UI/Components/LogsViewer/components/SavedViewsDropdown.js +20 -2
  215. package/build/dist/UI/Components/LogsViewer/components/SavedViewsDropdown.js.map +1 -1
  216. package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js +42 -0
  217. package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js.map +1 -1
  218. package/build/dist/UI/Components/Pagination/Pagination.js +174 -199
  219. package/build/dist/UI/Components/Pagination/Pagination.js.map +1 -1
  220. package/build/dist/UI/Components/Pagination/PaginationUtil.js +160 -0
  221. package/build/dist/UI/Components/Pagination/PaginationUtil.js.map +1 -0
  222. package/build/dist/UI/Components/TelemetryViewer/components/SavedViewsDropdown.js +20 -2
  223. package/build/dist/UI/Components/TelemetryViewer/components/SavedViewsDropdown.js.map +1 -1
  224. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryPagination.js +23 -47
  225. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryPagination.js.map +1 -1
  226. package/build/dist/UI/Components/TextArea/TextArea.js +1 -1
  227. package/build/dist/UI/Components/TextArea/TextArea.js.map +1 -1
  228. package/build/dist/UI/Components/Workflow/ArgumentsForm.js +14 -2
  229. package/build/dist/UI/Components/Workflow/ArgumentsForm.js.map +1 -1
  230. package/build/dist/UI/Components/Workflow/ColumnEditor/AddColumnDropdown.js +86 -0
  231. package/build/dist/UI/Components/Workflow/ColumnEditor/AddColumnDropdown.js.map +1 -0
  232. package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnConditionRow.js +119 -0
  233. package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnConditionRow.js.map +1 -0
  234. package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnControl.js +206 -0
  235. package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnControl.js.map +1 -0
  236. package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnFieldRow.js +57 -0
  237. package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnFieldRow.js.map +1 -0
  238. package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnOperators.js +150 -0
  239. package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnOperators.js.map +1 -0
  240. package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnRow.js +67 -0
  241. package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnRow.js.map +1 -0
  242. package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnRowSerialization.js +309 -0
  243. package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnRowSerialization.js.map +1 -0
  244. package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnValueInput.js +204 -0
  245. package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnValueInput.js.map +1 -0
  246. package/build/dist/UI/Components/Workflow/ColumnEditor/ModelQueryBuilder.js +66 -0
  247. package/build/dist/UI/Components/Workflow/ColumnEditor/ModelQueryBuilder.js.map +1 -0
  248. package/build/dist/UI/Components/Workflow/ColumnEditor/ModelRecordForm.js +83 -0
  249. package/build/dist/UI/Components/Workflow/ColumnEditor/ModelRecordForm.js.map +1 -0
  250. package/build/dist/UI/Components/Workflow/ModelColumnEditor.js +187 -68
  251. package/build/dist/UI/Components/Workflow/ModelColumnEditor.js.map +1 -1
  252. package/build/dist/UI/Utils/NetworkAutomation/RunNetworkRule.js +75 -0
  253. package/build/dist/UI/Utils/NetworkAutomation/RunNetworkRule.js.map +1 -0
  254. package/build/dist/Utils/API.js +175 -34
  255. package/build/dist/Utils/API.js.map +1 -1
  256. package/build/dist/Utils/Monitor/NetworkDeviceRoleUtil.js +37 -0
  257. package/build/dist/Utils/Monitor/NetworkDeviceRoleUtil.js.map +1 -1
  258. package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +25 -3
  259. package/build/dist/Utils/Monitor/NetworkTopologyUtil.js.map +1 -1
  260. package/build/dist/Utils/NetworkAutomation/RuleRunSummary.js +78 -0
  261. package/build/dist/Utils/NetworkAutomation/RuleRunSummary.js.map +1 -0
  262. package/package.json +1 -1
@@ -0,0 +1,263 @@
1
+ /**
2
+ * Pure pagination math, kept out of the component so the page-window and
3
+ * range arithmetic can be unit tested without rendering anything.
4
+ */
5
+
6
+ /**
7
+ * A slot in the rendered page list. Numbers are clickable pages; the two
8
+ * ellipsis markers are the gaps that were collapsed on either side of the
9
+ * current page. They are distinct values so the renderer can give them
10
+ * stable React keys (a list can contain both).
11
+ */
12
+ export type PageWindowItem = number | "ellipsis-start" | "ellipsis-end";
13
+
14
+ /**
15
+ * Page sizes offered in the "rows per page" dropdown. Anything else the
16
+ * table is already using (a shared link can carry an arbitrary size) is
17
+ * merged in by `getItemsOnPageOptions` so the select always has a value
18
+ * that matches what is on screen.
19
+ */
20
+ export const DefaultItemsOnPageOptions: Array<number> = [10, 20, 25, 50, 100];
21
+
22
+ /**
23
+ * How many slots the numbered page list may occupy, ellipses included.
24
+ * Five is the whole story a page list has to tell - where you are, where
25
+ * both ends are, and that there is more in between - and it keeps the
26
+ * control a constant width from page 1 through page 10,000. Stepping to a
27
+ * neighbour is what the arrows are for; landing somewhere arbitrary is what
28
+ * the gaps are for.
29
+ */
30
+ export const DefaultMaxVisiblePages: number = 5;
31
+
32
+ /**
33
+ * The narrowest window the collapsing rules can honour: first, gap, current,
34
+ * gap, last. Anything smaller is raised to this.
35
+ */
36
+ const MinMaxVisiblePages: number = 5;
37
+
38
+ export interface ItemRange {
39
+ firstItemNumber: number;
40
+ lastItemNumber: number;
41
+ isEmpty: boolean;
42
+ }
43
+
44
+ export interface ItemRangeInput {
45
+ currentPageNumber: number;
46
+ itemsOnPage: number;
47
+ totalItemsCount: number;
48
+ /*
49
+ * Rows this page actually rendered, when the caller knows. Analytics list
50
+ * endpoints over-fetch one probe row to derive `hasMore` and count it in
51
+ * `totalItemsCount` even though the payload dropped it, so the printed
52
+ * range has to be clamped to rows the page can really show.
53
+ */
54
+ itemsOnCurrentPage?: number | undefined;
55
+ }
56
+
57
+ export default class PaginationUtil {
58
+ /**
59
+ * Total pages for a known item count. Always at least 1 — an empty table
60
+ * still sits on "page 1 of 1" rather than "page 1 of 0".
61
+ */
62
+ public static getTotalPageCount(
63
+ totalItemsCount: number,
64
+ itemsOnPage: number,
65
+ ): number {
66
+ const safeItemsOnPage: number = Math.max(Math.floor(itemsOnPage) || 0, 1);
67
+ const safeTotalItemsCount: number = Math.max(
68
+ Math.floor(totalItemsCount) || 0,
69
+ 0,
70
+ );
71
+
72
+ return Math.max(Math.ceil(safeTotalItemsCount / safeItemsOnPage), 1);
73
+ }
74
+
75
+ /**
76
+ * Clamp a page number into the pages that exist. Used both for what the
77
+ * control highlights and for what a hand-typed "go to page" is allowed to
78
+ * request.
79
+ */
80
+ public static clampPageNumber(
81
+ pageNumber: number,
82
+ totalPageCount: number,
83
+ ): number {
84
+ const safeTotalPageCount: number = Math.max(
85
+ Math.floor(totalPageCount) || 0,
86
+ 1,
87
+ );
88
+ const safePageNumber: number = Math.floor(pageNumber) || 1;
89
+
90
+ if (safePageNumber < 1) {
91
+ return 1;
92
+ }
93
+
94
+ return Math.min(safePageNumber, safeTotalPageCount);
95
+ }
96
+
97
+ /**
98
+ * The page numbers to render, with collapsed gaps marked. When the list
99
+ * fits it is returned whole; otherwise the first and last pages are always
100
+ * present (so "jump to the start / end" is one click) and the pages around
101
+ * the current one fill the remaining slots.
102
+ */
103
+ public static getPageWindow(
104
+ currentPageNumber: number,
105
+ totalPageCount: number,
106
+ maxVisiblePages: number = DefaultMaxVisiblePages,
107
+ ): Array<PageWindowItem> {
108
+ const safeTotalPageCount: number = Math.max(
109
+ Math.floor(totalPageCount) || 0,
110
+ 1,
111
+ );
112
+ const safeMaxVisiblePages: number = Math.max(
113
+ Math.floor(maxVisiblePages) || 0,
114
+ MinMaxVisiblePages,
115
+ );
116
+ const currentPage: number = PaginationUtil.clampPageNumber(
117
+ currentPageNumber,
118
+ safeTotalPageCount,
119
+ );
120
+
121
+ const allPages: (from: number, to: number) => Array<PageWindowItem> = (
122
+ from: number,
123
+ to: number,
124
+ ): Array<PageWindowItem> => {
125
+ const pages: Array<PageWindowItem> = [];
126
+ for (let page: number = from; page <= to; page++) {
127
+ pages.push(page);
128
+ }
129
+ return pages;
130
+ };
131
+
132
+ if (safeTotalPageCount <= safeMaxVisiblePages) {
133
+ return allPages(1, safeTotalPageCount);
134
+ }
135
+
136
+ /*
137
+ * Pages kept on each side of the current one. Five slots are already
138
+ * spent on the first page, the last page, the current page and the two
139
+ * ellipses, so whatever a wider window is given goes here - and the
140
+ * rendered width stays at `safeMaxVisiblePages`.
141
+ */
142
+ const siblingCount: number = Math.max(
143
+ Math.floor((safeMaxVisiblePages - 5) / 2),
144
+ 0,
145
+ );
146
+
147
+ /*
148
+ * A gap has to hide at least two pages to be worth drawing: an ellipsis
149
+ * standing in for a single page number is both a lie and wider than the
150
+ * number it replaced. Where only one page would be hidden, the run of
151
+ * pages is extended to swallow it instead.
152
+ */
153
+ const hasGapBefore: boolean = currentPage - siblingCount > 3;
154
+ const hasGapAfter: boolean =
155
+ currentPage + siblingCount < safeTotalPageCount - 2;
156
+
157
+ if (!hasGapBefore && hasGapAfter) {
158
+ const headCount: number = safeMaxVisiblePages - 2;
159
+ return [...allPages(1, headCount), "ellipsis-end", safeTotalPageCount];
160
+ }
161
+
162
+ if (hasGapBefore && !hasGapAfter) {
163
+ const tailCount: number = safeMaxVisiblePages - 2;
164
+ return [
165
+ 1,
166
+ "ellipsis-start",
167
+ ...allPages(safeTotalPageCount - tailCount + 1, safeTotalPageCount),
168
+ ];
169
+ }
170
+
171
+ if (hasGapBefore && hasGapAfter) {
172
+ return [
173
+ 1,
174
+ "ellipsis-start",
175
+ ...allPages(currentPage - siblingCount, currentPage + siblingCount),
176
+ "ellipsis-end",
177
+ safeTotalPageCount,
178
+ ];
179
+ }
180
+
181
+ /*
182
+ * Unreachable for a list longer than the window — with neither gap the
183
+ * list is at most `safeMaxVisiblePages - 2` pages long — but returning
184
+ * the whole list is the honest fallback rather than an exception.
185
+ */
186
+ return allPages(1, safeTotalPageCount);
187
+ }
188
+
189
+ /**
190
+ * The "Showing 21-30 of 240" range. `itemsOnCurrentPage`, when given, caps
191
+ * the range at rows the page really rendered, which is what keeps a short
192
+ * final page (and the analytics probe row) from being over-reported.
193
+ */
194
+ public static getItemRange(input: ItemRangeInput): ItemRange {
195
+ const itemsOnPage: number = Math.max(Math.floor(input.itemsOnPage) || 0, 1);
196
+ const totalItemsCount: number = Math.max(
197
+ Math.floor(input.totalItemsCount) || 0,
198
+ 0,
199
+ );
200
+ const currentPageNumber: number = Math.max(
201
+ Math.floor(input.currentPageNumber) || 1,
202
+ 1,
203
+ );
204
+
205
+ const alreadySeenCount: number = itemsOnPage * (currentPageNumber - 1);
206
+ const firstItemNumber: number = alreadySeenCount + 1;
207
+
208
+ // What the count proves is left after the pages already paged past.
209
+ const provenOnPage: number = Math.max(
210
+ totalItemsCount - alreadySeenCount,
211
+ 0,
212
+ );
213
+
214
+ /*
215
+ * A page can hold no more than its page size, no more than the count
216
+ * proves is left, and - where the caller says so - no more than it
217
+ * actually rendered.
218
+ */
219
+ const countOnPage: number = Math.min(
220
+ provenOnPage,
221
+ itemsOnPage,
222
+ input.itemsOnCurrentPage === undefined
223
+ ? Number.POSITIVE_INFINITY
224
+ : Math.max(input.itemsOnCurrentPage, 0),
225
+ );
226
+
227
+ const lastItemNumber: number = alreadySeenCount + countOnPage;
228
+
229
+ return {
230
+ firstItemNumber: firstItemNumber,
231
+ lastItemNumber: lastItemNumber,
232
+ isEmpty: lastItemNumber < firstItemNumber,
233
+ };
234
+ }
235
+
236
+ /**
237
+ * The page sizes to offer. The size in use is always present and the list
238
+ * stays sorted, so a table restored from a link with `itemsOnPage=37`
239
+ * shows 37 as the selected option instead of silently snapping to 10.
240
+ */
241
+ public static getItemsOnPageOptions(
242
+ itemsOnPage: number,
243
+ options: Array<number> = DefaultItemsOnPageOptions,
244
+ ): Array<number> {
245
+ const validOptions: Array<number> = options.filter((option: number) => {
246
+ return Number.isFinite(option) && option > 0;
247
+ });
248
+
249
+ const uniqueOptions: Array<number> = Array.from(new Set(validOptions));
250
+
251
+ if (
252
+ Number.isFinite(itemsOnPage) &&
253
+ itemsOnPage > 0 &&
254
+ !uniqueOptions.includes(itemsOnPage)
255
+ ) {
256
+ uniqueOptions.push(itemsOnPage);
257
+ }
258
+
259
+ return uniqueOptions.sort((a: number, b: number) => {
260
+ return a - b;
261
+ });
262
+ }
263
+ }
@@ -0,0 +1,15 @@
1
+ # Pagination screenshots
2
+
3
+ What the shared pagination control (`../Pagination.tsx`) looks like in the
4
+ states that are worth arguing about. Rendered from the real component against
5
+ the dashboard's own Tailwind build, on a mock table.
6
+
7
+ | File | Shows |
8
+ | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
9
+ | `pagination-before-after.png` | The control before this change (page size and page number hidden behind a light-grey icon button) and after it (rows per page, the pages and the range all on the page). |
10
+ | `pagination-long-list.png` | Deep in a 24-page list, with the gaps collapsed; a short final page; and an empty result set. |
11
+ | `pagination-go-to-page-modal.png` | The jump-to-a-page dialog, opened by clicking one of the collapsed gaps. |
12
+ | `pagination-telemetry.png` | The compact skin used under logs and traces, and has-more mode, where the endpoint skips `COUNT(*)` so there is no last page to link to. |
13
+ | `pagination-mobile.png` | A narrow screen, where the numbered list gives way to a page-of-pages indicator. |
14
+
15
+ Re-shoot these when the control's layout changes.
@@ -8,6 +8,13 @@ export interface SavedViewsDropdownProps {
8
8
  savedViews: Array<SavedViewOption>;
9
9
  selectedSavedViewId?: string | null | undefined;
10
10
  onSelect: (viewId: string) => void;
11
+ /*
12
+ * Deselect the active view and return the explorer to its unfiltered
13
+ * default. Without this the checkmark is a one-way door: a view can be
14
+ * applied but never taken off. Hosts that cannot express "no view" simply
15
+ * omit it, and both clear affordances below disappear with it.
16
+ */
17
+ onClear?: (() => void) | undefined;
11
18
  onCreate?: (() => void) | undefined;
12
19
  onEdit?: ((viewId: string) => void) | undefined;
13
20
  onDelete?: ((viewId: string) => void) | undefined;
@@ -32,6 +39,17 @@ const SavedViewsDropdown: FunctionComponent<SavedViewsDropdownProps> = (
32
39
  },
33
40
  );
34
41
 
42
+ /*
43
+ * Clearing is only offered once a view is actually applied — an inert
44
+ * "Clear view" row sitting above an unfiltered explorer says nothing.
45
+ */
46
+ const canClear: boolean = Boolean(props.onClear && selectedView);
47
+
48
+ const clearSelection: () => void = (): void => {
49
+ props.onClear?.();
50
+ setIsComponentVisible(false);
51
+ };
52
+
35
53
  return (
36
54
  <div className="relative" ref={ref}>
37
55
  <button
@@ -77,6 +95,31 @@ const SavedViewsDropdown: FunctionComponent<SavedViewsDropdownProps> = (
77
95
  props.dropdownAlignment === "right" ? "right-0" : "left-0"
78
96
  }`}
79
97
  >
98
+ {/*
99
+ * Explicit way back to the unfiltered explorer. Clicking the
100
+ * applied view also clears it, but that is only discoverable once
101
+ * you try it — this row says so out loud.
102
+ */}
103
+ {canClear && (
104
+ <div className="border-b border-gray-100 py-1">
105
+ <button
106
+ type="button"
107
+ className="flex w-full items-center gap-2 px-3 py-1.5 text-left transition-colors hover:bg-gray-50"
108
+ onClick={clearSelection}
109
+ >
110
+ <span
111
+ className="w-4 shrink-0 text-center text-xs text-gray-400"
112
+ aria-hidden="true"
113
+ >
114
+
115
+ </span>
116
+ <span className="truncate text-sm text-gray-600">
117
+ Clear view
118
+ </span>
119
+ </button>
120
+ </div>
121
+ )}
122
+
80
123
  {/* View list */}
81
124
  <div className="max-h-72 overflow-y-auto py-1">
82
125
  {props.savedViews.length === 0 && (
@@ -98,13 +141,35 @@ const SavedViewsDropdown: FunctionComponent<SavedViewsDropdownProps> = (
98
141
  <button
99
142
  type="button"
100
143
  className="flex min-w-0 flex-1 items-center gap-2 text-left"
144
+ /*
145
+ * Spelled out so the row announces as its own name: the
146
+ * ✓ is aria-hidden, and the "default" tag reads as a word
147
+ * rather than a bare token appended to the view name.
148
+ */
149
+ aria-label={
150
+ view.isDefault ? `${view.name} (default)` : view.name
151
+ }
152
+ /*
153
+ * The checkmark reads as a checkbox, so the row behaves
154
+ * like one: clicking the applied view takes it off again
155
+ * instead of silently re-applying what is already on.
156
+ */
157
+ aria-pressed={isSelected}
101
158
  onClick={() => {
159
+ if (isSelected && props.onClear) {
160
+ clearSelection();
161
+ return;
162
+ }
163
+
102
164
  props.onSelect(view.id);
103
165
  setIsComponentVisible(false);
104
166
  }}
105
167
  >
106
168
  {/* Checkmark for selected */}
107
- <span className="w-4 shrink-0 text-center text-xs">
169
+ <span
170
+ className="w-4 shrink-0 text-center text-xs"
171
+ aria-hidden="true"
172
+ >
108
173
  {isSelected ? (
109
174
  <span className="text-indigo-600">✓</span>
110
175
  ) : (
@@ -1,3 +1,4 @@
1
+ import Pagination from "../../Pagination/Pagination";
1
2
  import React, { FunctionComponent, ReactElement } from "react";
2
3
 
3
4
  export interface TelemetryPaginationProps {
@@ -11,103 +12,48 @@ export interface TelemetryPaginationProps {
11
12
  itemLabel?: string | undefined;
12
13
  }
13
14
 
15
+ /**
16
+ * The telemetry footer is the shared pagination control in its compact skin,
17
+ * so "rows per page" and jump-to-page behave the same here as they do under
18
+ * every table in the product.
19
+ */
14
20
  const TelemetryPagination: FunctionComponent<TelemetryPaginationProps> = (
15
21
  props: TelemetryPaginationProps,
16
22
  ): ReactElement => {
17
- const totalPages: number = Math.max(
18
- 1,
19
- Math.ceil(
20
- props.totalItems === 0
21
- ? 1
22
- : props.totalItems / Math.max(props.pageSize, 1),
23
- ),
24
- );
25
-
26
- const safeCurrentPage: number = Math.min(props.currentPage, totalPages);
27
-
28
- const firstItemIndex: number =
29
- props.totalItems === 0 ? 0 : (safeCurrentPage - 1) * props.pageSize + 1;
30
- const lastItemIndex: number =
31
- props.totalItems === 0
32
- ? 0
33
- : Math.min(props.totalItems, safeCurrentPage * props.pageSize);
34
-
35
- const disablePrev: boolean =
36
- props.isDisabled || props.totalItems === 0 || safeCurrentPage <= 1;
37
- const disableNext: boolean =
38
- props.isDisabled || props.totalItems === 0 || safeCurrentPage >= totalPages;
23
+ /*
24
+ * Callers pass a plural label ("traces", "spans"). The singular is only
25
+ * used for a one-row result set, where trimming a trailing "s" is right
26
+ * for every label these views pass.
27
+ */
28
+ const pluralLabel: string = props.itemLabel || "results";
29
+ const singularLabel: string = pluralLabel.endsWith("s")
30
+ ? pluralLabel.slice(0, -1)
31
+ : pluralLabel;
39
32
 
40
33
  return (
41
- <div className="flex flex-col gap-3 border-t border-gray-200 bg-gray-50/50 px-4 py-2.5 text-xs text-gray-500 md:flex-row md:items-center md:justify-between">
42
- <div>
43
- {props.totalItems === 0 ? (
44
- <span className="text-gray-500">
45
- No {props.itemLabel || "results"} to display.
46
- </span>
47
- ) : (
48
- <span className="text-gray-500">
49
- Showing {firstItemIndex.toLocaleString()}-
50
- {lastItemIndex.toLocaleString()} of{" "}
51
- {props.totalItems.toLocaleString()}
52
- </span>
53
- )}
54
- </div>
55
-
56
- <div className="flex flex-wrap items-center gap-3">
57
- <label className="flex items-center gap-2">
58
- <span className="text-[10px] uppercase tracking-wide text-gray-400">
59
- Rows
60
- </span>
61
- <select
62
- className="rounded-md border border-gray-200 bg-white px-2 py-1 text-xs text-gray-700 focus:border-indigo-400 focus:outline-none focus:ring-1 focus:ring-indigo-200"
63
- value={props.pageSize}
64
- onChange={(event: React.ChangeEvent<HTMLSelectElement>) => {
65
- const size: number = Number(event.target.value) || props.pageSize;
66
- props.onPageSizeChange(size);
67
- }}
68
- disabled={props.isDisabled}
69
- >
70
- {props.pageSizeOptions.map((option: number) => {
71
- return (
72
- <option key={option} value={option}>
73
- {option}
74
- </option>
75
- );
76
- })}
77
- </select>
78
- </label>
79
-
80
- <div className="inline-flex items-center gap-1 rounded-lg border border-gray-200 bg-white p-0.5">
81
- <button
82
- type="button"
83
- className="rounded-md px-3 py-1 text-xs font-medium text-gray-600 transition-colors hover:bg-gray-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-300 disabled:cursor-not-allowed disabled:opacity-40"
84
- onClick={() => {
85
- if (!disablePrev) {
86
- props.onPageChange(Math.max(1, safeCurrentPage - 1));
87
- }
88
- }}
89
- disabled={disablePrev}
90
- >
91
- Previous
92
- </button>
93
- <span className="px-3 text-[11px] text-gray-400">
94
- Page {safeCurrentPage} / {totalPages}
95
- </span>
96
- <button
97
- type="button"
98
- className="rounded-md px-3 py-1 text-xs font-medium text-gray-600 transition-colors hover:bg-gray-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-300 disabled:cursor-not-allowed disabled:opacity-40"
99
- onClick={() => {
100
- if (!disableNext) {
101
- props.onPageChange(Math.min(totalPages, safeCurrentPage + 1));
102
- }
103
- }}
104
- disabled={disableNext}
105
- >
106
- Next
107
- </button>
108
- </div>
109
- </div>
110
- </div>
34
+ <Pagination
35
+ dataTestId="telemetry-pagination"
36
+ singularLabel={singularLabel}
37
+ pluralLabel={pluralLabel}
38
+ currentPageNumber={props.currentPage}
39
+ totalItemsCount={props.totalItems}
40
+ itemsOnPage={props.pageSize}
41
+ itemsOnPageOptions={props.pageSizeOptions}
42
+ isCompact={true}
43
+ className="bg-gray-50/50"
44
+ isLoading={false}
45
+ isError={false}
46
+ isDisabled={Boolean(props.isDisabled)}
47
+ onNavigateToPage={(pageNumber: number, itemsOnPage: number) => {
48
+ if (itemsOnPage !== props.pageSize) {
49
+ props.onPageSizeChange(itemsOnPage);
50
+ }
51
+
52
+ if (pageNumber !== props.currentPage) {
53
+ props.onPageChange(pageNumber);
54
+ }
55
+ }}
56
+ />
111
57
  );
112
58
  };
113
59
 
@@ -22,6 +22,8 @@ export interface ComponentProps {
22
22
  autoFocus?: boolean | undefined;
23
23
  dataTestId?: string | undefined;
24
24
  disableSpellCheck?: boolean | undefined;
25
+ /** The id of the element that names this control, when it is labelled elsewhere. */
26
+ ariaLabelledby?: string | undefined;
25
27
  }
26
28
 
27
29
  const TextArea: FunctionComponent<ComponentProps> = (
@@ -71,6 +73,7 @@ const TextArea: FunctionComponent<ComponentProps> = (
71
73
  value={text}
72
74
  rows={6}
73
75
  spellCheck={!props.disableSpellCheck}
76
+ aria-labelledby={props.ariaLabelledby}
74
77
  aria-invalid={props.error ? "true" : undefined}
75
78
  aria-describedby={props.error ? "textarea-error-message" : undefined}
76
79
  onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => {
@@ -6,7 +6,10 @@ import { CustomElementProps } from "../Forms/Types/Field";
6
6
  import FormValues from "../Forms/Types/FormValues";
7
7
  import ComponentValuePickerModal from "./ComponentValuePickerModal";
8
8
  import CronScheduleField from "./CronScheduleField";
9
- import ModelColumnEditor, { ModelColumnEditorMode } from "./ModelColumnEditor";
9
+ import ModelColumnEditor, {
10
+ ModelColumnEditorMode,
11
+ RecordIntent,
12
+ } from "./ModelColumnEditor";
10
13
  import ModelFieldPicker from "./ModelFieldPicker";
11
14
  import {
12
15
  componentInputTypeToFormFieldType,
@@ -488,6 +491,20 @@ const ArgumentsForm: FunctionComponent<ComponentProps> = (
488
491
  <ModelColumnEditor
489
492
  tableName={component.metadata.tableName as string}
490
493
  mode={columnEditorMode}
494
+ /*
495
+ * A create writes a whole record, so it opens with a
496
+ * row for every column it must be given. An update
497
+ * legitimately writes one column, and seeding it
498
+ * with six would present five fields nobody asked
499
+ * for. The create payload is the argument called
500
+ * "json" (BaseModel components); an update's is
501
+ * "data".
502
+ */
503
+ recordIntent={
504
+ arg.id === "json"
505
+ ? RecordIntent.Create
506
+ : RecordIntent.Update
507
+ }
491
508
  initialValue={customProps.initialValue}
492
509
  onChange={(value: string) => {
493
510
  void customProps.onChange?.(value);