@replicated/portal-components 0.0.19 → 0.0.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (244) hide show
  1. package/components/metadata/registry.json +2 -2
  2. package/components/metadata/registry.md +2 -2
  3. package/dist/actions/change-team.js +66 -7
  4. package/dist/actions/change-team.js.map +1 -1
  5. package/dist/actions/index.d.mts +3 -1
  6. package/dist/actions/index.d.ts +3 -1
  7. package/dist/actions/index.js +182 -465
  8. package/dist/actions/index.js.map +1 -1
  9. package/dist/actions/install-actions.d.mts +3 -1
  10. package/dist/actions/install-actions.d.ts +3 -1
  11. package/dist/actions/install-actions.js +58 -5
  12. package/dist/actions/install-actions.js.map +1 -1
  13. package/dist/actions/service-account.d.mts +3 -1
  14. package/dist/actions/service-account.d.ts +3 -1
  15. package/dist/actions/service-account.js +58 -5
  16. package/dist/actions/service-account.js.map +1 -1
  17. package/dist/actions/support-bundles.d.mts +3 -1
  18. package/dist/actions/support-bundles.d.ts +3 -1
  19. package/dist/actions/support-bundles.js +58 -5
  20. package/dist/actions/support-bundles.js.map +1 -1
  21. package/dist/actions/team-settings.d.mts +3 -1
  22. package/dist/actions/team-settings.d.ts +3 -1
  23. package/dist/actions/team-settings.js +91 -27
  24. package/dist/actions/team-settings.js.map +1 -1
  25. package/dist/actions/trial-signup.d.mts +24 -0
  26. package/dist/actions/trial-signup.d.ts +24 -0
  27. package/dist/actions/trial-signup.js +482 -0
  28. package/dist/actions/trial-signup.js.map +1 -0
  29. package/dist/actions/user-settings.d.mts +3 -1
  30. package/dist/actions/user-settings.d.ts +3 -1
  31. package/dist/actions/user-settings.js +58 -5
  32. package/dist/actions/user-settings.js.map +1 -1
  33. package/dist/airgap-instances.d.mts +3 -1
  34. package/dist/airgap-instances.d.ts +3 -1
  35. package/dist/airgap-instances.js +41 -112
  36. package/dist/airgap-instances.js.map +1 -1
  37. package/dist/branding-BsMSywts.d.mts +36 -0
  38. package/dist/branding-BsMSywts.d.ts +36 -0
  39. package/dist/error-page.js +10 -2
  40. package/dist/error-page.js.map +1 -1
  41. package/dist/error.js +10 -2
  42. package/dist/error.js.map +1 -1
  43. package/dist/esm/actions/change-team.js +66 -7
  44. package/dist/esm/actions/change-team.js.map +1 -1
  45. package/dist/esm/actions/index.js +181 -462
  46. package/dist/esm/actions/index.js.map +1 -1
  47. package/dist/esm/actions/install-actions.js +58 -5
  48. package/dist/esm/actions/install-actions.js.map +1 -1
  49. package/dist/esm/actions/service-account.js +58 -5
  50. package/dist/esm/actions/service-account.js.map +1 -1
  51. package/dist/esm/actions/support-bundles.js +58 -5
  52. package/dist/esm/actions/support-bundles.js.map +1 -1
  53. package/dist/esm/actions/team-settings.js +91 -27
  54. package/dist/esm/actions/team-settings.js.map +1 -1
  55. package/dist/esm/actions/trial-signup.js +478 -0
  56. package/dist/esm/actions/trial-signup.js.map +1 -0
  57. package/dist/esm/actions/user-settings.js +58 -5
  58. package/dist/esm/actions/user-settings.js.map +1 -1
  59. package/dist/esm/airgap-instances.js +40 -112
  60. package/dist/esm/airgap-instances.js.map +1 -1
  61. package/dist/esm/error-page.js +10 -2
  62. package/dist/esm/error-page.js.map +1 -1
  63. package/dist/esm/error.js +10 -2
  64. package/dist/esm/error.js.map +1 -1
  65. package/dist/esm/helm-install-wizard.js +118 -79
  66. package/dist/esm/helm-install-wizard.js.map +1 -1
  67. package/dist/esm/index.js +706 -438
  68. package/dist/esm/index.js.map +1 -1
  69. package/dist/esm/install-actions.js +40 -5
  70. package/dist/esm/install-actions.js.map +1 -1
  71. package/dist/esm/install-card.js +9 -6
  72. package/dist/esm/install-card.js.map +1 -1
  73. package/dist/esm/install-targets.js +9 -2
  74. package/dist/esm/install-targets.js.map +1 -1
  75. package/dist/esm/instance-card.js +39 -111
  76. package/dist/esm/instance-card.js.map +1 -1
  77. package/dist/esm/join-team.js +9 -3
  78. package/dist/esm/join-team.js.map +1 -1
  79. package/dist/esm/license-card.js +24 -22
  80. package/dist/esm/license-card.js.map +1 -1
  81. package/dist/esm/license-details.js +128 -334
  82. package/dist/esm/license-details.js.map +1 -1
  83. package/dist/esm/linux-install-wizard.js +95 -41
  84. package/dist/esm/linux-install-wizard.js.map +1 -1
  85. package/dist/esm/login.js +20 -4
  86. package/dist/esm/login.js.map +1 -1
  87. package/dist/esm/middleware.js +33 -0
  88. package/dist/esm/middleware.js.map +1 -0
  89. package/dist/esm/online-instance-list.js +40 -112
  90. package/dist/esm/online-instance-list.js.map +1 -1
  91. package/dist/esm/release-history-panel.js +27 -14
  92. package/dist/esm/release-history-panel.js.map +1 -1
  93. package/dist/esm/saml-callback-client.js +82 -0
  94. package/dist/esm/saml-callback-client.js.map +1 -0
  95. package/dist/esm/saml-handlers.js +138 -0
  96. package/dist/esm/saml-handlers.js.map +1 -0
  97. package/dist/esm/security-card.js +53 -38
  98. package/dist/esm/security-card.js.map +1 -1
  99. package/dist/esm/service-accounts-tab.js +800 -0
  100. package/dist/esm/service-accounts-tab.js.map +1 -0
  101. package/dist/esm/support-bundle-collection-card.js +48 -24
  102. package/dist/esm/support-bundle-collection-card.js.map +1 -1
  103. package/dist/esm/support-bundles-card.js +10 -5
  104. package/dist/esm/support-bundles-card.js.map +1 -1
  105. package/dist/esm/support-card.js +37 -5
  106. package/dist/esm/support-card.js.map +1 -1
  107. package/dist/esm/team-selection.js +5 -1
  108. package/dist/esm/team-selection.js.map +1 -1
  109. package/dist/esm/team-settings-card.js +5 -2
  110. package/dist/esm/team-settings-card.js.map +1 -1
  111. package/dist/esm/team-settings.js +7 -2
  112. package/dist/esm/team-settings.js.map +1 -1
  113. package/dist/esm/top-nav-user-menu.js +5 -1
  114. package/dist/esm/top-nav-user-menu.js.map +1 -1
  115. package/dist/esm/top-nav.js +175 -62
  116. package/dist/esm/top-nav.js.map +1 -1
  117. package/dist/esm/trial-signup.js +256 -0
  118. package/dist/esm/trial-signup.js.map +1 -0
  119. package/dist/esm/update-layout.js +175 -62
  120. package/dist/esm/update-layout.js.map +1 -1
  121. package/dist/esm/updates-card.js +15 -4
  122. package/dist/esm/updates-card.js.map +1 -1
  123. package/dist/esm/upload-support-bundle-modal.js +9 -4
  124. package/dist/esm/upload-support-bundle-modal.js.map +1 -1
  125. package/dist/esm/user-settings-card.js +5 -2
  126. package/dist/esm/user-settings-card.js.map +1 -1
  127. package/dist/esm/user-settings.js +12 -6
  128. package/dist/esm/user-settings.js.map +1 -1
  129. package/dist/esm/utils/index.js +204 -13
  130. package/dist/esm/utils/index.js.map +1 -1
  131. package/dist/fetch-license-iTyF7_GY.d.mts +81 -0
  132. package/dist/fetch-license-iTyF7_GY.d.ts +81 -0
  133. package/dist/helm-install-wizard.d.mts +11 -3
  134. package/dist/helm-install-wizard.d.ts +11 -3
  135. package/dist/helm-install-wizard.js +118 -79
  136. package/dist/helm-install-wizard.js.map +1 -1
  137. package/dist/{index-BAiVrSSR.d.mts → index-DyzJ0yKD.d.mts} +48 -50
  138. package/dist/{index-DWt-N5od.d.ts → index-sMbq94M7.d.ts} +48 -50
  139. package/dist/index.d.mts +8 -2
  140. package/dist/index.d.ts +8 -2
  141. package/dist/index.js +726 -438
  142. package/dist/index.js.map +1 -1
  143. package/dist/install-actions.d.mts +4 -2
  144. package/dist/install-actions.d.ts +4 -2
  145. package/dist/install-actions.js +40 -5
  146. package/dist/install-actions.js.map +1 -1
  147. package/dist/install-card.d.mts +2 -3
  148. package/dist/install-card.d.ts +2 -3
  149. package/dist/install-card.js +9 -6
  150. package/dist/install-card.js.map +1 -1
  151. package/dist/install-targets.js +9 -2
  152. package/dist/install-targets.js.map +1 -1
  153. package/dist/instance-card.d.mts +3 -1
  154. package/dist/instance-card.d.ts +3 -1
  155. package/dist/instance-card.js +40 -111
  156. package/dist/instance-card.js.map +1 -1
  157. package/dist/join-team.js +9 -3
  158. package/dist/join-team.js.map +1 -1
  159. package/dist/license-card.d.mts +2 -3
  160. package/dist/license-card.d.ts +2 -3
  161. package/dist/license-card.js +24 -22
  162. package/dist/license-card.js.map +1 -1
  163. package/dist/license-details.js +128 -334
  164. package/dist/license-details.js.map +1 -1
  165. package/dist/linux-install-wizard.d.mts +9 -3
  166. package/dist/linux-install-wizard.d.ts +9 -3
  167. package/dist/linux-install-wizard.js +95 -41
  168. package/dist/linux-install-wizard.js.map +1 -1
  169. package/dist/login.d.mts +4 -0
  170. package/dist/login.d.ts +4 -0
  171. package/dist/login.js +20 -4
  172. package/dist/login.js.map +1 -1
  173. package/dist/middleware.d.mts +13 -0
  174. package/dist/middleware.d.ts +13 -0
  175. package/dist/middleware.js +35 -0
  176. package/dist/middleware.js.map +1 -0
  177. package/dist/online-instance-list.d.mts +3 -1
  178. package/dist/online-instance-list.d.ts +3 -1
  179. package/dist/online-instance-list.js +41 -112
  180. package/dist/online-instance-list.js.map +1 -1
  181. package/dist/pending-installations.d.mts +3 -1
  182. package/dist/pending-installations.d.ts +3 -1
  183. package/dist/release-history-panel.js +27 -14
  184. package/dist/release-history-panel.js.map +1 -1
  185. package/dist/saml-callback-client.d.mts +36 -0
  186. package/dist/saml-callback-client.d.ts +36 -0
  187. package/dist/saml-callback-client.js +88 -0
  188. package/dist/saml-callback-client.js.map +1 -0
  189. package/dist/saml-handlers.d.mts +50 -0
  190. package/dist/saml-handlers.d.ts +50 -0
  191. package/dist/saml-handlers.js +141 -0
  192. package/dist/saml-handlers.js.map +1 -0
  193. package/dist/security-card.d.mts +3 -1
  194. package/dist/security-card.d.ts +3 -1
  195. package/dist/security-card.js +53 -38
  196. package/dist/security-card.js.map +1 -1
  197. package/dist/service-accounts-tab.d.mts +51 -0
  198. package/dist/service-accounts-tab.d.ts +51 -0
  199. package/dist/service-accounts-tab.js +802 -0
  200. package/dist/service-accounts-tab.js.map +1 -0
  201. package/dist/styles.css +375 -127
  202. package/dist/support-bundle-collection-card.d.mts +1 -1
  203. package/dist/support-bundle-collection-card.d.ts +1 -1
  204. package/dist/support-bundle-collection-card.js +47 -23
  205. package/dist/support-bundle-collection-card.js.map +1 -1
  206. package/dist/support-bundles-card.d.mts +4 -2
  207. package/dist/support-bundles-card.d.ts +4 -2
  208. package/dist/support-bundles-card.js +10 -5
  209. package/dist/support-bundles-card.js.map +1 -1
  210. package/dist/support-card.js +37 -5
  211. package/dist/support-card.js.map +1 -1
  212. package/dist/team-selection.js +5 -1
  213. package/dist/team-selection.js.map +1 -1
  214. package/dist/team-settings-card.js +5 -2
  215. package/dist/team-settings-card.js.map +1 -1
  216. package/dist/team-settings.js +7 -2
  217. package/dist/team-settings.js.map +1 -1
  218. package/dist/{top-nav-IRIn66wS.d.ts → top-nav-BUQAGoG1.d.mts} +14 -2
  219. package/dist/{top-nav-IRIn66wS.d.mts → top-nav-CEqw0KpO.d.ts} +14 -2
  220. package/dist/top-nav-user-menu.js +5 -1
  221. package/dist/top-nav-user-menu.js.map +1 -1
  222. package/dist/top-nav.d.mts +2 -1
  223. package/dist/top-nav.d.ts +2 -1
  224. package/dist/top-nav.js +175 -62
  225. package/dist/top-nav.js.map +1 -1
  226. package/dist/trial-signup.d.mts +31 -0
  227. package/dist/trial-signup.d.ts +31 -0
  228. package/dist/trial-signup.js +258 -0
  229. package/dist/trial-signup.js.map +1 -0
  230. package/dist/update-layout.js +175 -62
  231. package/dist/update-layout.js.map +1 -1
  232. package/dist/updates-card.js +15 -4
  233. package/dist/updates-card.js.map +1 -1
  234. package/dist/upload-support-bundle-modal.js +9 -4
  235. package/dist/upload-support-bundle-modal.js.map +1 -1
  236. package/dist/user-settings-card.js +5 -2
  237. package/dist/user-settings-card.js.map +1 -1
  238. package/dist/user-settings.js +12 -6
  239. package/dist/user-settings.js.map +1 -1
  240. package/dist/utils/index.d.mts +74 -16
  241. package/dist/utils/index.d.ts +74 -16
  242. package/dist/utils/index.js +215 -12
  243. package/dist/utils/index.js.map +1 -1
  244. package/package.json +37 -2
