@skyhook-io/radar-app 1.8.6 → 1.8.8

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 (109) hide show
  1. package/README.md +7 -1
  2. package/package.json +12 -10
  3. package/src/App.tsx +278 -230
  4. package/src/RadarApp.tsx +91 -20
  5. package/src/api/client.argoResourceSync.test.ts +69 -0
  6. package/src/api/client.delta.test.ts +89 -0
  7. package/src/api/client.deltaSync.test.ts +216 -0
  8. package/src/api/client.rightsizing.test.ts +32 -0
  9. package/src/api/client.ts +1462 -246
  10. package/src/api/diagnose.ts +288 -0
  11. package/src/api/timelineSource.test.ts +217 -0
  12. package/src/api/timelineSource.ts +582 -0
  13. package/src/components/ConnectionErrorView.tsx +174 -70
  14. package/src/components/ContextSwitcher.tsx +13 -5
  15. package/src/components/applications/ApplicationsView.tsx +775 -80
  16. package/src/components/audit/AuditSettingsDialog.tsx +2 -2
  17. package/src/components/cost/ApplicationCostTab.test.ts +204 -0
  18. package/src/components/cost/ApplicationCostTab.tsx +571 -0
  19. package/src/components/cost/CostTrendChart.tsx +103 -72
  20. package/src/components/cost/CostView.test.ts +12 -0
  21. package/src/components/cost/CostView.tsx +494 -229
  22. package/src/components/cost/CostViewTabs.test.tsx +21 -0
  23. package/src/components/cost/CostViewTabs.tsx +40 -0
  24. package/src/components/cost/CurrentAllocationUse.test.ts +21 -0
  25. package/src/components/cost/CurrentAllocationUse.tsx +126 -0
  26. package/src/components/cost/WorkloadCostTab.test.ts +153 -0
  27. package/src/components/cost/WorkloadCostTab.tsx +372 -0
  28. package/src/components/cost/cloud-console.test.ts +39 -0
  29. package/src/components/cost/cloud-console.ts +81 -0
  30. package/src/components/cost/errors.ts +8 -0
  31. package/src/components/cost/format.test.ts +27 -0
  32. package/src/components/cost/format.ts +46 -0
  33. package/src/components/cost/kinds.ts +5 -0
  34. package/src/components/curl/ServiceCurlButton.tsx +2 -2
  35. package/src/components/diagnose/AISettings.tsx +116 -0
  36. package/src/components/diagnose/DiagnoseContext.tsx +491 -0
  37. package/src/components/diagnose/DiagnoseSurface.tsx +385 -0
  38. package/src/components/diagnose/Home.tsx +163 -0
  39. package/src/components/diagnose/InvestigationView.tsx +604 -0
  40. package/src/components/diagnose/LocalDiagnoseAction.tsx +150 -0
  41. package/src/components/diagnose/launch.ts +65 -0
  42. package/src/components/diagnose/parts.tsx +1689 -0
  43. package/src/components/dock/BottomDock.tsx +2 -3
  44. package/src/components/dock/WorkloadLogsTab.tsx +21 -5
  45. package/src/components/execution/BatchExecutionView.test.ts +170 -0
  46. package/src/components/execution/BatchExecutionView.tsx +1329 -0
  47. package/src/components/execution/batch-run-actions.test.ts +48 -0
  48. package/src/components/execution/batch-run-actions.ts +24 -0
  49. package/src/components/execution/batch-timeline.test.ts +57 -0
  50. package/src/components/execution/batch-timeline.ts +46 -0
  51. package/src/components/execution/execution-definition.test.ts +208 -0
  52. package/src/components/execution/execution-definition.ts +245 -0
  53. package/src/components/gitops/ArgoResourceDiffLoader.tsx +23 -0
  54. package/src/components/gitops/GitOpsView.tsx +81 -14
  55. package/src/components/gitops/RevisionMetaChip.tsx +63 -0
  56. package/src/components/helm/ChartBrowser.tsx +2 -3
  57. package/src/components/helm/HelmCompareRoute.tsx +1 -2
  58. package/src/components/helm/HelmReleaseDrawer.test.ts +17 -0
  59. package/src/components/helm/HelmReleaseDrawer.tsx +376 -43
  60. package/src/components/helm/HelmView.tsx +2 -3
  61. package/src/components/helm/InstallWizard.tsx +3 -5
  62. package/src/components/helm/ManifestDiffViewer.tsx +1 -31
  63. package/src/components/helm/TrackChartSourceDialog.tsx +48 -4
  64. package/src/components/helm/ValuesDiffPreview.tsx +17 -7
  65. package/src/components/home/CostCard.tsx +21 -36
  66. package/src/components/home/HomeView.tsx +17 -15
  67. package/src/components/home/MCPSetupDialog.tsx +1 -1
  68. package/src/components/home/mcpToolCatalog.ts +2 -2
  69. package/src/components/issues/IssuesPane.tsx +9 -1
  70. package/src/components/logs/ScheduledWorkloadLogsViewer.tsx +135 -0
  71. package/src/components/portforward/PortForwardButton.tsx +2 -2
  72. package/src/components/portforward/PortForwardManager.tsx +19 -14
  73. package/src/components/resource/PrometheusChartsGrid.tsx +6 -80
  74. package/src/components/resource/RightsizingStrip.test.ts +109 -0
  75. package/src/components/resource/RightsizingStrip.tsx +320 -127
  76. package/src/components/resources/ImageFilesystemModal.tsx +2 -2
  77. package/src/components/resources/PodFilesystemModal.tsx +2 -3
  78. package/src/components/resources/ResourceDetailDrawer.tsx +2 -0
  79. package/src/components/resources/ResourcesView.tsx +13 -4
  80. package/src/components/resources/renderers/CronWorkflowRenderer.tsx +1 -0
  81. package/src/components/resources/renderers/index.ts +1 -0
  82. package/src/components/rightsizing/RightsizingScanView.tsx +938 -0
  83. package/src/components/rightsizing/copy.test.ts +56 -0
  84. package/src/components/rightsizing/model.test.ts +227 -0
  85. package/src/components/rightsizing/model.ts +158 -0
  86. package/src/components/rightsizing/presentation.test.ts +104 -0
  87. package/src/components/rightsizing/presentation.ts +94 -0
  88. package/src/components/settings/MyPermissionsDialog.tsx +66 -116
  89. package/src/components/settings/SettingsDialog.tsx +1290 -283
  90. package/src/components/shared/LargeClusterNamespacePicker.tsx +2 -2
  91. package/src/components/timeline/LocalTimelineScrubber.tsx +212 -0
  92. package/src/components/timeline/RetainedTimelineScrubber.tsx +311 -0
  93. package/src/components/timeline/TimelineList.tsx +81 -13
  94. package/src/components/timeline/TimelineView.tsx +842 -26
  95. package/src/components/timeline/TimelineView.urlparams.test.ts +335 -0
  96. package/src/components/traffic/TrafficFilterSidebar.tsx +2 -2
  97. package/src/components/traffic/TrafficView.tsx +8 -4
  98. package/src/components/ui/CommandPalette.tsx +2 -2
  99. package/src/components/workload/WorkloadView.tsx +912 -245
  100. package/src/context/ConnectionContext.tsx +109 -11
  101. package/src/context/DiagnoseCustomization.tsx +42 -0
  102. package/src/context/TimelineSource.tsx +50 -0
  103. package/src/hooks/useClusterLoadState.ts +73 -0
  104. package/src/hooks/useEventSource.ts +6 -0
  105. package/src/index.css +162 -1
  106. package/src/index.ts +12 -0
  107. package/src/main.tsx +1 -1
  108. package/src/types/clusterLoadState.ts +33 -0
  109. package/src/utils/navigation.ts +10 -0
