@skyhook-io/k8s-ui 1.13.3 → 1.13.4

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 (97) hide show
  1. package/package.json +1 -1
  2. package/src/components/applications/AppTooltips.tsx +13 -1
  3. package/src/components/applications/ApplicationDetail.test.tsx +25 -0
  4. package/src/components/applications/ApplicationDetail.tsx +39 -7
  5. package/src/components/applications/ApplicationsView.tsx +18 -3
  6. package/src/components/applications/ReadyBar.tsx +3 -2
  7. package/src/components/applications/applications-view.test.tsx +91 -0
  8. package/src/components/cluster-switcher/ClusterSwitcher.tsx +4 -1
  9. package/src/components/issues/IssuesView.tsx +40 -31
  10. package/src/components/issues/ResourceIssuesSection.tsx +3 -2
  11. package/src/components/issues/index.ts +3 -1
  12. package/src/components/issues/issue-timing.ts +123 -4
  13. package/src/components/issues/issues.test.ts +221 -10
  14. package/src/components/issues/types.ts +31 -5
  15. package/src/components/namespace-switcher/NamespacePicker.tsx +21 -13
  16. package/src/components/resources/ResourcesView.tsx +964 -96
  17. package/src/components/resources/column-settings-key.test.ts +98 -0
  18. package/src/components/resources/column-visibility-merge.test.ts +64 -0
  19. package/src/components/resources/curated-column-ownership.test.ts +236 -0
  20. package/src/components/resources/gateway-policy-status.test.ts +291 -0
  21. package/src/components/resources/gateway-policy-status.ts +237 -0
  22. package/src/components/resources/generic-status.test.ts +199 -0
  23. package/src/components/resources/generic-status.ts +171 -0
  24. package/src/components/resources/gpu-ecosystem-registry.test.ts +63 -0
  25. package/src/components/resources/index.ts +9 -1
  26. package/src/components/resources/istio-gateway-filter.test.ts +97 -0
  27. package/src/components/resources/printer-columns.test.ts +298 -0
  28. package/src/components/resources/printer-columns.ts +249 -0
  29. package/src/components/resources/renderers/EventRenderer.test.tsx +50 -1
  30. package/src/components/resources/renderers/EventRenderer.tsx +3 -5
  31. package/src/components/resources/renderers/GenericRenderer.tsx +18 -83
  32. package/src/components/resources/renderers/HPARenderer.test.tsx +28 -0
  33. package/src/components/resources/renderers/HPARenderer.tsx +6 -1
  34. package/src/components/resources/renderers/MetricsUnavailableNotice.tsx +2 -1
  35. package/src/components/resources/renderers/PVCRenderer.test.tsx +44 -0
  36. package/src/components/resources/renderers/PVCRenderer.tsx +19 -2
  37. package/src/components/resources/renderers/RolloutRenderer.test.tsx +35 -0
  38. package/src/components/resources/renderers/RolloutRenderer.tsx +22 -3
  39. package/src/components/resources/renderers/WorkloadRenderer.test.tsx +83 -0
  40. package/src/components/resources/renderers/WorkloadRenderer.tsx +25 -65
  41. package/src/components/resources/renderers/amd-gpu-cells.tsx +36 -0
  42. package/src/components/resources/renderers/inference-gateway-cells.tsx +62 -0
  43. package/src/components/resources/renderers/jobset-lws-cells.tsx +82 -0
  44. package/src/components/resources/renderers/kai-cells.tsx +67 -0
  45. package/src/components/resources/renderers/kaito-cells.tsx +62 -0
  46. package/src/components/resources/renderers/kserve-cells.tsx +145 -0
  47. package/src/components/resources/renderers/kubeflow-training-cells.tsx +72 -0
  48. package/src/components/resources/renderers/kueue-cells.tsx +176 -0
  49. package/src/components/resources/renderers/nim-cells.tsx +81 -0
  50. package/src/components/resources/renderers/ray-cells.tsx +124 -0
  51. package/src/components/resources/renderers/volcano-cells.tsx +140 -0
  52. package/src/components/resources/resource-utils-amd-gpu.ts +61 -0
  53. package/src/components/resources/resource-utils-gpu-ecosystem.test.ts +222 -0
  54. package/src/components/resources/resource-utils-hpa.test.ts +4 -0
  55. package/src/components/resources/resource-utils-hpa.ts +6 -2
  56. package/src/components/resources/resource-utils-inference-gateway.ts +119 -0
  57. package/src/components/resources/resource-utils-jobset-lws.ts +117 -0
  58. package/src/components/resources/resource-utils-kai.ts +105 -0
  59. package/src/components/resources/resource-utils-kaito.ts +114 -0
  60. package/src/components/resources/resource-utils-kserve.ts +186 -0
  61. package/src/components/resources/resource-utils-kubeflow-training.ts +121 -0
  62. package/src/components/resources/resource-utils-kueue.ts +198 -0
  63. package/src/components/resources/resource-utils-nim.ts +109 -0
  64. package/src/components/resources/resource-utils-ray.ts +214 -0
  65. package/src/components/resources/resource-utils-rollout.test.ts +183 -0
  66. package/src/components/resources/resource-utils-velero.test.ts +30 -0
  67. package/src/components/resources/resource-utils-volcano.ts +176 -0
  68. package/src/components/resources/resource-utils.ts +156 -47
  69. package/src/components/resources/resources-column-filter.test.ts +27 -1
  70. package/src/components/resources/workload-rollout-golden.test.ts +55 -0
  71. package/src/components/shared/CreateResourceDialog.tsx +1 -0
  72. package/src/components/shared/EditableYamlView.tsx +4 -2
  73. package/src/components/shared/ResourceActionsBar.tsx +170 -25
  74. package/src/components/shared/ResourceRendererDispatch.test.tsx +206 -1
  75. package/src/components/shared/ResourceRendererDispatch.tsx +84 -40
  76. package/src/components/shared/SetImageDialog.test.ts +129 -0
  77. package/src/components/shared/SetImageDialog.tsx +577 -0
  78. package/src/components/shared/index.ts +1 -0
  79. package/src/components/shared/revision-role-badges.test.ts +56 -4
  80. package/src/components/timeline/TimelineList.tsx +19 -3
  81. package/src/components/timeline/TimelineSwimlanes.tsx +11 -3
  82. package/src/components/trace/ReachabilityView.tsx +1 -1
  83. package/src/components/ui/drawer-components.tsx +12 -0
  84. package/src/components/workload/WorkloadRolloutNotice.tsx +60 -0
  85. package/src/components/workload/WorkloadView.tsx +144 -81
  86. package/src/hooks/useProgressiveRefresh.ts +37 -0
  87. package/src/source-hygiene.test.ts +28 -0
  88. package/src/theme/tailwind-theme.css +4 -0
  89. package/src/types/core.ts +55 -0
  90. package/src/utils/api-resources.test.ts +40 -1
  91. package/src/utils/api-resources.ts +11 -1
  92. package/src/utils/applications.ts +50 -0
  93. package/src/utils/bulk-workload-actions.test.ts +20 -0
  94. package/src/utils/bulk-workload-actions.ts +15 -0
  95. package/src/utils/navigation.test.ts +31 -1
  96. package/src/utils/navigation.ts +21 -2
  97. package/src/utils/workload-rollout.ts +292 -0
