@skyhook-io/radar-app 0.1.1

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 (251) hide show
  1. package/README.md +67 -0
  2. package/package.json +80 -0
  3. package/src/App.tsx +1538 -0
  4. package/src/RadarApp.tsx +145 -0
  5. package/src/api/apiResources.ts +28 -0
  6. package/src/api/client.ts +2583 -0
  7. package/src/api/config.ts +116 -0
  8. package/src/api/traffic.ts +139 -0
  9. package/src/components/ConnectionErrorView.tsx +272 -0
  10. package/src/components/ContextSwitcher.tsx +481 -0
  11. package/src/components/DebugOverlay.tsx +94 -0
  12. package/src/components/UserMenu.tsx +87 -0
  13. package/src/components/audit/AuditSettingsDialog.tsx +162 -0
  14. package/src/components/audit/AuditView.tsx +123 -0
  15. package/src/components/cost/CostTrendChart.tsx +388 -0
  16. package/src/components/cost/CostView.tsx +545 -0
  17. package/src/components/dock/BottomDock.tsx +96 -0
  18. package/src/components/dock/DockContext.tsx +11 -0
  19. package/src/components/dock/LocalTerminalTab.tsx +22 -0
  20. package/src/components/dock/LogsTab.tsx +26 -0
  21. package/src/components/dock/NodeTerminalTab.tsx +50 -0
  22. package/src/components/dock/TerminalTab.tsx +42 -0
  23. package/src/components/dock/TrafficFlowListTab.tsx +18 -0
  24. package/src/components/dock/WorkloadLogsTab.tsx +23 -0
  25. package/src/components/dock/index.ts +2 -0
  26. package/src/components/gitops/GitOpsActions.tsx +1 -0
  27. package/src/components/gitops/GitOpsStatusBadge.tsx +1 -0
  28. package/src/components/gitops/ManagedResourcesList.tsx +1 -0
  29. package/src/components/gitops/SyncCountdown.tsx +1 -0
  30. package/src/components/gitops/index.ts +4 -0
  31. package/src/components/helm/ChartBrowser.tsx +580 -0
  32. package/src/components/helm/HelmReleaseDrawer.tsx +774 -0
  33. package/src/components/helm/HelmView.tsx +475 -0
  34. package/src/components/helm/InstallWizard.tsx +1060 -0
  35. package/src/components/helm/ManifestDiffViewer.tsx +91 -0
  36. package/src/components/helm/ManifestViewer.tsx +61 -0
  37. package/src/components/helm/OwnedResources.tsx +465 -0
  38. package/src/components/helm/RevisionHistory.tsx +167 -0
  39. package/src/components/helm/ValuesDiffPreview.tsx +190 -0
  40. package/src/components/helm/ValuesViewer.tsx +365 -0
  41. package/src/components/helm/helm-utils.ts +37 -0
  42. package/src/components/home/ActivitySummary.tsx +262 -0
  43. package/src/components/home/CertificateHealthCard.tsx +105 -0
  44. package/src/components/home/ClusterHealthCard.tsx +483 -0
  45. package/src/components/home/CostCard.tsx +112 -0
  46. package/src/components/home/HealthRing.tsx +1 -0
  47. package/src/components/home/HelmSummary.tsx +129 -0
  48. package/src/components/home/HomeView.tsx +224 -0
  49. package/src/components/home/MCPSetupDialog.tsx +417 -0
  50. package/src/components/home/NetworkPolicyCoverageCard.tsx +109 -0
  51. package/src/components/home/TopologyPreview.tsx +219 -0
  52. package/src/components/home/TrafficSummary.tsx +154 -0
  53. package/src/components/logs/JsonLogLine.tsx +1 -0
  54. package/src/components/logs/LogCore.tsx +2 -0
  55. package/src/components/logs/LogsViewer.tsx +44 -0
  56. package/src/components/logs/WorkloadLogsViewer.tsx +40 -0
  57. package/src/components/logs/useLogBuffer.ts +2 -0
  58. package/src/components/logs/useLogSearch.ts +1 -0
  59. package/src/components/portforward/PortForwardButton.tsx +375 -0
  60. package/src/components/portforward/PortForwardManager.tsx +871 -0
  61. package/src/components/resource/PrometheusCharts.tsx +687 -0
  62. package/src/components/resource-drawer/ResourceDrawer.tsx +214 -0
  63. package/src/components/resources/ImageFilesystemModal.tsx +745 -0
  64. package/src/components/resources/PodFilesystemModal.tsx +407 -0
  65. package/src/components/resources/ResourceDetailDrawer.tsx +43 -0
  66. package/src/components/resources/ResourcesView.tsx +190 -0
  67. package/src/components/resources/drawer-components.tsx +1 -0
  68. package/src/components/resources/file-browser-utils.ts +35 -0
  69. package/src/components/resources/renderers/AlertRenderer.tsx +1 -0
  70. package/src/components/resources/renderers/ArgoApplicationRenderer.tsx +17 -0
  71. package/src/components/resources/renderers/CNPGBackupRenderer.tsx +1 -0
  72. package/src/components/resources/renderers/CNPGClusterRenderer.tsx +1 -0
  73. package/src/components/resources/renderers/CNPGPoolerRenderer.tsx +1 -0
  74. package/src/components/resources/renderers/CNPGScheduledBackupRenderer.tsx +1 -0
  75. package/src/components/resources/renderers/CertificateRenderer.tsx +1 -0
  76. package/src/components/resources/renderers/CertificateRequestRenderer.tsx +1 -0
  77. package/src/components/resources/renderers/ChallengeRenderer.tsx +1 -0
  78. package/src/components/resources/renderers/ClusterComplianceReportRenderer.tsx +1 -0
  79. package/src/components/resources/renderers/ClusterExternalSecretRenderer.tsx +1 -0
  80. package/src/components/resources/renderers/ClusterIssuerRenderer.tsx +1 -0
  81. package/src/components/resources/renderers/ConfigAuditReportRenderer.tsx +1 -0
  82. package/src/components/resources/renderers/ConfigMapRenderer.tsx +1 -0
  83. package/src/components/resources/renderers/CronJobRenderer.tsx +1 -0
  84. package/src/components/resources/renderers/EventRenderer.tsx +1 -0
  85. package/src/components/resources/renderers/ExposedSecretReportRenderer.tsx +1 -0
  86. package/src/components/resources/renderers/ExternalSecretRenderer.tsx +1 -0
  87. package/src/components/resources/renderers/FluxHelmReleaseRenderer.tsx +1 -0
  88. package/src/components/resources/renderers/GRPCRouteRenderer.tsx +1 -0
  89. package/src/components/resources/renderers/GatewayClassRenderer.tsx +1 -0
  90. package/src/components/resources/renderers/GatewayRenderer.tsx +1 -0
  91. package/src/components/resources/renderers/GenericRenderer.tsx +1 -0
  92. package/src/components/resources/renderers/GitRepositoryRenderer.tsx +1 -0
  93. package/src/components/resources/renderers/HPARenderer.tsx +1 -0
  94. package/src/components/resources/renderers/HTTPRouteRenderer.tsx +1 -0
  95. package/src/components/resources/renderers/HelmRepositoryRenderer.tsx +1 -0
  96. package/src/components/resources/renderers/IngressClassRenderer.tsx +1 -0
  97. package/src/components/resources/renderers/IngressRenderer.tsx +1 -0
  98. package/src/components/resources/renderers/IstioAuthorizationPolicyRenderer.tsx +1 -0
  99. package/src/components/resources/renderers/IstioDestinationRuleRenderer.tsx +1 -0
  100. package/src/components/resources/renderers/IstioGatewayRenderer.tsx +1 -0
  101. package/src/components/resources/renderers/IstioPeerAuthenticationRenderer.tsx +1 -0
  102. package/src/components/resources/renderers/IstioServiceEntryRenderer.tsx +1 -0
  103. package/src/components/resources/renderers/IstioVirtualServiceRenderer.tsx +1 -0
  104. package/src/components/resources/renderers/JobRenderer.tsx +1 -0
  105. package/src/components/resources/renderers/KarpenterEC2NodeClassRenderer.tsx +1 -0
  106. package/src/components/resources/renderers/KarpenterNodeClaimRenderer.tsx +1 -0
  107. package/src/components/resources/renderers/KarpenterNodePoolRenderer.tsx +1 -0
  108. package/src/components/resources/renderers/KedaScaledJobRenderer.tsx +1 -0
  109. package/src/components/resources/renderers/KedaScaledObjectRenderer.tsx +1 -0
  110. package/src/components/resources/renderers/KedaTriggerAuthRenderer.tsx +1 -0
  111. package/src/components/resources/renderers/KnativeConfigurationRenderer.tsx +1 -0
  112. package/src/components/resources/renderers/KnativeEventingRenderer.tsx +1 -0
  113. package/src/components/resources/renderers/KnativeFlowRenderer.tsx +1 -0
  114. package/src/components/resources/renderers/KnativeNetworkingRenderer.tsx +1 -0
  115. package/src/components/resources/renderers/KnativeRevisionRenderer.tsx +1 -0
  116. package/src/components/resources/renderers/KnativeRouteRenderer.tsx +1 -0
  117. package/src/components/resources/renderers/KnativeServiceRenderer.tsx +1 -0
  118. package/src/components/resources/renderers/KnativeSourceRenderer.tsx +1 -0
  119. package/src/components/resources/renderers/KustomizationRenderer.tsx +1 -0
  120. package/src/components/resources/renderers/KyvernoPolicyReportRenderer.tsx +1 -0
  121. package/src/components/resources/renderers/LeaseRenderer.tsx +1 -0
  122. package/src/components/resources/renderers/NetworkPolicyRenderer.tsx +1 -0
  123. package/src/components/resources/renderers/NodeRenderer.tsx +44 -0
  124. package/src/components/resources/renderers/OCIRepositoryRenderer.tsx +1 -0
  125. package/src/components/resources/renderers/OrderRenderer.tsx +1 -0
  126. package/src/components/resources/renderers/PVCRenderer.tsx +1 -0
  127. package/src/components/resources/renderers/PersistentVolumeRenderer.tsx +1 -0
  128. package/src/components/resources/renderers/PodDisruptionBudgetRenderer.tsx +1 -0
  129. package/src/components/resources/renderers/PodMonitorRenderer.tsx +1 -0
  130. package/src/components/resources/renderers/PodRenderer.tsx +94 -0
  131. package/src/components/resources/renderers/PriorityClassRenderer.tsx +1 -0
  132. package/src/components/resources/renderers/PrometheusRuleRenderer.tsx +1 -0
  133. package/src/components/resources/renderers/ReplicaSetRenderer.tsx +1 -0
  134. package/src/components/resources/renderers/RoleBindingRenderer.tsx +1 -0
  135. package/src/components/resources/renderers/RoleRenderer.tsx +1 -0
  136. package/src/components/resources/renderers/RolloutRenderer.tsx +1 -0
  137. package/src/components/resources/renderers/RuntimeClassRenderer.tsx +1 -0
  138. package/src/components/resources/renderers/SbomReportRenderer.tsx +1 -0
  139. package/src/components/resources/renderers/SealedSecretRenderer.tsx +1 -0
  140. package/src/components/resources/renderers/SecretRenderer.tsx +1 -0
  141. package/src/components/resources/renderers/SecretStoreRenderer.tsx +1 -0
  142. package/src/components/resources/renderers/ServiceAccountRenderer.tsx +1 -0
  143. package/src/components/resources/renderers/ServiceMonitorRenderer.tsx +1 -0
  144. package/src/components/resources/renderers/ServiceRenderer.tsx +26 -0
  145. package/src/components/resources/renderers/SimpleRouteRenderer.tsx +1 -0
  146. package/src/components/resources/renderers/StorageClassRenderer.tsx +1 -0
  147. package/src/components/resources/renderers/TraefikIngressRouteRenderer.tsx +1 -0
  148. package/src/components/resources/renderers/VPARenderer.tsx +1 -0
  149. package/src/components/resources/renderers/VeleroBSLRenderer.tsx +1 -0
  150. package/src/components/resources/renderers/VeleroBackupRenderer.tsx +1 -0
  151. package/src/components/resources/renderers/VeleroRestoreRenderer.tsx +1 -0
  152. package/src/components/resources/renderers/VeleroScheduleRenderer.tsx +1 -0
  153. package/src/components/resources/renderers/VeleroVSLRenderer.tsx +1 -0
  154. package/src/components/resources/renderers/VulnerabilityReportRenderer.tsx +1 -0
  155. package/src/components/resources/renderers/WebhookConfigRenderer.tsx +1 -0
  156. package/src/components/resources/renderers/WorkflowRenderer.tsx +1 -0
  157. package/src/components/resources/renderers/WorkflowTemplateRenderer.tsx +1 -0
  158. package/src/components/resources/renderers/WorkloadRenderer.tsx +52 -0
  159. package/src/components/resources/renderers/argo-cells.tsx +1 -0
  160. package/src/components/resources/renderers/certmanager-cells.tsx +1 -0
  161. package/src/components/resources/renderers/cnpg-cells.tsx +1 -0
  162. package/src/components/resources/renderers/eso-cells.tsx +1 -0
  163. package/src/components/resources/renderers/flux-cells.tsx +1 -0
  164. package/src/components/resources/renderers/index.ts +91 -0
  165. package/src/components/resources/renderers/istio-cells.tsx +1 -0
  166. package/src/components/resources/renderers/karpenter-cells.tsx +1 -0
  167. package/src/components/resources/renderers/keda-cells.tsx +1 -0
  168. package/src/components/resources/renderers/knative-cells.tsx +1 -0
  169. package/src/components/resources/renderers/kyverno-cells.tsx +1 -0
  170. package/src/components/resources/renderers/prometheus-cells.tsx +1 -0
  171. package/src/components/resources/renderers/traefik-cells.tsx +1 -0
  172. package/src/components/resources/renderers/trivy-cells.tsx +1 -0
  173. package/src/components/resources/renderers/trivy-shared.tsx +1 -0
  174. package/src/components/resources/renderers/velero-cells.tsx +1 -0
  175. package/src/components/resources/resource-utils-argo.ts +2 -0
  176. package/src/components/resources/resource-utils-certmanager.ts +2 -0
  177. package/src/components/resources/resource-utils-cnpg.ts +2 -0
  178. package/src/components/resources/resource-utils-eso.ts +2 -0
  179. package/src/components/resources/resource-utils-flux.ts +2 -0
  180. package/src/components/resources/resource-utils-istio.ts +2 -0
  181. package/src/components/resources/resource-utils-karpenter.ts +2 -0
  182. package/src/components/resources/resource-utils-keda.ts +2 -0
  183. package/src/components/resources/resource-utils-knative.ts +2 -0
  184. package/src/components/resources/resource-utils-kyverno.ts +2 -0
  185. package/src/components/resources/resource-utils-prometheus.ts +2 -0
  186. package/src/components/resources/resource-utils-traefik.ts +1 -0
  187. package/src/components/resources/resource-utils-trivy.ts +2 -0
  188. package/src/components/resources/resource-utils-velero.ts +2 -0
  189. package/src/components/resources/resource-utils.ts +5 -0
  190. package/src/components/settings/SettingsDialog.tsx +537 -0
  191. package/src/components/shared/CreateResourceDialog.tsx +17 -0
  192. package/src/components/shared/EditableYamlView.tsx +24 -0
  193. package/src/components/shared/LargeClusterNamespacePicker.tsx +70 -0
  194. package/src/components/shared/ResourceRendererDispatch.tsx +31 -0
  195. package/src/components/timeline/DiffViewer.tsx +1 -0
  196. package/src/components/timeline/TimelineList.tsx +69 -0
  197. package/src/components/timeline/TimelineSwimlanes.tsx +1308 -0
  198. package/src/components/timeline/TimelineView.tsx +157 -0
  199. package/src/components/timeline/shared.tsx +1 -0
  200. package/src/components/traffic/TrafficFilterSidebar.tsx +571 -0
  201. package/src/components/traffic/TrafficFlowList.tsx +415 -0
  202. package/src/components/traffic/TrafficFlowListContext.tsx +68 -0
  203. package/src/components/traffic/TrafficGraph.tsx +1546 -0
  204. package/src/components/traffic/TrafficView.tsx +1213 -0
  205. package/src/components/traffic/TrafficWizard.tsx +386 -0
  206. package/src/components/traffic/index.ts +3 -0
  207. package/src/components/ui/CodeViewer.tsx +8 -0
  208. package/src/components/ui/CommandPalette.tsx +460 -0
  209. package/src/components/ui/ConfirmDialog.tsx +1 -0
  210. package/src/components/ui/DiagnosticsOverlay.tsx +619 -0
  211. package/src/components/ui/ErrorBoundary.tsx +46 -0
  212. package/src/components/ui/ForceDeleteConfirmDialog.tsx +1 -0
  213. package/src/components/ui/Markdown.tsx +108 -0
  214. package/src/components/ui/MetricsChart.tsx +1 -0
  215. package/src/components/ui/NamespaceSelector.tsx +436 -0
  216. package/src/components/ui/ResourceBar.tsx +1 -0
  217. package/src/components/ui/ShortcutHelpOverlay.tsx +301 -0
  218. package/src/components/ui/Toast.tsx +1 -0
  219. package/src/components/ui/Tooltip.tsx +1 -0
  220. package/src/components/ui/UpdateNotification.tsx +299 -0
  221. package/src/components/ui/YamlEditor.tsx +1 -0
  222. package/src/components/workload/WorkloadView.tsx +532 -0
  223. package/src/context/ConnectionContext.tsx +173 -0
  224. package/src/context/ContextSwitchContext.tsx +56 -0
  225. package/src/context/NavCustomization.tsx +62 -0
  226. package/src/context/ThemeContext.tsx +97 -0
  227. package/src/contexts/CapabilitiesContext.tsx +130 -0
  228. package/src/hooks/useAnimatedUnmount.ts +1 -0
  229. package/src/hooks/useDesktopDownload.ts +41 -0
  230. package/src/hooks/useEventSource.ts +262 -0
  231. package/src/hooks/useFavorites.ts +69 -0
  232. package/src/hooks/useKeyboardShortcuts.tsx +7 -0
  233. package/src/hooks/useRefreshAnimation.ts +1 -0
  234. package/src/index.css +243 -0
  235. package/src/index.ts +17 -0
  236. package/src/main.tsx +158 -0
  237. package/src/types/gitops.ts +2 -0
  238. package/src/types.ts +3 -0
  239. package/src/utils/animation.ts +2 -0
  240. package/src/utils/badge-colors.ts +2 -0
  241. package/src/utils/context-name.ts +2 -0
  242. package/src/utils/desktop-download.ts +66 -0
  243. package/src/utils/desktop-open-folder.ts +21 -0
  244. package/src/utils/format.ts +2 -0
  245. package/src/utils/log-format.ts +12 -0
  246. package/src/utils/navigation.ts +23 -0
  247. package/src/utils/resource-hierarchy.ts +2 -0
  248. package/src/utils/resource-icons.ts +2 -0
  249. package/src/utils/skeleton-yaml.ts +2 -0
  250. package/src/utils/traffic-colors.ts +54 -0
  251. package/src/vite-env.d.ts +1 -0
