@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
package/dist/index.js CHANGED
@@ -3,6 +3,7 @@
3
3
  var react = require('react');
4
4
  var jsxRuntime = require('react/jsx-runtime');
5
5
  var buffer = require('buffer');
6
+ var headers = require('next/headers');
6
7
  var Link = require('next/link');
7
8
 
8
9
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
@@ -13,10 +14,119 @@ var Link__default = /*#__PURE__*/_interopDefault(Link);
13
14
  * Enterprise Portal Components
14
15
  * This file is generated by tsup. Do not edit manually.
15
16
  */
17
+ var __defProp = Object.defineProperty;
18
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
19
+ var __getOwnPropNames = Object.getOwnPropertyNames;
20
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
21
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
22
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
23
+ }) : x)(function(x) {
24
+ if (typeof require !== "undefined") return require.apply(this, arguments);
25
+ throw Error('Dynamic require of "' + x + '" is not supported');
26
+ });
27
+ var __esm = (fn, res) => function __init() {
28
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
29
+ };
30
+ var __export = (target, all) => {
31
+ for (var name in all)
32
+ __defProp(target, name, { get: all[name], enumerable: true });
33
+ };
34
+ var __copyProps = (to, from, except, desc) => {
35
+ if (from && typeof from === "object" || typeof from === "function") {
36
+ for (let key of __getOwnPropNames(from))
37
+ if (!__hasOwnProp.call(to, key) && key !== except)
38
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
39
+ }
40
+ return to;
41
+ };
42
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
43
+
44
+ // datadog/tracer.ts
45
+ var tracer_exports = {};
46
+ __export(tracer_exports, {
47
+ default: () => tracer_default
48
+ });
49
+ var rawFlag, isEnabled, tracer, getRoutePattern, tracer_default;
50
+ var init_tracer = __esm({
51
+ "datadog/tracer.ts"() {
52
+ rawFlag = String(process.env.USE_DATADOG_APM || "").toLowerCase();
53
+ isEnabled = rawFlag === "true";
54
+ process.env.DD_TRACE_ENABLED = isEnabled ? "1" : "0";
55
+ tracer = null;
56
+ if (isEnabled) {
57
+ const serviceName = process.env.DD_SERVICE || "enterprise-portal";
58
+ const environment = process.env.DD_ENV || process.env.NODE_ENV || "development";
59
+ const version = process.env.DD_VERSION || process.env.NEXT_PUBLIC_VERSION || "0.0.0-dev";
60
+ const agentHost = process.env.DD_AGENT_HOST || process.env.DATADOG_AGENT_HOST || "127.0.0.1";
61
+ const agentPort = process.env.DD_TRACE_AGENT_PORT || "8126";
62
+ process.env.DD_SERVICE = serviceName;
63
+ process.env.DD_ENV = environment;
64
+ {
65
+ process.env.DD_VERSION = version;
66
+ }
67
+ process.env.DD_AGENT_HOST = agentHost;
68
+ process.env.DD_TRACE_AGENT_PORT = agentPort;
69
+ const dbmPropagationMode = process.env.DD_DBM_PROPAGATION_MODE || "full";
70
+ process.env.DD_DBM_PROPAGATION_MODE = dbmPropagationMode;
71
+ try {
72
+ const ddTrace = __require("dd-trace");
73
+ tracer = ddTrace.init({
74
+ service: serviceName,
75
+ env: environment,
76
+ version,
77
+ logInjection: true,
78
+ runtimeMetrics: true,
79
+ appsec: false,
80
+ profiling: false,
81
+ startupLogs: true
82
+ // Enable for debugging
83
+ });
84
+ console.log(`[datadog] Tracer initialized: service=${serviceName}, env=${environment}, version=${version}, agent=${agentHost}:${agentPort}`);
85
+ tracer.use("dns", false);
86
+ tracer.use("net", false);
87
+ tracer.use("http", {
88
+ server: {
89
+ hooks: {
90
+ request: (span, req) => {
91
+ if (!span) return;
92
+ const url = req?.url || "";
93
+ const method = req?.method || "GET";
94
+ const path = url.split("?")[0];
95
+ if (path.startsWith("/_next/")) {
96
+ span.context()._trace.isRecording = false;
97
+ return;
98
+ }
99
+ const routePattern = getRoutePattern(path);
100
+ span.setTag("resource.name", `${method} ${routePattern}`);
101
+ span.setTag("http.route", routePattern);
102
+ }
103
+ }
104
+ }
105
+ });
106
+ } catch (err) {
107
+ console.error("[datadog] failed to initialize tracing", err);
108
+ tracer = null;
109
+ }
110
+ }
111
+ getRoutePattern = (path) => {
112
+ const routePatterns = [
113
+ // Update instance routes - normalize dynamic segments (capture suffix to preserve sub-routes)
114
+ { pattern: /^\/update\/instance\/[^/]+(.*)$/, replacement: "/update/instance/[instanceId]$1" }
115
+ ];
116
+ for (const { pattern, replacement } of routePatterns) {
117
+ if (pattern.test(path)) {
118
+ return path.replace(pattern, replacement);
119
+ }
120
+ }
121
+ return path;
122
+ };
123
+ tracer_default = tracer;
124
+ }
125
+ });
16
126
 
17
127
  // package.json
18
128
  var package_default = {
19
- version: "0.0.19"};
129
+ version: "0.0.21"};
20
130
 
21
131
  // src/tokens/index.ts
