@replicated/portal-components 0.0.19 → 0.0.21

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 (244) hide show
  1. package/components/metadata/registry.json +2 -2
  2. package/components/metadata/registry.md +2 -2
  3. package/dist/actions/change-team.js +66 -7
  4. package/dist/actions/change-team.js.map +1 -1
  5. package/dist/actions/index.d.mts +3 -1
  6. package/dist/actions/index.d.ts +3 -1
  7. package/dist/actions/index.js +182 -465
  8. package/dist/actions/index.js.map +1 -1
  9. package/dist/actions/install-actions.d.mts +3 -1
  10. package/dist/actions/install-actions.d.ts +3 -1
  11. package/dist/actions/install-actions.js +58 -5
  12. package/dist/actions/install-actions.js.map +1 -1
  13. package/dist/actions/service-account.d.mts +3 -1
  14. package/dist/actions/service-account.d.ts +3 -1
  15. package/dist/actions/service-account.js +58 -5
  16. package/dist/actions/service-account.js.map +1 -1
  17. package/dist/actions/support-bundles.d.mts +3 -1
  18. package/dist/actions/support-bundles.d.ts +3 -1
  19. package/dist/actions/support-bundles.js +58 -5
  20. package/dist/actions/support-bundles.js.map +1 -1
  21. package/dist/actions/team-settings.d.mts +3 -1
  22. package/dist/actions/team-settings.d.ts +3 -1
  23. package/dist/actions/team-settings.js +91 -27
  24. package/dist/actions/team-settings.js.map +1 -1
  25. package/dist/actions/trial-signup.d.mts +24 -0
  26. package/dist/actions/trial-signup.d.ts +24 -0
  27. package/dist/actions/trial-signup.js +482 -0
  28. package/dist/actions/trial-signup.js.map +1 -0
  29. package/dist/actions/user-settings.d.mts +3 -1
  30. package/dist/actions/user-settings.d.ts +3 -1
  31. package/dist/actions/user-settings.js +58 -5
  32. package/dist/actions/user-settings.js.map +1 -1
  33. package/dist/airgap-instances.d.mts +3 -1
  34. package/dist/airgap-instances.d.ts +3 -1
  35. package/dist/airgap-instances.js +41 -112
  36. package/dist/airgap-instances.js.map +1 -1
  37. package/dist/branding-BsMSywts.d.mts +36 -0
  38. package/dist/branding-BsMSywts.d.ts +36 -0
  39. package/dist/error-page.js +10 -2
  40. package/dist/error-page.js.map +1 -1
  41. package/dist/error.js +10 -2
  42. package/dist/error.js.map +1 -1
  43. package/dist/esm/actions/change-team.js +66 -7
  44. package/dist/esm/actions/change-team.js.map +1 -1
  45. package/dist/esm/actions/index.js +181 -462
  46. package/dist/esm/actions/index.js.map +1 -1
  47. package/dist/esm/actions/install-actions.js +58 -5
  48. package/dist/esm/actions/install-actions.js.map +1 -1
  49. package/dist/esm/actions/service-account.js +58 -5
  50. package/dist/esm/actions/service-account.js.map +1 -1
  51. package/dist/esm/actions/support-bundles.js +58 -5
  52. package/dist/esm/actions/support-bundles.js.map +1 -1
  53. package/dist/esm/actions/team-settings.js +91 -27
  54. package/dist/esm/actions/team-settings.js.map +1 -1
  55. package/dist/esm/actions/trial-signup.js +478 -0
  56. package/dist/esm/actions/trial-signup.js.map +1 -0
  57. package/dist/esm/actions/user-settings.js +58 -5
  58. package/dist/esm/actions/user-settings.js.map +1 -1
  59. package/dist/esm/airgap-instances.js +40 -112
  60. package/dist/esm/airgap-instances.js.map +1 -1
  61. package/dist/esm/error-page.js +10 -2
  62. package/dist/esm/error-page.js.map +1 -1
  63. package/dist/esm/error.js +10 -2
  64. package/dist/esm/error.js.map +1 -1
  65. package/dist/esm/helm-install-wizard.js +118 -79
  66. package/dist/esm/helm-install-wizard.js.map +1 -1
  67. package/dist/esm/index.js +706 -438
  68. package/dist/esm/index.js.map +1 -1
  69. package/dist/esm/install-actions.js +40 -5
  70. package/dist/esm/install-actions.js.map +1 -1
  71. package/dist/esm/install-card.js +9 -6
  72. package/dist/esm/install-card.js.map +1 -1
  73. package/dist/esm/install-targets.js +9 -2
  74. package/dist/esm/install-targets.js.map +1 -1
  75. package/dist/esm/instance-card.js +39 -111
  76. package/dist/esm/instance-card.js.map +1 -1
  77. package/dist/esm/join-team.js +9 -3
  78. package/dist/esm/join-team.js.map +1 -1
  79. package/dist/esm/license-card.js +24 -22
  80. package/dist/esm/license-card.js.map +1 -1
  81. package/dist/esm/license-details.js +128 -334
  82. package/dist/esm/license-details.js.map +1 -1
  83. package/dist/esm/linux-install-wizard.js +95 -41
  84. package/dist/esm/linux-install-wizard.js.map +1 -1
  85. package/dist/esm/login.js +20 -4
  86. package/dist/esm/login.js.map +1 -1
  87. package/dist/esm/middleware.js +33 -0
  88. package/dist/esm/middleware.js.map +1 -0
  89. package/dist/esm/online-instance-list.js +40 -112
  90. package/dist/esm/online-instance-list.js.map +1 -1
  91. package/dist/esm/release-history-panel.js +27 -14
  92. package/dist/esm/release-history-panel.js.map +1 -1
  93. package/dist/esm/saml-callback-client.js +82 -0
  94. package/dist/esm/saml-callback-client.js.map +1 -0
  95. package/dist/esm/saml-handlers.js +138 -0
  96. package/dist/esm/saml-handlers.js.map +1 -0
  97. package/dist/esm/security-card.js +53 -38
  98. package/dist/esm/security-card.js.map +1 -1
  99. package/dist/esm/service-accounts-tab.js +800 -0
  100. package/dist/esm/service-accounts-tab.js.map +1 -0
  101. package/dist/esm/support-bundle-collection-card.js +48 -24
  102. package/dist/esm/support-bundle-collection-card.js.map +1 -1
  103. package/dist/esm/support-bundles-card.js +10 -5
  104. package/dist/esm/support-bundles-card.js.map +1 -1
  105. package/dist/esm/support-card.js +37 -5
  106. package/dist/esm/support-card.js.map +1 -1
  107. package/dist/esm/team-selection.js +5 -1
  108. package/dist/esm/team-selection.js.map +1 -1
  109. package/dist/esm/team-settings-card.js +5 -2
  110. package/dist/esm/team-settings-card.js.map +1 -1
  111. package/dist/esm/team-settings.js +7 -2
  112. package/dist/esm/team-settings.js.map +1 -1
  113. package/dist/esm/top-nav-user-menu.js +5 -1
  114. package/dist/esm/top-nav-user-menu.js.map +1 -1
  115. package/dist/esm/top-nav.js +175 -62
  116. package/dist/esm/top-nav.js.map +1 -1
  117. package/dist/esm/trial-signup.js +256 -0
  118. package/dist/esm/trial-signup.js.map +1 -0
  119. package/dist/esm/update-layout.js +175 -62
  120. package/dist/esm/update-layout.js.map +1 -1
  121. package/dist/esm/updates-card.js +15 -4
  122. package/dist/esm/updates-card.js.map +1 -1
  123. package/dist/esm/upload-support-bundle-modal.js +9 -4
  124. package/dist/esm/upload-support-bundle-modal.js.map +1 -1
  125. package/dist/esm/user-settings-card.js +5 -2
  126. package/dist/esm/user-settings-card.js.map +1 -1
  127. package/dist/esm/user-settings.js +12 -6
  128. package/dist/esm/user-settings.js.map +1 -1
  129. package/dist/esm/utils/index.js +204 -13
  130. package/dist/esm/utils/index.js.map +1 -1
  131. package/dist/fetch-license-iTyF7_GY.d.mts +81 -0
  132. package/dist/fetch-license-iTyF7_GY.d.ts +81 -0
  133. package/dist/helm-install-wizard.d.mts +11 -3
  134. package/dist/helm-install-wizard.d.ts +11 -3
  135. package/dist/helm-install-wizard.js +118 -79
  136. package/dist/helm-install-wizard.js.map +1 -1
  137. package/dist/{index-BAiVrSSR.d.mts → index-DyzJ0yKD.d.mts} +48 -50
  138. package/dist/{index-DWt-N5od.d.ts → index-sMbq94M7.d.ts} +48 -50
  139. package/dist/index.d.mts +8 -2
  140. package/dist/index.d.ts +8 -2
  141. package/dist/index.js +726 -438
  142. package/dist/index.js.map +1 -1
  143. package/dist/install-actions.d.mts +4 -2
  144. package/dist/install-actions.d.ts +4 -2
  145. package/dist/install-actions.js +40 -5
  146. package/dist/install-actions.js.map +1 -1
  147. package/dist/install-card.d.mts +2 -3
  148. package/dist/install-card.d.ts +2 -3
  149. package/dist/install-card.js +9 -6
  150. package/dist/install-card.js.map +1 -1
  151. package/dist/install-targets.js +9 -2
  152. package/dist/install-targets.js.map +1 -1
  153. package/dist/instance-card.d.mts +3 -1
  154. package/dist/instance-card.d.ts +3 -1
  155. package/dist/instance-card.js +40 -111
  156. package/dist/instance-card.js.map +1 -1
  157. package/dist/join-team.js +9 -3
  158. package/dist/join-team.js.map +1 -1
  159. package/dist/license-card.d.mts +2 -3
  160. package/dist/license-card.d.ts +2 -3
  161. package/dist/license-card.js +24 -22
  162. package/dist/license-card.js.map +1 -1
  163. package/dist/license-details.js +128 -334
  164. package/dist/license-details.js.map +1 -1
  165. package/dist/linux-install-wizard.d.mts +9 -3
  166. package/dist/linux-install-wizard.d.ts +9 -3
  167. package/dist/linux-install-wizard.js +95 -41
  168. package/dist/linux-install-wizard.js.map +1 -1
  169. package/dist/login.d.mts +4 -0
  170. package/dist/login.d.ts +4 -0
  171. package/dist/login.js +20 -4
  172. package/dist/login.js.map +1 -1
  173. package/dist/middleware.d.mts +13 -0
  174. package/dist/middleware.d.ts +13 -0
  175. package/dist/middleware.js +35 -0
  176. package/dist/middleware.js.map +1 -0
  177. package/dist/online-instance-list.d.mts +3 -1
  178. package/dist/online-instance-list.d.ts +3 -1
  179. package/dist/online-instance-list.js +41 -112
  180. package/dist/online-instance-list.js.map +1 -1
  181. package/dist/pending-installations.d.mts +3 -1
  182. package/dist/pending-installations.d.ts +3 -1
  183. package/dist/release-history-panel.js +27 -14
  184. package/dist/release-history-panel.js.map +1 -1
  185. package/dist/saml-callback-client.d.mts +36 -0
  186. package/dist/saml-callback-client.d.ts +36 -0
  187. package/dist/saml-callback-client.js +88 -0
  188. package/dist/saml-callback-client.js.map +1 -0
  189. package/dist/saml-handlers.d.mts +50 -0
  190. package/dist/saml-handlers.d.ts +50 -0
  191. package/dist/saml-handlers.js +141 -0
  192. package/dist/saml-handlers.js.map +1 -0
  193. package/dist/security-card.d.mts +3 -1
  194. package/dist/security-card.d.ts +3 -1
  195. package/dist/security-card.js +53 -38
  196. package/dist/security-card.js.map +1 -1
  197. package/dist/service-accounts-tab.d.mts +51 -0
  198. package/dist/service-accounts-tab.d.ts +51 -0
  199. package/dist/service-accounts-tab.js +802 -0
  200. package/dist/service-accounts-tab.js.map +1 -0
  201. package/dist/styles.css +375 -127
  202. package/dist/support-bundle-collection-card.d.mts +1 -1
  203. package/dist/support-bundle-collection-card.d.ts +1 -1
  204. package/dist/support-bundle-collection-card.js +47 -23
  205. package/dist/support-bundle-collection-card.js.map +1 -1
  206. package/dist/support-bundles-card.d.mts +4 -2
  207. package/dist/support-bundles-card.d.ts +4 -2
  208. package/dist/support-bundles-card.js +10 -5
  209. package/dist/support-bundles-card.js.map +1 -1
  210. package/dist/support-card.js +37 -5
  211. package/dist/support-card.js.map +1 -1
  212. package/dist/team-selection.js +5 -1
  213. package/dist/team-selection.js.map +1 -1
  214. package/dist/team-settings-card.js +5 -2
  215. package/dist/team-settings-card.js.map +1 -1
  216. package/dist/team-settings.js +7 -2
  217. package/dist/team-settings.js.map +1 -1
  218. package/dist/{top-nav-IRIn66wS.d.ts → top-nav-BUQAGoG1.d.mts} +14 -2
  219. package/dist/{top-nav-IRIn66wS.d.mts → top-nav-CEqw0KpO.d.ts} +14 -2
  220. package/dist/top-nav-user-menu.js +5 -1
  221. package/dist/top-nav-user-menu.js.map +1 -1
  222. package/dist/top-nav.d.mts +2 -1
  223. package/dist/top-nav.d.ts +2 -1
  224. package/dist/top-nav.js +175 -62
  225. package/dist/top-nav.js.map +1 -1
  226. package/dist/trial-signup.d.mts +31 -0
  227. package/dist/trial-signup.d.ts +31 -0
  228. package/dist/trial-signup.js +258 -0
  229. package/dist/trial-signup.js.map +1 -0
  230. package/dist/update-layout.js +175 -62
  231. package/dist/update-layout.js.map +1 -1
  232. package/dist/updates-card.js +15 -4
  233. package/dist/updates-card.js.map +1 -1
  234. package/dist/upload-support-bundle-modal.js +9 -4
  235. package/dist/upload-support-bundle-modal.js.map +1 -1
  236. package/dist/user-settings-card.js +5 -2
  237. package/dist/user-settings-card.js.map +1 -1
  238. package/dist/user-settings.js +12 -6
  239. package/dist/user-settings.js.map +1 -1
  240. package/dist/utils/index.d.mts +74 -16
  241. package/dist/utils/index.d.ts +74 -16
  242. package/dist/utils/index.js +215 -12
  243. package/dist/utils/index.js.map +1 -1
  244. package/package.json +37 -2
