@cosmicdrift/kumiko-samples 0.341.0 → 0.342.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/package.json +1 -1
- package/packages/bundled-features/package.json +1 -1
- package/packages/bundled-features/src/audit/constants.ts +5 -0
- package/packages/bundled-features/src/audit/feature.ts +47 -6
- package/packages/bundled-features/src/audit/run-escape-hatch-retention.ts +53 -0
- package/packages/bundled-features/src/auth-email-password/bootstrap.ts +18 -2
- package/packages/bundled-features/src/auth-email-password/changes.json +12 -0
- package/packages/bundled-features/src/auth-email-password/web/auth-form-primitives.tsx +24 -6
- package/packages/bundled-features/src/auth-email-password/web/confirm-account-unlock-screen.tsx +12 -18
- package/packages/bundled-features/src/auth-email-password/web/forgot-password-screen.tsx +4 -4
- package/packages/bundled-features/src/auth-email-password/web/invite-accept-screen.tsx +65 -69
- package/packages/bundled-features/src/auth-email-password/web/login-screen.tsx +93 -95
- package/packages/bundled-features/src/auth-email-password/web/request-account-unlock-screen.tsx +4 -4
- package/packages/bundled-features/src/auth-email-password/web/reset-password-screen.tsx +8 -10
- package/packages/bundled-features/src/auth-email-password/web/session-bootstrap-error.tsx +13 -15
- package/packages/bundled-features/src/auth-email-password/web/signup-complete-screen.tsx +43 -49
- package/packages/bundled-features/src/auth-email-password/web/signup-screen.tsx +4 -4
- package/packages/bundled-features/src/auth-email-password/web/verify-email-screen.tsx +12 -18
- package/packages/bundled-features/src/auth-mfa/web/mfa-setup-preauth-screen.tsx +83 -85
- package/packages/bundled-features/src/auth-mfa/web/mfa-verify-screen.tsx +24 -26
- package/packages/bundled-features/src/channel-email/changes.json +6 -0
- package/packages/bundled-features/src/channel-email/email-channel.ts +8 -1
- package/packages/bundled-features/src/channel-email/pii-guard.ts +6 -5
- package/packages/bundled-features/src/channel-email/smtp-transport.ts +27 -1
- package/packages/bundled-features/src/channel-email/types.ts +4 -0
- package/packages/bundled-features/src/waitlist/changes.json +6 -0
- package/packages/bundled-features/src/waitlist/handlers/submit.write.ts +1 -1
- package/packages/bundled-features/src/waitlist/options.ts +3 -2
- package/samples/apps/use-all-bundled/feature-manifest.json +27 -2
|
@@ -195,104 +195,102 @@ export function LoginScreen({
|
|
|
195
195
|
|
|
196
196
|
return (
|
|
197
197
|
<AuthCard title={effectiveTitle} subtitle={subtitle}>
|
|
198
|
-
<
|
|
199
|
-
<
|
|
200
|
-
<
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
<
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
<
|
|
231
|
-
<
|
|
232
|
-
|
|
233
|
-
{(
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
email === failedLoginEmail && (
|
|
241
|
-
<span className="self-start">
|
|
242
|
-
<Button
|
|
243
|
-
variant="link"
|
|
244
|
-
onClick={() => void onResend()}
|
|
245
|
-
disabled={resendStatus.kind === "sending"}
|
|
246
|
-
>
|
|
247
|
-
{resendStatus.kind === "sending"
|
|
248
|
-
? t("auth.login.submitting")
|
|
249
|
-
: t("auth.login.resendVerification")}
|
|
250
|
-
</Button>
|
|
251
|
-
</span>
|
|
252
|
-
)}
|
|
253
|
-
{error.reason === "account_locked" && unlockAccountHref !== undefined && (
|
|
198
|
+
<Form onSubmit={onSubmit}>
|
|
199
|
+
<Field id="login-email" label={t("auth.login.email")} required>
|
|
200
|
+
<Input
|
|
201
|
+
kind="email"
|
|
202
|
+
id="login-email"
|
|
203
|
+
name="login-email"
|
|
204
|
+
value={email}
|
|
205
|
+
onChange={setEmail}
|
|
206
|
+
disabled={submitting}
|
|
207
|
+
required
|
|
208
|
+
autoComplete="email"
|
|
209
|
+
/>
|
|
210
|
+
</Field>
|
|
211
|
+
<Field id="login-password" label={t("auth.login.password")} required>
|
|
212
|
+
<Input
|
|
213
|
+
kind="password"
|
|
214
|
+
id="login-password"
|
|
215
|
+
name="login-password"
|
|
216
|
+
value={password}
|
|
217
|
+
onChange={setPassword}
|
|
218
|
+
disabled={submitting}
|
|
219
|
+
required
|
|
220
|
+
autoComplete="current-password"
|
|
221
|
+
/>
|
|
222
|
+
</Field>
|
|
223
|
+
{session.signedOutReason === "session-ended" && (
|
|
224
|
+
<Banner variant="info">{t("auth.login.sessionEnded")}</Banner>
|
|
225
|
+
)}
|
|
226
|
+
{resendStatus.kind === "success" ? (
|
|
227
|
+
<Banner variant="info">{t("auth.login.resendSuccess")}</Banner>
|
|
228
|
+
) : error !== null ? (
|
|
229
|
+
<Banner variant="error">
|
|
230
|
+
<div className="flex flex-col gap-1">
|
|
231
|
+
<span>
|
|
232
|
+
{(() => {
|
|
233
|
+
const { key, params } = reasonToKey(error);
|
|
234
|
+
return t(key, params);
|
|
235
|
+
})()}
|
|
236
|
+
</span>
|
|
237
|
+
{error.reason === "email_not_verified" &&
|
|
238
|
+
email.trim().length > 0 &&
|
|
239
|
+
email === failedLoginEmail && (
|
|
254
240
|
<span className="self-start">
|
|
255
|
-
<
|
|
256
|
-
|
|
257
|
-
|
|
241
|
+
<Button
|
|
242
|
+
variant="link"
|
|
243
|
+
onClick={() => void onResend()}
|
|
244
|
+
disabled={resendStatus.kind === "sending"}
|
|
245
|
+
>
|
|
246
|
+
{resendStatus.kind === "sending"
|
|
247
|
+
? t("auth.login.submitting")
|
|
248
|
+
: t("auth.login.resendVerification")}
|
|
249
|
+
</Button>
|
|
258
250
|
</span>
|
|
259
251
|
)}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
252
|
+
{error.reason === "account_locked" && unlockAccountHref !== undefined && (
|
|
253
|
+
<span className="self-start">
|
|
254
|
+
<Link href={unlockAccountHref} variant="default">
|
|
255
|
+
{t("auth.login.unlockAccount")}
|
|
256
|
+
</Link>
|
|
257
|
+
</span>
|
|
258
|
+
)}
|
|
259
|
+
{resendStatus.kind === "rateLimited" && (
|
|
260
|
+
<span className="text-xs">{t("auth.login.resendRateLimited")}</span>
|
|
261
|
+
)}
|
|
262
|
+
{resendStatus.kind === "error" && (
|
|
263
|
+
<span className="text-xs">{t("auth.login.resendError")}</span>
|
|
264
|
+
)}
|
|
265
|
+
</div>
|
|
266
|
+
</Banner>
|
|
267
|
+
) : null}
|
|
268
|
+
<Button type="submit" loading={submitting} disabled={submitting}>
|
|
269
|
+
{submitting ? t("auth.login.submitting") : effectiveSubmit}
|
|
270
|
+
</Button>
|
|
271
|
+
</Form>
|
|
272
|
+
{forgotPasswordHref !== undefined && (
|
|
273
|
+
<Link href={forgotPasswordHref} variant="muted" className="self-center">
|
|
274
|
+
{t("auth.login.forgotPassword")}
|
|
275
|
+
</Link>
|
|
276
|
+
)}
|
|
277
|
+
{signupHref !== undefined && (
|
|
278
|
+
<Link href={signupHref} variant="muted" className="self-center">
|
|
279
|
+
{t("auth.signup.title")}
|
|
280
|
+
</Link>
|
|
281
|
+
)}
|
|
282
|
+
{legalLinks !== undefined && legalLinks.length > 0 && (
|
|
283
|
+
<nav
|
|
284
|
+
data-testid="login-legal-links"
|
|
285
|
+
className="flex items-center justify-center gap-3 pt-2 border-t border-border/50"
|
|
286
|
+
>
|
|
287
|
+
{legalLinks.map((link) => (
|
|
288
|
+
<Link key={link.href} href={link.href} variant="muted" className="text-xs">
|
|
289
|
+
{link.label}
|
|
290
|
+
</Link>
|
|
291
|
+
))}
|
|
292
|
+
</nav>
|
|
293
|
+
)}
|
|
296
294
|
</AuthCard>
|
|
297
295
|
);
|
|
298
296
|
}
|
package/packages/bundled-features/src/auth-email-password/web/request-account-unlock-screen.tsx
CHANGED
|
@@ -73,7 +73,7 @@ export function RequestAccountUnlockScreen({
|
|
|
73
73
|
return (
|
|
74
74
|
<AuthCard title={effectiveTitle} subtitle={subtitle}>
|
|
75
75
|
{done ? (
|
|
76
|
-
|
|
76
|
+
<>
|
|
77
77
|
<Banner variant="info">
|
|
78
78
|
<p className="font-medium text-foreground">{t("auth.requestUnlock.successTitle")}</p>
|
|
79
79
|
<p className="mt-1">{t("auth.requestUnlock.successBody")}</p>
|
|
@@ -81,9 +81,9 @@ export function RequestAccountUnlockScreen({
|
|
|
81
81
|
<Link href={loginHref} variant="muted">
|
|
82
82
|
{t("auth.requestUnlock.backToLogin")}
|
|
83
83
|
</Link>
|
|
84
|
-
|
|
84
|
+
</>
|
|
85
85
|
) : (
|
|
86
|
-
|
|
86
|
+
<>
|
|
87
87
|
<p className="text-sm text-muted-foreground">{t("auth.requestUnlock.intro")}</p>
|
|
88
88
|
<Form onSubmit={onSubmit}>
|
|
89
89
|
<Field id="unlock-email" label={t("auth.requestUnlock.email")} required>
|
|
@@ -105,7 +105,7 @@ export function RequestAccountUnlockScreen({
|
|
|
105
105
|
<Link href={loginHref} variant="muted" className="self-center">
|
|
106
106
|
{t("auth.requestUnlock.backToLogin")}
|
|
107
107
|
</Link>
|
|
108
|
-
|
|
108
|
+
</>
|
|
109
109
|
)}
|
|
110
110
|
</AuthCard>
|
|
111
111
|
);
|
|
@@ -83,12 +83,10 @@ export function ResetPasswordScreen({
|
|
|
83
83
|
if (token === "") {
|
|
84
84
|
return (
|
|
85
85
|
<AuthCard title={effectiveTitle}>
|
|
86
|
-
<
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
</Link>
|
|
91
|
-
</div>
|
|
86
|
+
<p className="text-sm text-muted-foreground">{t("auth.resetPassword.missingToken")}</p>
|
|
87
|
+
<Link href={loginHref} variant="muted">
|
|
88
|
+
{t("auth.resetPassword.goToLogin")}
|
|
89
|
+
</Link>
|
|
92
90
|
</AuthCard>
|
|
93
91
|
);
|
|
94
92
|
}
|
|
@@ -96,7 +94,7 @@ export function ResetPasswordScreen({
|
|
|
96
94
|
return (
|
|
97
95
|
<AuthCard title={effectiveTitle}>
|
|
98
96
|
{done ? (
|
|
99
|
-
|
|
97
|
+
<>
|
|
100
98
|
<Banner variant="info">
|
|
101
99
|
<p className="font-medium text-foreground">{t("auth.resetPassword.successTitle")}</p>
|
|
102
100
|
<p className="mt-1">{t("auth.resetPassword.successBody")}</p>
|
|
@@ -104,9 +102,9 @@ export function ResetPasswordScreen({
|
|
|
104
102
|
<Link href={loginHref} variant="button">
|
|
105
103
|
{t("auth.resetPassword.goToLogin")}
|
|
106
104
|
</Link>
|
|
107
|
-
|
|
105
|
+
</>
|
|
108
106
|
) : (
|
|
109
|
-
|
|
107
|
+
<>
|
|
110
108
|
<p className="text-sm text-muted-foreground">{t("auth.resetPassword.intro")}</p>
|
|
111
109
|
<Form onSubmit={onSubmit}>
|
|
112
110
|
<Field id="reset-new-password" label={t("auth.resetPassword.newPassword")} required>
|
|
@@ -142,7 +140,7 @@ export function ResetPasswordScreen({
|
|
|
142
140
|
{submitting ? t("auth.resetPassword.submitting") : t("auth.resetPassword.submit")}
|
|
143
141
|
</Button>
|
|
144
142
|
</Form>
|
|
145
|
-
|
|
143
|
+
</>
|
|
146
144
|
)}
|
|
147
145
|
</AuthCard>
|
|
148
146
|
);
|
|
@@ -44,22 +44,20 @@ export function SessionBootstrapErrorScreen({
|
|
|
44
44
|
|
|
45
45
|
return (
|
|
46
46
|
<div data-testid="session-bootstrap-error" data-http-status={failure.httpStatus ?? "network"}>
|
|
47
|
-
<AuthCard title={t("auth.sessionBootstrap.errorTitle")}>
|
|
48
|
-
<
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
47
|
+
<AuthCard title={t("auth.sessionBootstrap.errorTitle")} bodyClassName="pb-4">
|
|
48
|
+
<Banner variant="error">
|
|
49
|
+
{failure.httpStatus === 429
|
|
50
|
+
? t("auth.sessionBootstrap.rateLimitedBody")
|
|
51
|
+
: t("auth.sessionBootstrap.errorBody")}
|
|
52
|
+
</Banner>
|
|
53
|
+
<Button variant="primary" onClick={handleRetry} disabled={retrying}>
|
|
54
|
+
{retrying ? t("auth.sessionBootstrap.retrying") : t("auth.sessionBootstrap.retry")}
|
|
55
|
+
</Button>
|
|
56
|
+
{onSignOut !== undefined && (
|
|
57
|
+
<Button variant="secondary" onClick={() => void onSignOut()} disabled={retrying}>
|
|
58
|
+
{t("auth.sessionBootstrap.signOut")}
|
|
56
59
|
</Button>
|
|
57
|
-
|
|
58
|
-
<Button variant="secondary" onClick={() => void onSignOut()} disabled={retrying}>
|
|
59
|
-
{t("auth.sessionBootstrap.signOut")}
|
|
60
|
-
</Button>
|
|
61
|
-
)}
|
|
62
|
-
</div>
|
|
60
|
+
)}
|
|
63
61
|
</AuthCard>
|
|
64
62
|
</div>
|
|
65
63
|
);
|
|
@@ -112,12 +112,10 @@ export function SignupCompleteScreen({
|
|
|
112
112
|
if (token === "") {
|
|
113
113
|
return (
|
|
114
114
|
<AuthCard title={effectiveTitle}>
|
|
115
|
-
<
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
</Link>
|
|
120
|
-
</div>
|
|
115
|
+
<p className="text-sm text-muted-foreground">{t("auth.signupComplete.missingToken")}</p>
|
|
116
|
+
<Link href={loginHref} variant="muted">
|
|
117
|
+
{t("auth.signup.haveAccount")}
|
|
118
|
+
</Link>
|
|
121
119
|
</AuthCard>
|
|
122
120
|
);
|
|
123
121
|
}
|
|
@@ -125,57 +123,53 @@ export function SignupCompleteScreen({
|
|
|
125
123
|
if (continueHref !== null) {
|
|
126
124
|
return (
|
|
127
125
|
<AuthCard title={effectiveTitle}>
|
|
128
|
-
<
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
</Link>
|
|
135
|
-
</div>
|
|
126
|
+
<p className="text-sm text-muted-foreground" role="status">
|
|
127
|
+
{t("auth.signupComplete.activated")}
|
|
128
|
+
</p>
|
|
129
|
+
<Link href={continueHref} variant="button">
|
|
130
|
+
{t("auth.signupComplete.continue")}
|
|
131
|
+
</Link>
|
|
136
132
|
</AuthCard>
|
|
137
133
|
);
|
|
138
134
|
}
|
|
139
135
|
|
|
140
136
|
return (
|
|
141
137
|
<AuthCard title={effectiveTitle}>
|
|
142
|
-
<
|
|
143
|
-
|
|
144
|
-
<
|
|
145
|
-
<
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
138
|
+
<p className="text-sm text-muted-foreground">{t("auth.signupComplete.intro")}</p>
|
|
139
|
+
<Form onSubmit={onSubmit}>
|
|
140
|
+
<Field id="signup-password" label={t("auth.signupComplete.password")} required>
|
|
141
|
+
<Input
|
|
142
|
+
kind="password"
|
|
143
|
+
id="signup-password"
|
|
144
|
+
name="signup-password"
|
|
145
|
+
value={password}
|
|
146
|
+
onChange={setPassword}
|
|
147
|
+
disabled={submitting}
|
|
148
|
+
required
|
|
149
|
+
autoComplete="new-password"
|
|
150
|
+
/>
|
|
151
|
+
</Field>
|
|
152
|
+
<Field
|
|
153
|
+
id="signup-confirm-password"
|
|
154
|
+
label={t("auth.signupComplete.confirmPassword")}
|
|
155
|
+
required
|
|
156
|
+
>
|
|
157
|
+
<Input
|
|
158
|
+
kind="password"
|
|
158
159
|
id="signup-confirm-password"
|
|
159
|
-
|
|
160
|
+
name="signup-confirm-password"
|
|
161
|
+
value={confirmPassword}
|
|
162
|
+
onChange={setConfirmPassword}
|
|
163
|
+
disabled={submitting}
|
|
160
164
|
required
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
required
|
|
170
|
-
autoComplete="new-password"
|
|
171
|
-
/>
|
|
172
|
-
</Field>
|
|
173
|
-
{error !== null && <Banner variant="error">{error}</Banner>}
|
|
174
|
-
<Button type="submit" loading={submitting} disabled={submitting}>
|
|
175
|
-
{submitting ? t("auth.signupComplete.submitting") : t("auth.signupComplete.submit")}
|
|
176
|
-
</Button>
|
|
177
|
-
</Form>
|
|
178
|
-
</div>
|
|
165
|
+
autoComplete="new-password"
|
|
166
|
+
/>
|
|
167
|
+
</Field>
|
|
168
|
+
{error !== null && <Banner variant="error">{error}</Banner>}
|
|
169
|
+
<Button type="submit" loading={submitting} disabled={submitting}>
|
|
170
|
+
{submitting ? t("auth.signupComplete.submitting") : t("auth.signupComplete.submit")}
|
|
171
|
+
</Button>
|
|
172
|
+
</Form>
|
|
179
173
|
</AuthCard>
|
|
180
174
|
);
|
|
181
175
|
}
|
|
@@ -85,7 +85,7 @@ export function SignupScreen({
|
|
|
85
85
|
return (
|
|
86
86
|
<AuthCard title={effectiveTitle} subtitle={subtitle}>
|
|
87
87
|
{done ? (
|
|
88
|
-
|
|
88
|
+
<>
|
|
89
89
|
<Banner variant="info">
|
|
90
90
|
<p className="font-medium text-foreground">{t("auth.signup.successTitle")}</p>
|
|
91
91
|
<p className="mt-1">{t("auth.signup.successBody")}</p>
|
|
@@ -96,9 +96,9 @@ export function SignupScreen({
|
|
|
96
96
|
<Link href={loginHref} variant="muted">
|
|
97
97
|
{t("auth.signup.haveAccount")}
|
|
98
98
|
</Link>
|
|
99
|
-
|
|
99
|
+
</>
|
|
100
100
|
) : (
|
|
101
|
-
|
|
101
|
+
<>
|
|
102
102
|
<p className="text-sm text-muted-foreground">{t("auth.signup.intro")}</p>
|
|
103
103
|
<Form onSubmit={onSubmit}>
|
|
104
104
|
<Field id="signup-email" label={t("auth.signup.email")} required>
|
|
@@ -121,7 +121,7 @@ export function SignupScreen({
|
|
|
121
121
|
<Link href={loginHref} variant="muted" className="self-center">
|
|
122
122
|
{t("auth.signup.haveAccount")}
|
|
123
123
|
</Link>
|
|
124
|
-
|
|
124
|
+
</>
|
|
125
125
|
)}
|
|
126
126
|
</AuthCard>
|
|
127
127
|
);
|
|
@@ -50,12 +50,10 @@ export function VerifyEmailScreen({
|
|
|
50
50
|
if (status === "missing-token") {
|
|
51
51
|
return (
|
|
52
52
|
<AuthCard title={title ?? t("auth.verifyEmail.errorTitle")}>
|
|
53
|
-
<
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
</Link>
|
|
58
|
-
</div>
|
|
53
|
+
<p className="text-sm text-muted-foreground">{t("auth.verifyEmail.missingToken")}</p>
|
|
54
|
+
<Link href={loginHref} variant="muted">
|
|
55
|
+
{t("auth.verifyEmail.goToLogin")}
|
|
56
|
+
</Link>
|
|
59
57
|
</AuthCard>
|
|
60
58
|
);
|
|
61
59
|
}
|
|
@@ -75,12 +73,10 @@ export function VerifyEmailScreen({
|
|
|
75
73
|
if (status === "success") {
|
|
76
74
|
return (
|
|
77
75
|
<AuthCard title={title ?? t("auth.verifyEmail.successTitle")}>
|
|
78
|
-
<
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
</Link>
|
|
83
|
-
</div>
|
|
76
|
+
<p className="text-sm text-muted-foreground">{t("auth.verifyEmail.successBody")}</p>
|
|
77
|
+
<Link href={loginHref} variant="button">
|
|
78
|
+
{t("auth.verifyEmail.goToLogin")}
|
|
79
|
+
</Link>
|
|
84
80
|
</AuthCard>
|
|
85
81
|
);
|
|
86
82
|
}
|
|
@@ -88,12 +84,10 @@ export function VerifyEmailScreen({
|
|
|
88
84
|
// status === "error"
|
|
89
85
|
return (
|
|
90
86
|
<AuthCard title={title ?? t("auth.verifyEmail.errorTitle")}>
|
|
91
|
-
<
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
</Link>
|
|
96
|
-
</div>
|
|
87
|
+
<p className="text-sm text-muted-foreground">{t("auth.verifyEmail.errorBody")}</p>
|
|
88
|
+
<Link href={loginHref} variant="muted">
|
|
89
|
+
{t("auth.verifyEmail.goToLogin")}
|
|
90
|
+
</Link>
|
|
97
91
|
</AuthCard>
|
|
98
92
|
);
|
|
99
93
|
}
|