@better-auth-ui/react 1.6.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.
- package/dist/components/auth/auth-provider.d.ts +32 -0
- package/dist/components/auth/auth-provider.js +44 -0
- package/dist/components/email/email-changed.d.ts +97 -0
- package/dist/components/email/email-changed.js +70 -0
- package/dist/components/email/email-localization.d.ts +61 -0
- package/dist/components/email/email-localization.js +24 -0
- package/dist/components/email/email-styles.d.ts +89 -0
- package/dist/components/email/email-styles.js +129 -0
- package/dist/components/email/email-verification.d.ts +97 -0
- package/dist/components/email/email-verification.js +75 -0
- package/dist/components/email/index.d.ts +9 -0
- package/dist/components/email/index.js +9 -0
- package/dist/components/email/magic-link.d.ts +93 -0
- package/dist/components/email/magic-link.js +72 -0
- package/dist/components/email/new-device.d.ts +123 -0
- package/dist/components/email/new-device.js +90 -0
- package/dist/components/email/otp-email.d.ts +88 -0
- package/dist/components/email/otp-email.js +65 -0
- package/dist/components/email/password-changed.d.ts +96 -0
- package/dist/components/email/password-changed.js +76 -0
- package/dist/components/email/reset-password.d.ts +90 -0
- package/dist/components/email/reset-password.js +68 -0
- package/dist/components/icons/apple.d.ts +8 -0
- package/dist/components/icons/apple.js +10 -0
- package/dist/components/icons/atlassian.d.ts +10 -0
- package/dist/components/icons/atlassian.js +14 -0
- package/dist/components/icons/cognito.d.ts +8 -0
- package/dist/components/icons/cognito.js +10 -0
- package/dist/components/icons/discord.d.ts +8 -0
- package/dist/components/icons/discord.js +10 -0
- package/dist/components/icons/dropbox.d.ts +8 -0
- package/dist/components/icons/dropbox.js +10 -0
- package/dist/components/icons/facebook.d.ts +10 -0
- package/dist/components/icons/facebook.js +12 -0
- package/dist/components/icons/figma.d.ts +11 -0
- package/dist/components/icons/figma.js +13 -0
- package/dist/components/icons/github.d.ts +10 -0
- package/dist/components/icons/github.js +12 -0
- package/dist/components/icons/gitlab.d.ts +8 -0
- package/dist/components/icons/gitlab.js +10 -0
- package/dist/components/icons/google.d.ts +8 -0
- package/dist/components/icons/google.js +10 -0
- package/dist/components/icons/huggingface.d.ts +8 -0
- package/dist/components/icons/huggingface.js +10 -0
- package/dist/components/icons/index.d.ts +35 -0
- package/dist/components/icons/index.js +35 -0
- package/dist/components/icons/kakao.d.ts +10 -0
- package/dist/components/icons/kakao.js +12 -0
- package/dist/components/icons/kick.d.ts +10 -0
- package/dist/components/icons/kick.js +12 -0
- package/dist/components/icons/line.d.ts +8 -0
- package/dist/components/icons/line.js +10 -0
- package/dist/components/icons/linear.d.ts +8 -0
- package/dist/components/icons/linear.js +10 -0
- package/dist/components/icons/linkedin.d.ts +8 -0
- package/dist/components/icons/linkedin.js +10 -0
- package/dist/components/icons/microsoft.d.ts +8 -0
- package/dist/components/icons/microsoft.js +10 -0
- package/dist/components/icons/naver.d.ts +8 -0
- package/dist/components/icons/naver.js +10 -0
- package/dist/components/icons/notion.d.ts +8 -0
- package/dist/components/icons/notion.js +10 -0
- package/dist/components/icons/paybin.d.ts +7 -0
- package/dist/components/icons/paybin.js +9 -0
- package/dist/components/icons/paypal.d.ts +8 -0
- package/dist/components/icons/paypal.js +10 -0
- package/dist/components/icons/polar.d.ts +8 -0
- package/dist/components/icons/polar.js +10 -0
- package/dist/components/icons/railway.d.ts +8 -0
- package/dist/components/icons/railway.js +10 -0
- package/dist/components/icons/reddit.d.ts +8 -0
- package/dist/components/icons/reddit.js +10 -0
- package/dist/components/icons/roblox.d.ts +11 -0
- package/dist/components/icons/roblox.js +13 -0
- package/dist/components/icons/salesforce.d.ts +8 -0
- package/dist/components/icons/salesforce.js +10 -0
- package/dist/components/icons/slack.d.ts +8 -0
- package/dist/components/icons/slack.js +10 -0
- package/dist/components/icons/spotify.d.ts +8 -0
- package/dist/components/icons/spotify.js +10 -0
- package/dist/components/icons/tiktok.d.ts +9 -0
- package/dist/components/icons/tiktok.js +11 -0
- package/dist/components/icons/twitch.d.ts +8 -0
- package/dist/components/icons/twitch.js +13 -0
- package/dist/components/icons/vercel.d.ts +8 -0
- package/dist/components/icons/vercel.js +10 -0
- package/dist/components/icons/vk.d.ts +8 -0
- package/dist/components/icons/vk.js +10 -0
- package/dist/components/icons/wechat.d.ts +8 -0
- package/dist/components/icons/wechat.js +10 -0
- package/dist/components/icons/x.d.ts +8 -0
- package/dist/components/icons/x.js +10 -0
- package/dist/components/icons/zoom.d.ts +8 -0
- package/dist/components/icons/zoom.js +12 -0
- package/dist/components/settings/account/theme-preview.d.ts +3 -0
- package/dist/components/settings/account/theme-preview.js +10 -0
- package/dist/core.d.ts +1 -0
- package/dist/core.js +1 -0
- package/dist/hooks/auth/use-auth-mutation.d.ts +18 -0
- package/dist/hooks/auth/use-auth-mutation.js +10 -0
- package/dist/hooks/auth/use-auth-query.d.ts +24 -0
- package/dist/hooks/auth/use-auth-query.js +10 -0
- package/dist/hooks/auth/use-authenticate.d.ts +9 -0
- package/dist/hooks/auth/use-authenticate.js +28 -0
- package/dist/hooks/auth/use-forgot-password.d.ts +20 -0
- package/dist/hooks/auth/use-forgot-password.js +14 -0
- package/dist/hooks/auth/use-request-password-reset.d.ts +26 -0
- package/dist/hooks/auth/use-request-password-reset.js +14 -0
- package/dist/hooks/auth/use-reset-password.d.ts +40 -0
- package/dist/hooks/auth/use-reset-password.js +13 -0
- package/dist/hooks/auth/use-send-verification-email.d.ts +24 -0
- package/dist/hooks/auth/use-send-verification-email.js +13 -0
- package/dist/hooks/auth/use-session.d.ts +9 -0
- package/dist/hooks/auth/use-session.js +18 -0
- package/dist/hooks/auth/use-sign-in-email.d.ts +53 -0
- package/dist/hooks/auth/use-sign-in-email.js +24 -0
- package/dist/hooks/auth/use-sign-in-magic-link.d.ts +32 -0
- package/dist/hooks/auth/use-sign-in-magic-link.js +13 -0
- package/dist/hooks/auth/use-sign-in-social.d.ts +93 -0
- package/dist/hooks/auth/use-sign-in-social.js +13 -0
- package/dist/hooks/auth/use-sign-out.d.ts +19 -0
- package/dist/hooks/auth/use-sign-out.js +24 -0
- package/dist/hooks/auth/use-sign-up-email.d.ts +526 -0
- package/dist/hooks/auth/use-sign-up-email.js +24 -0
- package/dist/hooks/auth/use-user.d.ts +513 -0
- package/dist/hooks/auth/use-user.js +14 -0
- package/dist/hooks/settings/use-account-info.d.ts +17 -0
- package/dist/hooks/settings/use-account-info.js +25 -0
- package/dist/hooks/settings/use-change-email.d.ts +24 -0
- package/dist/hooks/settings/use-change-email.js +23 -0
- package/dist/hooks/settings/use-change-password.d.ts +54 -0
- package/dist/hooks/settings/use-change-password.js +11 -0
- package/dist/hooks/settings/use-delete-user.d.ts +42 -0
- package/dist/hooks/settings/use-delete-user.js +11 -0
- package/dist/hooks/settings/use-link-social.d.ts +46 -0
- package/dist/hooks/settings/use-link-social.js +10 -0
- package/dist/hooks/settings/use-list-accounts.d.ts +20 -0
- package/dist/hooks/settings/use-list-accounts.js +23 -0
- package/dist/hooks/settings/use-list-device-sessions.d.ts +11 -0
- package/dist/hooks/settings/use-list-device-sessions.js +22 -0
- package/dist/hooks/settings/use-list-sessions.d.ts +20 -0
- package/dist/hooks/settings/use-list-sessions.js +22 -0
- package/dist/hooks/settings/use-revoke-multi-session.d.ts +19 -0
- package/dist/hooks/settings/use-revoke-multi-session.js +20 -0
- package/dist/hooks/settings/use-revoke-session.d.ts +19 -0
- package/dist/hooks/settings/use-revoke-session.js +21 -0
- package/dist/hooks/settings/use-set-active-session.d.ts +37 -0
- package/dist/hooks/settings/use-set-active-session.js +28 -0
- package/dist/hooks/settings/use-unlink-account.d.ts +21 -0
- package/dist/hooks/settings/use-unlink-account.js +21 -0
- package/dist/hooks/settings/use-update-user.d.ts +11 -0
- package/dist/hooks/settings/use-update-user.js +29 -0
- package/dist/hooks/use-hydrated.d.ts +6 -0
- package/dist/hooks/use-hydrated.js +18 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.js +33 -0
- package/dist/lib/auth-client.d.ts +1979 -0
- package/dist/lib/auth-client.js +5 -0
- package/dist/lib/auth-config.d.ts +43 -0
- package/dist/lib/auth-config.js +1 -0
- package/dist/lib/auth-context.d.ts +2 -0
- package/dist/lib/auth-context.js +3 -0
- package/dist/lib/auth-hook-options.d.ts +8 -0
- package/dist/lib/auth-hook-options.js +1 -0
- package/dist/lib/provider-icons.d.ts +13 -0
- package/dist/lib/provider-icons.js +48 -0
- package/dist/lib/utils.d.ts +2 -0
- package/dist/lib/utils.js +5 -0
- package/package.json +64 -0
- package/src/components/auth/auth-provider.tsx +82 -0
- package/src/components/email/email-changed.tsx +381 -0
- package/src/components/email/email-localization.tsx +33 -0
- package/src/components/email/email-styles.tsx +175 -0
- package/src/components/email/email-verification.tsx +350 -0
- package/src/components/email/index.ts +9 -0
- package/src/components/email/magic-link.tsx +349 -0
- package/src/components/email/new-device.tsx +452 -0
- package/src/components/email/otp-email.tsx +321 -0
- package/src/components/email/password-changed.tsx +380 -0
- package/src/components/email/reset-password.tsx +343 -0
- package/src/components/icons/apple.tsx +24 -0
- package/src/components/icons/atlassian.tsx +43 -0
- package/src/components/icons/cognito.tsx +25 -0
- package/src/components/icons/discord.tsx +24 -0
- package/src/components/icons/dropbox.tsx +24 -0
- package/src/components/icons/facebook.tsx +30 -0
- package/src/components/icons/figma.tsx +43 -0
- package/src/components/icons/github.tsx +26 -0
- package/src/components/icons/gitlab.tsx +36 -0
- package/src/components/icons/google.tsx +42 -0
- package/src/components/icons/huggingface.tsx +44 -0
- package/src/components/icons/index.ts +35 -0
- package/src/components/icons/kakao.tsx +26 -0
- package/src/components/icons/kick.tsx +30 -0
- package/src/components/icons/line.tsx +24 -0
- package/src/components/icons/linear.tsx +24 -0
- package/src/components/icons/linkedin.tsx +28 -0
- package/src/components/icons/microsoft.tsx +24 -0
- package/src/components/icons/naver.tsx +24 -0
- package/src/components/icons/notion.tsx +25 -0
- package/src/components/icons/paybin.tsx +37 -0
- package/src/components/icons/paypal.tsx +36 -0
- package/src/components/icons/polar.tsx +31 -0
- package/src/components/icons/railway.tsx +24 -0
- package/src/components/icons/reddit.tsx +25 -0
- package/src/components/icons/roblox.tsx +28 -0
- package/src/components/icons/salesforce.tsx +24 -0
- package/src/components/icons/slack.tsx +36 -0
- package/src/components/icons/spotify.tsx +24 -0
- package/src/components/icons/tiktok.tsx +32 -0
- package/src/components/icons/twitch.tsx +50 -0
- package/src/components/icons/vercel.tsx +21 -0
- package/src/components/icons/vk.tsx +24 -0
- package/src/components/icons/wechat.tsx +24 -0
- package/src/components/icons/x.tsx +24 -0
- package/src/components/icons/zoom.tsx +48 -0
- package/src/components/settings/account/theme-preview.tsx +271 -0
- package/src/core.ts +1 -0
- package/src/hooks/auth/use-auth-mutation.ts +56 -0
- package/src/hooks/auth/use-auth-query.ts +50 -0
- package/src/hooks/auth/use-authenticate.ts +35 -0
- package/src/hooks/auth/use-request-password-reset.ts +24 -0
- package/src/hooks/auth/use-reset-password.ts +23 -0
- package/src/hooks/auth/use-send-verification-email.ts +23 -0
- package/src/hooks/auth/use-session.ts +26 -0
- package/src/hooks/auth/use-sign-in-email.ts +35 -0
- package/src/hooks/auth/use-sign-in-magic-link.ts +23 -0
- package/src/hooks/auth/use-sign-in-social.ts +23 -0
- package/src/hooks/auth/use-sign-out.ts +34 -0
- package/src/hooks/auth/use-sign-up-email.ts +35 -0
- package/src/hooks/auth/use-user.ts +21 -0
- package/src/hooks/settings/use-account-info.ts +30 -0
- package/src/hooks/settings/use-change-email.ts +33 -0
- package/src/hooks/settings/use-change-password.ts +21 -0
- package/src/hooks/settings/use-delete-user.ts +21 -0
- package/src/hooks/settings/use-link-social.ts +19 -0
- package/src/hooks/settings/use-list-accounts.ts +27 -0
- package/src/hooks/settings/use-list-device-sessions.ts +32 -0
- package/src/hooks/settings/use-list-sessions.ts +26 -0
- package/src/hooks/settings/use-revoke-multi-session.ts +30 -0
- package/src/hooks/settings/use-revoke-session.ts +31 -0
- package/src/hooks/settings/use-set-active-session.ts +46 -0
- package/src/hooks/settings/use-unlink-account.ts +31 -0
- package/src/hooks/settings/use-update-user.ts +44 -0
- package/src/index.ts +34 -0
- package/src/lib/auth-client.ts +37 -0
- package/src/lib/auth-config.ts +48 -0
- package/src/lib/auth-context.ts +6 -0
- package/src/lib/provider-icons.ts +90 -0
- package/src/lib/utils.ts +6 -0
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Body,
|
|
3
|
+
Container,
|
|
4
|
+
Head,
|
|
5
|
+
Heading,
|
|
6
|
+
Hr,
|
|
7
|
+
Html,
|
|
8
|
+
Img,
|
|
9
|
+
Link,
|
|
10
|
+
Preview,
|
|
11
|
+
pixelBasedPreset,
|
|
12
|
+
Section,
|
|
13
|
+
Tailwind,
|
|
14
|
+
Text
|
|
15
|
+
} from "@react-email/components"
|
|
16
|
+
import type { ReactNode } from "react"
|
|
17
|
+
|
|
18
|
+
import { cn } from "../../lib/utils"
|
|
19
|
+
import {
|
|
20
|
+
type EmailClassNames,
|
|
21
|
+
type EmailColors,
|
|
22
|
+
EmailStyles
|
|
23
|
+
} from "./email-styles"
|
|
24
|
+
|
|
25
|
+
const otpEmailLocalization = {
|
|
26
|
+
YOUR_VERIFICATION_CODE_IS_CODE:
|
|
27
|
+
"Your verification code is {verificationCode}",
|
|
28
|
+
LOGO: "Logo",
|
|
29
|
+
VERIFY_YOUR_EMAIL: "Verify your email",
|
|
30
|
+
WE_NEED_TO_VERIFY_YOUR_EMAIL_ADDRESS:
|
|
31
|
+
"We need to verify your email address {email} before you can access your {appName} account. Enter the code below in your open browser window.",
|
|
32
|
+
THIS_CODE_EXPIRES_IN_MINUTES:
|
|
33
|
+
"This code expires in {expirationMinutes} minutes.",
|
|
34
|
+
EMAIL_SENT_BY: "Email sent by {appName}.",
|
|
35
|
+
IF_YOU_DIDNT_REQUEST_THIS_EMAIL:
|
|
36
|
+
"If you didn't request this email, you can safely ignore it. Someone else might have typed your email address by mistake.",
|
|
37
|
+
POWERED_BY_BETTER_AUTH: "Powered by {betterAuth}"
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Localization strings for the OtpEmail component.
|
|
42
|
+
*
|
|
43
|
+
* Contains all text content used in the OTP (One-Time Password) email template.
|
|
44
|
+
*/
|
|
45
|
+
export type OtpEmailEmailLocalization = typeof otpEmailLocalization
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Props for the OtpEmail component.
|
|
49
|
+
*/
|
|
50
|
+
export interface OtpEmailProps {
|
|
51
|
+
/** The one-time verification code to display */
|
|
52
|
+
verificationCode: string
|
|
53
|
+
/** Email address being verified */
|
|
54
|
+
email?: string
|
|
55
|
+
/** Name of the application sending the email */
|
|
56
|
+
appName?: string
|
|
57
|
+
/** Number of minutes until the verification code expires */
|
|
58
|
+
expirationMinutes?: number
|
|
59
|
+
/** Logo URL(s) - a single string or light/dark variants. If omitted, no logo is shown. */
|
|
60
|
+
logoURL?: string | { light: string; dark: string }
|
|
61
|
+
/** Custom CSS class names for styling specific parts of the email */
|
|
62
|
+
classNames?: EmailClassNames
|
|
63
|
+
/** Custom color scheme for light and dark modes */
|
|
64
|
+
colors?: EmailColors
|
|
65
|
+
/** Whether to show the "Powered by better-auth" footer */
|
|
66
|
+
poweredBy?: boolean
|
|
67
|
+
/** Whether to enable dark mode support */
|
|
68
|
+
darkMode?: boolean
|
|
69
|
+
/** Additional React nodes to inject into the email head */
|
|
70
|
+
head?: ReactNode
|
|
71
|
+
/**
|
|
72
|
+
* Localization overrides for customizing email text
|
|
73
|
+
* @remarks `OtpEmailEmailLocalization`
|
|
74
|
+
*/
|
|
75
|
+
localization?: Partial<OtpEmailEmailLocalization>
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Email template component that sends one-time password (OTP) verification codes to users.
|
|
80
|
+
*
|
|
81
|
+
* This email includes:
|
|
82
|
+
* - Large, prominently displayed verification code
|
|
83
|
+
* - Expiration time information
|
|
84
|
+
* - Security notice for unauthorized requests
|
|
85
|
+
* - Customizable branding and styling
|
|
86
|
+
* - Support for light/dark mode themes
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```tsx
|
|
90
|
+
* <OtpEmail
|
|
91
|
+
* verificationCode="069420"
|
|
92
|
+
* email="user@example.com"
|
|
93
|
+
* appName="My App"
|
|
94
|
+
* expirationMinutes={10}
|
|
95
|
+
* logoURL="https://example.com/logo.png"
|
|
96
|
+
* darkMode={true}
|
|
97
|
+
* />
|
|
98
|
+
* ```
|
|
99
|
+
*/
|
|
100
|
+
export const OtpEmail = ({
|
|
101
|
+
verificationCode,
|
|
102
|
+
email,
|
|
103
|
+
appName,
|
|
104
|
+
expirationMinutes = 10,
|
|
105
|
+
logoURL,
|
|
106
|
+
colors,
|
|
107
|
+
classNames,
|
|
108
|
+
darkMode = true,
|
|
109
|
+
poweredBy,
|
|
110
|
+
head,
|
|
111
|
+
...props
|
|
112
|
+
}: OtpEmailProps) => {
|
|
113
|
+
const localization = {
|
|
114
|
+
...OtpEmail.localization,
|
|
115
|
+
...props.localization
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const previewText = localization.YOUR_VERIFICATION_CODE_IS_CODE.replace(
|
|
119
|
+
"{verificationCode}",
|
|
120
|
+
verificationCode
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
return (
|
|
124
|
+
<Html>
|
|
125
|
+
<Head>
|
|
126
|
+
<meta content="light dark" name="color-scheme" />
|
|
127
|
+
<meta content="light dark" name="supported-color-schemes" />
|
|
128
|
+
|
|
129
|
+
<EmailStyles colors={colors} darkMode={darkMode} />
|
|
130
|
+
|
|
131
|
+
{head}
|
|
132
|
+
</Head>
|
|
133
|
+
|
|
134
|
+
<Preview>{previewText}</Preview>
|
|
135
|
+
|
|
136
|
+
<Tailwind config={{ presets: [pixelBasedPreset] }}>
|
|
137
|
+
<Body className={cn("bg-background font-sans", classNames?.body)}>
|
|
138
|
+
<Container
|
|
139
|
+
className={cn(
|
|
140
|
+
"mx-auto my-auto max-w-xl px-2 py-10",
|
|
141
|
+
classNames?.container
|
|
142
|
+
)}
|
|
143
|
+
>
|
|
144
|
+
<Section
|
|
145
|
+
className={cn(
|
|
146
|
+
"bg-card text-card-foreground rounded-none border border-border p-8",
|
|
147
|
+
classNames?.card
|
|
148
|
+
)}
|
|
149
|
+
>
|
|
150
|
+
{logoURL &&
|
|
151
|
+
(typeof logoURL === "string" ? (
|
|
152
|
+
<Img
|
|
153
|
+
src={logoURL}
|
|
154
|
+
width={48}
|
|
155
|
+
height={48}
|
|
156
|
+
alt={appName || localization.LOGO}
|
|
157
|
+
className={cn("mx-auto mb-8", classNames?.logo)}
|
|
158
|
+
/>
|
|
159
|
+
) : (
|
|
160
|
+
<>
|
|
161
|
+
<Img
|
|
162
|
+
src={logoURL.light}
|
|
163
|
+
width={48}
|
|
164
|
+
height={48}
|
|
165
|
+
alt={appName || localization.LOGO}
|
|
166
|
+
className={cn(
|
|
167
|
+
"mx-auto mb-8 logo-light",
|
|
168
|
+
classNames?.logo
|
|
169
|
+
)}
|
|
170
|
+
/>
|
|
171
|
+
<Img
|
|
172
|
+
src={logoURL.dark}
|
|
173
|
+
width={48}
|
|
174
|
+
height={48}
|
|
175
|
+
alt={appName || localization.LOGO}
|
|
176
|
+
className={cn(
|
|
177
|
+
"hidden mx-auto mb-8 logo-dark",
|
|
178
|
+
classNames?.logo
|
|
179
|
+
)}
|
|
180
|
+
/>
|
|
181
|
+
</>
|
|
182
|
+
))}
|
|
183
|
+
|
|
184
|
+
<Heading
|
|
185
|
+
className={cn("mb-5 text-2xl font-semibold", classNames?.title)}
|
|
186
|
+
>
|
|
187
|
+
{localization.VERIFY_YOUR_EMAIL}
|
|
188
|
+
</Heading>
|
|
189
|
+
|
|
190
|
+
<Text className={cn("text-sm font-normal", classNames?.content)}>
|
|
191
|
+
{(() => {
|
|
192
|
+
const textWithAppName =
|
|
193
|
+
localization.WE_NEED_TO_VERIFY_YOUR_EMAIL_ADDRESS.replace(
|
|
194
|
+
"{appName}",
|
|
195
|
+
appName || ""
|
|
196
|
+
)
|
|
197
|
+
.replace(/\s{2,}/g, " ")
|
|
198
|
+
.replace(" .", ".")
|
|
199
|
+
|
|
200
|
+
const [beforeEmail, afterEmail] =
|
|
201
|
+
textWithAppName.split("{email}")
|
|
202
|
+
|
|
203
|
+
return email ? (
|
|
204
|
+
<>
|
|
205
|
+
{beforeEmail}
|
|
206
|
+
|
|
207
|
+
<Link
|
|
208
|
+
href={`mailto:${email}`}
|
|
209
|
+
className="text-primary font-medium"
|
|
210
|
+
>
|
|
211
|
+
{email}
|
|
212
|
+
</Link>
|
|
213
|
+
|
|
214
|
+
{afterEmail}
|
|
215
|
+
</>
|
|
216
|
+
) : (
|
|
217
|
+
textWithAppName
|
|
218
|
+
.replace("{email}", "")
|
|
219
|
+
.replace(/\s{2,}/g, " ")
|
|
220
|
+
.replace(" .", ".")
|
|
221
|
+
)
|
|
222
|
+
})()}
|
|
223
|
+
</Text>
|
|
224
|
+
|
|
225
|
+
<Section
|
|
226
|
+
className={cn(
|
|
227
|
+
"my-6 border border-border bg-muted p-6",
|
|
228
|
+
classNames?.codeBlock
|
|
229
|
+
)}
|
|
230
|
+
>
|
|
231
|
+
<Text
|
|
232
|
+
className={cn(
|
|
233
|
+
"m-0 text-center text-4xl font-semibold tracking-widest",
|
|
234
|
+
classNames?.title
|
|
235
|
+
)}
|
|
236
|
+
>
|
|
237
|
+
{verificationCode}
|
|
238
|
+
</Text>
|
|
239
|
+
</Section>
|
|
240
|
+
|
|
241
|
+
<Hr
|
|
242
|
+
className={cn(
|
|
243
|
+
"my-6 w-full border border-solid border-border",
|
|
244
|
+
classNames?.separator
|
|
245
|
+
)}
|
|
246
|
+
/>
|
|
247
|
+
|
|
248
|
+
<Text
|
|
249
|
+
className={cn(
|
|
250
|
+
"mb-3 text-xs text-muted-foreground",
|
|
251
|
+
classNames?.description
|
|
252
|
+
)}
|
|
253
|
+
>
|
|
254
|
+
{localization.THIS_CODE_EXPIRES_IN_MINUTES.replace(
|
|
255
|
+
"{expirationMinutes}",
|
|
256
|
+
expirationMinutes.toString()
|
|
257
|
+
)}
|
|
258
|
+
{appName && (
|
|
259
|
+
<>
|
|
260
|
+
{" "}
|
|
261
|
+
{localization.EMAIL_SENT_BY.replace("{appName}", appName)}
|
|
262
|
+
</>
|
|
263
|
+
)}
|
|
264
|
+
</Text>
|
|
265
|
+
|
|
266
|
+
<Text
|
|
267
|
+
className={cn(
|
|
268
|
+
"mt-3 text-xs text-muted-foreground",
|
|
269
|
+
classNames?.description
|
|
270
|
+
)}
|
|
271
|
+
>
|
|
272
|
+
{localization.IF_YOU_DIDNT_REQUEST_THIS_EMAIL}
|
|
273
|
+
</Text>
|
|
274
|
+
|
|
275
|
+
{poweredBy && (
|
|
276
|
+
<Text
|
|
277
|
+
className={cn(
|
|
278
|
+
"mt-4 mb-0 text-center text-[11px] text-muted-foreground",
|
|
279
|
+
classNames?.poweredBy
|
|
280
|
+
)}
|
|
281
|
+
>
|
|
282
|
+
{(() => {
|
|
283
|
+
const [beforeBetterAuth, afterBetterAuth] =
|
|
284
|
+
localization.POWERED_BY_BETTER_AUTH.split("{betterAuth}")
|
|
285
|
+
|
|
286
|
+
return (
|
|
287
|
+
<>
|
|
288
|
+
{beforeBetterAuth}
|
|
289
|
+
<Link
|
|
290
|
+
href="https://better-auth.com"
|
|
291
|
+
className={cn(
|
|
292
|
+
"text-primary underline",
|
|
293
|
+
classNames?.link
|
|
294
|
+
)}
|
|
295
|
+
>
|
|
296
|
+
better-auth
|
|
297
|
+
</Link>
|
|
298
|
+
{afterBetterAuth}
|
|
299
|
+
</>
|
|
300
|
+
)
|
|
301
|
+
})()}
|
|
302
|
+
</Text>
|
|
303
|
+
)}
|
|
304
|
+
</Section>
|
|
305
|
+
</Container>
|
|
306
|
+
</Body>
|
|
307
|
+
</Tailwind>
|
|
308
|
+
</Html>
|
|
309
|
+
)
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
OtpEmail.localization = otpEmailLocalization
|
|
313
|
+
|
|
314
|
+
OtpEmail.PreviewProps = {
|
|
315
|
+
verificationCode: "069420",
|
|
316
|
+
email: "m@example.com",
|
|
317
|
+
appName: "Better Auth",
|
|
318
|
+
darkMode: true
|
|
319
|
+
} as OtpEmailProps
|
|
320
|
+
|
|
321
|
+
export default OtpEmail
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Body,
|
|
3
|
+
Button,
|
|
4
|
+
Container,
|
|
5
|
+
Head,
|
|
6
|
+
Heading,
|
|
7
|
+
Hr,
|
|
8
|
+
Html,
|
|
9
|
+
Img,
|
|
10
|
+
Link,
|
|
11
|
+
Preview,
|
|
12
|
+
pixelBasedPreset,
|
|
13
|
+
Section,
|
|
14
|
+
Tailwind,
|
|
15
|
+
Text
|
|
16
|
+
} from "@react-email/components"
|
|
17
|
+
import type { ReactNode } from "react"
|
|
18
|
+
|
|
19
|
+
import { cn } from "../../lib/utils"
|
|
20
|
+
import {
|
|
21
|
+
type EmailClassNames,
|
|
22
|
+
type EmailColors,
|
|
23
|
+
EmailStyles
|
|
24
|
+
} from "./email-styles"
|
|
25
|
+
|
|
26
|
+
const passwordChangedEmailLocalization = {
|
|
27
|
+
YOUR_PASSWORD_HAS_BEEN_CHANGED: "Your password has been changed",
|
|
28
|
+
LOGO: "Logo",
|
|
29
|
+
PASSWORD_CHANGED_SUCCESSFULLY: "Password changed successfully",
|
|
30
|
+
PASSWORD_FOR_YOUR_ACCOUNT_CHANGED:
|
|
31
|
+
"The password for your {appName} account {userEmail} has been changed successfully.",
|
|
32
|
+
CHANGED_AT: "Changed at",
|
|
33
|
+
IF_YOU_MADE_THIS_CHANGE:
|
|
34
|
+
"If you made this change, you can safely ignore this email. Your account is secure.",
|
|
35
|
+
I_DIDNT_MAKE_THIS_CHANGE: "I didn't make this change",
|
|
36
|
+
EMAIL_SENT_BY: "Email sent by {appName}.",
|
|
37
|
+
IF_YOU_DIDNT_AUTHORIZE_THIS_CHANGE:
|
|
38
|
+
"If you didn't authorize this change, please contact support immediately {supportEmail} to secure your account.",
|
|
39
|
+
POWERED_BY_BETTER_AUTH: "Powered by {betterAuth}"
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Localization strings for the PasswordChangedEmail component.
|
|
44
|
+
*
|
|
45
|
+
* Contains all text content used in the password changed notification email template.
|
|
46
|
+
*/
|
|
47
|
+
export type PasswordChangedEmailLocalization =
|
|
48
|
+
typeof passwordChangedEmailLocalization
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Props for the PasswordChangedEmail component.
|
|
52
|
+
*/
|
|
53
|
+
export interface PasswordChangedEmailProps {
|
|
54
|
+
/** Email address of the user account */
|
|
55
|
+
email?: string
|
|
56
|
+
/** Timestamp when the password was changed */
|
|
57
|
+
timestamp?: string
|
|
58
|
+
/** URL to secure the account if unauthorized change occurred */
|
|
59
|
+
secureAccountURL?: string
|
|
60
|
+
/** Name of the application sending the email */
|
|
61
|
+
appName?: string
|
|
62
|
+
/** Support email address for security concerns */
|
|
63
|
+
supportEmail?: string
|
|
64
|
+
/** Logo URL(s) - a single string or light/dark variants. If omitted, no logo is shown. */
|
|
65
|
+
logoURL?: string | { light: string; dark: string }
|
|
66
|
+
/** Custom CSS class names for styling specific parts of the email */
|
|
67
|
+
classNames?: EmailClassNames
|
|
68
|
+
/** Custom color scheme for light and dark modes */
|
|
69
|
+
colors?: EmailColors
|
|
70
|
+
/** Whether to show the "Powered by better-auth" footer */
|
|
71
|
+
poweredBy?: boolean
|
|
72
|
+
/** Whether to enable dark mode support */
|
|
73
|
+
darkMode?: boolean
|
|
74
|
+
/** Additional React nodes to inject into the email head */
|
|
75
|
+
head?: ReactNode
|
|
76
|
+
/**
|
|
77
|
+
* Localization overrides for customizing email text
|
|
78
|
+
* @remarks `PasswordChangedEmailLocalization`
|
|
79
|
+
*/
|
|
80
|
+
localization?: Partial<PasswordChangedEmailLocalization>
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Email template component that notifies users when their password has been changed.
|
|
85
|
+
*
|
|
86
|
+
* This email includes:
|
|
87
|
+
* - Password change confirmation message
|
|
88
|
+
* - Timestamp of the change
|
|
89
|
+
* - Secure account action button if unauthorized change occurred
|
|
90
|
+
* - Security warnings and support contact information
|
|
91
|
+
* - Customizable branding and styling
|
|
92
|
+
* - Support for light/dark mode themes
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* ```tsx
|
|
96
|
+
* <PasswordChangedEmail
|
|
97
|
+
* email="user@example.com"
|
|
98
|
+
* timestamp="February 10, 2025 at 4:20 PM UTC"
|
|
99
|
+
* secureAccountURL="https://example.com/settings/security"
|
|
100
|
+
* appName="My App"
|
|
101
|
+
* supportEmail="support@example.com"
|
|
102
|
+
* logoURL="https://example.com/logo.png"
|
|
103
|
+
* darkMode={true}
|
|
104
|
+
* />
|
|
105
|
+
* ```
|
|
106
|
+
*/
|
|
107
|
+
export const PasswordChangedEmail = ({
|
|
108
|
+
email,
|
|
109
|
+
timestamp,
|
|
110
|
+
secureAccountURL,
|
|
111
|
+
appName,
|
|
112
|
+
supportEmail,
|
|
113
|
+
logoURL,
|
|
114
|
+
colors,
|
|
115
|
+
classNames,
|
|
116
|
+
darkMode = true,
|
|
117
|
+
poweredBy,
|
|
118
|
+
head,
|
|
119
|
+
...props
|
|
120
|
+
}: PasswordChangedEmailProps) => {
|
|
121
|
+
const localization = {
|
|
122
|
+
...PasswordChangedEmail.localization,
|
|
123
|
+
...props.localization
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const previewText = localization.YOUR_PASSWORD_HAS_BEEN_CHANGED
|
|
127
|
+
|
|
128
|
+
return (
|
|
129
|
+
<Html>
|
|
130
|
+
<Head>
|
|
131
|
+
<meta content="light dark" name="color-scheme" />
|
|
132
|
+
<meta content="light dark" name="supported-color-schemes" />
|
|
133
|
+
|
|
134
|
+
<EmailStyles colors={colors} darkMode={darkMode} />
|
|
135
|
+
|
|
136
|
+
{head}
|
|
137
|
+
</Head>
|
|
138
|
+
|
|
139
|
+
<Preview>{previewText}</Preview>
|
|
140
|
+
|
|
141
|
+
<Tailwind config={{ presets: [pixelBasedPreset] }}>
|
|
142
|
+
<Body className={cn("bg-background font-sans", classNames?.body)}>
|
|
143
|
+
<Container
|
|
144
|
+
className={cn(
|
|
145
|
+
"mx-auto my-auto max-w-xl px-2 py-10",
|
|
146
|
+
classNames?.container
|
|
147
|
+
)}
|
|
148
|
+
>
|
|
149
|
+
<Section
|
|
150
|
+
className={cn(
|
|
151
|
+
"bg-card text-card-foreground rounded-none border border-border p-8",
|
|
152
|
+
classNames?.card
|
|
153
|
+
)}
|
|
154
|
+
>
|
|
155
|
+
{logoURL &&
|
|
156
|
+
(typeof logoURL === "string" ? (
|
|
157
|
+
<Img
|
|
158
|
+
src={logoURL}
|
|
159
|
+
width={48}
|
|
160
|
+
height={48}
|
|
161
|
+
alt={appName || localization.LOGO}
|
|
162
|
+
className={cn("mx-auto mb-8", classNames?.logo)}
|
|
163
|
+
/>
|
|
164
|
+
) : (
|
|
165
|
+
<>
|
|
166
|
+
<Img
|
|
167
|
+
src={logoURL.light}
|
|
168
|
+
width={48}
|
|
169
|
+
height={48}
|
|
170
|
+
alt={appName || localization.LOGO}
|
|
171
|
+
className={cn(
|
|
172
|
+
"mx-auto mb-8 logo-light",
|
|
173
|
+
classNames?.logo
|
|
174
|
+
)}
|
|
175
|
+
/>
|
|
176
|
+
<Img
|
|
177
|
+
src={logoURL.dark}
|
|
178
|
+
width={48}
|
|
179
|
+
height={48}
|
|
180
|
+
alt={appName || localization.LOGO}
|
|
181
|
+
className={cn(
|
|
182
|
+
"hidden mx-auto mb-8 logo-dark",
|
|
183
|
+
classNames?.logo
|
|
184
|
+
)}
|
|
185
|
+
/>
|
|
186
|
+
</>
|
|
187
|
+
))}
|
|
188
|
+
|
|
189
|
+
<Heading
|
|
190
|
+
className={cn(
|
|
191
|
+
"m-0 mb-5 text-2xl font-semibold",
|
|
192
|
+
classNames?.title
|
|
193
|
+
)}
|
|
194
|
+
>
|
|
195
|
+
{localization.PASSWORD_CHANGED_SUCCESSFULLY}
|
|
196
|
+
</Heading>
|
|
197
|
+
|
|
198
|
+
<Text className={cn("text-sm font-normal", classNames?.content)}>
|
|
199
|
+
{(() => {
|
|
200
|
+
const textWithAppName =
|
|
201
|
+
localization.PASSWORD_FOR_YOUR_ACCOUNT_CHANGED.replace(
|
|
202
|
+
"{appName}",
|
|
203
|
+
appName || ""
|
|
204
|
+
)
|
|
205
|
+
.replace(/\s{2,}/g, " ")
|
|
206
|
+
.replace(" .", ".")
|
|
207
|
+
|
|
208
|
+
const [beforeUserEmail, afterUserEmail] =
|
|
209
|
+
textWithAppName.split("{userEmail}")
|
|
210
|
+
|
|
211
|
+
return email ? (
|
|
212
|
+
<>
|
|
213
|
+
{beforeUserEmail}
|
|
214
|
+
|
|
215
|
+
<Link
|
|
216
|
+
href={`mailto:${email}`}
|
|
217
|
+
className="text-primary font-medium"
|
|
218
|
+
>
|
|
219
|
+
{email}
|
|
220
|
+
</Link>
|
|
221
|
+
|
|
222
|
+
{afterUserEmail}
|
|
223
|
+
</>
|
|
224
|
+
) : (
|
|
225
|
+
textWithAppName
|
|
226
|
+
.replace("{userEmail}", "")
|
|
227
|
+
.replace(/\s{2,}/g, " ")
|
|
228
|
+
.replace(" .", ".")
|
|
229
|
+
)
|
|
230
|
+
})()}
|
|
231
|
+
</Text>
|
|
232
|
+
|
|
233
|
+
{timestamp && (
|
|
234
|
+
<Section
|
|
235
|
+
className={cn(
|
|
236
|
+
"my-6 border border-border bg-muted p-4",
|
|
237
|
+
classNames?.codeBlock
|
|
238
|
+
)}
|
|
239
|
+
>
|
|
240
|
+
<Text
|
|
241
|
+
className={cn(
|
|
242
|
+
"m-0 mb-2 text-xs text-muted-foreground",
|
|
243
|
+
classNames?.description
|
|
244
|
+
)}
|
|
245
|
+
>
|
|
246
|
+
{localization.CHANGED_AT}:
|
|
247
|
+
</Text>
|
|
248
|
+
<Text
|
|
249
|
+
className={cn(
|
|
250
|
+
"m-0 text-sm font-semibold",
|
|
251
|
+
classNames?.content
|
|
252
|
+
)}
|
|
253
|
+
>
|
|
254
|
+
{timestamp}
|
|
255
|
+
</Text>
|
|
256
|
+
</Section>
|
|
257
|
+
)}
|
|
258
|
+
|
|
259
|
+
<Text className={cn("text-sm font-normal", classNames?.content)}>
|
|
260
|
+
{localization.IF_YOU_MADE_THIS_CHANGE}
|
|
261
|
+
</Text>
|
|
262
|
+
|
|
263
|
+
{secureAccountURL && (
|
|
264
|
+
<Section className="mt-6">
|
|
265
|
+
<Button
|
|
266
|
+
href={secureAccountURL}
|
|
267
|
+
className={cn(
|
|
268
|
+
"inline-block whitespace-nowrap rounded-none text-sm font-medium py-2.5 px-6 bg-primary text-primary-foreground no-underline",
|
|
269
|
+
classNames?.button
|
|
270
|
+
)}
|
|
271
|
+
>
|
|
272
|
+
{localization.I_DIDNT_MAKE_THIS_CHANGE}
|
|
273
|
+
</Button>
|
|
274
|
+
</Section>
|
|
275
|
+
)}
|
|
276
|
+
|
|
277
|
+
<Hr
|
|
278
|
+
className={cn(
|
|
279
|
+
"my-6 w-full border border-solid border-border",
|
|
280
|
+
classNames?.separator
|
|
281
|
+
)}
|
|
282
|
+
/>
|
|
283
|
+
|
|
284
|
+
{appName && (
|
|
285
|
+
<Text
|
|
286
|
+
className={cn(
|
|
287
|
+
"mb-3 text-xs text-muted-foreground",
|
|
288
|
+
classNames?.description
|
|
289
|
+
)}
|
|
290
|
+
>
|
|
291
|
+
{localization.EMAIL_SENT_BY.replace("{appName}", appName)}
|
|
292
|
+
</Text>
|
|
293
|
+
)}
|
|
294
|
+
|
|
295
|
+
<Text
|
|
296
|
+
className={cn(
|
|
297
|
+
"mt-3 text-xs text-muted-foreground",
|
|
298
|
+
classNames?.description
|
|
299
|
+
)}
|
|
300
|
+
>
|
|
301
|
+
{(() => {
|
|
302
|
+
const [beforeSupportEmail, afterSupportEmail] =
|
|
303
|
+
localization.IF_YOU_DIDNT_AUTHORIZE_THIS_CHANGE.split(
|
|
304
|
+
"{supportEmail}"
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
return supportEmail ? (
|
|
308
|
+
<>
|
|
309
|
+
{beforeSupportEmail}
|
|
310
|
+
<Link
|
|
311
|
+
href={`mailto:${supportEmail}`}
|
|
312
|
+
className={cn(
|
|
313
|
+
"text-primary underline",
|
|
314
|
+
classNames?.link
|
|
315
|
+
)}
|
|
316
|
+
>
|
|
317
|
+
{supportEmail}
|
|
318
|
+
</Link>
|
|
319
|
+
{afterSupportEmail}
|
|
320
|
+
</>
|
|
321
|
+
) : (
|
|
322
|
+
localization.IF_YOU_DIDNT_AUTHORIZE_THIS_CHANGE.replace(
|
|
323
|
+
"{supportEmail}",
|
|
324
|
+
""
|
|
325
|
+
)
|
|
326
|
+
.replace(/\s{2,}/g, " ")
|
|
327
|
+
.replace(" .", ".")
|
|
328
|
+
)
|
|
329
|
+
})()}
|
|
330
|
+
</Text>
|
|
331
|
+
|
|
332
|
+
{poweredBy && (
|
|
333
|
+
<Text
|
|
334
|
+
className={cn(
|
|
335
|
+
"mt-4 mb-0 text-center text-[11px] text-muted-foreground",
|
|
336
|
+
classNames?.poweredBy
|
|
337
|
+
)}
|
|
338
|
+
>
|
|
339
|
+
{(() => {
|
|
340
|
+
const [beforeBetterAuth, afterBetterAuth] =
|
|
341
|
+
localization.POWERED_BY_BETTER_AUTH.split("{betterAuth}")
|
|
342
|
+
|
|
343
|
+
return (
|
|
344
|
+
<>
|
|
345
|
+
{beforeBetterAuth}
|
|
346
|
+
<Link
|
|
347
|
+
href="https://better-auth.com"
|
|
348
|
+
className={cn(
|
|
349
|
+
"text-primary underline",
|
|
350
|
+
classNames?.link
|
|
351
|
+
)}
|
|
352
|
+
>
|
|
353
|
+
better-auth
|
|
354
|
+
</Link>
|
|
355
|
+
{afterBetterAuth}
|
|
356
|
+
</>
|
|
357
|
+
)
|
|
358
|
+
})()}
|
|
359
|
+
</Text>
|
|
360
|
+
)}
|
|
361
|
+
</Section>
|
|
362
|
+
</Container>
|
|
363
|
+
</Body>
|
|
364
|
+
</Tailwind>
|
|
365
|
+
</Html>
|
|
366
|
+
)
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
PasswordChangedEmail.localization = passwordChangedEmailLocalization
|
|
370
|
+
|
|
371
|
+
PasswordChangedEmail.PreviewProps = {
|
|
372
|
+
email: "m@example.com",
|
|
373
|
+
timestamp: "February 10, 2025 at 4:20 PM UTC",
|
|
374
|
+
secureAccountURL: "https://better-auth-ui.com/settings/security",
|
|
375
|
+
appName: "Better Auth",
|
|
376
|
+
supportEmail: "support@example.com",
|
|
377
|
+
darkMode: true
|
|
378
|
+
} as PasswordChangedEmailProps
|
|
379
|
+
|
|
380
|
+
export default PasswordChangedEmail
|