@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,195 @@
1
+ import { Globe, ArrowRight, Network } from 'lucide-react'
2
+ import { clsx } from 'clsx'
3
+ import { Section, PropertyList, Property, ConditionsSection, AlertBanner, ResourceRefBadge } from '../../ui/drawer-components'
4
+ import type { ResourceRef } from '../../../types'
5
+
6
+ interface SimpleRouteRendererProps {
7
+ data: any
8
+ kind: 'TCPRoute' | 'TLSRoute'
9
+ onNavigate?: (ref: ResourceRef) => void
10
+ }
11
+
12
+ export function SimpleRouteRenderer({ data, kind, onNavigate }: SimpleRouteRendererProps) {
13
+ const spec = data.spec || {}
14
+ const status = data.status || {}
15
+ const parentRefs = spec.parentRefs || []
16
+ const hostnames = spec.hostnames || []
17
+ const rules = spec.rules || []
18
+ const parentStatuses = status.parents || []
19
+ const routeNs = data.metadata?.namespace || ''
20
+
21
+ const isTLS = kind === 'TLSRoute'
22
+
23
+ const notAcceptedParents = parentStatuses.filter((p: any) =>
24
+ (p.conditions || []).some((c: any) => c.type === 'Accepted' && c.status === 'False')
25
+ )
26
+ const unresolvedRefsParents = parentStatuses.filter((p: any) =>
27
+ (p.conditions || []).some((c: any) => c.type === 'ResolvedRefs' && c.status === 'False')
28
+ )
29
+
30
+ const firstParentConditions = parentStatuses.length > 0
31
+ ? parentStatuses[0].conditions
32
+ : undefined
33
+
34
+ function toGatewayRef(ref: any): ResourceRef {
35
+ return {
36
+ kind: 'Gateway',
37
+ namespace: ref.namespace || routeNs,
38
+ name: ref.name,
39
+ group: 'gateway.networking.k8s.io',
40
+ }
41
+ }
42
+
43
+ function toServiceRef(backend: any): ResourceRef {
44
+ return {
45
+ kind: 'Service',
46
+ namespace: backend.namespace || routeNs,
47
+ name: backend.name,
48
+ }
49
+ }
50
+
51
+ return (
52
+ <>
53
+ {notAcceptedParents.length > 0 && (
54
+ <AlertBanner
55
+ variant="error"
56
+ title="Route Not Accepted"
57
+ message={notAcceptedParents.map((p: any) => {
58
+ const cond = (p.conditions || []).find((c: any) => c.type === 'Accepted' && c.status === 'False')
59
+ const gwName = p.parentRef?.name || 'unknown'
60
+ return cond?.reason
61
+ ? `Gateway "${gwName}": ${cond.reason}${cond.message ? ' — ' + cond.message : ''}`
62
+ : `Gateway "${gwName}" has not accepted this route.`
63
+ }).join('; ')}
64
+ />
65
+ )}
66
+
67
+ {unresolvedRefsParents.length > 0 && (
68
+ <AlertBanner
69
+ variant="warning"
70
+ title="Unresolved References"
71
+ message="Some backend references could not be resolved. Check that the target services exist and are accessible."
72
+ />
73
+ )}
74
+
75
+ <Section title="Status" icon={Globe}>
76
+ <PropertyList>
77
+ {isTLS && (
78
+ <Property
79
+ label="SNI Hostnames"
80
+ value={
81
+ hostnames.length > 0 ? (
82
+ <div className="flex flex-wrap gap-1">
83
+ {hostnames.map((h: string) => (
84
+ <span key={h} className="px-2 py-0.5 bg-theme-elevated rounded text-xs text-theme-text-secondary">{h}</span>
85
+ ))}
86
+ </div>
87
+ ) : (
88
+ <span className="text-xs text-theme-text-tertiary">Any</span>
89
+ )
90
+ }
91
+ />
92
+ )}
93
+ <Property
94
+ label="Parent Gateways"
95
+ value={
96
+ parentRefs.length > 0 ? (
97
+ <div className="flex flex-wrap gap-1">
98
+ {parentRefs.map((ref: any, i: number) => (
99
+ <ResourceRefBadge key={`${ref.namespace || ''}-${ref.name}-${i}`} resourceRef={toGatewayRef(ref)} onClick={onNavigate} />
100
+ ))}
101
+ </div>
102
+ ) : 'None'
103
+ }
104
+ />
105
+ </PropertyList>
106
+ </Section>
107
+
108
+ <Section title={`Rules (${rules.length})`} icon={Network} defaultExpanded>
109
+ <div className="space-y-1.5">
110
+ {rules.map((rule: any, ruleIdx: number) => {
111
+ const backendRefs = rule.backendRefs || []
112
+ const hasWeights = backendRefs.length > 1 && backendRefs.some((b: any) => b.weight !== undefined)
113
+ const totalWeight = hasWeights ? backendRefs.reduce((sum: number, b: any) => sum + (b.weight ?? 1), 0) : 0
114
+
115
+ return (
116
+ <div key={ruleIdx} className="bg-theme-elevated/30 rounded p-2.5">
117
+ {rules.length > 1 && (
118
+ <div className="text-xs font-medium text-theme-text-tertiary mb-1.5">
119
+ Rule {ruleIdx + 1}
120
+ </div>
121
+ )}
122
+
123
+ {backendRefs.length > 0 ? (
124
+ <div className="text-xs text-theme-text-secondary flex flex-wrap items-center gap-1.5">
125
+ <ArrowRight className="w-3 h-3 text-theme-text-tertiary shrink-0" />
126
+ {backendRefs.map((b: any, bi: number) => {
127
+ const pct = hasWeights ? Math.round(((b.weight ?? 1) / totalWeight) * 100) : null
128
+ return (
129
+ <span key={bi} className="flex items-center gap-1">
130
+ <ResourceRefBadge resourceRef={toServiceRef(b)} onClick={onNavigate} />
131
+ {b.port && <span className="text-theme-text-tertiary">:{b.port}</span>}
132
+ {pct !== null && (
133
+ <span className="text-theme-text-tertiary text-[10px]">{pct}%</span>
134
+ )}
135
+ </span>
136
+ )
137
+ })}
138
+ </div>
139
+ ) : (
140
+ <div className="text-xs text-theme-text-tertiary italic">No backends</div>
141
+ )}
142
+ </div>
143
+ )
144
+ })}
145
+ </div>
146
+ </Section>
147
+
148
+ {/* Parent Status */}
149
+ {parentStatuses.length > 0 && (
150
+ <Section title={`Parent Status (${parentStatuses.length})`} defaultExpanded>
151
+ <div className="space-y-2">
152
+ {parentStatuses.map((parent: any, idx: number) => {
153
+ const ref = parent.parentRef || {}
154
+ const conditions = parent.conditions || []
155
+ const accepted = conditions.find((c: any) => c.type === 'Accepted')
156
+ const resolved = conditions.find((c: any) => c.type === 'ResolvedRefs')
157
+
158
+ return (
159
+ <div key={`${ref.namespace || ''}-${ref.name}-${idx}`} className="bg-theme-elevated/30 rounded p-2.5">
160
+ <div className="text-sm font-medium text-theme-text-primary mb-1.5">
161
+ {ref.namespace ? `${ref.namespace}/` : ''}{ref.name || 'unknown'}
162
+ {ref.sectionName ? ` (${ref.sectionName})` : ''}
163
+ </div>
164
+ <div className="flex flex-wrap gap-2">
165
+ {accepted && (
166
+ <span className={clsx(
167
+ 'px-2 py-0.5 rounded text-xs font-medium',
168
+ accepted.status === 'True' ? 'bg-green-500/20 text-green-400' : 'bg-red-500/20 text-red-400'
169
+ )}>
170
+ {accepted.status === 'True' ? 'Accepted' : 'Not Accepted'}
171
+ </span>
172
+ )}
173
+ {resolved && (
174
+ <span className={clsx(
175
+ 'px-2 py-0.5 rounded text-xs font-medium',
176
+ resolved.status === 'True' ? 'bg-green-500/20 text-green-400' : 'bg-yellow-500/20 text-yellow-400'
177
+ )}>
178
+ {resolved.status === 'True' ? 'Refs Resolved' : 'Unresolved Refs'}
179
+ </span>
180
+ )}
181
+ </div>
182
+ {accepted?.message && accepted.status === 'False' && (
183
+ <div className="text-xs text-theme-text-tertiary mt-1">{accepted.message}</div>
184
+ )}
185
+ </div>
186
+ )
187
+ })}
188
+ </div>
189
+ </Section>
190
+ )}
191
+
192
+ <ConditionsSection conditions={firstParentConditions} />
193
+ </>
194
+ )
195
+ }
@@ -0,0 +1,82 @@
1
+ import { Database, Settings } from 'lucide-react'
2
+ import { Section, PropertyList, Property } from '../../ui/drawer-components'
3
+
4
+ interface StorageClassRendererProps {
5
+ data: any
6
+ }
7
+
8
+ export function StorageClassRenderer({ data }: StorageClassRendererProps) {
9
+ const parameters = data.parameters || {}
10
+ const mountOptions = data.mountOptions || []
11
+ const hasParameters = Object.keys(parameters).length > 0
12
+ const hasMountOptions = mountOptions.length > 0
13
+
14
+ return (
15
+ <>
16
+ <Section title="Configuration" icon={Database}>
17
+ <PropertyList>
18
+ <Property label="Provisioner" value={data.provisioner} />
19
+ <Property
20
+ label="Reclaim Policy"
21
+ value={
22
+ data.reclaimPolicy ? (
23
+ <span className={data.reclaimPolicy === 'Delete' ? 'text-red-400' : 'text-green-400'}>
24
+ {data.reclaimPolicy}
25
+ </span>
26
+ ) : undefined
27
+ }
28
+ />
29
+ <Property
30
+ label="Volume Binding Mode"
31
+ value={
32
+ data.volumeBindingMode ? (
33
+ <span title={
34
+ data.volumeBindingMode === 'WaitForFirstConsumer'
35
+ ? 'Volume binding is delayed until a Pod using this PVC is created'
36
+ : undefined
37
+ }>
38
+ {data.volumeBindingMode}
39
+ </span>
40
+ ) : undefined
41
+ }
42
+ />
43
+ <Property
44
+ label="Allow Volume Expansion"
45
+ value={
46
+ data.allowVolumeExpansion != null ? (
47
+ <span className={data.allowVolumeExpansion ? 'text-green-400' : undefined}>
48
+ {data.allowVolumeExpansion ? 'Yes' : 'No'}
49
+ </span>
50
+ ) : undefined
51
+ }
52
+ />
53
+ </PropertyList>
54
+ </Section>
55
+
56
+ {hasParameters && (
57
+ <Section title="Parameters" icon={Settings}>
58
+ <PropertyList>
59
+ {Object.entries(parameters).map(([key, value]) => (
60
+ <Property key={key} label={key} value={value as string} />
61
+ ))}
62
+ </PropertyList>
63
+ </Section>
64
+ )}
65
+
66
+ {hasMountOptions && (
67
+ <Section title="Mount Options">
68
+ <div className="flex flex-wrap gap-1.5">
69
+ {mountOptions.map((option: string) => (
70
+ <span
71
+ key={option}
72
+ className="px-2 py-0.5 rounded text-xs bg-blue-500/20 text-blue-400"
73
+ >
74
+ {option}
75
+ </span>
76
+ ))}
77
+ </div>
78
+ </Section>
79
+ )}
80
+ </>
81
+ )
82
+ }
@@ -0,0 +1,257 @@
1
+ import { Globe, Route, Layers, Lock } from 'lucide-react'
2
+ import { Section, PropertyList, Property, AlertBanner, ResourceLink } from '../../ui/drawer-components'
3
+
4
+ interface TraefikIngressRouteRendererProps {
5
+ data: any
6
+ onNavigate?: (ref: { kind: string; namespace: string; name: string }) => void
7
+ }
8
+
9
+ export function TraefikIngressRouteRenderer({ data, onNavigate }: TraefikIngressRouteRendererProps) {
10
+ const spec = data.spec || {}
11
+ const routes = spec.routes || []
12
+ const entryPoints = spec.entryPoints || []
13
+ const tls = spec.tls
14
+ const ns = data.metadata?.namespace || ''
15
+
16
+ // Collect unique services and middleware across all routes
17
+ const allServices: Array<{ name: string; kind: string; port?: number; namespace?: string; serversTransport?: string }> = []
18
+ const allMiddlewares: Array<{ name: string; namespace?: string }> = []
19
+ const seenSvc = new Set<string>()
20
+ const seenMw = new Set<string>()
21
+
22
+ for (const route of routes) {
23
+ for (const svc of route.services || []) {
24
+ const key = `${svc.kind || 'Service'}/${svc.namespace || ''}/${svc.name}`
25
+ if (!seenSvc.has(key)) {
26
+ seenSvc.add(key)
27
+ allServices.push(svc)
28
+ }
29
+ }
30
+ for (const mw of route.middlewares || []) {
31
+ const key = `${mw.namespace || ''}/${mw.name}`
32
+ if (!seenMw.has(key)) {
33
+ seenMw.add(key)
34
+ allMiddlewares.push(mw)
35
+ }
36
+ }
37
+ }
38
+
39
+ const hasNoRoutes = routes.length === 0
40
+ const hasNoServices = allServices.length === 0 && routes.length > 0
41
+
42
+ // Determine kind label from apiVersion/kind
43
+ const kindLabel = data.kind || 'IngressRoute'
44
+
45
+ return (
46
+ <>
47
+ {hasNoRoutes && (
48
+ <AlertBanner
49
+ variant="warning"
50
+ title="No Routes Defined"
51
+ message={`This ${kindLabel} has no routes configured. No traffic will be routed.`}
52
+ />
53
+ )}
54
+
55
+ {hasNoServices && (
56
+ <AlertBanner
57
+ variant="info"
58
+ title="No Backend Services"
59
+ message="Routes are defined but none reference backend services."
60
+ />
61
+ )}
62
+
63
+ <Section title={kindLabel} icon={Globe} defaultExpanded>
64
+ <PropertyList>
65
+ <Property label="Entry Points" value={entryPoints.length > 0 ? entryPoints.join(', ') : '-'} />
66
+ <Property label="Routes" value={`${routes.length}`} />
67
+ <Property label="Services" value={`${allServices.length}`} />
68
+ <Property label="Middlewares" value={allMiddlewares.length > 0 ? `${allMiddlewares.length}` : 'None'} />
69
+ <Property label="TLS" value={tls ? 'Enabled' : 'None'} />
70
+ </PropertyList>
71
+ </Section>
72
+
73
+ <Section title={`Routes (${routes.length})`} icon={Route} defaultExpanded>
74
+ <div className="space-y-3">
75
+ {routes.map((route: any, i: number) => {
76
+ const services = route.services || []
77
+ const middlewares = route.middlewares || []
78
+
79
+ return (
80
+ <div key={i} className="bg-theme-elevated/30 rounded p-3">
81
+ {/* Match expression */}
82
+ <div className="flex items-start gap-2 mb-2">
83
+ <span className="text-sm font-medium text-theme-text-primary break-all">
84
+ {route.match || 'No match rule'}
85
+ </span>
86
+ {route.priority && (
87
+ <span className="px-1.5 py-0.5 bg-theme-hover rounded text-[10px] text-theme-text-secondary shrink-0">
88
+ priority: {route.priority}
89
+ </span>
90
+ )}
91
+ {route.kind && route.kind !== 'Rule' && (
92
+ <span className="px-1.5 py-0.5 bg-theme-hover rounded text-[10px] text-theme-text-secondary shrink-0">
93
+ {route.kind}
94
+ </span>
95
+ )}
96
+ </div>
97
+
98
+ {/* Services */}
99
+ {services.length > 0 && (
100
+ <div className="mb-2">
101
+ <div className="text-[10px] font-medium text-theme-text-tertiary uppercase tracking-wider mb-1">Services</div>
102
+ <div className="space-y-1">
103
+ {services.map((svc: any, si: number) => {
104
+ const svcKind = svc.kind || 'Service'
105
+ const svcNs = svc.namespace || ns
106
+ const isTraefikSvc = svcKind === 'TraefikService'
107
+ const port = svc.port ? `:${svc.port}` : ''
108
+ const weight = svc.weight !== undefined ? ` (${svc.weight}%)` : ''
109
+
110
+ return (
111
+ <div key={si} className="flex items-center gap-2 text-xs">
112
+ {isTraefikSvc && (
113
+ <span className="px-1.5 py-0.5 bg-cyan-500/10 text-cyan-400 rounded text-[10px]">
114
+ TraefikService
115
+ </span>
116
+ )}
117
+ <ResourceLink
118
+ name={svc.name}
119
+ kind={isTraefikSvc ? 'traefikservices' : 'services'}
120
+ namespace={svcNs}
121
+ label={<span className="text-blue-400">{svc.name}{port}{weight}</span>}
122
+ onNavigate={onNavigate}
123
+ />
124
+ {svc.serversTransport && (
125
+ <span className="text-theme-text-tertiary">
126
+ transport: <ResourceLink
127
+ name={svc.serversTransport}
128
+ kind="serverstransports"
129
+ namespace={ns}
130
+ onNavigate={onNavigate}
131
+ />
132
+ </span>
133
+ )}
134
+ </div>
135
+ )
136
+ })}
137
+ </div>
138
+ </div>
139
+ )}
140
+
141
+ {/* Middlewares */}
142
+ {middlewares.length > 0 && (
143
+ <div>
144
+ <div className="text-[10px] font-medium text-theme-text-tertiary uppercase tracking-wider mb-1">Middlewares</div>
145
+ <div className="flex flex-wrap gap-1">
146
+ {middlewares.map((mw: any, mi: number) => {
147
+ const mwNs = mw.namespace || ns
148
+ return (
149
+ <ResourceLink
150
+ key={mi}
151
+ name={mw.name}
152
+ kind="middlewares"
153
+ namespace={mwNs}
154
+ label={
155
+ <span className="px-1.5 py-0.5 bg-purple-500/10 text-purple-400 rounded text-[10px] inline-block">
156
+ {mw.namespace && mw.namespace !== ns ? `${mw.namespace}/` : ''}{mw.name}
157
+ </span>
158
+ }
159
+ onNavigate={onNavigate}
160
+ />
161
+ )
162
+ })}
163
+ </div>
164
+ </div>
165
+ )}
166
+ </div>
167
+ )
168
+ })}
169
+ </div>
170
+ </Section>
171
+
172
+ {tls && (
173
+ <Section title="TLS" icon={Lock} defaultExpanded>
174
+ <PropertyList>
175
+ {tls.secretName && (
176
+ <Property label="Secret" value={
177
+ <ResourceLink
178
+ name={tls.secretName}
179
+ kind="secrets"
180
+ namespace={ns}
181
+ onNavigate={onNavigate}
182
+ />
183
+ } />
184
+ )}
185
+ {tls.options?.name && (
186
+ <Property label="TLS Option" value={
187
+ <ResourceLink
188
+ name={tls.options.name}
189
+ kind="tlsoptions"
190
+ namespace={tls.options.namespace || ns}
191
+ onNavigate={onNavigate}
192
+ />
193
+ } />
194
+ )}
195
+ {tls.store?.name && (
196
+ <Property label="TLS Store" value={
197
+ <ResourceLink
198
+ name={tls.store.name}
199
+ kind="tlsstores"
200
+ namespace={tls.store.namespace || ns}
201
+ onNavigate={onNavigate}
202
+ />
203
+ } />
204
+ )}
205
+ {tls.certResolver && (
206
+ <Property label="Cert Resolver" value={tls.certResolver} />
207
+ )}
208
+ {tls.domains && tls.domains.length > 0 && (
209
+ <Property label="Domains" value={
210
+ <div className="space-y-1">
211
+ {tls.domains.map((d: any, i: number) => (
212
+ <div key={i} className="text-xs">
213
+ {d.main && <span className="text-theme-text-secondary">{d.main}</span>}
214
+ {d.sans && d.sans.length > 0 && (
215
+ <span className="text-theme-text-tertiary"> + {d.sans.length} SAN(s)</span>
216
+ )}
217
+ </div>
218
+ ))}
219
+ </div>
220
+ } />
221
+ )}
222
+ {!tls.secretName && !tls.certResolver && (
223
+ <Property label="Mode" value="TLS termination (no explicit secret or resolver)" />
224
+ )}
225
+ </PropertyList>
226
+ </Section>
227
+ )}
228
+
229
+ {allMiddlewares.length > 0 && (
230
+ <Section title={`Middleware Chain (${allMiddlewares.length})`} icon={Layers}>
231
+ <div className="space-y-1">
232
+ {allMiddlewares.map((mw, i) => {
233
+ const mwNs = mw.namespace || ns
234
+ const isCrossNs = mw.namespace && mw.namespace !== ns
235
+ return (
236
+ <div key={i} className="flex items-center gap-2 text-xs">
237
+ <span className="text-theme-text-tertiary w-4 text-right">{i + 1}.</span>
238
+ <ResourceLink
239
+ name={mw.name}
240
+ kind="middlewares"
241
+ namespace={mwNs}
242
+ onNavigate={onNavigate}
243
+ />
244
+ {isCrossNs && (
245
+ <span className="px-1.5 py-0.5 bg-yellow-500/10 text-yellow-400 rounded text-[10px]">
246
+ {mw.namespace}
247
+ </span>
248
+ )}
249
+ </div>
250
+ )
251
+ })}
252
+ </div>
253
+ </Section>
254
+ )}
255
+ </>
256
+ )
257
+ }