@replicated/portal-components 0.0.15 → 0.0.17

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 (110) 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 +6 -11
  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 +5 -10
  38. package/dist/esm/top-nav.js.map +1 -1
  39. package/dist/esm/update-layout.js +5 -10
  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 +3 -2
  52. package/dist/index.d.ts +3 -2
  53. package/dist/index.js +6 -11
  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-0mb1K_H0.d.mts → top-nav-IRIn66wS.d.mts} +2 -1
  91. package/dist/{top-nav-0mb1K_H0.d.ts → top-nav-IRIn66wS.d.ts} +2 -1
  92. package/dist/top-nav-user-menu.d.mts +3 -1
  93. package/dist/top-nav-user-menu.d.ts +3 -1
  94. package/dist/top-nav-user-menu.js +4 -2
  95. package/dist/top-nav-user-menu.js.map +1 -1
  96. package/dist/top-nav.d.mts +1 -1
  97. package/dist/top-nav.d.ts +1 -1
  98. package/dist/top-nav.js +5 -10
  99. package/dist/top-nav.js.map +1 -1
  100. package/dist/update-layout.js +5 -10
  101. package/dist/update-layout.js.map +1 -1
  102. package/dist/upload-support-bundle-modal.d.mts +7 -3
  103. package/dist/upload-support-bundle-modal.d.ts +7 -3
  104. package/dist/upload-support-bundle-modal.js +19 -19
  105. package/dist/upload-support-bundle-modal.js.map +1 -1
  106. package/dist/utils/index.js +2 -1
  107. package/dist/utils/index.js.map +1 -1
  108. package/package.json +2 -1
  109. package/dist/install-B19AaKF_.d.mts +0 -233
  110. package/dist/install-Bi1qJ8Bu.d.ts +0 -233
@@ -2,6 +2,7 @@
2
2
  'use strict';
3
3
 
4
4
  var react = require('react');
5
+ var navigation = require('next/navigation');
5
6
  var Link = require('next/link');
6
7
  var jsxRuntime = require('react/jsx-runtime');
7
8
 
@@ -102,11 +103,18 @@ var formatTimeAgo = (dateString) => {
102
103
  };
