@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,17 @@
1
+ export * from './resource-utils'
2
+ export * from './resource-utils-argo'
3
+ export * from './resource-utils-certmanager'
4
+ export * from './resource-utils-cnpg'
5
+ export * from './resource-utils-eso'
6
+ export * from './resource-utils-flux'
7
+ export * from './resource-utils-istio'
8
+ export * from './resource-utils-karpenter'
9
+ export * from './resource-utils-keda'
10
+ export * from './resource-utils-knative'
11
+ export * from './resource-utils-kyverno'
12
+ export * from './resource-utils-prometheus'
13
+ export * from './resource-utils-trivy'
14
+ export * from './resource-utils-traefik'
15
+ export * from './resource-utils-velero'
16
+ export { ResourcesView, ResourcesViewDataContext } from './ResourcesView'
17
+ export type { ResourceQueryResult } from './ResourcesView'
@@ -0,0 +1,166 @@
1
+ import { Bell, AlertTriangle, Filter, Send } from 'lucide-react'
2
+ import { clsx } from 'clsx'
3
+ import { Section, PropertyList, Property, ConditionsSection } from '../../ui/drawer-components'
4
+ import { GitOpsStatusBadge } from '../../gitops'
5
+ import { fluxConditionsToGitOpsStatus, type FluxCondition } from '../../../types/gitops'
6
+
7
+ interface AlertRendererProps {
8
+ data: any
9
+ }
10
+
11
+ export function AlertRenderer({ data }: AlertRendererProps) {
12
+ const status = data.status || {}
13
+ const spec = data.spec || {}
14
+ const conditions = (status.conditions || []) as FluxCondition[]
15
+
16
+ // Convert to unified GitOps status
17
+ const gitOpsStatus = fluxConditionsToGitOpsStatus(conditions, spec.suspend === true)
18
+
19
+ // Problem detection
20
+ const problems: Array<{ color: 'red' | 'yellow'; message: string }> = []
21
+
22
+ if (gitOpsStatus.suspended) {
23
+ problems.push({ color: 'yellow', message: 'Alert is suspended' })
24
+ }
25
+
26
+ if (gitOpsStatus.health === 'Degraded' && gitOpsStatus.message) {
27
+ problems.push({ color: 'red', message: gitOpsStatus.message })
28
+ }
29
+
30
+ // Event sources
31
+ const eventSources = spec.eventSources || []
32
+ const eventSeverity = spec.eventSeverity || 'info'
33
+ const inclusionList = spec.inclusionList || []
34
+ const exclusionList = spec.exclusionList || []
35
+
36
+ return (
37
+ <>
38
+ {/* Problem alerts */}
39
+ {problems.map((problem, i) => (
40
+ <div
41
+ key={i}
42
+ className={clsx(
43
+ 'mb-4 p-3 border rounded-lg',
44
+ problem.color === 'red'
45
+ ? 'bg-red-500/10 border-red-500/30'
46
+ : 'bg-yellow-500/10 border-yellow-500/30'
47
+ )}
48
+ >
49
+ <div className="flex items-start gap-2">
50
+ <AlertTriangle
51
+ className={clsx(
52
+ 'w-4 h-4 mt-0.5 shrink-0',
53
+ problem.color === 'red' ? 'text-red-400' : 'text-yellow-400'
54
+ )}
55
+ />
56
+ <div className="flex-1 min-w-0">
57
+ <div
58
+ className={clsx(
59
+ 'text-sm font-medium',
60
+ problem.color === 'red' ? 'text-red-400' : 'text-yellow-400'
61
+ )}
62
+ >
63
+ {problem.color === 'red' ? 'Issue Detected' : 'Warning'}
64
+ </div>
65
+ <div
66
+ className={clsx(
67
+ 'text-xs mt-1',
68
+ problem.color === 'red' ? 'text-red-300/80' : 'text-yellow-300/80'
69
+ )}
70
+ >
71
+ {problem.message}
72
+ </div>
73
+ </div>
74
+ </div>
75
+ </div>
76
+ ))}
77
+
78
+ {/* Status section */}
79
+ <Section title="Status">
80
+ <GitOpsStatusBadge status={gitOpsStatus} showHealth={false} />
81
+ </Section>
82
+
83
+ {/* Provider section */}
84
+ <Section title="Provider" icon={Send}>
85
+ <PropertyList>
86
+ <Property label="Provider Ref" value={spec.providerRef?.name} />
87
+ <Property
88
+ label="Event Severity"
89
+ value={
90
+ <span className={clsx(
91
+ 'px-2 py-0.5 rounded text-xs font-medium',
92
+ eventSeverity === 'error' ? 'bg-red-500/20 text-red-400' :
93
+ eventSeverity === 'warning' ? 'bg-yellow-500/20 text-yellow-400' :
94
+ 'bg-blue-500/20 text-blue-400'
95
+ )}>
96
+ {eventSeverity}
97
+ </span>
98
+ }
99
+ />
100
+ {spec.summary && <Property label="Summary" value={spec.summary} />}
101
+ </PropertyList>
102
+ </Section>
103
+
104
+ {/* Event Sources section */}
105
+ {eventSources.length > 0 && (
106
+ <Section title={`Event Sources (${eventSources.length})`} icon={Bell}>
107
+ <div className="space-y-2">
108
+ {eventSources.map((source: any, idx: number) => (
109
+ <div
110
+ key={idx}
111
+ className="p-2 bg-theme-elevated/30 rounded text-sm"
112
+ >
113
+ <div className="flex items-center gap-2">
114
+ <span className="text-theme-text-tertiary">{source.kind}</span>
115
+ <span className="text-theme-text-primary">{source.name || '*'}</span>
116
+ </div>
117
+ {source.namespace && (
118
+ <div className="text-xs text-theme-text-tertiary mt-1">
119
+ Namespace: {source.namespace === '*' ? 'All' : source.namespace}
120
+ </div>
121
+ )}
122
+ {source.matchLabels && Object.keys(source.matchLabels).length > 0 && (
123
+ <div className="text-xs text-theme-text-tertiary mt-1">
124
+ Labels: {Object.entries(source.matchLabels).map(([k, v]) => `${k}=${v}`).join(', ')}
125
+ </div>
126
+ )}
127
+ </div>
128
+ ))}
129
+ </div>
130
+ </Section>
131
+ )}
132
+
133
+ {/* Filters section */}
134
+ {(inclusionList.length > 0 || exclusionList.length > 0) && (
135
+ <Section title="Filters" icon={Filter} defaultExpanded={false}>
136
+ <PropertyList>
137
+ {inclusionList.length > 0 && (
138
+ <Property
139
+ label="Include"
140
+ value={inclusionList.join(', ')}
141
+ />
142
+ )}
143
+ {exclusionList.length > 0 && (
144
+ <Property
145
+ label="Exclude"
146
+ value={exclusionList.join(', ')}
147
+ />
148
+ )}
149
+ </PropertyList>
150
+ </Section>
151
+ )}
152
+
153
+ {/* Additional Info */}
154
+ {status.observedGeneration !== undefined && (
155
+ <Section title="Additional Info" defaultExpanded={false}>
156
+ <PropertyList>
157
+ <Property label="Observed Generation" value={status.observedGeneration} />
158
+ </PropertyList>
159
+ </Section>
160
+ )}
161
+
162
+ {/* Conditions section */}
163
+ <ConditionsSection conditions={conditions} />
164
+ </>
165
+ )
166
+ }
@@ -0,0 +1,233 @@
1
+ import { GitBranch, FolderTree, Settings, Target, XCircle } from 'lucide-react'
2
+ import { clsx } from 'clsx'
3
+ import { Section, PropertyList, Property, ConditionsSection, ProblemAlerts } from '../../ui/drawer-components'
4
+ import { formatAge } from '../resource-utils'
5
+ import { GitOpsStatusBadge, ManagedResourcesList, SyncCountdown } from '../../gitops'
6
+ import {
7
+ argoStatusToGitOpsStatus,
8
+ parseArgoResources,
9
+ type ArgoAppStatus,
10
+ type ArgoResource,
11
+ } from '../../../types/gitops'
12
+
13
+ interface ArgoApplicationRendererProps {
14
+ data: any
15
+ onTerminate?: (params: { namespace: string; name: string }) => void
16
+ isTerminating?: boolean
17
+ }
18
+
19
+ export function ArgoApplicationRenderer({ data, onTerminate, isTerminating }: ArgoApplicationRendererProps) {
20
+ const status = (data.status || {}) as ArgoAppStatus & {
21
+ resources?: ArgoResource[]
22
+ conditions?: Array<{ type: string; status: string; message?: string; lastTransitionTime?: string }>
23
+ }
24
+ const spec = data.spec || {}
25
+
26
+ const namespace = data.metadata?.namespace || ''
27
+ const name = data.metadata?.name || ''
28
+
29
+ // Convert to unified GitOps status
30
+ const gitOpsStatus = argoStatusToGitOpsStatus(status)
31
+
32
+ // Parse managed resources from status.resources
33
+ const managedResources = parseArgoResources(status.resources || [])
34
+
35
+ // Problem detection
36
+ const problems: Array<{ color: 'red' | 'yellow'; message: string }> = []
37
+
38
+ if (gitOpsStatus.suspended) {
39
+ problems.push({ color: 'yellow', message: 'Application automated sync is disabled' })
40
+ }
41
+
42
+ if (gitOpsStatus.health === 'Degraded' && gitOpsStatus.message) {
43
+ problems.push({ color: 'red', message: gitOpsStatus.message })
44
+ }
45
+
46
+ if (gitOpsStatus.sync === 'OutOfSync') {
47
+ problems.push({ color: 'yellow', message: 'Application is out of sync with git' })
48
+ }
49
+
50
+ // Extract source info
51
+ const source = spec.source || {}
52
+ const destination = spec.destination || {}
53
+ const syncPolicy = spec.syncPolicy || {}
54
+ const operationState = status.operationState
55
+
56
+ // Check if sync is in progress
57
+ const isSyncing = operationState?.phase === 'Running'
58
+
59
+ return (
60
+ <>
61
+ <ProblemAlerts problems={problems} />
62
+
63
+ {/* Status section */}
64
+ <Section title="Status">
65
+ <div className="space-y-3">
66
+ <div className="flex items-center justify-between">
67
+ <GitOpsStatusBadge status={gitOpsStatus} />
68
+ {/* Terminate button (only when syncing and handler provided) */}
69
+ {isSyncing && onTerminate && (
70
+ <button
71
+ onClick={() => onTerminate({ namespace, name })}
72
+ disabled={isTerminating}
73
+ className="flex items-center gap-1.5 px-2 py-1 rounded text-xs bg-red-500/20 text-red-400 hover:bg-red-500/30 transition-colors disabled:opacity-50"
74
+ title="Terminate sync"
75
+ >
76
+ <XCircle className="w-3.5 h-3.5" />
77
+ {isTerminating ? 'Terminating...' : 'Terminate'}
78
+ </button>
79
+ )}
80
+ </div>
81
+
82
+ {/* Sync countdown (only if automated sync is enabled) */}
83
+ {syncPolicy.automated && (
84
+ <SyncCountdown
85
+ interval="5m" // ArgoCD default
86
+ lastSyncTime={status.reconciledAt}
87
+ suspended={gitOpsStatus.suspended}
88
+ />
89
+ )}
90
+ </div>
91
+ </Section>
92
+
93
+ {/* Source section */}
94
+ <Section title="Source" icon={FolderTree}>
95
+ <PropertyList>
96
+ <Property label="Repository" value={source.repoURL} />
97
+ {source.path && <Property label="Path" value={source.path} />}
98
+ {source.targetRevision && (
99
+ <Property
100
+ label="Target Revision"
101
+ value={
102
+ <span className="flex items-center gap-1">
103
+ <GitBranch className="w-3.5 h-3.5" />
104
+ {source.targetRevision}
105
+ </span>
106
+ }
107
+ />
108
+ )}
109
+ {source.chart && <Property label="Helm Chart" value={source.chart} />}
110
+ {source.helm?.valueFiles && source.helm.valueFiles.length > 0 && (
111
+ <Property label="Value Files" value={source.helm.valueFiles.join(', ')} />
112
+ )}
113
+ {source.kustomize?.namePrefix && (
114
+ <Property label="Kustomize Prefix" value={source.kustomize.namePrefix} />
115
+ )}
116
+ </PropertyList>
117
+ </Section>
118
+
119
+ {/* Destination section */}
120
+ <Section title="Destination" icon={Target}>
121
+ <PropertyList>
122
+ <Property label="Server" value={destination.server || destination.name || '-'} />
123
+ <Property label="Namespace" value={destination.namespace || 'default'} />
124
+ </PropertyList>
125
+ </Section>
126
+
127
+ {/* Sync Policy section */}
128
+ <Section title="Sync Policy" icon={Settings}>
129
+ <PropertyList>
130
+ <Property
131
+ label="Automated Sync"
132
+ value={
133
+ <span
134
+ className={clsx(
135
+ 'px-2 py-0.5 rounded text-xs font-medium',
136
+ syncPolicy.automated ? 'bg-green-500/20 text-green-400' : 'bg-gray-500/20 text-gray-400'
137
+ )}
138
+ >
139
+ {syncPolicy.automated ? 'Enabled' : 'Disabled'}
140
+ </span>
141
+ }
142
+ />
143
+ {syncPolicy.automated && (
144
+ <>
145
+ <Property
146
+ label="Self Heal"
147
+ value={syncPolicy.automated.selfHeal ? 'Yes' : 'No'}
148
+ />
149
+ <Property
150
+ label="Prune"
151
+ value={syncPolicy.automated.prune ? 'Yes' : 'No'}
152
+ />
153
+ </>
154
+ )}
155
+ {syncPolicy.retry && (
156
+ <>
157
+ <Property label="Retry Limit" value={syncPolicy.retry.limit} />
158
+ {syncPolicy.retry.backoff && (
159
+ <Property label="Backoff Duration" value={syncPolicy.retry.backoff.duration} />
160
+ )}
161
+ </>
162
+ )}
163
+ {syncPolicy.syncOptions && syncPolicy.syncOptions.length > 0 && (
164
+ <Property label="Sync Options" value={syncPolicy.syncOptions.join(', ')} />
165
+ )}
166
+ </PropertyList>
167
+ </Section>
168
+
169
+ {/* Operation State (current/last sync) */}
170
+ {operationState && (
171
+ <Section title="Last Operation" defaultExpanded={operationState.phase === 'Running'}>
172
+ <PropertyList>
173
+ <Property
174
+ label="Phase"
175
+ value={
176
+ <span
177
+ className={clsx(
178
+ 'px-2 py-0.5 rounded text-xs font-medium',
179
+ operationState.phase === 'Succeeded'
180
+ ? 'bg-green-500/20 text-green-400'
181
+ : operationState.phase === 'Running'
182
+ ? 'bg-blue-500/20 text-blue-400'
183
+ : operationState.phase === 'Failed' || operationState.phase === 'Error'
184
+ ? 'bg-red-500/20 text-red-400'
185
+ : 'bg-gray-500/20 text-gray-400'
186
+ )}
187
+ >
188
+ {operationState.phase}
189
+ </span>
190
+ }
191
+ />
192
+ {operationState.message && (
193
+ <Property label="Message" value={operationState.message} />
194
+ )}
195
+ {operationState.finishedAt && (
196
+ <Property label="Finished" value={formatAge(operationState.finishedAt)} />
197
+ )}
198
+ {operationState.syncResult?.revision && (
199
+ <Property label="Revision" value={operationState.syncResult.revision} />
200
+ )}
201
+ </PropertyList>
202
+ </Section>
203
+ )}
204
+
205
+ {/* Managed Resources */}
206
+ {managedResources.length > 0 && (
207
+ <ManagedResourcesList
208
+ resources={managedResources}
209
+ title={`Managed Resources (${managedResources.length})`}
210
+ />
211
+ )}
212
+
213
+ {/* Revision Info */}
214
+ {(status.sync?.revision || status.reconciledAt) && (
215
+ <Section title="Revision Info" defaultExpanded={false}>
216
+ <PropertyList>
217
+ {status.sync?.revision && (
218
+ <Property label="Current Revision" value={status.sync.revision} />
219
+ )}
220
+ {status.reconciledAt && (
221
+ <Property label="Last Reconciled" value={formatAge(status.reconciledAt)} />
222
+ )}
223
+ </PropertyList>
224
+ </Section>
225
+ )}
226
+
227
+ {/* Conditions section */}
228
+ {status.conditions && status.conditions.length > 0 && (
229
+ <ConditionsSection conditions={status.conditions} />
230
+ )}
231
+ </>
232
+ )
233
+ }
@@ -0,0 +1,89 @@
1
+ import { Database, Clock } from 'lucide-react'
2
+ import { Section, PropertyList, Property, AlertBanner, ResourceLink } from '../../ui/drawer-components'
3
+ import {
4
+ getCNPGBackupStatus,
5
+ getCNPGBackupCluster,
6
+ getCNPGBackupMethod,
7
+ getCNPGBackupPhase,
8
+ getCNPGBackupDuration,
9
+ getCNPGBackupName,
10
+ getCNPGBackupDestinationPath,
11
+ getCNPGBackupServerName,
12
+ getCNPGBackupError,
13
+ getCNPGBackupTarget,
14
+ } from '../resource-utils-cnpg'
15
+
16
+ interface CNPGBackupRendererProps {
17
+ data: any
18
+ onNavigate?: (ref: { kind: string; namespace: string; name: string }) => void
19
+ }
20
+
21
+ export function CNPGBackupRenderer({ data, onNavigate }: CNPGBackupRendererProps) {
22
+ const status = getCNPGBackupStatus(data)
23
+ const error = getCNPGBackupError(data)
24
+ const phase = getCNPGBackupPhase(data)
25
+ const target = getCNPGBackupTarget(data)
26
+ const clusterName = getCNPGBackupCluster(data)
27
+
28
+ return (
29
+ <>
30
+ {/* Problem alerts */}
31
+ {status.level === 'unhealthy' && error && (
32
+ <AlertBanner
33
+ variant="error"
34
+ title="Backup Failed"
35
+ message={error}
36
+ />
37
+ )}
38
+
39
+ {/* Status */}
40
+ <Section title="Backup Status" icon={Clock} defaultExpanded>
41
+ <PropertyList>
42
+ <Property label="Phase" value={phase} />
43
+ <Property label="Method" value={getCNPGBackupMethod(data)} />
44
+ <Property label="Duration" value={getCNPGBackupDuration(data)} />
45
+ <Property label="Backup Name" value={getCNPGBackupName(data)} />
46
+ </PropertyList>
47
+ {data.status?.startedAt && (
48
+ <div className="mt-2 pt-2 border-t border-theme-border">
49
+ <PropertyList>
50
+ <Property label="Started" value={data.status.startedAt} />
51
+ {data.status?.stoppedAt && <Property label="Stopped" value={data.status.stoppedAt} />}
52
+ </PropertyList>
53
+ </div>
54
+ )}
55
+ </Section>
56
+
57
+ {/* Backup Details */}
58
+ <Section title="Backup Details" icon={Database} defaultExpanded>
59
+ <PropertyList>
60
+ <Property label="Cluster" value={(() => {
61
+ if (clusterName && clusterName !== '-') {
62
+ return (
63
+ <ResourceLink
64
+ name={clusterName}
65
+ kind="clusters"
66
+ namespace={data.metadata?.namespace || ''}
67
+ group="postgresql.cnpg.io"
68
+ onNavigate={onNavigate}
69
+ />
70
+ )
71
+ }
72
+ return clusterName
73
+ })()} />
74
+ <Property label="Destination" value={getCNPGBackupDestinationPath(data)} />
75
+ <Property label="Server Name" value={getCNPGBackupServerName(data)} />
76
+ </PropertyList>
77
+ </Section>
78
+
79
+ {/* Target - for PITR backups */}
80
+ {target !== '-' && (
81
+ <Section title="Target" defaultExpanded>
82
+ <PropertyList>
83
+ <Property label="Recovery Target" value={target} />
84
+ </PropertyList>
85
+ </Section>
86
+ )}
87
+ </>
88
+ )
89
+ }