@@ -12,6 +12,9 @@ var Link__default = /*#__PURE__*/_interopDefault(Link);
12
12
  * This file is generated by tsup. Do not edit manually.
13
13
  */
14
14
 
15
+
16
+ // src/utils/constants.ts
17
+ var DEFAULT_SECONDARY_COLOR = "#6366f1";
15
18
  var GlobeIcon = (props) => /* @__PURE__ */ jsxRuntime.jsx(
16
19
  "svg",
17
20
  {
@@ -47,9 +50,17 @@ var headingClass = "text-lg font-semibold text-gray-900";
47
50
  var contentClass = "mt-4 flex-1 space-y-3";
48
51
  var itemClass = "flex items-center gap-3 text-sm text-gray-600";
49
52
  var iconClass = "h-5 w-5 text-gray-500";
50
- var footerClass = "mt-6 flex justify-end text-sm font-semibold text-primary hover:text-primary/80";
51
- 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";
52
- var Badge = ({ count }) => /* @__PURE__ */ jsxRuntime.jsx("span", { className: badgeClass, "aria-label": `${count} updates available`, children: count });
53
+ var footerClass = "mt-6 flex justify-end text-sm font-semibold hover:opacity-80";
54
+ var badgeClass = "ml-2 inline-flex h-5 min-w-[20px] items-center justify-center rounded-full px-1.5 text-xs font-medium text-white";
55
+ var Badge = ({ count }) => /* @__PURE__ */ jsxRuntime.jsx(
56
+ "span",
57
+ {
58
+ className: badgeClass,
59
+ style: { backgroundColor: `var(--portal-branding-primary, ${DEFAULT_SECONDARY_COLOR})` },
60
+ "aria-label": `${count} updates available`,
61
+ children: count
62
+ }
63
+ );
53
64
  var UpdatesCard = ({
54
65
  onlineActiveCount = 0,
55
66
  airgapCount = 0,
@@ -77,7 +88,7 @@ var UpdatesCard = ({
77
88
  airgapUpdates > 0 && /* @__PURE__ */ jsxRuntime.jsx(Badge, { count: airgapUpdates })
78
89
  ] })
79
90
  ] }),
80
- /* @__PURE__ */ jsxRuntime.jsx("footer", { className: footerClass, children: /* @__PURE__ */ jsxRuntime.jsx(Link__default.default, { href: "/update", children: "View updates \u2192" }) })
91
+ /* @__PURE__ */ jsxRuntime.jsx("footer", { className: footerClass, children: /* @__PURE__ */ jsxRuntime.jsx(Link__default.default, { href: "/update", style: { color: `var(--portal-branding-secondary, ${DEFAULT_SECONDARY_COLOR})` }, children: "View updates \u2192" }) })
81
92
  ] });
82
93
  UpdatesCard.displayName = "UpdatesCard";
