@skyhook-io/k8s-ui 1.3.2 → 1.4.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 (33) hide show
  1. package/package.json +1 -1
  2. package/src/components/audit/AuditAlerts.tsx +88 -0
  3. package/src/components/audit/AuditCard.tsx +130 -0
  4. package/src/components/audit/AuditFindingsTable.tsx +526 -0
  5. package/src/components/audit/index.ts +3 -0
  6. package/src/components/dock/DockContext.tsx +5 -2
  7. package/src/components/dock/LocalTerminalTab.tsx +11 -0
  8. package/src/components/resources/ResourcesView.tsx +14 -0
  9. package/src/components/resources/renderers/CiliumNetworkPolicyRenderer.tsx +228 -0
  10. package/src/components/resources/renderers/ClusterNetworkPolicyRenderer.tsx +174 -0
  11. package/src/components/resources/renderers/GenericRenderer.tsx +1 -1
  12. package/src/components/resources/renderers/NetworkPolicyDiagram.tsx +277 -0
  13. package/src/components/resources/renderers/NetworkPolicyRenderer.tsx +10 -1
  14. package/src/components/resources/renderers/ServiceRenderer.tsx +40 -28
  15. package/src/components/resources/renderers/index.ts +3 -0
  16. package/src/components/shared/CreateResourceDialog.tsx +236 -0
  17. package/src/components/shared/EditableYamlView.tsx +16 -1
  18. package/src/components/shared/ResourceActionsBar.tsx +9 -7
  19. package/src/components/shared/ResourceRendererDispatch.tsx +9 -2
  20. package/src/components/shared/index.ts +1 -0
  21. package/src/components/topology/K8sResourceNode.tsx +15 -0
  22. package/src/components/topology/TopologyControls.tsx +21 -1
  23. package/src/components/topology/TopologyGraph.tsx +1 -1
  24. package/src/components/ui/Badge.tsx +8 -0
  25. package/src/components/ui/drawer-components.tsx +20 -4
  26. package/src/components/workload/WorkloadView.tsx +55 -29
  27. package/src/index.ts +3 -0
  28. package/src/types/core.ts +2 -1
  29. package/src/utils/badge-colors.ts +7 -0
  30. package/src/utils/index.ts +2 -0
  31. package/src/utils/k8s-errors.ts +142 -0
  32. package/src/utils/resource-icons.ts +3 -0
  33. package/src/utils/skeleton-yaml.ts +302 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skyhook-io/k8s-ui",
