@checkstack/healthcheck-frontend 0.14.2 → 0.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/CHANGELOG.md +47 -0
- package/package.json +1 -1
- package/src/components/HealthCheckDrawer.tsx +546 -0
- package/src/components/HealthCheckList.tsx +102 -57
- package/src/components/HealthCheckSystemOverview.tsx +78 -458
- package/src/components/SystemHealthBadge.tsx +2 -2
- package/src/components/assignments/AssignmentTree.tsx +3 -1
- package/src/components/assignments/ExecutionPanel.tsx +4 -2
- package/src/components/assignments/GeneralPanel.tsx +5 -2
- package/src/components/assignments/RetentionPanel.tsx +10 -2
- package/src/components/assignments/ThresholdsPanel.tsx +13 -1
- package/src/pages/AssignmentIDEPage.tsx +16 -0
|
@@ -13,6 +13,7 @@ interface GeneralPanelProps {
|
|
|
13
13
|
onToggleEnabled: () => void;
|
|
14
14
|
onUnassign: () => void;
|
|
15
15
|
saving: boolean;
|
|
16
|
+
isLocked?: boolean;
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
/**
|
|
@@ -26,6 +27,7 @@ export const GeneralPanel: React.FC<GeneralPanelProps> = ({
|
|
|
26
27
|
onToggleEnabled,
|
|
27
28
|
onUnassign,
|
|
28
29
|
saving,
|
|
30
|
+
isLocked,
|
|
29
31
|
}) => {
|
|
30
32
|
const editUrl = resolveRoute(healthcheckRoutes.routes.edit, {
|
|
31
33
|
configId: configurationId,
|
|
@@ -46,7 +48,7 @@ export const GeneralPanel: React.FC<GeneralPanelProps> = ({
|
|
|
46
48
|
<Checkbox
|
|
47
49
|
checked={enabled}
|
|
48
50
|
onCheckedChange={onToggleEnabled}
|
|
49
|
-
disabled={saving}
|
|
51
|
+
disabled={saving || isLocked}
|
|
50
52
|
/>
|
|
51
53
|
<div>
|
|
52
54
|
<Label className="text-sm font-medium">Enabled</Label>
|
|
@@ -85,7 +87,8 @@ export const GeneralPanel: React.FC<GeneralPanelProps> = ({
|
|
|
85
87
|
variant="ghost"
|
|
86
88
|
size="sm"
|
|
87
89
|
onClick={onUnassign}
|
|
88
|
-
disabled={saving}
|
|
90
|
+
disabled={saving || isLocked}
|
|
91
|
+
title={isLocked ? "Managed by GitOps" : undefined}
|
|
89
92
|
className="text-destructive hover:text-destructive hover:bg-destructive/10"
|
|
90
93
|
>
|
|
91
94
|
<Trash2 className="h-3.5 w-3.5 mr-1.5" />
|
|
@@ -14,6 +14,7 @@ interface RetentionPanelProps {
|
|
|
14
14
|
onSave: () => void;
|
|
15
15
|
onReset: () => void;
|
|
16
16
|
saving: boolean;
|
|
17
|
+
isLocked?: boolean;
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
/**
|
|
@@ -25,6 +26,7 @@ export const RetentionPanel: React.FC<RetentionPanelProps> = ({
|
|
|
25
26
|
onSave,
|
|
26
27
|
onReset,
|
|
27
28
|
saving,
|
|
29
|
+
isLocked,
|
|
28
30
|
}) => {
|
|
29
31
|
if (!data) {
|
|
30
32
|
return (
|
|
@@ -68,6 +70,7 @@ export const RetentionPanel: React.FC<RetentionPanelProps> = ({
|
|
|
68
70
|
min={1}
|
|
69
71
|
max={30}
|
|
70
72
|
onChange={(v) => onFieldChange("rawRetentionDays", v)}
|
|
73
|
+
disabled={saving || isLocked}
|
|
71
74
|
/>
|
|
72
75
|
|
|
73
76
|
{/* Hourly Aggregates */}
|
|
@@ -78,6 +81,7 @@ export const RetentionPanel: React.FC<RetentionPanelProps> = ({
|
|
|
78
81
|
min={7}
|
|
79
82
|
max={365}
|
|
80
83
|
onChange={(v) => onFieldChange("hourlyRetentionDays", v)}
|
|
84
|
+
disabled={saving || isLocked}
|
|
81
85
|
/>
|
|
82
86
|
|
|
83
87
|
{/* Daily Aggregates */}
|
|
@@ -88,6 +92,7 @@ export const RetentionPanel: React.FC<RetentionPanelProps> = ({
|
|
|
88
92
|
min={30}
|
|
89
93
|
max={1095}
|
|
90
94
|
onChange={(v) => onFieldChange("dailyRetentionDays", v)}
|
|
95
|
+
disabled={saving || isLocked}
|
|
91
96
|
/>
|
|
92
97
|
|
|
93
98
|
{/* Actions */}
|
|
@@ -96,14 +101,14 @@ export const RetentionPanel: React.FC<RetentionPanelProps> = ({
|
|
|
96
101
|
variant="ghost"
|
|
97
102
|
size="sm"
|
|
98
103
|
onClick={onReset}
|
|
99
|
-
disabled={saving || !data.isCustom}
|
|
104
|
+
disabled={saving || isLocked || !data.isCustom}
|
|
100
105
|
>
|
|
101
106
|
Reset to Defaults
|
|
102
107
|
</Button>
|
|
103
108
|
<Button
|
|
104
109
|
size="sm"
|
|
105
110
|
onClick={onSave}
|
|
106
|
-
disabled={saving || !isValidHierarchy}
|
|
111
|
+
disabled={saving || isLocked || !isValidHierarchy}
|
|
107
112
|
>
|
|
108
113
|
{saving ? "Saving..." : "Save Retention"}
|
|
109
114
|
</Button>
|
|
@@ -123,6 +128,7 @@ function RetentionTier({
|
|
|
123
128
|
min,
|
|
124
129
|
max,
|
|
125
130
|
onChange,
|
|
131
|
+
disabled,
|
|
126
132
|
}: {
|
|
127
133
|
label: string;
|
|
128
134
|
description: string;
|
|
@@ -130,6 +136,7 @@ function RetentionTier({
|
|
|
130
136
|
min: number;
|
|
131
137
|
max: number;
|
|
132
138
|
onChange: (value: number) => void;
|
|
139
|
+
disabled?: boolean;
|
|
133
140
|
}) {
|
|
134
141
|
return (
|
|
135
142
|
<div className="p-3 rounded-lg border bg-muted/30">
|
|
@@ -145,6 +152,7 @@ function RetentionTier({
|
|
|
145
152
|
max={max}
|
|
146
153
|
value={value}
|
|
147
154
|
onChange={(e) => onChange(Number(e.target.value))}
|
|
155
|
+
disabled={disabled}
|
|
148
156
|
className="h-8 w-20 text-center"
|
|
149
157
|
/>
|
|
150
158
|
<span className="text-sm text-muted-foreground w-10">days</span>
|
|
@@ -18,6 +18,7 @@ interface ThresholdsPanelProps {
|
|
|
18
18
|
onChange: (thresholds: StateThresholds) => void;
|
|
19
19
|
onSave: () => void;
|
|
20
20
|
saving: boolean;
|
|
21
|
+
isLocked?: boolean;
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
/**
|
|
@@ -29,6 +30,7 @@ export const ThresholdsPanel: React.FC<ThresholdsPanelProps> = ({
|
|
|
29
30
|
onChange,
|
|
30
31
|
onSave,
|
|
31
32
|
saving,
|
|
33
|
+
isLocked,
|
|
32
34
|
}) => {
|
|
33
35
|
return (
|
|
34
36
|
<div className="p-6 space-y-4">
|
|
@@ -64,6 +66,7 @@ export const ThresholdsPanel: React.FC<ThresholdsPanelProps> = ({
|
|
|
64
66
|
});
|
|
65
67
|
}
|
|
66
68
|
}}
|
|
69
|
+
disabled={saving || isLocked}
|
|
67
70
|
>
|
|
68
71
|
<SelectTrigger className="w-full">
|
|
69
72
|
<SelectValue />
|
|
@@ -96,6 +99,7 @@ export const ThresholdsPanel: React.FC<ThresholdsPanelProps> = ({
|
|
|
96
99
|
onChange={(v) =>
|
|
97
100
|
onChange({ ...thresholds, healthy: { minSuccessCount: v } })
|
|
98
101
|
}
|
|
102
|
+
disabled={saving || isLocked}
|
|
99
103
|
/>
|
|
100
104
|
<ThresholdCard
|
|
101
105
|
status="warning"
|
|
@@ -106,6 +110,7 @@ export const ThresholdsPanel: React.FC<ThresholdsPanelProps> = ({
|
|
|
106
110
|
onChange={(v) =>
|
|
107
111
|
onChange({ ...thresholds, degraded: { minFailureCount: v } })
|
|
108
112
|
}
|
|
113
|
+
disabled={saving || isLocked}
|
|
109
114
|
/>
|
|
110
115
|
<ThresholdCard
|
|
111
116
|
status="destructive"
|
|
@@ -116,6 +121,7 @@ export const ThresholdsPanel: React.FC<ThresholdsPanelProps> = ({
|
|
|
116
121
|
onChange={(v) =>
|
|
117
122
|
onChange({ ...thresholds, unhealthy: { minFailureCount: v } })
|
|
118
123
|
}
|
|
124
|
+
disabled={saving || isLocked}
|
|
119
125
|
/>
|
|
120
126
|
</div>
|
|
121
127
|
) : (
|
|
@@ -139,6 +145,7 @@ export const ThresholdsPanel: React.FC<ThresholdsPanelProps> = ({
|
|
|
139
145
|
windowSize: Number.parseInt(e.target.value) || 10,
|
|
140
146
|
})
|
|
141
147
|
}
|
|
148
|
+
disabled={saving || isLocked}
|
|
142
149
|
className="h-8 w-16 text-center"
|
|
143
150
|
/>
|
|
144
151
|
<span className="text-xs text-muted-foreground">runs</span>
|
|
@@ -156,6 +163,7 @@ export const ThresholdsPanel: React.FC<ThresholdsPanelProps> = ({
|
|
|
156
163
|
onChange={(v) =>
|
|
157
164
|
onChange({ ...thresholds, degraded: { minFailureCount: v } })
|
|
158
165
|
}
|
|
166
|
+
disabled={saving || isLocked}
|
|
159
167
|
/>
|
|
160
168
|
<ThresholdCard
|
|
161
169
|
status="destructive"
|
|
@@ -167,13 +175,14 @@ export const ThresholdsPanel: React.FC<ThresholdsPanelProps> = ({
|
|
|
167
175
|
onChange={(v) =>
|
|
168
176
|
onChange({ ...thresholds, unhealthy: { minFailureCount: v } })
|
|
169
177
|
}
|
|
178
|
+
disabled={saving || isLocked}
|
|
170
179
|
/>
|
|
171
180
|
</div>
|
|
172
181
|
)}
|
|
173
182
|
|
|
174
183
|
{/* Save Button */}
|
|
175
184
|
<div className="flex justify-end pt-2 border-t">
|
|
176
|
-
<Button size="sm" onClick={onSave} disabled={saving}>
|
|
185
|
+
<Button size="sm" onClick={onSave} disabled={saving || isLocked}>
|
|
177
186
|
{saving ? "Saving..." : "Save Thresholds"}
|
|
178
187
|
</Button>
|
|
179
188
|
</div>
|
|
@@ -214,6 +223,7 @@ function ThresholdCard({
|
|
|
214
223
|
prefix,
|
|
215
224
|
suffix,
|
|
216
225
|
onChange,
|
|
226
|
+
disabled,
|
|
217
227
|
}: {
|
|
218
228
|
status: keyof typeof STATUS_STYLES;
|
|
219
229
|
label: string;
|
|
@@ -222,6 +232,7 @@ function ThresholdCard({
|
|
|
222
232
|
prefix?: string;
|
|
223
233
|
suffix: string;
|
|
224
234
|
onChange: (value: number) => void;
|
|
235
|
+
disabled?: boolean;
|
|
225
236
|
}) {
|
|
226
237
|
const styles = STATUS_STYLES[status];
|
|
227
238
|
return (
|
|
@@ -241,6 +252,7 @@ function ThresholdCard({
|
|
|
241
252
|
min={1}
|
|
242
253
|
value={value}
|
|
243
254
|
onChange={(e) => onChange(Number.parseInt(e.target.value) || 1)}
|
|
255
|
+
disabled={disabled}
|
|
244
256
|
className="h-8 w-16 text-center"
|
|
245
257
|
/>
|
|
246
258
|
<span className="text-xs text-muted-foreground w-20">{suffix}</span>
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
} from "../components/assignments/AssignmentTree";
|
|
19
19
|
import { GeneralPanel } from "../components/assignments/GeneralPanel";
|
|
20
20
|
import { ThresholdsPanel } from "../components/assignments/ThresholdsPanel";
|
|
21
|
+
import { useProvenanceLock, GitOpsLockBanner } from "@checkstack/gitops-frontend";
|
|
21
22
|
import {
|
|
22
23
|
RetentionPanel,
|
|
23
24
|
type RetentionData,
|
|
@@ -61,6 +62,11 @@ const AssignmentIDEPageContent = () => {
|
|
|
61
62
|
{ enabled: !!systemId },
|
|
62
63
|
);
|
|
63
64
|
|
|
65
|
+
const { isLocked, provenance } = useProvenanceLock({
|
|
66
|
+
kind: "System",
|
|
67
|
+
entityId: systemId,
|
|
68
|
+
});
|
|
69
|
+
|
|
64
70
|
const { data: satellitesData } = satelliteClient.listSatellites.useQuery({});
|
|
65
71
|
|
|
66
72
|
// --- UI State ---
|
|
@@ -390,6 +396,7 @@ const AssignmentIDEPageContent = () => {
|
|
|
390
396
|
onToggleEnabled={() => handleToggleEnabled(configId, assoc.enabled)}
|
|
391
397
|
onUnassign={() => handleToggleAssignment(configId, true)}
|
|
392
398
|
saving={saving}
|
|
399
|
+
isLocked={isLocked}
|
|
393
400
|
/>
|
|
394
401
|
);
|
|
395
402
|
}
|
|
@@ -404,6 +411,7 @@ const AssignmentIDEPageContent = () => {
|
|
|
404
411
|
onChange={(t) => handleThresholdChange(configId, t)}
|
|
405
412
|
onSave={() => handleSaveThresholds(configId)}
|
|
406
413
|
saving={saving}
|
|
414
|
+
isLocked={isLocked}
|
|
407
415
|
/>
|
|
408
416
|
);
|
|
409
417
|
}
|
|
@@ -417,6 +425,7 @@ const AssignmentIDEPageContent = () => {
|
|
|
417
425
|
onSave={() => handleSaveRetention(configId)}
|
|
418
426
|
onReset={() => handleResetRetention(configId)}
|
|
419
427
|
saving={saving}
|
|
428
|
+
isLocked={isLocked}
|
|
420
429
|
/>
|
|
421
430
|
);
|
|
422
431
|
}
|
|
@@ -431,6 +440,7 @@ const AssignmentIDEPageContent = () => {
|
|
|
431
440
|
handleToggleSatellite(configId, satId)
|
|
432
441
|
}
|
|
433
442
|
saving={saving}
|
|
443
|
+
isLocked={isLocked}
|
|
434
444
|
/>
|
|
435
445
|
);
|
|
436
446
|
}
|
|
@@ -458,6 +468,11 @@ const AssignmentIDEPageContent = () => {
|
|
|
458
468
|
</BackLink>
|
|
459
469
|
}
|
|
460
470
|
>
|
|
471
|
+
{isLocked && provenance && (
|
|
472
|
+
<div className="mb-4">
|
|
473
|
+
<GitOpsLockBanner provenance={provenance} />
|
|
474
|
+
</div>
|
|
475
|
+
)}
|
|
461
476
|
<IDELayout
|
|
462
477
|
tree={
|
|
463
478
|
<AssignmentTree
|
|
@@ -466,6 +481,7 @@ const AssignmentIDEPageContent = () => {
|
|
|
466
481
|
selectedNode={selectedNode}
|
|
467
482
|
onSelectNode={setSelectedNode}
|
|
468
483
|
onToggleAssignment={handleToggleAssignment}
|
|
484
|
+
isLocked={isLocked}
|
|
469
485
|
/>
|
|
470
486
|
}
|
|
471
487
|
panel={renderPanel()}
|