103
104
  var PendingInstallations = ({
104
105
  fetchPendingInstallationsAction,
106
+ discardInstallationAction,
105
107
  initialInstallations = [],
106
108
  pollIntervalMs = 5e3
107
109
  }) => {
108
110
  const [installations, setInstallations] = react.useState(initialInstallations);
109
111
  const [showAll, setShowAll] = react.useState(false);
112
+ const [showConfirmDialog, setShowConfirmDialog] = react.useState(false);
113
+ const [installationToDiscard, setInstallationToDiscard] = react.useState(null);
114
+ const [isDiscarding, setIsDiscarding] = react.useState(false);
115
+ const [discardError, setDiscardError] = react.useState(null);
116
+ const searchParams = navigation.useSearchParams();
117
+ const router = navigation.useRouter();
110
118
  react.useEffect(() => {
111
119
  const fetchInstallations = async () => {
112
120
  try {
@@ -123,117 +131,193 @@ var PendingInstallations = ({
123
131
  const intervalId = setInterval(fetchInstallations, pollIntervalMs);
124
132
  return () => clearInterval(intervalId);
125
133
  }, [fetchPendingInstallationsAction, pollIntervalMs]);
134
+ const installOptionsId = searchParams.get("installOptionsId");
135
+ if (installOptionsId) {
136
+ return null;
137
+ }
126
138
  if (installations.length === 0) {
127
139
  return null;
128
140
  }
129
141
  const firstInstallation = installations[0];
130
142
  const remainingCount = installations.length - 1;
131
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-6 rounded-xl border border-orange-200 bg-orange-50 p-6", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-4", children: [
132
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-shrink-0 text-orange-600", children: /* @__PURE__ */ jsxRuntime.jsx(ClockIcon, {}) }),
133
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 space-y-3", children: [
134
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-4", children: [
135
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1", children: [
136
- /* @__PURE__ */ jsxRuntime.jsxs("h3", { className: "text-base font-semibold text-gray-900", children: [
137
- "Pending installation: ",
138
- firstInstallation.name,
139
- " \u2022",
140
- " ",
141
- firstInstallation.method === "helm" ? "Helm" : "Linux",
142
- " installation"
143
- ] }),
144
- /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "mt-1 text-sm text-gray-600", children: [
145
- "Started ",
146
- formatTimeAgo(firstInstallation.startedAt),
147
- " by ",
148
- firstInstallation.startedBy
143
+ const handleDeleteClick = (installation) => {
144
+ setInstallationToDiscard(installation);
145
+ setShowConfirmDialog(true);
146
+ setDiscardError(null);
147
+ };
148
+ const handleCancelDiscard = () => {
149
+ setShowConfirmDialog(false);
150
+ setInstallationToDiscard(null);
151
+ setDiscardError(null);
152
+ };
153
+ const handleConfirmDiscard = async () => {
154
+ if (!installationToDiscard || !discardInstallationAction) {
155
+ return;
156
+ }
157
+ setIsDiscarding(true);
158
+ setDiscardError(null);
159
+ try {
160
+ await discardInstallationAction(installationToDiscard.id);
161
+ const result = await fetchPendingInstallationsAction();
162
+ setInstallations(result.installations);
163
+ const currentInstallOptionsId = searchParams.get("installOptionsId");
164
+ if (currentInstallOptionsId === installationToDiscard.id) {
165
+ router.push("/install");
166
+ }
167
+ setShowConfirmDialog(false);
168
+ setInstallationToDiscard(null);
169
+ } catch (error) {
170
+ console.error("[pending-installations] Failed to discard installation", error);
171
+ setDiscardError(error instanceof Error ? error.message : "Failed to discard installation");
172
+ } finally {
173
+ setIsDiscarding(false);
174
+ }
175
+ };
176
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-6 rounded-xl border border-orange-200 bg-orange-50 p-6", children: [
177
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-4", children: [
178
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-shrink-0 text-orange-600", children: /* @__PURE__ */ jsxRuntime.jsx(ClockIcon, {}) }),
179
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 space-y-3", children: [
180
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-4", children: [
181
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1", children: [
182
+ /* @__PURE__ */ jsxRuntime.jsxs("h3", { className: "text-base font-semibold text-gray-900", children: [
183
+ "Pending installation: ",
184
+ firstInstallation.name,
185
+ " \u2022",
186
+ " ",
187
+ firstInstallation.method === "helm" ? "Helm" : "Linux",
188
+ " installation"
189
+ ] }),
190
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "mt-1 text-sm text-gray-600", children: [
191
+ "Started ",
192
+ formatTimeAgo(firstInstallation.startedAt),
193
+ " by ",
194
+ firstInstallation.startedBy
195
+ ] }),
196
+ remainingCount > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
197
+ "button",
198
+ {
199
+ type: "button",
200
+ onClick: () => setShowAll(!showAll),
201
+ className: "mt-2 flex items-center gap-1 text-sm font-medium text-gray-700 hover:text-gray-900",
202
+ children: [
203
+ "View ",
204
+ remainingCount,
205
+ " more pending installation",
206
+ remainingCount !== 1 ? "s" : "",
207
+ /* @__PURE__ */ jsxRuntime.jsx(ChevronRightIcon, {})
208
+ ]
209
+ }
210
+ )
149
211
  ] }),
150
- remainingCount > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
151
- "button",
152
- {
153
- type: "button",
154
- onClick: () => setShowAll(!showAll),
155
- className: "mt-2 flex items-center gap-1 text-sm font-medium text-gray-700 hover:text-gray-900",
156
- children: [
157
- "View ",
158
- remainingCount,
159
- " more pending installation",
160
- remainingCount !== 1 ? "s" : "",
161
- /* @__PURE__ */ jsxRuntime.jsx(ChevronRightIcon, {})
162
- ]
163
- }
164
- )
212
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
213
+ discardInstallationAction && /* @__PURE__ */ jsxRuntime.jsx(
214
+ "button",
215
+ {
216
+ type: "button",
217
+ onClick: () => handleDeleteClick(firstInstallation),
218
+ className: "rounded-lg p-2 text-red-600 transition hover:bg-red-100",
219
+ "aria-label": "Delete installation",
220
+ children: /* @__PURE__ */ jsxRuntime.jsx(TrashIcon, {})
221
+ }
222
+ ),
223
+ /* @__PURE__ */ jsxRuntime.jsxs(
224
+ Link__default.default,
225
+ {
226
+ href: `/install?installOptionsId=${firstInstallation.id}&type=${firstInstallation.method}&step=2`,
227
+ 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",
228
+ children: [
229
+ "Continue installation",
230
+ /* @__PURE__ */ jsxRuntime.jsx(ArrowRightIcon, {})
231
+ ]
232
+ }
233
+ )
234
+ ] })
165
235
  ] }),