3
- "version": "1.3.2",
3
+ "version": "1.4.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/skyhook-io/radar",
@@ -0,0 +1,88 @@
1
+ import { useState } from 'react'
2
+ import { ClipboardCheck, ChevronRight, ShieldAlert, AlertTriangle, ArrowRight } from 'lucide-react'
3
+ import { clsx } from 'clsx'
4
+ import { SEVERITY_TEXT, BP_CATEGORY_BADGE, DEFAULT_BADGE_COLOR } from '../../utils/badge-colors'
5
+
6
+ export interface AuditFinding {
7
+ kind: string
8
+ namespace: string
9
+ name: string
10
+ checkID: string
11
+ category: string
12
+ severity: string
13
+ message: string
14
+ }
15
+
16
+ interface AuditAlertsProps {
17
+ findings: AuditFinding[]
18
+ onViewAll?: () => void
19
+ }
20
+
21
+ /**
22
+ * Subtle collapsible section showing audit findings for a resource.
23
+ * Renders as a collapsed summary by default — not intrusive.
24
+ */
25
+ export function AuditAlerts({ findings, onViewAll }: AuditAlertsProps) {
26
+ const [expanded, setExpanded] = useState(false)
27
+
28
+ if (findings.length === 0) return null
29
+
30
+ const dangers = findings.filter(f => f.severity === 'danger').length
31
+ const warnings = findings.filter(f => f.severity === 'warning').length
32
+
33
+ return (
34
+ <div className="border-b-subtle pb-4 last:border-0">
35
+ <button
36
+ onClick={() => setExpanded(!expanded)}
37
+ className="flex items-center gap-2 w-full text-left mb-2 hover:text-theme-text-primary transition-colors"
38
+ >
39
+ <ChevronRight className={clsx('w-4 h-4 text-theme-text-tertiary transition-transform duration-200', expanded && 'rotate-90')} />
40
+ <ClipboardCheck className="w-4 h-4 text-theme-text-secondary" />
41
+ <span className="text-sm font-medium text-theme-text-secondary">Audit Findings</span>
42
+ <div className="flex items-center gap-2 ml-1">
43
+ {dangers > 0 && (
44
+ <span className={clsx('text-xs font-medium tabular-nums', SEVERITY_TEXT.error)}>{dangers} critical</span>
45
+ )}
46
+ {warnings > 0 && (
47
+ <span className={clsx('text-xs font-medium tabular-nums', SEVERITY_TEXT.warning)}>{warnings} warning</span>
48
+ )}
49
+ </div>
50
+ </button>
51
+
52
+ <div
53
+ className="grid transition-[grid-template-rows] duration-200 ease-out"
54
+ style={{ gridTemplateRows: expanded ? '1fr' : '0fr' }}
55
+ >
56
+ <div className="overflow-hidden">
57
+ <div className="pl-6 pt-1 flex flex-col gap-0.5">
58
+ {findings.map((f, i) => {
59
+ const isDanger = f.severity === 'danger'
60
+ return (
61
+ <div key={`${f.checkID}-${i}`} className="flex items-start gap-2 py-1">
62
+ {isDanger ? (
63
+ <ShieldAlert className={clsx('w-3.5 h-3.5 shrink-0 mt-0.5', SEVERITY_TEXT.error)} />
64
+ ) : (
65
+ <AlertTriangle className={clsx('w-3.5 h-3.5 shrink-0 mt-0.5', SEVERITY_TEXT.warning)} />
66
+ )}
67
+ <span className="text-xs text-theme-text-secondary flex-1">{f.message}</span>
68
+ <span className={clsx('badge-sm text-[10px] shrink-0', BP_CATEGORY_BADGE[f.category] || DEFAULT_BADGE_COLOR)}>
69
+ {f.category}
70
+ </span>
71
+ </div>
72
+ )
73
+ })}
74
+ {onViewAll && (
75
+ <button
76
+ onClick={(e) => { e.stopPropagation(); onViewAll() }}
77
+ className="flex items-center gap-1 text-xs text-skyhook-500 hover:text-skyhook-400 mt-1 py-1 transition-colors"
78
+ >
79
+ View all findings
80
+ <ArrowRight className="w-3 h-3" />
81
+ </button>
82
+ )}
83
+ </div>
84
+ </div>
85
+ </div>
86
+ </div>
87
+ )
88
+ }
@@ -0,0 +1,130 @@
1
+ import { ClipboardCheck, ArrowRight, Check } from 'lucide-react'
2
+ import { clsx } from 'clsx'
3
+ import { SEVERITY_TEXT, SEVERITY_DOT } from '../../utils/badge-colors'
4
+
5
+ export interface AuditCardData {
6
+ passing: number
7
+ warning: number
8
+ danger: number
9
+ categories: Record<string, { passing: number; warning: number; danger: number }>
10
+ }
11
+
12
+ interface AuditCardProps {
13
+ data: AuditCardData
14
+ onNavigate: () => void
15
+ }
16
+
17
+ type SeverityLevel = 'success' | 'warning' | 'error'
18
+
19
+ function getSeverityLevel(data: AuditCardData): SeverityLevel {
20
+ if (data.warning + data.danger === 0) return 'success'
21
+ const dangerRatio = data.danger / (data.warning + data.danger)
22
+ if (dangerRatio > 0.2) return 'error'
23
+ return 'warning'
24
+ }
25
+
26
+ // Card-specific accent backgrounds (light opacity variants, work in both themes)
27
+ const ACCENT_BG: Record<SeverityLevel, string> = {
28
+ success: 'bg-green-500/10',
29
+ warning: 'bg-yellow-500/10',
30
+ error: 'bg-red-500/10',
31
+ }
32
+
33
+ export function AuditCard({ data, onNavigate }: AuditCardProps) {
34
+ const total = data.passing + data.warning + data.danger
35
+ const issueCount = data.warning + data.danger
36
+ const allPassing = issueCount === 0
37
+ const level = getSeverityLevel(data)
38
+ const accentColor = SEVERITY_TEXT[level]
39
+ const accentBg = ACCENT_BG[level]
40
+
41
+ return (
42
+ <button
43
+ onClick={onNavigate}
44
+ className="group h-[260px] rounded-xl bg-theme-surface shadow-theme-sm hover:-translate-y-1 hover:shadow-theme-md transition-all duration-200 text-left animate-fade-in-up"
45
+ >
46
+ <div className="flex flex-col h-full w-full">
47
+ <div className="flex items-center justify-between px-5 py-3 border-b border-theme-border/50">
48
+ <div className="flex items-center gap-2">
49
+ <ClipboardCheck className={clsx('w-4 h-4', accentColor)} />
50
+ <span className={clsx('text-xs font-semibold uppercase tracking-wider', accentColor)}>Cluster Audit</span>
51
+ {issueCount > 0 ? (
52
+ <span className={clsx('badge-sm', accentBg, accentColor)}>
53
+ {issueCount}
54
+ </span>
55
+ ) : (
56
+ <span className={clsx('badge-sm', accentBg, accentColor)}>
57
+ <Check className="w-3 h-3" />
58
+ </span>
59
+ )}
60
+ </div>
61
+ </div>
62
+
63
+ <div className="flex-1 min-h-0 flex flex-col items-center justify-center px-4 py-4">
64
+ {allPassing ? (
65
+ <div className="flex flex-col items-center gap-2">
66
+ <Check className={clsx('w-8 h-8', SEVERITY_TEXT.success)} />
67
+ <span className={clsx('text-sm font-medium', SEVERITY_TEXT.success)}>All checks passing</span>
68
+ {total > 0 && (
69
+ <span className="text-xs text-theme-text-tertiary">{total} checks across {Object.keys(data.categories).length} categories</span>
70
+ )}
71
+ </div>
72
+ ) : (
73
+ <>
74
+ {/* Distribution bar — only show when we have passing data for context */}
75
+ {data.passing > 0 && total > 0 && (
76
+ <div className="flex items-center gap-3 w-full">
77
+ <div className="flex-1 h-3 rounded-full overflow-hidden bg-theme-hover flex">
78
+ <div className={clsx('h-full', SEVERITY_DOT.success)} style={{ width: `${(data.passing / total) * 100}%` }} />
79
+ {data.warning > 0 && (
80
+ <div className={clsx('h-full', SEVERITY_DOT.warning)} style={{ width: `${(data.warning / total) * 100}%` }} />
81
+ )}
82
+ {data.danger > 0 && (
83
+ <div className={clsx('h-full', SEVERITY_DOT.error)} style={{ width: `${(data.danger / total) * 100}%` }} />
84
+ )}
85
+ </div>
86
+ </div>
87
+ )}
88
+
89
+ {/* Category breakdown */}
90
+ <div className="grid grid-cols-1 gap-y-2 mt-4 w-full">
91
+ {Object.entries(data.categories).map(([category, counts]) => {
92
+ const catIssues = counts.warning + counts.danger
93
+ const dotColor = counts.danger > 0 ? SEVERITY_DOT.error : counts.warning > 0 ? SEVERITY_DOT.warning : SEVERITY_DOT.success
94
+ return (
95
+ <div key={category} className="flex items-center gap-2">
96
+ <span className={clsx('w-2 h-2 rounded-full shrink-0', dotColor)} />
97
+ <span className="text-xs text-theme-text-secondary flex-1">{category}</span>
98
+ {catIssues > 0 ? (
99
+ <div className="flex items-center gap-2">
100
+ {counts.danger > 0 && (
101
+ <span className={clsx('text-xs font-semibold tabular-nums', SEVERITY_TEXT.error)}>{counts.danger} critical</span>
102
+ )}
103
+ {counts.warning > 0 && (
104
+ <span className={clsx('text-xs font-semibold tabular-nums', SEVERITY_TEXT.warning)}>{counts.warning} warning</span>
105
+ )}
106
+ </div>
107
+ ) : (
108
+ <span className={clsx('text-xs font-semibold', SEVERITY_TEXT.success)}>All passing</span>
109
+ )}
110
+ </div>
111
+ )
112
+ })}
113
+ </div>
114
+ </>
115
+ )}
116
+ </div>
117
+
118
+ <div className="px-4 py-1.5 border-t border-theme-border/50 flex items-center justify-end">
119
+ <span className={clsx(
120
+ 'flex items-center gap-1.5 text-[10px] font-semibold uppercase tracking-wider transition-colors',
121
+ accentColor,
122
+ )}>
123
+ View Details
124
+ <ArrowRight className="w-3.5 h-3.5 transition-transform group-hover:translate-x-0.5" />
125
+ </span>
126
+ </div>
127
+ </div>
128
+ </button>
129
+ )
130
+ }