83
94
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/updates-card.tsx"],"names":["jsx","jsxs","Link"],"mappings":";;;;;;;;;;;;;;AAGA,IAAM,SAAA,GAAY,CAAC,KAAA,qBACjBA,cAAA;AAAA,EAAC,KAAA;AAAA,EAAA;AAAA,IACC,KAAA,EAAM,4BAAA;AAAA,IACN,OAAA,EAAQ,WAAA;AAAA,IACR,IAAA,EAAK,MAAA;AAAA,IACL,MAAA,EAAO,cAAA;AAAA,IACP,WAAA,EAAa,GAAA;AAAA,IACb,aAAA,EAAc,OAAA;AAAA,IACd,cAAA,EAAe,OAAA;AAAA,IACf,aAAA,EAAY,MAAA;AAAA,IACX,GAAG,KAAA;AAAA,IAEJ,QAAA,kBAAAA,cAAA,CAAC,MAAA,EAAA,EAAK,CAAA,EAAE,+cAAA,EAAgd;AAAA;AAC1d,CAAA;AAGF,IAAM,UAAA,GAAa,CAAC,KAAA,qBAClBA,cAAA;AAAA,EAAC,KAAA;AAAA,EAAA;AAAA,IACC,KAAA,EAAM,4BAAA;AAAA,IACN,OAAA,EAAQ,WAAA;AAAA,IACR,IAAA,EAAK,MAAA;AAAA,IACL,MAAA,EAAO,cAAA;AAAA,IACP,WAAA,EAAa,GAAA;AAAA,IACb,aAAA,EAAc,OAAA;AAAA,IACd,cAAA,EAAe,OAAA;AAAA,IACf,aAAA,EAAY,MAAA;AAAA,IACX,GAAG,KAAA;AAAA,IAEJ,QAAA,kBAAAA,cAAA,CAAC,MAAA,EAAA,EAAK,CAAA,EAAE,2UAAA,EAA4U;AAAA;AACtV,CAAA;AAGF,IAAM,aAAA,GACJ,8GAAA;AACF,IAAM,YAAA,GAAe,qCAAA;AACrB,IAAM,YAAA,GAAe,uBAAA;AACrB,IAAM,SAAA,GAAY,+CAAA;AAClB,IAAM,SAAA,GAAY,uBAAA;AAClB,IAAM,WAAA,GACJ,gFAAA;AACF,IAAM,UAAA,GACJ,8HAAA;AAaF,IAAM,KAAA,GAAQ,CAAC,EAAE,KAAA,EAAM,qBACrBA,cAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAW,UAAA,EAAY,YAAA,EAAY,CAAA,EAAG,KAAK,sBAC9C,QAAA,EAAA,KAAA,EACH,CAAA;AAGK,IAAM,cAAc,CAAC;AAAA,EAC1B,iBAAA,GAAoB,CAAA;AAAA,EACpB,WAAA,GAAc,CAAA;AAAA,EACd,aAAA,GAAgB,CAAA;AAAA,EAChB,aAAA,GAAgB;AAClB,CAAA,qBACEC,eAAA,CAAC,SAAA,EAAA,EAAQ,SAAA,EAAW,aAAA,EAAe,mBAAgB,sBAAA,EACjD,QAAA,EAAA;AAAA,kBAAAD,cAAA,CAAC,QAAA,EAAA,EACC,yCAAC,IAAA,EAAA,EAAG,EAAA,EAAG,wBAAuB,SAAA,EAAW,YAAA,EAAc,qBAEvD,CAAA,EACF,CAAA;AAAA,kBACAC,eAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAW,YAAA,EACd,QAAA,EAAA;AAAA,oBAAAA,eAAA,CAAC,KAAA,EAAA,EAAI,WAAW,SAAA,EACd,QAAA,EAAA;AAAA,sBAAAD,cAAA,CAAC,SAAA,EAAA,EAAU,WAAW,SAAA,EAAW,CAAA;AAAA,sCAChC,MAAA,EAAA,EACE,QAAA,EAAA;AAAA,QAAA,iBAAA;AAAA,QAAkB,iBAAA;AAAA,QAAgB,iBAAA,KAAsB,IAAI,UAAA,GAAa;AAAA,OAAA,EAC5E,CAAA;AAAA,MACC,aAAA,GAAgB,CAAA,oBAAKA,cAAA,CAAC,KAAA,EAAA,EAAM,OAAO,aAAA,EAAe;AAAA,KAAA,EACrD,CAAA;AAAA,oBACAC,eAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAW,SAAA,EACd,QAAA,EAAA;AAAA,sBAAAD,cAAA,CAAC,UAAA,EAAA,EAAW,WAAW,SAAA,EAAW,CAAA;AAAA,sCACjC,MAAA,EAAA,EACE,QAAA,EAAA;AAAA,QAAA,WAAA;AAAA,QAAY,WAAA;AAAA,QAAU,WAAA,KAAgB,IAAI,UAAA,GAAa;AAAA,OAAA,EAC1D,CAAA;AAAA,MACC,aAAA,GAAgB,CAAA,oBAAKA,cAAA,CAAC,KAAA,EAAA,EAAM,OAAO,aAAA,EAAe;AAAA,KAAA,EACrD;AAAA,GAAA,EACF,CAAA;AAAA,kBACAA,cAAA,CAAC,YAAO,SAAA,EAAW,WAAA,EACjB,yCAACE,qBAAA,EAAA,EAAK,IAAA,EAAK,SAAA,EAAU,QAAA,EAAA,qBAAA,EAAc,CAAA,EACrC;AAAA,CAAA,EACF;AAGF,WAAA,CAAY,WAAA,GAAc,aAAA","file":"updates-card.js","sourcesContent":["import type { SVGProps } from \"react\";\nimport Link from \"next/link\";\n\nconst GlobeIcon = (props: SVGProps<SVGSVGElement>) => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={1.5}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n {...props}\n >\n <path d=\"M12 21a9.004 9.004 0 008.716-6.747M12 21a9.004 9.004 0 01-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 017.843 4.582M12 3a8.997 8.997 0 00-7.843 4.582m15.686 0A11.953 11.953 0 0112 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0121 12c0 .778-.099 1.533-.284 2.253m0 0A17.919 17.919 0 0112 16.5c-3.162 0-6.133-.815-8.716-2.247m0 0A9.015 9.015 0 013 12c0-1.605.42-3.113 1.157-4.418\" />\n </svg>\n);\n\nconst AirGapIcon = (props: SVGProps<SVGSVGElement>) => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={1.5}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n {...props}\n >\n <path d=\"M13.181 8.68a4.503 4.503 0 0 1 1.903 6.405m-9.768-2.782L3.56 14.06a4.5 4.5 0 0 0 6.364 6.365l3.129-3.129m5.614-5.615 1.757-1.757a4.5 4.5 0 0 0-6.364-6.365l-4.5 4.5c-.258.26-.479.541-.661.84m1.903 6.405a4.495 4.495 0 0 1-1.242-.88 4.483 4.483 0 0 1-1.062-1.683m6.587 2.345 5.907 5.907m-5.907-5.907L8.898 8.898M2.991 2.99 8.898 8.9\" />\n </svg>\n);\n\nconst baseCardClass =\n \"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)]\";\nconst headingClass = \"text-lg font-semibold text-gray-900\";\nconst contentClass = \"mt-4 flex-1 space-y-3\";\nconst itemClass = \"flex items-center gap-3 text-sm text-gray-600\";\nconst iconClass = \"h-5 w-5 text-gray-500\";\nconst footerClass =\n \"mt-6 flex justify-end text-sm font-semibold text-primary hover:text-primary/80\";\nconst badgeClass =\n \"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\";\n\nexport interface UpdatesCardProps {\n /** Number of active online instances (checked in within 24 hours) */\n onlineActiveCount?: number;\n /** Number of air gap instances */\n airgapCount?: number;\n /** Number of updates available for online instances */\n onlineUpdates?: number;\n /** Number of updates available for airgap instances */\n airgapUpdates?: number;\n}\n\nconst Badge = ({ count }: { count: number }) => (\n <span className={badgeClass} aria-label={`${count} updates available`}>\n {count}\n </span>\n);\n\nexport const UpdatesCard = ({\n onlineActiveCount = 0,\n airgapCount = 0,\n onlineUpdates = 0,\n airgapUpdates = 0\n}: UpdatesCardProps) => (\n <section className={baseCardClass} aria-labelledby=\"updates-card-heading\">\n <header>\n <h2 id=\"updates-card-heading\" className={headingClass}>\n Updates\n </h2>\n </header>\n <div className={contentClass}>\n <div className={itemClass}>\n <GlobeIcon className={iconClass} />\n <span>\n {onlineActiveCount} Active Online {onlineActiveCount === 1 ? \"instance\" : \"instances\"}\n </span>\n {onlineUpdates > 0 && <Badge count={onlineUpdates} />}\n </div>\n <div className={itemClass}>\n <AirGapIcon className={iconClass} />\n <span>\n {airgapCount} Air gap {airgapCount === 1 ? \"instance\" : \"instances\"}\n </span>\n {airgapUpdates > 0 && <Badge count={airgapUpdates} />}\n </div>\n </div>\n <footer className={footerClass}>\n <Link href=\"/update\">View updates →</Link>\n </footer>\n </section>\n);\n\nUpdatesCard.displayName = \"UpdatesCard\";\n"]}
1
+ {"version":3,"sources":["../src/utils/constants.ts","../src/components/updates-card.tsx"],"names":["jsx","jsxs","Link"],"mappings":"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","file":"updates-card.js","sourcesContent":["/**\n * Default globe favicon matching the CiGlobe icon (Circum Icons)\n * Used as fallback when custom branding doesn't provide a favicon\n */\nexport const DEFAULT_FAVICON = \"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%239ca3af' stroke-width='1.5'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cellipse cx='12' cy='12' rx='4' ry='10'/%3E%3Cpath d='M2 12h20'/%3E%3C/svg%3E\";\n\n/**\n * Default primary brand color\n */\nexport const DEFAULT_PRIMARY_COLOR = \"#4f46e5\";\n\n/**\n * Default secondary brand color\n */\nexport const DEFAULT_SECONDARY_COLOR = \"#6366f1\";\n\n/**\n * Check if the API origin is HTTP (used for repldev environments)\n * This determines cookie security settings for cross-origin iframes\n * @returns true if the API origin starts with http:// (not https://)\n */\nexport const isHttpApiOrigin = (): boolean => {\n return process.env.REPLICATED_APP_ORIGIN?.startsWith('http://') || false;\n};\n","import type { SVGProps } from \"react\";\nimport Link from \"next/link\";\nimport { DEFAULT_SECONDARY_COLOR } from \"../utils/constants\";\n\nconst GlobeIcon = (props: SVGProps<SVGSVGElement>) => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={1.5}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n {...props}\n >\n <path d=\"M12 21a9.004 9.004 0 008.716-6.747M12 21a9.004 9.004 0 01-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 017.843 4.582M12 3a8.997 8.997 0 00-7.843 4.582m15.686 0A11.953 11.953 0 0112 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0121 12c0 .778-.099 1.533-.284 2.253m0 0A17.919 17.919 0 0112 16.5c-3.162 0-6.133-.815-8.716-2.247m0 0A9.015 9.015 0 013 12c0-1.605.42-3.113 1.157-4.418\" />\n </svg>\n);\n\nconst AirGapIcon = (props: SVGProps<SVGSVGElement>) => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={1.5}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n {...props}\n >\n <path d=\"M13.181 8.68a4.503 4.503 0 0 1 1.903 6.405m-9.768-2.782L3.56 14.06a4.5 4.5 0 0 0 6.364 6.365l3.129-3.129m5.614-5.615 1.757-1.757a4.5 4.5 0 0 0-6.364-6.365l-4.5 4.5c-.258.26-.479.541-.661.84m1.903 6.405a4.495 4.495 0 0 1-1.242-.88 4.483 4.483 0 0 1-1.062-1.683m6.587 2.345 5.907 5.907m-5.907-5.907L8.898 8.898M2.991 2.99 8.898 8.9\" />\n </svg>\n);\n\nconst baseCardClass =\n \"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)]\";\nconst headingClass = \"text-lg font-semibold text-gray-900\";\nconst contentClass = \"mt-4 flex-1 space-y-3\";\nconst itemClass = \"flex items-center gap-3 text-sm text-gray-600\";\nconst iconClass = \"h-5 w-5 text-gray-500\";\nconst footerClass =\n \"mt-6 flex justify-end text-sm font-semibold hover:opacity-80\";\nconst badgeClass =\n \"ml-2 inline-flex h-5 min-w-[20px] items-center justify-center rounded-full px-1.5 text-xs font-medium text-white\";\n\nexport interface UpdatesCardProps {\n /** Number of active online instances (checked in within 24 hours) */\n onlineActiveCount?: number;\n /** Number of air gap instances */\n airgapCount?: number;\n /** Number of updates available for online instances */\n onlineUpdates?: number;\n /** Number of updates available for airgap instances */\n airgapUpdates?: number;\n}\n\nconst Badge = ({ count }: { count: number }) => (\n <span\n className={badgeClass}\n style={{ backgroundColor: `var(--portal-branding-primary, ${DEFAULT_SECONDARY_COLOR})` }}\n aria-label={`${count} updates available`}\n >\n {count}\n </span>\n);\n\nexport const UpdatesCard = ({\n onlineActiveCount = 0,\n airgapCount = 0,\n onlineUpdates = 0,\n airgapUpdates = 0\n}: UpdatesCardProps) => (\n <section className={baseCardClass} aria-labelledby=\"updates-card-heading\">\n <header>\n <h2 id=\"updates-card-heading\" className={headingClass}>\n Updates\n </h2>\n </header>\n <div className={contentClass}>\n <div className={itemClass}>\n <GlobeIcon className={iconClass} />\n <span>\n {onlineActiveCount} Active Online {onlineActiveCount === 1 ? \"instance\" : \"instances\"}\n </span>\n {onlineUpdates > 0 && <Badge count={onlineUpdates} />}\n </div>\n <div className={itemClass}>\n <AirGapIcon className={iconClass} />\n <span>\n {airgapCount} Air gap {airgapCount === 1 ? \"instance\" : \"instances\"}\n </span>\n {airgapUpdates > 0 && <Badge count={airgapUpdates} />}\n </div>\n </div>\n <footer className={footerClass}>\n <Link href=\"/update\" style={{ color: `var(--portal-branding-secondary, ${DEFAULT_SECONDARY_COLOR})` }}>View updates →</Link>\n </footer>\n </section>\n);\n\nUpdatesCard.displayName = \"UpdatesCard\";\n"]}
@@ -8,21 +8,26 @@ var jsxRuntime = require('react/jsx-runtime');
8
8
  * Enterprise Portal Components
9
9
  * This file is generated by tsup. Do not edit manually.
10
10
  */
