@replicated/portal-components 0.0.2 → 0.0.3

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 (216) hide show
  1. package/components/metadata/registry.json +83 -2
  2. package/components/metadata/registry.md +27 -2
  3. package/dist/actions/index.d.mts +566 -3
  4. package/dist/actions/index.d.ts +566 -3
  5. package/dist/actions/index.js +1853 -12
  6. package/dist/actions/index.js.map +1 -1
  7. package/dist/airgap-instances.d.mts +26 -0
  8. package/dist/airgap-instances.d.ts +26 -0
  9. package/dist/airgap-instances.js +354 -0
  10. package/dist/airgap-instances.js.map +1 -0
  11. package/dist/error-page.d.mts +14 -0
  12. package/dist/error-page.d.ts +14 -0
  13. package/dist/error-page.js +153 -0
  14. package/dist/error-page.js.map +1 -0
  15. package/dist/error.d.mts +15 -0
  16. package/dist/error.d.ts +15 -0
  17. package/dist/error.js +144 -0
  18. package/dist/error.js.map +1 -0
  19. package/dist/esm/actions/index.js +1816 -13
  20. package/dist/esm/actions/index.js.map +1 -1
  21. package/dist/esm/airgap-instances.js +352 -0
  22. package/dist/esm/airgap-instances.js.map +1 -0
  23. package/dist/esm/error-page.js +151 -0
  24. package/dist/esm/error-page.js.map +1 -0
  25. package/dist/esm/error.js +142 -0
  26. package/dist/esm/error.js.map +1 -0
  27. package/dist/esm/helm-install-wizard.js +1007 -0
  28. package/dist/esm/helm-install-wizard.js.map +1 -0
  29. package/dist/esm/index.js +2232 -155
  30. package/dist/esm/index.js.map +1 -1
  31. package/dist/esm/install-actions.js +746 -0
  32. package/dist/esm/install-actions.js.map +1 -0
  33. package/dist/esm/install-card.js +115 -0
  34. package/dist/esm/install-card.js.map +1 -0
  35. package/dist/esm/install-targets.js +48 -0
  36. package/dist/esm/install-targets.js.map +1 -0
  37. package/dist/esm/instance-card.js +197 -0
  38. package/dist/esm/instance-card.js.map +1 -0
  39. package/dist/esm/join-team.js +218 -0
  40. package/dist/esm/join-team.js.map +1 -0
  41. package/dist/esm/license-card.js +131 -0
  42. package/dist/esm/license-card.js.map +1 -0
  43. package/dist/esm/license-details.js +667 -0
  44. package/dist/esm/license-details.js.map +1 -0
  45. package/dist/esm/linux-install-wizard.js +1083 -0
  46. package/dist/esm/linux-install-wizard.js.map +1 -0
  47. package/dist/esm/login.js +261 -0
  48. package/dist/esm/login.js.map +1 -0
  49. package/dist/esm/online-instance-list.js +287 -0
  50. package/dist/esm/online-instance-list.js.map +1 -0
  51. package/dist/esm/pending-installations.js +235 -0
  52. package/dist/esm/pending-installations.js.map +1 -0
  53. package/dist/esm/release-history-panel.js +100 -0
  54. package/dist/esm/release-history-panel.js.map +1 -0
  55. package/dist/esm/release-notes-card.js +23 -0
  56. package/dist/esm/release-notes-card.js.map +1 -0
  57. package/dist/esm/security-card.js +700 -0
  58. package/dist/esm/security-card.js.map +1 -0
  59. package/dist/esm/support-bundle-collection-card.js +170 -0
  60. package/dist/esm/support-bundle-collection-card.js.map +1 -0
  61. package/dist/esm/support-bundles-card.js +306 -0
  62. package/dist/esm/support-bundles-card.js.map +1 -0
  63. package/dist/esm/support-card.js +305 -0
  64. package/dist/esm/support-card.js.map +1 -0
  65. package/dist/esm/team-selection.js +117 -0
  66. package/dist/esm/team-selection.js.map +1 -0
  67. package/dist/esm/team-settings-card.js +78 -0
  68. package/dist/esm/team-settings-card.js.map +1 -0
  69. package/dist/esm/team-settings.js +136 -0
  70. package/dist/esm/team-settings.js.map +1 -0
  71. package/dist/esm/top-nav-user-menu.js +173 -0
  72. package/dist/esm/top-nav-user-menu.js.map +1 -0
  73. package/dist/esm/top-nav.js +398 -0
  74. package/dist/esm/top-nav.js.map +1 -0
  75. package/dist/esm/update-layout.js +405 -0
  76. package/dist/esm/update-layout.js.map +1 -0
  77. package/dist/esm/updates-card.js +85 -0
  78. package/dist/esm/updates-card.js.map +1 -0
  79. package/dist/esm/upload-support-bundle-modal.js +143 -0
  80. package/dist/esm/upload-support-bundle-modal.js.map +1 -0
  81. package/dist/esm/user-settings-card.js +21 -0
  82. package/dist/esm/user-settings-card.js.map +1 -0
  83. package/dist/esm/user-settings.js +368 -0
  84. package/dist/esm/user-settings.js.map +1 -0
  85. package/dist/esm/utils/index.js +170 -0
  86. package/dist/esm/utils/index.js.map +1 -0
  87. package/dist/helm-install-wizard.d.mts +38 -0
  88. package/dist/helm-install-wizard.d.ts +38 -0
  89. package/dist/helm-install-wizard.js +1011 -0
  90. package/dist/helm-install-wizard.js.map +1 -0
  91. package/dist/index.d.mts +11 -27
  92. package/dist/index.d.ts +11 -27
  93. package/dist/index.js +2258 -154
  94. package/dist/index.js.map +1 -1
  95. package/dist/install-B19AaKF_.d.mts +233 -0
  96. package/dist/install-Bi1qJ8Bu.d.ts +233 -0
  97. package/dist/install-actions.d.mts +141 -0
  98. package/dist/install-actions.d.ts +141 -0
  99. package/dist/install-actions.js +765 -0
  100. package/dist/install-actions.js.map +1 -0
  101. package/dist/install-card.d.mts +15 -0
  102. package/dist/install-card.d.ts +15 -0
  103. package/dist/install-card.js +117 -0
  104. package/dist/install-card.js.map +1 -0
  105. package/dist/install-targets.d.mts +19 -0
  106. package/dist/install-targets.d.ts +19 -0
  107. package/dist/install-targets.js +50 -0
  108. package/dist/install-targets.js.map +1 -0
  109. package/dist/instance-card.d.mts +22 -0
  110. package/dist/instance-card.d.ts +22 -0
  111. package/dist/instance-card.js +199 -0
  112. package/dist/instance-card.js.map +1 -0
  113. package/dist/join-team.d.mts +30 -0
  114. package/dist/join-team.d.ts +30 -0
  115. package/dist/join-team.js +220 -0
  116. package/dist/join-team.js.map +1 -0
  117. package/dist/license-card.d.mts +15 -0
  118. package/dist/license-card.d.ts +15 -0
  119. package/dist/license-card.js +133 -0
  120. package/dist/license-card.js.map +1 -0
  121. package/dist/license-details.d.mts +10 -0
  122. package/dist/license-details.d.ts +10 -0
  123. package/dist/license-details.js +669 -0
  124. package/dist/license-details.js.map +1 -0
  125. package/dist/linux-install-wizard.d.mts +66 -0
  126. package/dist/linux-install-wizard.d.ts +66 -0
  127. package/dist/linux-install-wizard.js +1093 -0
  128. package/dist/linux-install-wizard.js.map +1 -0
  129. package/dist/login.d.mts +37 -0
  130. package/dist/login.d.ts +37 -0
  131. package/dist/login.js +263 -0
  132. package/dist/login.js.map +1 -0
  133. package/dist/online-instance-list.d.mts +22 -0
  134. package/dist/online-instance-list.d.ts +22 -0
  135. package/dist/online-instance-list.js +289 -0
  136. package/dist/online-instance-list.js.map +1 -0
  137. package/dist/pending-installations.d.mts +15 -0
  138. package/dist/pending-installations.d.ts +15 -0
  139. package/dist/pending-installations.js +237 -0
  140. package/dist/pending-installations.js.map +1 -0
  141. package/dist/release-history-panel.d.mts +22 -0
  142. package/dist/release-history-panel.d.ts +22 -0
  143. package/dist/release-history-panel.js +102 -0
  144. package/dist/release-history-panel.js.map +1 -0
  145. package/dist/release-notes-card.d.mts +13 -0
  146. package/dist/release-notes-card.d.ts +13 -0
  147. package/dist/release-notes-card.js +25 -0
  148. package/dist/release-notes-card.js.map +1 -0
  149. package/dist/security-card.d.mts +73 -0
  150. package/dist/security-card.d.ts +73 -0
  151. package/dist/security-card.js +702 -0
  152. package/dist/security-card.js.map +1 -0
  153. package/dist/styles.css +1877 -194
  154. package/dist/support-bundle-collection-card.d.mts +20 -0
  155. package/dist/support-bundle-collection-card.d.ts +20 -0
  156. package/dist/support-bundle-collection-card.js +172 -0
  157. package/dist/support-bundle-collection-card.js.map +1 -0
  158. package/dist/support-bundles-card.d.mts +19 -0
  159. package/dist/support-bundles-card.d.ts +19 -0
  160. package/dist/support-bundles-card.js +308 -0
  161. package/dist/support-bundles-card.js.map +1 -0
  162. package/dist/support-card.d.mts +8 -0
  163. package/dist/support-card.d.ts +8 -0
  164. package/dist/support-card.js +307 -0
  165. package/dist/support-card.js.map +1 -0
  166. package/dist/team-selection.d.mts +23 -0
  167. package/dist/team-selection.d.ts +23 -0
  168. package/dist/team-selection.js +119 -0
  169. package/dist/team-selection.js.map +1 -0
  170. package/dist/team-settings-card-Dq1d9b5c.d.mts +14 -0
  171. package/dist/team-settings-card-Dq1d9b5c.d.ts +14 -0
  172. package/dist/team-settings-card.d.mts +2 -0
  173. package/dist/team-settings-card.d.ts +2 -0
  174. package/dist/team-settings-card.js +80 -0
  175. package/dist/team-settings-card.js.map +1 -0
  176. package/dist/team-settings.d.mts +25 -0
  177. package/dist/team-settings.d.ts +25 -0
  178. package/dist/team-settings.js +138 -0
  179. package/dist/team-settings.js.map +1 -0
  180. package/dist/top-nav-0mb1K_H0.d.mts +32 -0
  181. package/dist/top-nav-0mb1K_H0.d.ts +32 -0
  182. package/dist/top-nav-user-menu.d.mts +18 -0
  183. package/dist/top-nav-user-menu.d.ts +18 -0
  184. package/dist/top-nav-user-menu.js +175 -0
  185. package/dist/top-nav-user-menu.js.map +1 -0
  186. package/dist/top-nav.d.mts +3 -0
  187. package/dist/top-nav.d.ts +3 -0
  188. package/dist/top-nav.js +400 -0
  189. package/dist/top-nav.js.map +1 -0
  190. package/dist/update-layout.d.mts +12 -0
  191. package/dist/update-layout.d.ts +12 -0
  192. package/dist/update-layout.js +407 -0
  193. package/dist/update-layout.js.map +1 -0
  194. package/dist/updates-card-BbubBrVR.d.mts +18 -0
  195. package/dist/updates-card-BbubBrVR.d.ts +18 -0
  196. package/dist/updates-card.d.mts +2 -0
  197. package/dist/updates-card.d.ts +2 -0
  198. package/dist/updates-card.js +87 -0
  199. package/dist/updates-card.js.map +1 -0
  200. package/dist/upload-support-bundle-modal.d.mts +19 -0
  201. package/dist/upload-support-bundle-modal.d.ts +19 -0
  202. package/dist/upload-support-bundle-modal.js +145 -0
  203. package/dist/upload-support-bundle-modal.js.map +1 -0
  204. package/dist/user-settings-card.d.mts +8 -0
  205. package/dist/user-settings-card.d.ts +8 -0
  206. package/dist/user-settings-card.js +23 -0
  207. package/dist/user-settings-card.js.map +1 -0
  208. package/dist/user-settings.d.mts +47 -0
  209. package/dist/user-settings.d.ts +47 -0
  210. package/dist/user-settings.js +370 -0
  211. package/dist/user-settings.js.map +1 -0
  212. package/dist/utils/index.d.mts +70 -0
  213. package/dist/utils/index.d.ts +70 -0
  214. package/dist/utils/index.js +177 -0
  215. package/dist/utils/index.js.map +1 -0
  216. package/package.json +163 -3
package/dist/index.js CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  var react = require('react');
4
4
  var jsxRuntime = require('react/jsx-runtime');
5
+ var buffer = require('buffer');
5
6
 
