@skyhook-io/k8s-ui 1.0.0

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 (193) hide show
  1. package/package.json +63 -0
  2. package/src/components/dock/BottomDock.tsx +204 -0
  3. package/src/components/dock/DockContext.tsx +212 -0
  4. package/src/components/dock/TerminalTab.tsx +333 -0
  5. package/src/components/dock/index.ts +3 -0
  6. package/src/components/gitops/GitOpsActions.tsx +227 -0
  7. package/src/components/gitops/GitOpsStatusBadge.tsx +221 -0
  8. package/src/components/gitops/ManagedResourcesList.tsx +210 -0
  9. package/src/components/gitops/SyncCountdown.tsx +146 -0
  10. package/src/components/gitops/index.ts +4 -0
  11. package/src/components/logs/JsonLogLine.tsx +90 -0
  12. package/src/components/logs/LogCore.tsx +553 -0
  13. package/src/components/logs/LogToolbarSelects.tsx +74 -0
  14. package/src/components/logs/LogsViewer.tsx +146 -0
  15. package/src/components/logs/WorkloadLogsViewer.tsx +263 -0
  16. package/src/components/logs/index.ts +13 -0
  17. package/src/components/logs/useLogBuffer.ts +138 -0
  18. package/src/components/logs/useLogSearch.ts +151 -0
  19. package/src/components/logs/useLogStream.ts +61 -0
  20. package/src/components/resources/ResourcesView.tsx +5494 -0
  21. package/src/components/resources/index.ts +17 -0
  22. package/src/components/resources/renderers/AlertRenderer.tsx +166 -0
  23. package/src/components/resources/renderers/ArgoApplicationRenderer.tsx +233 -0
  24. package/src/components/resources/renderers/CNPGBackupRenderer.tsx +89 -0
  25. package/src/components/resources/renderers/CNPGClusterRenderer.tsx +194 -0
  26. package/src/components/resources/renderers/CNPGPoolerRenderer.tsx +79 -0
  27. package/src/components/resources/renderers/CNPGScheduledBackupRenderer.tsx +68 -0
  28. package/src/components/resources/renderers/CertificateRenderer.tsx +209 -0
  29. package/src/components/resources/renderers/CertificateRequestRenderer.tsx +123 -0
  30. package/src/components/resources/renderers/ChallengeRenderer.tsx +111 -0
  31. package/src/components/resources/renderers/ClusterComplianceReportRenderer.tsx +164 -0
  32. package/src/components/resources/renderers/ClusterExternalSecretRenderer.tsx +140 -0
  33. package/src/components/resources/renderers/ClusterIssuerRenderer.tsx +153 -0
  34. package/src/components/resources/renderers/ConfigAuditReportRenderer.tsx +221 -0
  35. package/src/components/resources/renderers/ConfigMapRenderer.tsx +124 -0
  36. package/src/components/resources/renderers/CronJobRenderer.tsx +86 -0
  37. package/src/components/resources/renderers/EventRenderer.tsx +175 -0
  38. package/src/components/resources/renderers/ExposedSecretReportRenderer.tsx +119 -0
  39. package/src/components/resources/renderers/ExternalSecretRenderer.tsx +172 -0
  40. package/src/components/resources/renderers/FluxHelmReleaseRenderer.tsx +242 -0
  41. package/src/components/resources/renderers/GRPCRouteRenderer.tsx +263 -0
  42. package/src/components/resources/renderers/GatewayClassRenderer.tsx +68 -0
  43. package/src/components/resources/renderers/GatewayRenderer.tsx +218 -0
  44. package/src/components/resources/renderers/GenericRenderer.tsx +476 -0
  45. package/src/components/resources/renderers/GitRepositoryRenderer.tsx +154 -0
  46. package/src/components/resources/renderers/HPARenderer.tsx +164 -0
  47. package/src/components/resources/renderers/HTTPRouteRenderer.tsx +303 -0
  48. package/src/components/resources/renderers/HelmRepositoryRenderer.tsx +167 -0
  49. package/src/components/resources/renderers/IngressClassRenderer.tsx +49 -0
  50. package/src/components/resources/renderers/IngressRenderer.tsx +104 -0
  51. package/src/components/resources/renderers/IstioAuthorizationPolicyRenderer.tsx +175 -0
  52. package/src/components/resources/renderers/IstioDestinationRuleRenderer.tsx +176 -0
  53. package/src/components/resources/renderers/IstioGatewayRenderer.tsx +125 -0
  54. package/src/components/resources/renderers/IstioPeerAuthenticationRenderer.tsx +84 -0
  55. package/src/components/resources/renderers/IstioServiceEntryRenderer.tsx +112 -0
  56. package/src/components/resources/renderers/IstioVirtualServiceRenderer.tsx +288 -0
  57. package/src/components/resources/renderers/JobRenderer.tsx +103 -0
  58. package/src/components/resources/renderers/KarpenterEC2NodeClassRenderer.tsx +157 -0
  59. package/src/components/resources/renderers/KarpenterNodeClaimRenderer.tsx +188 -0
  60. package/src/components/resources/renderers/KarpenterNodePoolRenderer.tsx +148 -0
  61. package/src/components/resources/renderers/KedaScaledJobRenderer.tsx +90 -0
  62. package/src/components/resources/renderers/KedaScaledObjectRenderer.tsx +136 -0
  63. package/src/components/resources/renderers/KedaTriggerAuthRenderer.tsx +151 -0
  64. package/src/components/resources/renderers/KnativeConfigurationRenderer.tsx +70 -0
  65. package/src/components/resources/renderers/KnativeEventingRenderer.tsx +309 -0
  66. package/src/components/resources/renderers/KnativeFlowRenderer.tsx +142 -0
  67. package/src/components/resources/renderers/KnativeNetworkingRenderer.tsx +232 -0
  68. package/src/components/resources/renderers/KnativeRevisionRenderer.tsx +97 -0
  69. package/src/components/resources/renderers/KnativeRouteRenderer.tsx +74 -0
  70. package/src/components/resources/renderers/KnativeServiceRenderer.tsx +138 -0
  71. package/src/components/resources/renderers/KnativeSourceRenderer.tsx +220 -0
  72. package/src/components/resources/renderers/KustomizationRenderer.tsx +151 -0
  73. package/src/components/resources/renderers/KyvernoPolicyReportRenderer.tsx +352 -0
  74. package/src/components/resources/renderers/LeaseRenderer.tsx +93 -0
  75. package/src/components/resources/renderers/NetworkPolicyRenderer.tsx +208 -0
  76. package/src/components/resources/renderers/NodeRenderer.tsx +282 -0
  77. package/src/components/resources/renderers/OCIRepositoryRenderer.tsx +153 -0
  78. package/src/components/resources/renderers/OrderRenderer.tsx +108 -0
  79. package/src/components/resources/renderers/PVCRenderer.tsx +98 -0
  80. package/src/components/resources/renderers/PersistentVolumeRenderer.tsx +138 -0
  81. package/src/components/resources/renderers/PodDisruptionBudgetRenderer.tsx +126 -0
  82. package/src/components/resources/renderers/PodMonitorRenderer.tsx +63 -0
  83. package/src/components/resources/renderers/PodRenderer.tsx +677 -0
  84. package/src/components/resources/renderers/PriorityClassRenderer.tsx +50 -0
  85. package/src/components/resources/renderers/PrometheusRuleRenderer.tsx +62 -0
  86. package/src/components/resources/renderers/ReplicaSetRenderer.tsx +71 -0
  87. package/src/components/resources/renderers/RoleBindingRenderer.tsx +97 -0
  88. package/src/components/resources/renderers/RoleRenderer.tsx +192 -0
  89. package/src/components/resources/renderers/RolloutRenderer.tsx +227 -0
  90. package/src/components/resources/renderers/RuntimeClassRenderer.tsx +75 -0
  91. package/src/components/resources/renderers/SbomReportRenderer.tsx +98 -0
  92. package/src/components/resources/renderers/SealedSecretRenderer.tsx +121 -0
  93. package/src/components/resources/renderers/SecretRenderer.tsx +321 -0
  94. package/src/components/resources/renderers/SecretStoreRenderer.tsx +111 -0
  95. package/src/components/resources/renderers/ServiceAccountRenderer.tsx +81 -0
  96. package/src/components/resources/renderers/ServiceMonitorRenderer.tsx +65 -0
  97. package/src/components/resources/renderers/ServiceRenderer.tsx +98 -0
  98. package/src/components/resources/renderers/SimpleRouteRenderer.tsx +195 -0
  99. package/src/components/resources/renderers/StorageClassRenderer.tsx +82 -0
  100. package/src/components/resources/renderers/TraefikIngressRouteRenderer.tsx +257 -0
  101. package/src/components/resources/renderers/VPARenderer.tsx +193 -0
  102. package/src/components/resources/renderers/VeleroBSLRenderer.tsx +73 -0
  103. package/src/components/resources/renderers/VeleroBackupRenderer.tsx +193 -0
  104. package/src/components/resources/renderers/VeleroRestoreRenderer.tsx +161 -0
  105. package/src/components/resources/renderers/VeleroScheduleRenderer.tsx +134 -0
  106. package/src/components/resources/renderers/VeleroVSLRenderer.tsx +36 -0
  107. package/src/components/resources/renderers/VulnerabilityReportRenderer.tsx +347 -0
  108. package/src/components/resources/renderers/WebhookConfigRenderer.tsx +156 -0
  109. package/src/components/resources/renderers/WorkflowRenderer.tsx +202 -0
  110. package/src/components/resources/renderers/WorkflowTemplateRenderer.tsx +91 -0
  111. package/src/components/resources/renderers/WorkloadRenderer.tsx +311 -0
  112. package/src/components/resources/renderers/argo-cells.tsx +118 -0
  113. package/src/components/resources/renderers/certmanager-cells.tsx +164 -0
  114. package/src/components/resources/renderers/cnpg-cells.tsx +161 -0
  115. package/src/components/resources/renderers/eso-cells.tsx +115 -0
  116. package/src/components/resources/renderers/flux-cells.tsx +253 -0
  117. package/src/components/resources/renderers/index.ts +105 -0
  118. package/src/components/resources/renderers/istio-cells.tsx +210 -0
  119. package/src/components/resources/renderers/karpenter-cells.tsx +107 -0
  120. package/src/components/resources/renderers/keda-cells.tsx +94 -0
  121. package/src/components/resources/renderers/knative-cells.tsx +396 -0
  122. package/src/components/resources/renderers/kyverno-cells.tsx +85 -0
  123. package/src/components/resources/renderers/prometheus-cells.tsx +74 -0
  124. package/src/components/resources/renderers/traefik-cells.tsx +120 -0
  125. package/src/components/resources/renderers/trivy-cells.tsx +153 -0
  126. package/src/components/resources/renderers/trivy-shared.tsx +67 -0
  127. package/src/components/resources/renderers/velero-cells.tsx +164 -0
  128. package/src/components/resources/resource-utils-argo.ts +155 -0
  129. package/src/components/resources/resource-utils-certmanager.ts +198 -0
  130. package/src/components/resources/resource-utils-cnpg.ts +344 -0
  131. package/src/components/resources/resource-utils-eso.ts +385 -0
  132. package/src/components/resources/resource-utils-flux.ts +186 -0
  133. package/src/components/resources/resource-utils-istio.ts +372 -0
  134. package/src/components/resources/resource-utils-karpenter.ts +150 -0
  135. package/src/components/resources/resource-utils-keda.ts +203 -0
  136. package/src/components/resources/resource-utils-knative.ts +218 -0
  137. package/src/components/resources/resource-utils-kyverno.ts +224 -0
  138. package/src/components/resources/resource-utils-prometheus.ts +159 -0
  139. package/src/components/resources/resource-utils-traefik.ts +180 -0
  140. package/src/components/resources/resource-utils-trivy.ts +146 -0
  141. package/src/components/resources/resource-utils-velero.ts +293 -0
  142. package/src/components/resources/resource-utils.ts +1558 -0
  143. package/src/components/shared/EditableYamlView.tsx +302 -0
  144. package/src/components/shared/ResourceActionsBar.tsx +924 -0
  145. package/src/components/shared/ResourceRendererDispatch.tsx +557 -0
  146. package/src/components/shared/index.ts +3 -0
  147. package/src/components/timeline/index.ts +1 -0
  148. package/src/components/timeline/shared.tsx +614 -0
  149. package/src/components/topology/GroupNode.tsx +198 -0
  150. package/src/components/topology/K8sResourceNode.tsx +440 -0
  151. package/src/components/topology/TopologyControls.tsx +62 -0
  152. package/src/components/topology/TopologyFilterSidebar.tsx +377 -0
  153. package/src/components/topology/TopologyGraph.tsx +748 -0
  154. package/src/components/topology/TopologySearch.tsx +304 -0
  155. package/src/components/topology/index.ts +7 -0
  156. package/src/components/topology/layout.ts +734 -0
  157. package/src/components/topology/layout.worker.ts +272 -0
  158. package/src/components/topology/topology.css +129 -0
  159. package/src/components/ui/CodeViewer.tsx +451 -0
  160. package/src/components/ui/ConfirmDialog.tsx +188 -0
  161. package/src/components/ui/ForceDeleteConfirmDialog.tsx +57 -0
  162. package/src/components/ui/HealthRing.tsx +72 -0
  163. package/src/components/ui/MetricsChart.tsx +259 -0
  164. package/src/components/ui/ResourceBar.tsx +68 -0
  165. package/src/components/ui/Toast.tsx +225 -0
  166. package/src/components/ui/Tooltip.tsx +165 -0
  167. package/src/components/ui/YamlEditor.tsx +280 -0
  168. package/src/components/ui/drawer-components.tsx +792 -0
  169. package/src/components/ui/index.ts +10 -0
  170. package/src/components/workload/ResourceDetailDrawer.tsx +158 -0
  171. package/src/components/workload/WorkloadView.tsx +1109 -0
  172. package/src/components/workload/index.ts +2 -0
  173. package/src/hooks/index.ts +9 -0
  174. package/src/hooks/useAnimatedUnmount.ts +30 -0
  175. package/src/hooks/useKeyboardShortcuts.tsx +336 -0
  176. package/src/hooks/useRefreshAnimation.ts +58 -0
  177. package/src/index.ts +35 -0
  178. package/src/types/core.ts +894 -0
  179. package/src/types/gitops.ts +458 -0
  180. package/src/types/index.ts +2 -0
  181. package/src/utils/animation.ts +40 -0
  182. package/src/utils/api-resources.ts +185 -0
  183. package/src/utils/badge-colors.ts +399 -0
  184. package/src/utils/context-name.ts +72 -0
  185. package/src/utils/download.ts +19 -0
  186. package/src/utils/format.ts +233 -0
  187. package/src/utils/index.ts +10 -0
  188. package/src/utils/log-format.ts +300 -0
  189. package/src/utils/navigation.ts +119 -0
  190. package/src/utils/resource-hierarchy.ts +630 -0
  191. package/src/utils/resource-icons.ts +206 -0
  192. package/src/vite-env.d.ts +1 -0
  193. package/tsconfig.json +24 -0
