@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.
- package/package.json +63 -0
- package/src/components/dock/BottomDock.tsx +204 -0
- package/src/components/dock/DockContext.tsx +212 -0
- package/src/components/dock/TerminalTab.tsx +333 -0
- package/src/components/dock/index.ts +3 -0
- package/src/components/gitops/GitOpsActions.tsx +227 -0
- package/src/components/gitops/GitOpsStatusBadge.tsx +221 -0
- package/src/components/gitops/ManagedResourcesList.tsx +210 -0
- package/src/components/gitops/SyncCountdown.tsx +146 -0
- package/src/components/gitops/index.ts +4 -0
- package/src/components/logs/JsonLogLine.tsx +90 -0
- package/src/components/logs/LogCore.tsx +553 -0
- package/src/components/logs/LogToolbarSelects.tsx +74 -0
- package/src/components/logs/LogsViewer.tsx +146 -0
- package/src/components/logs/WorkloadLogsViewer.tsx +263 -0
- package/src/components/logs/index.ts +13 -0
- package/src/components/logs/useLogBuffer.ts +138 -0
- package/src/components/logs/useLogSearch.ts +151 -0
- package/src/components/logs/useLogStream.ts +61 -0
- package/src/components/resources/ResourcesView.tsx +5494 -0
- package/src/components/resources/index.ts +17 -0
- package/src/components/resources/renderers/AlertRenderer.tsx +166 -0
- package/src/components/resources/renderers/ArgoApplicationRenderer.tsx +233 -0
- package/src/components/resources/renderers/CNPGBackupRenderer.tsx +89 -0
- package/src/components/resources/renderers/CNPGClusterRenderer.tsx +194 -0
- package/src/components/resources/renderers/CNPGPoolerRenderer.tsx +79 -0
- package/src/components/resources/renderers/CNPGScheduledBackupRenderer.tsx +68 -0
- package/src/components/resources/renderers/CertificateRenderer.tsx +209 -0
- package/src/components/resources/renderers/CertificateRequestRenderer.tsx +123 -0
- package/src/components/resources/renderers/ChallengeRenderer.tsx +111 -0
- package/src/components/resources/renderers/ClusterComplianceReportRenderer.tsx +164 -0
- package/src/components/resources/renderers/ClusterExternalSecretRenderer.tsx +140 -0
- package/src/components/resources/renderers/ClusterIssuerRenderer.tsx +153 -0
- package/src/components/resources/renderers/ConfigAuditReportRenderer.tsx +221 -0
- package/src/components/resources/renderers/ConfigMapRenderer.tsx +124 -0
- package/src/components/resources/renderers/CronJobRenderer.tsx +86 -0
- package/src/components/resources/renderers/EventRenderer.tsx +175 -0
- package/src/components/resources/renderers/ExposedSecretReportRenderer.tsx +119 -0
- package/src/components/resources/renderers/ExternalSecretRenderer.tsx +172 -0
- package/src/components/resources/renderers/FluxHelmReleaseRenderer.tsx +242 -0
- package/src/components/resources/renderers/GRPCRouteRenderer.tsx +263 -0
- package/src/components/resources/renderers/GatewayClassRenderer.tsx +68 -0
- package/src/components/resources/renderers/GatewayRenderer.tsx +218 -0
- package/src/components/resources/renderers/GenericRenderer.tsx +476 -0
- package/src/components/resources/renderers/GitRepositoryRenderer.tsx +154 -0
- package/src/components/resources/renderers/HPARenderer.tsx +164 -0
- package/src/components/resources/renderers/HTTPRouteRenderer.tsx +303 -0
- package/src/components/resources/renderers/HelmRepositoryRenderer.tsx +167 -0
- package/src/components/resources/renderers/IngressClassRenderer.tsx +49 -0
- package/src/components/resources/renderers/IngressRenderer.tsx +104 -0
- package/src/components/resources/renderers/IstioAuthorizationPolicyRenderer.tsx +175 -0
- package/src/components/resources/renderers/IstioDestinationRuleRenderer.tsx +176 -0
- package/src/components/resources/renderers/IstioGatewayRenderer.tsx +125 -0
- package/src/components/resources/renderers/IstioPeerAuthenticationRenderer.tsx +84 -0
- package/src/components/resources/renderers/IstioServiceEntryRenderer.tsx +112 -0
- package/src/components/resources/renderers/IstioVirtualServiceRenderer.tsx +288 -0
- package/src/components/resources/renderers/JobRenderer.tsx +103 -0
- package/src/components/resources/renderers/KarpenterEC2NodeClassRenderer.tsx +157 -0
- package/src/components/resources/renderers/KarpenterNodeClaimRenderer.tsx +188 -0
- package/src/components/resources/renderers/KarpenterNodePoolRenderer.tsx +148 -0
- package/src/components/resources/renderers/KedaScaledJobRenderer.tsx +90 -0
- package/src/components/resources/renderers/KedaScaledObjectRenderer.tsx +136 -0
- package/src/components/resources/renderers/KedaTriggerAuthRenderer.tsx +151 -0
- package/src/components/resources/renderers/KnativeConfigurationRenderer.tsx +70 -0
- package/src/components/resources/renderers/KnativeEventingRenderer.tsx +309 -0
- package/src/components/resources/renderers/KnativeFlowRenderer.tsx +142 -0
- package/src/components/resources/renderers/KnativeNetworkingRenderer.tsx +232 -0
- package/src/components/resources/renderers/KnativeRevisionRenderer.tsx +97 -0
- package/src/components/resources/renderers/KnativeRouteRenderer.tsx +74 -0
- package/src/components/resources/renderers/KnativeServiceRenderer.tsx +138 -0
- package/src/components/resources/renderers/KnativeSourceRenderer.tsx +220 -0
- package/src/components/resources/renderers/KustomizationRenderer.tsx +151 -0
- package/src/components/resources/renderers/KyvernoPolicyReportRenderer.tsx +352 -0
- package/src/components/resources/renderers/LeaseRenderer.tsx +93 -0
- package/src/components/resources/renderers/NetworkPolicyRenderer.tsx +208 -0
- package/src/components/resources/renderers/NodeRenderer.tsx +282 -0
- package/src/components/resources/renderers/OCIRepositoryRenderer.tsx +153 -0
- package/src/components/resources/renderers/OrderRenderer.tsx +108 -0
- package/src/components/resources/renderers/PVCRenderer.tsx +98 -0
- package/src/components/resources/renderers/PersistentVolumeRenderer.tsx +138 -0
- package/src/components/resources/renderers/PodDisruptionBudgetRenderer.tsx +126 -0
- package/src/components/resources/renderers/PodMonitorRenderer.tsx +63 -0
- package/src/components/resources/renderers/PodRenderer.tsx +677 -0
- package/src/components/resources/renderers/PriorityClassRenderer.tsx +50 -0
- package/src/components/resources/renderers/PrometheusRuleRenderer.tsx +62 -0
- package/src/components/resources/renderers/ReplicaSetRenderer.tsx +71 -0
- package/src/components/resources/renderers/RoleBindingRenderer.tsx +97 -0
- package/src/components/resources/renderers/RoleRenderer.tsx +192 -0
- package/src/components/resources/renderers/RolloutRenderer.tsx +227 -0
- package/src/components/resources/renderers/RuntimeClassRenderer.tsx +75 -0
- package/src/components/resources/renderers/SbomReportRenderer.tsx +98 -0
- package/src/components/resources/renderers/SealedSecretRenderer.tsx +121 -0
- package/src/components/resources/renderers/SecretRenderer.tsx +321 -0
- package/src/components/resources/renderers/SecretStoreRenderer.tsx +111 -0
- package/src/components/resources/renderers/ServiceAccountRenderer.tsx +81 -0
- package/src/components/resources/renderers/ServiceMonitorRenderer.tsx +65 -0
- package/src/components/resources/renderers/ServiceRenderer.tsx +98 -0
- package/src/components/resources/renderers/SimpleRouteRenderer.tsx +195 -0
- package/src/components/resources/renderers/StorageClassRenderer.tsx +82 -0
- package/src/components/resources/renderers/TraefikIngressRouteRenderer.tsx +257 -0
- package/src/components/resources/renderers/VPARenderer.tsx +193 -0
- package/src/components/resources/renderers/VeleroBSLRenderer.tsx +73 -0
- package/src/components/resources/renderers/VeleroBackupRenderer.tsx +193 -0
- package/src/components/resources/renderers/VeleroRestoreRenderer.tsx +161 -0
- package/src/components/resources/renderers/VeleroScheduleRenderer.tsx +134 -0
- package/src/components/resources/renderers/VeleroVSLRenderer.tsx +36 -0
- package/src/components/resources/renderers/VulnerabilityReportRenderer.tsx +347 -0
- package/src/components/resources/renderers/WebhookConfigRenderer.tsx +156 -0
- package/src/components/resources/renderers/WorkflowRenderer.tsx +202 -0
- package/src/components/resources/renderers/WorkflowTemplateRenderer.tsx +91 -0
- package/src/components/resources/renderers/WorkloadRenderer.tsx +311 -0
- package/src/components/resources/renderers/argo-cells.tsx +118 -0
- package/src/components/resources/renderers/certmanager-cells.tsx +164 -0
- package/src/components/resources/renderers/cnpg-cells.tsx +161 -0
- package/src/components/resources/renderers/eso-cells.tsx +115 -0
- package/src/components/resources/renderers/flux-cells.tsx +253 -0
- package/src/components/resources/renderers/index.ts +105 -0
- package/src/components/resources/renderers/istio-cells.tsx +210 -0
- package/src/components/resources/renderers/karpenter-cells.tsx +107 -0
- package/src/components/resources/renderers/keda-cells.tsx +94 -0
- package/src/components/resources/renderers/knative-cells.tsx +396 -0
- package/src/components/resources/renderers/kyverno-cells.tsx +85 -0
- package/src/components/resources/renderers/prometheus-cells.tsx +74 -0
- package/src/components/resources/renderers/traefik-cells.tsx +120 -0
- package/src/components/resources/renderers/trivy-cells.tsx +153 -0
- package/src/components/resources/renderers/trivy-shared.tsx +67 -0
- package/src/components/resources/renderers/velero-cells.tsx +164 -0
- package/src/components/resources/resource-utils-argo.ts +155 -0
- package/src/components/resources/resource-utils-certmanager.ts +198 -0
- package/src/components/resources/resource-utils-cnpg.ts +344 -0
- package/src/components/resources/resource-utils-eso.ts +385 -0
- package/src/components/resources/resource-utils-flux.ts +186 -0
- package/src/components/resources/resource-utils-istio.ts +372 -0
- package/src/components/resources/resource-utils-karpenter.ts +150 -0
- package/src/components/resources/resource-utils-keda.ts +203 -0
- package/src/components/resources/resource-utils-knative.ts +218 -0
- package/src/components/resources/resource-utils-kyverno.ts +224 -0
- package/src/components/resources/resource-utils-prometheus.ts +159 -0
- package/src/components/resources/resource-utils-traefik.ts +180 -0
- package/src/components/resources/resource-utils-trivy.ts +146 -0
- package/src/components/resources/resource-utils-velero.ts +293 -0
- package/src/components/resources/resource-utils.ts +1558 -0
- package/src/components/shared/EditableYamlView.tsx +302 -0
- package/src/components/shared/ResourceActionsBar.tsx +924 -0
- package/src/components/shared/ResourceRendererDispatch.tsx +557 -0
- package/src/components/shared/index.ts +3 -0
- package/src/components/timeline/index.ts +1 -0
- package/src/components/timeline/shared.tsx +614 -0
- package/src/components/topology/GroupNode.tsx +198 -0
- package/src/components/topology/K8sResourceNode.tsx +440 -0
- package/src/components/topology/TopologyControls.tsx +62 -0
- package/src/components/topology/TopologyFilterSidebar.tsx +377 -0
- package/src/components/topology/TopologyGraph.tsx +748 -0
- package/src/components/topology/TopologySearch.tsx +304 -0
- package/src/components/topology/index.ts +7 -0
- package/src/components/topology/layout.ts +734 -0
- package/src/components/topology/layout.worker.ts +272 -0
- package/src/components/topology/topology.css +129 -0
- package/src/components/ui/CodeViewer.tsx +451 -0
- package/src/components/ui/ConfirmDialog.tsx +188 -0
- package/src/components/ui/ForceDeleteConfirmDialog.tsx +57 -0
- package/src/components/ui/HealthRing.tsx +72 -0
- package/src/components/ui/MetricsChart.tsx +259 -0
- package/src/components/ui/ResourceBar.tsx +68 -0
- package/src/components/ui/Toast.tsx +225 -0
- package/src/components/ui/Tooltip.tsx +165 -0
- package/src/components/ui/YamlEditor.tsx +280 -0
- package/src/components/ui/drawer-components.tsx +792 -0
- package/src/components/ui/index.ts +10 -0
- package/src/components/workload/ResourceDetailDrawer.tsx +158 -0
- package/src/components/workload/WorkloadView.tsx +1109 -0
- package/src/components/workload/index.ts +2 -0
- package/src/hooks/index.ts +9 -0
- package/src/hooks/useAnimatedUnmount.ts +30 -0
- package/src/hooks/useKeyboardShortcuts.tsx +336 -0
- package/src/hooks/useRefreshAnimation.ts +58 -0
- package/src/index.ts +35 -0
- package/src/types/core.ts +894 -0
- package/src/types/gitops.ts +458 -0
- package/src/types/index.ts +2 -0
- package/src/utils/animation.ts +40 -0
- package/src/utils/api-resources.ts +185 -0
- package/src/utils/badge-colors.ts +399 -0
- package/src/utils/context-name.ts +72 -0
- package/src/utils/download.ts +19 -0
- package/src/utils/format.ts +233 -0
- package/src/utils/index.ts +10 -0
- package/src/utils/log-format.ts +300 -0
- package/src/utils/navigation.ts +119 -0
- package/src/utils/resource-hierarchy.ts +630 -0
- package/src/utils/resource-icons.ts +206 -0
- package/src/vite-env.d.ts +1 -0
- package/tsconfig.json +24 -0
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
import { memo, useMemo, useState } from 'react'
|
|
2
|
+
import {
|
|
3
|
+
ChevronLeft,
|
|
4
|
+
ChevronRight,
|
|
5
|
+
Eye,
|
|
6
|
+
EyeOff,
|
|
7
|
+
AlertTriangle,
|
|
8
|
+
Zap
|
|
9
|
+
} from 'lucide-react'
|
|
10
|
+
import type { LucideIcon } from 'lucide-react'
|
|
11
|
+
import { clsx } from 'clsx'
|
|
12
|
+
import type { NodeKind, TopologyNode } from '../../types'
|
|
13
|
+
import { getTopologyIcon } from '../../utils/resource-icons'
|
|
14
|
+
|
|
15
|
+
// Resource kind configuration
|
|
16
|
+
const RESOURCE_KINDS: {
|
|
17
|
+
kind: NodeKind
|
|
18
|
+
label: string
|
|
19
|
+
icon: LucideIcon
|
|
20
|
+
color: string
|
|
21
|
+
category: 'gitops' | 'workloads' | 'networking' | 'config' | 'scaling' | 'custom'
|
|
22
|
+
}[] = [
|
|
23
|
+
// GitOps (ArgoCD + FluxCD)
|
|
24
|
+
{ kind: 'Application', label: 'Application', icon: getTopologyIcon('Application'), color: 'text-orange-400', category: 'gitops' },
|
|
25
|
+
{ kind: 'Kustomization', label: 'Kustomization', icon: getTopologyIcon('Kustomization'), color: 'text-sky-400', category: 'gitops' },
|
|
26
|
+
{ kind: 'HelmRelease', label: 'HelmRelease', icon: getTopologyIcon('HelmRelease'), color: 'text-sky-400', category: 'gitops' },
|
|
27
|
+
{ kind: 'GitRepository', label: 'GitRepository', icon: getTopologyIcon('GitRepository'), color: 'text-teal-400', category: 'gitops' },
|
|
28
|
+
|
|
29
|
+
// Networking
|
|
30
|
+
{ kind: 'Ingress', label: 'Ingress', icon: getTopologyIcon('Ingress'), color: 'text-purple-400', category: 'networking' },
|
|
31
|
+
{ kind: 'Gateway', label: 'Gateway', icon: getTopologyIcon('Gateway'), color: 'text-purple-400', category: 'networking' },
|
|
32
|
+
{ kind: 'HTTPRoute', label: 'HTTPRoute', icon: getTopologyIcon('HTTPRoute'), color: 'text-purple-300', category: 'networking' },
|
|
33
|
+
{ kind: 'GRPCRoute', label: 'GRPCRoute', icon: getTopologyIcon('GRPCRoute'), color: 'text-purple-300', category: 'networking' },
|
|
34
|
+
{ kind: 'TCPRoute', label: 'TCPRoute', icon: getTopologyIcon('TCPRoute'), color: 'text-purple-300', category: 'networking' },
|
|
35
|
+
{ kind: 'TLSRoute', label: 'TLSRoute', icon: getTopologyIcon('TLSRoute'), color: 'text-purple-300', category: 'networking' },
|
|
36
|
+
{ kind: 'Service', label: 'Service', icon: getTopologyIcon('Service'), color: 'text-blue-400', category: 'networking' },
|
|
37
|
+
|
|
38
|
+
// Workloads
|
|
39
|
+
{ kind: 'Deployment', label: 'Deployment', icon: getTopologyIcon('Deployment'), color: 'text-emerald-400', category: 'workloads' },
|
|
40
|
+
{ kind: 'Rollout', label: 'Rollout', icon: getTopologyIcon('Rollout'), color: 'text-emerald-400', category: 'workloads' },
|
|
41
|
+
{ kind: 'DaemonSet', label: 'DaemonSet', icon: getTopologyIcon('DaemonSet'), color: 'text-teal-400', category: 'workloads' },
|
|
42
|
+
{ kind: 'StatefulSet', label: 'StatefulSet', icon: getTopologyIcon('StatefulSet'), color: 'text-cyan-400', category: 'workloads' },
|
|
43
|
+
{ kind: 'ReplicaSet', label: 'ReplicaSet', icon: getTopologyIcon('ReplicaSet'), color: 'text-green-400', category: 'workloads' },
|
|
44
|
+
{ kind: 'Pod', label: 'Pod', icon: getTopologyIcon('Pod'), color: 'text-lime-400', category: 'workloads' },
|
|
45
|
+
{ kind: 'PodGroup', label: 'Pod Group', icon: getTopologyIcon('PodGroup'), color: 'text-lime-400', category: 'workloads' },
|
|
46
|
+
{ kind: 'Job', label: 'Job', icon: getTopologyIcon('Job'), color: 'text-orange-400', category: 'workloads' },
|
|
47
|
+
{ kind: 'CronJob', label: 'CronJob', icon: getTopologyIcon('CronJob'), color: 'text-orange-300', category: 'workloads' },
|
|
48
|
+
|
|
49
|
+
// Config
|
|
50
|
+
{ kind: 'ConfigMap', label: 'ConfigMap', icon: getTopologyIcon('ConfigMap'), color: 'text-amber-400', category: 'config' },
|
|
51
|
+
{ kind: 'Secret', label: 'Secret', icon: getTopologyIcon('Secret'), color: 'text-red-400', category: 'config' },
|
|
52
|
+
|
|
53
|
+
// Scaling
|
|
54
|
+
{ kind: 'HorizontalPodAutoscaler', label: 'HPA', icon: getTopologyIcon('HorizontalPodAutoscaler'), color: 'text-pink-400', category: 'scaling' },
|
|
55
|
+
|
|
56
|
+
// Knative
|
|
57
|
+
{ kind: 'KnativeService', label: 'Knative Service', icon: getTopologyIcon('KnativeService'), color: 'text-fuchsia-400', category: 'custom' },
|
|
58
|
+
{ kind: 'KnativeConfiguration', label: 'Knative Config', icon: getTopologyIcon('KnativeConfiguration'), color: 'text-fuchsia-400', category: 'custom' },
|
|
59
|
+
{ kind: 'KnativeRevision', label: 'Revision', icon: getTopologyIcon('KnativeRevision'), color: 'text-fuchsia-400', category: 'custom' },
|
|
60
|
+
{ kind: 'KnativeRoute', label: 'Route', icon: getTopologyIcon('KnativeRoute'), color: 'text-fuchsia-400', category: 'custom' },
|
|
61
|
+
{ kind: 'Broker', label: 'Broker', icon: getTopologyIcon('Broker'), color: 'text-fuchsia-400', category: 'custom' },
|
|
62
|
+
{ kind: 'Channel', label: 'Channel', icon: getTopologyIcon('Channel'), color: 'text-fuchsia-400', category: 'custom' },
|
|
63
|
+
{ kind: 'Trigger', label: 'Trigger', icon: getTopologyIcon('Trigger'), color: 'text-fuchsia-400', category: 'custom' },
|
|
64
|
+
{ kind: 'PingSource', label: 'PingSource', icon: getTopologyIcon('PingSource'), color: 'text-fuchsia-400', category: 'custom' },
|
|
65
|
+
{ kind: 'ApiServerSource', label: 'ApiServerSource', icon: getTopologyIcon('ApiServerSource'), color: 'text-fuchsia-400', category: 'custom' },
|
|
66
|
+
{ kind: 'ContainerSource', label: 'ContainerSource', icon: getTopologyIcon('ContainerSource'), color: 'text-fuchsia-400', category: 'custom' },
|
|
67
|
+
{ kind: 'SinkBinding', label: 'SinkBinding', icon: getTopologyIcon('SinkBinding'), color: 'text-fuchsia-400', category: 'custom' },
|
|
68
|
+
|
|
69
|
+
// Traefik
|
|
70
|
+
{ kind: 'IngressRoute', label: 'IngressRoute', icon: getTopologyIcon('IngressRoute'), color: 'text-cyan-400', category: 'networking' },
|
|
71
|
+
{ kind: 'IngressRouteTCP', label: 'TCP Route', icon: getTopologyIcon('IngressRouteTCP'), color: 'text-cyan-400', category: 'networking' },
|
|
72
|
+
{ kind: 'IngressRouteUDP', label: 'UDP Route', icon: getTopologyIcon('IngressRouteUDP'), color: 'text-cyan-400', category: 'networking' },
|
|
73
|
+
{ kind: 'Middleware', label: 'Middleware', icon: getTopologyIcon('Middleware'), color: 'text-cyan-400', category: 'custom' },
|
|
74
|
+
{ kind: 'MiddlewareTCP', label: 'MW TCP', icon: getTopologyIcon('MiddlewareTCP'), color: 'text-cyan-400', category: 'custom' },
|
|
75
|
+
{ kind: 'TraefikService', label: 'Traefik Svc', icon: getTopologyIcon('TraefikService'), color: 'text-cyan-400', category: 'custom' },
|
|
76
|
+
{ kind: 'ServersTransport', label: 'Transport', icon: getTopologyIcon('ServersTransport'), color: 'text-cyan-400', category: 'custom' },
|
|
77
|
+
{ kind: 'ServersTransportTCP', label: 'Transport TCP', icon: getTopologyIcon('ServersTransportTCP'), color: 'text-cyan-400', category: 'custom' },
|
|
78
|
+
{ kind: 'TLSOption', label: 'TLS Option', icon: getTopologyIcon('TLSOption'), color: 'text-cyan-400', category: 'custom' },
|
|
79
|
+
{ kind: 'TLSStore', label: 'TLS Store', icon: getTopologyIcon('TLSStore'), color: 'text-cyan-400', category: 'custom' },
|
|
80
|
+
]
|
|
81
|
+
|
|
82
|
+
const CATEGORIES = [
|
|
83
|
+
{ id: 'gitops', label: 'GitOps' },
|
|
84
|
+
{ id: 'networking', label: 'Networking' },
|
|
85
|
+
{ id: 'workloads', label: 'Workloads' },
|
|
86
|
+
{ id: 'config', label: 'Configuration' },
|
|
87
|
+
{ id: 'scaling', label: 'Scaling' },
|
|
88
|
+
{ id: 'custom', label: 'Custom Resources' },
|
|
89
|
+
] as const
|
|
90
|
+
|
|
91
|
+
interface TopologyFilterSidebarProps {
|
|
92
|
+
nodes: TopologyNode[]
|
|
93
|
+
visibleKinds: Set<NodeKind>
|
|
94
|
+
onToggleKind: (kind: NodeKind) => void
|
|
95
|
+
onShowAll: () => void
|
|
96
|
+
onHideAll: () => void
|
|
97
|
+
collapsed?: boolean
|
|
98
|
+
onToggleCollapse?: () => void
|
|
99
|
+
hiddenKinds?: string[] // Kinds auto-hidden for performance in large clusters
|
|
100
|
+
onEnableHiddenKind?: (kind: string) => void // Callback to re-enable a hidden kind
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export const TopologyFilterSidebar = memo(function TopologyFilterSidebar({
|
|
104
|
+
nodes,
|
|
105
|
+
visibleKinds,
|
|
106
|
+
onToggleKind,
|
|
107
|
+
onShowAll,
|
|
108
|
+
onHideAll,
|
|
109
|
+
collapsed = false,
|
|
110
|
+
onToggleCollapse,
|
|
111
|
+
hiddenKinds = [],
|
|
112
|
+
onEnableHiddenKind,
|
|
113
|
+
}: TopologyFilterSidebarProps) {
|
|
114
|
+
// Track which hidden kinds the user has confirmed to show (performance warning acknowledged)
|
|
115
|
+
const [confirmedKinds, setConfirmedKinds] = useState<Set<string>>(new Set())
|
|
116
|
+
// Track which kind is pending confirmation
|
|
117
|
+
const [pendingConfirmKind, setPendingConfirmKind] = useState<string | null>(null)
|
|
118
|
+
// Count nodes by kind
|
|
119
|
+
const kindCounts = useMemo(() => {
|
|
120
|
+
const counts = new Map<NodeKind, number>()
|
|
121
|
+
for (const node of nodes) {
|
|
122
|
+
counts.set(node.kind, (counts.get(node.kind) || 0) + 1)
|
|
123
|
+
}
|
|
124
|
+
return counts
|
|
125
|
+
}, [nodes])
|
|
126
|
+
|
|
127
|
+
// Filter to only show kinds that exist in the topology
|
|
128
|
+
// Also include dynamic CRD kinds not in RESOURCE_KINDS
|
|
129
|
+
const availableKinds = useMemo(() => {
|
|
130
|
+
// Start with known kinds that exist
|
|
131
|
+
const known = RESOURCE_KINDS.filter(k => kindCounts.has(k.kind))
|
|
132
|
+
|
|
133
|
+
// Find CRD kinds not in RESOURCE_KINDS (dynamic CRDs)
|
|
134
|
+
const knownKindSet = new Set(RESOURCE_KINDS.map(k => k.kind))
|
|
135
|
+
const dynamicKinds: typeof RESOURCE_KINDS = []
|
|
136
|
+
|
|
137
|
+
for (const [kind] of kindCounts) {
|
|
138
|
+
if (!knownKindSet.has(kind) && kind !== 'Internet') {
|
|
139
|
+
dynamicKinds.push({
|
|
140
|
+
kind: kind as NodeKind,
|
|
141
|
+
label: kind, // Use the kind name as label
|
|
142
|
+
icon: getTopologyIcon(kind), // Returns Puzzle for unknown kinds
|
|
143
|
+
color: 'text-gray-400',
|
|
144
|
+
category: 'custom',
|
|
145
|
+
})
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return [...known, ...dynamicKinds]
|
|
150
|
+
}, [kindCounts])
|
|
151
|
+
|
|
152
|
+
// Group by category
|
|
153
|
+
const kindsByCategory = useMemo(() => {
|
|
154
|
+
const grouped = new Map<string, typeof availableKinds>()
|
|
155
|
+
for (const category of CATEGORIES) {
|
|
156
|
+
const kinds = availableKinds.filter(k => k.category === category.id)
|
|
157
|
+
if (kinds.length > 0) {
|
|
158
|
+
grouped.set(category.id, kinds)
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return grouped
|
|
162
|
+
}, [availableKinds])
|
|
163
|
+
|
|
164
|
+
const allVisible = availableKinds.every(k => visibleKinds.has(k.kind))
|
|
165
|
+
const noneVisible = availableKinds.every(k => !visibleKinds.has(k.kind))
|
|
166
|
+
|
|
167
|
+
if (collapsed) {
|
|
168
|
+
return (
|
|
169
|
+
<div className="flex flex-col items-center py-3 px-1 bg-theme-surface/90 backdrop-blur border-r border-theme-border">
|
|
170
|
+
<button
|
|
171
|
+
onClick={onToggleCollapse}
|
|
172
|
+
className="p-2 text-theme-text-secondary hover:text-theme-text-primary hover:bg-theme-elevated rounded-lg transition-colors"
|
|
173
|
+
title="Expand filters"
|
|
174
|
+
>
|
|
175
|
+
<ChevronRight className="w-4 h-4" />
|
|
176
|
+
</button>
|
|
177
|
+
<div className="mt-3 flex flex-col gap-2">
|
|
178
|
+
{availableKinds.slice(0, 6).map(({ kind, icon: Icon, color }) => {
|
|
179
|
+
const isVisible = visibleKinds.has(kind)
|
|
180
|
+
return (
|
|
181
|
+
<button
|
|
182
|
+
key={kind}
|
|
183
|
+
onClick={() => onToggleKind(kind)}
|
|
184
|
+
className={clsx(
|
|
185
|
+
'p-1.5 rounded transition-colors',
|
|
186
|
+
isVisible
|
|
187
|
+
? 'bg-theme-elevated text-theme-text-primary'
|
|
188
|
+
: 'text-theme-text-tertiary hover:text-theme-text-secondary'
|
|
189
|
+
)}
|
|
190
|
+
title={kind}
|
|
191
|
+
>
|
|
192
|
+
<Icon className={clsx('w-4 h-4', isVisible && color)} />
|
|
193
|
+
</button>
|
|
194
|
+
)
|
|
195
|
+
})}
|
|
196
|
+
{availableKinds.length > 6 && (
|
|
197
|
+
<span className="text-xs text-theme-text-tertiary text-center">
|
|
198
|
+
+{availableKinds.length - 6}
|
|
199
|
+
</span>
|
|
200
|
+
)}
|
|
201
|
+
</div>
|
|
202
|
+
</div>
|
|
203
|
+
)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return (
|
|
207
|
+
<div className="w-56 flex flex-col bg-theme-surface/90 backdrop-blur border-r border-theme-border overflow-hidden">
|
|
208
|
+
{/* Header */}
|
|
209
|
+
<div className="flex items-center justify-between px-3 py-2 border-b border-theme-border">
|
|
210
|
+
<span className="text-sm font-medium text-theme-text-secondary">Filters</span>
|
|
211
|
+
<button
|
|
212
|
+
onClick={onToggleCollapse}
|
|
213
|
+
className="p-1 text-theme-text-secondary hover:text-theme-text-primary hover:bg-theme-elevated rounded transition-colors"
|
|
214
|
+
title="Collapse sidebar"
|
|
215
|
+
>
|
|
216
|
+
<ChevronLeft className="w-4 h-4" />
|
|
217
|
+
</button>
|
|
218
|
+
</div>
|
|
219
|
+
|
|
220
|
+
{/* Quick actions */}
|
|
221
|
+
<div className="flex gap-1 px-3 py-2 border-b border-theme-border">
|
|
222
|
+
<button
|
|
223
|
+
onClick={onShowAll}
|
|
224
|
+
disabled={allVisible}
|
|
225
|
+
className={clsx(
|
|
226
|
+
'flex-1 flex items-center justify-center gap-1 px-2 py-1.5 text-xs rounded transition-colors',
|
|
227
|
+
allVisible
|
|
228
|
+
? 'bg-theme-elevated/50 text-theme-text-tertiary cursor-not-allowed'
|
|
229
|
+
: 'bg-theme-elevated text-theme-text-secondary hover:bg-theme-hover hover:text-theme-text-primary'
|
|
230
|
+
)}
|
|
231
|
+
>
|
|
232
|
+
<Eye className="w-3 h-3" />
|
|
233
|
+
Show All
|
|
234
|
+
</button>
|
|
235
|
+
<button
|
|
236
|
+
onClick={onHideAll}
|
|
237
|
+
disabled={noneVisible}
|
|
238
|
+
className={clsx(
|
|
239
|
+
'flex-1 flex items-center justify-center gap-1 px-2 py-1.5 text-xs rounded transition-colors',
|
|
240
|
+
noneVisible
|
|
241
|
+
? 'bg-theme-elevated/50 text-theme-text-tertiary cursor-not-allowed'
|
|
242
|
+
: 'bg-theme-elevated text-theme-text-secondary hover:bg-theme-hover hover:text-theme-text-primary'
|
|
243
|
+
)}
|
|
244
|
+
>
|
|
245
|
+
<EyeOff className="w-3 h-3" />
|
|
246
|
+
Hide All
|
|
247
|
+
</button>
|
|
248
|
+
</div>
|
|
249
|
+
|
|
250
|
+
{/* Kind toggles by category */}
|
|
251
|
+
<div className="flex-1 overflow-y-auto">
|
|
252
|
+
{CATEGORIES.map(category => {
|
|
253
|
+
const kinds = kindsByCategory.get(category.id)
|
|
254
|
+
if (!kinds || kinds.length === 0) return null
|
|
255
|
+
|
|
256
|
+
return (
|
|
257
|
+
<div key={category.id} className="px-2 py-2">
|
|
258
|
+
<div className="text-xs font-medium text-theme-text-tertiary uppercase tracking-wider px-1 mb-1">
|
|
259
|
+
{category.label}
|
|
260
|
+
</div>
|
|
261
|
+
<div className="space-y-0.5">
|
|
262
|
+
{kinds.map(({ kind, label, icon: Icon, color }) => {
|
|
263
|
+
const count = kindCounts.get(kind) || 0
|
|
264
|
+
const isVisible = visibleKinds.has(kind)
|
|
265
|
+
|
|
266
|
+
return (
|
|
267
|
+
<button
|
|
268
|
+
key={kind}
|
|
269
|
+
onClick={() => onToggleKind(kind)}
|
|
270
|
+
className={clsx(
|
|
271
|
+
'w-full flex items-center gap-2 px-2 py-1.5 rounded-md text-left transition-colors',
|
|
272
|
+
isVisible
|
|
273
|
+
? 'bg-theme-elevated/70 text-theme-text-primary'
|
|
274
|
+
: 'text-theme-text-secondary hover:bg-theme-elevated/40 hover:text-theme-text-secondary'
|
|
275
|
+
)}
|
|
276
|
+
>
|
|
277
|
+
<Icon className={clsx('w-4 h-4 shrink-0', isVisible ? color : 'text-theme-text-tertiary')} />
|
|
278
|
+
<span className="flex-1 text-sm truncate">{label}</span>
|
|
279
|
+
<span className={clsx(
|
|
280
|
+
'text-xs px-1.5 py-0.5 rounded',
|
|
281
|
+
isVisible ? 'bg-theme-hover text-theme-text-secondary' : 'bg-theme-elevated/50 text-theme-text-tertiary'
|
|
282
|
+
)}>
|
|
283
|
+
{count}
|
|
284
|
+
</span>
|
|
285
|
+
</button>
|
|
286
|
+
)
|
|
287
|
+
})}
|
|
288
|
+
</div>
|
|
289
|
+
</div>
|
|
290
|
+
)
|
|
291
|
+
})}
|
|
292
|
+
|
|
293
|
+
{/* Auto-hidden kinds section (for large clusters) */}
|
|
294
|
+
{hiddenKinds.length > 0 && (
|
|
295
|
+
<div className="px-2 py-2 border-t border-theme-border/50">
|
|
296
|
+
<div className="flex items-center gap-1 text-xs font-medium text-amber-400/80 uppercase tracking-wider px-1 mb-1">
|
|
297
|
+
<Zap className="w-3 h-3" />
|
|
298
|
+
Hidden for Performance
|
|
299
|
+
</div>
|
|
300
|
+
<div className="space-y-0.5">
|
|
301
|
+
{hiddenKinds.map(kindName => {
|
|
302
|
+
const kindConfig = RESOURCE_KINDS.find(k => k.kind === kindName)
|
|
303
|
+
if (!kindConfig) return null
|
|
304
|
+
|
|
305
|
+
const { kind, label, icon: Icon } = kindConfig
|
|
306
|
+
const isConfirmed = confirmedKinds.has(kind)
|
|
307
|
+
const isPending = pendingConfirmKind === kind
|
|
308
|
+
|
|
309
|
+
return (
|
|
310
|
+
<div key={kind} className="relative">
|
|
311
|
+
<button
|
|
312
|
+
onClick={() => {
|
|
313
|
+
if (isConfirmed) {
|
|
314
|
+
// Already confirmed - just toggle
|
|
315
|
+
onEnableHiddenKind?.(kind)
|
|
316
|
+
} else {
|
|
317
|
+
// Show confirmation
|
|
318
|
+
setPendingConfirmKind(kind)
|
|
319
|
+
}
|
|
320
|
+
}}
|
|
321
|
+
className="w-full flex items-center gap-2 px-2 py-1.5 rounded-md text-left transition-colors text-theme-text-tertiary hover:bg-amber-500/10 hover:text-amber-400/80"
|
|
322
|
+
title="Hidden for performance - click to show"
|
|
323
|
+
>
|
|
324
|
+
<Icon className="w-4 h-4 shrink-0 opacity-50" />
|
|
325
|
+
<span className="flex-1 text-sm truncate opacity-70">{label}</span>
|
|
326
|
+
<EyeOff className="w-3 h-3 opacity-50" />
|
|
327
|
+
</button>
|
|
328
|
+
|
|
329
|
+
{/* Confirmation popup */}
|
|
330
|
+
{isPending && (
|
|
331
|
+
<div className="absolute left-0 right-0 top-full mt-1 z-50 bg-theme-surface border border-amber-500/30 rounded-lg shadow-lg p-3">
|
|
332
|
+
<div className="flex items-start gap-2 mb-2">
|
|
333
|
+
<AlertTriangle className="w-4 h-4 text-amber-400 shrink-0 mt-0.5" />
|
|
334
|
+
<div className="text-xs text-theme-text-secondary">
|
|
335
|
+
<p className="font-medium text-amber-400">Performance Warning</p>
|
|
336
|
+
<p className="mt-1">
|
|
337
|
+
Showing {label}s may slow down the topology view in large clusters.
|
|
338
|
+
</p>
|
|
339
|
+
</div>
|
|
340
|
+
</div>
|
|
341
|
+
<div className="flex gap-2">
|
|
342
|
+
<button
|
|
343
|
+
onClick={() => {
|
|
344
|
+
setConfirmedKinds(prev => new Set(prev).add(kind))
|
|
345
|
+
setPendingConfirmKind(null)
|
|
346
|
+
onEnableHiddenKind?.(kind)
|
|
347
|
+
}}
|
|
348
|
+
className="flex-1 px-2 py-1 text-xs bg-amber-500/20 text-amber-400 hover:bg-amber-500/30 rounded transition-colors"
|
|
349
|
+
>
|
|
350
|
+
Show Anyway
|
|
351
|
+
</button>
|
|
352
|
+
<button
|
|
353
|
+
onClick={() => setPendingConfirmKind(null)}
|
|
354
|
+
className="flex-1 px-2 py-1 text-xs bg-theme-elevated text-theme-text-secondary hover:bg-theme-hover rounded transition-colors"
|
|
355
|
+
>
|
|
356
|
+
Cancel
|
|
357
|
+
</button>
|
|
358
|
+
</div>
|
|
359
|
+
</div>
|
|
360
|
+
)}
|
|
361
|
+
</div>
|
|
362
|
+
)
|
|
363
|
+
})}
|
|
364
|
+
</div>
|
|
365
|
+
</div>
|
|
366
|
+
)}
|
|
367
|
+
</div>
|
|
368
|
+
|
|
369
|
+
{/* Footer stats */}
|
|
370
|
+
<div className="px-3 py-2 border-t border-theme-border bg-theme-surface/50">
|
|
371
|
+
<div className="text-xs text-theme-text-tertiary">
|
|
372
|
+
Showing {availableKinds.filter(k => visibleKinds.has(k.kind)).reduce((sum, k) => sum + (kindCounts.get(k.kind) || 0), 0)} of {nodes.length} resources
|
|
373
|
+
</div>
|
|
374
|
+
</div>
|
|
375
|
+
</div>
|
|
376
|
+
)
|
|
377
|
+
})
|