@erikey/react 0.4.26 → 0.4.27

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 (143) hide show
  1. package/package.json +2 -1
  2. package/src/__tests__/auth-client.test.ts +105 -0
  3. package/src/__tests__/security/localStorage-encryption.test.ts +171 -0
  4. package/src/auth-client.ts +158 -0
  5. package/src/dashboard-client.ts +60 -0
  6. package/src/index.ts +88 -0
  7. package/src/kv-client.ts +316 -0
  8. package/src/lib/cross-origin-auth.ts +99 -0
  9. package/src/stubs/captcha.ts +24 -0
  10. package/src/stubs/hashes.ts +16 -0
  11. package/src/stubs/index.ts +17 -0
  12. package/src/stubs/passkey.ts +12 -0
  13. package/src/stubs/qr-code.ts +10 -0
  14. package/src/stubs/query.ts +16 -0
  15. package/src/stubs/realtime.ts +17 -0
  16. package/src/stubs/use-sync-external-store.ts +12 -0
  17. package/src/styles.css +141 -0
  18. package/src/types.ts +14 -0
  19. package/src/ui/components/auth/auth-callback.tsx +36 -0
  20. package/src/ui/components/auth/auth-form.tsx +310 -0
  21. package/src/ui/components/auth/auth-view.tsx +435 -0
  22. package/src/ui/components/auth/email-otp-button.tsx +53 -0
  23. package/src/ui/components/auth/forms/email-otp-form.tsx +312 -0
  24. package/src/ui/components/auth/forms/email-verification-form.tsx +271 -0
  25. package/src/ui/components/auth/forms/forgot-password-form.tsx +173 -0
  26. package/src/ui/components/auth/forms/magic-link-form.tsx +196 -0
  27. package/src/ui/components/auth/forms/recover-account-form.tsx +143 -0
  28. package/src/ui/components/auth/forms/reset-password-form.tsx +220 -0
  29. package/src/ui/components/auth/forms/sign-in-form.tsx +323 -0
  30. package/src/ui/components/auth/forms/sign-up-form.tsx +820 -0
  31. package/src/ui/components/auth/forms/two-factor-form.tsx +381 -0
  32. package/src/ui/components/auth/magic-link-button.tsx +54 -0
  33. package/src/ui/components/auth/one-tap.tsx +53 -0
  34. package/src/ui/components/auth/otp-input-group.tsx +65 -0
  35. package/src/ui/components/auth/passkey-button.tsx +91 -0
  36. package/src/ui/components/auth/provider-button.tsx +155 -0
  37. package/src/ui/components/auth/sign-out.tsx +25 -0
  38. package/src/ui/components/auth/wallet-button.tsx +192 -0
  39. package/src/ui/components/auth-loading.tsx +21 -0
  40. package/src/ui/components/captcha/captcha.tsx +91 -0
  41. package/src/ui/components/captcha/recaptcha-badge.tsx +61 -0
  42. package/src/ui/components/captcha/recaptcha-v2.tsx +58 -0
  43. package/src/ui/components/captcha/recaptcha-v3.tsx +73 -0
  44. package/src/ui/components/email/email-template.tsx +216 -0
  45. package/src/ui/components/form-error.tsx +27 -0
  46. package/src/ui/components/password-input.tsx +56 -0
  47. package/src/ui/components/provider-icons.tsx +404 -0
  48. package/src/ui/components/redirect-to-sign-in.tsx +16 -0
  49. package/src/ui/components/redirect-to-sign-up.tsx +16 -0
  50. package/src/ui/components/signed-in.tsx +20 -0
  51. package/src/ui/components/signed-out.tsx +20 -0
  52. package/src/ui/components/ui/alert.tsx +66 -0
  53. package/src/ui/components/ui/button.tsx +70 -0
  54. package/src/ui/components/ui/card.tsx +92 -0
  55. package/src/ui/components/ui/checkbox.tsx +66 -0
  56. package/src/ui/components/ui/field.tsx +248 -0
  57. package/src/ui/components/ui/form.tsx +165 -0
  58. package/src/ui/components/ui/input-otp.tsx +77 -0
  59. package/src/ui/components/ui/input.tsx +21 -0
  60. package/src/ui/components/ui/label.tsx +23 -0
  61. package/src/ui/components/ui/separator.tsx +34 -0
  62. package/src/ui/components/ui/skeleton.tsx +13 -0
  63. package/src/ui/components/ui/textarea.tsx +18 -0
  64. package/src/ui/components/user-avatar.tsx +151 -0
  65. package/src/ui/hooks/use-auth-data.ts +193 -0
  66. package/src/ui/hooks/use-authenticate.ts +64 -0
  67. package/src/ui/hooks/use-captcha.tsx +151 -0
  68. package/src/ui/hooks/use-hydrated.ts +13 -0
  69. package/src/ui/hooks/use-lang.ts +32 -0
  70. package/src/ui/hooks/use-success-transition.ts +41 -0
  71. package/src/ui/hooks/use-theme.ts +39 -0
  72. package/src/ui/index.ts +46 -0
  73. package/src/ui/instantdb.ts +1 -0
  74. package/src/ui/lib/auth-data-cache.ts +90 -0
  75. package/src/ui/lib/auth-ui-provider.tsx +769 -0
  76. package/src/ui/lib/gravatar-utils.ts +58 -0
  77. package/src/ui/lib/image-utils.ts +55 -0
  78. package/src/ui/lib/instantdb/model-names.ts +24 -0
  79. package/src/ui/lib/instantdb/use-instant-options.ts +98 -0
  80. package/src/ui/lib/instantdb/use-list-accounts.ts +38 -0
  81. package/src/ui/lib/instantdb/use-list-sessions.ts +53 -0
  82. package/src/ui/lib/instantdb/use-session.ts +55 -0
  83. package/src/ui/lib/social-providers.ts +150 -0
  84. package/src/ui/lib/tanstack/auth-ui-provider-tanstack.tsx +49 -0
  85. package/src/ui/lib/tanstack/use-tanstack-options.ts +112 -0
  86. package/src/ui/lib/triplit/model-names.ts +24 -0
  87. package/src/ui/lib/triplit/use-conditional-query.ts +82 -0
  88. package/src/ui/lib/triplit/use-list-accounts.ts +31 -0
  89. package/src/ui/lib/triplit/use-list-sessions.ts +33 -0
  90. package/src/ui/lib/triplit/use-session.ts +42 -0
  91. package/src/ui/lib/triplit/use-triplit-hooks.ts +68 -0
  92. package/src/ui/lib/triplit/use-triplit-token.ts +44 -0
  93. package/src/ui/lib/utils.ts +119 -0
  94. package/src/ui/lib/view-paths.ts +61 -0
  95. package/src/ui/lib/wallet.ts +129 -0
  96. package/src/ui/localization/admin-error-codes.ts +20 -0
  97. package/src/ui/localization/anonymous-error-codes.ts +6 -0
  98. package/src/ui/localization/api-key-error-codes.ts +32 -0
  99. package/src/ui/localization/auth-localization.ts +865 -0
  100. package/src/ui/localization/base-error-codes.ts +27 -0
  101. package/src/ui/localization/captcha-error-codes.ts +17 -0
  102. package/src/ui/localization/email-otp-error-codes.ts +7 -0
  103. package/src/ui/localization/generic-oauth-error-codes.ts +3 -0
  104. package/src/ui/localization/haveibeenpwned-error-codes.ts +4 -0
  105. package/src/ui/localization/multi-session-error-codes.ts +3 -0
  106. package/src/ui/localization/organization-error-codes.ts +57 -0
  107. package/src/ui/localization/passkey-error-codes.ts +10 -0
  108. package/src/ui/localization/phone-number-error-codes.ts +10 -0
  109. package/src/ui/localization/stripe-localization.ts +12 -0
  110. package/src/ui/localization/team-error-codes.ts +12 -0
  111. package/src/ui/localization/two-factor-error-codes.ts +12 -0
  112. package/src/ui/localization/username-error-codes.ts +9 -0
  113. package/src/ui/server.ts +4 -0
  114. package/src/ui/style.css +146 -0
  115. package/src/ui/tanstack.ts +1 -0
  116. package/src/ui/triplit.ts +1 -0
  117. package/src/ui/types/account-options.ts +35 -0
  118. package/src/ui/types/additional-fields.ts +21 -0
  119. package/src/ui/types/any-auth-client.ts +6 -0
  120. package/src/ui/types/api-key.ts +9 -0
  121. package/src/ui/types/auth-client.ts +41 -0
  122. package/src/ui/types/auth-hooks.ts +81 -0
  123. package/src/ui/types/auth-mutators.ts +21 -0
  124. package/src/ui/types/avatar-options.ts +29 -0
  125. package/src/ui/types/captcha-options.ts +32 -0
  126. package/src/ui/types/captcha-provider.ts +7 -0
  127. package/src/ui/types/credentials-options.ts +38 -0
  128. package/src/ui/types/delete-user-options.ts +7 -0
  129. package/src/ui/types/email-verification-options.ts +7 -0
  130. package/src/ui/types/fetch-error.ts +6 -0
  131. package/src/ui/types/generic-oauth-options.ts +16 -0
  132. package/src/ui/types/gravatar-options.ts +21 -0
  133. package/src/ui/types/image.ts +7 -0
  134. package/src/ui/types/invitation.ts +10 -0
  135. package/src/ui/types/link.ts +7 -0
  136. package/src/ui/types/organization-options.ts +106 -0
  137. package/src/ui/types/password-validation.ts +16 -0
  138. package/src/ui/types/profile.ts +15 -0
  139. package/src/ui/types/refetch.ts +1 -0
  140. package/src/ui/types/render-toast.ts +9 -0
  141. package/src/ui/types/sign-up-options.ts +7 -0
  142. package/src/ui/types/social-options.ts +16 -0
  143. package/src/ui/types/team-options.ts +47 -0
