@burdenoff/microfe-auth 2026.601.5 → 2026.608.1

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.
@@ -3,38 +3,39 @@ import { Suspense as n, lazy as r, useCallback as i, useState as a } from "react
3
3
  import { useI18n as o } from "@burdenoff/fe-libs/shared/providers/shell";
4
4
  import { jsx as s } from "react/jsx-runtime";
5
5
  //#region src/auth/components/CaptchaV2.tsx
6
- var c = r(() => import("../../node_modules/react-google-recaptcha/lib/esm/index.js").then((t) => ({ default: function({ onVerify: n, onExpired: r }) {
7
- let o = t.default, [, c] = a(null), l = e(), u = i((e) => {
6
+ var c = r(() => import("../../node_modules/react-google-recaptcha/lib/esm/index.js").then((t) => ({ default: function({ onVerify: n, onExpired: r, siteKey: o }) {
7
+ let c = t.default, [, l] = a(null), u = o ?? e(), d = i((e) => {
8
8
  n(e);
9
- }, [n]), d = i(() => {
9
+ }, [n]), f = i(() => {
10
10
  n(null), r?.();
11
11
  }, [n, r]);
12
- return l ? /* @__PURE__ */ s("div", {
12
+ return u ? /* @__PURE__ */ s("div", {
13
13
  className: "flex justify-center",
14
- children: /* @__PURE__ */ s(o, {
15
- ref: c,
16
- sitekey: l,
17
- onChange: u,
18
- onExpired: d
14
+ children: /* @__PURE__ */ s(c, {
15
+ ref: l,
16
+ sitekey: u,
17
+ onChange: d,
18
+ onExpired: f
19
19
  })
20
20
  }) : /* @__PURE__ */ s("div", {
21
21
  className: "rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text",
22
22
  children: "reCAPTCHA site key is not configured."
23
23
  });
24
24
  } })));
25
- function l({ onVerify: e, onExpired: r }) {
26
- let { t: i } = o();
27
- return t() ? /* @__PURE__ */ s(n, {
25
+ function l({ onVerify: e, onExpired: r, enabled: i, siteKey: a }) {
26
+ let { t: l } = o();
27
+ return i ?? t() ? /* @__PURE__ */ s(n, {
28
28
  fallback: /* @__PURE__ */ s("div", {
29
29
  className: "flex justify-center py-2",
30
30
  children: ((e, t) => {
31
- let n = i(e);
31
+ let n = l(e);
32
32
  return n === e ? t : n;
33
33
  })("auth.captcha.loading", "Loading captcha...")
34
34
  }),
35
35
  children: /* @__PURE__ */ s(c, {
36
36
  onVerify: e,
37
- onExpired: r
37
+ onExpired: r,
38
+ siteKey: a
38
39
  })
39
40
  }) : null;
40
41
  }
@@ -1 +1 @@
1
- {"version":3,"file":"CaptchaV2.js","names":[],"sources":["../../../src/auth/components/CaptchaV2.tsx"],"sourcesContent":["import { lazy, Suspense, useState, useCallback } from \"react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell\";\nimport {\n getRecaptchaSiteKey,\n isCaptchaEnabled as getIsCaptchaEnabled,\n} from \"../utils/captchaConfig\";\n\ninterface CaptchaV2Props {\n onVerify: (token: string | null) => void;\n onExpired?: () => void;\n}\n\n/**\n * Lazily loaded inner component that imports react-google-recaptcha.\n * Only loaded when CAPTCHA_ENABLED is true, avoiding import resolution\n * errors in dev environments where the package may not be installed.\n */\nconst LazyCaptchaWidget = lazy(() =>\n import(\"react-google-recaptcha\").then((mod) => ({\n default: function CaptchaWidget({ onVerify, onExpired }: CaptchaV2Props) {\n const ReCAPTCHA = mod.default;\n const [, setRef] = useState<InstanceType<typeof ReCAPTCHA> | null>(null);\n const siteKey = getRecaptchaSiteKey();\n\n const forwardCaptchaToken = useCallback(\n (token: string | null) => {\n onVerify(token);\n },\n [onVerify],\n );\n\n const clearCaptchaToken = useCallback(() => {\n onVerify(null);\n onExpired?.();\n }, [onVerify, onExpired]);\n\n if (!siteKey) {\n return (\n <div className=\"rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n reCAPTCHA site key is not configured.\n </div>\n );\n }\n\n return (\n <div className=\"flex justify-center\">\n <ReCAPTCHA\n ref={setRef}\n sitekey={siteKey}\n onChange={forwardCaptchaToken}\n onExpired={clearCaptchaToken}\n />\n </div>\n );\n },\n })),\n);\n\n/**\n * reCAPTCHA v2 checkbox component.\n * Renders nothing when VITE_CAPTCHA_ENABLED is not \"true\".\n * Lazily loads the Google reCAPTCHA library only when enabled.\n */\nexport function CaptchaV2({ onVerify, onExpired }: CaptchaV2Props) {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n if (!getIsCaptchaEnabled()) {\n return null;\n }\n\n return (\n <Suspense\n fallback={\n <div className=\"flex justify-center py-2\">\n {tr(\"auth.captcha.loading\", \"Loading captcha...\")}\n </div>\n }\n >\n <LazyCaptchaWidget onVerify={onVerify} onExpired={onExpired} />\n </Suspense>\n );\n}\n"],"mappings":";;;;;AAiBA,IAAM,IAAoB,QACxB,OAAO,8DAA0B,MAAM,OAAS,EAC9C,SAAS,SAAuB,EAAE,aAAU,gBAA6B;CACvE,IAAM,IAAY,EAAI,SAChB,GAAG,KAAU,EAAgD,KAAK,EAClE,IAAU,GAAqB,EAE/B,IAAsB,GACzB,MAAyB;AACxB,IAAS,EAAM;IAEjB,CAAC,EAAS,CACX,EAEK,IAAoB,QAAkB;AAE1C,EADA,EAAS,KAAK,EACd,KAAa;IACZ,CAAC,GAAU,EAAU,CAAC;AAUzB,QARK,IASH,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,GAAD;GACE,KAAK;GACL,SAAS;GACT,UAAU;GACV,WAAW;GACX,CAAA;EACE,CAAA,GAdJ,kBAAC,OAAD;EAAK,WAAU;YAAgF;EAEzF,CAAA;GAeb,EAAE,CACJ;AAOD,SAAgB,EAAU,EAAE,aAAU,gBAA6B;CACjE,IAAM,EAAE,SAAM,GAAS;AASvB,QAJK,GAAqB,GAKxB,kBAAC,GAAD;EACE,UACE,kBAAC,OAAD;GAAK,WAAU;eAXT,GAAa,MAA6B;IACpD,IAAM,IAAa,EAAE,EAAI;AACzB,WAAO,MAAe,IAAM,IAAW;MAU7B,wBAAwB,qBAAqB;GAC7C,CAAA;YAGR,kBAAC,GAAD;GAA6B;GAAqB;GAAa,CAAA;EACtD,CAAA,GAZJ"}
1
+ {"version":3,"file":"CaptchaV2.js","names":[],"sources":["../../../src/auth/components/CaptchaV2.tsx"],"sourcesContent":["import { lazy, Suspense, useState, useCallback } from \"react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell\";\nimport {\n getRecaptchaSiteKey,\n isCaptchaEnabled as getIsCaptchaEnabled,\n} from \"../utils/captchaConfig\";\n\ninterface CaptchaV2Props {\n onVerify: (token: string | null) => void;\n onExpired?: () => void;\n /**\n * Host-driven enable flag. When provided it takes precedence over the\n * build-time env switch (used so a per-product site key can turn the widget\n * on/off without rebuilding). When omitted, falls back to env.\n */\n enabled?: boolean;\n /**\n * reCAPTCHA v2 site key to render with. Overrides the build-time env key.\n * Resolved per-product by the caller (see `useSignupCaptcha`).\n */\n siteKey?: string;\n}\n\ninterface CaptchaWidgetProps {\n onVerify: (token: string | null) => void;\n onExpired?: () => void;\n siteKey?: string;\n}\n\n/**\n * Lazily loaded inner component that imports react-google-recaptcha.\n * Only loaded when captcha is enabled, avoiding import resolution\n * errors in dev environments where the package may not be installed.\n */\nconst LazyCaptchaWidget = lazy(() =>\n import(\"react-google-recaptcha\").then((mod) => ({\n default: function CaptchaWidget({\n onVerify,\n onExpired,\n siteKey: siteKeyProp,\n }: CaptchaWidgetProps) {\n const ReCAPTCHA = mod.default;\n const [, setRef] = useState<InstanceType<typeof ReCAPTCHA> | null>(null);\n const siteKey = siteKeyProp ?? getRecaptchaSiteKey();\n\n const forwardCaptchaToken = useCallback(\n (token: string | null) => {\n onVerify(token);\n },\n [onVerify],\n );\n\n const clearCaptchaToken = useCallback(() => {\n onVerify(null);\n onExpired?.();\n }, [onVerify, onExpired]);\n\n if (!siteKey) {\n return (\n <div className=\"rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n reCAPTCHA site key is not configured.\n </div>\n );\n }\n\n return (\n <div className=\"flex justify-center\">\n <ReCAPTCHA\n ref={setRef}\n sitekey={siteKey}\n onChange={forwardCaptchaToken}\n onExpired={clearCaptchaToken}\n />\n </div>\n );\n },\n })),\n);\n\n/**\n * reCAPTCHA v2 checkbox component.\n *\n * Enablement is host-driven via the `enabled` prop (resolved per-product from\n * the platform catalog). When `enabled` is omitted it falls back to the\n * build-time `VITE_CAPTCHA_ENABLED` env for local source-dev. Renders nothing\n * when disabled and lazily loads the Google reCAPTCHA library only when enabled.\n */\nexport function CaptchaV2({\n onVerify,\n onExpired,\n enabled,\n siteKey,\n}: CaptchaV2Props) {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n\n const isEnabled = enabled ?? getIsCaptchaEnabled();\n if (!isEnabled) {\n return null;\n }\n\n return (\n <Suspense\n fallback={\n <div className=\"flex justify-center py-2\">\n {tr(\"auth.captcha.loading\", \"Loading captcha...\")}\n </div>\n }\n >\n <LazyCaptchaWidget\n onVerify={onVerify}\n onExpired={onExpired}\n siteKey={siteKey}\n />\n </Suspense>\n );\n}\n"],"mappings":";;;;;AAkCA,IAAM,IAAoB,QACxB,OAAO,8DAA0B,MAAM,OAAS,EAC9C,SAAS,SAAuB,EAC9B,aACA,cACA,SAAS,KACY;CACrB,IAAM,IAAY,EAAI,SAChB,GAAG,KAAU,EAAgD,KAAK,EAClE,IAAU,KAAe,GAAqB,EAE9C,IAAsB,GACzB,MAAyB;AACxB,IAAS,EAAM;IAEjB,CAAC,EAAS,CACX,EAEK,IAAoB,QAAkB;AAE1C,EADA,EAAS,KAAK,EACd,KAAa;IACZ,CAAC,GAAU,EAAU,CAAC;AAUzB,QARK,IASH,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,GAAD;GACE,KAAK;GACL,SAAS;GACT,UAAU;GACV,WAAW;GACX,CAAA;EACE,CAAA,GAdJ,kBAAC,OAAD;EAAK,WAAU;YAAgF;EAEzF,CAAA;GAeb,EAAE,CACJ;AAUD,SAAgB,EAAU,EACxB,aACA,cACA,YACA,cACiB;CACjB,IAAM,EAAE,SAAM,GAAS;AAWvB,QALkB,KAAW,GAAqB,GAMhD,kBAAC,GAAD;EACE,UACE,kBAAC,OAAD;GAAK,WAAU;eAbT,GAAa,MAA6B;IACpD,IAAM,IAAa,EAAE,EAAI;AACzB,WAAO,MAAe,IAAM,IAAW;MAY7B,wBAAwB,qBAAqB;GAC7C,CAAA;YAGR,kBAAC,GAAD;GACY;GACC;GACF;GACT,CAAA;EACO,CAAA,GAhBJ"}
@@ -0,0 +1,39 @@
1
+ import { useAuth as e } from "../providers/AuthProvider.js";
2
+ import { getRecaptchaSiteKey as t, isCaptchaEnabled as n } from "../utils/captchaConfig.js";
3
+ import { useQuery as r } from "@apollo/client/react";
4
+ import { gql as i } from "@apollo/client";
5
+ //#region src/auth/hooks/useSignupCaptcha.ts
6
+ var a = i`
7
+ query PublicProductCaptcha($slug: String!) {
8
+ publicProduct(slug: $slug) {
9
+ slug
10
+ recaptchaSiteKey
11
+ }
12
+ }
13
+ `;
14
+ function o() {
15
+ let { productSlug: i } = e(), o = i?.trim(), { data: s, loading: c } = r(a, {
16
+ variables: { slug: o },
17
+ skip: !o,
18
+ fetchPolicy: "cache-first",
19
+ errorPolicy: "all"
20
+ });
21
+ if (!o) {
22
+ let e = t(), r = n() && !!e;
23
+ return {
24
+ enabled: r,
25
+ siteKey: r ? e : void 0,
26
+ loading: !1
27
+ };
28
+ }
29
+ let l = s?.publicProduct?.recaptchaSiteKey ?? void 0, u = l && l.trim().length > 0 ? l.trim() : void 0;
30
+ return {
31
+ enabled: !!u,
32
+ siteKey: u,
33
+ loading: c
34
+ };
35
+ }
36
+ //#endregion
37
+ export { o as useSignupCaptcha };
38
+
39
+ //# sourceMappingURL=useSignupCaptcha.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSignupCaptcha.js","names":[],"sources":["../../../src/auth/hooks/useSignupCaptcha.ts"],"sourcesContent":["import { gql } from \"@apollo/client\";\nimport { useQuery } from \"@apollo/client/react\";\nimport { useAuth } from \"../providers/AuthProvider\";\nimport { getRecaptchaSiteKey, isCaptchaEnabled } from \"../utils/captchaConfig\";\n\n/**\n * Public, unauthenticated lookup of a product's reCAPTCHA v2 site key.\n *\n * The site key is intentionally public (it's embedded in the rendered widget);\n * the matching secret key lives only in the platform product catalog and is\n * verified server-side. `publicProduct` is `@rbac(public: true)`, so this runs\n * before the user has signed in.\n */\nconst PUBLIC_PRODUCT_CAPTCHA_QUERY = gql`\n query PublicProductCaptcha($slug: String!) {\n publicProduct(slug: $slug) {\n slug\n recaptchaSiteKey\n }\n }\n`;\n\ninterface PublicProductCaptchaData {\n publicProduct: {\n slug: string;\n recaptchaSiteKey: string | null;\n } | null;\n}\n\nexport interface SignupCaptchaState {\n /** Whether the reCAPTCHA v2 checkbox should be rendered + required on signup. */\n enabled: boolean;\n /** The reCAPTCHA v2 site key to render the widget with (undefined when disabled). */\n siteKey: string | undefined;\n /** True while the per-product config is still being fetched. */\n loading: boolean;\n}\n\n/**\n * Resolve whether the signup page should show the reCAPTCHA v2 checkbox, and\n * with which site key, for the current host product.\n *\n * Source of truth is the platform product catalog: a product opts into signup\n * captcha simply by having a `recaptchaSiteKey` configured (looked up here via\n * the host-provided `productSlug`). This keeps enablement per-product and\n * data-driven — no rebuild needed to turn it on/off for a product.\n *\n * Falls back to the build-time env (`VITE_CAPTCHA_ENABLED` +\n * `VITE_RECAPTCHA_SITE_KEY`) only for local source-dev where no `productSlug`\n * is provided, preserving the previous behavior.\n */\nexport function useSignupCaptcha(): SignupCaptchaState {\n const { productSlug } = useAuth();\n const slug = productSlug?.trim();\n\n const { data, loading } = useQuery<PublicProductCaptchaData>(\n PUBLIC_PRODUCT_CAPTCHA_QUERY,\n {\n variables: { slug },\n skip: !slug,\n // Site keys change rarely; serve from cache and only block once.\n fetchPolicy: \"cache-first\",\n // A lookup failure must never break the signup form — degrade to \"off\".\n errorPolicy: \"all\",\n },\n );\n\n if (!slug) {\n // Source-dev / hosts that don't pass a slug: honor the legacy env switch.\n const envSiteKey = getRecaptchaSiteKey();\n const envEnabled = isCaptchaEnabled() && Boolean(envSiteKey);\n return {\n enabled: envEnabled,\n siteKey: envEnabled ? envSiteKey : undefined,\n loading: false,\n };\n }\n\n const siteKey = data?.publicProduct?.recaptchaSiteKey ?? undefined;\n const trimmedKey =\n siteKey && siteKey.trim().length > 0 ? siteKey.trim() : undefined;\n\n return {\n enabled: Boolean(trimmedKey),\n siteKey: trimmedKey,\n loading,\n };\n}\n"],"mappings":";;;;;AAaA,IAAM,IAA+B,CAAG;;;;;;;;AAsCxC,SAAgB,IAAuC;CACrD,IAAM,EAAE,mBAAgB,GAAS,EAC3B,IAAO,GAAa,MAAM,EAE1B,EAAE,SAAM,eAAY,EACxB,GACA;EACE,WAAW,EAAE,SAAM;EACnB,MAAM,CAAC;EAEP,aAAa;EAEb,aAAa;EACd,CACF;AAED,KAAI,CAAC,GAAM;EAET,IAAM,IAAa,GAAqB,EAClC,IAAa,GAAkB,IAAI,EAAQ;AACjD,SAAO;GACL,SAAS;GACT,SAAS,IAAa,IAAa,KAAA;GACnC,SAAS;GACV;;CAGH,IAAM,IAAU,GAAM,eAAe,oBAAoB,KAAA,GACnD,IACJ,KAAW,EAAQ,MAAM,CAAC,SAAS,IAAI,EAAQ,MAAM,GAAG,KAAA;AAE1D,QAAO;EACL,SAAS,EAAQ;EACjB,SAAS;EACT;EACD"}
@@ -1,138 +1,138 @@
1
1
  import { useAuth as e } from "../providers/AuthProvider.js";
2
- import { isCaptchaEnabled as t } from "../utils/captchaConfig.js";
3
- import { useAuthOperations as n } from "../hooks/useAuthOperations.js";
4
- import { useOAuthOperations as r } from "../hooks/useOAuthOperations.js";
5
- import { AuthLayout as i } from "../components/AuthLayout.js";
6
- import { AlertIcon as a, CheckIcon as ee, EmailIcon as o, EyeIcon as s, EyeOffIcon as c, GitHubIcon as l, GoogleIcon as te, LockIcon as ne, MicrosoftIcon as u, SpinnerIcon as d, UserIcon as f, XIcon as p } from "../components/AuthIcons.js";
7
- import { validateEmail as m, validatePassword as h } from "../utils/validation.js";
8
- import { CaptchaV2 as re } from "../components/CaptchaV2.js";
9
- import { useState as g } from "react";
10
- import { useNavigate as ie } from "react-router-dom";
11
- import { useI18n as ae } from "@burdenoff/fe-libs/shared/providers/shell";
12
- import { Fragment as _, jsx as v, jsxs as y } from "react/jsx-runtime";
2
+ import { useAuthOperations as t } from "../hooks/useAuthOperations.js";
3
+ import { useOAuthOperations as n } from "../hooks/useOAuthOperations.js";
4
+ import { AuthLayout as r } from "../components/AuthLayout.js";
5
+ import { AlertIcon as i, CheckIcon as a, EmailIcon as o, EyeIcon as ee, EyeOffIcon as te, GitHubIcon as s, GoogleIcon as ne, LockIcon as re, MicrosoftIcon as c, SpinnerIcon as l, UserIcon as u, XIcon as d } from "../components/AuthIcons.js";
6
+ import { validateEmail as f, validatePassword as p } from "../utils/validation.js";
7
+ import { CaptchaV2 as m } from "../components/CaptchaV2.js";
8
+ import { useSignupCaptcha as ie } from "../hooks/useSignupCaptcha.js";
9
+ import { useState as h } from "react";
10
+ import { useNavigate as ae } from "react-router-dom";
11
+ import { useI18n as oe } from "@burdenoff/fe-libs/shared/providers/shell";
12
+ import { Fragment as se, jsx as g, jsxs as _ } from "react/jsx-runtime";
13
13
  //#region src/auth/pages/RegisterPage.tsx
14
- function b() {
15
- let { productName: b } = e(), x = ie(), { t: S } = ae(), C = (e, t) => {
16
- let n = S(e);
14
+ function v() {
15
+ let { productName: v } = e(), y = ae(), { t: b } = oe(), x = (e, t) => {
16
+ let n = b(e);
17
17
  return n === e ? t : n;
18
- }, { signUp: w, resendVerification: T, isLoading: E, errorCode: D } = n(), { initiateGoogleOAuth: oe, initiateMicrosoftOAuth: se, isProviderConfigured: O } = r(), [k, A] = g(!1), [j, M] = g({
18
+ }, { signUp: S, resendVerification: C, isLoading: w, errorCode: T } = t(), { initiateGoogleOAuth: ce, initiateMicrosoftOAuth: le, isProviderConfigured: E } = n(), [D, ue] = h(!1), [O, k] = h({
19
19
  email: "",
20
20
  username: "",
21
21
  password: "",
22
22
  firstName: "",
23
23
  lastName: ""
24
- }), [N, P] = g([]), [F, I] = g(!1), [L, R] = g(null), [z, B] = g(null), [V, H] = g(null), [U, W] = g(!1), [G, K] = g(null), q = !!L && D === "ACCOUNT_DEACTIVATED", J = !!L && D === "ACCOUNT_REMOVED", Y = !!L && D === "ACCOUNT_SUSPENDED", X = (() => {
25
- let e = j.password;
24
+ }), [A, j] = h([]), [M, N] = h(!1), [P, F] = h(null), [I, L] = h(null), [R, z] = h(null), [B, V] = h(0), { enabled: H, siteKey: de } = ie(), [U, W] = h(!1), [G, K] = h(null), q = !!P && T === "ACCOUNT_DEACTIVATED", J = !!P && T === "ACCOUNT_REMOVED", Y = !!P && T === "ACCOUNT_SUSPENDED", X = (() => {
25
+ let e = O.password;
26
26
  return [
27
27
  {
28
- label: C("auth.register.pwMin8", "At least 12 characters"),
28
+ label: x("auth.register.pwMin8", "At least 12 characters"),
29
29
  met: e.length >= 12
30
30
  },
31
31
  {
32
- label: C("auth.register.pwUppercase", "One uppercase letter"),
32
+ label: x("auth.register.pwUppercase", "One uppercase letter"),
33
33
  met: /[A-Z]/.test(e)
34
34
  },
35
35
  {
36
- label: C("auth.register.pwLowercase", "One lowercase letter"),
36
+ label: x("auth.register.pwLowercase", "One lowercase letter"),
37
37
  met: /[a-z]/.test(e)
38
38
  },
39
39
  {
40
- label: C("auth.register.pwNumber", "One number"),
40
+ label: x("auth.register.pwNumber", "One number"),
41
41
  met: /\d/.test(e)
42
42
  },
43
43
  {
44
- label: C("auth.register.pwSpecial", "One special character"),
44
+ label: x("auth.register.pwSpecial", "One special character"),
45
45
  met: /[!@#$%^&*(),.?":{}|<>]/.test(e)
46
46
  }
47
47
  ];
48
48
  })(), Z = (() => {
49
49
  let e = X.filter((e) => e.met).length;
50
50
  return e <= 2 ? {
51
- level: C("auth.register.passwordStrengthWeak", "weak"),
51
+ level: x("auth.register.passwordStrengthWeak", "weak"),
52
52
  color: "bg-status-error-bg-subtle0",
53
53
  width: "w-1/4"
54
54
  } : e <= 3 ? {
55
- level: C("auth.register.passwordStrengthFair", "fair"),
55
+ level: x("auth.register.passwordStrengthFair", "fair"),
56
56
  color: "bg-status-warning-text",
57
57
  width: "w-2/4"
58
58
  } : e <= 4 ? {
59
- level: C("auth.register.passwordStrengthGood", "good"),
59
+ level: x("auth.register.passwordStrengthGood", "good"),
60
60
  color: "bg-status-warning-bg-subtle0",
61
61
  width: "w-3/4"
62
62
  } : {
63
- level: C("auth.register.passwordStrengthStrong", "strong"),
63
+ level: x("auth.register.passwordStrengthStrong", "strong"),
64
64
  color: "bg-status-success-text",
65
65
  width: "w-full"
66
66
  };
67
- })(), ce = async (e) => {
67
+ })(), fe = async (e) => {
68
68
  e.preventDefault();
69
- let n = [];
70
- m(j.email) || n.push(C("auth.register.invalidEmail", "Please enter a valid email address")), (!j.username || j.username.length < 3) && n.push(C("auth.register.usernameMin3", "Username must be at least 3 characters")), /^[a-zA-Z0-9_-]+$/.test(j.username) || n.push(C("auth.register.usernameChars", "Username can only contain letters, numbers, underscores, and hyphens"));
71
- let r = h(j.password);
72
- if (r.valid || n.push(...r.errors), t() && !V && n.push(C("auth.register.completeCaptcha", "Please complete the reCAPTCHA verification")), n.length > 0) {
73
- P(n);
69
+ let t = [];
70
+ f(O.email) || t.push(x("auth.register.invalidEmail", "Please enter a valid email address")), (!O.username || O.username.length < 3) && t.push(x("auth.register.usernameMin3", "Username must be at least 3 characters")), /^[a-zA-Z0-9_-]+$/.test(O.username) || t.push(x("auth.register.usernameChars", "Username can only contain letters, numbers, underscores, and hyphens"));
71
+ let n = p(O.password);
72
+ if (n.valid || t.push(...n.errors), H && !R && t.push(x("auth.register.completeCaptcha", "Please complete the reCAPTCHA verification")), t.length > 0) {
73
+ j(t);
74
74
  return;
75
75
  }
76
- P([]), R(null);
76
+ j([]), F(null);
77
77
  try {
78
- await w({
79
- ...j,
80
- ...V ? { recaptchaToken: V } : {}
81
- }), I(!0);
78
+ await S({
79
+ ...O,
80
+ ...R ? { recaptchaToken: R } : {}
81
+ }), N(!0);
82
82
  } catch (e) {
83
- R(e instanceof Error ? e.message : C("auth.register.signUpFailed", "Sign up failed")), console.error("Sign up failed:", e);
83
+ F(e instanceof Error ? e.message : x("auth.register.signUpFailed", "Sign up failed")), console.error("Sign up failed:", e), H && (z(null), V((e) => e + 1));
84
84
  }
85
- }, le = () => {
86
- B(null);
85
+ }, pe = () => {
86
+ L(null);
87
87
  try {
88
- oe();
88
+ ce();
89
89
  } catch (e) {
90
- B(e instanceof Error ? e.message : C("auth.register.googleSignUpFailed", "Failed to initiate Google sign up"));
90
+ L(e instanceof Error ? e.message : x("auth.register.googleSignUpFailed", "Failed to initiate Google sign up"));
91
91
  }
92
- }, ue = () => {
93
- B(null);
92
+ }, me = () => {
93
+ L(null);
94
94
  try {
95
- se();
95
+ le();
96
96
  } catch (e) {
97
- B(e instanceof Error ? e.message : C("auth.register.microsoftSignUpFailed", "Failed to initiate Microsoft sign up"));
97
+ L(e instanceof Error ? e.message : x("auth.register.microsoftSignUpFailed", "Failed to initiate Microsoft sign up"));
98
98
  }
99
- }, Q = O("google"), $ = O("microsoft"), de = [
99
+ }, Q = E("google"), $ = E("microsoft"), he = [
100
100
  {
101
101
  name: "Google",
102
- icon: te,
103
- onClick: le,
102
+ icon: ne,
103
+ onClick: pe,
104
104
  disabled: !Q,
105
- tooltip: Q ? void 0 : C("auth.register.googleNotConfigured", "Google sign up is not configured")
105
+ tooltip: Q ? void 0 : x("auth.register.googleNotConfigured", "Google sign up is not configured")
106
106
  },
107
107
  {
108
108
  name: "GitHub",
109
- icon: l,
109
+ icon: s,
110
110
  onClick: () => {},
111
111
  disabled: !0,
112
- tooltip: C("auth.register.githubNotConfigured", "GitHub sign up is not configured")
112
+ tooltip: x("auth.register.githubNotConfigured", "GitHub sign up is not configured")
113
113
  },
114
114
  {
115
115
  name: "Microsoft",
116
- icon: u,
117
- onClick: ue,
116
+ icon: c,
117
+ onClick: me,
118
118
  disabled: !$,
119
- tooltip: $ ? void 0 : C("auth.register.microsoftNotConfigured", "Microsoft sign up is not configured")
119
+ tooltip: $ ? void 0 : x("auth.register.microsoftNotConfigured", "Microsoft sign up is not configured")
120
120
  }
121
- ], [fe, pe = ""] = C("auth.register.verificationEmailSentTo", "We've sent a verification email to {{email}}. Please check your inbox and click the verification link to activate your account.").split("{{email}}");
122
- return F ? /* @__PURE__ */ v(i, {
123
- title: C("auth.register.checkEmail", "Check your email"),
124
- subtitle: C("auth.register.verificationLinkSent", "We've sent you a verification link"),
125
- children: /* @__PURE__ */ y("div", {
121
+ ], [ge, _e = ""] = x("auth.register.verificationEmailSentTo", "We've sent a verification email to {{email}}. Please check your inbox and click the verification link to activate your account.").split("{{email}}");
122
+ return M ? /* @__PURE__ */ g(r, {
123
+ title: x("auth.register.checkEmail", "Check your email"),
124
+ subtitle: x("auth.register.verificationLinkSent", "We've sent you a verification link"),
125
+ children: /* @__PURE__ */ _("div", {
126
126
  className: "flex flex-col items-center justify-center py-6",
127
127
  children: [
128
- /* @__PURE__ */ v("div", {
128
+ /* @__PURE__ */ g("div", {
129
129
  className: "size-20 rounded-full bg-status-success-bg-subtle flex items-center justify-center mb-6",
130
- children: /* @__PURE__ */ v("svg", {
130
+ children: /* @__PURE__ */ g("svg", {
131
131
  className: "size-10 text-status-success-text",
132
132
  fill: "none",
133
133
  viewBox: "0 0 24 24",
134
134
  stroke: "currentColor",
135
- children: /* @__PURE__ */ v("path", {
135
+ children: /* @__PURE__ */ g("path", {
136
136
  strokeLinecap: "round",
137
137
  strokeLinejoin: "round",
138
138
  strokeWidth: 2,
@@ -140,54 +140,54 @@ function b() {
140
140
  })
141
141
  })
142
142
  }),
143
- /* @__PURE__ */ v("h3", {
143
+ /* @__PURE__ */ g("h3", {
144
144
  className: "text-lg font-semibold text-[var(--color-text-primary)] mb-2",
145
- children: C("auth.register.accountCreated", "Account created successfully!")
145
+ children: x("auth.register.accountCreated", "Account created successfully!")
146
146
  }),
147
- /* @__PURE__ */ y("p", {
147
+ /* @__PURE__ */ _("p", {
148
148
  className: "text-center text-[var(--color-text-secondary)] mb-8 max-w-sm",
149
149
  children: [
150
- fe,
151
- /* @__PURE__ */ v("span", {
150
+ ge,
151
+ /* @__PURE__ */ g("span", {
152
152
  className: "font-medium text-[var(--color-text-primary)]",
153
- children: j.email
153
+ children: O.email
154
154
  }),
155
- pe
155
+ _e
156
156
  ]
157
157
  }),
158
- /* @__PURE__ */ y("div", {
158
+ /* @__PURE__ */ _("div", {
159
159
  className: "space-y-3 w-full",
160
160
  children: [
161
- /* @__PURE__ */ v("button", {
161
+ /* @__PURE__ */ g("button", {
162
162
  type: "button",
163
- onClick: () => x("/auth/login"),
163
+ onClick: () => y("/auth/login"),
164
164
  className: "auth-primary-button w-full px-4 py-3 font-semibold",
165
- children: C("auth.register.continueToSignIn", "Continue to sign in")
165
+ children: x("auth.register.continueToSignIn", "Continue to sign in")
166
166
  }),
167
- G && /* @__PURE__ */ v("p", {
167
+ G && /* @__PURE__ */ g("p", {
168
168
  className: "text-center text-sm text-status-error-text",
169
169
  children: G
170
170
  }),
171
- /* @__PURE__ */ y("p", {
171
+ /* @__PURE__ */ _("p", {
172
172
  className: "text-center text-sm text-[var(--color-text-tertiary)]",
173
173
  children: [
174
- C("auth.register.didntReceiveEmail", "Didn't receive the email? Check your spam folder or"),
174
+ x("auth.register.didntReceiveEmail", "Didn't receive the email? Check your spam folder or"),
175
175
  " ",
176
- /* @__PURE__ */ v("button", {
176
+ /* @__PURE__ */ g("button", {
177
177
  type: "button",
178
178
  disabled: U,
179
179
  onClick: async () => {
180
180
  K(null), W(!0);
181
181
  try {
182
- await T(j.email);
182
+ await C(O.email);
183
183
  } catch (e) {
184
- K(e instanceof Error ? e.message : C("auth.register.resendFailed", "Failed to resend verification email"));
184
+ K(e instanceof Error ? e.message : x("auth.register.resendFailed", "Failed to resend verification email"));
185
185
  } finally {
186
186
  W(!1);
187
187
  }
188
188
  },
189
189
  className: "text-text-link hover:text-text-linkHover font-medium disabled:opacity-50 disabled:cursor-not-allowed",
190
- children: U ? C("auth.register.resending", "resending...") : C("auth.register.tryAgain", "try again")
190
+ children: U ? x("auth.register.resending", "resending...") : x("auth.register.tryAgain", "try again")
191
191
  })
192
192
  ]
193
193
  })
@@ -195,219 +195,219 @@ function b() {
195
195
  })
196
196
  ]
197
197
  })
198
- }) : /* @__PURE__ */ y(i, {
199
- title: C("auth.register.createAccount", "Create your account"),
200
- subtitle: C("auth.register.startJourney", "Start your journey with {{productName}}").replace("{{productName}}", b || "Burdenoff Workspaces"),
201
- productName: b,
198
+ }) : /* @__PURE__ */ _(r, {
199
+ title: x("auth.register.createAccount", "Create your account"),
200
+ subtitle: x("auth.register.startJourney", "Start your journey with {{productName}}").replace("{{productName}}", v || "Burdenoff Workspaces"),
201
+ productName: v,
202
202
  children: [
203
- /* @__PURE__ */ y("form", {
204
- onSubmit: ce,
203
+ /* @__PURE__ */ _("form", {
204
+ onSubmit: fe,
205
205
  className: "space-y-5",
206
206
  children: [
207
- q && /* @__PURE__ */ y("div", {
207
+ q && /* @__PURE__ */ _("div", {
208
208
  className: "flex items-start gap-3 p-4 rounded-xl bg-status-warning-bg-subtle border border-status-warning-border",
209
- children: [/* @__PURE__ */ v(a, { className: "size-5 text-status-warning-text flex-shrink-0 mt-0.5" }), /* @__PURE__ */ y("div", {
209
+ children: [/* @__PURE__ */ g(i, { className: "size-5 text-status-warning-text flex-shrink-0 mt-0.5" }), /* @__PURE__ */ _("div", {
210
210
  className: "space-y-2",
211
211
  children: [
212
- /* @__PURE__ */ v("p", {
212
+ /* @__PURE__ */ g("p", {
213
213
  className: "text-sm font-medium text-status-warning-text",
214
- children: C("auth.register.accountDeactivated", "This email belongs to a deactivated account.")
214
+ children: x("auth.register.accountDeactivated", "This email belongs to a deactivated account.")
215
215
  }),
216
- /* @__PURE__ */ v("p", {
216
+ /* @__PURE__ */ g("p", {
217
217
  className: "text-xs text-status-warning-text/80",
218
- children: C("auth.register.accountDeactivatedHelp", "If this is your account, you can reactivate it instead of creating a new one.")
218
+ children: x("auth.register.accountDeactivatedHelp", "If this is your account, you can reactivate it instead of creating a new one.")
219
219
  }),
220
- /* @__PURE__ */ v("button", {
220
+ /* @__PURE__ */ g("button", {
221
221
  type: "button",
222
- onClick: () => x("/auth/reactivate", { state: { email: j.email } }),
222
+ onClick: () => y("/auth/reactivate", { state: { email: O.email } }),
223
223
  className: "text-sm font-semibold text-status-warning-text underline underline-offset-2",
224
- children: C("auth.register.reactivateCta", "Reactivate my account")
224
+ children: x("auth.register.reactivateCta", "Reactivate my account")
225
225
  })
226
226
  ]
227
227
  })]
228
228
  }),
229
- (J || Y) && /* @__PURE__ */ y("div", {
229
+ (J || Y) && /* @__PURE__ */ _("div", {
230
230
  className: "flex items-start gap-3 p-4 rounded-xl bg-status-warning-bg-subtle border border-status-warning-border",
231
- children: [/* @__PURE__ */ v(a, { className: "size-5 text-status-warning-text flex-shrink-0 mt-0.5" }), /* @__PURE__ */ v("p", {
231
+ children: [/* @__PURE__ */ g(i, { className: "size-5 text-status-warning-text flex-shrink-0 mt-0.5" }), /* @__PURE__ */ g("p", {
232
232
  className: "text-sm text-status-warning-text",
233
- children: L
233
+ children: P
234
234
  })]
235
235
  }),
236
- (L && !q && !J && !Y || N.length > 0) && /* @__PURE__ */ y("div", {
236
+ (P && !q && !J && !Y || A.length > 0) && /* @__PURE__ */ _("div", {
237
237
  className: "flex items-start gap-3 p-4 rounded-xl bg-status-error-bg-subtle border border-status-error-border",
238
- children: [/* @__PURE__ */ v(a, { className: "size-5 text-status-error-text flex-shrink-0 mt-0.5" }), /* @__PURE__ */ y("div", {
238
+ children: [/* @__PURE__ */ g(i, { className: "size-5 text-status-error-text flex-shrink-0 mt-0.5" }), /* @__PURE__ */ _("div", {
239
239
  className: "space-y-1",
240
- children: [L && !q && !J && !Y && /* @__PURE__ */ v("p", {
240
+ children: [P && !q && !J && !Y && /* @__PURE__ */ g("p", {
241
241
  className: "text-sm text-status-error-text",
242
- children: L
243
- }), N.map((e) => /* @__PURE__ */ v("p", {
242
+ children: P
243
+ }), A.map((e) => /* @__PURE__ */ g("p", {
244
244
  className: "text-sm text-status-error-text",
245
245
  children: e
246
246
  }, e))]
247
247
  })]
248
248
  }),
249
- /* @__PURE__ */ y("div", {
249
+ /* @__PURE__ */ _("div", {
250
250
  className: "grid grid-cols-2 gap-4",
251
- children: [/* @__PURE__ */ y("div", {
251
+ children: [/* @__PURE__ */ _("div", {
252
252
  className: "space-y-2",
253
- children: [/* @__PURE__ */ v("label", {
253
+ children: [/* @__PURE__ */ g("label", {
254
254
  htmlFor: "firstName",
255
255
  className: "block text-sm font-medium text-[var(--color-text-primary)]",
256
- children: C("auth.register.firstName", "First Name")
257
- }), /* @__PURE__ */ v("input", {
256
+ children: x("auth.register.firstName", "First Name")
257
+ }), /* @__PURE__ */ g("input", {
258
258
  id: "firstName",
259
259
  type: "text",
260
260
  required: !0,
261
261
  "aria-required": "true",
262
- "aria-label": C("auth.register.firstName", "First Name"),
263
- value: j.firstName,
264
- onChange: (e) => M({
265
- ...j,
262
+ "aria-label": x("auth.register.firstName", "First Name"),
263
+ value: O.firstName,
264
+ onChange: (e) => k({
265
+ ...O,
266
266
  firstName: e.target.value
267
267
  }),
268
268
  className: "block w-full px-4 py-3 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] text-[var(--color-text-primary)] placeholder:text-[var(--color-text-tertiary)] focus:outline-none focus:ring-2 focus:ring-action-primary-bg/20 focus:border-action-primary-bg transition-all",
269
- placeholder: C("auth.register.firstNamePlaceholder", "Enter first name")
269
+ placeholder: x("auth.register.firstNamePlaceholder", "Enter first name")
270
270
  })]
271
- }), /* @__PURE__ */ y("div", {
271
+ }), /* @__PURE__ */ _("div", {
272
272
  className: "space-y-2",
273
- children: [/* @__PURE__ */ v("label", {
273
+ children: [/* @__PURE__ */ g("label", {
274
274
  htmlFor: "lastName",
275
275
  className: "block text-sm font-medium text-[var(--color-text-primary)]",
276
- children: C("auth.register.lastName", "Last Name")
277
- }), /* @__PURE__ */ v("input", {
276
+ children: x("auth.register.lastName", "Last Name")
277
+ }), /* @__PURE__ */ g("input", {
278
278
  id: "lastName",
279
279
  type: "text",
280
280
  required: !0,
281
281
  "aria-required": "true",
282
- "aria-label": C("auth.register.lastName", "Last Name"),
283
- value: j.lastName,
284
- onChange: (e) => M({
285
- ...j,
282
+ "aria-label": x("auth.register.lastName", "Last Name"),
283
+ value: O.lastName,
284
+ onChange: (e) => k({
285
+ ...O,
286
286
  lastName: e.target.value
287
287
  }),
288
288
  className: "block w-full px-4 py-3 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] text-[var(--color-text-primary)] placeholder:text-[var(--color-text-tertiary)] focus:outline-none focus:ring-2 focus:ring-action-primary-bg/20 focus:border-action-primary-bg transition-all",
289
- placeholder: C("auth.register.lastNamePlaceholder", "Enter last name")
289
+ placeholder: x("auth.register.lastNamePlaceholder", "Enter last name")
290
290
  })]
291
291
  })]
292
292
  }),
293
- /* @__PURE__ */ y("div", {
293
+ /* @__PURE__ */ _("div", {
294
294
  className: "space-y-2",
295
- children: [/* @__PURE__ */ v("label", {
295
+ children: [/* @__PURE__ */ g("label", {
296
296
  htmlFor: "username",
297
297
  className: "block text-sm font-medium text-[var(--color-text-primary)]",
298
- children: C("auth.register.username", "Username")
299
- }), /* @__PURE__ */ y("div", {
298
+ children: x("auth.register.username", "Username")
299
+ }), /* @__PURE__ */ _("div", {
300
300
  className: "relative",
301
- children: [/* @__PURE__ */ v("div", {
301
+ children: [/* @__PURE__ */ g("div", {
302
302
  className: "absolute inset-y-0 left-0 pl-3.5 flex items-center pointer-events-none",
303
- children: /* @__PURE__ */ v(f, { className: "size-5 text-[var(--color-text-tertiary)]" })
304
- }), /* @__PURE__ */ v("input", {
303
+ children: /* @__PURE__ */ g(u, { className: "size-5 text-[var(--color-text-tertiary)]" })
304
+ }), /* @__PURE__ */ g("input", {
305
305
  id: "username",
306
306
  type: "text",
307
307
  required: !0,
308
308
  "aria-required": "true",
309
- "aria-label": C("auth.register.username", "Username"),
309
+ "aria-label": x("auth.register.username", "Username"),
310
310
  autoComplete: "username",
311
- value: j.username,
312
- onChange: (e) => M({
313
- ...j,
311
+ value: O.username,
312
+ onChange: (e) => k({
313
+ ...O,
314
314
  username: e.target.value
315
315
  }),
316
316
  className: "block w-full pl-[3.5rem] pr-4 py-3 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] text-[var(--color-text-primary)] placeholder:text-[var(--color-text-tertiary)] focus:outline-none focus:ring-2 focus:ring-action-primary-bg/20 focus:border-action-primary-bg transition-all",
317
- placeholder: C("auth.register.usernamePlaceholder", "Choose a username")
317
+ placeholder: x("auth.register.usernamePlaceholder", "Choose a username")
318
318
  })]
319
319
  })]
320
320
  }),
321
- /* @__PURE__ */ y("div", {
321
+ /* @__PURE__ */ _("div", {
322
322
  className: "space-y-2",
323
- children: [/* @__PURE__ */ v("label", {
323
+ children: [/* @__PURE__ */ g("label", {
324
324
  htmlFor: "email",
325
325
  className: "block text-sm font-medium text-[var(--color-text-primary)]",
326
- children: C("auth.register.email", "Email")
327
- }), /* @__PURE__ */ y("div", {
326
+ children: x("auth.register.email", "Email")
327
+ }), /* @__PURE__ */ _("div", {
328
328
  className: "relative",
329
- children: [/* @__PURE__ */ v("div", {
329
+ children: [/* @__PURE__ */ g("div", {
330
330
  className: "absolute inset-y-0 left-0 pl-3.5 flex items-center pointer-events-none",
331
- children: /* @__PURE__ */ v(o, { className: "size-5 text-[var(--color-text-tertiary)]" })
332
- }), /* @__PURE__ */ v("input", {
331
+ children: /* @__PURE__ */ g(o, { className: "size-5 text-[var(--color-text-tertiary)]" })
332
+ }), /* @__PURE__ */ g("input", {
333
333
  id: "email",
334
334
  type: "email",
335
335
  required: !0,
336
336
  "aria-required": "true",
337
- "aria-label": C("auth.register.email", "Email"),
337
+ "aria-label": x("auth.register.email", "Email"),
338
338
  autoComplete: "email",
339
- value: j.email,
340
- onChange: (e) => M({
341
- ...j,
339
+ value: O.email,
340
+ onChange: (e) => k({
341
+ ...O,
342
342
  email: e.target.value
343
343
  }),
344
344
  className: "block w-full pl-[3.5rem] pr-4 py-3 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] text-[var(--color-text-primary)] placeholder:text-[var(--color-text-tertiary)] focus:outline-none focus:ring-2 focus:ring-action-primary-bg/20 focus:border-action-primary-bg transition-all",
345
- placeholder: C("auth.register.emailPlaceholder", "you@example.com")
345
+ placeholder: x("auth.register.emailPlaceholder", "you@example.com")
346
346
  })]
347
347
  })]
348
348
  }),
349
- /* @__PURE__ */ y("div", {
349
+ /* @__PURE__ */ _("div", {
350
350
  className: "space-y-2",
351
351
  children: [
352
- /* @__PURE__ */ v("label", {
352
+ /* @__PURE__ */ g("label", {
353
353
  htmlFor: "password",
354
354
  className: "block text-sm font-medium text-[var(--color-text-primary)]",
355
- children: C("auth.register.password", "Password")
355
+ children: x("auth.register.password", "Password")
356
356
  }),
357
- /* @__PURE__ */ y("div", {
357
+ /* @__PURE__ */ _("div", {
358
358
  className: "relative",
359
359
  children: [
360
- /* @__PURE__ */ v("div", {
360
+ /* @__PURE__ */ g("div", {
361
361
  className: "absolute inset-y-0 left-0 pl-3.5 flex items-center pointer-events-none",
362
- children: /* @__PURE__ */ v(ne, { className: "size-5 text-[var(--color-text-tertiary)]" })
362
+ children: /* @__PURE__ */ g(re, { className: "size-5 text-[var(--color-text-tertiary)]" })
363
363
  }),
364
- /* @__PURE__ */ v("input", {
364
+ /* @__PURE__ */ g("input", {
365
365
  id: "password",
366
- type: k ? "text" : "password",
366
+ type: D ? "text" : "password",
367
367
  required: !0,
368
368
  "aria-required": "true",
369
- "aria-label": C("auth.register.password", "Password"),
369
+ "aria-label": x("auth.register.password", "Password"),
370
370
  autoComplete: "new-password",
371
- value: j.password,
372
- onChange: (e) => M({
373
- ...j,
371
+ value: O.password,
372
+ onChange: (e) => k({
373
+ ...O,
374
374
  password: e.target.value
375
375
  }),
376
376
  className: "block w-full pl-[3.5rem] pr-12 py-3 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] text-[var(--color-text-primary)] placeholder:text-[var(--color-text-tertiary)] focus:outline-none focus:ring-2 focus:ring-action-primary-bg/20 focus:border-action-primary-bg transition-all",
377
- placeholder: C("auth.register.passwordPlaceholder", "Create a strong password")
377
+ placeholder: x("auth.register.passwordPlaceholder", "Create a strong password")
378
378
  }),
379
- /* @__PURE__ */ v("button", {
379
+ /* @__PURE__ */ g("button", {
380
380
  type: "button",
381
- onClick: () => A(!k),
382
- "aria-label": k ? "Hide password" : "Show password",
381
+ onClick: () => ue(!D),
382
+ "aria-label": D ? "Hide password" : "Show password",
383
383
  className: "absolute inset-y-0 right-0 pr-3.5 flex items-center text-[var(--color-text-tertiary)] hover:text-[var(--color-text-secondary)] transition-colors",
384
- children: v(k ? c : s, { className: "size-5" })
384
+ children: g(D ? te : ee, { className: "size-5" })
385
385
  })
386
386
  ]
387
387
  }),
388
- j.password && /* @__PURE__ */ y("div", {
388
+ O.password && /* @__PURE__ */ _("div", {
389
389
  className: "space-y-3 pt-2",
390
- children: [/* @__PURE__ */ y("div", {
390
+ children: [/* @__PURE__ */ _("div", {
391
391
  className: "space-y-1",
392
- children: [/* @__PURE__ */ v("div", {
392
+ children: [/* @__PURE__ */ g("div", {
393
393
  className: "h-1.5 bg-[var(--color-bg-sunken)] rounded-full overflow-hidden",
394
- children: /* @__PURE__ */ v("div", { className: `h-full ${Z.color} ${Z.width} transition-all duration-300` })
395
- }), /* @__PURE__ */ y("p", {
394
+ children: /* @__PURE__ */ g("div", { className: `h-full ${Z.color} ${Z.width} transition-all duration-300` })
395
+ }), /* @__PURE__ */ _("p", {
396
396
  className: "text-xs text-[var(--color-text-tertiary)] capitalize",
397
397
  children: [
398
- C("auth.register.passwordStrength", "Password strength:"),
398
+ x("auth.register.passwordStrength", "Password strength:"),
399
399
  " ",
400
- /* @__PURE__ */ v("span", {
400
+ /* @__PURE__ */ g("span", {
401
401
  className: "font-medium",
402
402
  children: Z.level
403
403
  })
404
404
  ]
405
405
  })]
406
- }), /* @__PURE__ */ v("div", {
406
+ }), /* @__PURE__ */ g("div", {
407
407
  className: "grid grid-cols-2 gap-2",
408
- children: X.map((e) => /* @__PURE__ */ y("div", {
408
+ children: X.map((e) => /* @__PURE__ */ _("div", {
409
409
  className: "flex items-center gap-2",
410
- children: [e.met ? /* @__PURE__ */ v(ee, { className: "size-4 text-status-success-text" }) : /* @__PURE__ */ v(p, { className: "size-4 text-[var(--color-text-tertiary)]" }), /* @__PURE__ */ v("span", {
410
+ children: [e.met ? /* @__PURE__ */ g(a, { className: "size-4 text-status-success-text" }) : /* @__PURE__ */ g(d, { className: "size-4 text-[var(--color-text-tertiary)]" }), /* @__PURE__ */ g("span", {
411
411
  className: `text-xs ${e.met ? "text-status-success-text" : "text-[var(--color-text-tertiary)]"}`,
412
412
  children: e.label
413
413
  })]
@@ -416,80 +416,85 @@ function b() {
416
416
  })
417
417
  ]
418
418
  }),
419
- /* @__PURE__ */ y("p", {
419
+ /* @__PURE__ */ _("p", {
420
420
  className: "text-xs text-[var(--color-text-tertiary)] leading-relaxed",
421
421
  children: [
422
- C("auth.register.agreeToTerms", "By creating an account, you agree to our"),
422
+ x("auth.register.agreeToTerms", "By creating an account, you agree to our"),
423
423
  " ",
424
- /* @__PURE__ */ v("a", {
424
+ /* @__PURE__ */ g("a", {
425
425
  href: "/legal/terms",
426
426
  className: "text-text-link hover:underline",
427
- children: C("auth.register.termsOfService", "Terms of Service")
427
+ children: x("auth.register.termsOfService", "Terms of Service")
428
428
  }),
429
429
  " ",
430
- C("auth.register.and", "and"),
430
+ x("auth.register.and", "and"),
431
431
  " ",
432
- /* @__PURE__ */ v("a", {
432
+ /* @__PURE__ */ g("a", {
433
433
  href: "/legal/privacy",
434
434
  className: "text-text-link hover:underline",
435
- children: C("auth.register.privacyPolicy", "Privacy Policy")
435
+ children: x("auth.register.privacyPolicy", "Privacy Policy")
436
436
  }),
437
437
  "."
438
438
  ]
439
439
  }),
440
- /* @__PURE__ */ v(re, { onVerify: H }),
441
- /* @__PURE__ */ v("button", {
440
+ /* @__PURE__ */ g(m, {
441
+ enabled: H,
442
+ siteKey: de,
443
+ onVerify: z,
444
+ onExpired: () => z(null)
445
+ }, B),
446
+ /* @__PURE__ */ g("button", {
442
447
  type: "submit",
443
- disabled: E,
448
+ disabled: w,
444
449
  className: "auth-primary-button flex w-full items-center justify-center gap-2 px-4 py-3 font-semibold",
445
- children: E ? /* @__PURE__ */ y(_, { children: [/* @__PURE__ */ v(d, { className: "size-5 animate-spin" }), /* @__PURE__ */ v("span", { children: C("auth.register.creatingAccount", "Creating account...") })] }) : C("auth.register.createAccountBtn", "Create account")
450
+ children: w ? /* @__PURE__ */ _(se, { children: [/* @__PURE__ */ g(l, { className: "size-5 animate-spin" }), /* @__PURE__ */ g("span", { children: x("auth.register.creatingAccount", "Creating account...") })] }) : x("auth.register.createAccountBtn", "Create account")
446
451
  })
447
452
  ]
448
453
  }),
449
- /* @__PURE__ */ y("div", {
454
+ /* @__PURE__ */ _("div", {
450
455
  className: "relative my-8",
451
- children: [/* @__PURE__ */ v("div", {
456
+ children: [/* @__PURE__ */ g("div", {
452
457
  className: "absolute inset-0 flex items-center",
453
- children: /* @__PURE__ */ v("div", { className: "w-full border-t border-[var(--color-border-default)]" })
454
- }), /* @__PURE__ */ v("div", {
458
+ children: /* @__PURE__ */ g("div", { className: "w-full border-t border-[var(--color-border-default)]" })
459
+ }), /* @__PURE__ */ g("div", {
455
460
  className: "relative flex justify-center",
456
- children: /* @__PURE__ */ v("span", {
461
+ children: /* @__PURE__ */ g("span", {
457
462
  className: "px-4 bg-[var(--color-bg-canvas)] text-sm text-[var(--color-text-tertiary)]",
458
- children: C("auth.register.orSignUpWith", "or sign up with")
463
+ children: x("auth.register.orSignUpWith", "or sign up with")
459
464
  })
460
465
  })]
461
466
  }),
462
- z && /* @__PURE__ */ y("div", {
467
+ I && /* @__PURE__ */ _("div", {
463
468
  className: "flex items-start gap-3 p-4 rounded-xl border bg-status-error-bg-subtle border-status-error-border",
464
- children: [/* @__PURE__ */ v(a, { className: "size-5 flex-shrink-0 mt-0.5 text-status-error-text" }), /* @__PURE__ */ v("p", {
469
+ children: [/* @__PURE__ */ g(i, { className: "size-5 flex-shrink-0 mt-0.5 text-status-error-text" }), /* @__PURE__ */ g("p", {
465
470
  className: "text-sm text-status-error-text",
466
- children: z
471
+ children: I
467
472
  })]
468
473
  }),
469
- /* @__PURE__ */ v("div", {
474
+ /* @__PURE__ */ g("div", {
470
475
  className: "grid grid-cols-3 gap-3",
471
- children: de.map((e) => /* @__PURE__ */ y("button", {
476
+ children: he.map((e) => /* @__PURE__ */ _("button", {
472
477
  type: "button",
473
478
  onClick: e.onClick,
474
479
  disabled: e.disabled,
475
480
  title: e.tooltip,
476
481
  className: `flex items-center justify-center gap-2 px-4 py-3 rounded-xl border transition-all duration-200 group ${e.disabled ? "border-[var(--color-border-subtle)] bg-[var(--color-bg-disabled)] cursor-not-allowed opacity-50" : "border-[var(--color-border-default)] bg-[var(--color-bg-surface)] hover:bg-[var(--color-bg-sunken)] hover:border-[var(--color-border-strong)]"}`,
477
- children: [/* @__PURE__ */ v(e.icon, { className: `size-5 ${e.disabled ? "opacity-50" : ""}` }), /* @__PURE__ */ v("span", {
482
+ children: [/* @__PURE__ */ g(e.icon, { className: `size-5 ${e.disabled ? "opacity-50" : ""}` }), /* @__PURE__ */ g("span", {
478
483
  className: `sr-only sm:not-sr-only text-sm font-medium transition-colors hidden sm:inline ${e.disabled ? "text-[var(--color-text-disabled)]" : "text-[var(--color-text-secondary)] group-hover:text-[var(--color-text-primary)]"}`,
479
484
  children: e.name
480
485
  })]
481
486
  }, e.name))
482
487
  }),
483
- /* @__PURE__ */ y("p", {
488
+ /* @__PURE__ */ _("p", {
484
489
  className: "mt-8 text-center text-sm text-[var(--color-text-secondary)]",
485
490
  children: [
486
- C("auth.register.alreadyHaveAccount", "Already have an account?"),
491
+ x("auth.register.alreadyHaveAccount", "Already have an account?"),
487
492
  " ",
488
- /* @__PURE__ */ v("button", {
493
+ /* @__PURE__ */ g("button", {
489
494
  type: "button",
490
- onClick: () => x("/auth/login"),
495
+ onClick: () => y("/auth/login"),
491
496
  className: "font-semibold text-text-link hover:text-text-linkHover transition-colors",
492
- children: C("auth.register.signIn", "Sign in")
497
+ children: x("auth.register.signIn", "Sign in")
493
498
  })
494
499
  ]
495
500
  })
@@ -497,6 +502,6 @@ function b() {
497
502
  });
498
503
  }
499
504
  //#endregion
500
- export { b as RegisterPage };
505
+ export { v as RegisterPage };
501
506
 
502
507
  //# sourceMappingURL=RegisterPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"RegisterPage.js","names":[],"sources":["../../../src/auth/pages/RegisterPage.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport { useNavigate } from \"react-router-dom\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell\";\nimport { validateEmail, validatePassword } from \"../utils/validation\";\nimport { useAuthOperations } from \"../hooks/useAuthOperations\";\nimport { useOAuthOperations } from \"../hooks/useOAuthOperations\";\nimport { AuthLayout } from \"../components/AuthLayout\";\nimport {\n EmailIcon,\n LockIcon,\n UserIcon,\n EyeIcon,\n EyeOffIcon,\n AlertIcon,\n CheckIcon,\n XIcon,\n SpinnerIcon,\n GoogleIcon,\n GitHubIcon,\n MicrosoftIcon,\n} from \"../components/AuthIcons\";\nimport { useAuth } from \"../providers/AuthProvider\";\nimport { CaptchaV2 } from \"../components/CaptchaV2\";\nimport { isCaptchaEnabled } from \"../utils/captchaConfig\";\nimport type { SignUpInput } from \"../types\";\n\n/**\n * Registration page with professional design\n */\nexport function RegisterPage() {\n const { productName } = useAuth();\n const navigate = useNavigate();\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n const { signUp, resendVerification, isLoading, errorCode } =\n useAuthOperations();\n const { initiateGoogleOAuth, initiateMicrosoftOAuth, isProviderConfigured } =\n useOAuthOperations();\n const [showPassword, setShowPassword] = useState(false);\n const [formData, setFormData] = useState<SignUpInput>({\n email: \"\",\n username: \"\",\n password: \"\",\n firstName: \"\",\n lastName: \"\",\n });\n const [validationErrors, setValidationErrors] = useState<string[]>([]);\n const [signupSuccess, setSignupSuccess] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [oauthError, setOauthError] = useState<string | null>(null);\n const [captchaToken, setCaptchaToken] = useState<string | null>(null);\n const [isResendingVerification, setIsResendingVerification] = useState(false);\n const [resendError, setResendError] = useState<string | null>(null);\n\n // Signing up with an email that already belongs to a non-active account:\n // route the user to the right recovery path instead of dead-ending.\n const isAccountDeactivated =\n Boolean(error) && errorCode === \"ACCOUNT_DEACTIVATED\";\n const isAccountRemoved = Boolean(error) && errorCode === \"ACCOUNT_REMOVED\";\n const isAccountSuspended =\n Boolean(error) && errorCode === \"ACCOUNT_SUSPENDED\";\n\n // Password strength indicators\n const passwordChecks = (() => {\n const password = formData.password;\n return [\n {\n label: tr(\"auth.register.pwMin8\", \"At least 12 characters\"),\n met: password.length >= 12,\n },\n {\n label: tr(\"auth.register.pwUppercase\", \"One uppercase letter\"),\n met: /[A-Z]/.test(password),\n },\n {\n label: tr(\"auth.register.pwLowercase\", \"One lowercase letter\"),\n met: /[a-z]/.test(password),\n },\n {\n label: tr(\"auth.register.pwNumber\", \"One number\"),\n met: /\\d/.test(password),\n },\n {\n label: tr(\"auth.register.pwSpecial\", \"One special character\"),\n met: /[!@#$%^&*(),.?\":{}|<>]/.test(password),\n },\n ];\n })();\n\n const passwordStrength = (() => {\n const metCount = passwordChecks.filter((c) => c.met).length;\n if (metCount <= 2)\n return {\n level: tr(\"auth.register.passwordStrengthWeak\", \"weak\"),\n color: \"bg-status-error-bg-subtle0\",\n width: \"w-1/4\",\n };\n if (metCount <= 3)\n return {\n level: tr(\"auth.register.passwordStrengthFair\", \"fair\"),\n color: \"bg-status-warning-text\",\n width: \"w-2/4\",\n };\n if (metCount <= 4)\n return {\n level: tr(\"auth.register.passwordStrengthGood\", \"good\"),\n color: \"bg-status-warning-bg-subtle0\",\n width: \"w-3/4\",\n };\n return {\n level: tr(\"auth.register.passwordStrengthStrong\", \"strong\"),\n color: \"bg-status-success-text\",\n width: \"w-full\",\n };\n })();\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n\n const errors: string[] = [];\n if (!validateEmail(formData.email)) {\n errors.push(\n tr(\"auth.register.invalidEmail\", \"Please enter a valid email address\"),\n );\n }\n if (!formData.username || formData.username.length < 3) {\n errors.push(\n tr(\n \"auth.register.usernameMin3\",\n \"Username must be at least 3 characters\",\n ),\n );\n }\n if (!/^[a-zA-Z0-9_-]+$/.test(formData.username)) {\n errors.push(\n tr(\n \"auth.register.usernameChars\",\n \"Username can only contain letters, numbers, underscores, and hyphens\",\n ),\n );\n }\n const passwordValidation = validatePassword(formData.password);\n if (!passwordValidation.valid) {\n errors.push(...passwordValidation.errors);\n }\n\n if (isCaptchaEnabled() && !captchaToken) {\n errors.push(\n tr(\n \"auth.register.completeCaptcha\",\n \"Please complete the reCAPTCHA verification\",\n ),\n );\n }\n\n if (errors.length > 0) {\n setValidationErrors(errors);\n return;\n }\n\n setValidationErrors([]);\n setError(null);\n try {\n await signUp({\n ...formData,\n ...(captchaToken ? { recaptchaToken: captchaToken } : {}),\n });\n // Sign up successful - check if email verification is required\n // (Backend returns empty tokens when email verification is needed)\n setSignupSuccess(true);\n } catch (err) {\n const message =\n err instanceof Error\n ? err.message\n : tr(\"auth.register.signUpFailed\", \"Sign up failed\");\n setError(message);\n console.error(\"Sign up failed:\", err);\n }\n };\n\n /**\n * Handle Google OAuth signup\n */\n const handleGoogleSignup = () => {\n setOauthError(null);\n try {\n initiateGoogleOAuth();\n } catch (err) {\n setOauthError(\n err instanceof Error\n ? err.message\n : tr(\n \"auth.register.googleSignUpFailed\",\n \"Failed to initiate Google sign up\",\n ),\n );\n }\n };\n\n /**\n * Handle Microsoft OAuth signup\n */\n const handleMicrosoftSignup = () => {\n setOauthError(null);\n try {\n initiateMicrosoftOAuth();\n } catch (err) {\n setOauthError(\n err instanceof Error\n ? err.message\n : tr(\n \"auth.register.microsoftSignUpFailed\",\n \"Failed to initiate Microsoft sign up\",\n ),\n );\n }\n };\n\n // Check if OAuth providers are configured\n const isGoogleConfigured = isProviderConfigured(\"google\");\n const isMicrosoftConfigured = isProviderConfigured(\"microsoft\");\n\n const oauthProviders = [\n {\n name: \"Google\",\n icon: GoogleIcon,\n onClick: handleGoogleSignup,\n disabled: !isGoogleConfigured,\n tooltip: isGoogleConfigured\n ? undefined\n : tr(\n \"auth.register.googleNotConfigured\",\n \"Google sign up is not configured\",\n ),\n },\n {\n name: \"GitHub\",\n icon: GitHubIcon,\n onClick: () => {},\n disabled: true,\n tooltip: tr(\n \"auth.register.githubNotConfigured\",\n \"GitHub sign up is not configured\",\n ),\n },\n {\n name: \"Microsoft\",\n icon: MicrosoftIcon,\n onClick: handleMicrosoftSignup,\n disabled: !isMicrosoftConfigured,\n tooltip: isMicrosoftConfigured\n ? undefined\n : tr(\n \"auth.register.microsoftNotConfigured\",\n \"Microsoft sign up is not configured\",\n ),\n },\n ];\n\n const verificationEmailMessage = tr(\n \"auth.register.verificationEmailSentTo\",\n \"We've sent a verification email to {{email}}. Please check your inbox and click the verification link to activate your account.\",\n );\n const [verificationEmailPrefix, verificationEmailSuffix = \"\"] =\n verificationEmailMessage.split(\"{{email}}\");\n\n // Show success state after signup\n if (signupSuccess) {\n return (\n <AuthLayout\n title={tr(\"auth.register.checkEmail\", \"Check your email\")}\n subtitle={tr(\n \"auth.register.verificationLinkSent\",\n \"We've sent you a verification link\",\n )}\n >\n <div className=\"flex flex-col items-center justify-center py-6\">\n {/* Success Icon */}\n <div className=\"size-20 rounded-full bg-status-success-bg-subtle flex items-center justify-center mb-6\">\n <svg\n className=\"size-10 text-status-success-text\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z\"\n />\n </svg>\n </div>\n\n <h3 className=\"text-lg font-semibold text-[var(--color-text-primary)] mb-2\">\n {tr(\n \"auth.register.accountCreated\",\n \"Account created successfully!\",\n )}\n </h3>\n\n <p className=\"text-center text-[var(--color-text-secondary)] mb-8 max-w-sm\">\n {verificationEmailPrefix}\n <span className=\"font-medium text-[var(--color-text-primary)]\">\n {formData.email}\n </span>\n {verificationEmailSuffix}\n </p>\n\n <div className=\"space-y-3 w-full\">\n <button\n type=\"button\"\n onClick={() => navigate(\"/auth/login\")}\n className=\"auth-primary-button w-full px-4 py-3 font-semibold\"\n >\n {tr(\"auth.register.continueToSignIn\", \"Continue to sign in\")}\n </button>\n\n {resendError && (\n <p className=\"text-center text-sm text-status-error-text\">\n {resendError}\n </p>\n )}\n <p className=\"text-center text-sm text-[var(--color-text-tertiary)]\">\n {tr(\n \"auth.register.didntReceiveEmail\",\n \"Didn't receive the email? Check your spam folder or\",\n )}{\" \"}\n <button\n type=\"button\"\n disabled={isResendingVerification}\n onClick={async () => {\n setResendError(null);\n setIsResendingVerification(true);\n try {\n await resendVerification(formData.email);\n } catch (err) {\n setResendError(\n err instanceof Error\n ? err.message\n : tr(\n \"auth.register.resendFailed\",\n \"Failed to resend verification email\",\n ),\n );\n } finally {\n setIsResendingVerification(false);\n }\n }}\n className=\"text-text-link hover:text-text-linkHover font-medium disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n {isResendingVerification\n ? tr(\"auth.register.resending\", \"resending...\")\n : tr(\"auth.register.tryAgain\", \"try again\")}\n </button>\n </p>\n </div>\n </div>\n </AuthLayout>\n );\n }\n\n return (\n <AuthLayout\n title={tr(\"auth.register.createAccount\", \"Create your account\")}\n subtitle={tr(\n \"auth.register.startJourney\",\n \"Start your journey with {{productName}}\",\n ).replace(\"{{productName}}\", productName || \"Burdenoff Workspaces\")}\n productName={productName}\n >\n <form onSubmit={handleSubmit} className=\"space-y-5\">\n {/* Deactivated account → offer self-service reactivation */}\n {isAccountDeactivated && (\n <div className=\"flex items-start gap-3 p-4 rounded-xl bg-status-warning-bg-subtle border border-status-warning-border\">\n <AlertIcon className=\"size-5 text-status-warning-text flex-shrink-0 mt-0.5\" />\n <div className=\"space-y-2\">\n <p className=\"text-sm font-medium text-status-warning-text\">\n {tr(\n \"auth.register.accountDeactivated\",\n \"This email belongs to a deactivated account.\",\n )}\n </p>\n <p className=\"text-xs text-status-warning-text/80\">\n {tr(\n \"auth.register.accountDeactivatedHelp\",\n \"If this is your account, you can reactivate it instead of creating a new one.\",\n )}\n </p>\n <button\n type=\"button\"\n onClick={() =>\n navigate(\"/auth/reactivate\", {\n state: { email: formData.email },\n })\n }\n className=\"text-sm font-semibold text-status-warning-text underline underline-offset-2\"\n >\n {tr(\"auth.register.reactivateCta\", \"Reactivate my account\")}\n </button>\n </div>\n </div>\n )}\n\n {/* Admin-removed / suspended → point to support (no self-service path) */}\n {(isAccountRemoved || isAccountSuspended) && (\n <div className=\"flex items-start gap-3 p-4 rounded-xl bg-status-warning-bg-subtle border border-status-warning-border\">\n <AlertIcon className=\"size-5 text-status-warning-text flex-shrink-0 mt-0.5\" />\n <p className=\"text-sm text-status-warning-text\">{error}</p>\n </div>\n )}\n\n {/* Generic error / validation alert */}\n {((error &&\n !isAccountDeactivated &&\n !isAccountRemoved &&\n !isAccountSuspended) ||\n validationErrors.length > 0) && (\n <div className=\"flex items-start gap-3 p-4 rounded-xl bg-status-error-bg-subtle border border-status-error-border\">\n <AlertIcon className=\"size-5 text-status-error-text flex-shrink-0 mt-0.5\" />\n <div className=\"space-y-1\">\n {error &&\n !isAccountDeactivated &&\n !isAccountRemoved &&\n !isAccountSuspended && (\n <p className=\"text-sm text-status-error-text\">{error}</p>\n )}\n {validationErrors.map((err) => (\n <p key={err} className=\"text-sm text-status-error-text\">\n {err}\n </p>\n ))}\n </div>\n </div>\n )}\n\n {/* Name Fields */}\n <div className=\"grid grid-cols-2 gap-4\">\n <div className=\"space-y-2\">\n <label\n htmlFor=\"firstName\"\n className=\"block text-sm font-medium text-[var(--color-text-primary)]\"\n >\n {tr(\"auth.register.firstName\", \"First Name\")}\n </label>\n <input\n id=\"firstName\"\n type=\"text\"\n required\n aria-required=\"true\"\n aria-label={tr(\"auth.register.firstName\", \"First Name\")}\n value={formData.firstName}\n onChange={(e) =>\n setFormData({ ...formData, firstName: e.target.value })\n }\n className=\"block w-full px-4 py-3 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] text-[var(--color-text-primary)] placeholder:text-[var(--color-text-tertiary)] focus:outline-none focus:ring-2 focus:ring-action-primary-bg/20 focus:border-action-primary-bg transition-all\"\n placeholder={tr(\n \"auth.register.firstNamePlaceholder\",\n \"Enter first name\",\n )}\n />\n </div>\n <div className=\"space-y-2\">\n <label\n htmlFor=\"lastName\"\n className=\"block text-sm font-medium text-[var(--color-text-primary)]\"\n >\n {tr(\"auth.register.lastName\", \"Last Name\")}\n </label>\n <input\n id=\"lastName\"\n type=\"text\"\n required\n aria-required=\"true\"\n aria-label={tr(\"auth.register.lastName\", \"Last Name\")}\n value={formData.lastName}\n onChange={(e) =>\n setFormData({ ...formData, lastName: e.target.value })\n }\n className=\"block w-full px-4 py-3 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] text-[var(--color-text-primary)] placeholder:text-[var(--color-text-tertiary)] focus:outline-none focus:ring-2 focus:ring-action-primary-bg/20 focus:border-action-primary-bg transition-all\"\n placeholder={tr(\n \"auth.register.lastNamePlaceholder\",\n \"Enter last name\",\n )}\n />\n </div>\n </div>\n\n {/* Username Input */}\n <div className=\"space-y-2\">\n <label\n htmlFor=\"username\"\n className=\"block text-sm font-medium text-[var(--color-text-primary)]\"\n >\n {tr(\"auth.register.username\", \"Username\")}\n </label>\n <div className=\"relative\">\n <div className=\"absolute inset-y-0 left-0 pl-3.5 flex items-center pointer-events-none\">\n <UserIcon className=\"size-5 text-[var(--color-text-tertiary)]\" />\n </div>\n <input\n id=\"username\"\n type=\"text\"\n required\n aria-required=\"true\"\n aria-label={tr(\"auth.register.username\", \"Username\")}\n autoComplete=\"username\"\n value={formData.username}\n onChange={(e) =>\n setFormData({ ...formData, username: e.target.value })\n }\n className=\"block w-full pl-[3.5rem] pr-4 py-3 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] text-[var(--color-text-primary)] placeholder:text-[var(--color-text-tertiary)] focus:outline-none focus:ring-2 focus:ring-action-primary-bg/20 focus:border-action-primary-bg transition-all\"\n placeholder={tr(\n \"auth.register.usernamePlaceholder\",\n \"Choose a username\",\n )}\n />\n </div>\n </div>\n\n {/* Email Input */}\n <div className=\"space-y-2\">\n <label\n htmlFor=\"email\"\n className=\"block text-sm font-medium text-[var(--color-text-primary)]\"\n >\n {tr(\"auth.register.email\", \"Email\")}\n </label>\n <div className=\"relative\">\n <div className=\"absolute inset-y-0 left-0 pl-3.5 flex items-center pointer-events-none\">\n <EmailIcon className=\"size-5 text-[var(--color-text-tertiary)]\" />\n </div>\n <input\n id=\"email\"\n type=\"email\"\n required\n aria-required=\"true\"\n aria-label={tr(\"auth.register.email\", \"Email\")}\n autoComplete=\"email\"\n value={formData.email}\n onChange={(e) =>\n setFormData({ ...formData, email: e.target.value })\n }\n className=\"block w-full pl-[3.5rem] pr-4 py-3 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] text-[var(--color-text-primary)] placeholder:text-[var(--color-text-tertiary)] focus:outline-none focus:ring-2 focus:ring-action-primary-bg/20 focus:border-action-primary-bg transition-all\"\n placeholder={tr(\n \"auth.register.emailPlaceholder\",\n \"you@example.com\",\n )}\n />\n </div>\n </div>\n\n {/* Password Input */}\n <div className=\"space-y-2\">\n <label\n htmlFor=\"password\"\n className=\"block text-sm font-medium text-[var(--color-text-primary)]\"\n >\n {tr(\"auth.register.password\", \"Password\")}\n </label>\n <div className=\"relative\">\n <div className=\"absolute inset-y-0 left-0 pl-3.5 flex items-center pointer-events-none\">\n <LockIcon className=\"size-5 text-[var(--color-text-tertiary)]\" />\n </div>\n <input\n id=\"password\"\n type={showPassword ? \"text\" : \"password\"}\n required\n aria-required=\"true\"\n aria-label={tr(\"auth.register.password\", \"Password\")}\n autoComplete=\"new-password\"\n value={formData.password}\n onChange={(e) =>\n setFormData({ ...formData, password: e.target.value })\n }\n className=\"block w-full pl-[3.5rem] pr-12 py-3 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] text-[var(--color-text-primary)] placeholder:text-[var(--color-text-tertiary)] focus:outline-none focus:ring-2 focus:ring-action-primary-bg/20 focus:border-action-primary-bg transition-all\"\n placeholder={tr(\n \"auth.register.passwordPlaceholder\",\n \"Create a strong password\",\n )}\n />\n <button\n type=\"button\"\n onClick={() => setShowPassword(!showPassword)}\n aria-label={showPassword ? \"Hide password\" : \"Show password\"}\n className=\"absolute inset-y-0 right-0 pr-3.5 flex items-center text-[var(--color-text-tertiary)] hover:text-[var(--color-text-secondary)] transition-colors\"\n >\n {showPassword ? (\n <EyeOffIcon className=\"size-5\" />\n ) : (\n <EyeIcon className=\"size-5\" />\n )}\n </button>\n </div>\n\n {/* Password Strength Indicator */}\n {formData.password && (\n <div className=\"space-y-3 pt-2\">\n {/* Strength Bar */}\n <div className=\"space-y-1\">\n <div className=\"h-1.5 bg-[var(--color-bg-sunken)] rounded-full overflow-hidden\">\n <div\n className={`h-full ${passwordStrength.color} ${passwordStrength.width} transition-all duration-300`}\n />\n </div>\n <p className=\"text-xs text-[var(--color-text-tertiary)] capitalize\">\n {tr(\"auth.register.passwordStrength\", \"Password strength:\")}{\" \"}\n <span className=\"font-medium\">{passwordStrength.level}</span>\n </p>\n </div>\n\n {/* Requirements Checklist */}\n <div className=\"grid grid-cols-2 gap-2\">\n {passwordChecks.map((check) => (\n <div key={check.label} className=\"flex items-center gap-2\">\n {check.met ? (\n <CheckIcon className=\"size-4 text-status-success-text\" />\n ) : (\n <XIcon className=\"size-4 text-[var(--color-text-tertiary)]\" />\n )}\n <span\n className={`text-xs ${\n check.met\n ? \"text-status-success-text\"\n : \"text-[var(--color-text-tertiary)]\"\n }`}\n >\n {check.label}\n </span>\n </div>\n ))}\n </div>\n </div>\n )}\n </div>\n\n {/* Terms Agreement */}\n <p className=\"text-xs text-[var(--color-text-tertiary)] leading-relaxed\">\n {tr(\n \"auth.register.agreeToTerms\",\n \"By creating an account, you agree to our\",\n )}{\" \"}\n <a href=\"/legal/terms\" className=\"text-text-link hover:underline\">\n {tr(\"auth.register.termsOfService\", \"Terms of Service\")}\n </a>{\" \"}\n {tr(\"auth.register.and\", \"and\")}{\" \"}\n <a href=\"/legal/privacy\" className=\"text-text-link hover:underline\">\n {tr(\"auth.register.privacyPolicy\", \"Privacy Policy\")}\n </a>\n .\n </p>\n\n {/* reCAPTCHA v2 Checkbox */}\n <CaptchaV2 onVerify={setCaptchaToken} />\n\n {/* Submit Button */}\n <button\n type=\"submit\"\n disabled={isLoading}\n className=\"auth-primary-button flex w-full items-center justify-center gap-2 px-4 py-3 font-semibold\"\n >\n {isLoading ? (\n <>\n <SpinnerIcon className=\"size-5 animate-spin\" />\n <span>\n {tr(\"auth.register.creatingAccount\", \"Creating account...\")}\n </span>\n </>\n ) : (\n tr(\"auth.register.createAccountBtn\", \"Create account\")\n )}\n </button>\n </form>\n\n {/* Divider */}\n <div className=\"relative my-8\">\n <div className=\"absolute inset-0 flex items-center\">\n <div className=\"w-full border-t border-[var(--color-border-default)]\" />\n </div>\n <div className=\"relative flex justify-center\">\n <span className=\"px-4 bg-[var(--color-bg-canvas)] text-sm text-[var(--color-text-tertiary)]\">\n {tr(\"auth.register.orSignUpWith\", \"or sign up with\")}\n </span>\n </div>\n </div>\n\n {/* OAuth Error */}\n {oauthError && (\n <div className=\"flex items-start gap-3 p-4 rounded-xl border bg-status-error-bg-subtle border-status-error-border\">\n <AlertIcon className=\"size-5 flex-shrink-0 mt-0.5 text-status-error-text\" />\n <p className=\"text-sm text-status-error-text\">{oauthError}</p>\n </div>\n )}\n\n {/* OAuth Buttons */}\n <div className=\"grid grid-cols-3 gap-3\">\n {oauthProviders.map((provider) => (\n <button\n key={provider.name}\n type=\"button\"\n onClick={provider.onClick}\n disabled={provider.disabled}\n title={provider.tooltip}\n className={`flex items-center justify-center gap-2 px-4 py-3 rounded-xl border transition-all duration-200 group ${\n provider.disabled\n ? \"border-[var(--color-border-subtle)] bg-[var(--color-bg-disabled)] cursor-not-allowed opacity-50\"\n : \"border-[var(--color-border-default)] bg-[var(--color-bg-surface)] hover:bg-[var(--color-bg-sunken)] hover:border-[var(--color-border-strong)]\"\n }`}\n >\n <provider.icon\n className={`size-5 ${provider.disabled ? \"opacity-50\" : \"\"}`}\n />\n <span\n className={`sr-only sm:not-sr-only text-sm font-medium transition-colors hidden sm:inline ${\n provider.disabled\n ? \"text-[var(--color-text-disabled)]\"\n : \"text-[var(--color-text-secondary)] group-hover:text-[var(--color-text-primary)]\"\n }`}\n >\n {provider.name}\n </span>\n </button>\n ))}\n </div>\n\n {/* Sign In Link */}\n <p className=\"mt-8 text-center text-sm text-[var(--color-text-secondary)]\">\n {tr(\"auth.register.alreadyHaveAccount\", \"Already have an account?\")}{\" \"}\n <button\n type=\"button\"\n onClick={() => navigate(\"/auth/login\")}\n className=\"font-semibold text-text-link hover:text-text-linkHover transition-colors\"\n >\n {tr(\"auth.register.signIn\", \"Sign in\")}\n </button>\n </p>\n </AuthLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"RegisterPage.js","names":[],"sources":["../../../src/auth/pages/RegisterPage.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport { useNavigate } from \"react-router-dom\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell\";\nimport { validateEmail, validatePassword } from \"../utils/validation\";\nimport { useAuthOperations } from \"../hooks/useAuthOperations\";\nimport { useOAuthOperations } from \"../hooks/useOAuthOperations\";\nimport { AuthLayout } from \"../components/AuthLayout\";\nimport {\n EmailIcon,\n LockIcon,\n UserIcon,\n EyeIcon,\n EyeOffIcon,\n AlertIcon,\n CheckIcon,\n XIcon,\n SpinnerIcon,\n GoogleIcon,\n GitHubIcon,\n MicrosoftIcon,\n} from \"../components/AuthIcons\";\nimport { useAuth } from \"../providers/AuthProvider\";\nimport { CaptchaV2 } from \"../components/CaptchaV2\";\nimport { useSignupCaptcha } from \"../hooks/useSignupCaptcha\";\nimport type { SignUpInput } from \"../types\";\n\n/**\n * Registration page with professional design\n */\nexport function RegisterPage() {\n const { productName } = useAuth();\n const navigate = useNavigate();\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n const { signUp, resendVerification, isLoading, errorCode } =\n useAuthOperations();\n const { initiateGoogleOAuth, initiateMicrosoftOAuth, isProviderConfigured } =\n useOAuthOperations();\n const [showPassword, setShowPassword] = useState(false);\n const [formData, setFormData] = useState<SignUpInput>({\n email: \"\",\n username: \"\",\n password: \"\",\n firstName: \"\",\n lastName: \"\",\n });\n const [validationErrors, setValidationErrors] = useState<string[]>([]);\n const [signupSuccess, setSignupSuccess] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [oauthError, setOauthError] = useState<string | null>(null);\n const [captchaToken, setCaptchaToken] = useState<string | null>(null);\n // reCAPTCHA v2 tokens are single-use. Bumping this key remounts the widget so\n // a fresh token is issued after a failed submit (the prior token is consumed\n // server-side; reusing it returns timeout-or-duplicate → CAPTCHA_FAILED loop).\n const [captchaResetKey, setCaptchaResetKey] = useState(0);\n const { enabled: captchaEnabled, siteKey: captchaSiteKey } =\n useSignupCaptcha();\n const [isResendingVerification, setIsResendingVerification] = useState(false);\n const [resendError, setResendError] = useState<string | null>(null);\n\n // Signing up with an email that already belongs to a non-active account:\n // route the user to the right recovery path instead of dead-ending.\n const isAccountDeactivated =\n Boolean(error) && errorCode === \"ACCOUNT_DEACTIVATED\";\n const isAccountRemoved = Boolean(error) && errorCode === \"ACCOUNT_REMOVED\";\n const isAccountSuspended =\n Boolean(error) && errorCode === \"ACCOUNT_SUSPENDED\";\n\n // Password strength indicators\n const passwordChecks = (() => {\n const password = formData.password;\n return [\n {\n label: tr(\"auth.register.pwMin8\", \"At least 12 characters\"),\n met: password.length >= 12,\n },\n {\n label: tr(\"auth.register.pwUppercase\", \"One uppercase letter\"),\n met: /[A-Z]/.test(password),\n },\n {\n label: tr(\"auth.register.pwLowercase\", \"One lowercase letter\"),\n met: /[a-z]/.test(password),\n },\n {\n label: tr(\"auth.register.pwNumber\", \"One number\"),\n met: /\\d/.test(password),\n },\n {\n label: tr(\"auth.register.pwSpecial\", \"One special character\"),\n met: /[!@#$%^&*(),.?\":{}|<>]/.test(password),\n },\n ];\n })();\n\n const passwordStrength = (() => {\n const metCount = passwordChecks.filter((c) => c.met).length;\n if (metCount <= 2)\n return {\n level: tr(\"auth.register.passwordStrengthWeak\", \"weak\"),\n color: \"bg-status-error-bg-subtle0\",\n width: \"w-1/4\",\n };\n if (metCount <= 3)\n return {\n level: tr(\"auth.register.passwordStrengthFair\", \"fair\"),\n color: \"bg-status-warning-text\",\n width: \"w-2/4\",\n };\n if (metCount <= 4)\n return {\n level: tr(\"auth.register.passwordStrengthGood\", \"good\"),\n color: \"bg-status-warning-bg-subtle0\",\n width: \"w-3/4\",\n };\n return {\n level: tr(\"auth.register.passwordStrengthStrong\", \"strong\"),\n color: \"bg-status-success-text\",\n width: \"w-full\",\n };\n })();\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n\n const errors: string[] = [];\n if (!validateEmail(formData.email)) {\n errors.push(\n tr(\"auth.register.invalidEmail\", \"Please enter a valid email address\"),\n );\n }\n if (!formData.username || formData.username.length < 3) {\n errors.push(\n tr(\n \"auth.register.usernameMin3\",\n \"Username must be at least 3 characters\",\n ),\n );\n }\n if (!/^[a-zA-Z0-9_-]+$/.test(formData.username)) {\n errors.push(\n tr(\n \"auth.register.usernameChars\",\n \"Username can only contain letters, numbers, underscores, and hyphens\",\n ),\n );\n }\n const passwordValidation = validatePassword(formData.password);\n if (!passwordValidation.valid) {\n errors.push(...passwordValidation.errors);\n }\n\n if (captchaEnabled && !captchaToken) {\n errors.push(\n tr(\n \"auth.register.completeCaptcha\",\n \"Please complete the reCAPTCHA verification\",\n ),\n );\n }\n\n if (errors.length > 0) {\n setValidationErrors(errors);\n return;\n }\n\n setValidationErrors([]);\n setError(null);\n try {\n await signUp({\n ...formData,\n ...(captchaToken ? { recaptchaToken: captchaToken } : {}),\n });\n // Sign up successful - check if email verification is required\n // (Backend returns empty tokens when email verification is needed)\n setSignupSuccess(true);\n } catch (err) {\n const message =\n err instanceof Error\n ? err.message\n : tr(\"auth.register.signUpFailed\", \"Sign up failed\");\n setError(message);\n console.error(\"Sign up failed:\", err);\n // The captcha token (if any) was consumed during this attempt — discard it\n // and remount the widget so the user gets a fresh, solvable challenge on\n // retry instead of being trapped in a CAPTCHA_FAILED loop.\n if (captchaEnabled) {\n setCaptchaToken(null);\n setCaptchaResetKey((k) => k + 1);\n }\n }\n };\n\n /**\n * Handle Google OAuth signup\n */\n const handleGoogleSignup = () => {\n setOauthError(null);\n try {\n initiateGoogleOAuth();\n } catch (err) {\n setOauthError(\n err instanceof Error\n ? err.message\n : tr(\n \"auth.register.googleSignUpFailed\",\n \"Failed to initiate Google sign up\",\n ),\n );\n }\n };\n\n /**\n * Handle Microsoft OAuth signup\n */\n const handleMicrosoftSignup = () => {\n setOauthError(null);\n try {\n initiateMicrosoftOAuth();\n } catch (err) {\n setOauthError(\n err instanceof Error\n ? err.message\n : tr(\n \"auth.register.microsoftSignUpFailed\",\n \"Failed to initiate Microsoft sign up\",\n ),\n );\n }\n };\n\n // Check if OAuth providers are configured\n const isGoogleConfigured = isProviderConfigured(\"google\");\n const isMicrosoftConfigured = isProviderConfigured(\"microsoft\");\n\n const oauthProviders = [\n {\n name: \"Google\",\n icon: GoogleIcon,\n onClick: handleGoogleSignup,\n disabled: !isGoogleConfigured,\n tooltip: isGoogleConfigured\n ? undefined\n : tr(\n \"auth.register.googleNotConfigured\",\n \"Google sign up is not configured\",\n ),\n },\n {\n name: \"GitHub\",\n icon: GitHubIcon,\n onClick: () => {},\n disabled: true,\n tooltip: tr(\n \"auth.register.githubNotConfigured\",\n \"GitHub sign up is not configured\",\n ),\n },\n {\n name: \"Microsoft\",\n icon: MicrosoftIcon,\n onClick: handleMicrosoftSignup,\n disabled: !isMicrosoftConfigured,\n tooltip: isMicrosoftConfigured\n ? undefined\n : tr(\n \"auth.register.microsoftNotConfigured\",\n \"Microsoft sign up is not configured\",\n ),\n },\n ];\n\n const verificationEmailMessage = tr(\n \"auth.register.verificationEmailSentTo\",\n \"We've sent a verification email to {{email}}. Please check your inbox and click the verification link to activate your account.\",\n );\n const [verificationEmailPrefix, verificationEmailSuffix = \"\"] =\n verificationEmailMessage.split(\"{{email}}\");\n\n // Show success state after signup\n if (signupSuccess) {\n return (\n <AuthLayout\n title={tr(\"auth.register.checkEmail\", \"Check your email\")}\n subtitle={tr(\n \"auth.register.verificationLinkSent\",\n \"We've sent you a verification link\",\n )}\n >\n <div className=\"flex flex-col items-center justify-center py-6\">\n {/* Success Icon */}\n <div className=\"size-20 rounded-full bg-status-success-bg-subtle flex items-center justify-center mb-6\">\n <svg\n className=\"size-10 text-status-success-text\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z\"\n />\n </svg>\n </div>\n\n <h3 className=\"text-lg font-semibold text-[var(--color-text-primary)] mb-2\">\n {tr(\n \"auth.register.accountCreated\",\n \"Account created successfully!\",\n )}\n </h3>\n\n <p className=\"text-center text-[var(--color-text-secondary)] mb-8 max-w-sm\">\n {verificationEmailPrefix}\n <span className=\"font-medium text-[var(--color-text-primary)]\">\n {formData.email}\n </span>\n {verificationEmailSuffix}\n </p>\n\n <div className=\"space-y-3 w-full\">\n <button\n type=\"button\"\n onClick={() => navigate(\"/auth/login\")}\n className=\"auth-primary-button w-full px-4 py-3 font-semibold\"\n >\n {tr(\"auth.register.continueToSignIn\", \"Continue to sign in\")}\n </button>\n\n {resendError && (\n <p className=\"text-center text-sm text-status-error-text\">\n {resendError}\n </p>\n )}\n <p className=\"text-center text-sm text-[var(--color-text-tertiary)]\">\n {tr(\n \"auth.register.didntReceiveEmail\",\n \"Didn't receive the email? Check your spam folder or\",\n )}{\" \"}\n <button\n type=\"button\"\n disabled={isResendingVerification}\n onClick={async () => {\n setResendError(null);\n setIsResendingVerification(true);\n try {\n await resendVerification(formData.email);\n } catch (err) {\n setResendError(\n err instanceof Error\n ? err.message\n : tr(\n \"auth.register.resendFailed\",\n \"Failed to resend verification email\",\n ),\n );\n } finally {\n setIsResendingVerification(false);\n }\n }}\n className=\"text-text-link hover:text-text-linkHover font-medium disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n {isResendingVerification\n ? tr(\"auth.register.resending\", \"resending...\")\n : tr(\"auth.register.tryAgain\", \"try again\")}\n </button>\n </p>\n </div>\n </div>\n </AuthLayout>\n );\n }\n\n return (\n <AuthLayout\n title={tr(\"auth.register.createAccount\", \"Create your account\")}\n subtitle={tr(\n \"auth.register.startJourney\",\n \"Start your journey with {{productName}}\",\n ).replace(\"{{productName}}\", productName || \"Burdenoff Workspaces\")}\n productName={productName}\n >\n <form onSubmit={handleSubmit} className=\"space-y-5\">\n {/* Deactivated account → offer self-service reactivation */}\n {isAccountDeactivated && (\n <div className=\"flex items-start gap-3 p-4 rounded-xl bg-status-warning-bg-subtle border border-status-warning-border\">\n <AlertIcon className=\"size-5 text-status-warning-text flex-shrink-0 mt-0.5\" />\n <div className=\"space-y-2\">\n <p className=\"text-sm font-medium text-status-warning-text\">\n {tr(\n \"auth.register.accountDeactivated\",\n \"This email belongs to a deactivated account.\",\n )}\n </p>\n <p className=\"text-xs text-status-warning-text/80\">\n {tr(\n \"auth.register.accountDeactivatedHelp\",\n \"If this is your account, you can reactivate it instead of creating a new one.\",\n )}\n </p>\n <button\n type=\"button\"\n onClick={() =>\n navigate(\"/auth/reactivate\", {\n state: { email: formData.email },\n })\n }\n className=\"text-sm font-semibold text-status-warning-text underline underline-offset-2\"\n >\n {tr(\"auth.register.reactivateCta\", \"Reactivate my account\")}\n </button>\n </div>\n </div>\n )}\n\n {/* Admin-removed / suspended → point to support (no self-service path) */}\n {(isAccountRemoved || isAccountSuspended) && (\n <div className=\"flex items-start gap-3 p-4 rounded-xl bg-status-warning-bg-subtle border border-status-warning-border\">\n <AlertIcon className=\"size-5 text-status-warning-text flex-shrink-0 mt-0.5\" />\n <p className=\"text-sm text-status-warning-text\">{error}</p>\n </div>\n )}\n\n {/* Generic error / validation alert */}\n {((error &&\n !isAccountDeactivated &&\n !isAccountRemoved &&\n !isAccountSuspended) ||\n validationErrors.length > 0) && (\n <div className=\"flex items-start gap-3 p-4 rounded-xl bg-status-error-bg-subtle border border-status-error-border\">\n <AlertIcon className=\"size-5 text-status-error-text flex-shrink-0 mt-0.5\" />\n <div className=\"space-y-1\">\n {error &&\n !isAccountDeactivated &&\n !isAccountRemoved &&\n !isAccountSuspended && (\n <p className=\"text-sm text-status-error-text\">{error}</p>\n )}\n {validationErrors.map((err) => (\n <p key={err} className=\"text-sm text-status-error-text\">\n {err}\n </p>\n ))}\n </div>\n </div>\n )}\n\n {/* Name Fields */}\n <div className=\"grid grid-cols-2 gap-4\">\n <div className=\"space-y-2\">\n <label\n htmlFor=\"firstName\"\n className=\"block text-sm font-medium text-[var(--color-text-primary)]\"\n >\n {tr(\"auth.register.firstName\", \"First Name\")}\n </label>\n <input\n id=\"firstName\"\n type=\"text\"\n required\n aria-required=\"true\"\n aria-label={tr(\"auth.register.firstName\", \"First Name\")}\n value={formData.firstName}\n onChange={(e) =>\n setFormData({ ...formData, firstName: e.target.value })\n }\n className=\"block w-full px-4 py-3 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] text-[var(--color-text-primary)] placeholder:text-[var(--color-text-tertiary)] focus:outline-none focus:ring-2 focus:ring-action-primary-bg/20 focus:border-action-primary-bg transition-all\"\n placeholder={tr(\n \"auth.register.firstNamePlaceholder\",\n \"Enter first name\",\n )}\n />\n </div>\n <div className=\"space-y-2\">\n <label\n htmlFor=\"lastName\"\n className=\"block text-sm font-medium text-[var(--color-text-primary)]\"\n >\n {tr(\"auth.register.lastName\", \"Last Name\")}\n </label>\n <input\n id=\"lastName\"\n type=\"text\"\n required\n aria-required=\"true\"\n aria-label={tr(\"auth.register.lastName\", \"Last Name\")}\n value={formData.lastName}\n onChange={(e) =>\n setFormData({ ...formData, lastName: e.target.value })\n }\n className=\"block w-full px-4 py-3 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] text-[var(--color-text-primary)] placeholder:text-[var(--color-text-tertiary)] focus:outline-none focus:ring-2 focus:ring-action-primary-bg/20 focus:border-action-primary-bg transition-all\"\n placeholder={tr(\n \"auth.register.lastNamePlaceholder\",\n \"Enter last name\",\n )}\n />\n </div>\n </div>\n\n {/* Username Input */}\n <div className=\"space-y-2\">\n <label\n htmlFor=\"username\"\n className=\"block text-sm font-medium text-[var(--color-text-primary)]\"\n >\n {tr(\"auth.register.username\", \"Username\")}\n </label>\n <div className=\"relative\">\n <div className=\"absolute inset-y-0 left-0 pl-3.5 flex items-center pointer-events-none\">\n <UserIcon className=\"size-5 text-[var(--color-text-tertiary)]\" />\n </div>\n <input\n id=\"username\"\n type=\"text\"\n required\n aria-required=\"true\"\n aria-label={tr(\"auth.register.username\", \"Username\")}\n autoComplete=\"username\"\n value={formData.username}\n onChange={(e) =>\n setFormData({ ...formData, username: e.target.value })\n }\n className=\"block w-full pl-[3.5rem] pr-4 py-3 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] text-[var(--color-text-primary)] placeholder:text-[var(--color-text-tertiary)] focus:outline-none focus:ring-2 focus:ring-action-primary-bg/20 focus:border-action-primary-bg transition-all\"\n placeholder={tr(\n \"auth.register.usernamePlaceholder\",\n \"Choose a username\",\n )}\n />\n </div>\n </div>\n\n {/* Email Input */}\n <div className=\"space-y-2\">\n <label\n htmlFor=\"email\"\n className=\"block text-sm font-medium text-[var(--color-text-primary)]\"\n >\n {tr(\"auth.register.email\", \"Email\")}\n </label>\n <div className=\"relative\">\n <div className=\"absolute inset-y-0 left-0 pl-3.5 flex items-center pointer-events-none\">\n <EmailIcon className=\"size-5 text-[var(--color-text-tertiary)]\" />\n </div>\n <input\n id=\"email\"\n type=\"email\"\n required\n aria-required=\"true\"\n aria-label={tr(\"auth.register.email\", \"Email\")}\n autoComplete=\"email\"\n value={formData.email}\n onChange={(e) =>\n setFormData({ ...formData, email: e.target.value })\n }\n className=\"block w-full pl-[3.5rem] pr-4 py-3 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] text-[var(--color-text-primary)] placeholder:text-[var(--color-text-tertiary)] focus:outline-none focus:ring-2 focus:ring-action-primary-bg/20 focus:border-action-primary-bg transition-all\"\n placeholder={tr(\n \"auth.register.emailPlaceholder\",\n \"you@example.com\",\n )}\n />\n </div>\n </div>\n\n {/* Password Input */}\n <div className=\"space-y-2\">\n <label\n htmlFor=\"password\"\n className=\"block text-sm font-medium text-[var(--color-text-primary)]\"\n >\n {tr(\"auth.register.password\", \"Password\")}\n </label>\n <div className=\"relative\">\n <div className=\"absolute inset-y-0 left-0 pl-3.5 flex items-center pointer-events-none\">\n <LockIcon className=\"size-5 text-[var(--color-text-tertiary)]\" />\n </div>\n <input\n id=\"password\"\n type={showPassword ? \"text\" : \"password\"}\n required\n aria-required=\"true\"\n aria-label={tr(\"auth.register.password\", \"Password\")}\n autoComplete=\"new-password\"\n value={formData.password}\n onChange={(e) =>\n setFormData({ ...formData, password: e.target.value })\n }\n className=\"block w-full pl-[3.5rem] pr-12 py-3 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] text-[var(--color-text-primary)] placeholder:text-[var(--color-text-tertiary)] focus:outline-none focus:ring-2 focus:ring-action-primary-bg/20 focus:border-action-primary-bg transition-all\"\n placeholder={tr(\n \"auth.register.passwordPlaceholder\",\n \"Create a strong password\",\n )}\n />\n <button\n type=\"button\"\n onClick={() => setShowPassword(!showPassword)}\n aria-label={showPassword ? \"Hide password\" : \"Show password\"}\n className=\"absolute inset-y-0 right-0 pr-3.5 flex items-center text-[var(--color-text-tertiary)] hover:text-[var(--color-text-secondary)] transition-colors\"\n >\n {showPassword ? (\n <EyeOffIcon className=\"size-5\" />\n ) : (\n <EyeIcon className=\"size-5\" />\n )}\n </button>\n </div>\n\n {/* Password Strength Indicator */}\n {formData.password && (\n <div className=\"space-y-3 pt-2\">\n {/* Strength Bar */}\n <div className=\"space-y-1\">\n <div className=\"h-1.5 bg-[var(--color-bg-sunken)] rounded-full overflow-hidden\">\n <div\n className={`h-full ${passwordStrength.color} ${passwordStrength.width} transition-all duration-300`}\n />\n </div>\n <p className=\"text-xs text-[var(--color-text-tertiary)] capitalize\">\n {tr(\"auth.register.passwordStrength\", \"Password strength:\")}{\" \"}\n <span className=\"font-medium\">{passwordStrength.level}</span>\n </p>\n </div>\n\n {/* Requirements Checklist */}\n <div className=\"grid grid-cols-2 gap-2\">\n {passwordChecks.map((check) => (\n <div key={check.label} className=\"flex items-center gap-2\">\n {check.met ? (\n <CheckIcon className=\"size-4 text-status-success-text\" />\n ) : (\n <XIcon className=\"size-4 text-[var(--color-text-tertiary)]\" />\n )}\n <span\n className={`text-xs ${\n check.met\n ? \"text-status-success-text\"\n : \"text-[var(--color-text-tertiary)]\"\n }`}\n >\n {check.label}\n </span>\n </div>\n ))}\n </div>\n </div>\n )}\n </div>\n\n {/* Terms Agreement */}\n <p className=\"text-xs text-[var(--color-text-tertiary)] leading-relaxed\">\n {tr(\n \"auth.register.agreeToTerms\",\n \"By creating an account, you agree to our\",\n )}{\" \"}\n <a href=\"/legal/terms\" className=\"text-text-link hover:underline\">\n {tr(\"auth.register.termsOfService\", \"Terms of Service\")}\n </a>{\" \"}\n {tr(\"auth.register.and\", \"and\")}{\" \"}\n <a href=\"/legal/privacy\" className=\"text-text-link hover:underline\">\n {tr(\"auth.register.privacyPolicy\", \"Privacy Policy\")}\n </a>\n .\n </p>\n\n {/* reCAPTCHA v2 Checkbox — enabled per-product via the platform catalog.\n `key` remounts it after a failed submit so a fresh token is issued. */}\n <CaptchaV2\n key={captchaResetKey}\n enabled={captchaEnabled}\n siteKey={captchaSiteKey}\n onVerify={setCaptchaToken}\n onExpired={() => setCaptchaToken(null)}\n />\n\n {/* Submit Button */}\n <button\n type=\"submit\"\n disabled={isLoading}\n className=\"auth-primary-button flex w-full items-center justify-center gap-2 px-4 py-3 font-semibold\"\n >\n {isLoading ? (\n <>\n <SpinnerIcon className=\"size-5 animate-spin\" />\n <span>\n {tr(\"auth.register.creatingAccount\", \"Creating account...\")}\n </span>\n </>\n ) : (\n tr(\"auth.register.createAccountBtn\", \"Create account\")\n )}\n </button>\n </form>\n\n {/* Divider */}\n <div className=\"relative my-8\">\n <div className=\"absolute inset-0 flex items-center\">\n <div className=\"w-full border-t border-[var(--color-border-default)]\" />\n </div>\n <div className=\"relative flex justify-center\">\n <span className=\"px-4 bg-[var(--color-bg-canvas)] text-sm text-[var(--color-text-tertiary)]\">\n {tr(\"auth.register.orSignUpWith\", \"or sign up with\")}\n </span>\n </div>\n </div>\n\n {/* OAuth Error */}\n {oauthError && (\n <div className=\"flex items-start gap-3 p-4 rounded-xl border bg-status-error-bg-subtle border-status-error-border\">\n <AlertIcon className=\"size-5 flex-shrink-0 mt-0.5 text-status-error-text\" />\n <p className=\"text-sm text-status-error-text\">{oauthError}</p>\n </div>\n )}\n\n {/* OAuth Buttons */}\n <div className=\"grid grid-cols-3 gap-3\">\n {oauthProviders.map((provider) => (\n <button\n key={provider.name}\n type=\"button\"\n onClick={provider.onClick}\n disabled={provider.disabled}\n title={provider.tooltip}\n className={`flex items-center justify-center gap-2 px-4 py-3 rounded-xl border transition-all duration-200 group ${\n provider.disabled\n ? \"border-[var(--color-border-subtle)] bg-[var(--color-bg-disabled)] cursor-not-allowed opacity-50\"\n : \"border-[var(--color-border-default)] bg-[var(--color-bg-surface)] hover:bg-[var(--color-bg-sunken)] hover:border-[var(--color-border-strong)]\"\n }`}\n >\n <provider.icon\n className={`size-5 ${provider.disabled ? \"opacity-50\" : \"\"}`}\n />\n <span\n className={`sr-only sm:not-sr-only text-sm font-medium transition-colors hidden sm:inline ${\n provider.disabled\n ? \"text-[var(--color-text-disabled)]\"\n : \"text-[var(--color-text-secondary)] group-hover:text-[var(--color-text-primary)]\"\n }`}\n >\n {provider.name}\n </span>\n </button>\n ))}\n </div>\n\n {/* Sign In Link */}\n <p className=\"mt-8 text-center text-sm text-[var(--color-text-secondary)]\">\n {tr(\"auth.register.alreadyHaveAccount\", \"Already have an account?\")}{\" \"}\n <button\n type=\"button\"\n onClick={() => navigate(\"/auth/login\")}\n className=\"font-semibold text-text-link hover:text-text-linkHover transition-colors\"\n >\n {tr(\"auth.register.signIn\", \"Sign in\")}\n </button>\n </p>\n </AuthLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;;AA6BA,SAAgB,IAAe;CAC7B,IAAM,EAAE,mBAAgB,GAAS,EAC3B,IAAW,IAAa,EACxB,EAAE,SAAM,IAAS,EACjB,KAAM,GAAa,MAA6B;EACpD,IAAM,IAAa,EAAE,EAAI;AACzB,SAAO,MAAe,IAAM,IAAW;IAEnC,EAAE,WAAQ,uBAAoB,cAAW,iBAC7C,GAAmB,EACf,EAAE,yBAAqB,4BAAwB,4BACnD,GAAoB,EAChB,CAAC,GAAc,MAAmB,EAAS,GAAM,EACjD,CAAC,GAAU,KAAe,EAAsB;EACpD,OAAO;EACP,UAAU;EACV,UAAU;EACV,WAAW;EACX,UAAU;EACX,CAAC,EACI,CAAC,GAAkB,KAAuB,EAAmB,EAAE,CAAC,EAChE,CAAC,GAAe,KAAoB,EAAS,GAAM,EACnD,CAAC,GAAO,KAAY,EAAwB,KAAK,EACjD,CAAC,GAAY,KAAiB,EAAwB,KAAK,EAC3D,CAAC,GAAc,KAAmB,EAAwB,KAAK,EAI/D,CAAC,GAAiB,KAAsB,EAAS,EAAE,EACnD,EAAE,SAAS,GAAgB,SAAS,OACxC,IAAkB,EACd,CAAC,GAAyB,KAA8B,EAAS,GAAM,EACvE,CAAC,GAAa,KAAkB,EAAwB,KAAK,EAI7D,IACJ,EAAQ,KAAU,MAAc,uBAC5B,IAAmB,EAAQ,KAAU,MAAc,mBACnD,IACJ,EAAQ,KAAU,MAAc,qBAG5B,WAAwB;EAC5B,IAAM,IAAW,EAAS;AAC1B,SAAO;GACL;IACE,OAAO,EAAG,wBAAwB,yBAAyB;IAC3D,KAAK,EAAS,UAAU;IACzB;GACD;IACE,OAAO,EAAG,6BAA6B,uBAAuB;IAC9D,KAAK,QAAQ,KAAK,EAAS;IAC5B;GACD;IACE,OAAO,EAAG,6BAA6B,uBAAuB;IAC9D,KAAK,QAAQ,KAAK,EAAS;IAC5B;GACD;IACE,OAAO,EAAG,0BAA0B,aAAa;IACjD,KAAK,KAAK,KAAK,EAAS;IACzB;GACD;IACE,OAAO,EAAG,2BAA2B,wBAAwB;IAC7D,KAAK,yBAAyB,KAAK,EAAS;IAC7C;GACF;KACC,EAEE,WAA0B;EAC9B,IAAM,IAAW,EAAe,QAAQ,MAAM,EAAE,IAAI,CAAC;AAmBrD,SAlBI,KAAY,IACP;GACL,OAAO,EAAG,sCAAsC,OAAO;GACvD,OAAO;GACP,OAAO;GACR,GACC,KAAY,IACP;GACL,OAAO,EAAG,sCAAsC,OAAO;GACvD,OAAO;GACP,OAAO;GACR,GACC,KAAY,IACP;GACL,OAAO,EAAG,sCAAsC,OAAO;GACvD,OAAO;GACP,OAAO;GACR,GACI;GACL,OAAO,EAAG,wCAAwC,SAAS;GAC3D,OAAO;GACP,OAAO;GACR;KACC,EAEE,KAAe,OAAO,MAAuB;AACjD,IAAE,gBAAgB;EAElB,IAAM,IAAmB,EAAE;AAc3B,EAbK,EAAc,EAAS,MAAM,IAChC,EAAO,KACL,EAAG,8BAA8B,qCAAqC,CACvE,GAEC,CAAC,EAAS,YAAY,EAAS,SAAS,SAAS,MACnD,EAAO,KACL,EACE,8BACA,yCACD,CACF,EAEE,mBAAmB,KAAK,EAAS,SAAS,IAC7C,EAAO,KACL,EACE,+BACA,uEACD,CACF;EAEH,IAAM,IAAqB,EAAiB,EAAS,SAAS;AAc9D,MAbK,EAAmB,SACtB,EAAO,KAAK,GAAG,EAAmB,OAAO,EAGvC,KAAkB,CAAC,KACrB,EAAO,KACL,EACE,iCACA,6CACD,CACF,EAGC,EAAO,SAAS,GAAG;AACrB,KAAoB,EAAO;AAC3B;;AAIF,EADA,EAAoB,EAAE,CAAC,EACvB,EAAS,KAAK;AACd,MAAI;AAOF,GANA,MAAM,EAAO;IACX,GAAG;IACH,GAAI,IAAe,EAAE,gBAAgB,GAAc,GAAG,EAAE;IACzD,CAAC,EAGF,EAAiB,GAAK;WACf,GAAK;AAUZ,GALA,EAHE,aAAe,QACX,EAAI,UACJ,EAAG,8BAA8B,iBAAiB,CACvC,EACjB,QAAQ,MAAM,mBAAmB,EAAI,EAIjC,MACF,EAAgB,KAAK,EACrB,GAAoB,MAAM,IAAI,EAAE;;IAQhC,WAA2B;AAC/B,IAAc,KAAK;AACnB,MAAI;AACF,OAAqB;WACd,GAAK;AACZ,KACE,aAAe,QACX,EAAI,UACJ,EACE,oCACA,oCACD,CACN;;IAOC,WAA8B;AAClC,IAAc,KAAK;AACnB,MAAI;AACF,OAAwB;WACjB,GAAK;AACZ,KACE,aAAe,QACX,EAAI,UACJ,EACE,uCACA,uCACD,CACN;;IAKC,IAAqB,EAAqB,SAAS,EACnD,IAAwB,EAAqB,YAAY,EAEzD,KAAiB;EACrB;GACE,MAAM;GACN,MAAM;GACN,SAAS;GACT,UAAU,CAAC;GACX,SAAS,IACL,KAAA,IACA,EACE,qCACA,mCACD;GACN;EACD;GACE,MAAM;GACN,MAAM;GACN,eAAe;GACf,UAAU;GACV,SAAS,EACP,qCACA,mCACD;GACF;EACD;GACE,MAAM;GACN,MAAM;GACN,SAAS;GACT,UAAU,CAAC;GACX,SAAS,IACL,KAAA,IACA,EACE,wCACA,sCACD;GACN;EACF,EAMK,CAAC,IAAyB,KAA0B,MAJzB,EAC/B,yCACA,kIACD,CAE0B,MAAM,YAAY;AAkG7C,QA/FI,IAEA,kBAAC,GAAD;EACE,OAAO,EAAG,4BAA4B,mBAAmB;EACzD,UAAU,EACR,sCACA,qCACD;YAED,kBAAC,OAAD;GAAK,WAAU;aAAf;IAEE,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,OAAD;MACE,WAAU;MACV,MAAK;MACL,SAAQ;MACR,QAAO;gBAEP,kBAAC,QAAD;OACE,eAAc;OACd,gBAAe;OACf,aAAa;OACb,GAAE;OACF,CAAA;MACE,CAAA;KACF,CAAA;IAEN,kBAAC,MAAD;KAAI,WAAU;eACX,EACC,gCACA,gCACD;KACE,CAAA;IAEL,kBAAC,KAAD;KAAG,WAAU;eAAb;MACG;MACD,kBAAC,QAAD;OAAM,WAAU;iBACb,EAAS;OACL,CAAA;MACN;MACC;;IAEJ,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,EAAS,cAAc;OACtC,WAAU;iBAET,EAAG,kCAAkC,sBAAsB;OACrD,CAAA;MAER,KACC,kBAAC,KAAD;OAAG,WAAU;iBACV;OACC,CAAA;MAEN,kBAAC,KAAD;OAAG,WAAU;iBAAb;QACG,EACC,mCACA,sDACD;QAAE;QACH,kBAAC,UAAD;SACE,MAAK;SACL,UAAU;SACV,SAAS,YAAY;AAEnB,UADA,EAAe,KAAK,EACpB,EAA2B,GAAK;AAChC,cAAI;AACF,iBAAM,EAAmB,EAAS,MAAM;mBACjC,GAAK;AACZ,aACE,aAAe,QACX,EAAI,UACJ,EACE,8BACA,sCACD,CACN;oBACO;AACR,aAA2B,GAAM;;;SAGrC,WAAU;mBAET,IACG,EAAG,2BAA2B,eAAe,GAC7C,EAAG,0BAA0B,YAAY;SACtC,CAAA;QACP;;MACA;;IACF;;EACK,CAAA,GAKf,kBAAC,GAAD;EACE,OAAO,EAAG,+BAA+B,sBAAsB;EAC/D,UAAU,EACR,8BACA,0CACD,CAAC,QAAQ,mBAAmB,KAAe,uBAAuB;EACtD;YANf;GAQE,kBAAC,QAAD;IAAM,UAAU;IAAc,WAAU;cAAxC;KAEG,KACC,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD,EAAW,WAAU,wDAAyD,CAAA,EAC9E,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,KAAD;SAAG,WAAU;mBACV,EACC,oCACA,+CACD;SACC,CAAA;QACJ,kBAAC,KAAD;SAAG,WAAU;mBACV,EACC,wCACA,gFACD;SACC,CAAA;QACJ,kBAAC,UAAD;SACE,MAAK;SACL,eACE,EAAS,oBAAoB,EAC3B,OAAO,EAAE,OAAO,EAAS,OAAO,EACjC,CAAC;SAEJ,WAAU;mBAET,EAAG,+BAA+B,wBAAwB;SACpD,CAAA;QACL;SACF;;MAIN,KAAoB,MACpB,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD,EAAW,WAAU,wDAAyD,CAAA,EAC9E,kBAAC,KAAD;OAAG,WAAU;iBAAoC;OAAU,CAAA,CACvD;;MAIL,KACD,CAAC,KACD,CAAC,KACD,CAAC,KACD,EAAiB,SAAS,MAC1B,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD,EAAW,WAAU,sDAAuD,CAAA,EAC5E,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACG,KACC,CAAC,KACD,CAAC,KACD,CAAC,KACC,kBAAC,KAAD;QAAG,WAAU;kBAAkC;QAAU,CAAA,EAE5D,EAAiB,KAAK,MACrB,kBAAC,KAAD;QAAa,WAAU;kBACpB;QACC,EAFI,EAEJ,CACJ,CACE;SACF;;KAIR,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,SAAD;QACE,SAAQ;QACR,WAAU;kBAET,EAAG,2BAA2B,aAAa;QACtC,CAAA,EACR,kBAAC,SAAD;QACE,IAAG;QACH,MAAK;QACL,UAAA;QACA,iBAAc;QACd,cAAY,EAAG,2BAA2B,aAAa;QACvD,OAAO,EAAS;QAChB,WAAW,MACT,EAAY;SAAE,GAAG;SAAU,WAAW,EAAE,OAAO;SAAO,CAAC;QAEzD,WAAU;QACV,aAAa,EACX,sCACA,mBACD;QACD,CAAA,CACE;UACN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,SAAD;QACE,SAAQ;QACR,WAAU;kBAET,EAAG,0BAA0B,YAAY;QACpC,CAAA,EACR,kBAAC,SAAD;QACE,IAAG;QACH,MAAK;QACL,UAAA;QACA,iBAAc;QACd,cAAY,EAAG,0BAA0B,YAAY;QACrD,OAAO,EAAS;QAChB,WAAW,MACT,EAAY;SAAE,GAAG;SAAU,UAAU,EAAE,OAAO;SAAO,CAAC;QAExD,WAAU;QACV,aAAa,EACX,qCACA,kBACD;QACD,CAAA,CACE;SACF;;KAGN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,SAAD;OACE,SAAQ;OACR,WAAU;iBAET,EAAG,0BAA0B,WAAW;OACnC,CAAA,EACR,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBACb,kBAAC,GAAD,EAAU,WAAU,4CAA6C,CAAA;QAC7D,CAAA,EACN,kBAAC,SAAD;QACE,IAAG;QACH,MAAK;QACL,UAAA;QACA,iBAAc;QACd,cAAY,EAAG,0BAA0B,WAAW;QACpD,cAAa;QACb,OAAO,EAAS;QAChB,WAAW,MACT,EAAY;SAAE,GAAG;SAAU,UAAU,EAAE,OAAO;SAAO,CAAC;QAExD,WAAU;QACV,aAAa,EACX,qCACA,oBACD;QACD,CAAA,CACE;SACF;;KAGN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,SAAD;OACE,SAAQ;OACR,WAAU;iBAET,EAAG,uBAAuB,QAAQ;OAC7B,CAAA,EACR,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBACb,kBAAC,GAAD,EAAW,WAAU,4CAA6C,CAAA;QAC9D,CAAA,EACN,kBAAC,SAAD;QACE,IAAG;QACH,MAAK;QACL,UAAA;QACA,iBAAc;QACd,cAAY,EAAG,uBAAuB,QAAQ;QAC9C,cAAa;QACb,OAAO,EAAS;QAChB,WAAW,MACT,EAAY;SAAE,GAAG;SAAU,OAAO,EAAE,OAAO;SAAO,CAAC;QAErD,WAAU;QACV,aAAa,EACX,kCACA,kBACD;QACD,CAAA,CACE;SACF;;KAGN,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,SAAD;QACE,SAAQ;QACR,WAAU;kBAET,EAAG,0BAA0B,WAAW;QACnC,CAAA;OACR,kBAAC,OAAD;QAAK,WAAU;kBAAf;SACE,kBAAC,OAAD;UAAK,WAAU;oBACb,kBAAC,IAAD,EAAU,WAAU,4CAA6C,CAAA;UAC7D,CAAA;SACN,kBAAC,SAAD;UACE,IAAG;UACH,MAAM,IAAe,SAAS;UAC9B,UAAA;UACA,iBAAc;UACd,cAAY,EAAG,0BAA0B,WAAW;UACpD,cAAa;UACb,OAAO,EAAS;UAChB,WAAW,MACT,EAAY;WAAE,GAAG;WAAU,UAAU,EAAE,OAAO;WAAO,CAAC;UAExD,WAAU;UACV,aAAa,EACX,qCACA,2BACD;UACD,CAAA;SACF,kBAAC,UAAD;UACE,MAAK;UACL,eAAe,GAAgB,CAAC,EAAa;UAC7C,cAAY,IAAe,kBAAkB;UAC7C,WAAU;oBAGR,EADD,IACE,KAEA,IAFD,EAAY,WAAU,UAAW,CAEH;UAEzB,CAAA;SACL;;OAGL,EAAS,YACR,kBAAC,OAAD;QAAK,WAAU;kBAAf,CAEE,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,OAAD;UAAK,WAAU;oBACb,kBAAC,OAAD,EACE,WAAW,UAAU,EAAiB,MAAM,GAAG,EAAiB,MAAM,+BACtE,CAAA;UACE,CAAA,EACN,kBAAC,KAAD;UAAG,WAAU;oBAAb;WACG,EAAG,kCAAkC,qBAAqB;WAAE;WAC7D,kBAAC,QAAD;YAAM,WAAU;sBAAe,EAAiB;YAAa,CAAA;WAC3D;YACA;YAGN,kBAAC,OAAD;SAAK,WAAU;mBACZ,EAAe,KAAK,MACnB,kBAAC,OAAD;UAAuB,WAAU;oBAAjC,CACG,EAAM,MACL,kBAAC,GAAD,EAAW,WAAU,mCAAoC,CAAA,GAEzD,kBAAC,GAAD,EAAO,WAAU,4CAA6C,CAAA,EAEhE,kBAAC,QAAD;WACE,WAAW,WACT,EAAM,MACF,6BACA;qBAGL,EAAM;WACF,CAAA,CACH;YAfI,EAAM,MAeV,CACN;SACE,CAAA,CACF;;OAEJ;;KAGN,kBAAC,KAAD;MAAG,WAAU;gBAAb;OACG,EACC,8BACA,2CACD;OAAE;OACH,kBAAC,KAAD;QAAG,MAAK;QAAe,WAAU;kBAC9B,EAAG,gCAAgC,mBAAmB;QACrD,CAAA;OAAC;OACJ,EAAG,qBAAqB,MAAM;OAAE;OACjC,kBAAC,KAAD;QAAG,MAAK;QAAiB,WAAU;kBAChC,EAAG,+BAA+B,iBAAiB;QAClD,CAAA;;OAEF;;KAIJ,kBAAC,GAAD;MAEE,SAAS;MACT,SAAS;MACT,UAAU;MACV,iBAAiB,EAAgB,KAAK;MACtC,EALK,EAKL;KAGF,kBAAC,UAAD;MACE,MAAK;MACL,UAAU;MACV,WAAU;gBAET,IACC,kBAAA,IAAA,EAAA,UAAA,CACE,kBAAC,GAAD,EAAa,WAAU,uBAAwB,CAAA,EAC/C,kBAAC,QAAD,EAAA,UACG,EAAG,iCAAiC,sBAAsB,EACtD,CAAA,CACN,EAAA,CAAA,GAEH,EAAG,kCAAkC,iBAAiB;MAEjD,CAAA;KACJ;;GAGP,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,OAAD,EAAK,WAAU,wDAAyD,CAAA;KACpE,CAAA,EACN,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,QAAD;MAAM,WAAU;gBACb,EAAG,8BAA8B,kBAAkB;MAC/C,CAAA;KACH,CAAA,CACF;;GAGL,KACC,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD,EAAW,WAAU,sDAAuD,CAAA,EAC5E,kBAAC,KAAD;KAAG,WAAU;eAAkC;KAAe,CAAA,CAC1D;;GAIR,kBAAC,OAAD;IAAK,WAAU;cACZ,GAAe,KAAK,MACnB,kBAAC,UAAD;KAEE,MAAK;KACL,SAAS,EAAS;KAClB,UAAU,EAAS;KACnB,OAAO,EAAS;KAChB,WAAW,wGACT,EAAS,WACL,oGACA;eATR,CAYE,kBAAC,EAAS,MAAV,EACE,WAAW,UAAU,EAAS,WAAW,eAAe,MACxD,CAAA,EACF,kBAAC,QAAD;MACE,WAAW,iFACT,EAAS,WACL,sCACA;gBAGL,EAAS;MACL,CAAA,CACA;OAvBF,EAAS,KAuBP,CACT;IACE,CAAA;GAGN,kBAAC,KAAD;IAAG,WAAU;cAAb;KACG,EAAG,oCAAoC,2BAA2B;KAAE;KACrE,kBAAC,UAAD;MACE,MAAK;MACL,eAAe,EAAS,cAAc;MACtC,WAAU;gBAET,EAAG,wBAAwB,UAAU;MAC/B,CAAA;KACP;;GACO"}
@@ -7,8 +7,8 @@ var a = e(null), o = () => {
7
7
  let e = t(a);
8
8
  if (!e) throw Error("useAuth must be used within AuthProvider");
9
9
  return e;
10
- }, s = ({ children: e, basePath: t, navigate: o, currentUser: s, workspaceId: c, tenantId: l, organizationId: u, apiGatewayUrl: d, authToken: f, productName: p, onAuthSuccess: m, onLogout: h, defaultPostLoginPath: g, oauthClientIds: _ }) => {
11
- let v = {
10
+ }, s = ({ children: e, basePath: t, navigate: o, currentUser: s, workspaceId: c, tenantId: l, organizationId: u, apiGatewayUrl: d, authToken: f, productName: p, onAuthSuccess: m, onLogout: h, defaultPostLoginPath: g, oauthClientIds: _, productSlug: v }) => {
11
+ let y = {
12
12
  basePath: t,
13
13
  navigate: o,
14
14
  currentUser: s,
@@ -21,16 +21,17 @@ var a = e(null), o = () => {
21
21
  onAuthSuccess: m,
22
22
  onLogout: h,
23
23
  defaultPostLoginPath: g,
24
- oauthClientIds: _
25
- }, y = u ?? l ?? null, b = /* @__PURE__ */ i(a.Provider, {
26
- value: v,
24
+ oauthClientIds: _,
25
+ productSlug: v
26
+ }, b = u ?? l ?? null, x = /* @__PURE__ */ i(a.Provider, {
27
+ value: y,
27
28
  children: /* @__PURE__ */ i(r, {
28
- scopeId: y,
29
+ scopeId: b,
29
30
  gateway: "global",
30
31
  children: e
31
32
  })
32
33
  });
33
- return t ? b : /* @__PURE__ */ i(n, { children: b });
34
+ return t ? x : /* @__PURE__ */ i(n, { children: x });
34
35
  };
35
36
  //#endregion
36
37
  export { s as AuthProvider, o as useAuth };
@@ -1 +1 @@
1
- {"version":3,"file":"AuthProvider.js","names":[],"sources":["../../../src/auth/providers/AuthProvider.tsx"],"sourcesContent":["import type { FC, PropsWithChildren } from \"react\";\nimport { createContext, useContext } from \"react\";\nimport { MemoryRouter } from \"react-router-dom\";\nimport { PermissionProvider } from \"@burdenoff/fe-libs/shared/providers/shell\";\nimport type { AuthRootProps } from \"../types\";\n\n/**\n * Context value for Auth microfrontend\n */\ntype AuthContextValue = AuthRootProps;\n\nconst AuthContext = createContext<AuthContextValue | null>(null);\n\n/**\n * Hook to access Auth context\n */\nexport const useAuth = () => {\n const context = useContext(AuthContext);\n if (!context) {\n throw new Error(\"useAuth must be used within AuthProvider\");\n }\n return context;\n};\n\n/**\n * Provider component for Auth microfrontend\n * Sets up routing and context.\n *\n * NOTE: Apollo client is provided by the app shell's MultiGatewayProvider +\n * GatewayRoute. Auth routes are wrapped with <GatewayRoute gateway=\"global\">\n * so all useQuery/useMutation calls automatically use the global gateway client.\n * This MFE does NOT create its own Apollo client.\n */\nexport const AuthProvider: FC<PropsWithChildren<AuthRootProps>> = ({\n children,\n basePath,\n navigate,\n currentUser,\n workspaceId,\n tenantId,\n organizationId,\n apiGatewayUrl,\n authToken,\n productName,\n onAuthSuccess,\n onLogout,\n defaultPostLoginPath,\n oauthClientIds,\n}) => {\n const contextValue: AuthContextValue = {\n basePath,\n navigate,\n currentUser,\n workspaceId,\n tenantId,\n organizationId,\n apiGatewayUrl,\n authToken,\n productName,\n onAuthSuccess,\n onLogout,\n defaultPostLoginPath,\n oauthClientIds,\n };\n\n // global-rbac has no tenant scope; org roles are scoped to organizationId.\n const permissionScopeId = organizationId ?? tenantId ?? null;\n\n const content = (\n <AuthContext.Provider value={contextValue}>\n <PermissionProvider scopeId={permissionScopeId} gateway=\"global\">\n {children}\n </PermissionProvider>\n </AuthContext.Provider>\n );\n\n // When integrated into a shell (basePath provided), the shell provides\n // BrowserRouter AND ApolloProvider — just render the context\n if (basePath) {\n return content;\n }\n\n // Standalone mode: create MemoryRouter for independent operation\n // (Apollo client must be provided externally or via a dev harness)\n return <MemoryRouter>{content}</MemoryRouter>;\n};\n"],"mappings":";;;;;AAWA,IAAM,IAAc,EAAuC,KAAK,EAKnD,UAAgB;CAC3B,IAAM,IAAU,EAAW,EAAY;AACvC,KAAI,CAAC,EACH,OAAU,MAAM,2CAA2C;AAE7D,QAAO;GAYI,KAAsD,EACjE,aACA,aACA,aACA,gBACA,gBACA,aACA,mBACA,kBACA,cACA,gBACA,kBACA,aACA,yBACA,wBACI;CACJ,IAAM,IAAiC;EACrC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,EAGK,IAAoB,KAAkB,KAAY,MAElD,IACJ,kBAAC,EAAY,UAAb;EAAsB,OAAO;YAC3B,kBAAC,GAAD;GAAoB,SAAS;GAAmB,SAAQ;GACrD;GACkB,CAAA;EACA,CAAA;AAWzB,QANI,IACK,IAKF,kBAAC,GAAD,EAAA,UAAe,GAAuB,CAAA"}
1
+ {"version":3,"file":"AuthProvider.js","names":[],"sources":["../../../src/auth/providers/AuthProvider.tsx"],"sourcesContent":["import type { FC, PropsWithChildren } from \"react\";\nimport { createContext, useContext } from \"react\";\nimport { MemoryRouter } from \"react-router-dom\";\nimport { PermissionProvider } from \"@burdenoff/fe-libs/shared/providers/shell\";\nimport type { AuthRootProps } from \"../types\";\n\n/**\n * Context value for Auth microfrontend\n */\ntype AuthContextValue = AuthRootProps;\n\nconst AuthContext = createContext<AuthContextValue | null>(null);\n\n/**\n * Hook to access Auth context\n */\nexport const useAuth = () => {\n const context = useContext(AuthContext);\n if (!context) {\n throw new Error(\"useAuth must be used within AuthProvider\");\n }\n return context;\n};\n\n/**\n * Provider component for Auth microfrontend\n * Sets up routing and context.\n *\n * NOTE: Apollo client is provided by the app shell's MultiGatewayProvider +\n * GatewayRoute. Auth routes are wrapped with <GatewayRoute gateway=\"global\">\n * so all useQuery/useMutation calls automatically use the global gateway client.\n * This MFE does NOT create its own Apollo client.\n */\nexport const AuthProvider: FC<PropsWithChildren<AuthRootProps>> = ({\n children,\n basePath,\n navigate,\n currentUser,\n workspaceId,\n tenantId,\n organizationId,\n apiGatewayUrl,\n authToken,\n productName,\n onAuthSuccess,\n onLogout,\n defaultPostLoginPath,\n oauthClientIds,\n productSlug,\n}) => {\n const contextValue: AuthContextValue = {\n basePath,\n navigate,\n currentUser,\n workspaceId,\n tenantId,\n organizationId,\n apiGatewayUrl,\n authToken,\n productName,\n onAuthSuccess,\n onLogout,\n defaultPostLoginPath,\n oauthClientIds,\n productSlug,\n };\n\n // global-rbac has no tenant scope; org roles are scoped to organizationId.\n const permissionScopeId = organizationId ?? tenantId ?? null;\n\n const content = (\n <AuthContext.Provider value={contextValue}>\n <PermissionProvider scopeId={permissionScopeId} gateway=\"global\">\n {children}\n </PermissionProvider>\n </AuthContext.Provider>\n );\n\n // When integrated into a shell (basePath provided), the shell provides\n // BrowserRouter AND ApolloProvider — just render the context\n if (basePath) {\n return content;\n }\n\n // Standalone mode: create MemoryRouter for independent operation\n // (Apollo client must be provided externally or via a dev harness)\n return <MemoryRouter>{content}</MemoryRouter>;\n};\n"],"mappings":";;;;;AAWA,IAAM,IAAc,EAAuC,KAAK,EAKnD,UAAgB;CAC3B,IAAM,IAAU,EAAW,EAAY;AACvC,KAAI,CAAC,EACH,OAAU,MAAM,2CAA2C;AAE7D,QAAO;GAYI,KAAsD,EACjE,aACA,aACA,aACA,gBACA,gBACA,aACA,mBACA,kBACA,cACA,gBACA,kBACA,aACA,yBACA,mBACA,qBACI;CACJ,IAAM,IAAiC;EACrC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,EAGK,IAAoB,KAAkB,KAAY,MAElD,IACJ,kBAAC,EAAY,UAAb;EAAsB,OAAO;YAC3B,kBAAC,GAAD;GAAoB,SAAS;GAAmB,SAAQ;GACrD;GACkB,CAAA;EACA,CAAA;AAWzB,QANI,IACK,IAKF,kBAAC,GAAD,EAAA,UAAe,GAAuB,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-auth",
3
- "version": "2026.601.5",
3
+ "version": "2026.608.1",
4
4
  "description": "Auth microfrontend for Burdenoff products",
5
5
  "type": "module",
6
6
  "imports": {