@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.
Files changed (106) hide show
  1. package/components/metadata/registry.json +2 -2
  2. package/components/metadata/registry.md +2 -2
  3. package/dist/actions/index.d.mts +1 -637
  4. package/dist/actions/index.d.ts +1 -637
  5. package/dist/actions/index.js +2 -1
  6. package/dist/actions/index.js.map +1 -1
  7. package/dist/airgap-instances.d.mts +5 -3
  8. package/dist/airgap-instances.d.ts +5 -3
  9. package/dist/airgap-instances.js +94 -5
  10. package/dist/airgap-instances.js.map +1 -1
  11. package/dist/esm/actions/index.js +2 -1
  12. package/dist/esm/actions/index.js.map +1 -1
  13. package/dist/esm/airgap-instances.js +90 -5
  14. package/dist/esm/airgap-instances.js.map +1 -1
  15. package/dist/esm/helm-install-wizard.js +28 -15
  16. package/dist/esm/helm-install-wizard.js.map +1 -1
  17. package/dist/esm/index.js +3 -2
  18. package/dist/esm/index.js.map +1 -1
  19. package/dist/esm/install-actions.js +54 -11
  20. package/dist/esm/install-actions.js.map +1 -1
  21. package/dist/esm/instance-card.js +79 -3
  22. package/dist/esm/instance-card.js.map +1 -1
  23. package/dist/esm/license-details.js +2 -1
  24. package/dist/esm/license-details.js.map +1 -1
  25. package/dist/esm/linux-install-wizard.js +90 -14
  26. package/dist/esm/linux-install-wizard.js.map +1 -1
  27. package/dist/esm/online-instance-list.js +90 -5
  28. package/dist/esm/online-instance-list.js.map +1 -1
  29. package/dist/esm/pending-installations.js +187 -103
  30. package/dist/esm/pending-installations.js.map +1 -1
  31. package/dist/esm/security-card.js +76 -16
  32. package/dist/esm/security-card.js.map +1 -1
  33. package/dist/esm/support-card.js +2 -1
  34. package/dist/esm/support-card.js.map +1 -1
  35. package/dist/esm/top-nav-user-menu.js +4 -2
  36. package/dist/esm/top-nav-user-menu.js.map +1 -1
  37. package/dist/esm/top-nav.js +2 -1
  38. package/dist/esm/top-nav.js.map +1 -1
  39. package/dist/esm/update-layout.js +2 -1
  40. package/dist/esm/update-layout.js.map +1 -1
  41. package/dist/esm/upload-support-bundle-modal.js +19 -19
  42. package/dist/esm/upload-support-bundle-modal.js.map +1 -1
  43. package/dist/esm/utils/index.js +2 -1
  44. package/dist/esm/utils/index.js.map +1 -1
  45. package/dist/helm-install-wizard.d.mts +5 -4
  46. package/dist/helm-install-wizard.d.ts +5 -4
  47. package/dist/helm-install-wizard.js +28 -15
  48. package/dist/helm-install-wizard.js.map +1 -1
  49. package/dist/index-DkjaogsF.d.mts +891 -0
  50. package/dist/index-DkjaogsF.d.ts +891 -0
  51. package/dist/index.d.mts +2 -1
  52. package/dist/index.d.ts +2 -1
  53. package/dist/index.js +3 -2
  54. package/dist/index.js.map +1 -1
  55. package/dist/install-actions.d.mts +2 -3
  56. package/dist/install-actions.d.ts +2 -3
  57. package/dist/install-actions.js +54 -10
  58. package/dist/install-actions.js.map +1 -1
  59. package/dist/install-card.d.mts +1 -1
  60. package/dist/install-card.d.ts +1 -1
  61. package/dist/instance-card.d.mts +5 -3
  62. package/dist/instance-card.d.ts +5 -3
  63. package/dist/instance-card.js +83 -3
  64. package/dist/instance-card.js.map +1 -1
  65. package/dist/license-card.d.mts +1 -1
  66. package/dist/license-card.d.ts +1 -1
  67. package/dist/license-details.js +2 -1
  68. package/dist/license-details.js.map +1 -1
  69. package/dist/linux-install-wizard.d.mts +6 -6
  70. package/dist/linux-install-wizard.d.ts +6 -6
  71. package/dist/linux-install-wizard.js +90 -14
  72. package/dist/linux-install-wizard.js.map +1 -1
  73. package/dist/online-instance-list.d.mts +5 -3
  74. package/dist/online-instance-list.d.ts +5 -3
  75. package/dist/online-instance-list.js +94 -5
  76. package/dist/online-instance-list.js.map +1 -1
  77. package/dist/pending-installations.d.mts +3 -3
  78. package/dist/pending-installations.d.ts +3 -3
  79. package/dist/pending-installations.js +186 -102
  80. package/dist/pending-installations.js.map +1 -1
  81. package/dist/security-card.d.mts +3 -2
  82. package/dist/security-card.d.ts +3 -2
  83. package/dist/security-card.js +76 -16
  84. package/dist/security-card.js.map +1 -1
  85. package/dist/styles.css +43 -0
  86. package/dist/support-bundles-card.d.mts +1 -1
  87. package/dist/support-bundles-card.d.ts +1 -1
  88. package/dist/support-card.js +2 -1
  89. package/dist/support-card.js.map +1 -1
  90. package/dist/top-nav-user-menu.d.mts +3 -1
  91. package/dist/top-nav-user-menu.d.ts +3 -1
  92. package/dist/top-nav-user-menu.js +4 -2
  93. package/dist/top-nav-user-menu.js.map +1 -1
  94. package/dist/top-nav.js +2 -1
  95. package/dist/top-nav.js.map +1 -1
  96. package/dist/update-layout.js +2 -1
  97. package/dist/update-layout.js.map +1 -1
  98. package/dist/upload-support-bundle-modal.d.mts +7 -3
  99. package/dist/upload-support-bundle-modal.d.ts +7 -3
  100. package/dist/upload-support-bundle-modal.js +19 -19
  101. package/dist/upload-support-bundle-modal.js.map +1 -1
  102. package/dist/utils/index.js +2 -1
  103. package/dist/utils/index.js.map +1 -1
  104. package/package.json +1 -1
  105. package/dist/install-B19AaKF_.d.mts +0 -233
  106. 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 { jsx, jsxs } from 'react/jsx-runtime';
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
- return /* @__PURE__ */ jsx("div", { className: "mb-6 rounded-xl border border-orange-200 bg-orange-50 p-6", children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-4", children: [
126
- /* @__PURE__ */ jsx("div", { className: "flex-shrink-0 text-orange-600", children: /* @__PURE__ */ jsx(ClockIcon, {}) }),
127
- /* @__PURE__ */ jsxs("div", { className: "flex-1 space-y-3", children: [
128
- /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-4", children: [
129
- /* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
130
- /* @__PURE__ */ jsxs("h3", { className: "text-base font-semibold text-gray-900", children: [
131
- "Pending installation: ",
132
- firstInstallation.name,
133
- " \u2022",
134
- " ",
135
- firstInstallation.method === "helm" ? "Helm" : "Linux",
136
- " installation"
137
- ] }),
138
- /* @__PURE__ */ jsxs("p", { className: "mt-1 text-sm text-gray-600", children: [
139
- "Started ",
140
- formatTimeAgo(firstInstallation.startedAt),
141
- " by ",
142
- firstInstallation.startedBy
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
- remainingCount > 0 && /* @__PURE__ */ jsxs(
145
- "button",
146
- {
147
- type: "button",
148
- onClick: () => setShowAll(!showAll),
149
- className: "mt-2 flex items-center gap-1 text-sm font-medium text-gray-700 hover:text-gray-900",
150
- children: [
151
- "View ",
152
- remainingCount,
153
- " more pending installation",
154
- remainingCount !== 1 ? "s" : "",
155
- /* @__PURE__ */ jsx(ChevronRightIcon, {})
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__ */ jsxs("div", { className: "flex items-center gap-2", children: [
161
- /* @__PURE__ */ jsx(
162
- "button",
163
- {
164
- type: "button",
165
- className: "rounded-lg p-2 text-red-600 transition hover:bg-red-100",
166
- "aria-label": "Delete installation",
167
- children: /* @__PURE__ */ jsx(TrashIcon, {})
168
- }
169
- ),
170
- /* @__PURE__ */ jsxs(
171
- Link,
172
- {
173
- href: `/install?installOptionsId=${firstInstallation.id}&type=${firstInstallation.method}&step=2`,
174
- 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",
175
- children: [
176
- "Continue installation",
177
- /* @__PURE__ */ jsx(ArrowRightIcon, {})
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("p", { className: "mt-0.5 text-xs text-gray-600", children: [
197
- "Started ",
198
- formatTimeAgo(installation.startedAt),
199
- " by ",
200
- installation.startedBy
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
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
204
- /* @__PURE__ */ jsx(
205
- "button",
206
- {
207
- type: "button",
208
- className: "rounded-lg p-2 text-red-600 transition hover:bg-red-100",
209
- "aria-label": "Delete installation",
210
- children: /* @__PURE__ */ jsx(TrashIcon, {})
211
- }
212
- ),
213
- /* @__PURE__ */ jsxs(
214
- Link,
215
- {
216
- href: `/install?installOptionsId=${installation.id}&type=${installation.method}&step=2`,
217
- 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",
218
- children: [
219
- "Continue",
220
- /* @__PURE__ */ jsx(ArrowRightIcon, {})
221
- ]
222
- }
223
- )
224
- ] })
225
- ]
226
- },
227
- installation.id
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":"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","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__ */ jsx("span", { className: "font-semibold text-gray-900", children: option.label }),
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 previousRelease = useMemo(() => {
351
- if (!selectedRelease) return null;
352
- const idx = filteredReleases.findIndex(
353
- (r) => r.channelSequence === selectedRelease.channelSequence
354
- );
355
- if (idx === -1 || idx >= filteredReleases.length - 1) return null;
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 (previousRelease && onFetchSecurityDiff) {
396
+ if (!isSelectedLatest && latestRelease && onFetchSecurityDiff) {
395
397
  try {
396
398
  const diffResult = await onFetchSecurityDiff({
397
399
  installType: selectedInstallType,
398
- fromChannelSequence: previousRelease.channelSequence,
399
- toChannelSequence: selectedRelease.channelSequence
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, previousRelease, onFetchSecurityInfo, onFetchSecurityDiff, onFetchSBOM]);
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: "grid grid-cols-1 gap-6 lg:grid-cols-2", children: [
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: [