@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,193 @@
1
+ import { Cpu } from 'lucide-react'
2
+ import { clsx } from 'clsx'
3
+ import { Section, PropertyList, Property, ConditionsSection, AlertBanner, ResourceLink } from '../../ui/drawer-components'
4
+
5
+ interface VPARendererProps {
6
+ data: any
7
+ onNavigate?: (ref: { kind: string; namespace: string; name: string }) => void
8
+ }
9
+
10
+ function formatResource(value: string | undefined): string {
11
+ if (!value) return '-'
12
+ return value
13
+ }
14
+
15
+ export function VPARenderer({ data, onNavigate }: VPARendererProps) {
16
+ const spec = data.spec || {}
17
+ const status = data.status || {}
18
+ const targetRef = spec.targetRef || {}
19
+ const updatePolicy = spec.updatePolicy || {}
20
+ const resourcePolicy = spec.resourcePolicy || {}
21
+ const containerPolicies = resourcePolicy.containerPolicies || []
22
+ const recommendations = status.recommendation?.containerRecommendations || []
23
+ const conditions = status.conditions || []
24
+
25
+ const updateMode = updatePolicy.updateMode || 'Auto'
26
+ const isOff = updateMode === 'Off'
27
+
28
+ // Problem detection
29
+ const hasRecommendation = conditions.some((c: any) => c.type === 'RecommendationProvided' && c.status === 'True')
30
+ const noRecommendation = conditions.some((c: any) => c.type === 'RecommendationProvided' && c.status === 'False')
31
+ const configUnsupported = conditions.some((c: any) => c.type === 'ConfigUnsupported' && c.status === 'True')
32
+ const lowConfidence = conditions.some((c: any) => c.type === 'LowConfidence' && c.status === 'True')
33
+
34
+ return (
35
+ <>
36
+ {/* Problem alerts */}
37
+ {configUnsupported && (
38
+ <AlertBanner
39
+ variant="error"
40
+ title="Configuration Unsupported"
41
+ message={conditions.find((c: any) => c.type === 'ConfigUnsupported')?.message}
42
+ />
43
+ )}
44
+
45
+ {noRecommendation && !configUnsupported && (
46
+ <AlertBanner
47
+ variant="warning"
48
+ title="No Recommendations"
49
+ message={conditions.find((c: any) => c.type === 'RecommendationProvided')?.message || 'VPA has not produced recommendations yet — insufficient metrics data'}
50
+ />
51
+ )}
52
+
53
+ {lowConfidence && hasRecommendation && (
54
+ <AlertBanner
55
+ variant="info"
56
+ title="Low Confidence"
57
+ message="Recommendations are based on limited data — consider waiting for more metrics"
58
+ />
59
+ )}
60
+
61
+ {/* Target & Mode */}
62
+ <Section title="Configuration" icon={Cpu}>
63
+ <PropertyList>
64
+ <Property label="Target" value={
65
+ targetRef.name ? (
66
+ <ResourceLink
67
+ name={targetRef.name}
68
+ kind={(targetRef.kind || 'Deployment').toLowerCase() + 's'}
69
+ namespace={data.metadata?.namespace || ''}
70
+ label={`${targetRef.kind}/${targetRef.name}`}
71
+ onNavigate={onNavigate}
72
+ />
73
+ ) : undefined
74
+ } />
75
+ <Property label="Update Mode" value={
76
+ <span className={clsx(
77
+ updateMode === 'Auto' && 'text-green-400',
78
+ updateMode === 'Recreate' && 'text-yellow-400',
79
+ updateMode === 'Initial' && 'text-blue-400',
80
+ updateMode === 'Off' && 'text-theme-text-tertiary',
81
+ )}>
82
+ {updateMode}
83
+ </span>
84
+ } />
85
+ {isOff && (
86
+ <Property label="" value={
87
+ <span className="text-xs text-theme-text-tertiary">Recommendation-only mode — no automatic updates</span>
88
+ } />
89
+ )}
90
+ </PropertyList>
91
+ </Section>
92
+
93
+ {/* Recommendations */}
94
+ {recommendations.length > 0 && (
95
+ <Section title="Recommendations" defaultExpanded>
96
+ <div className="space-y-3">
97
+ {recommendations.map((rec: any) => (
98
+ <div key={rec.containerName} className="bg-theme-elevated/30 rounded-lg p-3">
99
+ <div className="text-sm font-medium text-theme-text-primary mb-2">{rec.containerName}</div>
100
+ <div className="overflow-x-auto">
101
+ <table className="w-full text-xs">
102
+ <thead>
103
+ <tr className="text-theme-text-tertiary">
104
+ <th className="text-left py-1 pr-3 font-medium w-24"></th>
105
+ <th className="text-right py-1 px-2 font-medium">CPU</th>
106
+ <th className="text-right py-1 px-2 font-medium">Memory</th>
107
+ </tr>
108
+ </thead>
109
+ <tbody>
110
+ <tr>
111
+ <td className="py-1 pr-3 text-theme-text-secondary">Target</td>
112
+ <td className="text-right py-1 px-2 text-green-400 font-medium">{formatResource(rec.target?.cpu)}</td>
113
+ <td className="text-right py-1 px-2 text-green-400 font-medium">{formatResource(rec.target?.memory)}</td>
114
+ </tr>
115
+ <tr>
116
+ <td className="py-1 pr-3 text-theme-text-secondary">Lower Bound</td>
117
+ <td className="text-right py-1 px-2 text-theme-text-secondary">{formatResource(rec.lowerBound?.cpu)}</td>
118
+ <td className="text-right py-1 px-2 text-theme-text-secondary">{formatResource(rec.lowerBound?.memory)}</td>
119
+ </tr>
120
+ <tr>
121
+ <td className="py-1 pr-3 text-theme-text-secondary">Upper Bound</td>
122
+ <td className="text-right py-1 px-2 text-theme-text-secondary">{formatResource(rec.upperBound?.cpu)}</td>
123
+ <td className="text-right py-1 px-2 text-theme-text-secondary">{formatResource(rec.upperBound?.memory)}</td>
124
+ </tr>
125
+ {rec.uncappedTarget && (
126
+ <tr>
127
+ <td className="py-1 pr-3 text-theme-text-tertiary">Uncapped</td>
128
+ <td className="text-right py-1 px-2 text-theme-text-tertiary">{formatResource(rec.uncappedTarget?.cpu)}</td>
129
+ <td className="text-right py-1 px-2 text-theme-text-tertiary">{formatResource(rec.uncappedTarget?.memory)}</td>
130
+ </tr>
131
+ )}
132
+ </tbody>
133
+ </table>
134
+ </div>
135
+ </div>
136
+ ))}
137
+ </div>
138
+ </Section>
139
+ )}
140
+
141
+ {/* Resource Policy */}
142
+ {containerPolicies.length > 0 && (
143
+ <Section title="Resource Policy" defaultExpanded>
144
+ <div className="space-y-3">
145
+ {containerPolicies.map((policy: any, i: number) => (
146
+ <div key={i} className="bg-theme-elevated/30 rounded-lg p-3">
147
+ <div className="flex items-center gap-2 mb-2">
148
+ <span className="text-sm font-medium text-theme-text-primary">{policy.containerName || '*'}</span>
149
+ {policy.mode && (
150
+ <span className={clsx(
151
+ 'px-1.5 py-0.5 rounded text-[10px] font-medium',
152
+ policy.mode === 'Off' ? 'bg-theme-hover text-theme-text-tertiary' : 'bg-blue-500/20 text-blue-400'
153
+ )}>
154
+ {policy.mode}
155
+ </span>
156
+ )}
157
+ </div>
158
+ <PropertyList>
159
+ {policy.minAllowed && (
160
+ <Property label="Min Allowed" value={
161
+ <span className="text-xs">
162
+ {policy.minAllowed.cpu && `CPU: ${policy.minAllowed.cpu}`}
163
+ {policy.minAllowed.cpu && policy.minAllowed.memory && ' · '}
164
+ {policy.minAllowed.memory && `Mem: ${policy.minAllowed.memory}`}
165
+ </span>
166
+ } />
167
+ )}
168
+ {policy.maxAllowed && (
169
+ <Property label="Max Allowed" value={
170
+ <span className="text-xs">
171
+ {policy.maxAllowed.cpu && `CPU: ${policy.maxAllowed.cpu}`}
172
+ {policy.maxAllowed.cpu && policy.maxAllowed.memory && ' · '}
173
+ {policy.maxAllowed.memory && `Mem: ${policy.maxAllowed.memory}`}
174
+ </span>
175
+ } />
176
+ )}
177
+ {policy.controlledResources && (
178
+ <Property label="Controlled" value={policy.controlledResources.join(', ')} />
179
+ )}
180
+ {policy.controlledValues && (
181
+ <Property label="Controlled Values" value={policy.controlledValues} />
182
+ )}
183
+ </PropertyList>
184
+ </div>
185
+ ))}
186
+ </div>
187
+ </Section>
188
+ )}
189
+
190
+ <ConditionsSection conditions={conditions} />
191
+ </>
192
+ )
193
+ }
@@ -0,0 +1,73 @@
1
+ import { HardDrive, Clock } from 'lucide-react'
2
+ import { clsx } from 'clsx'
3
+ import { Section, PropertyList, Property, ConditionsSection } from '../../ui/drawer-components'
4
+ import {
5
+ getBSLStatus,
6
+ getBSLProvider,
7
+ getBSLBucket,
8
+ getBSLPrefix,
9
+ getBSLRegion,
10
+ getBSLDefault,
11
+ getBSLAccessMode,
12
+ getBSLLastValidation,
13
+ getBSLLastSynced,
14
+ } from '../resource-utils-velero'
15
+
16
+ interface VeleroBSLRendererProps {
17
+ data: any
18
+ }
19
+
20
+ export function VeleroBSLRenderer({ data }: VeleroBSLRendererProps) {
21
+ const status = data.status || {}
22
+ const conditions = status.conditions || []
23
+ const bslStatus = getBSLStatus(data)
24
+ const config = data.spec?.config || {}
25
+
26
+ return (
27
+ <>
28
+ {/* Status section */}
29
+ <Section title="Status" icon={Clock} defaultExpanded>
30
+ <PropertyList>
31
+ <Property label="Phase" value={
32
+ <span className={clsx('inline-flex items-center px-2 py-0.5 rounded text-xs font-medium', bslStatus.color)}>
33
+ {bslStatus.text}
34
+ </span>
35
+ } />
36
+ <Property label="Last Validation" value={getBSLLastValidation(data)} />
37
+ <Property label="Last Synced" value={getBSLLastSynced(data)} />
38
+ {status.lastSyncedRevision && (
39
+ <Property label="Last Synced Revision" value={
40
+ <span className="text-sm font-mono text-theme-text-secondary break-all">{status.lastSyncedRevision}</span>
41
+ } />
42
+ )}
43
+ </PropertyList>
44
+ </Section>
45
+
46
+ {/* Provider section */}
47
+ <Section title="Provider" icon={HardDrive} defaultExpanded>
48
+ <PropertyList>
49
+ <Property label="Provider" value={getBSLProvider(data)} />
50
+ <Property label="Bucket" value={getBSLBucket(data)} />
51
+ <Property label="Prefix" value={getBSLPrefix(data)} />
52
+ <Property label="Region" value={getBSLRegion(data)} />
53
+ <Property label="Access Mode" value={getBSLAccessMode(data)} />
54
+ <Property label="Default" value={getBSLDefault(data) ? 'Yes' : 'No'} />
55
+ </PropertyList>
56
+ {Object.keys(config).length > 0 && (
57
+ <div className="mt-2 pt-2 border-t border-theme-border">
58
+ <div className="text-xs font-medium text-theme-text-secondary uppercase tracking-wider mb-1">Config</div>
59
+ <div className="flex flex-wrap gap-1">
60
+ {Object.entries(config).map(([k, v]) => (
61
+ <span key={k} className="px-1.5 py-0.5 bg-theme-hover rounded text-xs text-theme-text-secondary">
62
+ {k}: {String(v)}
63
+ </span>
64
+ ))}
65
+ </div>
66
+ </div>
67
+ )}
68
+ </Section>
69
+
70
+ <ConditionsSection conditions={conditions} />
71
+ </>
72
+ )
73
+ }
@@ -0,0 +1,193 @@
1
+ import { Archive, Clock, HardDrive, Filter } from 'lucide-react'
2
+ import { clsx } from 'clsx'
3
+ import { Section, PropertyList, Property, ConditionsSection, AlertBanner } from '../../ui/drawer-components'
4
+ import {
5
+ getBackupStatus,
6
+ getBackupStorageLocation,
7
+ getBackupIncludedNamespaces,
8
+ getBackupExcludedNamespaces,
9
+ getBackupIncludedResources,
10
+ getBackupExcludedResources,
11
+ getBackupDuration,
12
+ getBackupItemCount,
13
+ getBackupExpiry,
14
+ getBackupErrors,
15
+ getBackupWarnings,
16
+ getBackupValidationErrors,
17
+ getBackupTTL,
18
+ getBackupSnapshotVolumes,
19
+ getBackupDefaultVolumesToFsBackup,
20
+ getBackupVolumeSnapshotLocations,
21
+ } from '../resource-utils-velero'
22
+ import { formatAge } from '../resource-utils'
23
+
24
+ interface VeleroBackupRendererProps {
25
+ data: any
26
+ }
27
+
28
+ export function VeleroBackupRenderer({ data }: VeleroBackupRendererProps) {
29
+ const status = data.status || {}
30
+ const conditions = status.conditions || []
31
+
32
+ const backupStatus = getBackupStatus(data)
33
+ const errors = getBackupErrors(data)
34
+ const warnings = getBackupWarnings(data)
35
+ const validationErrors = getBackupValidationErrors(data)
36
+ const includedNamespaces = getBackupIncludedNamespaces(data)
37
+ const excludedNamespaces = getBackupExcludedNamespaces(data)
38
+ const includedResources = getBackupIncludedResources(data)
39
+ const excludedResources = getBackupExcludedResources(data)
40
+ const vslLocations = getBackupVolumeSnapshotLocations(data)
41
+
42
+ const isFailed = backupStatus.level === 'unhealthy'
43
+ const isPartiallyFailed = backupStatus.text === 'PartiallyFailed'
44
+ const isInProgress = backupStatus.text === 'InProgress' || backupStatus.text === 'Uploading'
45
+
46
+ // Progress data
47
+ const progress = status.progress
48
+ const itemsBacked = progress?.itemsBackedUp ?? 0
49
+ const totalItems = progress?.totalItems ?? 0
50
+ const progressPercent = totalItems > 0 ? Math.round((itemsBacked / totalItems) * 100) : 0
51
+
52
+ return (
53
+ <>
54
+ {/* Problem alerts */}
55
+ {(isFailed || isPartiallyFailed) && (
56
+ <AlertBanner
57
+ variant="error"
58
+ title={isFailed ? 'Backup Failed' : 'Backup Partially Failed'}
59
+ message={`${errors} error(s) occurred during backup.`}
60
+ items={validationErrors.length > 0 ? validationErrors : undefined}
61
+ />
62
+ )}
63
+ {warnings > 0 && !isFailed && (
64
+ <AlertBanner
65
+ variant="warning"
66
+ title={`${warnings} Warning(s)`}
67
+ message={`Backup completed with ${warnings} warning(s).`}
68
+ />
69
+ )}
70
+
71
+ {/* Status section */}
72
+ <Section title="Status" icon={Archive} defaultExpanded>
73
+ <PropertyList>
74
+ <Property label="Phase" value={
75
+ <span className={clsx('inline-flex items-center px-2 py-0.5 rounded text-xs font-medium', backupStatus.color)}>
76
+ {backupStatus.text}
77
+ </span>
78
+ } />
79
+ {status.startTimestamp && (
80
+ <Property label="Started" value={formatAge(status.startTimestamp) + ' ago'} />
81
+ )}
82
+ {status.completionTimestamp && (
83
+ <Property label="Completed" value={formatAge(status.completionTimestamp) + ' ago'} />
84
+ )}
85
+ <Property label="Duration" value={getBackupDuration(data)} />
86
+ <Property label="Expiration" value={getBackupExpiry(data)} />
87
+ {errors > 0 && <Property label="Errors" value={String(errors)} />}
88
+ {warnings > 0 && <Property label="Warnings" value={String(warnings)} />}
89
+ </PropertyList>
90
+ </Section>
91
+
92
+ {/* Progress section (if in progress) */}
93
+ {isInProgress && progress && (
94
+ <Section title="Progress" defaultExpanded>
95
+ <div className="space-y-2">
96
+ <div className="flex items-center justify-between text-sm">
97
+ <span className="text-theme-text-secondary">Items backed up</span>
98
+ <span className="text-theme-text-primary font-medium">{getBackupItemCount(data)}</span>
99
+ </div>
100
+ <div className="w-full bg-theme-elevated rounded-full h-2">
101
+ <div
102
+ className="bg-blue-500 h-2 rounded-full transition-all"
103
+ style={{ width: `${progressPercent}%` }}
104
+ />
105
+ </div>
106
+ <div className="text-xs text-theme-text-tertiary text-right">{progressPercent}%</div>
107
+ </div>
108
+ </Section>
109
+ )}
110
+
111
+ {/* Scope section */}
112
+ {(includedNamespaces.length > 0 || excludedNamespaces.length > 0 || includedResources.length > 0 || excludedResources.length > 0 || data.spec?.labelSelector) && (
113
+ <Section title="Scope" icon={Filter} defaultExpanded>
114
+ <PropertyList>
115
+ {includedNamespaces.length > 0 && (
116
+ <Property label="Included Namespaces" value={
117
+ <div className="flex flex-wrap gap-1">
118
+ {includedNamespaces.map((ns: string) => (
119
+ <span key={ns} className="px-1.5 py-0.5 bg-theme-hover rounded text-xs text-theme-text-secondary">{ns}</span>
120
+ ))}
121
+ </div>
122
+ } />
123
+ )}
124
+ {includedNamespaces.length === 0 && (
125
+ <Property label="Included Namespaces" value="* (all)" />
126
+ )}
127
+ {excludedNamespaces.length > 0 && (
128
+ <Property label="Excluded Namespaces" value={
129
+ <div className="flex flex-wrap gap-1">
130
+ {excludedNamespaces.map((ns: string) => (
131
+ <span key={ns} className="px-1.5 py-0.5 bg-red-500/10 rounded text-xs text-red-400">{ns}</span>
132
+ ))}
133
+ </div>
134
+ } />
135
+ )}
136
+ {includedResources.length > 0 && (
137
+ <Property label="Included Resources" value={
138
+ <div className="flex flex-wrap gap-1">
139
+ {includedResources.map((r: string) => (
140
+ <span key={r} className="px-1.5 py-0.5 bg-theme-hover rounded text-xs text-theme-text-secondary">{r}</span>
141
+ ))}
142
+ </div>
143
+ } />
144
+ )}
145
+ {excludedResources.length > 0 && (
146
+ <Property label="Excluded Resources" value={
147
+ <div className="flex flex-wrap gap-1">
148
+ {excludedResources.map((r: string) => (
149
+ <span key={r} className="px-1.5 py-0.5 bg-red-500/10 rounded text-xs text-red-400">{r}</span>
150
+ ))}
151
+ </div>
152
+ } />
153
+ )}
154
+ {data.spec?.labelSelector && (
155
+ <Property label="Label Selector" value={
156
+ <span className="text-sm font-mono text-theme-text-secondary break-all">
157
+ {JSON.stringify(data.spec.labelSelector)}
158
+ </span>
159
+ } />
160
+ )}
161
+ </PropertyList>
162
+ </Section>
163
+ )}
164
+
165
+ {/* Storage section */}
166
+ <Section title="Storage" icon={HardDrive} defaultExpanded>
167
+ <PropertyList>
168
+ <Property label="Storage Location" value={getBackupStorageLocation(data)} />
169
+ {vslLocations.length > 0 && (
170
+ <Property label="Volume Snapshot Locations" value={
171
+ <div className="flex flex-wrap gap-1">
172
+ {vslLocations.map((loc: string) => (
173
+ <span key={loc} className="px-1.5 py-0.5 bg-theme-hover rounded text-xs text-theme-text-secondary">{loc}</span>
174
+ ))}
175
+ </div>
176
+ } />
177
+ )}
178
+ </PropertyList>
179
+ </Section>
180
+
181
+ {/* Options section */}
182
+ <Section title="Options" icon={Clock}>
183
+ <PropertyList>
184
+ <Property label="TTL" value={getBackupTTL(data)} />
185
+ <Property label="Snapshot Volumes" value={getBackupSnapshotVolumes(data)} />
186
+ <Property label="Default FS Backup" value={getBackupDefaultVolumesToFsBackup(data)} />
187
+ </PropertyList>
188
+ </Section>
189
+
190
+ <ConditionsSection conditions={conditions} />
191
+ </>
192
+ )
193
+ }
@@ -0,0 +1,161 @@
1
+ import { ArchiveRestore, Filter } from 'lucide-react'
2
+ import { clsx } from 'clsx'
3
+ import { Section, PropertyList, Property, ConditionsSection, AlertBanner } from '../../ui/drawer-components'
4
+ import {
5
+ getRestoreStatus,
6
+ getRestoreBackupName,
7
+ getRestoreIncludedNamespaces,
8
+ getRestoreExcludedNamespaces,
9
+ getRestoreIncludedResources,
10
+ getRestoreExcludedResources,
11
+ getRestoreDuration,
12
+ getRestoreErrors,
13
+ getRestoreWarnings,
14
+ getRestorePVs,
15
+ getRestoreExistingResourcePolicy,
16
+ } from '../resource-utils-velero'
17
+ import { formatAge } from '../resource-utils'
18
+
19
+ interface VeleroRestoreRendererProps {
20
+ data: any
21
+ }
22
+
23
+ export function VeleroRestoreRenderer({ data }: VeleroRestoreRendererProps) {
24
+ const status = data.status || {}
25
+ const conditions = status.conditions || []
26
+
27
+ const restoreStatus = getRestoreStatus(data)
28
+ const errors = getRestoreErrors(data)
29
+ const warnings = getRestoreWarnings(data)
30
+ const includedNamespaces = getRestoreIncludedNamespaces(data)
31
+ const excludedNamespaces = getRestoreExcludedNamespaces(data)
32
+ const includedResources = getRestoreIncludedResources(data)
33
+ const excludedResources = getRestoreExcludedResources(data)
34
+
35
+ const isFailed = restoreStatus.level === 'unhealthy'
36
+ const isPartiallyFailed = restoreStatus.text === 'PartiallyFailed'
37
+ const isInProgress = restoreStatus.text === 'InProgress'
38
+
39
+ // Progress data
40
+ const progress = status.progress
41
+ const itemsRestored = progress?.itemsRestored ?? 0
42
+ const totalItems = progress?.totalItems ?? 0
43
+ const progressPercent = totalItems > 0 ? Math.round((itemsRestored / totalItems) * 100) : 0
44
+
45
+ return (
46
+ <>
47
+ {/* Problem alerts */}
48
+ {(isFailed || isPartiallyFailed) && (
49
+ <AlertBanner
50
+ variant="error"
51
+ title={isFailed ? 'Restore Failed' : 'Restore Partially Failed'}
52
+ message={`${errors} error(s) occurred during restore.`}
53
+ />
54
+ )}
55
+ {warnings > 0 && !isFailed && (
56
+ <AlertBanner
57
+ variant="warning"
58
+ title={`${warnings} Warning(s)`}
59
+ message={`Restore completed with ${warnings} warning(s).`}
60
+ />
61
+ )}
62
+
63
+ {/* Status section */}
64
+ <Section title="Status" icon={ArchiveRestore} defaultExpanded>
65
+ <PropertyList>
66
+ <Property label="Phase" value={
67
+ <span className={clsx('inline-flex items-center px-2 py-0.5 rounded text-xs font-medium', restoreStatus.color)}>
68
+ {restoreStatus.text}
69
+ </span>
70
+ } />
71
+ <Property label="Backup" value={getRestoreBackupName(data)} />
72
+ {status.startTimestamp && (
73
+ <Property label="Started" value={formatAge(status.startTimestamp) + ' ago'} />
74
+ )}
75
+ {status.completionTimestamp && (
76
+ <Property label="Completed" value={formatAge(status.completionTimestamp) + ' ago'} />
77
+ )}
78
+ <Property label="Duration" value={getRestoreDuration(data)} />
79
+ {errors > 0 && <Property label="Errors" value={String(errors)} />}
80
+ {warnings > 0 && <Property label="Warnings" value={String(warnings)} />}
81
+ </PropertyList>
82
+ </Section>
83
+
84
+ {/* Progress section (if in progress) */}
85
+ {isInProgress && progress && (
86
+ <Section title="Progress" defaultExpanded>
87
+ <div className="space-y-2">
88
+ <div className="flex items-center justify-between text-sm">
89
+ <span className="text-theme-text-secondary">Items restored</span>
90
+ <span className="text-theme-text-primary font-medium">{itemsRestored}/{totalItems}</span>
91
+ </div>
92
+ <div className="w-full bg-theme-elevated rounded-full h-2">
93
+ <div
94
+ className="bg-blue-500 h-2 rounded-full transition-all"
95
+ style={{ width: `${progressPercent}%` }}
96
+ />
97
+ </div>
98
+ <div className="text-xs text-theme-text-tertiary text-right">{progressPercent}%</div>
99
+ </div>
100
+ </Section>
101
+ )}
102
+
103
+ {/* Scope section */}
104
+ {(includedNamespaces.length > 0 || excludedNamespaces.length > 0 || includedResources.length > 0 || excludedResources.length > 0) && (
105
+ <Section title="Scope" icon={Filter} defaultExpanded>
106
+ <PropertyList>
107
+ {includedNamespaces.length > 0 && (
108
+ <Property label="Included Namespaces" value={
109
+ <div className="flex flex-wrap gap-1">
110
+ {includedNamespaces.map((ns: string) => (
111
+ <span key={ns} className="px-1.5 py-0.5 bg-theme-hover rounded text-xs text-theme-text-secondary">{ns}</span>
112
+ ))}
113
+ </div>
114
+ } />
115
+ )}
116
+ {includedNamespaces.length === 0 && (
117
+ <Property label="Included Namespaces" value="* (all)" />
118
+ )}
119
+ {excludedNamespaces.length > 0 && (
120
+ <Property label="Excluded Namespaces" value={
121
+ <div className="flex flex-wrap gap-1">
122
+ {excludedNamespaces.map((ns: string) => (
123
+ <span key={ns} className="px-1.5 py-0.5 bg-red-500/10 rounded text-xs text-red-400">{ns}</span>
124
+ ))}
125
+ </div>
126
+ } />
127
+ )}
128
+ {includedResources.length > 0 && (
129
+ <Property label="Included Resources" value={
130
+ <div className="flex flex-wrap gap-1">
131
+ {includedResources.map((r: string) => (
132
+ <span key={r} className="px-1.5 py-0.5 bg-theme-hover rounded text-xs text-theme-text-secondary">{r}</span>
133
+ ))}
134
+ </div>
135
+ } />
136
+ )}
137
+ {excludedResources.length > 0 && (
138
+ <Property label="Excluded Resources" value={
139
+ <div className="flex flex-wrap gap-1">
140
+ {excludedResources.map((r: string) => (
141
+ <span key={r} className="px-1.5 py-0.5 bg-red-500/10 rounded text-xs text-red-400">{r}</span>
142
+ ))}
143
+ </div>
144
+ } />
145
+ )}
146
+ </PropertyList>
147
+ </Section>
148
+ )}
149
+
150
+ {/* Options section */}
151
+ <Section title="Options">
152
+ <PropertyList>
153
+ <Property label="Restore PVs" value={getRestorePVs(data)} />
154
+ <Property label="Existing Resource Policy" value={getRestoreExistingResourcePolicy(data)} />
155
+ </PropertyList>
156
+ </Section>
157
+
158
+ <ConditionsSection conditions={conditions} />
159
+ </>
160
+ )
161
+ }