166
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
167
- /* @__PURE__ */ jsxRuntime.jsx(
168
- "button",
169
- {
170
- type: "button",
171
- className: "rounded-lg p-2 text-red-600 transition hover:bg-red-100",
172
- "aria-label": "Delete installation",
173
- children: /* @__PURE__ */ jsxRuntime.jsx(TrashIcon, {})
174
- }
175
- ),
176
- /* @__PURE__ */ jsxRuntime.jsxs(
177
- Link__default.default,
178
- {
179
- href: `/install?installOptionsId=${firstInstallation.id}&type=${firstInstallation.method}&step=2`,
180
- 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",
181
- children: [
182
- "Continue installation",
183
- /* @__PURE__ */ jsxRuntime.jsx(ArrowRightIcon, {})
184
- ]
185
- }
186
- )
187
- ] })
188
- ] }),
189
- showAll && remainingCount > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-4 space-y-2 border-t border-orange-200 pt-4", children: installations.slice(1).map((installation) => /* @__PURE__ */ jsxRuntime.jsxs(
190
- "div",
191
- {
192
- className: "flex items-center justify-between rounded-lg bg-white p-3",
193
- children: [
194
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
195
- /* @__PURE__ */ jsxRuntime.jsxs("h4", { className: "text-sm font-medium text-gray-900", children: [
196
- installation.name,
197
- " \u2022",
198
- " ",
199
- installation.method === "helm" ? "Helm" : "Linux",
200
- " installation"
236
+ showAll && remainingCount > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-4 space-y-2 border-t border-orange-200 pt-4", children: installations.slice(1).map((installation) => /* @__PURE__ */ jsxRuntime.jsxs(
237
+ "div",
238
+ {
239
+ className: "flex items-center justify-between rounded-lg bg-white p-3",
240
+ children: [
241
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
242
+ /* @__PURE__ */ jsxRuntime.jsxs("h4", { className: "text-sm font-medium text-gray-900", children: [
243
+ installation.name,
244
+ " \u2022",
245
+ " ",
246
+ installation.method === "helm" ? "Helm" : "Linux",
247
+ " installation"
248
+ ] }),
249
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "mt-0.5 text-xs text-gray-600", children: [
250
+ "Started ",
251
+ formatTimeAgo(installation.startedAt),
252
+ " by ",
253
+ installation.startedBy
254
+ ] })
201
255
  ] }),
202
- /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "mt-0.5 text-xs text-gray-600", children: [
203
- "Started ",
204
- formatTimeAgo(installation.startedAt),
205
- " by ",
206
- installation.startedBy
256
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
257
+ discardInstallationAction && /* @__PURE__ */ jsxRuntime.jsx(
258
+ "button",
259
+ {
260
+ type: "button",
261
+ onClick: () => handleDeleteClick(installation),
262
+ className: "rounded-lg p-2 text-red-600 transition hover:bg-red-100",
263
+ "aria-label": "Delete installation",
264
+ children: /* @__PURE__ */ jsxRuntime.jsx(TrashIcon, {})
265
+ }
266
+ ),
267
+ /* @__PURE__ */ jsxRuntime.jsxs(
268
+ Link__default.default,
269
+ {
270
+ href: `/install?installOptionsId=${installation.id}&type=${installation.method}&step=2`,
271
+ 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",
272
+ children: [
273
+ "Continue",
274
+ /* @__PURE__ */ jsxRuntime.jsx(ArrowRightIcon, {})
275
+ ]
276
+ }
277
+ )
207
278
  ] })