@@ -0,0 +1,227 @@
1
+ import { Server, GitBranch, AlertTriangle } from 'lucide-react'
2
+ import { clsx } from 'clsx'
3
+ import { Section, PropertyList, Property, ConditionsSection, PodTemplateSection } from '../../ui/drawer-components'
4
+
5
+ interface RolloutRendererProps {
6
+ data: any
7
+ }
8
+
9
+ export function RolloutRenderer({ data }: RolloutRendererProps) {
10
+ const status = data.status || {}
11
+ const spec = data.spec || {}
12
+ const phase = status.phase || 'Unknown'
13
+ const conditions = status.conditions || []
14
+
15
+ const canaryStrategy = spec.strategy?.canary
16
+ const blueGreenStrategy = spec.strategy?.blueGreen
17
+ const isCanary = !!canaryStrategy
18
+ const steps = canaryStrategy?.steps || []
19
+ const currentStepIndex = status.currentStepIndex
20
+
21
+ // Problem detection
22
+ const problems: Array<{ color: 'red' | 'yellow'; message: string }> = []
23
+
24
+ if (phase === 'Degraded') {
25
+ problems.push({ color: 'red', message: status.message || 'Rollout is degraded' })
26
+ }
27
+
28
+ const progressDeadlineCond = conditions.find(
29
+ (c: any) => c.type === 'Progressing' && c.status === 'False' && c.reason === 'ProgressDeadlineExceeded'
30
+ )
31
+ if (progressDeadlineCond) {
32
+ problems.push({ color: 'red', message: progressDeadlineCond.message || 'Progress deadline exceeded' })
33
+ }
34
+
35
+ const invalidSpecCond = conditions.find(
36
+ (c: any) => c.type === 'InvalidSpec' && c.status === 'True'
37
+ )
38
+ if (invalidSpecCond) {
39
+ problems.push({ color: 'red', message: invalidSpecCond.message || 'Invalid rollout spec' })
40
+ }
41
+
42
+ if (phase === 'Paused') {
43
+ problems.push({ color: 'yellow', message: 'Rollout is paused' })
44
+ }
45
+
46
+ // Phase badge color
47
+ const phaseColor = (() => {
48
+ switch (phase) {
49
+ case 'Healthy': return 'status-healthy'
50
+ case 'Paused': return 'status-degraded'
51
+ case 'Progressing': return 'status-degraded'
52
+ case 'Degraded': return 'status-unhealthy'
53
+ default: return 'status-unknown'
54
+ }
55
+ })()
56
+
57
+ return (
58
+ <>
59
+ {/* Problem alerts */}
60
+ {problems.map((problem, i) => (
61
+ <div
62
+ key={i}
63
+ className={clsx(
64
+ 'mb-4 p-3 border rounded-lg',
65
+ problem.color === 'red'
66
+ ? 'bg-red-500/10 border-red-500/30'
67
+ : 'bg-yellow-500/10 border-yellow-500/30'
68
+ )}
69
+ >
70
+ <div className="flex items-start gap-2">
71
+ <AlertTriangle
72
+ className={clsx(
73
+ 'w-4 h-4 mt-0.5 shrink-0',
74
+ problem.color === 'red' ? 'text-red-400' : 'text-yellow-400'
75
+ )}
76
+ />
77
+ <div className="flex-1 min-w-0">
78
+ <div
79
+ className={clsx(
80
+ 'text-sm font-medium',
81
+ problem.color === 'red' ? 'text-red-400' : 'text-yellow-400'
82
+ )}
83
+ >
84
+ {problem.color === 'red' ? 'Issue Detected' : 'Warning'}
85
+ </div>
86
+ <div
87
+ className={clsx(
88
+ 'text-xs mt-1',
89
+ problem.color === 'red' ? 'text-red-300/80' : 'text-yellow-300/80'
90
+ )}
91
+ >
92
+ {problem.message}
93
+ </div>
94
+ </div>
95
+ </div>
96
+ </div>
97
+ ))}
98
+
99
+ {/* Status section */}
100
+ <Section title="Status" icon={Server}>
101
+ <PropertyList>
102
+ <Property
103
+ label="Phase"
104
+ value={
105
+ <span className={clsx('px-2 py-0.5 rounded text-xs font-medium', phaseColor)}>
106
+ {phase}
107
+ </span>
108
+ }
109
+ />
110
+ <Property label="Replicas" value={`${status.availableReplicas || 0}/${spec.replicas || 0}`} />
111
+ <Property label="Updated" value={status.updatedReplicas} />
112
+ <Property label="Current Pod Hash" value={status.currentPodHash} />
113
+ <Property label="Stable RS" value={status.stableRS} />
114
+ <Property label="Message" value={status.message} />
115
+ </PropertyList>
116
+ </Section>
117
+
118
+ {/* Strategy section */}
119
+ <Section title="Strategy" icon={GitBranch}>
120
+ <PropertyList>
121
+ {isCanary ? (
122
+ <>
123
+ <Property label="Strategy" value="Canary" />
124
+ {steps.length > 0 && currentStepIndex !== undefined && (
125
+ <Property label="Current Step" value={`${currentStepIndex}/${steps.length}`} />
126
+ )}
127
+ {status.canary?.weights?.canary?.weight !== undefined && (
128
+ <Property label="Canary Weight" value={`${status.canary.weights.canary.weight}%`} />
129
+ )}
130
+ {status.canary?.weights?.stable?.weight !== undefined && (
131
+ <Property label="Stable Weight" value={`${status.canary.weights.stable.weight}%`} />
132
+ )}
133
+ <Property label="Canary Service" value={canaryStrategy.canaryService} />
134
+ <Property label="Stable Service" value={canaryStrategy.stableService} />
135
+ </>
136
+ ) : blueGreenStrategy ? (
137
+ <>
138
+ <Property label="Strategy" value="Blue-Green" />
139
+ <Property label="Active Service" value={blueGreenStrategy.activeService} />
140
+ <Property label="Preview Service" value={blueGreenStrategy.previewService} />
141
+ <Property
142
+ label="Auto Promote"
143
+ value={
144
+ blueGreenStrategy.autoPromotionEnabled !== undefined
145
+ ? String(blueGreenStrategy.autoPromotionEnabled)
146
+ : undefined
147
+ }
148
+ />
149
+ </>
150
+ ) : (
151
+ <Property label="Strategy" value="Unknown" />
152
+ )}
153
+ </PropertyList>
154
+ </Section>
155
+
156
+ {/* Canary Steps visual */}
157
+ {isCanary && steps.length > 0 && (
158
+ <Section title={`Canary Steps (${steps.length})`} defaultExpanded>
159
+ <div className="space-y-1">
160
+ {steps.map((step: any, index: number) => {
161
+ const isCompleted = currentStepIndex !== undefined && index < currentStepIndex
162
+ const isCurrent = currentStepIndex !== undefined && index === currentStepIndex
163
+ const isPending = currentStepIndex === undefined || index > currentStepIndex
164
+
165
+ const stepLabel = (() => {
166
+ if (step.setWeight !== undefined) return `Set Weight: ${step.setWeight}%`
167
+ if (step.pause !== undefined) {
168
+ if (step.pause.duration) return `Pause: ${step.pause.duration}`
169
+ return 'Pause: manual'
170
+ }
171
+ // Handle other step types generically
172
+ const key = Object.keys(step)[0]
173
+ if (key) return `${key}: ${JSON.stringify(step[key])}`
174
+ return 'Unknown step'
175
+ })()
176
+
177
+ return (
178
+ <div
179
+ key={index}
180
+ className={clsx(
181
+ 'flex items-center gap-2 px-2 py-1.5 rounded text-sm',
182
+ isCurrent && 'bg-blue-500/10 border border-blue-500/30',
183
+ isCompleted && 'opacity-80',
184
+ isPending && 'opacity-50'
185
+ )}
186
+ >
187
+ {/* Status indicator */}
188
+ <span
189
+ className={clsx(
190
+ 'w-5 h-5 rounded-full flex items-center justify-center text-xs shrink-0',
191
+ isCompleted && 'bg-green-500/20 text-green-400',
192
+ isCurrent && 'bg-blue-500/20 text-blue-400',
193
+ isPending && 'bg-gray-500/20 text-gray-400'
194
+ )}
195
+ >
196
+ {isCompleted ? '\u2713' : isCurrent ? '\u25CF' : '\u25CB'}
197
+ </span>
198
+
199
+ {/* Step index */}
200
+ <span className="text-theme-text-tertiary text-xs w-4 shrink-0">{index}</span>
201
+
202
+ {/* Step label */}
203
+ <span
204
+ className={clsx(
205
+ 'text-sm',
206
+ isCurrent ? 'text-theme-text-primary font-medium' : 'text-theme-text-secondary'
207
+ )}
208
+ >
209
+ {stepLabel}
210
+ </span>
211
+ </div>
212
+ )
213
+ })}
214
+ </div>
215
+ </Section>
216
+ )}
217
+
218
+ {/* Pod Template section (collapsed by default) */}
219
+ <Section title="Pod Template" defaultExpanded={false}>
220
+ <PodTemplateSection template={spec.template} />
221
+ </Section>
222
+
223
+ {/* Conditions section */}
224
+ <ConditionsSection conditions={status.conditions} />
225
+ </>
226
+ )
227
+ }
@@ -0,0 +1,75 @@
1
+ import { Cpu } from 'lucide-react'
2
+ import { Section, PropertyList, Property } from '../../ui/drawer-components'
3
+
4
+ interface RuntimeClassRendererProps {
5
+ data: any
6
+ }
7
+
8
+ export function RuntimeClassRenderer({ data }: RuntimeClassRendererProps) {
9
+ const overhead = data.overhead?.podFixed
10
+ const hasOverhead = overhead && Object.keys(overhead).length > 0
11
+ const scheduling = data.scheduling
12
+ const nodeSelector = scheduling?.nodeSelector
13
+ const hasNodeSelector = nodeSelector && Object.keys(nodeSelector).length > 0
14
+ const tolerations = scheduling?.tolerations
15
+ const hasTolerations = tolerations && tolerations.length > 0
16
+
17
+ return (
18
+ <>
19
+ <Section title="Runtime" icon={Cpu}>
20
+ <PropertyList>
21
+ <Property label="Handler" value={data.handler} />
22
+ </PropertyList>
23
+ </Section>
24
+
25
+ {hasOverhead && (
26
+ <Section title="Overhead">
27
+ <PropertyList>
28
+ {Object.entries(overhead).map(([resource, quantity]) => (
29
+ <Property key={resource} label={resource} value={quantity as string} />
30
+ ))}
31
+ </PropertyList>
32
+ </Section>
33
+ )}
34
+
35
+ {(hasNodeSelector || hasTolerations) && (
36
+ <Section title="Scheduling">
37
+ <div className="space-y-3">
38
+ {hasNodeSelector && (
39
+ <div>
40
+ <div className="text-xs text-theme-text-tertiary mb-1">Node Selector</div>
41
+ <div className="flex flex-wrap gap-1">
42
+ {Object.entries(nodeSelector).map(([key, value]) => (
43
+ <span
44
+ key={key}
45
+ className="px-2 py-0.5 rounded text-xs bg-blue-500/20 text-blue-400"
46
+ >
47
+ {key}={value as string}
48
+ </span>
49
+ ))}
50
+ </div>
51
+ </div>
52
+ )}
53
+ {hasTolerations && (
54
+ <div>
55
+ <div className="text-xs text-theme-text-tertiary mb-1">Tolerations</div>
56
+ <div className="space-y-1.5">
57
+ {tolerations.map((t: any, i: number) => {
58
+ const parts = [t.key, t.operator, t.value].filter(Boolean).join(' ')
59
+ const effect = t.effect ? `: ${t.effect}` : ''
60
+ const seconds = t.tolerationSeconds != null ? ` (${t.tolerationSeconds}s)` : ''
61
+ return (
62
+ <div key={i} className="bg-theme-elevated/30 rounded p-2 text-xs text-theme-text-secondary">
63
+ {parts}{effect}{seconds}
64
+ </div>
65
+ )
66
+ })}
67
+ </div>
68
+ </div>
69
+ )}
70
+ </div>
71
+ </Section>
72
+ )}
73
+ </>
74
+ )
75
+ }
@@ -0,0 +1,98 @@
1
+ import { useState } from 'react'
2
+ import { Package, ChevronDown, ChevronRight } from 'lucide-react'
3
+ import { Section, PropertyList, Property } from '../../ui/drawer-components'
4
+ import { formatAge } from '../resource-utils'
5
+ import { formatTrivyImage } from './trivy-shared'
6
+
7
+ interface SbomReportRendererProps {
8
+ data: any
9
+ }
10
+
11
+ const INITIAL_SHOW_COUNT = 100
12
+
13
+ export function SbomReportRenderer({ data }: SbomReportRendererProps) {
14
+ const [showAll, setShowAll] = useState(false)
15
+ const [expanded, setExpanded] = useState(true)
16
+
17
+ const report = data.report || {}
18
+ const scanner = report.scanner || {}
19
+ const labels = data.metadata?.labels || {}
20
+ const components = report.components || {}
21
+ const bom = components.components || []
22
+ const summary = report.summary || {}
23
+
24
+ const containerName = labels['trivy-operator.container.name'] || '-'
25
+ const image = formatTrivyImage(report)
26
+
27
+ const componentsCount = summary.componentsCount || bom.length
28
+ const depsCount = summary.dependenciesCount || 0
29
+ const bomFormat = components.bomFormat || '-'
30
+ const specVersion = components.specVersion || '-'
31
+
32
+ const displayedComponents = showAll ? bom : bom.slice(0, INITIAL_SHOW_COUNT)
33
+
34
+ return (
35
+ <>
36
+ {/* Report Overview */}
37
+ <Section title="Report Overview" icon={Package}>
38
+ <PropertyList>
39
+ {containerName !== '-' && <Property label="Container" value={containerName} />}
40
+ <Property label="Image" value={image} />
41
+ <Property label="Format" value={`${bomFormat} ${specVersion}`} />
42
+ <Property label="Components" value={String(componentsCount)} />
43
+ {depsCount > 0 && <Property label="Dependencies" value={String(depsCount)} />}
44
+ <Property label="Scanner" value={scanner.name ? `${scanner.name} ${scanner.version || ''}`.trim() : '-'} />
45
+ <Property label="Scanned" value={report.updateTimestamp ? formatAge(report.updateTimestamp) + ' ago' : '-'} />
46
+ </PropertyList>
47
+ </Section>
48
+
49
+ {/* Components Table */}
50
+ {bom.length > 0 && (
51
+ <Section title="Components">
52
+ <button
53
+ onClick={() => setExpanded(!expanded)}
54
+ className="flex items-center gap-1 text-xs text-theme-text-secondary hover:text-theme-text-primary mb-2"
55
+ >
56
+ {expanded ? <ChevronDown className="w-3.5 h-3.5" /> : <ChevronRight className="w-3.5 h-3.5" />}
57
+ {bom.length} components
58
+ </button>
59
+ {expanded && (
60
+ <div className="overflow-x-auto -mx-1">
61
+ <table className="w-full text-xs">
62
+ <thead>
63
+ <tr className="border-b border-theme-border text-theme-text-tertiary">
64
+ <th className="text-left py-1.5 px-1 font-medium">Name</th>
65
+ <th className="text-left py-1.5 px-1 font-medium">Version</th>
66
+ <th className="text-left py-1.5 px-1 font-medium">Type</th>
67
+ <th className="text-left py-1.5 px-1 font-medium">License</th>
68
+ </tr>
69
+ </thead>
70
+ <tbody>
71
+ {displayedComponents.map((comp: any, i: number) => {
72
+ const license = comp.licenses?.[0]?.license?.id || comp.licenses?.[0]?.license?.name || '-'
73
+ return (
74
+ <tr key={`${comp.name}-${comp.version}-${i}`} className="border-b border-theme-border/50 hover:bg-theme-hover/50">
75
+ <td className="py-1.5 px-1 text-theme-text-secondary max-w-[200px] truncate" title={comp.purl || comp.name}>{comp.name || '-'}</td>
76
+ <td className="py-1.5 px-1 text-theme-text-secondary font-mono">{comp.version || '-'}</td>
77
+ <td className="py-1.5 px-1 text-theme-text-tertiary">{comp.type || '-'}</td>
78
+ <td className="py-1.5 px-1 text-theme-text-tertiary max-w-[120px] truncate" title={license}>{license}</td>
79
+ </tr>
80
+ )
81
+ })}
82
+ </tbody>
83
+ </table>
84
+ {!showAll && bom.length > INITIAL_SHOW_COUNT && (
85
+ <button
86
+ onClick={() => setShowAll(true)}
87
+ className="mt-2 text-xs text-blue-400 hover:text-blue-300 hover:underline"
88
+ >
89
+ Show all {bom.length} components
90
+ </button>
91
+ )}
92
+ </div>
93
+ )}
94
+ </Section>
95
+ )}
96
+ </>
97
+ )
98
+ }
@@ -0,0 +1,121 @@
1
+ import { Lock, Key, FileText } from 'lucide-react'
2
+ import { clsx } from 'clsx'
3
+ import { Section, PropertyList, Property, ConditionsSection, KeyValueBadgeList, AlertBanner } from '../../ui/drawer-components'
4
+
5
+ interface SealedSecretRendererProps {
6
+ data: any
7
+ }
8
+
9
+ function getScope(annotations: Record<string, string> | undefined): string {
10
+ if (!annotations) return 'strict'
11
+ if (annotations['sealedsecrets.bitnami.com/cluster-wide'] === 'true') return 'cluster-wide'
12
+ if (annotations['sealedsecrets.bitnami.com/namespace-wide'] === 'true') return 'namespace-wide'
13
+ return 'strict'
14
+ }
15
+
16
+ export function SealedSecretRenderer({ data }: SealedSecretRendererProps) {
17
+ const spec = data.spec || {}
18
+ const status = data.status || {}
19
+ const conditions = status.conditions || []
20
+ const annotations = data.metadata?.annotations || {}
21
+
22
+ const encryptedData = spec.encryptedData || {}
23
+ const encryptedKeys = Object.keys(encryptedData)
24
+ const template = spec.template || {}
25
+ const templateMetadata = template.metadata || {}
26
+ const templateLabels = templateMetadata.labels
27
+ const templateAnnotations = templateMetadata.annotations
28
+ const secretType = template.type || 'Opaque'
29
+ const scope = getScope(annotations)
30
+
31
+ const syncedCond = conditions.find((c: any) => c.type === 'Synced')
32
+ const isSynced = syncedCond?.status === 'True'
33
+ const isNotSynced = syncedCond?.status === 'False'
34
+
35
+ const hasTemplateMetadata =
36
+ (templateLabels && Object.keys(templateLabels).length > 0) ||
37
+ (templateAnnotations && Object.keys(templateAnnotations).length > 0)
38
+
39
+ return (
40
+ <>
41
+ {/* Problem detection alert */}
42
+ {isNotSynced && (
43
+ <AlertBanner
44
+ variant="error"
45
+ title="Secret is not synced"
46
+ message={<>{syncedCond.reason && <span className="font-medium">{syncedCond.reason}: </span>}{syncedCond.message || 'The sealed secret failed to unseal and sync to a Secret.'}</>}
47
+ />
48
+ )}
49
+
50
+ {/* Status */}
51
+ <Section title="Status" icon={Lock}>
52
+ <PropertyList>
53
+ <Property
54
+ label="Synced"
55
+ value={
56
+ <span className={clsx(
57
+ 'px-2 py-0.5 rounded text-xs font-medium',
58
+ isSynced
59
+ ? 'bg-green-500/20 text-green-400'
60
+ : isNotSynced
61
+ ? 'bg-red-500/20 text-red-400'
62
+ : 'bg-yellow-500/20 text-yellow-400'
63
+ )}>
64
+ {isSynced ? 'Synced' : isNotSynced ? 'Not Synced' : 'Unknown'}
65
+ </span>
66
+ }
67
+ />
68
+ <Property label="Secret Type" value={secretType} />
69
+ <Property label="Scope" value={scope} />
70
+ <Property label="Observed Gen" value={status.observedGeneration} />
71
+ </PropertyList>
72
+ </Section>
73
+
74
+ {/* Encrypted Keys */}
75
+ <Section title={`Encrypted Keys (${encryptedKeys.length})`} icon={Key}>
76
+ {encryptedKeys.length > 0 ? (
77
+ <div className="flex flex-wrap gap-1">
78
+ {encryptedKeys.map((key) => (
79
+ <span
80
+ key={key}
81
+ className="px-2 py-0.5 bg-theme-elevated rounded text-xs text-theme-text-secondary"
82
+ >
83
+ {key}
84
+ </span>
85
+ ))}
86
+ </div>
87
+ ) : (
88
+ <div className="text-sm text-theme-text-tertiary">No encrypted keys</div>
89
+ )}
90
+ </Section>
91
+
92
+ {/* Template Metadata */}
93
+ {hasTemplateMetadata && (
94
+ <Section title="Template Metadata" icon={FileText}>
95
+ {templateLabels && Object.keys(templateLabels).length > 0 && (
96
+ <div className="mb-3">
97
+ <div className="text-xs text-theme-text-tertiary mb-1">Labels</div>
98
+ <KeyValueBadgeList items={templateLabels} />
99
+ </div>
100
+ )}
101
+ {templateAnnotations && Object.keys(templateAnnotations).length > 0 && (
102
+ <div>
103
+ <div className="text-xs text-theme-text-tertiary mb-1">Annotations</div>
104
+ <div className="space-y-1 max-h-48 overflow-y-auto">
105
+ {Object.entries(templateAnnotations).map(([k, v]) => (
106
+ <div key={k} className="text-xs">
107
+ <span className="text-theme-text-tertiary">{k}:</span>
108
+ <span className="text-theme-text-secondary ml-1 break-all">{v as string}</span>
109
+ </div>
110
+ ))}
111
+ </div>
112
+ </div>
113
+ )}
114
+ </Section>
115
+ )}
116
+
117
+ {/* Conditions */}
118
+ <ConditionsSection conditions={conditions} />
119
+ </>
120
+ )
121
+ }