@skyhook-io/radar-app 1.16.3 → 1.17.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (321) hide show
  1. package/README.md +1 -1
  2. package/package.json +3 -3
  3. package/src/App.tsx +138 -93
  4. package/src/RadarApp.tsx +27 -17
  5. package/src/api/actions.errorCode.test.ts +40 -0
  6. package/src/api/actions.test.ts +48 -0
  7. package/src/api/actions.ts +79 -0
  8. package/src/api/apiResources.navigation.test.tsx +232 -0
  9. package/src/api/apiResources.ts +46 -3
  10. package/src/api/client.goneResource.test.ts +49 -0
  11. package/src/api/client.helmSource.test.ts +39 -0
  12. package/src/api/client.namespaceSwitch.hook.test.tsx +54 -0
  13. package/src/api/client.namespaceSwitch.test.ts +107 -0
  14. package/src/api/client.ts +381 -47
  15. package/src/api/cnpg-ha.live.test.ts +140 -0
  16. package/src/api/cnpg-ha.test.ts +78 -0
  17. package/src/api/cnpg-ha.ts +19 -0
  18. package/src/api/cnpg-history.ts +123 -0
  19. package/src/api/cnpg-inspect.ts +102 -0
  20. package/src/api/cnpg-protection.ts +112 -0
  21. package/src/api/cnpg-recovery.test.tsx +60 -0
  22. package/src/api/cnpg-recovery.ts +262 -0
  23. package/src/api/cnpg-sessions.ts +175 -0
  24. package/src/api/cnpg-storage.ts +138 -0
  25. package/src/api/cnpg.legacy-reads.test.ts +43 -0
  26. package/src/api/cnpg.pooler-runtime.test.ts +15 -0
  27. package/src/api/cnpg.ts +428 -10
  28. package/src/api/diagnose.test.ts +40 -0
  29. package/src/api/diagnose.ts +12 -1
  30. package/src/api/radarFeatures.render.test.tsx +65 -3
  31. package/src/api/radarFeatures.ts +6 -0
  32. package/src/api/reviewedActionMutation.test.tsx +164 -0
  33. package/src/api/reviewedActionMutation.ts +66 -0
  34. package/src/api/timelineSource.test.ts +9 -0
  35. package/src/api/timelineSource.ts +8 -0
  36. package/src/api/traffic.sources.test.ts +55 -0
  37. package/src/api/traffic.ts +173 -14
  38. package/src/components/applications/ApplicationsView.tsx +3 -0
  39. package/src/components/audit/AuditView.tsx +9 -0
  40. package/src/components/capacity/CapacityActivity.tsx +3 -31
  41. package/src/components/capacity/CapacityDemand.tsx +4 -36
  42. package/src/components/capacity/CapacityOverview.tsx +217 -56
  43. package/src/components/capacity/CapacityPoolDetail.tsx +24 -62
  44. package/src/components/capacity/CapacityView.test.tsx +354 -1
  45. package/src/components/capacity/ClusterSchedulingCard.tsx +2 -10
  46. package/src/components/capacity/certaintyGlyph.test.tsx +2 -9
  47. package/src/components/capacity/routes.ts +42 -0
  48. package/src/components/capacity/shared.tsx +23 -150
  49. package/src/components/capacity/workspaceRoute.ts +18 -0
  50. package/src/components/cnpg/CNPGActionHost.compatibility.test.tsx +30 -0
  51. package/src/components/cnpg/CNPGArchivingRepair.tsx +191 -0
  52. package/src/components/cnpg/CNPGBlockingSessions.test.tsx +50 -0
  53. package/src/components/cnpg/CNPGBlockingSessions.tsx +333 -0
  54. package/src/components/cnpg/CNPGClusterActivity.test.tsx +11 -0
  55. package/src/components/cnpg/CNPGClusterActivity.tsx +18 -9
  56. package/src/components/cnpg/CNPGClusterLogs.compatibility.test.tsx +130 -0
  57. package/src/components/cnpg/CNPGClusterLogs.empty.test.tsx +29 -0
  58. package/src/components/cnpg/CNPGClusterLogs.test.ts +9 -0
  59. package/src/components/cnpg/CNPGClusterLogs.tsx +81 -24
  60. package/src/components/cnpg/CNPGClusterTabs.test.tsx +152 -0
  61. package/src/components/cnpg/CNPGClusterTabs.tsx +187 -0
  62. package/src/components/cnpg/CNPGClustersKindView.tsx +46 -0
  63. package/src/components/cnpg/CNPGConfiguration.test.tsx +223 -0
  64. package/src/components/cnpg/CNPGConfiguration.tsx +190 -0
  65. package/src/components/cnpg/CNPGCreateClusterDialog.test.tsx +113 -0
  66. package/src/components/cnpg/CNPGCreateClusterDialog.tsx +74 -0
  67. package/src/components/cnpg/CNPGDeclarations.test.ts +107 -0
  68. package/src/components/cnpg/CNPGDeclarations.tsx +236 -78
  69. package/src/components/cnpg/CNPGDetailPage.test.tsx +62 -0
  70. package/src/components/cnpg/CNPGDetailPage.tsx +159 -98
  71. package/src/components/cnpg/CNPGDrawerTrail.tsx +2 -2
  72. package/src/components/cnpg/CNPGInvestigationAction.test.tsx +30 -0
  73. package/src/components/cnpg/CNPGOperator.test.tsx +34 -0
  74. package/src/components/cnpg/CNPGOperator.tsx +78 -30
  75. package/src/components/cnpg/CNPGOperatorBanner.tsx +39 -0
  76. package/src/components/cnpg/CNPGOperatorConditions.test.tsx +45 -0
  77. package/src/components/cnpg/CNPGOperatorConditions.tsx +16 -0
  78. package/src/components/cnpg/CNPGOperatorConfig.test.tsx +27 -0
  79. package/src/components/cnpg/CNPGOperatorDiagnosis.tsx +247 -0
  80. package/src/components/cnpg/CNPGOverview.test.tsx +171 -0
  81. package/src/components/cnpg/CNPGOverview.tsx +238 -63
  82. package/src/components/cnpg/CNPGParametersInEffect.test.tsx +51 -0
  83. package/src/components/cnpg/CNPGParametersInEffect.tsx +120 -0
  84. package/src/components/cnpg/CNPGPerformance.prerequisite.test.tsx +48 -0
  85. package/src/components/cnpg/CNPGPerformance.test.tsx +73 -0
  86. package/src/components/cnpg/CNPGPerformance.tsx +477 -0
  87. package/src/components/cnpg/CNPGPooling.test.tsx +89 -0
  88. package/src/components/cnpg/CNPGPooling.tsx +97 -40
  89. package/src/components/cnpg/CNPGProtection.test.tsx +145 -0
  90. package/src/components/cnpg/CNPGProtection.tsx +230 -124
  91. package/src/components/cnpg/CNPGReplicationTab.test.tsx +87 -0
  92. package/src/components/cnpg/CNPGReplicationTab.tsx +212 -0
  93. package/src/components/cnpg/CNPGReplicationView.tsx +264 -0
  94. package/src/components/cnpg/CNPGStorage.test.tsx +140 -0
  95. package/src/components/cnpg/CNPGStorage.tsx +619 -0
  96. package/src/components/cnpg/CNPGSummaryHost.overview.test.tsx +44 -0
  97. package/src/components/cnpg/CNPGSummaryHost.test.tsx +23 -0
  98. package/src/components/cnpg/CNPGSummaryHost.tsx +228 -54
  99. package/src/components/cnpg/CNPGTrends.denied.test.tsx +33 -0
  100. package/src/components/cnpg/CNPGTrends.missing.test.tsx +69 -0
  101. package/src/components/cnpg/CNPGTrends.test.ts +38 -0
  102. package/src/components/cnpg/CNPGTrends.tsx +702 -0
  103. package/src/components/cnpg/CNPGView.tsx +34 -21
  104. package/src/components/cnpg/CNPGWorkspace.compatibility.test.tsx +90 -0
  105. package/src/components/cnpg/actions/CNPGClusterActions.dialog.test.tsx +79 -0
  106. package/src/components/cnpg/actions/CNPGClusterActions.test.tsx +142 -0
  107. package/src/components/cnpg/actions/CNPGClusterActions.tsx +853 -0
  108. package/src/components/cnpg/actions/CNPGConnectButton.read.test.tsx +40 -0
  109. package/src/components/cnpg/actions/CNPGConnectButton.test.ts +25 -0
  110. package/src/components/cnpg/actions/CNPGConnectButton.tsx +131 -0
  111. package/src/components/cnpg/actions/CNPGDestroyInstanceDialog.tsx +185 -0
  112. package/src/components/cnpg/actions/CNPGInstanceActions.test.tsx +38 -0
  113. package/src/components/cnpg/actions/CNPGInstanceActions.tsx +74 -0
  114. package/src/components/cnpg/actions/CNPGMaintenanceBanner.tsx +53 -0
  115. package/src/components/cnpg/actions/CNPGPoolerActions.test.tsx +35 -0
  116. package/src/components/cnpg/actions/CNPGPoolerActions.tsx +79 -0
  117. package/src/components/cnpg/actions/CNPGReportDialog.test.tsx +12 -0
  118. package/src/components/cnpg/actions/CNPGReportDialog.tsx +91 -0
  119. package/src/components/cnpg/actions/CNPGScheduleActions.run.test.tsx +58 -0
  120. package/src/components/cnpg/actions/CNPGScheduleActions.test.tsx +54 -0
  121. package/src/components/cnpg/actions/CNPGScheduleActions.tsx +208 -0
  122. package/src/components/cnpg/actions/actionModel.test.ts +111 -0
  123. package/src/components/cnpg/actions/actionModel.ts +127 -0
  124. package/src/components/cnpg/actions/renderCNPGHeaderActions.tsx +60 -0
  125. package/src/components/cnpg/actions/useCNPGWriteGuard.tsx +58 -0
  126. package/src/components/cnpg/actions/useOpenCNPGPsql.ts +22 -0
  127. package/src/components/cnpg/archivingRepair.test.ts +104 -0
  128. package/src/components/cnpg/archivingRepair.ts +145 -0
  129. package/src/components/cnpg/baseBackup.test.ts +36 -0
  130. package/src/components/cnpg/baseBackup.ts +36 -0
  131. package/src/components/cnpg/blocking.test.ts +59 -0
  132. package/src/components/cnpg/blocking.ts +131 -0
  133. package/src/components/cnpg/coverageText.test.ts +27 -0
  134. package/src/components/cnpg/creation/CNPGTargetFields.tsx +110 -0
  135. package/src/components/cnpg/creation/targetModel.test.ts +137 -0
  136. package/src/components/cnpg/creation/targetModel.ts +149 -0
  137. package/src/components/cnpg/fleetStatus.test.ts +46 -0
  138. package/src/components/cnpg/fleetStatus.ts +43 -0
  139. package/src/components/cnpg/formFields.ts +2 -0
  140. package/src/components/cnpg/grantText.test.tsx +11 -0
  141. package/src/components/cnpg/host.test.tsx +24 -0
  142. package/src/components/cnpg/host.tsx +101 -0
  143. package/src/components/cnpg/inspectModel.test.ts +120 -0
  144. package/src/components/cnpg/inspectModel.ts +140 -0
  145. package/src/components/cnpg/logicalSlots.test.ts +30 -0
  146. package/src/components/cnpg/logicalSlots.ts +29 -0
  147. package/src/components/cnpg/lsn.test.ts +40 -0
  148. package/src/components/cnpg/lsn.ts +43 -0
  149. package/src/components/cnpg/operations/CNPGFleetOperation.tsx +30 -0
  150. package/src/components/cnpg/operations/CNPGOperationTracker.test.tsx +247 -0
  151. package/src/components/cnpg/operations/CNPGOperationTracker.tsx +255 -0
  152. package/src/components/cnpg/operations/model.test.ts +510 -0
  153. package/src/components/cnpg/operations/model.ts +499 -0
  154. package/src/components/cnpg/operations/presentation.test.ts +80 -0
  155. package/src/components/cnpg/operations/presentation.ts +84 -0
  156. package/src/components/cnpg/operations/store.ts +98 -0
  157. package/src/components/cnpg/operatorStatus.test.ts +134 -0
  158. package/src/components/cnpg/operatorStatus.ts +194 -0
  159. package/src/components/cnpg/paths.test.ts +54 -0
  160. package/src/components/cnpg/paths.ts +47 -4
  161. package/src/components/cnpg/protection/CNPGAttachArchiveDialog.tsx +185 -0
  162. package/src/components/cnpg/protection/CNPGCreateScheduleDialog.tsx +196 -0
  163. package/src/components/cnpg/protection/CNPGProtectionSetup.test.tsx +121 -0
  164. package/src/components/cnpg/protection/CNPGProtectionSetup.tsx +452 -0
  165. package/src/components/cnpg/protection/CNPGScheduleInput.tsx +115 -0
  166. package/src/components/cnpg/protection/CNPGScheduleRepairButton.tsx +120 -0
  167. package/src/components/cnpg/protection/scheduleModel.test.ts +19 -0
  168. package/src/components/cnpg/protection/scheduleModel.ts +16 -0
  169. package/src/components/cnpg/recovery/CNPGRecoveryPoint.tsx +220 -0
  170. package/src/components/cnpg/recovery/CNPGRestoreButton.test.tsx +29 -0
  171. package/src/components/cnpg/recovery/CNPGRestoreButton.tsx +26 -0
  172. package/src/components/cnpg/recovery/CNPGRestoreChecks.test.tsx +23 -0
  173. package/src/components/cnpg/recovery/CNPGRestoreChecks.tsx +153 -0
  174. package/src/components/cnpg/recovery/CNPGRestoreDialog.back.test.tsx +149 -0
  175. package/src/components/cnpg/recovery/CNPGRestoreDialog.test.tsx +46 -0
  176. package/src/components/cnpg/recovery/CNPGRestoreDialog.tsx +333 -0
  177. package/src/components/cnpg/recovery/CNPGRestoreProgress.tsx +182 -0
  178. package/src/components/cnpg/recovery/CNPGRestoreValidation.tsx +216 -0
  179. package/src/components/cnpg/recovery/restoreModel.test.ts +709 -0
  180. package/src/components/cnpg/recovery/restoreModel.ts +685 -0
  181. package/src/components/cnpg/recovery/restoreOperation.ts +56 -0
  182. package/src/components/cnpg/refresh.test.ts +38 -0
  183. package/src/components/cnpg/refresh.ts +17 -0
  184. package/src/components/cnpg/refreshNotice.test.tsx +29 -0
  185. package/src/components/cnpg/routes.test.ts +34 -3
  186. package/src/components/cnpg/routes.ts +60 -48
  187. package/src/components/cnpg/runtimeAssessment.ts +198 -0
  188. package/src/components/cnpg/runtimeModel.test.ts +175 -0
  189. package/src/components/cnpg/runtimeModel.ts +196 -0
  190. package/src/components/cnpg/runtimeParts.test.tsx +17 -0
  191. package/src/components/cnpg/runtimeParts.tsx +79 -0
  192. package/src/components/cnpg/shared.test.tsx +59 -0
  193. package/src/components/cnpg/shared.tsx +100 -182
  194. package/src/components/cnpg/storageModel.test.ts +107 -0
  195. package/src/components/cnpg/storageModel.ts +101 -0
  196. package/src/components/cnpg/trendSamples.test.ts +148 -0
  197. package/src/components/cnpg/trendSamples.ts +222 -0
  198. package/src/components/cnpg/useCNPGClusterAssessment.ts +30 -0
  199. package/src/components/cnpg/useCNPGKubectlContext.test.tsx +45 -0
  200. package/src/components/cnpg/useCNPGKubectlContext.ts +13 -0
  201. package/src/components/cnpg/useCNPGNavigate.test.tsx +58 -0
  202. package/src/components/cnpg/useCNPGNavigate.ts +21 -0
  203. package/src/components/cnpg/useCNPGPoolerLive.ts +46 -0
  204. package/src/components/cnpg/useCNPGScreenParams.ts +24 -0
  205. package/src/components/cnpg/useCNPGSidebarWorkspace.ts +66 -16
  206. package/src/components/cnpg/workspaceRoute.ts +23 -0
  207. package/src/components/cost/ApplicationCostTab.test.ts +7 -0
  208. package/src/components/cost/ApplicationCostTab.tsx +7 -7
  209. package/src/components/cost/CostView.test.ts +2 -1
  210. package/src/components/cost/CostView.tsx +0 -2
  211. package/src/components/cost/WorkloadCostTab.tsx +2 -4
  212. package/src/components/cost/errors.ts +3 -1
  213. package/src/components/cost/source.test.ts +10 -0
  214. package/src/components/cost/source.ts +5 -1
  215. package/src/components/diagnose/AgentSetupNotice.test.tsx +58 -0
  216. package/src/components/diagnose/AgentSetupNotice.tsx +142 -51
  217. package/src/components/diagnose/ApplyDialog.ack.test.tsx +47 -0
  218. package/src/components/diagnose/ApplyDialog.test.tsx +59 -6
  219. package/src/components/diagnose/ApplyDialog.tsx +26 -28
  220. package/src/components/diagnose/DiagnoseContext.test.ts +43 -0
  221. package/src/components/diagnose/DiagnoseContext.tsx +136 -54
  222. package/src/components/diagnose/DiagnoseSurface.tsx +16 -3
  223. package/src/components/diagnose/InvestigationView.tsx +39 -2
  224. package/src/components/diagnose/LocalDiagnoseAction.tsx +40 -20
  225. package/src/components/diagnose/agentCatalog.ts +2 -2
  226. package/src/components/diagnose/diagnoseEvidenceTypes.ts +2 -0
  227. package/src/components/diagnose/investigationEvidence/adapters/issues.ts +8 -0
  228. package/src/components/diagnose/investigationEvidence/projection.test.ts +16 -0
  229. package/src/components/dock/BottomDock.tsx +2 -0
  230. package/src/components/dock/TerminalTab.test.tsx +17 -0
  231. package/src/components/dock/TerminalTab.tsx +7 -3
  232. package/src/components/dock/TrafficFlowListTab.tsx +27 -6
  233. package/src/components/execution/BatchExecutionView.tsx +11 -11
  234. package/src/components/gitops/ArgoResourceDiffLoader.tsx +4 -2
  235. package/src/components/gitops/GitOpsView.tsx +16 -23
  236. package/src/components/helm/ChartBrowser.tsx +3 -3
  237. package/src/components/helm/HelmCompareRoute.test.tsx +59 -0
  238. package/src/components/helm/HelmCompareRoute.tsx +57 -26
  239. package/src/components/helm/HelmReleaseDrawer.test.ts +55 -3
  240. package/src/components/helm/HelmReleaseDrawer.tsx +50 -12
  241. package/src/components/helm/RevisionHistory.tsx +3 -2
  242. package/src/components/helm/TrackChartSourceDialog.render.test.tsx +120 -0
  243. package/src/components/helm/TrackChartSourceDialog.test.ts +50 -0
  244. package/src/components/helm/TrackChartSourceDialog.tsx +153 -176
  245. package/src/components/helm/ValuesViewer.test.tsx +301 -0
  246. package/src/components/helm/ValuesViewer.tsx +169 -78
  247. package/src/components/home/ActivitySummary.tsx +2 -1
  248. package/src/components/home/ClusterHealthCard.tsx +7 -0
  249. package/src/components/home/HomeView.tsx +20 -9
  250. package/src/components/home/ProblemsPanel.test.tsx +81 -0
  251. package/src/components/home/TopologyPreview.tsx +2 -2
  252. package/src/components/home/mcpToolCatalog.ts +1 -1
  253. package/src/components/issues/IssuesPane.render.test.tsx +122 -0
  254. package/src/components/issues/IssuesPane.test.ts +65 -1
  255. package/src/components/issues/IssuesPane.tsx +188 -25
  256. package/src/components/issues/uncheckedSources.ts +21 -0
  257. package/src/components/resource/PVCUsageBar.render.test.tsx +32 -0
  258. package/src/components/resource/PVCUsageBar.test.tsx +24 -0
  259. package/src/components/resource/PVCUsageBar.tsx +15 -6
  260. package/src/components/resource/WorkloadMetricsHelpDialog.test.tsx +3 -1
  261. package/src/components/resource/WorkloadMetricsHelpDialog.tsx +1 -1
  262. package/src/components/resources/CompositeRenderer.tsx +4 -4
  263. package/src/components/resources/ResourceDetailDrawer.tsx +2 -2
  264. package/src/components/resources/ResourcesView.tsx +26 -12
  265. package/src/components/resources/renderers/KyvernoPolicyQueued.tsx +2 -1
  266. package/src/components/resources/renderers/NodeRenderer.tsx +7 -1
  267. package/src/components/resources/renderers/ServiceRenderer.test.tsx +81 -0
  268. package/src/components/resources/renderers/ServiceRenderer.tsx +19 -14
  269. package/src/components/settings/LocalIntegrationStatus.test.tsx +52 -0
  270. package/src/components/settings/LocalIntegrationStatus.tsx +3 -4
  271. package/src/components/settings/SettingsDialog.tsx +150 -34
  272. package/src/components/shared/LargeClusterNamespacePicker.tsx +11 -5
  273. package/src/components/timeline/TimelineList.tsx +25 -24
  274. package/src/components/timeline/TimelineLoadError.test.tsx +62 -0
  275. package/src/components/timeline/TimelineLoadError.tsx +96 -0
  276. package/src/components/timeline/TimelineRetentionNote.test.tsx +49 -0
  277. package/src/components/timeline/TimelineRetentionNote.tsx +20 -0
  278. package/src/components/timeline/TimelineView.tsx +25 -45
  279. package/src/components/timeline/ViewModeToggle.tsx +20 -0
  280. package/src/components/timeline/useStickyError.test.tsx +47 -0
  281. package/src/components/timeline/useStickyError.ts +15 -0
  282. package/src/components/traffic/TrafficConnectionsTable.tsx +144 -0
  283. package/src/components/traffic/TrafficFilterSidebar.tsx +22 -1
  284. package/src/components/traffic/TrafficFlowListContext.tsx +12 -4
  285. package/src/components/traffic/TrafficFocusSearch.tsx +172 -0
  286. package/src/components/traffic/TrafficGraph.tsx +109 -26
  287. package/src/components/traffic/TrafficGraphTooLarge.tsx +150 -0
  288. package/src/components/traffic/TrafficView.tsx +582 -149
  289. package/src/components/traffic/trafficFilters.test.ts +398 -1
  290. package/src/components/traffic/trafficFilters.ts +408 -0
  291. package/src/components/ui/DiagnosticsOverlay.tsx +34 -2
  292. package/src/components/ui/Markdown.test.tsx +65 -0
  293. package/src/components/ui/Markdown.tsx +144 -127
  294. package/src/components/whats-new/releaseNotes.ts +63 -1
  295. package/src/components/workload/WorkloadView.test.ts +11 -1
  296. package/src/components/workload/WorkloadView.tsx +162 -529
  297. package/src/components/workspace/layout.test.tsx +29 -0
  298. package/src/components/workspace/layout.tsx +259 -0
  299. package/src/components/workspace/table.ts +7 -0
  300. package/src/context/TimelineCustomization.tsx +15 -0
  301. package/src/contexts/CapabilitiesContext.tsx +1 -1
  302. package/src/hooks/useGitOpsWriteGuard.test.tsx +457 -0
  303. package/src/hooks/useGitOpsWriteGuard.ts +193 -0
  304. package/src/hooks/useResolvedGitOpsOwner.ts +256 -0
  305. package/src/integrations/resourceHost.test.tsx +55 -0
  306. package/src/integrations/resourceHost.ts +99 -0
  307. package/src/integrations/resourceHosts.test.tsx +141 -0
  308. package/src/integrations/resourceHosts.tsx +119 -0
  309. package/src/integrations/workspaceRoute.ts +20 -0
  310. package/src/integrations/workspaceRoutes.test.ts +40 -0
  311. package/src/integrations/workspaceRoutes.ts +22 -0
  312. package/src/integrations/workspaceScreens.tsx +14 -0
  313. package/src/utils/drawer-trail.ts +35 -0
  314. package/src/utils/kindFilterStore.test.ts +147 -0
  315. package/src/utils/kindFilterStore.ts +91 -0
  316. package/src/utils/navigation.test.ts +13 -1
  317. package/src/utils/navigation.ts +39 -2
  318. package/src/utils/page-links.ts +21 -0
  319. package/src/utils/tabStorage.ts +40 -0
  320. package/src/utils/viewMemory.test.ts +78 -0
  321. package/src/utils/viewMemory.ts +54 -0