@@ -1,5 +1,5 @@
1
1
  import { useState, useEffect } from 'react'
2
- import { Server, ExternalLink, Scale, Minus, Plus, Loader2, Shield } from 'lucide-react'
2
+ import { Server, ExternalLink, Scale, Minus, Plus, Shield } from 'lucide-react'
3
3
  import { clsx } from 'clsx'
4
4
  import { PolicySection } from './PolicySection'
5
5
  import type { PolicyResourceResponse } from '../../../types/policy'
@@ -7,7 +7,7 @@ import { Section, PropertyList, Property, ConditionsSection, PodTemplateSection,
7
7
  import { DialogPortal } from '../../ui/DialogPortal'
8
8
  import { Tooltip } from '../../ui/Tooltip'
9
9
  import { Badge, type BadgeSeverity } from '../../ui/Badge'
10
- import type { RBACSubjectResponse, RBACPolicyRule, ResourceRef, HPADiagnosis } from '../../../types'
10
+ import type { RBACSubjectResponse, RBACPolicyRule, ResourceRef, HPADiagnosis, WorkloadPodInfo } from '../../../types'
11
11
  import { detectBlastRadius, rulePermissivenessScore } from '../../../utils/rbac-blast-radius'
12
12
  import { RBACErrorSection, isRBACUnavailable } from './RBACErrorSection'
13
13
  import { hpaStateLabel, hpaStateLevel } from '../resource-utils-hpa'
@@ -16,6 +16,9 @@ import {
16
16
  rbacResourceBadgeClass,
17
17
  rbacApiGroupBadgeClass,
18
18
  } from '../../../utils/rbac-badges'
19
+ import { getWorkloadRolloutActivity } from '../../../utils/workload-rollout'
20
+ import { useProgressiveRefresh } from '../../../hooks/useProgressiveRefresh'
21
+ import { WorkloadRolloutNotice } from '../../workload/WorkloadRolloutNotice'
19
22
 
20
23
  export interface ScalerDiagnosis {
21
24
  ref: ResourceRef
@@ -33,6 +36,7 @@ interface WorkloadRendererProps {
33
36
  isScalePending?: boolean
34
37
  scaleBlockedBy?: ResourceRef[]
35
38
  scalerDiagnostics?: ScalerDiagnosis[]
39
+ workloadPods?: WorkloadPodInfo[]
36
40
  onRequestRefresh?: () => void
37
41
  /**
38
42
  * RBAC reverse-lookup for the workload's pod-template ServiceAccount.
@@ -52,21 +56,13 @@ interface WorkloadRendererProps {
52
56
  policyError?: Error | null
53
57
  }
54
58
 
55
- // Check if the workload is actively progressing (scaling, rolling update)
56
- function isWorkloadProgressing(status: any): boolean {
57
- const conditions = status.conditions || []
58
- const progressing = conditions.find((c: any) => c.type === 'Progressing')
59
- return progressing?.status === 'True' && progressing?.reason !== 'ProgressDeadlineExceeded'
60
- }
61
-
62
59
  // Extract real problems from workload status (excludes normal rollout progress)
63
- function getWorkloadProblems(status: any, spec: any, kind: string): string[] {
60
+ function getWorkloadProblems(status: any, spec: any, kind: string, rolloutExplainsShortfall: boolean): string[] {
64
61
  const problems: string[] = []
65
- const progressing = isWorkloadProgressing(status)
66
62
  const isDaemonSet = kind === 'daemonsets'
67
63
 
68
64
  // Check replica/pod counts — only flag as problem if NOT actively progressing
69
- if (!progressing) {
65
+ if (!rolloutExplainsShortfall) {
70
66
  if (isDaemonSet) {
71
67
  const ready = status.numberReady || 0
72
68
  const desired = status.desiredNumberScheduled || 0
@@ -78,7 +74,7 @@ function getWorkloadProblems(status: any, spec: any, kind: string): string[] {
78
74
  }
79
75
  } else {
80
76
  const ready = status.readyReplicas || 0
81
- const desired = spec.replicas || 0
77
+ const desired = spec.replicas ?? 1
82
78
  if (desired > 0 && ready < desired) {
83
79
  problems.push(`${desired - ready} of ${desired} replicas are not ready`)
84
80
  }
@@ -96,7 +92,7 @@ function getWorkloadProblems(status: any, spec: any, kind: string): string[] {
96
92
  }
97
93
  // Show condition failures, but skip Available=False during active rollout (that's expected)
98
94
  if (cond.status === 'False' && cond.message) {
99
- if (progressing && cond.type === 'Available') continue
95
+ if (rolloutExplainsShortfall && cond.type === 'Available') continue
100
96
  problems.push(`${cond.type}: ${cond.message}`)
101
97
  }
102
98
  }
@@ -104,27 +100,6 @@ function getWorkloadProblems(status: any, spec: any, kind: string): string[] {
104
100
  return problems
105
101
  }
106
102
 
107
- // Get progress info for active rollouts
108
- function getWorkloadProgress(status: any, spec: any, kind: string): string | null {
109
- if (!isWorkloadProgressing(status)) return null
110
-
111
- const isDaemonSet = kind === 'daemonsets'
112
- if (isDaemonSet) {
113
- const ready = status.numberReady || 0
114
- const desired = status.desiredNumberScheduled || 0
115
- if (desired > 0 && ready < desired) {
116
- return `${ready} of ${desired} pods ready`
117
- }
118
- } else {
119
- const ready = status.readyReplicas || 0
120
- const desired = spec.replicas || 0
121
- if (desired > 0 && ready < desired) {
122
- return `${ready} of ${desired} replicas ready`
123
- }
124
- }
125
- return null
126
- }
127
-
128
103
  function formatScalerLabel(ref: ResourceRef): string {
129
104
  const prefix = ref.namespace ? `${ref.namespace}/` : ''
130
105
  return `${ref.kind} ${prefix}${ref.name}`
@@ -150,7 +125,7 @@ function compactHPASummary(diagnosis: HPADiagnosis): string {
150
125
  return diagnosis.summary
151
126
  }
152
127
 
153
- export function WorkloadRenderer({ kind, data, onNavigate, onViewPods, onScale, isScalePending, scaleBlockedBy, scalerDiagnostics, onRequestRefresh, rbacData, rbacLoading, rbacError, policyData, policyLoading, policyError }: WorkloadRendererProps) {
128
+ export function WorkloadRenderer({ kind, data, onNavigate, onViewPods, onScale, isScalePending, scaleBlockedBy, scalerDiagnostics, workloadPods, onRequestRefresh, rbacData, rbacLoading, rbacError, policyData, policyLoading, policyError }: WorkloadRendererProps) {
154
129
  const status = data.status || {}
155
130
  const spec = data.spec || {}
156
131
  const metadata = data.metadata || {}
@@ -165,7 +140,7 @@ export function WorkloadRenderer({ kind, data, onNavigate, onViewPods, onScale,
165
140
 
166
141
  // Scale dialog state
167
142
  const [showScaleDialog, setShowScaleDialog] = useState(false)
168
- const [targetReplicas, setTargetReplicas] = useState(spec.replicas || 0)
143
+ const [targetReplicas, setTargetReplicas] = useState(spec.replicas ?? 1)
169
144
  const [scaledTo, setScaledTo] = useState<number | null>(null)
170
145
 
171
146
  // Clear scaledTo once the backend data catches up
@@ -179,19 +154,15 @@ export function WorkloadRenderer({ kind, data, onNavigate, onViewPods, onScale,
179
154
  // Issues section is shown — it carries the workload's own issues plus its pods'
180
155
  // (richer, with cause/action), so the workload-status problems would duplicate.
181
156
  const operationalIssuesShown = useOperationalIssuesShown()
182
- const problems = operationalIssuesShown ? [] : getWorkloadProblems(status, spec, kind)
157
+ const rolloutActivity = getWorkloadRolloutActivity(data, kind, workloadPods)
158
+ const rolloutExplainsShortfall = rolloutActivity.phase === 'applying' || rolloutActivity.phase === 'progressing'
159
+ const problems = operationalIssuesShown ? [] : getWorkloadProblems(status, spec, kind, rolloutExplainsShortfall)
183
160
  const hasProblems = problems.length > 0
184
- const progressMessage = getWorkloadProgress(status, spec, kind)
161
+ const displayedActivity = scaledTo !== null && rolloutActivity.phase === 'idle'
162
+ ? { ...rolloutActivity, phase: 'applying' as const, active: true, label: 'Scaling workload', detail: `Waiting for the controller to apply ${scaledTo} replicas` }
163
+ : rolloutActivity
185
164
 
186
- // Request data refresh while scaling is in progress
187
- const isScaling = scaledTo !== null || progressMessage !== null
188
- useEffect(() => {
189
- if (!isScaling || !onRequestRefresh) return
190
- const interval = setInterval(() => {
191
- onRequestRefresh()
192
- }, 2000)
193
- return () => clearInterval(interval)
194
- }, [isScaling, onRequestRefresh])
165
+ useProgressiveRefresh(scaledTo !== null, onRequestRefresh)
195
166
 
196
167
  useEffect(() => {
197
168
  if (isScaleBlocked) {
@@ -211,24 +182,13 @@ export function WorkloadRenderer({ kind, data, onNavigate, onViewPods, onScale,
211
182
  }
212
183
 
213
184
  const openScaleDialog = () => {
214
- setTargetReplicas(spec.replicas || 0)
185
+ setTargetReplicas(spec.replicas ?? 1)
215
186
  setShowScaleDialog(true)
216
187
  }
217
188
 
218
189
  return (
219
190
  <>
220
- {/* Scaling in progress banner — amber: replicas short of desired is an
221
- attention state, not an info note (it may be a stuck rollout). */}
222
- {(scaledTo !== null || progressMessage) && !hasProblems && (
223
- <div className="mb-4 p-3 bg-amber-500/10 border border-amber-500/30 rounded-lg">
224
- <div className="flex items-center gap-2">
225
- <Loader2 className="w-4 h-4 text-amber-500 animate-spin shrink-0" />
226
- <div className="text-sm text-amber-700 dark:text-amber-300">
227
- {progressMessage || `Scaling to ${scaledTo} replicas...`}
228
- </div>
229
- </div>
230
- </div>
231
- )}
191
+ {(!hasProblems || displayedActivity.phase === 'stalled') && <WorkloadRolloutNotice activity={displayedActivity} className="mb-4" />}
232
192
 
233
193
  {/* Problems alert - shown at top when there are real issues */}
234
194
  {hasProblems && (
@@ -262,7 +222,7 @@ export function WorkloadRenderer({ kind, data, onNavigate, onViewPods, onScale,
262
222
  </>
263
223
  ) : (
264
224
  <>
265
- <Property label="Replicas" value={`${status.readyReplicas || 0}/${spec.replicas || 0}`} />
225
+ <Property label="Replicas" value={`${status.readyReplicas || 0}/${spec.replicas ?? 1}`} />
266
226
  {scaleBlockedBy && scaleBlockedBy.length > 0 && (
267
227
  <Property
268
228
  label="Controlled by"
@@ -357,8 +317,8 @@ export function WorkloadRenderer({ kind, data, onNavigate, onViewPods, onScale,
357
317
  </div>
358
318
 
359
319
  <div className="text-xs text-theme-text-tertiary text-center mb-4">
360
- Current: {spec.replicas || 0} replicas
361
- {targetReplicas !== (spec.replicas || 0) && (
320
+ Current: {spec.replicas ?? 1} replicas
321
+ {targetReplicas !== (spec.replicas ?? 1) && (
362
322
  <span className="text-theme-text-secondary">
363
323
  {' '}→ {targetReplicas}
364
324
  </span>
@@ -374,7 +334,7 @@ export function WorkloadRenderer({ kind, data, onNavigate, onViewPods, onScale,
374
334
  </button>
375
335
  <button
376
336
  onClick={handleScale}
377
- disabled={isScalePending || targetReplicas === (spec.replicas || 0)}
337
+ disabled={isScalePending || targetReplicas === (spec.replicas ?? 1)}
378
338
  className="flex-1 px-3 py-2 text-sm btn-brand disabled:cursor-not-allowed rounded-lg"
379
339
  >
380
340
  {isScalePending ? 'Scaling...' : 'Apply'}
@@ -0,0 +1,36 @@
1
+ // AMD GPU Operator cell components for ResourcesView table
2
+
3
+ import { clsx } from 'clsx'
4
+ import {
5
+ getAMDDeviceConfigStatus,
6
+ getAMDDeviceConfigDriver,
7
+ getAMDDeviceConfigDevicePluginImage,
8
+ getAMDDeviceConfigNodeCount,
9
+ } from '../resource-utils-amd-gpu'
10
+
11
+ export function AMDDeviceConfigCell({ resource, column }: { resource: any; column: string }) {
12
+ switch (column) {
13
+ case 'status': {
14
+ const status = getAMDDeviceConfigStatus(resource)
15
+ return (
16
+ <span className={clsx('badge', status.color)}>
17
+ {status.text}
18
+ </span>
19
+ )
20
+ }
21
+ case 'driver': {
22
+ const driver = getAMDDeviceConfigDriver(resource)
23
+ return <span className="text-sm text-theme-text-secondary">{driver}</span>
24
+ }
25
+ case 'devicePluginImage': {
26
+ const image = getAMDDeviceConfigDevicePluginImage(resource)
27
+ return <span className="text-sm text-theme-text-secondary truncate block">{image}</span>
28
+ }
29
+ case 'nodes': {
30
+ const nodes = getAMDDeviceConfigNodeCount(resource)
31
+ return <span className="text-sm text-theme-text-secondary">{nodes}</span>
32
+ }
33
+ default:
34
+ return <span className="text-sm text-theme-text-tertiary">-</span>
35
+ }
36
+ }
@@ -0,0 +1,62 @@
1
+ // Gateway API Inference Extension cell components for ResourcesView table
2
+
3
+ import { clsx } from 'clsx'
4
+ import {
5
+ getInferencePoolStatus,
6
+ getInferencePoolSelector,
7
+ getInferencePoolTargetPorts,
8
+ getInferencePoolExtensionRef,
9
+ getInferenceObjectiveStatus,
10
+ getInferenceObjectivePoolRef,
11
+ getInferenceObjectivePriority,
12
+ } from '../resource-utils-inference-gateway'
13
+
14
+ export function InferencePoolCell({ resource, column }: { resource: any; column: string }) {
15
+ switch (column) {
16
+ case 'status': {
17
+ const status = getInferencePoolStatus(resource)
18
+ return (
19
+ <span className={clsx('badge', status.color)}>
20
+ {status.text}
21
+ </span>
22
+ )
23
+ }
24
+ case 'selector': {
25
+ const selector = getInferencePoolSelector(resource)
26
+ return <span className="text-sm text-theme-text-secondary truncate block">{selector}</span>
27
+ }
28
+ case 'targetPorts': {
29
+ const ports = getInferencePoolTargetPorts(resource)
30
+ return <span className="text-sm text-theme-text-secondary">{ports}</span>
31
+ }
32
+ case 'extensionRef': {
33
+ const ref = getInferencePoolExtensionRef(resource)
34
+ return <span className="text-sm text-theme-text-secondary truncate block">{ref}</span>
35
+ }
36
+ default:
37
+ return <span className="text-sm text-theme-text-tertiary">-</span>
38
+ }
39
+ }
40
+
41
+ export function InferenceObjectiveCell({ resource, column }: { resource: any; column: string }) {
42
+ switch (column) {
43
+ case 'status': {
44
+ const status = getInferenceObjectiveStatus(resource)
45
+ return (
46
+ <span className={clsx('badge', status.color)}>
47
+ {status.text}
48
+ </span>
49
+ )
50
+ }
51
+ case 'poolRef': {
52
+ const poolRef = getInferenceObjectivePoolRef(resource)
53
+ return <span className="text-sm text-theme-text-secondary truncate block">{poolRef}</span>
54
+ }
55
+ case 'priority': {
56
+ const priority = getInferenceObjectivePriority(resource)
57
+ return <span className="text-sm text-theme-text-secondary">{priority}</span>
58
+ }
59
+ default:
60
+ return <span className="text-sm text-theme-text-tertiary">-</span>
61
+ }
62
+ }
@@ -0,0 +1,82 @@
1
+ // LeaderWorkerSet and JobSet cell components for ResourcesView table
2
+
3
+ import { clsx } from 'clsx'
4
+ import {
5
+ getLeaderWorkerSetStatus,
6
+ getLeaderWorkerSetReplicas,
7
+ getLeaderWorkerSetSize,
8
+ getLeaderWorkerSetReady,
9
+ getLeaderWorkerSetUpdated,
10
+ getJobSetStatus,
11
+ getJobSetReplicatedJobs,
12
+ getJobSetReadyJobs,
13
+ getJobSetSucceededJobs,
14
+ getJobSetFailedJobs,
15
+ getJobSetRestarts,
16
+ } from '../resource-utils-jobset-lws'
17
+
18
+ export function LeaderWorkerSetCell({ resource, column }: { resource: any; column: string }) {
19
+ switch (column) {
20
+ case 'status': {
21
+ const status = getLeaderWorkerSetStatus(resource)
22
+ return (
23
+ <span className={clsx('badge', status.color)}>
24
+ {status.text}
25
+ </span>
26
+ )
27
+ }
28
+ case 'replicas': {
29
+ const replicas = getLeaderWorkerSetReplicas(resource)
30
+ return <span className="text-sm text-theme-text-secondary">{replicas}</span>
31
+ }
32
+ case 'size': {
33
+ const size = getLeaderWorkerSetSize(resource)
34
+ return <span className="text-sm text-theme-text-secondary">{size}</span>
35
+ }
36
+ case 'ready': {
37
+ const ready = getLeaderWorkerSetReady(resource)
38
+ return <span className="text-sm text-theme-text-secondary">{ready}</span>
39
+ }
40
+ case 'updated': {
41
+ const updated = getLeaderWorkerSetUpdated(resource)
42
+ return <span className="text-sm text-theme-text-secondary">{updated}</span>
43
+ }
44
+ default:
45
+ return <span className="text-sm text-theme-text-tertiary">-</span>
46
+ }
47
+ }
48
+
49
+ export function JobSetCell({ resource, column }: { resource: any; column: string }) {
50
+ switch (column) {
51
+ case 'status': {
52
+ const status = getJobSetStatus(resource)
53
+ return (
54
+ <span className={clsx('badge', status.color)}>
55
+ {status.text}
56
+ </span>
57
+ )
58
+ }
59
+ case 'replicatedJobs': {
60
+ const count = getJobSetReplicatedJobs(resource)
61
+ return <span className="text-sm text-theme-text-secondary">{count}</span>
62
+ }
63
+ case 'ready': {
64
+ const ready = getJobSetReadyJobs(resource)
65
+ return <span className="text-sm text-theme-text-secondary">{ready}</span>
66
+ }
67
+ case 'succeeded': {
68
+ const succeeded = getJobSetSucceededJobs(resource)
69
+ return <span className="text-sm text-theme-text-secondary">{succeeded}</span>
70
+ }
71
+ case 'failed': {
72
+ const failed = getJobSetFailedJobs(resource)
73
+ return <span className={clsx('text-sm', failed > 0 ? 'text-theme-text-primary' : 'text-theme-text-secondary')}>{failed}</span>
74
+ }
75
+ case 'restarts': {
76
+ const restarts = getJobSetRestarts(resource)
77
+ return <span className="text-sm text-theme-text-secondary">{restarts}</span>
78
+ }
79
+ default:
80
+ return <span className="text-sm text-theme-text-tertiary">-</span>
81
+ }
82
+ }
@@ -0,0 +1,67 @@
1
+ // KAI Scheduler cell components for ResourcesView table
2
+
3
+ import { clsx } from 'clsx'
4
+ import {
5
+ getKaiQueueStatus,
6
+ getKaiQueueParent,
7
+ getKaiQueuePriority,
8
+ getKaiQueueQuota,
9
+ getKaiQueueAllocated,
10
+ getKaiPodGroupStatus,
11
+ getKaiPodGroupQueue,
12
+ getKaiPodGroupMinMember,
13
+ } from '../resource-utils-kai'
14
+
15
+ export function KaiQueueCell({ resource, column }: { resource: any; column: string }) {
16
+ switch (column) {
17
+ case 'status': {
18
+ const status = getKaiQueueStatus(resource)
19
+ return (
20
+ <span className={clsx('badge', status.color)}>
21
+ {status.text}
22
+ </span>
23
+ )
24
+ }
25
+ case 'parentQueue': {
26
+ const parent = getKaiQueueParent(resource)
27
+ return <span className="text-sm text-theme-text-secondary">{parent}</span>
28
+ }
29
+ case 'priority': {
30
+ const priority = getKaiQueuePriority(resource)
31
+ return <span className="text-sm text-theme-text-secondary">{priority}</span>
32
+ }
33
+ case 'quota': {
34
+ const quota = getKaiQueueQuota(resource)
35
+ return <span className="text-sm text-theme-text-secondary truncate block">{quota}</span>
36
+ }
37
+ case 'allocated': {
38
+ const allocated = getKaiQueueAllocated(resource)
39
+ return <span className="text-sm text-theme-text-secondary truncate block">{allocated}</span>
40
+ }
41
+ default:
42
+ return <span className="text-sm text-theme-text-tertiary">-</span>
43
+ }
44
+ }
45
+
46
+ export function KaiPodGroupCell({ resource, column }: { resource: any; column: string }) {
47
+ switch (column) {
48
+ case 'status': {
49
+ const status = getKaiPodGroupStatus(resource)
50
+ return (
51
+ <span className={clsx('badge', status.color)}>
52
+ {status.text}
53
+ </span>
54
+ )
55
+ }
56
+ case 'queue': {
57
+ const queue = getKaiPodGroupQueue(resource)
58
+ return <span className="text-sm text-theme-text-secondary">{queue}</span>
59
+ }
60
+ case 'minMember': {
61
+ const minMember = getKaiPodGroupMinMember(resource)
62
+ return <span className="text-sm text-theme-text-secondary">{minMember}</span>
63
+ }
64
+ default:
65
+ return <span className="text-sm text-theme-text-tertiary">-</span>
66
+ }
67
+ }
@@ -0,0 +1,62 @@
1
+ // KAITO cell components for ResourcesView table
2
+
3
+ import { clsx } from 'clsx'
4
+ import {
5
+ getKaitoWorkspaceStatus,
6
+ getKaitoWorkspaceInstanceType,
7
+ getKaitoWorkspacePreset,
8
+ getKaitoWorkspaceNodeCount,
9
+ getRAGEngineStatus,
10
+ getRAGEngineEmbeddingModel,
11
+ getRAGEngineInstanceType,
12
+ } from '../resource-utils-kaito'
13
+
14
+ export function KaitoWorkspaceCell({ resource, column }: { resource: any; column: string }) {
15
+ switch (column) {
16
+ case 'status': {
17
+ const status = getKaitoWorkspaceStatus(resource)
18
+ return (
19
+ <span className={clsx('badge', status.color)}>
20
+ {status.text}
21
+ </span>
22
+ )
23
+ }
24
+ case 'instanceType': {
25
+ const instanceType = getKaitoWorkspaceInstanceType(resource)
26
+ return <span className="text-sm text-theme-text-secondary">{instanceType}</span>
27
+ }
28
+ case 'preset': {
29
+ const preset = getKaitoWorkspacePreset(resource)
30
+ return <span className="text-sm text-theme-text-secondary truncate block">{preset}</span>
31
+ }
32
+ case 'nodes': {
33
+ const nodes = getKaitoWorkspaceNodeCount(resource)
34
+ return <span className="text-sm text-theme-text-secondary">{nodes}</span>
35
+ }
36
+ default:
37
+ return <span className="text-sm text-theme-text-tertiary">-</span>
38
+ }
39
+ }
40
+
41
+ export function RAGEngineCell({ resource, column }: { resource: any; column: string }) {
42
+ switch (column) {
43
+ case 'status': {
44
+ const status = getRAGEngineStatus(resource)
45
+ return (
46
+ <span className={clsx('badge', status.color)}>
47
+ {status.text}
48
+ </span>
49
+ )
50
+ }
51
+ case 'embedding': {
52
+ const model = getRAGEngineEmbeddingModel(resource)
53
+ return <span className="text-sm text-theme-text-secondary truncate block">{model}</span>
54
+ }
55
+ case 'instanceType': {
56
+ const instanceType = getRAGEngineInstanceType(resource)
57
+ return <span className="text-sm text-theme-text-secondary">{instanceType}</span>
58
+ }
59
+ default:
60
+ return <span className="text-sm text-theme-text-tertiary">-</span>
61
+ }
62
+ }
@@ -0,0 +1,145 @@
1
+ // KServe cell components for ResourcesView table
2
+
3
+ import { clsx } from 'clsx'
4
+ import {
5
+ getInferenceServiceStatus,
6
+ getInferenceServiceUrl,
7
+ getInferenceServiceModelFormat,
8
+ getInferenceServiceRuntime,
9
+ getInferenceServiceDeploymentMode,
10
+ getServingRuntimeStatus,
11
+ getServingRuntimeModelFormats,
12
+ getServingRuntimeImage,
13
+ getInferenceGraphStatus,
14
+ getInferenceGraphNodeCount,
15
+ getTrainedModelStatus,
16
+ getTrainedModelFramework,
17
+ getTrainedModelStorageUri,
18
+ getTrainedModelInferenceService,
19
+ getLLMInferenceServiceStatus,
20
+ getLLMInferenceServiceModel,
21
+ getLLMInferenceServiceReplicas,
22
+ } from '../resource-utils-kserve'
23
+
24
+ export function InferenceServiceCell({ resource, column }: { resource: any; column: string }) {
25
+ switch (column) {
26
+ case 'status': {
27
+ const status = getInferenceServiceStatus(resource)
28
+ return (
29
+ <span className={clsx('badge', status.color)}>
30
+ {status.text}
31
+ </span>
32
+ )
33
+ }
34
+ case 'url': {
35
+ const url = getInferenceServiceUrl(resource)
36
+ return <span className="text-sm text-theme-text-secondary truncate block">{url}</span>
37
+ }
38
+ case 'modelFormat': {
39
+ const format = getInferenceServiceModelFormat(resource)
40
+ return <span className="text-sm text-theme-text-secondary">{format}</span>
41
+ }
42
+ case 'runtime': {
43
+ const runtime = getInferenceServiceRuntime(resource)
44
+ return <span className="text-sm text-theme-text-secondary truncate block">{runtime}</span>
45
+ }
46
+ case 'deploymentMode': {
47
+ const mode = getInferenceServiceDeploymentMode(resource)
48
+ return <span className="text-sm text-theme-text-secondary">{mode}</span>
49
+ }
50
+ default:
51
+ return <span className="text-sm text-theme-text-tertiary">-</span>
52
+ }
53
+ }
54
+
55
+ export function ServingRuntimeCell({ resource, column }: { resource: any; column: string }) {
56
+ switch (column) {
57
+ case 'status': {
58
+ const status = getServingRuntimeStatus(resource)
59
+ return (
60
+ <span className={clsx('badge', status.color)}>
61
+ {status.text}
62
+ </span>
63
+ )
64
+ }
65
+ case 'modelFormats': {
66
+ const formats = getServingRuntimeModelFormats(resource)
67
+ return <span className="text-sm text-theme-text-secondary truncate block">{formats}</span>
68
+ }
69
+ case 'image': {
70
+ const image = getServingRuntimeImage(resource)
71
+ return <span className="text-sm text-theme-text-secondary truncate block">{image}</span>
72
+ }
73
+ default:
74
+ return <span className="text-sm text-theme-text-tertiary">-</span>
75
+ }
76
+ }
77
+
78
+ export function InferenceGraphCell({ resource, column }: { resource: any; column: string }) {
79
+ switch (column) {
80
+ case 'status': {
81
+ const status = getInferenceGraphStatus(resource)
82
+ return (
83
+ <span className={clsx('badge', status.color)}>
84
+ {status.text}
85
+ </span>
86
+ )
87
+ }
88
+ case 'nodes': {
89
+ const count = getInferenceGraphNodeCount(resource)
90
+ return <span className="text-sm text-theme-text-secondary">{count > 0 ? count : '-'}</span>
91
+ }
92
+ default:
93
+ return <span className="text-sm text-theme-text-tertiary">-</span>
94
+ }
95
+ }
96
+
97
+ export function TrainedModelCell({ resource, column }: { resource: any; column: string }) {
98
+ switch (column) {
99
+ case 'status': {
100
+ const status = getTrainedModelStatus(resource)
101
+ return (
102
+ <span className={clsx('badge', status.color)}>
103
+ {status.text}
104
+ </span>
105
+ )
106
+ }
107
+ case 'framework': {
108
+ const framework = getTrainedModelFramework(resource)
109
+ return <span className="text-sm text-theme-text-secondary">{framework}</span>
110
+ }
111
+ case 'storageUri': {
112
+ const uri = getTrainedModelStorageUri(resource)
113
+ return <span className="text-sm text-theme-text-secondary truncate block">{uri}</span>
114
+ }
115
+ case 'inferenceService': {
116
+ const parent = getTrainedModelInferenceService(resource)
117
+ return <span className="text-sm text-theme-text-secondary truncate block">{parent}</span>
118
+ }
119
+ default:
120
+ return <span className="text-sm text-theme-text-tertiary">-</span>
121
+ }
122
+ }
123
+
124
+ export function LLMInferenceServiceCell({ resource, column }: { resource: any; column: string }) {
125
+ switch (column) {
126
+ case 'status': {
127
+ const status = getLLMInferenceServiceStatus(resource)
128
+ return (
129
+ <span className={clsx('badge', status.color)}>
130
+ {status.text}
131
+ </span>
132
+ )
133
+ }
134
+ case 'model': {
135
+ const model = getLLMInferenceServiceModel(resource)
136
+ return <span className="text-sm text-theme-text-secondary truncate block">{model}</span>
137
+ }
138
+ case 'replicas': {
139
+ const replicas = getLLMInferenceServiceReplicas(resource)
140
+ return <span className="text-sm text-theme-text-secondary">{replicas}</span>
141
+ }
142
+ default:
143
+ return <span className="text-sm text-theme-text-tertiary">-</span>
144
+ }
145
+ }