@@ -1,7 +1,9 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { Instance } from './install-actions.mjs';
3
- import { C as ChannelRelease, I as InstallOptions, S as SecurityReleaseImage, G as GetSecurityInfoDiffResult } from './index-BAiVrSSR.mjs';
3
+ import { C as ChannelRelease, I as InstallOptions, S as SecurityReleaseImage, G as GetSecurityInfoDiffResult } from './index-DyzJ0yKD.mjs';
4
+ import './fetch-license-iTyF7_GY.mjs';
4
5
  import './actions/change-team.mjs';
6
+ import './actions/trial-signup.mjs';
5
7
 
6
8
  interface AirgapInstancesProps {
7
9
  instances?: Instance[];
@@ -1,7 +1,9 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { Instance } from './install-actions.js';
3
- import { C as ChannelRelease, I as InstallOptions, S as SecurityReleaseImage, G as GetSecurityInfoDiffResult } from './index-DWt-N5od.js';
3
+ import { C as ChannelRelease, I as InstallOptions, S as SecurityReleaseImage, G as GetSecurityInfoDiffResult } from './index-sMbq94M7.js';
4
+ import './fetch-license-iTyF7_GY.js';
4
5
  import './actions/change-team.js';
6
+ import './actions/trial-signup.js';
5
7
 
6
8
  interface AirgapInstancesProps {
7
9
  instances?: Instance[];
@@ -3,100 +3,43 @@
3
3
 
4
4
  var react = require('react');
5
5
  var Link = require('next/link');
6
+ var cx = require('classnames');
6
7
  var jsxRuntime = require('react/jsx-runtime');
7
8
 
8
9
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
9
10
 
10
11
  var Link__default = /*#__PURE__*/_interopDefault(Link);
12
+ var cx__default = /*#__PURE__*/_interopDefault(cx);
11
13
 
12
14
  /**
13
15
  * Enterprise Portal Components
14
16
  * This file is generated by tsup. Do not edit manually.
15
17
  */
16
- var __create = Object.create;
17
- var __defProp = Object.defineProperty;
18
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
19
- var __getOwnPropNames = Object.getOwnPropertyNames;
20
- var __getProtoOf = Object.getPrototypeOf;
21
- var __hasOwnProp = Object.prototype.hasOwnProperty;
22
- var __commonJS = (cb, mod) => function __require() {
23
- return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
24
- };
25
- var __copyProps = (to, from, except, desc) => {
26
- if (from && typeof from === "object" || typeof from === "function") {
27
- for (let key of __getOwnPropNames(from))
28
- if (!__hasOwnProp.call(to, key) && key !== except)
29
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
30
- }
31
- return to;
32
- };
33
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
34
- // If the importer is in node compatibility mode or this is not an ESM
35
- // file that has been converted to a CommonJS file using a Babel-
36
- // compatible transform (i.e. "__esModule" has not been set), then set
37
- // "default" to the CommonJS "module.exports" for node compatibility.
38
- !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
39
- mod
40
- ));
41
18
 
42
- // node_modules/classnames/index.js
43
- var require_classnames = __commonJS({
44
- "node_modules/classnames/index.js"(exports$1, module) {
45
- (function() {
46
- var hasOwn = {}.hasOwnProperty;
47
- function classNames() {
48
- var classes = "";
49
- for (var i = 0; i < arguments.length; i++) {
50
- var arg = arguments[i];
51
- if (arg) {
52
- classes = appendClass(classes, parseValue(arg));
53
- }
54
- }
55
- return classes;
56
- }
57
- function parseValue(arg) {
58
- if (typeof arg === "string" || typeof arg === "number") {
59
- return arg;
60
- }
61
- if (typeof arg !== "object") {
62
- return "";
63
- }
64
- if (Array.isArray(arg)) {
65
- return classNames.apply(null, arg);
66
- }
67
- if (arg.toString !== Object.prototype.toString && !arg.toString.toString().includes("[native code]")) {
68
- return arg.toString();
69
- }
70
- var classes = "";
71
- for (var key in arg) {
72
- if (hasOwn.call(arg, key) && arg[key]) {
73
- classes = appendClass(classes, key);
74
- }
75
- }
76
- return classes;
77
- }
78
- function appendClass(value, newClass) {
79
- if (!newClass) {
80
- return value;
81
- }
82
- if (value) {
83
- return value + " " + newClass;
84
- }
85
- return value + newClass;
86
- }
87
- if (typeof module !== "undefined" && module.exports) {
88
- classNames.default = classNames;
89
- module.exports = classNames;
90
- } else if (typeof define === "function" && typeof define.amd === "object" && define.amd) {
91
- define("classnames", [], function() {
92
- return classNames;
93
- });
94
- } else {
95
- window.classNames = classNames;
96
- }
97
- })();
19
+
20
+ // src/utils/constants.ts
21
+ var DEFAULT_SECONDARY_COLOR = "#6366f1";
22
+
23
+ // src/utils/format.ts
24
+ function formatDateTimeLocal(dateString) {
25
+ if (!dateString) return "N/A";
26
+ try {
27
+ const date = new Date(dateString);
28
+ if (isNaN(date.getTime())) {
29
+ return dateString;
30
+ }
31
+ return date.toLocaleDateString("en-US", {
32
+ month: "numeric",
33
+ day: "numeric",
34
+ year: "numeric",
35
+ hour: "numeric",
36
+ minute: "2-digit",
37
+ hour12: true
38
+ });
39
+ } catch {
40
+ return "N/A";
98
41
  }
99
- });
42
+ }
100
43
 
101
44
  // src/actions/instances.ts
102
45
  var ACTIVE_INSTANCE_THRESHOLD_MS = 24 * 60 * 60 * 1e3;
@@ -156,9 +99,6 @@ function getInstanceName(instance) {
156
99
  const nameTag = instance.tags?.find((tag) => tag.key === "name");
157
100
  return nameTag?.value || instance.id.slice(0, 7);
158
101
  }
159
-
160
- // src/components/app-status-badge.tsx
161
- var import_classnames = __toESM(require_classnames());
162
102
  var toTitle = (value) => {
163
103
  const s = value.trim();
164
104
  if (!s) return s;
@@ -282,7 +222,7 @@ function AppStatusBadge({
282
222
  return /* @__PURE__ */ jsxRuntime.jsxs(
283
223
  "span",
284
224
  {
285
- className: (0, import_classnames.default)(
225
+ className: cx__default.default(
286
226
  "inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-xs font-medium",
287
227
  getClassNameForType(type),
288
228
  className
@@ -294,18 +234,6 @@ function AppStatusBadge({
294
234
  }
295
235
  );
296
236
  }
297
- function formatDateTime(dateString) {
298
- if (!dateString) return "N/A";
299
- const date = new Date(dateString);
300
- return date.toLocaleDateString("en-US", {
301
- month: "numeric",
302
- day: "numeric",
303
- year: "numeric",
304
- hour: "numeric",
305
- minute: "2-digit",
306
- hour12: true
307
- });
308
- }
309
237
  function UpdateBadge({ count }) {
310
238
  if (count <= 0) return null;
311
239
  return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-1.5 inline-flex h-5 w-5 items-center justify-center rounded-full bg-rose-500 text-xs font-medium text-white", children: count });
@@ -343,7 +271,7 @@ function InstanceCard({
343
271
  channelReleases = [],
344
272
  installOptions = [],
345
273
  onUpdateClick,
346
- primaryColor = "#6366f1",
274
+ primaryColor = DEFAULT_SECONDARY_COLOR,
347
275
  securityData = {},
348
276
  securityDiffData = {},
349
277
  securityEnabled = false
@@ -443,18 +371,28 @@ function InstanceCard({
443
371
  instance.versionLabel || "Unknown",
444
372
  /* @__PURE__ */ jsxRuntime.jsx(UpdateBadge, { count: availableUpdates })
445
373
  ] }),
446
- instance.isAirgap && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rounded-full bg-indigo-100 px-2 py-0.5 text-xs font-medium text-indigo-700", children: "air gap" }),
374
+ instance.isAirgap && /* @__PURE__ */ jsxRuntime.jsx(
375
+ "span",
376
+ {
377
+ className: "rounded-full px-2 py-0.5 text-xs font-medium",
378
+ style: {
379
+ backgroundColor: `var(--portal-branding-primary-light, ${DEFAULT_SECONDARY_COLOR}20)`,
380
+ color: `var(--portal-branding-primary, ${DEFAULT_SECONDARY_COLOR})`
381
+ },
382
+ children: "air gap"
383
+ }
384
+ ),
447
385
  installType && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rounded-full bg-gray-100 px-2 py-0.5 text-xs font-medium text-gray-600", children: installType }),
448
386
  (instance.appStatus || instance.resourceStates && instance.resourceStates.length > 0) && /* @__PURE__ */ jsxRuntime.jsx(AppStatusBadge, { status: statusForBadge })
449
387
  ] }),
450
388
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap gap-4", children: [
451
389
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
452
390
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-gray-500", children: "First check-in:" }),
453
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-gray-600", children: formatDateTime(instance.firstCheckin) })
391
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-gray-600", children: formatDateTimeLocal(instance.firstCheckin) })
454
392
  ] }),
455
393
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
456
394
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-gray-500", children: "Last check-in:" }),
457
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-gray-600", children: formatDateTime(instance.lastCheckin) })
395
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-gray-600", children: formatDateTimeLocal(instance.lastCheckin) })
458
396
  ] })
459
397
  ] }),
460
398
  securityEnabled && hasSecurityInfo && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
@@ -504,7 +442,7 @@ var AirgapInstances = ({
504
442
  onCreateFromBundleClick,
505
443
  onCreateManuallyClick,
506
444
  onCreateClick,
507
- primaryColor = "#6366f1",
445
+ primaryColor = DEFAULT_SECONDARY_COLOR,
508
446
  securityData = {},
509
447
  securityDiffData = {},
510
448
  securityEnabled = false,
@@ -644,15 +582,6 @@ var AirgapInstances = ({
644
582
  ] });
645
583
  };
646
584
  AirgapInstances.displayName = "AirgapInstances";
647
- /*! Bundled license information:
648
-
649
- classnames/index.js:
650
- (*!
651
- Copyright (c) 2018 Jed Watson.
652
- Licensed under the MIT License (MIT), see
653
- http://jedwatson.github.io/classnames
654
- *)
655
- */
656
585
 
657
586
  exports.AirgapInstances = AirgapInstances;