6
7
  /**
7
8
  * Enterprise Portal Components
@@ -10,7 +11,7 @@ var jsxRuntime = require('react/jsx-runtime');
10
11
 
11
12
  // package.json
12
13
  var package_default = {
13
- version: "0.0.2"};
14
+ version: "0.0.3"};
14
15
 
15
16
  // src/tokens/index.ts
16
17
  var baseTokens = {
@@ -124,190 +125,2293 @@ var Button = react.forwardRef(
124
125
  }
125
126
  );
126
127
  Button.displayName = "Button";
127
- var Login = react.forwardRef(
128
- ({
129
- logo,
130
- title = "Enterprise Factory Installation Portal",
131
- description = "Sign in to manage your enterprise installation",
132
- label = "Work email address",
133
- placeholder = "you@company.com",
134
- ctaLabel = "Continue with email \u2192",
135
- initialEmail = "",
136
- isSubmitting = false,
137
- onContinue,
138
- onVerifyCode,
139
- redirectPath = "/",
140
- className,
141
- ...props
142
- }, ref) => {
143
- const [mode, setMode] = react.useState("email");
144
- const [email, setEmail] = react.useState(initialEmail);
145
- const [submittedEmail, setSubmittedEmail] = react.useState(initialEmail);
146
- const [verificationCode, setVerificationCode] = react.useState("");
147
- const [verifying, setVerifying] = react.useState(false);
148
- const [pending, setPending] = react.useState(false);
149
- const [verificationError, setVerificationError] = react.useState(
150
- null
128
+
129
+ // src/utils/api-client.ts
130
+ function isRedirectError(error) {
131
+ return typeof error === "object" && error !== null && "digest" in error && typeof error.digest === "string" && error.digest.startsWith("NEXT_REDIRECT");
132
+ }
133
+ async function authenticatedFetch(url, options = {}) {
134
+ const { token, ...fetchOptions } = options;
135
+ const headers = new Headers(fetchOptions.headers);
136
+ if (token) {
137
+ headers.set("authorization", `Bearer ${token}`);
138
+ }
139
+ const response = await fetch(url, {
140
+ ...fetchOptions,
141
+ headers
142
+ });
143
+ if (response.status === 401) {
144
+ await handle401();
145
+ }
146
+ if (response.status === 502 || response.status === 503 || response.status === 504) {
147
+ await handleServerError(response.status);
148
+ }
149
+ return response;
150
+ }
151
+ async function handle401() {
152
+ const { redirect } = await import('next/navigation');
153
+ return redirect("/?expired=1");
154
+ }
155
+ async function handleServerError(statusCode) {
156
+ const { redirect } = await import('next/navigation');
157
+ let sourceUrl;
158
+ try {
159
+ const { headers } = await import('next/headers');
160
+ const headersList = await headers();
161
+ const referer = headersList.get("referer");
162
+ const host = headersList.get("host");
163
+ const pathname = headersList.get("x-invoke-path") || headersList.get("x-forwarded-path");
164
+ if (referer) {
165
+ sourceUrl = referer;
166
+ } else if (host && pathname) {
167
+ const protocol = headersList.get("x-forwarded-proto") || "https";
168
+ sourceUrl = `${protocol}://${host}${pathname}`;
169
+ }
170
+ } catch (error) {
171
+ console.debug("[portal-components] Could not determine source URL", error);
172
+ }
173
+ const params = new URLSearchParams({ code: String(statusCode) });
174
+ if (sourceUrl) {
175
+ params.set("source", sourceUrl);
176
+ }
177
+ return redirect(`/error?${params.toString()}`);
178
+ }
179
+
180
+ // src/actions/index.ts
181
+ var getApiOrigin = () => {
182
+ return (process.env.REPLICATED_APP_ORIGIN || "https://replicated.app").replace(/\/+$/, "");
183
+ };
184
+ var defineServerAction = (definition) => definition;
185
+ var initiateLogin = defineServerAction({
186
+ id: "auth/initiate-login",
187
+ description: "Begins the passwordless login flow by dispatching a magic link email.",
188
+ visibility: "customer",
189
+ tags: ["auth", "login", "session"],
190
+ async run(input) {
191
+ const endpoint = `${getApiOrigin()}/v3/login/magic-link`;
192
+ const appSlug = process.env.PORTAL_APP_SLUG;
193
+ if (!appSlug) {
194
+ throw new Error("PORTAL_APP_SLUG is not configured");
195
+ }
196
+ const portalOrigin = process.env.PORTAL_ORIGIN ?? "https://enterprise.replicated.com";
197
+ const redirectUri = `${portalOrigin.replace(/\/+$/, "")}/${appSlug}/login`;
198
+ const response = await fetch(endpoint, {
199
+ method: "POST",
200
+ headers: {
201
+ "content-type": "application/json"
202
+ },
203
+ body: JSON.stringify({
204
+ app_slug: appSlug,
205
+ email_address: input.email,
206
+ redirect_uri: redirectUri
207
+ })
208
+ });
209
+ if (!response.ok) {
210
+ throw new Error(
211
+ `Magic link request failed (${response.status} ${response.statusText})`
212
+ );
213
+ }
214
+ const data = await response.json();
215
+ if (data.saml_redirect_required && data.saml_customer_id) {
216
+ return {
217
+ status: "saml_redirect",
218
+ requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
219
+ message: "SAML authentication required",
220
+ saml: {
221
+ redirectRequired: true,
222
+ customerId: data.saml_customer_id,
223
+ email: input.email,
224
+ appSlug
225
+ }
226
+ };
227
+ }
228
+ return {
229
+ status: "ok",
230
+ requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
231
+ message: `Magic link requested for ${input.email}`
232
+ };
233
+ }
234
+ });
235
+ var verifyMagicLink = defineServerAction({
236
+ id: "auth/verify-magic-link",
237
+ description: "Verifies the 12-digit code provided via email and returns a JWT.",
238
+ visibility: "customer",
239
+ tags: ["auth", "login", "verify"],
240
+ async run({ nonce }) {
241
+ const endpoint = `${getApiOrigin()}/v3/login/magic-link/verify`;
242
+ if (process.env.NODE_ENV !== "production") {
243
+ console.debug(
244
+ "[portal-components] verifying magic link via %s",
245
+ endpoint
246
+ );
247
+ }
248
+ const response = await fetch(endpoint, {
249
+ method: "POST",
250
+ headers: {
251
+ "content-type": "application/json"
252
+ },
253
+ body: JSON.stringify({ nonce })
254
+ });
255
+ if (!response.ok) {
256
+ if (response.status === 401) {
257
+ try {
258
+ const errorBody = await response.json();
259
+ if (errorBody?.is_expired === true) {
260
+ const error3 = {
261
+ code: "expired",
262
+ message: "Magic link has expired. A new link has been sent to your email.",
263
+ isExpired: true
264
+ };
265
+ throw error3;
266
+ }
267
+ } catch (parseError) {
268
+ if (parseError?.code === "expired") {
269
+ throw parseError;
270
+ }
271
+ }
272
+ const error2 = {
273
+ code: "invalid_code",
274
+ message: "Incorrect code, check your email and try again."
275
+ };
276
+ throw error2;
277
+ }
278
+ const error = {
279
+ code: "unknown",
280
+ message: `Magic link verification failed (${response.status} ${response.statusText})`
281
+ };
282
+ throw error;
283
+ }
284
+ const payload = await response.json();
285
+ const token = payload?.token ?? payload?.jwt ?? payload?.access_token;
286
+ if (typeof token !== "string") {
287
+ throw new Error("Magic link verification succeeded but no token returned");
288
+ }
289
+ return { token, raw: payload };
290
+ }
291
+ });
292
+ var fetchCustomBrandingImpl = async () => {
293
+ const appSlug = process.env.PORTAL_APP_SLUG;
294
+ if (!appSlug) {
295
+ throw new Error("PORTAL_APP_SLUG is not configured");
296
+ }
297
+ const url = `${getApiOrigin()}/v3/custom-branding?app_slug=${encodeURIComponent(
298
+ appSlug
299
+ )}`;
300
+ if (process.env.NODE_ENV !== "production") {
301
+ console.debug(
302
+ "[portal-components] fetching custom branding via %s",
303
+ url
151
304
  );
152
- const handleSubmit = async (event) => {
153
- event.preventDefault();
154
- if (!onContinue || mode !== "email") {
155
- return;
305
+ }
306
+ const response = await fetch(url, {
307
+ headers: {
308
+ accept: "application/json"
309
+ }
310
+ });
311
+ if (!response.ok) {
312
+ throw new Error(
313
+ `Custom branding request failed (${response.status} ${response.statusText})`
314
+ );
315
+ }
316
+ const payload = await response.json();
317
+ const brandingData = payload?.branding_data;
318
+ if (typeof brandingData !== "string") {
319
+ throw new Error("Custom branding response missing branding_data string");
320
+ }
321
+ return {
322
+ brandingData,
323
+ documentation: payload?.documentation ?? null
324
+ };
325
+ };
326
+ var fetchCustomBranding = react.cache(fetchCustomBrandingImpl);
327
+ var decodeJwtPayload = (token) => {
328
+ const parts = token.split(".");
329
+ if (parts.length !== 3) {
330
+ throw new Error("Invalid JWT received");
331
+ }
332
+ const payloadSegment = parts[1];
333
+ if (!payloadSegment) {
334
+ throw new Error("JWT payload segment missing");
335
+ }
336
+ const padded = payloadSegment.padEnd(
337
+ payloadSegment.length + (4 - payloadSegment.length % 4) % 4,
338
+ "="
339
+ );
340
+ const decoded = Buffer.from(padded, "base64").toString("utf-8");
341
+ return JSON.parse(decoded);
342
+ };
343
+ var getCustomerIdFromToken = (token) => {
344
+ const payload = decodeJwtPayload(token);
345
+ const customerId = payload?.customer_id || payload?.customerId;
346
+ if (typeof customerId !== "string" || !customerId.trim()) {
347
+ throw new Error("Unable to determine customer_id from session token");
348
+ }
349
+ return customerId.trim();
350
+ };
351
+ var resolveSupportBundlesEndpoint = () => {
352
+ const fallback = `${getApiOrigin()}/v3/supportbundles`;
353
+ const explicit = process.env.SUPPORT_BUNDLES_ENDPOINT;
354
+ if (!explicit) {
355
+ return new URL(fallback);
356
+ }
357
+ try {
358
+ return new URL(explicit);
359
+ } catch (error) {
360
+ console.warn(
361
+ `[portal-components] invalid SUPPORT_BUNDLES_ENDPOINT, using fallback`,
362
+ error
363
+ );
364
+ return new URL(fallback);
365
+ }
366
+ };
367
+ var listSupportBundles = defineServerAction({
368
+ id: "support/list-bundles",
369
+ description: "Fetches support bundles associated with the customer found in the portal session JWT.",
370
+ visibility: "customer",
371
+ tags: ["support", "bundles"],
372
+ async run({ token }, context) {
373
+ if (!token || typeof token !== "string") {
374
+ throw new Error("Support bundle listing requires a session token");
375
+ }
376
+ const payload = decodeJwtPayload(token);
377
+ const customerId = payload?.customer_id;
378
+ if (typeof customerId !== "string" || !customerId.trim()) {
379
+ throw new Error("Unable to determine customer_id from session token");
380
+ }
381
+ const url = resolveSupportBundlesEndpoint();
382
+ url.searchParams.set("customer_id", customerId.trim());
383
+ if (process.env.NODE_ENV !== "production") {
384
+ console.debug("[portal-components] fetching support bundles via %s", url);
385
+ }
386
+ const response = await authenticatedFetch(url.toString(), {
387
+ token,
388
+ headers: {
389
+ accept: "application/json"
390
+ },
391
+ signal: context?.signal
392
+ });
393
+ if (context?.signal?.aborted) {
394
+ throw new Error("Support bundles request was aborted");
395
+ }
396
+ if (!response.ok) {
397
+ throw new Error(
398
+ `Support bundles request failed (${response.status} ${response.statusText})`
399
+ );
400
+ }
401
+ const raw = await response.json();
402
+ const rawRecord = raw && typeof raw === "object" ? raw : void 0;
403
+ const parseInsights = (raw2) => {
404
+ if (!Array.isArray(raw2)) return void 0;
405
+ return raw2.filter((i) => i && typeof i === "object").map((i) => ({
406
+ level: String(i.level ?? ""),
407
+ primary: String(i.primary ?? ""),
408
+ key: typeof i.key === "string" ? i.key : void 0,
409
+ detail: typeof i.detail === "string" ? i.detail : void 0
410
+ }));
411
+ };
412
+ const bundles = Array.isArray(
413
+ rawRecord?.supportBundles
414
+ ) ? (rawRecord?.supportBundles).map((item) => {
415
+ if (!item || typeof item !== "object") {
416
+ return {
417
+ id: "",
418
+ createdAt: void 0,
419
+ status: void 0,
420
+ size: void 0,
421
+ instanceId: void 0,
422
+ insights: void 0,
423
+ metadata: void 0
424
+ };
156
425
  }
157
- try {
158
- setPending(true);
159
- await onContinue(email);
160
- setSubmittedEmail(email);
161
- setVerificationCode("");
162
- setVerificationError(null);
163
- setMode("verify");
164
- } finally {
165
- setPending(false);
426
+ const record = item;
427
+ return {
428
+ id: String(record.id ?? ""),
429
+ createdAt: typeof record.createdAt === "string" ? record.createdAt : void 0,
430
+ status: typeof record.status === "string" ? record.status : void 0,
431
+ size: typeof record.size === "number" ? record.size : void 0,
432
+ instanceId: typeof record.instanceId === "string" ? record.instanceId : void 0,
433
+ insights: parseInsights(record.insights),
434
+ metadata: record
435
+ };
436
+ }) : Array.isArray(raw) ? raw.map((item) => {
437
+ if (!item || typeof item !== "object") {
438
+ return {
439
+ id: "",
440
+ createdAt: void 0,
441
+ status: void 0,
442
+ size: void 0,
443
+ instanceId: void 0,
444
+ insights: void 0,
445
+ metadata: void 0
446
+ };
447
+ }
448
+ const record = item;
449
+ return {
450
+ id: String(record.id ?? ""),
451
+ createdAt: typeof record.createdAt === "string" ? record.createdAt : void 0,
452
+ status: typeof record.status === "string" ? record.status : void 0,
453
+ size: typeof record.size === "number" ? record.size : void 0,
454
+ instanceId: typeof record.instanceId === "string" ? record.instanceId : void 0,
455
+ insights: parseInsights(record.insights),
456
+ metadata: record
457
+ };
458
+ }) : [];
459
+ const totalCount = (() => {
460
+ if (rawRecord) {
461
+ if (typeof rawRecord.totalCount === "number" && Number.isFinite(rawRecord.totalCount)) {
462
+ return rawRecord.totalCount;
463
+ }
464
+ if (Array.isArray(rawRecord.supportBundles)) {
465
+ return rawRecord.supportBundles.length;
466
+ }
467
+ }
468
+ if (Array.isArray(raw)) {
469
+ return raw.length;
470
+ }
471
+ return bundles.length;
472
+ })();
473
+ return {
474
+ bundles,
475
+ totalCount,
476
+ raw
477
+ };
478
+ }
479
+ });
480
+ var downloadSupportBundle = defineServerAction({
481
+ id: "support/download-bundle",
482
+ description: "Gets a signed URL for downloading a support bundle.",
483
+ visibility: "customer",
484
+ tags: ["support", "bundles", "download"],
485
+ async run({ token, bundleId }, context) {
486
+ if (!token || typeof token !== "string") {
487
+ throw new Error("Support bundle download requires a session token");
488
+ }
489
+ if (!bundleId || typeof bundleId !== "string") {
490
+ throw new Error("Support bundle download requires a bundle ID");
491
+ }
492
+ const payload = decodeJwtPayload(token);
493
+ const customerId = payload?.customer_id;
494
+ if (typeof customerId !== "string" || !customerId.trim()) {
495
+ throw new Error("Unable to determine customer_id from session token");
496
+ }
497
+ const endpoint = `${getApiOrigin()}/v3/supportbundle/${encodeURIComponent(bundleId)}/download?customer_id=${encodeURIComponent(customerId.trim())}`;
498
+ if (process.env.NODE_ENV !== "production") {
499
+ console.debug("[portal-components] getting support bundle download URL via %s", endpoint);
500
+ }
501
+ const response = await authenticatedFetch(endpoint, {
502
+ method: "GET",
503
+ token,
504
+ headers: {
505
+ accept: "application/json"
506
+ },
507
+ signal: context?.signal
508
+ });
509
+ if (!response.ok) {
510
+ const errorText = await response.text().catch(() => "");
511
+ throw new Error(
512
+ `Support bundle download URL request failed (${response.status} ${response.statusText}): ${errorText}`
513
+ );
514
+ }
515
+ const data = await response.json();
516
+ const signedUrl = data?.signedUrl;
517
+ if (typeof signedUrl !== "string" || !signedUrl) {
518
+ throw new Error("Support bundle download response missing signedUrl");
519
+ }
520
+ return { signedUrl };
521
+ }
522
+ });
523
+ var deleteSupportBundle = defineServerAction({
524
+ id: "support/delete-bundle",
525
+ description: "Deletes a support bundle.",
526
+ visibility: "customer",
527
+ tags: ["support", "bundles", "delete"],
528
+ async run({ token, bundleId }, context) {
529
+ if (!token || typeof token !== "string") {
530
+ throw new Error("Support bundle deletion requires a session token");
531
+ }
532
+ if (!bundleId || typeof bundleId !== "string") {
533
+ throw new Error("Support bundle deletion requires a bundle ID");
534
+ }
535
+ const payload = decodeJwtPayload(token);
536
+ const customerId = payload?.customer_id;
537
+ if (typeof customerId !== "string" || !customerId.trim()) {
538
+ throw new Error("Unable to determine customer_id from session token");
539
+ }
540
+ const endpoint = `${getApiOrigin()}/v3/supportbundle/${encodeURIComponent(bundleId)}?customer_id=${encodeURIComponent(customerId.trim())}`;
541
+ if (process.env.NODE_ENV !== "production") {
542
+ console.debug("[portal-components] deleting support bundle via %s", endpoint);
543
+ }
544
+ const response = await authenticatedFetch(endpoint, {
545
+ method: "DELETE",
546
+ token,
547
+ headers: {
548
+ accept: "application/json"
549
+ },
550
+ signal: context?.signal
551
+ });
552
+ if (!response.ok) {
553
+ const errorText = await response.text().catch(() => "");
554
+ if (response.status === 404) {
555
+ throw new Error("Support bundle not found");
166
556
  }
557
+ throw new Error(
558
+ `Support bundle deletion failed (${response.status} ${response.statusText}): ${errorText}`
559
+ );
560
+ }
561
+ return { success: true };
562
+ }
563
+ });
564
+ var uploadSupportBundle = defineServerAction({
565
+ id: "support/upload-bundle",
566
+ description: "Uploads a support bundle to the server.",
567
+ visibility: "customer",
568
+ tags: ["support", "bundles", "upload"],
569
+ async run({ token, appId, fileContent, contentLength }, context) {
570
+ if (!token || typeof token !== "string") {
571
+ throw new Error("Support bundle upload requires a session token");
572
+ }
573
+ if (!appId || typeof appId !== "string") {
574
+ throw new Error("Support bundle upload requires an app ID");
575
+ }
576
+ if (!fileContent || !(fileContent instanceof ArrayBuffer)) {
577
+ throw new Error("Support bundle upload requires file content");
578
+ }
579
+ const endpoint = `${getApiOrigin()}/v3/supportbundle/upload/${encodeURIComponent(appId)}`;
580
+ if (process.env.NODE_ENV !== "production") {
581
+ console.debug("[portal-components] uploading support bundle via %s", endpoint);
582
+ }
583
+ const response = await authenticatedFetch(endpoint, {
584
+ method: "POST",
585
+ token,
586
+ headers: {
587
+ "content-type": "application/gzip",
588
+ "content-length": String(contentLength)
589
+ },
590
+ body: fileContent,
591
+ signal: context?.signal
592
+ });
593
+ if (!response.ok) {
594
+ const errorText = await response.text().catch(() => "");
595
+ throw new Error(
596
+ `Support bundle upload failed (${response.status} ${response.statusText}): ${errorText}`
597
+ );
598
+ }
599
+ const data = await response.json();
600
+ return {
601
+ bundleId: data?.bundleId ?? data?.bundle_id ?? "",
602
+ slug: data?.slug ?? ""
603
+ };
604
+ }
605
+ });
606
+ var getSupportBundleUploadUrl = (appId) => {
607
+ return `${getApiOrigin()}/v3/supportbundle/upload/${encodeURIComponent(appId)}`;
608
+ };
609
+ var listReleases = defineServerAction({
610
+ id: "releases/list",
611
+ description: "Lists available releases for the authenticated customer.",
612
+ visibility: "customer",
613
+ tags: ["releases"],
614
+ async run({ token }, context) {
615
+ if (!token || typeof token !== "string") {
616
+ throw new Error("List releases requires a session token");
617
+ }
618
+ const endpoint = `${getApiOrigin()}/v3/release-history`;
619
+ console.log("[portal-components] listReleases request", {
620
+ endpoint
621
+ });
622
+ const response = await authenticatedFetch(endpoint, {
623
+ method: "GET",
624
+ token,
625
+ headers: {
626
+ accept: "application/json"
627
+ },
628
+ signal: context?.signal
629
+ });
630
+ const bodyText = await response.text().catch((error) => {
631
+ console.warn("[portal-components] listReleases read error", error);
632
+ return null;
633
+ });
634
+ console.log("[portal-components] listReleases response", response.status, bodyText);
635
+ if (!response.ok) {
636
+ throw new Error(
637
+ `List releases request failed (${response.status} ${response.statusText})`
638
+ );
639
+ }
640
+ return {
641
+ status: response.status,
642
+ body: bodyText
167
643
  };
168
- const disabled = mode === "email" && (isSubmitting || pending);
169
- const verificationMessage = react.useMemo(() => {
170
- if (!submittedEmail) {
644
+ }
645
+ });
646
+ var asRecord = (value) => {
647
+ if (value && typeof value === "object") {
648
+ return value;
649
+ }
650
+ return void 0;
651
+ };
652
+ var getValue = (record, key) => record ? record[key] : void 0;
653
+ var getString = (record, key) => {
654
+ const value = getValue(record, key);
655
+ return typeof value === "string" ? value : void 0;
656
+ };
657
+ var getBoolean = (record, key) => {
658
+ const value = getValue(record, key);
659
+ if (typeof value === "boolean") {
660
+ return value;
661
+ }
662
+ if (typeof value === "number") {
663
+ return value === 1;
664
+ }
665
+ if (typeof value === "string") {
666
+ const normalized = value.trim().toLowerCase();
667
+ if (["true", "1", "yes"].includes(normalized)) {
668
+ return true;
669
+ }
670
+ if (["false", "0", "no"].includes(normalized)) {
671
+ return false;
672
+ }
673
+ }
674
+ return void 0;
675
+ };
676
+ var toDisplayValue = (value) => {
677
+ if (value === null || value === void 0) {
678
+ return null;
679
+ }
680
+ if (typeof value === "string") {
681
+ return value;
682
+ }
683
+ if (typeof value === "number" || typeof value === "boolean") {
684
+ return String(value);
685
+ }
686
+ try {
687
+ return JSON.stringify(value);
688
+ } catch {
689
+ return String(value);
690
+ }
691
+ };
692
+ var normalizeStringArray = (value) => {
693
+ if (Array.isArray(value)) {
694
+ const normalized = value.map(
695
+ (item) => typeof item === "string" ? item.trim() : ""
696
+ ).filter((item) => item.length > 0);
697
+ return normalized.length ? normalized : void 0;
698
+ }
699
+ if (typeof value === "string") {
700
+ const normalized = value.split(",").map((item) => item.trim()).filter((item) => item.length > 0);
701
+ return normalized.length ? normalized : void 0;
702
+ }
703
+ return void 0;
704
+ };
705
+ var normalizeLicenseFields = (input) => {
706
+ if (!input) {
707
+ return [];
708
+ }
709
+ if (Array.isArray(input)) {
710
+ return input.map((field, index) => {
711
+ if (!field || typeof field !== "object") {
171
712
  return null;
172
713
  }
173
- return `If there is an account for ${submittedEmail}, an email will be sent with a login code.`;
174
- }, [submittedEmail]);
175
- const handleVerify = async () => {
176
- if (!onVerifyCode || !verificationCode) {
177
- return;
714
+ const candidate = field;
715
+ 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}`;
716
+ 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;
717
+ let value = candidate.value ?? candidate.data ?? candidate.content;
718
+ if ((value === void 0 || value === null) && typeof candidate.text === "string") {
719
+ value = candidate.text;
178
720
  }
179
- try {
180
- setVerificationError(null);
181
- setVerifying(true);
182
- const result = await onVerifyCode(verificationCode, redirectPath);
183
- if (result && typeof result === "object" && "success" in result && result.success === false) {
184
- const message = result.message ?? "Unable to verify code. Try again.";
185
- setVerificationError(message);
186
- return;
721
+ if ((value === void 0 || value === null) && typeof candidate.defaultValue === "string") {
722
+ value = candidate.defaultValue;
723
+ }
724
+ const isSecret = Boolean(
725
+ candidate.isSecret ?? candidate.secret ?? candidate.masked
726
+ );
727
+ const resolved = toDisplayValue(value);
728
+ return {
729
+ key,
730
+ label,
731
+ value: resolved,
732
+ isSecret
733
+ };
734
+ }).filter((field) => Boolean(field));
735
+ }
736
+ if (typeof input === "object") {
737
+ return Object.entries(input).map(
738
+ ([key, value]) => {
739
+ let resolvedValue = value;
740
+ let isSecret = false;
741
+ if (value && typeof value === "object") {
742
+ const obj = value;
743
+ if ("value" in obj) {
744
+ resolvedValue = obj.value;
745
+ }
746
+ isSecret = Boolean(obj.isSecret ?? obj.secret ?? obj.masked);
187
747
  }
188
- setVerificationCode("");
189
- } catch (error) {
190
- if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") {
191
- setVerificationError(error.message);
192
- } else {
193
- setVerificationError("Unable to verify code. Try again.");
748
+ const normalized = toDisplayValue(resolvedValue);
749
+ return {
750
+ key,
751
+ label: key,
752
+ value: normalized,
753
+ isSecret
754
+ };
755
+ }
756
+ );
757
+ }
758
+ return [];
759
+ };
760
+ var extractChannelNames = (input) => {
761
+ if (!Array.isArray(input)) {
762
+ return void 0;
763
+ }
764
+ const names = input.map((item) => {
765
+ if (typeof item === "string") {
766
+ return item.trim();
767
+ }
768
+ const record = asRecord(item);
769
+ if (!record) {
770
+ return null;
771
+ }
772
+ return getString(record, "name") ?? getString(record, "channelName") ?? getString(record, "channel") ?? getString(record, "channelSlug") ?? getString(record, "slug") ?? void 0;
773
+ }).filter((name) => Boolean(name && name.length));
774
+ return names.length ? names : void 0;
775
+ };
776
+ var normalizeEntitlementFields = (fieldsInput, valuesInput) => {
777
+ const valuesMap = /* @__PURE__ */ new Map();
778
+ const assignValue = (key, value) => {
779
+ if (!key) {
780
+ return;
781
+ }
782
+ valuesMap.set(key, toDisplayValue(value));
783
+ };
784
+ if (Array.isArray(valuesInput)) {
785
+ valuesInput.forEach((item) => {
786
+ const record = asRecord(item);
787
+ if (!record) {
788
+ if (typeof item === "string") {
789
+ assignValue(item, item);
194
790
  }
195
- } finally {
196
- setVerifying(false);
791
+ return;
197
792
  }
793
+ const key = getString(record, "name") ?? getString(record, "field") ?? getString(record, "title") ?? getString(record, "label") ?? getString(record, "slug") ?? (() => {
794
+ const idValue = getValue(record, "id");
795
+ if (typeof idValue === "string" || typeof idValue === "number") {
796
+ return String(idValue);
797
+ }
798
+ return void 0;
799
+ })();
800
+ const value = getValue(record, "value") ?? getValue(record, "currentValue") ?? getValue(record, "entitlementValue") ?? getValue(record, "content") ?? getValue(record, "data") ?? getValue(record, "defaultVal") ?? getValue(record, "defaultValue");
801
+ assignValue(key, value);
802
+ });
803
+ } else if (valuesInput && typeof valuesInput === "object") {
804
+ Object.entries(valuesInput).forEach(
805
+ ([key, value]) => assignValue(key, value)
806
+ );
807
+ }
808
+ const normalized = [];
809
+ if (Array.isArray(fieldsInput)) {
810
+ fieldsInput.forEach((item, index) => {
811
+ const record = asRecord(item);
812
+ if (!record) {
813
+ return;
814
+ }
815
+ const baseKey = getString(record, "name") ?? getString(record, "field") ?? getString(record, "slug") ?? `entitlement-${index}`;
816
+ const key = `entitlement-${baseKey}`;
817
+ const label = getString(record, "title") ?? getString(record, "label") ?? baseKey;
818
+ const defaultValue = getString(record, "defaultVal") ?? getString(record, "default") ?? getString(record, "defaultValue");
819
+ const value = valuesMap.get(baseKey) ?? valuesMap.get(label) ?? defaultValue ?? null;
820
+ const isSecret = Boolean(
821
+ getBoolean(record, "secret") ?? getBoolean(record, "isSecret") ?? getBoolean(record, "masked")
822
+ );
823
+ normalized.push({
824
+ key,
825
+ label,
826
+ value,
827
+ isSecret
828
+ });
829
+ });
830
+ }
831
+ valuesMap.forEach((value, key) => {
832
+ const normalizedKey = `entitlement-${key}`;
833
+ if (!normalized.some((field) => field.key === normalizedKey)) {
834
+ normalized.push({
835
+ key: normalizedKey,
836
+ label: key,
837
+ value
838
+ });
839
+ }
840
+ });
841
+ return normalized;
842
+ };
843
+ var normalizeLicensePayload = (payload) => {
844
+ const payloadRecord = asRecord(payload);
845
+ const rootRecord = asRecord(getValue(payloadRecord, "license")) ?? asRecord(getValue(payloadRecord, "data")) ?? payloadRecord ?? {};
846
+ const sourceRecord = asRecord(getValue(rootRecord, "metadata")) ?? rootRecord;
847
+ const customer = asRecord(getValue(rootRecord, "customer")) ?? asRecord(getValue(sourceRecord, "customer")) ?? asRecord(getValue(payloadRecord, "customer")) ?? {};
848
+ let releaseChannels = normalizeStringArray(
849
+ getValue(rootRecord, "releaseChannels") ?? getValue(sourceRecord, "releaseChannels") ?? getValue(sourceRecord, "channels") ?? getValue(rootRecord, "channels") ?? getValue(sourceRecord, "channel") ?? getValue(rootRecord, "channel")
850
+ ) ?? void 0;
851
+ if (!releaseChannels) {
852
+ releaseChannels = extractChannelNames(getValue(rootRecord, "channels")) ?? extractChannelNames(getValue(sourceRecord, "channels")) ?? void 0;
853
+ }
854
+ let installMethods = normalizeStringArray(
855
+ getValue(rootRecord, "installMethods") ?? getValue(sourceRecord, "installMethods") ?? getValue(sourceRecord, "install_options") ?? getValue(rootRecord, "install_options") ?? getValue(sourceRecord, "installOptions")
856
+ ) ?? void 0;
857
+ if (!installMethods || installMethods.length === 0) {
858
+ const resolved = [];
859
+ const flag = (key) => getBoolean(rootRecord, key) ?? getBoolean(sourceRecord, key) ?? false;
860
+ if (flag("isKotsInstallEnabled")) {
861
+ resolved.push("Replicated KOTS");
862
+ }
863
+ if (flag("isHelmInstallEnabled")) {
864
+ resolved.push("Helm");
865
+ }
866
+ if (flag("isHelmAirgapEnabled")) {
867
+ resolved.push("Helm Airgap");
868
+ }
869
+ if (flag("isEmbeddedClusterDownloadEnabled") || flag("isEmbeddedClusterMultiNodeEnabled")) {
870
+ resolved.push("Embedded Cluster");
871
+ }
872
+ if (flag("isKurlInstallEnabled")) {
873
+ resolved.push("kURL");
874
+ }
875
+ if (flag("isGitopsSupported")) {
876
+ resolved.push("GitOps");
877
+ }
878
+ if (resolved.length) {
879
+ installMethods = Array.from(new Set(resolved));
880
+ }
881
+ }
882
+ 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");
883
+ const expiresAt = typeof expiresAtSource === "string" && expiresAtSource.trim().length ? expiresAtSource : expiresAtSource === null ? null : void 0;
884
+ const baseFields = normalizeLicenseFields(
885
+ getValue(rootRecord, "additionalFields") ?? getValue(sourceRecord, "additionalFields") ?? getValue(sourceRecord, "fields") ?? getValue(rootRecord, "fields") ?? getValue(payloadRecord, "fields") ?? getValue(payloadRecord, "additional_fields")
886
+ );
887
+ const entitlementFields = normalizeEntitlementFields(
888
+ getValue(rootRecord, "entitlementFields") ?? getValue(sourceRecord, "entitlementFields"),
889
+ getValue(rootRecord, "entitlementValues") ?? getValue(sourceRecord, "entitlementValues")
890
+ );
891
+ const fields = [
892
+ ...baseFields,
893
+ ...entitlementFields.filter(
894
+ (field) => !baseFields.some((existing) => existing.key === field.key)
895
+ )
896
+ ];
897
+ const statusFromSource = getString(sourceRecord, "status") ?? getString(sourceRecord, "state");
898
+ const statusLabelFromSource = getString(sourceRecord, "statusLabel") ?? getString(sourceRecord, "stateLabel");
899
+ const expiredFlag = getBoolean(sourceRecord, "isExpired") ?? getBoolean(rootRecord, "isExpired");
900
+ const derivedStatus = statusFromSource ?? (typeof expiredFlag === "boolean" ? expiredFlag ? "expired" : "active" : void 0);
901
+ const statusLabel = statusLabelFromSource ?? (derivedStatus ? derivedStatus.charAt(0).toUpperCase() + derivedStatus.slice(1) : void 0);
902
+ const licenseType = getString(sourceRecord, "licenseType") ?? getString(rootRecord, "licenseType");
903
+ const status = derivedStatus;
904
+ const license = {
905
+ id: getString(rootRecord, "id") ?? getString(sourceRecord, "id") ?? getString(sourceRecord, "licenseId") ?? getString(customer, "licenseId") ?? void 0,
906
+ status,
907
+ statusLabel,
908
+ environment: getString(sourceRecord, "environment") ?? getString(sourceRecord, "tier") ?? licenseType ?? void 0,
909
+ expiresAt: expiresAt ?? null,
910
+ releaseChannels: releaseChannels ?? [
911
+ getString(rootRecord, "channelName") ?? getString(rootRecord, "channel") ?? void 0
912
+ ].filter((value) => Boolean(value)),
913
+ installMethods,
914
+ installNotes: getString(sourceRecord, "installNotes"),
915
+ customerName: getString(sourceRecord, "customerName") ?? getString(customer, "name") ?? void 0,
916
+ customerId: getString(sourceRecord, "customerId") ?? getString(customer, "id") ?? getString(rootRecord, "customerId") ?? void 0,
917
+ customerOrganization: getString(customer, "organization") ?? getString(sourceRecord, "customerOrganization") ?? getString(rootRecord, "customerOrganization") ?? void 0,
918
+ fields
919
+ };
920
+ return license;
921
+ };
922
+ var fetchLicenseDetails = defineServerAction({
923
+ id: "license/fetch-details",
924
+ description: "Fetches the authenticated user's enterprise license details.",
925
+ visibility: "customer",
926
+ tags: ["license", "entitlements"],
927
+ async run({ token }, context) {
928
+ if (typeof token !== "string" || token.trim().length === 0) {
929
+ throw new Error("fetchLicenseDetails requires a non-empty token");
930
+ }
931
+ const endpoint = `${getApiOrigin()}/v3/license`;
932
+ const response = await authenticatedFetch(endpoint, {
933
+ method: "GET",
934
+ token,
935
+ headers: {
936
+ accept: "application/json"
937
+ },
938
+ signal: context?.signal
939
+ });
940
+ if (!response.ok) {
941
+ throw new Error(
942
+ `License request failed (${response.status} ${response.statusText})`
943
+ );
944
+ }
945
+ const payload = await response.json();
946
+ const license = normalizeLicensePayload(payload);
947
+ return {
948
+ license,
949
+ raw: payload ?? null
198
950
  };
199
- return /* @__PURE__ */ jsxRuntime.jsxs(
200
- "form",
951
+ }
952
+ });
953
+ var getSecurityInfo = defineServerAction({
954
+ id: "security/get-info",
955
+ description: "Fetches CVE security scan results for a specific release",
956
+ visibility: "customer",
957
+ tags: ["security", "cve"],
958
+ async run({ token, installType, channelSequence, isAirgap = false }, context) {
959
+ if (!token || typeof token !== "string") {
960
+ throw new Error("Security info request requires a session token");
961
+ }
962
+ const customerId = getCustomerIdFromToken(token);
963
+ const params = new URLSearchParams({
964
+ customer_id: customerId,
965
+ install_type: installType,
966
+ channel_sequence: channelSequence.toString(),
967
+ is_airgap: isAirgap.toString()
968
+ });
969
+ const url = `${getApiOrigin()}/v3/security-info?${params.toString()}`;
970
+ if (process.env.NODE_ENV !== "production") {
971
+ console.debug("[portal-components] fetching security info via %s", url);
972
+ }
973
+ const response = await authenticatedFetch(url, {
974
+ token,
975
+ headers: { accept: "application/json" },
976
+ signal: context?.signal
977
+ });
978
+ if (!response.ok) {
979
+ throw new Error(
980
+ `Security info request failed (${response.status} ${response.statusText})`
981
+ );
982
+ }
983
+ const data = await response.json();
984
+ return data;
985
+ }
986
+ });
987
+ var getSecurityInfoDiff = defineServerAction({
988
+ id: "security/get-info-diff",
989
+ description: "Fetches CVE diff between two releases showing fixed and added vulnerabilities",
990
+ visibility: "customer",
991
+ tags: ["security", "cve", "diff"],
992
+ async run({ token, installType, fromChannelSequence, toChannelSequence, isAirgap = false }, context) {
993
+ if (!token || typeof token !== "string") {
994
+ throw new Error("Security info diff request requires a session token");
995
+ }
996
+ const customerId = getCustomerIdFromToken(token);
997
+ const params = new URLSearchParams({
998
+ customer_id: customerId,
999
+ install_type: installType,
1000
+ from_channel_sequence: fromChannelSequence.toString(),
1001
+ to_channel_sequence: toChannelSequence.toString(),
1002
+ is_airgap: isAirgap.toString()
1003
+ });
1004
+ const url = `${getApiOrigin()}/v3/security-info-diff?${params.toString()}`;
1005
+ if (process.env.NODE_ENV !== "production") {
1006
+ console.debug("[portal-components] fetching security info diff via %s", url);
1007
+ }
1008
+ const response = await authenticatedFetch(url, {
1009
+ token,
1010
+ headers: { accept: "application/json" },
1011
+ signal: context?.signal
1012
+ });
1013
+ if (!response.ok) {
1014
+ throw new Error(
1015
+ `Security info diff request failed (${response.status} ${response.statusText})`
1016
+ );
1017
+ }
1018
+ const data = await response.json();
1019
+ return data;
1020
+ }
1021
+ });
1022
+ var getSecurityInfoSBOM = defineServerAction({
1023
+ id: "security/get-sbom",
1024
+ description: "Fetches Software Bill of Materials (SBOM) for a specific release",
1025
+ visibility: "customer",
1026
+ tags: ["security", "sbom"],
1027
+ async run({ token, installType, channelSequence, isAirgap = false, unifiedSbom = true }, context) {
1028
+ if (!token || typeof token !== "string") {
1029
+ throw new Error("Security SBOM request requires a session token");
1030
+ }
1031
+ const customerId = getCustomerIdFromToken(token);
1032
+ const params = new URLSearchParams({
1033
+ customer_id: customerId,
1034
+ install_type: installType,
1035
+ channel_sequence: channelSequence.toString(),
1036
+ is_airgap: isAirgap.toString(),
1037
+ unified_sbom: unifiedSbom.toString()
1038
+ });
1039
+ const url = `${getApiOrigin()}/v3/security-info-sbom?${params.toString()}`;
1040
+ if (process.env.NODE_ENV !== "production") {
1041
+ console.debug("[portal-components] fetching security SBOM via %s", url);
1042
+ }
1043
+ const response = await authenticatedFetch(url, {
1044
+ token,
1045
+ headers: { accept: "application/json" },
1046
+ signal: context?.signal
1047
+ });
1048
+ if (response.status === 204) {
1049
+ return { sboms: {} };
1050
+ }
1051
+ if (!response.ok) {
1052
+ throw new Error(
1053
+ `Security SBOM request failed (${response.status} ${response.statusText})`
1054
+ );
1055
+ }
1056
+ const data = await response.json();
1057
+ return data;
1058
+ }
1059
+ });
1060
+ var fetchCurrentUser = defineServerAction({
1061
+ id: "user/fetch-current",
1062
+ description: "Fetches the current user's profile information",
1063
+ visibility: "customer",
1064
+ tags: ["user", "profile"],
1065
+ async run({ token }, context) {
1066
+ if (!token || typeof token !== "string") {
1067
+ throw new Error("Fetch current user requires a session token");
1068
+ }
1069
+ const endpoint = `${getApiOrigin()}/v3/user`;
1070
+ if (process.env.NODE_ENV !== "production") {
1071
+ console.debug("[portal-components] fetching current user via %s", endpoint);
1072
+ }
1073
+ const response = await authenticatedFetch(endpoint, {
1074
+ method: "GET",
1075
+ token,
1076
+ headers: { accept: "application/json" },
1077
+ signal: context?.signal
1078
+ });
1079
+ if (!response.ok) {
1080
+ throw new Error(
1081
+ `Fetch current user request failed (${response.status} ${response.statusText})`
1082
+ );
1083
+ }
1084
+ const data = await response.json();
1085
+ return {
1086
+ user: {
1087
+ emailAddress: data.emailAddress || "",
1088
+ firstName: data.firstName || "",
1089
+ lastName: data.lastName || ""
1090
+ }
1091
+ };
1092
+ }
1093
+ });
1094
+ var updateUser = defineServerAction({
1095
+ id: "user/update",
1096
+ description: "Updates the current user's first and/or last name",
1097
+ visibility: "customer",
1098
+ tags: ["user", "profile"],
1099
+ async run({ token, firstName, lastName }, context) {
1100
+ if (!token || typeof token !== "string") {
1101
+ throw new Error("Update user requires a session token");
1102
+ }
1103
+ if (!firstName && !lastName) {
1104
+ throw new Error("At least one of firstName or lastName must be provided");
1105
+ }
1106
+ const endpoint = `${getApiOrigin()}/v3/user`;
1107
+ if (process.env.NODE_ENV !== "production") {
1108
+ console.debug("[portal-components] updating user via %s", endpoint);
1109
+ }
1110
+ const body = {};
1111
+ if (firstName !== void 0) body.firstName = firstName;
1112
+ if (lastName !== void 0) body.lastName = lastName;
1113
+ const response = await authenticatedFetch(endpoint, {
1114
+ method: "POST",
1115
+ token,
1116
+ headers: {
1117
+ "content-type": "application/json",
1118
+ accept: "application/json"
1119
+ },
1120
+ body: JSON.stringify(body),
1121
+ signal: context?.signal
1122
+ });
1123
+ if (!response.ok) {
1124
+ const errorText = await response.text().catch(() => "");
1125
+ throw new Error(
1126
+ `Update user request failed (${response.status} ${response.statusText}): ${errorText}`
1127
+ );
1128
+ }
1129
+ return { success: true };
1130
+ }
1131
+ });
1132
+ var fetchNotifications = defineServerAction({
1133
+ id: "notifications/fetch",
1134
+ description: "Fetches notification preferences for a specific team",
1135
+ visibility: "customer",
1136
+ tags: ["notifications", "user"],
1137
+ async run({ token, customerId }, context) {
1138
+ if (!token || typeof token !== "string") {
1139
+ throw new Error("Fetch notifications requires a session token");
1140
+ }
1141
+ if (!customerId || typeof customerId !== "string") {
1142
+ throw new Error("Fetch notifications requires a customerId");
1143
+ }
1144
+ const endpoint = `${getApiOrigin()}/v3/notifications?customer_id=${encodeURIComponent(customerId)}`;
1145
+ if (process.env.NODE_ENV !== "production") {
1146
+ console.debug("[portal-components] fetching notifications via %s", endpoint);
1147
+ }
1148
+ const response = await authenticatedFetch(endpoint, {
1149
+ method: "GET",
1150
+ token,
1151
+ headers: { accept: "application/json" },
1152
+ signal: context?.signal
1153
+ });
1154
+ if (!response.ok) {
1155
+ throw new Error(
1156
+ `Fetch notifications request failed (${response.status} ${response.statusText})`
1157
+ );
1158
+ }
1159
+ const data = await response.json();
1160
+ return {
1161
+ notifications: data.notifications || []
1162
+ };
1163
+ }
1164
+ });
1165
+ var updateNotifications = defineServerAction({
1166
+ id: "notifications/update",
1167
+ description: "Updates notification preferences for a specific team",
1168
+ visibility: "customer",
1169
+ tags: ["notifications", "user"],
1170
+ async run({ token, customerId, notifications }, context) {
1171
+ if (!token || typeof token !== "string") {
1172
+ throw new Error("Update notifications requires a session token");
1173
+ }
1174
+ if (!customerId || typeof customerId !== "string") {
1175
+ throw new Error("Update notifications requires a customerId");
1176
+ }
1177
+ if (!Array.isArray(notifications)) {
1178
+ throw new Error("Update notifications requires a notifications array");
1179
+ }
1180
+ const endpoint = `${getApiOrigin()}/v3/notifications?customer_id=${encodeURIComponent(customerId)}`;
1181
+ if (process.env.NODE_ENV !== "production") {
1182
+ console.debug("[portal-components] updating notifications via %s", endpoint);
1183
+ }
1184
+ const response = await authenticatedFetch(endpoint, {
1185
+ method: "PUT",
1186
+ token,
1187
+ headers: {
1188
+ "content-type": "application/json",
1189
+ accept: "application/json"
1190
+ },
1191
+ body: JSON.stringify({ notifications }),
1192
+ signal: context?.signal
1193
+ });
1194
+ if (!response.ok) {
1195
+ const errorText = await response.text().catch(() => "");
1196
+ throw new Error(
1197
+ `Update notifications request failed (${response.status} ${response.statusText}): ${errorText}`
1198
+ );
1199
+ }
1200
+ const data = await response.json();
1201
+ return {
1202
+ notifications: data.notifications || []
1203
+ };
1204
+ }
1205
+ });
1206
+ var ShieldIcon = (props) => /* @__PURE__ */ jsxRuntime.jsx(
1207
+ "svg",
1208
+ {
1209
+ xmlns: "http://www.w3.org/2000/svg",
1210
+ viewBox: "0 0 24 24",
1211
+ fill: "none",
1212
+ stroke: "currentColor",
1213
+ strokeWidth: 2,
1214
+ strokeLinecap: "round",
1215
+ strokeLinejoin: "round",
1216
+ "aria-hidden": "true",
1217
+ ...props,
1218
+ children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z" })
1219
+ }
1220
+ );
1221
+ var CalendarIcon = (props) => /* @__PURE__ */ jsxRuntime.jsxs(
1222
+ "svg",
1223
+ {
1224
+ xmlns: "http://www.w3.org/2000/svg",
1225
+ viewBox: "0 0 24 24",
1226
+ fill: "none",
1227
+ stroke: "currentColor",
1228
+ strokeWidth: 2,
1229
+ strokeLinecap: "round",
1230
+ strokeLinejoin: "round",
1231
+ "aria-hidden": "true",
1232
+ ...props,
1233
+ children: [
1234
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 2v4" }),
1235
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M16 2v4" }),
1236
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { width: 18, height: 18, x: 3, y: 4, rx: 2 }),
1237
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3 10h18" })
1238
+ ]
1239
+ }
1240
+ );
1241
+ var DownloadIcon = (props) => /* @__PURE__ */ jsxRuntime.jsxs(
1242
+ "svg",
1243
+ {
1244
+ xmlns: "http://www.w3.org/2000/svg",
1245
+ viewBox: "0 0 24 24",
1246
+ fill: "none",
1247
+ stroke: "currentColor",
1248
+ strokeWidth: 2,
1249
+ strokeLinecap: "round",
1250
+ strokeLinejoin: "round",
1251
+ "aria-hidden": "true",
1252
+ ...props,
1253
+ children: [
1254
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 17V3" }),
1255
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m6 11 6 6 6-6" }),
1256
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M19 21H5" })
1257
+ ]
1258
+ }
1259
+ );
1260
+ var BuildingIcon = (props) => /* @__PURE__ */ jsxRuntime.jsxs(
1261
+ "svg",
1262
+ {
1263
+ xmlns: "http://www.w3.org/2000/svg",
1264
+ viewBox: "0 0 24 24",
1265
+ fill: "none",
1266
+ stroke: "currentColor",
1267
+ strokeWidth: 2,
1268
+ strokeLinecap: "round",
1269
+ strokeLinejoin: "round",
1270
+ "aria-hidden": "true",
1271
+ ...props,
1272
+ children: [
1273
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { width: 16, height: 20, x: 4, y: 2, rx: 2, ry: 2 }),
1274
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M9 22v-4h6v4" }),
1275
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 6h.01" }),
1276
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M16 6h.01" }),
1277
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 6h.01" }),
1278
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 10h.01" }),
1279
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 14h.01" }),
1280
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M16 10h.01" }),
1281
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M16 14h.01" }),
1282
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 10h.01" }),
1283
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 14h.01" })
1284
+ ]
1285
+ }
1286
+ );
1287
+ var CheckIcon = (props) => /* @__PURE__ */ jsxRuntime.jsxs(
1288
+ "svg",
1289
+ {
1290
+ xmlns: "http://www.w3.org/2000/svg",
1291
+ viewBox: "0 0 24 24",
1292
+ fill: "none",
1293
+ stroke: "currentColor",
1294
+ strokeWidth: 2,
1295
+ strokeLinecap: "round",
1296
+ strokeLinejoin: "round",
1297
+ "aria-hidden": "true",
1298
+ ...props,
1299
+ children: [
1300
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21.8 10A10 10 0 1 1 17 3.33" }),
1301
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m9 11 3 3L22 4" })
1302
+ ]
1303
+ }
1304
+ );
1305
+ var formatExpiration = (value) => {
1306
+ if (!value || value === "never") {
1307
+ return "Does not expire";
1308
+ }
1309
+ const date = new Date(value);
1310
+ if (Number.isNaN(date.getTime())) {
1311
+ return value;
1312
+ }
1313
+ return date.toLocaleDateString(void 0, {
1314
+ year: "numeric",
1315
+ month: "short",
1316
+ day: "numeric"
1317
+ });
1318
+ };
1319
+ var maskValue = (value, isSecret) => {
1320
+ if (isSecret) {
1321
+ return "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022";
1322
+ }
1323
+ if (typeof value === "string" && value.trim().length > 0) {
1324
+ return value;
1325
+ }
1326
+ return "No value set";
1327
+ };
1328
+ var getFieldTextClass = (value) => {
1329
+ if (value === "does-not-expire") {
1330
+ return "text-gray-500";
1331
+ }
1332
+ return value && value.trim().length > 0 ? "text-gray-500" : "text-gray-300";
1333
+ };
1334
+ var InfoRow = ({
1335
+ icon,
1336
+ title,
1337
+ children
1338
+ }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start space-x-3", children: [
1339
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-1 text-gray-900", children: icon }),
1340
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1341
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-sm font-medium text-gray-900", children: title }),
1342
+ children
1343
+ ] })
1344
+ ] });
1345
+ var renderInstallOrChannel = (values) => {
1346
+ if (!values || values.length === 0) {
1347
+ return /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-sm text-gray-500", children: "Not specified" });
1348
+ }
1349
+ return /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-sm text-gray-500", children: values.join(", ") });
1350
+ };
1351
+ var buildPrimaryRows = (license) => {
1352
+ const rows = [];
1353
+ rows.push({
1354
+ key: "status",
1355
+ node: /* @__PURE__ */ jsxRuntime.jsxs(
1356
+ InfoRow,
201
1357
  {
202
- ref,
203
- className: [
204
- "w-full max-w-md rounded-3xl border border-white/15 bg-slate-950/80 p-10 shadow-[0_25px_70px_rgba(5,7,11,0.65)] backdrop-blur",
205
- "text-white transition-shadow",
206
- className
207
- ].filter(Boolean).join(" "),
208
- onSubmit: handleSubmit,
209
- ...props,
1358
+ icon: /* @__PURE__ */ jsxRuntime.jsx(ShieldIcon, { className: "h-5 w-5" }),
1359
+ title: "License Status",
210
1360
  children: [
211
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-4 text-center", children: [
212
- logo ?? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-16 w-16 items-center justify-center rounded-2xl border border-white/20 bg-white/10 text-lg font-semibold leading-tight", children: "EP" }),
213
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
214
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-base font-semibold uppercase tracking-[0.35em] text-white/65", children: "Enterprise Portal" }),
215
- /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "mt-3 text-3xl font-semibold tracking-tight", children: title }),
216
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-2 text-sm text-white/70", children: description })
217
- ] })
1361
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-1 flex items-center text-sm text-gray-600", children: [
1362
+ /* @__PURE__ */ jsxRuntime.jsx(CheckIcon, { className: "mr-1.5 h-4 w-4 text-green-500" }),
1363
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: license.statusLabel ?? (license.status ? license.status.charAt(0).toUpperCase() + license.status.slice(1) : "Unknown") })
218
1364
  ] }),
