@skyhook-io/radar-app 1.14.4 → 1.14.6

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 (98) hide show
  1. package/README.md +2 -9
  2. package/package.json +6 -6
  3. package/src/App.tsx +91 -328
  4. package/src/RadarApp.tsx +3 -8
  5. package/src/api/client.jobset.test.ts +18 -0
  6. package/src/api/client.resource-identity.test.ts +44 -0
  7. package/src/api/client.rightsizing.test.ts +7 -3
  8. package/src/api/client.ts +233 -78
  9. package/src/api/preferences.test.ts +54 -0
  10. package/src/api/preferences.ts +38 -0
  11. package/src/components/CloudFunnelButton.test.tsx +155 -0
  12. package/src/components/CloudFunnelButton.tsx +14 -8
  13. package/src/components/ContextSwitcher.tsx +13 -132
  14. package/src/components/applications/ApplicationsView.identity.test.ts +39 -0
  15. package/src/components/applications/ApplicationsView.tsx +5 -3
  16. package/src/components/audit/AuditSettingsDialog.tsx +15 -11
  17. package/src/components/diagnose/AIMarkdown.tsx +8 -3
  18. package/src/components/diagnose/AgentControls.tsx +26 -3
  19. package/src/components/diagnose/AssessmentCard.tsx +249 -237
  20. package/src/components/diagnose/DiagnoseContext.tsx +1 -0
  21. package/src/components/diagnose/InvestigationEvidencePane.test.tsx +47 -0
  22. package/src/components/diagnose/InvestigationEvidencePane.tsx +15 -2
  23. package/src/components/diagnose/InvestigationEvidenceSections.tsx +1 -1
  24. package/src/components/diagnose/InvestigationView.tsx +4 -1
  25. package/src/components/diagnose/agentCatalog.ts +6 -0
  26. package/src/components/diagnose/investigationCase.test.tsx +56 -0
  27. package/src/components/diagnose/investigationEvidence/adapters/logs.test.ts +60 -0
  28. package/src/components/diagnose/investigationEvidence/adapters/logs.ts +7 -3
  29. package/src/components/diagnose/investigationEvidence/bodies/streams.tsx +1 -1
  30. package/src/components/diagnose/investigationEvidence/observations.ts +8 -5
  31. package/src/components/diagnose/investigationEvidence/types.ts +1 -1
  32. package/src/components/diagnose/launch.test.ts +23 -0
  33. package/src/components/diagnose/launch.ts +4 -0
  34. package/src/components/diagnose/parts.test.tsx +159 -2
  35. package/src/components/dock/NodeTerminalTab.test.tsx +34 -0
  36. package/src/components/dock/NodeTerminalTab.tsx +5 -3
  37. package/src/components/dock/WorkloadLogsTab.test.tsx +66 -0
  38. package/src/components/dock/WorkloadLogsTab.tsx +2 -1
  39. package/src/components/execution/BatchExecutionView.render.test.tsx +2 -1
  40. package/src/components/execution/BatchExecutionView.test.ts +52 -1
  41. package/src/components/execution/BatchExecutionView.tsx +324 -65
  42. package/src/components/execution/JobSetAdmission.test.tsx +42 -0
  43. package/src/components/execution/JobSetAdmission.tsx +17 -0
  44. package/src/components/execution/JobSetMemberComparison.test.tsx +30 -0
  45. package/src/components/execution/JobSetMemberComparison.tsx +88 -0
  46. package/src/components/execution/batch-run-actions.test.ts +1 -0
  47. package/src/components/execution/batch-timeline.test.ts +3 -0
  48. package/src/components/execution/execution-definition.test.ts +15 -1
  49. package/src/components/execution/execution-definition.ts +4 -0
  50. package/src/components/execution/member-collection.render.test.tsx +142 -0
  51. package/src/components/gitops/GitOpsView.tsx +33 -2
  52. package/src/components/gitops/destination-toast.test.ts +33 -0
  53. package/src/components/gitops/destination-toast.ts +46 -0
  54. package/src/components/gitops/useDestinationCluster.ts +30 -0
  55. package/src/components/helm/TrackChartSourceDialog.tsx +20 -11
  56. package/src/components/home/MCPSetupDialog.tsx +10 -0
  57. package/src/components/logs/ScheduledWorkloadLogsViewer.tsx +75 -22
  58. package/src/components/logs/WorkloadLogsViewer.tsx +7 -4
  59. package/src/components/nav/PrimaryNavRail.tsx +2 -5
  60. package/src/components/resource/PVCUsageBar.render.test.tsx +168 -0
  61. package/src/components/resource/PVCUsageBar.tsx +62 -16
  62. package/src/components/resource/PrometheusChartsGrid.render.test.tsx +1 -1
  63. package/src/components/resources/ResourcesView.tsx +3 -3
  64. package/src/components/resources/renderers/CAPIClusterRenderer.tsx +8 -0
  65. package/src/components/resources/renderers/KueueWorkloadRenderer.test.tsx +39 -0
  66. package/src/components/resources/renderers/KueueWorkloadRenderer.tsx +37 -0
  67. package/src/components/resources/renderers/RayClusterRenderer.test.tsx +18 -0
  68. package/src/components/resources/renderers/RayClusterRenderer.tsx +17 -0
  69. package/src/components/resources/renderers/RayServiceRenderer.test.tsx +69 -0
  70. package/src/components/resources/renderers/RayServiceRenderer.tsx +38 -0
  71. package/src/components/rightsizing/RightsizingScanView.tsx +207 -83
  72. package/src/components/rightsizing/copy.test.ts +7 -0
  73. package/src/components/rightsizing/empty-state.test.tsx +67 -0
  74. package/src/components/rightsizing/model.test.ts +120 -2
  75. package/src/components/rightsizing/model.ts +36 -13
  76. package/src/components/rightsizing/notices.test.tsx +176 -0
  77. package/src/components/settings/OperatorManagedNotice.tsx +17 -0
  78. package/src/components/settings/SettingsDialog.tsx +157 -42
  79. package/src/components/settings/settings-state.test.ts +18 -0
  80. package/src/components/settings/settings-state.ts +18 -0
  81. package/src/components/timeline/TimelineView.tsx +9 -3
  82. package/src/components/timeline/TimelineView.urlparams.test.ts +18 -1
  83. package/src/components/ui/Markdown.tsx +33 -6
  84. package/src/components/ui/command-items.ts +0 -3
  85. package/src/components/useContextSwitchFlow.tsx +147 -0
  86. package/src/components/workload/WorkloadView.test.ts +17 -1
  87. package/src/components/workload/WorkloadView.tsx +36 -18
  88. package/src/context/ContextSwitchContext.tsx +18 -2
  89. package/src/context/NavCustomization.tsx +4 -58
  90. package/src/context/ThemeContext.tsx +3 -11
  91. package/src/hooks/useClusterLoadState.ts +2 -2
  92. package/src/hooks/useFavorites.ts +3 -5
  93. package/src/utils/auditBadges.ts +1 -1
  94. package/src/utils/navigation.test.ts +18 -1
  95. package/src/utils/navigation.ts +12 -5
  96. package/src/utils/topology-selection.test.ts +56 -0
  97. package/src/utils/topology-selection.ts +9 -9
  98. package/src/components/ui/CommandPalette.tsx +0 -261
