@forjio/auth-ui 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/dist/AuthForm.cjs +38 -30
  2. package/dist/AuthForm.cjs.map +1 -1
  3. package/dist/AuthForm.d.cts +2 -2
  4. package/dist/AuthForm.d.ts +2 -2
  5. package/dist/AuthForm.js +38 -30
  6. package/dist/AuthForm.js.map +1 -1
  7. package/dist/ForgotPasswordForm.cjs +11 -6
  8. package/dist/ForgotPasswordForm.cjs.map +1 -1
  9. package/dist/ForgotPasswordForm.d.cts +2 -2
  10. package/dist/ForgotPasswordForm.d.ts +2 -2
  11. package/dist/ForgotPasswordForm.js +11 -6
  12. package/dist/ForgotPasswordForm.js.map +1 -1
  13. package/dist/ResetPasswordForm.cjs +8 -5
  14. package/dist/ResetPasswordForm.cjs.map +1 -1
  15. package/dist/ResetPasswordForm.d.cts +2 -2
  16. package/dist/ResetPasswordForm.d.ts +2 -2
  17. package/dist/ResetPasswordForm.js +8 -5
  18. package/dist/ResetPasswordForm.js.map +1 -1
  19. package/dist/components/ui/button.cjs +84 -0
  20. package/dist/components/ui/button.cjs.map +1 -0
  21. package/dist/components/ui/button.d.cts +14 -0
  22. package/dist/components/ui/button.d.ts +14 -0
  23. package/dist/components/ui/button.js +49 -0
  24. package/dist/components/ui/button.js.map +1 -0
  25. package/dist/components/ui/input.cjs +58 -0
  26. package/dist/components/ui/input.cjs.map +1 -0
  27. package/dist/components/ui/input.d.cts +5 -0
  28. package/dist/components/ui/input.d.ts +5 -0
  29. package/dist/components/ui/input.js +24 -0
  30. package/dist/components/ui/input.js.map +1 -0
  31. package/dist/components/ui/label.cjs +56 -0
  32. package/dist/components/ui/label.cjs.map +1 -0
  33. package/dist/components/ui/label.d.cts +8 -0
  34. package/dist/components/ui/label.d.ts +8 -0
  35. package/dist/components/ui/label.js +22 -0
  36. package/dist/components/ui/label.js.map +1 -0
  37. package/dist/lib/utils.cjs +33 -0
  38. package/dist/lib/utils.cjs.map +1 -0
  39. package/dist/lib/utils.d.cts +9 -0
  40. package/dist/lib/utils.d.ts +9 -0
  41. package/dist/lib/utils.js +9 -0
  42. package/dist/lib/utils.js.map +1 -0
  43. package/dist/useTurnstileTheme.cjs +44 -0
  44. package/dist/useTurnstileTheme.cjs.map +1 -0
  45. package/dist/useTurnstileTheme.d.cts +3 -0
  46. package/dist/useTurnstileTheme.d.ts +3 -0
  47. package/dist/useTurnstileTheme.js +20 -0
  48. package/dist/useTurnstileTheme.js.map +1 -0
  49. package/package.json +7 -2
package/dist/AuthForm.cjs CHANGED
@@ -38,6 +38,10 @@ var import_navigation = require("next/navigation");
38
38
  var import_link = __toESM(require("next/link"), 1);
39
39
  var import_lucide_react = require("lucide-react");
40
40
  var import_react_turnstile = require("@marsidev/react-turnstile");
41
+ var import_useTurnstileTheme = require("./useTurnstileTheme");
42
+ var import_button = require("./components/ui/button");
43
+ var import_input = require("./components/ui/input");
44
+ var import_label = require("./components/ui/label");
41
45
  var import_types = require("./types");
42
46
  const TURNSTILE_SITE_KEY = process.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY;
43
47
  function AuthForm({
@@ -64,6 +68,7 @@ function AuthForm({
64
68
  const [submitting, setSubmitting] = (0, import_react.useState)(false);
65
69
  const [redirecting, setRedirecting] = (0, import_react.useState)(false);
66
70
  const [turnstileToken, setTurnstileToken] = (0, import_react.useState)("");
71
+ const turnstileTheme = (0, import_useTurnstileTheme.useTurnstileTheme)();
67
72
  const [error, setError] = (0, import_react.useState)(
68
73
  ssoError ? `Sign-in failed: ${ssoDetail || ssoError}` : null
69
74
  );
@@ -121,37 +126,40 @@ function AuthForm({
121
126
  ] }),
122
127
  hasAnySocial && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
123
128
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "grid gap-2", children: [
124
- showGoogle && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
125
- "a",
129
+ showGoogle && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
130
+ import_button.Button,
126
131
  {
127
- href: socialUrl("google"),
128
- className: "flex w-full items-center justify-center gap-2 rounded-md border border-border bg-background py-2 text-sm font-medium hover:bg-accent",
129
- children: [
132
+ asChild: true,
133
+ variant: "outline",
134
+ className: "flex h-auto w-full border-border py-2 shadow-none",
135
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("a", { href: socialUrl("google"), children: [
130
136
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GoogleMark, { className: "h-4 w-4" }),
131
137
  "Continue with Google"
132
- ]
138
+ ] })
133
139
  }
134
140
  ),
135
- showApple && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
136
- "a",
141
+ showApple && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
142
+ import_button.Button,
137
143
  {
138
- href: socialUrl("apple"),
139
- className: "flex w-full items-center justify-center gap-2 rounded-md border border-border bg-background py-2 text-sm font-medium hover:bg-accent",
140
- children: [
144
+ asChild: true,
145
+ variant: "outline",
146
+ className: "flex h-auto w-full border-border py-2 shadow-none",
147
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("a", { href: socialUrl("apple"), children: [
141
148
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AppleMark, { className: "h-4 w-4" }),
142
149
  "Continue with Apple"
143
- ]
150
+ ] })
144
151
  }
145
152
  ),
146
- showFacebook && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
147
- "a",
153
+ showFacebook && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
154
+ import_button.Button,
148
155
  {
149
- href: socialUrl("facebook"),
150
- className: "flex w-full items-center justify-center gap-2 rounded-md border border-border bg-background py-2 text-sm font-medium hover:bg-accent",
151
- children: [
156
+ asChild: true,
157
+ variant: "outline",
158
+ className: "flex h-auto w-full border-border py-2 shadow-none",
159
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("a", { href: socialUrl("facebook"), children: [
152
160
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FacebookMark, { className: "h-4 w-4" }),
153
161
  "Continue with Facebook"
154
- ]
162
+ ] })
155
163
  }
156
164
  )
157
165
  ] }),
@@ -163,23 +171,23 @@ function AuthForm({
163
171
  ] }),
164
172
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { onSubmit: submit, className: "space-y-3", children: [
165
173
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
166
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: "mb-1 block text-xs font-medium text-muted-foreground", children: "Email" }),
174
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_label.Label, { className: "mb-1 block text-xs leading-4 text-muted-foreground", children: "Email" }),
167
175
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
168
- "input",
176
+ import_input.Input,
169
177
  {
170
178
  type: "email",
171
179
  required: true,
172
180
  value: email,
173
181
  onChange: (e) => setEmail(e.target.value),
174
182
  autoComplete: "email",
175
- className: "w-full rounded-md border border-border bg-background px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-primary"
183
+ className: "h-auto border-border bg-background py-2 text-sm shadow-none focus:outline-none focus:ring-1 focus:ring-primary"
176
184
  }
177
185
  )
178
186
  ] }),
179
187
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
180
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: "mb-1 block text-xs font-medium text-muted-foreground", children: "Password" }),
188
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_label.Label, { className: "mb-1 block text-xs leading-4 text-muted-foreground", children: "Password" }),
181
189
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
182
- "input",
190
+ import_input.Input,
183
191
  {
184
192
  type: "password",
185
193
  required: true,
@@ -187,24 +195,24 @@ function AuthForm({
187
195
  value: password,
188
196
  onChange: (e) => setPassword(e.target.value),
189
197
  autoComplete: mode === "signup" ? "new-password" : "current-password",
190
- className: "w-full rounded-md border border-border bg-background px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-primary"
198
+ className: "h-auto border-border bg-background py-2 text-sm shadow-none focus:outline-none focus:ring-1 focus:ring-primary"
191
199
  }
192
200
  ),
193
201
  mode === "signup" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "mt-1 text-[11px] text-muted-foreground", children: "At least 10 characters, with a letter and a number." })
194
202
  ] }),
195
203
  mode === "signup" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
196
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "mb-1 block text-xs font-medium text-muted-foreground", children: [
204
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_label.Label, { className: "mb-1 block text-xs leading-4 text-muted-foreground", children: [
197
205
  "Your name ",
198
206
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-muted-foreground/60", children: "(optional)" })
199
207
  ] }),
200
208
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
201
- "input",
209
+ import_input.Input,
202
210
  {
203
211
  type: "text",
204
212
  value: name,
205
213
  onChange: (e) => setName(e.target.value),
206
214
  autoComplete: "name",
207
- className: "w-full rounded-md border border-border bg-background px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-primary"
215
+ className: "h-auto border-border bg-background py-2 text-sm shadow-none focus:outline-none focus:ring-1 focus:ring-primary"
208
216
  }
209
217
  )
210
218
  ] }),
@@ -214,15 +222,15 @@ function AuthForm({
214
222
  siteKey: TURNSTILE_SITE_KEY,
215
223
  onSuccess: setTurnstileToken,
216
224
  onError: () => setError("Security check failed."),
217
- options: { theme: "auto" }
225
+ options: { theme: turnstileTheme }
218
226
  }
219
227
  ) }),
