@replicated/portal-components 0.0.16 → 0.0.18
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/components/metadata/registry.json +2 -2
- package/components/metadata/registry.md +2 -2
- package/dist/actions/index.d.mts +1 -637
- package/dist/actions/index.d.ts +1 -637
- package/dist/actions/index.js +2 -1
- package/dist/actions/index.js.map +1 -1
- package/dist/airgap-instances.d.mts +5 -3
- package/dist/airgap-instances.d.ts +5 -3
- package/dist/airgap-instances.js +94 -5
- package/dist/airgap-instances.js.map +1 -1
- package/dist/esm/actions/index.js +2 -1
- package/dist/esm/actions/index.js.map +1 -1
- package/dist/esm/airgap-instances.js +90 -5
- package/dist/esm/airgap-instances.js.map +1 -1
- package/dist/esm/helm-install-wizard.js +28 -15
- package/dist/esm/helm-install-wizard.js.map +1 -1
- package/dist/esm/index.js +3 -2
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/install-actions.js +54 -11
- package/dist/esm/install-actions.js.map +1 -1
- package/dist/esm/instance-card.js +79 -3
- package/dist/esm/instance-card.js.map +1 -1
- package/dist/esm/license-details.js +2 -1
- package/dist/esm/license-details.js.map +1 -1
- package/dist/esm/linux-install-wizard.js +90 -14
- package/dist/esm/linux-install-wizard.js.map +1 -1
- package/dist/esm/online-instance-list.js +90 -5
- package/dist/esm/online-instance-list.js.map +1 -1
- package/dist/esm/pending-installations.js +187 -103
- package/dist/esm/pending-installations.js.map +1 -1
- package/dist/esm/security-card.js +76 -16
- package/dist/esm/security-card.js.map +1 -1
- package/dist/esm/support-card.js +2 -1
- package/dist/esm/support-card.js.map +1 -1
- package/dist/esm/top-nav-user-menu.js +4 -2
- package/dist/esm/top-nav-user-menu.js.map +1 -1
- package/dist/esm/top-nav.js +2 -1
- package/dist/esm/top-nav.js.map +1 -1
- package/dist/esm/update-layout.js +2 -1
- package/dist/esm/update-layout.js.map +1 -1
- package/dist/esm/upload-support-bundle-modal.js +19 -19
- package/dist/esm/upload-support-bundle-modal.js.map +1 -1
- package/dist/esm/utils/index.js +2 -1
- package/dist/esm/utils/index.js.map +1 -1
- package/dist/helm-install-wizard.d.mts +5 -4
- package/dist/helm-install-wizard.d.ts +5 -4
- package/dist/helm-install-wizard.js +28 -15
- package/dist/helm-install-wizard.js.map +1 -1
- package/dist/index-DkjaogsF.d.mts +891 -0
- package/dist/index-DkjaogsF.d.ts +891 -0
- package/dist/index.d.mts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/install-actions.d.mts +2 -3
- package/dist/install-actions.d.ts +2 -3
- package/dist/install-actions.js +54 -10
- package/dist/install-actions.js.map +1 -1
- package/dist/install-card.d.mts +1 -1
- package/dist/install-card.d.ts +1 -1
- package/dist/instance-card.d.mts +5 -3
- package/dist/instance-card.d.ts +5 -3
- package/dist/instance-card.js +83 -3
- package/dist/instance-card.js.map +1 -1
- package/dist/license-card.d.mts +1 -1
- package/dist/license-card.d.ts +1 -1
- package/dist/license-details.js +2 -1
- package/dist/license-details.js.map +1 -1
- package/dist/linux-install-wizard.d.mts +6 -6
- package/dist/linux-install-wizard.d.ts +6 -6
- package/dist/linux-install-wizard.js +90 -14
- package/dist/linux-install-wizard.js.map +1 -1
- package/dist/online-instance-list.d.mts +5 -3
- package/dist/online-instance-list.d.ts +5 -3
- package/dist/online-instance-list.js +94 -5
- package/dist/online-instance-list.js.map +1 -1
- package/dist/pending-installations.d.mts +3 -3
- package/dist/pending-installations.d.ts +3 -3
- package/dist/pending-installations.js +186 -102
- package/dist/pending-installations.js.map +1 -1
- package/dist/security-card.d.mts +3 -2
- package/dist/security-card.d.ts +3 -2
- package/dist/security-card.js +76 -16
- package/dist/security-card.js.map +1 -1
- package/dist/styles.css +43 -0
- package/dist/support-bundles-card.d.mts +1 -1
- package/dist/support-bundles-card.d.ts +1 -1
- package/dist/support-card.js +2 -1
- package/dist/support-card.js.map +1 -1
- package/dist/top-nav-user-menu.d.mts +3 -1
- package/dist/top-nav-user-menu.d.ts +3 -1
- package/dist/top-nav-user-menu.js +4 -2
- package/dist/top-nav-user-menu.js.map +1 -1
- package/dist/top-nav.js +2 -1
- package/dist/top-nav.js.map +1 -1
- package/dist/update-layout.js +2 -1
- package/dist/update-layout.js.map +1 -1
- package/dist/upload-support-bundle-modal.d.mts +7 -3
- package/dist/upload-support-bundle-modal.d.ts +7 -3
- package/dist/upload-support-bundle-modal.js +19 -19
- package/dist/upload-support-bundle-modal.js.map +1 -1
- package/dist/utils/index.js +2 -1
- package/dist/utils/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/install-B19AaKF_.d.mts +0 -233
- package/dist/install-Bi1qJ8Bu.d.ts +0 -233
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useState, useEffect } from 'react';
|
|
3
|
+
import { useSearchParams, useRouter } from 'next/navigation';
|
|
3
4
|
import Link from 'next/link';
|
|
4
|
-
import {
|
|
5
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Enterprise Portal Components
|
|
@@ -96,11 +97,18 @@ var formatTimeAgo = (dateString) => {
|
|
|
96
97
|
};
|
|
97
98
|
var PendingInstallations = ({
|
|
98
99
|
fetchPendingInstallationsAction,
|
|
100
|
+
discardInstallationAction,
|
|
99
101
|
initialInstallations = [],
|
|
100
102
|
pollIntervalMs = 5e3
|
|
101
103
|
}) => {
|
|
102
104
|
const [installations, setInstallations] = useState(initialInstallations);
|
|
103
105
|
const [showAll, setShowAll] = useState(false);
|
|
106
|
+
const [showConfirmDialog, setShowConfirmDialog] = useState(false);
|
|
107
|
+
const [installationToDiscard, setInstallationToDiscard] = useState(null);
|
|
108
|
+
const [isDiscarding, setIsDiscarding] = useState(false);
|
|
109
|
+
const [discardError, setDiscardError] = useState(null);
|
|
110
|
+
const searchParams = useSearchParams();
|
|
111
|
+
const router = useRouter();
|
|
104
112
|
useEffect(() => {
|
|
105
113
|
const fetchInstallations = async () => {
|
|
106
114
|
try {
|
|
@@ -117,117 +125,193 @@ var PendingInstallations = ({
|
|
|
117
125
|
const intervalId = setInterval(fetchInstallations, pollIntervalMs);
|
|
118
126
|
return () => clearInterval(intervalId);
|
|
119
127
|
}, [fetchPendingInstallationsAction, pollIntervalMs]);
|
|
128
|
+
const installOptionsId = searchParams.get("installOptionsId");
|
|
129
|
+
if (installOptionsId) {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
120
132
|
if (installations.length === 0) {
|
|
121
133
|
return null;
|
|
122
134
|
}
|
|
123
135
|
const firstInstallation = installations[0];
|
|
124
136
|
const remainingCount = installations.length - 1;
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
137
|
+
const handleDeleteClick = (installation) => {
|
|
138
|
+
setInstallationToDiscard(installation);
|
|
139
|
+
setShowConfirmDialog(true);
|
|
140
|
+
setDiscardError(null);
|
|
141
|
+
};
|
|
142
|
+
const handleCancelDiscard = () => {
|
|
143
|
+
setShowConfirmDialog(false);
|
|
144
|
+
setInstallationToDiscard(null);
|
|
145
|
+
setDiscardError(null);
|
|
146
|
+
};
|
|
147
|
+
const handleConfirmDiscard = async () => {
|
|
148
|
+
if (!installationToDiscard || !discardInstallationAction) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
setIsDiscarding(true);
|
|
152
|
+
setDiscardError(null);
|
|
153
|
+
try {
|
|
154
|
+
await discardInstallationAction(installationToDiscard.id);
|
|
155
|
+
const result = await fetchPendingInstallationsAction();
|
|
156
|
+
setInstallations(result.installations);
|
|
157
|
+
const currentInstallOptionsId = searchParams.get("installOptionsId");
|
|
158
|
+
if (currentInstallOptionsId === installationToDiscard.id) {
|
|
159
|
+
router.push("/install");
|
|
160
|
+
}
|
|
161
|
+
setShowConfirmDialog(false);
|
|
162
|
+
setInstallationToDiscard(null);
|
|
163
|
+
} catch (error) {
|
|
164
|
+
console.error("[pending-installations] Failed to discard installation", error);
|
|
165
|
+
setDiscardError(error instanceof Error ? error.message : "Failed to discard installation");
|
|
166
|
+
} finally {
|
|
167
|
+
setIsDiscarding(false);
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
return /* @__PURE__ */ jsxs("div", { className: "mb-6 rounded-xl border border-orange-200 bg-orange-50 p-6", children: [
|
|
171
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start gap-4", children: [
|
|
172
|
+
/* @__PURE__ */ jsx("div", { className: "flex-shrink-0 text-orange-600", children: /* @__PURE__ */ jsx(ClockIcon, {}) }),
|
|
173
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 space-y-3", children: [
|
|
174
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-4", children: [
|
|
175
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
|
|
176
|
+
/* @__PURE__ */ jsxs("h3", { className: "text-base font-semibold text-gray-900", children: [
|
|
177
|
+
"Pending installation: ",
|
|
178
|
+
firstInstallation.name,
|
|
179
|
+
" \u2022",
|
|
180
|
+
" ",
|
|
181
|
+
firstInstallation.method === "helm" ? "Helm" : "Linux",
|
|
182
|
+
" installation"
|
|
183
|
+
] }),
|
|
184
|
+
/* @__PURE__ */ jsxs("p", { className: "mt-1 text-sm text-gray-600", children: [
|
|
185
|
+
"Started ",
|
|
186
|
+
formatTimeAgo(firstInstallation.startedAt),
|
|
187
|
+
" by ",
|
|
188
|
+
firstInstallation.startedBy
|
|
189
|
+
] }),
|
|
190
|
+
remainingCount > 0 && /* @__PURE__ */ jsxs(
|
|
191
|
+
"button",
|
|
192
|
+
{
|
|
193
|
+
type: "button",
|
|
194
|
+
onClick: () => setShowAll(!showAll),
|
|
195
|
+
className: "mt-2 flex items-center gap-1 text-sm font-medium text-gray-700 hover:text-gray-900",
|
|
196
|
+
children: [
|
|
197
|
+
"View ",
|
|
198
|
+
remainingCount,
|
|
199
|
+
" more pending installation",
|
|
200
|
+
remainingCount !== 1 ? "s" : "",
|
|
201
|
+
/* @__PURE__ */ jsx(ChevronRightIcon, {})
|
|
202
|
+
]
|
|
203
|
+
}
|
|
204
|
+
)
|
|
143
205
|
] }),
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
"
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
206
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
207
|
+
discardInstallationAction && /* @__PURE__ */ jsx(
|
|
208
|
+
"button",
|
|
209
|
+
{
|
|
210
|
+
type: "button",
|
|
211
|
+
onClick: () => handleDeleteClick(firstInstallation),
|
|
212
|
+
className: "rounded-lg p-2 text-red-600 transition hover:bg-red-100",
|
|
213
|
+
"aria-label": "Delete installation",
|
|
214
|
+
children: /* @__PURE__ */ jsx(TrashIcon, {})
|
|
215
|
+
}
|
|
216
|
+
),
|
|
217
|
+
/* @__PURE__ */ jsxs(
|
|
218
|
+
Link,
|
|
219
|
+
{
|
|
220
|
+
href: `/install?installOptionsId=${firstInstallation.id}&type=${firstInstallation.method}&step=2`,
|
|
221
|
+
className: "inline-flex items-center gap-2 rounded-lg bg-orange-200 px-4 py-2 text-sm font-semibold text-gray-900 transition hover:bg-orange-300",
|
|
222
|
+
children: [
|
|
223
|
+
"Continue installation",
|
|
224
|
+
/* @__PURE__ */ jsx(ArrowRightIcon, {})
|
|
225
|
+
]
|
|
226
|
+
}
|
|
227
|
+
)
|
|
228
|
+
] })
|
|
159
229
|
] }),
|
|
160
|
-
/* @__PURE__ */
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
}
|
|
180
|
-
)
|
|
181
|
-
] })
|
|
182
|
-
] }),
|
|
183
|
-
showAll && remainingCount > 0 && /* @__PURE__ */ jsx("div", { className: "mt-4 space-y-2 border-t border-orange-200 pt-4", children: installations.slice(1).map((installation) => /* @__PURE__ */ jsxs(
|
|
184
|
-
"div",
|
|
185
|
-
{
|
|
186
|
-
className: "flex items-center justify-between rounded-lg bg-white p-3",
|
|
187
|
-
children: [
|
|
188
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
189
|
-
/* @__PURE__ */ jsxs("h4", { className: "text-sm font-medium text-gray-900", children: [
|
|
190
|
-
installation.name,
|
|
191
|
-
" \u2022",
|
|
192
|
-
" ",
|
|
193
|
-
installation.method === "helm" ? "Helm" : "Linux",
|
|
194
|
-
" installation"
|
|
230
|
+
showAll && remainingCount > 0 && /* @__PURE__ */ jsx("div", { className: "mt-4 space-y-2 border-t border-orange-200 pt-4", children: installations.slice(1).map((installation) => /* @__PURE__ */ jsxs(
|
|
231
|
+
"div",
|
|
232
|
+
{
|
|
233
|
+
className: "flex items-center justify-between rounded-lg bg-white p-3",
|
|
234
|
+
children: [
|
|
235
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
236
|
+
/* @__PURE__ */ jsxs("h4", { className: "text-sm font-medium text-gray-900", children: [
|
|
237
|
+
installation.name,
|
|
238
|
+
" \u2022",
|
|
239
|
+
" ",
|
|
240
|
+
installation.method === "helm" ? "Helm" : "Linux",
|
|
241
|
+
" installation"
|
|
242
|
+
] }),
|
|
243
|
+
/* @__PURE__ */ jsxs("p", { className: "mt-0.5 text-xs text-gray-600", children: [
|
|
244
|
+
"Started ",
|
|
245
|
+
formatTimeAgo(installation.startedAt),
|
|
246
|
+
" by ",
|
|
247
|
+
installation.startedBy
|
|
248
|
+
] })
|
|
195
249
|
] }),
|
|
196
|
-
/* @__PURE__ */ jsxs("
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
250
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
251
|
+
discardInstallationAction && /* @__PURE__ */ jsx(
|
|
252
|
+
"button",
|
|
253
|
+
{
|
|
254
|
+
type: "button",
|
|
255
|
+
onClick: () => handleDeleteClick(installation),
|
|
256
|
+
className: "rounded-lg p-2 text-red-600 transition hover:bg-red-100",
|
|
257
|
+
"aria-label": "Delete installation",
|
|
258
|
+
children: /* @__PURE__ */ jsx(TrashIcon, {})
|
|
259
|
+
}
|
|
260
|
+
),
|
|
261
|
+
/* @__PURE__ */ jsxs(
|
|
262
|
+
Link,
|
|
263
|
+
{
|
|
264
|
+
href: `/install?installOptionsId=${installation.id}&type=${installation.method}&step=2`,
|
|
265
|
+
className: "inline-flex items-center gap-2 rounded-lg bg-orange-200 px-3 py-1.5 text-xs font-semibold text-gray-900 transition hover:bg-orange-300",
|
|
266
|
+
children: [
|
|
267
|
+
"Continue",
|
|
268
|
+
/* @__PURE__ */ jsx(ArrowRightIcon, {})
|
|
269
|
+
]
|
|
270
|
+
}
|
|
271
|
+
)
|
|
201
272
|
] })
|
|
202
|
-
]
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
273
|
+
]
|
|
274
|
+
},
|
|
275
|
+
installation.id
|
|
276
|
+
)) })
|
|
277
|
+
] })
|
|
278
|
+
] }),
|
|
279
|
+
showConfirmDialog && installationToDiscard && /* @__PURE__ */ jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-50", children: /* @__PURE__ */ jsxs("div", { className: "w-full max-w-md rounded-lg bg-white p-6 shadow-xl", children: [
|
|
280
|
+
/* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold text-gray-900", children: "Discard install and service account token" }),
|
|
281
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-4", children: [
|
|
282
|
+
/* @__PURE__ */ jsxs("p", { className: "text-sm text-gray-600", children: [
|
|
283
|
+
"This will permanently delete the service account",
|
|
284
|
+
" ",
|
|
285
|
+
/* @__PURE__ */ jsx("code", { className: "rounded bg-gray-100 px-1 py-0.5 text-sm", children: installationToDiscard.name }),
|
|
286
|
+
" ",
|
|
287
|
+
"and remove the pending installation. This action cannot be undone."
|
|
288
|
+
] }),
|
|
289
|
+
discardError && /* @__PURE__ */ jsx("p", { className: "mt-3 text-sm text-red-600", children: "Failed to discard installation. Please try again." })
|
|
290
|
+
] }),
|
|
291
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-6 flex justify-end gap-3", children: [
|
|
292
|
+
/* @__PURE__ */ jsx(
|
|
293
|
+
"button",
|
|
294
|
+
{
|
|
295
|
+
type: "button",
|
|
296
|
+
onClick: handleCancelDiscard,
|
|
297
|
+
disabled: isDiscarding,
|
|
298
|
+
className: "rounded-lg border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 disabled:opacity-50",
|
|
299
|
+
children: "Cancel"
|
|
300
|
+
}
|
|
301
|
+
),
|
|
302
|
+
/* @__PURE__ */ jsx(
|
|
303
|
+
"button",
|
|
304
|
+
{
|
|
305
|
+
type: "button",
|
|
306
|
+
onClick: handleConfirmDiscard,
|
|
307
|
+
disabled: isDiscarding,
|
|
308
|
+
className: "rounded-lg bg-red-600 px-4 py-2 text-sm font-medium text-white hover:bg-red-700 disabled:opacity-50",
|
|
309
|
+
children: isDiscarding ? "Discarding..." : "Discard install and token"
|
|
310
|
+
}
|
|
311
|
+
)
|
|
312
|
+
] })
|
|
313
|
+
] }) })
|
|
314
|
+
] });
|
|
231
315
|
};
|
|
232
316
|
PendingInstallations.displayName = "PendingInstallations";
|
|
233
317
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/pending-installations.tsx"],"names":[],"mappings":"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","file":"pending-installations.js","sourcesContent":["\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport Link from \"next/link\";\nimport type { PendingInstallation, FetchPendingInstallationsResult } from \"../actions/install\";\n\nexport interface PendingInstallationsProps {\n fetchPendingInstallationsAction: () => Promise<FetchPendingInstallationsResult>;\n initialInstallations?: PendingInstallation[];\n pollIntervalMs?: number;\n}\n\nconst ClockIcon = () => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"h-6 w-6\"\n >\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <polyline points=\"12 6 12 12 16 14\" />\n </svg>\n);\n\nconst TrashIcon = () => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"h-5 w-5\"\n >\n <path d=\"M3 6h18\" />\n <path d=\"M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6\" />\n <path d=\"M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2\" />\n </svg>\n);\n\nconst ChevronRightIcon = () => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"h-4 w-4\"\n >\n <polyline points=\"9 18 15 12 9 6\" />\n </svg>\n);\n\nconst ArrowRightIcon = () => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"h-4 w-4\"\n >\n <line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\" />\n <polyline points=\"12 5 19 12 12 19\" />\n </svg>\n);\n\nconst formatTimeAgo = (dateString: string): string => {\n try {\n const date = new Date(dateString);\n const now = new Date();\n const seconds = Math.floor((now.getTime() - date.getTime()) / 1000);\n\n if (seconds < 60) return \"just now\";\n if (seconds < 3600) {\n const minutes = Math.floor(seconds / 60);\n return `${minutes} ${minutes === 1 ? \"minute\" : \"minutes\"} ago`;\n }\n if (seconds < 86400) {\n const hours = Math.floor(seconds / 3600);\n return `${hours === 1 ? \"an hour\" : `${hours} hours`} ago`;\n }\n const days = Math.floor(seconds / 86400);\n return `${days} ${days === 1 ? \"day\" : \"days\"} ago`;\n } catch {\n return dateString;\n }\n};\n\nexport const PendingInstallations = ({\n fetchPendingInstallationsAction,\n initialInstallations = [],\n pollIntervalMs = 5000\n}: PendingInstallationsProps) => {\n const [installations, setInstallations] = useState<PendingInstallation[]>(initialInstallations);\n const [showAll, setShowAll] = useState(false);\n\n useEffect(() => {\n const fetchInstallations = async () => {\n try {\n const result = await fetchPendingInstallationsAction();\n setInstallations(result.installations);\n } catch (error) {\n console.error(\"[pending-installations] Failed to fetch pending installations\", error);\n }\n };\n\n // Fetch immediately\n fetchInstallations();\n\n // Set up polling interval (skip if pollIntervalMs is 0 or falsy)\n if (!pollIntervalMs) {\n return;\n }\n const intervalId = setInterval(fetchInstallations, pollIntervalMs);\n\n // Cleanup on unmount\n return () => clearInterval(intervalId);\n }, [fetchPendingInstallationsAction, pollIntervalMs]);\n\n // Don't render if no pending installations\n if (installations.length === 0) {\n return null;\n }\n\n const firstInstallation = installations[0]!;\n const remainingCount = installations.length - 1;\n\n return (\n <div className=\"mb-6 rounded-xl border border-orange-200 bg-orange-50 p-6\">\n <div className=\"flex items-start gap-4\">\n <div className=\"flex-shrink-0 text-orange-600\">\n <ClockIcon />\n </div>\n <div className=\"flex-1 space-y-3\">\n <div className=\"flex items-start justify-between gap-4\">\n <div className=\"flex-1\">\n <h3 className=\"text-base font-semibold text-gray-900\">\n Pending installation: {firstInstallation.name} •{\" \"}\n {firstInstallation.method === \"helm\" ? \"Helm\" : \"Linux\"} installation\n </h3>\n <p className=\"mt-1 text-sm text-gray-600\">\n Started {formatTimeAgo(firstInstallation.startedAt)} by {firstInstallation.startedBy}\n </p>\n {remainingCount > 0 && (\n <button\n type=\"button\"\n onClick={() => setShowAll(!showAll)}\n className=\"mt-2 flex items-center gap-1 text-sm font-medium text-gray-700 hover:text-gray-900\"\n >\n View {remainingCount} more pending installation{remainingCount !== 1 ? \"s\" : \"\"}\n <ChevronRightIcon />\n </button>\n )}\n </div>\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n className=\"rounded-lg p-2 text-red-600 transition hover:bg-red-100\"\n aria-label=\"Delete installation\"\n >\n <TrashIcon />\n </button>\n <Link\n href={`/install?installOptionsId=${firstInstallation.id}&type=${firstInstallation.method}&step=2`}\n className=\"inline-flex items-center gap-2 rounded-lg bg-orange-200 px-4 py-2 text-sm font-semibold text-gray-900 transition hover:bg-orange-300\"\n >\n Continue installation\n <ArrowRightIcon />\n </Link>\n </div>\n </div>\n\n {showAll && remainingCount > 0 && (\n <div className=\"mt-4 space-y-2 border-t border-orange-200 pt-4\">\n {installations.slice(1).map((installation) => (\n <div\n key={installation.id}\n className=\"flex items-center justify-between rounded-lg bg-white p-3\"\n >\n <div>\n <h4 className=\"text-sm font-medium text-gray-900\">\n {installation.name} •{\" \"}\n {installation.method === \"helm\" ? \"Helm\" : \"Linux\"} installation\n </h4>\n <p className=\"mt-0.5 text-xs text-gray-600\">\n Started {formatTimeAgo(installation.startedAt)} by {installation.startedBy}\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n className=\"rounded-lg p-2 text-red-600 transition hover:bg-red-100\"\n aria-label=\"Delete installation\"\n >\n <TrashIcon />\n </button>\n <Link\n href={`/install?installOptionsId=${installation.id}&type=${installation.method}&step=2`}\n className=\"inline-flex items-center gap-2 rounded-lg bg-orange-200 px-3 py-1.5 text-xs font-semibold text-gray-900 transition hover:bg-orange-300\"\n >\n Continue\n <ArrowRightIcon />\n </Link>\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n </div>\n </div>\n );\n};\n\nPendingInstallations.displayName = \"PendingInstallations\";\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/pending-installations.tsx"],"names":[],"mappings":";;;;;;;;;;AAcA,IAAM,YAAY,sBAChB,IAAA;AAAA,EAAC,KAAA;AAAA,EAAA;AAAA,IACC,KAAA,EAAM,4BAAA;AAAA,IACN,OAAA,EAAQ,WAAA;AAAA,IACR,IAAA,EAAK,MAAA;AAAA,IACL,MAAA,EAAO,cAAA;AAAA,IACP,WAAA,EAAY,GAAA;AAAA,IACZ,aAAA,EAAc,OAAA;AAAA,IACd,cAAA,EAAe,OAAA;AAAA,IACf,SAAA,EAAU,SAAA;AAAA,IAEV,QAAA,EAAA;AAAA,sBAAA,GAAA,CAAC,YAAO,EAAA,EAAG,IAAA,EAAK,EAAA,EAAG,IAAA,EAAK,GAAE,IAAA,EAAK,CAAA;AAAA,sBAC/B,GAAA,CAAC,UAAA,EAAA,EAAS,MAAA,EAAO,kBAAA,EAAmB;AAAA;AAAA;AACtC,CAAA;AAGF,IAAM,YAAY,sBAChB,IAAA;AAAA,EAAC,KAAA;AAAA,EAAA;AAAA,IACC,KAAA,EAAM,4BAAA;AAAA,IACN,OAAA,EAAQ,WAAA;AAAA,IACR,IAAA,EAAK,MAAA;AAAA,IACL,MAAA,EAAO,cAAA;AAAA,IACP,WAAA,EAAY,GAAA;AAAA,IACZ,aAAA,EAAc,OAAA;AAAA,IACd,cAAA,EAAe,OAAA;AAAA,IACf,SAAA,EAAU,SAAA;AAAA,IAEV,QAAA,EAAA;AAAA,sBAAA,GAAA,CAAC,MAAA,EAAA,EAAK,GAAE,SAAA,EAAU,CAAA;AAAA,sBAClB,GAAA,CAAC,MAAA,EAAA,EAAK,CAAA,EAAE,uCAAA,EAAwC,CAAA;AAAA,sBAChD,GAAA,CAAC,MAAA,EAAA,EAAK,CAAA,EAAE,oCAAA,EAAqC;AAAA;AAAA;AAC/C,CAAA;AAGF,IAAM,mBAAmB,sBACvB,GAAA;AAAA,EAAC,KAAA;AAAA,EAAA;AAAA,IACC,KAAA,EAAM,4BAAA;AAAA,IACN,OAAA,EAAQ,WAAA;AAAA,IACR,IAAA,EAAK,MAAA;AAAA,IACL,MAAA,EAAO,cAAA;AAAA,IACP,WAAA,EAAY,GAAA;AAAA,IACZ,aAAA,EAAc,OAAA;AAAA,IACd,cAAA,EAAe,OAAA;AAAA,IACf,SAAA,EAAU,SAAA;AAAA,IAEV,QAAA,kBAAA,GAAA,CAAC,UAAA,EAAA,EAAS,MAAA,EAAO,gBAAA,EAAiB;AAAA;AACpC,CAAA;AAGF,IAAM,iBAAiB,sBACrB,IAAA;AAAA,EAAC,KAAA;AAAA,EAAA;AAAA,IACC,KAAA,EAAM,4BAAA;AAAA,IACN,OAAA,EAAQ,WAAA;AAAA,IACR,IAAA,EAAK,MAAA;AAAA,IACL,MAAA,EAAO,cAAA;AAAA,IACP,WAAA,EAAY,GAAA;AAAA,IACZ,aAAA,EAAc,OAAA;AAAA,IACd,cAAA,EAAe,OAAA;AAAA,IACf,SAAA,EAAU,SAAA;AAAA,IAEV,QAAA,EAAA;AAAA,sBAAA,GAAA,CAAC,MAAA,EAAA,EAAK,IAAG,GAAA,EAAI,EAAA,EAAG,MAAK,EAAA,EAAG,IAAA,EAAK,IAAG,IAAA,EAAK,CAAA;AAAA,sBACrC,GAAA,CAAC,UAAA,EAAA,EAAS,MAAA,EAAO,kBAAA,EAAmB;AAAA;AAAA;AACtC,CAAA;AAGF,IAAM,aAAA,GAAgB,CAAC,UAAA,KAA+B;AACpD,EAAA,IAAI;AACF,IAAA,MAAM,IAAA,GAAO,IAAI,IAAA,CAAK,UAAU,CAAA;AAChC,IAAA,MAAM,GAAA,uBAAU,IAAA,EAAK;AACrB,IAAA,MAAM,OAAA,GAAU,KAAK,KAAA,CAAA,CAAO,GAAA,CAAI,SAAQ,GAAI,IAAA,CAAK,OAAA,EAAQ,IAAK,GAAI,CAAA;AAElE,IAAA,IAAI,OAAA,GAAU,IAAI,OAAO,UAAA;AACzB,IAAA,IAAI,UAAU,IAAA,EAAM;AAClB,MAAA,MAAM,OAAA,GAAU,IAAA,CAAK,KAAA,CAAM,OAAA,GAAU,EAAE,CAAA;AACvC,MAAA,OAAO,GAAG,OAAO,CAAA,CAAA,EAAI,OAAA,KAAY,CAAA,GAAI,WAAW,SAAS,CAAA,IAAA,CAAA;AAAA,IAC3D;AACA,IAAA,IAAI,UAAU,KAAA,EAAO;AACnB,MAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,KAAA,CAAM,OAAA,GAAU,IAAI,CAAA;AACvC,MAAA,OAAO,GAAG,KAAA,KAAU,CAAA,GAAI,SAAA,GAAY,CAAA,EAAG,KAAK,CAAA,MAAA,CAAQ,CAAA,IAAA,CAAA;AAAA,IACtD;AACA,IAAA,MAAM,IAAA,GAAO,IAAA,CAAK,KAAA,CAAM,OAAA,GAAU,KAAK,CAAA;AACvC,IAAA,OAAO,GAAG,IAAI,CAAA,CAAA,EAAI,IAAA,KAAS,CAAA,GAAI,QAAQ,MAAM,CAAA,IAAA,CAAA;AAAA,EAC/C,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,UAAA;AAAA,EACT;AACF,CAAA;AAEO,IAAM,uBAAuB,CAAC;AAAA,EACnC,+BAAA;AAAA,EACA,yBAAA;AAAA,EACA,uBAAuB,EAAC;AAAA,EACxB,cAAA,GAAiB;AACnB,CAAA,KAAiC;AAC/B,EAAA,MAAM,CAAC,aAAA,EAAe,gBAAgB,CAAA,GAAI,SAAgC,oBAAoB,CAAA;AAC9F,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,SAAS,KAAK,CAAA;AAC5C,EAAA,MAAM,CAAC,iBAAA,EAAmB,oBAAoB,CAAA,GAAI,SAAS,KAAK,CAAA;AAChE,EAAA,MAAM,CAAC,qBAAA,EAAuB,wBAAwB,CAAA,GAAI,SAAqC,IAAI,CAAA;AACnG,EAAA,MAAM,CAAC,YAAA,EAAc,eAAe,CAAA,GAAI,SAAS,KAAK,CAAA;AACtD,EAAA,MAAM,CAAC,YAAA,EAAc,eAAe,CAAA,GAAI,SAAwB,IAAI,CAAA;AACpE,EAAA,MAAM,eAAe,eAAA,EAAgB;AACrC,EAAA,MAAM,SAAS,SAAA,EAAU;AAEzB,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,qBAAqB,YAAY;AACrC,MAAA,IAAI;AACF,QAAA,MAAM,MAAA,GAAS,MAAM,+BAAA,EAAgC;AACrD,QAAA,gBAAA,CAAiB,OAAO,aAAa,CAAA;AAAA,MACvC,SAAS,KAAA,EAAO;AACd,QAAA,OAAA,CAAQ,KAAA,CAAM,iEAAiE,KAAK,CAAA;AAAA,MACtF;AAAA,IACF,CAAA;AAGA,IAAA,kBAAA,EAAmB;AAGnB,IAAA,IAAI,CAAC,cAAA,EAAgB;AACnB,MAAA;AAAA,IACF;AACA,IAAA,MAAM,UAAA,GAAa,WAAA,CAAY,kBAAA,EAAoB,cAAc,CAAA;AAGjE,IAAA,OAAO,MAAM,cAAc,UAAU,CAAA;AAAA,EACvC,CAAA,EAAG,CAAC,+BAAA,EAAiC,cAAc,CAAC,CAAA;AAGpD,EAAA,MAAM,gBAAA,GAAmB,YAAA,CAAa,GAAA,CAAI,kBAAkB,CAAA;AAC5D,EAAA,IAAI,gBAAA,EAAkB;AACpB,IAAA,OAAO,IAAA;AAAA,EACT;AAGA,EAAA,IAAI,aAAA,CAAc,WAAW,CAAA,EAAG;AAC9B,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,MAAM,iBAAA,GAAoB,cAAc,CAAC,CAAA;AACzC,EAAA,MAAM,cAAA,GAAiB,cAAc,MAAA,GAAS,CAAA;AAE9C,EAAA,MAAM,iBAAA,GAAoB,CAAC,YAAA,KAAsC;AAC/D,IAAA,wBAAA,CAAyB,YAAY,CAAA;AACrC,IAAA,oBAAA,CAAqB,IAAI,CAAA;AACzB,IAAA,eAAA,CAAgB,IAAI,CAAA;AAAA,EACtB,CAAA;AAEA,EAAA,MAAM,sBAAsB,MAAM;AAChC,IAAA,oBAAA,CAAqB,KAAK,CAAA;AAC1B,IAAA,wBAAA,CAAyB,IAAI,CAAA;AAC7B,IAAA,eAAA,CAAgB,IAAI,CAAA;AAAA,EACtB,CAAA;AAEA,EAAA,MAAM,uBAAuB,YAAY;AACvC,IAAA,IAAI,CAAC,qBAAA,IAAyB,CAAC,yBAAA,EAA2B;AACxD,MAAA;AAAA,IACF;AAEA,IAAA,eAAA,CAAgB,IAAI,CAAA;AACpB,IAAA,eAAA,CAAgB,IAAI,CAAA;AAEpB,IAAA,IAAI;AACF,MAAA,MAAM,yBAAA,CAA0B,sBAAsB,EAAE,CAAA;AAGxD,MAAA,MAAM,MAAA,GAAS,MAAM,+BAAA,EAAgC;AACrD,MAAA,gBAAA,CAAiB,OAAO,aAAa,CAAA;AAIrC,MAAA,MAAM,uBAAA,GAA0B,YAAA,CAAa,GAAA,CAAI,kBAAkB,CAAA;AACnE,MAAA,IAAI,uBAAA,KAA4B,sBAAsB,EAAA,EAAI;AACxD,QAAA,MAAA,CAAO,KAAK,UAAU,CAAA;AAAA,MACxB;AAGA,MAAA,oBAAA,CAAqB,KAAK,CAAA;AAC1B,MAAA,wBAAA,CAAyB,IAAI,CAAA;AAAA,IAC/B,SAAS,KAAA,EAAO;AACd,MAAA,OAAA,CAAQ,KAAA,CAAM,0DAA0D,KAAK,CAAA;AAC7E,MAAA,eAAA,CAAgB,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU,gCAAgC,CAAA;AAAA,IAC3F,CAAA,SAAE;AACA,MAAA,eAAA,CAAgB,KAAK,CAAA;AAAA,IACvB;AAAA,EACF,CAAA;AAEA,EAAA,uBACE,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,2DAAA,EACb,QAAA,EAAA;AAAA,oBAAA,IAAA,CAAC,KAAA,EAAA,EAAI,WAAU,wBAAA,EACb,QAAA,EAAA;AAAA,sBAAA,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,+BAAA,EACb,QAAA,kBAAA,GAAA,CAAC,aAAU,CAAA,EACb,CAAA;AAAA,sBACA,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,kBAAA,EACb,QAAA,EAAA;AAAA,wBAAA,IAAA,CAAC,KAAA,EAAA,EAAI,WAAU,wCAAA,EACb,QAAA,EAAA;AAAA,0BAAA,IAAA,CAAC,KAAA,EAAA,EAAI,WAAU,QAAA,EACb,QAAA,EAAA;AAAA,4BAAA,IAAA,CAAC,IAAA,EAAA,EAAG,WAAU,uCAAA,EAAwC,QAAA,EAAA;AAAA,cAAA,wBAAA;AAAA,cAC7B,iBAAA,CAAkB,IAAA;AAAA,cAAK,SAAA;AAAA,cAAG,GAAA;AAAA,cAChD,iBAAA,CAAkB,MAAA,KAAW,MAAA,GAAS,MAAA,GAAS,OAAA;AAAA,cAAQ;AAAA,aAAA,EAC1D,CAAA;AAAA,4BACA,IAAA,CAAC,GAAA,EAAA,EAAE,SAAA,EAAU,4BAAA,EAA6B,QAAA,EAAA;AAAA,cAAA,UAAA;AAAA,cAC/B,aAAA,CAAc,kBAAkB,SAAS,CAAA;AAAA,cAAE,MAAA;AAAA,cAAK,iBAAA,CAAkB;AAAA,aAAA,EAC7E,CAAA;AAAA,YACC,iBAAiB,CAAA,oBAChB,IAAA;AAAA,cAAC,QAAA;AAAA,cAAA;AAAA,gBACC,IAAA,EAAK,QAAA;AAAA,gBACL,OAAA,EAAS,MAAM,UAAA,CAAW,CAAC,OAAO,CAAA;AAAA,gBAClC,SAAA,EAAU,oFAAA;AAAA,gBACX,QAAA,EAAA;AAAA,kBAAA,OAAA;AAAA,kBACO,cAAA;AAAA,kBAAe,4BAAA;AAAA,kBAA2B,cAAA,KAAmB,IAAI,GAAA,GAAM,EAAA;AAAA,sCAC5E,gBAAA,EAAA,EAAiB;AAAA;AAAA;AAAA;AACpB,WAAA,EAEJ,CAAA;AAAA,0BACA,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,yBAAA,EACZ,QAAA,EAAA;AAAA,YAAA,yBAAA,oBACC,GAAA;AAAA,cAAC,QAAA;AAAA,cAAA;AAAA,gBACC,IAAA,EAAK,QAAA;AAAA,gBACL,OAAA,EAAS,MAAM,iBAAA,CAAkB,iBAAiB,CAAA;AAAA,gBAClD,SAAA,EAAU,yDAAA;AAAA,gBACV,YAAA,EAAW,qBAAA;AAAA,gBAEX,8BAAC,SAAA,EAAA,EAAU;AAAA;AAAA,aACb;AAAA,4BAEF,IAAA;AAAA,cAAC,IAAA;AAAA,cAAA;AAAA,gBACC,MAAM,CAAA,0BAAA,EAA6B,iBAAA,CAAkB,EAAE,CAAA,MAAA,EAAS,kBAAkB,MAAM,CAAA,OAAA,CAAA;AAAA,gBACxF,SAAA,EAAU,sIAAA;AAAA,gBACX,QAAA,EAAA;AAAA,kBAAA,uBAAA;AAAA,sCAEE,cAAA,EAAA,EAAe;AAAA;AAAA;AAAA;AAClB,WAAA,EACF;AAAA,SAAA,EACF,CAAA;AAAA,QAEC,OAAA,IAAW,cAAA,GAAiB,CAAA,oBAC3B,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,gDAAA,EACZ,QAAA,EAAA,aAAA,CAAc,KAAA,CAAM,CAAC,CAAA,CAAE,GAAA,CAAI,CAAC,YAAA,qBAC3B,IAAA;AAAA,UAAC,KAAA;AAAA,UAAA;AAAA,YAEC,SAAA,EAAU,2DAAA;AAAA,YAEV,QAAA,EAAA;AAAA,8BAAA,IAAA,CAAC,KAAA,EAAA,EACC,QAAA,EAAA;AAAA,gCAAA,IAAA,CAAC,IAAA,EAAA,EAAG,WAAU,mCAAA,EACX,QAAA,EAAA;AAAA,kBAAA,YAAA,CAAa,IAAA;AAAA,kBAAK,SAAA;AAAA,kBAAG,GAAA;AAAA,kBACrB,YAAA,CAAa,MAAA,KAAW,MAAA,GAAS,MAAA,GAAS,OAAA;AAAA,kBAAQ;AAAA,iBAAA,EACrD,CAAA;AAAA,gCACA,IAAA,CAAC,GAAA,EAAA,EAAE,SAAA,EAAU,8BAAA,EAA+B,QAAA,EAAA;AAAA,kBAAA,UAAA;AAAA,kBACjC,aAAA,CAAc,aAAa,SAAS,CAAA;AAAA,kBAAE,MAAA;AAAA,kBAAK,YAAA,CAAa;AAAA,iBAAA,EACnE;AAAA,eAAA,EACF,CAAA;AAAA,8BACA,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,yBAAA,EACZ,QAAA,EAAA;AAAA,gBAAA,yBAAA,oBACC,GAAA;AAAA,kBAAC,QAAA;AAAA,kBAAA;AAAA,oBACC,IAAA,EAAK,QAAA;AAAA,oBACL,OAAA,EAAS,MAAM,iBAAA,CAAkB,YAAY,CAAA;AAAA,oBAC7C,SAAA,EAAU,yDAAA;AAAA,oBACV,YAAA,EAAW,qBAAA;AAAA,oBAEX,8BAAC,SAAA,EAAA,EAAU;AAAA;AAAA,iBACb;AAAA,gCAEF,IAAA;AAAA,kBAAC,IAAA;AAAA,kBAAA;AAAA,oBACC,MAAM,CAAA,0BAAA,EAA6B,YAAA,CAAa,EAAE,CAAA,MAAA,EAAS,aAAa,MAAM,CAAA,OAAA,CAAA;AAAA,oBAC9E,SAAA,EAAU,wIAAA;AAAA,oBACX,QAAA,EAAA;AAAA,sBAAA,UAAA;AAAA,0CAEE,cAAA,EAAA,EAAe;AAAA;AAAA;AAAA;AAClB,eAAA,EACF;AAAA;AAAA,WAAA;AAAA,UA9BK,YAAA,CAAa;AAAA,SAgCrB,CAAA,EACH;AAAA,OAAA,EAEJ;AAAA,KAAA,EACF,CAAA;AAAA,IAGC,iBAAA,IAAqB,yCACpB,GAAA,CAAC,KAAA,EAAA,EAAI,WAAU,4EAAA,EACb,QAAA,kBAAA,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,mDAAA,EACb,QAAA,EAAA;AAAA,sBAAA,GAAA,CAAC,IAAA,EAAA,EAAG,SAAA,EAAU,qCAAA,EAAsC,QAAA,EAAA,2CAAA,EAEpD,CAAA;AAAA,sBACA,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,MAAA,EACb,QAAA,EAAA;AAAA,wBAAA,IAAA,CAAC,GAAA,EAAA,EAAE,WAAU,uBAAA,EAAwB,QAAA,EAAA;AAAA,UAAA,kDAAA;AAAA,UACc,GAAA;AAAA,0BACjD,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,yCAAA,EAA2C,gCAAsB,IAAA,EAAK,CAAA;AAAA,UAAQ,GAAA;AAAA,UAAI;AAAA,SAAA,EAEpG,CAAA;AAAA,QACC,YAAA,oBACC,GAAA,CAAC,GAAA,EAAA,EAAE,SAAA,EAAU,6BAA4B,QAAA,EAAA,mDAAA,EAEzC;AAAA,OAAA,EAEJ,CAAA;AAAA,sBACA,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,6BAAA,EACb,QAAA,EAAA;AAAA,wBAAA,GAAA;AAAA,UAAC,QAAA;AAAA,UAAA;AAAA,YACC,IAAA,EAAK,QAAA;AAAA,YACL,OAAA,EAAS,mBAAA;AAAA,YACT,QAAA,EAAU,YAAA;AAAA,YACV,SAAA,EAAU,6HAAA;AAAA,YACX,QAAA,EAAA;AAAA;AAAA,SAED;AAAA,wBACA,GAAA;AAAA,UAAC,QAAA;AAAA,UAAA;AAAA,YACC,IAAA,EAAK,QAAA;AAAA,YACL,OAAA,EAAS,oBAAA;AAAA,YACT,QAAA,EAAU,YAAA;AAAA,YACV,SAAA,EAAU,qGAAA;AAAA,YAET,yBAAe,eAAA,GAAkB;AAAA;AAAA;AACpC,OAAA,EACF;AAAA,KAAA,EACF,CAAA,EACF;AAAA,GAAA,EAEJ,CAAA;AAEJ;AAEA,oBAAA,CAAqB,WAAA,GAAc,sBAAA","file":"pending-installations.js","sourcesContent":["\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport { useSearchParams, useRouter } from \"next/navigation\";\nimport Link from \"next/link\";\nimport type { PendingInstallation, FetchPendingInstallationsResult, DiscardInstallationResult } from \"../actions/install\";\n\nexport interface PendingInstallationsProps {\n fetchPendingInstallationsAction: () => Promise<FetchPendingInstallationsResult>;\n discardInstallationAction?: (installOptionsId: string) => Promise<DiscardInstallationResult>;\n initialInstallations?: PendingInstallation[];\n pollIntervalMs?: number;\n}\n\nconst ClockIcon = () => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"h-6 w-6\"\n >\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <polyline points=\"12 6 12 12 16 14\" />\n </svg>\n);\n\nconst TrashIcon = () => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"h-5 w-5\"\n >\n <path d=\"M3 6h18\" />\n <path d=\"M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6\" />\n <path d=\"M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2\" />\n </svg>\n);\n\nconst ChevronRightIcon = () => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"h-4 w-4\"\n >\n <polyline points=\"9 18 15 12 9 6\" />\n </svg>\n);\n\nconst ArrowRightIcon = () => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"h-4 w-4\"\n >\n <line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\" />\n <polyline points=\"12 5 19 12 12 19\" />\n </svg>\n);\n\nconst formatTimeAgo = (dateString: string): string => {\n try {\n const date = new Date(dateString);\n const now = new Date();\n const seconds = Math.floor((now.getTime() - date.getTime()) / 1000);\n\n if (seconds < 60) return \"just now\";\n if (seconds < 3600) {\n const minutes = Math.floor(seconds / 60);\n return `${minutes} ${minutes === 1 ? \"minute\" : \"minutes\"} ago`;\n }\n if (seconds < 86400) {\n const hours = Math.floor(seconds / 3600);\n return `${hours === 1 ? \"an hour\" : `${hours} hours`} ago`;\n }\n const days = Math.floor(seconds / 86400);\n return `${days} ${days === 1 ? \"day\" : \"days\"} ago`;\n } catch {\n return dateString;\n }\n};\n\nexport const PendingInstallations = ({\n fetchPendingInstallationsAction,\n discardInstallationAction,\n initialInstallations = [],\n pollIntervalMs = 5000\n}: PendingInstallationsProps) => {\n const [installations, setInstallations] = useState<PendingInstallation[]>(initialInstallations);\n const [showAll, setShowAll] = useState(false);\n const [showConfirmDialog, setShowConfirmDialog] = useState(false);\n const [installationToDiscard, setInstallationToDiscard] = useState<PendingInstallation | null>(null);\n const [isDiscarding, setIsDiscarding] = useState(false);\n const [discardError, setDiscardError] = useState<string | null>(null);\n const searchParams = useSearchParams();\n const router = useRouter();\n\n useEffect(() => {\n const fetchInstallations = async () => {\n try {\n const result = await fetchPendingInstallationsAction();\n setInstallations(result.installations);\n } catch (error) {\n console.error(\"[pending-installations] Failed to fetch pending installations\", error);\n }\n };\n\n // Fetch immediately\n fetchInstallations();\n\n // Set up polling interval (skip if pollIntervalMs is 0 or falsy)\n if (!pollIntervalMs) {\n return;\n }\n const intervalId = setInterval(fetchInstallations, pollIntervalMs);\n\n // Cleanup on unmount\n return () => clearInterval(intervalId);\n }, [fetchPendingInstallationsAction, pollIntervalMs]);\n\n // Don't show banner if already viewing a pending installation\n const installOptionsId = searchParams.get(\"installOptionsId\");\n if (installOptionsId) {\n return null;\n }\n\n // Don't render if no pending installations\n if (installations.length === 0) {\n return null;\n }\n\n const firstInstallation = installations[0]!;\n const remainingCount = installations.length - 1;\n\n const handleDeleteClick = (installation: PendingInstallation) => {\n setInstallationToDiscard(installation);\n setShowConfirmDialog(true);\n setDiscardError(null);\n };\n\n const handleCancelDiscard = () => {\n setShowConfirmDialog(false);\n setInstallationToDiscard(null);\n setDiscardError(null);\n };\n\n const handleConfirmDiscard = async () => {\n if (!installationToDiscard || !discardInstallationAction) {\n return;\n }\n\n setIsDiscarding(true);\n setDiscardError(null);\n\n try {\n await discardInstallationAction(installationToDiscard.id);\n \n // Refresh the installations list\n const result = await fetchPendingInstallationsAction();\n setInstallations(result.installations);\n \n // If we're currently viewing the discarded installation, redirect to install page\n // Check current URL state, not captured closure value\n const currentInstallOptionsId = searchParams.get(\"installOptionsId\");\n if (currentInstallOptionsId === installationToDiscard.id) {\n router.push(\"/install\");\n }\n \n // Close the dialog\n setShowConfirmDialog(false);\n setInstallationToDiscard(null);\n } catch (error) {\n console.error(\"[pending-installations] Failed to discard installation\", error);\n setDiscardError(error instanceof Error ? error.message : \"Failed to discard installation\");\n } finally {\n setIsDiscarding(false);\n }\n };\n\n return (\n <div className=\"mb-6 rounded-xl border border-orange-200 bg-orange-50 p-6\">\n <div className=\"flex items-start gap-4\">\n <div className=\"flex-shrink-0 text-orange-600\">\n <ClockIcon />\n </div>\n <div className=\"flex-1 space-y-3\">\n <div className=\"flex items-start justify-between gap-4\">\n <div className=\"flex-1\">\n <h3 className=\"text-base font-semibold text-gray-900\">\n Pending installation: {firstInstallation.name} •{\" \"}\n {firstInstallation.method === \"helm\" ? \"Helm\" : \"Linux\"} installation\n </h3>\n <p className=\"mt-1 text-sm text-gray-600\">\n Started {formatTimeAgo(firstInstallation.startedAt)} by {firstInstallation.startedBy}\n </p>\n {remainingCount > 0 && (\n <button\n type=\"button\"\n onClick={() => setShowAll(!showAll)}\n className=\"mt-2 flex items-center gap-1 text-sm font-medium text-gray-700 hover:text-gray-900\"\n >\n View {remainingCount} more pending installation{remainingCount !== 1 ? \"s\" : \"\"}\n <ChevronRightIcon />\n </button>\n )}\n </div>\n <div className=\"flex items-center gap-2\">\n {discardInstallationAction && (\n <button\n type=\"button\"\n onClick={() => handleDeleteClick(firstInstallation)}\n className=\"rounded-lg p-2 text-red-600 transition hover:bg-red-100\"\n aria-label=\"Delete installation\"\n >\n <TrashIcon />\n </button>\n )}\n <Link\n href={`/install?installOptionsId=${firstInstallation.id}&type=${firstInstallation.method}&step=2`}\n className=\"inline-flex items-center gap-2 rounded-lg bg-orange-200 px-4 py-2 text-sm font-semibold text-gray-900 transition hover:bg-orange-300\"\n >\n Continue installation\n <ArrowRightIcon />\n </Link>\n </div>\n </div>\n\n {showAll && remainingCount > 0 && (\n <div className=\"mt-4 space-y-2 border-t border-orange-200 pt-4\">\n {installations.slice(1).map((installation) => (\n <div\n key={installation.id}\n className=\"flex items-center justify-between rounded-lg bg-white p-3\"\n >\n <div>\n <h4 className=\"text-sm font-medium text-gray-900\">\n {installation.name} •{\" \"}\n {installation.method === \"helm\" ? \"Helm\" : \"Linux\"} installation\n </h4>\n <p className=\"mt-0.5 text-xs text-gray-600\">\n Started {formatTimeAgo(installation.startedAt)} by {installation.startedBy}\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n {discardInstallationAction && (\n <button\n type=\"button\"\n onClick={() => handleDeleteClick(installation)}\n className=\"rounded-lg p-2 text-red-600 transition hover:bg-red-100\"\n aria-label=\"Delete installation\"\n >\n <TrashIcon />\n </button>\n )}\n <Link\n href={`/install?installOptionsId=${installation.id}&type=${installation.method}&step=2`}\n className=\"inline-flex items-center gap-2 rounded-lg bg-orange-200 px-3 py-1.5 text-xs font-semibold text-gray-900 transition hover:bg-orange-300\"\n >\n Continue\n <ArrowRightIcon />\n </Link>\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n </div>\n\n {/* Confirmation Dialog */}\n {showConfirmDialog && installationToDiscard && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-50\">\n <div className=\"w-full max-w-md rounded-lg bg-white p-6 shadow-xl\">\n <h3 className=\"text-lg font-semibold text-gray-900\">\n Discard install and service account token\n </h3>\n <div className=\"mt-4\">\n <p className=\"text-sm text-gray-600\">\n This will permanently delete the service account{\" \"}\n <code className=\"rounded bg-gray-100 px-1 py-0.5 text-sm\">{installationToDiscard.name}</code>{\" \"}\n and remove the pending installation. This action cannot be undone.\n </p>\n {discardError && (\n <p className=\"mt-3 text-sm text-red-600\">\n Failed to discard installation. Please try again.\n </p>\n )}\n </div>\n <div className=\"mt-6 flex justify-end gap-3\">\n <button\n type=\"button\"\n onClick={handleCancelDiscard}\n disabled={isDiscarding}\n className=\"rounded-lg border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 disabled:opacity-50\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={handleConfirmDiscard}\n disabled={isDiscarding}\n className=\"rounded-lg bg-red-600 px-4 py-2 text-sm font-medium text-white hover:bg-red-700 disabled:opacity-50\"\n >\n {isDiscarding ? \"Discarding...\" : \"Discard install and token\"}\n </button>\n </div>\n </div>\n </div>\n )}\n </div>\n );\n};\n\nPendingInstallations.displayName = \"PendingInstallations\";\n"]}
|
|
@@ -166,6 +166,7 @@ var Dropdown = ({ value, options, onChange, disabled, testId }) => {
|
|
|
166
166
|
children: [
|
|
167
167
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
168
168
|
/* @__PURE__ */ jsx("span", { children: selected?.label || "Select..." }),
|
|
169
|
+
selected?.badge && /* @__PURE__ */ jsx("span", { className: "rounded bg-green-50 px-1.5 py-0.5 text-xs font-medium text-green-600", children: selected.badge }),
|
|
169
170
|
selected?.sublabel && /* @__PURE__ */ jsx("span", { className: "text-xs font-normal text-gray-500", children: selected.sublabel })
|
|
170
171
|
] }),
|
|
171
172
|
/* @__PURE__ */ jsx(ChevronDownIcon, { className: "h-4 w-4 text-gray-500" })
|
|
@@ -185,7 +186,10 @@ var Dropdown = ({ value, options, onChange, disabled, testId }) => {
|
|
|
185
186
|
"data-testid": `${testId}-option-${option.value}`,
|
|
186
187
|
className: "flex w-full items-center justify-between px-4 py-2 text-left text-sm hover:bg-gray-100",
|
|
187
188
|
children: [
|
|
188
|
-
/* @__PURE__ */
|
|
189
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
190
|
+
/* @__PURE__ */ jsx("span", { className: "font-semibold text-gray-900", children: option.label }),
|
|
191
|
+
option.badge && /* @__PURE__ */ jsx("span", { className: "rounded bg-green-50 px-1.5 py-0.5 text-xs font-medium text-green-600", children: option.badge })
|
|
192
|
+
] }),
|
|
189
193
|
option.sublabel && /* @__PURE__ */ jsx("span", { className: "text-xs text-gray-500", children: option.sublabel })
|
|
190
194
|
]
|
|
191
195
|
},
|
|
@@ -347,14 +351,12 @@ function SecurityCard({
|
|
|
347
351
|
if (!selectedSequence) return filteredReleases[0] || null;
|
|
348
352
|
return filteredReleases.find((r) => r.channelSequence === selectedSequence) || null;
|
|
349
353
|
}, [filteredReleases, selectedSequence]);
|
|
350
|
-
const
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
return filteredReleases[idx + 1];
|
|
357
|
-
}, [filteredReleases, selectedRelease]);
|
|
354
|
+
const latestRelease = useMemo(() => {
|
|
355
|
+
return filteredReleases.length > 0 ? filteredReleases[0] : null;
|
|
356
|
+
}, [filteredReleases]);
|
|
357
|
+
const isSelectedLatest = useMemo(() => {
|
|
358
|
+
return selectedRelease?.channelSequence === latestRelease?.channelSequence;
|
|
359
|
+
}, [selectedRelease, latestRelease]);
|
|
358
360
|
useEffect(() => {
|
|
359
361
|
const firstRelease = filteredReleases[0];
|
|
360
362
|
if (filteredReleases.length > 0 && !selectedSequence && firstRelease) {
|
|
@@ -391,12 +393,12 @@ function SecurityCard({
|
|
|
391
393
|
setSecurityError(err instanceof Error ? err.message : "Failed to fetch security data");
|
|
392
394
|
setSecurityData(null);
|
|
393
395
|
}
|
|
394
|
-
if (
|
|
396
|
+
if (!isSelectedLatest && latestRelease && onFetchSecurityDiff) {
|
|
395
397
|
try {
|
|
396
398
|
const diffResult = await onFetchSecurityDiff({
|
|
397
399
|
installType: selectedInstallType,
|
|
398
|
-
fromChannelSequence:
|
|
399
|
-
toChannelSequence:
|
|
400
|
+
fromChannelSequence: selectedRelease.channelSequence,
|
|
401
|
+
toChannelSequence: latestRelease.channelSequence
|
|
400
402
|
});
|
|
401
403
|
setDiffData(diffResult);
|
|
402
404
|
} catch (err) {
|
|
@@ -422,7 +424,7 @@ function SecurityCard({
|
|
|
422
424
|
setIsLoadingSbom(false);
|
|
423
425
|
};
|
|
424
426
|
fetchData();
|
|
425
|
-
}, [selectedRelease, selectedInstallType,
|
|
427
|
+
}, [selectedRelease, selectedInstallType, latestRelease, isSelectedLatest, onFetchSecurityInfo, onFetchSecurityDiff, onFetchSBOM]);
|
|
426
428
|
const installTypeOptions = useMemo(() => {
|
|
427
429
|
const options = [];
|
|
428
430
|
if (showLinux) options.push({ value: "linux", label: "Linux" });
|
|
@@ -430,9 +432,10 @@ function SecurityCard({
|
|
|
430
432
|
return options;
|
|
431
433
|
}, [showLinux, showHelm]);
|
|
432
434
|
const releaseOptions = useMemo(() => {
|
|
433
|
-
return filteredReleases.map((r) => ({
|
|
435
|
+
return filteredReleases.map((r, index) => ({
|
|
434
436
|
value: r.channelSequence.toString(),
|
|
435
437
|
label: `Version ${r.versionLabel}`,
|
|
438
|
+
badge: index === 0 ? "Latest" : void 0,
|
|
436
439
|
sublabel: `Sequence ${r.channelSequence}${r.isRequired ? " (required)" : ""}`
|
|
437
440
|
}));
|
|
438
441
|
}, [filteredReleases]);
|
|
@@ -499,12 +502,31 @@ function SecurityCard({
|
|
|
499
502
|
}
|
|
500
503
|
}, [selectedRelease, selectedInstallType, onDownloadSBOM]);
|
|
501
504
|
const hasSecurityData = securityData?.images?.some((img) => img.security);
|
|
505
|
+
const activeInstancesOnVersion = useMemo(() => {
|
|
506
|
+
if (!selectedRelease?.versionLabel || !securityData?.activeInstancesByVersion) {
|
|
507
|
+
return 0;
|
|
508
|
+
}
|
|
509
|
+
return securityData.activeInstancesByVersion[selectedRelease.versionLabel] || 0;
|
|
510
|
+
}, [selectedRelease, securityData]);
|
|
511
|
+
const fixedCVEsToLatest = useMemo(() => {
|
|
512
|
+
if (!diffData?.images) return 0;
|
|
513
|
+
return Object.values(diffData.images).reduce((total, imageDiff) => {
|
|
514
|
+
if (!imageDiff.removed) return total;
|
|
515
|
+
return total + Object.keys(imageDiff.removed.critical || {}).length + Object.keys(imageDiff.removed.high || {}).length + Object.keys(imageDiff.removed.medium || {}).length + Object.keys(imageDiff.removed.low || {}).length;
|
|
516
|
+
}, 0);
|
|
517
|
+
}, [diffData]);
|
|
518
|
+
const hasCriticalFixed = useMemo(() => {
|
|
519
|
+
if (!diffData?.images) return false;
|
|
520
|
+
return Object.values(diffData.images).some(
|
|
521
|
+
(imageDiff) => imageDiff.removed?.critical && Object.keys(imageDiff.removed.critical).length > 0
|
|
522
|
+
);
|
|
523
|
+
}, [diffData]);
|
|
502
524
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
|
|
503
525
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
504
526
|
/* @__PURE__ */ jsx("h1", { className: "text-3xl font-bold text-gray-900", children: "Security Center" }),
|
|
505
527
|
/* @__PURE__ */ jsx("p", { className: "text-gray-600", children: "Security information and compliance reports for each release" })
|
|
506
528
|
] }),
|
|
507
|
-
/* @__PURE__ */ jsxs("div", { className: "
|
|
529
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-end gap-6", children: [
|
|
508
530
|
/* @__PURE__ */ jsxs("div", { className: "w-72", children: [
|
|
509
531
|
/* @__PURE__ */ jsx("span", { className: "mb-2 block text-sm font-medium text-gray-700", children: "Install Type" }),
|
|
510
532
|
availableInstallTypes.length > 1 ? /* @__PURE__ */ jsx(
|
|
@@ -529,7 +551,45 @@ function SecurityCard({
|
|
|
529
551
|
testId: "release-dropdown"
|
|
530
552
|
}
|
|
531
553
|
)
|
|
532
|
-
] })
|
|
554
|
+
] }),
|
|
555
|
+
!isSelectedLatest ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1 self-start pt-7", children: [
|
|
556
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
557
|
+
/* @__PURE__ */ jsx(
|
|
558
|
+
"a",
|
|
559
|
+
{
|
|
560
|
+
href: "/update",
|
|
561
|
+
className: "rounded px-3 py-1 text-xs text-white no-underline",
|
|
562
|
+
style: {
|
|
563
|
+
backgroundColor: hasCriticalFixed ? "#dc2626" : primaryColor || "#2563eb",
|
|
564
|
+
borderColor: hasCriticalFixed ? "#dc2626" : primaryColor || "#2563eb"
|
|
565
|
+
},
|
|
566
|
+
children: hasCriticalFixed ? "Critical update available" : "Update available"
|
|
567
|
+
}
|
|
568
|
+
),
|
|
569
|
+
fixedCVEsToLatest > 0 && /* @__PURE__ */ jsxs("span", { className: "text-xs font-medium text-gray-600", children: [
|
|
570
|
+
fixedCVEsToLatest,
|
|
571
|
+
" Fixed CVEs"
|
|
572
|
+
] })
|
|
573
|
+
] }),
|
|
574
|
+
activeInstancesOnVersion > 0 && /* @__PURE__ */ jsxs("span", { className: "text-sm text-gray-600", children: [
|
|
575
|
+
activeInstancesOnVersion,
|
|
576
|
+
" active instance",
|
|
577
|
+
activeInstancesOnVersion !== 1 ? "s" : "",
|
|
578
|
+
" on this version.",
|
|
579
|
+
" ",
|
|
580
|
+
/* @__PURE__ */ jsx("a", { href: "/update", className: "font-semibold text-blue-500 hover:underline", children: "See more." })
|
|
581
|
+
] })
|
|
582
|
+
] }) : (
|
|
583
|
+
/* Show active instances inline when on latest */
|
|
584
|
+
activeInstancesOnVersion > 0 && /* @__PURE__ */ jsxs("div", { className: "pb-2 text-sm text-gray-600", children: [
|
|
585
|
+
activeInstancesOnVersion,
|
|
586
|
+
" active instance",
|
|
587
|
+
activeInstancesOnVersion !== 1 ? "s" : "",
|
|
588
|
+
" on this version.",
|
|
589
|
+
" ",
|
|
590
|
+
/* @__PURE__ */ jsx("a", { href: "/update", className: "font-semibold text-blue-500 hover:underline", children: "See more." })
|
|
591
|
+
] })
|
|
592
|
+
)
|
|
533
593
|
] }),
|
|
534
594
|
(hasSecurityData || isLoadingScans || isLoadingSbom || securityError || sbomError) && /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-6 lg:grid-cols-2", children: [
|
|
535
595
|
/* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-gray-200 bg-white", children: [
|