@@ -1,6 +1,5 @@
1
1
  import { useState, useEffect, useRef } from 'react'
2
- import { createPortal } from 'react-dom'
3
- import { Shield, X, Loader2, Lock, ExternalLink } from 'lucide-react'
2
+ import { Loader2, Lock, ExternalLink } from 'lucide-react'
4
3
  import { clsx } from 'clsx'
5
4
  import { useQuery } from '@tanstack/react-query'
6
5
  import {
@@ -11,137 +10,88 @@ import {
11
10
  rbacNonResourceUrlBadgeClass,
12
11
  type RBACWhoamiResponse,
13
12
  } from '@skyhook-io/k8s-ui'
14
- import { useAnimatedUnmount } from '../../hooks/useAnimatedUnmount'
15
- import { TRANSITION_BACKDROP, TRANSITION_PANEL } from '../../utils/animation'
16
13
  import { useNamespaces, useAuthMe, fetchJSON } from '../../api/client'
17
14
  import { useRBACWhoami } from '../../api/rbac'
18
15
 
19
- interface MyPermissionsDialogProps {
20
- open: boolean
21
- onClose: () => void
22
- }
23
-
24
- export function MyPermissionsDialog({ open, onClose }: MyPermissionsDialogProps) {
25
- const dialogRef = useRef<HTMLDivElement>(null)
26
- const { shouldRender, isOpen } = useAnimatedUnmount(open, 200)
16
+ // MyPermissionsContent renders the "your access on this cluster" viewer inline
17
+ // (no modal chrome) so the Settings dialog can host it directly in its content
18
+ // pane. `active` gates the apiserver queries so opening Settings on another
19
+ // section doesn't fire a SelfSubjectRulesReview the user never asked for.
20
+ export function MyPermissionsContent({ active }: { active: boolean }) {
27
21
  const [namespace, setNamespace] = useState('default')
28
-
29
22
  const { data: authMe } = useAuthMe()
30
23
  const { data: namespaces } = useNamespaces()
31
- const { data: whoami, isLoading, error } = useRBACWhoami(namespace, open)
24
+ const { data: whoami, isLoading, error } = useRBACWhoami(namespace, active)
32
25
 
33
- // ESC + focus
26
+ // Land on an accessible namespace: once the list loads, if the current pick
27
+ // ('default' initially) isn't among the user's namespaces and they haven't
28
+ // chosen one, switch to the first accessible one — otherwise a user scoped to
29
+ // e.g. team-a would open on an empty 'default'.
30
+ const userPicked = useRef(false)
34
31
  useEffect(() => {
35
- if (!open) return
36
- const handler = (e: KeyboardEvent) => {
37
- if (e.key === 'Escape') {
38
- e.stopPropagation()
39
- onClose()
40
- }
32
+ if (userPicked.current || !namespaces?.length) return
33
+ if (!namespaces.some((ns) => ns.name === namespace)) {
34
+ setNamespace(namespaces[0].name)
41
35
  }
42
- document.addEventListener('keydown', handler, true)
43
- return () => document.removeEventListener('keydown', handler, true)
44
- }, [open, onClose])
45
- useEffect(() => {
46
- if (open && dialogRef.current) dialogRef.current.focus()
47
- }, [open])
36
+ }, [namespaces, namespace])
48
37
 
49
- if (!shouldRender) return null
50
-
51
- return createPortal(
52
- <div className="fixed inset-0 z-50 flex items-center justify-center">
53
- <div
54
- className={clsx(
55
- 'absolute inset-0 bg-black/60 backdrop-blur-sm',
56
- TRANSITION_BACKDROP,
57
- isOpen ? 'opacity-100' : 'opacity-0',
58
- )}
59
- onClick={onClose}
60
- />
61
- <div
62
- ref={dialogRef}
63
- tabIndex={-1}
64
- className={clsx(
65
- 'relative bg-theme-surface border border-theme-border shadow-theme-lg w-full outline-none flex flex-col',
66
- 'max-sm:inset-0 max-sm:absolute max-sm:rounded-none max-sm:max-h-full max-sm:border-0',
67
- 'sm:rounded-xl sm:max-w-3xl sm:mx-4 sm:max-h-[85vh]',
68
- TRANSITION_PANEL,
69
- isOpen ? 'opacity-100 scale-100' : 'opacity-0 scale-95',
70
- )}
71
- >
72
- {/* Header */}
73
- <div className="flex items-center justify-between p-4 border-b border-theme-border shrink-0">
74
- <div className="flex items-center gap-2">
75
- <Shield className="w-5 h-5 text-theme-text-secondary" />
76
- <h2 className="text-lg font-semibold text-theme-text-primary">Your access on this cluster</h2>
38
+ return (
39
+ <div className="space-y-4">
40
+ {/* Identity + namespace selector */}
41
+ <div className="flex flex-wrap items-end gap-3">
42
+ <div className="flex-1 min-w-[180px]">
43
+ <label className="block text-xs text-theme-text-tertiary mb-1">Identity</label>
44
+ <div className="px-2 py-1.5 text-sm bg-theme-elevated rounded border border-theme-border text-theme-text-primary truncate">
45
+ {authMe?.username || '(current kubeconfig user)'}
77
46
  </div>
78
- <button
79
- onClick={onClose}
80
- className="p-1 text-theme-text-secondary hover:text-theme-text-primary hover:bg-theme-elevated rounded"
47
+ </div>
48
+ <div className="flex-1 min-w-[180px]">
49
+ <label className="block text-xs text-theme-text-tertiary mb-1">Namespace</label>
50
+ <select
51
+ value={namespace}
52
+ onChange={(e) => { userPicked.current = true; setNamespace(e.target.value) }}
53
+ className="w-full px-2 py-1.5 text-sm bg-theme-elevated border border-theme-border rounded text-theme-text-primary focus:outline-none focus:border-blue-500"
81
54
  >
82
- <X className="w-5 h-5" />
83
- </button>
55
+ {namespaces?.map((ns) => (
56
+ <option key={ns.name} value={ns.name}>{ns.name}</option>
57
+ ))}
58
+ {!namespaces?.some((ns) => ns.name === namespace) && (
59
+ <option value={namespace}>{namespace}</option>
60
+ )}
61
+ </select>
84
62
  </div>
63
+ </div>
85
64
 
86
- <div className="overflow-y-auto p-4 flex-1 space-y-4">
87
- {/* Identity + namespace selector */}
88
- <div className="flex flex-wrap items-end gap-3">
89
- <div className="flex-1 min-w-[180px]">
90
- <label className="block text-xs text-theme-text-tertiary mb-1">Identity</label>
91
- <div className="px-2 py-1.5 text-sm bg-theme-elevated rounded border border-theme-border text-theme-text-primary truncate">
92
- {authMe?.username || '(current kubeconfig user)'}
93
- </div>
94
- </div>
95
- <div className="flex-1 min-w-[180px]">
96
- <label className="block text-xs text-theme-text-tertiary mb-1">Namespace</label>
97
- <select
98
- value={namespace}
99
- onChange={(e) => setNamespace(e.target.value)}
100
- className="w-full px-2 py-1.5 text-sm bg-theme-elevated border border-theme-border rounded text-theme-text-primary focus:outline-none focus:border-blue-500"
101
- >
102
- {namespaces?.map((ns) => (
103
- <option key={ns.name} value={ns.name}>{ns.name}</option>
104
- ))}
105
- {!namespaces?.some((ns) => ns.name === namespace) && (
106
- <option value={namespace}>{namespace}</option>
107
- )}
108
- </select>
109
- </div>
110
- </div>
111
-
112
- <p className="text-xs text-theme-text-tertiary">
113
- Computed by the Kubernetes API via{' '}
114
- <code className="inline-code">SelfSubjectRulesReview</code>.
115
- Shows what you can do in <span className="text-theme-text-secondary">{namespace}</span>,
116
- plus any cluster-scoped rules that apply everywhere.
117
- </p>
118
-
119
- {whoami?.incomplete && (
120
- <div className="px-2.5 py-1.5 text-xs bg-amber-500/10 border border-amber-500/20 rounded">
121
- The apiserver returned an incomplete rule set
122
- {whoami.evaluationError ? ` (${whoami.evaluationError})` : ''}.
123
- The list below may understate your actual permissions.
124
- </div>
125
- )}
65
+ <p className="text-xs text-theme-text-tertiary">
66
+ Computed by the Kubernetes API via{' '}
67
+ <code className="inline-code">SelfSubjectRulesReview</code>.
68
+ Shows what you can do in <span className="text-theme-text-secondary">{namespace}</span>,
69
+ plus any cluster-scoped rules that apply everywhere.
70
+ </p>
126
71
 
127
- {isLoading ? (
128
- <div className="flex items-center gap-2 text-sm text-theme-text-tertiary">
129
- <Loader2 className="w-3.5 h-3.5 animate-spin" />
130
- Querying apiserver…
131
- </div>
132
- ) : error ? (
133
- <div className="text-sm text-red-400">
134
- Failed to load: {(error as Error).message}
135
- </div>
136
- ) : whoami ? (
137
- <PermissionsTable whoami={whoami} />
138
- ) : null}
72
+ {whoami?.incomplete && (
73
+ <div className="px-2.5 py-1.5 text-xs bg-amber-500/10 border border-amber-500/20 rounded">
74
+ The apiserver returned an incomplete rule set
75
+ {whoami.evaluationError ? ` (${whoami.evaluationError})` : ''}.
76
+ The list below may understate your actual permissions.
77
+ </div>
78
+ )}
139
79
 
140
- <RestrictedResources enabled={open} />
80
+ {isLoading ? (
81
+ <div className="flex items-center gap-2 text-sm text-theme-text-tertiary">
82
+ <Loader2 className="w-3.5 h-3.5 animate-spin" />
83
+ Querying apiserver…
141
84
  </div>
142
- </div>
143
- </div>,
144
- document.body,
85
+ ) : error ? (
86
+ <div className="text-sm text-red-400">
87
+ Failed to load: {(error as Error).message}
88
+ </div>
89
+ ) : whoami ? (
90
+ <PermissionsTable whoami={whoami} />
91
+ ) : null}
92
+
93
+ <RestrictedResources enabled={active} />
94
+ </div>
145
95
  )
146
96
  }
147
97