@main12/auth-login 0.4.2 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/README.md +54 -7
  2. package/dist/components/AuthCard.d.ts +41 -0
  3. package/dist/components/AuthCard.js +28 -0
  4. package/dist/components/AuthCardServer.d.ts +29 -0
  5. package/dist/components/AuthCardServer.js +57 -0
  6. package/dist/components/AuthLayout.d.ts +23 -13
  7. package/dist/components/AuthLayout.js +26 -87
  8. package/dist/components/AuthPages.d.ts +4 -29
  9. package/dist/components/AuthPages.js +23 -105
  10. package/dist/components/AuthPagesServer.d.ts +3 -4
  11. package/dist/components/AuthPagesServer.js +15 -8
  12. package/dist/components/auth-card/AuthCardShell.d.ts +21 -0
  13. package/dist/components/auth-card/AuthCardShell.js +104 -0
  14. package/dist/components/auth-card/FormRenderer.d.ts +32 -0
  15. package/dist/components/auth-card/FormRenderer.js +88 -0
  16. package/dist/components/auth-card/formConfigs.d.ts +25 -0
  17. package/dist/components/auth-card/formConfigs.js +56 -0
  18. package/dist/components/auth-card/index.d.ts +3 -0
  19. package/dist/components/auth-card/index.js +3 -0
  20. package/dist/components/forms/ForgotPasswordForm.d.ts +7 -0
  21. package/dist/components/forms/ForgotPasswordForm.js +56 -0
  22. package/dist/components/forms/LoginForm.d.ts +15 -0
  23. package/dist/components/forms/LoginForm.js +247 -0
  24. package/dist/components/forms/SetPasswordForm.d.ts +7 -0
  25. package/dist/components/forms/SetPasswordForm.js +75 -0
  26. package/dist/components/forms/SignupForm.d.ts +13 -0
  27. package/dist/components/forms/SignupForm.js +152 -0
  28. package/dist/components/forms/VerifyOtpForm.d.ts +9 -0
  29. package/dist/components/forms/VerifyOtpForm.js +107 -0
  30. package/dist/components/forms/index.d.ts +19 -0
  31. package/dist/components/forms/index.js +21 -0
  32. package/dist/components/pages/ForgotPasswordPage.d.ts +2 -1
  33. package/dist/components/pages/ForgotPasswordPageHero.d.ts +3 -2
  34. package/dist/components/pages/ForgotPasswordPageHero.js +70 -64
  35. package/dist/components/pages/ForgotPasswordPageTailwind.d.ts +3 -2
  36. package/dist/components/pages/ForgotPasswordPageTailwind.js +42 -36
  37. package/dist/components/pages/LoginPage.d.ts +2 -1
  38. package/dist/components/pages/LoginPageHero.d.ts +2 -1
  39. package/dist/components/pages/LoginPageHero.js +297 -291
  40. package/dist/components/pages/LoginPageTailwind.d.ts +2 -1
  41. package/dist/components/pages/LoginPageTailwind.js +197 -191
  42. package/dist/components/pages/SetPasswordPage.d.ts +2 -1
  43. package/dist/components/pages/SetPasswordPageHero.d.ts +3 -2
  44. package/dist/components/pages/SetPasswordPageHero.js +98 -92
  45. package/dist/components/pages/SetPasswordPageTailwind.d.ts +3 -2
  46. package/dist/components/pages/SetPasswordPageTailwind.js +63 -57
  47. package/dist/components/pages/SignupPage.d.ts +2 -1
  48. package/dist/components/pages/SignupPageHero.d.ts +3 -2
  49. package/dist/components/pages/SignupPageHero.js +139 -133
  50. package/dist/components/pages/SignupPageTailwind.d.ts +3 -2
  51. package/dist/components/pages/SignupPageTailwind.js +123 -117
  52. package/dist/components/pages/VerifyOtpPage.d.ts +2 -1
  53. package/dist/components/pages/VerifyOtpPageHero.d.ts +2 -1
  54. package/dist/components/pages/VerifyOtpPageHero.js +110 -104
  55. package/dist/components/pages/VerifyOtpPageTailwind.d.ts +2 -1
  56. package/dist/components/pages/VerifyOtpPageTailwind.js +60 -54
  57. package/dist/components/ui/translations.d.ts +9 -0
  58. package/dist/components/ui/translations.js +18 -0
  59. package/dist/config.d.ts +12 -0
  60. package/dist/config.js +11 -0
  61. package/dist/exports/client.d.ts +5 -0
  62. package/dist/exports/client.js +5 -0
  63. package/dist/exports/rsc.d.ts +7 -0
  64. package/dist/exports/rsc.js +8 -0
  65. package/dist/index.js +12 -2
  66. package/package.json +3 -2