658
587
  //# sourceMappingURL=airgap-instances.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../node_modules/classnames/index.js","../src/actions/instances.ts","../src/components/app-status-badge.tsx","../src/components/instance-card.tsx","../src/components/airgap-instances.tsx"],"names":["exports","jsx","jsxs","cx","useState","Link","Fragment"],"mappings":"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","file":"airgap-instances.js","sourcesContent":["/*!\n\tCopyright (c) 2018 Jed Watson.\n\tLicensed under the MIT License (MIT), see\n\thttp://jedwatson.github.io/classnames\n*/\n/* global define */\n\n(function () {\n\t'use strict';\n\n\tvar hasOwn = {}.hasOwnProperty;\n\n\tfunction classNames () {\n\t\tvar classes = '';\n\n\t\tfor (var i = 0; i < arguments.length; i++) {\n\t\t\tvar arg = arguments[i];\n\t\t\tif (arg) {\n\t\t\t\tclasses = appendClass(classes, parseValue(arg));\n\t\t\t}\n\t\t}\n\n\t\treturn classes;\n\t}\n\n\tfunction parseValue (arg) {\n\t\tif (typeof arg === 'string' || typeof arg === 'number') {\n\t\t\treturn arg;\n\t\t}\n\n\t\tif (typeof arg !== 'object') {\n\t\t\treturn '';\n\t\t}\n\n\t\tif (Array.isArray(arg)) {\n\t\t\treturn classNames.apply(null, arg);\n\t\t}\n\n\t\tif (arg.toString !== Object.prototype.toString && !arg.toString.toString().includes('[native code]')) {\n\t\t\treturn arg.toString();\n\t\t}\n\n\t\tvar classes = '';\n\n\t\tfor (var key in arg) {\n\t\t\tif (hasOwn.call(arg, key) && arg[key]) {\n\t\t\t\tclasses = appendClass(classes, key);\n\t\t\t}\n\t\t}\n\n\t\treturn classes;\n\t}\n\n\tfunction appendClass (value, newClass) {\n\t\tif (!newClass) {\n\t\t\treturn value;\n\t\t}\n\t\n\t\tif (value) {\n\t\t\treturn value + ' ' + newClass;\n\t\t}\n\t\n\t\treturn value + newClass;\n\t}\n\n\tif (typeof module !== 'undefined' && module.exports) {\n\t\tclassNames.default = classNames;\n\t\tmodule.exports = classNames;\n\t} else if (typeof define === 'function' && typeof define.amd === 'object' && define.amd) {\n\t\t// register as 'classnames', consistent with npm package name\n\t\tdefine('classnames', [], function () {\n\t\t\treturn classNames;\n\t\t});\n\t} else {\n\t\twindow.classNames = classNames;\n\t}\n}());\n","/**\n * Instance-related server actions for the Update page.\n * \n * These actions handle fetching and managing instances including:\n * - Listing all instances (online and airgap)\n * - Fetching install options for instances\n * - Calculating update availability\n */\n\nimport { authenticatedFetch } from \"../utils/api-client\";\nimport { getApiOrigin, getCustomerIdFromToken } from \"./index\";\nimport type { PortalActionContext } from \"./index\";\nimport type { ChannelRelease, InstallOptions } from \"./install\";\nexport type { InstallOptions } from \"./install\";\n\n// =============================================================================\n// Types - Instance\n// =============================================================================\n\nexport type ResourceStatus = \"ready\" | \"updating\" | \"unknown\" | \"missing\" | \"unavailable\" | \"degraded\";\n\nexport interface ResourceState {\n kind: string;\n name: string;\n namespace: string;\n state: ResourceStatus;\n}\n\nexport interface InstanceTag {\n key: string;\n value: string;\n origin?: string;\n}\n\nexport interface Instance {\n id: string;\n firstCheckin: string;\n firstReadyAt?: string;\n lastCheckin: string;\n isAirgap: boolean;\n appStatus?: string;\n resourceStates?: ResourceState[];\n versionLabel?: string;\n channelId?: string;\n channelSequence?: number;\n serviceAccountId?: string;\n embeddedClusterId?: string;\n kotsInstallId?: string;\n kurlInstallId?: string;\n k8sVersion?: string;\n k8sDistribution?: string;\n tags?: InstanceTag[];\n}\n\nexport interface FetchInstancesInput {\n token: string;\n}\n\nexport interface FetchInstancesResult {\n instances: Instance[];\n online: Instance[];\n airgap: Instance[];\n}\n\n// =============================================================================\n// Types - Install Options by Instance\n// =============================================================================\n\nexport interface FetchInstallOptionsByInstanceIdsInput {\n token: string;\n instanceIds: string[];\n}\n\nexport interface FetchInstallOptionsByInstanceIdsResult {\n installOptions: InstallOptions[];\n}\n\n// =============================================================================\n// Constants\n// =============================================================================\n\n/** 24 hours in milliseconds - threshold for active vs inactive instances */\nexport const ACTIVE_INSTANCE_THRESHOLD_MS = 24 * 60 * 60 * 1000;\n\n// =============================================================================\n// Helper Functions\n// =============================================================================\n\n/**\n * Determines if an instance is active based on last check-in time.\n * Active = last check-in within the last 24 hours.\n */\nexport function isInstanceActive(instance: Instance): boolean {\n if (!instance.lastCheckin) {\n return false;\n }\n const lastCheckinTime = new Date(instance.lastCheckin).getTime();\n const threshold = Date.now() - ACTIVE_INSTANCE_THRESHOLD_MS;\n return lastCheckinTime > threshold;\n}\n\n/**\n * Filters instances into active and inactive lists.\n */\nexport function filterActiveInactiveInstances(instances: Instance[]): {\n activeInstances: Instance[];\n inactiveInstances: Instance[];\n} {\n const activeInstances = instances.filter(isInstanceActive);\n const inactiveInstances = instances.filter((instance) => !isInstanceActive(instance));\n return { activeInstances, inactiveInstances };\n}\n\n/**\n * Gets the install type for an instance based on instance data and install options.\n */\nexport function getInstallType(\n instance: Instance,\n installOptions?: InstallOptions[]\n): \"helm\" | \"embedded cluster\" | null {\n const instanceHasNoInstallTypes =\n !instance.kotsInstallId &&\n !instance.kurlInstallId &&\n !instance.embeddedClusterId;\n const isManuallyCreatedAirgapInstance =\n instance.isAirgap && instanceHasNoInstallTypes;\n\n // Check for embedded cluster first - explicitly set\n if (instance.embeddedClusterId) {\n return \"embedded cluster\";\n }\n\n // Check for helm install with k8s info\n if (instanceHasNoInstallTypes && instance.k8sDistribution) {\n return \"helm\";\n }\n\n // Default fallback for ambiguous cases (manually created airgap without k8s info):\n // Check install options first, then assume embedded cluster\n if (isManuallyCreatedAirgapInstance && !instance.k8sDistribution) {\n if (installOptions) {\n const matchingInstallOption = installOptions.find(\n (option) => option.instance_id === instance.id\n );\n if (matchingInstallOption) {\n if (matchingInstallOption.install_type === \"helm\") {\n return \"helm\";\n }\n if (matchingInstallOption.install_type === \"linux\") {\n return \"embedded cluster\";\n }\n }\n }\n // Final fallback: assume embedded cluster\n return \"embedded cluster\";\n }\n\n return null;\n}\n\n/**\n * Calculates the number of available updates for an instance.\n */\nexport function calculateUpdatesForInstance(\n instance: Instance,\n channelReleases: ChannelRelease[]\n): number {\n if (!channelReleases || !Array.isArray(channelReleases)) {\n return 0;\n }\n\n const instanceChannelSequence = instance.channelSequence ?? 0;\n\n // Filter to matching channel and count releases with higher sequence\n const matchingReleases = channelReleases.filter(\n (release) => release.channelId === instance.channelId\n );\n\n return matchingReleases.filter(\n (release) => release.channelSequence > instanceChannelSequence\n ).length;\n}\n\n/**\n * Gets the instance name from tags or returns truncated ID.\n */\nexport function getInstanceName(instance: Instance): string {\n const nameTag = instance.tags?.find((tag) => tag.key === \"name\");\n return nameTag?.value || instance.id.slice(0, 7);\n}\n\n// =============================================================================\n// Actions\n// =============================================================================\n\n/**\n * Fetches all instances for the customer.\n * Returns instances split into online and airgap categories.\n */\nexport async function fetchInstances(\n input: FetchInstancesInput,\n context?: PortalActionContext\n): Promise<FetchInstancesResult> {\n const { token } = input;\n\n if (!token || typeof token !== \"string\") {\n throw new Error(\"fetchInstances requires a session token\");\n }\n\n const customerId = getCustomerIdFromToken(token);\n const origin = getApiOrigin();\n\n // NEW: Use Enterprise Portal API endpoint (no customer_id needed)\n const url = new URL(`${origin}/enterprise-portal/instances`);\n\n if (process.env.NODE_ENV !== \"production\") {\n console.debug(\"[portal-components] fetching instances via %s\", url.toString());\n }\n\n const response = await authenticatedFetch(url.toString(), {\n method: \"GET\",\n token,\n headers: {\n accept: \"application/json\"\n },\n signal: context?.signal\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(\n `Instances request failed (${response.status} ${response.statusText}): ${errorText}`\n );\n }\n\n const payload = await response.json();\n // Extract from Enterprise Portal API envelope\n const data = payload.data;\n const allInstances: Instance[] = data?.instances || [];\n\n // Split into online and airgap\n const online = allInstances.filter((instance) => !instance.isAirgap);\n const airgap = allInstances.filter((instance) => instance.isAirgap);\n\n return {\n instances: allInstances,\n online,\n airgap\n };\n}\n\n// =============================================================================\n// Types - Create Air Gap Instance\n// =============================================================================\n\nexport type AirgapInstanceStatus = \"unavailable\" | \"missing\";\n\nexport interface CreateAirgapInstanceInput {\n token: string;\n serviceAccountId: string;\n instanceName?: string;\n channelId: string;\n channelSequence: number;\n k8sVersion?: string;\n k8sDistribution?: string;\n appStatus?: AirgapInstanceStatus;\n}\n\nexport interface CreateAirgapInstanceResult {\n instanceId: string;\n licenseId: string;\n createdAt: string;\n lastActive: string;\n appFirstReadyAt: string | null;\n appStatus: string;\n isAirgap: boolean;\n channelId?: string;\n channelSequence?: number;\n k8sVersion?: string;\n k8sDistribution?: string;\n}\n\n/**\n * Creates an air gap instance record manually.\n * This is used when customers want to track air gap instances that can't check in.\n */\nexport async function createAirgapInstance(\n input: CreateAirgapInstanceInput,\n context?: PortalActionContext\n): Promise<CreateAirgapInstanceResult> {\n const {\n token,\n serviceAccountId,\n instanceName,\n channelId,\n channelSequence,\n k8sVersion,\n k8sDistribution,\n appStatus = \"missing\"\n } = input;\n\n if (!token || typeof token !== \"string\") {\n throw new Error(\"createAirgapInstance requires a session token\");\n }\n\n if (!serviceAccountId?.trim()) {\n throw new Error(\"Service account ID is required\");\n }\n\n if (!channelId?.trim()) {\n throw new Error(\"Channel ID is required\");\n }\n\n const origin = getApiOrigin();\n\n // NEW: Use Enterprise Portal API endpoint (no customer_id needed)\n const url = new URL(`${origin}/enterprise-portal/instances/airgap`);\n\n const body: Record<string, unknown> = {\n service_account_id: serviceAccountId.trim(),\n channel_id: channelId.trim(),\n channel_sequence: channelSequence,\n app_status: appStatus\n };\n\n if (instanceName?.trim()) {\n body.instance_name = instanceName.trim();\n }\n if (k8sVersion?.trim()) {\n body.k8s_version = k8sVersion.trim();\n }\n if (k8sDistribution?.trim()) {\n body.k8s_distribution = k8sDistribution.trim();\n }\n\n if (process.env.NODE_ENV !== \"production\") {\n console.debug(\"[portal-components] creating airgap instance via %s\", url.toString());\n }\n\n const response = await authenticatedFetch(url.toString(), {\n method: \"POST\",\n token,\n headers: {\n \"content-type\": \"application/json\",\n accept: \"application/json\"\n },\n body: JSON.stringify(body),\n signal: context?.signal\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n let errorMessage = \"Failed to create airgap instance\";\n try {\n const errorData = JSON.parse(errorText);\n errorMessage = errorData.error || errorData.message || errorMessage;\n } catch {\n // Use default message\n }\n throw new Error(errorMessage);\n }\n\n const data = await response.json();\n \n return {\n instanceId: data.instance_id,\n licenseId: data.license_id,\n createdAt: data.created_at,\n lastActive: data.last_active,\n appFirstReadyAt: data.app_first_ready_at,\n appStatus: data.app_status,\n isAirgap: data.is_airgap,\n channelId: data.channel_id,\n channelSequence: data.channel_sequence,\n k8sVersion: data.k8s_version,\n k8sDistribution: data.k8s_distribution\n };\n}\n\n// =============================================================================\n// Types - Update Air Gap Instance\n// =============================================================================\n\nexport interface UpdateAirgapInstanceInput {\n token: string;\n instanceId: string;\n serviceAccountId?: string;\n channelId: string;\n channelSequence: number;\n}\n\nexport interface UpdateAirgapInstanceResult {\n success: boolean;\n}\n\n/**\n * Updates an air gap instance record (mark update complete).\n * This is called after completing an air gap update to update the instance version.\n */\nexport async function updateAirgapInstance(\n input: UpdateAirgapInstanceInput,\n context?: PortalActionContext\n): Promise<UpdateAirgapInstanceResult> {\n const {\n token,\n instanceId,\n serviceAccountId,\n channelId,\n channelSequence\n } = input;\n\n if (!token || typeof token !== \"string\") {\n throw new Error(\"updateAirgapInstance requires a session token\");\n }\n\n if (!instanceId?.trim()) {\n throw new Error(\"Instance ID is required\");\n }\n\n if (!channelId?.trim()) {\n throw new Error(\"Channel ID is required\");\n }\n\n const origin = getApiOrigin();\n\n // NEW: Use Enterprise Portal API endpoint (no customer_id needed)\n const url = new URL(`${origin}/enterprise-portal/instances/airgap/${instanceId.trim()}`);\n\n const body: Record<string, unknown> = {\n channel_id: channelId.trim(),\n channel_sequence: channelSequence\n };\n\n if (serviceAccountId?.trim()) {\n body.service_account_id = serviceAccountId.trim();\n }\n\n if (process.env.NODE_ENV !== \"production\") {\n console.debug(\"[portal-components] updating airgap instance via %s\", url.toString());\n }\n\n const response = await authenticatedFetch(url.toString(), {\n method: \"PATCH\",\n token,\n headers: {\n \"content-type\": \"application/json\",\n accept: \"application/json\"\n },\n body: JSON.stringify(body),\n signal: context?.signal\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n let errorMessage = \"Failed to update airgap instance\";\n try {\n const errorData = JSON.parse(errorText);\n errorMessage = errorData.error || errorData.message || errorMessage;\n } catch {\n // Use default message\n }\n throw new Error(errorMessage);\n }\n\n return { success: true };\n}\n\n/**\n * Fetches install options for a batch of instance IDs.\n * Handles chunking automatically if more than 50 IDs are provided.\n */\nexport async function fetchInstallOptionsByInstanceIds(\n input: FetchInstallOptionsByInstanceIdsInput,\n context?: PortalActionContext\n): Promise<FetchInstallOptionsByInstanceIdsResult> {\n const { token, instanceIds } = input;\n\n if (!token || typeof token !== \"string\") {\n throw new Error(\"fetchInstallOptionsByInstanceIds requires a session token\");\n }\n\n if (!instanceIds || instanceIds.length === 0) {\n return { installOptions: [] };\n }\n\n const customerId = getCustomerIdFromToken(token);\n const origin = getApiOrigin();\n\n // Chunk instance IDs into groups of 50 (API limit)\n const chunks: string[][] = [];\n for (let i = 0; i < instanceIds.length; i += 50) {\n chunks.push(instanceIds.slice(i, i + 50));\n }\n\n const allInstallOptions: InstallOptions[] = [];\n\n for (const chunk of chunks) {\n if (chunk.length === 0) continue;\n\n // Build query string with multiple instance_id params\n const queryParams = chunk\n .map((id) => `instance_id=${encodeURIComponent(id)}`)\n .join(\"&\");\n\n // NEW: Use Enterprise Portal API endpoint (no customer_id needed)\n const url = `${origin}/enterprise-portal/install-options?${queryParams}`;\n\n if (process.env.NODE_ENV !== \"production\") {\n console.debug(\"[portal-components] fetching install options via %s\", url);\n }\n\n const response = await authenticatedFetch(url, {\n method: \"GET\",\n token,\n headers: {\n accept: \"application/json\"\n },\n signal: context?.signal\n });\n\n if (response.status === 404) {\n // No install options found for this chunk - continue\n continue;\n }\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(\n `Install options request failed (${response.status} ${response.statusText}): ${errorText}`\n );\n }\n\n const envelope = await response.json();\n // Extract from Enterprise Portal API envelope\n const options: InstallOptions[] = envelope?.data?.install_options || [];\n allInstallOptions.push(...options);\n }\n\n return { installOptions: allInstallOptions };\n}\n\n","\"use client\";\n\nimport cx from \"classnames\";\n\ntype StatusType = \"success\" | \"pending\" | \"warn\" | \"error\" | \"disabled\" | \"info\";\n\nconst toTitle = (value: string) => {\n const s = value.trim();\n if (!s) return s;\n return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();\n};\n\nconst getStatusType = (status: string): StatusType => {\n switch (status.toUpperCase()) {\n case \"READY\":\n return \"success\";\n case \"UPDATING\":\n return \"pending\";\n case \"DEGRADED\":\n return \"warn\";\n case \"INACTIVE\":\n return \"disabled\";\n case \"UNAVAILABLE\":\n case \"MISSING\":\n return \"error\";\n default:\n return \"info\";\n }\n};\n\nconst getClassNameForType = (type: StatusType) => {\n switch (type) {\n case \"success\":\n return \"bg-green-50 text-green-600\";\n case \"pending\":\n return \"bg-blue-50 text-blue-600\";\n case \"warn\":\n return \"bg-yellow-50 text-yellow-600\";\n case \"error\":\n return \"bg-rose-50 text-rose-600\";\n case \"disabled\":\n return \"bg-gray-100 text-gray-600\";\n case \"info\":\n default:\n return \"bg-gray-50 text-gray-600\";\n }\n};\n\nconst Icon = ({ type }: { type: StatusType }) => {\n // Keep icons intentionally simple; the key is consistent mapping + capitalization.\n if (type === \"success\") {\n return (\n <svg className=\"h-3 w-3\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path\n fillRule=\"evenodd\"\n d=\"M16.704 5.293a1 1 0 010 1.414l-7.5 7.5a1 1 0 01-1.414 0l-3.5-3.5a1 1 0 011.414-1.414l2.793 2.793 6.793-6.793a1 1 0 011.414 0z\"\n clipRule=\"evenodd\"\n />\n </svg>\n );\n }\n if (type === \"warn\") {\n return (\n <svg className=\"h-3 w-3\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path\n fillRule=\"evenodd\"\n d=\"M8.257 3.099c.765-1.36 2.721-1.36 3.486 0l6.518 11.59c.75 1.334-.213 2.987-1.742 2.987H3.48c-1.53 0-2.492-1.653-1.743-2.987L8.257 3.1zM10 7a1 1 0 00-1 1v3a1 1 0 102 0V8a1 1 0 00-1-1zm0 8a1.25 1.25 0 100-2.5A1.25 1.25 0 0010 15z\"\n clipRule=\"evenodd\"\n />\n </svg>\n );\n }\n if (type === \"error\") {\n return (\n <svg className=\"h-3 w-3\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path\n fillRule=\"evenodd\"\n d=\"M10 18a8 8 0 100-16 8 8 0 000 16zm2.707-10.707a1 1 0 00-1.414-1.414L10 7.172 8.707 5.879a1 1 0 10-1.414 1.414L8.586 8.586l-1.293 1.293a1 1 0 101.414 1.414L10 10l1.293 1.293a1 1 0 001.414-1.414L11.414 8.586l1.293-1.293z\"\n clipRule=\"evenodd\"\n />\n </svg>\n );\n }\n if (type === \"disabled\") {\n return (\n <svg className=\"h-3 w-3\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M10 2a8 8 0 105.657 13.657L4.343 4.343A7.96 7.96 0 0010 2z\" />\n </svg>\n );\n }\n return (\n <svg className=\"h-3 w-3\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path\n fillRule=\"evenodd\"\n d=\"M18 10A8 8 0 11 2 10a8 8 0 0116 0zM9 9a1 1 0 112 0v5a1 1 0 11-2 0V9zm1-4a1.25 1.25 0 100 2.5A1.25 1.25 0 0010 5z\"\n clipRule=\"evenodd\"\n />\n </svg>\n );\n};\n\nconst Spinner = () => (\n <svg\n className=\"h-3 w-3 animate-spin\"\n viewBox=\"0 0 24 24\"\n aria-label=\"Updating\"\n >\n <circle\n className=\"opacity-25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n strokeWidth=\"4\"\n fill=\"none\"\n />\n <path\n className=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8v3a5 5 0 00-5 5H4z\"\n />\n </svg>\n);\n\nexport function AppStatusBadge({\n status,\n className\n}: {\n status?: string | null;\n className?: string;\n}) {\n const normalized = typeof status === \"string\" ? status.trim() : \"\";\n if (!normalized) return null;\n\n const type = getStatusType(normalized);\n const isUpdating = normalized.toLowerCase() === \"updating\";\n\n return (\n <span\n className={cx(\n \"inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-xs font-medium\",\n getClassNameForType(type),\n className\n )}\n >\n {isUpdating ? <Spinner /> : <Icon type={type} />}\n {toTitle(normalized)}\n </span>\n );\n}\n\n","\"use client\";\n\nimport { useState } from \"react\";\nimport Link from \"next/link\";\nimport { type Instance, type InstallOptions, getInstallType, getInstanceName, calculateUpdatesForInstance } from \"../actions/instances\";\nimport type { ChannelRelease } from \"../actions/install\";\nimport { AppStatusBadge } from \"./app-status-badge\";\nimport type { SecurityReleaseImage, GetSecurityInfoDiffResult } from \"../actions\";\n\ninterface InstanceCardProps {\n instance: Instance;\n isActive: boolean;\n channelReleases?: ChannelRelease[];\n installOptions?: InstallOptions[];\n onUpdateClick?: (instanceId: string) => void;\n primaryColor?: string;\n securityData?: Record<string, SecurityReleaseImage[]>;\n securityDiffData?: Record<string, GetSecurityInfoDiffResult>;\n securityEnabled?: boolean;\n}\n\ntype CVESeverity = \"critical\" | \"high\" | \"medium\" | \"low\";\n\n/**\n * Formats a date string to a human-readable format.\n */\nfunction formatDateTime(dateString?: string): string {\n if (!dateString) return \"N/A\";\n const date = new Date(dateString);\n return date.toLocaleDateString(\"en-US\", {\n month: \"numeric\",\n day: \"numeric\",\n year: \"numeric\",\n hour: \"numeric\",\n minute: \"2-digit\",\n hour12: true\n });\n}\n\n/**\n * Badge component for showing update count.\n */\nfunction UpdateBadge({ count }: { count: number }) {\n if (count <= 0) return null;\n return (\n <span className=\"ml-1.5 inline-flex h-5 w-5 items-center justify-center rounded-full bg-rose-500 text-xs font-medium text-white\">\n {count}\n </span>\n );\n}\n\n/**\n * Badge component for showing CVE counts by severity.\n */\nfunction CVEBadge({ severity, count }: { severity: CVESeverity; count: number }) {\n if (count === 0) return null;\n\n const severityColors: Record<CVESeverity, string> = {\n critical: \"bg-pink-50 text-pink-600\",\n high: \"bg-yellow-50 text-yellow-600\",\n medium: \"bg-blue-50 text-blue-400\",\n low: \"bg-green-50 text-green-500\"\n };\n\n const severityLabels: Record<CVESeverity, string> = {\n critical: \"C\",\n high: \"H\",\n medium: \"M\",\n low: \"L\"\n };\n\n const colorClass = severityColors[severity] || \"bg-gray-50 text-gray-600\";\n\n return (\n <span\n className={`inline-flex items-center gap-0.5 rounded-md px-2 py-0.5 text-xs font-medium ${colorClass}`}\n title={`${count} ${severity} CVE${count > 1 ? \"s\" : \"\"}`}\n >\n {count}\n {severityLabels[severity]}\n </span>\n );\n}\n\n/**\n * Instance card component for displaying an instance in the Update page.\n */\nexport function InstanceCard({\n instance,\n isActive,\n channelReleases = [],\n installOptions = [],\n onUpdateClick,\n primaryColor = \"#6366f1\",\n securityData = {},\n securityDiffData = {},\n securityEnabled = false\n}: InstanceCardProps) {\n const [showInstanceId, setShowInstanceId] = useState(false);\n const [showTooltip, setShowTooltip] = useState(false);\n \n const instanceName = getInstanceName(instance);\n const installType = getInstallType(instance, installOptions);\n const availableUpdates = calculateUpdatesForInstance(instance, channelReleases);\n\n // Get security info for this instance\n // Convert install type to match the key format used when storing security data\n // - \"embedded cluster\" -> \"linux\" (security API expects \"linux\")\n // - null (kots/kurl) -> \"linux\" (default used in update-client.tsx)\n // - \"helm\" -> \"helm\"\n let securityInstallType: \"helm\" | \"linux\" = \"linux\";\n if (installType === \"helm\") {\n securityInstallType = \"helm\";\n } else if (installType === \"embedded cluster\" || installType === null) {\n securityInstallType = \"linux\";\n }\n \n const securityInfoKey = instance.channelSequence\n ? `${instance.channelSequence}-${securityInstallType}-${instance.isAirgap}`\n : null;\n const securityInfo = securityInfoKey ? securityData[securityInfoKey] : undefined;\n const hasSecurityInfo = securityInfo && securityInfo.length > 0;\n\n // Calculate CVE summary\n const cveSummary: Record<CVESeverity, number> = hasSecurityInfo\n ? securityInfo.reduce(\n (acc, image) => {\n if (!image.security?.result) {\n return acc;\n }\n acc.critical += Object.keys(image.security.result.critical || {}).length;\n acc.high += Object.keys(image.security.result.high || {}).length;\n acc.medium += Object.keys(image.security.result.medium || {}).length;\n acc.low += Object.keys(image.security.result.low || {}).length;\n return acc;\n },\n { critical: 0, high: 0, medium: 0, low: 0 }\n )\n : { critical: 0, high: 0, medium: 0, low: 0 };\n\n const hasCVEs =\n cveSummary.critical > 0 ||\n cveSummary.high > 0 ||\n cveSummary.medium > 0 ||\n cveSummary.low > 0;\n\n // Get security diff for this instance to check for critical fixes\n const securityDiff = securityInfoKey ? securityDiffData[securityInfoKey] : undefined;\n const hasCriticalFixed = securityDiff?.images\n ? Object.entries(securityDiff.images).some(\n ([, image]) =>\n image.removed?.critical &&\n Object.keys(image.removed.critical).length > 0\n )\n : false;\n\n // Get the display text for the instance name/ID\n const displayText = showInstanceId \n ? instance.id.slice(0, 7)\n : instanceName || instance.id.slice(0, 7);\n\n // Get the tooltip text\n const getTooltipText = () => {\n if (instanceName) {\n return showInstanceId \n ? \"Click to show instance name\" \n : \"Click to show instance ID\";\n }\n return \"Instance ID\";\n };\n\n // Get overall resource status\n const getOverallStatus = () => {\n if (!instance.resourceStates || instance.resourceStates.length === 0) {\n return \"unknown\";\n }\n // Count states\n const stateCounts = instance.resourceStates.reduce((acc, resource) => {\n acc[resource.state] = (acc[resource.state] || 0) + 1;\n return acc;\n }, {} as Record<string, number>);\n \n // Return most common state\n let maxCount = 0;\n let maxStatus = \"unknown\";\n for (const [status, count] of Object.entries(stateCounts)) {\n if (count > maxCount) {\n maxCount = count;\n maxStatus = status;\n }\n }\n return maxStatus;\n };\n\n const overallStatus = getOverallStatus();\n // Prefer backend-provided appStatus (aligns with vendor portal customers page),\n // otherwise fall back to derived resource-state rollup.\n const statusForBadge = instance.appStatus || overallStatus;\n\n const handleUpdateClick = () => {\n if (onUpdateClick) {\n onUpdateClick(instance.id);\n }\n };\n\n return (\n <div className=\"rounded-lg border border-gray-200 bg-white p-4\">\n <div className=\"grid grid-cols-[auto_1fr_auto] gap-3\">\n {/* Status indicator dot */}\n <div\n className={`mt-1.5 h-2 w-2 rounded-full ${\n isActive ? \"bg-green-400\" : \"bg-gray-300\"\n }`}\n />\n\n {/* Instance details */}\n <div className=\"flex flex-col gap-3\">\n {/* Instance name, version, badges, status */}\n <div className=\"flex h-min flex-wrap items-center gap-3\">\n <div className=\"relative\">\n <span \n className=\"text-sm font-medium text-gray-900 cursor-pointer hover:underline\"\n onClick={() => setShowInstanceId(!showInstanceId)}\n onMouseEnter={() => setShowTooltip(true)}\n onMouseLeave={() => setShowTooltip(false)}\n title={showInstanceId ? instance.id : instanceName || instance.id}\n >\n {displayText}\n </span>\n {showTooltip && (\n <div className=\"absolute bottom-full left-1/2 -translate-x-1/2 mb-2 px-2 py-1 text-xs text-white bg-gray-800 rounded whitespace-nowrap z-10\">\n {getTooltipText()}\n <div className=\"absolute top-full left-1/2 -translate-x-1/2 border-4 border-transparent border-t-gray-800\" />\n </div>\n )}\n </div>\n <span className=\"flex items-center gap-1 text-xs text-gray-600\">\n Version: {instance.versionLabel || \"Unknown\"}\n <UpdateBadge count={availableUpdates} />\n </span>\n {instance.isAirgap && (\n <span className=\"rounded-full bg-indigo-100 px-2 py-0.5 text-xs font-medium text-indigo-700\">\n air gap\n </span>\n )}\n {installType && (\n <span className=\"rounded-full bg-gray-100 px-2 py-0.5 text-xs font-medium text-gray-600\">\n {installType}\n </span>\n )}\n {(instance.appStatus ||\n (instance.resourceStates && instance.resourceStates.length > 0)) && (\n <AppStatusBadge status={statusForBadge} />\n )}\n </div>\n\n {/* Check-in times */}\n <div className=\"flex flex-wrap gap-4\">\n <div className=\"flex items-center gap-1\">\n <span className=\"text-xs text-gray-500\">First check-in:</span>\n <span className=\"text-xs text-gray-600\">\n {formatDateTime(instance.firstCheckin)}\n </span>\n </div>\n <div className=\"flex items-center gap-1\">\n <span className=\"text-xs text-gray-500\">Last check-in:</span>\n <span className=\"text-xs text-gray-600\">\n {formatDateTime(instance.lastCheckin)}\n </span>\n </div>\n </div>\n\n {/* CVE Information */}\n {securityEnabled && hasSecurityInfo && (\n <div className=\"flex items-center gap-2\">\n <span className=\"text-xs text-gray-600\">CVEs:</span>\n {hasCVEs && (\n <div className=\"flex items-center gap-1\">\n <CVEBadge severity=\"critical\" count={cveSummary.critical} />\n <CVEBadge severity=\"high\" count={cveSummary.high} />\n <CVEBadge severity=\"medium\" count={cveSummary.medium} />\n <CVEBadge severity=\"low\" count={cveSummary.low} />\n <Link\n href={`/security?sequence=${instance.channelSequence}&installType=${securityInstallType}`}\n className=\"text-xs font-medium text-blue-500 hover:underline\"\n >\n details\n </Link>\n </div>\n )}\n {!hasCVEs && (\n <div className=\"inline-flex h-5 items-center justify-center\">\n <span className=\"text-xs text-gray-600\">None</span>\n </div>\n )}\n </div>\n )}\n\n {/* Tags (excluding name) */}\n {instance.tags && instance.tags.filter(tag => tag.key !== \"name\").length > 0 && (\n <div className=\"flex flex-wrap gap-2\">\n {instance.tags\n .filter(tag => tag.key !== \"name\")\n .map((tag) => (\n <span\n key={tag.key}\n className=\"rounded-md bg-gray-100 px-2 py-1 text-sm text-gray-600\"\n >\n {tag.value}\n </span>\n ))}\n </div>\n )}\n </div>\n\n {/* Update button */}\n {availableUpdates > 0 && (\n <div className=\"flex items-center\">\n <button\n onClick={handleUpdateClick}\n className=\"rounded px-4 py-1.5 text-sm font-medium text-white transition-opacity hover:opacity-90\"\n style={{ backgroundColor: hasCriticalFixed ? \"#dc2626\" : primaryColor }}\n >\n {hasCriticalFixed ? \"Critical update available\" : \"Update available\"}\n </button>\n </div>\n )}\n </div>\n </div>\n );\n}\n\nInstanceCard.displayName = \"InstanceCard\";\n\n","\"use client\";\n\nimport { useState } from \"react\";\nimport { type Instance, type InstallOptions, filterActiveInactiveInstances } from \"../actions/instances\";\nimport type { ChannelRelease } from \"../actions/install\";\nimport type { SecurityReleaseImage, GetSecurityInfoDiffResult } from \"../actions\";\nimport { InstanceCard } from \"./instance-card\";\n\ninterface AirgapInstancesProps {\n instances?: Instance[];\n channelReleases?: ChannelRelease[];\n installOptions?: InstallOptions[];\n onUpdateClick?: (instanceId: string) => void;\n /** Called when \"From a support bundle\" is clicked */\n onCreateFromBundleClick?: () => void;\n /** Called when \"Enter information manually\" is clicked */\n onCreateManuallyClick?: () => void;\n /** @deprecated Use onCreateFromBundleClick or onCreateManuallyClick instead */\n onCreateClick?: () => void;\n primaryColor?: string;\n securityData?: Record<string, SecurityReleaseImage[]>;\n securityDiffData?: Record<string, GetSecurityInfoDiffResult>;\n securityEnabled?: boolean;\n /** Whether to show the create button dropdown */\n showCreateButton?: boolean;\n}\n\nexport const AirgapInstances = ({\n instances = [],\n channelReleases = [],\n installOptions = [],\n onUpdateClick,\n onCreateFromBundleClick,\n onCreateManuallyClick,\n onCreateClick,\n primaryColor = \"#6366f1\",\n securityData = {},\n securityDiffData = {},\n securityEnabled = false,\n showCreateButton = true\n}: AirgapInstancesProps) => {\n const [showInactive, setShowInactive] = useState(true);\n const [isDropdownOpen, setIsDropdownOpen] = useState(false);\n\n // Filter active and inactive instances\n const { activeInstances, inactiveInstances } = filterActiveInactiveInstances(instances);\n\n const hasInactiveInstances = inactiveInstances.length > 0;\n\n const handleFromBundleClick = () => {\n setIsDropdownOpen(false);\n if (onCreateFromBundleClick) {\n onCreateFromBundleClick();\n } else if (onCreateClick) {\n // Fallback for backwards compatibility\n onCreateClick();\n }\n };\n\n const handleManuallyClick = () => {\n setIsDropdownOpen(false);\n if (onCreateManuallyClick) {\n onCreateManuallyClick();\n } else if (onCreateClick) {\n // Fallback for backwards compatibility\n onCreateClick();\n }\n };\n\n return (\n <div className=\"mt-6 space-y-6\">\n {/* Header */}\n <div className=\"flex items-center justify-between\">\n <h2 className=\"mb-0 flex items-center gap-4 text-xl font-semibold text-gray-900\">\n Air gap instances\n <span className=\"text-sm font-semibold text-emerald-500\">\n {activeInstances.length} active\n </span>\n <span className=\"text-sm font-semibold text-rose-400\">\n {inactiveInstances.length} inactive\n </span>\n </h2>\n {hasInactiveInstances && (\n <button\n onClick={() => setShowInactive(!showInactive)}\n className=\"mb-0 cursor-pointer text-sm font-medium text-blue-500 hover:underline\"\n >\n {showInactive ? \"Hide inactive instances\" : \"Show inactive instances\"}\n </button>\n )}\n </div>\n\n {/* Empty state */}\n {instances.length === 0 && (\n <div className=\"rounded-lg border border-gray-200 bg-white p-6\">\n <p className=\"mb-6 text-gray-600\">\n Air gap updates require an air gap instance record to determine install type, version etc.\n </p>\n </div>\n )}\n\n {/* Instance lists when there are instances */}\n {instances.length > 0 && (\n <div className=\"space-y-6\">\n {/* Active instances section */}\n <div className=\"space-y-4\">\n {activeInstances.length === 0 ? (\n <div className=\"rounded-lg border border-gray-200 bg-white p-4 text-sm text-gray-500\">\n No active instances found\n </div>\n ) : (\n <>\n <div className=\"flex items-center gap-2\">\n <h3 className=\"text-sm font-medium text-gray-800\">\n Active\n <span className=\"ml-2 text-xs font-medium text-gray-400\">\n last 24 hours\n </span>\n </h3>\n </div>\n <div className=\"space-y-4\">\n {activeInstances.map((instance) => (\n <InstanceCard\n key={instance.id}\n instance={instance}\n isActive={true}\n channelReleases={channelReleases}\n installOptions={installOptions}\n onUpdateClick={onUpdateClick}\n primaryColor={primaryColor}\n securityData={securityData}\n securityDiffData={securityDiffData}\n securityEnabled={securityEnabled}\n />\n ))}\n </div>\n </>\n )}\n </div>\n\n {/* Inactive instances section */}\n {showInactive && inactiveInstances.length > 0 && (\n <div className=\"space-y-4\">\n <div className=\"flex items-center gap-2\">\n <h3 className=\"text-sm font-medium text-gray-800\">Inactive</h3>\n </div>\n <div className=\"space-y-4\">\n {inactiveInstances.map((instance) => (\n <InstanceCard\n key={instance.id}\n instance={instance}\n isActive={false}\n channelReleases={channelReleases}\n installOptions={installOptions}\n onUpdateClick={onUpdateClick}\n primaryColor={primaryColor}\n securityData={securityData}\n securityDiffData={securityDiffData}\n securityEnabled={securityEnabled}\n />\n ))}\n </div>\n </div>\n )}\n </div>\n )}\n\n {/* Create instance button with dropdown */}\n {showCreateButton && (\n <div className=\"relative\">\n <button\n onClick={() => setIsDropdownOpen(!isDropdownOpen)}\n className=\"inline-flex items-center gap-2 rounded-md px-4 py-2 text-sm font-medium text-white hover:opacity-90\"\n style={{ backgroundColor: primaryColor }}\n >\n Create air gap instance record\n <svg\n className={`h-4 w-4 transition-transform ${isDropdownOpen ? \"rotate-180\" : \"\"}`}\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M19 9l-7 7-7-7\" />\n </svg>\n </button>\n\n {isDropdownOpen && (\n <div className=\"absolute left-0 z-10 mt-2 w-72 rounded-md border border-gray-200 bg-white shadow-lg\">\n <button\n onClick={handleFromBundleClick}\n className=\"flex w-full items-center gap-2 px-4 py-2 text-left text-sm text-gray-700 hover:bg-gray-50\"\n >\n <svg className=\"h-4 w-4 text-gray-500\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4\" />\n </svg>\n From a support bundle\n </button>\n <button\n onClick={handleManuallyClick}\n className=\"flex w-full items-center gap-2 px-4 py-2 text-left text-sm text-gray-700 hover:bg-gray-50\"\n >\n <svg className=\"h-4 w-4 text-gray-500\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z\" />\n </svg>\n Enter information manually\n </button>\n </div>\n )}\n </div>\n )}\n </div>\n );\n};\n\nAirgapInstances.displayName = \"AirgapInstances\";\n\n"]}