@@ -0,0 +1,435 @@
1
+ "use client"
2
+
3
+ import { ArrowLeftIcon } from "lucide-react"
4
+ import { type ReactNode, useContext, useEffect, useState } from "react"
5
+ import { useIsHydrated } from "../../hooks/use-hydrated"
6
+ import { AuthUIContext } from "../../lib/auth-ui-provider"
7
+ import { socialProviders } from "../../lib/social-providers"
8
+ import { cn, getViewByPath } from "../../lib/utils"
9
+ import type { AuthViewPaths } from "../../lib/view-paths"
10
+ import type { AuthLocalization } from "../../localization/auth-localization"
11
+ import { Button } from "../ui/button"
12
+ import {
13
+ Card,
14
+ CardContent,
15
+ CardDescription,
16
+ CardFooter,
17
+ CardHeader,
18
+ CardTitle
19
+ } from "../ui/card"
20
+ import { Separator } from "../ui/separator"
21
+ import { AuthCallback } from "./auth-callback"
22
+ import { AuthForm, type AuthFormClassNames } from "./auth-form"
23
+ import { EmailOTPButton } from "./email-otp-button"
24
+ import { MagicLinkButton } from "./magic-link-button"
25
+ import { OneTap } from "./one-tap"
26
+ import { PasskeyButton } from "./passkey-button"
27
+ import { ProviderButton } from "./provider-button"
28
+ import { WalletButton } from "./wallet-button"
29
+ import { SignOut } from "./sign-out"
30
+
31
+ export type AuthViewClassNames = {
32
+ base?: string
33
+ content?: string
34
+ description?: string
35
+ footer?: string
36
+ footerLink?: string
37
+ continueWith?: string
38
+ form?: AuthFormClassNames
39
+ header?: string
40
+ separator?: string
41
+ title?: string
42
+ }
43
+
44
+ export interface AuthViewProps {
45
+ className?: string
46
+ classNames?: AuthViewClassNames
47
+ callbackURL?: string
48
+ cardHeader?: ReactNode
49
+ cardFooter?: ReactNode
50
+ localization?: AuthLocalization
51
+ path?: string
52
+ pathname?: string
53
+ redirectTo?: string
54
+ socialLayout?: "auto" | "horizontal" | "grid" | "vertical"
55
+ view?: keyof AuthViewPaths
56
+ otpSeparators?: 0 | 1 | 2
57
+ }
58
+
59
+ export function AuthView({
60
+ className,
61
+ classNames,
62
+ callbackURL,
63
+ cardHeader,
64
+ cardFooter,
65
+ localization,
66
+ path: pathProp,
67
+ pathname,
68
+ redirectTo,
69
+ socialLayout: socialLayoutProp = "auto",
70
+ view: viewProp,
71
+ otpSeparators = 0
72
+ }: AuthViewProps) {
73
+ const isHydrated = useIsHydrated()
74
+ const {
75
+ basePath,
76
+ credentials,
77
+ localization: contextLocalization,
78
+ magicLink,
79
+ emailOTP,
80
+ oneTap,
81
+ passkey,
82
+ wallet,
83
+ signUp,
84
+ social,
85
+ genericOAuth,
86
+ viewPaths,
87
+ Link
88
+ } = useContext(AuthUIContext)
89
+
90
+ localization = { ...contextLocalization, ...localization }
91
+
92
+ let socialLayout = socialLayoutProp
93
+ if (socialLayout === "auto") {
94
+ socialLayout = !credentials
95
+ ? "vertical"
96
+ : social?.providers && social.providers.length > 2
97
+ ? "horizontal"
98
+ : "vertical"
99
+ }
100
+
101
+ const path = pathProp ?? pathname?.split("/").pop()
102
+
103
+ const view = viewProp || getViewByPath(viewPaths!, path) || "SIGN_IN"
104
+
105
+ const [isSubmitting, setIsSubmitting] = useState(false)
106
+ const [providerError, setProviderError] = useState<string | null>(null)
107
+
108
+ // Clear provider error when starting a new submission
109
+ const handleSetIsSubmitting = (value: boolean) => {
110
+ if (value) setProviderError(null)
111
+ setIsSubmitting(value)
112
+ }
113
+
114
+ useEffect(() => {
115
+ const handlePageHide = () => setIsSubmitting(false)
116
+ window.addEventListener("pagehide", handlePageHide)
117
+ return () => {
118
+ setIsSubmitting(false)
119
+ window.removeEventListener("pagehide", handlePageHide)
120
+ }
121
+ }, [])
122
+
123
+ if (view === "CALLBACK") return <AuthCallback redirectTo={redirectTo} />
124
+ if (view === "SIGN_OUT") return <SignOut redirectTo={redirectTo} />
125
+
126
+ const description =
127
+ !credentials && !magicLink && !emailOTP
128
+ ? localization.DISABLED_CREDENTIALS_DESCRIPTION
129
+ : localization[`${view}_DESCRIPTION` as keyof typeof localization]
130
+
131
+ return (
132
+ <Card className={cn("w-full max-w-sm", className, classNames?.base)}>
133
+ <CardHeader className={classNames?.header}>
134
+ {cardHeader ? (
135
+ cardHeader
136
+ ) : (
137
+ <>
138
+ <CardTitle
139
+ className={cn(
140
+ "text-lg md:text-xl",
141
+ classNames?.title
142
+ )}
143
+ >
144
+ {localization[view as keyof typeof localization]}
145
+ </CardTitle>
146
+ {description && (
147
+ <CardDescription
148
+ className={cn(
149
+ "text-xs md:text-sm",
150
+ classNames?.description
151
+ )}
152
+ >
153
+ {description}
154
+ </CardDescription>
155
+ )}
156
+ </>
157
+ )}
158
+ </CardHeader>
159
+
160
+ <CardContent className={cn("grid gap-6", classNames?.content)}>
161
+ {oneTap &&
162
+ ["SIGN_IN", "SIGN_UP", "MAGIC_LINK", "EMAIL_OTP"].includes(
163
+ view as string
164
+ ) && (
165
+ <OneTap
166
+ localization={localization}
167
+ redirectTo={redirectTo}
168
+ />
169
+ )}
170
+
171
+ {(credentials || magicLink || emailOTP) && (
172
+ <div className="grid gap-4">
173
+ <AuthForm
174
+ classNames={classNames?.form}
175
+ callbackURL={callbackURL}
176
+ isSubmitting={isSubmitting}
177
+ localization={localization}
178
+ otpSeparators={otpSeparators}
179
+ view={view}
180
+ redirectTo={redirectTo}
181
+ setIsSubmitting={handleSetIsSubmitting}
182
+ />
183
+
184
+ {magicLink &&
185
+ ((credentials &&
186
+ [
187
+ "FORGOT_PASSWORD",
188
+ "SIGN_UP",
189
+ "SIGN_IN",
190
+ "MAGIC_LINK",
191
+ "EMAIL_OTP"
192
+ ].includes(view as string)) ||
193
+ (emailOTP && view === "EMAIL_OTP")) && (
194
+ <MagicLinkButton
195
+ classNames={classNames}
196
+ localization={localization}
197
+ view={view}
198
+ isSubmitting={isSubmitting}
199
+ />
200
+ )}
201
+
202
+ {emailOTP &&
203
+ ((credentials &&
204
+ [
205
+ "FORGOT_PASSWORD",
206
+ "SIGN_UP",
207
+ "SIGN_IN",
208
+ "MAGIC_LINK",
209
+ "EMAIL_OTP"
210
+ ].includes(view as string)) ||
211
+ (magicLink &&
212
+ ["SIGN_IN", "MAGIC_LINK"].includes(
213
+ view as string
214
+ ))) && (
215
+ <EmailOTPButton
216
+ classNames={classNames}
217
+ localization={localization}
218
+ view={view}
219
+ isSubmitting={isSubmitting}
220
+ />
221
+ )}
222
+ </div>
223
+ )}
224
+
225
+ {view !== "RESET_PASSWORD" &&
226
+ view !== "EMAIL_VERIFICATION" &&
227
+ (social?.providers?.length ||
228
+ genericOAuth?.providers?.length ||
229
+ (view === "SIGN_IN" && passkey)) && (
230
+ <>
231
+ {(credentials || magicLink || emailOTP) && (
232
+ <div
233
+ className={cn(
234
+ "flex items-center gap-2",
235
+ classNames?.continueWith
236
+ )}
237
+ >
238
+ <Separator
239
+ className={cn(
240
+ "!w-auto grow",
241
+ classNames?.separator
242
+ )}
243
+ />
244
+ <span className="flex-shrink-0 text-muted-foreground text-sm">
245
+ {localization.OR_CONTINUE_WITH}
246
+ </span>
247
+ <Separator
248
+ className={cn(
249
+ "!w-auto grow",
250
+ classNames?.separator
251
+ )}
252
+ />
253
+ </div>
254
+ )}
255
+
256
+ <div className="grid gap-4">
257
+ {(social?.providers?.length ||
258
+ genericOAuth?.providers?.length) && (
259
+ <div
260
+ className={cn(
261
+ "flex w-full items-center justify-between gap-4",
262
+ socialLayout === "horizontal" &&
263
+ "flex-wrap",
264
+ socialLayout === "vertical" &&
265
+ "flex-col",
266
+ socialLayout === "grid" &&
267
+ "grid grid-cols-2"
268
+ )}
269
+ >
270
+ {social?.providers?.map((provider) => {
271
+ const socialProvider =
272
+ socialProviders.find(
273
+ (socialProvider) =>
274
+ socialProvider.provider ===
275
+ provider
276
+ )
277
+ if (!socialProvider) return null
278
+ return (
279
+ <ProviderButton
280
+ key={provider}
281
+ classNames={classNames}
282
+ callbackURL={callbackURL}
283
+ isSubmitting={isSubmitting}
284
+ localization={localization}
285
+ onError={setProviderError}
286
+ provider={socialProvider}
287
+ redirectTo={redirectTo}
288
+ setIsSubmitting={
289
+ handleSetIsSubmitting
290
+ }
291
+ socialLayout={socialLayout}
292
+ />
293
+ )
294
+ })}
295
+ {genericOAuth?.providers?.map(
296
+ (provider) => (
297
+ <ProviderButton
298
+ key={provider.provider}
299
+ classNames={classNames}
300
+ callbackURL={callbackURL}
301
+ isSubmitting={isSubmitting}
302
+ localization={localization}
303
+ onError={setProviderError}
304
+ provider={provider}
305
+ redirectTo={redirectTo}
306
+ setIsSubmitting={
307
+ handleSetIsSubmitting
308
+ }
309
+ socialLayout={socialLayout}
310
+ other
311
+ />
312
+ )
313
+ )}
314
+ </div>
315
+ )}
316
+
317
+ {providerError && (
318
+ <div className="rounded-md bg-destructive/10 p-3 text-sm text-destructive">
319
+ {providerError}
320
+ </div>
321
+ )}
322
+
323
+ {passkey &&
324
+ [
325
+ "SIGN_IN",
326
+ "MAGIC_LINK",
327
+ "EMAIL_OTP",
328
+ "RECOVER_ACCOUNT",
329
+ "TWO_FACTOR",
330
+ "FORGOT_PASSWORD"
331
+ ].includes(view as string) && (
332
+ <PasskeyButton
333
+ classNames={classNames}
334
+ isSubmitting={isSubmitting}
335
+ localization={localization}
336
+ redirectTo={redirectTo}
337
+ setIsSubmitting={handleSetIsSubmitting}
338
+ />
339
+ )}
340
+
341
+ {wallet &&
342
+ [
343
+ "SIGN_IN",
344
+ "SIGN_UP",
345
+ "MAGIC_LINK",
346
+ "EMAIL_OTP"
347
+ ].includes(view as string) && (
348
+ <WalletButton
349
+ classNames={classNames}
350
+ isSubmitting={isSubmitting}
351
+ localization={localization}
352
+ redirectTo={redirectTo}
353
+ setIsSubmitting={handleSetIsSubmitting}
354
+ />
355
+ )}
356
+ </div>
357
+ </>
358
+ )}
359
+ </CardContent>
360
+
361
+ {cardFooter && (
362
+ <CardFooter className={classNames?.footer}>
363
+ {cardFooter}
364
+ </CardFooter>
365
+ )}
366
+
367
+ {credentials && signUp && (
368
+ <CardFooter
369
+ className={cn(
370
+ "justify-center gap-1.5 text-muted-foreground text-sm",
371
+ classNames?.footer
372
+ )}
373
+ >
374
+ {view === "SIGN_IN" ||
375
+ view === "MAGIC_LINK" ||
376
+ view === "EMAIL_OTP" ? (
377
+ localization.DONT_HAVE_AN_ACCOUNT
378
+ ) : view === "SIGN_UP" ? (
379
+ localization.ALREADY_HAVE_AN_ACCOUNT
380
+ ) : (
381
+ <ArrowLeftIcon className="size-3" />
382
+ )}
383
+
384
+ {view === "SIGN_IN" ||
385
+ view === "MAGIC_LINK" ||
386
+ view === "EMAIL_OTP" ||
387
+ view === "SIGN_UP" ? (
388
+ <Link
389
+ className={cn(
390
+ "text-foreground underline",
391
+ classNames?.footerLink
392
+ )}
393
+ href={`${basePath}/${
394
+ viewPaths[
395
+ view === "SIGN_IN" ||
396
+ view === "MAGIC_LINK" ||
397
+ view === "EMAIL_OTP"
398
+ ? "SIGN_UP"
399
+ : "SIGN_IN"
400
+ ]
401
+ }${isHydrated ? window.location.search : ""}`}
402
+ >
403
+ <Button
404
+ variant="link"
405
+ size="sm"
406
+ className={cn(
407
+ "px-0 text-foreground underline",
408
+ classNames?.footerLink
409
+ )}
410
+ >
411
+ {view === "SIGN_IN" ||
412
+ view === "MAGIC_LINK" ||
413
+ view === "EMAIL_OTP"
414
+ ? localization.SIGN_UP
415
+ : localization.SIGN_IN}
416
+ </Button>
417
+ </Link>
418
+ ) : (
419
+ <Button
420
+ variant="link"
421
+ size="sm"
422
+ className={cn(
423
+ "px-0 text-foreground underline",
424
+ classNames?.footerLink
425
+ )}
426
+ onClick={() => window.history.back()}
427
+ >
428
+ {localization.GO_BACK}
429
+ </Button>
430
+ )}
431
+ </CardFooter>
432
+ )}
433
+ </Card>
434
+ )
435
+ }
@@ -0,0 +1,53 @@
1
+ import { LockIcon, MailIcon } from "lucide-react"
2
+ import { useContext } from "react"
3
+
4
+ import { AuthUIContext } from "../../lib/auth-ui-provider"
5
+ import { cn } from "../../lib/utils"
6
+ import type { AuthViewPath } from "../../lib/view-paths"
7
+ import type { AuthLocalization } from "../../localization/auth-localization"
8
+ import { Button } from "../ui/button"
9
+ import type { AuthViewClassNames } from "./auth-view"
10
+
11
+ interface EmailOTPButtonProps {
12
+ classNames?: AuthViewClassNames
13
+ isSubmitting?: boolean
14
+ localization: Partial<AuthLocalization>
15
+ view: AuthViewPath
16
+ }
17
+
18
+ export function EmailOTPButton({
19
+ classNames,
20
+ isSubmitting,
21
+ localization,
22
+ view
23
+ }: EmailOTPButtonProps) {
24
+ const { viewPaths, navigate, basePath } = useContext(AuthUIContext)
25
+
26
+ return (
27
+ <Button
28
+ className={cn(
29
+ "w-full",
30
+ classNames?.form?.button,
31
+ classNames?.form?.secondaryButton
32
+ )}
33
+ disabled={isSubmitting}
34
+ type="button"
35
+ variant="secondary"
36
+ onClick={() =>
37
+ navigate(
38
+ `${basePath}/${view === "EMAIL_OTP" ? viewPaths.SIGN_IN : viewPaths.EMAIL_OTP}${window.location.search}`
39
+ )
40
+ }
41
+ >
42
+ {view === "EMAIL_OTP" ? (
43
+ <LockIcon className={classNames?.form?.icon} />
44
+ ) : (
45
+ <MailIcon className={classNames?.form?.icon} />
46
+ )}
47
+ {localization.SIGN_IN_WITH}{" "}
48
+ {view === "EMAIL_OTP"
49
+ ? localization.PASSWORD
50
+ : localization.EMAIL_OTP}
51
+ </Button>
52
+ )
53
+ }