package/README.md CHANGED
@@ -35,7 +35,7 @@ plugins: [
35
35
  - **Auto-verify OTP** — automatically verifies when all 6 digits are entered
36
36
  - **Smart redirects** — logged-in users are redirected away from auth pages
37
37
  - **Google account picker** — always shows account selection by default (`prompt: 'select_account'`)
38
- - **Zero runtime deps** (Tailwind mode) — Next.js + React are peer dependencies
38
+ - **No UI library required** (Tailwind mode) — only Next.js/React/Payload peers; `style: 'hero-ui'` additionally requires HeroUI + Framer Motion
39
39
  - **Multi-language** — built-in English/Spanish, auto-detected per-request, fully overridable via `messages` prop, works with or without next-intl/next-i18next
40
40
 
41
41
  ---
@@ -194,6 +194,9 @@ authLoginPlugin({
194
194
  style: 'tailwind', // 'tailwind' | 'hero-ui'
195
195
  enabled: true,
196
196
 
197
+ // Signup
198
+ allowSignup: true, // Allow new users to sign up (default: true)
199
+
197
200
  // Login methods
198
201
  passwordLogin: true, // Allow password-based login (default: true)
199
202
  otpLogin: true, // Allow OTP-based login (default: true)
@@ -294,10 +297,20 @@ The `AuthPages` component accepts these props for customization:
294
297
  logo={<MyLogo />} // Custom logo component
295
298
  basePath="/auth" // Base path for sibling links (default: '/auth')
296
299
  showGoogleOAuth={true} // Override Google OAuth visibility
300
+ allowSignup={true} // Override plugin's allowSignup setting for this render
301
+ passwordLogin={true} // Override plugin's passwordLogin setting for this render
302
+ otpLogin={true} // Override plugin's otpLogin setting for this render
297
303
  onPasswordLogin={customLogin} // Custom login handler
298
304
  onSignup={customSignup} // Custom signup handler
299
305
  locale="es" // Override auto-detected locale (see Multi-Language Support)
300
306
  messages={{ es: { login: { title: 'Bienvenido' } } }} // Partial translation overrides
307
+
308
+ // Card chrome — same options accepted by <AuthCard> (see Components Reference)
309
+ poweredBy={{ enabled: true }} // Configure/hide the "Powered by Main 12" badge
310
+ cardClassName="" // Extra classes on the card wrapper
311
+ removeBorder={false} // Remove the card border (useful for split/custom layouts)
312
+ removeShadow={false} // Remove the card shadow
313
+ mobileVariant="plain" // 'plain' | 'card' — how the card renders on mobile widths
301
314
  />
302
315
  ```
303
316
 
@@ -429,6 +442,39 @@ export default function Page() {
429
442
 
430
443
  ---
431
444
 
445
+ ## Custom Layouts (Split-Screen, etc.)
446
+
447
+ For layouts `AuthPages` can't express (e.g. a split-screen with an image pane), compose `AuthLayout` + `AuthCard` directly — the same primitives every built-in page is built from. Pass `slug` to auto-render the matching form (with full translation/config support), and use `removeBorder`/`removeShadow`/`cardClassName` to fit the card into your own layout:
448
+
449
+ ```tsx
450
+ // app/(auth)/auth/[...slug]/page.tsx (server component, no 'use client' needed)
451
+ import { AuthLayout, AuthCard } from '@main12/auth-login/rsc'
452
+
453
+ export default async function Page({ params }: { params: Promise<{ slug: string[] }> }) {
454
+ const { slug } = await params
455
+ return (
456
+ <AuthLayout backgroundClass="bg-white">
457
+ <div className="min-h-screen grid md:grid-cols-2">
458
+ <div className="hidden md:block bg-cover bg-center" style={{ backgroundImage: 'url(/hero.jpg)' }} />
459
+ <div className="flex items-center justify-center">
460
+ <AuthCard
461
+ slug={slug?.[0] ?? 'login'}
462
+ removeShadow
463
+ removeBorder
464
+ basePath="/auth"
465
+ redirectTo="/admin"
466
+ />
467
+ </div>
468
+ </div>
469
+ </AuthLayout>
470
+ )
471
+ }
472
+ ```
473
+
474
+ `AuthCard` also accepts `children` instead of `slug` for fully custom form content — see [Building Custom Pages](#building-custom-pages) below.
475
+
476
+ ---
477
+
432
478
  ## Building Custom Pages
433
479
 
434
480
  Use the plugin's hooks to build your own UI with any component library (HeroUI, shadcn, plain Tailwind).
@@ -437,10 +483,10 @@ Use the plugin's hooks to build your own UI with any component library (HeroUI,
437
483
 
438
484
  | Hook | Returns | Key inputs |
439
485
  |------|---------|------------|
440
- | `useLoginFlow({ redirectTo, onPasswordLogin })` | `step, email, password, error, isLoading, handleEmailSubmit, handlePasswordSubmit, handleSendOtp, handleEditEmail` | `redirectTo: string`, `onPasswordLogin: (creds) => Promise<void>` |
486
+ | `useLoginFlow({ redirectTo, onPasswordLogin })` | `step, email, password, error, isLoading, isSendingOtp, showPassword, setEmail, setPassword, setShowPassword, handleEmailSubmit, handlePasswordSubmit, handleSendOtp, handleEditEmail, handleGoogleLogin` | `redirectTo: string`, `onPasswordLogin: (creds) => Promise<void>` |
441
487
  | `useForgotPasswordFlow()` | `email, error, isLoading, setEmail, handleSubmit` | none |
442
488
  | `useVerifyOtpFlow({ email, purpose, redirectTo })` | `otp, error, isLoading, isResending, resendCooldown, setOtp, handleSubmit, handleResendCode` | `email: string`, `purpose: 'login'\|'signup'\|'password-reset'` |
443
- | `useSetPasswordFlow({ redirectTo })` | `password, confirmPassword, error, isLoading, strength, setPassword, setConfirmPassword, handleSubmit` | `redirectTo: string` |
489
+ | `useSetPasswordFlow({ redirectTo })` | `password, confirmPassword, error, isLoading, showPassword, strength, setPassword, setConfirmPassword, setShowPassword, handleSubmit` | `redirectTo?: string` |
444
490
 
445
491
  > **Signup note:** No hook needed — call `signup(name, email)` from `@main12/auth-login/client`, then redirect to `/verify-otp?email=...&purpose=signup`.
446
492
 
@@ -592,7 +638,8 @@ await payload.sendEmail({
592
638
  | `ForgotPasswordPage` | Standalone forgot-password page | Individual route setup |
593
639
  | `VerifyOtpPage` | Standalone OTP verification page — **requires `?email=...` in the URL** | Reached via redirect from signup/login/forgot-password |
594
640
  | `SetPasswordPage` | Standalone set/reset password page | Individual route setup, or post-OTP password creation |
595
- | `AuthLayout` | Shared card/background/logo/footer chrome used by every page — use it directly when building fully custom pages | `<AuthLayout title="..." subtitle="...">{children}</AuthLayout>` |
641
+ | `AuthCard` | The card chrome (logo, title/subtitle, footer, "Powered by" badge) — pass `slug` to auto-render the matching form, or `children` for fully custom content. `removeBorder`/`removeShadow`/`cardClassName`/`mobileVariant` support custom layouts like split-screen | `<AuthCard slug="login" removeShadow basePath="/auth" />` or `<AuthCard title="..." subtitle="...">{children}</AuthCard>` |
642
+ | `AuthLayout` | Outermost full-height background wrapper used by every page — compose it with `AuthCard` (and e.g. a split-screen image) when building fully custom layouts | `<AuthLayout backgroundClass="bg-white"><AuthCard slug="login" /></AuthLayout>` |
596
643
  | `AuthClientInit` | Drop into your root layout to sync server plugin config (`style`, Google OAuth flag) to client bundles. Optional — only needed if you hit issues with plugin config not reaching client components in certain bundler setups | `<AuthClientInit />` inside `<body>` |
597
644
  | `PoweredBy` | The "Powered by Main 12" footer badge, rendered automatically on every page (configurable via `poweredBy` prop) | Rarely used standalone — mostly internal |
598
645
 
@@ -600,10 +647,10 @@ await payload.sendEmail({
600
647
 
601
648
  | Hook | Returns | Key inputs |
602
649
  |------|---------|------------|
603
- | `useLoginFlow({ redirectTo, onPasswordLogin })` | `step, email, password, error, isLoading, handleEmailSubmit, handlePasswordSubmit, handleSendOtp, handleEditEmail` | `redirectTo: string`, `onPasswordLogin: (creds) => Promise<void>` |
650
+ | `useLoginFlow({ redirectTo, onPasswordLogin })` | `step, email, password, error, isLoading, isSendingOtp, showPassword, setEmail, setPassword, setShowPassword, handleEmailSubmit, handlePasswordSubmit, handleSendOtp, handleEditEmail, handleGoogleLogin` | `redirectTo: string`, `onPasswordLogin: (creds) => Promise<void>` |
604
651
  | `useForgotPasswordFlow()` | `email, error, isLoading, setEmail, handleSubmit` | none |
605
652
  | `useVerifyOtpFlow({ email, purpose, redirectTo })` | `otp, error, isLoading, isResending, resendCooldown, setOtp, handleSubmit, handleResendCode` | `email: string`, `purpose: 'login'\|'signup'\|'password-reset'` |
606
- | `useSetPasswordFlow({ redirectTo })` | `password, confirmPassword, error, isLoading, strength, setPassword, setConfirmPassword, handleSubmit` | `redirectTo: string` |
653
+ | `useSetPasswordFlow({ redirectTo })` | `password, confirmPassword, error, isLoading, showPassword, strength, setPassword, setConfirmPassword, setShowPassword, handleSubmit` | `redirectTo?: string` |
607
654
 
608
655
  ### Service functions (`@main12/auth-login/client`)
609
656
 
@@ -766,7 +813,7 @@ The auth-login plugin uses `payload.sendEmail()` internally — which routes thr
766
813
  | Payload CMS | `^3.82.0` | ✅ |
767
814
  | Next.js | `^16.0.0` | ✅ |
768
815
  | React | `^19.0.0` | ✅ |
769
- | HeroUI | `^2.x` | Only for `style: 'hero-ui'` |
816
+ | HeroUI | `>=3.2.0` | Only for `style: 'hero-ui'` |
770
817
  | Framer Motion | `^12.x` | Only for `style: 'hero-ui'` |
771
818
 
772
819
  ---
@@ -0,0 +1,41 @@
1
+ import React from 'react';
2
+ import type { DeepPartial, UiTranslations } from './ui/translations';
3
+ import { type PoweredByConfig } from './auth-card/index';
4
+ export interface AuthCardConfig {
5
+ logo?: React.ReactNode;
6
+ poweredBy?: PoweredByConfig;
7
+ cardClassName?: string;
8
+ removeBorder?: boolean;
9
+ removeShadow?: boolean;
10
+ mobileVariant?: 'plain' | 'card';
11
+ }
12
+ export interface AuthCardWithSlugProps extends AuthCardConfig {
13
+ slug: string | string[];
14
+ redirectTo?: string;
15
+ onPasswordLogin?: (credentials: {
16
+ email: string;
17
+ password: string;
18
+ }) => Promise<void>;
19
+ onSignup?: (data: {
20
+ name: string;
21
+ email: string;
22
+ }) => Promise<void>;
23
+ basePath?: string;
24
+ showGoogleOAuth?: boolean;
25
+ allowSignup?: boolean;
26
+ passwordLogin?: boolean;
27
+ otpLogin?: boolean;
28
+ locale?: string;
29
+ messages?: Record<string, DeepPartial<UiTranslations>>;
30
+ footer?: React.ReactNode;
31
+ title?: string;
32
+ subtitle?: string;
33
+ }
34
+ export interface AuthCardWithChildrenProps extends AuthCardConfig {
35
+ children: React.ReactNode;
36
+ footer?: React.ReactNode;
37
+ title?: string;
38
+ subtitle?: string;
39
+ }
40
+ export type AuthCardProps = AuthCardWithSlugProps | AuthCardWithChildrenProps;
41
+ export declare const AuthCard: React.FC<AuthCardProps>;
@@ -0,0 +1,28 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import React, { Suspense } from 'react';
4
+ import { Spinner } from './ui/index.js';
5
+ import { AuthCardShell, FormRenderer } from './auth-card/index.js';
6
+ function hasSlug(props) {
7
+ return 'slug' in props && props.slug !== undefined;
8
+ }
9
+ export const AuthCard = (props)=>{
10
+ if (hasSlug(props)) {
11
+ return /*#__PURE__*/ _jsx(Suspense, {
12
+ fallback: /*#__PURE__*/ _jsx("div", {
13
+ className: "flex items-center justify-center py-12",
14
+ children: /*#__PURE__*/ _jsx(Spinner, {
15
+ size: "lg"
16
+ })
17
+ }),
18
+ children: /*#__PURE__*/ _jsx(FormRenderer, {
19
+ ...props
20
+ })
21
+ });
22
+ }
23
+ const { children, ...shellProps } = props;
24
+ return /*#__PURE__*/ _jsx(AuthCardShell, {
25
+ ...shellProps,
26
+ children: children
27
+ });
28
+ };
@@ -0,0 +1,29 @@
1
+ import { type AuthCardWithSlugProps, type AuthCardWithChildrenProps } from './AuthCard';
2
+ export type { AuthCardWithSlugProps, AuthCardWithChildrenProps };
3
+ export type AuthCardProps = AuthCardWithSlugProps | AuthCardWithChildrenProps;
4
+ /**
5
+ * Server component wrapper for AuthCard.
6
+ * Reads plugin config directly from the shared `pluginConfig` singleton
7
+ * (set by the plugin factory at Payload config build time) and passes it
8
+ * down to the client component. This ensures settings like `googleOAuthEnabled`,
9
+ * `allowSignup`, `passwordLogin`, and `otpLogin` work automatically without
10
+ * requiring explicit props.
11
+ *
12
+ * Locale is resolved per-request: an explicit `locale` prop always wins,
13
+ * otherwise it's auto-detected from the `NEXT_LOCALE` cookie or
14
+ * `Accept-Language` header.
15
+ *
16
+ * ```tsx
17
+ * // app/auth/login/page.tsx (NO 'use client' needed!)
18
+ * import { AuthCard } from '@main12/auth-login/rsc'
19
+ * export default async function Page() {
20
+ * return (
21
+ * <AuthCard
22
+ * slug="login"
23
+ * logo={<Logo />}
24
+ * />
25
+ * )
26
+ * }
27
+ * ```
28
+ */
29
+ export declare function AuthCard(props: AuthCardProps): Promise<import("react/jsx-runtime").JSX.Element>;
@@ -0,0 +1,57 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { headers } from 'next/headers';
3
+ import { redirect } from 'next/navigation';
4
+ import { AuthCard as AuthCardClient } from './AuthCard.js';
5
+ import { getServerAllowSignup, getServerGoogleOAuthEnabled, pluginConfig } from '../config.js';
6
+ import { detectServerLocale } from './ui/locale.js';
7
+ // Use pluginConfig singleton directly - standard Payload plugin pattern
8
+ /**
9
+ * Server component wrapper for AuthCard.
10
+ * Reads plugin config directly from the shared `pluginConfig` singleton
11
+ * (set by the plugin factory at Payload config build time) and passes it
12
+ * down to the client component. This ensures settings like `googleOAuthEnabled`,
13
+ * `allowSignup`, `passwordLogin`, and `otpLogin` work automatically without
14
+ * requiring explicit props.
15
+ *
16
+ * Locale is resolved per-request: an explicit `locale` prop always wins,
17
+ * otherwise it's auto-detected from the `NEXT_LOCALE` cookie or
18
+ * `Accept-Language` header.
19
+ *
20
+ * ```tsx
21
+ * // app/auth/login/page.tsx (NO 'use client' needed!)
22
+ * import { AuthCard } from '@main12/auth-login/rsc'
23
+ * export default async function Page() {
24
+ * return (
25
+ * <AuthCard
26
+ * slug="login"
27
+ * logo={<Logo />}
28
+ * />
29
+ * )
30
+ * }
31
+ * ```
32
+ */ export async function AuthCard(props) {
33
+ // Check if it's a slug-based AuthCard
34
+ if ('slug' in props && props.slug !== undefined) {
35
+ const slugProps = props;
36
+ const resolvedLocale = slugProps.locale ?? detectServerLocale(await headers());
37
+ const resolvedAllowSignup = slugProps.allowSignup ?? getServerAllowSignup();
38
+ const basePath = slugProps.basePath?.replace(/\/$/, '') ?? '/auth';
39
+ // Redirect /signup to /login when signup is disabled
40
+ const currentSlug = Array.isArray(slugProps.slug) ? slugProps.slug[0] : slugProps.slug;
41
+ if (!resolvedAllowSignup && currentSlug === 'signup') {
42
+ redirect(`${basePath}/login`);
43
+ }
44
+ return /*#__PURE__*/ _jsx(AuthCardClient, {
45
+ ...slugProps,
46
+ showGoogleOAuth: slugProps.showGoogleOAuth ?? getServerGoogleOAuthEnabled(),
47
+ allowSignup: resolvedAllowSignup,
48
+ passwordLogin: slugProps.passwordLogin ?? pluginConfig.passwordLogin,
49
+ otpLogin: slugProps.otpLogin ?? pluginConfig.otpLogin,
50
+ locale: resolvedLocale
51
+ });
52
+ }
53
+ // Children-based AuthCard - just pass through
54
+ return /*#__PURE__*/ _jsx(AuthCardClient, {
55
+ ...props
56
+ });
57
+ }
@@ -1,18 +1,6 @@
1
1
  import React from 'react';
2
2
  import type { DeepPartial, UiTranslations } from './ui/translations';
3
3
  export interface AuthLayoutConfig {
4
- /** Component or URL. Falls back to pluginConfig when omitted. */
5
- logo?: React.ReactNode;
6
- title?: string;
7
- subtitle?: string;
8
- poweredBy?: {
9
- enabled?: boolean;
10
- logoUrl?: string;
11
- linkUrl?: string;
12
- width?: number;
13
- height?: number;
14
- };
15
- cardClassName?: string;
16
4
  backgroundClass?: string;
17
5
  /**
18
6
  * Locale for this page's copy. Built-in support for 'en' and 'es'.
@@ -25,6 +13,28 @@ export interface AuthLayoutConfig {
25
13
  }
26
14
  export interface AuthLayoutProps extends AuthLayoutConfig {
27
15
  children: React.ReactNode;
28
- footer?: React.ReactNode;
29
16
  }
17
+ /**
18
+ * The outermost auth page wrapper — background + full-height container only.
19
+ * Compose it with `<AuthCard>` (and any other elements, e.g. a split-screen
20
+ * image) for the actual content:
21
+ *
22
+ * ```tsx
23
+ * <AuthLayout backgroundClass="bg-accent">
24
+ * <AuthCard title="Welcome Back">{children}</AuthCard>
25
+ * </AuthLayout>
26
+ * ```
27
+ *
28
+ * For split/left/right layouts, just wrap `<AuthCard>` in your own grid or
29
+ * flex container — `AuthCard` centers itself within whatever space it's given:
30
+ *
31
+ * ```tsx
32
+ * <AuthLayout backgroundClass="bg-white">
33
+ * <div className="min-h-screen grid md:grid-cols-2">
34
+ * <div className="hidden md:block bg-cover" style={{ backgroundImage: 'url(/hero.jpg)' }} />
35
+ * <AuthCard removeShadow title="Create Account">{children}</AuthCard>
36
+ * </div>
37
+ * </AuthLayout>
38
+ * ```
39
+ */
30
40
  export declare const AuthLayout: React.FC<AuthLayoutProps>;
@@ -1,92 +1,31 @@
1
1
  'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import React from 'react';
4
- import { Card, CardContent, CardFooter } from './ui/index.js';
5
- import { PoweredBy } from './PoweredBy.js';
6
- import { pluginConfig } from '../config.js';
7
- function DefaultLogo() {
8
- if (pluginConfig.Logo) {
9
- const L = pluginConfig.Logo;
10
- return /*#__PURE__*/ _jsx(L, {});
11
- }
12
- if (pluginConfig.logoUrl) return /*#__PURE__*/ _jsx("img", {
13
- src: pluginConfig.logoUrl,
14
- alt: "",
15
- width: 180,
16
- height: 42,
17
- className: "object-contain"
18
- });
19
- return null;
20
- }
21
- export const AuthLayout = ({ children, logo, title, subtitle, footer, poweredBy, cardClassName = '', backgroundClass = 'bg-accent' })=>{
22
- const displayLogo = logo || /*#__PURE__*/ _jsx(DefaultLogo, {});
23
- const header = (displayLogo || title) && /*#__PURE__*/ _jsxs("div", {
24
- className: "flex flex-col items-center gap-2 pt-8 pb-4 px-8",
25
- children: [
26
- displayLogo && /*#__PURE__*/ _jsx("div", {
27
- className: "flex justify-center mb-3",
28
- children: displayLogo
29
- }),
30
- title && /*#__PURE__*/ _jsx("h1", {
31
- className: "text-2xl font-bold text-gray-900 text-center tracking-tight",
32
- children: title
33
- }),
34
- subtitle && /*#__PURE__*/ _jsx("p", {
35
- className: "text-gray-600 text-sm text-center",
36
- children: subtitle
37
- })
38
- ]
39
- });
40
- return /*#__PURE__*/ _jsxs("main", {
4
+ /**
5
+ * The outermost auth page wrapper — background + full-height container only.
6
+ * Compose it with `<AuthCard>` (and any other elements, e.g. a split-screen
7
+ * image) for the actual content:
8
+ *
9
+ * ```tsx
10
+ * <AuthLayout backgroundClass="bg-accent">
11
+ * <AuthCard title="Welcome Back">{children}</AuthCard>
12
+ * </AuthLayout>
13
+ * ```
14
+ *
15
+ * For split/left/right layouts, just wrap `<AuthCard>` in your own grid or
16
+ * flex container — `AuthCard` centers itself within whatever space it's given:
17
+ *
18
+ * ```tsx
19
+ * <AuthLayout backgroundClass="bg-white">
20
+ * <div className="min-h-screen grid md:grid-cols-2">
21
+ * <div className="hidden md:block bg-cover" style={{ backgroundImage: 'url(/hero.jpg)' }} />
22
+ * <AuthCard removeShadow title="Create Account">{children}</AuthCard>
23
+ * </div>
24
+ * </AuthLayout>
25
+ * ```
26
+ */ export const AuthLayout = ({ children, backgroundClass = 'bg-accent' })=>{
27
+ return /*#__PURE__*/ _jsx("main", {
41
28
  className: `flex flex-col min-h-screen ${backgroundClass}`,
42
- children: [
43
- /*#__PURE__*/ _jsxs("div", {
44
- className: "md:hidden flex flex-col w-full max-w-md mx-auto light [color-scheme:light] px-4 py-8",
45
- "data-theme": "light",
46
- style: {
47
- maxWidth: '448px'
48
- },
49
- children: [
50
- header,
51
- /*#__PURE__*/ _jsx("div", {
52
- className: "px-8 pb-6",
53
- children: children
54
- }),
55
- footer && /*#__PURE__*/ _jsx("div", {
56
- className: "px-8 pb-8 flex justify-center",
57
- children: footer
58
- }),
59
- /*#__PURE__*/ _jsx(PoweredBy, {
60
- ...poweredBy
61
- })
62
- ]
63
- }),
64
- /*#__PURE__*/ _jsx("div", {
65
- className: "hidden md:flex min-h-screen items-center justify-center px-4 py-12",
66
- children: /*#__PURE__*/ _jsxs("div", {
67
- className: "w-full max-w-md animate-[fadeIn_0.5s_ease-out]",
68
- style: {
69
- maxWidth: '448px'
70
- },
71
- children: [
72
- /*#__PURE__*/ _jsxs(Card, {
73
- className: cardClassName,
74
- children: [
75
- header,
76
- /*#__PURE__*/ _jsx(CardContent, {
77
- children: children
78
- }),
79
- footer && /*#__PURE__*/ _jsx(CardFooter, {
80
- children: footer
81
- })
82
- ]
83
- }),
84
- /*#__PURE__*/ _jsx(PoweredBy, {
85
- ...poweredBy
86
- })
87
- ]
88
- })
89
- })
90
- ]
29
+ children: children
91
30
  });
92
31
  };
@@ -1,48 +1,23 @@
1
- import type React from 'react';
2
1
  import type { DeepPartial, UiTranslations } from './ui/translations';
3
- export interface AuthPagesProps {
4
- /** The slug segments from the catch-all route, e.g. ['login'] or ['forgot-password'] */
2
+ import { type AuthLayoutConfig } from './AuthLayout';
3
+ import { type AuthCardConfig } from './AuthCard';
4
+ export interface AuthPagesProps extends AuthCardConfig, AuthLayoutConfig {
5
5
  slug?: string[];
6
- /** Where to redirect after successful login / set-password */
7
6
  redirectTo?: string;
8
- /** Optional logo component */
9
- logo?: React.ReactNode;
10
- /** Custom handler for password login (email+password). If omitted, uses default fetch to /api/users/login */
11
7
  onPasswordLogin?: (credentials: {
12
8
  email: string;
13
9
  password: string;
14
10
  }) => Promise<void>;
15
- /** Custom handler for signup. If omitted, uses default fetch to /api/auth/signup */
16
11
  onSignup?: (data: {
17
12
  name: string;
18
13
  email: string;
19
14
  }) => Promise<void>;
20
- /** Base path prefix, defaults to '/auth'. Used to compute sibling URLs. */
21
15
  basePath?: string;
22
- /** Show Google OAuth buttons. If omitted, reads from plugin config. */
23
16
  showGoogleOAuth?: boolean;
24
- /** Override the page background. Defaults to 'bg-white md:bg-[#191919]'. */
25
- backgroundClass?: string;
26
- /** UI style override. If omitted, reads from plugin config. */
27
- style?: 'tailwind' | 'hero-ui';
28
- /** Allow new user signups. When false, hides signup page and signup links. Defaults to true. */
29
17
  allowSignup?: boolean;
30
- /** Allow password-based login. @default true */
31
18
  passwordLogin?: boolean;
32
- /** Allow OTP-based login. @default true */
33
19
  otpLogin?: boolean;
34
- /**
35
- * Locale for the auth pages' copy. Built-in support for 'en' and 'es'.
36
- * If omitted, auto-detected from the `NEXT_LOCALE` cookie, `Accept-Language`
37
- * header, or `<html lang>` — works automatically with next-intl,
38
- * next-i18next, or no i18n library at all. Falls back to 'en'.
39
- */
40
20
  locale?: string;
41
- /**
42
- * Partial translation overrides, keyed by locale. Any key you omit falls
43
- * back to the built-in English/Spanish copy. You can also add entirely new
44
- * locales this way (e.g. `messages={{ fr: { login: { title: 'Bienvenue' } } }}`).
45
- */
46
21
  messages?: Record<string, DeepPartial<UiTranslations>>;
47
22
  }
48
- export default function AuthPages({ slug, redirectTo, logo, onPasswordLogin, onSignup, basePath, showGoogleOAuth, backgroundClass, style, allowSignup, passwordLogin, otpLogin, locale, messages, }: AuthPagesProps): import("react/jsx-runtime").JSX.Element;
23
+ export default function AuthPages({ backgroundClass, slug, redirectTo, logo, onPasswordLogin, onSignup, basePath, showGoogleOAuth, allowSignup, passwordLogin, otpLogin, locale, messages, poweredBy, cardClassName, removeBorder, removeShadow, mobileVariant, }: AuthPagesProps): import("react/jsx-runtime").JSX.Element;
@@ -1,110 +1,28 @@
1
1
  'use client';
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
- import { pluginConfig, initClientConfig } from '../config.js';
4
- import { detectClientLocale } from './ui/locale.js';
5
- import LoginPage from './pages/LoginPage.js';
6
- import SignupPage from './pages/SignupPage.js';
7
- import ForgotPasswordPage from './pages/ForgotPasswordPage.js';
8
- import VerifyOtpPage from './pages/VerifyOtpPage.js';
9
- import SetPasswordPage from './pages/SetPasswordPage.js';
10
- const defaultPasswordLogin = async ({ email, password })=>{
11
- const res = await fetch('/api/users/login', {
12
- method: 'POST',
13
- headers: {
14
- 'Content-Type': 'application/json'
15
- },
16
- body: JSON.stringify({
17
- email,
18
- password
3
+ import { AuthLayout } from './AuthLayout.js';
4
+ import { AuthCard } from './AuthCard.js';
5
+ export default function AuthPages({ backgroundClass, slug, redirectTo = '/admin', logo, onPasswordLogin, onSignup, basePath = '/auth', showGoogleOAuth, allowSignup = true, passwordLogin = true, otpLogin = true, locale, messages, poweredBy, cardClassName, removeBorder, removeShadow, mobileVariant }) {
6
+ return /*#__PURE__*/ _jsx(AuthLayout, {
7
+ backgroundClass: backgroundClass,
8
+ children: /*#__PURE__*/ _jsx(AuthCard, {
9
+ slug: slug?.[0] ?? 'login',
10
+ redirectTo: redirectTo,
11
+ logo: logo,
12
+ onPasswordLogin: onPasswordLogin,
13
+ onSignup: onSignup,
14
+ basePath: basePath,
15
+ showGoogleOAuth: showGoogleOAuth,
16
+ allowSignup: allowSignup,
17
+ passwordLogin: passwordLogin,
18
+ otpLogin: otpLogin,
19
+ locale: locale,
20
+ messages: messages,
21
+ poweredBy: poweredBy,
22
+ cardClassName: cardClassName,
23
+ removeBorder: removeBorder,
24
+ removeShadow: removeShadow,
25
+ mobileVariant: mobileVariant
19
26
  })
20
27
  });
21
- if (!res.ok) {
22
- const err = await res.json();
23
- throw new Error(err.errors?.[0]?.message || 'Login failed');
24
- }
25
- };
26
- const defaultSignup = async ({ name, email })=>{
27
- const res = await fetch('/api/auth/signup', {
28
- method: 'POST',
29
- headers: {
30
- 'Content-Type': 'application/json'
31
- },
32
- body: JSON.stringify({
33
- name,
34
- email
35
- })
36
- });
37
- if (!res.ok) {
38
- const err = await res.json();
39
- throw new Error(err.message || 'Signup failed');
40
- }
41
- };
42
- export default function AuthPages({ slug, redirectTo = '/admin', logo, onPasswordLogin = defaultPasswordLogin, onSignup = defaultSignup, basePath = '/auth', showGoogleOAuth, backgroundClass, style, allowSignup = true, passwordLogin = true, otpLogin = true, locale, messages }) {
43
- // Sync server plugin config to client using props (which come from the server via AuthPagesServer)
44
- initClientConfig({
45
- style: style ?? pluginConfig.style,
46
- googleOAuthEnabled: showGoogleOAuth ?? pluginConfig.googleOAuthEnabled,
47
- passwordLogin,
48
- otpLogin
49
- });
50
- // Resolve locale client-side only if the server didn't already resolve one
51
- // (AuthPagesServer always resolves and passes an explicit locale down).
52
- const resolvedLocale = locale ?? detectClientLocale();
53
- const page = slug?.[0] ?? 'login';
54
- const base = basePath.replace(/\/$/, '');
55
- const shared = {
56
- logo,
57
- locale: resolvedLocale,
58
- messages,
59
- ...showGoogleOAuth !== undefined ? {
60
- showGoogleOAuth
61
- } : {},
62
- ...backgroundClass !== undefined ? {
63
- backgroundClass
64
- } : {}
65
- };
66
- switch(page){
67
- case 'login':
68
- return /*#__PURE__*/ _jsx(LoginPage, {
69
- onPasswordLogin: onPasswordLogin,
70
- redirectTo: redirectTo,
71
- signupUrl: allowSignup ? `${base}/signup` : undefined,
72
- ...shared
73
- });
74
- case 'signup':
75
- if (!allowSignup) {
76
- return /*#__PURE__*/ _jsx(LoginPage, {
77
- onPasswordLogin: onPasswordLogin,
78
- redirectTo: redirectTo,
79
- ...shared
80
- });
81
- }
82
- return /*#__PURE__*/ _jsx(SignupPage, {
83
- onSignup: onSignup,
84
- loginUrl: `${base}/login`,
85
- ...shared
86
- });
87
- case 'forgot-password':
88
- return /*#__PURE__*/ _jsx(ForgotPasswordPage, {
89
- loginUrl: `${base}/login`,
90
- ...shared
91
- });
92
- case 'verify-otp':
93
- return /*#__PURE__*/ _jsx(VerifyOtpPage, {
94
- loginUrl: `${base}/login`,
95
- ...shared
96
- });
97
- case 'set-password':
98
- return /*#__PURE__*/ _jsx(SetPasswordPage, {
99
- redirectTo: redirectTo,
100
- ...shared
101
- });
102
- default:
103
- return /*#__PURE__*/ _jsx(LoginPage, {
104
- onPasswordLogin: onPasswordLogin,
105
- redirectTo: redirectTo,
106
- signupUrl: `${base}/signup`,
107
- ...shared
108
- });
109
- }
110
28
  }
@@ -2,10 +2,9 @@ import type { AuthPagesProps } from './AuthPages';
2
2
  export type { AuthPagesProps };
3
3
  /**
4
4
  * Server component wrapper for AuthPages.
5
- * Reads plugin config directly from the shared `pluginConfig` singleton
6
- * (set by the plugin factory at Payload config build time) and passes it
7
- * down to the client component. No env vars involved — the plugin's
8
- * `style`, `googleOAuthEnabled`, etc. options are the single source of truth.
5
+ * Reads plugin config from environment variables set during plugin initialization,
6
+ * ensuring the correct settings are used even in linked monorepo setups where
7
+ * module singletons may not be shared across entry points.
9
8
  *
10
9
  * Locale is resolved per-request: an explicit `locale` prop always wins,
11
10
  * otherwise it's auto-detected from the `NEXT_LOCALE` cookie or