1
+ {"version":3,"sources":["../src/utils/constants.ts","../src/utils/format.ts","../src/actions/instances.ts","../src/components/app-status-badge.tsx","../src/components/instance-card.tsx","../src/components/airgap-instances.tsx"],"names":["jsx","jsxs","cx","useState","Link","Fragment"],"mappings":"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","file":"airgap-instances.js","sourcesContent":["/**\n * Default globe favicon matching the CiGlobe icon (Circum Icons)\n * Used as fallback when custom branding doesn't provide a favicon\n */\nexport const DEFAULT_FAVICON = \"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%239ca3af' stroke-width='1.5'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cellipse cx='12' cy='12' rx='4' ry='10'/%3E%3Cpath d='M2 12h20'/%3E%3C/svg%3E\";\n\n/**\n * Default primary brand color\n */\nexport const DEFAULT_PRIMARY_COLOR = \"#4f46e5\";\n\n/**\n * Default secondary brand color\n */\nexport const DEFAULT_SECONDARY_COLOR = \"#6366f1\";\n\n/**\n * Check if the API origin is HTTP (used for repldev environments)\n * This determines cookie security settings for cross-origin iframes\n * @returns true if the API origin starts with http:// (not https://)\n */\nexport const isHttpApiOrigin = (): boolean => {\n return process.env.REPLICATED_APP_ORIGIN?.startsWith('http://') || false;\n};\n","/**\n * Format bytes to human-readable string\n * @param bytes - Number of bytes\n * @param decimals - Number of decimal places (default: 1)\n * @returns Formatted string (e.g., \"1.5 MB\")\n */\nexport function formatBytes(bytes: number, decimals = 1): string {\n if (bytes === 0) return \"0 Bytes\";\n\n const k = 1024;\n const dm = decimals < 0 ? 0 : decimals;\n const sizes = [\"Bytes\", \"KB\", \"MB\", \"GB\", \"TB\"];\n\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n\n return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;\n}\n\n/**\n * Format date string to short numeric date (no time)\n * @param dateString - ISO date string or null/undefined\n * @returns Formatted string (e.g., \"01/27/2026\") or \"Never\" if no date\n */\nexport function formatDateShort(dateString?: string | null): string {\n if (!dateString) return \"Never\";\n const date = new Date(dateString);\n if (isNaN(date.getTime())) {\n return dateString;\n }\n return date.toLocaleDateString(\"en-US\", {\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\"\n });\n}\n\n/**\n * Format date string to human-readable local timestamp\n * @param dateString - ISO date string or null/undefined\n * @returns Formatted string (e.g., \"Jan 27, 2026, 10:32 PM\") or \"Never\" if no date\n */\nexport function formatDate(dateString?: string | null): string {\n if (!dateString) return \"Never\";\n const date = new Date(dateString);\n if (isNaN(date.getTime())) {\n return dateString;\n }\n return date.toLocaleDateString(\"en-US\", {\n month: \"short\",\n day: \"numeric\",\n year: \"numeric\",\n hour: \"numeric\",\n minute: \"2-digit\",\n hour12: true\n });\n}\n\n/**\n * Format date string to human-readable UTC timestamp\n * @param dateString - ISO date string\n * @returns Formatted string (e.g., \"01/27/2026, 22:32:39 UTC\")\n */\nexport function formatDateTime(dateString: string): string {\n const date = new Date(dateString);\n if (isNaN(date.getTime())) {\n return dateString;\n }\n return date.toLocaleString(\"en-US\", {\n timeZone: \"UTC\",\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\",\n hour12: false\n }) + \" UTC\";\n}\n\n/**\n * Format date string to user-friendly local timestamp with 12-hour time\n * @param dateString - ISO date string or null/undefined\n * @returns Formatted string (e.g., \"1/27/2026, 10:32 PM\") or \"N/A\" if not provided\n */\nexport function formatDateTimeLocal(dateString?: string | null): string {\n if (!dateString) return \"N/A\";\n\n try {\n const date = new Date(dateString);\n if (isNaN(date.getTime())) {\n return dateString;\n }\n return date.toLocaleDateString(\"en-US\", {\n month: \"numeric\",\n day: \"numeric\",\n year: \"numeric\",\n hour: \"numeric\",\n minute: \"2-digit\",\n hour12: true\n });\n } catch {\n return \"N/A\";\n }\n}\n","/**\n * Instance-related server actions for the Update page.\n * \n * These actions handle fetching and managing instances including:\n * - Listing all instances (online and airgap)\n * - Fetching install options for instances\n * - Calculating update availability\n */\n\nimport { authenticatedFetch } from \"../utils/api-client\";\nimport { getApiOrigin, getCustomerIdFromToken } from \"./index\";\nimport type { PortalActionContext } from \"./index\";\nimport type { ChannelRelease, InstallOptions } from \"./install\";\nexport type { InstallOptions } from \"./install\";\n\n// =============================================================================\n// Types - Instance\n// =============================================================================\n\nexport type ResourceStatus = \"ready\" | \"updating\" | \"unknown\" | \"missing\" | \"unavailable\" | \"degraded\";\n\nexport interface ResourceState {\n kind: string;\n name: string;\n namespace: string;\n state: ResourceStatus;\n}\n\nexport interface InstanceTag {\n key: string;\n value: string;\n origin?: string;\n}\n\nexport interface Instance {\n id: string;\n firstCheckin: string;\n firstReadyAt?: string;\n lastCheckin: string;\n isAirgap: boolean;\n appStatus?: string;\n resourceStates?: ResourceState[];\n versionLabel?: string;\n channelId?: string;\n channelSequence?: number;\n serviceAccountId?: string;\n embeddedClusterId?: string;\n kotsInstallId?: string;\n kurlInstallId?: string;\n k8sVersion?: string;\n k8sDistribution?: string;\n tags?: InstanceTag[];\n}\n\nexport interface FetchInstancesInput {\n token: string;\n}\n\nexport interface FetchInstancesResult {\n instances: Instance[];\n online: Instance[];\n airgap: Instance[];\n}\n\n// =============================================================================\n// Types - Install Options by Instance\n// =============================================================================\n\nexport interface FetchInstallOptionsByInstanceIdsInput {\n token: string;\n instanceIds: string[];\n}\n\nexport interface FetchInstallOptionsByInstanceIdsResult {\n installOptions: InstallOptions[];\n}\n\n// =============================================================================\n// Constants\n// =============================================================================\n\n/** 24 hours in milliseconds - threshold for active vs inactive instances */\nexport const ACTIVE_INSTANCE_THRESHOLD_MS = 24 * 60 * 60 * 1000;\n\n// =============================================================================\n// Helper Functions\n// =============================================================================\n\n/**\n * Determines if an instance is active based on last check-in time.\n * Active = last check-in within the last 24 hours.\n */\nexport function isInstanceActive(instance: Instance): boolean {\n if (!instance.lastCheckin) {\n return false;\n }\n const lastCheckinTime = new Date(instance.lastCheckin).getTime();\n const threshold = Date.now() - ACTIVE_INSTANCE_THRESHOLD_MS;\n return lastCheckinTime > threshold;\n}\n\n/**\n * Filters instances into active and inactive lists.\n */\nexport function filterActiveInactiveInstances(instances: Instance[]): {\n activeInstances: Instance[];\n inactiveInstances: Instance[];\n} {\n const activeInstances = instances.filter(isInstanceActive);\n const inactiveInstances = instances.filter((instance) => !isInstanceActive(instance));\n return { activeInstances, inactiveInstances };\n}\n\n/**\n * Gets the install type for an instance based on instance data and install options.\n */\nexport function getInstallType(\n instance: Instance,\n installOptions?: InstallOptions[]\n): \"helm\" | \"embedded cluster\" | null {\n const instanceHasNoInstallTypes =\n !instance.kotsInstallId &&\n !instance.kurlInstallId &&\n !instance.embeddedClusterId;\n const isManuallyCreatedAirgapInstance =\n instance.isAirgap && instanceHasNoInstallTypes;\n\n // Check for embedded cluster first - explicitly set\n if (instance.embeddedClusterId) {\n return \"embedded cluster\";\n }\n\n // Check for helm install with k8s info\n if (instanceHasNoInstallTypes && instance.k8sDistribution) {\n return \"helm\";\n }\n\n // Default fallback for ambiguous cases (manually created airgap without k8s info):\n // Check install options first, then assume embedded cluster\n if (isManuallyCreatedAirgapInstance && !instance.k8sDistribution) {\n if (installOptions) {\n const matchingInstallOption = installOptions.find(\n (option) => option.instance_id === instance.id\n );\n if (matchingInstallOption) {\n if (matchingInstallOption.install_type === \"helm\") {\n return \"helm\";\n }\n if (matchingInstallOption.install_type === \"linux\") {\n return \"embedded cluster\";\n }\n }\n }\n // Final fallback: assume embedded cluster\n return \"embedded cluster\";\n }\n\n return null;\n}\n\n/**\n * Calculates the number of available updates for an instance.\n */\nexport function calculateUpdatesForInstance(\n instance: Instance,\n channelReleases: ChannelRelease[]\n): number {\n if (!channelReleases || !Array.isArray(channelReleases)) {\n return 0;\n }\n\n const instanceChannelSequence = instance.channelSequence ?? 0;\n\n // Filter to matching channel and count releases with higher sequence\n const matchingReleases = channelReleases.filter(\n (release) => release.channelId === instance.channelId\n );\n\n return matchingReleases.filter(\n (release) => release.channelSequence > instanceChannelSequence\n ).length;\n}\n\n/**\n * Gets the instance name from tags or returns truncated ID.\n */\nexport function getInstanceName(instance: Instance): string {\n const nameTag = instance.tags?.find((tag) => tag.key === \"name\");\n return nameTag?.value || instance.id.slice(0, 7);\n}\n\n// =============================================================================\n// Actions\n// =============================================================================\n\n/**\n * Fetches all instances for the customer.\n * Returns instances split into online and airgap categories.\n */\nexport async function fetchInstances(\n input: FetchInstancesInput,\n context?: PortalActionContext\n): Promise<FetchInstancesResult> {\n const { token } = input;\n\n if (!token || typeof token !== \"string\") {\n throw new Error(\"fetchInstances requires a session token\");\n }\n\n const customerId = getCustomerIdFromToken(token);\n const origin = getApiOrigin();\n\n // NEW: Use Enterprise Portal API endpoint (no customer_id needed)\n const url = new URL(`${origin}/enterprise-portal/instances`);\n\n if (process.env.NODE_ENV !== \"production\") {\n console.debug(\"[portal-components] fetching instances via %s\", url.toString());\n }\n\n const response = await authenticatedFetch(url.toString(), {\n method: \"GET\",\n token,\n headers: {\n accept: \"application/json\"\n },\n signal: context?.signal\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(\n `Instances request failed (${response.status} ${response.statusText}): ${errorText}`\n );\n }\n\n const payload = await response.json();\n // Extract from Enterprise Portal API envelope\n const data = payload.data;\n const allInstances: Instance[] = data?.instances || [];\n\n // Split into online and airgap\n const online = allInstances.filter((instance) => !instance.isAirgap);\n const airgap = allInstances.filter((instance) => instance.isAirgap);\n\n return {\n instances: allInstances,\n online,\n airgap\n };\n}\n\n// =============================================================================\n// Types - Create Air Gap Instance\n// =============================================================================\n\nexport type AirgapInstanceStatus = \"unavailable\" | \"missing\";\n\nexport interface CreateAirgapInstanceInput {\n token: string;\n serviceAccountId: string;\n instanceName?: string;\n channelId: string;\n channelSequence: number;\n k8sVersion?: string;\n k8sDistribution?: string;\n appStatus?: AirgapInstanceStatus;\n}\n\nexport interface CreateAirgapInstanceResult {\n instanceId: string;\n licenseId: string;\n createdAt: string;\n lastActive: string;\n appFirstReadyAt: string | null;\n appStatus: string;\n isAirgap: boolean;\n channelId?: string;\n channelSequence?: number;\n k8sVersion?: string;\n k8sDistribution?: string;\n}\n\n/**\n * Creates an air gap instance record manually.\n * This is used when customers want to track air gap instances that can't check in.\n */\nexport async function createAirgapInstance(\n input: CreateAirgapInstanceInput,\n context?: PortalActionContext\n): Promise<CreateAirgapInstanceResult> {\n const {\n token,\n serviceAccountId,\n instanceName,\n channelId,\n channelSequence,\n k8sVersion,\n k8sDistribution,\n appStatus = \"missing\"\n } = input;\n\n if (!token || typeof token !== \"string\") {\n throw new Error(\"createAirgapInstance requires a session token\");\n }\n\n if (!serviceAccountId?.trim()) {\n throw new Error(\"Service account ID is required\");\n }\n\n if (!channelId?.trim()) {\n throw new Error(\"Channel ID is required\");\n }\n\n const origin = getApiOrigin();\n\n // NEW: Use Enterprise Portal API endpoint (no customer_id needed)\n const url = new URL(`${origin}/enterprise-portal/instances/airgap`);\n\n const body: Record<string, unknown> = {\n service_account_id: serviceAccountId.trim(),\n channel_id: channelId.trim(),\n channel_sequence: channelSequence,\n app_status: appStatus\n };\n\n if (instanceName?.trim()) {\n body.instance_name = instanceName.trim();\n }\n if (k8sVersion?.trim()) {\n body.k8s_version = k8sVersion.trim();\n }\n if (k8sDistribution?.trim()) {\n body.k8s_distribution = k8sDistribution.trim();\n }\n\n if (process.env.NODE_ENV !== \"production\") {\n console.debug(\"[portal-components] creating airgap instance via %s\", url.toString());\n }\n\n const response = await authenticatedFetch(url.toString(), {\n method: \"POST\",\n token,\n headers: {\n \"content-type\": \"application/json\",\n accept: \"application/json\"\n },\n body: JSON.stringify(body),\n signal: context?.signal\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n let errorMessage = \"Failed to create airgap instance\";\n try {\n const errorData = JSON.parse(errorText);\n errorMessage = errorData.error || errorData.message || errorMessage;\n } catch {\n // Use default message\n }\n throw new Error(errorMessage);\n }\n\n const data = await response.json();\n \n return {\n instanceId: data.instance_id,\n licenseId: data.license_id,\n createdAt: data.created_at,\n lastActive: data.last_active,\n appFirstReadyAt: data.app_first_ready_at,\n appStatus: data.app_status,\n isAirgap: data.is_airgap,\n channelId: data.channel_id,\n channelSequence: data.channel_sequence,\n k8sVersion: data.k8s_version,\n k8sDistribution: data.k8s_distribution\n };\n}\n\n// =============================================================================\n// Types - Update Air Gap Instance\n// =============================================================================\n\nexport interface UpdateAirgapInstanceInput {\n token: string;\n instanceId: string;\n serviceAccountId?: string;\n channelId: string;\n channelSequence: number;\n}\n\nexport interface UpdateAirgapInstanceResult {\n success: boolean;\n}\n\n/**\n * Updates an air gap instance record (mark update complete).\n * This is called after completing an air gap update to update the instance version.\n */\nexport async function updateAirgapInstance(\n input: UpdateAirgapInstanceInput,\n context?: PortalActionContext\n): Promise<UpdateAirgapInstanceResult> {\n const {\n token,\n instanceId,\n serviceAccountId,\n channelId,\n channelSequence\n } = input;\n\n if (!token || typeof token !== \"string\") {\n throw new Error(\"updateAirgapInstance requires a session token\");\n }\n\n if (!instanceId?.trim()) {\n throw new Error(\"Instance ID is required\");\n }\n\n if (!channelId?.trim()) {\n throw new Error(\"Channel ID is required\");\n }\n\n const origin = getApiOrigin();\n\n // NEW: Use Enterprise Portal API endpoint (no customer_id needed)\n const url = new URL(`${origin}/enterprise-portal/instances/airgap/${instanceId.trim()}`);\n\n const body: Record<string, unknown> = {\n channel_id: channelId.trim(),\n channel_sequence: channelSequence\n };\n\n if (serviceAccountId?.trim()) {\n body.service_account_id = serviceAccountId.trim();\n }\n\n if (process.env.NODE_ENV !== \"production\") {\n console.debug(\"[portal-components] updating airgap instance via %s\", url.toString());\n }\n\n const response = await authenticatedFetch(url.toString(), {\n method: \"PATCH\",\n token,\n headers: {\n \"content-type\": \"application/json\",\n accept: \"application/json\"\n },\n body: JSON.stringify(body),\n signal: context?.signal\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n let errorMessage = \"Failed to update airgap instance\";\n try {\n const errorData = JSON.parse(errorText);\n errorMessage = errorData.error || errorData.message || errorMessage;\n } catch {\n // Use default message\n }\n throw new Error(errorMessage);\n }\n\n return { success: true };\n}\n\n/**\n * Fetches install options for a batch of instance IDs.\n * Handles chunking automatically if more than 50 IDs are provided.\n */\nexport async function fetchInstallOptionsByInstanceIds(\n input: FetchInstallOptionsByInstanceIdsInput,\n context?: PortalActionContext\n): Promise<FetchInstallOptionsByInstanceIdsResult> {\n const { token, instanceIds } = input;\n\n if (!token || typeof token !== \"string\") {\n throw new Error(\"fetchInstallOptionsByInstanceIds requires a session token\");\n }\n\n if (!instanceIds || instanceIds.length === 0) {\n return { installOptions: [] };\n }\n\n const customerId = getCustomerIdFromToken(token);\n const origin = getApiOrigin();\n\n // Chunk instance IDs into groups of 50 (API limit)\n const chunks: string[][] = [];\n for (let i = 0; i < instanceIds.length; i += 50) {\n chunks.push(instanceIds.slice(i, i + 50));\n }\n\n const allInstallOptions: InstallOptions[] = [];\n\n for (const chunk of chunks) {\n if (chunk.length === 0) continue;\n\n // Build query string with multiple instance_id params\n const queryParams = chunk\n .map((id) => `instance_id=${encodeURIComponent(id)}`)\n .join(\"&\");\n\n // NEW: Use Enterprise Portal API endpoint (no customer_id needed)\n const url = `${origin}/enterprise-portal/install-options?${queryParams}`;\n\n if (process.env.NODE_ENV !== \"production\") {\n console.debug(\"[portal-components] fetching install options via %s\", url);\n }\n\n const response = await authenticatedFetch(url, {\n method: \"GET\",\n token,\n headers: {\n accept: \"application/json\"\n },\n signal: context?.signal\n });\n\n if (response.status === 404) {\n // No install options found for this chunk - continue\n continue;\n }\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(\n `Install options request failed (${response.status} ${response.statusText}): ${errorText}`\n );\n }\n\n const envelope = await response.json();\n // Extract from Enterprise Portal API envelope\n const options: InstallOptions[] = envelope?.data?.install_options || [];\n allInstallOptions.push(...options);\n }\n\n return { installOptions: allInstallOptions };\n}\n\n","\"use client\";\n\nimport cx from \"classnames\";\n\ntype StatusType = \"success\" | \"pending\" | \"warn\" | \"error\" | \"disabled\" | \"info\";\n\nconst toTitle = (value: string) => {\n const s = value.trim();\n if (!s) return s;\n return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();\n};\n\nconst getStatusType = (status: string): StatusType => {\n switch (status.toUpperCase()) {\n case \"READY\":\n return \"success\";\n case \"UPDATING\":\n return \"pending\";\n case \"DEGRADED\":\n return \"warn\";\n case \"INACTIVE\":\n return \"disabled\";\n case \"UNAVAILABLE\":\n case \"MISSING\":\n return \"error\";\n default:\n return \"info\";\n }\n};\n\nconst getClassNameForType = (type: StatusType) => {\n switch (type) {\n case \"success\":\n return \"bg-green-50 text-green-600\";\n case \"pending\":\n return \"bg-blue-50 text-blue-600\";\n case \"warn\":\n return \"bg-yellow-50 text-yellow-600\";\n case \"error\":\n return \"bg-rose-50 text-rose-600\";\n case \"disabled\":\n return \"bg-gray-100 text-gray-600\";\n case \"info\":\n default:\n return \"bg-gray-50 text-gray-600\";\n }\n};\n\nconst Icon = ({ type }: { type: StatusType }) => {\n // Keep icons intentionally simple; the key is consistent mapping + capitalization.\n if (type === \"success\") {\n return (\n <svg className=\"h-3 w-3\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path\n fillRule=\"evenodd\"\n d=\"M16.704 5.293a1 1 0 010 1.414l-7.5 7.5a1 1 0 01-1.414 0l-3.5-3.5a1 1 0 011.414-1.414l2.793 2.793 6.793-6.793a1 1 0 011.414 0z\"\n clipRule=\"evenodd\"\n />\n </svg>\n );\n }\n if (type === \"warn\") {\n return (\n <svg className=\"h-3 w-3\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path\n fillRule=\"evenodd\"\n d=\"M8.257 3.099c.765-1.36 2.721-1.36 3.486 0l6.518 11.59c.75 1.334-.213 2.987-1.742 2.987H3.48c-1.53 0-2.492-1.653-1.743-2.987L8.257 3.1zM10 7a1 1 0 00-1 1v3a1 1 0 102 0V8a1 1 0 00-1-1zm0 8a1.25 1.25 0 100-2.5A1.25 1.25 0 0010 15z\"\n clipRule=\"evenodd\"\n />\n </svg>\n );\n }\n if (type === \"error\") {\n return (\n <svg className=\"h-3 w-3\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path\n fillRule=\"evenodd\"\n d=\"M10 18a8 8 0 100-16 8 8 0 000 16zm2.707-10.707a1 1 0 00-1.414-1.414L10 7.172 8.707 5.879a1 1 0 10-1.414 1.414L8.586 8.586l-1.293 1.293a1 1 0 101.414 1.414L10 10l1.293 1.293a1 1 0 001.414-1.414L11.414 8.586l1.293-1.293z\"\n clipRule=\"evenodd\"\n />\n </svg>\n );\n }\n if (type === \"disabled\") {\n return (\n <svg className=\"h-3 w-3\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M10 2a8 8 0 105.657 13.657L4.343 4.343A7.96 7.96 0 0010 2z\" />\n </svg>\n );\n }\n return (\n <svg className=\"h-3 w-3\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path\n fillRule=\"evenodd\"\n d=\"M18 10A8 8 0 11 2 10a8 8 0 0116 0zM9 9a1 1 0 112 0v5a1 1 0 11-2 0V9zm1-4a1.25 1.25 0 100 2.5A1.25 1.25 0 0010 5z\"\n clipRule=\"evenodd\"\n />\n </svg>\n );\n};\n\nconst Spinner = () => (\n <svg\n className=\"h-3 w-3 animate-spin\"\n viewBox=\"0 0 24 24\"\n aria-label=\"Updating\"\n >\n <circle\n className=\"opacity-25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n strokeWidth=\"4\"\n fill=\"none\"\n />\n <path\n className=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8v3a5 5 0 00-5 5H4z\"\n />\n </svg>\n);\n\nexport function AppStatusBadge({\n status,\n className\n}: {\n status?: string | null;\n className?: string;\n}) {\n const normalized = typeof status === \"string\" ? status.trim() : \"\";\n if (!normalized) return null;\n\n const type = getStatusType(normalized);\n const isUpdating = normalized.toLowerCase() === \"updating\";\n\n return (\n <span\n className={cx(\n \"inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-xs font-medium\",\n getClassNameForType(type),\n className\n )}\n >\n {isUpdating ? <Spinner /> : <Icon type={type} />}\n {toTitle(normalized)}\n </span>\n );\n}\n\n","\"use client\";\n\nimport { useState } from \"react\";\nimport Link from \"next/link\";\nimport { type Instance, type InstallOptions, getInstallType, getInstanceName, calculateUpdatesForInstance } from \"../actions/instances\";\nimport type { ChannelRelease } from \"../actions/install\";\nimport { AppStatusBadge } from \"./app-status-badge\";\nimport type { SecurityReleaseImage, GetSecurityInfoDiffResult } from \"../actions\";\nimport { DEFAULT_SECONDARY_COLOR } from \"../utils/constants\";\nimport { formatDateTimeLocal } from \"../utils/format\";\n\ninterface InstanceCardProps {\n instance: Instance;\n isActive: boolean;\n channelReleases?: ChannelRelease[];\n installOptions?: InstallOptions[];\n onUpdateClick?: (instanceId: string) => void;\n primaryColor?: string;\n securityData?: Record<string, SecurityReleaseImage[]>;\n securityDiffData?: Record<string, GetSecurityInfoDiffResult>;\n securityEnabled?: boolean;\n}\n\ntype CVESeverity = \"critical\" | \"high\" | \"medium\" | \"low\";\n\n/**\n * Badge component for showing update count.\n */\nfunction UpdateBadge({ count }: { count: number }) {\n if (count <= 0) return null;\n return (\n <span className=\"ml-1.5 inline-flex h-5 w-5 items-center justify-center rounded-full bg-rose-500 text-xs font-medium text-white\">\n {count}\n </span>\n );\n}\n\n/**\n * Badge component for showing CVE counts by severity.\n */\nfunction CVEBadge({ severity, count }: { severity: CVESeverity; count: number }) {\n if (count === 0) return null;\n\n const severityColors: Record<CVESeverity, string> = {\n critical: \"bg-pink-50 text-pink-600\",\n high: \"bg-yellow-50 text-yellow-600\",\n medium: \"bg-blue-50 text-blue-400\",\n low: \"bg-green-50 text-green-500\"\n };\n\n const severityLabels: Record<CVESeverity, string> = {\n critical: \"C\",\n high: \"H\",\n medium: \"M\",\n low: \"L\"\n };\n\n const colorClass = severityColors[severity] || \"bg-gray-50 text-gray-600\";\n\n return (\n <span\n className={`inline-flex items-center gap-0.5 rounded-md px-2 py-0.5 text-xs font-medium ${colorClass}`}\n title={`${count} ${severity} CVE${count > 1 ? \"s\" : \"\"}`}\n >\n {count}\n {severityLabels[severity]}\n </span>\n );\n}\n\n/**\n * Instance card component for displaying an instance in the Update page.\n */\nexport function InstanceCard({\n instance,\n isActive,\n channelReleases = [],\n installOptions = [],\n onUpdateClick,\n primaryColor = DEFAULT_SECONDARY_COLOR,\n securityData = {},\n securityDiffData = {},\n securityEnabled = false\n}: InstanceCardProps) {\n const [showInstanceId, setShowInstanceId] = useState(false);\n const [showTooltip, setShowTooltip] = useState(false);\n \n const instanceName = getInstanceName(instance);\n const installType = getInstallType(instance, installOptions);\n const availableUpdates = calculateUpdatesForInstance(instance, channelReleases);\n\n // Get security info for this instance\n // Convert install type to match the key format used when storing security data\n // - \"embedded cluster\" -> \"linux\" (security API expects \"linux\")\n // - null (kots/kurl) -> \"linux\" (default used in update-client.tsx)\n // - \"helm\" -> \"helm\"\n let securityInstallType: \"helm\" | \"linux\" = \"linux\";\n if (installType === \"helm\") {\n securityInstallType = \"helm\";\n } else if (installType === \"embedded cluster\" || installType === null) {\n securityInstallType = \"linux\";\n }\n \n const securityInfoKey = instance.channelSequence\n ? `${instance.channelSequence}-${securityInstallType}-${instance.isAirgap}`\n : null;\n const securityInfo = securityInfoKey ? securityData[securityInfoKey] : undefined;\n const hasSecurityInfo = securityInfo && securityInfo.length > 0;\n\n // Calculate CVE summary\n const cveSummary: Record<CVESeverity, number> = hasSecurityInfo\n ? securityInfo.reduce(\n (acc, image) => {\n if (!image.security?.result) {\n return acc;\n }\n acc.critical += Object.keys(image.security.result.critical || {}).length;\n acc.high += Object.keys(image.security.result.high || {}).length;\n acc.medium += Object.keys(image.security.result.medium || {}).length;\n acc.low += Object.keys(image.security.result.low || {}).length;\n return acc;\n },\n { critical: 0, high: 0, medium: 0, low: 0 }\n )\n : { critical: 0, high: 0, medium: 0, low: 0 };\n\n const hasCVEs =\n cveSummary.critical > 0 ||\n cveSummary.high > 0 ||\n cveSummary.medium > 0 ||\n cveSummary.low > 0;\n\n // Get security diff for this instance to check for critical fixes\n const securityDiff = securityInfoKey ? securityDiffData[securityInfoKey] : undefined;\n const hasCriticalFixed = securityDiff?.images\n ? Object.entries(securityDiff.images).some(\n ([, image]) =>\n image.removed?.critical &&\n Object.keys(image.removed.critical).length > 0\n )\n : false;\n\n // Get the display text for the instance name/ID\n const displayText = showInstanceId \n ? instance.id.slice(0, 7)\n : instanceName || instance.id.slice(0, 7);\n\n // Get the tooltip text\n const getTooltipText = () => {\n if (instanceName) {\n return showInstanceId \n ? \"Click to show instance name\" \n : \"Click to show instance ID\";\n }\n return \"Instance ID\";\n };\n\n // Get overall resource status\n const getOverallStatus = () => {\n if (!instance.resourceStates || instance.resourceStates.length === 0) {\n return \"unknown\";\n }\n // Count states\n const stateCounts = instance.resourceStates.reduce((acc, resource) => {\n acc[resource.state] = (acc[resource.state] || 0) + 1;\n return acc;\n }, {} as Record<string, number>);\n \n // Return most common state\n let maxCount = 0;\n let maxStatus = \"unknown\";\n for (const [status, count] of Object.entries(stateCounts)) {\n if (count > maxCount) {\n maxCount = count;\n maxStatus = status;\n }\n }\n return maxStatus;\n };\n\n const overallStatus = getOverallStatus();\n // Prefer backend-provided appStatus (aligns with vendor portal customers page),\n // otherwise fall back to derived resource-state rollup.\n const statusForBadge = instance.appStatus || overallStatus;\n\n const handleUpdateClick = () => {\n if (onUpdateClick) {\n onUpdateClick(instance.id);\n }\n };\n\n return (\n <div className=\"rounded-lg border border-gray-200 bg-white p-4\">\n <div className=\"grid grid-cols-[auto_1fr_auto] gap-3\">\n {/* Status indicator dot */}\n <div\n className={`mt-1.5 h-2 w-2 rounded-full ${\n isActive ? \"bg-green-400\" : \"bg-gray-300\"\n }`}\n />\n\n {/* Instance details */}\n <div className=\"flex flex-col gap-3\">\n {/* Instance name, version, badges, status */}\n <div className=\"flex h-min flex-wrap items-center gap-3\">\n <div className=\"relative\">\n <span \n className=\"text-sm font-medium text-gray-900 cursor-pointer hover:underline\"\n onClick={() => setShowInstanceId(!showInstanceId)}\n onMouseEnter={() => setShowTooltip(true)}\n onMouseLeave={() => setShowTooltip(false)}\n title={showInstanceId ? instance.id : instanceName || instance.id}\n >\n {displayText}\n </span>\n {showTooltip && (\n <div className=\"absolute bottom-full left-1/2 -translate-x-1/2 mb-2 px-2 py-1 text-xs text-white bg-gray-800 rounded whitespace-nowrap z-10\">\n {getTooltipText()}\n <div className=\"absolute top-full left-1/2 -translate-x-1/2 border-4 border-transparent border-t-gray-800\" />\n </div>\n )}\n </div>\n <span className=\"flex items-center gap-1 text-xs text-gray-600\">\n Version: {instance.versionLabel || \"Unknown\"}\n <UpdateBadge count={availableUpdates} />\n </span>\n {instance.isAirgap && (\n <span\n className=\"rounded-full px-2 py-0.5 text-xs font-medium\"\n style={{\n backgroundColor: `var(--portal-branding-primary-light, ${DEFAULT_SECONDARY_COLOR}20)`,\n color: `var(--portal-branding-primary, ${DEFAULT_SECONDARY_COLOR})`\n }}\n >\n air gap\n </span>\n )}\n {installType && (\n <span className=\"rounded-full bg-gray-100 px-2 py-0.5 text-xs font-medium text-gray-600\">\n {installType}\n </span>\n )}\n {(instance.appStatus ||\n (instance.resourceStates && instance.resourceStates.length > 0)) && (\n <AppStatusBadge status={statusForBadge} />\n )}\n </div>\n\n {/* Check-in times */}\n <div className=\"flex flex-wrap gap-4\">\n <div className=\"flex items-center gap-1\">\n <span className=\"text-xs text-gray-500\">First check-in:</span>\n <span className=\"text-xs text-gray-600\">\n {formatDateTimeLocal(instance.firstCheckin)}\n </span>\n </div>\n <div className=\"flex items-center gap-1\">\n <span className=\"text-xs text-gray-500\">Last check-in:</span>\n <span className=\"text-xs text-gray-600\">\n {formatDateTimeLocal(instance.lastCheckin)}\n </span>\n </div>\n </div>\n\n {/* CVE Information */}\n {securityEnabled && hasSecurityInfo && (\n <div className=\"flex items-center gap-2\">\n <span className=\"text-xs text-gray-600\">CVEs:</span>\n {hasCVEs && (\n <div className=\"flex items-center gap-1\">\n <CVEBadge severity=\"critical\" count={cveSummary.critical} />\n <CVEBadge severity=\"high\" count={cveSummary.high} />\n <CVEBadge severity=\"medium\" count={cveSummary.medium} />\n <CVEBadge severity=\"low\" count={cveSummary.low} />\n <Link\n href={`/security?sequence=${instance.channelSequence}&installType=${securityInstallType}`}\n className=\"text-xs font-medium text-blue-500 hover:underline\"\n >\n details\n </Link>\n </div>\n )}\n {!hasCVEs && (\n <div className=\"inline-flex h-5 items-center justify-center\">\n <span className=\"text-xs text-gray-600\">None</span>\n </div>\n )}\n </div>\n )}\n\n {/* Tags (excluding name) */}\n {instance.tags && instance.tags.filter(tag => tag.key !== \"name\").length > 0 && (\n <div className=\"flex flex-wrap gap-2\">\n {instance.tags\n .filter(tag => tag.key !== \"name\")\n .map((tag) => (\n <span\n key={tag.key}\n className=\"rounded-md bg-gray-100 px-2 py-1 text-sm text-gray-600\"\n >\n {tag.value}\n </span>\n ))}\n </div>\n )}\n </div>\n\n {/* Update button */}\n {availableUpdates > 0 && (\n <div className=\"flex items-center\">\n <button\n onClick={handleUpdateClick}\n className=\"rounded px-4 py-1.5 text-sm font-medium text-white transition-opacity hover:opacity-90\"\n style={{ backgroundColor: hasCriticalFixed ? \"#dc2626\" : primaryColor }}\n >\n {hasCriticalFixed ? \"Critical update available\" : \"Update available\"}\n </button>\n </div>\n )}\n </div>\n </div>\n );\n}\n\nInstanceCard.displayName = \"InstanceCard\";\n\n","\"use client\";\n\nimport { useState } from \"react\";\nimport { type Instance, type InstallOptions, filterActiveInactiveInstances } from \"../actions/instances\";\nimport type { ChannelRelease } from \"../actions/install\";\nimport type { SecurityReleaseImage, GetSecurityInfoDiffResult } from \"../actions\";\nimport { InstanceCard } from \"./instance-card\";\nimport { DEFAULT_SECONDARY_COLOR } from \"../utils/constants\";\n\ninterface AirgapInstancesProps {\n instances?: Instance[];\n channelReleases?: ChannelRelease[];\n installOptions?: InstallOptions[];\n onUpdateClick?: (instanceId: string) => void;\n /** Called when \"From a support bundle\" is clicked */\n onCreateFromBundleClick?: () => void;\n /** Called when \"Enter information manually\" is clicked */\n onCreateManuallyClick?: () => void;\n /** @deprecated Use onCreateFromBundleClick or onCreateManuallyClick instead */\n onCreateClick?: () => void;\n primaryColor?: string;\n securityData?: Record<string, SecurityReleaseImage[]>;\n securityDiffData?: Record<string, GetSecurityInfoDiffResult>;\n securityEnabled?: boolean;\n /** Whether to show the create button dropdown */\n showCreateButton?: boolean;\n}\n\nexport const AirgapInstances = ({\n instances = [],\n channelReleases = [],\n installOptions = [],\n onUpdateClick,\n onCreateFromBundleClick,\n onCreateManuallyClick,\n onCreateClick,\n primaryColor = DEFAULT_SECONDARY_COLOR,\n securityData = {},\n securityDiffData = {},\n securityEnabled = false,\n showCreateButton = true\n}: AirgapInstancesProps) => {\n const [showInactive, setShowInactive] = useState(true);\n const [isDropdownOpen, setIsDropdownOpen] = useState(false);\n\n // Filter active and inactive instances\n const { activeInstances, inactiveInstances } = filterActiveInactiveInstances(instances);\n\n const hasInactiveInstances = inactiveInstances.length > 0;\n\n const handleFromBundleClick = () => {\n setIsDropdownOpen(false);\n if (onCreateFromBundleClick) {\n onCreateFromBundleClick();\n } else if (onCreateClick) {\n // Fallback for backwards compatibility\n onCreateClick();\n }\n };\n\n const handleManuallyClick = () => {\n setIsDropdownOpen(false);\n if (onCreateManuallyClick) {\n onCreateManuallyClick();\n } else if (onCreateClick) {\n // Fallback for backwards compatibility\n onCreateClick();\n }\n };\n\n return (\n <div className=\"mt-6 space-y-6\">\n {/* Header */}\n <div className=\"flex items-center justify-between\">\n <h2 className=\"mb-0 flex items-center gap-4 text-xl font-semibold text-gray-900\">\n Air gap instances\n <span className=\"text-sm font-semibold text-emerald-500\">\n {activeInstances.length} active\n </span>\n <span className=\"text-sm font-semibold text-rose-400\">\n {inactiveInstances.length} inactive\n </span>\n </h2>\n {hasInactiveInstances && (\n <button\n onClick={() => setShowInactive(!showInactive)}\n className=\"mb-0 cursor-pointer text-sm font-medium text-blue-500 hover:underline\"\n >\n {showInactive ? \"Hide inactive instances\" : \"Show inactive instances\"}\n </button>\n )}\n </div>\n\n {/* Empty state */}\n {instances.length === 0 && (\n <div className=\"rounded-lg border border-gray-200 bg-white p-6\">\n <p className=\"mb-6 text-gray-600\">\n Air gap updates require an air gap instance record to determine install type, version etc.\n </p>\n </div>\n )}\n\n {/* Instance lists when there are instances */}\n {instances.length > 0 && (\n <div className=\"space-y-6\">\n {/* Active instances section */}\n <div className=\"space-y-4\">\n {activeInstances.length === 0 ? (\n <div className=\"rounded-lg border border-gray-200 bg-white p-4 text-sm text-gray-500\">\n No active instances found\n </div>\n ) : (\n <>\n <div className=\"flex items-center gap-2\">\n <h3 className=\"text-sm font-medium text-gray-800\">\n Active\n <span className=\"ml-2 text-xs font-medium text-gray-400\">\n last 24 hours\n </span>\n </h3>\n </div>\n <div className=\"space-y-4\">\n {activeInstances.map((instance) => (\n <InstanceCard\n key={instance.id}\n instance={instance}\n isActive={true}\n channelReleases={channelReleases}\n installOptions={installOptions}\n onUpdateClick={onUpdateClick}\n primaryColor={primaryColor}\n securityData={securityData}\n securityDiffData={securityDiffData}\n securityEnabled={securityEnabled}\n />\n ))}\n </div>\n </>\n )}\n </div>\n\n {/* Inactive instances section */}\n {showInactive && inactiveInstances.length > 0 && (\n <div className=\"space-y-4\">\n <div className=\"flex items-center gap-2\">\n <h3 className=\"text-sm font-medium text-gray-800\">Inactive</h3>\n </div>\n <div className=\"space-y-4\">\n {inactiveInstances.map((instance) => (\n <InstanceCard\n key={instance.id}\n instance={instance}\n isActive={false}\n channelReleases={channelReleases}\n installOptions={installOptions}\n onUpdateClick={onUpdateClick}\n primaryColor={primaryColor}\n securityData={securityData}\n securityDiffData={securityDiffData}\n securityEnabled={securityEnabled}\n />\n ))}\n </div>\n </div>\n )}\n </div>\n )}\n\n {/* Create instance button with dropdown */}\n {showCreateButton && (\n <div className=\"relative\">\n <button\n onClick={() => setIsDropdownOpen(!isDropdownOpen)}\n className=\"inline-flex items-center gap-2 rounded-md px-4 py-2 text-sm font-medium text-white hover:opacity-90\"\n style={{ backgroundColor: primaryColor }}\n >\n Create air gap instance record\n <svg\n className={`h-4 w-4 transition-transform ${isDropdownOpen ? \"rotate-180\" : \"\"}`}\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M19 9l-7 7-7-7\" />\n </svg>\n </button>\n\n {isDropdownOpen && (\n <div className=\"absolute left-0 z-10 mt-2 w-72 rounded-md border border-gray-200 bg-white shadow-lg\">\n <button\n onClick={handleFromBundleClick}\n className=\"flex w-full items-center gap-2 px-4 py-2 text-left text-sm text-gray-700 hover:bg-gray-50\"\n >\n <svg className=\"h-4 w-4 text-gray-500\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4\" />\n </svg>\n From a support bundle\n </button>\n <button\n onClick={handleManuallyClick}\n className=\"flex w-full items-center gap-2 px-4 py-2 text-left text-sm text-gray-700 hover:bg-gray-50\"\n >\n <svg className=\"h-4 w-4 text-gray-500\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z\" />\n </svg>\n Enter information manually\n </button>\n </div>\n )}\n </div>\n )}\n </div>\n );\n};\n\nAirgapInstances.displayName = \"AirgapInstances\";\n\n"]}
@@ -0,0 +1,36 @@
1
+ interface RawBranding {
2
+ brandingData?: string;
3
+ }
4
+ type BackgroundType = "minimal" | "custom" | "image";
5
+ interface BrandingResult {
6
+ logo?: string;
7
+ title?: string;
8
+ favicon?: string;
9
+ primaryColor: string;
10
+ secondaryColor: string;
11
+ supportPortalLink?: string;
12
+ background?: BackgroundType;
13
+ backgroundImage?: string;
14
+ backgroundGradientStart?: string;
15
+ backgroundGradientEnd?: string;
16
+ }
17
+ /**
18
+ * Normalizes and validates CSS color values to prevent CSS injection attacks.
19
+ * Only supports hex color format (#rgb or #rrggbb) as that's what the API sends.
20
+ *
21
+ * @param color - The color value to normalize
22
+ * @returns The normalized hex color or undefined if invalid
23
+ */
24
+ declare const normalizeColor: (color?: string) => string | undefined;
25
+ /**
26
+ * Validates and sanitizes a URL for safe use in CSS url() functions.
27
+ * This prevents CSS injection attacks by validating the URL format and
28
+ * escaping characters that could break out of the url() context.
29
+ *
30
+ * @param url - The URL to validate and sanitize
31
+ * @returns The sanitized URL or undefined if invalid
32
+ */
33
+ declare const sanitizeUrlForCss: (url?: string) => string | undefined;
34
+ declare const decodeBranding: ({ brandingData }: RawBranding) => BrandingResult;
35
+
36
+ export { type BackgroundType as B, decodeBranding as d, normalizeColor as n, sanitizeUrlForCss as s };