mirror of
https://github.com/Hestia-Homes/assessment-model.git
synced 2026-06-08 11:37:25 +00:00
Drop link from email body; fix paste + stale-resend-notice UX bugs
Email body now contains the 6-digit code only. The /verify/[token] route and the EmailProvider link callback are intentionally left wired (just not advertised in the email) so reverting to a link-bearing template is a content-only change if the all-code variant doesn't improve deliverability for the Atkins-style blocked recipients. Hypothesis being tested: corporate gateway URL scanners are part of why some emails got silently quarantined, and a short transactional body without an auth-token URL clears more filters. Two small UX bugs surfaced in preview testing also fixed here: - Paste of "482 911" (with the visual space from the email's formatted code) was dropping a digit. Root cause: maxLength=6 on the input truncated the 7-char paste before our \D-stripping ran. Drop the attribute and let the existing .slice(0, 6) after stripping handle the bound. Pasting the formatted code now works. - After requesting a resend and then typing into the code field, the green "we've sent a new code" notice would re-appear as soon as the previous error message cleared. handleChange now clears the resend notice on the next keystroke, alongside the error clear it already did. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
d042606955
commit
ee506425fd
2 changed files with 9 additions and 53 deletions
|
|
@ -57,6 +57,7 @@ export default function VerifyCodeForm({ email }: { email: string }) {
|
|||
const digits = next.replace(/\D/g, "").slice(0, 6);
|
||||
setCode(digits);
|
||||
if (error) setError(null);
|
||||
if (resendNotice) setResendNotice(null);
|
||||
if (digits.length === 6) void submitCode(digits);
|
||||
}
|
||||
|
||||
|
|
@ -93,7 +94,6 @@ export default function VerifyCodeForm({ email }: { email: string }) {
|
|||
id="verify-code"
|
||||
inputMode="numeric"
|
||||
autoComplete="one-time-code"
|
||||
maxLength={6}
|
||||
value={code}
|
||||
onChange={(e) => handleChange(e.target.value)}
|
||||
placeholder="••••••"
|
||||
|
|
@ -133,9 +133,6 @@ export default function VerifyCodeForm({ email }: { email: string }) {
|
|||
</button>
|
||||
</div>
|
||||
|
||||
<p className="text-xs text-gray-400 text-center">
|
||||
Or use the one-click link from your email instead.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// Contains the email template for user sign in. Email contains a 6-digit code
|
||||
// (primary action — type at /auth/verify-code) and a sign-in link (fast-path
|
||||
// for users whose corporate gateway doesn't mangle URLs).
|
||||
// Email template for user sign-in. Body contains a 6-digit code only — the
|
||||
// magic-link path is still wired (see /verify/[token] and the EmailProvider
|
||||
// callback) but the URL is intentionally omitted from the email to reduce
|
||||
// the content-scanner surface area for corporate email gateways.
|
||||
|
||||
import { createTransport } from "nodemailer";
|
||||
import { buildMailHeaders } from "./buildMailHeaders";
|
||||
|
|
@ -18,41 +19,25 @@ export async function MagicLinksEmail({
|
|||
}): Promise<{ messageId: string }> {
|
||||
const parsed = new URL(url);
|
||||
const host = parsed.host;
|
||||
|
||||
const baseUrl = parsed.origin;
|
||||
const logoUrl = `${baseUrl}/domna-email-logo.png`;
|
||||
|
||||
const token = parsed.searchParams.get("token");
|
||||
const email = parsed.searchParams.get("email");
|
||||
|
||||
if (!token || !email) {
|
||||
throw new Error("Magic link token or email missing");
|
||||
}
|
||||
|
||||
// Direct the link at the existing two-step verify page (defends against
|
||||
// email-scanner pre-fetching), not at the NextAuth callback.
|
||||
const loginUrl = `${parsed.origin}/verify/${token}`;
|
||||
const logoUrl = `${parsed.origin}/domna-email-logo.png`;
|
||||
|
||||
const transport = createTransport(provider.server);
|
||||
|
||||
const brandColor = "#14163d";
|
||||
const accentColor = "#2d348f";
|
||||
const brown = "#c4a47c";
|
||||
const background = "#F9F9F9";
|
||||
|
||||
const result = await transport.sendMail({
|
||||
to: identifier,
|
||||
from: provider.from,
|
||||
subject: "Sign in to Ara",
|
||||
text: plainText({ code, url: loginUrl, host }),
|
||||
text: plainText({ code, host }),
|
||||
html: domnaHtml({
|
||||
code,
|
||||
url: loginUrl,
|
||||
logoUrl,
|
||||
host,
|
||||
brandColor,
|
||||
accentColor,
|
||||
brown,
|
||||
background,
|
||||
}),
|
||||
headers: buildMailHeaders({
|
||||
|
|
@ -77,21 +62,17 @@ function formatCodeForDisplay(code: string): string {
|
|||
|
||||
function domnaHtml({
|
||||
code,
|
||||
url,
|
||||
logoUrl,
|
||||
host,
|
||||
brandColor,
|
||||
accentColor,
|
||||
brown,
|
||||
background,
|
||||
}: {
|
||||
code: string;
|
||||
url: string;
|
||||
logoUrl: string;
|
||||
host: string;
|
||||
brandColor: string;
|
||||
accentColor: string;
|
||||
brown: string;
|
||||
background: string;
|
||||
}) {
|
||||
const escapedHost = host.replace(/\./g, "​.");
|
||||
|
|
@ -113,7 +94,7 @@ function domnaHtml({
|
|||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" style="padding: 28px 24px 12px; color: #333;">
|
||||
<td align="center" style="padding: 28px 24px 28px; color: #333;">
|
||||
<h2 style="color: ${brandColor}; font-size: 22px; margin: 0 0 8px;">Your sign-in code</h2>
|
||||
<p style="font-size: 14px; line-height: 1.5; color: #666; margin: 0 0 20px;">
|
||||
Enter this code at <span style="color: ${accentColor};">${escapedHost}</span> to sign in to Ara.
|
||||
|
|
@ -124,17 +105,6 @@ function domnaHtml({
|
|||
<p style="font-size: 12px; color: #888; margin: 12px 0 0;">
|
||||
This code expires in 10 minutes.
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" style="padding: 4px 24px 24px; color: #333;">
|
||||
<p style="font-size: 13px; color: #777; margin: 8px 0 12px;">
|
||||
Or use the one-click link instead:
|
||||
</p>
|
||||
<a href="${url}" target="_blank"
|
||||
style="display: inline-block; padding: 10px 22px; background: ${brown}; color: #fff; text-decoration: none; border-radius: 6px; font-weight: 600; font-size: 14px;">
|
||||
Sign in to Ara
|
||||
</a>
|
||||
<p style="margin-top: 28px; font-size: 13px; color: #777;">
|
||||
If you didn’t request this email, you can safely ignore it.
|
||||
</p>
|
||||
|
|
@ -150,24 +120,13 @@ function domnaHtml({
|
|||
`;
|
||||
}
|
||||
|
||||
function plainText({
|
||||
code,
|
||||
url,
|
||||
host,
|
||||
}: {
|
||||
code: string;
|
||||
url: string;
|
||||
host: string;
|
||||
}) {
|
||||
function plainText({ code, host }: { code: string; host: string }) {
|
||||
return `Sign in to Ara by Domna
|
||||
|
||||
Your sign-in code: ${code}
|
||||
|
||||
Enter this code at ${host}/auth/verify-code to sign in.
|
||||
|
||||
Or use the one-click link instead:
|
||||
${url}
|
||||
|
||||
This code expires in 10 minutes. If you did not request this email, you can safely ignore it.
|
||||
`;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue