@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.
- package/README.md +54 -7
- package/dist/components/AuthCard.d.ts +41 -0
- package/dist/components/AuthCard.js +28 -0
- package/dist/components/AuthCardServer.d.ts +29 -0
- package/dist/components/AuthCardServer.js +57 -0
- package/dist/components/AuthLayout.d.ts +23 -13
- package/dist/components/AuthLayout.js +26 -87
- package/dist/components/AuthPages.d.ts +4 -29
- package/dist/components/AuthPages.js +23 -105
- package/dist/components/AuthPagesServer.d.ts +3 -4
- package/dist/components/AuthPagesServer.js +15 -8
- package/dist/components/auth-card/AuthCardShell.d.ts +21 -0
- package/dist/components/auth-card/AuthCardShell.js +104 -0
- package/dist/components/auth-card/FormRenderer.d.ts +32 -0
- package/dist/components/auth-card/FormRenderer.js +88 -0
- package/dist/components/auth-card/formConfigs.d.ts +25 -0
- package/dist/components/auth-card/formConfigs.js +56 -0
- package/dist/components/auth-card/index.d.ts +3 -0
- package/dist/components/auth-card/index.js +3 -0
- package/dist/components/forms/ForgotPasswordForm.d.ts +7 -0
- package/dist/components/forms/ForgotPasswordForm.js +56 -0
- package/dist/components/forms/LoginForm.d.ts +15 -0
- package/dist/components/forms/LoginForm.js +247 -0
- package/dist/components/forms/SetPasswordForm.d.ts +7 -0
- package/dist/components/forms/SetPasswordForm.js +75 -0
- package/dist/components/forms/SignupForm.d.ts +13 -0
- package/dist/components/forms/SignupForm.js +152 -0
- package/dist/components/forms/VerifyOtpForm.d.ts +9 -0
- package/dist/components/forms/VerifyOtpForm.js +107 -0
- package/dist/components/forms/index.d.ts +19 -0
- package/dist/components/forms/index.js +21 -0
- package/dist/components/pages/ForgotPasswordPage.d.ts +2 -1
- package/dist/components/pages/ForgotPasswordPageHero.d.ts +3 -2
- package/dist/components/pages/ForgotPasswordPageHero.js +70 -64
- package/dist/components/pages/ForgotPasswordPageTailwind.d.ts +3 -2
- package/dist/components/pages/ForgotPasswordPageTailwind.js +42 -36
- package/dist/components/pages/LoginPage.d.ts +2 -1
- package/dist/components/pages/LoginPageHero.d.ts +2 -1
- package/dist/components/pages/LoginPageHero.js +297 -291
- package/dist/components/pages/LoginPageTailwind.d.ts +2 -1
- package/dist/components/pages/LoginPageTailwind.js +197 -191
- package/dist/components/pages/SetPasswordPage.d.ts +2 -1
- package/dist/components/pages/SetPasswordPageHero.d.ts +3 -2
- package/dist/components/pages/SetPasswordPageHero.js +98 -92
- package/dist/components/pages/SetPasswordPageTailwind.d.ts +3 -2
- package/dist/components/pages/SetPasswordPageTailwind.js +63 -57
- package/dist/components/pages/SignupPage.d.ts +2 -1
- package/dist/components/pages/SignupPageHero.d.ts +3 -2
- package/dist/components/pages/SignupPageHero.js +139 -133
- package/dist/components/pages/SignupPageTailwind.d.ts +3 -2
- package/dist/components/pages/SignupPageTailwind.js +123 -117
- package/dist/components/pages/VerifyOtpPage.d.ts +2 -1
- package/dist/components/pages/VerifyOtpPageHero.d.ts +2 -1
- package/dist/components/pages/VerifyOtpPageHero.js +110 -104
- package/dist/components/pages/VerifyOtpPageTailwind.d.ts +2 -1
- package/dist/components/pages/VerifyOtpPageTailwind.js +60 -54
- package/dist/components/ui/translations.d.ts +9 -0
- package/dist/components/ui/translations.js +18 -0
- package/dist/config.d.ts +12 -0
- package/dist/config.js +11 -0
- package/dist/exports/client.d.ts +5 -0
- package/dist/exports/client.js +5 -0
- package/dist/exports/rsc.d.ts +7 -0
- package/dist/exports/rsc.js +8 -0
- package/dist/index.js +12 -2
- 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
|
-
- **
|
|
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
|
|
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
|
-
| `
|
|
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
|
|
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 |
|
|
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
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
3
|
import React from 'react';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
|
|
4
|
-
|
|
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,
|
|
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 {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
|
6
|
-
*
|
|
7
|
-
*
|
|
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
|