@skyhook-io/radar-app 1.1.1 → 1.1.2

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 (32) hide show
  1. package/package.json +1 -1
  2. package/src/App.tsx +81 -18
  3. package/src/api/client.ts +165 -11
  4. package/src/api/rbac.ts +57 -0
  5. package/src/components/compare/CompareViewRoute.tsx +116 -0
  6. package/src/components/compare/useCompareCandidates.ts +27 -0
  7. package/src/components/compare/useCompareLauncher.tsx +76 -0
  8. package/src/components/cost/CostView.tsx +1 -1
  9. package/src/components/gitops/GitOpsView.tsx +1 -1
  10. package/src/components/helm/InstallWizard.tsx +5 -5
  11. package/src/components/helm/ValuesViewer.tsx +3 -39
  12. package/src/components/home/HomeView.tsx +18 -2
  13. package/src/components/resource/HPACharts.tsx +232 -0
  14. package/src/components/resource/PVCUsageBar.tsx +59 -0
  15. package/src/components/resource/PrometheusCharts.tsx +151 -434
  16. package/src/components/resource/PrometheusChartsGrid.tsx +339 -0
  17. package/src/components/resource/RestartChart.tsx +124 -0
  18. package/src/components/resource/RightsizingStrip.tsx +167 -0
  19. package/src/components/resources/CompositeRenderer.tsx +101 -0
  20. package/src/components/resources/renderers/HPARenderer.tsx +17 -1
  21. package/src/components/resources/renderers/NamespaceRenderer.tsx +22 -0
  22. package/src/components/resources/renderers/PVCRenderer.tsx +19 -1
  23. package/src/components/resources/renderers/PodRenderer.tsx +13 -0
  24. package/src/components/resources/renderers/RoleBindingRenderer.tsx +43 -1
  25. package/src/components/resources/renderers/RoleRenderer.tsx +27 -1
  26. package/src/components/resources/renderers/ServiceAccountRenderer.tsx +28 -1
  27. package/src/components/resources/renderers/WorkloadRenderer.tsx +12 -0
  28. package/src/components/resources/renderers/index.ts +1 -0
  29. package/src/components/settings/MyPermissionsDialog.tsx +231 -0
  30. package/src/components/ui/DiagnosticsOverlay.tsx +1 -0
  31. package/src/components/workload/WorkloadView.tsx +107 -3
  32. package/src/context/NavCustomization.tsx +13 -0
@@ -23,6 +23,9 @@ import {
23
23
  fetchJSON,
24
24
  } from '../../api/client'
25
25
  import { PrometheusCharts, isPrometheusSupported } from '../resource/PrometheusCharts'
26
+ import { PrometheusChartsGrid } from '../resource/PrometheusChartsGrid'
27
+ import { RestartEventLane } from '../resource/RestartChart'
28
+ import { RightsizingStrip } from '../resource/RightsizingStrip'
26
29
  import { useResourceAudit, useResources } from '../../api/client'
27
30
  import { AuditAlerts } from '@skyhook-io/k8s-ui'
28
31
  import { WorkloadLogsViewer } from '../logs/WorkloadLogsViewer'
@@ -35,15 +38,30 @@ import { PodRenderer } from '../resources/renderers/PodRenderer'
35
38
  import { NodeRenderer } from '../resources/renderers/NodeRenderer'
36
39
  import { ServiceRenderer } from '../resources/renderers/ServiceRenderer'
37
40
  import { WorkloadRenderer } from '../resources/renderers/WorkloadRenderer'
41
+ import { CompositeRenderer } from '../resources/CompositeRenderer'
42
+ import { ServiceAccountRenderer } from '../resources/renderers/ServiceAccountRenderer'
43
+ import { RoleRenderer } from '../resources/renderers/RoleRenderer'
44
+ import { RoleBindingRenderer } from '../resources/renderers/RoleBindingRenderer'
45
+ import { NamespaceRenderer } from '../resources/renderers/NamespaceRenderer'
46
+ import { HPARenderer } from '../resources/renderers/HPARenderer'
47
+ import { PVCRenderer } from '../resources/renderers/PVCRenderer'
38
48
  import { CreateResourceDialog } from '../shared/CreateResourceDialog'