220
228
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
221
- "button",
229
+ import_button.Button,
222
230
  {
223
231
  type: "submit",
224
232
  disabled: submitting || redirecting,
225
- className: "flex w-full items-center justify-center gap-2 rounded-md bg-primary py-2.5 text-sm font-medium text-primary-foreground transition hover:opacity-90 disabled:opacity-50",
233
+ className: "flex h-auto w-full py-2.5 shadow-none hover:opacity-90",
226
234
  children: [
227
235
  (submitting || redirecting) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Loader2, { className: "h-4 w-4 animate-spin" }),
228
236
  redirecting ? "Redirecting\u2026" : submitting ? mode === "signup" ? "Creating\u2026" : "Signing in\u2026" : mode === "signup" ? "Create account" : "Sign in"
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/AuthForm.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport { useRouter, useSearchParams } from 'next/navigation';\nimport Link from 'next/link';\nimport { Loader2, AlertCircle } from 'lucide-react';\nimport { Turnstile } from '@marsidev/react-turnstile';\nimport { defaultEndpoints, type AuthEndpoints, type SocialProviders } from './types';\n\n// Cloudflare Turnstile is enabled family-wide by setting\n// NEXT_PUBLIC_TURNSTILE_SITE_KEY at build time. Next inlines NEXT_PUBLIC_*\n// referenced here (in node_modules) into each product's bundle, so no\n// per-product page edit is needed — set the env var + the widget appears,\n// and the token rides the login/signup request as `cf-turnstile-response`.\n// The backend (@forjio/sdk/auth-server) verifies it when\n// TURNSTILE_SECRET_KEY is set; both sides bypass gracefully when unset.\nconst TURNSTILE_SITE_KEY = process.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY;\n\nexport interface AuthFormProps {\n mode: 'login' | 'signup';\n /** Display name shown in copy (\"New to Plugipay?\", \"Welcome back\"). */\n brand: string;\n /** Override the auth endpoint paths. Default matches Forjio family\n * `@forjio/sdk/auth-handlers` mounts. */\n endpoints?: Partial<AuthEndpoints>;\n /** Which social providers to render. Host fetches the provider\n * status; pass undefined to show all (fail-open). */\n providers?: SocialProviders | null;\n /** Default redirect target after a successful auth. Default\n * `/dashboard`. Search-param `?return_to=` overrides at runtime. */\n defaultReturnTo?: string;\n /** Mode-switch link targets. Default `/login` / `/signup`. Override\n * for products with non-default auth routes (e.g. role-scoped\n * `/creators/login` + `/creators/onboarding`). `?return_to=` is\n * appended automatically. */\n loginHref?: string;\n signupHref?: string;\n /** \"Forgot password?\" link target. Default `/forgot-password`. */\n forgotPasswordHref?: string;\n /** Extra fields merged into the login/signup request body — e.g. a\n * `role` discriminator for multi-role products. */\n extraBody?: Record<string, unknown>;\n /** Extra query params appended to the social-start URL — e.g.\n * `{ role }`, which the Huudis OIDC start needs to mint the correct\n * per-role session on callback. */\n socialParams?: Record<string, string>;\n}\n\nexport function AuthForm({\n mode,\n brand,\n endpoints,\n providers,\n defaultReturnTo = '/dashboard',\n loginHref = '/login',\n signupHref = '/signup',\n forgotPasswordHref = '/forgot-password',\n extraBody,\n socialParams,\n}: AuthFormProps) {\n const router = useRouter();\n const params = useSearchParams();\n const returnTo = params?.get('return_to') || defaultReturnTo;\n const ssoError = params?.get('sso_error');\n const ssoDetail = params?.get('sso_detail');\n const ep: AuthEndpoints = { ...defaultEndpoints, ...endpoints };\n\n const [email, setEmail] = useState('');\n const [password, setPassword] = useState('');\n const [name, setName] = useState('');\n const [submitting, setSubmitting] = useState(false);\n const [redirecting, setRedirecting] = useState(false);\n const [turnstileToken, setTurnstileToken] = useState('');\n const [error, setError] = useState<string | null>(\n ssoError ? `Sign-in failed: ${ssoDetail || ssoError}` : null,\n );\n\n async function submit(e: React.FormEvent) {\n e.preventDefault();\n setError(null);\n setSubmitting(true);\n try {\n const path = mode === 'signup' ? ep.signup : ep.login;\n const body: Record<string, unknown> = { email, password, ...extraBody };\n if (mode === 'signup' && name.trim()) body.name = name.trim();\n if (TURNSTILE_SITE_KEY) body['cf-turnstile-response'] = turnstileToken;\n const res = await fetch(path, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n credentials: 'include',\n body: JSON.stringify(body),\n });\n if (!res.ok) {\n const payload = (await res.json().catch(() => null)) as {\n error?: { code?: string; message?: string };\n } | null;\n // The product BFF does not do inline MFA. When a user has MFA\n // enabled, Huudis ROPC fails and the SDK returns 401 with\n // `code: 'MFA_REQUIRED'`. Hand off to the Huudis hosted-login\n // flow (no `provider=` param) — Huudis performs the challenge.\n // `socialParams` (e.g. `{ role }`) MUST ride along so a\n // multi-role product mints the correct per-role session on the\n // OIDC callback — otherwise the role is lost and the user is\n // gated on the wrong portal.\n if (payload?.error?.code === 'MFA_REQUIRED') {\n setRedirecting(true);\n const qs = new URLSearchParams({ return_to: returnTo, ...socialParams });\n window.location.href = `${ep.socialStart}?${qs.toString()}`;\n return;\n }\n throw new Error(payload?.error?.message ?? `Request failed (${res.status})`);\n }\n router.push(returnTo);\n router.refresh();\n } catch (err) {\n setError((err as Error).message);\n } finally {\n setSubmitting(false);\n }\n }\n\n const otherMode = mode === 'login' ? 'signup' : 'login';\n const otherHref = `${otherMode === 'signup' ? signupHref : loginHref}?return_to=${encodeURIComponent(returnTo)}`;\n const socialUrl = (provider: 'google' | 'apple' | 'facebook') => {\n const qs = new URLSearchParams({ provider, return_to: returnTo, ...socialParams });\n return `${ep.socialStart}?${qs.toString()}`;\n };\n\n const showGoogle = providers?.google !== false;\n const showApple = providers?.apple !== false;\n const showFacebook = providers?.facebook !== false;\n const hasAnySocial = showGoogle || showApple || showFacebook;\n\n return (\n <div className=\"space-y-4\">\n {error && !redirecting && (\n <div className=\"flex items-start gap-2 rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-sm text-destructive\">\n <AlertCircle className=\"h-4 w-4 shrink-0 mt-0.5\" />\n <span>{error}</span>\n </div>\n )}\n\n {redirecting && (\n <div className=\"flex items-start gap-2 rounded-md border border-border bg-accent px-3 py-2 text-sm text-muted-foreground\">\n <Loader2 className=\"h-4 w-4 shrink-0 mt-0.5 animate-spin\" />\n <span>Redirecting you to complete two-factor sign-in…</span>\n </div>\n )}\n\n {hasAnySocial && (\n <>\n <div className=\"grid gap-2\">\n {showGoogle && (\n <a\n href={socialUrl('google')}\n className=\"flex w-full items-center justify-center gap-2 rounded-md border border-border bg-background py-2 text-sm font-medium hover:bg-accent\"\n >\n <GoogleMark className=\"h-4 w-4\" />\n Continue with Google\n </a>\n )}\n {showApple && (\n <a\n href={socialUrl('apple')}\n className=\"flex w-full items-center justify-center gap-2 rounded-md border border-border bg-background py-2 text-sm font-medium hover:bg-accent\"\n >\n <AppleMark className=\"h-4 w-4\" />\n Continue with Apple\n </a>\n )}\n {showFacebook && (\n <a\n href={socialUrl('facebook')}\n className=\"flex w-full items-center justify-center gap-2 rounded-md border border-border bg-background py-2 text-sm font-medium hover:bg-accent\"\n >\n <FacebookMark className=\"h-4 w-4\" />\n Continue with Facebook\n </a>\n )}\n </div>\n\n <div className=\"my-4 flex items-center gap-3 text-[11px] text-muted-foreground\">\n <div className=\"flex-1 border-t border-border\" />\n OR\n <div className=\"flex-1 border-t border-border\" />\n </div>\n </>\n )}\n\n <form onSubmit={submit} className=\"space-y-3\">\n <div>\n <label className=\"mb-1 block text-xs font-medium text-muted-foreground\">Email</label>\n <input\n type=\"email\"\n required\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n autoComplete=\"email\"\n className=\"w-full rounded-md border border-border bg-background px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-primary\"\n />\n </div>\n <div>\n <label className=\"mb-1 block text-xs font-medium text-muted-foreground\">Password</label>\n <input\n type=\"password\"\n required\n minLength={mode === 'signup' ? 10 : undefined}\n value={password}\n onChange={(e) => setPassword(e.target.value)}\n autoComplete={mode === 'signup' ? 'new-password' : 'current-password'}\n className=\"w-full rounded-md border border-border bg-background px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-primary\"\n />\n {mode === 'signup' && (\n <p className=\"mt-1 text-[11px] text-muted-foreground\">At least 10 characters, with a letter and a number.</p>\n )}\n </div>\n {mode === 'signup' && (\n <div>\n <label className=\"mb-1 block text-xs font-medium text-muted-foreground\">\n Your name <span className=\"text-muted-foreground/60\">(optional)</span>\n </label>\n <input\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n autoComplete=\"name\"\n className=\"w-full rounded-md border border-border bg-background px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-primary\"\n />\n </div>\n )}\n {TURNSTILE_SITE_KEY && (\n <div className=\"flex justify-center py-1\">\n <Turnstile\n siteKey={TURNSTILE_SITE_KEY}\n onSuccess={setTurnstileToken}\n onError={() => setError('Security check failed.')}\n options={{ theme: 'auto' }}\n />\n </div>\n )}\n <button\n type=\"submit\"\n disabled={submitting || redirecting}\n className=\"flex w-full items-center justify-center gap-2 rounded-md bg-primary py-2.5 text-sm font-medium text-primary-foreground transition hover:opacity-90 disabled:opacity-50\"\n >\n {(submitting || redirecting) && <Loader2 className=\"h-4 w-4 animate-spin\" />}\n {redirecting\n ? 'Redirecting…'\n : submitting\n ? mode === 'signup'\n ? 'Creating…'\n : 'Signing in…'\n : mode === 'signup'\n ? 'Create account'\n : 'Sign in'}\n </button>\n </form>\n\n <div className=\"flex items-center justify-between pt-2 text-xs text-muted-foreground\">\n {mode === 'login' && (\n <Link href={forgotPasswordHref} className=\"hover:text-foreground\">\n Forgot password?\n </Link>\n )}\n <span className={mode === 'login' ? '' : 'ml-auto'}>\n {mode === 'login' ? `New to ${brand}?` : 'Already have an account?'}{' '}\n <Link href={otherHref} className=\"font-medium text-foreground hover:underline\">\n {mode === 'login' ? 'Sign up' : 'Sign in'}\n </Link>\n </span>\n </div>\n <p className=\"pt-2 text-[11px] leading-relaxed text-muted-foreground/80\">\n Identity is powered by{' '}\n <a href=\"https://huudis.com\" className=\"underline hover:text-foreground\">\n Huudis\n </a>\n . One account for every Forjio product.\n </p>\n </div>\n );\n}\n\nfunction GoogleMark({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path d=\"M21.6 12.227c0-.708-.064-1.39-.182-2.045H12v3.868h5.384a4.603 4.603 0 0 1-1.997 3.018v2.51h3.232c1.891-1.742 2.98-4.307 2.98-7.35Z\" fill=\"#4285F4\" />\n <path d=\"M12 22c2.7 0 4.965-.895 6.62-2.422l-3.233-2.51c-.895.6-2.041.955-3.386.955-2.604 0-4.81-1.76-5.596-4.122H3.067v2.59A9.996 9.996 0 0 0 12 22Z\" fill=\"#34A853\" />\n <path d=\"M6.404 13.9a6.016 6.016 0 0 1 0-3.8V7.512H3.067a9.996 9.996 0 0 0 0 8.977L6.404 13.9Z\" fill=\"#FBBC05\" />\n <path d=\"M12 5.977c1.468 0 2.786.505 3.823 1.497l2.868-2.868C16.96 2.986 14.696 2 12 2 8.118 2 4.76 4.232 3.067 7.51l3.337 2.59C7.19 7.737 9.396 5.977 12 5.977Z\" fill=\"#EA4335\" />\n </svg>\n );\n}\n\nfunction FacebookMark({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"#1877F2\" aria-hidden=\"true\">\n <path d=\"M24 12.073C24 5.404 18.627 0 12 0S0 5.404 0 12.073c0 6.026 4.388 11.022 10.125 11.927v-8.437H7.078v-3.49h3.047V9.41c0-3.026 1.792-4.697 4.533-4.697 1.313 0 2.686.236 2.686.236v2.971H15.83c-1.49 0-1.955.93-1.955 1.886v2.266h3.328l-.532 3.49h-2.796v8.437C19.612 23.095 24 18.099 24 12.073Z\" />\n </svg>\n );\n}\n\nfunction AppleMark({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M17.564 12.73c-.037-3.16 2.58-4.678 2.698-4.752-1.47-2.146-3.76-2.44-4.576-2.473-1.948-.2-3.8 1.148-4.788 1.148-.993 0-2.513-1.12-4.13-1.091-2.127.03-4.085 1.236-5.174 3.142-2.207 3.82-.562 9.463 1.58 12.56 1.052 1.514 2.306 3.216 3.952 3.155 1.586-.065 2.185-1.026 4.102-1.026 1.917 0 2.455 1.026 4.133.99 1.705-.03 2.785-1.546 3.83-3.066 1.207-1.757 1.702-3.462 1.731-3.55-.038-.018-3.325-1.274-3.358-5.037Zm-3.154-9.24c.878-1.06 1.467-2.542 1.306-4.014-1.26.051-2.79.838-3.695 1.898-.813.937-1.524 2.433-1.333 3.885 1.405.108 2.843-.712 3.722-1.77Z\" />\n </svg>\n );\n}\n"],"mappings":"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","names":["Link"]}
1
+ {"version":3,"sources":["../src/AuthForm.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport { useRouter, useSearchParams } from 'next/navigation';\nimport Link from 'next/link';\nimport { Loader2, AlertCircle } from 'lucide-react';\nimport { Turnstile } from '@marsidev/react-turnstile';\nimport { useTurnstileTheme } from './useTurnstileTheme';\nimport { Button } from './components/ui/button';\nimport { Input } from './components/ui/input';\nimport { Label } from './components/ui/label';\nimport { defaultEndpoints, type AuthEndpoints, type SocialProviders } from './types';\n\n// Cloudflare Turnstile is enabled family-wide by setting\n// NEXT_PUBLIC_TURNSTILE_SITE_KEY at build time. Next inlines NEXT_PUBLIC_*\n// referenced here (in node_modules) into each product's bundle, so no\n// per-product page edit is needed — set the env var + the widget appears,\n// and the token rides the login/signup request as `cf-turnstile-response`.\n// The backend (@forjio/sdk/auth-server) verifies it when\n// TURNSTILE_SECRET_KEY is set; both sides bypass gracefully when unset.\nconst TURNSTILE_SITE_KEY = process.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY;\n\nexport interface AuthFormProps {\n mode: 'login' | 'signup';\n /** Display name shown in copy (\"New to Plugipay?\", \"Welcome back\"). */\n brand: string;\n /** Override the auth endpoint paths. Default matches Forjio family\n * `@forjio/sdk/auth-handlers` mounts. */\n endpoints?: Partial<AuthEndpoints>;\n /** Which social providers to render. Host fetches the provider\n * status; pass undefined to show all (fail-open). */\n providers?: SocialProviders | null;\n /** Default redirect target after a successful auth. Default\n * `/dashboard`. Search-param `?return_to=` overrides at runtime. */\n defaultReturnTo?: string;\n /** Mode-switch link targets. Default `/login` / `/signup`. Override\n * for products with non-default auth routes (e.g. role-scoped\n * `/creators/login` + `/creators/onboarding`). `?return_to=` is\n * appended automatically. */\n loginHref?: string;\n signupHref?: string;\n /** \"Forgot password?\" link target. Default `/forgot-password`. */\n forgotPasswordHref?: string;\n /** Extra fields merged into the login/signup request body — e.g. a\n * `role` discriminator for multi-role products. */\n extraBody?: Record<string, unknown>;\n /** Extra query params appended to the social-start URL — e.g.\n * `{ role }`, which the Huudis OIDC start needs to mint the correct\n * per-role session on callback. */\n socialParams?: Record<string, string>;\n}\n\nexport function AuthForm({\n mode,\n brand,\n endpoints,\n providers,\n defaultReturnTo = '/dashboard',\n loginHref = '/login',\n signupHref = '/signup',\n forgotPasswordHref = '/forgot-password',\n extraBody,\n socialParams,\n}: AuthFormProps) {\n const router = useRouter();\n const params = useSearchParams();\n const returnTo = params?.get('return_to') || defaultReturnTo;\n const ssoError = params?.get('sso_error');\n const ssoDetail = params?.get('sso_detail');\n const ep: AuthEndpoints = { ...defaultEndpoints, ...endpoints };\n\n const [email, setEmail] = useState('');\n const [password, setPassword] = useState('');\n const [name, setName] = useState('');\n const [submitting, setSubmitting] = useState(false);\n const [redirecting, setRedirecting] = useState(false);\n const [turnstileToken, setTurnstileToken] = useState('');\n const turnstileTheme = useTurnstileTheme();\n const [error, setError] = useState<string | null>(\n ssoError ? `Sign-in failed: ${ssoDetail || ssoError}` : null,\n );\n\n async function submit(e: React.FormEvent) {\n e.preventDefault();\n setError(null);\n setSubmitting(true);\n try {\n const path = mode === 'signup' ? ep.signup : ep.login;\n const body: Record<string, unknown> = { email, password, ...extraBody };\n if (mode === 'signup' && name.trim()) body.name = name.trim();\n if (TURNSTILE_SITE_KEY) body['cf-turnstile-response'] = turnstileToken;\n const res = await fetch(path, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n credentials: 'include',\n body: JSON.stringify(body),\n });\n if (!res.ok) {\n const payload = (await res.json().catch(() => null)) as {\n error?: { code?: string; message?: string };\n } | null;\n // The product BFF does not do inline MFA. When a user has MFA\n // enabled, Huudis ROPC fails and the SDK returns 401 with\n // `code: 'MFA_REQUIRED'`. Hand off to the Huudis hosted-login\n // flow (no `provider=` param) — Huudis performs the challenge.\n // `socialParams` (e.g. `{ role }`) MUST ride along so a\n // multi-role product mints the correct per-role session on the\n // OIDC callback — otherwise the role is lost and the user is\n // gated on the wrong portal.\n if (payload?.error?.code === 'MFA_REQUIRED') {\n setRedirecting(true);\n const qs = new URLSearchParams({ return_to: returnTo, ...socialParams });\n window.location.href = `${ep.socialStart}?${qs.toString()}`;\n return;\n }\n throw new Error(payload?.error?.message ?? `Request failed (${res.status})`);\n }\n router.push(returnTo);\n router.refresh();\n } catch (err) {\n setError((err as Error).message);\n } finally {\n setSubmitting(false);\n }\n }\n\n const otherMode = mode === 'login' ? 'signup' : 'login';\n const otherHref = `${otherMode === 'signup' ? signupHref : loginHref}?return_to=${encodeURIComponent(returnTo)}`;\n const socialUrl = (provider: 'google' | 'apple' | 'facebook') => {\n const qs = new URLSearchParams({ provider, return_to: returnTo, ...socialParams });\n return `${ep.socialStart}?${qs.toString()}`;\n };\n\n const showGoogle = providers?.google !== false;\n const showApple = providers?.apple !== false;\n const showFacebook = providers?.facebook !== false;\n const hasAnySocial = showGoogle || showApple || showFacebook;\n\n return (\n <div className=\"space-y-4\">\n {error && !redirecting && (\n <div className=\"flex items-start gap-2 rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-sm text-destructive\">\n <AlertCircle className=\"h-4 w-4 shrink-0 mt-0.5\" />\n <span>{error}</span>\n </div>\n )}\n\n {redirecting && (\n <div className=\"flex items-start gap-2 rounded-md border border-border bg-accent px-3 py-2 text-sm text-muted-foreground\">\n <Loader2 className=\"h-4 w-4 shrink-0 mt-0.5 animate-spin\" />\n <span>Redirecting you to complete two-factor sign-in…</span>\n </div>\n )}\n\n {hasAnySocial && (\n <>\n <div className=\"grid gap-2\">\n {showGoogle && (\n <Button\n asChild\n variant=\"outline\"\n className=\"flex h-auto w-full border-border py-2 shadow-none\"\n >\n <a href={socialUrl('google')}>\n <GoogleMark className=\"h-4 w-4\" />\n Continue with Google\n </a>\n </Button>\n )}\n {showApple && (\n <Button\n asChild\n variant=\"outline\"\n className=\"flex h-auto w-full border-border py-2 shadow-none\"\n >\n <a href={socialUrl('apple')}>\n <AppleMark className=\"h-4 w-4\" />\n Continue with Apple\n </a>\n </Button>\n )}\n {showFacebook && (\n <Button\n asChild\n variant=\"outline\"\n className=\"flex h-auto w-full border-border py-2 shadow-none\"\n >\n <a href={socialUrl('facebook')}>\n <FacebookMark className=\"h-4 w-4\" />\n Continue with Facebook\n </a>\n </Button>\n )}\n </div>\n\n <div className=\"my-4 flex items-center gap-3 text-[11px] text-muted-foreground\">\n <div className=\"flex-1 border-t border-border\" />\n OR\n <div className=\"flex-1 border-t border-border\" />\n </div>\n </>\n )}\n\n <form onSubmit={submit} className=\"space-y-3\">\n <div>\n <Label className=\"mb-1 block text-xs leading-4 text-muted-foreground\">Email</Label>\n <Input\n type=\"email\"\n required\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n autoComplete=\"email\"\n className=\"h-auto border-border bg-background py-2 text-sm shadow-none focus:outline-none focus:ring-1 focus:ring-primary\"\n />\n </div>\n <div>\n <Label className=\"mb-1 block text-xs leading-4 text-muted-foreground\">Password</Label>\n <Input\n type=\"password\"\n required\n minLength={mode === 'signup' ? 10 : undefined}\n value={password}\n onChange={(e) => setPassword(e.target.value)}\n autoComplete={mode === 'signup' ? 'new-password' : 'current-password'}\n className=\"h-auto border-border bg-background py-2 text-sm shadow-none focus:outline-none focus:ring-1 focus:ring-primary\"\n />\n {mode === 'signup' && (\n <p className=\"mt-1 text-[11px] text-muted-foreground\">At least 10 characters, with a letter and a number.</p>\n )}\n </div>\n {mode === 'signup' && (\n <div>\n <Label className=\"mb-1 block text-xs leading-4 text-muted-foreground\">\n Your name <span className=\"text-muted-foreground/60\">(optional)</span>\n </Label>\n <Input\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n autoComplete=\"name\"\n className=\"h-auto border-border bg-background py-2 text-sm shadow-none focus:outline-none focus:ring-1 focus:ring-primary\"\n />\n </div>\n )}\n {TURNSTILE_SITE_KEY && (\n <div className=\"flex justify-center py-1\">\n <Turnstile\n siteKey={TURNSTILE_SITE_KEY}\n onSuccess={setTurnstileToken}\n onError={() => setError('Security check failed.')}\n options={{ theme: turnstileTheme }}\n />\n </div>\n )}\n <Button\n type=\"submit\"\n disabled={submitting || redirecting}\n className=\"flex h-auto w-full py-2.5 shadow-none hover:opacity-90\"\n >\n {(submitting || redirecting) && <Loader2 className=\"h-4 w-4 animate-spin\" />}\n {redirecting\n ? 'Redirecting…'\n : submitting\n ? mode === 'signup'\n ? 'Creating…'\n : 'Signing in…'\n : mode === 'signup'\n ? 'Create account'\n : 'Sign in'}\n </Button>\n </form>\n\n <div className=\"flex items-center justify-between pt-2 text-xs text-muted-foreground\">\n {mode === 'login' && (\n <Link href={forgotPasswordHref} className=\"hover:text-foreground\">\n Forgot password?\n </Link>\n )}\n <span className={mode === 'login' ? '' : 'ml-auto'}>\n {mode === 'login' ? `New to ${brand}?` : 'Already have an account?'}{' '}\n <Link href={otherHref} className=\"font-medium text-foreground hover:underline\">\n {mode === 'login' ? 'Sign up' : 'Sign in'}\n </Link>\n </span>\n </div>\n <p className=\"pt-2 text-[11px] leading-relaxed text-muted-foreground/80\">\n Identity is powered by{' '}\n <a href=\"https://huudis.com\" className=\"underline hover:text-foreground\">\n Huudis\n </a>\n . One account for every Forjio product.\n </p>\n </div>\n );\n}\n\nfunction GoogleMark({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path d=\"M21.6 12.227c0-.708-.064-1.39-.182-2.045H12v3.868h5.384a4.603 4.603 0 0 1-1.997 3.018v2.51h3.232c1.891-1.742 2.98-4.307 2.98-7.35Z\" fill=\"#4285F4\" />\n <path d=\"M12 22c2.7 0 4.965-.895 6.62-2.422l-3.233-2.51c-.895.6-2.041.955-3.386.955-2.604 0-4.81-1.76-5.596-4.122H3.067v2.59A9.996 9.996 0 0 0 12 22Z\" fill=\"#34A853\" />\n <path d=\"M6.404 13.9a6.016 6.016 0 0 1 0-3.8V7.512H3.067a9.996 9.996 0 0 0 0 8.977L6.404 13.9Z\" fill=\"#FBBC05\" />\n <path d=\"M12 5.977c1.468 0 2.786.505 3.823 1.497l2.868-2.868C16.96 2.986 14.696 2 12 2 8.118 2 4.76 4.232 3.067 7.51l3.337 2.59C7.19 7.737 9.396 5.977 12 5.977Z\" fill=\"#EA4335\" />\n </svg>\n );\n}\n\nfunction FacebookMark({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"#1877F2\" aria-hidden=\"true\">\n <path d=\"M24 12.073C24 5.404 18.627 0 12 0S0 5.404 0 12.073c0 6.026 4.388 11.022 10.125 11.927v-8.437H7.078v-3.49h3.047V9.41c0-3.026 1.792-4.697 4.533-4.697 1.313 0 2.686.236 2.686.236v2.971H15.83c-1.49 0-1.955.93-1.955 1.886v2.266h3.328l-.532 3.49h-2.796v8.437C19.612 23.095 24 18.099 24 12.073Z\" />\n </svg>\n );\n}\n\nfunction AppleMark({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M17.564 12.73c-.037-3.16 2.58-4.678 2.698-4.752-1.47-2.146-3.76-2.44-4.576-2.473-1.948-.2-3.8 1.148-4.788 1.148-.993 0-2.513-1.12-4.13-1.091-2.127.03-4.085 1.236-5.174 3.142-2.207 3.82-.562 9.463 1.58 12.56 1.052 1.514 2.306 3.216 3.952 3.155 1.586-.065 2.185-1.026 4.102-1.026 1.917 0 2.455 1.026 4.133.99 1.705-.03 2.785-1.546 3.83-3.066 1.207-1.757 1.702-3.462 1.731-3.55-.038-.018-3.325-1.274-3.358-5.037Zm-3.154-9.24c.878-1.06 1.467-2.542 1.306-4.014-1.26.051-2.79.838-3.695 1.898-.813.937-1.524 2.433-1.333 3.885 1.405.108 2.843-.712 3.722-1.77Z\" />\n </svg>\n );\n}\n"],"mappings":"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","names":["Link"]}
@@ -1,4 +1,4 @@
1
- import * as react from 'react';
1
+ import * as React from 'react';
2
2
  import { AuthEndpoints, SocialProviders } from './types.cjs';
3
3
 
4
4
  interface AuthFormProps {
@@ -30,6 +30,6 @@ interface AuthFormProps {
30
30
  * per-role session on callback. */
31
31
  socialParams?: Record<string, string>;
32
32
  }
33
- declare function AuthForm({ mode, brand, endpoints, providers, defaultReturnTo, loginHref, signupHref, forgotPasswordHref, extraBody, socialParams, }: AuthFormProps): react.JSX.Element;
33
+ declare function AuthForm({ mode, brand, endpoints, providers, defaultReturnTo, loginHref, signupHref, forgotPasswordHref, extraBody, socialParams, }: AuthFormProps): React.JSX.Element;
34
34
 
35
35
  export { AuthForm, type AuthFormProps };
@@ -1,4 +1,4 @@
1
- import * as react from 'react';
1
+ import * as React from 'react';
2
2
  import { AuthEndpoints, SocialProviders } from './types.js';
3
3
 
4
4
  interface AuthFormProps {
@@ -30,6 +30,6 @@ interface AuthFormProps {
30
30
  * per-role session on callback. */
31
31
  socialParams?: Record<string, string>;
32
32
  }
33
- declare function AuthForm({ mode, brand, endpoints, providers, defaultReturnTo, loginHref, signupHref, forgotPasswordHref, extraBody, socialParams, }: AuthFormProps): react.JSX.Element;
33
+ declare function AuthForm({ mode, brand, endpoints, providers, defaultReturnTo, loginHref, signupHref, forgotPasswordHref, extraBody, socialParams, }: AuthFormProps): React.JSX.Element;
34
34
 
35
35
  export { AuthForm, type AuthFormProps };
package/dist/AuthForm.js CHANGED
@@ -5,6 +5,10 @@ import { useRouter, useSearchParams } from "next/navigation";
5
5
  import Link from "next/link";
6
6
  import { Loader2, AlertCircle } from "lucide-react";
7
7
  import { Turnstile } from "@marsidev/react-turnstile";
8
+ import { useTurnstileTheme } from "./useTurnstileTheme";
9
+ import { Button } from "./components/ui/button";
10
+ import { Input } from "./components/ui/input";
11
+ import { Label } from "./components/ui/label";
8
12
  import { defaultEndpoints } from "./types";
9
13
  const TURNSTILE_SITE_KEY = process.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY;
10
14
  function AuthForm({
@@ -31,6 +35,7 @@ function AuthForm({
31
35
  const [submitting, setSubmitting] = useState(false);
32
36
  const [redirecting, setRedirecting] = useState(false);
33
37
  const [turnstileToken, setTurnstileToken] = useState("");
38
+ const turnstileTheme = useTurnstileTheme();
34
39
  const [error, setError] = useState(
35
40
  ssoError ? `Sign-in failed: ${ssoDetail || ssoError}` : null
36
41
  );
@@ -88,37 +93,40 @@ function AuthForm({
88
93
  ] }),
89
94
  hasAnySocial && /* @__PURE__ */ jsxs(Fragment, { children: [
90
95
  /* @__PURE__ */ jsxs("div", { className: "grid gap-2", children: [
91
- showGoogle && /* @__PURE__ */ jsxs(
92
- "a",
96
+ showGoogle && /* @__PURE__ */ jsx(
97
+ Button,
93
98
  {
94
- href: socialUrl("google"),
95
- className: "flex w-full items-center justify-center gap-2 rounded-md border border-border bg-background py-2 text-sm font-medium hover:bg-accent",
96
- children: [
99
+ asChild: true,
100
+ variant: "outline",
101
+ className: "flex h-auto w-full border-border py-2 shadow-none",
102
+ children: /* @__PURE__ */ jsxs("a", { href: socialUrl("google"), children: [
97
103
  /* @__PURE__ */ jsx(GoogleMark, { className: "h-4 w-4" }),
98
104
  "Continue with Google"
99
- ]
105
+ ] })
100
106
  }
101
107
  ),
102
- showApple && /* @__PURE__ */ jsxs(
103
- "a",
108
+ showApple && /* @__PURE__ */ jsx(
109
+ Button,
104
110
  {
105
- href: socialUrl("apple"),
106
- className: "flex w-full items-center justify-center gap-2 rounded-md border border-border bg-background py-2 text-sm font-medium hover:bg-accent",
107
- children: [
111
+ asChild: true,
112
+ variant: "outline",
113
+ className: "flex h-auto w-full border-border py-2 shadow-none",
114
+ children: /* @__PURE__ */ jsxs("a", { href: socialUrl("apple"), children: [
108
115
  /* @__PURE__ */ jsx(AppleMark, { className: "h-4 w-4" }),
109
116
  "Continue with Apple"
110
- ]
117
+ ] })
111
118
  }
112
119
  ),
113
- showFacebook && /* @__PURE__ */ jsxs(
114
- "a",
120
+ showFacebook && /* @__PURE__ */ jsx(
121
+ Button,
115
122
  {
116
- href: socialUrl("facebook"),
117
- className: "flex w-full items-center justify-center gap-2 rounded-md border border-border bg-background py-2 text-sm font-medium hover:bg-accent",
118
- children: [
123
+ asChild: true,
124
+ variant: "outline",
125
+ className: "flex h-auto w-full border-border py-2 shadow-none",
126
+ children: /* @__PURE__ */ jsxs("a", { href: socialUrl("facebook"), children: [
119
127
  /* @__PURE__ */ jsx(FacebookMark, { className: "h-4 w-4" }),
120
128
  "Continue with Facebook"
121
- ]
129
+ ] })
122
130
  }
123
131
  )
124
132
  ] }),
@@ -130,23 +138,23 @@ function AuthForm({
130
138
  ] }),
131
139
  /* @__PURE__ */ jsxs("form", { onSubmit: submit, className: "space-y-3", children: [
132
140
  /* @__PURE__ */ jsxs("div", { children: [
133
- /* @__PURE__ */ jsx("label", { className: "mb-1 block text-xs font-medium text-muted-foreground", children: "Email" }),
141
+ /* @__PURE__ */ jsx(Label, { className: "mb-1 block text-xs leading-4 text-muted-foreground", children: "Email" }),
134
142
  /* @__PURE__ */ jsx(
135
- "input",
143
+ Input,
136
144
  {
137
145
  type: "email",
138
146
  required: true,
139
147
  value: email,
140
148
  onChange: (e) => setEmail(e.target.value),
141
149
  autoComplete: "email",
142
- className: "w-full rounded-md border border-border bg-background px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-primary"
150
+ className: "h-auto border-border bg-background py-2 text-sm shadow-none focus:outline-none focus:ring-1 focus:ring-primary"
143
151
  }
144
152
  )
145
153
  ] }),
146
154
  /* @__PURE__ */ jsxs("div", { children: [
147
- /* @__PURE__ */ jsx("label", { className: "mb-1 block text-xs font-medium text-muted-foreground", children: "Password" }),
155
+ /* @__PURE__ */ jsx(Label, { className: "mb-1 block text-xs leading-4 text-muted-foreground", children: "Password" }),
148
156
  /* @__PURE__ */ jsx(
149
- "input",
157
+ Input,
150
158
  {
151
159
  type: "password",
152
160
  required: true,
@@ -154,24 +162,24 @@ function AuthForm({
154
162
  value: password,
155
163
  onChange: (e) => setPassword(e.target.value),
156
164
  autoComplete: mode === "signup" ? "new-password" : "current-password",
157
- className: "w-full rounded-md border border-border bg-background px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-primary"
165
+ className: "h-auto border-border bg-background py-2 text-sm shadow-none focus:outline-none focus:ring-1 focus:ring-primary"
158
166
  }
159
167
  ),
160
168
  mode === "signup" && /* @__PURE__ */ jsx("p", { className: "mt-1 text-[11px] text-muted-foreground", children: "At least 10 characters, with a letter and a number." })
161
169
  ] }),
162
170
  mode === "signup" && /* @__PURE__ */ jsxs("div", { children: [
163
- /* @__PURE__ */ jsxs("label", { className: "mb-1 block text-xs font-medium text-muted-foreground", children: [
171
+ /* @__PURE__ */ jsxs(Label, { className: "mb-1 block text-xs leading-4 text-muted-foreground", children: [
164
172
  "Your name ",
165
173
  /* @__PURE__ */ jsx("span", { className: "text-muted-foreground/60", children: "(optional)" })
166
174
  ] }),
167
175
  /* @__PURE__ */ jsx(
168
- "input",
176
+ Input,
169
177
  {
170
178
  type: "text",
171
179
  value: name,
172
180
  onChange: (e) => setName(e.target.value),
173
181
  autoComplete: "name",
174
- className: "w-full rounded-md border border-border bg-background px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-primary"
182
+ className: "h-auto border-border bg-background py-2 text-sm shadow-none focus:outline-none focus:ring-1 focus:ring-primary"
175
183
  }
176
184
  )
177
185
  ] }),
@@ -181,15 +189,15 @@ function AuthForm({
181
189
  siteKey: TURNSTILE_SITE_KEY,
182
190
  onSuccess: setTurnstileToken,
183
191
  onError: () => setError("Security check failed."),
184
- options: { theme: "auto" }
192
+ options: { theme: turnstileTheme }
185
193
  }
186
194
  ) }),
187
195
  /* @__PURE__ */ jsxs(
188
- "button",
196
+ Button,
189
197
  {
190
198
  type: "submit",
191
199
  disabled: submitting || redirecting,
192
- className: "flex w-full items-center justify-center gap-2 rounded-md bg-primary py-2.5 text-sm font-medium text-primary-foreground transition hover:opacity-90 disabled:opacity-50",
200
+ className: "flex h-auto w-full py-2.5 shadow-none hover:opacity-90",
193
201
  children: [
194
202
  (submitting || redirecting) && /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin" }),
195
203
  redirecting ? "Redirecting\u2026" : submitting ? mode === "signup" ? "Creating\u2026" : "Signing in\u2026" : mode === "signup" ? "Create account" : "Sign in"
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/AuthForm.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport { useRouter, useSearchParams } from 'next/navigation';\nimport Link from 'next/link';\nimport { Loader2, AlertCircle } from 'lucide-react';\nimport { Turnstile } from '@marsidev/react-turnstile';\nimport { defaultEndpoints, type AuthEndpoints, type SocialProviders } from './types';\n\n// Cloudflare Turnstile is enabled family-wide by setting\n// NEXT_PUBLIC_TURNSTILE_SITE_KEY at build time. Next inlines NEXT_PUBLIC_*\n// referenced here (in node_modules) into each product's bundle, so no\n// per-product page edit is needed — set the env var + the widget appears,\n// and the token rides the login/signup request as `cf-turnstile-response`.\n// The backend (@forjio/sdk/auth-server) verifies it when\n// TURNSTILE_SECRET_KEY is set; both sides bypass gracefully when unset.\nconst TURNSTILE_SITE_KEY = process.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY;\n\nexport interface AuthFormProps {\n mode: 'login' | 'signup';\n /** Display name shown in copy (\"New to Plugipay?\", \"Welcome back\"). */\n brand: string;\n /** Override the auth endpoint paths. Default matches Forjio family\n * `@forjio/sdk/auth-handlers` mounts. */\n endpoints?: Partial<AuthEndpoints>;\n /** Which social providers to render. Host fetches the provider\n * status; pass undefined to show all (fail-open). */\n providers?: SocialProviders | null;\n /** Default redirect target after a successful auth. Default\n * `/dashboard`. Search-param `?return_to=` overrides at runtime. */\n defaultReturnTo?: string;\n /** Mode-switch link targets. Default `/login` / `/signup`. Override\n * for products with non-default auth routes (e.g. role-scoped\n * `/creators/login` + `/creators/onboarding`). `?return_to=` is\n * appended automatically. */\n loginHref?: string;\n signupHref?: string;\n /** \"Forgot password?\" link target. Default `/forgot-password`. */\n forgotPasswordHref?: string;\n /** Extra fields merged into the login/signup request body — e.g. a\n * `role` discriminator for multi-role products. */\n extraBody?: Record<string, unknown>;\n /** Extra query params appended to the social-start URL — e.g.\n * `{ role }`, which the Huudis OIDC start needs to mint the correct\n * per-role session on callback. */\n socialParams?: Record<string, string>;\n}\n\nexport function AuthForm({\n mode,\n brand,\n endpoints,\n providers,\n defaultReturnTo = '/dashboard',\n loginHref = '/login',\n signupHref = '/signup',\n forgotPasswordHref = '/forgot-password',\n extraBody,\n socialParams,\n}: AuthFormProps) {\n const router = useRouter();\n const params = useSearchParams();\n const returnTo = params?.get('return_to') || defaultReturnTo;\n const ssoError = params?.get('sso_error');\n const ssoDetail = params?.get('sso_detail');\n const ep: AuthEndpoints = { ...defaultEndpoints, ...endpoints };\n\n const [email, setEmail] = useState('');\n const [password, setPassword] = useState('');\n const [name, setName] = useState('');\n const [submitting, setSubmitting] = useState(false);\n const [redirecting, setRedirecting] = useState(false);\n const [turnstileToken, setTurnstileToken] = useState('');\n const [error, setError] = useState<string | null>(\n ssoError ? `Sign-in failed: ${ssoDetail || ssoError}` : null,\n );\n\n async function submit(e: React.FormEvent) {\n e.preventDefault();\n setError(null);\n setSubmitting(true);\n try {\n const path = mode === 'signup' ? ep.signup : ep.login;\n const body: Record<string, unknown> = { email, password, ...extraBody };\n if (mode === 'signup' && name.trim()) body.name = name.trim();\n if (TURNSTILE_SITE_KEY) body['cf-turnstile-response'] = turnstileToken;\n const res = await fetch(path, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n credentials: 'include',\n body: JSON.stringify(body),\n });\n if (!res.ok) {\n const payload = (await res.json().catch(() => null)) as {\n error?: { code?: string; message?: string };\n } | null;\n // The product BFF does not do inline MFA. When a user has MFA\n // enabled, Huudis ROPC fails and the SDK returns 401 with\n // `code: 'MFA_REQUIRED'`. Hand off to the Huudis hosted-login\n // flow (no `provider=` param) — Huudis performs the challenge.\n // `socialParams` (e.g. `{ role }`) MUST ride along so a\n // multi-role product mints the correct per-role session on the\n // OIDC callback — otherwise the role is lost and the user is\n // gated on the wrong portal.\n if (payload?.error?.code === 'MFA_REQUIRED') {\n setRedirecting(true);\n const qs = new URLSearchParams({ return_to: returnTo, ...socialParams });\n window.location.href = `${ep.socialStart}?${qs.toString()}`;\n return;\n }\n throw new Error(payload?.error?.message ?? `Request failed (${res.status})`);\n }\n router.push(returnTo);\n router.refresh();\n } catch (err) {\n setError((err as Error).message);\n } finally {\n setSubmitting(false);\n }\n }\n\n const otherMode = mode === 'login' ? 'signup' : 'login';\n const otherHref = `${otherMode === 'signup' ? signupHref : loginHref}?return_to=${encodeURIComponent(returnTo)}`;\n const socialUrl = (provider: 'google' | 'apple' | 'facebook') => {\n const qs = new URLSearchParams({ provider, return_to: returnTo, ...socialParams });\n return `${ep.socialStart}?${qs.toString()}`;\n };\n\n const showGoogle = providers?.google !== false;\n const showApple = providers?.apple !== false;\n const showFacebook = providers?.facebook !== false;\n const hasAnySocial = showGoogle || showApple || showFacebook;\n\n return (\n <div className=\"space-y-4\">\n {error && !redirecting && (\n <div className=\"flex items-start gap-2 rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-sm text-destructive\">\n <AlertCircle className=\"h-4 w-4 shrink-0 mt-0.5\" />\n <span>{error}</span>\n </div>\n )}\n\n {redirecting && (\n <div className=\"flex items-start gap-2 rounded-md border border-border bg-accent px-3 py-2 text-sm text-muted-foreground\">\n <Loader2 className=\"h-4 w-4 shrink-0 mt-0.5 animate-spin\" />\n <span>Redirecting you to complete two-factor sign-in…</span>\n </div>\n )}\n\n {hasAnySocial && (\n <>\n <div className=\"grid gap-2\">\n {showGoogle && (\n <a\n href={socialUrl('google')}\n className=\"flex w-full items-center justify-center gap-2 rounded-md border border-border bg-background py-2 text-sm font-medium hover:bg-accent\"\n >\n <GoogleMark className=\"h-4 w-4\" />\n Continue with Google\n </a>\n )}\n {showApple && (\n <a\n href={socialUrl('apple')}\n className=\"flex w-full items-center justify-center gap-2 rounded-md border border-border bg-background py-2 text-sm font-medium hover:bg-accent\"\n >\n <AppleMark className=\"h-4 w-4\" />\n Continue with Apple\n </a>\n )}\n {showFacebook && (\n <a\n href={socialUrl('facebook')}\n className=\"flex w-full items-center justify-center gap-2 rounded-md border border-border bg-background py-2 text-sm font-medium hover:bg-accent\"\n >\n <FacebookMark className=\"h-4 w-4\" />\n Continue with Facebook\n </a>\n )}\n </div>\n\n <div className=\"my-4 flex items-center gap-3 text-[11px] text-muted-foreground\">\n <div className=\"flex-1 border-t border-border\" />\n OR\n <div className=\"flex-1 border-t border-border\" />\n </div>\n </>\n )}\n\n <form onSubmit={submit} className=\"space-y-3\">\n <div>\n <label className=\"mb-1 block text-xs font-medium text-muted-foreground\">Email</label>\n <input\n type=\"email\"\n required\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n autoComplete=\"email\"\n className=\"w-full rounded-md border border-border bg-background px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-primary\"\n />\n </div>\n <div>\n <label className=\"mb-1 block text-xs font-medium text-muted-foreground\">Password</label>\n <input\n type=\"password\"\n required\n minLength={mode === 'signup' ? 10 : undefined}\n value={password}\n onChange={(e) => setPassword(e.target.value)}\n autoComplete={mode === 'signup' ? 'new-password' : 'current-password'}\n className=\"w-full rounded-md border border-border bg-background px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-primary\"\n />\n {mode === 'signup' && (\n <p className=\"mt-1 text-[11px] text-muted-foreground\">At least 10 characters, with a letter and a number.</p>\n )}\n </div>\n {mode === 'signup' && (\n <div>\n <label className=\"mb-1 block text-xs font-medium text-muted-foreground\">\n Your name <span className=\"text-muted-foreground/60\">(optional)</span>\n </label>\n <input\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n autoComplete=\"name\"\n className=\"w-full rounded-md border border-border bg-background px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-primary\"\n />\n </div>\n )}\n {TURNSTILE_SITE_KEY && (\n <div className=\"flex justify-center py-1\">\n <Turnstile\n siteKey={TURNSTILE_SITE_KEY}\n onSuccess={setTurnstileToken}\n onError={() => setError('Security check failed.')}\n options={{ theme: 'auto' }}\n />\n </div>\n )}\n <button\n type=\"submit\"\n disabled={submitting || redirecting}\n className=\"flex w-full items-center justify-center gap-2 rounded-md bg-primary py-2.5 text-sm font-medium text-primary-foreground transition hover:opacity-90 disabled:opacity-50\"\n >\n {(submitting || redirecting) && <Loader2 className=\"h-4 w-4 animate-spin\" />}\n {redirecting\n ? 'Redirecting…'\n : submitting\n ? mode === 'signup'\n ? 'Creating…'\n : 'Signing in…'\n : mode === 'signup'\n ? 'Create account'\n : 'Sign in'}\n </button>\n </form>\n\n <div className=\"flex items-center justify-between pt-2 text-xs text-muted-foreground\">\n {mode === 'login' && (\n <Link href={forgotPasswordHref} className=\"hover:text-foreground\">\n Forgot password?\n </Link>\n )}\n <span className={mode === 'login' ? '' : 'ml-auto'}>\n {mode === 'login' ? `New to ${brand}?` : 'Already have an account?'}{' '}\n <Link href={otherHref} className=\"font-medium text-foreground hover:underline\">\n {mode === 'login' ? 'Sign up' : 'Sign in'}\n </Link>\n </span>\n </div>\n <p className=\"pt-2 text-[11px] leading-relaxed text-muted-foreground/80\">\n Identity is powered by{' '}\n <a href=\"https://huudis.com\" className=\"underline hover:text-foreground\">\n Huudis\n </a>\n . One account for every Forjio product.\n </p>\n </div>\n );\n}\n\nfunction GoogleMark({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path d=\"M21.6 12.227c0-.708-.064-1.39-.182-2.045H12v3.868h5.384a4.603 4.603 0 0 1-1.997 3.018v2.51h3.232c1.891-1.742 2.98-4.307 2.98-7.35Z\" fill=\"#4285F4\" />\n <path d=\"M12 22c2.7 0 4.965-.895 6.62-2.422l-3.233-2.51c-.895.6-2.041.955-3.386.955-2.604 0-4.81-1.76-5.596-4.122H3.067v2.59A9.996 9.996 0 0 0 12 22Z\" fill=\"#34A853\" />\n <path d=\"M6.404 13.9a6.016 6.016 0 0 1 0-3.8V7.512H3.067a9.996 9.996 0 0 0 0 8.977L6.404 13.9Z\" fill=\"#FBBC05\" />\n <path d=\"M12 5.977c1.468 0 2.786.505 3.823 1.497l2.868-2.868C16.96 2.986 14.696 2 12 2 8.118 2 4.76 4.232 3.067 7.51l3.337 2.59C7.19 7.737 9.396 5.977 12 5.977Z\" fill=\"#EA4335\" />\n </svg>\n );\n}\n\nfunction FacebookMark({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"#1877F2\" aria-hidden=\"true\">\n <path d=\"M24 12.073C24 5.404 18.627 0 12 0S0 5.404 0 12.073c0 6.026 4.388 11.022 10.125 11.927v-8.437H7.078v-3.49h3.047V9.41c0-3.026 1.792-4.697 4.533-4.697 1.313 0 2.686.236 2.686.236v2.971H15.83c-1.49 0-1.955.93-1.955 1.886v2.266h3.328l-.532 3.49h-2.796v8.437C19.612 23.095 24 18.099 24 12.073Z\" />\n </svg>\n );\n}\n\nfunction AppleMark({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M17.564 12.73c-.037-3.16 2.58-4.678 2.698-4.752-1.47-2.146-3.76-2.44-4.576-2.473-1.948-.2-3.8 1.148-4.788 1.148-.993 0-2.513-1.12-4.13-1.091-2.127.03-4.085 1.236-5.174 3.142-2.207 3.82-.562 9.463 1.58 12.56 1.052 1.514 2.306 3.216 3.952 3.155 1.586-.065 2.185-1.026 4.102-1.026 1.917 0 2.455 1.026 4.133.99 1.705-.03 2.785-1.546 3.83-3.066 1.207-1.757 1.702-3.462 1.731-3.55-.038-.018-3.325-1.274-3.358-5.037Zm-3.154-9.24c.878-1.06 1.467-2.542 1.306-4.014-1.26.051-2.79.838-3.695 1.898-.813.937-1.524 2.433-1.333 3.885 1.405.108 2.843-.712 3.722-1.77Z\" />\n </svg>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/AuthForm.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport { useRouter, useSearchParams } from 'next/navigation';\nimport Link from 'next/link';\nimport { Loader2, AlertCircle } from 'lucide-react';\nimport { Turnstile } from '@marsidev/react-turnstile';\nimport { useTurnstileTheme } from './useTurnstileTheme';\nimport { Button } from './components/ui/button';\nimport { Input } from './components/ui/input';\nimport { Label } from './components/ui/label';\nimport { defaultEndpoints, type AuthEndpoints, type SocialProviders } from './types';\n\n// Cloudflare Turnstile is enabled family-wide by setting\n// NEXT_PUBLIC_TURNSTILE_SITE_KEY at build time. Next inlines NEXT_PUBLIC_*\n// referenced here (in node_modules) into each product's bundle, so no\n// per-product page edit is needed — set the env var + the widget appears,\n// and the token rides the login/signup request as `cf-turnstile-response`.\n// The backend (@forjio/sdk/auth-server) verifies it when\n// TURNSTILE_SECRET_KEY is set; both sides bypass gracefully when unset.\nconst TURNSTILE_SITE_KEY = process.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY;\n\nexport interface AuthFormProps {\n mode: 'login' | 'signup';\n /** Display name shown in copy (\"New to Plugipay?\", \"Welcome back\"). */\n brand: string;\n /** Override the auth endpoint paths. Default matches Forjio family\n * `@forjio/sdk/auth-handlers` mounts. */\n endpoints?: Partial<AuthEndpoints>;\n /** Which social providers to render. Host fetches the provider\n * status; pass undefined to show all (fail-open). */\n providers?: SocialProviders | null;\n /** Default redirect target after a successful auth. Default\n * `/dashboard`. Search-param `?return_to=` overrides at runtime. */\n defaultReturnTo?: string;\n /** Mode-switch link targets. Default `/login` / `/signup`. Override\n * for products with non-default auth routes (e.g. role-scoped\n * `/creators/login` + `/creators/onboarding`). `?return_to=` is\n * appended automatically. */\n loginHref?: string;\n signupHref?: string;\n /** \"Forgot password?\" link target. Default `/forgot-password`. */\n forgotPasswordHref?: string;\n /** Extra fields merged into the login/signup request body — e.g. a\n * `role` discriminator for multi-role products. */\n extraBody?: Record<string, unknown>;\n /** Extra query params appended to the social-start URL — e.g.\n * `{ role }`, which the Huudis OIDC start needs to mint the correct\n * per-role session on callback. */\n socialParams?: Record<string, string>;\n}\n\nexport function AuthForm({\n mode,\n brand,\n endpoints,\n providers,\n defaultReturnTo = '/dashboard',\n loginHref = '/login',\n signupHref = '/signup',\n forgotPasswordHref = '/forgot-password',\n extraBody,\n socialParams,\n}: AuthFormProps) {\n const router = useRouter();\n const params = useSearchParams();\n const returnTo = params?.get('return_to') || defaultReturnTo;\n const ssoError = params?.get('sso_error');\n const ssoDetail = params?.get('sso_detail');\n const ep: AuthEndpoints = { ...defaultEndpoints, ...endpoints };\n\n const [email, setEmail] = useState('');\n const [password, setPassword] = useState('');\n const [name, setName] = useState('');\n const [submitting, setSubmitting] = useState(false);\n const [redirecting, setRedirecting] = useState(false);\n const [turnstileToken, setTurnstileToken] = useState('');\n const turnstileTheme = useTurnstileTheme();\n const [error, setError] = useState<string | null>(\n ssoError ? `Sign-in failed: ${ssoDetail || ssoError}` : null,\n );\n\n async function submit(e: React.FormEvent) {\n e.preventDefault();\n setError(null);\n setSubmitting(true);\n try {\n const path = mode === 'signup' ? ep.signup : ep.login;\n const body: Record<string, unknown> = { email, password, ...extraBody };\n if (mode === 'signup' && name.trim()) body.name = name.trim();\n if (TURNSTILE_SITE_KEY) body['cf-turnstile-response'] = turnstileToken;\n const res = await fetch(path, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n credentials: 'include',\n body: JSON.stringify(body),\n });\n if (!res.ok) {\n const payload = (await res.json().catch(() => null)) as {\n error?: { code?: string; message?: string };\n } | null;\n // The product BFF does not do inline MFA. When a user has MFA\n // enabled, Huudis ROPC fails and the SDK returns 401 with\n // `code: 'MFA_REQUIRED'`. Hand off to the Huudis hosted-login\n // flow (no `provider=` param) — Huudis performs the challenge.\n // `socialParams` (e.g. `{ role }`) MUST ride along so a\n // multi-role product mints the correct per-role session on the\n // OIDC callback — otherwise the role is lost and the user is\n // gated on the wrong portal.\n if (payload?.error?.code === 'MFA_REQUIRED') {\n setRedirecting(true);\n const qs = new URLSearchParams({ return_to: returnTo, ...socialParams });\n window.location.href = `${ep.socialStart}?${qs.toString()}`;\n return;\n }\n throw new Error(payload?.error?.message ?? `Request failed (${res.status})`);\n }\n router.push(returnTo);\n router.refresh();\n } catch (err) {\n setError((err as Error).message);\n } finally {\n setSubmitting(false);\n }\n }\n\n const otherMode = mode === 'login' ? 'signup' : 'login';\n const otherHref = `${otherMode === 'signup' ? signupHref : loginHref}?return_to=${encodeURIComponent(returnTo)}`;\n const socialUrl = (provider: 'google' | 'apple' | 'facebook') => {\n const qs = new URLSearchParams({ provider, return_to: returnTo, ...socialParams });\n return `${ep.socialStart}?${qs.toString()}`;\n };\n\n const showGoogle = providers?.google !== false;\n const showApple = providers?.apple !== false;\n const showFacebook = providers?.facebook !== false;\n const hasAnySocial = showGoogle || showApple || showFacebook;\n\n return (\n <div className=\"space-y-4\">\n {error && !redirecting && (\n <div className=\"flex items-start gap-2 rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-sm text-destructive\">\n <AlertCircle className=\"h-4 w-4 shrink-0 mt-0.5\" />\n <span>{error}</span>\n </div>\n )}\n\n {redirecting && (\n <div className=\"flex items-start gap-2 rounded-md border border-border bg-accent px-3 py-2 text-sm text-muted-foreground\">\n <Loader2 className=\"h-4 w-4 shrink-0 mt-0.5 animate-spin\" />\n <span>Redirecting you to complete two-factor sign-in…</span>\n </div>\n )}\n\n {hasAnySocial && (\n <>\n <div className=\"grid gap-2\">\n {showGoogle && (\n <Button\n asChild\n variant=\"outline\"\n className=\"flex h-auto w-full border-border py-2 shadow-none\"\n >\n <a href={socialUrl('google')}>\n <GoogleMark className=\"h-4 w-4\" />\n Continue with Google\n </a>\n </Button>\n )}\n {showApple && (\n <Button\n asChild\n variant=\"outline\"\n className=\"flex h-auto w-full border-border py-2 shadow-none\"\n >\n <a href={socialUrl('apple')}>\n <AppleMark className=\"h-4 w-4\" />\n Continue with Apple\n </a>\n </Button>\n )}\n {showFacebook && (\n <Button\n asChild\n variant=\"outline\"\n className=\"flex h-auto w-full border-border py-2 shadow-none\"\n >\n <a href={socialUrl('facebook')}>\n <FacebookMark className=\"h-4 w-4\" />\n Continue with Facebook\n </a>\n </Button>\n )}\n </div>\n\n <div className=\"my-4 flex items-center gap-3 text-[11px] text-muted-foreground\">\n <div className=\"flex-1 border-t border-border\" />\n OR\n <div className=\"flex-1 border-t border-border\" />\n </div>\n </>\n )}\n\n <form onSubmit={submit} className=\"space-y-3\">\n <div>\n <Label className=\"mb-1 block text-xs leading-4 text-muted-foreground\">Email</Label>\n <Input\n type=\"email\"\n required\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n autoComplete=\"email\"\n className=\"h-auto border-border bg-background py-2 text-sm shadow-none focus:outline-none focus:ring-1 focus:ring-primary\"\n />\n </div>\n <div>\n <Label className=\"mb-1 block text-xs leading-4 text-muted-foreground\">Password</Label>\n <Input\n type=\"password\"\n required\n minLength={mode === 'signup' ? 10 : undefined}\n value={password}\n onChange={(e) => setPassword(e.target.value)}\n autoComplete={mode === 'signup' ? 'new-password' : 'current-password'}\n className=\"h-auto border-border bg-background py-2 text-sm shadow-none focus:outline-none focus:ring-1 focus:ring-primary\"\n />\n {mode === 'signup' && (\n <p className=\"mt-1 text-[11px] text-muted-foreground\">At least 10 characters, with a letter and a number.</p>\n )}\n </div>\n {mode === 'signup' && (\n <div>\n <Label className=\"mb-1 block text-xs leading-4 text-muted-foreground\">\n Your name <span className=\"text-muted-foreground/60\">(optional)</span>\n </Label>\n <Input\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n autoComplete=\"name\"\n className=\"h-auto border-border bg-background py-2 text-sm shadow-none focus:outline-none focus:ring-1 focus:ring-primary\"\n />\n </div>\n )}\n {TURNSTILE_SITE_KEY && (\n <div className=\"flex justify-center py-1\">\n <Turnstile\n siteKey={TURNSTILE_SITE_KEY}\n onSuccess={setTurnstileToken}\n onError={() => setError('Security check failed.')}\n options={{ theme: turnstileTheme }}\n />\n </div>\n )}\n <Button\n type=\"submit\"\n disabled={submitting || redirecting}\n className=\"flex h-auto w-full py-2.5 shadow-none hover:opacity-90\"\n >\n {(submitting || redirecting) && <Loader2 className=\"h-4 w-4 animate-spin\" />}\n {redirecting\n ? 'Redirecting…'\n : submitting\n ? mode === 'signup'\n ? 'Creating…'\n : 'Signing in…'\n : mode === 'signup'\n ? 'Create account'\n : 'Sign in'}\n </Button>\n </form>\n\n <div className=\"flex items-center justify-between pt-2 text-xs text-muted-foreground\">\n {mode === 'login' && (\n <Link href={forgotPasswordHref} className=\"hover:text-foreground\">\n Forgot password?\n </Link>\n )}\n <span className={mode === 'login' ? '' : 'ml-auto'}>\n {mode === 'login' ? `New to ${brand}?` : 'Already have an account?'}{' '}\n <Link href={otherHref} className=\"font-medium text-foreground hover:underline\">\n {mode === 'login' ? 'Sign up' : 'Sign in'}\n </Link>\n </span>\n </div>\n <p className=\"pt-2 text-[11px] leading-relaxed text-muted-foreground/80\">\n Identity is powered by{' '}\n <a href=\"https://huudis.com\" className=\"underline hover:text-foreground\">\n Huudis\n </a>\n . One account for every Forjio product.\n </p>\n </div>\n );\n}\n\nfunction GoogleMark({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path d=\"M21.6 12.227c0-.708-.064-1.39-.182-2.045H12v3.868h5.384a4.603 4.603 0 0 1-1.997 3.018v2.51h3.232c1.891-1.742 2.98-4.307 2.98-7.35Z\" fill=\"#4285F4\" />\n <path d=\"M12 22c2.7 0 4.965-.895 6.62-2.422l-3.233-2.51c-.895.6-2.041.955-3.386.955-2.604 0-4.81-1.76-5.596-4.122H3.067v2.59A9.996 9.996 0 0 0 12 22Z\" fill=\"#34A853\" />\n <path d=\"M6.404 13.9a6.016 6.016 0 0 1 0-3.8V7.512H3.067a9.996 9.996 0 0 0 0 8.977L6.404 13.9Z\" fill=\"#FBBC05\" />\n <path d=\"M12 5.977c1.468 0 2.786.505 3.823 1.497l2.868-2.868C16.96 2.986 14.696 2 12 2 8.118 2 4.76 4.232 3.067 7.51l3.337 2.59C7.19 7.737 9.396 5.977 12 5.977Z\" fill=\"#EA4335\" />\n </svg>\n );\n}\n\nfunction FacebookMark({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"#1877F2\" aria-hidden=\"true\">\n <path d=\"M24 12.073C24 5.404 18.627 0 12 0S0 5.404 0 12.073c0 6.026 4.388 11.022 10.125 11.927v-8.437H7.078v-3.49h3.047V9.41c0-3.026 1.792-4.697 4.533-4.697 1.313 0 2.686.236 2.686.236v2.971H15.83c-1.49 0-1.955.93-1.955 1.886v2.266h3.328l-.532 3.49h-2.796v8.437C19.612 23.095 24 18.099 24 12.073Z\" />\n </svg>\n );\n}\n\nfunction AppleMark({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M17.564 12.73c-.037-3.16 2.58-4.678 2.698-4.752-1.47-2.146-3.76-2.44-4.576-2.473-1.948-.2-3.8 1.148-4.788 1.148-.993 0-2.513-1.12-4.13-1.091-2.127.03-4.085 1.236-5.174 3.142-2.207 3.82-.562 9.463 1.58 12.56 1.052 1.514 2.306 3.216 3.952 3.155 1.586-.065 2.185-1.026 4.102-1.026 1.917 0 2.455 1.026 4.133.99 1.705-.03 2.785-1.546 3.83-3.066 1.207-1.757 1.702-3.462 1.731-3.55-.038-.018-3.325-1.274-3.358-5.037Zm-3.154-9.24c.878-1.06 1.467-2.542 1.306-4.014-1.26.051-2.79.838-3.695 1.898-.813.937-1.524 2.433-1.333 3.885 1.405.108 2.843-.712 3.722-1.77Z\" />\n </svg>\n );\n}\n"],"mappings":"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","names":[]}
@@ -37,6 +37,10 @@ var import_react = require("react");
37
37
  var import_link = __toESM(require("next/link"), 1);
38
38
  var import_lucide_react = require("lucide-react");
39
39
  var import_react_turnstile = require("@marsidev/react-turnstile");
40
+ var import_useTurnstileTheme = require("./useTurnstileTheme");
41
+ var import_button = require("./components/ui/button");
42
+ var import_input = require("./components/ui/input");
43
+ var import_label = require("./components/ui/label");
40
44
  var import_types = require("./types");
41
45
  const TURNSTILE_SITE_KEY = process.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY;
42
46
  function ForgotPasswordForm({ endpoints } = {}) {
@@ -45,6 +49,7 @@ function ForgotPasswordForm({ endpoints } = {}) {
45
49
  const [submitting, setSubmitting] = (0, import_react.useState)(false);
46
50
  const [sent, setSent] = (0, import_react.useState)(false);
47
51
  const [turnstileToken, setTurnstileToken] = (0, import_react.useState)("");
52
+ const turnstileTheme = (0, import_useTurnstileTheme.useTurnstileTheme)();
48
53
  const [error, setError] = (0, import_react.useState)(null);
49
54
  async function submit(e) {
50
55
  e.preventDefault();
@@ -89,9 +94,9 @@ function ForgotPasswordForm({ endpoints } = {}) {
89
94
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: error })
90
95
  ] }),
91
96
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
92
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: "mb-1 block text-xs font-medium text-muted-foreground", children: "Email" }),
97
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_label.Label, { className: "mb-1 block text-xs leading-4 text-muted-foreground", children: "Email" }),
93
98
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
94
- "input",
99
+ import_input.Input,
95
100
  {
96
101
  type: "email",
97
102
  required: true,
@@ -99,7 +104,7 @@ function ForgotPasswordForm({ endpoints } = {}) {
99
104
  onChange: (e) => setEmail(e.target.value),
100
105
  autoComplete: "email",
101
106
  autoFocus: true,
102
- className: "w-full rounded-md border border-border bg-background px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-primary"
107
+ className: "h-auto border-border bg-background py-2 text-sm shadow-none focus:outline-none focus:ring-1 focus:ring-primary"
103
108
  }
104
109
  )
105
110
  ] }),
@@ -109,15 +114,15 @@ function ForgotPasswordForm({ endpoints } = {}) {
109
114
  siteKey: TURNSTILE_SITE_KEY,
110
115
  onSuccess: setTurnstileToken,
111
116
  onError: () => setError("Security check failed."),
112
- options: { theme: "auto" }
117
+ options: { theme: turnstileTheme }
113
118
  }
114
119
  ) }),
115
120
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
116
- "button",
121
+ import_button.Button,
117
122
  {
118
123
  type: "submit",
119
124
  disabled: submitting,
120
- className: "flex w-full items-center justify-center gap-2 rounded-md bg-primary py-2.5 text-sm font-medium text-primary-foreground transition hover:opacity-90 disabled:opacity-50",
125
+ className: "flex h-auto w-full py-2.5 shadow-none hover:opacity-90",
121
126
  children: [
122
127
  submitting && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Loader2, { className: "h-4 w-4 animate-spin" }),
123
128
  submitting ? "Sending\u2026" : "Send reset link"