219
- mode === "email" ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-8 space-y-3", children: [
220
- /* @__PURE__ */ jsxRuntime.jsx(
221
- "label",
222
- {
223
- htmlFor: "login-email",
224
- className: "text-sm font-medium text-white/70",
225
- children: label
226
- }
227
- ),
228
- /* @__PURE__ */ jsxRuntime.jsx(
229
- "input",
230
- {
231
- id: "login-email",
232
- type: "email",
233
- inputMode: "email",
234
- autoComplete: "email",
235
- placeholder,
236
- value: email,
237
- onChange: (event) => setEmail(event.target.value),
238
- className: "w-full rounded-2xl border border-white/15 bg-white/5 px-5 py-4 text-base text-white placeholder:text-white/40 focus:border-white/40 focus:outline-none focus:ring-2 focus:ring-white/25",
239
- disabled,
240
- required: true
241
- }
242
- ),
243
- /* @__PURE__ */ jsxRuntime.jsx(
244
- Button,
245
- {
246
- type: "submit",
247
- size: "lg",
248
- className: "w-full justify-center",
249
- disabled,
250
- isLoading: disabled,
251
- children: ctaLabel
252
- }
253
- )
254
- ] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-8 space-y-4", children: [
255
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
256
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold uppercase tracking-[0.35em] text-white/65", children: "Verification Code" }),
257
- verificationMessage ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-2 text-sm text-white/70", children: verificationMessage }) : null,
258
- verificationError ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-2 text-sm font-medium text-rose-300", children: verificationError }) : null
1365
+ license.environment ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-sm text-gray-500", children: license.environment }) : null
1366
+ ]
1367
+ }
1368
+ )
1369
+ });
1370
+ rows.push({
1371
+ key: "expiration",
1372
+ node: /* @__PURE__ */ jsxRuntime.jsx(
1373
+ InfoRow,
1374
+ {
1375
+ icon: /* @__PURE__ */ jsxRuntime.jsx(CalendarIcon, { className: "h-5 w-5" }),
1376
+ title: "Expiration Date",
1377
+ children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-sm text-gray-500", children: formatExpiration(license.expiresAt) })
1378
+ }
1379
+ )
1380
+ });
1381
+ rows.push({
1382
+ key: "channels",
1383
+ node: /* @__PURE__ */ jsxRuntime.jsx(
1384
+ InfoRow,
1385
+ {
1386
+ icon: /* @__PURE__ */ jsxRuntime.jsx(ShieldIcon, { className: "h-5 w-5" }),
1387
+ title: "Release Channel(s)",
1388
+ children: renderInstallOrChannel(license.releaseChannels)
1389
+ }
1390
+ )
1391
+ });
1392
+ rows.push({
1393
+ key: "installs",
1394
+ node: /* @__PURE__ */ jsxRuntime.jsx(
1395
+ InfoRow,
1396
+ {
1397
+ icon: /* @__PURE__ */ jsxRuntime.jsx(DownloadIcon, { className: "h-5 w-5" }),
1398
+ title: "Install Options",
1399
+ children: renderInstallOrChannel(license.installMethods)
1400
+ }
1401
+ )
1402
+ });
1403
+ rows.push({
1404
+ key: "customer",
1405
+ node: /* @__PURE__ */ jsxRuntime.jsxs(
1406
+ InfoRow,
1407
+ {
1408
+ icon: /* @__PURE__ */ jsxRuntime.jsx(BuildingIcon, { className: "h-5 w-5" }),
1409
+ title: "Customer Name",
1410
+ children: [
1411
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-sm text-gray-500", children: license.customerName ?? "Unknown customer" }),
1412
+ license.customerId ? /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-gray-400", children: [
1413
+ "Customer ID: ",
1414
+ license.customerId
1415
+ ] }) : null
1416
+ ]
1417
+ }
1418
+ )
1419
+ });
1420
+ return rows;
1421
+ };
1422
+ var renderFields = (fields) => {
1423
+ if (!fields.length) {
1424
+ return /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-500", children: "No additional fields available." });
1425
+ }
1426
+ 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: [
1427
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-gray-900", children: field.label }),
1428
+ /* @__PURE__ */ jsxRuntime.jsx(
1429
+ "p",
1430
+ {
1431
+ className: `mt-1 break-words text-sm ${getFieldTextClass(
1432
+ field.value
1433
+ )}`,
1434
+ children: maskValue(field.value, field.isSecret)
1435
+ }
1436
+ )
1437
+ ] }, field.key)) });
1438
+ };
1439
+ var LicenseDetails = async ({
1440
+ token,
1441
+ title = "License Details",
1442
+ description = "View your enterprise license details"
1443
+ }) => {
1444
+ if (typeof token !== "string" || token.trim().length === 0) {
1445
+ throw new Error("LicenseDetails component requires a non-empty token");
1446
+ }
1447
+ const response = await fetchLicenseDetails.run({ token });
1448
+ console.debug("[portal-components] license response", response.raw);
1449
+ const { license } = response;
1450
+ const rows = buildPrimaryRows(license);
1451
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-3xl border border-gray-100 bg-white p-8 shadow-[0_18px_45px_rgba(17,24,39,0.08)]", children: [
1452
+ /* @__PURE__ */ jsxRuntime.jsxs("header", { className: "flex flex-col border-b border-gray-100 pb-6", children: [
1453
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-3xl font-bold text-gray-900", children: title }),
1454
+ description ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-2 text-sm text-gray-600", children: description }) : null
1455
+ ] }),
1456
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-6 space-y-6", children: [
1457
+ /* @__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)) }) }),
1458
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border border-gray-100 bg-white p-6 sm:p-8", children: [
1459
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "mb-4 text-lg font-medium text-gray-900", children: "Additional License Fields" }),
1460
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-x-auto", children: renderFields(license.fields ?? []) })
1461
+ ] })
1462
+ ] })
1463
+ ] });
1464
+ };
1465
+ var defaultHiddenLabels = ["Download"];
1466
+ var defaultTopNavLinks = [
1467
+ {
1468
+ label: "Dashboard",
1469
+ href: "/",
1470
+ icon: /* @__PURE__ */ jsxRuntime.jsxs(
1471
+ "svg",
1472
+ {
1473
+ xmlns: "http://www.w3.org/2000/svg",
1474
+ viewBox: "0 0 24 24",
1475
+ className: "h-4 w-4",
1476
+ fill: "none",
1477
+ stroke: "currentColor",
1478
+ strokeWidth: "1.5",
1479
+ children: [
1480
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M4 13h6V3H4z" }),
1481
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M14 21h6V3h-6z" })
1482
+ ]
1483
+ }
1484
+ )
1485
+ },
1486
+ {
1487
+ label: "Download",
1488
+ icon: /* @__PURE__ */ jsxRuntime.jsxs(
1489
+ "svg",
1490
+ {
1491
+ xmlns: "http://www.w3.org/2000/svg",
1492
+ viewBox: "0 0 24 24",
1493
+ className: "h-4 w-4",
1494
+ fill: "none",
1495
+ stroke: "currentColor",
1496
+ strokeWidth: "1.5",
1497
+ children: [
1498
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 3v12" }),
1499
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m7 11 5 5 5-5" }),
1500
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 21h14" })
1501
+ ]
1502
+ }
1503
+ )
1504
+ },
1505
+ {
1506
+ label: "Install",
1507
+ href: "/install",
1508
+ icon: /* @__PURE__ */ jsxRuntime.jsxs(
1509
+ "svg",
1510
+ {
1511
+ xmlns: "http://www.w3.org/2000/svg",
1512
+ viewBox: "0 0 24 24",
1513
+ className: "h-4 w-4",
1514
+ fill: "none",
1515
+ stroke: "currentColor",
1516
+ strokeWidth: "1.5",
1517
+ children: [
1518
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 5v14" }),
1519
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 12h14" })
1520
+ ]
1521
+ }
1522
+ )
1523
+ },
1524
+ {
1525
+ label: "Update",
1526
+ href: "/update",
1527
+ icon: /* @__PURE__ */ jsxRuntime.jsxs(
1528
+ "svg",
1529
+ {
1530
+ xmlns: "http://www.w3.org/2000/svg",
1531
+ viewBox: "0 0 24 24",
1532
+ className: "h-4 w-4",
1533
+ fill: "none",
1534
+ stroke: "currentColor",
1535
+ strokeWidth: "1.5",
1536
+ children: [
1537
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M4 4v6h6" }),
1538
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M20 20v-6h-6" }),
1539
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M4 10c1.5-4 6-6 10-4m6 4c-1.5 4-6 6-10 4" })
1540
+ ]
1541
+ }
1542
+ )
1543
+ },
1544
+ {
1545
+ label: "Release History",
1546
+ href: "/release-history",
1547
+ icon: /* @__PURE__ */ jsxRuntime.jsxs(
1548
+ "svg",
1549
+ {
1550
+ xmlns: "http://www.w3.org/2000/svg",
1551
+ viewBox: "0 0 24 24",
1552
+ className: "h-4 w-4",
1553
+ fill: "none",
1554
+ stroke: "currentColor",
1555
+ strokeWidth: "1.5",
1556
+ children: [
1557
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 8v4l3 2" }),
1558
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "9" })
1559
+ ]
1560
+ }
1561
+ )
1562
+ },
1563
+ {
1564
+ label: "License",
1565
+ href: "/license",
1566
+ icon: /* @__PURE__ */ jsxRuntime.jsxs(
1567
+ "svg",
1568
+ {
1569
+ xmlns: "http://www.w3.org/2000/svg",
1570
+ viewBox: "0 0 24 24",
1571
+ className: "h-4 w-4",
1572
+ fill: "none",
1573
+ stroke: "currentColor",
1574
+ strokeWidth: "1.5",
1575
+ children: [
1576
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { width: "14", height: "18", x: "5", y: "3", rx: "2" }),
1577
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M9 7h6" }),
1578
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M9 11h6" }),
1579
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M9 15h6" })
1580
+ ]
1581
+ }
1582
+ )
1583
+ },
1584
+ {
1585
+ label: "Support",
1586
+ href: "/support",
1587
+ icon: /* @__PURE__ */ jsxRuntime.jsxs(
1588
+ "svg",
1589
+ {
1590
+ xmlns: "http://www.w3.org/2000/svg",
1591
+ viewBox: "0 0 24 24",
1592
+ className: "h-4 w-4",
1593
+ fill: "none",
1594
+ stroke: "currentColor",
1595
+ strokeWidth: "1.5",
1596
+ children: [
1597
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 16a6 6 0 1 0-12 0v2h12Z" }),
1598
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "7", r: "4" })
1599
+ ]
1600
+ }
1601
+ )
1602
+ }
1603
+ ];
1604
+ var orderLinks = (links, order) => {
1605
+ if (!order || order.length === 0) {
1606
+ return links;
1607
+ }
1608
+ const orderMap = new Map(order.map((label, index) => [label, index]));
1609
+ return links.map((link, index) => ({
1610
+ link,
1611
+ originalIndex: index,
1612
+ orderIndex: orderMap.has(link.label) ? orderMap.get(link.label) : order.length + index
1613
+ })).sort((a, b) => {
1614
+ if (a.orderIndex === b.orderIndex) {
1615
+ return a.originalIndex - b.originalIndex;
1616
+ }
1617
+ return a.orderIndex - b.orderIndex;
1618
+ }).map((item) => item.link);
1619
+ };
1620
+ var TopNav = async ({
1621
+ links,
1622
+ hiddenLabels,
1623
+ additionalLinks,
1624
+ order,
1625
+ userMenuLabel,
1626
+ activeLabel,
1627
+ customerName,
1628
+ customers,
1629
+ currentCustomerId,
1630
+ onChangeTeam,
1631
+ userMenuChildren
1632
+ }) => {
1633
+ const displayLabel = userMenuLabel || (customerName ? `Team: ${customerName}` : "Team: Example");
1634
+ let logo;
1635
+ let brandTitle;
1636
+ let customColor1;
1637
+ let customColor2;
1638
+ const normalizeColor = (color) => {
1639
+ if (!color || typeof color !== "string") {
1640
+ return void 0;
1641
+ }
1642
+ const trimmed = color.trim();
1643
+ if (/^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$/.test(trimmed)) {
1644
+ return trimmed.startsWith("#") ? trimmed : `#${trimmed}`;
1645
+ }
1646
+ return trimmed;
1647
+ };
1648
+ try {
1649
+ const branding = await fetchCustomBranding();
1650
+ if (branding.brandingData) {
1651
+ const decoded = buffer.Buffer.from(branding.brandingData, "base64").toString(
1652
+ "utf-8"
1653
+ );
1654
+ try {
1655
+ const parsed = JSON.parse(decoded);
1656
+ if (parsed?.logo && typeof parsed.logo === "string") {
1657
+ logo = parsed.logo;
1658
+ }
1659
+ if (parsed?.title && typeof parsed.title === "string") {
1660
+ const normalizedTitle = parsed.title.trim();
1661
+ if (normalizedTitle) {
1662
+ brandTitle = normalizedTitle;
1663
+ }
1664
+ }
1665
+ if (parsed?.customColor1 && typeof parsed.customColor1 === "string") {
1666
+ customColor1 = normalizeColor(parsed.customColor1);
1667
+ }
1668
+ if (parsed?.customColor2 && typeof parsed.customColor2 === "string") {
1669
+ customColor2 = normalizeColor(parsed.customColor2);
1670
+ }
1671
+ } catch (error) {
1672
+ console.debug(
1673
+ "[portal-components] unable to parse branding JSON",
1674
+ error
1675
+ );
1676
+ }
1677
+ } else {
1678
+ console.debug("[portal-components] branding", branding);
1679
+ }
1680
+ } catch (error) {
1681
+ console.debug("[portal-components] branding fetch failed", error);
1682
+ }
1683
+ const baseLinks = links ?? defaultTopNavLinks;
1684
+ const computedHiddenLabels = hiddenLabels ? hiddenLabels : links ? void 0 : defaultHiddenLabels;
1685
+ const hiddenSet = computedHiddenLabels ? new Set(computedHiddenLabels) : null;
1686
+ let resolvedLinks = baseLinks.filter(
1687
+ (link) => !hiddenSet?.has(link.label)
1688
+ );
1689
+ if (additionalLinks?.length) {
1690
+ resolvedLinks = [...resolvedLinks, ...additionalLinks];
1691
+ }
1692
+ resolvedLinks = orderLinks(resolvedLinks, order);
1693
+ const gradientStart = customColor1 ?? "rgb(235, 102, 88)";
1694
+ const gradientEnd = customColor2 ?? customColor1 ?? "rgb(184, 83, 71)";
1695
+ return /* @__PURE__ */ jsxRuntime.jsx(
1696
+ "div",
1697
+ {
1698
+ className: "relative flex h-[165px] w-full items-start justify-center",
1699
+ style: {
1700
+ backgroundImage: `linear-gradient(to top, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0) 33%), linear-gradient(${gradientStart}, ${gradientEnd})`,
1701
+ backgroundRepeat: "no-repeat",
1702
+ backgroundSize: "100% 100%"
1703
+ },
1704
+ 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: [
1705
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
1706
+ logo || brandTitle ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
1707
+ logo ? (
1708
+ // eslint-disable-next-line @next/next/no-img-element
1709
+ /* @__PURE__ */ jsxRuntime.jsx(
1710
+ "img",
1711
+ {
1712
+ src: logo,
1713
+ alt: "Portal logo",
1714
+ className: "object-contain",
1715
+ style: {
1716
+ maxWidth: "240px",
1717
+ maxHeight: "48px"
1718
+ }
1719
+ }
1720
+ )
1721
+ ) : null,
1722
+ brandTitle ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-lg font-semibold text-gray-900", children: brandTitle }) : null
1723
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", {}),
1724
+ /* @__PURE__ */ jsxRuntime.jsxs("details", { className: "group relative", children: [
1725
+ /* @__PURE__ */ jsxRuntime.jsxs("summary", { className: "flex cursor-pointer items-center gap-2 rounded-lg bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 list-none", children: [
1726
+ /* @__PURE__ */ jsxRuntime.jsxs(
1727
+ "svg",
1728
+ {
1729
+ xmlns: "http://www.w3.org/2000/svg",
1730
+ width: "16",
1731
+ height: "16",
1732
+ viewBox: "0 0 24 24",
1733
+ fill: "none",
1734
+ stroke: "currentColor",
1735
+ strokeWidth: "2",
1736
+ strokeLinecap: "round",
1737
+ strokeLinejoin: "round",
1738
+ className: "text-gray-500",
1739
+ children: [
1740
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2" }),
1741
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "7", r: "4" })
1742
+ ]
1743
+ }
1744
+ ),
1745
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: displayLabel }),
1746
+ /* @__PURE__ */ jsxRuntime.jsx(
1747
+ "svg",
1748
+ {
1749
+ xmlns: "http://www.w3.org/2000/svg",
1750
+ width: "16",
1751
+ height: "16",
1752
+ viewBox: "0 0 24 24",
1753
+ fill: "none",
1754
+ stroke: "currentColor",
1755
+ strokeWidth: "2",
1756
+ strokeLinecap: "round",
1757
+ strokeLinejoin: "round",
1758
+ className: "text-gray-500",
1759
+ children: /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "6 9 12 15 18 9" })
1760
+ }
1761
+ )
259
1762
  ] }),
260
- /* @__PURE__ */ jsxRuntime.jsx(
261
- "label",
262
- {
263
- htmlFor: "login-code",
264
- className: "text-sm font-medium text-white/70",
265
- children: "One-time code"
266
- }
267
- ),
1763
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute right-0 mt-2 w-48 rounded-md border border-gray-200 bg-white py-2 text-sm text-gray-700 shadow-lg z-50", children: [
1764
+ /* @__PURE__ */ jsxRuntime.jsx(
1765
+ "a",
1766
+ {
1767
+ href: "/user-settings",
1768
+ className: "block w-full px-4 py-2 text-left hover:bg-gray-100",
1769
+ children: "User settings"
1770
+ }
1771
+ ),
1772
+ /* @__PURE__ */ jsxRuntime.jsx(
1773
+ "a",
1774
+ {
1775
+ href: "/team-settings",
1776
+ className: "block w-full px-4 py-2 text-left hover:bg-gray-100",
1777
+ children: "Team settings"
1778
+ }
1779
+ ),
1780
+ userMenuChildren,
1781
+ /* @__PURE__ */ jsxRuntime.jsx(
1782
+ "a",
1783
+ {
1784
+ href: "/?expired=1",
1785
+ className: "block w-full px-4 py-2 text-left hover:bg-gray-100",
1786
+ children: "Logout"
1787
+ }
1788
+ )
1789
+ ] })
1790
+ ] })
1791
+ ] }),
1792
+ /* @__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 }) => {
1793
+ const isActive = activeLabel === label;
1794
+ const className = `flex items-center gap-2 px-4 py-1 transition text-gray-500 ${isActive ? "underline underline-offset-8 decoration-2" : ""}`;
1795
+ if (href) {
1796
+ return /* @__PURE__ */ jsxRuntime.jsxs("a", { href, className, children: [
1797
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: isActive ? "text-rose-600" : "text-gray-500", children: icon }),
1798
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: label })
1799
+ ] }, label);
1800
+ }
1801
+ return /* @__PURE__ */ jsxRuntime.jsxs("button", { className, type: "button", children: [
1802
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: isActive ? "text-rose-600" : "text-gray-500", children: icon }),
1803
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: label })
1804
+ ] }, label);
1805
+ }) })
1806
+ ] }) })
1807
+ }
1808
+ );
1809
+ };
1810
+ var GlobeIcon = (props) => /* @__PURE__ */ jsxRuntime.jsxs(
1811
+ "svg",
1812
+ {
1813
+ xmlns: "http://www.w3.org/2000/svg",
1814
+ viewBox: "0 0 24 24",
1815
+ fill: "none",
1816
+ stroke: "currentColor",
1817
+ strokeWidth: 1.5,
1818
+ "aria-hidden": "true",
1819
+ ...props,
1820
+ children: [
1821
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: 12, cy: 12, r: 10 }),
1822
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M2 12h20" }),
1823
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10Z" })
1824
+ ]
1825
+ }
1826
+ );
1827
+ var AirGapIcon = (props) => /* @__PURE__ */ jsxRuntime.jsxs(
1828
+ "svg",
1829
+ {
1830
+ xmlns: "http://www.w3.org/2000/svg",
1831
+ viewBox: "0 0 24 24",
1832
+ fill: "none",
1833
+ stroke: "currentColor",
1834
+ strokeWidth: 1.5,
1835
+ "aria-hidden": "true",
1836
+ ...props,
1837
+ children: [
1838
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m4 4 16 16" }),
1839
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M7.5 4.5a8 8 0 0 1 9 0" }),
1840
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M4.5 7.5a8 8 0 0 0 0 9" }),
1841
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M16.5 19.5a8 8 0 0 0 0-9" }),
1842
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M7 12a5 5 0 0 0 5 5" })
1843
+ ]
1844
+ }
1845
+ );
1846
+ var baseCardClass = "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)]";
1847
+ var headingClass = "text-lg font-semibold text-gray-900";
1848
+ var contentClass = "mt-4 flex-1 space-y-3";
1849
+ var itemClass = "flex items-center gap-3 text-sm text-gray-600";
1850
+ var iconClass = "h-5 w-5 text-gray-500";
1851
+ var footerClass = "mt-6 flex justify-end text-sm font-semibold text-primary hover:text-primary/80";
1852
+ 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";
1853
+ var Badge = ({ count }) => /* @__PURE__ */ jsxRuntime.jsx("span", { className: badgeClass, "aria-label": `${count} updates available`, children: count });
1854
+ var UpdatesCard = ({
1855
+ onlineActiveCount = 0,
1856
+ airgapCount = 0,
1857
+ onlineUpdates = 0,
1858
+ airgapUpdates = 0
1859
+ }) => /* @__PURE__ */ jsxRuntime.jsxs("section", { className: baseCardClass, "aria-labelledby": "updates-card-heading", children: [
1860
+ /* @__PURE__ */ jsxRuntime.jsx("header", { children: /* @__PURE__ */ jsxRuntime.jsx("h2", { id: "updates-card-heading", className: headingClass, children: "Updates" }) }),
1861
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: contentClass, children: [
1862
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: itemClass, children: [
1863
+ /* @__PURE__ */ jsxRuntime.jsx(GlobeIcon, { className: iconClass }),
1864
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
1865
+ onlineActiveCount,
1866
+ " Active Online ",
1867
+ onlineActiveCount === 1 ? "instance" : "instances"
1868
+ ] }),
1869
+ onlineUpdates > 0 && /* @__PURE__ */ jsxRuntime.jsx(Badge, { count: onlineUpdates })
1870
+ ] }),
1871
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: itemClass, children: [
1872
+ /* @__PURE__ */ jsxRuntime.jsx(AirGapIcon, { className: iconClass }),
1873
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
1874
+ airgapCount,
1875
+ " Air gap ",
1876
+ airgapCount === 1 ? "instance" : "instances"
1877
+ ] }),
1878
+ airgapUpdates > 0 && /* @__PURE__ */ jsxRuntime.jsx(Badge, { count: airgapUpdates })
1879
+ ] })
1880
+ ] }),
1881
+ /* @__PURE__ */ jsxRuntime.jsx("footer", { className: footerClass, children: /* @__PURE__ */ jsxRuntime.jsx("a", { href: "/update", children: "View updates \u2192" }) })
1882
+ ] });
1883
+ UpdatesCard.displayName = "UpdatesCard";
1884
+ var UploadIcon = (props) => /* @__PURE__ */ jsxRuntime.jsxs(
1885
+ "svg",
1886
+ {
1887
+ xmlns: "http://www.w3.org/2000/svg",
1888
+ viewBox: "0 0 24 24",
1889
+ fill: "none",
1890
+ stroke: "currentColor",
1891
+ strokeWidth: 1.5,
1892
+ "aria-hidden": "true",
1893
+ ...props,
1894
+ children: [
1895
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 16V3" }),
1896
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 7l4-4 4 4" }),
1897
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 21h14" })
1898
+ ]
1899
+ }
1900
+ );
1901
+ var baseCardClass2 = "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)]";
1902
+ var headingClass2 = "text-lg font-semibold text-gray-900";
1903
+ var contentClass2 = "mt-4 flex-1 space-y-3";
1904
+ var itemClass2 = "flex items-center gap-3 text-sm text-gray-600";
1905
+ var iconClass2 = "h-5 w-5 text-gray-500";
1906
+ var footerClass2 = "mt-6 flex justify-end text-sm font-semibold text-primary hover:text-primary/80";
1907
+ var SupportCard = async () => {
1908
+ let totalCount = null;
1909
+ try {
1910
+ const headersModule = await import('next/headers');
1911
+ const store = await headersModule.cookies();
1912
+ const session = store.get("portal_session");
1913
+ const token = session?.value;
1914
+ if (token) {
1915
+ const result = await listSupportBundles.run({ token });
1916
+ totalCount = result.totalCount;
1917
+ } else {
1918
+ console.debug("[portal-components] no portal_session token for support bundles");
1919
+ }
1920
+ } catch (error) {
1921
+ if (isRedirectError(error)) {
1922
+ throw error;
1923
+ }
1924
+ console.error("[portal-components] support bundles fetch failed", error);
1925
+ }
1926
+ return /* @__PURE__ */ jsxRuntime.jsxs("section", { className: baseCardClass2, "aria-labelledby": "support-card-heading", children: [
1927
+ /* @__PURE__ */ jsxRuntime.jsx("header", { children: /* @__PURE__ */ jsxRuntime.jsx("h2", { id: "support-card-heading", className: headingClass2, children: "Support" }) }),
1928
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: contentClass2, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: itemClass2, children: [
1929
+ /* @__PURE__ */ jsxRuntime.jsx(UploadIcon, { className: iconClass2 }),
1930
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
1931
+ "Support bundles uploaded (",
1932
+ totalCount ?? 0,
1933
+ ")"
1934
+ ] })
1935
+ ] }) }),
1936
+ /* @__PURE__ */ jsxRuntime.jsx("footer", { className: footerClass2, children: /* @__PURE__ */ jsxRuntime.jsx("a", { href: "/support", className: "inline-flex items-center gap-1", children: "View support guide \u2192" }) })
1937
+ ] });
1938
+ };
1939
+ SupportCard.displayName = "SupportCard";
1940
+ var NOTIFICATION_DESCRIPTIONS = {
1941
+ "new-version": "New version available"
1942
+ };
1943
+ var EmailNotificationsSection = ({
1944
+ teamName,
1945
+ notifications,
1946
+ isLoading,
1947
+ onToggle
1948
+ }) => {
1949
+ const allNotificationTypes = Object.keys(NOTIFICATION_DESCRIPTIONS);
1950
+ const mergedNotifications = allNotificationTypes.map((type) => {
1951
+ const existing = notifications.find((n) => n.type === type);
1952
+ return {
1953
+ type,
1954
+ enabled: existing ? existing.enabled : type === "new-version"
1955
+ // Default new-version to true
1956
+ };
1957
+ });
1958
+ if (isLoading) {
1959
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-2xl border border-gray-200 bg-white p-6 shadow-sm", children: [
1960
+ /* @__PURE__ */ jsxRuntime.jsxs("h2", { className: "text-lg font-semibold text-gray-900", children: [
1961
+ "Email Notifications for ",
1962
+ teamName
1963
+ ] }),
1964
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-sm text-gray-500", children: "Choose which email notifications you want to receive for this team." }),
1965
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-6 flex justify-center py-8", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-500", children: "Loading notifications..." }) })
1966
+ ] });
1967
+ }
1968
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-2xl border border-gray-200 bg-white p-6 shadow-sm", children: [
1969
+ /* @__PURE__ */ jsxRuntime.jsxs("h2", { className: "text-lg font-semibold text-gray-900", children: [
1970
+ "Email Notifications for ",
1971
+ teamName
1972
+ ] }),
1973
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-sm text-gray-500", children: "Choose which email notifications you want to receive for this team." }),
1974
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-6 overflow-hidden rounded-lg border border-gray-200", children: [
1975
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between bg-gray-50 px-4 py-2", children: [
1976
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium uppercase text-gray-600", children: "Description" }),
1977
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium uppercase text-gray-600", children: "Enabled" })
1978
+ ] }),
1979
+ mergedNotifications.map((notification) => /* @__PURE__ */ jsxRuntime.jsxs(
1980
+ "div",
1981
+ {
1982
+ className: "flex items-center justify-between border-t border-gray-200 px-4 py-3",
1983
+ children: [
1984
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-gray-900", children: NOTIFICATION_DESCRIPTIONS[notification.type] }),
268
1985
  /* @__PURE__ */ jsxRuntime.jsx(
269
1986
  "input",
270
1987
  {
271
- id: "login-code",
272
- inputMode: "numeric",
273
- maxLength: 12,
274
- placeholder: "Enter 12 digit code",
275
- className: "w-full rounded-2xl border border-white/15 bg-white/5 px-5 py-4 text-base text-white placeholder:text-white/40 focus:border-white/40 focus:outline-none focus:ring-2 focus:ring-white/25",
276
- value: verificationCode,
277
- onChange: (event) => setVerificationCode(event.target.value)
278
- }
279
- ),
280
- /* @__PURE__ */ jsxRuntime.jsx(
281
- Button,
282
- {
283
- type: "button",
284
- size: "lg",
285
- className: "w-full justify-center",
286
- disabled: verificationCode.length !== 12 || verifying,
287
- isLoading: verifying,
288
- onClick: handleVerify,
289
- children: "Verify code"
1988
+ type: "checkbox",
1989
+ checked: notification.enabled,
1990
+ onChange: () => onToggle?.(notification.type, !notification.enabled),
1991
+ className: "portal-checkbox"
290
1992
  }
291
1993
  )
292
- ] })
293
- ]
1994
+ ]
1995
+ },
1996
+ notification.type
1997
+ ))
1998
+ ] }),
1999
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "mt-3 text-xs text-gray-500", children: [
2000
+ "These notification preferences are specific to the ",
2001
+ teamName,
2002
+ " team. Switch teams to manage notifications for other teams."
2003
+ ] })
2004
+ ] });
2005
+ };
2006
+ var TeamsSection = ({
2007
+ teams,
2008
+ currentCustomer,
2009
+ isLoading,
2010
+ onTeamSwitch,
2011
+ primaryColor
2012
+ }) => {
2013
+ const groupedTeams = react.useMemo(() => {
2014
+ const groups = /* @__PURE__ */ new Map();
2015
+ for (const team of teams) {
2016
+ const existing = groups.get(team.appId);
2017
+ if (existing) {
2018
+ existing.teams.push(team);
2019
+ } else {
2020
+ groups.set(team.appId, {
2021
+ appName: team.appName,
2022
+ teams: [team]
2023
+ });
294
2024
  }
295
- );
2025
+ }
2026
+ return groups;
2027
+ }, [teams]);
2028
+ if (isLoading) {
2029
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-2xl border border-gray-200 bg-white p-6 shadow-sm", children: [
2030
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-semibold text-gray-900", children: "Teams" }),
2031
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-sm text-gray-500", children: "Teams associate your email address with any additional licenses your account has access to." }),
2032
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-6", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-500", children: "Loading teams..." }) })
2033
+ ] });
2034
+ }
2035
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-2xl border border-gray-200 bg-white p-6 shadow-sm", children: [
2036
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-semibold text-gray-900", children: "Teams" }),
2037
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-sm text-gray-500", children: "Teams associate your email address with any additional licenses your account has access to." }),
2038
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-6 space-y-6", children: Array.from(groupedTeams.values()).map((appGroup) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
2039
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-base font-medium text-gray-900", children: appGroup.appName }),
2040
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2", children: appGroup.teams.map((team) => {
2041
+ const isCurrentTeam = currentCustomer?.id === team.id;
2042
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2043
+ "button",
2044
+ {
2045
+ type: "button",
2046
+ onClick: () => !isCurrentTeam && onTeamSwitch?.(team),
2047
+ disabled: isCurrentTeam,
2048
+ 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"}`,
2049
+ style: !isCurrentTeam && primaryColor ? { "--hover-border": primaryColor } : void 0,
2050
+ children: [
2051
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
2052
+ /* @__PURE__ */ jsxRuntime.jsx(
2053
+ "svg",
2054
+ {
2055
+ className: "h-4 w-4",
2056
+ style: { color: primaryColor || "#6366f1" },
2057
+ fill: "none",
2058
+ viewBox: "0 0 24 24",
2059
+ stroke: "currentColor",
2060
+ strokeWidth: 2,
2061
+ children: /* @__PURE__ */ jsxRuntime.jsx(
2062
+ "path",
2063
+ {
2064
+ strokeLinecap: "round",
2065
+ strokeLinejoin: "round",
2066
+ d: "M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"
2067
+ }
2068
+ )
2069
+ }
2070
+ ),
2071
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2072
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-gray-900", children: team.name }),
2073
+ isCurrentTeam && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-gray-500", children: "Current Team" })
2074
+ ] })
2075
+ ] }),
2076
+ !isCurrentTeam && /* @__PURE__ */ jsxRuntime.jsx(
2077
+ "svg",
2078
+ {
2079
+ className: "h-4 w-4",
2080
+ style: { color: primaryColor || "#6366f1" },
2081
+ fill: "none",
2082
+ viewBox: "0 0 24 24",
2083
+ stroke: "currentColor",
2084
+ strokeWidth: 2,
2085
+ children: /* @__PURE__ */ jsxRuntime.jsx(
2086
+ "path",
2087
+ {
2088
+ strokeLinecap: "round",
2089
+ strokeLinejoin: "round",
2090
+ d: "M9 5l7 7-7 7"
2091
+ }
2092
+ )
2093
+ }
2094
+ )
2095
+ ]
2096
+ },
2097
+ team.id
2098
+ );
2099
+ }) })
2100
+ ] }, appGroup.appName)) })
2101
+ ] });
2102
+ };
2103
+ var ProfileSection = ({
2104
+ user,
2105
+ isLoading,
2106
+ isUpdating,
2107
+ error,
2108
+ updateError,
2109
+ onSave,
2110
+ primaryColor
2111
+ }) => {
2112
+ const [isEditing, setIsEditing] = react.useState(false);
2113
+ const [firstName, setFirstName] = react.useState(user?.firstName || "");
2114
+ const [lastName, setLastName] = react.useState(user?.lastName || "");
2115
+ const [inputError, setInputError] = react.useState(null);
2116
+ const resetForm = () => {
2117
+ setFirstName(user?.firstName || "");
2118
+ setLastName(user?.lastName || "");
2119
+ setInputError(null);
2120
+ };
2121
+ const handleSave = async () => {
2122
+ if (!firstName && !lastName) {
2123
+ setInputError("Please enter a first name or last name");
2124
+ return;
2125
+ }
2126
+ if (firstName === user?.firstName && lastName === user?.lastName) {
2127
+ setIsEditing(false);
2128
+ return;
2129
+ }
2130
+ try {
2131
+ await onSave?.({ firstName, lastName });
2132
+ setIsEditing(false);
2133
+ } catch (err) {
2134
+ console.error("Error updating user information:", err);
2135
+ }
2136
+ };
2137
+ const handleCancel = () => {
2138
+ setIsEditing(false);
2139
+ resetForm();
2140
+ };
2141
+ const handleEdit = () => {
2142
+ resetForm();
2143
+ setIsEditing(true);
2144
+ };
2145
+ const buttonStyle = primaryColor ? { backgroundColor: primaryColor, borderColor: primaryColor } : void 0;
2146
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-2xl border border-gray-200 bg-white p-6 shadow-sm", children: [
2147
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-4", children: [
2148
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2149
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-semibold text-gray-900", children: "Profile Information" }),
2150
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-sm text-gray-500", children: "Your account information." }),
2151
+ (error || updateError) && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-2 text-sm text-red-600", children: error?.message || updateError?.message || "Failed to update profile" }),
2152
+ inputError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-2 text-sm text-red-600", children: inputError })
2153
+ ] }),
2154
+ isEditing ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
2155
+ /* @__PURE__ */ jsxRuntime.jsx(
2156
+ "button",
2157
+ {
2158
+ type: "button",
2159
+ onClick: handleCancel,
2160
+ disabled: isUpdating,
2161
+ className: "inline-flex items-center rounded-md border border-gray-300 bg-white px-3 py-1.5 text-sm font-medium text-gray-700 shadow-sm transition hover:bg-gray-50 disabled:opacity-50",
2162
+ children: "Cancel"
2163
+ }
2164
+ ),
2165
+ /* @__PURE__ */ jsxRuntime.jsx(
2166
+ "button",
2167
+ {
2168
+ type: "button",
2169
+ onClick: handleSave,
2170
+ disabled: isUpdating,
2171
+ style: buttonStyle,
2172
+ 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",
2173
+ children: isUpdating ? "Saving..." : "Save"
2174
+ }
2175
+ )
2176
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(
2177
+ "button",
2178
+ {
2179
+ type: "button",
2180
+ onClick: handleEdit,
2181
+ style: buttonStyle,
2182
+ 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",
2183
+ children: "Edit"
2184
+ }
2185
+ )
2186
+ ] }),
2187
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-6 flex gap-8", children: [
2188
+ isEditing ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-4", children: [
2189
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2190
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "block text-sm font-medium text-gray-600", children: "First Name" }),
2191
+ /* @__PURE__ */ jsxRuntime.jsx(
2192
+ "input",
2193
+ {
2194
+ type: "text",
2195
+ value: firstName,
2196
+ onChange: (e) => {
2197
+ setInputError(null);
2198
+ setFirstName(e.target.value);
2199
+ },
2200
+ disabled: isUpdating,
2201
+ placeholder: "First Name",
2202
+ className: "portal-input mt-1 block w-48"
2203
+ }
2204
+ )
2205
+ ] }),
2206
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2207
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "block text-sm font-medium text-gray-600", children: "Last Name" }),
2208
+ /* @__PURE__ */ jsxRuntime.jsx(
2209
+ "input",
2210
+ {
2211
+ type: "text",
2212
+ value: lastName,
2213
+ onChange: (e) => {
2214
+ setInputError(null);
2215
+ setLastName(e.target.value);
2216
+ },
2217
+ disabled: isUpdating,
2218
+ placeholder: "Last Name",
2219
+ className: "portal-input mt-1 block w-48"
2220
+ }
2221
+ )
2222
+ ] })
2223
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1", children: [
2224
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "block text-sm font-medium text-gray-600", children: "Full Name (optional)" }),
2225
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-sm text-gray-900", children: user?.firstName || user?.lastName ? `${user.firstName || ""} ${user.lastName || ""}`.trim() : "\u2014" })
2226
+ ] }),
2227
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1", children: [
2228
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "block text-sm font-medium text-gray-600", children: "Email" }),
2229
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-sm text-gray-500", children: isLoading ? "Loading..." : user?.emailAddress || "" })
2230
+ ] })
2231
+ ] })
2232
+ ] });
2233
+ };
2234
+ var UserSettings = ({
2235
+ user,
2236
+ isUserLoading = false,
2237
+ userError,
2238
+ isUpdating = false,
2239
+ updateError,
2240
+ onUpdateUser,
2241
+ teams = [],
2242
+ currentCustomer,
2243
+ isTeamsLoading = false,
2244
+ onTeamSwitch,
2245
+ notifications = [],
2246
+ isNotificationsLoading = false,
2247
+ onToggleNotification,
2248
+ primaryColor
2249
+ }) => {
2250
+ if (userError) {
2251
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-8", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-red-600", children: [
2252
+ "Failed to load user settings. ",
2253
+ userError?.message,
2254
+ " Please try again later."
2255
+ ] }) });
2256
+ }
2257
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full space-y-6", children: [
2258
+ /* @__PURE__ */ jsxRuntime.jsx("header", { children: /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-2xl font-semibold text-gray-900", children: "User Settings" }) }),
2259
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-6", children: [
2260
+ /* @__PURE__ */ jsxRuntime.jsx(
2261
+ ProfileSection,
2262
+ {
2263
+ user,
2264
+ isLoading: isUserLoading,
2265
+ isUpdating,
2266
+ error: userError,
2267
+ updateError,
2268
+ onSave: onUpdateUser,
2269
+ primaryColor
2270
+ }
2271
+ ),
2272
+ /* @__PURE__ */ jsxRuntime.jsx(
2273
+ TeamsSection,
2274
+ {
2275
+ teams,
2276
+ currentCustomer,
2277
+ isLoading: isTeamsLoading,
2278
+ onTeamSwitch,
2279
+ primaryColor
2280
+ }
2281
+ ),
2282
+ currentCustomer && /* @__PURE__ */ jsxRuntime.jsx(
2283
+ EmailNotificationsSection,
2284
+ {
2285
+ teamName: currentCustomer.name,
2286
+ notifications,
2287
+ isLoading: isNotificationsLoading,
2288
+ onToggle: onToggleNotification
2289
+ }
2290
+ )
2291
+ ] })
2292
+ ] });
2293
+ };
2294
+ UserSettings.displayName = "UserSettings";
2295
+ 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)]";
2296
+ var headingClass3 = "text-lg font-semibold text-gray-900";
2297
+ var bodySpacerClass = "mt-4 flex-1";
2298
+ var footerClass3 = "mt-6 flex justify-end text-sm font-semibold text-primary hover:text-primary/80";
2299
+ var UserSettingsCard = () => /* @__PURE__ */ jsxRuntime.jsxs("section", { className: baseCardClass3, "aria-labelledby": "user-settings-card-heading", children: [
2300
+ /* @__PURE__ */ jsxRuntime.jsx("header", { children: /* @__PURE__ */ jsxRuntime.jsx("h2", { id: "user-settings-card-heading", className: headingClass3, children: "User Settings" }) }),
2301
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: bodySpacerClass }),
2302
+ /* @__PURE__ */ jsxRuntime.jsx("footer", { className: footerClass3, children: /* @__PURE__ */ jsxRuntime.jsx("a", { href: "/user-settings", children: "View user settings \u2192" }) })
2303
+ ] });
2304
+ UserSettingsCard.displayName = "UserSettingsCard";
2305
+ var UsersIcon = (props) => /* @__PURE__ */ jsxRuntime.jsxs(
2306
+ "svg",
2307
+ {
2308
+ xmlns: "http://www.w3.org/2000/svg",
2309
+ viewBox: "0 0 24 24",
2310
+ fill: "none",
2311
+ stroke: "currentColor",
2312
+ strokeWidth: 1.5,
2313
+ "aria-hidden": "true",
2314
+ ...props,
2315
+ children: [
2316
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M16 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" }),
2317
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: 10, cy: 8, r: 4 }),
2318
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M20 21v-2a4 4 0 0 0-3-3.87" }),
2319
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M17 3a4 4 0 0 1 0 8" })
2320
+ ]
296
2321
  }
