@skyhook-io/k8s-ui 1.14.12 → 1.16.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 +3 -3
- package/src/components/audit/AuditFindingsTable.tsx +1 -1
- package/src/components/checks/ChecksView.tsx +8 -8
- package/src/components/compare/CompareResourcePicker.tsx +4 -3
- package/src/components/gitops/GitOpsDetailLayout.tsx +3 -1
- package/src/components/gitops/GitOpsHealthSourceNotice.tsx +12 -2
- package/src/components/gitops/RollbackDialog.tsx +4 -3
- package/src/components/gitops/SyncOptionsDialog.tsx +4 -3
- package/src/components/gitops/health-provenance.test.ts +1 -1
- package/src/components/gitops/index.ts +1 -0
- package/src/components/gitops/insights/ArgoResourceDiff.tsx +1 -0
- package/src/components/gitops/insights/GitOpsInsightViews.tsx +4 -1
- package/src/components/issues/IssuesView.tsx +2 -2
- package/src/components/logs/LogCore.tsx +131 -97
- package/src/components/logs/StructuredLogLine.tsx +18 -28
- package/src/components/logs/log-palette.ts +4 -0
- package/src/components/logs/useLogBuffer.ts +8 -39
- package/src/components/logs/useLogSearch.ts +52 -24
- package/src/components/namespace-switcher/NamespacePicker.test.tsx +140 -0
- package/src/components/namespace-switcher/NamespacePicker.tsx +26 -20
- package/src/components/resources/KueueAdmissionSection.test.tsx +183 -6
- package/src/components/resources/KueueAdmissionSection.tsx +140 -51
- package/src/components/resources/ResourcesView.tsx +4 -0
- package/src/components/resources/printer-columns.test.ts +1 -1
- package/src/components/resources/renderers/JobRenderer.tsx +5 -1
- package/src/components/resources/renderers/JobSetRenderer.tsx +4 -1
- package/src/components/resources/renderers/KueueQueueNavigation.test.tsx +2 -2
- package/src/components/resources/renderers/KueueWorkloadRenderer.tsx +2 -15
- package/src/components/resources/renderers/PodRenderer.test.tsx +15 -0
- package/src/components/resources/renderers/PodRenderer.tsx +10 -2
- package/src/components/resources/renderers/PolicySection.test.tsx +42 -0
- package/src/components/resources/renderers/PolicySection.tsx +10 -1
- package/src/components/resources/renderers/RayClusterRenderer.tsx +7 -7
- package/src/components/resources/renderers/RayJobRenderer.test.tsx +71 -0
- package/src/components/resources/renderers/RayJobRenderer.tsx +134 -0
- package/src/components/resources/renderers/ReflectorSection.test.tsx +4 -2
- package/src/components/resources/renderers/RolloutRenderer.test.tsx +1 -1
- package/src/components/resources/renderers/WorkloadRenderer.tsx +7 -3
- package/src/components/resources/renderers/index.ts +2 -0
- package/src/components/resources/resource-utils-kueue.ts +14 -0
- package/src/components/resources/resource-utils-ray.ts +14 -4
- package/src/components/shared/CreateResourceDialog.tsx +6 -2
- package/src/components/shared/DrainPlanDialog.test.tsx +10 -0
- package/src/components/shared/DrainPlanDialog.tsx +14 -3
- package/src/components/shared/ResourceActionsBar.tsx +18 -11
- package/src/components/shared/ResourceRendererDispatch.test.tsx +12 -5
- package/src/components/shared/ResourceRendererDispatch.tsx +15 -6
- package/src/components/shared/SetImageDialog.tsx +4 -2
- package/src/components/shared/revision-role-badges.test.ts +1 -1
- package/src/components/trace/reachGraphModel.test.ts +1 -0
- package/src/components/trace/reachInspector.test.ts +4 -9
- package/src/components/ui/ConfirmDialog.tsx +5 -3
- package/src/components/ui/DialogPortal.test.tsx +279 -0
- package/src/components/ui/DialogPortal.tsx +147 -9
- package/src/components/ui/FilterPill.tsx +1 -1
- package/src/components/ui/RadarUpgradeNote.click.test.tsx +24 -0
- package/src/components/ui/RadarUpgradeNote.test.tsx +66 -0
- package/src/components/ui/RadarUpgradeNote.tsx +129 -0
- package/src/components/ui/ResourceBar.tsx +12 -6
- package/src/components/ui/SelectMenu.tsx +62 -19
- package/src/components/ui/YamlReview.tsx +4 -1
- package/src/components/ui/drawer-components.test.tsx +14 -1
- package/src/components/ui/drawer-components.tsx +50 -40
- package/src/components/ui/index.ts +8 -0
- package/src/components/workload/WorkloadView.tsx +109 -3
- package/src/types/core.ts +24 -4
- package/src/types/fetch-error.ts +33 -0
- package/src/utils/log-format.ts +1 -1
- package/src/utils/log-level.test.ts +269 -0
- package/src/utils/log-level.ts +301 -0
- package/src/utils/resource-hierarchy.test.ts +69 -0
- package/src/utils/resource-hierarchy.ts +87 -52
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@skyhook-io/k8s-ui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.16.0",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/skyhook-io/radar",
|
|
@@ -100,12 +100,12 @@
|
|
|
100
100
|
"diff": "^9.0.0",
|
|
101
101
|
"elkjs": "^0.11.1",
|
|
102
102
|
"jsdom": "^29.1.1",
|
|
103
|
-
"lucide-react": "^1.
|
|
103
|
+
"lucide-react": "^1.48.0",
|
|
104
104
|
"monaco-editor": "0.55.1",
|
|
105
105
|
"react": "^19.3.0",
|
|
106
106
|
"react-dom": "^19.3.0",
|
|
107
107
|
"typescript": "^6.0.2",
|
|
108
|
-
"vitest": "^
|
|
108
|
+
"vitest": "^5.0.2",
|
|
109
109
|
"yaml": "^2.9.0"
|
|
110
110
|
}
|
|
111
111
|
}
|
|
@@ -515,7 +515,7 @@ function FlatFindingRow({ finding, onResourceClick, showCluster, onClusterClick
|
|
|
515
515
|
onClusterClick ? (
|
|
516
516
|
<button
|
|
517
517
|
onClick={() => onClusterClick(finding.cluster!.id)}
|
|
518
|
-
className="text-xs text-[var(--color-radar-accent)] hover:underline shrink-0 max-w-[160px] truncate text-left"
|
|
518
|
+
className="text-xs text-[var(--color-radar-accent,var(--accent))] hover:underline shrink-0 max-w-[160px] truncate text-left"
|
|
519
519
|
>
|
|
520
520
|
{finding.cluster.name}
|
|
521
521
|
</button>
|
|
@@ -285,7 +285,7 @@ export function ChecksView({ checks, catalog, anyData, evaluated, missingInputs
|
|
|
285
285
|
placeholder="Search checks…"
|
|
286
286
|
value={search}
|
|
287
287
|
onChange={(e) => filters.setString('q', e.target.value)}
|
|
288
|
-
className="w-64 rounded-lg border border-theme-border-light bg-theme-base py-1.5 pl-9 pr-8 text-sm text-theme-text-primary placeholder-theme-text-disabled focus:outline-none focus:ring-2 focus:ring-[var(--color-radar-accent)]"
|
|
288
|
+
className="w-64 rounded-lg border border-theme-border-light bg-theme-base py-1.5 pl-9 pr-8 text-sm text-theme-text-primary placeholder-theme-text-disabled focus:outline-none focus:ring-2 focus:ring-[var(--color-radar-accent,var(--accent))]"
|
|
289
289
|
/>
|
|
290
290
|
{search && (
|
|
291
291
|
<button
|
|
@@ -340,7 +340,7 @@ export function ChecksView({ checks, catalog, anyData, evaluated, missingInputs
|
|
|
340
340
|
return (
|
|
341
341
|
<span
|
|
342
342
|
key={id}
|
|
343
|
-
className="inline-flex items-center gap-1 rounded-full border border-[var(--color-radar-accent)]/30 bg-[var(--color-radar-accent)]/10 py-1 pl-2.5 pr-1 text-xs text-theme-text-primary"
|
|
343
|
+
className="inline-flex items-center gap-1 rounded-full border border-[var(--color-radar-accent,var(--accent))]/30 bg-[var(--color-radar-accent,var(--accent))]/10 py-1 pl-2.5 pr-1 text-xs text-theme-text-primary"
|
|
344
344
|
>
|
|
345
345
|
<span className="min-w-0 max-w-[12rem] truncate">
|
|
346
346
|
<ClusterName name={label} />
|
|
@@ -520,7 +520,7 @@ function CheckReferenceLinks({ references }: { references: CheckReference[] }) {
|
|
|
520
520
|
href={r.url}
|
|
521
521
|
target="_blank"
|
|
522
522
|
rel="noreferrer"
|
|
523
|
-
className="inline-flex items-center gap-1 text-xs font-medium text-[var(--color-radar-accent)] hover:underline"
|
|
523
|
+
className="inline-flex items-center gap-1 text-xs font-medium text-[var(--color-radar-accent,var(--accent))] hover:underline"
|
|
524
524
|
>
|
|
525
525
|
{r.label}
|
|
526
526
|
<ExternalLink className="h-3 w-3" />
|
|
@@ -594,7 +594,7 @@ export function CheckCardShell({
|
|
|
594
594
|
onToggle()
|
|
595
595
|
}
|
|
596
596
|
}}
|
|
597
|
-
className={`group flex cursor-pointer items-center gap-3 border-l-[3px] py-3 pl-3 pr-4 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-radar-accent)]/40 ${open ? SEVERITY_HEADER_BAND_CLASS[sev] : SEVERITY_RAIL_CLASS[sev]}`}
|
|
597
|
+
className={`group flex cursor-pointer items-center gap-3 border-l-[3px] py-3 pl-3 pr-4 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-radar-accent,var(--accent))]/40 ${open ? SEVERITY_HEADER_BAND_CLASS[sev] : SEVERITY_RAIL_CLASS[sev]}`}
|
|
598
598
|
>
|
|
599
599
|
<SeverityIcon className={`h-[18px] w-[18px] shrink-0 ${SEVERITY_TEXT_CLASS[sev]}`} aria-hidden />
|
|
600
600
|
|
|
@@ -701,7 +701,7 @@ export function CheckClusterBreakdownShell<T extends CheckClusterBreakdownGroup>
|
|
|
701
701
|
<button
|
|
702
702
|
type="button"
|
|
703
703
|
onClick={() => setShowAllClusters(true)}
|
|
704
|
-
className="mt-0.5 inline-flex w-fit items-center gap-1 rounded px-2 py-1 text-xs font-medium text-[var(--color-radar-accent)] hover:underline"
|
|
704
|
+
className="mt-0.5 inline-flex w-fit items-center gap-1 rounded px-2 py-1 text-xs font-medium text-[var(--color-radar-accent,var(--accent))] hover:underline"
|
|
705
705
|
>
|
|
706
706
|
View all {groups.length} clusters →
|
|
707
707
|
</button>
|
|
@@ -849,7 +849,7 @@ function ResourceList({
|
|
|
849
849
|
<button
|
|
850
850
|
type="button"
|
|
851
851
|
onClick={() => setShowAll(true)}
|
|
852
|
-
className="mt-0.5 inline-flex w-fit items-center gap-1 rounded px-2 py-1 text-xs font-medium text-[var(--color-radar-accent)] hover:underline"
|
|
852
|
+
className="mt-0.5 inline-flex w-fit items-center gap-1 rounded px-2 py-1 text-xs font-medium text-[var(--color-radar-accent,var(--accent))] hover:underline"
|
|
853
853
|
>
|
|
854
854
|
View all {check.findings.length} →
|
|
855
855
|
</button>
|
|
@@ -875,7 +875,7 @@ function FindingLine({
|
|
|
875
875
|
<>
|
|
876
876
|
<span className="flex min-w-0 items-baseline gap-2">
|
|
877
877
|
<span className="shrink-0 font-mono text-[11px] uppercase tracking-wide text-theme-text-tertiary">{r.kind}</span>
|
|
878
|
-
<span className={`min-w-0 break-all font-medium ${linkable ? 'text-[var(--color-radar-accent)]' : 'text-theme-text-primary'}`}>
|
|
878
|
+
<span className={`min-w-0 break-all font-medium ${linkable ? 'text-[var(--color-radar-accent,var(--accent))]' : 'text-theme-text-primary'}`}>
|
|
879
879
|
{r.namespace ? `${r.namespace} / ` : ''}
|
|
880
880
|
{r.name}
|
|
881
881
|
</span>
|
|
@@ -1010,7 +1010,7 @@ function ClusterFilter({
|
|
|
1010
1010
|
onClick={() => onToggle(o.id)}
|
|
1011
1011
|
className="flex w-full items-center gap-2 px-3 py-1.5 text-left text-sm text-theme-text-secondary transition-colors hover:bg-theme-hover hover:text-theme-text-primary"
|
|
1012
1012
|
>
|
|
1013
|
-
<span className={`flex h-3.5 w-3.5 shrink-0 items-center justify-center rounded-sm border ${on ? 'border-[var(--color-radar-accent)] bg-[var(--color-radar-accent)] text-white' : 'border-theme-border'}`}>
|
|
1013
|
+
<span className={`flex h-3.5 w-3.5 shrink-0 items-center justify-center rounded-sm border ${on ? 'border-[var(--color-radar-accent,var(--accent))] bg-[var(--color-radar-accent,var(--accent))] text-white' : 'border-theme-border'}`}>
|
|
1014
1014
|
{on && <span className="text-[9px] leading-none">✓</span>}
|
|
1015
1015
|
</span>
|
|
1016
1016
|
<span className="min-w-0 truncate">
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useEffect, useMemo, useRef, useState } from 'react'
|
|
1
|
+
import { useEffect, useMemo, useRef, useState, useId } from 'react'
|
|
2
2
|
import { clsx } from 'clsx'
|
|
3
3
|
import { GitCompare, Search, X } from 'lucide-react'
|
|
4
4
|
import { DialogPortal } from '../ui/DialogPortal'
|
|
@@ -33,6 +33,7 @@ export function CompareResourcePicker({
|
|
|
33
33
|
error,
|
|
34
34
|
onPick,
|
|
35
35
|
}: CompareResourcePickerProps) {
|
|
36
|
+
const titleId = useId()
|
|
36
37
|
const [query, setQuery] = useState('')
|
|
37
38
|
const [highlightIdx, setHighlightIdx] = useState(0)
|
|
38
39
|
const listRef = useRef<HTMLUListElement | null>(null)
|
|
@@ -90,11 +91,11 @@ export function CompareResourcePicker({
|
|
|
90
91
|
const sourceChipBg = SIDE_TONES[sourceSide].chipBg
|
|
91
92
|
|
|
92
93
|
return (
|
|
93
|
-
<DialogPortal open={open} onClose={onClose} className="max-w-xl w-full max-h-[70vh] flex flex-col">
|
|
94
|
+
<DialogPortal ariaLabelledBy={titleId} open={open} onClose={onClose} className="max-w-xl w-full max-h-[70vh] flex flex-col">
|
|
94
95
|
<div className="flex items-center justify-between px-4 py-3 border-b border-theme-border shrink-0">
|
|
95
96
|
<div className="flex items-center gap-2">
|
|
96
97
|
<GitCompare className="w-5 h-5 text-skyhook-400" />
|
|
97
|
-
<h3 className="text-sm font-semibold text-theme-text-primary">
|
|
98
|
+
<h3 id={titleId} className="text-sm font-semibold text-theme-text-primary">
|
|
98
99
|
{sourceSide === 'a'
|
|
99
100
|
? `Compare to another ${pluralToKind(source.kind)}`
|
|
100
101
|
: `Replace side B with another ${pluralToKind(source.kind)}`}
|
|
@@ -147,6 +147,7 @@ export interface GitOpsDetailLayoutProps {
|
|
|
147
147
|
remoteDestinationHint?: ReactNode
|
|
148
148
|
// Opens the host's Argo CD settings, when it has such a place.
|
|
149
149
|
onOpenSettings?: () => void
|
|
150
|
+
settingsNote?: string
|
|
150
151
|
|
|
151
152
|
// Action buttons — Argo + Flux. Exactly one applies for any given CR.
|
|
152
153
|
isArgoApp: boolean
|
|
@@ -235,6 +236,7 @@ export function GitOpsDetailLayout(props: GitOpsDetailLayoutProps) {
|
|
|
235
236
|
healthDocsUrl,
|
|
236
237
|
remoteDestinationHint,
|
|
237
238
|
onOpenSettings,
|
|
239
|
+
settingsNote,
|
|
238
240
|
isArgoApp,
|
|
239
241
|
isFlux,
|
|
240
242
|
isFluxWorkload,
|
|
@@ -475,7 +477,7 @@ export function GitOpsDetailLayout(props: GitOpsDetailLayoutProps) {
|
|
|
475
477
|
{!fullscreen && (
|
|
476
478
|
<>
|
|
477
479
|
<GitOpsStatusStrip insight={insight ?? undefined} loading={insightLoading} renderRevisionMeta={renderRevisionMeta} />
|
|
478
|
-
<GitOpsHealthSourceNotice summary={insight?.summary} changes={insight?.changes} docsUrl={healthDocsUrl} remoteDestinationHint={remoteDestinationHint} onOpenSettings={onOpenSettings} />
|
|
480
|
+
<GitOpsHealthSourceNotice summary={insight?.summary} changes={insight?.changes} docsUrl={healthDocsUrl} remoteDestinationHint={remoteDestinationHint} onOpenSettings={onOpenSettings} settingsNote={settingsNote} />
|
|
479
481
|
<GitOpsIssuesBand
|
|
480
482
|
issues={insight?.issues}
|
|
481
483
|
terminating={terminating}
|
|
@@ -61,12 +61,18 @@ export function hasRadarFinding(changes: GitOpsChange[] | undefined): boolean {
|
|
|
61
61
|
return (changes ?? []).some((c) => c.healthSource === 'radar' && (c.health === 'Degraded' || c.health === 'Missing'))
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
export function hasHealthSettingsHint(summary: HealthSourceNoticeSummary | undefined, changes: GitOpsChange[] | undefined): boolean {
|
|
65
|
+
return healthSourceNoticeKind(summary) === 'appTree'
|
|
66
|
+
&& (!!summary?.resourceHealthApiError || (!hasRadarFinding(changes) && summary?.health === 'Degraded'))
|
|
67
|
+
}
|
|
68
|
+
|
|
64
69
|
export function GitOpsHealthSourceNotice({
|
|
65
70
|
summary,
|
|
66
71
|
changes,
|
|
67
72
|
docsUrl,
|
|
68
73
|
remoteDestinationHint,
|
|
69
74
|
onOpenSettings,
|
|
75
|
+
settingsNote,
|
|
70
76
|
}: {
|
|
71
77
|
summary: HealthSourceNoticeSummary | undefined
|
|
72
78
|
// Decides between the two appTree sentences: with a Radar-sourced
|
|
@@ -84,6 +90,9 @@ export function GitOpsHealthSourceNotice({
|
|
|
84
90
|
// the first way out, and "check Settings" when a configured connection
|
|
85
91
|
// didn't deliver. Without it, only the Argo-side knob is named.
|
|
86
92
|
onOpenSettings?: () => void
|
|
93
|
+
// Host context shown before the settings link, such as settings that can
|
|
94
|
+
// be copied for this cluster.
|
|
95
|
+
settingsNote?: string
|
|
87
96
|
}) {
|
|
88
97
|
const kind = healthSourceNoticeKind(summary)
|
|
89
98
|
if (!kind) return null
|
|
@@ -92,7 +101,7 @@ export function GitOpsHealthSourceNotice({
|
|
|
92
101
|
// app with no Radar finding has nothing here to attribute.
|
|
93
102
|
const radarFinding = hasRadarFinding(changes)
|
|
94
103
|
const apiError = summary?.resourceHealthApiError
|
|
95
|
-
if (kind === 'appTree' && !radarFinding && !
|
|
104
|
+
if (kind === 'appTree' && !radarFinding && !hasHealthSettingsHint(summary, changes)) return null
|
|
96
105
|
const settingsLink = (label: string) =>
|
|
97
106
|
onOpenSettings ? (
|
|
98
107
|
<button type="button" onClick={onOpenSettings} className="underline decoration-theme-border underline-offset-2 hover:text-theme-text-primary">
|
|
@@ -117,7 +126,7 @@ export function GitOpsHealthSourceNotice({
|
|
|
117
126
|
{apiError ? (
|
|
118
127
|
<>
|
|
119
128
|
{APP_TREE_API_ERROR_NOTICE}: {apiError}.
|
|
120
|
-
{onOpenSettings && <> {settingsLink('Check Argo CD in Settings')}.</>}
|
|
129
|
+
{onOpenSettings && <> {settingsNote} {settingsLink('Check Argo CD in Settings')}.</>}
|
|
121
130
|
{' '}
|
|
122
131
|
{radarFinding ? APP_TREE_API_ERROR_FINDINGS : APP_TREE_API_ERROR_NO_FINDINGS}
|
|
123
132
|
</>
|
|
@@ -127,6 +136,7 @@ export function GitOpsHealthSourceNotice({
|
|
|
127
136
|
{!radarFinding && (
|
|
128
137
|
<>
|
|
129
138
|
{' '}
|
|
139
|
+
{settingsNote && onOpenSettings && <>{settingsNote}{' '}</>}
|
|
130
140
|
To see Argo's per-resource health here, {onOpenSettings && <>{settingsLink('connect Radar to your Argo CD server')}, or </>}
|
|
131
141
|
{APP_TREE_PERSIST_REMEDY}
|
|
132
142
|
</>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useState, useEffect } from 'react'
|
|
1
|
+
import { useState, useEffect, useId } from 'react'
|
|
2
2
|
import { History, Loader2 } from 'lucide-react'
|
|
3
3
|
|
|
4
4
|
import { DialogPortal } from '../ui/DialogPortal'
|
|
@@ -30,6 +30,7 @@ export interface RollbackDialogProps {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
export function RollbackDialog({ open, appLabel, revision, historyId, pending, onCancel, onConfirm }: RollbackDialogProps) {
|
|
33
|
+
const titleId = useId()
|
|
33
34
|
const [prune, setPrune] = useState(false)
|
|
34
35
|
const [dryRun, setDryRun] = useState(false)
|
|
35
36
|
|
|
@@ -41,9 +42,9 @@ export function RollbackDialog({ open, appLabel, revision, historyId, pending, o
|
|
|
41
42
|
}, [open])
|
|
42
43
|
|
|
43
44
|
return (
|
|
44
|
-
<DialogPortal open={open} onClose={pending ? () => {} : onCancel} className="w-[440px]" closable={!pending}>
|
|
45
|
+
<DialogPortal ariaLabelledBy={titleId} open={open} onClose={pending ? () => {} : onCancel} className="w-[440px]" closable={!pending}>
|
|
45
46
|
<div className="border-b border-theme-border px-4 py-3">
|
|
46
|
-
<h2 className="text-sm font-semibold text-theme-text-primary">Roll back application</h2>
|
|
47
|
+
<h2 id={titleId} className="text-sm font-semibold text-theme-text-primary">Roll back application</h2>
|
|
47
48
|
<p className="mt-0.5 text-xs text-theme-text-tertiary">{appLabel}</p>
|
|
48
49
|
</div>
|
|
49
50
|
<div className="space-y-4 px-4 py-4 text-sm">
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useState, useEffect, type ComponentType } from 'react'
|
|
1
|
+
import { useState, useEffect, type ComponentType, useId } from 'react'
|
|
2
2
|
import { AlertTriangle, CheckCircle2, CircleAlert, Loader2, RefreshCw, ShieldCheck, XCircle } from 'lucide-react'
|
|
3
3
|
|
|
4
4
|
import { DialogPortal } from '../ui/DialogPortal'
|
|
@@ -58,6 +58,7 @@ export interface ResourceValidationResult {
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
export function SyncOptionsDialog({ open, appLabel, resource, pending, autoSyncEnabled, validationPending, operationInProgress, validationResult, validationError, onCancel, onConfirm, onValidate, onValidationReset }: SyncOptionsDialogProps) {
|
|
61
|
+
const titleId = useId()
|
|
61
62
|
const [revision, setRevision] = useState('')
|
|
62
63
|
const [prune, setPrune] = useState(true)
|
|
63
64
|
const [dryRun, setDryRun] = useState(false)
|
|
@@ -97,9 +98,9 @@ export function SyncOptionsDialog({ open, appLabel, resource, pending, autoSyncE
|
|
|
97
98
|
}
|
|
98
99
|
|
|
99
100
|
return (
|
|
100
|
-
<DialogPortal open={open} onClose={busy ? () => {} : onCancel} className="w-[480px]" closable={!busy}>
|
|
101
|
+
<DialogPortal ariaLabelledBy={titleId} open={open} onClose={busy ? () => {} : onCancel} className="w-[480px]" closable={!busy}>
|
|
101
102
|
<div className="border-b border-theme-border px-4 py-3">
|
|
102
|
-
<h2 className="text-sm font-semibold text-theme-text-primary">{resource ? 'Sync resource' : 'Sync application'}</h2>
|
|
103
|
+
<h2 id={titleId} className="text-sm font-semibold text-theme-text-primary">{resource ? 'Sync resource' : 'Sync application'}</h2>
|
|
103
104
|
<p className="mt-0.5 break-all text-xs text-theme-text-tertiary">{appLabel}</p>
|
|
104
105
|
</div>
|
|
105
106
|
<div className="space-y-4 px-4 py-4 text-sm">
|
|
@@ -30,7 +30,7 @@ describe('radarHealthNote', () => {
|
|
|
30
30
|
|
|
31
31
|
test('hasRadarFinding reads the rows the markers read', () => {
|
|
32
32
|
const row = (health: string, healthSource?: string) =>
|
|
33
|
-
({ ref: { kind: 'Deployment', namespace: 'p', name: 'w' }, category: 'Unknown', health, healthSource, hasDesired: false, hasLive: true }) as const
|
|
33
|
+
({ ref: { kind: 'Deployment', namespace: 'p', name: 'w' }, category: 'Unknown', health, healthSource, hasDesired: false, hasLive: true, partial: false }) as const
|
|
34
34
|
expect(hasRadarFinding(undefined)).toBe(false)
|
|
35
35
|
expect(hasRadarFinding([row('Degraded', 'controller')])).toBe(false)
|
|
36
36
|
expect(hasRadarFinding([row('Healthy', 'radar')])).toBe(false)
|
|
@@ -61,4 +61,5 @@ export type {
|
|
|
61
61
|
GitOpsHelmValuesData,
|
|
62
62
|
} from './GitOpsDetailLayout'
|
|
63
63
|
export { GitOpsHealthSourceNotice, healthSourceNoticeKind, hasRadarFinding, APP_TREE_HEALTH_NOTICE, APP_TREE_NO_FINDINGS_NOTICE, REMOTE_DESTINATION_NOTICE, REMOTE_FLUX_TARGET_NOTICE } from './GitOpsHealthSourceNotice'
|
|
64
|
+
export { hasHealthSettingsHint } from './GitOpsHealthSourceNotice'
|
|
64
65
|
export { radarHealthNote, RADAR_HEALTH_NOTE } from './health-provenance'
|
|
@@ -83,6 +83,7 @@ export function ArgoResourceDiff({ diff, loading, error }: ArgoResourceDiffProps
|
|
|
83
83
|
<DialogPortal
|
|
84
84
|
open={maximized}
|
|
85
85
|
onClose={() => setMaximized(false)}
|
|
86
|
+
ariaLabel="Resource diff"
|
|
86
87
|
className="dialog flex h-[90vh] w-full max-w-6xl flex-col"
|
|
87
88
|
>
|
|
88
89
|
<ArgoResourceDiffContent diff={diff} onClose={() => setMaximized(false)} />
|
|
@@ -739,6 +739,7 @@ interface GitOpsChangesViewProps {
|
|
|
739
739
|
// Opens the global Settings dialog. Backs the "Connect Argo CD" hint shown
|
|
740
740
|
// when the root is an Argo Application without the diff integration.
|
|
741
741
|
onOpenSettings?: () => void
|
|
742
|
+
settingsNote?: string
|
|
742
743
|
}
|
|
743
744
|
|
|
744
745
|
// Status facets for the Resources list. OutOfSync is a sync-status concern
|
|
@@ -749,7 +750,7 @@ const STATUS_FACETS: { key: ResourceStatusFacet; label: string; tone: FilterPill
|
|
|
749
750
|
{ key: 'missing', label: 'Missing', tone: 'danger' },
|
|
750
751
|
]
|
|
751
752
|
|
|
752
|
-
export function GitOpsChangesView({ insight, error, onOpenResource, onSyncResource, syncResourceDisabledReason, focusKey, tree, renderResourceDiff, onOpenSettings }: GitOpsChangesViewProps) {
|
|
753
|
+
export function GitOpsChangesView({ insight, error, onOpenResource, onSyncResource, syncResourceDisabledReason, focusKey, tree, renderResourceDiff, onOpenSettings, settingsNote }: GitOpsChangesViewProps) {
|
|
753
754
|
// "All resources" toggle: when on, render generated descendants alongside
|
|
754
755
|
// the controller's declared inventory. Argo's UI defaults to "all" — we
|
|
755
756
|
// default to "declared" because the diagnostic data (drift, events) lives
|
|
@@ -1009,12 +1010,14 @@ export function GitOpsChangesView({ insight, error, onOpenResource, onSyncResour
|
|
|
1009
1010
|
{insight.capabilities?.argoConfigured ? (
|
|
1010
1011
|
<>
|
|
1011
1012
|
Argo CD's full Git-rendered diff isn't available for this app (connection down or token not authorized).{' '}
|
|
1013
|
+
{settingsNote && <>{settingsNote} </>}
|
|
1012
1014
|
<button type="button" onClick={onOpenSettings} className="font-medium text-accent-text hover:underline">
|
|
1013
1015
|
Check Argo CD in Settings
|
|
1014
1016
|
</button>.
|
|
1015
1017
|
</>
|
|
1016
1018
|
) : (
|
|
1017
1019
|
<>
|
|
1020
|
+
{settingsNote && <>{settingsNote} </>}
|
|
1018
1021
|
<button type="button" onClick={onOpenSettings} className="font-medium text-accent-text hover:underline">
|
|
1019
1022
|
Connect Argo CD
|
|
1020
1023
|
</button>{' '}
|
|
@@ -283,7 +283,7 @@ export function IssueRow({
|
|
|
283
283
|
onToggle();
|
|
284
284
|
}
|
|
285
285
|
}}
|
|
286
|
-
className={`group @container/issue flex cursor-pointer items-center gap-3 border-l-[3px] py-3 pl-3 pr-4 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-radar-accent)]/40 ${open ? ISSUE_SEVERITY_HEADER_BAND_CLASS[severity] : ISSUE_SEVERITY_RAIL_CLASS[severity]}`}
|
|
286
|
+
className={`group @container/issue flex cursor-pointer items-center gap-3 border-l-[3px] py-3 pl-3 pr-4 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-radar-accent,var(--accent))]/40 ${open ? ISSUE_SEVERITY_HEADER_BAND_CLASS[severity] : ISSUE_SEVERITY_RAIL_CLASS[severity]}`}
|
|
287
287
|
>
|
|
288
288
|
<SeverityIcon className={`h-[18px] w-[18px] shrink-0 ${ISSUE_SEVERITY_TEXT_CLASS[severity]}`} aria-hidden />
|
|
289
289
|
|
|
@@ -719,7 +719,7 @@ function ResourceLine({
|
|
|
719
719
|
) : (
|
|
720
720
|
<span className="shrink-0 font-mono text-[11px] uppercase tracking-wide text-theme-text-tertiary">{r.kind}</span>
|
|
721
721
|
)}
|
|
722
|
-
<span className={`min-w-0 truncate text-sm ${linkable ? `${compact ? 'font-semibold' : 'font-medium'} text-[var(--color-radar-accent)]` : 'font-medium text-theme-text-primary'}`}>
|
|
722
|
+
<span className={`min-w-0 truncate text-sm ${linkable ? `${compact ? 'font-semibold' : 'font-medium'} text-[var(--color-radar-accent,var(--accent))]` : 'font-medium text-theme-text-primary'}`}>
|
|
723
723
|
{r.namespace ? `${r.namespace} / ` : ''}
|
|
724
724
|
{r.name}
|
|
725
725
|
</span>
|