11
- var formatBytes = (bytes, decimals = 2) => {
11
+
12
+ // src/utils/format.ts
13
+ function formatBytes(bytes, decimals = 1) {
12
14
  if (bytes === 0) return "0 Bytes";
13
15
  const k = 1024;
14
16
  const dm = decimals < 0 ? 0 : decimals;
15
17
  const sizes = ["Bytes", "KB", "MB", "GB", "TB"];
16
18
  const i = Math.floor(Math.log(bytes) / Math.log(k));
17
19
  return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
18
- };
20
+ }
21
+
22
+ // src/utils/constants.ts
23
+ var DEFAULT_SECONDARY_COLOR = "#6366f1";
19
24
  var UploadSupportBundleModal = ({
20
25
  isOpen,
21
26
  onClose,
22
27
  uploadSupportBundle,
23
28
  onUploadSuccess,
24
29
  appId,
25
- primaryColor = "#6366f1"
30
+ primaryColor = DEFAULT_SECONDARY_COLOR
26
31
  }) => {
27
32
  const [selectedFile, setSelectedFile] = react.useState(null);
28
33
  const [isUploading, setIsUploading] = react.useState(false);
@@ -128,7 +133,7 @@ var UploadSupportBundleModal = ({
128
133
  type: "button",
129
134
  onClick: handleUpload,
130
135
  disabled: !selectedFile || isUploading,
131
- className: "rounded-lg px-4 py-2 text-sm font-medium text-white transition hover:opacity-90 disabled:opacity-50",
136
+ className: "rounded-lg px-4 py-2 text-sm font-medium text-white transition-opacity duration-200 hover:opacity-90 disabled:opacity-50",
132
137
  style: { backgroundColor: primaryColor },
133
138
  children: isUploading ? "Uploading..." : "Upload"
134
139
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/upload-support-bundle-modal.tsx"],"names":["useState","useRef","useEffect","useCallback","jsx","jsxs"],"mappings":"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","file":"upload-support-bundle-modal.js","sourcesContent":["\"use client\";\n\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\ninterface UploadSupportBundleModalProps {\n isOpen: boolean;\n onClose: () => void;\n /** Server action to upload the support bundle */\n uploadSupportBundle: (formData: FormData) => Promise<{ \n success: boolean; \n error?: string; \n bundleId?: string;\n bundleSlug?: string;\n }>;\n /** Callback after successful upload - performs async polling to refresh bundle list */\n onUploadSuccess: () => Promise<void>;\n appId: string;\n primaryColor?: string;\n}\n\nconst formatBytes = (bytes: number, decimals = 2): string => {\n if (bytes === 0) return \"0 Bytes\";\n\n const k = 1024;\n const dm = decimals < 0 ? 0 : decimals;\n const sizes = [\"Bytes\", \"KB\", \"MB\", \"GB\", \"TB\"];\n\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n\n return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;\n};\n\nexport const UploadSupportBundleModal = ({\n isOpen,\n onClose,\n uploadSupportBundle,\n onUploadSuccess,\n appId,\n primaryColor = \"#6366f1\"\n}: UploadSupportBundleModalProps) => {\n const [selectedFile, setSelectedFile] = useState<File | null>(null);\n const [isUploading, setIsUploading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const fileInputRef = useRef<HTMLInputElement>(null);\n\n // Reset state when modal opens/closes\n useEffect(() => {\n if (!isOpen) {\n setSelectedFile(null);\n setIsUploading(false);\n setError(null);\n }\n }, [isOpen]);\n\n const handleFileChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {\n const file = event.target.files?.[0];\n if (!file) return;\n\n if (!file.name.endsWith(\".tgz\") && !file.name.endsWith(\".tar.gz\")) {\n setError(\"Invalid file type. Please select a .tgz or .tar.gz file.\");\n return;\n }\n\n setError(null);\n setSelectedFile(file);\n }, []);\n\n const handleUpload = useCallback(async () => {\n if (!selectedFile || !appId) return;\n\n setError(null);\n setIsUploading(true);\n\n try {\n // Create FormData to send to server action\n const formData = new FormData();\n formData.append(\"file\", selectedFile);\n formData.append(\"appId\", appId);\n\n // Upload via server action (streams to Enterprise Portal API)\n const result = await uploadSupportBundle(formData);\n\n if (!result.success) {\n throw new Error(result.error ?? \"Failed to upload support bundle\");\n }\n\n // Success - notify parent and wait for polling to complete\n await onUploadSuccess();\n onClose();\n } catch (err) {\n console.error(\"Upload error:\", err);\n setError(err instanceof Error ? err.message : \"Upload failed\");\n } finally {\n setIsUploading(false);\n }\n }, [selectedFile, appId, uploadSupportBundle, onUploadSuccess, onClose]);\n\n const handleCancel = useCallback(() => {\n // Note: Cannot abort server action once started\n if (isUploading) {\n setError(\"Upload cannot be cancelled once started\");\n return;\n }\n onClose();\n }, [isUploading, onClose]);\n\n const handleBackdropClick = useCallback((e: React.MouseEvent) => {\n if (e.target === e.currentTarget && !isUploading) {\n onClose();\n }\n }, [isUploading, onClose]);\n\n if (!isOpen) return null;\n\n return (\n <div\n className=\"fixed inset-0 z-50 flex items-center justify-center bg-black/50\"\n onClick={handleBackdropClick}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"upload-modal-title\"\n >\n <div className=\"w-full max-w-md rounded-2xl bg-white p-6 shadow-xl\">\n <h2 id=\"upload-modal-title\" className=\"text-lg font-semibold text-gray-900\">\n Upload Support Bundle\n </h2>\n\n <div className=\"mt-4\">\n <label className=\"block text-sm font-medium text-gray-700\">\n Select Support Bundle\n </label>\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\".tgz,.tar.gz,application/gzip,application/x-gzip\"\n onChange={handleFileChange}\n disabled={isUploading}\n className=\"mt-2 block w-full text-sm text-gray-500 file:mr-4 file:rounded-lg file:border-0 file:bg-gray-100 file:px-4 file:py-2 file:text-sm file:font-medium file:text-gray-700 hover:file:bg-gray-200 disabled:opacity-50\"\n />\n {selectedFile && (\n <div className=\"mt-3 flex items-center justify-between rounded-lg bg-gray-50 px-3 py-2 text-sm\">\n <span className=\"truncate text-gray-700\">{selectedFile.name}</span>\n <span className=\"ml-2 shrink-0 text-gray-500\">{formatBytes(selectedFile.size)}</span>\n </div>\n )}\n </div>\n\n {error && (\n <div className=\"mt-4 rounded-lg bg-red-50 px-3 py-2 text-sm text-red-700\">\n {error}\n </div>\n )}\n\n {isUploading && (\n <div className=\"mt-6 mb-6 flex items-center justify-center gap-3\">\n <div className=\"h-5 w-5 animate-spin rounded-full border-2 border-gray-300 border-t-transparent\" style={{ borderTopColor: primaryColor }} />\n <span className=\"text-sm font-medium text-gray-700\">\n Uploading support bundle...\n </span>\n </div>\n )}\n\n <div className=\"mt-6 flex justify-end gap-3\">\n <button\n type=\"button\"\n onClick={handleCancel}\n className=\"rounded-lg px-4 py-2 text-sm font-medium text-gray-600 transition hover:bg-gray-100\"\n >\n {isUploading ? \"Cancel Upload\" : \"Cancel\"}\n </button>\n <button\n type=\"button\"\n onClick={handleUpload}\n disabled={!selectedFile || isUploading}\n className=\"rounded-lg px-4 py-2 text-sm font-medium text-white transition hover:opacity-90 disabled:opacity-50\"\n style={{ backgroundColor: primaryColor }}\n >\n {isUploading ? \"Uploading...\" : \"Upload\"}\n </button>\n </div>\n </div>\n </div>\n );\n};\n\nUploadSupportBundleModal.displayName = \"UploadSupportBundleModal\";\n"]}
1
+ {"version":3,"sources":["../src/utils/format.ts","../src/utils/constants.ts","../src/components/upload-support-bundle-modal.tsx"],"names":["useState","useRef","useEffect","useCallback","jsx","jsxs"],"mappings":"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","file":"upload-support-bundle-modal.js","sourcesContent":["/**\n * Format bytes to human-readable string\n * @param bytes - Number of bytes\n * @param decimals - Number of decimal places (default: 1)\n * @returns Formatted string (e.g., \"1.5 MB\")\n */\nexport function formatBytes(bytes: number, decimals = 1): string {\n if (bytes === 0) return \"0 Bytes\";\n\n const k = 1024;\n const dm = decimals < 0 ? 0 : decimals;\n const sizes = [\"Bytes\", \"KB\", \"MB\", \"GB\", \"TB\"];\n\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n\n return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;\n}\n\n/**\n * Format date string to short numeric date (no time)\n * @param dateString - ISO date string or null/undefined\n * @returns Formatted string (e.g., \"01/27/2026\") or \"Never\" if no date\n */\nexport function formatDateShort(dateString?: string | null): string {\n if (!dateString) return \"Never\";\n const date = new Date(dateString);\n if (isNaN(date.getTime())) {\n return dateString;\n }\n return date.toLocaleDateString(\"en-US\", {\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\"\n });\n}\n\n/**\n * Format date string to human-readable local timestamp\n * @param dateString - ISO date string or null/undefined\n * @returns Formatted string (e.g., \"Jan 27, 2026, 10:32 PM\") or \"Never\" if no date\n */\nexport function formatDate(dateString?: string | null): string {\n if (!dateString) return \"Never\";\n const date = new Date(dateString);\n if (isNaN(date.getTime())) {\n return dateString;\n }\n return date.toLocaleDateString(\"en-US\", {\n month: \"short\",\n day: \"numeric\",\n year: \"numeric\",\n hour: \"numeric\",\n minute: \"2-digit\",\n hour12: true\n });\n}\n\n/**\n * Format date string to human-readable UTC timestamp\n * @param dateString - ISO date string\n * @returns Formatted string (e.g., \"01/27/2026, 22:32:39 UTC\")\n */\nexport function formatDateTime(dateString: string): string {\n const date = new Date(dateString);\n if (isNaN(date.getTime())) {\n return dateString;\n }\n return date.toLocaleString(\"en-US\", {\n timeZone: \"UTC\",\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\",\n hour12: false\n }) + \" UTC\";\n}\n\n/**\n * Format date string to user-friendly local timestamp with 12-hour time\n * @param dateString - ISO date string or null/undefined\n * @returns Formatted string (e.g., \"1/27/2026, 10:32 PM\") or \"N/A\" if not provided\n */\nexport function formatDateTimeLocal(dateString?: string | null): string {\n if (!dateString) return \"N/A\";\n\n try {\n const date = new Date(dateString);\n if (isNaN(date.getTime())) {\n return dateString;\n }\n return date.toLocaleDateString(\"en-US\", {\n month: \"numeric\",\n day: \"numeric\",\n year: \"numeric\",\n hour: \"numeric\",\n minute: \"2-digit\",\n hour12: true\n });\n } catch {\n return \"N/A\";\n }\n}\n","/**\n * Default globe favicon matching the CiGlobe icon (Circum Icons)\n * Used as fallback when custom branding doesn't provide a favicon\n */\nexport const DEFAULT_FAVICON = \"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%239ca3af' stroke-width='1.5'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cellipse cx='12' cy='12' rx='4' ry='10'/%3E%3Cpath d='M2 12h20'/%3E%3C/svg%3E\";\n\n/**\n * Default primary brand color\n */\nexport const DEFAULT_PRIMARY_COLOR = \"#4f46e5\";\n\n/**\n * Default secondary brand color\n */\nexport const DEFAULT_SECONDARY_COLOR = \"#6366f1\";\n\n/**\n * Check if the API origin is HTTP (used for repldev environments)\n * This determines cookie security settings for cross-origin iframes\n * @returns true if the API origin starts with http:// (not https://)\n */\nexport const isHttpApiOrigin = (): boolean => {\n return process.env.REPLICATED_APP_ORIGIN?.startsWith('http://') || false;\n};\n","\"use client\";\n\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport { formatBytes } from \"../utils/format\";\nimport { DEFAULT_SECONDARY_COLOR } from \"../utils/constants\";\n\ninterface UploadSupportBundleModalProps {\n isOpen: boolean;\n onClose: () => void;\n /** Server action to upload the support bundle */\n uploadSupportBundle: (formData: FormData) => Promise<{ \n success: boolean; \n error?: string; \n bundleId?: string;\n bundleSlug?: string;\n }>;\n /** Callback after successful upload - performs async polling to refresh bundle list */\n onUploadSuccess: () => Promise<void>;\n appId: string;\n primaryColor?: string;\n}\n\nexport const UploadSupportBundleModal = ({\n isOpen,\n onClose,\n uploadSupportBundle,\n onUploadSuccess,\n appId,\n primaryColor = DEFAULT_SECONDARY_COLOR\n}: UploadSupportBundleModalProps) => {\n const [selectedFile, setSelectedFile] = useState<File | null>(null);\n const [isUploading, setIsUploading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const fileInputRef = useRef<HTMLInputElement>(null);\n\n // Reset state when modal opens/closes\n useEffect(() => {\n if (!isOpen) {\n setSelectedFile(null);\n setIsUploading(false);\n setError(null);\n }\n }, [isOpen]);\n\n const handleFileChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {\n const file = event.target.files?.[0];\n if (!file) return;\n\n if (!file.name.endsWith(\".tgz\") && !file.name.endsWith(\".tar.gz\")) {\n setError(\"Invalid file type. Please select a .tgz or .tar.gz file.\");\n return;\n }\n\n setError(null);\n setSelectedFile(file);\n }, []);\n\n const handleUpload = useCallback(async () => {\n if (!selectedFile || !appId) return;\n\n setError(null);\n setIsUploading(true);\n\n try {\n // Create FormData to send to server action\n const formData = new FormData();\n formData.append(\"file\", selectedFile);\n formData.append(\"appId\", appId);\n\n // Upload via server action (streams to Enterprise Portal API)\n const result = await uploadSupportBundle(formData);\n\n if (!result.success) {\n throw new Error(result.error ?? \"Failed to upload support bundle\");\n }\n\n // Success - notify parent and wait for polling to complete\n await onUploadSuccess();\n onClose();\n } catch (err) {\n console.error(\"Upload error:\", err);\n setError(err instanceof Error ? err.message : \"Upload failed\");\n } finally {\n setIsUploading(false);\n }\n }, [selectedFile, appId, uploadSupportBundle, onUploadSuccess, onClose]);\n\n const handleCancel = useCallback(() => {\n // Note: Cannot abort server action once started\n if (isUploading) {\n setError(\"Upload cannot be cancelled once started\");\n return;\n }\n onClose();\n }, [isUploading, onClose]);\n\n const handleBackdropClick = useCallback((e: React.MouseEvent) => {\n if (e.target === e.currentTarget && !isUploading) {\n onClose();\n }\n }, [isUploading, onClose]);\n\n if (!isOpen) return null;\n\n return (\n <div\n className=\"fixed inset-0 z-50 flex items-center justify-center bg-black/50\"\n onClick={handleBackdropClick}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"upload-modal-title\"\n >\n <div className=\"w-full max-w-md rounded-2xl bg-white p-6 shadow-xl\">\n <h2 id=\"upload-modal-title\" className=\"text-lg font-semibold text-gray-900\">\n Upload Support Bundle\n </h2>\n\n <div className=\"mt-4\">\n <label className=\"block text-sm font-medium text-gray-700\">\n Select Support Bundle\n </label>\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\".tgz,.tar.gz,application/gzip,application/x-gzip\"\n onChange={handleFileChange}\n disabled={isUploading}\n className=\"mt-2 block w-full text-sm text-gray-500 file:mr-4 file:rounded-lg file:border-0 file:bg-gray-100 file:px-4 file:py-2 file:text-sm file:font-medium file:text-gray-700 hover:file:bg-gray-200 disabled:opacity-50\"\n />\n {selectedFile && (\n <div className=\"mt-3 flex items-center justify-between rounded-lg bg-gray-50 px-3 py-2 text-sm\">\n <span className=\"truncate text-gray-700\">{selectedFile.name}</span>\n <span className=\"ml-2 shrink-0 text-gray-500\">{formatBytes(selectedFile.size)}</span>\n </div>\n )}\n </div>\n\n {error && (\n <div className=\"mt-4 rounded-lg bg-red-50 px-3 py-2 text-sm text-red-700\">\n {error}\n </div>\n )}\n\n {isUploading && (\n <div className=\"mt-6 mb-6 flex items-center justify-center gap-3\">\n <div className=\"h-5 w-5 animate-spin rounded-full border-2 border-gray-300 border-t-transparent\" style={{ borderTopColor: primaryColor }} />\n <span className=\"text-sm font-medium text-gray-700\">\n Uploading support bundle...\n </span>\n </div>\n )}\n\n <div className=\"mt-6 flex justify-end gap-3\">\n <button\n type=\"button\"\n onClick={handleCancel}\n className=\"rounded-lg px-4 py-2 text-sm font-medium text-gray-600 transition hover:bg-gray-100\"\n >\n {isUploading ? \"Cancel Upload\" : \"Cancel\"}\n </button>\n <button\n type=\"button\"\n onClick={handleUpload}\n disabled={!selectedFile || isUploading}\n className=\"rounded-lg px-4 py-2 text-sm font-medium text-white transition-opacity duration-200 hover:opacity-90 disabled:opacity-50\"\n style={{ backgroundColor: primaryColor }}\n >\n {isUploading ? \"Uploading...\" : \"Upload\"}\n </button>\n </div>\n </div>\n </div>\n );\n};\n\nUploadSupportBundleModal.displayName = \"UploadSupportBundleModal\";\n"]}
@@ -12,14 +12,17 @@ var Link__default = /*#__PURE__*/_interopDefault(Link);
12
12
  * This file is generated by tsup. Do not edit manually.
13
13
  */
14
14
 
15
+
16
+ // src/utils/constants.ts
17
+ var DEFAULT_SECONDARY_COLOR = "#6366f1";
15
18
  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)]";