297
2322
  );
298
- Login.displayName = "Login";
299
-
300
- // src/actions/index.ts
301
- var defineServerAction = (definition) => definition;
2323
+ var KeyIcon = (props) => /* @__PURE__ */ jsxRuntime.jsxs(
2324
+ "svg",
2325
+ {
2326
+ xmlns: "http://www.w3.org/2000/svg",
2327
+ viewBox: "0 0 24 24",
2328
+ fill: "none",
2329
+ stroke: "currentColor",
2330
+ strokeWidth: 1.5,
2331
+ "aria-hidden": "true",
2332
+ ...props,
2333
+ children: [
2334
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: 7, cy: 15, r: 4 }),
2335
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m10.85 12.15 4.3-4.3a2 2 0 0 1 2.83 0l.47.47a2 2 0 0 1 0 2.83l-4.3 4.3" }),
2336
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M16 11l1.5 1.5" })
2337
+ ]
2338
+ }
2339
+ );
2340
+ var baseCardClass4 = "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)]";
2341
+ var headingClass4 = "text-lg font-semibold text-gray-900";
2342
+ var contentClass3 = "mt-4 flex-1 space-y-3";
2343
+ var itemClass3 = "flex items-center gap-3 text-sm text-gray-600";
2344
+ var iconClass3 = "h-5 w-5 text-gray-500";
2345
+ var footerClass4 = "mt-6 flex justify-end text-sm font-semibold text-primary hover:text-primary/80";
2346
+ var TeamSettingsCard = ({
2347
+ userCount = 0,
2348
+ serviceAccountCount = 0
2349
+ }) => /* @__PURE__ */ jsxRuntime.jsxs("section", { className: baseCardClass4, "aria-labelledby": "team-settings-card-heading", children: [
2350
+ /* @__PURE__ */ jsxRuntime.jsx("header", { children: /* @__PURE__ */ jsxRuntime.jsx("h2", { id: "team-settings-card-heading", className: headingClass4, children: "Team Settings" }) }),
2351
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: contentClass3, children: [
2352
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: itemClass3, children: [
2353
+ /* @__PURE__ */ jsxRuntime.jsx(UsersIcon, { className: iconClass3 }),
2354
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
2355
+ userCount,
2356
+ " Team ",
2357
+ userCount === 1 ? "Member" : "Members"
2358
+ ] })
2359
+ ] }),
2360
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: itemClass3, children: [
2361
+ /* @__PURE__ */ jsxRuntime.jsx(KeyIcon, { className: iconClass3 }),
2362
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
2363
+ serviceAccountCount,
2364
+ " Service ",
2365
+ serviceAccountCount === 1 ? "Account" : "Accounts"
2366
+ ] })
2367
+ ] })
2368
+ ] }),
2369
+ /* @__PURE__ */ jsxRuntime.jsx("footer", { className: footerClass4, children: /* @__PURE__ */ jsxRuntime.jsx("a", { href: "/team-settings", children: "View team settings \u2192" }) })
2370
+ ] });
2371
+ TeamSettingsCard.displayName = "TeamSettingsCard";
2372
+ var UpdateLayout = ({ children }) => {
2373
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-h-screen bg-white", children: [
2374
+ /* @__PURE__ */ jsxRuntime.jsx(TopNav, { activeLabel: "Updates" }),
2375
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-8", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mx-auto w-full max-w-[1248px]", children }) })
2376
+ ] });
2377
+ };
2378
+ UpdateLayout.displayName = "UpdateLayout";
302
2379
 
