@insforge/react 0.4.8 → 0.4.10
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 +55 -95
- package/dist/atoms.cjs +31 -46
- package/dist/atoms.cjs.map +1 -1
- package/dist/atoms.d.cts +3 -2
- package/dist/atoms.d.ts +3 -2
- package/dist/atoms.js +31 -46
- package/dist/atoms.js.map +1 -1
- package/dist/components.cjs +71 -59
- package/dist/components.cjs.map +1 -1
- package/dist/components.js +71 -59
- package/dist/components.js.map +1 -1
- package/dist/forms.cjs +53 -55
- package/dist/forms.cjs.map +1 -1
- package/dist/forms.d.cts +2 -4
- package/dist/forms.d.ts +2 -4
- package/dist/forms.js +53 -55
- package/dist/forms.js.map +1 -1
- package/dist/hooks.cjs.map +1 -1
- package/dist/hooks.js.map +1 -1
- package/dist/index.cjs +80 -63
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -4
- package/dist/index.d.ts +1 -4
- package/dist/index.js +80 -63
- package/dist/index.js.map +1 -1
- package/dist/styles.css +0 -8
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
✅ **5-Minute Setup** - One provider + one line of router config = done
|
|
8
8
|
✅ **Built-in Auth UI** - Use deployed auth pages (like Next.js middleware)
|
|
9
9
|
✅ **Framework Agnostic** - Works with any React framework
|
|
10
|
-
✅ **Full TypeScript** - Complete type safety out of the box
|
|
10
|
+
✅ **Full TypeScript** - Complete type safety out of the box
|
|
11
11
|
|
|
12
12
|
---
|
|
13
13
|
|
|
@@ -25,6 +25,15 @@ yarn add @insforge/react
|
|
|
25
25
|
pnpm add @insforge/react
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
+
**Required Dependencies:** `bash npm install react@^19.0.0 react-dom@^19.0.0
|
|
29
|
+
react-router-dom@^7.9.0 `
|
|
30
|
+
|
|
31
|
+
#### Environment Variables
|
|
32
|
+
|
|
33
|
+
```bash .env
|
|
34
|
+
VITE_INSFORGE_BASE_URL=https://your-project.insforge.app/
|
|
35
|
+
```
|
|
36
|
+
|
|
28
37
|
### 2. Setup Provider
|
|
29
38
|
|
|
30
39
|
Wrap your app with `InsforgeProvider`:
|
|
@@ -34,39 +43,45 @@ Wrap your app with `InsforgeProvider`:
|
|
|
34
43
|
import { StrictMode } from 'react';
|
|
35
44
|
import { createRoot } from 'react-dom/client';
|
|
36
45
|
import { InsforgeProvider } from '@insforge/react';
|
|
37
|
-
import '@insforge/react/styles.css';
|
|
38
46
|
import App from './App';
|
|
39
47
|
|
|
40
48
|
createRoot(document.getElementById('root')!).render(
|
|
41
49
|
<StrictMode>
|
|
42
|
-
<InsforgeProvider
|
|
43
|
-
baseUrl={import.meta.env.VITE_INSFORGE_BASE_URL}
|
|
44
|
-
afterSignInUrl="/dashboard"
|
|
45
|
-
>
|
|
50
|
+
<InsforgeProvider baseUrl={import.meta.env.VITE_INSFORGE_BASE_URL} afterSignInUrl="/dashboard">
|
|
46
51
|
<App />
|
|
47
52
|
</InsforgeProvider>
|
|
48
53
|
</StrictMode>
|
|
49
54
|
);
|
|
50
55
|
```
|
|
51
56
|
|
|
52
|
-
### 3
|
|
57
|
+
### Step 3: Configure Router (Built-in Auth)
|
|
53
58
|
|
|
54
|
-
|
|
55
|
-
|
|
59
|
+
**The fastest way** - Use deployed auth pages with Layout pattern:
|
|
60
|
+
|
|
61
|
+
```tsx src/App.tsx
|
|
56
62
|
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
|
|
57
63
|
import { getInsforgeRoutes } from '@insforge/react/router';
|
|
64
|
+
import Layout from './components/Layout';
|
|
58
65
|
import Home from './pages/Home';
|
|
59
66
|
import Dashboard from './pages/Dashboard';
|
|
60
67
|
|
|
61
68
|
const router = createBrowserRouter([
|
|
62
|
-
{
|
|
63
|
-
|
|
69
|
+
{
|
|
70
|
+
path: '/',
|
|
71
|
+
element: <Layout />,
|
|
72
|
+
children: [
|
|
73
|
+
{ index: true, element: <Home /> },
|
|
74
|
+
|
|
75
|
+
{ path: 'dashboard', element: <Dashboard /> }
|
|
76
|
+
]
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
// Add built-in auth redirect routes (sign-in, sign-up, forgot-password, etc.)
|
|
64
80
|
...getInsforgeRoutes({
|
|
65
81
|
baseUrl: import.meta.env.VITE_INSFORGE_BASE_URL,
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
{ path: '/dashboard', element: <Dashboard /> }
|
|
82
|
+
afterSignInUrl="/dashboard"
|
|
83
|
+
builtInAuth: true // Redirects to deployed auth pages
|
|
84
|
+
})
|
|
70
85
|
]);
|
|
71
86
|
|
|
72
87
|
export default function App() {
|
|
@@ -75,17 +90,22 @@ export default function App() {
|
|
|
75
90
|
```
|
|
76
91
|
|
|
77
92
|
**What this does:**
|
|
93
|
+
|
|
78
94
|
- Visiting `/sign-in` → Redirects to `your-project.insforge.app/auth/sign-in`
|
|
79
95
|
- Visiting `/sign-up` → Redirects to `your-project.insforge.app/auth/sign-up`
|
|
96
|
+
- Visiting `/forgot-password` → Redirects to `your-project.insforge.app/auth/forgot-password`
|
|
80
97
|
- After auth → Backend redirects with token in URL → SDK auto-detects and stores token
|
|
81
98
|
|
|
82
|
-
### 4.
|
|
99
|
+
### 4. Use Hooks & Components
|
|
83
100
|
|
|
84
101
|
```tsx
|
|
85
102
|
// src/pages/Home.tsx
|
|
86
|
-
import { SignedIn, SignedOut, UserButton } from '@insforge/react';
|
|
103
|
+
import { SignedIn, SignedOut, UserButton, useAuth, useUser } from '@insforge/react';
|
|
87
104
|
|
|
88
105
|
export default function Home() {
|
|
106
|
+
const { isSignedIn } = useAuth();
|
|
107
|
+
const { user } = useUser();
|
|
108
|
+
|
|
89
109
|
return (
|
|
90
110
|
<div>
|
|
91
111
|
<nav>
|
|
@@ -95,6 +115,7 @@ export default function Home() {
|
|
|
95
115
|
|
|
96
116
|
<SignedIn>
|
|
97
117
|
<UserButton afterSignOutUrl="/" />
|
|
118
|
+
<h1>Welcome, {user?.email}!</h1>
|
|
98
119
|
</SignedIn>
|
|
99
120
|
</nav>
|
|
100
121
|
|
|
@@ -121,9 +142,7 @@ Uses your deployed Insforge auth pages (includes all flows):
|
|
|
121
142
|
paths: {
|
|
122
143
|
signIn: '/sign-in', // Sign in page
|
|
123
144
|
signUp: '/sign-up', // Sign up page
|
|
124
|
-
verifyEmail: '/verify-email', // Email verification page
|
|
125
145
|
forgotPassword: '/forgot-password', // Request password reset
|
|
126
|
-
resetPassword: '/reset-password' // Reset password with token
|
|
127
146
|
}
|
|
128
147
|
})
|
|
129
148
|
```
|
|
@@ -133,23 +152,15 @@ Uses your deployed Insforge auth pages (includes all flows):
|
|
|
133
152
|
Use package components with your own styling:
|
|
134
153
|
|
|
135
154
|
```tsx
|
|
136
|
-
import {
|
|
137
|
-
SignIn,
|
|
138
|
-
SignUp,
|
|
139
|
-
ForgotPassword,
|
|
140
|
-
ResetPassword,
|
|
141
|
-
VerifyEmail
|
|
142
|
-
} from '@insforge/react';
|
|
155
|
+
import { SignIn, SignUp, ForgotPassword, ResetPassword, VerifyEmail } from '@insforge/react';
|
|
143
156
|
|
|
144
157
|
const router = createBrowserRouter([
|
|
145
158
|
{ path: '/', element: <Home /> },
|
|
146
|
-
|
|
159
|
+
|
|
147
160
|
// Use package components for complete auth flows
|
|
148
161
|
{ path: '/sign-in', element: <SignIn afterSignInUrl="/dashboard" /> },
|
|
149
162
|
{ path: '/sign-up', element: <SignUp afterSignUpUrl="/dashboard" /> },
|
|
150
|
-
{ path: '/forgot-password', element: <ForgotPassword
|
|
151
|
-
{ path: '/reset-password', element: <ResetPassword token={searchParams.get('token')} /> },
|
|
152
|
-
{ path: '/verify-email', element: <VerifyEmail token={searchParams.get('token')} /> }
|
|
163
|
+
{ path: '/forgot-password', element: <ForgotPassword /> },
|
|
153
164
|
]);
|
|
154
165
|
```
|
|
155
166
|
|
|
@@ -162,13 +173,13 @@ import { useAuth } from '@insforge/react';
|
|
|
162
173
|
|
|
163
174
|
function CustomSignIn() {
|
|
164
175
|
const { signIn } = useAuth();
|
|
165
|
-
|
|
176
|
+
|
|
166
177
|
const handleSubmit = async (e) => {
|
|
167
178
|
e.preventDefault();
|
|
168
179
|
await signIn(email, password);
|
|
169
180
|
navigate('/dashboard');
|
|
170
181
|
};
|
|
171
|
-
|
|
182
|
+
|
|
172
183
|
return <form onSubmit={handleSubmit}>...</form>;
|
|
173
184
|
}
|
|
174
185
|
```
|
|
@@ -180,6 +191,7 @@ function CustomSignIn() {
|
|
|
180
191
|
### Components
|
|
181
192
|
|
|
182
193
|
**Pre-built with Business Logic:**
|
|
194
|
+
|
|
183
195
|
- `<SignIn />` - Complete sign-in with email/password & OAuth
|
|
184
196
|
- `<SignUp />` - Registration with password validation & email verification
|
|
185
197
|
- `<ForgotPassword />` - Request password reset with email validation
|
|
@@ -190,6 +202,7 @@ function CustomSignIn() {
|
|
|
190
202
|
- `<SignedIn>` / `<SignedOut>` - Conditional rendering
|
|
191
203
|
|
|
192
204
|
**Form Components (Pure UI):**
|
|
205
|
+
|
|
193
206
|
- `<SignInForm />` - Sign-in UI without logic
|
|
194
207
|
- `<SignUpForm />` - Sign-up UI without logic
|
|
195
208
|
- `<ForgotPasswordForm />` - Password reset request UI
|
|
@@ -197,6 +210,7 @@ function CustomSignIn() {
|
|
|
197
210
|
- `<VerifyEmailStatus />` - Email verification status UI
|
|
198
211
|
|
|
199
212
|
**Atomic Components (14 total):**
|
|
213
|
+
|
|
200
214
|
- `<AuthContainer />`, `<AuthHeader />`, `<AuthFormField />`, `<AuthPasswordField />`, `<AuthEmailVerificationStep />`, etc.
|
|
201
215
|
|
|
202
216
|
### Hooks
|
|
@@ -239,6 +253,7 @@ All components support full text customization:
|
|
|
239
253
|
successMessage="We've sent a reset code to your inbox"
|
|
240
254
|
/>
|
|
241
255
|
```
|
|
256
|
+
|
|
242
257
|
---
|
|
243
258
|
|
|
244
259
|
## Advanced Usage
|
|
@@ -260,7 +275,7 @@ function CustomSignIn() {
|
|
|
260
275
|
e.preventDefault();
|
|
261
276
|
setLoading(true);
|
|
262
277
|
setError('');
|
|
263
|
-
|
|
278
|
+
|
|
264
279
|
try {
|
|
265
280
|
await signIn(email, password);
|
|
266
281
|
// Custom success logic
|
|
@@ -282,7 +297,7 @@ function CustomSignIn() {
|
|
|
282
297
|
loading={loading}
|
|
283
298
|
availableProviders={['google', 'github']}
|
|
284
299
|
onOAuthClick={(provider) => {
|
|
285
|
-
|
|
300
|
+
console.log(provider);
|
|
286
301
|
}}
|
|
287
302
|
/>
|
|
288
303
|
);
|
|
@@ -307,10 +322,7 @@ import {
|
|
|
307
322
|
function CompletelyCustomAuth() {
|
|
308
323
|
return (
|
|
309
324
|
<AuthContainer>
|
|
310
|
-
<AuthHeader
|
|
311
|
-
title="Welcome to MyApp"
|
|
312
|
-
subtitle="Sign in to continue"
|
|
313
|
-
/>
|
|
325
|
+
<AuthHeader title="Welcome to MyApp" subtitle="Sign in to continue" />
|
|
314
326
|
|
|
315
327
|
<AuthErrorBanner error={error} />
|
|
316
328
|
|
|
@@ -332,9 +344,7 @@ function CompletelyCustomAuth() {
|
|
|
332
344
|
showStrengthIndicator
|
|
333
345
|
/>
|
|
334
346
|
|
|
335
|
-
<AuthSubmitButton isLoading={loading}>
|
|
336
|
-
Sign In
|
|
337
|
-
</AuthSubmitButton>
|
|
347
|
+
<AuthSubmitButton isLoading={loading}>Sign In</AuthSubmitButton>
|
|
338
348
|
</form>
|
|
339
349
|
|
|
340
350
|
<AuthDivider text="or" />
|
|
@@ -345,11 +355,7 @@ function CompletelyCustomAuth() {
|
|
|
345
355
|
loading={oauthLoading}
|
|
346
356
|
/>
|
|
347
357
|
|
|
348
|
-
<AuthLink
|
|
349
|
-
text="Don't have an account?"
|
|
350
|
-
linkText="Sign up"
|
|
351
|
-
href="/sign-up"
|
|
352
|
-
/>
|
|
358
|
+
<AuthLink text="Don't have an account?" linkText="Sign up" href="/sign-up" />
|
|
353
359
|
</AuthContainer>
|
|
354
360
|
);
|
|
355
361
|
}
|
|
@@ -364,17 +370,14 @@ function Dashboard() {
|
|
|
364
370
|
return (
|
|
365
371
|
<div>
|
|
366
372
|
<h1>Dashboard</h1>
|
|
367
|
-
|
|
373
|
+
|
|
368
374
|
{/* Simple protection */}
|
|
369
375
|
<Protect redirectTo="/sign-in">
|
|
370
376
|
<UserContent />
|
|
371
377
|
</Protect>
|
|
372
378
|
|
|
373
379
|
{/* Role-based protection */}
|
|
374
|
-
<Protect
|
|
375
|
-
redirectTo="/unauthorized"
|
|
376
|
-
condition={(user) => user.role === 'admin'}
|
|
377
|
-
>
|
|
380
|
+
<Protect redirectTo="/unauthorized" condition={(user) => user.role === 'admin'}>
|
|
378
381
|
<AdminPanel />
|
|
379
382
|
</Protect>
|
|
380
383
|
</div>
|
|
@@ -384,39 +387,10 @@ function Dashboard() {
|
|
|
384
387
|
|
|
385
388
|
---
|
|
386
389
|
|
|
387
|
-
## TypeScript
|
|
388
|
-
|
|
389
|
-
Full TypeScript support with exported types:
|
|
390
|
-
|
|
391
|
-
```tsx
|
|
392
|
-
import type {
|
|
393
|
-
InsforgeUser,
|
|
394
|
-
SignInProps,
|
|
395
|
-
SignUpProps,
|
|
396
|
-
ForgotPasswordProps,
|
|
397
|
-
ResetPasswordProps,
|
|
398
|
-
VerifyEmailProps,
|
|
399
|
-
UserButtonProps,
|
|
400
|
-
ProtectProps,
|
|
401
|
-
ConditionalProps,
|
|
402
|
-
SignInFormProps,
|
|
403
|
-
SignUpFormProps,
|
|
404
|
-
ForgotPasswordFormProps,
|
|
405
|
-
ResetPasswordFormProps,
|
|
406
|
-
VerifyEmailStatusProps,
|
|
407
|
-
AuthFormFieldProps,
|
|
408
|
-
OAuthProvider,
|
|
409
|
-
AuthConfig,
|
|
410
|
-
InsforgeProviderProps,
|
|
411
|
-
GetInsforgeRoutesConfig,
|
|
412
|
-
} from '@insforge/react';
|
|
413
|
-
```
|
|
414
|
-
|
|
415
|
-
---
|
|
416
|
-
|
|
417
390
|
## OAuth Providers
|
|
418
391
|
|
|
419
392
|
Built-in support for 10+ OAuth providers:
|
|
393
|
+
|
|
420
394
|
- Google
|
|
421
395
|
- GitHub
|
|
422
396
|
- Discord
|
|
@@ -433,20 +407,6 @@ Providers are auto-detected from your backend configuration.
|
|
|
433
407
|
|
|
434
408
|
---
|
|
435
409
|
|
|
436
|
-
## Validation Utilities
|
|
437
|
-
|
|
438
|
-
```tsx
|
|
439
|
-
import { emailSchema, cn } from '@insforge/react/lib';
|
|
440
|
-
|
|
441
|
-
// Validate email with Zod
|
|
442
|
-
const result = emailSchema.safeParse('user@example.com');
|
|
443
|
-
|
|
444
|
-
// Merge Tailwind classes
|
|
445
|
-
const className = cn('px-4 py-2', 'bg-blue-500', conditionalClass);
|
|
446
|
-
```
|
|
447
|
-
|
|
448
|
-
---
|
|
449
|
-
|
|
450
410
|
## Available Atomic Components
|
|
451
411
|
|
|
452
412
|
Low-level building blocks for complete customization:
|
package/dist/atoms.cjs
CHANGED
|
@@ -108,34 +108,27 @@ function AuthPasswordStrengthIndicator({
|
|
|
108
108
|
config
|
|
109
109
|
}) {
|
|
110
110
|
const requirements = createRequirements(config);
|
|
111
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "if-passwordStrength if-internal-ps6w3k", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "if-passwordStrength-requirements", children: requirements.map((req) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
"
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
}
|
|
133
|
-
),
|
|
134
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: req.label })
|
|
135
|
-
]
|
|
136
|
-
},
|
|
137
|
-
req.label
|
|
138
|
-
)) }) });
|
|
111
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "if-passwordStrength if-internal-ps6w3k", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "if-passwordStrength-requirements", children: requirements.map((req) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "if-passwordStrength-requirement", children: [
|
|
112
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
113
|
+
"div",
|
|
114
|
+
{
|
|
115
|
+
style: {
|
|
116
|
+
display: "flex",
|
|
117
|
+
alignItems: "center",
|
|
118
|
+
justifyContent: "center",
|
|
119
|
+
width: "1rem",
|
|
120
|
+
height: "1rem",
|
|
121
|
+
borderRadius: "50%",
|
|
122
|
+
border: "2px solid",
|
|
123
|
+
borderColor: req.test(password) ? "transparent" : "#9ca3af",
|
|
124
|
+
backgroundColor: req.test(password) ? "#059669" : "white",
|
|
125
|
+
transition: "all 0.2s"
|
|
126
|
+
},
|
|
127
|
+
children: req.test(password) && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { style: { width: "0.75rem", height: "0.75rem", color: "white" } })
|
|
128
|
+
}
|
|
129
|
+
),
|
|
130
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: req.label })
|
|
131
|
+
] }, req.label)) }) });
|
|
139
132
|
}
|
|
140
133
|
function createRequirements(config) {
|
|
141
134
|
const requirements = [];
|
|
@@ -203,11 +196,12 @@ function AuthPasswordField({
|
|
|
203
196
|
onFocus,
|
|
204
197
|
...props
|
|
205
198
|
}) {
|
|
199
|
+
const [searchParams] = reactRouterDom.useSearchParams();
|
|
206
200
|
const [showPassword, setShowPassword] = react.useState(false);
|
|
207
201
|
const [showStrength, setShowStrength] = react.useState(false);
|
|
208
202
|
const resolvedForgotPasswordHref = react.useMemo(
|
|
209
|
-
() => forgotPasswordLink ?
|
|
210
|
-
[forgotPasswordLink]
|
|
203
|
+
() => forgotPasswordLink ? resolveAuthUrl(forgotPasswordLink.href, searchParams) : void 0,
|
|
204
|
+
[forgotPasswordLink, searchParams]
|
|
211
205
|
);
|
|
212
206
|
const handleFocus = (e) => {
|
|
213
207
|
if (showStrengthIndicator) {
|
|
@@ -622,29 +616,20 @@ function useInsforge() {
|
|
|
622
616
|
}
|
|
623
617
|
function AuthEmailVerificationStep({
|
|
624
618
|
email,
|
|
625
|
-
method
|
|
626
|
-
onVerifyCode
|
|
619
|
+
method,
|
|
620
|
+
onVerifyCode,
|
|
621
|
+
emailSent = false
|
|
627
622
|
}) {
|
|
628
623
|
const { sendVerificationEmail } = useInsforge();
|
|
629
624
|
const [resendDisabled, setResendDisabled] = react.useState(true);
|
|
630
|
-
const [resendCountdown, setResendCountdown] = react.useState(60);
|
|
625
|
+
const [resendCountdown, setResendCountdown] = react.useState(emailSent ? 60 : 0);
|
|
631
626
|
const [isSending, setIsSending] = react.useState(false);
|
|
632
627
|
const [verificationCode, setVerificationCode] = react.useState("");
|
|
633
628
|
const [isVerifying, setIsVerifying] = react.useState(false);
|
|
634
629
|
const isLinkMethod = method === "link";
|
|
635
|
-
const displayDescription = isLinkMethod ? "We
|
|
630
|
+
const displayDescription = isLinkMethod ? "We've sent an email to {email}. Please check your email to confirm your account before signing in. The confirmation link expires in 10 minutes." : "We've sent a verification code to your inbox at {email}. Enter it below to proceed.";
|
|
636
631
|
react.useEffect(() => {
|
|
637
|
-
|
|
638
|
-
try {
|
|
639
|
-
await sendVerificationEmail(email);
|
|
640
|
-
} catch (error) {
|
|
641
|
-
console.error("Failed to send verification email:", error);
|
|
642
|
-
}
|
|
643
|
-
};
|
|
644
|
-
void sendInitialEmail();
|
|
645
|
-
}, [email, sendVerificationEmail]);
|
|
646
|
-
react.useEffect(() => {
|
|
647
|
-
if (resendCountdown > 0) {
|
|
632
|
+
if (emailSent && resendCountdown > 0) {
|
|
648
633
|
const timer = setInterval(() => {
|
|
649
634
|
setResendCountdown((prev) => {
|
|
650
635
|
if (prev <= 1) {
|
|
@@ -656,7 +641,7 @@ function AuthEmailVerificationStep({
|
|
|
656
641
|
}, 1e3);
|
|
657
642
|
return () => clearInterval(timer);
|
|
658
643
|
}
|
|
659
|
-
}, [resendCountdown]);
|
|
644
|
+
}, [emailSent, resendCountdown]);
|
|
660
645
|
const handleResend = async () => {
|
|
661
646
|
setResendDisabled(true);
|
|
662
647
|
setResendCountdown(60);
|