208
- ] }),
209
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
210
- /* @__PURE__ */ jsxRuntime.jsx(
211
- "button",
212
- {
213
- type: "button",
214
- className: "rounded-lg p-2 text-red-600 transition hover:bg-red-100",
215
- "aria-label": "Delete installation",
216
- children: /* @__PURE__ */ jsxRuntime.jsx(TrashIcon, {})
217
- }
218
- ),
219
- /* @__PURE__ */ jsxRuntime.jsxs(
220
- Link__default.default,
221
- {
222
- href: `/install?installOptionsId=${installation.id}&type=${installation.method}&step=2`,
223
- 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",
224
- children: [
225
- "Continue",
226
- /* @__PURE__ */ jsxRuntime.jsx(ArrowRightIcon, {})
227
- ]
228
- }
229
- )
230
- ] })
231
- ]
232
- },
233
- installation.id
234
- )) })
235
- ] })
236
- ] }) });
279
+ ]
280
+ },
281
+ installation.id
282
+ )) })
283
+ ] })
284
+ ] }),
285
+ showConfirmDialog && installationToDiscard && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-50", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full max-w-md rounded-lg bg-white p-6 shadow-xl", children: [
286
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold text-gray-900", children: "Discard install and service account token" }),
287
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4", children: [
288
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-gray-600", children: [
289
+ "This will permanently delete the service account",
290
+ " ",
291
+ /* @__PURE__ */ jsxRuntime.jsx("code", { className: "rounded bg-gray-100 px-1 py-0.5 text-sm", children: installationToDiscard.name }),
292
+ " ",
293
+ "and remove the pending installation. This action cannot be undone."
294
+ ] }),
295
+ discardError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-3 text-sm text-red-600", children: "Failed to discard installation. Please try again." })
296
+ ] }),
297
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-6 flex justify-end gap-3", children: [
298
+ /* @__PURE__ */ jsxRuntime.jsx(
299
+ "button",
300
+ {
301
+ type: "button",
302
+ onClick: handleCancelDiscard,
303
+ disabled: isDiscarding,
304
+ 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",
305
+ children: "Cancel"
306
+ }
307
+ ),
308
+ /* @__PURE__ */ jsxRuntime.jsx(
309
+ "button",
310
+ {
311
+ type: "button",
312
+ onClick: handleConfirmDiscard,
313
+ disabled: isDiscarding,
314
+ className: "rounded-lg bg-red-600 px-4 py-2 text-sm font-medium text-white hover:bg-red-700 disabled:opacity-50",
315
+ children: isDiscarding ? "Discarding..." : "Discard install and token"
316
+ }
317
+ )
318
+ ] })
319
+ ] }) })
320
+ ] });
237
321
  };
238
322
  PendingInstallations.displayName = "PendingInstallations";
239
323
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/pending-installations.tsx"],"names":["jsxs","jsx","useState","useEffect","Link"],"mappings":";;;;;;;;;;;;;;;AAYA,IAAM,YAAY,sBAChBA,eAAA;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,sBAAAC,cAAA,CAAC,YAAO,EAAA,EAAG,IAAA,EAAK,EAAA,EAAG,IAAA,EAAK,GAAE,IAAA,EAAK,CAAA;AAAA,sBAC/BA,cAAA,CAAC,UAAA,EAAA,EAAS,MAAA,EAAO,kBAAA,EAAmB;AAAA;AAAA;AACtC,CAAA;AAGF,IAAM,YAAY,sBAChBD,eAAA;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,sBAAAC,cAAA,CAAC,MAAA,EAAA,EAAK,GAAE,SAAA,EAAU,CAAA;AAAA,sBAClBA,cAAA,CAAC,MAAA,EAAA,EAAK,CAAA,EAAE,uCAAA,EAAwC,CAAA;AAAA,sBAChDA,cAAA,CAAC,MAAA,EAAA,EAAK,CAAA,EAAE,oCAAA,EAAqC;AAAA;AAAA;AAC/C,CAAA;AAGF,IAAM,mBAAmB,sBACvBA,cAAA;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,kBAAAA,cAAA,CAAC,UAAA,EAAA,EAAS,MAAA,EAAO,gBAAA,EAAiB;AAAA;AACpC,CAAA;AAGF,IAAM,iBAAiB,sBACrBD,eAAA;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,sBAAAC,cAAA,CAAC,MAAA,EAAA,EAAK,IAAG,GAAA,EAAI,EAAA,EAAG,MAAK,EAAA,EAAG,IAAA,EAAK,IAAG,IAAA,EAAK,CAAA;AAAA,sBACrCA,cAAA,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,uBAAuB,EAAC;AAAA,EACxB,cAAA,GAAiB;AACnB,CAAA,KAAiC;AAC/B,EAAA,MAAM,CAAC,aAAA,EAAe,gBAAgB,CAAA,GAAIC,eAAgC,oBAAoB,CAAA;AAC9F,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAIA,eAAS,KAAK,CAAA;AAE5C,EAAAC,eAAA,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,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,sCACG,KAAA,EAAA,EAAI,SAAA,EAAU,6DACb,QAAA,kBAAAH,eAAA,CAAC,KAAA,EAAA,EAAI,WAAU,wBAAA,EACb,QAAA,EAAA;AAAA,oBAAAC,cAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,+BAAA,EACb,QAAA,kBAAAA,cAAA,CAAC,aAAU,CAAA,EACb,CAAA;AAAA,oBACAD,eAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,kBAAA,EACb,QAAA,EAAA;AAAA,sBAAAA,eAAA,CAAC,KAAA,EAAA,EAAI,WAAU,wCAAA,EACb,QAAA,EAAA;AAAA,wBAAAA,eAAA,CAAC,KAAA,EAAA,EAAI,WAAU,QAAA,EACb,QAAA,EAAA;AAAA,0BAAAA,eAAA,CAAC,IAAA,EAAA,EAAG,WAAU,uCAAA,EAAwC,QAAA,EAAA;AAAA,YAAA,wBAAA;AAAA,YAC7B,iBAAA,CAAkB,IAAA;AAAA,YAAK,SAAA;AAAA,YAAG,GAAA;AAAA,YAChD,iBAAA,CAAkB,MAAA,KAAW,MAAA,GAAS,MAAA,GAAS,OAAA;AAAA,YAAQ;AAAA,WAAA,EAC1D,CAAA;AAAA,0BACAA,eAAA,CAAC,GAAA,EAAA,EAAE,SAAA,EAAU,4BAAA,EAA6B,QAAA,EAAA;AAAA,YAAA,UAAA;AAAA,YAC/B,aAAA,CAAc,kBAAkB,SAAS,CAAA;AAAA,YAAE,MAAA;AAAA,YAAK,iBAAA,CAAkB;AAAA,WAAA,EAC7E,CAAA;AAAA,UACC,iBAAiB,CAAA,oBAChBA,eAAA;AAAA,YAAC,QAAA;AAAA,YAAA;AAAA,cACC,IAAA,EAAK,QAAA;AAAA,cACL,OAAA,EAAS,MAAM,UAAA,CAAW,CAAC,OAAO,CAAA;AAAA,cAClC,SAAA,EAAU,oFAAA;AAAA,cACX,QAAA,EAAA;AAAA,gBAAA,OAAA;AAAA,gBACO,cAAA;AAAA,gBAAe,4BAAA;AAAA,gBAA2B,cAAA,KAAmB,IAAI,GAAA,GAAM,EAAA;AAAA,+CAC5E,gBAAA,EAAA,EAAiB;AAAA;AAAA;AAAA;AACpB,SAAA,EAEJ,CAAA;AAAA,wBACAA,eAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,yBAAA,EACb,QAAA,EAAA;AAAA,0BAAAC,cAAA;AAAA,YAAC,QAAA;AAAA,YAAA;AAAA,cACC,IAAA,EAAK,QAAA;AAAA,cACL,SAAA,EAAU,yDAAA;AAAA,cACV,YAAA,EAAW,qBAAA;AAAA,cAEX,yCAAC,SAAA,EAAA,EAAU;AAAA;AAAA,WACb;AAAA,0BACAD,eAAA;AAAA,YAACI,qBAAA;AAAA,YAAA;AAAA,cACC,MAAM,CAAA,0BAAA,EAA6B,iBAAA,CAAkB,EAAE,CAAA,MAAA,EAAS,kBAAkB,MAAM,CAAA,OAAA,CAAA;AAAA,cACxF,SAAA,EAAU,sIAAA;AAAA,cACX,QAAA,EAAA;AAAA,gBAAA,uBAAA;AAAA,+CAEE,cAAA,EAAA,EAAe;AAAA;AAAA;AAAA;AAClB,SAAA,EACF;AAAA,OAAA,EACF,CAAA;AAAA,MAEC,OAAA,IAAW,cAAA,GAAiB,CAAA,oBAC3BH,cAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,gDAAA,EACZ,QAAA,EAAA,aAAA,CAAc,KAAA,CAAM,CAAC,CAAA,CAAE,GAAA,CAAI,CAAC,YAAA,qBAC3BD,eAAA;AAAA,QAAC,KAAA;AAAA,QAAA;AAAA,UAEC,SAAA,EAAU,2DAAA;AAAA,UAEV,QAAA,EAAA;AAAA,4BAAAA,eAAA,CAAC,KAAA,EAAA,EACC,QAAA,EAAA;AAAA,8BAAAA,eAAA,CAAC,IAAA,EAAA,EAAG,WAAU,mCAAA,EACX,QAAA,EAAA;AAAA,gBAAA,YAAA,CAAa,IAAA;AAAA,gBAAK,SAAA;AAAA,gBAAG,GAAA;AAAA,gBACrB,YAAA,CAAa,MAAA,KAAW,MAAA,GAAS,MAAA,GAAS,OAAA;AAAA,gBAAQ;AAAA,eAAA,EACrD,CAAA;AAAA,8BACAA,eAAA,CAAC,GAAA,EAAA,EAAE,SAAA,EAAU,8BAAA,EAA+B,QAAA,EAAA;AAAA,gBAAA,UAAA;AAAA,gBACjC,aAAA,CAAc,aAAa,SAAS,CAAA;AAAA,gBAAE,MAAA;AAAA,gBAAK,YAAA,CAAa;AAAA,eAAA,EACnE;AAAA,aAAA,EACF,CAAA;AAAA,4BACAA,eAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,yBAAA,EACb,QAAA,EAAA;AAAA,8BAAAC,cAAA;AAAA,gBAAC,QAAA;AAAA,gBAAA;AAAA,kBACC,IAAA,EAAK,QAAA;AAAA,kBACL,SAAA,EAAU,yDAAA;AAAA,kBACV,YAAA,EAAW,qBAAA;AAAA,kBAEX,yCAAC,SAAA,EAAA,EAAU;AAAA;AAAA,eACb;AAAA,8BACAD,eAAA;AAAA,gBAACI,qBAAA;AAAA,gBAAA;AAAA,kBACC,MAAM,CAAA,0BAAA,EAA6B,YAAA,CAAa,EAAE,CAAA,MAAA,EAAS,aAAa,MAAM,CAAA,OAAA,CAAA;AAAA,kBAC9E,SAAA,EAAU,wIAAA;AAAA,kBACX,QAAA,EAAA;AAAA,oBAAA,UAAA;AAAA,mDAEE,cAAA,EAAA,EAAe;AAAA;AAAA;AAAA;AAClB,aAAA,EACF;AAAA;AAAA,SAAA;AAAA,QA3BK,YAAA,CAAa;AAAA,OA6BrB,CAAA,EACH;AAAA,KAAA,EAEJ;AAAA,GAAA,EACF,CAAA,EACF,CAAA;AAEJ;AAEA,oBAAA,CAAqB,WAAA,GAAc,sBAAA","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":["jsxs","jsx","useState","useSearchParams","useRouter","useEffect","Link"],"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"]}
@@ -1,12 +1,12 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { SecurityInstallType, SecurityReleaseImage, SecurityInfoDiff, SBOMMetadata } from './actions/index.mjs';
3
- import { C as ChannelRelease } from './install-B19AaKF_.mjs';
2
+ import { S as SecurityReleaseImage, aq as SecurityInfoDiff, as as SBOMMetadata, al as SecurityInstallType, C as ChannelRelease } from './index-DkjaogsF.mjs';
4
3
 