303
2380
  // src/index.ts
304
2381
  var portalComponentsVersion = package_default.version;
305
2382
 
306
2383
  exports.Button = Button;
307
- exports.Login = Login;
2384
+ exports.LicenseDetails = LicenseDetails;
2385
+ exports.SupportCard = SupportCard;
2386
+ exports.TeamSettingsCard = TeamSettingsCard;
2387
+ exports.TopNav = TopNav;
2388
+ exports.UpdateLayout = UpdateLayout;
2389
+ exports.UpdatesCard = UpdatesCard;
2390
+ exports.UserSettings = UserSettings;
2391
+ exports.UserSettingsCard = UserSettingsCard;
308
2392
  exports.createPortalTheme = createPortalTheme;
2393
+ exports.decodeJwtPayload = decodeJwtPayload;
2394
+ exports.defaultTopNavLinks = defaultTopNavLinks;
309
2395
  exports.defineServerAction = defineServerAction;
2396
+ exports.deleteSupportBundle = deleteSupportBundle;
2397
+ exports.downloadSupportBundle = downloadSupportBundle;
2398
+ exports.fetchCurrentUser = fetchCurrentUser;
2399
+ exports.fetchCustomBranding = fetchCustomBranding;
2400
+ exports.fetchLicenseDetails = fetchLicenseDetails;
2401
+ exports.fetchNotifications = fetchNotifications;
2402
+ exports.getCustomerIdFromToken = getCustomerIdFromToken;
2403
+ exports.getSecurityInfo = getSecurityInfo;
2404
+ exports.getSecurityInfoDiff = getSecurityInfoDiff;
2405
+ exports.getSecurityInfoSBOM = getSecurityInfoSBOM;
2406
+ exports.getSupportBundleUploadUrl = getSupportBundleUploadUrl;
2407
+ exports.initiateLogin = initiateLogin;
2408
+ exports.listReleases = listReleases;
2409
+ exports.listSupportBundles = listSupportBundles;
310
2410
  exports.portalComponentsVersion = portalComponentsVersion;
311
2411
  exports.portalThemeTokens = portalThemeTokens;
2412
+ exports.updateNotifications = updateNotifications;
2413
+ exports.updateUser = updateUser;
2414
+ exports.uploadSupportBundle = uploadSupportBundle;
2415
+ exports.verifyMagicLink = verifyMagicLink;
312
2416
  //# sourceMappingURL=index.js.map
313
2417
  //# sourceMappingURL=index.js.map