@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/esm/index.js CHANGED
@@ -1,16 +1,126 @@
1
1
  import { forwardRef, cache, useState, useMemo } from 'react';
2
2
  import { jsxs, jsx } from 'react/jsx-runtime';
3
3
  import { Buffer } from 'buffer';
4
+ import { cookies } from 'next/headers';
4
5
  import Link from 'next/link';
5
6
 
6
7
  /**
7
8
  * Enterprise Portal Components
8
9
  * This file is generated by tsup. Do not edit manually.
9
10
  */
11
+ var __defProp = Object.defineProperty;
12
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
13
+ var __getOwnPropNames = Object.getOwnPropertyNames;
14
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
15
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
16
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
17
+ }) : x)(function(x) {
18
+ if (typeof require !== "undefined") return require.apply(this, arguments);
19
+ throw Error('Dynamic require of "' + x + '" is not supported');
20
+ });
21
+ var __esm = (fn, res) => function __init() {
22
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
23
+ };
24
+ var __export = (target, all) => {
25
+ for (var name in all)
26
+ __defProp(target, name, { get: all[name], enumerable: true });
27
+ };
28
+ var __copyProps = (to, from, except, desc) => {
29
+ if (from && typeof from === "object" || typeof from === "function") {
30
+ for (let key of __getOwnPropNames(from))
31
+ if (!__hasOwnProp.call(to, key) && key !== except)
32
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
33
+ }
34
+ return to;
35
+ };
36
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
37
+
38
+ // datadog/tracer.ts
39
+ var tracer_exports = {};
40
+ __export(tracer_exports, {
41
+ default: () => tracer_default
42
+ });
43
+ var rawFlag, isEnabled, tracer, getRoutePattern, tracer_default;
44
+ var init_tracer = __esm({
45
+ "datadog/tracer.ts"() {
46
+ rawFlag = String(process.env.USE_DATADOG_APM || "").toLowerCase();
47
+ isEnabled = rawFlag === "true";
48
+ process.env.DD_TRACE_ENABLED = isEnabled ? "1" : "0";
49
+ tracer = null;
50
+ if (isEnabled) {
51
+ const serviceName = process.env.DD_SERVICE || "enterprise-portal";
52
+ const environment = process.env.DD_ENV || process.env.NODE_ENV || "development";
53
+ const version = process.env.DD_VERSION || process.env.NEXT_PUBLIC_VERSION || "0.0.0-dev";
54
+ const agentHost = process.env.DD_AGENT_HOST || process.env.DATADOG_AGENT_HOST || "127.0.0.1";
55
+ const agentPort = process.env.DD_TRACE_AGENT_PORT || "8126";
56
+ process.env.DD_SERVICE = serviceName;
57
+ process.env.DD_ENV = environment;
58
+ {
59
+ process.env.DD_VERSION = version;
60
+ }
61
+ process.env.DD_AGENT_HOST = agentHost;
62
+ process.env.DD_TRACE_AGENT_PORT = agentPort;
63
+ const dbmPropagationMode = process.env.DD_DBM_PROPAGATION_MODE || "full";
64
+ process.env.DD_DBM_PROPAGATION_MODE = dbmPropagationMode;
65
+ try {
66
+ const ddTrace = __require("dd-trace");
67
+ tracer = ddTrace.init({
68
+ service: serviceName,
69
+ env: environment,
70
+ version,
71
+ logInjection: true,
72
+ runtimeMetrics: true,
73
+ appsec: false,
74
+ profiling: false,
75
+ startupLogs: true
76
+ // Enable for debugging
77
+ });
78
+ console.log(`[datadog] Tracer initialized: service=${serviceName}, env=${environment}, version=${version}, agent=${agentHost}:${agentPort}`);
79
+ tracer.use("dns", false);
80
+ tracer.use("net", false);
81
+ tracer.use("http", {
82
+ server: {
83
+ hooks: {
84
+ request: (span, req) => {
85
+ if (!span) return;
86
+ const url = req?.url || "";
87
+ const method = req?.method || "GET";
88
+ const path = url.split("?")[0];
89
+ if (path.startsWith("/_next/")) {
90
+ span.context()._trace.isRecording = false;
91
+ return;
92
+ }
93
+ const routePattern = getRoutePattern(path);
94
+ span.setTag("resource.name", `${method} ${routePattern}`);
95
+ span.setTag("http.route", routePattern);
96
+ }
97
+ }
98
+ }
99
+ });
100
+ } catch (err) {
101
+ console.error("[datadog] failed to initialize tracing", err);
102
+ tracer = null;
103
+ }
104
+ }
105
+ getRoutePattern = (path) => {
106
+ const routePatterns = [
107
+ // Update instance routes - normalize dynamic segments (capture suffix to preserve sub-routes)
108
+ { pattern: /^\/update\/instance\/[^/]+(.*)$/, replacement: "/update/instance/[instanceId]$1" }
109
+ ];
110
+ for (const { pattern, replacement } of routePatterns) {
111
+ if (pattern.test(path)) {
112
+ return path.replace(pattern, replacement);
113
+ }
114
+ }
115
+ return path;
116
+ };
117
+ tracer_default = tracer;
118
+ }
119
+ });
10
120
 
11
121
  // package.json
12
122
  var package_default = {
13
- version: "0.0.19"};
123
+ version: "0.0.21"};
14
124
 
15
125
  // src/tokens/index.ts
