@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,119 @@
1
+ import { useState } from 'react'
2
+ import { KeyRound, ChevronDown, ChevronRight } from 'lucide-react'
3
+ import { clsx } from 'clsx'
4
+ import { Section, PropertyList, Property } from '../../ui/drawer-components'
5
+ import { formatAge } from '../resource-utils'
6
+ import { SEVERITY_BADGE_COLORS, TrivyAlertBanner, formatTrivyImage } from './trivy-shared'
7
+
8
+ interface ExposedSecretReportRendererProps {
9
+ data: any
10
+ }
11
+
12
+ const INITIAL_SHOW_COUNT = 50
13
+
14
+ export function ExposedSecretReportRenderer({ data }: ExposedSecretReportRendererProps) {
15
+ const [showAll, setShowAll] = useState(false)
16
+ const [expanded, setExpanded] = useState(true)
17
+
18
+ const report = data.report || {}
19
+ const summary = report.summary || {}
20
+ const secrets = Array.isArray(report.secrets) ? report.secrets : []
21
+ const scanner = report.scanner || {}
22
+ const labels = data.metadata?.labels || {}
23
+
24
+ const critical = summary.criticalCount || 0
25
+ const high = summary.highCount || 0
26
+ const medium = summary.mediumCount || 0
27
+ const low = summary.lowCount || 0
28
+
29
+ const containerName = labels['trivy-operator.container.name'] || '-'
30
+ const image = formatTrivyImage(report)
31
+
32
+ const displayedSecrets = showAll ? secrets : secrets.slice(0, INITIAL_SHOW_COUNT)
33
+
34
+ return (
35
+ <>
36
+ <TrivyAlertBanner critical={critical} high={high} noun="exposed secret" />
37
+
38
+ {/* Report Overview */}
39
+ <Section title="Report Overview" icon={KeyRound}>
40
+ <PropertyList>
41
+ <Property label="Container" value={containerName} />
42
+ <Property label="Image" value={image} />
43
+ <Property label="Scanner" value={scanner.name ? `${scanner.name} ${scanner.version || ''}`.trim() : '-'} />
44
+ <Property label="Scanned" value={report.updateTimestamp ? formatAge(report.updateTimestamp) + ' ago' : '-'} />
45
+ </PropertyList>
46
+ </Section>
47
+
48
+ {/* Severity Summary */}
49
+ <Section title="Summary">
50
+ <div className="flex flex-wrap gap-2">
51
+ {[
52
+ { label: 'Critical', count: critical, key: 'CRITICAL' },
53
+ { label: 'High', count: high, key: 'HIGH' },
54
+ { label: 'Medium', count: medium, key: 'MEDIUM' },
55
+ { label: 'Low', count: low, key: 'LOW' },
56
+ ].filter(({ count }) => count > 0).map(({ label, count, key }) => (
57
+ <span key={key} className={clsx('px-2 py-0.5 rounded text-xs font-medium', SEVERITY_BADGE_COLORS[key])}>
58
+ {count} {label}
59
+ </span>
60
+ ))}
61
+ {secrets.length === 0 && (
62
+ <span className="px-2 py-0.5 rounded text-xs font-medium bg-green-500/20 text-green-400">No secrets found</span>
63
+ )}
64
+ </div>
65
+ </Section>
66
+
67
+ {/* Secrets Table */}
68
+ {secrets.length > 0 && (
69
+ <Section title="Exposed Secrets">
70
+ <button
71
+ onClick={() => setExpanded(!expanded)}
72
+ className="flex items-center gap-1 text-xs text-theme-text-secondary hover:text-theme-text-primary mb-2"
73
+ >
74
+ {expanded ? <ChevronDown className="w-3.5 h-3.5" /> : <ChevronRight className="w-3.5 h-3.5" />}
75
+ {secrets.length} secret{secrets.length !== 1 ? 's' : ''}
76
+ </button>
77
+ {expanded && (
78
+ <div className="overflow-x-auto -mx-1">
79
+ <table className="w-full text-xs">
80
+ <thead>
81
+ <tr className="border-b border-theme-border text-theme-text-tertiary">
82
+ <th className="text-left py-1.5 px-1 font-medium">Rule</th>
83
+ <th className="text-left py-1.5 px-1 font-medium">Severity</th>
84
+ <th className="text-left py-1.5 px-1 font-medium">Category</th>
85
+ <th className="text-left py-1.5 px-1 font-medium">Target</th>
86
+ <th className="text-left py-1.5 px-1 font-medium">Match</th>
87
+ </tr>
88
+ </thead>
89
+ <tbody>
90
+ {displayedSecrets.map((secret: any, i: number) => (
91
+ <tr key={`${secret.ruleID || i}-${i}`} className="border-b border-theme-border/50 hover:bg-theme-hover/50">
92
+ <td className="py-1.5 px-1 text-theme-text-secondary font-mono">{secret.ruleID || '-'}</td>
93
+ <td className="py-1.5 px-1">
94
+ <span className={clsx('px-1.5 py-0.5 rounded text-[10px] font-medium', SEVERITY_BADGE_COLORS[secret.severity] || 'bg-gray-500/20 text-gray-400')}>
95
+ {secret.severity || '-'}
96
+ </span>
97
+ </td>
98
+ <td className="py-1.5 px-1 text-theme-text-secondary">{secret.category || secret.title || '-'}</td>
99
+ <td className="py-1.5 px-1 text-theme-text-secondary max-w-[180px] truncate font-mono" title={secret.target}>{secret.target || '-'}</td>
100
+ <td className="py-1.5 px-1 text-theme-text-tertiary max-w-[150px] truncate font-mono" title={secret.match}>{secret.match || '-'}</td>
101
+ </tr>
102
+ ))}
103
+ </tbody>
104
+ </table>
105
+ {!showAll && secrets.length > INITIAL_SHOW_COUNT && (
106
+ <button
107
+ onClick={() => setShowAll(true)}
108
+ className="mt-2 text-xs text-blue-400 hover:text-blue-300 hover:underline"
109
+ >
110
+ Show all {secrets.length} secrets
111
+ </button>
112
+ )}
113
+ </div>
114
+ )}
115
+ </Section>
116
+ )}
117
+ </>
118
+ )
119
+ }
@@ -0,0 +1,172 @@
1
+ import { KeyRound, RefreshCw, CloudCog } from 'lucide-react'
2
+ import { Section, PropertyList, Property, ConditionsSection, AlertBanner, ResourceLink } from '../../ui/drawer-components'
3
+ import {
4
+ getExternalSecretStatus,
5
+ getExternalSecretStore,
6
+ getExternalSecretRefreshInterval,
7
+ getExternalSecretLastSync,
8
+ getExternalSecretTargetName,
9
+ getExternalSecretTargetCreationPolicy,
10
+ getExternalSecretTargetDeletionPolicy,
11
+ getExternalSecretDataMappings,
12
+ getExternalSecretDataFromSources,
13
+ } from '../resource-utils-eso'
14
+
15
+ interface ExternalSecretRendererProps {
16
+ data: any
17
+ onNavigate?: (ref: { kind: string; namespace: string; name: string }) => void
18
+ }
19
+
20
+ export function ExternalSecretRenderer({ data, onNavigate }: ExternalSecretRendererProps) {
21
+ const status = data.status || {}
22
+ const conditions = status.conditions || []
23
+
24
+ const esStatus = getExternalSecretStatus(data)
25
+ const store = getExternalSecretStore(data)
26
+ const dataMappings = getExternalSecretDataMappings(data)
27
+ const dataFromSources = getExternalSecretDataFromSources(data)
28
+ const targetTemplate = data.spec?.target?.template
29
+
30
+ // Problem detection
31
+ const readyCond = conditions.find((c: any) => c.type === 'Ready')
32
+ const isNotReady = readyCond?.status === 'False'
33
+
34
+ return (
35
+ <>
36
+ {/* Alert if not synced */}
37
+ {isNotReady && (
38
+ <AlertBanner
39
+ variant="error"
40
+ title="ExternalSecret Not Synced"
41
+ message={readyCond?.message || 'The ExternalSecret is not in a synced state.'}
42
+ />
43
+ )}
44
+
45
+ {/* Status section */}
46
+ <Section title="Sync Status" icon={RefreshCw} defaultExpanded>
47
+ <PropertyList>
48
+ <Property label="Status" value={esStatus.text} />
49
+ <Property label="Last Sync" value={getExternalSecretLastSync(data)} />
50
+ <Property label="Refresh Interval" value={getExternalSecretRefreshInterval(data)} />
51
+ <Property label="Target Secret" value={(() => {
52
+ const targetName = getExternalSecretTargetName(data)
53
+ if (targetName && targetName !== '-') {
54
+ return (
55
+ <ResourceLink
56
+ name={targetName}
57
+ kind="secrets"
58
+ namespace={data.metadata?.namespace || ''}
59
+ onNavigate={onNavigate}
60
+ />
61
+ )
62
+ }
63
+ return targetName
64
+ })()} />
65
+ {status.syncedResourceVersion && (
66
+ <Property label="Synced Version" value={status.syncedResourceVersion} />
67
+ )}
68
+ {status.binding?.name && (
69
+ <Property label="Binding" value={status.binding.name} />
70
+ )}
71
+ </PropertyList>
72
+ </Section>
73
+
74
+ {/* Store Reference */}
75
+ <Section title="Store Reference" icon={CloudCog} defaultExpanded>
76
+ <PropertyList>
77
+ <Property label="Store Name" value={(() => {
78
+ if (store.name && store.name !== '-') {
79
+ const storeKind = store.kind === 'ClusterSecretStore'
80
+ ? 'clustersecretstores'
81
+ : 'secretstores'
82
+ return (
83
+ <ResourceLink
84
+ name={store.name}
85
+ kind={storeKind}
86
+ namespace={store.kind === 'ClusterSecretStore' ? '' : (data.metadata?.namespace || '')}
87
+ onNavigate={onNavigate}
88
+ />
89
+ )
90
+ }
91
+ return store.name
92
+ })()} />
93
+ <Property label="Store Kind" value={store.kind} />
94
+ </PropertyList>
95
+ </Section>
96
+
97
+ {/* Secret Mappings - data[] */}
98
+ {dataMappings.length > 0 && (
99
+ <Section title={`Secret Mappings (${dataMappings.length})`} icon={KeyRound} defaultExpanded>
100
+ <div className="overflow-x-auto">
101
+ <table className="w-full text-sm">
102
+ <thead>
103
+ <tr className="text-theme-text-secondary text-left text-xs uppercase tracking-wider">
104
+ <th className="pb-2 pr-3 font-medium">Secret Key</th>
105
+ <th className="pb-2 pr-3 font-medium">Remote Key</th>
106
+ <th className="pb-2 font-medium">Property</th>
107
+ </tr>
108
+ </thead>
109
+ <tbody>
110
+ {dataMappings.map((mapping, i) => (
111
+ <tr key={i} className="border-t border-theme-border/50">
112
+ <td className="py-1.5 pr-3 text-theme-text-primary font-mono text-xs">{mapping.secretKey || '-'}</td>
113
+ <td className="py-1.5 pr-3 text-theme-text-secondary font-mono text-xs break-all">{mapping.remoteKey || '-'}</td>
114
+ <td className="py-1.5 text-theme-text-secondary text-xs">
115
+ {mapping.remoteProperty || '-'}
116
+ {mapping.remoteVersion && (
117
+ <span className="ml-1 text-theme-text-tertiary">v{mapping.remoteVersion}</span>
118
+ )}
119
+ </td>
120
+ </tr>
121
+ ))}
122
+ </tbody>
123
+ </table>
124
+ </div>
125
+ </Section>
126
+ )}
127
+
128
+ {/* DataFrom sources */}
129
+ {dataFromSources.length > 0 && (
130
+ <Section title={`Data Sources (${dataFromSources.length})`} defaultExpanded>
131
+ <div className="space-y-1">
132
+ {dataFromSources.map((source, i) => (
133
+ <div key={i} className="bg-theme-elevated/30 rounded p-2">
134
+ <div className="flex items-center gap-2 text-sm">
135
+ <span className="px-1.5 py-0.5 bg-theme-hover rounded text-xs text-theme-text-secondary font-medium">
136
+ {source.type}
137
+ </span>
138
+ {source.details && (
139
+ <span className="text-theme-text-tertiary text-xs font-mono break-all">{source.details}</span>
140
+ )}
141
+ </div>
142
+ </div>
143
+ ))}
144
+ </div>
145
+ </Section>
146
+ )}
147
+
148
+ {/* Target Configuration */}
149
+ <Section title="Target Configuration" defaultExpanded={false}>
150
+ <PropertyList>
151
+ <Property label="Target Name" value={getExternalSecretTargetName(data)} />
152
+ <Property label="Creation Policy" value={getExternalSecretTargetCreationPolicy(data)} />
153
+ <Property label="Deletion Policy" value={getExternalSecretTargetDeletionPolicy(data)} />
154
+ {targetTemplate?.type && (
155
+ <Property label="Template Type" value={targetTemplate.type} />
156
+ )}
157
+ {targetTemplate?.engineVersion && (
158
+ <Property label="Engine Version" value={targetTemplate.engineVersion} />
159
+ )}
160
+ {targetTemplate?.metadata?.labels && (
161
+ <Property label="Template Labels" value={Object.entries(targetTemplate.metadata.labels).map(([k, v]) => `${k}=${v}`).join(', ')} />
162
+ )}
163
+ {targetTemplate?.metadata?.annotations && (
164
+ <Property label="Template Annotations" value={Object.entries(targetTemplate.metadata.annotations).map(([k, v]) => `${k}=${v}`).join(', ')} />
165
+ )}
166
+ </PropertyList>
167
+ </Section>
168
+
169
+ <ConditionsSection conditions={conditions} />
170
+ </>
171
+ )
172
+ }
@@ -0,0 +1,242 @@
1
+ import { Package, Settings, CheckCircle2, History } 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, SyncCountdown } from '../../gitops'
6
+ import { fluxConditionsToGitOpsStatus, type FluxCondition } from '../../../types/gitops'
7
+
8
+ interface FluxHelmReleaseRendererProps {
9
+ data: any
10
+ }
11
+
12
+ export function FluxHelmReleaseRenderer({ data }: FluxHelmReleaseRendererProps) {
13
+ const status = data.status || {}
14
+ const spec = data.spec || {}
15
+ const conditions = (status.conditions || []) as FluxCondition[]
16
+ const history = status.history || []
17
+
18
+ // Convert to unified GitOps status
19
+ const gitOpsStatus = fluxConditionsToGitOpsStatus(conditions, spec.suspend === true)
20
+
21
+ // Problem detection
22
+ const problems: Array<{ color: 'red' | 'yellow'; message: string }> = []
23
+
24
+ if (gitOpsStatus.suspended) {
25
+ problems.push({ color: 'yellow', message: 'HelmRelease is suspended' })
26
+ }
27
+
28
+ if (gitOpsStatus.health === 'Degraded' && gitOpsStatus.message) {
29
+ problems.push({ color: 'red', message: gitOpsStatus.message })
30
+ }
31
+
32
+ // Check for test failures
33
+ const testFailCondition = conditions.find(
34
+ (c) => c.type === 'TestSuccess' && c.status === 'False'
35
+ )
36
+ if (testFailCondition) {
37
+ problems.push({
38
+ color: 'yellow',
39
+ message: testFailCondition.message || 'Helm tests failed',
40
+ })
41
+ }
42
+
43
+ // Chart reference
44
+ const chartRef = spec.chart?.spec || {}
45
+ const sourceRef = chartRef.sourceRef || {}
46
+
47
+ // Current release info
48
+ const lastRelease = history.length > 0 ? history[0] : null
49
+
50
+ return (
51
+ <>
52
+ <ProblemAlerts problems={problems} />
53
+
54
+ {/* Status section */}
55
+ <Section title="Status">
56
+ <div className="space-y-3">
57
+ <GitOpsStatusBadge status={gitOpsStatus} />
58
+ {spec.interval && (
59
+ <SyncCountdown
60
+ interval={spec.interval}
61
+ lastSyncTime={status.lastHandledReconcileAt}
62
+ suspended={gitOpsStatus.suspended}
63
+ />
64
+ )}
65
+ </div>
66
+ </Section>
67
+
68
+ {/* Chart section */}
69
+ <Section title="Chart" icon={Package}>
70
+ <PropertyList>
71
+ <Property label="Chart" value={chartRef.chart} />
72
+ <Property label="Version" value={chartRef.version || '*'} />
73
+ <Property label="Source Kind" value={sourceRef.kind} />
74
+ <Property label="Source Name" value={sourceRef.name} />
75
+ {sourceRef.namespace && (
76
+ <Property label="Source Namespace" value={sourceRef.namespace} />
77
+ )}
78
+ <Property
79
+ label="Reconcile Strategy"
80
+ value={chartRef.reconcileStrategy || 'ChartVersion'}
81
+ />
82
+ </PropertyList>
83
+ </Section>
84
+
85
+ {/* Release Configuration */}
86
+ <Section title="Configuration" icon={Settings}>
87
+ <PropertyList>
88
+ <Property label="Release Name" value={spec.releaseName || data.metadata?.name} />
89
+ <Property label="Target Namespace" value={spec.targetNamespace || data.metadata?.namespace} />
90
+ {spec.timeout && <Property label="Timeout" value={spec.timeout} />}
91
+ {spec.serviceAccountName && (
92
+ <Property label="Service Account" value={spec.serviceAccountName} />
93
+ )}
94
+ {spec.maxHistory !== undefined && (
95
+ <Property label="Max History" value={spec.maxHistory} />
96
+ )}
97
+ </PropertyList>
98
+ </Section>
99
+
100
+ {/* Install/Upgrade settings */}
101
+ {(spec.install || spec.upgrade) && (
102
+ <Section title="Install/Upgrade Settings" defaultExpanded={false}>
103
+ <PropertyList>
104
+ {spec.install && (
105
+ <>
106
+ <Property
107
+ label="Create Namespace"
108
+ value={spec.install.createNamespace ? 'Yes' : 'No'}
109
+ />
110
+ <Property
111
+ label="Replace CRDs"
112
+ value={spec.install.crds || 'Skip'}
113
+ />
114
+ {spec.install.remediation && (
115
+ <Property
116
+ label="Install Retries"
117
+ value={spec.install.remediation.retries}
118
+ />
119
+ )}
120
+ </>
121
+ )}
122
+ {spec.upgrade && (
123
+ <>
124
+ <Property
125
+ label="Cleanup on Fail"
126
+ value={spec.upgrade.cleanupOnFail ? 'Yes' : 'No'}
127
+ />
128
+ <Property
129
+ label="Force Upgrade"
130
+ value={spec.upgrade.force ? 'Yes' : 'No'}
131
+ />
132
+ {spec.upgrade.remediation && (
133
+ <>
134
+ <Property
135
+ label="Upgrade Retries"
136
+ value={spec.upgrade.remediation.retries}
137
+ />
138
+ <Property
139
+ label="Rollback on Fail"
140
+ value={spec.upgrade.remediation.remediateLastFailure ? 'Yes' : 'No'}
141
+ />
142
+ </>
143
+ )}
144
+ </>
145
+ )}
146
+ </PropertyList>
147
+ </Section>
148
+ )}
149
+
150
+ {/* Current Release */}
151
+ {lastRelease && (
152
+ <Section title="Current Release" icon={CheckCircle2}>
153
+ <PropertyList>
154
+ <Property label="Revision" value={lastRelease.revision} />
155
+ <Property label="Chart Version" value={lastRelease.chartVersion} />
156
+ <Property label="App Version" value={lastRelease.appVersion} />
157
+ <Property label="Status" value={lastRelease.status} />
158
+ {lastRelease.firstDeployed && (
159
+ <Property
160
+ label="First Deployed"
161
+ value={formatAge(lastRelease.firstDeployed)}
162
+ />
163
+ )}
164
+ {lastRelease.lastDeployed && (
165
+ <Property
166
+ label="Last Deployed"
167
+ value={formatAge(lastRelease.lastDeployed)}
168
+ />
169
+ )}
170
+ </PropertyList>
171
+ </Section>
172
+ )}
173
+
174
+ {/* Release History */}
175
+ {history.length > 1 && (
176
+ <Section title={`History (${history.length} revisions)`} icon={History} defaultExpanded={false}>
177
+ <div className="space-y-2 max-h-48 overflow-y-auto">
178
+ {history.map((release: any, idx: number) => (
179
+ <div
180
+ key={idx}
181
+ className={clsx(
182
+ 'p-2 rounded text-sm',
183
+ idx === 0
184
+ ? 'bg-green-500/10 border border-green-500/30'
185
+ : 'bg-theme-elevated/30'
186
+ )}
187
+ >
188
+ <div className="flex items-center justify-between">
189
+ <span className="font-medium text-theme-text-primary">
190
+ Rev {release.revision}
191
+ </span>
192
+ <span
193
+ className={clsx(
194
+ 'px-1.5 py-0.5 rounded text-xs',
195
+ release.status === 'deployed'
196
+ ? 'status-healthy'
197
+ : release.status === 'failed'
198
+ ? 'status-unhealthy'
199
+ : 'status-neutral'
200
+ )}
201
+ >
202
+ {release.status}
203
+ </span>
204
+ </div>
205
+ <div className="text-xs text-theme-text-secondary mt-1">
206
+ {release.chartVersion}
207
+ {release.appVersion && ` (app: ${release.appVersion})`}
208
+ </div>
209
+ </div>
210
+ ))}
211
+ </div>
212
+ </Section>
213
+ )}
214
+
215
+ {/* Revision info */}
216
+ {(status.helmChart || status.lastAppliedRevision || status.lastAttemptedRevision) && (
217
+ <Section title="Revision Info" defaultExpanded={false}>
218
+ <PropertyList>
219
+ {status.helmChart && (
220
+ <Property label="Helm Chart" value={status.helmChart} />
221
+ )}
222
+ {status.lastAppliedRevision && (
223
+ <Property label="Applied Revision" value={status.lastAppliedRevision} />
224
+ )}
225
+ {status.lastAttemptedRevision && (
226
+ <Property label="Attempted Revision" value={status.lastAttemptedRevision} />
227
+ )}
228
+ {status.lastHandledReconcileAt && (
229
+ <Property
230
+ label="Last Reconciled"
231
+ value={formatAge(status.lastHandledReconcileAt)}
232
+ />
233
+ )}
234
+ </PropertyList>
235
+ </Section>
236
+ )}
237
+
238
+ {/* Conditions section */}
239
+ <ConditionsSection conditions={conditions} />
240
+ </>
241
+ )
242
+ }