39
49
  import { cleanYamlForDuplicate } from '../../utils/skeleton-yaml'
40
50
  import { useDesktopDownload } from '../../hooks/useDesktopDownload'
51
+ import { useCompareLauncher } from '../compare/useCompareLauncher'
52
+ import { apiVersionToGroup } from '../../utils/navigation'
41
53
 
42
54
  type TabType = 'overview' | 'timeline' | 'logs' | 'metrics' | 'yaml'
43
55
 
44
56
  // Stable reference — web renderer wrappers inject platform hooks internally
45
57
  const rendererOverrides: RendererOverrides = {
46
- PodRenderer, NodeRenderer, ServiceRenderer, WorkloadRenderer,
58
+ PodRenderer, NodeRenderer, ServiceRenderer, WorkloadRenderer, CompositeRenderer,
59
+ ServiceAccountRenderer,
60
+ RoleRenderer,
61
+ RoleBindingRenderer,
62
+ NamespaceRenderer,
63
+ HPARenderer,
64
+ PVCRenderer,
47
65
  }
48
66
 
49
67
  // ============================================================================
@@ -323,9 +341,27 @@ export function WorkloadView({
323
341
  // RBAC
324
342
  const canUpdateSecrets = useCanUpdateSecrets()
325
343
  const updateResource = useUpdateResource()
326
- const actionsBarProps = useActionsBarProps(kindProp, namespace, name)
344
+ const baseActionsBarProps = useActionsBarProps(kindProp, namespace, name)
327
345
  const desktopDownload = useDesktopDownload()
328
346
 
347
+ const resourceGroup = useMemo(
348
+ () => (resource?.apiVersion ? apiVersionToGroup(resource.apiVersion) : undefined),
349
+ [resource?.apiVersion],
350
+ )
351
+ const { onCompareTo, onCompareAcrossClusters, picker: comparePicker } = useCompareLauncher({
352
+ kind: kindProp,
353
+ namespace,
354
+ name,
355
+ // Prefer the URL-supplied group so Compare works even before the resource
356
+ // fetch completes; fall back to the derived group for callers that don't
357
+ // pass one.
358
+ group: rest.group || resourceGroup || undefined,
359
+ })
360
+ const actionsBarProps = useMemo(
361
+ () => ({ ...baseActionsBarProps, onCompareTo, onCompareAcrossClusters }),
362
+ [baseActionsBarProps, onCompareTo, onCompareAcrossClusters],
363
+ )
364
+
329
365
  const handleUpdateResource = useCallback(async (params: { kind: string; namespace: string; name: string; yaml: string }) => {
330
366
  await updateResource.mutateAsync(params)
331
367
  }, [updateResource])
@@ -384,7 +420,7 @@ export function WorkloadView({
384
420
  // Render props
385
421
  renderLogsTab={(props) => <LogsTabContent {...props} />}
386
422
  renderMetricsTab={({ kind, namespace: ns, name: n }) => (
387
- <PrometheusCharts kind={kind} namespace={ns} name={n} showEmptyState />
423
+ <MetricsTabContent kind={kind} namespace={ns} name={n} resource={resource} expanded={expanded} />
388
424
  )}
389
425
  isMetricsAvailable={(kind, res) =>
390
426
  isPrometheusSupported(kind) && !(kind === 'Pod' && res?.status?.phase === 'Pending')
@@ -412,6 +448,7 @@ export function WorkloadView({
412
448
  rest.onNavigateToResource?.({ kind: kindToPlural(result.kind), namespace: result.namespace, name: result.name, group: '' })
413
449
  }}
414
450
  />
451
+ {comparePicker}
415
452
  </>
416
453
  )
417
454
  }
@@ -651,6 +688,73 @@ function FluxSourceConsumersInner({ sourceKind, namespace, name }: { sourceKind:
651
688
  )
652
689
  }
653
690
 
691
+ // Drawer mode: single chart + category tabs (compact for ~500px width).
692
+ // Full-screen mode: multi-chart grid so CPU + Memory + Network can be
693
+ // compared side-by-side without tab switching.
694
+ function MetricsTabContent({ kind, namespace, name, resource, expanded }: {
695
+ kind: string
696
+ namespace: string
697
+ name: string
698
+ resource: any
699
+ expanded: boolean
700
+ }) {
701
+ const showRightsizing = expanded && ['Deployment', 'StatefulSet', 'DaemonSet'].includes(kind)
702
+
703
+ if (expanded) {
704
+ return (
705
+ <div className="flex flex-col h-full">
706
+ {showRightsizing && (
707
+ <div className="px-4 pt-4">
708
+ <RightsizingStrip kind={kind} namespace={namespace} name={name} />
709
+ </div>
710
+ )}
711
+ <div className="flex-1 min-h-0">
712
+ <PrometheusChartsGrid
713
+ kind={kind}
714
+ namespace={namespace}
715
+ name={name}
716
+ resource={resource}
717
+ />
718
+ </div>
719
+ </div>
720
+ )
721
+ }
722
+
723
+ // Drawer fallback: single chart with tabs + restart lane below. The chart's
724
+ // time-range selector is mirrored to the restart lane so they stay aligned.
725
+ return (
726
+ <DrawerMetricsContent
727
+ kind={kind}
728
+ namespace={namespace}
729
+ name={name}
730
+ resource={resource}
731
+ />
732
+ )
733
+ }
734
+
735
+ function DrawerMetricsContent({ kind, namespace, name, resource }: {
736
+ kind: string
737
+ namespace: string
738
+ name: string
739
+ resource: any
740
+ }) {
741
+ const [chartRange, setChartRange] = useState<import('../../api/client').PrometheusTimeRange>('1h')
742
+ const showRestartLane = kind !== 'Node'
743
+
744
+ return (
745
+ <div className="flex flex-col h-full">
746
+ <div className="flex-1 min-h-0">
747
+ <PrometheusCharts kind={kind} namespace={namespace} name={name} showEmptyState resource={resource} onTimeRangeChange={setChartRange} />
748
+ </div>
749
+ {showRestartLane && (
750
+ <div className="px-4 pb-4">
751
+ <RestartEventLane kind={kind} namespace={namespace} name={name} range={chartRange} />
752
+ </div>
753
+ )}
754
+ </div>
755
+ )
756
+ }
757
+
654
758
  // FLUX_SOURCE_KIND_BY_LOWER maps lowercase kind (what the inner WorkloadView
655
759
  // produces via its plural-to-singular fallback) to the wire-correct
656
760
  // PascalCase form that consumers carry in spec.sourceRef.kind. HelmChart is
@@ -18,6 +18,19 @@ interface NavCustomizationBase {
18
18
  brandSlot?: ReactNode;
19
19
  /** Replaces the ContextSwitcher (kubeconfig-context picker). */
20
20
  contextSlot?: ReactNode;
21
+ /**
22
+ * When set, a "Compare across clusters" option is added to the Compare
23
+ * button in resource action bars. The host returns the URL that should
24
+ * be navigated to (via window.location.assign — typically a hub fleet
25
+ * route). Standalone Radar omits this and the compare action stays
26
+ * single-cluster.
27
+ */
28
+ crossClusterCompareHref?: (ref: {
29
+ kind: string;
30
+ namespace: string;
31
+ name: string;
32
+ group?: string;
33
+ }) => string;
21
34
  }
22
35
 
23
36
  /**