16
126
  var baseTokens = {
@@ -126,6 +236,12 @@ var Button = forwardRef(
126
236
  Button.displayName = "Button";
127
237
 
128
238
  // src/utils/api-client.ts
239
+ var UnauthorizedError = class extends Error {
240
+ constructor(message = "Unauthorized") {
241
+ super(message);
242
+ this.name = "UnauthorizedError";
243
+ }
244
+ };
129
245
  function isRedirectError(error) {
130
246
  return typeof error === "object" && error !== null && "digest" in error && typeof error.digest === "string" && error.digest.startsWith("NEXT_REDIRECT");
131
247
  }
@@ -151,8 +267,17 @@ async function handle401() {
151
267
  const { redirect } = await import('next/navigation');
152
268
  return redirect("/?expired=1");
153
269
  }
270
+ function isErrorPage(url) {
271
+ try {
272
+ const urlObj = new URL(url);
273
+ return urlObj.pathname === "/error";
274
+ } catch {
275
+ return url === "/error" || url.startsWith("/error?");
276
+ }
277
+ }
154
278
  async function handleServerError(statusCode) {
155
279
  const { redirect } = await import('next/navigation');
280
+ const { cookies: cookies2 } = await import('next/headers');
156
281
  let sourceUrl;
157
282
  try {
158
283
  const { headers } = await import('next/headers');
@@ -160,12 +285,20 @@ async function handleServerError(statusCode) {
160
285
  const referer = headersList.get("referer");
161
286
  const host = headersList.get("host");
162
287
  const pathname = headersList.get("x-invoke-path") || headersList.get("x-forwarded-path");
163
- if (referer) {
288
+ if (referer && !isErrorPage(referer)) {
164
289
  sourceUrl = referer;
165
290
  } else if (host && pathname) {
166
291
  const protocol = headersList.get("x-forwarded-proto") || "https";
167
292
  sourceUrl = `${protocol}://${host}${pathname}`;
168
293
  }
294
+ if (!sourceUrl || isErrorPage(sourceUrl)) {
295
+ const cookieStore = await cookies2();
296
+ const preservedSource = cookieStore.get("portal_error_source");
297
+ if (preservedSource?.value) {
298
+ sourceUrl = decodeURIComponent(preservedSource.value);
299
+ cookieStore.delete("portal_error_source");
300
+ }
301
+ }
169
302
  } catch (error) {
170
303
  console.debug("[portal-components] Could not determine source URL", error);
171
304
  }
@@ -176,6 +309,359 @@ async function handleServerError(statusCode) {
176
309
  return redirect(`/error?${params.toString()}`);
177
310
  }
178
311
 
312
+ // src/utils/observability/tracing.ts
313
+ var tracerCache = void 0;
314
+ function getTracer() {
315
+ if (tracerCache !== void 0) {
316
+ return tracerCache;
317
+ }
318
+ const rawFlag2 = String(process.env.USE_DATADOG_APM || "").toLowerCase();
319
+ const isEnabled2 = rawFlag2 === "true";
320
+ if (!isEnabled2) {
321
+ tracerCache = null;
322
+ return null;
323
+ }
324
+ try {
325
+ const tracerModule = (init_tracer(), __toCommonJS(tracer_exports));
326
+ const tracer2 = tracerModule.default || tracerModule;
327
+ if (tracer2 && typeof tracer2.trace === "function") {
328
+ tracerCache = tracer2;
329
+ return tracerCache;
330
+ }
331
+ } catch (err) {
332
+ console.warn("Failed to load tracer:", err);
333
+ }
334
+ tracerCache = null;
335
+ return null;
336
+ }
337
+ async function withTrace(name, fn) {
338
+ const activeTracer = getTracer();
339
+ if (!activeTracer) {
340
+ return fn(void 0);
341
+ }
342
+ return activeTracer.trace(name, async (span) => {
343
+ if (span) {
344
+ span.setTag("component", "application");
345
+ }
346
+ try {
347
+ const result = await fn(span);
348
+ return result;
349
+ } catch (error) {
350
+ if (span) {
351
+ span.setTag("error", error);
352
+ }
353
+ throw error;
354
+ }
355
+ });
356
+ }
357
+ function traceServerAction(name, fn) {
358
+ const spanName = name.startsWith("server.action.") ? name : `server.action.${name}`;
359
+ const traced = async (...args) => {
360
+ return withTrace(spanName, async (span) => {
361
+ if (span) {
362
+ span.setTag("component", "server-action");
363
+ }
364
+ const result = await fn(...args);
365
+ return result;
366
+ });
367
+ };
368
+ return traced;
369
+ }
370
+ var deriveInstallMethods = (licenseData) => {
371
+ const methods = [];
372
+ if (licenseData.isKotsInstallEnabled) {
373
+ methods.push("Replicated KOTS");
374
+ }
375
+ if (licenseData.isHelmInstallEnabled) {
376
+ methods.push("Helm");
377
+ }
378
+ if (licenseData.isHelmAirgapEnabled) {
379
+ methods.push("Helm Airgap");
380
+ }
381
+ if (licenseData.isEmbeddedClusterDownloadEnabled || licenseData.isEmbeddedClusterMultiNodeEnabled) {
382
+ methods.push("Embedded Cluster");
383
+ }
384
+ if (licenseData.isKurlInstallEnabled) {
385
+ methods.push("kURL");
386
+ }
387
+ if (licenseData.isGitopsSupported) {
388
+ methods.push("GitOps");
389
+ }
390
+ return Array.from(new Set(methods));
391
+ };
392
+ var convertEntitlementsToFields = (entitlementFields, entitlementValues) => {
393
+ const valuesMap = /* @__PURE__ */ new Map();
394
+ entitlementValues.forEach((ev) => {
395
+ valuesMap.set(ev.name, ev.value);
396
+ });
397
+ return entitlementFields.filter((field) => field.hidden === 0).map((field) => ({
398
+ key: field.name,
399
+ label: field.title || field.name,
400
+ value: valuesMap.get(field.name) || field.defaultVal || null,
401
+ isSecret: field.type === "Password"
402
+ }));
403
+ };
404
+ async function fetchLicenseCore() {
405
+ const sessionStore = await cookies();
406
+ const session = sessionStore.get("portal_session");
407
+ const token = session?.value;
408
+ if (!token) {
409
+ throw new Error("No session found - user must be authenticated");
410
+ }
411
+ const endpoint = `${getApiOrigin()}/enterprise-portal/license`;
412
+ if (process.env.NODE_ENV !== "production") {
413
+ console.debug("[portal-components] fetching license via %s (Enterprise Portal API)", endpoint);
414
+ }
415
+ const response = await authenticatedFetch(endpoint, {
416
+ method: "GET",
417
+ token,
418
+ headers: {
419
+ Accept: "application/json"
420
+ }
421
+ });
422
+ if (!response.ok) {
423
+ throw new Error(
424
+ `License request failed (${response.status} ${response.statusText})`
425
+ );
426
+ }
427
+ const envelope = await response.json();
428
+ const licenseData = envelope.data;
429
+ const license = {
430
+ ...licenseData,
431
+ // Alias fields for backward compatibility
432
+ expiresAt: licenseData.expireAt,
433
+ environment: licenseData.licenseType,
434
+ // Extract channel names from channels array
435
+ releaseChannels: (licenseData.channels || []).map((ch) => ch.channelName),
436
+ // Derive install methods from feature flags
437
+ installMethods: deriveInstallMethods(licenseData),
438
+ // Convert entitlements to fields format
439
+ fields: convertEntitlementsToFields(licenseData.entitlementFields || [], licenseData.entitlementValues || [])
440
+ };
441
+ return license;
442
+ }
443
+ var fetchLicenseImpl = cache(fetchLicenseCore);
444
+ var fetchLicense = fetchLicenseImpl;
445
+
446
+ // src/utils/constants.ts
447
+ 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";
448
+ var DEFAULT_PRIMARY_COLOR = "#4f46e5";
449
+ var DEFAULT_SECONDARY_COLOR = "#6366f1";
450
+ var isHttpApiOrigin = () => {
451
+ return process.env.REPLICATED_APP_ORIGIN?.startsWith("http://") || false;
452
+ };
453
+
454
+ // src/utils/branding.ts
455
+ var normalizeColor = (color) => {
456
+ if (!color || typeof color !== "string") {
457
+ return void 0;
458
+ }
459
+ const trimmed = color.trim();
460
+ if (/^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$/.test(trimmed)) {
461
+ return trimmed.startsWith("#") ? trimmed : `#${trimmed}`;
462
+ }
463
+ console.debug("[portal-components] Invalid color format rejected (only hex supported):", trimmed);
464
+ return void 0;
465
+ };
466
+ var sanitizeUrlForCss = (url) => {
467
+ if (!url || typeof url !== "string") {
468
+ return void 0;
469
+ }
470
+ const trimmed = url.trim();
471
+ try {
472
+ const urlObj = new URL(trimmed);
473
+ if (!["http:", "https:", "data:"].includes(urlObj.protocol)) {
474
+ console.debug("[portal-components] Invalid URL protocol for background image:", urlObj.protocol);
475
+ return void 0;
476
+ }
477
+ } catch {
478
+ console.debug("[portal-components] Invalid URL format for background image:", trimmed);
479
+ return void 0;
480
+ }
481
+ const escaped = trimmed.replace(/\\/g, "\\\\").replace(/\)/g, "\\)").replace(/"/g, '\\"').replace(/'/g, "\\'").replace(/[\x00-\x1F\x7F]/g, "");
482
+ return escaped;
483
+ };
484
+ var decodeBranding = ({ brandingData }) => {
485
+ if (!brandingData || typeof brandingData !== "string") {
486
+ return {
487
+ primaryColor: DEFAULT_PRIMARY_COLOR,
488
+ secondaryColor: DEFAULT_SECONDARY_COLOR
489
+ };
490
+ }
491
+ try {
492
+ const decoded = Buffer.from(brandingData, "base64").toString("utf-8");
493
+ const parsed = JSON.parse(decoded);
494
+ const logo = typeof parsed.logo === "string" ? parsed.logo : void 0;
495
+ const titleRaw = typeof parsed.title === "string" ? parsed.title.trim() : "";
496
+ const title = titleRaw ? titleRaw : void 0;
497
+ const favicon = typeof parsed.favicon === "string" ? parsed.favicon : void 0;
498
+ const primaryColorRaw = parsed.primaryColor ?? parsed.primary_color;
499
+ const secondaryColorRaw = parsed.secondaryColor ?? parsed.secondary_color;
500
+ const primaryColor = normalizeColor(primaryColorRaw);
501
+ const secondaryColor = normalizeColor(secondaryColorRaw);
502
+ const supportPortalLink = typeof parsed.supportPortalLink === "string" ? parsed.supportPortalLink : void 0;
503
+ const backgroundRaw = parsed.background;
504
+ const background = backgroundRaw === "minimal" || backgroundRaw === "custom" || backgroundRaw === "image" ? backgroundRaw : void 0;
505
+ const backgroundImage = sanitizeUrlForCss(parsed.backgroundImage);
506
+ const backgroundGradientStart = normalizeColor(parsed.customColor1);
507
+ const backgroundGradientEnd = normalizeColor(parsed.customColor2);
508
+ return {
509
+ logo,
510
+ title,
511
+ favicon,
512
+ primaryColor: primaryColor || DEFAULT_PRIMARY_COLOR,
513
+ secondaryColor: secondaryColor || DEFAULT_SECONDARY_COLOR,
514
+ supportPortalLink,
515
+ background,
516
+ backgroundImage,
517
+ backgroundGradientStart,
518
+ backgroundGradientEnd
519
+ };
520
+ } catch (error) {
521
+ console.debug("[portal-components] unable to parse branding JSON", error);
522
+ return {
523
+ primaryColor: DEFAULT_PRIMARY_COLOR,
524
+ secondaryColor: DEFAULT_SECONDARY_COLOR
525
+ };
526
+ }
527
+ };
528
+
529
+ // src/utils/session.ts
530
+ async function validateSession(token) {
531
+ if (!token || typeof token !== "string" || !token.trim()) {
532
+ return false;
533
+ }
534
+ try {
535
+ const endpoint = `${getApiOrigin()}/enterprise-portal/user`;
536
+ const response = await fetch(endpoint, {
537
+ method: "GET",
538
+ headers: {
539
+ authorization: `Bearer ${token}`
540
+ },
541
+ // Short timeout for validation
542
+ signal: AbortSignal.timeout(5e3)
543
+ });
544
+ if (response.status === 401) {
545
+ return false;
546
+ }
547
+ return response.ok;
548
+ } catch (error) {
549
+ console.warn("[portal-components] session validation error:", error);
550
+ return true;
551
+ }
552
+ }
553
+ async function deleteSessionCookie() {
554
+ const { cookies: cookies2 } = await import('next/headers');
555
+ const cookieStore = await cookies2();
556
+ cookieStore.delete("portal_session");
557
+ }
558
+
559
+ // src/utils/format.ts
560
+ function formatBytes(bytes, decimals = 1) {
561
+ if (bytes === 0) return "0 Bytes";
562
+ const k = 1024;
563
+ const dm = decimals < 0 ? 0 : decimals;
564
+ const sizes = ["Bytes", "KB", "MB", "GB", "TB"];
565
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
566
+ return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
567
+ }
568
+ function formatDateShort(dateString) {
569
+ if (!dateString) return "Never";
570
+ const date = new Date(dateString);
571
+ if (isNaN(date.getTime())) {
572
+ return dateString;
573
+ }
574
+ return date.toLocaleDateString("en-US", {
575
+ year: "numeric",
576
+ month: "2-digit",
577
+ day: "2-digit"
578
+ });
579
+ }
580
+ function formatDate(dateString) {
581
+ if (!dateString) return "Never";
582
+ const date = new Date(dateString);
583
+ if (isNaN(date.getTime())) {
584
+ return dateString;
585
+ }
586
+ return date.toLocaleDateString("en-US", {
587
+ month: "short",
588
+ day: "numeric",
589
+ year: "numeric",
590
+ hour: "numeric",
591
+ minute: "2-digit",
592
+ hour12: true
593
+ });
594
+ }
595
+ function formatDateTime(dateString) {
596
+ const date = new Date(dateString);
597
+ if (isNaN(date.getTime())) {
598
+ return dateString;
599
+ }
600
+ return date.toLocaleString("en-US", {
601
+ timeZone: "UTC",
602
+ year: "numeric",
603
+ month: "2-digit",
604
+ day: "2-digit",
605
+ hour: "2-digit",
606
+ minute: "2-digit",
607
+ second: "2-digit",
608
+ hour12: false
609
+ }) + " UTC";
610
+ }
611
+ function formatDateTimeLocal(dateString) {
612
+ if (!dateString) return "N/A";
613
+ try {
614
+ const date = new Date(dateString);
615
+ if (isNaN(date.getTime())) {
616
+ return dateString;
617
+ }
618
+ return date.toLocaleDateString("en-US", {
619
+ month: "numeric",
620
+ day: "numeric",
621
+ year: "numeric",
622
+ hour: "numeric",
623
+ minute: "2-digit",
624
+ hour12: true
625
+ });
626
+ } catch {
627
+ return "N/A";
628
+ }
629
+ }
630
+
631
+ // src/utils/release-helpers.ts
632
+ function convertToReleaseEntry(release, channelName, options) {
633
+ const sections = [];
634
+ if (release.release_notes && release.release_notes.trim().length > 0) {
635
+ if (options.releaseNotesMode === "markdown") {
636
+ sections.push({
637
+ title: "Release Notes",
638
+ description: release.release_notes
639
+ });
640
+ } else {
641
+ const releaseNotesItems = release.release_notes.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0);
642
+ sections.push({
643
+ title: "Release Notes",
644
+ description: releaseNotesItems.length === 0 ? release.release_notes : void 0,
645
+ items: releaseNotesItems.length > 0 ? releaseNotesItems : void 0
646
+ });
647
+ }
648
+ }
649
+ const charts = release.helm_charts ? release.helm_charts.filter((chart) => !chart.is_kots_installer_only).map((chart) => `${chart.name} v${chart.version}`) : [];
650
+ if (charts.length > 0) {
651
+ sections.push({
652
+ title: "Helm Charts",
653
+ items: charts,
654
+ description: "Latest chart versions included in this release."
655
+ });
656
+ }
657
+ return {
658
+ version: release.label || `Release ${release.release_sequence}`,
659
+ channel: channelName || `Channel ${release.channel_sequence}`,
660
+ releasedAt: release.release_date,
661
+ sections
662
+ };
663
+ }
664
+
179
665
  // src/actions/index.ts
180
666
  var getApiOrigin = () => {
181
667
  return (process.env.REPLICATED_APP_ORIGIN || "https://replicated.app").replace(/\/+$/, "");
@@ -315,11 +801,15 @@ var fetchCustomBrandingImpl = async () => {
315
801
  const payload = await response.json();
316
802
  const brandingObject = {
317
803
  logo: payload.logoUrl,
318
- title: payload.appName,
319
- customColor1: payload.primaryColor,
320
- customColor2: payload.secondaryColor,
804
+ title: payload.title,
805
+ primaryColor: payload.primaryColor,
806
+ secondaryColor: payload.secondaryColor,
321
807
  favicon: payload.faviconUrl,
322
- supportPortalLink: payload.supportPortalLink || ""
808
+ supportPortalLink: payload.supportPortalLink || "",
809
+ background: payload.background,
810
+ backgroundImage: payload.backgroundImage,
811
+ customColor1: payload.customColor1,
812
+ customColor2: payload.customColor2
323
813
  };
324
814
  const brandingData = Buffer.from(JSON.stringify(brandingObject)).toString("base64");
325
815
  return {
@@ -582,353 +1072,38 @@ var uploadSupportBundle = defineServerAction({
582
1072
  var getSupportBundleUploadUrl = (appId) => {
583
1073
  return `${getApiOrigin()}/enterprise-portal/support-bundles/upload/${encodeURIComponent(appId)}`;
584
1074
  };
585
- var listReleases = defineServerAction({
586
- id: "releases/list",
587
- description: "Lists available releases for the authenticated customer.",
588
- visibility: "customer",
589
- tags: ["releases"],
590
- async run({ token }, context) {
591
- if (!token || typeof token !== "string") {
592
- throw new Error("List releases requires a session token");
593
- }
594
- const endpoint = `${getApiOrigin()}/enterprise-portal/releases`;
595
- if (process.env.NODE_ENV !== "production") {
596
- console.debug("[portal-components] fetching releases via %s (Enterprise Portal API)", endpoint);
597
- }
598
- const response = await authenticatedFetch(endpoint, {
599
- method: "GET",
600
- token,
601
- headers: {
602
- accept: "application/json"
603
- },
604
- signal: context?.signal
605
- });
606
- const bodyText = await response.text().catch((error) => {
607
- console.warn("[portal-components] listReleases read error", error);
608
- return null;
609
- });
610
- if (!response.ok) {
611
- throw new Error(
612
- `List releases request failed (${response.status} ${response.statusText})`
613
- );
614
- }
615
- return {
616
- status: response.status,
617
- body: bodyText
618
- };
619
- }
620
- });
621
- var asRecord = (value) => {
622
- if (value && typeof value === "object") {
623
- return value;
624
- }
625
- return void 0;
626
- };
627
- var getValue = (record, key) => record ? record[key] : void 0;
628
- var getString = (record, key) => {
629
- const value = getValue(record, key);
630
- return typeof value === "string" ? value : void 0;
631
- };
632
- var getBoolean = (record, key) => {
633
- const value = getValue(record, key);
634
- if (typeof value === "boolean") {
635
- return value;
636
- }
637
- if (typeof value === "number") {
638
- return value === 1;
639
- }
640
- if (typeof value === "string") {
641
- const normalized = value.trim().toLowerCase();
642
- if (["true", "1", "yes"].includes(normalized)) {
643
- return true;
644
- }
645
- if (["false", "0", "no"].includes(normalized)) {
646
- return false;
647
- }
648
- }
649
- return void 0;
650
- };
651
- var toDisplayValue = (value) => {
652
- if (value === null || value === void 0) {
653
- return null;
654
- }
655
- if (typeof value === "string") {
656
- return value;
657
- }
658
- if (typeof value === "number" || typeof value === "boolean") {
659
- return String(value);
660
- }
661
- try {
662
- return JSON.stringify(value);
663
- } catch {
664
- return String(value);
665
- }
666
- };
667
- var normalizeStringArray = (value) => {
668
- if (Array.isArray(value)) {
669
- const normalized = value.map(
670
- (item) => typeof item === "string" ? item.trim() : ""
671
- ).filter((item) => item.length > 0);
672
- return normalized.length ? normalized : void 0;
673
- }
674
- if (typeof value === "string") {
675
- const normalized = value.split(",").map((item) => item.trim()).filter((item) => item.length > 0);
676
- return normalized.length ? normalized : void 0;
677
- }
678
- return void 0;
679
- };
680
- var normalizeLicenseFields = (input) => {
681
- if (!input) {
682
- return [];
683
- }
684
- if (Array.isArray(input)) {
685
- return input.map((field, index) => {
686
- if (!field || typeof field !== "object") {
687
- return null;
688
- }
689
- const candidate = field;
690
- 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}`;
691
- 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;
692
- let value = candidate.value ?? candidate.data ?? candidate.content;
693
- if ((value === void 0 || value === null) && typeof candidate.text === "string") {
694
- value = candidate.text;
695
- }
696
- if ((value === void 0 || value === null) && typeof candidate.defaultValue === "string") {
697
- value = candidate.defaultValue;
698
- }
699
- const isSecret = Boolean(
700
- candidate.isSecret ?? candidate.secret ?? candidate.masked
701
- );
702
- const resolved = toDisplayValue(value);
703
- return {
704
- key,
705
- label,
706
- value: resolved,
707
- isSecret
708
- };
709
- }).filter((field) => Boolean(field));
710
- }
711
- if (typeof input === "object") {
712
- return Object.entries(input).map(
713
- ([key, value]) => {
714
- let resolvedValue = value;
715
- let isSecret = false;
716
- if (value && typeof value === "object") {
717
- const obj = value;
718
- if ("value" in obj) {
719
- resolvedValue = obj.value;
720
- }
721
- isSecret = Boolean(obj.isSecret ?? obj.secret ?? obj.masked);
722
- }
723
- const normalized = toDisplayValue(resolvedValue);
724
- return {
725
- key,
726
- label: key,
727
- value: normalized,
728
- isSecret
729
- };
730
- }
731
- );
732
- }
733
- return [];
734
- };
735
- var extractChannelNames = (input) => {
736
- if (!Array.isArray(input)) {
737
- return void 0;
1075
+ async function listReleasesImpl() {
1076
+ const { cookies: cookies2 } = await import('next/headers');
1077
+ const sessionStore = await cookies2();
1078
+ const session = sessionStore.get("portal_session");
1079
+ const token = session?.value;
1080
+ if (!token) {
1081
+ throw new Error("List releases requires a session token");
1082
+ }
1083
+ const endpoint = `${getApiOrigin()}/enterprise-portal/releases`;
1084
+ if (process.env.NODE_ENV !== "production") {
1085
+ console.debug("[portal-components] fetching releases via %s (Enterprise Portal API)", endpoint);
738
1086
  }
739
- const names = input.map((item) => {
740
- if (typeof item === "string") {
741
- return item.trim();
742
- }
743
- const record = asRecord(item);
744
- if (!record) {
745
- return null;
746
- }
747
- return getString(record, "name") ?? getString(record, "channelName") ?? getString(record, "channel") ?? getString(record, "channelSlug") ?? getString(record, "slug") ?? void 0;
748
- }).filter((name) => Boolean(name && name.length));
749
- return names.length ? names : void 0;
750
- };
751
- var normalizeEntitlementFields = (fieldsInput, valuesInput) => {
752
- const valuesMap = /* @__PURE__ */ new Map();
753
- const assignValue = (key, value) => {
754
- if (!key) {
755
- return;
1087
+ const response = await authenticatedFetch(endpoint, {
1088
+ method: "GET",
1089
+ token,
1090
+ headers: {
1091
+ accept: "application/json"
756
1092
  }
757
- valuesMap.set(key, toDisplayValue(value));
758
- };
759
- if (Array.isArray(valuesInput)) {
760
- valuesInput.forEach((item) => {
761
- const record = asRecord(item);
762
- if (!record) {
763
- if (typeof item === "string") {
764
- assignValue(item, item);
765
- }
766
- return;
767
- }
768
- const key = getString(record, "name") ?? getString(record, "field") ?? getString(record, "title") ?? getString(record, "label") ?? getString(record, "slug") ?? (() => {
769
- const idValue = getValue(record, "id");
770
- if (typeof idValue === "string" || typeof idValue === "number") {
771
- return String(idValue);
772
- }
773
- return void 0;
774
- })();
775
- const value = getValue(record, "value") ?? getValue(record, "currentValue") ?? getValue(record, "entitlementValue") ?? getValue(record, "content") ?? getValue(record, "data") ?? getValue(record, "defaultVal") ?? getValue(record, "defaultValue");
776
- assignValue(key, value);
777
- });
778
- } else if (valuesInput && typeof valuesInput === "object") {
779
- Object.entries(valuesInput).forEach(
780
- ([key, value]) => assignValue(key, value)
1093
+ });
1094
+ if (!response.ok) {
1095
+ throw new Error(
1096
+ `List releases request failed (${response.status} ${response.statusText})`
781
1097
  );
782
1098
  }
783
- const normalized = [];
784
- if (Array.isArray(fieldsInput)) {
785
- fieldsInput.forEach((item, index) => {
786
- const record = asRecord(item);
787
- if (!record) {
788
- return;
789
- }
790
- const baseKey = getString(record, "name") ?? getString(record, "field") ?? getString(record, "slug") ?? `entitlement-${index}`;
791
- const key = `entitlement-${baseKey}`;
792
- const label = getString(record, "title") ?? getString(record, "label") ?? baseKey;
793
- const defaultValue = getString(record, "defaultVal") ?? getString(record, "default") ?? getString(record, "defaultValue");
794
- const value = valuesMap.get(baseKey) ?? valuesMap.get(label) ?? defaultValue ?? null;
795
- const isSecret = Boolean(
796
- getBoolean(record, "secret") ?? getBoolean(record, "isSecret") ?? getBoolean(record, "masked")
797
- );
798
- normalized.push({
799
- key,
800
- label,
801
- value,
802
- isSecret
803
- });
804
- });
805
- }
806
- valuesMap.forEach((value, key) => {
807
- const normalizedKey = `entitlement-${key}`;
808
- if (!normalized.some((field) => field.key === normalizedKey)) {
809
- normalized.push({
810
- key: normalizedKey,
811
- label: key,
812
- value
813
- });
814
- }
815
- });
816
- return normalized;
817
- };
818
- var normalizeLicensePayload = (payload) => {
819
- const payloadRecord = asRecord(payload);
820
- const rootRecord = asRecord(getValue(payloadRecord, "license")) ?? asRecord(getValue(payloadRecord, "data")) ?? payloadRecord ?? {};
821
- const sourceRecord = asRecord(getValue(rootRecord, "metadata")) ?? rootRecord;
822
- const customer = asRecord(getValue(rootRecord, "customer")) ?? asRecord(getValue(sourceRecord, "customer")) ?? asRecord(getValue(payloadRecord, "customer")) ?? {};
823
- let releaseChannels = normalizeStringArray(
824
- getValue(rootRecord, "releaseChannels") ?? getValue(sourceRecord, "releaseChannels") ?? getValue(sourceRecord, "channels") ?? getValue(rootRecord, "channels") ?? getValue(sourceRecord, "channel") ?? getValue(rootRecord, "channel")
825
- ) ?? void 0;
826
- if (!releaseChannels) {
827
- releaseChannels = extractChannelNames(getValue(rootRecord, "channels")) ?? extractChannelNames(getValue(sourceRecord, "channels")) ?? void 0;
828
- }
829
- let installMethods = normalizeStringArray(
830
- getValue(rootRecord, "installMethods") ?? getValue(sourceRecord, "installMethods") ?? getValue(sourceRecord, "install_options") ?? getValue(rootRecord, "install_options") ?? getValue(sourceRecord, "installOptions")
831
- ) ?? void 0;
832
- if (!installMethods || installMethods.length === 0) {
833
- const resolved = [];
834
- const flag = (key) => getBoolean(rootRecord, key) ?? getBoolean(sourceRecord, key) ?? false;
835
- if (flag("isKotsInstallEnabled")) {
836
- resolved.push("Replicated KOTS");
837
- }
838
- if (flag("isHelmInstallEnabled")) {
839
- resolved.push("Helm");
840
- }
841
- if (flag("isHelmAirgapEnabled")) {
842
- resolved.push("Helm Airgap");
843
- }
844
- if (flag("isEmbeddedClusterDownloadEnabled") || flag("isEmbeddedClusterMultiNodeEnabled")) {
845
- resolved.push("Embedded Cluster");
846
- }
847
- if (flag("isKurlInstallEnabled")) {
848
- resolved.push("kURL");
849
- }
850
- if (flag("isGitopsSupported")) {
851
- resolved.push("GitOps");
852
- }
853
- if (resolved.length) {
854
- installMethods = Array.from(new Set(resolved));
855
- }
856
- }
857
- 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");
858
- const expiresAt = typeof expiresAtSource === "string" && expiresAtSource.trim().length ? expiresAtSource : expiresAtSource === null ? null : void 0;
859
- const baseFields = normalizeLicenseFields(
860
- getValue(rootRecord, "additionalFields") ?? getValue(sourceRecord, "additionalFields") ?? getValue(sourceRecord, "fields") ?? getValue(rootRecord, "fields") ?? getValue(payloadRecord, "fields") ?? getValue(payloadRecord, "additional_fields")
861
- );
862
- const entitlementFields = normalizeEntitlementFields(
863
- getValue(rootRecord, "entitlementFields") ?? getValue(sourceRecord, "entitlementFields"),
864
- getValue(rootRecord, "entitlementValues") ?? getValue(sourceRecord, "entitlementValues")
865
- );
866
- const fields = [
867
- ...baseFields,
868
- ...entitlementFields.filter(
869
- (field) => !baseFields.some((existing) => existing.key === field.key)
870
- )
871
- ];
872
- const statusFromSource = getString(sourceRecord, "status") ?? getString(sourceRecord, "state");
873
- const statusLabelFromSource = getString(sourceRecord, "statusLabel") ?? getString(sourceRecord, "stateLabel");
874
- const expiredFlag = getBoolean(sourceRecord, "isExpired") ?? getBoolean(rootRecord, "isExpired");
875
- const derivedStatus = statusFromSource ?? (typeof expiredFlag === "boolean" ? expiredFlag ? "expired" : "active" : void 0);
876
- const statusLabel = statusLabelFromSource ?? (derivedStatus ? derivedStatus.charAt(0).toUpperCase() + derivedStatus.slice(1) : void 0);
877
- const licenseType = getString(sourceRecord, "licenseType") ?? getString(rootRecord, "licenseType");
878
- const status = derivedStatus;
879
- const license = {
880
- id: getString(rootRecord, "id") ?? getString(sourceRecord, "id") ?? getString(sourceRecord, "licenseId") ?? getString(customer, "licenseId") ?? void 0,
881
- status,
882
- statusLabel,
883
- environment: getString(sourceRecord, "environment") ?? getString(sourceRecord, "tier") ?? licenseType ?? void 0,
884
- expiresAt: expiresAt ?? null,
885
- releaseChannels: releaseChannels ?? [
886
- getString(rootRecord, "channelName") ?? getString(rootRecord, "channel") ?? void 0
887
- ].filter((value) => Boolean(value)),
888
- installMethods,
889
- installNotes: getString(sourceRecord, "installNotes"),
890
- customerName: getString(sourceRecord, "customerName") ?? getString(customer, "name") ?? void 0,
891
- customerId: getString(sourceRecord, "customerId") ?? getString(customer, "id") ?? getString(rootRecord, "customerId") ?? void 0,
892
- customerOrganization: getString(customer, "organization") ?? getString(sourceRecord, "customerOrganization") ?? getString(rootRecord, "customerOrganization") ?? void 0,
893
- fields
1099
+ const envelope = await response.json();
1100
+ const data = envelope.data;
1101
+ return {
1102
+ releases: Array.isArray(data?.releases) ? data.releases : [],
1103
+ totalCount: data?.totalCount || 0
894
1104
  };
895
- return license;
896
- };
897
- var fetchLicenseDetails = defineServerAction({
898
- id: "license/fetch-details",
899
- description: "Fetches the authenticated user's enterprise license details.",
900
- visibility: "customer",
901
- tags: ["license", "entitlements"],
902
- async run({ token }, context) {
903
- if (typeof token !== "string" || token.trim().length === 0) {
904
- throw new Error("fetchLicenseDetails requires a non-empty token");
905
- }
906
- const endpoint = `${getApiOrigin()}/enterprise-portal/license`;
907
- if (process.env.NODE_ENV !== "production") {
908
- console.debug("[portal-components] fetching license via %s (Enterprise Portal API)", endpoint);
909
- }
910
- const response = await authenticatedFetch(endpoint, {
911
- method: "GET",
912
- token,
913
- headers: {
914
- accept: "application/json"
915
- },
916
- signal: context?.signal
917
- });
918
- if (!response.ok) {
919
- throw new Error(
920
- `License request failed (${response.status} ${response.statusText})`
921
- );
922
- }
923
- const payload = await response.json();
924
- const licenseData = payload.data;
925
- const license = normalizeLicensePayload(licenseData);
926
- return {
927
- license,
928
- raw: licenseData ?? null
929
- };
930
- }
931
- });
1105
+ }
1106
+ var listReleases = traceServerAction("listReleases", listReleasesImpl);
932
1107
  var getSecurityInfo = defineServerAction({
933
1108
  id: "security/get-info",
934
1109
  description: "Fetches CVE security scan results for a specific release",
@@ -1407,12 +1582,12 @@ var InfoRow = ({
1407
1582
  icon,
1408
1583
  title,
1409
1584
  children
1410
- }) => /* @__PURE__ */ jsxs("div", { className: "flex items-start space-x-3", children: [
1411
- /* @__PURE__ */ jsx("div", { className: "mt-1 text-gray-900", children: icon }),
1412
- /* @__PURE__ */ jsxs("div", { children: [
1413
- /* @__PURE__ */ jsx("h3", { className: "text-sm font-medium text-gray-900", children: title }),
1414
- children
1415
- ] })
1585
+ }) => /* @__PURE__ */ jsxs("div", { children: [
1586
+ /* @__PURE__ */ jsxs("div", { className: "flex items-baseline gap-2", children: [
1587
+ /* @__PURE__ */ jsx("div", { className: "flex h-4 w-4 flex-shrink-0 items-center justify-center text-gray-900", children: icon }),
1588
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-medium leading-4 text-gray-900", children: title })
1589
+ ] }),
1590
+ /* @__PURE__ */ jsx("div", { className: "pl-6", children })
1416
1591
  ] });
1417
1592
  var renderInstallOrChannel = (values) => {
1418
1593
  if (!values || values.length === 0) {
@@ -1427,12 +1602,12 @@ var buildPrimaryRows = (license) => {
1427
1602
  node: /* @__PURE__ */ jsxs(
1428
1603
  InfoRow,
1429
1604
  {
1430
- icon: /* @__PURE__ */ jsx(ShieldIcon, { className: "h-5 w-5" }),
1605
+ icon: /* @__PURE__ */ jsx(ShieldIcon, { className: "h-4 w-4" }),
1431
1606
  title: "License Status",
1432
1607
  children: [
1433
1608
  /* @__PURE__ */ jsxs("div", { className: "mt-1 flex items-center text-sm text-gray-600", children: [
1434
1609
  /* @__PURE__ */ jsx(CheckIcon, { className: "mr-1.5 h-4 w-4 text-green-500" }),
1435
- /* @__PURE__ */ jsx("span", { children: license.statusLabel ?? (license.status ? license.status.charAt(0).toUpperCase() + license.status.slice(1) : "Unknown") })
1610
+ /* @__PURE__ */ jsx("span", { children: license.isExpired ? "Expired" : "Active" })
1436
1611
  ] }),
1437
1612
  license.environment ? /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-gray-500", children: license.environment }) : null
1438
1613
  ]
@@ -1444,7 +1619,7 @@ var buildPrimaryRows = (license) => {
1444
1619
  node: /* @__PURE__ */ jsx(
1445
1620
  InfoRow,
1446
1621
  {
1447
- icon: /* @__PURE__ */ jsx(CalendarIcon, { className: "h-5 w-5" }),
1622
+ icon: /* @__PURE__ */ jsx(CalendarIcon, { className: "h-4 w-4" }),
1448
1623
  title: "Expiration Date",
1449
1624
  children: /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-gray-500", children: formatExpiration(license.expiresAt) })
1450
1625
  }
@@ -1455,7 +1630,7 @@ var buildPrimaryRows = (license) => {
1455
1630
  node: /* @__PURE__ */ jsx(
1456
1631
  InfoRow,
1457
1632
  {
1458
- icon: /* @__PURE__ */ jsx(ShieldIcon, { className: "h-5 w-5" }),
1633
+ icon: /* @__PURE__ */ jsx(ShieldIcon, { className: "h-4 w-4" }),
1459
1634
  title: "Release Channel(s)",
1460
1635
  children: renderInstallOrChannel(license.releaseChannels)
1461
1636
  }
@@ -1466,7 +1641,7 @@ var buildPrimaryRows = (license) => {
1466
1641
  node: /* @__PURE__ */ jsx(
1467
1642
  InfoRow,
1468
1643
  {
1469
- icon: /* @__PURE__ */ jsx(DownloadIcon, { className: "h-5 w-5" }),
1644
+ icon: /* @__PURE__ */ jsx(DownloadIcon, { className: "h-4 w-4" }),
1470
1645
  title: "Install Options",
1471
1646
  children: renderInstallOrChannel(license.installMethods)
1472
1647
  }
@@ -1477,7 +1652,7 @@ var buildPrimaryRows = (license) => {
1477
1652
  node: /* @__PURE__ */ jsxs(
1478
1653
  InfoRow,
1479
1654
  {
1480
- icon: /* @__PURE__ */ jsx(BuildingIcon, { className: "h-5 w-5" }),
1655
+ icon: /* @__PURE__ */ jsx(BuildingIcon, { className: "h-4 w-4" }),
1481
1656
  title: "Customer Name",
1482
1657
  children: [
1483
1658
  /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-gray-500", children: license.customerName ?? "Unknown customer" }),
@@ -1495,7 +1670,7 @@ var renderFields = (fields) => {
1495
1670
  if (!fields.length) {
1496
1671
  return /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-500", children: "No additional fields available." });
1497
1672
  }
1498
- return /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3", children: fields.map((field) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
1673
+ return /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 gap-6 md:grid-cols-2 xl:grid-cols-3", children: fields.map((field) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
1499
1674
  /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-gray-900", children: field.label }),
1500
1675
  /* @__PURE__ */ jsx(
1501
1676
  "p",
@@ -1516,9 +1691,7 @@ var LicenseDetails = async ({
1516
1691
  if (typeof token !== "string" || token.trim().length === 0) {
1517
1692
  throw new Error("LicenseDetails component requires a non-empty token");
1518
1693
  }
1519
- const response = await fetchLicenseDetails.run({ token });
1520
- console.debug("[portal-components] license response", response.raw);
1521
- const { license } = response;
1694
+ const license = await fetchLicense();
1522
1695
  const rows = buildPrimaryRows(license);
1523
1696
  return /* @__PURE__ */ jsxs("div", { className: "rounded border border-gray-100 bg-white p-8 shadow-[0_18px_45px_rgba(17,24,39,0.08)]", children: [
1524
1697
  /* @__PURE__ */ jsxs("header", { className: "flex flex-col border-b border-gray-100 pb-6", children: [
@@ -1526,7 +1699,7 @@ var LicenseDetails = async ({
1526
1699
  description ? /* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-gray-600", children: description }) : null
1527
1700
  ] }),
1528
1701
  /* @__PURE__ */ jsxs("div", { className: "mt-6 space-y-6", children: [
1529
- /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-gray-100 bg-white p-6 sm:p-8", children: /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3", children: rows.map((row) => /* @__PURE__ */ jsx("div", { children: row.node }, row.key)) }) }),
1702
+ /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-gray-100 bg-white p-6 sm:p-8", children: /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 items-start gap-6 md:grid-cols-2 xl:grid-cols-3", children: rows.map((row) => /* @__PURE__ */ jsx("div", { children: row.node }, row.key)) }) }),
1530
1703
  /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-gray-100 bg-white p-6 sm:p-8", children: [
1531
1704
  /* @__PURE__ */ jsx("h2", { className: "mb-4 text-lg font-medium text-gray-900", children: "Additional License Fields" }),
1532
1705
  /* @__PURE__ */ jsx("div", { className: "overflow-x-auto", children: renderFields(license.fields ?? []) })
@@ -1719,57 +1892,49 @@ var TopNav = async ({
1719
1892
  currentCustomerId,
1720
1893
  onChangeTeam,
1721
1894
  userMenuChildren,
1722
- logoutButton
1895
+ logoutButton,
1896
+ branding: brandingProp
1723
1897
  }) => {
1724
1898
  const displayLabel = userMenuLabel || (customerName ? `Team: ${customerName}` : "Team: Example");
1725
1899
  let logo;
1726
1900
  let brandTitle;
1727
- let customColor1;
1728
- let customColor2;
1729
- const normalizeColor = (color) => {
1730
- if (!color || typeof color !== "string") {
1731
- return void 0;
1732
- }
1733
- const trimmed = color.trim();
1734
- if (/^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$/.test(trimmed)) {
1735
- return trimmed.startsWith("#") ? trimmed : `#${trimmed}`;
1736
- }
1737
- return trimmed;
1738
- };
1739
- try {
1740
- const branding = await fetchCustomBranding();
1741
- if (branding.brandingData) {
1742
- const decoded = Buffer.from(branding.brandingData, "base64").toString(
1743
- "utf-8"
1744
- );
1745
- try {
1746
- const parsed = JSON.parse(decoded);
1747
- if (parsed?.logo && typeof parsed.logo === "string") {
1748
- logo = parsed.logo;
1749
- }
1750
- if (parsed?.title && typeof parsed.title === "string") {
1751
- const normalizedTitle = parsed.title.trim();
1752
- if (normalizedTitle) {
1753
- brandTitle = normalizedTitle;
1754
- }
1755
- }
1756
- if (parsed?.customColor1 && typeof parsed.customColor1 === "string") {
1757
- customColor1 = normalizeColor(parsed.customColor1);
1758
- }
1759
- if (parsed?.customColor2 && typeof parsed.customColor2 === "string") {
1760
- customColor2 = normalizeColor(parsed.customColor2);
1761
- }
1762
- } catch (error) {
1763
- console.debug(
1764
- "[portal-components] unable to parse branding JSON",
1765
- error
1766
- );
1901
+ let primaryColor;
1902
+ let secondaryColor;
1903
+ let background;
1904
+ let backgroundImageUrl;
1905
+ let backgroundGradientStart;
1906
+ let backgroundGradientEnd;
1907
+ if (brandingProp) {
1908
+ if (brandingProp.logo && typeof brandingProp.logo === "string") {
1909
+ logo = brandingProp.logo;
1910
+ }
1911
+ if (brandingProp.title && typeof brandingProp.title === "string") {
1912
+ const normalizedTitle = brandingProp.title.trim();
1913
+ if (normalizedTitle) {
1914
+ brandTitle = normalizedTitle;
1767
1915
  }
1768
- } else {
1769
- console.debug("[portal-components] branding", branding);
1770
1916
  }
1771
- } catch (error) {
1772
- console.debug("[portal-components] branding fetch failed", error);
1917
+ primaryColor = normalizeColor(brandingProp.primaryColor);
1918
+ secondaryColor = normalizeColor(brandingProp.secondaryColor);
1919
+ background = brandingProp.background;
1920
+ backgroundImageUrl = brandingProp.backgroundImage;
1921
+ backgroundGradientStart = normalizeColor(brandingProp.backgroundGradientStart);
1922
+ backgroundGradientEnd = normalizeColor(brandingProp.backgroundGradientEnd);
1923
+ } else {
1924
+ try {
1925
+ const brandingResponse = await fetchCustomBranding();
1926
+ const branding = decodeBranding({ brandingData: brandingResponse.brandingData });
1927
+ logo = branding.logo;
1928
+ brandTitle = branding.title;
1929
+ primaryColor = branding.primaryColor;
1930
+ secondaryColor = branding.secondaryColor;
1931
+ background = branding.background;
1932
+ backgroundImageUrl = branding.backgroundImage;
1933
+ backgroundGradientStart = branding.backgroundGradientStart;
1934
+ backgroundGradientEnd = branding.backgroundGradientEnd;
1935
+ } catch (error) {
1936
+ console.debug("[portal-components] branding fetch failed", error);
1937
+ }
1773
1938
  }
1774
1939
  const baseLinks = links ?? defaultTopNavLinks;
1775
1940
  const hiddenSet = hiddenLabels ? new Set(hiddenLabels) : null;
@@ -1780,20 +1945,32 @@ var TopNav = async ({
1780
1945
  resolvedLinks = [...resolvedLinks, ...additionalLinks];
1781
1946
  }
1782
1947
  resolvedLinks = orderLinks(resolvedLinks, order);
1783
- const gradientStart = customColor1 ?? "rgb(235, 102, 88)";
1784
- const gradientEnd = customColor2 ?? customColor1 ?? "rgb(184, 83, 71)";
1948
+ const getHeaderBackgroundStyle = () => {
1949
+ if (background === "image" && backgroundImageUrl) {
1950
+ return {
1951
+ background: `url(${backgroundImageUrl})`,
1952
+ backgroundImage: `linear-gradient(to top, rgba(255, 255, 255, 0.3) 30%, rgba(255, 255, 255, 0)), url(${backgroundImageUrl})`,
1953
+ backgroundSize: "cover",
1954
+ backgroundPosition: "center",
1955
+ backgroundRepeat: "no-repeat"
1956
+ };
1957
+ }
1958
+ const gradientStart = background === "custom" && backgroundGradientStart ? backgroundGradientStart : primaryColor;
1959
+ const gradientEnd = background === "custom" && backgroundGradientEnd ? backgroundGradientEnd : secondaryColor;
1960
+ return {
1961
+ backgroundImage: `linear-gradient(to top, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0) 33%), linear-gradient(${gradientStart}, ${gradientEnd})`,
1962
+ backgroundRepeat: "no-repeat",
1963
+ backgroundSize: "100% 100%"
1964
+ };
1965
+ };
1785
1966
  return /* @__PURE__ */ jsx(
1786
1967
  "div",
1787
1968
  {
1788
1969
  className: "relative flex h-[280px] w-full items-start justify-center",
1789
- style: {
1790
- backgroundImage: `linear-gradient(to top, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0) 33%), linear-gradient(${gradientStart}, ${gradientEnd})`,
1791
- backgroundRepeat: "no-repeat",
1792
- backgroundSize: "100% 100%"
1793
- },
1794
- children: /* @__PURE__ */ jsx("div", { className: "mx-auto mt-[30px] w-full max-w-[1248px] px-6", children: /* @__PURE__ */ 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: [
1970
+ style: getHeaderBackgroundStyle(),
1971
+ children: /* @__PURE__ */ jsx("div", { className: "mx-auto mt-[30px] w-full max-w-[1248px] px-6", children: /* @__PURE__ */ 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: [
1795
1972
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
1796
- logo || brandTitle ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
1973
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
1797
1974
  logo ? (
1798
1975
  // eslint-disable-next-line @next/next/no-img-element
1799
1976
  /* @__PURE__ */ jsx(
@@ -1808,9 +1985,24 @@ var TopNav = async ({
1808
1985
  }
1809
1986
  }
1810
1987
  )
1811
- ) : null,
1812
- brandTitle ? /* @__PURE__ */ jsx("span", { className: "text-lg font-semibold text-gray-900", children: brandTitle }) : null
1813
- ] }) : /* @__PURE__ */ jsx("div", {}),
1988
+ ) : /* @__PURE__ */ jsxs(
1989
+ "svg",
1990
+ {
1991
+ className: "h-8 w-8 text-gray-400",
1992
+ viewBox: "0 0 24 24",
1993
+ fill: "none",
1994
+ stroke: "currentColor",
1995
+ strokeWidth: "1.5",
1996
+ "aria-hidden": "true",
1997
+ children: [
1998
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }),
1999
+ /* @__PURE__ */ jsx("ellipse", { cx: "12", cy: "12", rx: "4", ry: "10" }),
2000
+ /* @__PURE__ */ jsx("path", { d: "M2 12h20" })
2001
+ ]
2002
+ }
2003
+ ),
2004
+ /* @__PURE__ */ jsx("span", { className: "text-xl font-bold text-gray-900", children: brandTitle || "Enterprise Portal" })
2005
+ ] }),
1814
2006
  /* @__PURE__ */ jsxs("details", { className: "group relative", children: [
1815
2007
  /* @__PURE__ */ jsxs("summary", { className: "flex cursor-pointer items-center gap-2 text-sm font-medium text-gray-600 hover:text-gray-900 list-none", children: [
1816
2008
  /* @__PURE__ */ jsxs(
@@ -1872,7 +2064,7 @@ var TopNav = async ({
1872
2064
  ] })
1873
2065
  ] })
1874
2066
  ] }),
1875
- /* @__PURE__ */ 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 }) => {
2067
+ /* @__PURE__ */ 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 }) => {
1876
2068
  const isActive = activeLabel === label;
1877
2069
  const className = `flex items-center gap-2 px-4 py-1 transition text-gray-500 ${isActive ? "underline underline-offset-8 decoration-2" : ""}`;
1878
2070
  if (href) {
@@ -1925,9 +2117,17 @@ var headingClass = "text-lg font-semibold text-gray-900";
1925
2117
  var contentClass = "mt-4 flex-1 space-y-3";
1926
2118
  var itemClass = "flex items-center gap-3 text-sm text-gray-600";
1927
2119
  var iconClass = "h-5 w-5 text-gray-500";
1928
- var footerClass = "mt-6 flex justify-end text-sm font-semibold text-primary hover:text-primary/80";
1929
- 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";
1930
- var Badge = ({ count }) => /* @__PURE__ */ jsx("span", { className: badgeClass, "aria-label": `${count} updates available`, children: count });
2120
+ var footerClass = "mt-6 flex justify-end text-sm font-semibold hover:opacity-80";
2121
+ 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";
2122
+ var Badge = ({ count }) => /* @__PURE__ */ jsx(
2123
+ "span",
2124
+ {
2125
+ className: badgeClass,
2126
+ style: { backgroundColor: `var(--portal-branding-primary, ${DEFAULT_SECONDARY_COLOR})` },
2127
+ "aria-label": `${count} updates available`,
2128
+ children: count
2129
+ }
2130
+ );
1931
2131
  var UpdatesCard = ({
1932
2132
  onlineActiveCount = 0,
1933
2133
  airgapCount = 0,
@@ -1955,7 +2155,7 @@ var UpdatesCard = ({
1955
2155
  airgapUpdates > 0 && /* @__PURE__ */ jsx(Badge, { count: airgapUpdates })
1956
2156
  ] })
1957
2157
  ] }),
1958
- /* @__PURE__ */ jsx("footer", { className: footerClass, children: /* @__PURE__ */ jsx(Link, { href: "/update", children: "View updates \u2192" }) })
2158
+ /* @__PURE__ */ jsx("footer", { className: footerClass, children: /* @__PURE__ */ jsx(Link, { href: "/update", style: { color: `var(--portal-branding-secondary, ${DEFAULT_SECONDARY_COLOR})` }, children: "View updates \u2192" }) })
1959
2159
  ] });
1960
2160
  UpdatesCard.displayName = "UpdatesCard";
1961
2161
  var UploadIcon = (props) => /* @__PURE__ */ jsx(
@@ -2120,7 +2320,7 @@ var TeamsSection = ({
2120
2320
  type: "button",
2121
2321
  onClick: () => !isCurrentTeam && onTeamSwitch?.(team),
2122
2322
  disabled: isCurrentTeam,
2123
- 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"}`,
2323
+ 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"}`,
2124
2324
  style: !isCurrentTeam && primaryColor ? { "--hover-border": primaryColor } : void 0,
2125
2325
  children: [
2126
2326
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
@@ -2128,7 +2328,7 @@ var TeamsSection = ({
2128
2328
  "svg",
2129
2329
  {
2130
2330
  className: "h-4 w-4",
2131
- style: { color: primaryColor || "#6366f1" },
2331
+ style: { color: primaryColor || DEFAULT_SECONDARY_COLOR },
2132
2332
  fill: "none",
2133
2333
  viewBox: "0 0 24 24",
2134
2334
  stroke: "currentColor",
@@ -2152,7 +2352,7 @@ var TeamsSection = ({
2152
2352
  "svg",
2153
2353
  {
2154
2354
  className: "h-4 w-4",
2155
- style: { color: primaryColor || "#6366f1" },
2355
+ style: { color: primaryColor || DEFAULT_SECONDARY_COLOR },
2156
2356
  fill: "none",
2157
2357
  viewBox: "0 0 24 24",
2158
2358
  stroke: "currentColor",
@@ -2217,7 +2417,10 @@ var ProfileSection = ({
2217
2417
  resetForm();
2218
2418
  setIsEditing(true);
2219
2419
  };
2220
- const buttonStyle = primaryColor ? { backgroundColor: primaryColor, borderColor: primaryColor } : void 0;
2420
+ const buttonStyle = {
2421
+ backgroundColor: primaryColor || DEFAULT_SECONDARY_COLOR,
2422
+ borderColor: primaryColor || DEFAULT_SECONDARY_COLOR
2423
+ };
2221
2424
  return /* @__PURE__ */ jsxs("div", { className: "rounded-2xl border border-gray-200 bg-white p-6 shadow-sm", children: [
2222
2425
  /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-4", children: [
2223
2426
  /* @__PURE__ */ jsxs("div", { children: [
@@ -2244,7 +2447,7 @@ var ProfileSection = ({
2244
2447
  onClick: handleSave,
2245
2448
  disabled: isUpdating,
2246
2449
  style: buttonStyle,
2247
- 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",
2450
+ 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]",
2248
2451
  children: isUpdating ? "Saving..." : "Save"
2249
2452
  }
2250
2453
  )
@@ -2254,7 +2457,7 @@ var ProfileSection = ({
2254
2457
  type: "button",
2255
2458
  onClick: handleEdit,
2256
2459
  style: buttonStyle,
2257
- 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",
2460
+ 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]",
2258
2461
  children: "Edit"
2259
2462
  }
2260
2463
  )
@@ -2370,11 +2573,11 @@ UserSettings.displayName = "UserSettings";
2370
2573
  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)]";
2371
2574
  var headingClass3 = "text-lg font-semibold text-gray-900";
2372
2575
  var bodySpacerClass = "mt-4 flex-1";
2373
- var footerClass3 = "mt-6 flex justify-end text-sm font-semibold text-primary hover:text-primary/80";
2576
+ var footerClass3 = "mt-6 flex justify-end text-sm font-semibold hover:opacity-80";
2374
2577
  var UserSettingsCard = () => /* @__PURE__ */ jsxs("section", { className: baseCardClass3, "aria-labelledby": "user-settings-card-heading", children: [
2375
2578
  /* @__PURE__ */ jsx("header", { children: /* @__PURE__ */ jsx("h2", { id: "user-settings-card-heading", className: headingClass3, children: "User Settings" }) }),
2376
2579
  /* @__PURE__ */ jsx("div", { className: bodySpacerClass }),
2377
- /* @__PURE__ */ jsx("footer", { className: footerClass3, children: /* @__PURE__ */ jsx(Link, { href: "/user-settings", children: "View user settings \u2192" }) })
2580
+ /* @__PURE__ */ jsx("footer", { className: footerClass3, children: /* @__PURE__ */ jsx(Link, { href: "/user-settings", style: { color: `var(--portal-branding-secondary, ${DEFAULT_SECONDARY_COLOR})` }, children: "View user settings \u2192" }) })
2378
2581
  ] });
2379
2582
  UserSettingsCard.displayName = "UserSettingsCard";
2380
2583
  var UsersIcon = (props) => /* @__PURE__ */ jsx(
@@ -2412,7 +2615,7 @@ var headingClass4 = "text-lg font-semibold text-gray-900";
2412
2615
  var contentClass3 = "mt-4 flex-1 space-y-3";
2413
2616
  var itemClass3 = "flex items-center gap-3 text-sm text-gray-600";
2414
2617
  var iconClass3 = "h-5 w-5 text-gray-500";
2415
- var footerClass4 = "mt-6 flex justify-end text-sm font-semibold text-primary hover:text-primary/80";
2618
+ var footerClass4 = "mt-6 flex justify-end text-sm font-semibold hover:opacity-80";
2416
2619
  var TeamSettingsCard = ({
2417
2620
  userCount = 0,
2418
2621
  serviceAccountCount = 0
@@ -2436,7 +2639,7 @@ var TeamSettingsCard = ({
2436
2639
  ] })
2437
2640
  ] })
2438
2641
  ] }),
2439
- /* @__PURE__ */ jsx("footer", { className: footerClass4, children: /* @__PURE__ */ jsx(Link, { href: "/team-settings", children: "View team settings \u2192" }) })
2642
+ /* @__PURE__ */ jsx("footer", { className: footerClass4, children: /* @__PURE__ */ jsx(Link, { href: "/team-settings", style: { color: `var(--portal-branding-secondary, ${DEFAULT_SECONDARY_COLOR})` }, children: "View team settings \u2192" }) })
2440
2643
  ] });
2441
2644
  TeamSettingsCard.displayName = "TeamSettingsCard";
2442
2645
  var UpdateLayout = ({ children }) => {
@@ -2447,9 +2650,74 @@ var UpdateLayout = ({ children }) => {
2447
2650
  };
2448
2651
  UpdateLayout.displayName = "UpdateLayout";
2449
2652
 
2653
+ // src/saml-handlers.ts
2654
+ async function setSamlSessionImpl(token) {
2655
+ try {
2656
+ if (!token) {
2657
+ return { success: false, message: "No token provided" };
2658
+ }
2659
+ const cleanToken = token.startsWith("Bearer ") ? token.slice(7) : token;
2660
+ const { cookies: cookies2 } = await import('next/headers');
2661
+ const cookieStore = await cookies2();
2662
+ const isHttpOrigin = process.env.REPLICATED_APP_ORIGIN?.startsWith("http://");
2663
+ const secure = true;
2664
+ const sameSiteValue = isHttpOrigin ? "none" : "lax";
2665
+ cookieStore.set("portal_session", cleanToken, {
2666
+ httpOnly: true,
2667
+ secure,
2668
+ sameSite: sameSiteValue,
2669
+ path: "/"
2670
+ });
2671
+ return { success: true };
2672
+ } catch (error) {
2673
+ console.error("[saml-handlers] Error setting session:", error);
2674
+ return {
2675
+ success: false,
2676
+ message: "Failed to establish session"
2677
+ };
2678
+ }
2679
+ }
2680
+ async function handleSamlAcs(request) {
2681
+ try {
2682
+ const apiOrigin = getApiOrigin();
2683
+ const formData = await request.formData();
2684
+ const response = await fetch(`${apiOrigin}/saml/acs`, {
2685
+ method: "POST",
2686
+ body: formData,
2687
+ redirect: "manual"
2688
+ });
2689
+ if (response.status === 302 || response.status === 301) {
2690
+ const location = response.headers.get("Location");
2691
+ if (location) {
2692
+ return Response.redirect(location, response.status);
2693
+ }
2694
+ }
2695
+ console.error("[saml-acs] Error response from backend:", {
2696
+ status: response.status,
2697
+ statusText: response.statusText
2698
+ });
2699
+ let errorMessage = "SAML authentication failed";
2700
+ if (response.status === 401 || response.status === 403) {
2701
+ errorMessage = "Authentication failed. Please check your credentials or contact your administrator.";
2702
+ } else if (response.status === 400) {
2703
+ errorMessage = "Invalid SAML request. Please try again.";
2704
+ }
2705
+ return Response.redirect(
2706
+ new URL(`/error?code=${response.status}&message=${encodeURIComponent(errorMessage)}`, request.url),
2707
+ 302
2708
+ );
2709
+ } catch (error) {
2710
+ console.error("[saml-acs] Error processing SAML response:", error);
2711
+ return Response.redirect(
2712
+ new URL(`/error?code=500&message=${encodeURIComponent("SAML authentication error")}`, request.url),
2713
+ 302
2714
+ );
2715
+ }
2716
+ }
2717
+
2450
2718
  // src/index.ts
2451
2719
  var portalComponentsVersion = package_default.version;
2452
2720
 
2453
- export { Button, LicenseDetails, SupportCard, TeamSettingsCard, TopNav, UpdateLayout, UpdatesCard, UserSettings, UserSettingsCard, createPortalTheme, decodeJwtPayload, defaultTopNavLinks, defineServerAction, deleteSupportBundle, downloadSecuritySBOM, downloadSupportBundle, fetchCurrentUser, fetchCustomBranding, fetchDashboardComposite, fetchLicenseDetails, fetchNotifications, getCustomerIdFromToken, getSecurityInfo, getSecurityInfoDiff, getSecurityInfoSBOM, getSupportBundleUploadUrl, initiateLogin, listReleases, listSupportBundles, portalComponentsVersion, portalThemeTokens, updateNotifications, updateUser, uploadSupportBundle, verifyMagicLink };
2721
+ export { Button, DEFAULT_FAVICON, DEFAULT_PRIMARY_COLOR, DEFAULT_SECONDARY_COLOR, LicenseDetails, SupportCard, TeamSettingsCard, TopNav, UnauthorizedError, UpdateLayout, UpdatesCard, UserSettings, UserSettingsCard, authenticatedFetch, convertToReleaseEntry, createPortalTheme, decodeBranding, decodeJwtPayload, defaultTopNavLinks, defineServerAction, deleteSessionCookie, deleteSupportBundle, downloadSecuritySBOM, downloadSupportBundle, fetchCurrentUser, fetchCustomBranding, fetchDashboardComposite, fetchLicense, fetchNotifications, formatBytes, formatDate, formatDateShort, formatDateTime, formatDateTimeLocal, getCustomerIdFromToken, getSecurityInfo, getSecurityInfoDiff, getSecurityInfoSBOM, getSupportBundleUploadUrl, handleSamlAcs, initiateLogin, isHttpApiOrigin, isRedirectError, listReleases, listSupportBundles, normalizeColor, portalComponentsVersion, portalThemeTokens, sanitizeUrlForCss, setSamlSessionImpl, updateNotifications, updateUser, uploadSupportBundle, validateSession, verifyMagicLink };
2454
2722
  //# sourceMappingURL=index.js.map
2455
2723
  //# sourceMappingURL=index.js.map