@@ -0,0 +1,66 @@
1
+ // @vitest-environment jsdom
2
+ import { act } from 'react'
3
+ import { createRoot, type Root } from 'react-dom/client'
4
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
5
+ import { WorkloadLogsTab } from './WorkloadLogsTab'
6
+
7
+ const state = vi.hoisted(() => ({ targets: [] as Array<{ kind: string; name: string; role?: string; snapshotOnly?: boolean }> }))
8
+ vi.mock('../../api/client', () => ({
9
+ useResource: (_kind: string, _namespace: string, name: string) => ({
10
+ data: { metadata: { uid: name }, spec: { replicatedJobs: [{ name: name === 'first' ? 'workers' : 'leader' }] } },
11
+ }),
12
+ useWorkloadRuns: (_kind: string, namespace: string, name: string) => ({
13
+ data: { collection: 'members', runs: [{ group: 'batch', kind: 'jobs', namespace, name: `${name}-0`, phase: 'Running', active: true }], total: 1 },
14
+ }),
15
+ }))
16
+ vi.mock('../logs/WorkloadLogsViewer', () => ({
17
+ WorkloadLogsViewer: (props: { kind: string; name: string; role?: string; snapshotOnly?: boolean }) => {
18
+ state.targets.push(props)
19
+ return <div data-log-target={`${props.kind}/${props.name}`} />
20
+ },
21
+ }))
22
+
23
+ Object.assign(globalThis, { IS_REACT_ACT_ENVIRONMENT: true })
24
+ let root: Root
25
+ let element: HTMLDivElement
26
+ beforeEach(() => {
27
+ state.targets = []
28
+ element = document.createElement('div')
29
+ document.body.appendChild(element)
30
+ root = createRoot(element)
31
+ })
32
+ afterEach(async () => {
33
+ await act(async () => root.unmount())
34
+ element.remove()
35
+ })
36
+ async function render(name: string, kind = 'JobSet') {
37
+ await act(async () => root.render(<WorkloadLogsTab namespace="training" workloadKind={kind} workloadName={name} />))
38
+ }
39
+
40
+ describe('dock JobSet logs', () => {
41
+ it.each(['JobSet', 'jobsets'])('routes %s to selected member logs and supports role snapshots', async kind => {
42
+ await render('first', kind)
43
+ expect(element.querySelector('[data-log-target="jobs/first-0"]')).not.toBeNull()
44
+ const scope = element.querySelector<HTMLSelectElement>('#jobset-log-scope')!
45
+ await act(async () => {
46
+ scope.value = 'role'
47
+ scope.dispatchEvent(new Event('change', { bubbles: true }))
48
+ })
49
+ expect(state.targets.at(-1)).toMatchObject({ kind: 'jobsets', name: 'first', role: 'workers', snapshotOnly: true })
50
+ })
51
+
52
+ it('resets aggregate scope and role before rendering another JobSet log source', async () => {
53
+ await render('first')
54
+ const scope = element.querySelector<HTMLSelectElement>('#jobset-log-scope')!
55
+ await act(async () => {
56
+ scope.value = 'role'
57
+ scope.dispatchEvent(new Event('change', { bubbles: true }))
58
+ })
59
+ state.targets = []
60
+ await render('second')
61
+ expect(element.querySelector<HTMLSelectElement>('#jobset-log-scope')?.value).toBe('selected')
62
+ expect(element.textContent).not.toContain('workers')
63
+ expect(state.targets.every(target => target.name === 'second-0' && target.role === undefined)).toBe(true)
64
+ expect(element.querySelector('[data-log-target="jobs/second-0"]')).not.toBeNull()
65
+ })
66
+ })
@@ -17,7 +17,8 @@ export function WorkloadLogsTab({
17
17
  normalizedKind === 'cronworkflow' || normalizedKind === 'cronworkflows' ||
18
18
  normalizedKind === 'workflowtemplate' || normalizedKind === 'workflowtemplates' ||
19
19
  normalizedKind === 'clusterworkflowtemplate' || normalizedKind === 'clusterworkflowtemplates' ||
20
- normalizedKind === 'scaledjob' || normalizedKind === 'scaledjobs'
20
+ normalizedKind === 'scaledjob' || normalizedKind === 'scaledjobs' ||
21
+ normalizedKind === 'jobset' || normalizedKind === 'jobsets'
21
22
 
22
23
  return (
23
24
  <div className="h-full">
@@ -5,7 +5,8 @@ import { BatchExecutionFullscreen } from './BatchExecutionView'
5
5
  vi.mock('../../api/client', () => ({
6
6
  useResource: () => ({}),
7
7
  useWorkloadPods: () => ({}),
8
- useWorkloadRuns: () => ({ data: { runs: [] } }),
8
+ useJobSetResources: () => ({ data: undefined, isLoading: false }),
9
+ useWorkloadRuns: () => ({ data: { collection: 'runs', runs: [], total: 0, truncated: false } }),
9
10
  }))
10
11
 
11
12
  describe.each(['Job', 'CronJob', 'ScaledJob'])('%s cleanup retention display', (kind) => {
@@ -1,6 +1,18 @@
1
1
  import { describe, expect, it } from 'vitest'
2
2
  import type { WorkflowExecutionActivity } from '@skyhook-io/k8s-ui/utils/workflow-execution'
3
- import { activityPreviewItems, effectiveDefinitionResource, isDirectRunKind, retentionHistoryCopy, runMessageNeedsDisclosure, workflowDefinitionParameters, workflowDefinitionTarget, workflowRunArguments } from './BatchExecutionView'
3
+ import {
4
+ activityPreviewItems,
5
+ selectedRunMissing,
6
+ effectiveDefinitionResource,
7
+ isDirectRunKind,
8
+ resourceTargetForRun,
9
+ retentionHistoryCopy,
10
+ runMessageNeedsDisclosure,
11
+ workflowDefinitionParameters,
12
+ workflowDefinitionTarget,
13
+ workflowRunArguments,
14
+ workloadRunKey,
15
+ } from './BatchExecutionView'
4
16
 
5
17
  function activity(id: string, tone: WorkflowExecutionActivity['tone'] = 'success'): WorkflowExecutionActivity {
6
18
  return { id, at: '2026-01-01T00:00:00Z', label: id, tone }
@@ -168,3 +180,42 @@ describe('direct run identity', () => {
168
180
  expect(isDirectRunKind('WorkflowTemplate', 'workflows')).toBe(false)
169
181
  })
170
182
  })
183
+
184
+ describe('JobSet member presentation', () => {
185
+ it('keeps the selected Job as the core Kubernetes intermediate', () => {
186
+ expect(resourceTargetForRun({
187
+ kind: 'jobs',
188
+ group: 'batch',
189
+ namespace: 'training',
190
+ name: 'distributed-workers-2',
191
+ phase: 'Running',
192
+ active: true,
193
+ })).toEqual({
194
+ kind: 'jobs',
195
+ group: 'batch',
196
+ namespace: 'training',
197
+ name: 'distributed-workers-2',
198
+ })
199
+ })
200
+ })
201
+
202
+ describe('selection across a member refresh', () => {
203
+ it('preserves a selected Job that has fallen outside the shown window', () => {
204
+ const shown = [{ kind: 'jobs', group: 'batch', namespace: 'training', name: 'current', phase: 'Running', active: true }]
205
+ expect(selectedRunMissing(shown, 'jobs/training/earlier')).toBe(true)
206
+ expect(selectedRunMissing(shown, 'jobs/training/current')).toBe(false)
207
+ expect(selectedRunMissing([], 'jobs/training/earlier')).toBe(true)
208
+ expect(selectedRunMissing(shown, '')).toBe(false)
209
+ })
210
+ })
211
+
212
+
213
+ describe('run navigation identity', () => {
214
+ it('uses the reported group and preserves the released selection address', () => {
215
+ const run = { group: 'batch', kind: 'jobs', namespace: 'training', name: 'worker', phase: 'Running', active: true }
216
+ expect(workloadRunKey(run)).toBe('jobs/training/worker')
217
+ expect(resourceTargetForRun(run).group).toBe('batch')
218
+ expect(resourceTargetForRun({ ...run, group: 'batch.volcano.sh' }).group).toBe('batch.volcano.sh')
219
+ expect(resourceTargetForRun({ ...run, group: 'argoproj.io', kind: 'workflows' }).group).toBe('argoproj.io')
220
+ })
221
+ })