16
19
  var headingClass = "text-lg font-semibold text-gray-900";
17
20
  var bodySpacerClass = "mt-4 flex-1";
18
- var footerClass = "mt-6 flex justify-end text-sm font-semibold text-primary hover:text-primary/80";
21
+ var footerClass = "mt-6 flex justify-end text-sm font-semibold hover:opacity-80";
19
22
  var UserSettingsCard = () => /* @__PURE__ */ jsxRuntime.jsxs("section", { className: baseCardClass, "aria-labelledby": "user-settings-card-heading", children: [
20
23
  /* @__PURE__ */ jsxRuntime.jsx("header", { children: /* @__PURE__ */ jsxRuntime.jsx("h2", { id: "user-settings-card-heading", className: headingClass, children: "User Settings" }) }),
21
24
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: bodySpacerClass }),
22
- /* @__PURE__ */ jsxRuntime.jsx("footer", { className: footerClass, children: /* @__PURE__ */ jsxRuntime.jsx(Link__default.default, { href: "/user-settings", children: "View user settings \u2192" }) })
25
+ /* @__PURE__ */ jsxRuntime.jsx("footer", { className: footerClass, children: /* @__PURE__ */ jsxRuntime.jsx(Link__default.default, { href: "/user-settings", style: { color: `var(--portal-branding-secondary, ${DEFAULT_SECONDARY_COLOR})` }, children: "View user settings \u2192" }) })
23
26
  ] });
24
27
  UserSettingsCard.displayName = "UserSettingsCard";
25
28
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/user-settings-card.tsx"],"names":["jsxs","jsx","Link"],"mappings":";;;;;;;;;;;;;;AAEA,IAAM,aAAA,GACJ,8GAAA;AACF,IAAM,YAAA,GAAe,qCAAA;AACrB,IAAM,eAAA,GAAkB,aAAA;AACxB,IAAM,WAAA,GACJ,gFAAA;AAEK,IAAM,mBAAmB,sBAC9BA,eAAA,CAAC,aAAQ,SAAA,EAAW,aAAA,EAAe,mBAAgB,4BAAA,EACjD,QAAA,EAAA;AAAA,kBAAAC,cAAA,CAAC,QAAA,EAAA,EACC,yCAAC,IAAA,EAAA,EAAG,EAAA,EAAG,8BAA6B,SAAA,EAAW,YAAA,EAAc,2BAE7D,CAAA,EACF,CAAA;AAAA,kBACAA,cAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAW,eAAA,EAAiB,CAAA;AAAA,kBACjCA,cAAA,CAAC,YAAO,SAAA,EAAW,WAAA,EACjB,yCAACC,qBAAA,EAAA,EAAK,IAAA,EAAK,gBAAA,EAAiB,QAAA,EAAA,2BAAA,EAAoB,CAAA,EAClD;AAAA,CAAA,EACF;AAGF,gBAAA,CAAiB,WAAA,GAAc,kBAAA","file":"user-settings-card.js","sourcesContent":["import Link from \"next/link\";\n\nconst baseCardClass =\n \"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)]\";\nconst headingClass = \"text-lg font-semibold text-gray-900\";\nconst bodySpacerClass = \"mt-4 flex-1\";\nconst footerClass =\n \"mt-6 flex justify-end text-sm font-semibold text-primary hover:text-primary/80\";\n\nexport const UserSettingsCard = () => (\n <section className={baseCardClass} aria-labelledby=\"user-settings-card-heading\">\n <header>\n <h2 id=\"user-settings-card-heading\" className={headingClass}>\n User Settings\n </h2>\n </header>\n <div className={bodySpacerClass} />\n <footer className={footerClass}>\n <Link href=\"/user-settings\">View user settings →</Link>\n </footer>\n </section>\n);\n\nUserSettingsCard.displayName = \"UserSettingsCard\";\n"]}
1
+ {"version":3,"sources":["../src/utils/constants.ts","../src/components/user-settings-card.tsx"],"names":["jsxs","jsx","Link"],"mappings":";;;;;;;;;;;;;;;;AAcO,IAAM,uBAAA,GAA0B,SAAA;ACXvC,IAAM,aAAA,GACJ,8GAAA;AACF,IAAM,YAAA,GAAe,qCAAA;AACrB,IAAM,eAAA,GAAkB,aAAA;AACxB,IAAM,WAAA,GACJ,8DAAA;AAEK,IAAM,mBAAmB,sBAC9BA,eAAA,CAAC,aAAQ,SAAA,EAAW,aAAA,EAAe,mBAAgB,4BAAA,EACjD,QAAA,EAAA;AAAA,kBAAAC,cAAA,CAAC,QAAA,EAAA,EACC,yCAAC,IAAA,EAAA,EAAG,EAAA,EAAG,8BAA6B,SAAA,EAAW,YAAA,EAAc,2BAE7D,CAAA,EACF,CAAA;AAAA,kBACAA,cAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAW,eAAA,EAAiB,CAAA;AAAA,iCAChC,QAAA,EAAA,EAAO,SAAA,EAAW,WAAA,EACjB,QAAA,kBAAAA,cAAA,CAACC,yBAAK,IAAA,EAAK,gBAAA,EAAiB,KAAA,EAAO,EAAE,OAAO,CAAA,iCAAA,EAAoC,uBAAuB,CAAA,CAAA,CAAA,EAAI,EAAG,uCAAoB,CAAA,EACpI;AAAA,CAAA,EACF;AAGF,gBAAA,CAAiB,WAAA,GAAc,kBAAA","file":"user-settings-card.js","sourcesContent":["/**\n * Default globe favicon matching the CiGlobe icon (Circum Icons)\n * Used as fallback when custom branding doesn't provide a favicon\n */\nexport const DEFAULT_FAVICON = \"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%239ca3af' stroke-width='1.5'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cellipse cx='12' cy='12' rx='4' ry='10'/%3E%3Cpath d='M2 12h20'/%3E%3C/svg%3E\";\n\n/**\n * Default primary brand color\n */\nexport const DEFAULT_PRIMARY_COLOR = \"#4f46e5\";\n\n/**\n * Default secondary brand color\n */\nexport const DEFAULT_SECONDARY_COLOR = \"#6366f1\";\n\n/**\n * Check if the API origin is HTTP (used for repldev environments)\n * This determines cookie security settings for cross-origin iframes\n * @returns true if the API origin starts with http:// (not https://)\n */\nexport const isHttpApiOrigin = (): boolean => {\n return process.env.REPLICATED_APP_ORIGIN?.startsWith('http://') || false;\n};\n","import Link from \"next/link\";\nimport { DEFAULT_SECONDARY_COLOR } from \"../utils/constants\";\n\nconst baseCardClass =\n \"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)]\";\nconst headingClass = \"text-lg font-semibold text-gray-900\";\nconst bodySpacerClass = \"mt-4 flex-1\";\nconst footerClass =\n \"mt-6 flex justify-end text-sm font-semibold hover:opacity-80\";\n\nexport const UserSettingsCard = () => (\n <section className={baseCardClass} aria-labelledby=\"user-settings-card-heading\">\n <header>\n <h2 id=\"user-settings-card-heading\" className={headingClass}>\n User Settings\n </h2>\n </header>\n <div className={bodySpacerClass} />\n <footer className={footerClass}>\n <Link href=\"/user-settings\" style={{ color: `var(--portal-branding-secondary, ${DEFAULT_SECONDARY_COLOR})` }}>View user settings →</Link>\n </footer>\n </section>\n);\n\nUserSettingsCard.displayName = \"UserSettingsCard\";\n"]}
@@ -9,6 +9,9 @@ var jsxRuntime = require('react/jsx-runtime');
9
9
  * This file is generated by tsup. Do not edit manually.
10
10
  */
11
11
 
12
+
13
+ // src/utils/constants.ts
14
+ var DEFAULT_SECONDARY_COLOR = "#6366f1";
12
15
  var NOTIFICATION_DESCRIPTIONS = {
13
16
  "new-version": "New version available"
14
17
  };
