@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,136 @@
1
+ import { Cpu } from 'lucide-react'
2
+ import { Section, PropertyList, Property, ConditionsSection, AlertBanner, ResourceLink } from '../../ui/drawer-components'
3
+ import {
4
+ getScaledObjectStatus,
5
+ getScaledObjectTarget,
6
+ getScaledObjectReplicas,
7
+ getScaledObjectTriggers,
8
+ getScaledObjectHpaName,
9
+ getScaledObjectLastActiveTime,
10
+ getScaledObjectPollingInterval,
11
+ getScaledObjectCooldownPeriod,
12
+ } from '../resource-utils-keda'
13
+
14
+ interface KedaScaledObjectRendererProps {
15
+ data: any
16
+ onNavigate?: (ref: { kind: string; namespace: string; name: string }) => void
17
+ }
18
+
19
+ export function KedaScaledObjectRenderer({ data, onNavigate }: KedaScaledObjectRendererProps) {
20
+ const status = data.status || {}
21
+ const conditions = status.conditions || []
22
+
23
+ const soStatus = getScaledObjectStatus(data)
24
+ const triggers = getScaledObjectTriggers(data)
25
+ const fallback = data.spec?.fallback
26
+
27
+ // Problem detection
28
+ const isPaused = soStatus.text === 'Paused'
29
+ const isFallback = soStatus.text === 'Fallback'
30
+ const isNotReady = soStatus.level === 'unhealthy'
31
+
32
+ const readyCond = conditions.find((c: any) => c.type === 'Ready')
33
+ const fallbackCond = conditions.find((c: any) => c.type === 'Fallback')
34
+
35
+ return (
36
+ <>
37
+ {/* Problem alerts */}
38
+ {isFallback && (
39
+ <AlertBanner
40
+ variant="error"
41
+ title="Fallback Active"
42
+ message={fallbackCond?.message || 'KEDA is using fallback replicas because triggers are failing.'}
43
+ />
44
+ )}
45
+ {isNotReady && !isFallback && (
46
+ <AlertBanner
47
+ variant="error"
48
+ title="ScaledObject Not Ready"
49
+ message={readyCond?.message || 'The ScaledObject is not in a ready state.'}
50
+ />
51
+ )}
52
+ {isPaused && (
53
+ <AlertBanner
54
+ variant="warning"
55
+ title="Scaling Paused"
56
+ message="Autoscaling is paused via annotation."
57
+ />
58
+ )}
59
+
60
+ {/* Scaling section */}
61
+ <Section title="Scaling" icon={Cpu}>
62
+ <PropertyList>
63
+ <Property label="Target" value={(() => {
64
+ const target = data.spec?.scaleTargetRef
65
+ if (target?.name) {
66
+ return (
67
+ <ResourceLink
68
+ name={target.name}
69
+ kind={(target.kind || 'Deployment').toLowerCase() + 's'}
70
+ namespace={data.metadata?.namespace || ''}
71
+ label={getScaledObjectTarget(data)}
72
+ onNavigate={onNavigate}
73
+ />
74
+ )
75
+ }
76
+ return getScaledObjectTarget(data)
77
+ })()} />
78
+ <Property label="Replicas" value={getScaledObjectReplicas(data)} />
79
+ <Property label="Polling Interval" value={`${getScaledObjectPollingInterval(data)}s`} />
80
+ <Property label="Cooldown Period" value={`${getScaledObjectCooldownPeriod(data)}s`} />
81
+ <Property label="Generated HPA" value={(() => {
82
+ const hpaName = getScaledObjectHpaName(data)
83
+ if (hpaName && hpaName !== '-') {
84
+ return <ResourceLink name={hpaName} kind="horizontalpodautoscalers" namespace={data.metadata?.namespace || ''} onNavigate={onNavigate} />
85
+ }
86
+ return hpaName
87
+ })()} />
88
+ <Property label="Last Active" value={getScaledObjectLastActiveTime(data)} />
89
+ </PropertyList>
90
+ {fallback && (
91
+ <div className="mt-2 pt-2 border-t border-theme-border">
92
+ <div className="text-xs font-medium text-theme-text-secondary uppercase tracking-wider mb-1">Fallback</div>
93
+ <PropertyList>
94
+ <Property label="Failure Threshold" value={String(fallback.failureThreshold ?? '-')} />
95
+ <Property label="Replicas" value={String(fallback.replicas ?? '-')} />
96
+ </PropertyList>
97
+ </div>
98
+ )}
99
+ </Section>
100
+
101
+ {/* Triggers section */}
102
+ {triggers.length > 0 && (
103
+ <Section title={`Triggers (${triggers.length})`} defaultExpanded>
104
+ <div className="space-y-2">
105
+ {triggers.map((trigger, i) => (
106
+ <div key={i} className="bg-theme-elevated/30 rounded p-2">
107
+ <div className="flex items-center gap-2 text-sm">
108
+ <span className="text-theme-text-primary font-medium">{trigger.type}</span>
109
+ {trigger.name && (
110
+ <span className="text-theme-text-tertiary">({trigger.name})</span>
111
+ )}
112
+ {trigger.authenticationRef && (
113
+ <span className="px-1.5 py-0.5 bg-theme-hover rounded text-xs text-theme-text-secondary">
114
+ auth: {trigger.authenticationRef.name}
115
+ </span>
116
+ )}
117
+ </div>
118
+ {trigger.metadata && Object.keys(trigger.metadata).length > 0 && (
119
+ <div className="mt-1 flex flex-wrap gap-1">
120
+ {Object.entries(trigger.metadata).map(([k, v]) => (
121
+ <span key={k} className="px-1.5 py-0.5 bg-theme-hover rounded text-xs text-theme-text-secondary">
122
+ {k}: {v}
123
+ </span>
124
+ ))}
125
+ </div>
126
+ )}
127
+ </div>
128
+ ))}
129
+ </div>
130
+ </Section>
131
+ )}
132
+
133
+ <ConditionsSection conditions={conditions} />
134
+ </>
135
+ )
136
+ }
@@ -0,0 +1,151 @@
1
+ import { KeyRound, Server, Cloud, Shield } from 'lucide-react'
2
+ import { Section, PropertyList, Property, ConditionsSection, ResourceLink } from '../../ui/drawer-components'
3
+ import {
4
+ getTriggerAuthSecretRefs,
5
+ getTriggerAuthEnvVars,
6
+ } from '../resource-utils-keda'
7
+
8
+ interface KedaTriggerAuthRendererProps {
9
+ data: any
10
+ onNavigate?: (ref: { kind: string; namespace: string; name: string }) => void
11
+ }
12
+
13
+ export function KedaTriggerAuthRenderer({ data, onNavigate }: KedaTriggerAuthRendererProps) {
14
+ const spec = data.spec || {}
15
+ const conditions = data.status?.conditions || []
16
+
17
+ const secretRefs = getTriggerAuthSecretRefs(data)
18
+ const envVars = getTriggerAuthEnvVars(data)
19
+ const vault = spec.hashiCorpVault
20
+ const azureKeyVault = spec.azureKeyVault
21
+ const awsSecretManager = spec.awsSecretManager
22
+ const podIdentity = spec.podIdentity
23
+
24
+ return (
25
+ <>
26
+ {/* Pod Identity */}
27
+ {podIdentity && (
28
+ <Section title="Pod Identity" icon={Shield}>
29
+ <PropertyList>
30
+ <Property label="Provider" value={podIdentity.provider || '-'} />
31
+ {podIdentity.identityId && (
32
+ <Property label="Identity ID" value={podIdentity.identityId} />
33
+ )}
34
+ {podIdentity.identityOwner && (
35
+ <Property label="Identity Owner" value={podIdentity.identityOwner} />
36
+ )}
37
+ </PropertyList>
38
+ </Section>
39
+ )}
40
+
41
+ {/* Secret References */}
42
+ {secretRefs.length > 0 && (
43
+ <Section title={`Secret References (${secretRefs.length})`} icon={KeyRound} defaultExpanded>
44
+ <div className="space-y-2">
45
+ {secretRefs.map((ref, i) => (
46
+ <div key={i} className="bg-theme-elevated/30 rounded p-2">
47
+ <div className="flex flex-wrap gap-x-4 gap-y-1 text-sm">
48
+ <span className="text-theme-text-primary font-medium">{ref.parameter}</span>
49
+ <span className="text-theme-text-secondary">
50
+ Secret: <ResourceLink name={ref.name} kind="secrets" namespace={data.metadata?.namespace || ''} onNavigate={onNavigate} /> / {ref.key}
51
+ </span>
52
+ </div>
53
+ </div>
54
+ ))}
55
+ </div>
56
+ </Section>
57
+ )}
58
+
59
+ {/* Environment Variables */}
60
+ {envVars.length > 0 && (
61
+ <Section title={`Environment Variables (${envVars.length})`} defaultExpanded>
62
+ <div className="space-y-2">
63
+ {envVars.map((env, i) => (
64
+ <div key={i} className="bg-theme-elevated/30 rounded p-2">
65
+ <div className="flex flex-wrap gap-x-4 gap-y-1 text-sm">
66
+ <span className="text-theme-text-primary font-medium">{env.parameter}</span>
67
+ <span className="text-theme-text-secondary">Env: {env.name}</span>
68
+ {env.containerName && (
69
+ <span className="text-theme-text-tertiary">Container: {env.containerName}</span>
70
+ )}
71
+ </div>
72
+ </div>
73
+ ))}
74
+ </div>
75
+ </Section>
76
+ )}
77
+
78
+ {/* HashiCorp Vault */}
79
+ {vault && (
80
+ <Section title="HashiCorp Vault" icon={Server}>
81
+ <PropertyList>
82
+ <Property label="Address" value={vault.address || '-'} />
83
+ {vault.authentication && (
84
+ <Property label="Authentication" value={vault.authentication} />
85
+ )}
86
+ {vault.role && <Property label="Role" value={vault.role} />}
87
+ {vault.mount && <Property label="Mount" value={vault.mount} />}
88
+ </PropertyList>
89
+ {vault.secrets && vault.secrets.length > 0 && (
90
+ <div className="mt-2 space-y-1">
91
+ <div className="text-xs font-medium text-theme-text-secondary uppercase tracking-wider mb-1">Secrets</div>
92
+ {vault.secrets.map((s: any, i: number) => (
93
+ <div key={i} className="bg-theme-elevated/30 rounded p-2 text-sm">
94
+ <span className="text-theme-text-primary font-medium">{s.parameter}</span>
95
+ <span className="text-theme-text-secondary ml-2">{s.path} / {s.key}</span>
96
+ </div>
97
+ ))}
98
+ </div>
99
+ )}
100
+ </Section>
101
+ )}
102
+
103
+ {/* Azure Key Vault */}
104
+ {azureKeyVault && (
105
+ <Section title="Azure Key Vault" icon={Cloud}>
106
+ <PropertyList>
107
+ <Property label="Vault URI" value={azureKeyVault.vaultUri || '-'} />
108
+ {azureKeyVault.credentials?.clientId && (
109
+ <Property label="Client ID" value={azureKeyVault.credentials.clientId} />
110
+ )}
111
+ </PropertyList>
112
+ {azureKeyVault.secrets && azureKeyVault.secrets.length > 0 && (
113
+ <div className="mt-2 space-y-1">
114
+ <div className="text-xs font-medium text-theme-text-secondary uppercase tracking-wider mb-1">Secrets</div>
115
+ {azureKeyVault.secrets.map((s: any, i: number) => (
116
+ <div key={i} className="bg-theme-elevated/30 rounded p-2 text-sm">
117
+ <span className="text-theme-text-primary font-medium">{s.parameter}</span>
118
+ <span className="text-theme-text-secondary ml-2">{s.name}</span>
119
+ </div>
120
+ ))}
121
+ </div>
122
+ )}
123
+ </Section>
124
+ )}
125
+
126
+ {/* AWS Secrets Manager */}
127
+ {awsSecretManager && (
128
+ <Section title="AWS Secrets Manager" icon={Cloud}>
129
+ <PropertyList>
130
+ {awsSecretManager.region && (
131
+ <Property label="Region" value={awsSecretManager.region} />
132
+ )}
133
+ </PropertyList>
134
+ {awsSecretManager.secrets && awsSecretManager.secrets.length > 0 && (
135
+ <div className="mt-2 space-y-1">
136
+ <div className="text-xs font-medium text-theme-text-secondary uppercase tracking-wider mb-1">Secrets</div>
137
+ {awsSecretManager.secrets.map((s: any, i: number) => (
138
+ <div key={i} className="bg-theme-elevated/30 rounded p-2 text-sm">
139
+ <span className="text-theme-text-primary font-medium">{s.parameter}</span>
140
+ <span className="text-theme-text-secondary ml-2">{s.name}</span>
141
+ </div>
142
+ ))}
143
+ </div>
144
+ )}
145
+ </Section>
146
+ )}
147
+
148
+ {conditions.length > 0 && <ConditionsSection conditions={conditions} />}
149
+ </>
150
+ )
151
+ }
@@ -0,0 +1,70 @@
1
+ import { Settings, Container } from 'lucide-react'
2
+ import { clsx } from 'clsx'
3
+ import { Section, PropertyList, Property, ConditionsSection, KnativeNotReadyBanner, ResourceLink } from '../../ui/drawer-components'
4
+ import { getKnativeConditionStatus } from '../resource-utils-knative'
5
+
6
+ interface KnativeConfigurationRendererProps {
7
+ data: any
8
+ onNavigate?: (ref: { kind: string; namespace: string; name: string }) => void
9
+ }
10
+
11
+ export function KnativeConfigurationRenderer({ data, onNavigate }: KnativeConfigurationRendererProps) {
12
+ const status = getKnativeConditionStatus(data)
13
+ const ns = data.metadata?.namespace || ''
14
+
15
+ const latestCreated = data.status?.latestCreatedRevisionName
16
+ const latestReady = data.status?.latestReadyRevisionName
17
+ const containers = data.spec?.template?.spec?.containers || []
18
+
19
+ return (
20
+ <>
21
+ <KnativeNotReadyBanner status={status} data={data} resourceType="Configuration" />
22
+
23
+ <Section title="Overview" icon={Settings} defaultExpanded>
24
+ <PropertyList>
25
+ <Property label="Status" value={
26
+ <span className={clsx('px-2 py-0.5 rounded text-xs font-medium', status.color)}>
27
+ {status.text}
28
+ </span>
29
+ } />
30
+ <Property label="Latest Created" value={latestCreated ? (
31
+ <ResourceLink
32
+ name={latestCreated}
33
+ kind="revisions"
34
+ namespace={ns}
35
+ onNavigate={onNavigate}
36
+ />
37
+ ) : '-'} />
38
+ <Property label="Latest Ready" value={latestReady ? (
39
+ <ResourceLink
40
+ name={latestReady}
41
+ kind="revisions"
42
+ namespace={ns}
43
+ onNavigate={onNavigate}
44
+ />
45
+ ) : '-'} />
46
+ </PropertyList>
47
+ </Section>
48
+
49
+ {containers.length > 0 && (
50
+ <Section title="Template" icon={Container} defaultExpanded>
51
+ <div className="space-y-2">
52
+ {containers.map((c: any, i: number) => (
53
+ <div key={i} className="bg-theme-elevated/30 rounded p-2 text-sm">
54
+ <div className="font-medium text-theme-text-primary">{c.name || 'container'}</div>
55
+ <div className="text-xs text-theme-text-secondary truncate" title={c.image}>{c.image}</div>
56
+ {c.ports && c.ports.length > 0 && (
57
+ <div className="text-xs text-theme-text-tertiary mt-1">
58
+ Ports: {c.ports.map((p: any) => `${p.containerPort}/${p.protocol || 'TCP'}`).join(', ')}
59
+ </div>
60
+ )}
61
+ </div>
62
+ ))}
63
+ </div>
64
+ </Section>
65
+ )}
66
+
67
+ <ConditionsSection conditions={data.status?.conditions || []} />
68
+ </>
69
+ )
70
+ }
@@ -0,0 +1,309 @@
1
+ import { Radio, Filter, FileType, Inbox, ArrowRightLeft } from 'lucide-react'
2
+ import { clsx } from 'clsx'
3
+ import { Section, PropertyList, Property, ConditionsSection, KnativeNotReadyBanner, ResourceLink } from '../../ui/drawer-components'
4
+ import { getKnativeConditionStatus } from '../resource-utils-knative'
5
+
6
+ interface RendererProps {
7
+ data: any
8
+ onNavigate?: (ref: { kind: string; namespace: string; name: string }) => void
9
+ }
10
+
11
+ // ============================================================================
12
+ // Broker
13
+ // ============================================================================
14
+
15
+ export function BrokerRenderer({ data, onNavigate }: RendererProps) {
16
+ const status = getKnativeConditionStatus(data)
17
+ const ns = data.metadata?.namespace || ''
18
+ const address = data.status?.address?.url
19
+ const delivery = data.spec?.delivery
20
+ const brokerClass = data.metadata?.annotations?.['eventing.knative.dev/broker.class']
21
+ const configRef = data.spec?.config
22
+
23
+ return (
24
+ <>
25
+ <KnativeNotReadyBanner status={status} data={data} resourceType="Broker" />
26
+
27
+ <Section title="Overview" icon={Radio} defaultExpanded>
28
+ <PropertyList>
29
+ <Property label="Status" value={
30
+ <span className={clsx('px-2 py-0.5 rounded text-xs font-medium', status.color)}>
31
+ {status.text}
32
+ </span>
33
+ } />
34
+ <Property label="Address" value={address ? (
35
+ <span className="text-theme-text-primary break-all">{address}</span>
36
+ ) : undefined} />
37
+ <Property label="Class" value={brokerClass} />
38
+ {configRef && (
39
+ <Property label="Config" value={`${configRef.kind}/${configRef.name}`} />
40
+ )}
41
+ </PropertyList>
42
+ </Section>
43
+
44
+ {delivery && (
45
+ <Section title="Delivery" defaultExpanded>
46
+ <PropertyList>
47
+ {delivery.deadLetterSink?.ref && (
48
+ <Property label="Dead Letter" value={
49
+ <ResourceLink
50
+ name={delivery.deadLetterSink.ref.name}
51
+ kind={delivery.deadLetterSink.ref.kind?.toLowerCase() + 's'}
52
+ namespace={delivery.deadLetterSink.ref.namespace || ns}
53
+ onNavigate={onNavigate}
54
+ />
55
+ } />
56
+ )}
57
+ {delivery.deadLetterSink?.uri && (
58
+ <Property label="Dead Letter URI" value={delivery.deadLetterSink.uri} />
59
+ )}
60
+ <Property label="Retry" value={delivery.retry != null ? String(delivery.retry) : undefined} />
61
+ <Property label="Backoff Policy" value={delivery.backoffPolicy} />
62
+ <Property label="Backoff Delay" value={delivery.backoffDelay} />
63
+ </PropertyList>
64
+ </Section>
65
+ )}
66
+
67
+ <ConditionsSection conditions={data.status?.conditions || []} />
68
+ </>
69
+ )
70
+ }
71
+
72
+ // ============================================================================
73
+ // Trigger
74
+ // ============================================================================
75
+
76
+ export function TriggerRenderer({ data, onNavigate }: RendererProps) {
77
+ const status = getKnativeConditionStatus(data)
78
+ const ns = data.metadata?.namespace || ''
79
+ const spec = data.spec || {}
80
+ const brokerName = spec.broker || 'default'
81
+ const subscriberRef = spec.subscriber?.ref
82
+ const subscriberUri = spec.subscriber?.uri
83
+ const filterAttrs = spec.filter?.attributes || {}
84
+ const filterEntries = Object.entries(filterAttrs)
85
+
86
+ return (
87
+ <>
88
+ <KnativeNotReadyBanner status={status} data={data} resourceType="Trigger" />
89
+
90
+ <Section title="Overview" icon={Filter} defaultExpanded>
91
+ <PropertyList>
92
+ <Property label="Status" value={
93
+ <span className={clsx('px-2 py-0.5 rounded text-xs font-medium', status.color)}>
94
+ {status.text}
95
+ </span>
96
+ } />
97
+ <Property label="Broker" value={
98
+ <ResourceLink
99
+ name={brokerName}
100
+ kind="brokers"
101
+ namespace={ns}
102
+ onNavigate={onNavigate}
103
+ />
104
+ } />
105
+ <Property label="Subscriber" value={
106
+ subscriberRef ? (
107
+ <ResourceLink
108
+ name={subscriberRef.name}
109
+ kind={(subscriberRef.kind?.toLowerCase() || 'services') + (subscriberRef.kind?.toLowerCase().endsWith('s') ? '' : 's')}
110
+ namespace={subscriberRef.namespace || ns}
111
+ onNavigate={onNavigate}
112
+ />
113
+ ) : subscriberUri ? (
114
+ <span className="text-theme-text-primary break-all">{subscriberUri}</span>
115
+ ) : '-'
116
+ } />
117
+ </PropertyList>
118
+ </Section>
119
+
120
+ {filterEntries.length > 0 && (
121
+ <Section title={`Filter (${filterEntries.length} attributes)`} defaultExpanded>
122
+ <div className="flex flex-wrap gap-1">
123
+ {filterEntries.map(([key, val]) => (
124
+ <span key={key} className="px-2 py-0.5 bg-theme-elevated rounded text-xs text-theme-text-secondary">
125
+ {key}={String(val)}
126
+ </span>
127
+ ))}
128
+ </div>
129
+ </Section>
130
+ )}
131
+
132
+ <ConditionsSection conditions={data.status?.conditions || []} />
133
+ </>
134
+ )
135
+ }
136
+
137
+ // ============================================================================
138
+ // EventType
139
+ // ============================================================================
140
+
141
+ export function EventTypeRenderer({ data }: RendererProps) {
142
+ const spec = data.spec || {}
143
+ const status = data.status || {}
144
+
145
+ return (
146
+ <>
147
+ <Section title="Overview" icon={FileType} defaultExpanded>
148
+ <PropertyList>
149
+ <Property label="Type" value={spec.type} />
150
+ <Property label="Source" value={spec.source} />
151
+ <Property label="Schema" value={spec.schema} />
152
+ <Property label="Description" value={spec.description} />
153
+ <Property label="Broker" value={spec.reference?.name || spec.broker} />
154
+ </PropertyList>
155
+ </Section>
156
+
157
+ <ConditionsSection conditions={status.conditions || []} />
158
+ </>
159
+ )
160
+ }
161
+
162
+ // ============================================================================
163
+ // Channel
164
+ // ============================================================================
165
+
166
+ export function ChannelRenderer({ data }: RendererProps) {
167
+ const status = getKnativeConditionStatus(data)
168
+ const address = data.status?.address?.url
169
+ const subscribers = data.status?.subscribers || []
170
+
171
+ return (
172
+ <>
173
+ <KnativeNotReadyBanner status={status} data={data} resourceType="Channel" />
174
+
175
+ <Section title="Overview" icon={Inbox} defaultExpanded>
176
+ <PropertyList>
177
+ <Property label="Status" value={
178
+ <span className={clsx('px-2 py-0.5 rounded text-xs font-medium', status.color)}>
179
+ {status.text}
180
+ </span>
181
+ } />
182
+ <Property label="Address" value={address ? (
183
+ <span className="text-theme-text-primary break-all">{address}</span>
184
+ ) : undefined} />
185
+ </PropertyList>
186
+ </Section>
187
+
188
+ {subscribers.length > 0 && (
189
+ <Section title={`Subscribers (${subscribers.length})`} defaultExpanded>
190
+ <div className="space-y-1.5">
191
+ {subscribers.map((sub: any, i: number) => (
192
+ <div key={i} className="text-sm text-theme-text-secondary">
193
+ {sub.subscriberURI || sub.replyURI || `Subscriber ${i + 1}`}
194
+ {sub.ready === 'True' && (
195
+ <span className="ml-2 px-1.5 py-0.5 bg-green-500/20 text-green-400 rounded text-[10px]">ready</span>
196
+ )}
197
+ {sub.ready === 'False' && (
198
+ <span className="ml-2 px-1.5 py-0.5 bg-red-500/20 text-red-400 rounded text-[10px]">not ready</span>
199
+ )}
200
+ </div>
201
+ ))}
202
+ </div>
203
+ </Section>
204
+ )}
205
+
206
+ <ConditionsSection conditions={data.status?.conditions || []} />
207
+ </>
208
+ )
209
+ }
210
+
211
+ // ============================================================================
212
+ // InMemoryChannel
213
+ // ============================================================================
214
+
215
+ export function InMemoryChannelRenderer({ data, onNavigate }: RendererProps) {
216
+ // InMemoryChannel has the same shape as Channel
217
+ return <ChannelRenderer data={data} onNavigate={onNavigate} />
218
+ }
219
+
220
+ // ============================================================================
221
+ // Subscription
222
+ // ============================================================================
223
+
224
+ export function SubscriptionRenderer({ data, onNavigate }: RendererProps) {
225
+ const status = getKnativeConditionStatus(data)
226
+ const ns = data.metadata?.namespace || ''
227
+ const spec = data.spec || {}
228
+
229
+ const channelRef = spec.channel
230
+ const subscriberRef = spec.subscriber?.ref
231
+ const subscriberUri = spec.subscriber?.uri
232
+ const replyRef = spec.reply?.ref
233
+ const replyUri = spec.reply?.uri
234
+ const delivery = spec.delivery
235
+
236
+ return (
237
+ <>
238
+ <KnativeNotReadyBanner status={status} data={data} resourceType="Subscription" />
239
+
240
+ <Section title="Overview" icon={ArrowRightLeft} defaultExpanded>
241
+ <PropertyList>
242
+ <Property label="Status" value={
243
+ <span className={clsx('px-2 py-0.5 rounded text-xs font-medium', status.color)}>
244
+ {status.text}
245
+ </span>
246
+ } />
247
+ {channelRef && (
248
+ <Property label="Channel" value={
249
+ <ResourceLink
250
+ name={channelRef.name}
251
+ kind={(channelRef.kind?.toLowerCase() || 'channels') + (channelRef.kind?.toLowerCase().endsWith('s') ? '' : 's')}
252
+ namespace={ns}
253
+ onNavigate={onNavigate}
254
+ />
255
+ } />
256
+ )}
257
+ <Property label="Subscriber" value={
258
+ subscriberRef ? (
259
+ <ResourceLink
260
+ name={subscriberRef.name}
261
+ kind={(subscriberRef.kind?.toLowerCase() || 'services') + (subscriberRef.kind?.toLowerCase().endsWith('s') ? '' : 's')}
262
+ namespace={subscriberRef.namespace || ns}
263
+ onNavigate={onNavigate}
264
+ />
265
+ ) : subscriberUri ? (
266
+ <span className="text-theme-text-primary break-all">{subscriberUri}</span>
267
+ ) : '-'
268
+ } />
269
+ <Property label="Reply" value={
270
+ replyRef ? (
271
+ <ResourceLink
272
+ name={replyRef.name}
273
+ kind={(replyRef.kind?.toLowerCase() || 'channels') + (replyRef.kind?.toLowerCase().endsWith('s') ? '' : 's')}
274
+ namespace={replyRef.namespace || ns}
275
+ onNavigate={onNavigate}
276
+ />
277
+ ) : replyUri ? (
278
+ <span className="text-theme-text-primary break-all">{replyUri}</span>
279
+ ) : undefined
280
+ } />
281
+ </PropertyList>
282
+ </Section>
283
+
284
+ {delivery?.deadLetterSink && (
285
+ <Section title="Dead Letter" defaultExpanded>
286
+ <PropertyList>
287
+ {delivery.deadLetterSink.ref && (
288
+ <Property label="Sink" value={
289
+ <ResourceLink
290
+ name={delivery.deadLetterSink.ref.name}
291
+ kind={(delivery.deadLetterSink.ref.kind?.toLowerCase() || 'services') + 's'}
292
+ namespace={delivery.deadLetterSink.ref.namespace || ns}
293
+ onNavigate={onNavigate}
294
+ />
295
+ } />
296
+ )}
297
+ {delivery.deadLetterSink.uri && (
298
+ <Property label="URI" value={delivery.deadLetterSink.uri} />
299
+ )}
300
+ <Property label="Retry" value={delivery.retry != null ? String(delivery.retry) : undefined} />
301
+ <Property label="Backoff Policy" value={delivery.backoffPolicy} />
302
+ </PropertyList>
303
+ </Section>
304
+ )}
305
+
306
+ <ConditionsSection conditions={data.status?.conditions || []} />
307
+ </>
308
+ )
309
+ }