@skyhook-io/k8s-ui 1.6.0 → 1.6.2
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 +7 -2
- package/src/components/charts/AreaChart.tsx +371 -0
- package/src/components/charts/MetricsSummary.tsx +49 -0
- package/src/components/charts/SeriesLegend.tsx +27 -0
- package/src/components/charts/colors.ts +49 -0
- package/src/components/charts/format.ts +41 -0
- package/src/components/charts/index.ts +14 -0
- package/src/components/charts/saturation.test.ts +60 -0
- package/src/components/charts/saturation.ts +32 -0
- package/src/components/charts/types.ts +36 -0
- package/src/components/compare/CompareResourcePicker.tsx +192 -0
- package/src/components/compare/CompareTray.tsx +130 -0
- package/src/components/compare/ResourceCompareView.tsx +272 -0
- package/src/components/compare/index.ts +14 -0
- package/src/components/compare/normalize.test.ts +193 -0
- package/src/components/compare/normalize.ts +69 -0
- package/src/components/compare/picks.test.ts +97 -0
- package/src/components/compare/picks.ts +35 -0
- package/src/components/compare/sort.test.ts +78 -0
- package/src/components/compare/sort.ts +26 -0
- package/src/components/compare/types.ts +43 -0
- package/src/components/compare/url.test.ts +61 -0
- package/src/components/compare/url.ts +26 -0
- package/src/components/gitops/GitOpsDetailLayout.tsx +621 -0
- package/src/components/gitops/GitOpsGraphFilterRail.tsx +216 -0
- package/src/components/gitops/GitOpsTableView.tsx +1441 -0
- package/src/components/gitops/RollbackDialog.tsx +117 -0
- package/src/components/gitops/SyncOptionsDialog.tsx +160 -0
- package/src/components/gitops/detail-helpers.test.ts +97 -0
- package/src/components/gitops/detail-helpers.ts +112 -0
- package/src/components/gitops/index.ts +52 -1
- package/src/components/gitops/short-cluster-name.test.ts +36 -0
- package/src/components/gitops/tree/index.ts +2 -0
- package/src/components/gitops/tree/merge.test.ts +240 -0
- package/src/components/gitops/tree/merge.ts +160 -0
- package/src/components/resources/ResourcesSidebar.tsx +42 -15
- package/src/components/resources/ResourcesView.tsx +403 -69
- package/src/components/resources/index.ts +1 -0
- package/src/components/resources/renderers/CompositeRenderer.tsx +233 -0
- package/src/components/resources/renderers/CompositionRenderer.tsx +218 -0
- package/src/components/resources/renderers/CrossplanePackageRenderer.tsx +145 -0
- package/src/components/resources/renderers/CrossplaneProviderConfigRenderer.tsx +71 -0
- package/src/components/resources/renderers/HPARenderer.tsx +6 -1
- package/src/components/resources/renderers/ManagedResourceRenderer.tsx +131 -0
- package/src/components/resources/renderers/NamespaceRenderer.tsx +223 -0
- package/src/components/resources/renderers/PVCRenderer.tsx +6 -1
- package/src/components/resources/renderers/PodRenderer.tsx +207 -2
- package/src/components/resources/renderers/RoleBindingRenderer.tsx +132 -20
- package/src/components/resources/renderers/RoleRenderer.tsx +148 -18
- package/src/components/resources/renderers/ServiceAccountRenderer.tsx +456 -3
- package/src/components/resources/renderers/WorkloadRenderer.tsx +189 -2
- package/src/components/resources/renderers/XRDRenderer.tsx +153 -0
- package/src/components/resources/renderers/crossplane-cells.tsx +146 -0
- package/src/components/resources/renderers/flux-cells.tsx +12 -0
- package/src/components/resources/renderers/index.ts +8 -0
- package/src/components/resources/resource-utils-crossplane.test.ts +609 -0
- package/src/components/resources/resource-utils-crossplane.ts +325 -0
- package/src/components/resources/resource-utils-flux.ts +14 -0
- package/src/components/resources/resource-utils.ts +1 -0
- package/src/components/resources/resources-column-filter.test.ts +74 -0
- package/src/components/shared/ResourceActionsBar.tsx +77 -0
- package/src/components/shared/ResourceRendererDispatch.tsx +138 -9
- package/src/components/ui/YamlEditor.tsx +28 -15
- package/src/components/ui/drawer-components.tsx +8 -20
- package/src/components/workload/ResourceDetailDrawer.tsx +1 -1
- package/src/components/workload/WorkloadView.tsx +2 -2
- package/src/index.ts +3 -0
- package/src/types/core.ts +31 -6
- package/src/types/index.ts +1 -0
- package/src/types/rbac.ts +99 -0
- package/src/utils/api-resources.ts +9 -1
- package/src/utils/badge-colors.ts +25 -0
- package/src/utils/gitops-owner.test.ts +70 -111
- package/src/utils/gitops-owner.ts +37 -74
- package/src/utils/helm-status.test.ts +50 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/rbac-badges.ts +61 -0
- package/src/utils/rbac-blast-radius.test.ts +137 -0
- package/src/utils/rbac-blast-radius.ts +98 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { CheckCircle2, CircleDot, GitBranch, HeartPulse, LayoutGrid, List, Search } from 'lucide-react'
|
|
2
|
+
|
|
3
|
+
import { GitOpsFacetButton, GitOpsFilterSection } from './GitOpsTableView'
|
|
4
|
+
import type { GitOpsResourceTree } from '../../types/gitops-tree'
|
|
5
|
+
import type { GitOpsTreePreset } from './tree'
|
|
6
|
+
|
|
7
|
+
// =============================================================================
|
|
8
|
+
// GitOpsGraphFilterRail — the left-side filter sidebar that pairs with
|
|
9
|
+
// <GitOpsTreeGraph> in the Topology tab of the GitOps detail page.
|
|
10
|
+
//
|
|
11
|
+
// Originally inline in OSS radar/web/src/components/gitops/GitOpsView.tsx;
|
|
12
|
+
// extracted here so Radar Hub's fleet detail page can mount the same rail
|
|
13
|
+
// next to the same graph. Stateless — caller owns preset/search/filter
|
|
14
|
+
// state and passes it down; click handlers thread back through callbacks.
|
|
15
|
+
//
|
|
16
|
+
// buildTreeFacets is the data-prep helper that turns a ResourceTree into
|
|
17
|
+
// the facet count buckets the rail consumes. Exported so callers can
|
|
18
|
+
// build it once + pass into both this rail and any sibling display.
|
|
19
|
+
// =============================================================================
|
|
20
|
+
|
|
21
|
+
export interface GitOpsTreeFacets {
|
|
22
|
+
kinds: Array<{ name: string; count: number }>
|
|
23
|
+
sync: Array<{ name: string; count: number }>
|
|
24
|
+
health: Array<{ name: string; count: number }>
|
|
25
|
+
namespaces: Array<{ name: string; count: number }>
|
|
26
|
+
roles: Array<{ name: string; count: number }>
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function buildTreeFacets(tree: GitOpsResourceTree | null | undefined): GitOpsTreeFacets {
|
|
30
|
+
const nodes = tree?.nodes ?? []
|
|
31
|
+
return {
|
|
32
|
+
kinds: countValues(
|
|
33
|
+
nodes.filter((n) => n.role !== 'group').map((n) => n.ref.kind).filter(Boolean),
|
|
34
|
+
),
|
|
35
|
+
sync: countValues(nodes.map((n) => n.sync || 'Unknown')),
|
|
36
|
+
health: countValues(nodes.map((n) => n.health || 'Unknown')),
|
|
37
|
+
namespaces: countValues(nodes.map((n) => n.ref.namespace || '(cluster)')),
|
|
38
|
+
roles: countValues(nodes.map((n) => n.role)),
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface GitOpsGraphFilterRailProps {
|
|
43
|
+
facets: GitOpsTreeFacets
|
|
44
|
+
preset: GitOpsTreePreset
|
|
45
|
+
onPresetChange: (preset: GitOpsTreePreset) => void
|
|
46
|
+
search: string
|
|
47
|
+
onSearchChange: (value: string) => void
|
|
48
|
+
kinds: Set<string>
|
|
49
|
+
onToggleKind: (value: string) => void
|
|
50
|
+
sync: Set<string>
|
|
51
|
+
onToggleSync: (value: string) => void
|
|
52
|
+
health: Set<string>
|
|
53
|
+
onToggleHealth: (value: string) => void
|
|
54
|
+
namespaces: Set<string>
|
|
55
|
+
onToggleNamespace: (value: string) => void
|
|
56
|
+
roles: Set<string>
|
|
57
|
+
onToggleRole: (value: string) => void
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function GitOpsGraphFilterRail({
|
|
61
|
+
facets,
|
|
62
|
+
preset,
|
|
63
|
+
onPresetChange,
|
|
64
|
+
search,
|
|
65
|
+
onSearchChange,
|
|
66
|
+
kinds,
|
|
67
|
+
onToggleKind,
|
|
68
|
+
sync,
|
|
69
|
+
onToggleSync,
|
|
70
|
+
health,
|
|
71
|
+
onToggleHealth,
|
|
72
|
+
namespaces,
|
|
73
|
+
onToggleNamespace,
|
|
74
|
+
roles,
|
|
75
|
+
onToggleRole,
|
|
76
|
+
}: GitOpsGraphFilterRailProps) {
|
|
77
|
+
return (
|
|
78
|
+
<aside className="min-h-0 overflow-y-auto bg-theme-surface/90 max-lg:h-48 max-lg:max-h-48">
|
|
79
|
+
<div className="border-b border-theme-border px-3 py-3">
|
|
80
|
+
<div className="relative">
|
|
81
|
+
<Search className="pointer-events-none absolute left-2.5 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-theme-text-tertiary" />
|
|
82
|
+
<input
|
|
83
|
+
value={search}
|
|
84
|
+
onChange={(event) => onSearchChange(event.target.value)}
|
|
85
|
+
placeholder="Filter resources..."
|
|
86
|
+
className="h-8 w-full rounded-md border border-theme-border bg-theme-base pl-8 pr-3 text-sm text-theme-text-primary placeholder:text-theme-text-tertiary focus:outline-none focus:ring-1 focus:ring-blue-500/50"
|
|
87
|
+
/>
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
<GitOpsFilterSection icon={GitBranch} title="Graph">
|
|
91
|
+
<div className="grid grid-cols-2 gap-1">
|
|
92
|
+
{(['compact', 'workloads', 'app', 'full'] as GitOpsTreePreset[]).map((value) => (
|
|
93
|
+
<button
|
|
94
|
+
key={value}
|
|
95
|
+
type="button"
|
|
96
|
+
onClick={() => onPresetChange(value)}
|
|
97
|
+
className={`rounded-md px-2 py-1.5 text-left text-[11px] font-medium transition-colors ${
|
|
98
|
+
preset === value
|
|
99
|
+
? 'bg-skyhook-500 text-white'
|
|
100
|
+
: 'bg-theme-elevated text-theme-text-secondary hover:bg-theme-hover hover:text-theme-text-primary'
|
|
101
|
+
}`}
|
|
102
|
+
>
|
|
103
|
+
{value === 'app' ? 'Declared' : value[0].toUpperCase() + value.slice(1)}
|
|
104
|
+
</button>
|
|
105
|
+
))}
|
|
106
|
+
</div>
|
|
107
|
+
</GitOpsFilterSection>
|
|
108
|
+
<GitOpsFilterSection icon={List} title="Kinds">
|
|
109
|
+
{facets.kinds.slice(0, 14).map((item) => (
|
|
110
|
+
<GitOpsFacetButton
|
|
111
|
+
key={item.name}
|
|
112
|
+
label={item.name}
|
|
113
|
+
count={item.count}
|
|
114
|
+
active={kinds.has(item.name)}
|
|
115
|
+
onClick={() => onToggleKind(item.name)}
|
|
116
|
+
/>
|
|
117
|
+
))}
|
|
118
|
+
</GitOpsFilterSection>
|
|
119
|
+
<GitOpsFilterSection icon={CheckCircle2} title="Sync">
|
|
120
|
+
{facets.sync.map((item) => (
|
|
121
|
+
<GitOpsFacetButton
|
|
122
|
+
key={item.name}
|
|
123
|
+
label={item.name}
|
|
124
|
+
count={item.count}
|
|
125
|
+
active={sync.has(item.name)}
|
|
126
|
+
tone={syncTone(item.name)}
|
|
127
|
+
onClick={() => onToggleSync(item.name)}
|
|
128
|
+
/>
|
|
129
|
+
))}
|
|
130
|
+
</GitOpsFilterSection>
|
|
131
|
+
<GitOpsFilterSection icon={HeartPulse} title="Health">
|
|
132
|
+
{facets.health.map((item) => (
|
|
133
|
+
<GitOpsFacetButton
|
|
134
|
+
key={item.name}
|
|
135
|
+
label={item.name}
|
|
136
|
+
count={item.count}
|
|
137
|
+
active={health.has(item.name)}
|
|
138
|
+
tone={healthTone(item.name)}
|
|
139
|
+
onClick={() => onToggleHealth(item.name)}
|
|
140
|
+
/>
|
|
141
|
+
))}
|
|
142
|
+
</GitOpsFilterSection>
|
|
143
|
+
<GitOpsFilterSection icon={CircleDot} title="Role">
|
|
144
|
+
{facets.roles.map((item) => (
|
|
145
|
+
<GitOpsFacetButton
|
|
146
|
+
key={item.name}
|
|
147
|
+
label={roleLabel(item.name)}
|
|
148
|
+
count={item.count}
|
|
149
|
+
active={roles.has(item.name)}
|
|
150
|
+
onClick={() => onToggleRole(item.name)}
|
|
151
|
+
/>
|
|
152
|
+
))}
|
|
153
|
+
</GitOpsFilterSection>
|
|
154
|
+
<GitOpsFilterSection icon={LayoutGrid} title="Namespaces">
|
|
155
|
+
{facets.namespaces.slice(0, 12).map((item) => (
|
|
156
|
+
<GitOpsFacetButton
|
|
157
|
+
key={item.name}
|
|
158
|
+
label={item.name}
|
|
159
|
+
count={item.count}
|
|
160
|
+
active={namespaces.has(item.name)}
|
|
161
|
+
onClick={() => onToggleNamespace(item.name)}
|
|
162
|
+
/>
|
|
163
|
+
))}
|
|
164
|
+
</GitOpsFilterSection>
|
|
165
|
+
</aside>
|
|
166
|
+
)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// ----- Helpers shared with consumers -----
|
|
170
|
+
|
|
171
|
+
// toggleSet is the standard Set<string> add/remove pair used to back the
|
|
172
|
+
// rail's facet click handlers. Caller owns the Set state; this just keeps
|
|
173
|
+
// the call-site terse.
|
|
174
|
+
export function toggleSet(set: Set<string>, setter: (next: Set<string>) => void, value: string) {
|
|
175
|
+
const next = new Set(set)
|
|
176
|
+
if (next.has(value)) next.delete(value)
|
|
177
|
+
else next.add(value)
|
|
178
|
+
setter(next)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function countValues(values: string[]): Array<{ name: string; count: number }> {
|
|
182
|
+
const map = new Map<string, number>()
|
|
183
|
+
for (const v of values) {
|
|
184
|
+
if (!v) continue
|
|
185
|
+
map.set(v, (map.get(v) ?? 0) + 1)
|
|
186
|
+
}
|
|
187
|
+
return Array.from(map.entries())
|
|
188
|
+
.map(([name, count]) => ({ name, count }))
|
|
189
|
+
.sort((a, b) => b.count - a.count)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function syncTone(value: string): 'neutral' | 'success' | 'warning' | 'error' | 'info' {
|
|
193
|
+
if (value === 'Synced') return 'success'
|
|
194
|
+
if (value === 'OutOfSync') return 'warning'
|
|
195
|
+
if (value === 'Reconciling') return 'info'
|
|
196
|
+
return 'neutral'
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function healthTone(value: string): 'neutral' | 'success' | 'warning' | 'error' | 'info' {
|
|
200
|
+
if (value === 'Healthy') return 'success'
|
|
201
|
+
if (value === 'Degraded' || value === 'Missing') return 'error'
|
|
202
|
+
if (value === 'Progressing') return 'info'
|
|
203
|
+
if (value === 'Suspended') return 'warning'
|
|
204
|
+
return 'neutral'
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function roleLabel(value: string): string {
|
|
208
|
+
return (
|
|
209
|
+
{
|
|
210
|
+
root: 'Root',
|
|
211
|
+
declared: 'Declared',
|
|
212
|
+
generated: 'Generated',
|
|
213
|
+
group: 'Groups',
|
|
214
|
+
} as Record<string, string>
|
|
215
|
+
)[value] ?? value
|
|
216
|
+
}
|