@@ -0,0 +1,571 @@
1
+ import { memo, useState, useRef } from 'react'
2
+ import { createPortal } from 'react-dom'
3
+ import {
4
+ ChevronDown,
5
+ Eye,
6
+ Layers,
7
+ Globe,
8
+ Cpu,
9
+ Network,
10
+ Clock,
11
+ Filter,
12
+ Info,
13
+ Puzzle,
14
+ } from 'lucide-react'
15
+ import { clsx } from 'clsx'
16
+ import { SEVERITY_BADGE } from '@skyhook-io/k8s-ui/utils/badge-colors'
17
+ import type { AddonMode } from './TrafficView'
18
+ import { getNamespaceColor } from '../../utils/traffic-colors'
19
+
20
+ // Fast tooltip component using portal to escape overflow
21
+ function Tooltip({ children, content }: { children: React.ReactNode; content: string }) {
22
+ const [show, setShow] = useState(false)
23
+ const [pos, setPos] = useState({ x: 0, y: 0 })
24
+ const ref = useRef<HTMLDivElement>(null)
25
+
26
+ const handleMouseEnter = () => {
27
+ if (ref.current) {
28
+ const rect = ref.current.getBoundingClientRect()
29
+ setPos({ x: rect.right + 8, y: rect.top + rect.height / 2 })
30
+ }
31
+ setShow(true)
32
+ }
33
+
34
+ return (
35
+ <div
36
+ ref={ref}
37
+ className="inline-flex"
38
+ onMouseEnter={handleMouseEnter}
39
+ onMouseLeave={() => setShow(false)}
40
+ >
41
+ {children}
42
+ {show && createPortal(
43
+ <div
44
+ className="fixed z-[9999] pointer-events-none"
45
+ style={{ left: pos.x, top: pos.y, transform: 'translateY(-50%)' }}
46
+ >
47
+ <div className="bg-gray-900 text-white text-[10px] px-2 py-1.5 rounded shadow-lg max-w-[180px] leading-tight whitespace-normal">
48
+ {content}
49
+ </div>
50
+ </div>,
51
+ document.body
52
+ )}
53
+ </div>
54
+ )
55
+ }
56
+
57
+ // Connection threshold options
58
+ const CONNECTION_THRESHOLDS = [
59
+ { value: 0, label: 'All traffic' },
60
+ { value: 100, label: '100+ connections' },
61
+ { value: 1000, label: '1K+ connections' },
62
+ { value: 10000, label: '10K+ connections' },
63
+ { value: 100000, label: '100K+ connections' },
64
+ ]
65
+
66
+ // Time range options
67
+ const TIME_RANGES = [
68
+ { value: '1m', label: '1 minute' },
69
+ { value: '5m', label: '5 minutes' },
70
+ { value: '15m', label: '15 minutes' },
71
+ { value: '1h', label: '1 hour' },
72
+ ]
73
+
74
+ interface TrafficFilterSidebarProps {
75
+ // Filter state
76
+ hideSystem: boolean
77
+ setHideSystem: (v: boolean) => void
78
+ hideExternal: boolean
79
+ setHideExternal: (v: boolean) => void
80
+ minConnections: number
81
+ setMinConnections: (v: number) => void
82
+
83
+ // Display options
84
+ showNamespaceGroups: boolean
85
+ setShowNamespaceGroups: (v: boolean) => void
86
+ collapseInternet: boolean
87
+ setCollapseInternet: (v: boolean) => void
88
+ addonMode: AddonMode
89
+ setAddonMode: (v: AddonMode) => void
90
+
91
+ // Detection options
92
+ aggregateExternal: boolean
93
+ setAggregateExternal: (v: boolean) => void
94
+ detectServices: boolean
95
+ setDetectServices: (v: boolean) => void
96
+
97
+ // Time
98
+ timeRange: string
99
+ setTimeRange: (v: string) => void
100
+
101
+ // L7 filters (Hubble-only)
102
+ isHubble?: boolean
103
+ l7Protocol: string // 'all' | 'HTTP' | 'DNS' | 'TCP'
104
+ setL7Protocol: (v: string) => void
105
+ l7Methods: Set<string>
106
+ onToggleL7Method: (method: string) => void
107
+ l7StatusRanges: Set<string>
108
+ onToggleL7StatusRange: (range: string) => void
109
+ l7Verdicts: Set<string>
110
+ onToggleL7Verdict: (verdict: string) => void
111
+ dnsPattern: string
112
+ setDnsPattern: (v: string) => void
113
+
114
+ // Namespace filtering
115
+ namespaces: Array<{ name: string; nodeCount: number }>
116
+ hiddenNamespaces: Set<string>
117
+ onToggleNamespace: (ns: string) => void
118
+
119
+ }
120
+
121
+ // Compact toggle component with tooltip
122
+ function ToggleOption({
123
+ label,
124
+ description,
125
+ enabled,
126
+ onToggle,
127
+ icon: Icon,
128
+ }: {
129
+ label: string
130
+ description: string
131
+ enabled: boolean
132
+ onToggle: () => void
133
+ icon: typeof Eye
134
+ }) {
135
+ return (
136
+ <div className={clsx(
137
+ 'flex items-center gap-2 px-2 py-1.5 rounded transition-colors',
138
+ enabled ? 'selection' : 'hover:bg-theme-elevated'
139
+ )}>
140
+ <button
141
+ onClick={onToggle}
142
+ className="flex-1 flex items-center gap-2 text-left"
143
+ >
144
+ <Icon className={clsx(
145
+ 'w-3.5 h-3.5 shrink-0',
146
+ enabled ? 'selection-text' : 'text-theme-text-tertiary'
147
+ )} />
148
+ <span className={clsx(
149
+ 'flex-1 text-xs',
150
+ enabled ? 'selection-text' : 'text-theme-text-primary'
151
+ )}>
152
+ {label}
153
+ </span>
154
+ </button>
155
+ <Tooltip content={description}>
156
+ <Info className="w-3 h-3 text-theme-text-tertiary hover:text-theme-text-secondary cursor-help" />
157
+ </Tooltip>
158
+ <button
159
+ onClick={onToggle}
160
+ className={clsx(
161
+ 'w-7 h-4 rounded-full transition-colors relative shrink-0',
162
+ enabled ? 'bg-skyhook-500' : 'bg-theme-elevated'
163
+ )}
164
+ >
165
+ <div className={clsx(
166
+ 'absolute top-0.5 w-3 h-3 rounded-full bg-white transition-transform',
167
+ enabled ? 'translate-x-3.5' : 'translate-x-0.5'
168
+ )} />
169
+ </button>
170
+ </div>
171
+ )
172
+ }
173
+
174
+ export const TrafficFilterSidebar = memo(function TrafficFilterSidebar({
175
+ hideSystem,
176
+ setHideSystem,
177
+ hideExternal,
178
+ setHideExternal,
179
+ minConnections,
180
+ setMinConnections,
181
+ showNamespaceGroups,
182
+ setShowNamespaceGroups,
183
+ collapseInternet,
184
+ setCollapseInternet,
185
+ addonMode,
186
+ setAddonMode,
187
+ aggregateExternal,
188
+ setAggregateExternal,
189
+ detectServices,
190
+ setDetectServices,
191
+ timeRange,
192
+ setTimeRange,
193
+ isHubble,
194
+ l7Protocol,
195
+ setL7Protocol,
196
+ l7Methods,
197
+ onToggleL7Method,
198
+ l7StatusRanges,
199
+ onToggleL7StatusRange,
200
+ l7Verdicts,
201
+ onToggleL7Verdict,
202
+ dnsPattern,
203
+ setDnsPattern,
204
+ namespaces,
205
+ hiddenNamespaces,
206
+ onToggleNamespace,
207
+ }: TrafficFilterSidebarProps) {
208
+ const [namespacesExpanded, setNamespacesExpanded] = useState(false)
209
+
210
+ // Sort namespaces by node count (descending)
211
+ const sortedNamespaces = [...namespaces].sort((a, b) => b.nodeCount - a.nodeCount)
212
+ const visibleNamespaces = namespacesExpanded ? sortedNamespaces : sortedNamespaces.slice(0, 8)
213
+ const hasMore = sortedNamespaces.length > 8
214
+
215
+ return (
216
+ <div className="w-72 flex flex-col shrink-0 bg-theme-surface/90 backdrop-blur border-r border-theme-border overflow-hidden">
217
+ {/* Header */}
218
+ <div className="flex items-center px-3 py-2 border-b border-theme-border">
219
+ <span className="text-sm font-medium text-theme-text-secondary">Traffic Filters</span>
220
+ </div>
221
+
222
+ {/* Scrollable content */}
223
+ <div className="flex-1 overflow-y-auto">
224
+ {/* Time Range & Threshold */}
225
+ <div className="px-3 py-2 border-b border-theme-border space-y-1.5">
226
+ <div className="flex items-center gap-2">
227
+ <Clock className="w-3.5 h-3.5 text-theme-text-tertiary" />
228
+ <select
229
+ value={timeRange}
230
+ onChange={(e) => setTimeRange(e.target.value)}
231
+ title="Show traffic from the selected time window"
232
+ className="flex-1 bg-theme-elevated text-theme-text-primary text-xs rounded px-2 py-1.5 border border-theme-border focus:outline-none focus:ring-1 focus:ring-blue-500"
233
+ >
234
+ {TIME_RANGES.map(({ value, label }) => (
235
+ <option key={value} value={value}>{label}</option>
236
+ ))}
237
+ </select>
238
+ </div>
239
+ <div className="flex items-center gap-2">
240
+ <Filter className="w-3.5 h-3.5 text-theme-text-tertiary" />
241
+ <select
242
+ value={minConnections}
243
+ onChange={(e) => setMinConnections(Number(e.target.value))}
244
+ title="Hide low-traffic flows to reduce noise"
245
+ className="flex-1 bg-theme-elevated text-theme-text-primary text-xs rounded px-2 py-1.5 border border-theme-border focus:outline-none focus:ring-1 focus:ring-blue-500"
246
+ >
247
+ {CONNECTION_THRESHOLDS.map(({ value, label }) => (
248
+ <option key={value} value={value}>{label}</option>
249
+ ))}
250
+ </select>
251
+ </div>
252
+ </div>
253
+
254
+ {/* Filtering */}
255
+ <div className="px-3 py-2 border-b border-theme-border">
256
+ <div className="flex items-center gap-2 mb-1.5">
257
+ <Filter className="w-3.5 h-3.5 text-theme-text-tertiary" />
258
+ <span className="text-[10px] font-medium text-theme-text-tertiary uppercase tracking-wider">Filtering</span>
259
+ </div>
260
+ <div className="space-y-0.5">
261
+ <ToggleOption
262
+ label="Hide System"
263
+ description="Filter out infrastructure traffic (kube-system, monitoring, etc.)"
264
+ enabled={hideSystem}
265
+ onToggle={() => setHideSystem(!hideSystem)}
266
+ icon={Cpu}
267
+ />
268
+ <ToggleOption
269
+ label="Hide External"
270
+ description="Hide traffic to/from external services"
271
+ enabled={hideExternal}
272
+ onToggle={() => setHideExternal(!hideExternal)}
273
+ icon={Globe}
274
+ />
275
+ </div>
276
+
277
+ {/* Cluster Addons 3-way toggle */}
278
+ <div className="mt-2 pt-2 border-t border-theme-border/50">
279
+ <div className="flex items-center gap-2 mb-1.5">
280
+ <Puzzle className="w-3.5 h-3.5 text-theme-text-tertiary" />
281
+ <span className="text-xs text-theme-text-primary">Cluster Addons</span>
282
+ <Tooltip content="Monitoring, logging, cert-manager, etc. Excludes ingress controllers and service mesh.">
283
+ <Info className="w-3 h-3 text-theme-text-tertiary hover:text-theme-text-secondary cursor-help" />
284
+ </Tooltip>
285
+ </div>
286
+ <div className="flex rounded-md overflow-hidden border border-theme-border">
287
+ {(['show', 'group', 'hide'] as const).map((mode) => (
288
+ <button
289
+ key={mode}
290
+ onClick={() => setAddonMode(mode)}
291
+ className={clsx(
292
+ 'flex-1 px-2 py-1.5 text-[10px] font-medium transition-colors capitalize',
293
+ addonMode === mode
294
+ ? 'bg-skyhook-500 text-white'
295
+ : 'bg-theme-elevated text-theme-text-secondary hover:bg-theme-hover'
296
+ )}
297
+ >
298
+ {mode}
299
+ </button>
300
+ ))}
301
+ </div>
302
+ </div>
303
+ </div>
304
+
305
+ {/* Display */}
306
+ <div className="px-3 py-2 border-b border-theme-border">
307
+ <div className="flex items-center gap-2 mb-1.5">
308
+ <Eye className="w-3.5 h-3.5 text-theme-text-tertiary" />
309
+ <span className="text-[10px] font-medium text-theme-text-tertiary uppercase tracking-wider">Display</span>
310
+ </div>
311
+ <div className="space-y-0.5">
312
+ <ToggleOption
313
+ label="Namespace Colors"
314
+ description="Color nodes by their namespace"
315
+ enabled={showNamespaceGroups}
316
+ onToggle={() => setShowNamespaceGroups(!showNamespaceGroups)}
317
+ icon={Layers}
318
+ />
319
+ <ToggleOption
320
+ label="Collapse Internet"
321
+ description="Group inbound external IPs into single 'Internet' node"
322
+ enabled={collapseInternet}
323
+ onToggle={() => setCollapseInternet(!collapseInternet)}
324
+ icon={Globe}
325
+ />
326
+ </div>
327
+ </div>
328
+
329
+ {/* Service Detection */}
330
+ <div className="px-3 py-2 border-b border-theme-border">
331
+ <div className="flex items-center gap-2 mb-1.5">
332
+ <Network className="w-3.5 h-3.5 text-theme-text-tertiary" />
333
+ <span className="text-[10px] font-medium text-theme-text-tertiary uppercase tracking-wider">Detection</span>
334
+ </div>
335
+ <div className="space-y-0.5">
336
+ <ToggleOption
337
+ label="Aggregate External"
338
+ description="Group traffic to same external service (e.g., multiple MongoDB hosts)"
339
+ enabled={aggregateExternal}
340
+ onToggle={() => setAggregateExternal(!aggregateExternal)}
341
+ icon={Layers}
342
+ />
343
+ <ToggleOption
344
+ label="Identify by Port"
345
+ description="Label well-known ports (27017→MongoDB, 6379→Redis). Heuristic-based."
346
+ enabled={detectServices}
347
+ onToggle={() => setDetectServices(!detectServices)}
348
+ icon={Cpu}
349
+ />
350
+ </div>
351
+ </div>
352
+
353
+ {/* L7 Filters (Hubble only) */}
354
+ {isHubble && (
355
+ <div className="space-y-2 px-3 py-2 border-t border-theme-border">
356
+ <div className="flex items-center gap-1.5">
357
+ <Filter className="w-3 h-3 text-theme-text-tertiary" />
358
+ <span className="text-[10px] font-medium text-theme-text-tertiary uppercase tracking-wider">L7 Filters</span>
359
+ </div>
360
+
361
+ {/* Protocol selector */}
362
+ <div>
363
+ <div className="text-[10px] text-theme-text-tertiary mb-1">Protocol</div>
364
+ <div className="flex rounded-md overflow-hidden border border-theme-border">
365
+ {['all', 'HTTP', 'DNS', 'TCP'].map(proto => (
366
+ <button
367
+ key={proto}
368
+ onClick={() => setL7Protocol(proto)}
369
+ className={clsx(
370
+ 'flex-1 px-2 py-1 text-[10px] font-medium transition-colors capitalize',
371
+ l7Protocol === proto
372
+ ? 'bg-skyhook-500 text-white'
373
+ : 'bg-theme-elevated text-theme-text-secondary hover:bg-theme-hover'
374
+ )}
375
+ >
376
+ {proto === 'all' ? 'All' : proto}
377
+ </button>
378
+ ))}
379
+ </div>
380
+ </div>
381
+
382
+ {/* HTTP sub-filters (visible when protocol is All or HTTP) */}
383
+ {(l7Protocol === 'all' || l7Protocol === 'HTTP') && (
384
+ <>
385
+ <div>
386
+ <div className="text-[10px] text-theme-text-tertiary mb-1">HTTP Method</div>
387
+ <div className="flex flex-wrap gap-1">
388
+ {['GET', 'POST', 'PUT', 'DELETE', 'PATCH'].map(method => (
389
+ <button
390
+ key={method}
391
+ onClick={() => onToggleL7Method(method)}
392
+ className={clsx(
393
+ 'px-1.5 py-0.5 rounded text-[10px] font-medium transition-colors',
394
+ l7Methods.has(method)
395
+ ? SEVERITY_BADGE.info
396
+ : 'bg-theme-elevated text-theme-text-tertiary hover:text-theme-text-secondary'
397
+ )}
398
+ >
399
+ {method}
400
+ </button>
401
+ ))}
402
+ </div>
403
+ </div>
404
+
405
+ <div>
406
+ <div className="text-[10px] text-theme-text-tertiary mb-1">Status Code</div>
407
+ <div className="flex flex-wrap gap-1">
408
+ {([
409
+ { label: '2xx', active: SEVERITY_BADGE.success },
410
+ { label: '3xx', active: SEVERITY_BADGE.neutral },
411
+ { label: '4xx', active: SEVERITY_BADGE.warning },
412
+ { label: '5xx', active: SEVERITY_BADGE.error },
413
+ ] as const).map(({ label, active }) => (
414
+ <button
415
+ key={label}
416
+ onClick={() => onToggleL7StatusRange(label)}
417
+ className={clsx(
418
+ 'px-1.5 py-0.5 rounded text-[10px] font-medium transition-colors',
419
+ l7StatusRanges.has(label)
420
+ ? active
421
+ : 'bg-theme-elevated text-theme-text-tertiary hover:text-theme-text-secondary'
422
+ )}
423
+ >
424
+ {label}
425
+ </button>
426
+ ))}
427
+ </div>
428
+ </div>
429
+ </>
430
+ )}
431
+
432
+ {/* DNS sub-filter (visible when protocol is All or DNS) */}
433
+ {(l7Protocol === 'all' || l7Protocol === 'DNS') && (
434
+ <div>
435
+ <div className="text-[10px] text-theme-text-tertiary mb-1">DNS Query</div>
436
+ <input
437
+ type="text"
438
+ value={dnsPattern}
439
+ onChange={(e) => setDnsPattern(e.target.value)}
440
+ placeholder="e.g. example.com"
441
+ className="w-full px-2 py-1 text-[11px] rounded bg-theme-elevated border border-theme-border text-theme-text-primary placeholder:text-theme-text-tertiary focus:outline-none focus:ring-1 focus:ring-blue-500/50"
442
+ />
443
+ </div>
444
+ )}
445
+
446
+ {/* Verdict (always visible — applies to all protocols) */}
447
+ <div>
448
+ <div className="text-[10px] text-theme-text-tertiary mb-1">Verdict</div>
449
+ <div className="flex flex-wrap gap-1">
450
+ {([
451
+ { label: 'forwarded', active: SEVERITY_BADGE.success },
452
+ { label: 'dropped', active: SEVERITY_BADGE.error },
453
+ { label: 'error', active: SEVERITY_BADGE.warning },
454
+ ] as const).map(({ label, active }) => (
455
+ <button
456
+ key={label}
457
+ onClick={() => onToggleL7Verdict(label)}
458
+ className={clsx(
459
+ 'px-1.5 py-0.5 rounded text-[10px] font-medium capitalize transition-colors',
460
+ l7Verdicts.has(label)
461
+ ? active
462
+ : 'bg-theme-elevated text-theme-text-tertiary hover:text-theme-text-secondary'
463
+ )}
464
+ >
465
+ {label}
466
+ </button>
467
+ ))}
468
+ </div>
469
+ </div>
470
+ </div>
471
+ )}
472
+
473
+ {/* Namespaces */}
474
+ {sortedNamespaces.length > 0 && (
475
+ <div className="px-3 py-2">
476
+ <div className="flex items-center justify-between mb-1.5">
477
+ <div className="flex items-center gap-2">
478
+ <Layers className="w-3.5 h-3.5 text-theme-text-tertiary" />
479
+ <span className="text-[10px] font-medium text-theme-text-tertiary uppercase tracking-wider">Namespaces</span>
480
+ </div>
481
+ <div className="flex items-center gap-2 text-[10px]">
482
+ <button
483
+ onClick={() => {
484
+ hiddenNamespaces.forEach(ns => onToggleNamespace(ns))
485
+ }}
486
+ disabled={hiddenNamespaces.size === 0}
487
+ className={clsx(
488
+ hiddenNamespaces.size > 0
489
+ ? 'text-blue-400 hover:text-blue-300'
490
+ : 'text-theme-text-tertiary/50 cursor-default'
491
+ )}
492
+ >
493
+ All
494
+ </button>
495
+ <span className="text-theme-text-tertiary/30">|</span>
496
+ <button
497
+ onClick={() => {
498
+ sortedNamespaces.forEach(({ name }) => {
499
+ if (!hiddenNamespaces.has(name)) {
500
+ onToggleNamespace(name)
501
+ }
502
+ })
503
+ }}
504
+ disabled={hiddenNamespaces.size === sortedNamespaces.length}
505
+ className={clsx(
506
+ hiddenNamespaces.size < sortedNamespaces.length
507
+ ? 'text-blue-400 hover:text-blue-300'
508
+ : 'text-theme-text-tertiary/50 cursor-default'
509
+ )}
510
+ >
511
+ None
512
+ </button>
513
+ </div>
514
+ </div>
515
+ <div className="space-y-0.5">
516
+ {visibleNamespaces.map(({ name, nodeCount }) => {
517
+ const isHidden = hiddenNamespaces.has(name)
518
+ return (
519
+ <button
520
+ key={name}
521
+ onClick={() => onToggleNamespace(name)}
522
+ className={clsx(
523
+ 'w-full flex items-center gap-2 px-2 py-1 rounded text-left transition-all',
524
+ isHidden
525
+ ? 'opacity-50 hover:opacity-70'
526
+ : 'hover:ring-1 hover:ring-white/20'
527
+ )}
528
+ style={{
529
+ backgroundColor: isHidden ? 'transparent' : getNamespaceColor(name),
530
+ }}
531
+ >
532
+ {isHidden && (
533
+ <div
534
+ className="w-2.5 h-2.5 rounded-sm shrink-0"
535
+ style={{ backgroundColor: getNamespaceColor(name) }}
536
+ />
537
+ )}
538
+ <span className={clsx(
539
+ 'text-[11px] font-medium truncate flex-1',
540
+ isHidden ? 'text-theme-text-tertiary line-through' : 'text-white'
541
+ )}>
542
+ {name}
543
+ </span>
544
+ <span className={clsx(
545
+ 'text-[10px] tabular-nums',
546
+ isHidden ? 'text-theme-text-tertiary' : 'text-white/70'
547
+ )}>
548
+ {nodeCount}
549
+ </span>
550
+ </button>
551
+ )
552
+ })}
553
+ </div>
554
+ {hasMore && (
555
+ <button
556
+ onClick={() => setNamespacesExpanded(!namespacesExpanded)}
557
+ className="w-full flex items-center justify-center gap-1 mt-2 py-1 text-[10px] text-theme-text-tertiary hover:text-theme-text-secondary"
558
+ >
559
+ <ChevronDown className={clsx(
560
+ 'w-3 h-3 transition-transform',
561
+ namespacesExpanded && 'rotate-180'
562
+ )} />
563
+ {namespacesExpanded ? 'Show less' : `+${sortedNamespaces.length - 8} more`}
564
+ </button>
565
+ )}
566
+ </div>
567
+ )}
568
+ </div>
569
+ </div>
570
+ )
571
+ })