@@ -117,7 +120,7 @@ var TeamsSection = ({
117
120
  type: "button",
118
121
  onClick: () => !isCurrentTeam && onTeamSwitch?.(team),
119
122
  disabled: isCurrentTeam,
120
- 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"}`,
123
+ className: `flex w-full items-center justify-between rounded-lg border p-4 text-left transition ${isCurrentTeam ? "cursor-default border-gray-300 bg-gray-50" : "cursor-pointer border-gray-200 hover:border-gray-400"}`,
121
124
  style: !isCurrentTeam && primaryColor ? { "--hover-border": primaryColor } : void 0,
122
125
  children: [
123
126
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
@@ -125,7 +128,7 @@ var TeamsSection = ({
125
128
  "svg",
126
129
  {
127
130
  className: "h-4 w-4",
128
- style: { color: primaryColor || "#6366f1" },
131
+ style: { color: primaryColor || DEFAULT_SECONDARY_COLOR },
129
132
  fill: "none",
130
133
  viewBox: "0 0 24 24",
131
134
  stroke: "currentColor",
@@ -149,7 +152,7 @@ var TeamsSection = ({
149
152
  "svg",
150
153
  {
151
154
  className: "h-4 w-4",
152
- style: { color: primaryColor || "#6366f1" },
155
+ style: { color: primaryColor || DEFAULT_SECONDARY_COLOR },
153
156
  fill: "none",
154
157
  viewBox: "0 0 24 24",
155
158
  stroke: "currentColor",
@@ -214,7 +217,10 @@ var ProfileSection = ({
214
217
  resetForm();
215
218
  setIsEditing(true);
216
219
  };
217
- const buttonStyle = primaryColor ? { backgroundColor: primaryColor, borderColor: primaryColor } : void 0;
220
+ const buttonStyle = {
221
+ backgroundColor: primaryColor || DEFAULT_SECONDARY_COLOR,
222
+ borderColor: primaryColor || DEFAULT_SECONDARY_COLOR
223
+ };
218
224
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-2xl border border-gray-200 bg-white p-6 shadow-sm", children: [
219
225
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-4", children: [
220
226
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
@@ -241,7 +247,7 @@ var ProfileSection = ({
241
247
  onClick: handleSave,
242
248
  disabled: isUpdating,
243
249
  style: buttonStyle,
244
- 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",
250
+ className: "inline-flex items-center justify-center rounded-md px-4 py-2 text-sm font-medium text-white shadow-sm transition-opacity duration-200 hover:opacity-90 disabled:opacity-50 min-w-[70px]",
245
251
  children: isUpdating ? "Saving..." : "Save"
246
252
  }
247
253
  )
@@ -251,7 +257,7 @@ var ProfileSection = ({
251
257
  type: "button",
252
258
  onClick: handleEdit,
253
259
  style: buttonStyle,
254
- 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",
260
+ className: "inline-flex items-center justify-center rounded-md px-4 py-2 text-sm font-medium text-white shadow-sm transition-opacity duration-200 hover:opacity-90 min-w-[70px]",
255
261
  children: "Edit"
256
262
  }
257
263
  )
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/user-settings.tsx"],"names":["jsxs","jsx","useMemo","useState"],"mappings":"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","file":"user-settings.js","sourcesContent":["\"use client\";\n\nimport { useState, useMemo } from \"react\";\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface UserSettingsUser {\n emailAddress: string;\n firstName?: string;\n lastName?: string;\n}\n\nexport interface UserSettingsTeam {\n id: string;\n name: string;\n appId: string;\n appName: string;\n appSlug: string;\n}\n\nexport interface UserSettingsCustomer {\n id: string;\n name: string;\n}\n\nexport interface UserSettingsNotification {\n type: string;\n enabled: boolean;\n}\n\nexport interface UserSettingsProps {\n // User profile\n user?: UserSettingsUser;\n isUserLoading?: boolean;\n userError?: Error | null;\n \n // Profile editing\n isUpdating?: boolean;\n updateError?: Error | null;\n onUpdateUser?: (data: { firstName?: string; lastName?: string }) => Promise<void>;\n \n // Teams\n teams?: UserSettingsTeam[];\n currentCustomer?: UserSettingsCustomer;\n isTeamsLoading?: boolean;\n onTeamSwitch?: (team: UserSettingsTeam) => Promise<void>;\n \n // Notifications\n notifications?: UserSettingsNotification[];\n isNotificationsLoading?: boolean;\n onToggleNotification?: (notificationType: string, enabled: boolean) => Promise<void>;\n \n // Optional branding\n primaryColor?: string;\n}\n\n// =============================================================================\n// Notification descriptions\n// =============================================================================\n\nconst NOTIFICATION_DESCRIPTIONS: Record<string, string> = {\n \"new-version\": \"New version available\",\n};\n\n// =============================================================================\n// Email Notifications Section\n// =============================================================================\n\ninterface EmailNotificationsSectionProps {\n teamName: string;\n notifications: UserSettingsNotification[];\n isLoading: boolean;\n onToggle?: (type: string, enabled: boolean) => Promise<void>;\n}\n\nconst EmailNotificationsSection = ({\n teamName,\n notifications,\n isLoading,\n onToggle,\n}: EmailNotificationsSectionProps) => {\n // Merge API data with all notification types to ensure all options are always shown\n const allNotificationTypes = Object.keys(NOTIFICATION_DESCRIPTIONS);\n const mergedNotifications = allNotificationTypes.map((type) => {\n const existing = notifications.find((n) => n.type === type);\n return {\n type,\n enabled: existing ? existing.enabled : type === \"new-version\", // Default new-version to true\n };\n });\n\n if (isLoading) {\n return (\n <div className=\"rounded-2xl border border-gray-200 bg-white p-6 shadow-sm\">\n <h2 className=\"text-lg font-semibold text-gray-900\">\n Email Notifications for {teamName}\n </h2>\n <p className=\"mt-1 text-sm text-gray-500\">\n Choose which email notifications you want to receive for this team.\n </p>\n <div className=\"mt-6 flex justify-center py-8\">\n <p className=\"text-sm text-gray-500\">Loading notifications...</p>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"rounded-2xl border border-gray-200 bg-white p-6 shadow-sm\">\n <h2 className=\"text-lg font-semibold text-gray-900\">\n Email Notifications for {teamName}\n </h2>\n <p className=\"mt-1 text-sm text-gray-500\">\n Choose which email notifications you want to receive for this team.\n </p>\n\n <div className=\"mt-6 overflow-hidden rounded-lg border border-gray-200\">\n <div className=\"flex items-center justify-between bg-gray-50 px-4 py-2\">\n <span className=\"text-xs font-medium uppercase text-gray-600\">\n Description\n </span>\n <span className=\"text-xs font-medium uppercase text-gray-600\">\n Enabled\n </span>\n </div>\n {mergedNotifications.map((notification) => (\n <div\n key={notification.type}\n className=\"flex items-center justify-between border-t border-gray-200 px-4 py-3\"\n >\n <span className=\"text-sm text-gray-900\">\n {NOTIFICATION_DESCRIPTIONS[notification.type]}\n </span>\n <input\n type=\"checkbox\"\n checked={notification.enabled}\n onChange={() => onToggle?.(notification.type, !notification.enabled)}\n className=\"portal-checkbox\"\n />\n </div>\n ))}\n </div>\n <p className=\"mt-3 text-xs text-gray-500\">\n These notification preferences are specific to the {teamName} team.\n Switch teams to manage notifications for other teams.\n </p>\n </div>\n );\n};\n\n// =============================================================================\n// Teams Section\n// =============================================================================\n\ninterface TeamsSectionProps {\n teams: UserSettingsTeam[];\n currentCustomer?: UserSettingsCustomer;\n isLoading: boolean;\n onTeamSwitch?: (team: UserSettingsTeam) => Promise<void>;\n primaryColor?: string;\n}\n\nconst TeamsSection = ({\n teams,\n currentCustomer,\n isLoading,\n onTeamSwitch,\n primaryColor,\n}: TeamsSectionProps) => {\n // Group teams by app\n const groupedTeams = useMemo(() => {\n const groups = new Map<string, { appName: string; teams: UserSettingsTeam[] }>();\n \n for (const team of teams) {\n const existing = groups.get(team.appId);\n if (existing) {\n existing.teams.push(team);\n } else {\n groups.set(team.appId, {\n appName: team.appName,\n teams: [team],\n });\n }\n }\n \n return groups;\n }, [teams]);\n\n if (isLoading) {\n return (\n <div className=\"rounded-2xl border border-gray-200 bg-white p-6 shadow-sm\">\n <h2 className=\"text-lg font-semibold text-gray-900\">Teams</h2>\n <p className=\"mt-1 text-sm text-gray-500\">\n Teams associate your email address with any additional licenses your\n account has access to.\n </p>\n <div className=\"mt-6\">\n <p className=\"text-sm text-gray-500\">Loading teams...</p>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"rounded-2xl border border-gray-200 bg-white p-6 shadow-sm\">\n <h2 className=\"text-lg font-semibold text-gray-900\">Teams</h2>\n <p className=\"mt-1 text-sm text-gray-500\">\n Teams associate your email address with any additional licenses your\n account has access to.\n </p>\n\n <div className=\"mt-6 space-y-6\">\n {Array.from(groupedTeams.values()).map((appGroup) => (\n <div key={appGroup.appName} className=\"space-y-3\">\n <h3 className=\"text-base font-medium text-gray-900\">\n {appGroup.appName}\n </h3>\n <div className=\"space-y-2\">\n {appGroup.teams.map((team) => {\n const isCurrentTeam = currentCustomer?.id === team.id;\n return (\n <button\n key={team.id}\n type=\"button\"\n onClick={() => !isCurrentTeam && onTeamSwitch?.(team)}\n disabled={isCurrentTeam}\n className={`flex w-full items-center justify-between rounded-lg border p-4 text-left transition ${\n isCurrentTeam\n ? \"cursor-default border-gray-300 bg-gray-50\"\n : \"cursor-pointer border-gray-200 hover:border-indigo-500\"\n }`}\n style={\n !isCurrentTeam && primaryColor\n ? { \"--hover-border\": primaryColor } as React.CSSProperties\n : undefined\n }\n >\n <div className=\"flex items-center gap-3\">\n <svg\n className=\"h-4 w-4\"\n style={{ color: primaryColor || \"#6366f1\" }}\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n strokeWidth={2}\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n 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\"\n />\n </svg>\n <div>\n <p className=\"text-sm font-medium text-gray-900\">\n {team.name}\n </p>\n {isCurrentTeam && (\n <p className=\"text-xs text-gray-500\">Current Team</p>\n )}\n </div>\n </div>\n {!isCurrentTeam && (\n <svg\n className=\"h-4 w-4\"\n style={{ color: primaryColor || \"#6366f1\" }}\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n strokeWidth={2}\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n d=\"M9 5l7 7-7 7\"\n />\n </svg>\n )}\n </button>\n );\n })}\n </div>\n </div>\n ))}\n </div>\n </div>\n );\n};\n\n// =============================================================================\n// Profile Section\n// =============================================================================\n\ninterface ProfileSectionProps {\n user?: UserSettingsUser;\n isLoading: boolean;\n isUpdating: boolean;\n error?: Error | null;\n updateError?: Error | null;\n onSave?: (data: { firstName?: string; lastName?: string }) => Promise<void>;\n primaryColor?: string;\n}\n\nconst ProfileSection = ({\n user,\n isLoading,\n isUpdating,\n error,\n updateError,\n onSave,\n primaryColor,\n}: ProfileSectionProps) => {\n const [isEditing, setIsEditing] = useState(false);\n const [firstName, setFirstName] = useState(user?.firstName || \"\");\n const [lastName, setLastName] = useState(user?.lastName || \"\");\n const [inputError, setInputError] = useState<string | null>(null);\n\n // Reset form when user changes\n const resetForm = () => {\n setFirstName(user?.firstName || \"\");\n setLastName(user?.lastName || \"\");\n setInputError(null);\n };\n\n const handleSave = async () => {\n if (!firstName && !lastName) {\n setInputError(\"Please enter a first name or last name\");\n return;\n }\n\n if (firstName === user?.firstName && lastName === user?.lastName) {\n setIsEditing(false);\n return;\n }\n\n try {\n await onSave?.({ firstName, lastName });\n setIsEditing(false);\n } catch (err) {\n console.error(\"Error updating user information:\", err);\n }\n };\n\n const handleCancel = () => {\n setIsEditing(false);\n resetForm();\n };\n\n const handleEdit = () => {\n resetForm();\n setIsEditing(true);\n };\n\n const buttonStyle = primaryColor\n ? { backgroundColor: primaryColor, borderColor: primaryColor }\n : undefined;\n\n return (\n <div className=\"rounded-2xl border border-gray-200 bg-white p-6 shadow-sm\">\n <div className=\"flex items-start justify-between gap-4\">\n <div>\n <h2 className=\"text-lg font-semibold text-gray-900\">\n Profile Information\n </h2>\n <p className=\"mt-1 text-sm text-gray-500\">Your account information.</p>\n {(error || updateError) && (\n <p className=\"mt-2 text-sm text-red-600\">\n {error?.message || updateError?.message || \"Failed to update profile\"}\n </p>\n )}\n {inputError && (\n <p className=\"mt-2 text-sm text-red-600\">{inputError}</p>\n )}\n </div>\n {isEditing ? (\n <div className=\"flex gap-2\">\n <button\n type=\"button\"\n onClick={handleCancel}\n disabled={isUpdating}\n 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\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={handleSave}\n disabled={isUpdating}\n style={buttonStyle}\n 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\"\n >\n {isUpdating ? \"Saving...\" : \"Save\"}\n </button>\n </div>\n ) : (\n <button\n type=\"button\"\n onClick={handleEdit}\n style={buttonStyle}\n 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\"\n >\n Edit\n </button>\n )}\n </div>\n\n <div className=\"mt-6 flex gap-8\">\n {isEditing ? (\n <div className=\"flex gap-4\">\n <div>\n <label className=\"block text-sm font-medium text-gray-600\">\n First Name\n </label>\n <input\n type=\"text\"\n value={firstName}\n onChange={(e) => {\n setInputError(null);\n setFirstName(e.target.value);\n }}\n disabled={isUpdating}\n placeholder=\"First Name\"\n className=\"portal-input mt-1 block w-48\"\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-gray-600\">\n Last Name\n </label>\n <input\n type=\"text\"\n value={lastName}\n onChange={(e) => {\n setInputError(null);\n setLastName(e.target.value);\n }}\n disabled={isUpdating}\n placeholder=\"Last Name\"\n className=\"portal-input mt-1 block w-48\"\n />\n </div>\n </div>\n ) : (\n <div className=\"flex-1\">\n <label className=\"block text-sm font-medium text-gray-600\">\n Full Name (optional)\n </label>\n <p className=\"mt-1 text-sm text-gray-900\">\n {user?.firstName || user?.lastName\n ? `${user.firstName || \"\"} ${user.lastName || \"\"}`.trim()\n : \"—\"}\n </p>\n </div>\n )}\n <div className=\"flex-1\">\n <label className=\"block text-sm font-medium text-gray-600\">Email</label>\n <p className=\"mt-1 text-sm text-gray-500\">\n {isLoading ? \"Loading...\" : user?.emailAddress || \"\"}\n </p>\n </div>\n </div>\n </div>\n );\n};\n\n// =============================================================================\n// Main Component\n// =============================================================================\n\nexport const UserSettings = ({\n user,\n isUserLoading = false,\n userError,\n isUpdating = false,\n updateError,\n onUpdateUser,\n teams = [],\n currentCustomer,\n isTeamsLoading = false,\n onTeamSwitch,\n notifications = [],\n isNotificationsLoading = false,\n onToggleNotification,\n primaryColor,\n}: UserSettingsProps) => {\n if (userError) {\n return (\n <div className=\"p-8\">\n <div className=\"text-red-600\">\n Failed to load user settings. {userError?.message} Please try again later.\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"w-full space-y-6\">\n <header>\n <h1 className=\"text-2xl font-semibold text-gray-900\">User Settings</h1>\n </header>\n\n <div className=\"space-y-6\">\n <ProfileSection\n user={user}\n isLoading={isUserLoading}\n isUpdating={isUpdating}\n error={userError}\n updateError={updateError}\n onSave={onUpdateUser}\n primaryColor={primaryColor}\n />\n\n <TeamsSection\n teams={teams}\n currentCustomer={currentCustomer}\n isLoading={isTeamsLoading}\n onTeamSwitch={onTeamSwitch}\n primaryColor={primaryColor}\n />\n\n {currentCustomer && (\n <EmailNotificationsSection\n teamName={currentCustomer.name}\n notifications={notifications}\n isLoading={isNotificationsLoading}\n onToggle={onToggleNotification}\n />\n )}\n </div>\n </div>\n );\n};\n\nUserSettings.displayName = \"UserSettings\";\n"]}
1
+ {"version":3,"sources":["../src/utils/constants.ts","../src/components/user-settings.tsx"],"names":["jsxs","jsx","useMemo","useState"],"mappings":"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","file":"user-settings.js","sourcesContent":["/**\n * Default globe favicon matching the CiGlobe icon (Circum Icons)\n * Used as fallback when custom branding doesn't provide a favicon\n */\nexport const DEFAULT_FAVICON = \"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%239ca3af' stroke-width='1.5'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cellipse cx='12' cy='12' rx='4' ry='10'/%3E%3Cpath d='M2 12h20'/%3E%3C/svg%3E\";\n\n/**\n * Default primary brand color\n */\nexport const DEFAULT_PRIMARY_COLOR = \"#4f46e5\";\n\n/**\n * Default secondary brand color\n */\nexport const DEFAULT_SECONDARY_COLOR = \"#6366f1\";\n\n/**\n * Check if the API origin is HTTP (used for repldev environments)\n * This determines cookie security settings for cross-origin iframes\n * @returns true if the API origin starts with http:// (not https://)\n */\nexport const isHttpApiOrigin = (): boolean => {\n return process.env.REPLICATED_APP_ORIGIN?.startsWith('http://') || false;\n};\n","\"use client\";\n\nimport { useState, useMemo } from \"react\";\nimport { DEFAULT_SECONDARY_COLOR } from \"../utils/constants\";\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface UserSettingsUser {\n emailAddress: string;\n firstName?: string;\n lastName?: string;\n}\n\nexport interface UserSettingsTeam {\n id: string;\n name: string;\n appId: string;\n appName: string;\n appSlug: string;\n}\n\nexport interface UserSettingsCustomer {\n id: string;\n name: string;\n}\n\nexport interface UserSettingsNotification {\n type: string;\n enabled: boolean;\n}\n\nexport interface UserSettingsProps {\n // User profile\n user?: UserSettingsUser;\n isUserLoading?: boolean;\n userError?: Error | null;\n \n // Profile editing\n isUpdating?: boolean;\n updateError?: Error | null;\n onUpdateUser?: (data: { firstName?: string; lastName?: string }) => Promise<void>;\n \n // Teams\n teams?: UserSettingsTeam[];\n currentCustomer?: UserSettingsCustomer;\n isTeamsLoading?: boolean;\n onTeamSwitch?: (team: UserSettingsTeam) => Promise<void>;\n \n // Notifications\n notifications?: UserSettingsNotification[];\n isNotificationsLoading?: boolean;\n onToggleNotification?: (notificationType: string, enabled: boolean) => Promise<void>;\n \n // Optional branding\n primaryColor?: string;\n}\n\n// =============================================================================\n// Notification descriptions\n// =============================================================================\n\nconst NOTIFICATION_DESCRIPTIONS: Record<string, string> = {\n \"new-version\": \"New version available\",\n};\n\n// =============================================================================\n// Email Notifications Section\n// =============================================================================\n\ninterface EmailNotificationsSectionProps {\n teamName: string;\n notifications: UserSettingsNotification[];\n isLoading: boolean;\n onToggle?: (type: string, enabled: boolean) => Promise<void>;\n}\n\nconst EmailNotificationsSection = ({\n teamName,\n notifications,\n isLoading,\n onToggle,\n}: EmailNotificationsSectionProps) => {\n // Merge API data with all notification types to ensure all options are always shown\n const allNotificationTypes = Object.keys(NOTIFICATION_DESCRIPTIONS);\n const mergedNotifications = allNotificationTypes.map((type) => {\n const existing = notifications.find((n) => n.type === type);\n return {\n type,\n enabled: existing ? existing.enabled : type === \"new-version\", // Default new-version to true\n };\n });\n\n if (isLoading) {\n return (\n <div className=\"rounded-2xl border border-gray-200 bg-white p-6 shadow-sm\">\n <h2 className=\"text-lg font-semibold text-gray-900\">\n Email Notifications for {teamName}\n </h2>\n <p className=\"mt-1 text-sm text-gray-500\">\n Choose which email notifications you want to receive for this team.\n </p>\n <div className=\"mt-6 flex justify-center py-8\">\n <p className=\"text-sm text-gray-500\">Loading notifications...</p>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"rounded-2xl border border-gray-200 bg-white p-6 shadow-sm\">\n <h2 className=\"text-lg font-semibold text-gray-900\">\n Email Notifications for {teamName}\n </h2>\n <p className=\"mt-1 text-sm text-gray-500\">\n Choose which email notifications you want to receive for this team.\n </p>\n\n <div className=\"mt-6 overflow-hidden rounded-lg border border-gray-200\">\n <div className=\"flex items-center justify-between bg-gray-50 px-4 py-2\">\n <span className=\"text-xs font-medium uppercase text-gray-600\">\n Description\n </span>\n <span className=\"text-xs font-medium uppercase text-gray-600\">\n Enabled\n </span>\n </div>\n {mergedNotifications.map((notification) => (\n <div\n key={notification.type}\n className=\"flex items-center justify-between border-t border-gray-200 px-4 py-3\"\n >\n <span className=\"text-sm text-gray-900\">\n {NOTIFICATION_DESCRIPTIONS[notification.type]}\n </span>\n <input\n type=\"checkbox\"\n checked={notification.enabled}\n onChange={() => onToggle?.(notification.type, !notification.enabled)}\n className=\"portal-checkbox\"\n />\n </div>\n ))}\n </div>\n <p className=\"mt-3 text-xs text-gray-500\">\n These notification preferences are specific to the {teamName} team.\n Switch teams to manage notifications for other teams.\n </p>\n </div>\n );\n};\n\n// =============================================================================\n// Teams Section\n// =============================================================================\n\ninterface TeamsSectionProps {\n teams: UserSettingsTeam[];\n currentCustomer?: UserSettingsCustomer;\n isLoading: boolean;\n onTeamSwitch?: (team: UserSettingsTeam) => Promise<void>;\n primaryColor?: string;\n}\n\nconst TeamsSection = ({\n teams,\n currentCustomer,\n isLoading,\n onTeamSwitch,\n primaryColor,\n}: TeamsSectionProps) => {\n // Group teams by app\n const groupedTeams = useMemo(() => {\n const groups = new Map<string, { appName: string; teams: UserSettingsTeam[] }>();\n \n for (const team of teams) {\n const existing = groups.get(team.appId);\n if (existing) {\n existing.teams.push(team);\n } else {\n groups.set(team.appId, {\n appName: team.appName,\n teams: [team],\n });\n }\n }\n \n return groups;\n }, [teams]);\n\n if (isLoading) {\n return (\n <div className=\"rounded-2xl border border-gray-200 bg-white p-6 shadow-sm\">\n <h2 className=\"text-lg font-semibold text-gray-900\">Teams</h2>\n <p className=\"mt-1 text-sm text-gray-500\">\n Teams associate your email address with any additional licenses your\n account has access to.\n </p>\n <div className=\"mt-6\">\n <p className=\"text-sm text-gray-500\">Loading teams...</p>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"rounded-2xl border border-gray-200 bg-white p-6 shadow-sm\">\n <h2 className=\"text-lg font-semibold text-gray-900\">Teams</h2>\n <p className=\"mt-1 text-sm text-gray-500\">\n Teams associate your email address with any additional licenses your\n account has access to.\n </p>\n\n <div className=\"mt-6 space-y-6\">\n {Array.from(groupedTeams.values()).map((appGroup) => (\n <div key={appGroup.appName} className=\"space-y-3\">\n <h3 className=\"text-base font-medium text-gray-900\">\n {appGroup.appName}\n </h3>\n <div className=\"space-y-2\">\n {appGroup.teams.map((team) => {\n const isCurrentTeam = currentCustomer?.id === team.id;\n return (\n <button\n key={team.id}\n type=\"button\"\n onClick={() => !isCurrentTeam && onTeamSwitch?.(team)}\n disabled={isCurrentTeam}\n className={`flex w-full items-center justify-between rounded-lg border p-4 text-left transition ${\n isCurrentTeam\n ? \"cursor-default border-gray-300 bg-gray-50\"\n : \"cursor-pointer border-gray-200 hover:border-gray-400\"\n }`}\n style={\n !isCurrentTeam && primaryColor\n ? { \"--hover-border\": primaryColor } as React.CSSProperties\n : undefined\n }\n >\n <div className=\"flex items-center gap-3\">\n <svg\n className=\"h-4 w-4\"\n style={{ color: primaryColor || DEFAULT_SECONDARY_COLOR }}\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n strokeWidth={2}\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n 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\"\n />\n </svg>\n <div>\n <p className=\"text-sm font-medium text-gray-900\">\n {team.name}\n </p>\n {isCurrentTeam && (\n <p className=\"text-xs text-gray-500\">Current Team</p>\n )}\n </div>\n </div>\n {!isCurrentTeam && (\n <svg\n className=\"h-4 w-4\"\n style={{ color: primaryColor || DEFAULT_SECONDARY_COLOR }}\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n strokeWidth={2}\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n d=\"M9 5l7 7-7 7\"\n />\n </svg>\n )}\n </button>\n );\n })}\n </div>\n </div>\n ))}\n </div>\n </div>\n );\n};\n\n// =============================================================================\n// Profile Section\n// =============================================================================\n\ninterface ProfileSectionProps {\n user?: UserSettingsUser;\n isLoading: boolean;\n isUpdating: boolean;\n error?: Error | null;\n updateError?: Error | null;\n onSave?: (data: { firstName?: string; lastName?: string }) => Promise<void>;\n primaryColor?: string;\n}\n\nconst ProfileSection = ({\n user,\n isLoading,\n isUpdating,\n error,\n updateError,\n onSave,\n primaryColor,\n}: ProfileSectionProps) => {\n const [isEditing, setIsEditing] = useState(false);\n const [firstName, setFirstName] = useState(user?.firstName || \"\");\n const [lastName, setLastName] = useState(user?.lastName || \"\");\n const [inputError, setInputError] = useState<string | null>(null);\n\n // Reset form when user changes\n const resetForm = () => {\n setFirstName(user?.firstName || \"\");\n setLastName(user?.lastName || \"\");\n setInputError(null);\n };\n\n const handleSave = async () => {\n if (!firstName && !lastName) {\n setInputError(\"Please enter a first name or last name\");\n return;\n }\n\n if (firstName === user?.firstName && lastName === user?.lastName) {\n setIsEditing(false);\n return;\n }\n\n try {\n await onSave?.({ firstName, lastName });\n setIsEditing(false);\n } catch (err) {\n console.error(\"Error updating user information:\", err);\n }\n };\n\n const handleCancel = () => {\n setIsEditing(false);\n resetForm();\n };\n\n const handleEdit = () => {\n resetForm();\n setIsEditing(true);\n };\n\n const buttonStyle = {\n backgroundColor: primaryColor || DEFAULT_SECONDARY_COLOR,\n borderColor: primaryColor || DEFAULT_SECONDARY_COLOR\n };\n\n return (\n <div className=\"rounded-2xl border border-gray-200 bg-white p-6 shadow-sm\">\n <div className=\"flex items-start justify-between gap-4\">\n <div>\n <h2 className=\"text-lg font-semibold text-gray-900\">\n Profile Information\n </h2>\n <p className=\"mt-1 text-sm text-gray-500\">Your account information.</p>\n {(error || updateError) && (\n <p className=\"mt-2 text-sm text-red-600\">\n {error?.message || updateError?.message || \"Failed to update profile\"}\n </p>\n )}\n {inputError && (\n <p className=\"mt-2 text-sm text-red-600\">{inputError}</p>\n )}\n </div>\n {isEditing ? (\n <div className=\"flex gap-2\">\n <button\n type=\"button\"\n onClick={handleCancel}\n disabled={isUpdating}\n 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\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={handleSave}\n disabled={isUpdating}\n style={buttonStyle}\n className=\"inline-flex items-center justify-center rounded-md px-4 py-2 text-sm font-medium text-white shadow-sm transition-opacity duration-200 hover:opacity-90 disabled:opacity-50 min-w-[70px]\"\n >\n {isUpdating ? \"Saving...\" : \"Save\"}\n </button>\n </div>\n ) : (\n <button\n type=\"button\"\n onClick={handleEdit}\n style={buttonStyle}\n className=\"inline-flex items-center justify-center rounded-md px-4 py-2 text-sm font-medium text-white shadow-sm transition-opacity duration-200 hover:opacity-90 min-w-[70px]\"\n >\n Edit\n </button>\n )}\n </div>\n\n <div className=\"mt-6 flex gap-8\">\n {isEditing ? (\n <div className=\"flex gap-4\">\n <div>\n <label className=\"block text-sm font-medium text-gray-600\">\n First Name\n </label>\n <input\n type=\"text\"\n value={firstName}\n onChange={(e) => {\n setInputError(null);\n setFirstName(e.target.value);\n }}\n disabled={isUpdating}\n placeholder=\"First Name\"\n className=\"portal-input mt-1 block w-48\"\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-gray-600\">\n Last Name\n </label>\n <input\n type=\"text\"\n value={lastName}\n onChange={(e) => {\n setInputError(null);\n setLastName(e.target.value);\n }}\n disabled={isUpdating}\n placeholder=\"Last Name\"\n className=\"portal-input mt-1 block w-48\"\n />\n </div>\n </div>\n ) : (\n <div className=\"flex-1\">\n <label className=\"block text-sm font-medium text-gray-600\">\n Full Name (optional)\n </label>\n <p className=\"mt-1 text-sm text-gray-900\">\n {user?.firstName || user?.lastName\n ? `${user.firstName || \"\"} ${user.lastName || \"\"}`.trim()\n : \"—\"}\n </p>\n </div>\n )}\n <div className=\"flex-1\">\n <label className=\"block text-sm font-medium text-gray-600\">Email</label>\n <p className=\"mt-1 text-sm text-gray-500\">\n {isLoading ? \"Loading...\" : user?.emailAddress || \"\"}\n </p>\n </div>\n </div>\n </div>\n );\n};\n\n// =============================================================================\n// Main Component\n// =============================================================================\n\nexport const UserSettings = ({\n user,\n isUserLoading = false,\n userError,\n isUpdating = false,\n updateError,\n onUpdateUser,\n teams = [],\n currentCustomer,\n isTeamsLoading = false,\n onTeamSwitch,\n notifications = [],\n isNotificationsLoading = false,\n onToggleNotification,\n primaryColor,\n}: UserSettingsProps) => {\n if (userError) {\n return (\n <div className=\"p-8\">\n <div className=\"text-red-600\">\n Failed to load user settings. {userError?.message} Please try again later.\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"w-full space-y-6\">\n <header>\n <h1 className=\"text-2xl font-semibold text-gray-900\">User Settings</h1>\n </header>\n\n <div className=\"space-y-6\">\n <ProfileSection\n user={user}\n isLoading={isUserLoading}\n isUpdating={isUpdating}\n error={userError}\n updateError={updateError}\n onSave={onUpdateUser}\n primaryColor={primaryColor}\n />\n\n <TeamsSection\n teams={teams}\n currentCustomer={currentCustomer}\n isLoading={isTeamsLoading}\n onTeamSwitch={onTeamSwitch}\n primaryColor={primaryColor}\n />\n\n {currentCustomer && (\n <EmailNotificationsSection\n teamName={currentCustomer.name}\n notifications={notifications}\n isLoading={isNotificationsLoading}\n onToggle={onToggleNotification}\n />\n )}\n </div>\n </div>\n );\n};\n\nUserSettings.displayName = \"UserSettings\";\n"]}
@@ -1,3 +1,11 @@
1
+ export { d as decodeBranding, n as normalizeColor, s as sanitizeUrlForCss } from '../branding-BsMSywts.mjs';
2
+ import { a1 as DownloadPortalRelease } from '../index-DyzJ0yKD.mjs';
3
+ import { ReleaseEntry } from '../release-history-panel.mjs';
4
+ import '../fetch-license-iTyF7_GY.mjs';
5
+ import '../actions/change-team.mjs';
6
+ import '../actions/trial-signup.mjs';
7
+ import 'react/jsx-runtime';
8
+
1
9
  /**
2
10
  * Centralized API client utility for handling authenticated requests
3
11
  * with automatic 401 detection, cookie deletion, and redirect.
@@ -25,21 +33,6 @@ declare function isRedirectError(error: unknown): boolean;
25
33
  */
26
34
  declare function authenticatedFetch(url: string, options?: ApiFetchOptions): Promise<Response>;
27
35
 
28
- interface RawBranding {
29
- brandingData?: string;
30
- }
31
- declare const decodeBranding: ({ brandingData }: RawBranding) => {
32
- logo?: undefined;
33
- title?: undefined;
34
- customColor1?: undefined;
35
- customColor2?: undefined;
36
- } | {
37
- logo: string | undefined;
38
- title: string | undefined;
39
- customColor1: string | undefined;
40
- customColor2: string | undefined;
41
- };
42
-
43
36
  /**
44
37
  * Session validation utilities for handling authentication tokens.
45
38
  */
@@ -67,4 +60,69 @@ declare function validateSession(token: string): Promise<boolean>;
67
60
  */
68
61
  declare function deleteSessionCookie(): Promise<void>;
69
62
 
70
- export { UnauthorizedError, authenticatedFetch, decodeBranding, deleteSessionCookie, isRedirectError, validateSession };
63
+ /**
64
+ * Format bytes to human-readable string
65
+ * @param bytes - Number of bytes
66
+ * @param decimals - Number of decimal places (default: 1)
67
+ * @returns Formatted string (e.g., "1.5 MB")
68
+ */
69
+ declare function formatBytes(bytes: number, decimals?: number): string;
70
+ /**
71
+ * Format date string to short numeric date (no time)
72
+ * @param dateString - ISO date string or null/undefined
73
+ * @returns Formatted string (e.g., "01/27/2026") or "Never" if no date
74
+ */
75
+ declare function formatDateShort(dateString?: string | null): string;
76
+ /**
77
+ * Format date string to human-readable local timestamp
78
+ * @param dateString - ISO date string or null/undefined
79
+ * @returns Formatted string (e.g., "Jan 27, 2026, 10:32 PM") or "Never" if no date
80
+ */
81
+ declare function formatDate(dateString?: string | null): string;
82
+ /**
83
+ * Format date string to human-readable UTC timestamp
84
+ * @param dateString - ISO date string
85
+ * @returns Formatted string (e.g., "01/27/2026, 22:32:39 UTC")
86
+ */
87
+ declare function formatDateTime(dateString: string): string;
88
+ /**
89
+ * Format date string to user-friendly local timestamp with 12-hour time
90
+ * @param dateString - ISO date string or null/undefined
91
+ * @returns Formatted string (e.g., "1/27/2026, 10:32 PM") or "N/A" if not provided
92
+ */
93
+ declare function formatDateTimeLocal(dateString?: string | null): string;
94
+
95
+ type ReleaseNotesMode = "markdown" | "items";
96
+ interface ConvertToReleaseEntryOptions {
97
+ releaseNotesMode: ReleaseNotesMode;
98
+ }
99
+ /**
100
+ * Converts a DownloadPortalRelease to a ReleaseEntry for display
101
+ * @param release - The release data from the API
102
+ * @param channelName - The channel name for display
103
+ * @param options - Options for how to format the release entry
104
+ * @returns A ReleaseEntry ready for display
105
+ */
106
+ declare function convertToReleaseEntry(release: DownloadPortalRelease, channelName: string, options: ConvertToReleaseEntryOptions): ReleaseEntry;
107
+
108
+ /**
109
+ * Default globe favicon matching the CiGlobe icon (Circum Icons)
110
+ * Used as fallback when custom branding doesn't provide a favicon
111
+ */
112
+ declare const DEFAULT_FAVICON = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%239ca3af' stroke-width='1.5'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cellipse cx='12' cy='12' rx='4' ry='10'/%3E%3Cpath d='M2 12h20'/%3E%3C/svg%3E";
113
+ /**
114
+ * Default primary brand color
115
+ */
116
+ declare const DEFAULT_PRIMARY_COLOR = "#4f46e5";
117
+ /**
118
+ * Default secondary brand color
119
+ */
120
+ declare const DEFAULT_SECONDARY_COLOR = "#6366f1";
121
+ /**
122
+ * Check if the API origin is HTTP (used for repldev environments)
123
+ * This determines cookie security settings for cross-origin iframes
124
+ * @returns true if the API origin starts with http:// (not https://)
125
+ */
126
+ declare const isHttpApiOrigin: () => boolean;
127
+
128
+ export { type ConvertToReleaseEntryOptions, DEFAULT_FAVICON, DEFAULT_PRIMARY_COLOR, DEFAULT_SECONDARY_COLOR, type ReleaseNotesMode, UnauthorizedError, authenticatedFetch, convertToReleaseEntry, deleteSessionCookie, formatBytes, formatDate, formatDateShort, formatDateTime, formatDateTimeLocal, isHttpApiOrigin, isRedirectError, validateSession };