5
4
  /** Pre-fetched security data for SSR */
6
5
  interface InitialSecurityData {
7
6
  /** Security scan results */
8
7
  securityInfo?: {
9
8
  images: SecurityReleaseImage[];
9
+ activeInstancesByVersion?: Record<string, number>;
10
10
  };
11
11
  /** CVE diff between releases */
12
12
  securityDiff?: {
@@ -46,6 +46,7 @@ interface SecurityCardProps {
46
46
  channelSequence: number;
47
47
  }) => Promise<{
48
48
  images: SecurityReleaseImage[];
49
+ activeInstancesByVersion?: Record<string, number>;
49
50
  }>;
50
51
  /** Callback to fetch security diff */
51
52
  onFetchSecurityDiff?: (params: {
@@ -1,12 +1,12 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { SecurityInstallType, SecurityReleaseImage, SecurityInfoDiff, SBOMMetadata } from './actions/index.js';
3
- import { C as ChannelRelease } from './install-Bi1qJ8Bu.js';
2
+ import { S as SecurityReleaseImage, aq as SecurityInfoDiff, as as SBOMMetadata, al as SecurityInstallType, C as ChannelRelease } from './index-DkjaogsF.js';
4
3
 
5
4
  /** Pre-fetched security data for SSR */
6
5
  interface InitialSecurityData {
7
6
  /** Security scan results */
8
7
  securityInfo?: {
9
8
  images: SecurityReleaseImage[];
9
+ activeInstancesByVersion?: Record<string, number>;
10
10
  };
11
11
  /** CVE diff between releases */
12
12
  securityDiff?: {
@@ -46,6 +46,7 @@ interface SecurityCardProps {
46
46
  channelSequence: number;
47
47
  }) => Promise<{
48
48
  images: SecurityReleaseImage[];
49
+ activeInstancesByVersion?: Record<string, number>;
49
50
  }>;
50
51
  /** Callback to fetch security diff */
51
52
  onFetchSecurityDiff?: (params: {
@@ -168,6 +168,7 @@ var Dropdown = ({ value, options, onChange, disabled, testId }) => {
168
168
  children: [
169
169
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
170
170
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: selected?.label || "Select..." }),
171
+ selected?.badge && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rounded bg-green-50 px-1.5 py-0.5 text-xs font-medium text-green-600", children: selected.badge }),
171
172
  selected?.sublabel && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-normal text-gray-500", children: selected.sublabel })
172
173
  ] }),
173
174
  /* @__PURE__ */ jsxRuntime.jsx(ChevronDownIcon, { className: "h-4 w-4 text-gray-500" })
@@ -187,7 +188,10 @@ var Dropdown = ({ value, options, onChange, disabled, testId }) => {
187
188
  "data-testid": `${testId}-option-${option.value}`,
188
189
  className: "flex w-full items-center justify-between px-4 py-2 text-left text-sm hover:bg-gray-100",
189
190
  children: [
190
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-gray-900", children: option.label }),
191
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
192
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-gray-900", children: option.label }),
193
+ option.badge && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rounded bg-green-50 px-1.5 py-0.5 text-xs font-medium text-green-600", children: option.badge })
194
+ ] }),
191
195
  option.sublabel && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-gray-500", children: option.sublabel })
192
196
  ]
193
197
  },