22
132
  var baseTokens = {
@@ -132,6 +242,12 @@ var Button = react.forwardRef(
132
242
  Button.displayName = "Button";
133
243
 
134
244
  // src/utils/api-client.ts
245
+ var UnauthorizedError = class extends Error {
246
+ constructor(message = "Unauthorized") {
247
+ super(message);
248
+ this.name = "UnauthorizedError";
249
+ }
250
+ };
135
251
  function isRedirectError(error) {
136
252
  return typeof error === "object" && error !== null && "digest" in error && typeof error.digest === "string" && error.digest.startsWith("NEXT_REDIRECT");
137
253
  }
@@ -157,8 +273,17 @@ async function handle401() {
157
273
  const { redirect } = await import('next/navigation');
158
274
  return redirect("/?expired=1");
159
275
  }
276
+ function isErrorPage(url) {
277
+ try {
278
+ const urlObj = new URL(url);
279
+ return urlObj.pathname === "/error";
280
+ } catch {
281
+ return url === "/error" || url.startsWith("/error?");
282
+ }
283
+ }
160
284
  async function handleServerError(statusCode) {
161
285
  const { redirect } = await import('next/navigation');
286
+ const { cookies: cookies2 } = await import('next/headers');
162
287
  let sourceUrl;
163
288
  try {
164
289
  const { headers } = await import('next/headers');
@@ -166,12 +291,20 @@ async function handleServerError(statusCode) {
166
291
  const referer = headersList.get("referer");
167
292
  const host = headersList.get("host");
168
293
  const pathname = headersList.get("x-invoke-path") || headersList.get("x-forwarded-path");
169
- if (referer) {
294
+ if (referer && !isErrorPage(referer)) {
170
295
  sourceUrl = referer;
171
296
  } else if (host && pathname) {
172
297
  const protocol = headersList.get("x-forwarded-proto") || "https";
173
298
  sourceUrl = `${protocol}://${host}${pathname}`;
174
299
  }
300
+ if (!sourceUrl || isErrorPage(sourceUrl)) {
301
+ const cookieStore = await cookies2();
302
+ const preservedSource = cookieStore.get("portal_error_source");
303
+ if (preservedSource?.value) {
304
+ sourceUrl = decodeURIComponent(preservedSource.value);
305
+ cookieStore.delete("portal_error_source");
306
+ }
307
+ }
175
308
  } catch (error) {
176
309
  console.debug("[portal-components] Could not determine source URL", error);
177
310
  }
@@ -182,6 +315,359 @@ async function handleServerError(statusCode) {
182
315
  return redirect(`/error?${params.toString()}`);
183
316
  }
184
317
 
318
+ // src/utils/observability/tracing.ts
319
+ var tracerCache = void 0;
320
+ function getTracer() {
321
+ if (tracerCache !== void 0) {
322
+ return tracerCache;
323
+ }
324
+ const rawFlag2 = String(process.env.USE_DATADOG_APM || "").toLowerCase();
325
+ const isEnabled2 = rawFlag2 === "true";
326
+ if (!isEnabled2) {
327
+ tracerCache = null;
328
+ return null;
329
+ }
330
+ try {
331
+ const tracerModule = (init_tracer(), __toCommonJS(tracer_exports));
332
+ const tracer2 = tracerModule.default || tracerModule;
333
+ if (tracer2 && typeof tracer2.trace === "function") {
334
+ tracerCache = tracer2;
335
+ return tracerCache;
336
+ }
337
+ } catch (err) {
338
+ console.warn("Failed to load tracer:", err);
339
+ }
340
+ tracerCache = null;
341
+ return null;
342
+ }
343
+ async function withTrace(name, fn) {
344
+ const activeTracer = getTracer();
345
+ if (!activeTracer) {
346
+ return fn(void 0);
347
+ }
348
+ return activeTracer.trace(name, async (span) => {
349
+ if (span) {
350
+ span.setTag("component", "application");
351
+ }
352
+ try {
353
+ const result = await fn(span);
354
+ return result;
355
+ } catch (error) {
356
+ if (span) {
357
+ span.setTag("error", error);
358
+ }
359
+ throw error;
360
+ }
361
+ });
362
+ }
363
+ function traceServerAction(name, fn) {
364
+ const spanName = name.startsWith("server.action.") ? name : `server.action.${name}`;
365
+ const traced = async (...args) => {
366
+ return withTrace(spanName, async (span) => {
367
+ if (span) {
368
+ span.setTag("component", "server-action");
369
+ }
370
+ const result = await fn(...args);
371
+ return result;
372
+ });
373
+ };
374
+ return traced;
375
+ }
376
+ var deriveInstallMethods = (licenseData) => {
377
+ const methods = [];
378
+ if (licenseData.isKotsInstallEnabled) {
379
+ methods.push("Replicated KOTS");
380
+ }
381
+ if (licenseData.isHelmInstallEnabled) {
382
+ methods.push("Helm");
383
+ }
384
+ if (licenseData.isHelmAirgapEnabled) {
385
+ methods.push("Helm Airgap");
386
+ }
387
+ if (licenseData.isEmbeddedClusterDownloadEnabled || licenseData.isEmbeddedClusterMultiNodeEnabled) {
388
+ methods.push("Embedded Cluster");
389
+ }
390
+ if (licenseData.isKurlInstallEnabled) {
391
+ methods.push("kURL");
392
+ }
393
+ if (licenseData.isGitopsSupported) {
394
+ methods.push("GitOps");
395
+ }
396
+ return Array.from(new Set(methods));
397
+ };
398
+ var convertEntitlementsToFields = (entitlementFields, entitlementValues) => {
399
+ const valuesMap = /* @__PURE__ */ new Map();
400
+ entitlementValues.forEach((ev) => {
401
+ valuesMap.set(ev.name, ev.value);
402
+ });
403
+ return entitlementFields.filter((field) => field.hidden === 0).map((field) => ({
404
+ key: field.name,
405
+ label: field.title || field.name,
406
+ value: valuesMap.get(field.name) || field.defaultVal || null,
407
+ isSecret: field.type === "Password"
408
+ }));
409
+ };
410
+ async function fetchLicenseCore() {
411
+ const sessionStore = await headers.cookies();
412
+ const session = sessionStore.get("portal_session");
413
+ const token = session?.value;
414
+ if (!token) {
415
+ throw new Error("No session found - user must be authenticated");
416
+ }
417
+ const endpoint = `${getApiOrigin()}/enterprise-portal/license`;
418
+ if (process.env.NODE_ENV !== "production") {
419
+ console.debug("[portal-components] fetching license via %s (Enterprise Portal API)", endpoint);
420
+ }
421
+ const response = await authenticatedFetch(endpoint, {
422
+ method: "GET",
423
+ token,
424
+ headers: {
425
+ Accept: "application/json"
426
+ }
427
+ });
428
+ if (!response.ok) {
429
+ throw new Error(
430
+ `License request failed (${response.status} ${response.statusText})`
431
+ );
432
+ }
433
+ const envelope = await response.json();
434
+ const licenseData = envelope.data;
435
+ const license = {
436
+ ...licenseData,
437
+ // Alias fields for backward compatibility
438
+ expiresAt: licenseData.expireAt,
439
+ environment: licenseData.licenseType,
440
+ // Extract channel names from channels array
441
+ releaseChannels: (licenseData.channels || []).map((ch) => ch.channelName),
442
+ // Derive install methods from feature flags
443
+ installMethods: deriveInstallMethods(licenseData),
444
+ // Convert entitlements to fields format
445
+ fields: convertEntitlementsToFields(licenseData.entitlementFields || [], licenseData.entitlementValues || [])
446
+ };
447
+ return license;
448
+ }
449
+ var fetchLicenseImpl = react.cache(fetchLicenseCore);
450
+ var fetchLicense = fetchLicenseImpl;
451
+
452
+ // src/utils/constants.ts
453
+ var 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";
454
+ var DEFAULT_PRIMARY_COLOR = "#4f46e5";
455
+ var DEFAULT_SECONDARY_COLOR = "#6366f1";
456
+ var isHttpApiOrigin = () => {
457
+ return process.env.REPLICATED_APP_ORIGIN?.startsWith("http://") || false;
458
+ };
459
+
460
+ // src/utils/branding.ts
461
+ var normalizeColor = (color) => {
462
+ if (!color || typeof color !== "string") {
463
+ return void 0;
464
+ }
465
+ const trimmed = color.trim();
466
+ if (/^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$/.test(trimmed)) {
467
+ return trimmed.startsWith("#") ? trimmed : `#${trimmed}`;
468
+ }
469
+ console.debug("[portal-components] Invalid color format rejected (only hex supported):", trimmed);
470
+ return void 0;
471
+ };
472
+ var sanitizeUrlForCss = (url) => {
473
+ if (!url || typeof url !== "string") {
474
+ return void 0;
475
+ }
476
+ const trimmed = url.trim();
477
+ try {
478
+ const urlObj = new URL(trimmed);
479
+ if (!["http:", "https:", "data:"].includes(urlObj.protocol)) {
480
+ console.debug("[portal-components] Invalid URL protocol for background image:", urlObj.protocol);
481
+ return void 0;
482
+ }
483
+ } catch {
484
+ console.debug("[portal-components] Invalid URL format for background image:", trimmed);
485
+ return void 0;
486
+ }
487
+ const escaped = trimmed.replace(/\\/g, "\\\\").replace(/\)/g, "\\)").replace(/"/g, '\\"').replace(/'/g, "\\'").replace(/[\x00-\x1F\x7F]/g, "");
488
+ return escaped;
489
+ };
490
+ var decodeBranding = ({ brandingData }) => {
491
+ if (!brandingData || typeof brandingData !== "string") {
492
+ return {
493
+ primaryColor: DEFAULT_PRIMARY_COLOR,
494
+ secondaryColor: DEFAULT_SECONDARY_COLOR
495
+ };
496
+ }
497
+ try {
498
+ const decoded = buffer.Buffer.from(brandingData, "base64").toString("utf-8");
499
+ const parsed = JSON.parse(decoded);
500
+ const logo = typeof parsed.logo === "string" ? parsed.logo : void 0;
501
+ const titleRaw = typeof parsed.title === "string" ? parsed.title.trim() : "";
502
+ const title = titleRaw ? titleRaw : void 0;
503
+ const favicon = typeof parsed.favicon === "string" ? parsed.favicon : void 0;
504
+ const primaryColorRaw = parsed.primaryColor ?? parsed.primary_color;
505
+ const secondaryColorRaw = parsed.secondaryColor ?? parsed.secondary_color;
506
+ const primaryColor = normalizeColor(primaryColorRaw);
507
+ const secondaryColor = normalizeColor(secondaryColorRaw);
508
+ const supportPortalLink = typeof parsed.supportPortalLink === "string" ? parsed.supportPortalLink : void 0;
509
+ const backgroundRaw = parsed.background;
510
+ const background = backgroundRaw === "minimal" || backgroundRaw === "custom" || backgroundRaw === "image" ? backgroundRaw : void 0;
511
+ const backgroundImage = sanitizeUrlForCss(parsed.backgroundImage);
512
+ const backgroundGradientStart = normalizeColor(parsed.customColor1);
513
+ const backgroundGradientEnd = normalizeColor(parsed.customColor2);
514
+ return {
515
+ logo,
516
+ title,
517
+ favicon,
518
+ primaryColor: primaryColor || DEFAULT_PRIMARY_COLOR,
519
+ secondaryColor: secondaryColor || DEFAULT_SECONDARY_COLOR,
520
+ supportPortalLink,
521
+ background,
522
+ backgroundImage,
523
+ backgroundGradientStart,
524
+ backgroundGradientEnd
525
+ };
526
+ } catch (error) {
527
+ console.debug("[portal-components] unable to parse branding JSON", error);
528
+ return {
529
+ primaryColor: DEFAULT_PRIMARY_COLOR,
530
+ secondaryColor: DEFAULT_SECONDARY_COLOR
531
+ };
532
+ }
533
+ };
534
+
535
+ // src/utils/session.ts
536
+ async function validateSession(token) {
537
+ if (!token || typeof token !== "string" || !token.trim()) {
538
+ return false;
539
+ }
540
+ try {
541
+ const endpoint = `${getApiOrigin()}/enterprise-portal/user`;
542
+ const response = await fetch(endpoint, {
543
+ method: "GET",
544
+ headers: {
545
+ authorization: `Bearer ${token}`
546
+ },
547
+ // Short timeout for validation
548
+ signal: AbortSignal.timeout(5e3)
549
+ });
550
+ if (response.status === 401) {
551
+ return false;
552
+ }
553
+ return response.ok;
554
+ } catch (error) {
555
+ console.warn("[portal-components] session validation error:", error);
556
+ return true;
557
+ }
558
+ }
559
+ async function deleteSessionCookie() {
560
+ const { cookies: cookies2 } = await import('next/headers');
561
+ const cookieStore = await cookies2();
562
+ cookieStore.delete("portal_session");
563
+ }
564
+
565
+ // src/utils/format.ts
566
+ function formatBytes(bytes, decimals = 1) {
567
+ if (bytes === 0) return "0 Bytes";
568
+ const k = 1024;
569
+ const dm = decimals < 0 ? 0 : decimals;
570
+ const sizes = ["Bytes", "KB", "MB", "GB", "TB"];
571
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
572
+ return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
573
+ }
574
+ function formatDateShort(dateString) {
575
+ if (!dateString) return "Never";
576
+ const date = new Date(dateString);
577
+ if (isNaN(date.getTime())) {
578
+ return dateString;
579
+ }
580
+ return date.toLocaleDateString("en-US", {
581
+ year: "numeric",
582
+ month: "2-digit",
583
+ day: "2-digit"
584
+ });
585
+ }
586
+ function formatDate(dateString) {
587
+ if (!dateString) return "Never";
588
+ const date = new Date(dateString);
589
+ if (isNaN(date.getTime())) {
590
+ return dateString;
591
+ }
592
+ return date.toLocaleDateString("en-US", {
593
+ month: "short",
594
+ day: "numeric",
595
+ year: "numeric",
596
+ hour: "numeric",
597
+ minute: "2-digit",
598
+ hour12: true
599
+ });
600
+ }
601
+ function formatDateTime(dateString) {
602
+ const date = new Date(dateString);
603
+ if (isNaN(date.getTime())) {
604
+ return dateString;
605
+ }
606
+ return date.toLocaleString("en-US", {
607
+ timeZone: "UTC",
608
+ year: "numeric",
609
+ month: "2-digit",
610
+ day: "2-digit",
611
+ hour: "2-digit",
612
+ minute: "2-digit",
613
+ second: "2-digit",
614
+ hour12: false
615
+ }) + " UTC";
616
+ }
617
+ function formatDateTimeLocal(dateString) {
618
+ if (!dateString) return "N/A";
619
+ try {
620
+ const date = new Date(dateString);
621
+ if (isNaN(date.getTime())) {
622
+ return dateString;
623
+ }
624
+ return date.toLocaleDateString("en-US", {
625
+ month: "numeric",
626
+ day: "numeric",
627
+ year: "numeric",
628
+ hour: "numeric",
629
+ minute: "2-digit",
630
+ hour12: true
631
+ });
632
+ } catch {
633
+ return "N/A";
634
+ }
635
+ }
636
+
637
+ // src/utils/release-helpers.ts
638
+ function convertToReleaseEntry(release, channelName, options) {
639
+ const sections = [];
640
+ if (release.release_notes && release.release_notes.trim().length > 0) {
641
+ if (options.releaseNotesMode === "markdown") {
642
+ sections.push({
643
+ title: "Release Notes",
644
+ description: release.release_notes
645
+ });
646
+ } else {
647
+ const releaseNotesItems = release.release_notes.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0);
648
+ sections.push({
649
+ title: "Release Notes",
650
+ description: releaseNotesItems.length === 0 ? release.release_notes : void 0,
651
+ items: releaseNotesItems.length > 0 ? releaseNotesItems : void 0
652
+ });
653
+ }
654
+ }
655
+ const charts = release.helm_charts ? release.helm_charts.filter((chart) => !chart.is_kots_installer_only).map((chart) => `${chart.name} v${chart.version}`) : [];
656
+ if (charts.length > 0) {
657
+ sections.push({
658
+ title: "Helm Charts",
659
+ items: charts,
660
+ description: "Latest chart versions included in this release."
661
+ });
662
+ }
663
+ return {
664
+ version: release.label || `Release ${release.release_sequence}`,
665
+ channel: channelName || `Channel ${release.channel_sequence}`,
666
+ releasedAt: release.release_date,
667
+ sections
668
+ };
669
+ }
670
+
185
671
  // src/actions/index.ts
186
672
  var getApiOrigin = () => {
187
673
  return (process.env.REPLICATED_APP_ORIGIN || "https://replicated.app").replace(/\/+$/, "");
@@ -321,11 +807,15 @@ var fetchCustomBrandingImpl = async () => {
321
807
  const payload = await response.json();
322
808
  const brandingObject = {
323
809
  logo: payload.logoUrl,
324
- title: payload.appName,
325
- customColor1: payload.primaryColor,
326
- customColor2: payload.secondaryColor,
810
+ title: payload.title,
811
+ primaryColor: payload.primaryColor,
812
+ secondaryColor: payload.secondaryColor,
327
813
  favicon: payload.faviconUrl,
328
- supportPortalLink: payload.supportPortalLink || ""
814
+ supportPortalLink: payload.supportPortalLink || "",
815
+ background: payload.background,
816
+ backgroundImage: payload.backgroundImage,
817
+ customColor1: payload.customColor1,
818
+ customColor2: payload.customColor2
329
819
  };
330
820
  const brandingData = buffer.Buffer.from(JSON.stringify(brandingObject)).toString("base64");
331
821
  return {
@@ -588,353 +1078,38 @@ var uploadSupportBundle = defineServerAction({
588
1078
  var getSupportBundleUploadUrl = (appId) => {
589
1079
  return `${getApiOrigin()}/enterprise-portal/support-bundles/upload/${encodeURIComponent(appId)}`;
590
1080
  };
591
- var listReleases = defineServerAction({
592
- id: "releases/list",
593
- description: "Lists available releases for the authenticated customer.",
594
- visibility: "customer",
595
- tags: ["releases"],
596
- async run({ token }, context) {
597
- if (!token || typeof token !== "string") {
598
- throw new Error("List releases requires a session token");
599
- }
600
- const endpoint = `${getApiOrigin()}/enterprise-portal/releases`;
601
- if (process.env.NODE_ENV !== "production") {
602
- console.debug("[portal-components] fetching releases via %s (Enterprise Portal API)", endpoint);
603
- }
604
- const response = await authenticatedFetch(endpoint, {
605
- method: "GET",
606
- token,
607
- headers: {
608
- accept: "application/json"
609
- },
610
- signal: context?.signal
611
- });
612
- const bodyText = await response.text().catch((error) => {
613
- console.warn("[portal-components] listReleases read error", error);
614
- return null;
615
- });
616
- if (!response.ok) {
617
- throw new Error(
618
- `List releases request failed (${response.status} ${response.statusText})`
619
- );
620
- }
621
- return {
622
- status: response.status,
623
- body: bodyText
624
- };
625
- }
626
- });
627
- var asRecord = (value) => {
628
- if (value && typeof value === "object") {
629
- return value;
630
- }
631
- return void 0;
632
- };
633
- var getValue = (record, key) => record ? record[key] : void 0;
634
- var getString = (record, key) => {
635
- const value = getValue(record, key);
636
- return typeof value === "string" ? value : void 0;
637
- };
638
- var getBoolean = (record, key) => {
639
- const value = getValue(record, key);
640
- if (typeof value === "boolean") {
641
- return value;
642
- }
643
- if (typeof value === "number") {
644
- return value === 1;
645
- }
646
- if (typeof value === "string") {
647
- const normalized = value.trim().toLowerCase();
648
- if (["true", "1", "yes"].includes(normalized)) {
649
- return true;
650
- }
651
- if (["false", "0", "no"].includes(normalized)) {
652
- return false;
653
- }
654
- }
655
- return void 0;
656
- };
657
- var toDisplayValue = (value) => {
658
- if (value === null || value === void 0) {
659
- return null;
660
- }
661
- if (typeof value === "string") {
662
- return value;
663
- }
664
- if (typeof value === "number" || typeof value === "boolean") {
665
- return String(value);
666
- }
667
- try {
668
- return JSON.stringify(value);
669
- } catch {
670
- return String(value);
671
- }
672
- };
673
- var normalizeStringArray = (value) => {
674
- if (Array.isArray(value)) {
675
- const normalized = value.map(
676
- (item) => typeof item === "string" ? item.trim() : ""
677
- ).filter((item) => item.length > 0);
678
- return normalized.length ? normalized : void 0;
679
- }
680
- if (typeof value === "string") {
681
- const normalized = value.split(",").map((item) => item.trim()).filter((item) => item.length > 0);
682
- return normalized.length ? normalized : void 0;
683
- }
684
- return void 0;
685
- };
686
- var normalizeLicenseFields = (input) => {
687
- if (!input) {
688
- return [];
689
- }
690
- if (Array.isArray(input)) {
691
- return input.map((field, index) => {
692
- if (!field || typeof field !== "object") {
693
- return null;
694
- }
695
- const candidate = field;
696
- const key = typeof candidate.key === "string" && candidate.key.trim().length ? candidate.key.trim() : typeof candidate.name === "string" && candidate.name.trim().length ? candidate.name.trim() : typeof candidate.label === "string" && candidate.label.trim().length ? candidate.label.trim() : `field-${index}`;
697
- const label = typeof candidate.label === "string" && candidate.label.trim().length ? candidate.label.trim() : typeof candidate.name === "string" && candidate.name.trim().length ? candidate.name.trim() : key;
698
- let value = candidate.value ?? candidate.data ?? candidate.content;
699
- if ((value === void 0 || value === null) && typeof candidate.text === "string") {
700
- value = candidate.text;
701
- }
702
- if ((value === void 0 || value === null) && typeof candidate.defaultValue === "string") {
703
- value = candidate.defaultValue;
704
- }
705
- const isSecret = Boolean(
706
- candidate.isSecret ?? candidate.secret ?? candidate.masked
707
- );
708
- const resolved = toDisplayValue(value);
709
- return {
710
- key,
711
- label,
712
- value: resolved,
713
- isSecret
714
- };
715
- }).filter((field) => Boolean(field));
716
- }
717
- if (typeof input === "object") {
718
- return Object.entries(input).map(
719
- ([key, value]) => {
720
- let resolvedValue = value;
721
- let isSecret = false;
722
- if (value && typeof value === "object") {
723
- const obj = value;
724
- if ("value" in obj) {
725
- resolvedValue = obj.value;
726
- }
727
- isSecret = Boolean(obj.isSecret ?? obj.secret ?? obj.masked);
728
- }
729
- const normalized = toDisplayValue(resolvedValue);
730
- return {
731
- key,
732
- label: key,
733
- value: normalized,
734
- isSecret
735
- };
736
- }
737
- );
738
- }
739
- return [];
740
- };
741
- var extractChannelNames = (input) => {
742
- if (!Array.isArray(input)) {
743
- return void 0;
1081
+ async function listReleasesImpl() {
1082
+ const { cookies: cookies2 } = await import('next/headers');
1083
+ const sessionStore = await cookies2();
1084
+ const session = sessionStore.get("portal_session");
1085
+ const token = session?.value;
1086
+ if (!token) {
1087
+ throw new Error("List releases requires a session token");
1088
+ }
1089
+ const endpoint = `${getApiOrigin()}/enterprise-portal/releases`;
1090
+ if (process.env.NODE_ENV !== "production") {
1091
+ console.debug("[portal-components] fetching releases via %s (Enterprise Portal API)", endpoint);
744
1092
  }
745
- const names = input.map((item) => {
746
- if (typeof item === "string") {
747
- return item.trim();
748
- }
749
- const record = asRecord(item);
750
- if (!record) {
751
- return null;
752
- }
753
- return getString(record, "name") ?? getString(record, "channelName") ?? getString(record, "channel") ?? getString(record, "channelSlug") ?? getString(record, "slug") ?? void 0;
754
- }).filter((name) => Boolean(name && name.length));
755
- return names.length ? names : void 0;
756
- };
757
- var normalizeEntitlementFields = (fieldsInput, valuesInput) => {
758
- const valuesMap = /* @__PURE__ */ new Map();
759
- const assignValue = (key, value) => {
760
- if (!key) {
761
- return;
1093
+ const response = await authenticatedFetch(endpoint, {
1094
+ method: "GET",
1095
+ token,
1096
+ headers: {
1097
+ accept: "application/json"
762
1098
  }
763
- valuesMap.set(key, toDisplayValue(value));
764
- };
765
- if (Array.isArray(valuesInput)) {
766
- valuesInput.forEach((item) => {
767
- const record = asRecord(item);
768
- if (!record) {
769
- if (typeof item === "string") {
770
- assignValue(item, item);
771
- }
772
- return;
773
- }
774
- const key = getString(record, "name") ?? getString(record, "field") ?? getString(record, "title") ?? getString(record, "label") ?? getString(record, "slug") ?? (() => {
775
- const idValue = getValue(record, "id");
776
- if (typeof idValue === "string" || typeof idValue === "number") {
777
- return String(idValue);
778
- }
779
- return void 0;
780
- })();
781
- const value = getValue(record, "value") ?? getValue(record, "currentValue") ?? getValue(record, "entitlementValue") ?? getValue(record, "content") ?? getValue(record, "data") ?? getValue(record, "defaultVal") ?? getValue(record, "defaultValue");
782
- assignValue(key, value);
783
- });
784
- } else if (valuesInput && typeof valuesInput === "object") {
785
- Object.entries(valuesInput).forEach(
786
- ([key, value]) => assignValue(key, value)
1099
+ });
1100
+ if (!response.ok) {
1101
+ throw new Error(
1102
+ `List releases request failed (${response.status} ${response.statusText})`
787
1103
  );
788
1104
  }
789
- const normalized = [];
790
- if (Array.isArray(fieldsInput)) {
791
- fieldsInput.forEach((item, index) => {
792
- const record = asRecord(item);
793
- if (!record) {
794
- return;
795
- }
796
- const baseKey = getString(record, "name") ?? getString(record, "field") ?? getString(record, "slug") ?? `entitlement-${index}`;
797
- const key = `entitlement-${baseKey}`;
798
- const label = getString(record, "title") ?? getString(record, "label") ?? baseKey;
799
- const defaultValue = getString(record, "defaultVal") ?? getString(record, "default") ?? getString(record, "defaultValue");
800
- const value = valuesMap.get(baseKey) ?? valuesMap.get(label) ?? defaultValue ?? null;
801
- const isSecret = Boolean(
802
- getBoolean(record, "secret") ?? getBoolean(record, "isSecret") ?? getBoolean(record, "masked")
803
- );
804
- normalized.push({
805
- key,
806
- label,
807
- value,
808
- isSecret
809
- });
810
- });
811
- }
812
- valuesMap.forEach((value, key) => {
813
- const normalizedKey = `entitlement-${key}`;
814
- if (!normalized.some((field) => field.key === normalizedKey)) {
815
- normalized.push({
816
- key: normalizedKey,
817
- label: key,
818
- value
819
- });
820
- }
821
- });
822
- return normalized;
823
- };
824
- var normalizeLicensePayload = (payload) => {
825
- const payloadRecord = asRecord(payload);
826
- const rootRecord = asRecord(getValue(payloadRecord, "license")) ?? asRecord(getValue(payloadRecord, "data")) ?? payloadRecord ?? {};
827
- const sourceRecord = asRecord(getValue(rootRecord, "metadata")) ?? rootRecord;
828
- const customer = asRecord(getValue(rootRecord, "customer")) ?? asRecord(getValue(sourceRecord, "customer")) ?? asRecord(getValue(payloadRecord, "customer")) ?? {};
829
- let releaseChannels = normalizeStringArray(
830
- getValue(rootRecord, "releaseChannels") ?? getValue(sourceRecord, "releaseChannels") ?? getValue(sourceRecord, "channels") ?? getValue(rootRecord, "channels") ?? getValue(sourceRecord, "channel") ?? getValue(rootRecord, "channel")
831
- ) ?? void 0;
832
- if (!releaseChannels) {
833
- releaseChannels = extractChannelNames(getValue(rootRecord, "channels")) ?? extractChannelNames(getValue(sourceRecord, "channels")) ?? void 0;
834
- }
835
- let installMethods = normalizeStringArray(
836
- getValue(rootRecord, "installMethods") ?? getValue(sourceRecord, "installMethods") ?? getValue(sourceRecord, "install_options") ?? getValue(rootRecord, "install_options") ?? getValue(sourceRecord, "installOptions")
837
- ) ?? void 0;
838
- if (!installMethods || installMethods.length === 0) {
839
- const resolved = [];
840
- const flag = (key) => getBoolean(rootRecord, key) ?? getBoolean(sourceRecord, key) ?? false;
841
- if (flag("isKotsInstallEnabled")) {
842
- resolved.push("Replicated KOTS");
843
- }
844
- if (flag("isHelmInstallEnabled")) {
845
- resolved.push("Helm");
846
- }
847
- if (flag("isHelmAirgapEnabled")) {
848
- resolved.push("Helm Airgap");
849
- }
850
- if (flag("isEmbeddedClusterDownloadEnabled") || flag("isEmbeddedClusterMultiNodeEnabled")) {
851
- resolved.push("Embedded Cluster");
852
- }
853
- if (flag("isKurlInstallEnabled")) {
854
- resolved.push("kURL");
855
- }
856
- if (flag("isGitopsSupported")) {
857
- resolved.push("GitOps");
858
- }
859
- if (resolved.length) {
860
- installMethods = Array.from(new Set(resolved));
861
- }
862
- }
863
- const expiresAtSource = getValue(sourceRecord, "expiresAt") ?? getValue(sourceRecord, "expireAt") ?? getValue(sourceRecord, "expire_at") ?? getValue(sourceRecord, "expiration") ?? getValue(sourceRecord, "expirationDate") ?? getValue(sourceRecord, "expires_on") ?? getValue(rootRecord, "expiresAt") ?? getValue(rootRecord, "expireAt") ?? getValue(rootRecord, "expire_at") ?? getValue(rootRecord, "expiration");
864
- const expiresAt = typeof expiresAtSource === "string" && expiresAtSource.trim().length ? expiresAtSource : expiresAtSource === null ? null : void 0;
865
- const baseFields = normalizeLicenseFields(
866
- getValue(rootRecord, "additionalFields") ?? getValue(sourceRecord, "additionalFields") ?? getValue(sourceRecord, "fields") ?? getValue(rootRecord, "fields") ?? getValue(payloadRecord, "fields") ?? getValue(payloadRecord, "additional_fields")
867
- );
868
- const entitlementFields = normalizeEntitlementFields(
869
- getValue(rootRecord, "entitlementFields") ?? getValue(sourceRecord, "entitlementFields"),
870
- getValue(rootRecord, "entitlementValues") ?? getValue(sourceRecord, "entitlementValues")
871
- );
872
- const fields = [
873
- ...baseFields,
874
- ...entitlementFields.filter(
875
- (field) => !baseFields.some((existing) => existing.key === field.key)
876
- )
877
- ];
878
- const statusFromSource = getString(sourceRecord, "status") ?? getString(sourceRecord, "state");
879
- const statusLabelFromSource = getString(sourceRecord, "statusLabel") ?? getString(sourceRecord, "stateLabel");
880
- const expiredFlag = getBoolean(sourceRecord, "isExpired") ?? getBoolean(rootRecord, "isExpired");
881
- const derivedStatus = statusFromSource ?? (typeof expiredFlag === "boolean" ? expiredFlag ? "expired" : "active" : void 0);
882
- const statusLabel = statusLabelFromSource ?? (derivedStatus ? derivedStatus.charAt(0).toUpperCase() + derivedStatus.slice(1) : void 0);
883
- const licenseType = getString(sourceRecord, "licenseType") ?? getString(rootRecord, "licenseType");
884
- const status = derivedStatus;
885
- const license = {
886
- id: getString(rootRecord, "id") ?? getString(sourceRecord, "id") ?? getString(sourceRecord, "licenseId") ?? getString(customer, "licenseId") ?? void 0,
887
- status,
888
- statusLabel,
889
- environment: getString(sourceRecord, "environment") ?? getString(sourceRecord, "tier") ?? licenseType ?? void 0,
890
- expiresAt: expiresAt ?? null,
891
- releaseChannels: releaseChannels ?? [
892
- getString(rootRecord, "channelName") ?? getString(rootRecord, "channel") ?? void 0
893
- ].filter((value) => Boolean(value)),
894
- installMethods,
895
- installNotes: getString(sourceRecord, "installNotes"),
896
- customerName: getString(sourceRecord, "customerName") ?? getString(customer, "name") ?? void 0,
897
- customerId: getString(sourceRecord, "customerId") ?? getString(customer, "id") ?? getString(rootRecord, "customerId") ?? void 0,
898
- customerOrganization: getString(customer, "organization") ?? getString(sourceRecord, "customerOrganization") ?? getString(rootRecord, "customerOrganization") ?? void 0,
899
- fields
1105
+ const envelope = await response.json();
1106
+ const data = envelope.data;
1107
+ return {
1108
+ releases: Array.isArray(data?.releases) ? data.releases : [],
1109
+ totalCount: data?.totalCount || 0
900
1110
  };
901
- return license;
902
- };
903
- var fetchLicenseDetails = defineServerAction({
904
- id: "license/fetch-details",
905
- description: "Fetches the authenticated user's enterprise license details.",
906
- visibility: "customer",
907
- tags: ["license", "entitlements"],
908
- async run({ token }, context) {
909
- if (typeof token !== "string" || token.trim().length === 0) {
910
- throw new Error("fetchLicenseDetails requires a non-empty token");
911
- }
912
- const endpoint = `${getApiOrigin()}/enterprise-portal/license`;
913
- if (process.env.NODE_ENV !== "production") {
914
- console.debug("[portal-components] fetching license via %s (Enterprise Portal API)", endpoint);
915
- }
916
- const response = await authenticatedFetch(endpoint, {
917
- method: "GET",
918
- token,
919
- headers: {
920
- accept: "application/json"
921
- },
922
- signal: context?.signal
923
- });
924
- if (!response.ok) {
925
- throw new Error(
926
- `License request failed (${response.status} ${response.statusText})`
927
- );
928
- }
929
- const payload = await response.json();
930
- const licenseData = payload.data;
931
- const license = normalizeLicensePayload(licenseData);
932
- return {
933
- license,
934
- raw: licenseData ?? null
935
- };
936
- }
937
- });
1111
+ }
1112
+ var listReleases = traceServerAction("listReleases", listReleasesImpl);
938
1113
  var getSecurityInfo = defineServerAction({
939
1114
  id: "security/get-info",
940
1115
  description: "Fetches CVE security scan results for a specific release",
@@ -1413,12 +1588,12 @@ var InfoRow = ({
1413
1588
  icon,
1414
1589
  title,
1415
1590
  children
1416
- }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start space-x-3", children: [
1417
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-1 text-gray-900", children: icon }),
1418
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1419
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-sm font-medium text-gray-900", children: title }),
1420
- children
1421
- ] })
1591
+ }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1592
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-baseline gap-2", children: [
1593
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-4 w-4 flex-shrink-0 items-center justify-center text-gray-900", children: icon }),
1594
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-sm font-medium leading-4 text-gray-900", children: title })
1595
+ ] }),
1596
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pl-6", children })
1422
1597
  ] });
1423
1598
  var renderInstallOrChannel = (values) => {
1424
1599
  if (!values || values.length === 0) {
@@ -1433,12 +1608,12 @@ var buildPrimaryRows = (license) => {
1433
1608
  node: /* @__PURE__ */ jsxRuntime.jsxs(
1434
1609
  InfoRow,
1435
1610
  {
1436
- icon: /* @__PURE__ */ jsxRuntime.jsx(ShieldIcon, { className: "h-5 w-5" }),
1611
+ icon: /* @__PURE__ */ jsxRuntime.jsx(ShieldIcon, { className: "h-4 w-4" }),
1437
1612
  title: "License Status",
1438
1613
  children: [
1439
1614
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-1 flex items-center text-sm text-gray-600", children: [
1440
1615
  /* @__PURE__ */ jsxRuntime.jsx(CheckIcon, { className: "mr-1.5 h-4 w-4 text-green-500" }),
1441
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: license.statusLabel ?? (license.status ? license.status.charAt(0).toUpperCase() + license.status.slice(1) : "Unknown") })
1616
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: license.isExpired ? "Expired" : "Active" })
1442
1617
  ] }),
1443
1618
  license.environment ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-sm text-gray-500", children: license.environment }) : null
1444
1619
  ]
@@ -1450,7 +1625,7 @@ var buildPrimaryRows = (license) => {
1450
1625
  node: /* @__PURE__ */ jsxRuntime.jsx(
1451
1626
  InfoRow,
1452
1627
  {
1453
- icon: /* @__PURE__ */ jsxRuntime.jsx(CalendarIcon, { className: "h-5 w-5" }),
1628
+ icon: /* @__PURE__ */ jsxRuntime.jsx(CalendarIcon, { className: "h-4 w-4" }),
1454
1629
  title: "Expiration Date",
1455
1630
  children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-sm text-gray-500", children: formatExpiration(license.expiresAt) })
1456
1631
  }
@@ -1461,7 +1636,7 @@ var buildPrimaryRows = (license) => {
1461
1636
  node: /* @__PURE__ */ jsxRuntime.jsx(
1462
1637
  InfoRow,
1463
1638
  {
1464
- icon: /* @__PURE__ */ jsxRuntime.jsx(ShieldIcon, { className: "h-5 w-5" }),
1639
+ icon: /* @__PURE__ */ jsxRuntime.jsx(ShieldIcon, { className: "h-4 w-4" }),
1465
1640
  title: "Release Channel(s)",
1466
1641
  children: renderInstallOrChannel(license.releaseChannels)
1467
1642
  }
@@ -1472,7 +1647,7 @@ var buildPrimaryRows = (license) => {
1472
1647
  node: /* @__PURE__ */ jsxRuntime.jsx(
1473
1648
  InfoRow,
1474
1649
  {
1475
- icon: /* @__PURE__ */ jsxRuntime.jsx(DownloadIcon, { className: "h-5 w-5" }),
1650
+ icon: /* @__PURE__ */ jsxRuntime.jsx(DownloadIcon, { className: "h-4 w-4" }),
1476
1651
  title: "Install Options",
1477
1652
  children: renderInstallOrChannel(license.installMethods)
1478
1653
  }
@@ -1483,7 +1658,7 @@ var buildPrimaryRows = (license) => {
1483
1658
  node: /* @__PURE__ */ jsxRuntime.jsxs(
1484
1659
  InfoRow,
1485
1660
  {
1486
- icon: /* @__PURE__ */ jsxRuntime.jsx(BuildingIcon, { className: "h-5 w-5" }),
1661
+ icon: /* @__PURE__ */ jsxRuntime.jsx(BuildingIcon, { className: "h-4 w-4" }),
1487
1662
  title: "Customer Name",
1488
1663
  children: [
1489
1664
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-sm text-gray-500", children: license.customerName ?? "Unknown customer" }),
@@ -1501,7 +1676,7 @@ var renderFields = (fields) => {
1501
1676
  if (!fields.length) {
1502
1677
  return /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-500", children: "No additional fields available." });
1503
1678
  }
1504
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3", children: fields.map((field) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", children: [
1679
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-1 gap-6 md:grid-cols-2 xl:grid-cols-3", children: fields.map((field) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", children: [
1505
1680
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-gray-900", children: field.label }),
1506
1681
  /* @__PURE__ */ jsxRuntime.jsx(
1507
1682
  "p",
@@ -1522,9 +1697,7 @@ var LicenseDetails = async ({
1522
1697
  if (typeof token !== "string" || token.trim().length === 0) {
1523
1698
  throw new Error("LicenseDetails component requires a non-empty token");
1524
1699
  }
1525
- const response = await fetchLicenseDetails.run({ token });
1526
- console.debug("[portal-components] license response", response.raw);
1527
- const { license } = response;
1700
+ const license = await fetchLicense();
1528
1701
  const rows = buildPrimaryRows(license);
1529
1702
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded border border-gray-100 bg-white p-8 shadow-[0_18px_45px_rgba(17,24,39,0.08)]", children: [
1530
1703
  /* @__PURE__ */ jsxRuntime.jsxs("header", { className: "flex flex-col border-b border-gray-100 pb-6", children: [
@@ -1532,7 +1705,7 @@ var LicenseDetails = async ({
1532
1705
  description ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-2 text-sm text-gray-600", children: description }) : null
1533
1706
  ] }),
1534
1707
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-6 space-y-6", children: [
1535
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-lg border border-gray-100 bg-white p-6 sm:p-8", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3", children: rows.map((row) => /* @__PURE__ */ jsxRuntime.jsx("div", { children: row.node }, row.key)) }) }),
1708
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-lg border border-gray-100 bg-white p-6 sm:p-8", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-1 items-start gap-6 md:grid-cols-2 xl:grid-cols-3", children: rows.map((row) => /* @__PURE__ */ jsxRuntime.jsx("div", { children: row.node }, row.key)) }) }),
1536
1709
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border border-gray-100 bg-white p-6 sm:p-8", children: [
1537
1710
  /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "mb-4 text-lg font-medium text-gray-900", children: "Additional License Fields" }),
1538
1711
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-x-auto", children: renderFields(license.fields ?? []) })
@@ -1725,57 +1898,49 @@ var TopNav = async ({
1725
1898
  currentCustomerId,
1726
1899
  onChangeTeam,
1727
1900
  userMenuChildren,
1728
- logoutButton
1901
+ logoutButton,
1902
+ branding: brandingProp
1729
1903
  }) => {
1730
1904
  const displayLabel = userMenuLabel || (customerName ? `Team: ${customerName}` : "Team: Example");
1731
1905
  let logo;
1732
1906
  let brandTitle;
1733
- let customColor1;
1734
- let customColor2;
1735
- const normalizeColor = (color) => {
1736
- if (!color || typeof color !== "string") {
1737
- return void 0;
1738
- }
1739
- const trimmed = color.trim();
1740
- if (/^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$/.test(trimmed)) {
1741
- return trimmed.startsWith("#") ? trimmed : `#${trimmed}`;
1742
- }
1743
- return trimmed;
1744
- };
1745
- try {
1746
- const branding = await fetchCustomBranding();
1747
- if (branding.brandingData) {
1748
- const decoded = buffer.Buffer.from(branding.brandingData, "base64").toString(
1749
- "utf-8"
1750
- );
1751
- try {
1752
- const parsed = JSON.parse(decoded);
1753
- if (parsed?.logo && typeof parsed.logo === "string") {
1754
- logo = parsed.logo;
1755
- }
1756
- if (parsed?.title && typeof parsed.title === "string") {
1757
- const normalizedTitle = parsed.title.trim();
1758
- if (normalizedTitle) {
1759
- brandTitle = normalizedTitle;
1760
- }
1761
- }
1762
- if (parsed?.customColor1 && typeof parsed.customColor1 === "string") {
1763
- customColor1 = normalizeColor(parsed.customColor1);
1764
- }
1765
- if (parsed?.customColor2 && typeof parsed.customColor2 === "string") {
1766
- customColor2 = normalizeColor(parsed.customColor2);
1767
- }
1768
- } catch (error) {
1769
- console.debug(
1770
- "[portal-components] unable to parse branding JSON",
1771
- error
1772
- );
1907
+ let primaryColor;
1908
+ let secondaryColor;
1909
+ let background;
1910
+ let backgroundImageUrl;
1911
+ let backgroundGradientStart;
1912
+ let backgroundGradientEnd;
1913
+ if (brandingProp) {
1914
+ if (brandingProp.logo && typeof brandingProp.logo === "string") {
1915
+ logo = brandingProp.logo;
1916
+ }
1917
+ if (brandingProp.title && typeof brandingProp.title === "string") {
1918
+ const normalizedTitle = brandingProp.title.trim();
1919
+ if (normalizedTitle) {
1920
+ brandTitle = normalizedTitle;
1773
1921
  }
1774
- } else {
1775
- console.debug("[portal-components] branding", branding);
1776
1922
  }
1777
- } catch (error) {
1778
- console.debug("[portal-components] branding fetch failed", error);
1923
+ primaryColor = normalizeColor(brandingProp.primaryColor);
1924
+ secondaryColor = normalizeColor(brandingProp.secondaryColor);
1925
+ background = brandingProp.background;
1926
+ backgroundImageUrl = brandingProp.backgroundImage;
1927
+ backgroundGradientStart = normalizeColor(brandingProp.backgroundGradientStart);
1928
+ backgroundGradientEnd = normalizeColor(brandingProp.backgroundGradientEnd);
1929
+ } else {
1930
+ try {
1931
+ const brandingResponse = await fetchCustomBranding();
1932
+ const branding = decodeBranding({ brandingData: brandingResponse.brandingData });
1933
+ logo = branding.logo;
1934
+ brandTitle = branding.title;
1935
+ primaryColor = branding.primaryColor;
1936
+ secondaryColor = branding.secondaryColor;
1937
+ background = branding.background;
1938
+ backgroundImageUrl = branding.backgroundImage;
1939
+ backgroundGradientStart = branding.backgroundGradientStart;
1940
+ backgroundGradientEnd = branding.backgroundGradientEnd;
1941
+ } catch (error) {
1942
+ console.debug("[portal-components] branding fetch failed", error);
1943
+ }
1779
1944
  }
1780
1945
  const baseLinks = links ?? defaultTopNavLinks;
1781
1946
  const hiddenSet = hiddenLabels ? new Set(hiddenLabels) : null;
@@ -1786,20 +1951,32 @@ var TopNav = async ({
1786
1951
  resolvedLinks = [...resolvedLinks, ...additionalLinks];
1787
1952
  }
1788
1953
  resolvedLinks = orderLinks(resolvedLinks, order);
1789
- const gradientStart = customColor1 ?? "rgb(235, 102, 88)";
1790
- const gradientEnd = customColor2 ?? customColor1 ?? "rgb(184, 83, 71)";
1954
+ const getHeaderBackgroundStyle = () => {
1955
+ if (background === "image" && backgroundImageUrl) {
1956
+ return {
1957
+ background: `url(${backgroundImageUrl})`,
1958
+ backgroundImage: `linear-gradient(to top, rgba(255, 255, 255, 0.3) 30%, rgba(255, 255, 255, 0)), url(${backgroundImageUrl})`,
1959
+ backgroundSize: "cover",
1960
+ backgroundPosition: "center",
1961
+ backgroundRepeat: "no-repeat"
1962
+ };
1963
+ }
1964
+ const gradientStart = background === "custom" && backgroundGradientStart ? backgroundGradientStart : primaryColor;
1965
+ const gradientEnd = background === "custom" && backgroundGradientEnd ? backgroundGradientEnd : secondaryColor;
1966
+ return {
1967
+ backgroundImage: `linear-gradient(to top, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0) 33%), linear-gradient(${gradientStart}, ${gradientEnd})`,
1968
+ backgroundRepeat: "no-repeat",
1969
+ backgroundSize: "100% 100%"
1970
+ };
1971
+ };
1791
1972
  return /* @__PURE__ */ jsxRuntime.jsx(
1792
1973
  "div",
1793
1974
  {
1794
1975
  className: "relative flex h-[280px] w-full items-start justify-center",
1795
- style: {
1796
- backgroundImage: `linear-gradient(to top, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0) 33%), linear-gradient(${gradientStart}, ${gradientEnd})`,
1797
- backgroundRepeat: "no-repeat",
1798
- backgroundSize: "100% 100%"
1799
- },
1800
- children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mx-auto mt-[30px] w-full max-w-[1248px] px-6", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-[135px] flex-col justify-between space-y-4 rounded bg-[#ffffffe6] px-6 pt-6 pb-4 shadow-[0_10px_60px_rgba(16,16,16,0.35)]", children: [
1976
+ style: getHeaderBackgroundStyle(),
1977
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mx-auto mt-[30px] w-full max-w-[1248px] px-6", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-[142px] flex-col justify-between space-y-4 rounded bg-[#ffffffe6] px-6 pt-6 pb-4 shadow-[0_10px_60px_rgba(16,16,16,0.35)]", children: [
1801
1978
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
1802
- logo || brandTitle ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
1979
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
1803
1980
  logo ? (
1804
1981
  // eslint-disable-next-line @next/next/no-img-element
1805
1982
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -1814,9 +1991,24 @@ var TopNav = async ({
1814
1991
  }
1815
1992
  }
1816
1993
  )
1817
- ) : null,
1818
- brandTitle ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-lg font-semibold text-gray-900", children: brandTitle }) : null
1819
- ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", {}),
1994
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(
1995
+ "svg",
1996
+ {
1997
+ className: "h-8 w-8 text-gray-400",
1998
+ viewBox: "0 0 24 24",
1999
+ fill: "none",
2000
+ stroke: "currentColor",
2001
+ strokeWidth: "1.5",
2002
+ "aria-hidden": "true",
2003
+ children: [
2004
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "10" }),
2005
+ /* @__PURE__ */ jsxRuntime.jsx("ellipse", { cx: "12", cy: "12", rx: "4", ry: "10" }),
2006
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M2 12h20" })
2007
+ ]
2008
+ }
2009
+ ),
2010
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xl font-bold text-gray-900", children: brandTitle || "Enterprise Portal" })
2011
+ ] }),
1820
2012
  /* @__PURE__ */ jsxRuntime.jsxs("details", { className: "group relative", children: [
1821
2013
  /* @__PURE__ */ jsxRuntime.jsxs("summary", { className: "flex cursor-pointer items-center gap-2 text-sm font-medium text-gray-600 hover:text-gray-900 list-none", children: [
1822
2014
  /* @__PURE__ */ jsxRuntime.jsxs(
@@ -1878,7 +2070,7 @@ var TopNav = async ({
1878
2070
  ] })
1879
2071
  ] })
1880
2072
  ] }),
1881
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-3 flex flex-wrap gap-3 border-b border-gray-200 pb-2 text-sm font-medium text-gray-500", children: resolvedLinks.map(({ label, icon, href }) => {
2073
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-3 flex flex-wrap gap-3 pb-2 text-sm font-medium text-gray-500", children: resolvedLinks.map(({ label, icon, href }) => {
1882
2074
  const isActive = activeLabel === label;
1883
2075
  const className = `flex items-center gap-2 px-4 py-1 transition text-gray-500 ${isActive ? "underline underline-offset-8 decoration-2" : ""}`;
1884
2076
  if (href) {
@@ -1931,9 +2123,17 @@ var headingClass = "text-lg font-semibold text-gray-900";
1931
2123
  var contentClass = "mt-4 flex-1 space-y-3";
1932
2124
  var itemClass = "flex items-center gap-3 text-sm text-gray-600";
1933
2125
  var iconClass = "h-5 w-5 text-gray-500";
1934
- var footerClass = "mt-6 flex justify-end text-sm font-semibold text-primary hover:text-primary/80";
1935
- var badgeClass = "ml-2 inline-flex h-5 min-w-[20px] items-center justify-center rounded-full bg-blue-500 px-1.5 text-xs font-medium text-white";
1936
- var Badge = ({ count }) => /* @__PURE__ */ jsxRuntime.jsx("span", { className: badgeClass, "aria-label": `${count} updates available`, children: count });
2126
+ var footerClass = "mt-6 flex justify-end text-sm font-semibold hover:opacity-80";
2127
+ var badgeClass = "ml-2 inline-flex h-5 min-w-[20px] items-center justify-center rounded-full px-1.5 text-xs font-medium text-white";
2128
+ var Badge = ({ count }) => /* @__PURE__ */ jsxRuntime.jsx(
2129
+ "span",
2130
+ {
2131
+ className: badgeClass,
2132
+ style: { backgroundColor: `var(--portal-branding-primary, ${DEFAULT_SECONDARY_COLOR})` },
2133
+ "aria-label": `${count} updates available`,
2134
+ children: count
2135
+ }
2136
+ );
1937
2137
  var UpdatesCard = ({
1938
2138
  onlineActiveCount = 0,
1939
2139
  airgapCount = 0,
@@ -1961,7 +2161,7 @@ var UpdatesCard = ({
1961
2161
  airgapUpdates > 0 && /* @__PURE__ */ jsxRuntime.jsx(Badge, { count: airgapUpdates })
1962
2162
  ] })
1963
2163
  ] }),
1964
- /* @__PURE__ */ jsxRuntime.jsx("footer", { className: footerClass, children: /* @__PURE__ */ jsxRuntime.jsx(Link__default.default, { href: "/update", children: "View updates \u2192" }) })
2164
+ /* @__PURE__ */ jsxRuntime.jsx("footer", { className: footerClass, children: /* @__PURE__ */ jsxRuntime.jsx(Link__default.default, { href: "/update", style: { color: `var(--portal-branding-secondary, ${DEFAULT_SECONDARY_COLOR})` }, children: "View updates \u2192" }) })
1965
2165
  ] });
1966
2166
  UpdatesCard.displayName = "UpdatesCard";
1967
2167
  var UploadIcon = (props) => /* @__PURE__ */ jsxRuntime.jsx(
@@ -2126,7 +2326,7 @@ var TeamsSection = ({
2126
2326
  type: "button",
2127
2327
  onClick: () => !isCurrentTeam && onTeamSwitch?.(team),
2128
2328
  disabled: isCurrentTeam,
2129
- className: `flex w-full items-center justify-between rounded-lg border p-4 text-left transition ${isCurrentTeam ? "cursor-default border-gray-300 bg-gray-50" : "cursor-pointer border-gray-200 hover:border-indigo-500"}`,
2329
+ className: `flex w-full items-center justify-between rounded-lg border p-4 text-left transition ${isCurrentTeam ? "cursor-default border-gray-300 bg-gray-50" : "cursor-pointer border-gray-200 hover:border-gray-400"}`,
2130
2330
  style: !isCurrentTeam && primaryColor ? { "--hover-border": primaryColor } : void 0,
2131
2331
  children: [
2132
2332
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
@@ -2134,7 +2334,7 @@ var TeamsSection = ({
2134
2334
  "svg",
2135
2335
  {
2136
2336
  className: "h-4 w-4",
2137
- style: { color: primaryColor || "#6366f1" },
2337
+ style: { color: primaryColor || DEFAULT_SECONDARY_COLOR },
2138
2338
  fill: "none",
2139
2339
  viewBox: "0 0 24 24",
2140
2340
  stroke: "currentColor",
@@ -2158,7 +2358,7 @@ var TeamsSection = ({
2158
2358
  "svg",
2159
2359
  {
2160
2360
  className: "h-4 w-4",
2161
- style: { color: primaryColor || "#6366f1" },
2361
+ style: { color: primaryColor || DEFAULT_SECONDARY_COLOR },
2162
2362
  fill: "none",
2163
2363
  viewBox: "0 0 24 24",
2164
2364
  stroke: "currentColor",
@@ -2223,7 +2423,10 @@ var ProfileSection = ({
2223
2423
  resetForm();
2224
2424
  setIsEditing(true);
2225
2425
  };
2226
- const buttonStyle = primaryColor ? { backgroundColor: primaryColor, borderColor: primaryColor } : void 0;
2426
+ const buttonStyle = {
2427
+ backgroundColor: primaryColor || DEFAULT_SECONDARY_COLOR,
2428
+ borderColor: primaryColor || DEFAULT_SECONDARY_COLOR
2429
+ };
2227
2430
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-2xl border border-gray-200 bg-white p-6 shadow-sm", children: [
2228
2431
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-4", children: [
2229
2432
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
@@ -2250,7 +2453,7 @@ var ProfileSection = ({
2250
2453
  onClick: handleSave,
2251
2454
  disabled: isUpdating,
2252
2455
  style: buttonStyle,
2253
- className: "inline-flex items-center rounded-md bg-indigo-500 px-3 py-1.5 text-sm font-medium text-white shadow-sm transition hover:bg-indigo-600 disabled:opacity-50",
2456
+ className: "inline-flex items-center justify-center rounded-md px-4 py-2 text-sm font-medium text-white shadow-sm transition-opacity duration-200 hover:opacity-90 disabled:opacity-50 min-w-[70px]",
2254
2457
  children: isUpdating ? "Saving..." : "Save"
2255
2458
  }
2256
2459
  )
@@ -2260,7 +2463,7 @@ var ProfileSection = ({
2260
2463
  type: "button",
2261
2464
  onClick: handleEdit,
2262
2465
  style: buttonStyle,
2263
- className: "inline-flex items-center rounded-md bg-indigo-500 px-3 py-1.5 text-sm font-medium text-white shadow-sm transition hover:bg-indigo-600",
2466
+ className: "inline-flex items-center justify-center rounded-md px-4 py-2 text-sm font-medium text-white shadow-sm transition-opacity duration-200 hover:opacity-90 min-w-[70px]",
2264
2467
  children: "Edit"
2265
2468
  }
2266
2469
  )
@@ -2376,11 +2579,11 @@ UserSettings.displayName = "UserSettings";
2376
2579
  var baseCardClass3 = "flex h-full flex-col rounded-xl border border-gray-200 bg-white p-6 shadow-[0_16px_32px_rgba(15,23,42,0.05)]";
2377
2580
  var headingClass3 = "text-lg font-semibold text-gray-900";
2378
2581
  var bodySpacerClass = "mt-4 flex-1";
2379
- var footerClass3 = "mt-6 flex justify-end text-sm font-semibold text-primary hover:text-primary/80";
2582
+ var footerClass3 = "mt-6 flex justify-end text-sm font-semibold hover:opacity-80";
2380
2583
  var UserSettingsCard = () => /* @__PURE__ */ jsxRuntime.jsxs("section", { className: baseCardClass3, "aria-labelledby": "user-settings-card-heading", children: [
2381
2584
  /* @__PURE__ */ jsxRuntime.jsx("header", { children: /* @__PURE__ */ jsxRuntime.jsx("h2", { id: "user-settings-card-heading", className: headingClass3, children: "User Settings" }) }),
2382
2585
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: bodySpacerClass }),
2383
- /* @__PURE__ */ jsxRuntime.jsx("footer", { className: footerClass3, children: /* @__PURE__ */ jsxRuntime.jsx(Link__default.default, { href: "/user-settings", children: "View user settings \u2192" }) })
2586
+ /* @__PURE__ */ jsxRuntime.jsx("footer", { className: footerClass3, children: /* @__PURE__ */ jsxRuntime.jsx(Link__default.default, { href: "/user-settings", style: { color: `var(--portal-branding-secondary, ${DEFAULT_SECONDARY_COLOR})` }, children: "View user settings \u2192" }) })
2384
2587
  ] });
2385
2588
  UserSettingsCard.displayName = "UserSettingsCard";
2386
2589
  var UsersIcon = (props) => /* @__PURE__ */ jsxRuntime.jsx(
@@ -2418,7 +2621,7 @@ var headingClass4 = "text-lg font-semibold text-gray-900";
2418
2621
  var contentClass3 = "mt-4 flex-1 space-y-3";
2419
2622
  var itemClass3 = "flex items-center gap-3 text-sm text-gray-600";
2420
2623
  var iconClass3 = "h-5 w-5 text-gray-500";
2421
- var footerClass4 = "mt-6 flex justify-end text-sm font-semibold text-primary hover:text-primary/80";
2624
+ var footerClass4 = "mt-6 flex justify-end text-sm font-semibold hover:opacity-80";
2422
2625
  var TeamSettingsCard = ({
2423
2626
  userCount = 0,
2424
2627
  serviceAccountCount = 0
@@ -2442,7 +2645,7 @@ var TeamSettingsCard = ({
2442
2645
  ] })
2443
2646
  ] })
2444
2647
  ] }),
2445
- /* @__PURE__ */ jsxRuntime.jsx("footer", { className: footerClass4, children: /* @__PURE__ */ jsxRuntime.jsx(Link__default.default, { href: "/team-settings", children: "View team settings \u2192" }) })
2648
+ /* @__PURE__ */ jsxRuntime.jsx("footer", { className: footerClass4, children: /* @__PURE__ */ jsxRuntime.jsx(Link__default.default, { href: "/team-settings", style: { color: `var(--portal-branding-secondary, ${DEFAULT_SECONDARY_COLOR})` }, children: "View team settings \u2192" }) })
2446
2649
  ] });
2447
2650
  TeamSettingsCard.displayName = "TeamSettingsCard";
2448
2651
  var UpdateLayout = ({ children }) => {
@@ -2453,43 +2656,128 @@ var UpdateLayout = ({ children }) => {
2453
2656
  };
2454
2657
  UpdateLayout.displayName = "UpdateLayout";
2455
2658
 
2659
+ // src/saml-handlers.ts
2660
+ async function setSamlSessionImpl(token) {
2661
+ try {
2662
+ if (!token) {
2663
+ return { success: false, message: "No token provided" };
2664
+ }
2665
+ const cleanToken = token.startsWith("Bearer ") ? token.slice(7) : token;
2666
+ const { cookies: cookies2 } = await import('next/headers');
2667
+ const cookieStore = await cookies2();
2668
+ const isHttpOrigin = process.env.REPLICATED_APP_ORIGIN?.startsWith("http://");
2669
+ const secure = true;
2670
+ const sameSiteValue = isHttpOrigin ? "none" : "lax";
2671
+ cookieStore.set("portal_session", cleanToken, {
2672
+ httpOnly: true,
2673
+ secure,
2674
+ sameSite: sameSiteValue,
2675
+ path: "/"
2676
+ });
2677
+ return { success: true };
2678
+ } catch (error) {
2679
+ console.error("[saml-handlers] Error setting session:", error);
2680
+ return {
2681
+ success: false,
2682
+ message: "Failed to establish session"
2683
+ };
2684
+ }
2685
+ }
2686
+ async function handleSamlAcs(request) {
2687
+ try {
2688
+ const apiOrigin = getApiOrigin();
2689
+ const formData = await request.formData();
2690
+ const response = await fetch(`${apiOrigin}/saml/acs`, {
2691
+ method: "POST",
2692
+ body: formData,
2693
+ redirect: "manual"
2694
+ });
2695
+ if (response.status === 302 || response.status === 301) {
2696
+ const location = response.headers.get("Location");
2697
+ if (location) {
2698
+ return Response.redirect(location, response.status);
2699
+ }
2700
+ }
2701
+ console.error("[saml-acs] Error response from backend:", {
2702
+ status: response.status,
2703
+ statusText: response.statusText
2704
+ });
2705
+ let errorMessage = "SAML authentication failed";
2706
+ if (response.status === 401 || response.status === 403) {
2707
+ errorMessage = "Authentication failed. Please check your credentials or contact your administrator.";
2708
+ } else if (response.status === 400) {
2709
+ errorMessage = "Invalid SAML request. Please try again.";
2710
+ }
2711
+ return Response.redirect(
2712
+ new URL(`/error?code=${response.status}&message=${encodeURIComponent(errorMessage)}`, request.url),
2713
+ 302
2714
+ );
2715
+ } catch (error) {
2716
+ console.error("[saml-acs] Error processing SAML response:", error);
2717
+ return Response.redirect(
2718
+ new URL(`/error?code=500&message=${encodeURIComponent("SAML authentication error")}`, request.url),
2719
+ 302
2720
+ );
2721
+ }
2722
+ }
2723
+
2456
2724
  // src/index.ts
2457
2725
  var portalComponentsVersion = package_default.version;
2458
2726
 
2459
2727
  exports.Button = Button;
2728
+ exports.DEFAULT_FAVICON = DEFAULT_FAVICON;
2729
+ exports.DEFAULT_PRIMARY_COLOR = DEFAULT_PRIMARY_COLOR;
2730
+ exports.DEFAULT_SECONDARY_COLOR = DEFAULT_SECONDARY_COLOR;
2460
2731
  exports.LicenseDetails = LicenseDetails;
2461
2732
  exports.SupportCard = SupportCard;
2462
2733
  exports.TeamSettingsCard = TeamSettingsCard;
2463
2734
  exports.TopNav = TopNav;
2735
+ exports.UnauthorizedError = UnauthorizedError;
2464
2736
  exports.UpdateLayout = UpdateLayout;
2465
2737
  exports.UpdatesCard = UpdatesCard;
2466
2738
  exports.UserSettings = UserSettings;
2467
2739
  exports.UserSettingsCard = UserSettingsCard;
2740
+ exports.authenticatedFetch = authenticatedFetch;
2741
+ exports.convertToReleaseEntry = convertToReleaseEntry;
2468
2742
  exports.createPortalTheme = createPortalTheme;
2743
+ exports.decodeBranding = decodeBranding;
2469
2744
  exports.decodeJwtPayload = decodeJwtPayload;
2470
2745
  exports.defaultTopNavLinks = defaultTopNavLinks;
2471
2746
  exports.defineServerAction = defineServerAction;
2747
+ exports.deleteSessionCookie = deleteSessionCookie;
2472
2748
  exports.deleteSupportBundle = deleteSupportBundle;
2473
2749
  exports.downloadSecuritySBOM = downloadSecuritySBOM;
2474
2750
  exports.downloadSupportBundle = downloadSupportBundle;
2475
2751
  exports.fetchCurrentUser = fetchCurrentUser;
2476
2752
  exports.fetchCustomBranding = fetchCustomBranding;
2477
2753
  exports.fetchDashboardComposite = fetchDashboardComposite;
2478
- exports.fetchLicenseDetails = fetchLicenseDetails;
2754
+ exports.fetchLicense = fetchLicense;
2479
2755
  exports.fetchNotifications = fetchNotifications;
2756
+ exports.formatBytes = formatBytes;
2757
+ exports.formatDate = formatDate;
2758
+ exports.formatDateShort = formatDateShort;
2759
+ exports.formatDateTime = formatDateTime;
2760
+ exports.formatDateTimeLocal = formatDateTimeLocal;
2480
2761
  exports.getCustomerIdFromToken = getCustomerIdFromToken;
2481
2762
  exports.getSecurityInfo = getSecurityInfo;
2482
2763
  exports.getSecurityInfoDiff = getSecurityInfoDiff;
2483
2764
  exports.getSecurityInfoSBOM = getSecurityInfoSBOM;
2484
2765
  exports.getSupportBundleUploadUrl = getSupportBundleUploadUrl;
2766
+ exports.handleSamlAcs = handleSamlAcs;
2485
2767
  exports.initiateLogin = initiateLogin;
2768
+ exports.isHttpApiOrigin = isHttpApiOrigin;
2769
+ exports.isRedirectError = isRedirectError;
2486
2770
  exports.listReleases = listReleases;
2487
2771
  exports.listSupportBundles = listSupportBundles;
2772
+ exports.normalizeColor = normalizeColor;
2488
2773
  exports.portalComponentsVersion = portalComponentsVersion;
2489
2774
  exports.portalThemeTokens = portalThemeTokens;
2775
+ exports.sanitizeUrlForCss = sanitizeUrlForCss;
2776
+ exports.setSamlSessionImpl = setSamlSessionImpl;
2490
2777
  exports.updateNotifications = updateNotifications;
2491
2778
  exports.updateUser = updateUser;
2492
2779
  exports.uploadSupportBundle = uploadSupportBundle;
2780
+ exports.validateSession = validateSession;
2493
2781
  exports.verifyMagicLink = verifyMagicLink;
2494
2782
  //# sourceMappingURL=index.js.map
2495
2783
  //# sourceMappingURL=index.js.map