@burdenoff/microfe-auth 2026.828.2 → 2026.830.2
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.
- package/dist/auth/AuthRoot.js +43 -41
- package/dist/auth/AuthRoot.js.map +1 -1
- package/dist/auth/components/AuthLayout.js +65 -67
- package/dist/auth/components/AuthLayout.js.map +1 -1
- package/dist/auth/components/CaptchaV2.js +21 -24
- package/dist/auth/components/CaptchaV2.js.map +1 -1
- package/dist/auth/components/ConfirmActionDialog.js +33 -32
- package/dist/auth/components/ConfirmActionDialog.js.map +1 -1
- package/dist/auth/components/profile/UserProfileMenu.js +44 -46
- package/dist/auth/components/profile/UserProfileMenu.js.map +1 -1
- package/dist/auth/components/workspace/WorkspaceSelector.js +62 -68
- package/dist/auth/components/workspace/WorkspaceSelector.js.map +1 -1
- package/dist/auth/pages/AuditLogPage.js +65 -68
- package/dist/auth/pages/AuditLogPage.js.map +1 -1
- package/dist/auth/pages/EmailVerifyPage.js +51 -54
- package/dist/auth/pages/EmailVerifyPage.js.map +1 -1
- package/dist/auth/pages/LoginPage.js +223 -225
- package/dist/auth/pages/LoginPage.js.map +1 -1
- package/dist/auth/pages/MagicLinkPage.js +67 -69
- package/dist/auth/pages/MagicLinkPage.js.map +1 -1
- package/dist/auth/pages/MfaSetupPage.js +85 -88
- package/dist/auth/pages/MfaSetupPage.js.map +1 -1
- package/dist/auth/pages/MfaVerifyPage.js +60 -63
- package/dist/auth/pages/MfaVerifyPage.js.map +1 -1
- package/dist/auth/pages/OAuthCallbackPage.js +37 -40
- package/dist/auth/pages/OAuthCallbackPage.js.map +1 -1
- package/dist/auth/pages/PasswordResetPage.js +102 -105
- package/dist/auth/pages/PasswordResetPage.js.map +1 -1
- package/dist/auth/pages/ReactivatePage.js +54 -57
- package/dist/auth/pages/ReactivatePage.js.map +1 -1
- package/dist/auth/pages/RegisterPage.js +171 -174
- package/dist/auth/pages/RegisterPage.js.map +1 -1
- package/dist/auth/pages/SSOClearPage.js +21 -20
- package/dist/auth/pages/SSOClearPage.js.map +1 -1
- package/dist/auth/pages/SSODepositPage.js +27 -26
- package/dist/auth/pages/SSODepositPage.js.map +1 -1
- package/dist/auth/pages/SSORelayCallbackPage.js +19 -18
- package/dist/auth/pages/SSORelayCallbackPage.js.map +1 -1
- package/dist/auth/pages/SecurityPage.js +247 -250
- package/dist/auth/pages/SecurityPage.js.map +1 -1
- package/dist/auth/pages/SessionsPage.js +95 -98
- package/dist/auth/pages/SessionsPage.js.map +1 -1
- package/dist/auth/pages/SsoCallbackPage.js +53 -56
- package/dist/auth/pages/SsoCallbackPage.js.map +1 -1
- package/dist/auth/pages/SsoConfigPage.js +296 -299
- package/dist/auth/pages/SsoConfigPage.js.map +1 -1
- package/dist/auth/pages/SsoLoginPage.js +65 -68
- package/dist/auth/pages/SsoLoginPage.js.map +1 -1
- package/dist/auth/pages/UserDetailsPage.js +124 -127
- package/dist/auth/pages/UserDetailsPage.js.map +1 -1
- package/dist/auth/pages/UserProfilePage.js +171 -174
- package/dist/auth/pages/UserProfilePage.js.map +1 -1
- package/dist/auth/pages/UsersPage.js +322 -325
- package/dist/auth/pages/UsersPage.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -1
- package/dist/shared/hooks/index.d.ts +1 -6
- package/dist/shared/hooks/useTr.d.ts +16 -0
- package/dist/shared/hooks/useTr.js +14 -0
- package/dist/shared/hooks/useTr.js.map +1 -0
- package/dist/translations/en.d.ts +806 -248
- package/dist/translations/en.js +870 -0
- package/dist/translations/en.js.map +1 -0
- package/package.json +1 -1
package/dist/auth/AuthRoot.js
CHANGED
|
@@ -1,83 +1,85 @@
|
|
|
1
1
|
import { AuthProvider as e } from "./providers/AuthProvider.js";
|
|
2
2
|
import { CaptchaProvider as t } from "./providers/CaptchaProvider.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
3
|
+
import { useTr as n } from "../shared/hooks/useTr.js";
|
|
4
|
+
import { AuthRoutes as r } from "./AuthRoutes.js";
|
|
5
|
+
import { useEffect as i, useMemo as a } from "react";
|
|
6
|
+
import { ErrorBoundary as o } from "@burdenoff/fe-libs/shared/components";
|
|
7
|
+
import { useLocation as s } from "react-router";
|
|
8
|
+
import { Building as c, Key as l, Laptop as u, ShieldCheck as d } from "lucide-react";
|
|
9
|
+
import { jsx as f } from "react/jsx-runtime";
|
|
10
|
+
import { FeatureFlagProvider as p } from "@burdenoff/fe-libs/shared/feature-flags";
|
|
10
11
|
//#region src/auth/AuthRoot.tsx
|
|
11
|
-
var
|
|
12
|
-
let { registerNavItems:
|
|
13
|
-
return
|
|
14
|
-
if (
|
|
12
|
+
var m = (m) => {
|
|
13
|
+
let { registerNavItems: h, registerFooterItems: g, basePath: _ = "/", enableAdminRoutes: v = !1, bypassEnterprisePlanGate: y = !1 } = m, b = n(), x = s(), S = a(() => _ && _ !== "/" ? _.endsWith("/") ? _.slice(0, -1) : _ : "", [_]), C = (e, t) => (e.endsWith("/") ? e.slice(0, -1) : e) + (t.startsWith("/") ? t : `/${t}`);
|
|
14
|
+
return i(() => {
|
|
15
|
+
if (h) return h([
|
|
15
16
|
{
|
|
16
17
|
id: "auth-security",
|
|
17
|
-
label: "Security Settings",
|
|
18
|
-
path:
|
|
19
|
-
icon: /* @__PURE__ */ d
|
|
18
|
+
label: b("auth.root.navSecurity", "Security Settings"),
|
|
19
|
+
path: C(_, "security"),
|
|
20
|
+
icon: /* @__PURE__ */ f(d, { size: 20 }),
|
|
20
21
|
order: 10,
|
|
21
22
|
group: "auth"
|
|
22
23
|
},
|
|
23
24
|
{
|
|
24
25
|
id: "auth-sessions",
|
|
25
|
-
label: "Active Sessions",
|
|
26
|
-
path:
|
|
27
|
-
icon: /* @__PURE__ */
|
|
26
|
+
label: b("auth.root.navSessions", "Active Sessions"),
|
|
27
|
+
path: C(_, "sessions"),
|
|
28
|
+
icon: /* @__PURE__ */ f(u, { size: 20 }),
|
|
28
29
|
order: 20,
|
|
29
30
|
group: "auth"
|
|
30
31
|
},
|
|
31
32
|
{
|
|
32
33
|
id: "auth-mfa",
|
|
33
|
-
label: "Multi-Factor Auth",
|
|
34
|
-
path:
|
|
35
|
-
icon: /* @__PURE__ */
|
|
34
|
+
label: b("auth.root.navMfa", "Multi-Factor Auth"),
|
|
35
|
+
path: C(_, "mfa"),
|
|
36
|
+
icon: /* @__PURE__ */ f(l, { size: 20 }),
|
|
36
37
|
order: 30,
|
|
37
38
|
group: "auth"
|
|
38
39
|
},
|
|
39
|
-
...
|
|
40
|
+
...v ? [{
|
|
40
41
|
id: "auth-sso",
|
|
41
|
-
label: "SSO Configuration",
|
|
42
|
-
path:
|
|
43
|
-
icon: /* @__PURE__ */
|
|
42
|
+
label: b("auth.root.navSso", "SSO Configuration"),
|
|
43
|
+
path: C(_, "sso"),
|
|
44
|
+
icon: /* @__PURE__ */ f(c, { size: 20 }),
|
|
44
45
|
order: 50,
|
|
45
46
|
group: "auth"
|
|
46
47
|
}] : []
|
|
47
48
|
]);
|
|
48
49
|
}, [
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
50
|
+
h,
|
|
51
|
+
_,
|
|
52
|
+
v,
|
|
53
|
+
b
|
|
54
|
+
]), i(() => {
|
|
55
|
+
if (g) return g([{
|
|
54
56
|
id: "auth-status",
|
|
55
|
-
label: "Auth Ready",
|
|
57
|
+
label: b("auth.root.footerReady", "Auth Ready"),
|
|
56
58
|
type: "status",
|
|
57
59
|
order: 10,
|
|
58
60
|
section: "left"
|
|
59
61
|
}]);
|
|
60
|
-
}, [
|
|
61
|
-
fallback: /* @__PURE__ */
|
|
62
|
+
}, [g, b]), a(() => S ? x.pathname === S || x.pathname.startsWith(S + "/") : !0, [S, x.pathname]) ? /* @__PURE__ */ f(o, {
|
|
63
|
+
fallback: /* @__PURE__ */ f("div", {
|
|
62
64
|
className: "p-6 text-center text-text-secondary",
|
|
63
|
-
children: "Something went wrong loading this module. Please refresh the page."
|
|
65
|
+
children: b("auth.root.loadError", "Something went wrong loading this module. Please refresh the page.")
|
|
64
66
|
}),
|
|
65
|
-
children: /* @__PURE__ */
|
|
67
|
+
children: /* @__PURE__ */ f(p, {
|
|
66
68
|
workspaceId: null,
|
|
67
69
|
gateway: "global",
|
|
68
70
|
defaultEnabled: !0,
|
|
69
|
-
children: /* @__PURE__ */
|
|
70
|
-
...
|
|
71
|
-
children: /* @__PURE__ */
|
|
72
|
-
basePath:
|
|
73
|
-
enableAdminRoutes:
|
|
74
|
-
bypassEnterprisePlanGate:
|
|
71
|
+
children: /* @__PURE__ */ f(e, {
|
|
72
|
+
...m,
|
|
73
|
+
children: /* @__PURE__ */ f(t, { children: /* @__PURE__ */ f(r, {
|
|
74
|
+
basePath: S,
|
|
75
|
+
enableAdminRoutes: v,
|
|
76
|
+
bypassEnterprisePlanGate: y
|
|
75
77
|
}) })
|
|
76
78
|
})
|
|
77
79
|
})
|
|
78
80
|
}) : null;
|
|
79
81
|
};
|
|
80
82
|
//#endregion
|
|
81
|
-
export {
|
|
83
|
+
export { m as AuthRoot };
|
|
82
84
|
|
|
83
85
|
//# sourceMappingURL=AuthRoot.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AuthRoot.js","names":[],"sources":["../../src/auth/AuthRoot.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { useEffect, useMemo } from \"react\";\nimport { ErrorBoundary } from \"@burdenoff/fe-libs/shared/components\";\nimport { useLocation } from \"react-router\";\nimport { ShieldCheck, Laptop, Key, /* Code, */ Building } from \"lucide-react\";\nimport { AuthProvider } from \"./providers/AuthProvider\";\nimport { FeatureFlagProvider } from \"@burdenoff/fe-libs/shared/feature-flags\";\nimport { CaptchaProvider } from \"./providers/CaptchaProvider\";\nimport type { NavItem, AuthRootProps } from \"./types\";\nimport { AuthRoutes } from \"./AuthRoutes\";\n\n/**\n * Root component for the Auth microfrontend.\n *\n * This is the main entry point that host applications import and render.\n * It sets up the necessary providers and renders the internal routing.\n * Automatically registers navigation items with the shell if registration functions are provided.\n *\n * @example\n * ```tsx\n * import { AuthRoot, type AuthRootProps } from '@burdenoff/microfe-auth';\n *\n * function App() {\n * const props: AuthRootProps = {\n * basePath: '/auth',\n * navigate: (path) => router.push(path),\n * apiGatewayUrl: 'http://localhost:4002',\n * authToken: 'token',\n * onAuthSuccess: (user) => shellLogin(user),\n * onLogout: () => shellLogout(),\n * };\n *\n * return <AuthRoot {...props} />;\n * }\n * ```\n */\nexport const AuthRoot: FC<AuthRootProps> = (props) => {\n const {\n registerNavItems,\n registerFooterItems,\n basePath = \"/\",\n // Admin routes (`/auth/users`, `/auth/users/:id`, `/auth/sso`,\n // `/auth/audit-log`) are opt-in: only the admin app shell should pass\n // `enableAdminRoutes={true}`. User-facing product hosts (vibecontrols,\n // workspaces, botlit, healthybowl, etc.) intentionally do not expose\n // these surfaces — gateway RBAC still gates the underlying\n // queries/mutations, but hiding the routes prevents accidental UI\n // discoverability. Must match the corresponding default in\n // `AuthRoutes.tsx`.\n enableAdminRoutes = false,\n bypassEnterprisePlanGate = false,\n } = props;\n const location = useLocation();\n\n // Normalize basePath (remove trailing slash)\n const normalizedBasePath = useMemo(\n () =>\n basePath && basePath !== \"/\"\n ? basePath.endsWith(\"/\")\n ? basePath.slice(0, -1)\n : basePath\n : \"\",\n [basePath],\n );\n\n // Helper to join paths correctly, avoiding double slashes\n const joinPath = (base: string, path: string): string => {\n const cleanBase = base.endsWith(\"/\") ? base.slice(0, -1) : base;\n const cleanPath = path.startsWith(\"/\") ? path : `/${path}`;\n return cleanBase + cleanPath;\n };\n\n // Register navigation items with the shell\n useEffect(() => {\n if (!registerNavItems) return;\n\n const navItems: NavItem[] = [\n {\n id: \"auth-security\",\n label: \"Security Settings\",\n path: joinPath(basePath, \"security\"),\n icon: <ShieldCheck size={20} />,\n order: 10,\n group: \"auth\",\n },\n {\n id: \"auth-sessions\",\n label: \"Active Sessions\",\n path: joinPath(basePath, \"sessions\"),\n icon: <Laptop size={20} />,\n order: 20,\n group: \"auth\",\n },\n {\n id: \"auth-mfa\",\n label: \"Multi-Factor Auth\",\n path: joinPath(basePath, \"mfa\"),\n icon: <Key size={20} />,\n order: 30,\n group: \"auth\",\n },\n // Hidden — moved to Developer Portal\n // {\n // id: \"auth-api-keys\",\n // label: \"API Keys\",\n // path: joinPath(basePath, \"api-keys\"),\n // icon: <Code size={20} />,\n // order: 40,\n // group: \"auth\",\n // },\n ...(enableAdminRoutes\n ? [\n {\n id: \"auth-sso\",\n label: \"SSO Configuration\",\n path: joinPath(basePath, \"sso\"),\n icon: <Building size={20} />,\n order: 50,\n group: \"auth\",\n },\n ]\n : []),\n ];\n\n // Register and get cleanup function\n const cleanup = registerNavItems(navItems);\n\n // Return cleanup function to unregister on unmount\n return cleanup;\n }, [registerNavItems, basePath, enableAdminRoutes]);\n\n // Register footer items (example: auth status indicator)\n useEffect(() => {\n if (!registerFooterItems) return;\n\n const footerItems = [\n {\n id: \"auth-status\",\n label: \"Auth Ready\",\n type: \"status\" as const,\n order: 10,\n section: \"left\" as const,\n },\n ];\n\n const cleanup = registerFooterItems(footerItems);\n return cleanup;\n }, [registerFooterItems]);\n\n // Check if path matches - must be after all hooks\n const pathMatches = useMemo(() => {\n if (!normalizedBasePath) return true;\n return (\n location.pathname === normalizedBasePath ||\n location.pathname.startsWith(normalizedBasePath + \"/\")\n );\n }, [normalizedBasePath, location.pathname]);\n\n // Early return after all hooks are called\n if (!pathMatches) {\n return null;\n }\n\n return (\n <ErrorBoundary\n fallback={\n <div className=\"p-6 text-center text-text-secondary\">\n Something went wrong loading this module. Please refresh the page.\n </div>\n }\n >\n <FeatureFlagProvider\n workspaceId={null}\n gateway=\"global\"\n defaultEnabled={true}\n >\n <AuthProvider {...props}>\n <CaptchaProvider>\n <AuthRoutes\n basePath={normalizedBasePath}\n enableAdminRoutes={enableAdminRoutes}\n bypassEnterprisePlanGate={bypassEnterprisePlanGate}\n />\n </CaptchaProvider>\n </AuthProvider>\n </FeatureFlagProvider>\n </ErrorBoundary>\n );\n};\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"AuthRoot.js","names":[],"sources":["../../src/auth/AuthRoot.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { useEffect, useMemo } from \"react\";\nimport { ErrorBoundary } from \"@burdenoff/fe-libs/shared/components\";\nimport { useLocation } from \"react-router\";\nimport { ShieldCheck, Laptop, Key, /* Code, */ Building } from \"lucide-react\";\nimport { AuthProvider } from \"./providers/AuthProvider\";\nimport { FeatureFlagProvider } from \"@burdenoff/fe-libs/shared/feature-flags\";\nimport { CaptchaProvider } from \"./providers/CaptchaProvider\";\nimport type { NavItem, AuthRootProps } from \"./types\";\nimport { AuthRoutes } from \"./AuthRoutes\";\nimport { useTr } from \"../shared/hooks/useTr\";\n\n/**\n * Root component for the Auth microfrontend.\n *\n * This is the main entry point that host applications import and render.\n * It sets up the necessary providers and renders the internal routing.\n * Automatically registers navigation items with the shell if registration functions are provided.\n *\n * @example\n * ```tsx\n * import { AuthRoot, type AuthRootProps } from '@burdenoff/microfe-auth';\n *\n * function App() {\n * const props: AuthRootProps = {\n * basePath: '/auth',\n * navigate: (path) => router.push(path),\n * apiGatewayUrl: 'http://localhost:4002',\n * authToken: 'token',\n * onAuthSuccess: (user) => shellLogin(user),\n * onLogout: () => shellLogout(),\n * };\n *\n * return <AuthRoot {...props} />;\n * }\n * ```\n */\nexport const AuthRoot: FC<AuthRootProps> = (props) => {\n const {\n registerNavItems,\n registerFooterItems,\n basePath = \"/\",\n // Admin routes (`/auth/users`, `/auth/users/:id`, `/auth/sso`,\n // `/auth/audit-log`) are opt-in: only the admin app shell should pass\n // `enableAdminRoutes={true}`. User-facing product hosts (vibecontrols,\n // workspaces, botlit, healthybowl, etc.) intentionally do not expose\n // these surfaces — gateway RBAC still gates the underlying\n // queries/mutations, but hiding the routes prevents accidental UI\n // discoverability. Must match the corresponding default in\n // `AuthRoutes.tsx`.\n enableAdminRoutes = false,\n bypassEnterprisePlanGate = false,\n } = props;\n const tr = useTr();\n const location = useLocation();\n\n // Normalize basePath (remove trailing slash)\n const normalizedBasePath = useMemo(\n () =>\n basePath && basePath !== \"/\"\n ? basePath.endsWith(\"/\")\n ? basePath.slice(0, -1)\n : basePath\n : \"\",\n [basePath],\n );\n\n // Helper to join paths correctly, avoiding double slashes\n const joinPath = (base: string, path: string): string => {\n const cleanBase = base.endsWith(\"/\") ? base.slice(0, -1) : base;\n const cleanPath = path.startsWith(\"/\") ? path : `/${path}`;\n return cleanBase + cleanPath;\n };\n\n // Register navigation items with the shell\n useEffect(() => {\n if (!registerNavItems) return;\n\n const navItems: NavItem[] = [\n {\n id: \"auth-security\",\n label: tr(\"auth.root.navSecurity\", \"Security Settings\"),\n path: joinPath(basePath, \"security\"),\n icon: <ShieldCheck size={20} />,\n order: 10,\n group: \"auth\",\n },\n {\n id: \"auth-sessions\",\n label: tr(\"auth.root.navSessions\", \"Active Sessions\"),\n path: joinPath(basePath, \"sessions\"),\n icon: <Laptop size={20} />,\n order: 20,\n group: \"auth\",\n },\n {\n id: \"auth-mfa\",\n label: tr(\"auth.root.navMfa\", \"Multi-Factor Auth\"),\n path: joinPath(basePath, \"mfa\"),\n icon: <Key size={20} />,\n order: 30,\n group: \"auth\",\n },\n // Hidden — moved to Developer Portal\n // {\n // id: \"auth-api-keys\",\n // label: \"API Keys\",\n // path: joinPath(basePath, \"api-keys\"),\n // icon: <Code size={20} />,\n // order: 40,\n // group: \"auth\",\n // },\n ...(enableAdminRoutes\n ? [\n {\n id: \"auth-sso\",\n label: tr(\"auth.root.navSso\", \"SSO Configuration\"),\n path: joinPath(basePath, \"sso\"),\n icon: <Building size={20} />,\n order: 50,\n group: \"auth\",\n },\n ]\n : []),\n ];\n\n // Register and get cleanup function\n const cleanup = registerNavItems(navItems);\n\n // Return cleanup function to unregister on unmount\n return cleanup;\n }, [registerNavItems, basePath, enableAdminRoutes, tr]);\n\n // Register footer items (example: auth status indicator)\n useEffect(() => {\n if (!registerFooterItems) return;\n\n const footerItems = [\n {\n id: \"auth-status\",\n label: tr(\"auth.root.footerReady\", \"Auth Ready\"),\n type: \"status\" as const,\n order: 10,\n section: \"left\" as const,\n },\n ];\n\n const cleanup = registerFooterItems(footerItems);\n return cleanup;\n }, [registerFooterItems, tr]);\n\n // Check if path matches - must be after all hooks\n const pathMatches = useMemo(() => {\n if (!normalizedBasePath) return true;\n return (\n location.pathname === normalizedBasePath ||\n location.pathname.startsWith(normalizedBasePath + \"/\")\n );\n }, [normalizedBasePath, location.pathname]);\n\n // Early return after all hooks are called\n if (!pathMatches) {\n return null;\n }\n\n return (\n <ErrorBoundary\n fallback={\n <div className=\"p-6 text-center text-text-secondary\">\n {tr(\n \"auth.root.loadError\",\n \"Something went wrong loading this module. Please refresh the page.\",\n )}\n </div>\n }\n >\n <FeatureFlagProvider\n workspaceId={null}\n gateway=\"global\"\n defaultEnabled={true}\n >\n <AuthProvider {...props}>\n <CaptchaProvider>\n <AuthRoutes\n basePath={normalizedBasePath}\n enableAdminRoutes={enableAdminRoutes}\n bypassEnterprisePlanGate={bypassEnterprisePlanGate}\n />\n </CaptchaProvider>\n </AuthProvider>\n </FeatureFlagProvider>\n </ErrorBoundary>\n );\n};\n"],"mappings":";;;;;;;;;;;AAqCA,IAAa,KAA+B,MAAU;CACpD,IAAM,EACJ,qBACA,wBACA,cAAW,KASX,uBAAoB,IACpB,8BAA2B,OACzB,GACE,IAAK,GAAO,EACZ,IAAW,GAAa,EAGxB,IAAqB,QAEvB,KAAY,MAAa,MACrB,EAAS,SAAS,IAAI,GACpB,EAAS,MAAM,GAAG,GAAG,GACrB,IACF,IACN,CAAC,EAAS,CACX,EAGK,KAAY,GAAc,OACZ,EAAK,SAAS,IAAI,GAAG,EAAK,MAAM,GAAG,GAAG,GAAG,MACzC,EAAK,WAAW,IAAI,GAAG,IAAO,IAAI;AA+FtD,QA1FA,QAAgB;AACT,QAsDL,QAHgB,EAjDY;GAC1B;IACE,IAAI;IACJ,OAAO,EAAG,yBAAyB,oBAAoB;IACvD,MAAM,EAAS,GAAU,WAAW;IACpC,MAAM,kBAAC,GAAD,EAAa,MAAM,IAAM,CAAA;IAC/B,OAAO;IACP,OAAO;IACR;GACD;IACE,IAAI;IACJ,OAAO,EAAG,yBAAyB,kBAAkB;IACrD,MAAM,EAAS,GAAU,WAAW;IACpC,MAAM,kBAAC,GAAD,EAAQ,MAAM,IAAM,CAAA;IAC1B,OAAO;IACP,OAAO;IACR;GACD;IACE,IAAI;IACJ,OAAO,EAAG,oBAAoB,oBAAoB;IAClD,MAAM,EAAS,GAAU,MAAM;IAC/B,MAAM,kBAAC,GAAD,EAAK,MAAM,IAAM,CAAA;IACvB,OAAO;IACP,OAAO;IACR;GAUD,GAAI,IACA,CACE;IACE,IAAI;IACJ,OAAO,EAAG,oBAAoB,oBAAoB;IAClD,MAAM,EAAS,GAAU,MAAM;IAC/B,MAAM,kBAAC,GAAD,EAAU,MAAM,IAAM,CAAA;IAC5B,OAAO;IACP,OAAO;IACR,CACF,GACD,EAAE;GACP,CAGyC;IAIzC;EAAC;EAAkB;EAAU;EAAmB;EAAG,CAAC,EAGvD,QAAgB;AACT,QAaL,QADgB,EAVI,CAClB;GACE,IAAI;GACJ,OAAO,EAAG,yBAAyB,aAAa;GAChD,MAAM;GACN,OAAO;GACP,SAAS;GACV,CACF,CAE+C;IAE/C,CAAC,GAAqB,EAAG,CAAC,EAGT,QACb,IAEH,EAAS,aAAa,KACtB,EAAS,SAAS,WAAW,IAAqB,IAAI,GAHxB,IAK/B,CAAC,GAAoB,EAAS,SAAS,CAAC,GAQzC,kBAAC,GAAD;EACE,UACE,kBAAC,OAAD;GAAK,WAAU;aACZ,EACC,uBACA,qEACD;GACG,CAAA;YAGR,kBAAC,GAAD;GACE,aAAa;GACb,SAAQ;GACR,gBAAgB;aAEhB,kBAAC,GAAD;IAAc,GAAI;cAChB,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;KACE,UAAU;KACS;KACO;KAC1B,CAAA,EACc,CAAA;IACL,CAAA;GACK,CAAA;EACR,CAAA,GA7BT"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { useTr as e } from "../../shared/hooks/useTr.js";
|
|
2
|
+
import { useState as t } from "react";
|
|
3
|
+
import { useAppShell as n, useI18n as r } from "@burdenoff/fe-libs/shared/providers/shell";
|
|
4
|
+
import { jsx as i, jsxs as a } from "react/jsx-runtime";
|
|
5
|
+
import { LanguageSwitcher as o } from "@burdenoff/fe-libs/chrome";
|
|
5
6
|
//#region src/auth/components/AuthLayout.tsx
|
|
6
|
-
var
|
|
7
|
+
var s = [
|
|
7
8
|
{
|
|
8
9
|
code: "en",
|
|
9
10
|
name: "English",
|
|
@@ -41,129 +42,126 @@ var o = [
|
|
|
41
42
|
flag: "🇸🇦"
|
|
42
43
|
}
|
|
43
44
|
];
|
|
44
|
-
function
|
|
45
|
-
let [n,
|
|
46
|
-
return n ? /* @__PURE__ */
|
|
45
|
+
function c({ brandInitial: e }) {
|
|
46
|
+
let [n, r] = t(!1);
|
|
47
|
+
return n ? /* @__PURE__ */ i("div", {
|
|
47
48
|
className: "auth-brand-badge flex size-10 items-center justify-center rounded-xl",
|
|
48
|
-
children: /* @__PURE__ */
|
|
49
|
+
children: /* @__PURE__ */ i("span", {
|
|
49
50
|
className: "text-lg font-bold leading-none text-white",
|
|
50
|
-
children:
|
|
51
|
+
children: e
|
|
51
52
|
})
|
|
52
|
-
}) : /* @__PURE__ */
|
|
53
|
+
}) : /* @__PURE__ */ i("img", {
|
|
53
54
|
src: "/icons/icon.svg",
|
|
54
55
|
alt: "",
|
|
55
56
|
"aria-hidden": "true",
|
|
56
57
|
className: "size-10 rounded-xl object-contain",
|
|
57
|
-
onError: () =>
|
|
58
|
+
onError: () => r(!0)
|
|
58
59
|
});
|
|
59
60
|
}
|
|
60
|
-
function
|
|
61
|
-
let
|
|
62
|
-
|
|
63
|
-
return n === e ? t : n;
|
|
64
|
-
};
|
|
65
|
-
return /* @__PURE__ */ i("div", {
|
|
61
|
+
function l({ children: t, title: l, subtitle: u, productName: d, tagline: f }) {
|
|
62
|
+
let p = e(), m = n(), h = d || m.productName || "Burdenoff", g = m.brandInitial || "B", _ = f || m.tagline || "Enterprise platform", { locale: v, setLocale: y } = r();
|
|
63
|
+
return /* @__PURE__ */ a("div", {
|
|
66
64
|
className: "min-h-screen flex relative",
|
|
67
|
-
children: [/* @__PURE__ */
|
|
65
|
+
children: [/* @__PURE__ */ a("div", {
|
|
68
66
|
className: "auth-brand-panel hidden overflow-hidden lg:flex lg:w-1/2 relative",
|
|
69
|
-
children: [/* @__PURE__ */
|
|
67
|
+
children: [/* @__PURE__ */ a("div", {
|
|
70
68
|
className: "absolute inset-0 overflow-hidden",
|
|
71
69
|
children: [
|
|
72
|
-
/* @__PURE__ */
|
|
73
|
-
/* @__PURE__ */
|
|
74
|
-
/* @__PURE__ */
|
|
75
|
-
/* @__PURE__ */
|
|
70
|
+
/* @__PURE__ */ i("div", { className: "absolute -top-40 -left-40 size-80 rounded-full bg-accent-blue/20 blur-3xl" }),
|
|
71
|
+
/* @__PURE__ */ i("div", { className: "absolute top-1/2 -right-20 size-96 rounded-full bg-accent-indigo/20 blur-3xl" }),
|
|
72
|
+
/* @__PURE__ */ i("div", { className: "absolute -bottom-40 left-1/4 size-72 rounded-full bg-accent-purple/20 blur-3xl" }),
|
|
73
|
+
/* @__PURE__ */ i("div", {
|
|
76
74
|
className: "absolute inset-0 opacity-5",
|
|
77
75
|
style: { backgroundImage: "url(\"data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%23ffffff' fill-opacity='1'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E\")" }
|
|
78
76
|
})
|
|
79
77
|
]
|
|
80
|
-
}), /* @__PURE__ */
|
|
78
|
+
}), /* @__PURE__ */ a("div", {
|
|
81
79
|
className: "relative z-10 flex flex-col justify-between p-12 w-full",
|
|
82
80
|
children: [
|
|
83
|
-
/* @__PURE__ */
|
|
81
|
+
/* @__PURE__ */ a("div", {
|
|
84
82
|
className: "flex items-center gap-3",
|
|
85
|
-
children: [/* @__PURE__ */
|
|
83
|
+
children: [/* @__PURE__ */ i(c, { brandInitial: g }), /* @__PURE__ */ i("span", {
|
|
86
84
|
className: "text-xl font-bold text-[var(--color-text-primary)]",
|
|
87
|
-
children:
|
|
85
|
+
children: h
|
|
88
86
|
})]
|
|
89
87
|
}),
|
|
90
|
-
/* @__PURE__ */
|
|
88
|
+
/* @__PURE__ */ a("div", {
|
|
91
89
|
className: "max-w-md",
|
|
92
90
|
children: [
|
|
93
|
-
/* @__PURE__ */
|
|
91
|
+
/* @__PURE__ */ i("h1", {
|
|
94
92
|
className: "mb-4 text-4xl font-bold leading-tight text-[var(--color-text-primary)]",
|
|
95
|
-
children:
|
|
93
|
+
children: _
|
|
96
94
|
}),
|
|
97
|
-
/* @__PURE__ */
|
|
95
|
+
/* @__PURE__ */ i("p", {
|
|
98
96
|
className: "auth-brand-copy text-lg leading-relaxed",
|
|
99
|
-
children:
|
|
97
|
+
children: p("auth.layout.taglineDescription", "Unify your teams, projects, and workflows in one intelligent workspace. Built for scale with enterprise-grade security, real-time collaboration, and seamless integrations across your entire organization.")
|
|
100
98
|
}),
|
|
101
|
-
/* @__PURE__ */
|
|
99
|
+
/* @__PURE__ */ i("div", {
|
|
102
100
|
className: "mt-10 space-y-4",
|
|
103
101
|
children: [
|
|
104
102
|
{
|
|
105
103
|
icon: "workspace",
|
|
106
|
-
text:
|
|
104
|
+
text: p("auth.layout.featureWorkspace", "Modular workspace architecture")
|
|
107
105
|
},
|
|
108
106
|
{
|
|
109
107
|
icon: "realtime",
|
|
110
|
-
text:
|
|
108
|
+
text: p("auth.layout.featureRealtime", "Real-time collaboration & sync")
|
|
111
109
|
},
|
|
112
110
|
{
|
|
113
111
|
icon: "security",
|
|
114
|
-
text:
|
|
112
|
+
text: p("auth.layout.featureSecurity", "Enterprise-grade security & SSO")
|
|
115
113
|
},
|
|
116
114
|
{
|
|
117
115
|
icon: "scale",
|
|
118
|
-
text:
|
|
116
|
+
text: p("auth.layout.featureScale", "Unlimited scalability & performance")
|
|
119
117
|
}
|
|
120
|
-
].map((e) => /* @__PURE__ */
|
|
118
|
+
].map((e) => /* @__PURE__ */ a("div", {
|
|
121
119
|
className: "flex items-center gap-3",
|
|
122
|
-
children: [/* @__PURE__ */
|
|
120
|
+
children: [/* @__PURE__ */ a("div", {
|
|
123
121
|
className: "auth-highlight-chip flex size-8 items-center justify-center rounded-lg",
|
|
124
122
|
children: [
|
|
125
|
-
e.icon === "workspace" && /* @__PURE__ */
|
|
123
|
+
e.icon === "workspace" && /* @__PURE__ */ i("svg", {
|
|
126
124
|
className: "size-4 text-accent-blue",
|
|
127
125
|
fill: "none",
|
|
128
126
|
viewBox: "0 0 24 24",
|
|
129
127
|
stroke: "currentColor",
|
|
130
|
-
children: /* @__PURE__ */
|
|
128
|
+
children: /* @__PURE__ */ i("path", {
|
|
131
129
|
strokeLinecap: "round",
|
|
132
130
|
strokeLinejoin: "round",
|
|
133
131
|
strokeWidth: 2,
|
|
134
132
|
d: "M4 5a1 1 0 011-1h14a1 1 0 011 1v2a1 1 0 01-1 1H5a1 1 0 01-1-1V5zM4 13a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1H5a1 1 0 01-1-1v-6zM16 13a1 1 0 011-1h2a1 1 0 011 1v6a1 1 0 01-1 1h-2a1 1 0 01-1-1v-6z"
|
|
135
133
|
})
|
|
136
134
|
}),
|
|
137
|
-
e.icon === "realtime" && /* @__PURE__ */
|
|
135
|
+
e.icon === "realtime" && /* @__PURE__ */ i("svg", {
|
|
138
136
|
className: "size-4 text-accent-blue",
|
|
139
137
|
fill: "none",
|
|
140
138
|
viewBox: "0 0 24 24",
|
|
141
139
|
stroke: "currentColor",
|
|
142
|
-
children: /* @__PURE__ */
|
|
140
|
+
children: /* @__PURE__ */ i("path", {
|
|
143
141
|
strokeLinecap: "round",
|
|
144
142
|
strokeLinejoin: "round",
|
|
145
143
|
strokeWidth: 2,
|
|
146
144
|
d: "M17 8h2a2 2 0 012 2v6a2 2 0 01-2 2h-2v4l-4-4H9a1.994 1.994 0 01-1.414-.586m0 0L11 14h4a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2v4l.586-.586z"
|
|
147
145
|
})
|
|
148
146
|
}),
|
|
149
|
-
e.icon === "security" && /* @__PURE__ */
|
|
147
|
+
e.icon === "security" && /* @__PURE__ */ i("svg", {
|
|
150
148
|
className: "size-4 text-accent-blue",
|
|
151
149
|
fill: "none",
|
|
152
150
|
viewBox: "0 0 24 24",
|
|
153
151
|
stroke: "currentColor",
|
|
154
|
-
children: /* @__PURE__ */
|
|
152
|
+
children: /* @__PURE__ */ i("path", {
|
|
155
153
|
strokeLinecap: "round",
|
|
156
154
|
strokeLinejoin: "round",
|
|
157
155
|
strokeWidth: 2,
|
|
158
156
|
d: "M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z"
|
|
159
157
|
})
|
|
160
158
|
}),
|
|
161
|
-
e.icon === "scale" && /* @__PURE__ */
|
|
159
|
+
e.icon === "scale" && /* @__PURE__ */ i("svg", {
|
|
162
160
|
className: "size-4 text-accent-blue",
|
|
163
161
|
fill: "none",
|
|
164
162
|
viewBox: "0 0 24 24",
|
|
165
163
|
stroke: "currentColor",
|
|
166
|
-
children: /* @__PURE__ */
|
|
164
|
+
children: /* @__PURE__ */ i("path", {
|
|
167
165
|
strokeLinecap: "round",
|
|
168
166
|
strokeLinejoin: "round",
|
|
169
167
|
strokeWidth: 2,
|
|
@@ -171,7 +169,7 @@ function c({ children: e, title: c, subtitle: l, productName: u, tagline: d }) {
|
|
|
171
169
|
})
|
|
172
170
|
})
|
|
173
171
|
]
|
|
174
|
-
}), /* @__PURE__ */
|
|
172
|
+
}), /* @__PURE__ */ i("span", {
|
|
175
173
|
className: "auth-brand-copy text-sm font-medium",
|
|
176
174
|
children: e.text
|
|
177
175
|
})]
|
|
@@ -179,7 +177,7 @@ function c({ children: e, title: c, subtitle: l, productName: u, tagline: d }) {
|
|
|
179
177
|
})
|
|
180
178
|
]
|
|
181
179
|
}),
|
|
182
|
-
/* @__PURE__ */
|
|
180
|
+
/* @__PURE__ */ a("div", {
|
|
183
181
|
className: "auth-brand-copy-muted text-sm",
|
|
184
182
|
children: [
|
|
185
183
|
"© ",
|
|
@@ -189,44 +187,44 @@ function c({ children: e, title: c, subtitle: l, productName: u, tagline: d }) {
|
|
|
189
187
|
})
|
|
190
188
|
]
|
|
191
189
|
})]
|
|
192
|
-
}), /* @__PURE__ */
|
|
190
|
+
}), /* @__PURE__ */ a("main", {
|
|
193
191
|
className: "auth-shell-gradient relative flex flex-1 items-center justify-center bg-[var(--color-bg-canvas)] p-6 sm:p-12",
|
|
194
|
-
children: [/* @__PURE__ */
|
|
192
|
+
children: [/* @__PURE__ */ i("div", {
|
|
195
193
|
className: "absolute top-4 right-4 sm:top-6 sm:right-6 z-10",
|
|
196
|
-
children: /* @__PURE__ */
|
|
197
|
-
languages:
|
|
198
|
-
currentLanguage:
|
|
194
|
+
children: /* @__PURE__ */ i(o, {
|
|
195
|
+
languages: s,
|
|
196
|
+
currentLanguage: v,
|
|
199
197
|
onLanguageChange: (e) => {
|
|
200
|
-
|
|
198
|
+
y(e.code);
|
|
201
199
|
}
|
|
202
200
|
})
|
|
203
|
-
}), /* @__PURE__ */
|
|
201
|
+
}), /* @__PURE__ */ a("div", {
|
|
204
202
|
className: "w-full max-w-md",
|
|
205
203
|
children: [
|
|
206
|
-
/* @__PURE__ */
|
|
204
|
+
/* @__PURE__ */ a("div", {
|
|
207
205
|
className: "lg:hidden flex items-center justify-center gap-3 mb-8",
|
|
208
|
-
children: [/* @__PURE__ */
|
|
206
|
+
children: [/* @__PURE__ */ i(c, { brandInitial: g }), /* @__PURE__ */ i("span", {
|
|
209
207
|
className: "text-xl font-bold text-[var(--color-text-primary)]",
|
|
210
|
-
children:
|
|
208
|
+
children: h
|
|
211
209
|
})]
|
|
212
210
|
}),
|
|
213
|
-
/* @__PURE__ */
|
|
211
|
+
/* @__PURE__ */ a("div", {
|
|
214
212
|
className: "text-center lg:text-left mb-8",
|
|
215
|
-
children: [/* @__PURE__ */
|
|
213
|
+
children: [/* @__PURE__ */ i("h2", {
|
|
216
214
|
className: "text-2xl sm:text-3xl font-bold text-[var(--color-text-primary)] mb-2",
|
|
217
|
-
children: c
|
|
218
|
-
}), /* @__PURE__ */ r("p", {
|
|
219
|
-
className: "text-[var(--color-text-secondary)]",
|
|
220
215
|
children: l
|
|
216
|
+
}), /* @__PURE__ */ i("p", {
|
|
217
|
+
className: "text-[var(--color-text-secondary)]",
|
|
218
|
+
children: u
|
|
221
219
|
})]
|
|
222
220
|
}),
|
|
223
|
-
|
|
221
|
+
t
|
|
224
222
|
]
|
|
225
223
|
})]
|
|
226
224
|
})]
|
|
227
225
|
});
|
|
228
226
|
}
|
|
229
227
|
//#endregion
|
|
230
|
-
export {
|
|
228
|
+
export { l as AuthLayout };
|
|
231
229
|
|
|
232
230
|
//# sourceMappingURL=AuthLayout.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AuthLayout.js","names":[],"sources":["../../../src/auth/components/AuthLayout.tsx"],"sourcesContent":["import { useState, type ReactNode } from \"react\";\nimport { LanguageSwitcher } from \"@burdenoff/fe-libs/chrome\";\nimport type { Language } from \"@burdenoff/fe-libs/chrome\";\nimport {\n useI18n,\n useAppShell,\n} from \"@burdenoff/fe-libs/shared/providers/shell\";\nimport type { Locale } from \"@burdenoff/fe-libs/shared/providers/shell\";\n\ninterface AuthLayoutProps {\n children: ReactNode;\n title: string;\n subtitle: string;\n productName?: string;\n tagline?: string;\n}\n\n/**\n * Supported languages for the auth pages.\n *\n * Must stay in sync with the locales actually seeded in\n * `i18n_translations` for the consumer products that mount microfe-auth.\n * If a locale is listed here but NOT seeded, selecting it sets\n * `setLocale(<code>)` on the I18nProvider, the next `exportTranslations`\n * call returns zero rows, every `tr()` falls back to its English default,\n * and the user gets an English UI under a non-English flag.\n *\n * Audited 2026-05-31 against alpha `i18n_translations`: only `en/ta/hi/\n * zh/es/ar` have actual translations (vibecontrols 40k+ rows each).\n * `fr` was previously listed but no product seeds it — selecting it\n * produced a half-translated UI. Removed for parity with the dashboard\n * `LanguageSwitcherSlot` (which is data-driven from `i18n_languages`).\n *\n * Adding a new locale requires updating BOTH:\n * - this list\n * - each consumer product's seed (e.g. vibecontrols-app/scripts/\n * build-i18n-bundle.ts + the new `<code>.ts` translation source).\n */\nconst AUTH_LANGUAGES: Language[] = [\n { code: \"en\", name: \"English\", nativeName: \"English\", flag: \"🇺🇸\" },\n { code: \"ta\", name: \"Tamil\", nativeName: \"தமிழ்\", flag: \"🇮🇳\" },\n { code: \"hi\", name: \"Hindi\", nativeName: \"हिन्दी\", flag: \"🇮🇳\" },\n { code: \"zh\", name: \"Chinese\", nativeName: \"中文\", flag: \"🇨🇳\" },\n { code: \"es\", name: \"Spanish\", nativeName: \"Español\", flag: \"🇪🇸\" },\n { code: \"ar\", name: \"Arabic\", nativeName: \"العربية\", flag: \"🇸🇦\" },\n];\n\n/**\n * Brand logo for the auth pages.\n *\n * Renders the host app shell's real product logo served at `/icons/icon.svg`\n * (the same asset the marketing landing page uses) so the login screen and the\n * landing page show an identical mark instead of a generic placeholder glyph.\n * Host shells that don't ship that asset (e.g. admin-app) fall back to a\n * branded initial badge rather than a broken image.\n */\nfunction BrandLogo({ brandInitial }: { brandInitial: string }) {\n const [failed, setFailed] = useState(false);\n\n if (failed) {\n return (\n <div className=\"auth-brand-badge flex size-10 items-center justify-center rounded-xl\">\n <span className=\"text-lg font-bold leading-none text-white\">\n {brandInitial}\n </span>\n </div>\n );\n }\n\n return (\n <img\n src=\"/icons/icon.svg\"\n alt=\"\"\n aria-hidden=\"true\"\n className=\"size-10 rounded-xl object-contain\"\n onError={() => setFailed(true)}\n />\n );\n}\n\n/**\n * Professional auth layout with split-screen design\n * Left side: Branding with gradient background\n * Right side: Auth form\n */\nexport function AuthLayout({\n children,\n title,\n subtitle,\n productName,\n tagline,\n}: AuthLayoutProps) {\n const shell = useAppShell();\n const displayName = productName || shell.productName || \"Burdenoff\";\n const brandInitial = shell.brandInitial || \"B\";\n const displayTagline = tagline || shell.tagline || \"Enterprise platform\";\n const { t, locale, setLocale } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n\n const handleLanguageChange = (language: Language) => {\n // AUTH_LANGUAGES intentionally lists locales (e.g. \"ar\") that are seeded\n // in consumer products but not yet present in the fe-libs Locale union.\n // The runtime I18nProvider accepts any locale code, so we cast through\n // unknown to Locale to keep the picker in parity until fe-libs widens the\n // union. Keep AUTH_LANGUAGES in sync with consumer product seeds.\n setLocale(language.code as unknown as Locale);\n };\n\n return (\n <div className=\"min-h-screen flex relative\">\n {/* Left Panel - Branding */}\n <div className=\"auth-brand-panel hidden overflow-hidden lg:flex lg:w-1/2 relative\">\n {/* Decorative Elements */}\n <div className=\"absolute inset-0 overflow-hidden\">\n <div className=\"absolute -top-40 -left-40 size-80 rounded-full bg-accent-blue/20 blur-3xl\" />\n <div className=\"absolute top-1/2 -right-20 size-96 rounded-full bg-accent-indigo/20 blur-3xl\" />\n <div className=\"absolute -bottom-40 left-1/4 size-72 rounded-full bg-accent-purple/20 blur-3xl\" />\n\n {/* Grid Pattern */}\n <div\n className=\"absolute inset-0 opacity-5\"\n style={{\n backgroundImage: `url(\"data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%23ffffff' fill-opacity='1'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E\")`,\n }}\n />\n </div>\n\n {/* Content */}\n <div className=\"relative z-10 flex flex-col justify-between p-12 w-full\">\n {/* Logo */}\n <div className=\"flex items-center gap-3\">\n <BrandLogo brandInitial={brandInitial} />\n <span className=\"text-xl font-bold text-[var(--color-text-primary)]\">\n {displayName}\n </span>\n </div>\n\n {/* Main Content */}\n <div className=\"max-w-md\">\n <h1 className=\"mb-4 text-4xl font-bold leading-tight text-[var(--color-text-primary)]\">\n {displayTagline}\n </h1>\n <p className=\"auth-brand-copy text-lg leading-relaxed\">\n {tr(\n \"auth.layout.taglineDescription\",\n \"Unify your teams, projects, and workflows in one intelligent workspace. Built for scale with enterprise-grade security, real-time collaboration, and seamless integrations across your entire organization.\",\n )}\n </p>\n\n {/* Features */}\n <div className=\"mt-10 space-y-4\">\n {[\n {\n icon: \"workspace\",\n text: tr(\n \"auth.layout.featureWorkspace\",\n \"Modular workspace architecture\",\n ),\n },\n {\n icon: \"realtime\",\n text: tr(\n \"auth.layout.featureRealtime\",\n \"Real-time collaboration & sync\",\n ),\n },\n {\n icon: \"security\",\n text: tr(\n \"auth.layout.featureSecurity\",\n \"Enterprise-grade security & SSO\",\n ),\n },\n {\n icon: \"scale\",\n text: tr(\n \"auth.layout.featureScale\",\n \"Unlimited scalability & performance\",\n ),\n },\n ].map((feature) => (\n <div key={feature.icon} className=\"flex items-center gap-3\">\n <div className=\"auth-highlight-chip flex size-8 items-center justify-center rounded-lg\">\n {feature.icon === \"workspace\" && (\n <svg\n className=\"size-4 text-accent-blue\"\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=\"M4 5a1 1 0 011-1h14a1 1 0 011 1v2a1 1 0 01-1 1H5a1 1 0 01-1-1V5zM4 13a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1H5a1 1 0 01-1-1v-6zM16 13a1 1 0 011-1h2a1 1 0 011 1v6a1 1 0 01-1 1h-2a1 1 0 01-1-1v-6z\"\n />\n </svg>\n )}\n {feature.icon === \"realtime\" && (\n <svg\n className=\"size-4 text-accent-blue\"\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=\"M17 8h2a2 2 0 012 2v6a2 2 0 01-2 2h-2v4l-4-4H9a1.994 1.994 0 01-1.414-.586m0 0L11 14h4a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2v4l.586-.586z\"\n />\n </svg>\n )}\n {feature.icon === \"security\" && (\n <svg\n className=\"size-4 text-accent-blue\"\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=\"M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z\"\n />\n </svg>\n )}\n {feature.icon === \"scale\" && (\n <svg\n className=\"size-4 text-accent-blue\"\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=\"M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z\"\n />\n </svg>\n )}\n </div>\n <span className=\"auth-brand-copy text-sm font-medium\">\n {feature.text}\n </span>\n </div>\n ))}\n </div>\n </div>\n\n {/* Footer */}\n <div className=\"auth-brand-copy-muted text-sm\">\n © {new Date().getFullYear()} Burdenoff. All rights reserved.\n </div>\n </div>\n </div>\n\n {/* Right Panel - Form */}\n <main className=\"auth-shell-gradient relative flex flex-1 items-center justify-center bg-[var(--color-bg-canvas)] p-6 sm:p-12\">\n {/* Language Switcher - Top Right */}\n <div className=\"absolute top-4 right-4 sm:top-6 sm:right-6 z-10\">\n <LanguageSwitcher\n languages={AUTH_LANGUAGES}\n currentLanguage={locale}\n onLanguageChange={handleLanguageChange}\n />\n </div>\n <div className=\"w-full max-w-md\">\n {/* Mobile Logo */}\n <div className=\"lg:hidden flex items-center justify-center gap-3 mb-8\">\n <BrandLogo brandInitial={brandInitial} />\n <span className=\"text-xl font-bold text-[var(--color-text-primary)]\">\n {displayName}\n </span>\n </div>\n\n {/* Header */}\n <div className=\"text-center lg:text-left mb-8\">\n <h2 className=\"text-2xl sm:text-3xl font-bold text-[var(--color-text-primary)] mb-2\">\n {title}\n </h2>\n <p className=\"text-[var(--color-text-secondary)]\">{subtitle}</p>\n </div>\n\n {/* Form Content */}\n {children}\n </div>\n </main>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AuthLayout.js","names":[],"sources":["../../../src/auth/components/AuthLayout.tsx"],"sourcesContent":["import { useState, type ReactNode } from \"react\";\nimport { LanguageSwitcher } from \"@burdenoff/fe-libs/chrome\";\nimport type { Language } from \"@burdenoff/fe-libs/chrome\";\nimport {\n useI18n,\n useAppShell,\n} from \"@burdenoff/fe-libs/shared/providers/shell\";\nimport { useTr } from \"../../shared/hooks/useTr\";\nimport type { Locale } from \"@burdenoff/fe-libs/shared/providers/shell\";\n\ninterface AuthLayoutProps {\n children: ReactNode;\n title: string;\n subtitle: string;\n productName?: string;\n tagline?: string;\n}\n\n/**\n * Supported languages for the auth pages.\n *\n * Must stay in sync with the locales actually seeded in\n * `i18n_translations` for the consumer products that mount microfe-auth.\n * If a locale is listed here but NOT seeded, selecting it sets\n * `setLocale(<code>)` on the I18nProvider, the next `exportTranslations`\n * call returns zero rows, every `tr()` falls back to its English default,\n * and the user gets an English UI under a non-English flag.\n *\n * Audited 2026-05-31 against alpha `i18n_translations`: only `en/ta/hi/\n * zh/es/ar` have actual translations (vibecontrols 40k+ rows each).\n * `fr` was previously listed but no product seeds it — selecting it\n * produced a half-translated UI. Removed for parity with the dashboard\n * `LanguageSwitcherSlot` (which is data-driven from `i18n_languages`).\n *\n * Adding a new locale requires updating BOTH:\n * - this list\n * - each consumer product's seed (e.g. vibecontrols-app/scripts/\n * build-i18n-bundle.ts + the new `<code>.ts` translation source).\n */\nconst AUTH_LANGUAGES: Language[] = [\n { code: \"en\", name: \"English\", nativeName: \"English\", flag: \"🇺🇸\" },\n { code: \"ta\", name: \"Tamil\", nativeName: \"தமிழ்\", flag: \"🇮🇳\" },\n { code: \"hi\", name: \"Hindi\", nativeName: \"हिन्दी\", flag: \"🇮🇳\" },\n { code: \"zh\", name: \"Chinese\", nativeName: \"中文\", flag: \"🇨🇳\" },\n { code: \"es\", name: \"Spanish\", nativeName: \"Español\", flag: \"🇪🇸\" },\n { code: \"ar\", name: \"Arabic\", nativeName: \"العربية\", flag: \"🇸🇦\" },\n];\n\n/**\n * Brand logo for the auth pages.\n *\n * Renders the host app shell's real product logo served at `/icons/icon.svg`\n * (the same asset the marketing landing page uses) so the login screen and the\n * landing page show an identical mark instead of a generic placeholder glyph.\n * Host shells that don't ship that asset (e.g. admin-app) fall back to a\n * branded initial badge rather than a broken image.\n */\nfunction BrandLogo({ brandInitial }: { brandInitial: string }) {\n const [failed, setFailed] = useState(false);\n\n if (failed) {\n return (\n <div className=\"auth-brand-badge flex size-10 items-center justify-center rounded-xl\">\n <span className=\"text-lg font-bold leading-none text-white\">\n {brandInitial}\n </span>\n </div>\n );\n }\n\n return (\n <img\n src=\"/icons/icon.svg\"\n alt=\"\"\n aria-hidden=\"true\"\n className=\"size-10 rounded-xl object-contain\"\n onError={() => setFailed(true)}\n />\n );\n}\n\n/**\n * Professional auth layout with split-screen design\n * Left side: Branding with gradient background\n * Right side: Auth form\n */\nexport function AuthLayout({\n children,\n title,\n subtitle,\n productName,\n tagline,\n}: AuthLayoutProps) {\n const tr = useTr();\n const shell = useAppShell();\n const displayName = productName || shell.productName || \"Burdenoff\";\n const brandInitial = shell.brandInitial || \"B\";\n const displayTagline = tagline || shell.tagline || \"Enterprise platform\";\n const { locale, setLocale } = useI18n();\n\n const handleLanguageChange = (language: Language) => {\n // AUTH_LANGUAGES intentionally lists locales (e.g. \"ar\") that are seeded\n // in consumer products but not yet present in the fe-libs Locale union.\n // The runtime I18nProvider accepts any locale code, so we cast through\n // unknown to Locale to keep the picker in parity until fe-libs widens the\n // union. Keep AUTH_LANGUAGES in sync with consumer product seeds.\n setLocale(language.code as unknown as Locale);\n };\n\n return (\n <div className=\"min-h-screen flex relative\">\n {/* Left Panel - Branding */}\n <div className=\"auth-brand-panel hidden overflow-hidden lg:flex lg:w-1/2 relative\">\n {/* Decorative Elements */}\n <div className=\"absolute inset-0 overflow-hidden\">\n <div className=\"absolute -top-40 -left-40 size-80 rounded-full bg-accent-blue/20 blur-3xl\" />\n <div className=\"absolute top-1/2 -right-20 size-96 rounded-full bg-accent-indigo/20 blur-3xl\" />\n <div className=\"absolute -bottom-40 left-1/4 size-72 rounded-full bg-accent-purple/20 blur-3xl\" />\n\n {/* Grid Pattern */}\n <div\n className=\"absolute inset-0 opacity-5\"\n style={{\n backgroundImage: `url(\"data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%23ffffff' fill-opacity='1'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E\")`,\n }}\n />\n </div>\n\n {/* Content */}\n <div className=\"relative z-10 flex flex-col justify-between p-12 w-full\">\n {/* Logo */}\n <div className=\"flex items-center gap-3\">\n <BrandLogo brandInitial={brandInitial} />\n <span className=\"text-xl font-bold text-[var(--color-text-primary)]\">\n {displayName}\n </span>\n </div>\n\n {/* Main Content */}\n <div className=\"max-w-md\">\n <h1 className=\"mb-4 text-4xl font-bold leading-tight text-[var(--color-text-primary)]\">\n {displayTagline}\n </h1>\n <p className=\"auth-brand-copy text-lg leading-relaxed\">\n {tr(\n \"auth.layout.taglineDescription\",\n \"Unify your teams, projects, and workflows in one intelligent workspace. Built for scale with enterprise-grade security, real-time collaboration, and seamless integrations across your entire organization.\",\n )}\n </p>\n\n {/* Features */}\n <div className=\"mt-10 space-y-4\">\n {[\n {\n icon: \"workspace\",\n text: tr(\n \"auth.layout.featureWorkspace\",\n \"Modular workspace architecture\",\n ),\n },\n {\n icon: \"realtime\",\n text: tr(\n \"auth.layout.featureRealtime\",\n \"Real-time collaboration & sync\",\n ),\n },\n {\n icon: \"security\",\n text: tr(\n \"auth.layout.featureSecurity\",\n \"Enterprise-grade security & SSO\",\n ),\n },\n {\n icon: \"scale\",\n text: tr(\n \"auth.layout.featureScale\",\n \"Unlimited scalability & performance\",\n ),\n },\n ].map((feature) => (\n <div key={feature.icon} className=\"flex items-center gap-3\">\n <div className=\"auth-highlight-chip flex size-8 items-center justify-center rounded-lg\">\n {feature.icon === \"workspace\" && (\n <svg\n className=\"size-4 text-accent-blue\"\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=\"M4 5a1 1 0 011-1h14a1 1 0 011 1v2a1 1 0 01-1 1H5a1 1 0 01-1-1V5zM4 13a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1H5a1 1 0 01-1-1v-6zM16 13a1 1 0 011-1h2a1 1 0 011 1v6a1 1 0 01-1 1h-2a1 1 0 01-1-1v-6z\"\n />\n </svg>\n )}\n {feature.icon === \"realtime\" && (\n <svg\n className=\"size-4 text-accent-blue\"\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=\"M17 8h2a2 2 0 012 2v6a2 2 0 01-2 2h-2v4l-4-4H9a1.994 1.994 0 01-1.414-.586m0 0L11 14h4a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2v4l.586-.586z\"\n />\n </svg>\n )}\n {feature.icon === \"security\" && (\n <svg\n className=\"size-4 text-accent-blue\"\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=\"M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z\"\n />\n </svg>\n )}\n {feature.icon === \"scale\" && (\n <svg\n className=\"size-4 text-accent-blue\"\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=\"M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z\"\n />\n </svg>\n )}\n </div>\n <span className=\"auth-brand-copy text-sm font-medium\">\n {feature.text}\n </span>\n </div>\n ))}\n </div>\n </div>\n\n {/* Footer */}\n <div className=\"auth-brand-copy-muted text-sm\">\n © {new Date().getFullYear()} Burdenoff. All rights reserved.\n </div>\n </div>\n </div>\n\n {/* Right Panel - Form */}\n <main className=\"auth-shell-gradient relative flex flex-1 items-center justify-center bg-[var(--color-bg-canvas)] p-6 sm:p-12\">\n {/* Language Switcher - Top Right */}\n <div className=\"absolute top-4 right-4 sm:top-6 sm:right-6 z-10\">\n <LanguageSwitcher\n languages={AUTH_LANGUAGES}\n currentLanguage={locale}\n onLanguageChange={handleLanguageChange}\n />\n </div>\n <div className=\"w-full max-w-md\">\n {/* Mobile Logo */}\n <div className=\"lg:hidden flex items-center justify-center gap-3 mb-8\">\n <BrandLogo brandInitial={brandInitial} />\n <span className=\"text-xl font-bold text-[var(--color-text-primary)]\">\n {displayName}\n </span>\n </div>\n\n {/* Header */}\n <div className=\"text-center lg:text-left mb-8\">\n <h2 className=\"text-2xl sm:text-3xl font-bold text-[var(--color-text-primary)] mb-2\">\n {title}\n </h2>\n <p className=\"text-[var(--color-text-secondary)]\">{subtitle}</p>\n </div>\n\n {/* Form Content */}\n {children}\n </div>\n </main>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,41 +1,38 @@
|
|
|
1
1
|
import { getRecaptchaSiteKey as e, isCaptchaEnabled as t } from "../utils/captchaConfig.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { useTr as n } from "../../shared/hooks/useTr.js";
|
|
3
|
+
import { Suspense as r, lazy as i, useCallback as a, useState as o } from "react";
|
|
4
4
|
import { jsx as s } from "react/jsx-runtime";
|
|
5
5
|
//#region src/auth/components/CaptchaV2.tsx
|
|
6
|
-
var c =
|
|
7
|
-
let
|
|
8
|
-
|
|
9
|
-
}, [
|
|
10
|
-
|
|
11
|
-
}, [
|
|
12
|
-
return
|
|
6
|
+
var c = i(() => import("../../node_modules/react-google-recaptcha/lib/esm/index.js").then((t) => ({ default: function({ onVerify: r, onExpired: i, siteKey: c }) {
|
|
7
|
+
let l = n(), u = t.default, [, d] = o(null), f = c ?? e(), p = a((e) => {
|
|
8
|
+
r(e);
|
|
9
|
+
}, [r]), m = a(() => {
|
|
10
|
+
r(null), i?.();
|
|
11
|
+
}, [r, i]);
|
|
12
|
+
return f ? /* @__PURE__ */ s("div", {
|
|
13
13
|
className: "flex justify-center",
|
|
14
|
-
children: /* @__PURE__ */ s(
|
|
15
|
-
ref:
|
|
16
|
-
sitekey:
|
|
17
|
-
onChange:
|
|
18
|
-
onExpired:
|
|
14
|
+
children: /* @__PURE__ */ s(u, {
|
|
15
|
+
ref: d,
|
|
16
|
+
sitekey: f,
|
|
17
|
+
onChange: p,
|
|
18
|
+
onExpired: m
|
|
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
|
-
children: "reCAPTCHA site key is not configured."
|
|
22
|
+
children: l("auth.captcha.siteKeyMissing", "reCAPTCHA site key is not configured.")
|
|
23
23
|
});
|
|
24
24
|
} })));
|
|
25
|
-
function l({ onVerify: e, onExpired:
|
|
26
|
-
let
|
|
27
|
-
return
|
|
25
|
+
function l({ onVerify: e, onExpired: i, enabled: a, siteKey: o }) {
|
|
26
|
+
let l = n();
|
|
27
|
+
return a ?? t() ? /* @__PURE__ */ s(r, {
|
|
28
28
|
fallback: /* @__PURE__ */ s("div", {
|
|
29
29
|
className: "flex justify-center py-2",
|
|
30
|
-
children: (
|
|
31
|
-
let n = l(e);
|
|
32
|
-
return n === e ? t : n;
|
|
33
|
-
})("auth.captcha.loading", "Loading captcha...")
|
|
30
|
+
children: l("auth.captcha.loading", "Loading captcha...")
|
|
34
31
|
}),
|
|
35
32
|
children: /* @__PURE__ */ s(c, {
|
|
36
33
|
onVerify: e,
|
|
37
|
-
onExpired:
|
|
38
|
-
siteKey:
|
|
34
|
+
onExpired: i,
|
|
35
|
+
siteKey: o
|
|
39
36
|
})
|
|
40
37
|
}) : null;
|
|
41
38
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CaptchaV2.js","names":[],"sources":["../../../src/auth/components/CaptchaV2.tsx"],"sourcesContent":["import { lazy, Suspense, useState, useCallback } from \"react\";\nimport {
|
|
1
|
+
{"version":3,"file":"CaptchaV2.js","names":[],"sources":["../../../src/auth/components/CaptchaV2.tsx"],"sourcesContent":["import { lazy, Suspense, useState, useCallback } from \"react\";\nimport { useTr } from \"../../shared/hooks/useTr\";\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 tr = useTr();\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 {tr(\n \"auth.captcha.siteKeyMissing\",\n \"reCAPTCHA site key is not configured.\",\n )}\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 tr = useTr();\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,IAAK,GAAO,EACZ,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;AAazB,QAXK,IAYH,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,GAAD;GACE,KAAK;GACL,SAAS;GACT,UAAU;GACV,WAAW;GACX,CAAA;EACE,CAAA,GAjBJ,kBAAC,OAAD;EAAK,WAAU;YACZ,EACC,+BACA,wCACD;EACG,CAAA;GAeb,EAAE,CACJ;AAUD,SAAgB,EAAU,EACxB,aACA,cACA,YACA,cACiB;CACjB,IAAM,IAAK,GAAO;AAOlB,QALkB,KAAW,GAAqB,GAMhD,kBAAC,GAAD;EACE,UACE,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAG,wBAAwB,qBAAqB;GAC7C,CAAA;YAGR,kBAAC,GAAD;GACY;GACC;GACF;GACT,CAAA;EACO,CAAA,GAhBJ"}
|