@@ -349,14 +353,12 @@ function SecurityCard({
349
353
  if (!selectedSequence) return filteredReleases[0] || null;
350
354
  return filteredReleases.find((r) => r.channelSequence === selectedSequence) || null;
351
355
  }, [filteredReleases, selectedSequence]);
352
- const previousRelease = react.useMemo(() => {
353
- if (!selectedRelease) return null;
354
- const idx = filteredReleases.findIndex(
355
- (r) => r.channelSequence === selectedRelease.channelSequence
356
- );
357
- if (idx === -1 || idx >= filteredReleases.length - 1) return null;
358
- return filteredReleases[idx + 1];
359
- }, [filteredReleases, selectedRelease]);
356
+ const latestRelease = react.useMemo(() => {
357
+ return filteredReleases.length > 0 ? filteredReleases[0] : null;
358
+ }, [filteredReleases]);
359
+ const isSelectedLatest = react.useMemo(() => {
360
+ return selectedRelease?.channelSequence === latestRelease?.channelSequence;
361
+ }, [selectedRelease, latestRelease]);
360
362
  react.useEffect(() => {
361
363
  const firstRelease = filteredReleases[0];
362
364
  if (filteredReleases.length > 0 && !selectedSequence && firstRelease) {
@@ -393,12 +395,12 @@ function SecurityCard({
393
395
  setSecurityError(err instanceof Error ? err.message : "Failed to fetch security data");
394
396
  setSecurityData(null);
395
397
  }
396
- if (previousRelease && onFetchSecurityDiff) {
398
+ if (!isSelectedLatest && latestRelease && onFetchSecurityDiff) {
397
399
  try {
398
400
  const diffResult = await onFetchSecurityDiff({
399
401
  installType: selectedInstallType,
400
- fromChannelSequence: previousRelease.channelSequence,
401
- toChannelSequence: selectedRelease.channelSequence
402
+ fromChannelSequence: selectedRelease.channelSequence,
403
+ toChannelSequence: latestRelease.channelSequence
402
404
  });
403
405
  setDiffData(diffResult);
404
406
  } catch (err) {
@@ -424,7 +426,7 @@ function SecurityCard({
424
426
  setIsLoadingSbom(false);
425
427
  };
426
428
  fetchData();
427
- }, [selectedRelease, selectedInstallType, previousRelease, onFetchSecurityInfo, onFetchSecurityDiff, onFetchSBOM]);
429
+ }, [selectedRelease, selectedInstallType, latestRelease, isSelectedLatest, onFetchSecurityInfo, onFetchSecurityDiff, onFetchSBOM]);
428
430
  const installTypeOptions = react.useMemo(() => {
429
431
  const options = [];
430
432
  if (showLinux) options.push({ value: "linux", label: "Linux" });
@@ -432,9 +434,10 @@ function SecurityCard({
432
434
  return options;
433
435
  }, [showLinux, showHelm]);
434
436
  const releaseOptions = react.useMemo(() => {
435
- return filteredReleases.map((r) => ({
437
+ return filteredReleases.map((r, index) => ({
436
438
  value: r.channelSequence.toString(),
437
439
  label: `Version ${r.versionLabel}`,
440
+ badge: index === 0 ? "Latest" : void 0,
438
441
  sublabel: `Sequence ${r.channelSequence}${r.isRequired ? " (required)" : ""}`
439
442
  }));
440
443
  }, [filteredReleases]);
@@ -501,12 +504,31 @@ function SecurityCard({
501
504
  }
502
505
  }, [selectedRelease, selectedInstallType, onDownloadSBOM]);
503
506
  const hasSecurityData = securityData?.images?.some((img) => img.security);
507
+ const activeInstancesOnVersion = react.useMemo(() => {
508
+ if (!selectedRelease?.versionLabel || !securityData?.activeInstancesByVersion) {
509
+ return 0;
510
+ }
511
+ return securityData.activeInstancesByVersion[selectedRelease.versionLabel] || 0;
512
+ }, [selectedRelease, securityData]);
513
+ const fixedCVEsToLatest = react.useMemo(() => {
514
+ if (!diffData?.images) return 0;
515
+ return Object.values(diffData.images).reduce((total, imageDiff) => {
516
+ if (!imageDiff.removed) return total;
517
+ 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;
518
+ }, 0);
519
+ }, [diffData]);
520
+ const hasCriticalFixed = react.useMemo(() => {
521
+ if (!diffData?.images) return false;
522
+ return Object.values(diffData.images).some(
523
+ (imageDiff) => imageDiff.removed?.critical && Object.keys(imageDiff.removed.critical).length > 0
524
+ );
525
+ }, [diffData]);
504
526
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-6", children: [
505
527
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
506
528
  /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-3xl font-bold text-gray-900", children: "Security Center" }),
507
529
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-600", children: "Security information and compliance reports for each release" })
508
530
  ] }),
509
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 gap-6 lg:grid-cols-2", children: [
531
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-end gap-6", children: [
510
532
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-72", children: [
511
533
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mb-2 block text-sm font-medium text-gray-700", children: "Install Type" }),
512
534
  availableInstallTypes.length > 1 ? /* @__PURE__ */ jsxRuntime.jsx(
@@ -531,7 +553,45 @@ function SecurityCard({
531
553
  testId: "release-dropdown"
532
554
  }
533
555
  )
534
- ] })
556
+ ] }),
557
+ !isSelectedLatest ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1 self-start pt-7", children: [
558
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
559
+ /* @__PURE__ */ jsxRuntime.jsx(
560
+ "a",
561
+ {
562
+ href: "/update",
563
+ className: "rounded px-3 py-1 text-xs text-white no-underline",
564
+ style: {
565
+ backgroundColor: hasCriticalFixed ? "#dc2626" : primaryColor || "#2563eb",
566
+ borderColor: hasCriticalFixed ? "#dc2626" : primaryColor || "#2563eb"
567
+ },
568
+ children: hasCriticalFixed ? "Critical update available" : "Update available"
569
+ }
570
+ ),
571
+ fixedCVEsToLatest > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs font-medium text-gray-600", children: [
572
+ fixedCVEsToLatest,
573
+ " Fixed CVEs"
574
+ ] })
575
+ ] }),
576
+ activeInstancesOnVersion > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm text-gray-600", children: [
577
+ activeInstancesOnVersion,
578
+ " active instance",
579
+ activeInstancesOnVersion !== 1 ? "s" : "",
580
+ " on this version.",
581
+ " ",
582
+ /* @__PURE__ */ jsxRuntime.jsx("a", { href: "/update", className: "font-semibold text-blue-500 hover:underline", children: "See more." })
583
+ ] })
584
+ ] }) : (
585
+ /* Show active instances inline when on latest */
586
+ activeInstancesOnVersion > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pb-2 text-sm text-gray-600", children: [
587
+ activeInstancesOnVersion,
588
+ " active instance",
589
+ activeInstancesOnVersion !== 1 ? "s" : "",
590
+ " on this version.",
591
+ " ",
592
+ /* @__PURE__ */ jsxRuntime.jsx("a", { href: "/update", className: "font-semibold text-blue-500 hover:underline", children: "See more." })
593
+ ] })
594
+ )
535
595
  ] }),
536
596
  (hasSecurityData || isLoadingScans || isLoadingSbom || securityError || sbomError) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 gap-6 lg:grid-cols-2", children: [
537
597
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border border-gray-200 bg-white", children: [