@@ -0,0 +1,29 @@
1
+ // @vitest-environment jsdom
2
+ import { act } from 'react'
3
+ import { createRoot } from 'react-dom/client'
4
+ import { expect, it, vi } from 'vitest'
5
+ import { Mono } from './layout'
6
+
7
+ ;(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true
8
+
9
+ it('uses normal word boundaries with overflow wrapping and keeps the complete value in a hover tooltip', () => {
10
+ vi.useFakeTimers()
11
+ const host = document.createElement('div')
12
+ document.body.append(host)
13
+ const root = createRoot(host)
14
+ const image = 'ghcr.io/cloudnative-pg/postgresql:17.4-standard-bookworm'
15
+ act(() => root.render(<Mono>{image}</Mono>))
16
+ const mono = host.querySelector('.font-mono')!
17
+ expect(mono.classList.contains('break-all')).toBe(false)
18
+ expect(mono.classList.contains('break-normal')).toBe(true)
19
+ expect(mono.textContent).toBe(image)
20
+ expect(mono.classList.contains('[overflow-wrap:anywhere]')).toBe(true)
21
+ const trigger = mono.parentElement!
22
+ act(() => trigger.dispatchEvent(new MouseEvent('mouseenter', { bubbles: true })))
23
+ act(() => trigger.dispatchEvent(new MouseEvent('mouseover', { bubbles: true })))
24
+ act(() => vi.advanceTimersByTime(500))
25
+ expect(document.body.querySelector('.fixed')?.textContent).toBe(image)
26
+ act(() => root.unmount())
27
+ host.remove()
28
+ vi.useRealTimers()
29
+ })
@@ -0,0 +1,259 @@
1
+ import type { ComponentType, ReactNode } from 'react'
2
+ import type { UseQueryResult } from '@tanstack/react-query'
3
+ import { clsx } from 'clsx'
4
+ import { AlertTriangle, X } from 'lucide-react'
5
+ import { Tooltip, formatUpdatedAgo, grantParts, toneTextClass, type Grant } from '@skyhook-io/k8s-ui'
6
+ import type { SelectedResource } from '../../types'
7
+ import { sameSelectedResource } from '../../utils/drawer-trail'
8
+ import { ROW_HOVER, TABLE_HEAD, TABLE_WRAP, TBODY, TD, TH } from './table'
9
+
10
+ export function Notice({ children }: { children: ReactNode }) {
11
+ return (
12
+ <div className="flex items-start gap-2 rounded-lg border border-theme-border bg-theme-surface px-3 py-2 text-sm text-theme-text-secondary">
13
+ <AlertTriangle className="mt-0.5 h-4 w-4 shrink-0 text-theme-text-tertiary" />
14
+ <div>{children}</div>
15
+ </div>
16
+ );
17
+ }
18
+
19
+ /** A whole screen with nothing to show: not installed, unreadable, or empty. */
20
+ export function ScreenEmptyState({
21
+ icon: Icon,
22
+ title,
23
+ detail,
24
+ action,
25
+ }: {
26
+ icon: ComponentType<{ className?: string }>;
27
+ title: string;
28
+ detail: ReactNode;
29
+ action?: ReactNode;
30
+ }) {
31
+ return (
32
+ <div className="flex min-h-0 flex-1 items-center justify-center bg-theme-base p-6">
33
+ <div className="max-w-md text-center">
34
+ <Icon className="mx-auto h-9 w-9 text-theme-text-tertiary/50" />
35
+ <h2 className="mt-3 text-lg font-medium text-theme-text-primary">
36
+ {title}
37
+ </h2>
38
+ <p className="mt-1 text-sm text-theme-text-secondary">{detail}</p>
39
+ {action}
40
+ </div>
41
+ </div>
42
+ );
43
+ }
44
+
45
+ export function ScreenBody({ children }: { children: ReactNode }) {
46
+ return (
47
+ <div className="min-h-0 flex-1 overflow-y-auto [scrollbar-gutter:stable]">
48
+ <div className="space-y-5 px-5 pb-6 pt-3 xl:px-7">{children}</div>
49
+ </div>
50
+ )
51
+ }
52
+
53
+ export function FilterChips({ chips }: { chips: { label: string; onClear: () => void }[] }) {
54
+ if (chips.length === 0) return null
55
+ return (
56
+ <div className="flex flex-wrap gap-1.5">
57
+ {chips.map((c) => (
58
+ <span key={c.label} className="inline-flex items-center gap-1 rounded-full bg-theme-elevated px-2.5 py-0.5 text-xs text-theme-text-secondary">
59
+ {c.label}
60
+ <button type="button" onClick={c.onClear} aria-label={`Remove ${c.label}`} className="rounded-full p-0.5 hover:bg-theme-hover">
61
+ <X className="h-3 w-3" />
62
+ </button>
63
+ </span>
64
+ ))}
65
+ </div>
66
+ )
67
+ }
68
+
69
+ export function namespaceChip(namespaces: string[], onClear: () => void) {
70
+ return namespaces.length > 0 ? [{ label: `Namespace: ${namespaces.join(', ')}`, onClear }] : []
71
+ }
72
+
73
+ export function Segments<T extends string>({
74
+ value,
75
+ options,
76
+ onChange,
77
+ label,
78
+ }: {
79
+ value: T
80
+ options: { id: T; label: string; count?: number | string }[]
81
+ onChange: (id: T) => void
82
+ label: string
83
+ }) {
84
+ return (
85
+ <div role="tablist" aria-label={label} className="inline-flex rounded-lg bg-theme-elevated p-0.5">
86
+ {options.map((o) => {
87
+ const on = o.id === value
88
+ return (
89
+ <button
90
+ key={o.id}
91
+ type="button"
92
+ role="tab"
93
+ aria-selected={on}
94
+ onClick={() => onChange(o.id)}
95
+ className={clsx(
96
+ 'inline-flex items-center gap-1.5 rounded-md px-3 py-1 text-sm font-medium transition-colors',
97
+ on ? 'bg-theme-surface text-theme-text-primary shadow-theme-sm' : 'text-theme-text-secondary hover:text-theme-text-primary',
98
+ )}
99
+ >
100
+ {o.label}
101
+ {o.count !== undefined && <span className="font-mono text-xs text-theme-text-tertiary">{o.count}</span>}
102
+ </button>
103
+ )
104
+ })}
105
+ </div>
106
+ )
107
+ }
108
+
109
+ export interface TableColumn<T> {
110
+ header: ReactNode
111
+ width?: string
112
+ cell: (row: T) => ReactNode
113
+ className?: string
114
+ }
115
+
116
+ /** A workspace table. Rows inspect in the drawer; the inspected row is highlighted. */
117
+ export function SectionTable<T>({
118
+ title,
119
+ subtitle,
120
+ columns,
121
+ rows,
122
+ rowKey,
123
+ rowResource,
124
+ onInspect,
125
+ inspected,
126
+ empty,
127
+ minWidth = 760,
128
+ footer,
129
+ }: {
130
+ title: ReactNode
131
+ subtitle?: ReactNode
132
+ columns: TableColumn<T>[]
133
+ rows: T[]
134
+ rowKey: (row: T) => string
135
+ rowResource?: (row: T) => SelectedResource | null
136
+ onInspect?: (resource: SelectedResource) => void
137
+ inspected?: SelectedResource | null
138
+ empty: ReactNode
139
+ minWidth?: number
140
+ footer?: ReactNode
141
+ }) {
142
+ return (
143
+ <section>
144
+ <div className="mb-2 flex flex-wrap items-baseline gap-x-2.5 gap-y-1">
145
+ <h2 className="text-sm font-semibold text-theme-text-primary">{title}</h2>
146
+ {subtitle && <span className="text-xs text-theme-text-tertiary">{subtitle}</span>}
147
+ </div>
148
+ <div className="overflow-hidden rounded-xl border border-theme-border bg-theme-surface shadow-theme-sm">
149
+ {rows.length === 0 ? (
150
+ <div className="px-4 py-5 text-sm text-theme-text-tertiary">{empty}</div>
151
+ ) : (
152
+ <div className={TABLE_WRAP}>
153
+ <table className="w-full table-fixed" style={{ minWidth }}>
154
+ <colgroup>
155
+ {columns.map((c, i) => (
156
+ <col key={i} style={c.width ? { width: c.width } : undefined} />
157
+ ))}
158
+ </colgroup>
159
+ <thead className={TABLE_HEAD}>
160
+ <tr>
161
+ {columns.map((c, i) => (
162
+ <th key={i} className={TH}>{c.header}</th>
163
+ ))}
164
+ </tr>
165
+ </thead>
166
+ <tbody className={TBODY}>
167
+ {rows.map((row) => {
168
+ const res = rowResource?.(row) ?? null
169
+ const active = !!res && sameSelectedResource(inspected, res)
170
+ return (
171
+ <tr
172
+ key={rowKey(row)}
173
+ onClick={res && onInspect ? () => onInspect(res) : undefined}
174
+ className={clsx(res && onInspect && 'cursor-pointer', ROW_HOVER, active && 'selection')}
175
+ aria-selected={res ? active : undefined}
176
+ >
177
+ {columns.map((c, i) => (
178
+ <td key={i} className={clsx(TD, c.className)}>{c.cell(row)}</td>
179
+ ))}
180
+ </tr>
181
+ )
182
+ })}
183
+ </tbody>
184
+ </table>
185
+ </div>
186
+ )}
187
+ </div>
188
+ {footer && <div className="mt-1.5 text-xs text-theme-text-tertiary">{footer}</div>}
189
+ </section>
190
+ )
191
+ }
192
+
193
+ /**
194
+ * Text that wraps only after `after` ("/" for a path, "-" for a resource
195
+ * name), never mid-word; a single segment too long for its cell is cut with an
196
+ * ellipsis. The whole value shows on hover.
197
+ */
198
+ export function BreakText({ value, after, className }: { value: string; after: '/' | '-'; className?: string }) {
199
+ const parts = value.split(after === '/' ? /(?<=\/)/ : /(?<=-)/)
200
+ return (
201
+ <Tooltip content={value} wrapperClassName="max-w-full">
202
+ <span className={clsx('block max-w-full', className)}>
203
+ {parts.map((p, i) => (
204
+ <span key={i} className="inline-block max-w-full truncate align-top">
205
+ {p}
206
+ </span>
207
+ ))}
208
+ </span>
209
+ </Tooltip>
210
+ )
211
+ }
212
+
213
+ /** A URL or path; see BreakText. */
214
+ export function PathText({ value, className }: { value: string; className?: string }) {
215
+ return <BreakText value={value} after="/" className={clsx('font-mono text-[12.5px]', className)} />
216
+ }
217
+
218
+ /**
219
+ * A grant as one unit: the verb and resource in a code span that does not
220
+ * wrap, its scope ("cluster-wide", "in namespace pg") as plain text after it.
221
+ */
222
+ export function GrantText({ grant }: { grant: Grant }) {
223
+ const { what, scope } = grantParts(grant)
224
+ return (
225
+ <>
226
+ <code className="whitespace-nowrap rounded bg-theme-elevated px-1 font-mono text-[12px]">{what}</code>
227
+ {scope}
228
+ </>
229
+ )
230
+ }
231
+
232
+ export function Mono({ children }: { children: ReactNode }) {
233
+ return <Tooltip content={children} wrapperClassName="min-w-0"><span className="font-mono text-[12.5px] break-normal [overflow-wrap:anywhere]">{children}</span></Tooltip>
234
+ }
235
+
236
+ export function Sub({ children }: { children: ReactNode }) {
237
+ return <div className="mt-0.5 text-xs text-theme-text-tertiary break-words">{children}</div>
238
+ }
239
+
240
+ type RefreshableQuery = Pick<UseQueryResult<unknown>, 'isRefetchError' | 'error' | 'dataUpdatedAt'>
241
+
242
+ /**
243
+ * A refetch failed while the last good answer stays on screen: say so, why,
244
+ * and how old that answer is, so cached values are not read as current.
245
+ */
246
+ export function RefreshFailedNotice({ queries, className }: { queries: RefreshableQuery[]; className?: string }) {
247
+ const failed = queries.filter((q) => q.isRefetchError)
248
+ if (failed.length === 0) return null
249
+ const oldest = failed.reduce((a, b) => (b.dataUpdatedAt < a.dataUpdatedAt ? b : a))
250
+ const reason = oldest.error instanceof Error ? oldest.error.message : 'unknown error'
251
+ return (
252
+ <div role="status" className={clsx('flex items-start gap-1.5 text-xs text-theme-text-secondary', className)}>
253
+ <AlertTriangle className={clsx('mt-px h-3.5 w-3.5 shrink-0', toneTextClass('degraded'))} />
254
+ <span>
255
+ Last refresh failed: {reason.length > 160 ? `${reason.slice(0, 160)}…` : reason} · showing data from {formatUpdatedAgo(Date.now() - oldest.dataUpdatedAt)}
256
+ </span>
257
+ </div>
258
+ )
259
+ }
@@ -0,0 +1,7 @@
1
+ export const TABLE_WRAP = 'overflow-x-auto';
2
+ export const TABLE_HEAD =
3
+ 'border-b border-theme-border bg-theme-base/60 text-[11px] uppercase tracking-wide text-theme-text-tertiary';
4
+ export const TH = 'px-3 py-2.5 text-left font-medium whitespace-nowrap';
5
+ export const TD = 'px-3 py-2.5 align-top text-sm text-theme-text-primary';
6
+ export const TBODY = 'table-divide-subtle';
7
+ export const ROW_HOVER = 'transition-colors hover:bg-theme-hover/50';
@@ -0,0 +1,15 @@
1
+ import { createContext, useContext } from 'react'
2
+ import type { ReactNode } from 'react'
3
+
4
+ /**
5
+ * A note from the host about how far back the timeline goes. The host owns
6
+ * the wording and decides when there is one. Standalone Radar serves
7
+ * everything it holds and never sets it.
8
+ */
9
+ const TimelineRetentionNoteContext = createContext<ReactNode>(undefined)
10
+
11
+ export const TimelineRetentionNoteProvider = TimelineRetentionNoteContext.Provider
12
+
13
+ export function useTimelineRetentionNote(): ReactNode {
14
+ return useContext(TimelineRetentionNoteContext)
15
+ }
@@ -155,7 +155,7 @@ export function useNamespacedCapabilities(namespace: string | undefined) {
155
155
  const { data: nsCaps, error, isPending } = useNamespaceCapabilities(namespace, globalCaps)
156
156
 
157
157
  if (error) {
158
- console.warn(`Failed to fetch namespace capabilities for ${namespace}, using global:`, error)
158
+ console.warn('Failed to fetch namespace capabilities, using global:', namespace, error)
159
159
  }
160
160
 
161
161
  return useMemo(() => ({