Skip to content

Commit b4ab1f6

Browse files
committed
Refresh email layout: brand-aligned header, tagline + divider in footer, bold recipient email, larger secondary text
1 parent aa0d72b commit b4ab1f6

14 files changed

Lines changed: 150 additions & 132 deletions

File tree

application/account/Tests/EmailAuthentication/StartEmailLoginTests.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -163,10 +163,10 @@ public async Task StartEmailLoginCommand_WhenUserDoesNotExist_ShouldReturnFakeEm
163163
await EmailClient.Received(1).SendAsync(
164164
Arg.Is<EmailMessage>(m =>
165165
m.Recipient == email.ToLower() &&
166-
m.Subject == "Unknown user tried to login to PlatformPlatform" &&
167-
m.HtmlBody.Contains("You or someone else tried to login to PlatformPlatform") &&
168-
m.HtmlBody.Contains("This request was made by entering your mail") &&
169-
m.PlainTextBody.Contains("You or someone else tried to login to PlatformPlatform") &&
166+
m.Subject == "No account found" &&
167+
m.HtmlBody.Contains("Is this the right email address?") &&
168+
m.HtmlBody.Contains("PlatformPlatform account tied to") &&
169+
m.PlainTextBody.Contains("Is this the right email address?") &&
170170
m.PlainTextBody.Contains(email.ToLower())
171171
),
172172
Arg.Any<CancellationToken>()
@@ -247,10 +247,10 @@ public async Task StartEmailLogin_WhenUserIsSoftDeleted_ShouldReturnFakeEmailLog
247247
await EmailClient.Received(1).SendAsync(
248248
Arg.Is<EmailMessage>(m =>
249249
m.Recipient == email.ToLower() &&
250-
m.Subject == "Unknown user tried to login to PlatformPlatform" &&
251-
m.HtmlBody.Contains("You or someone else tried to login to PlatformPlatform") &&
252-
m.HtmlBody.Contains("This request was made by entering your mail") &&
253-
m.PlainTextBody.Contains("You or someone else tried to login to PlatformPlatform") &&
250+
m.Subject == "No account found" &&
251+
m.HtmlBody.Contains("Is this the right email address?") &&
252+
m.HtmlBody.Contains("PlatformPlatform account tied to") &&
253+
m.PlainTextBody.Contains("Is this the right email address?") &&
254254
m.PlainTextBody.Contains(email.ToLower())
255255
),
256256
Arg.Any<CancellationToken>()

application/account/WebApp/emails/templates/InviteUser.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,15 @@ export default function InviteUser({ locale }: Readonly<InviteUserProps>) {
2929
<Link href="{{LoginUrl}}" className="email-link text-[#0f172a] underline">
3030
go to this page in your open browser
3131
</Link>{" "}
32-
and login using <Value path="Email" sample="invitee@example.com" />.
32+
and login using{" "}
33+
<strong>
34+
<Value path="Email" sample="invitee@example.com" />
35+
</strong>
36+
.
3337
</Trans>
3438
</Text>
3539

36-
<Text className="email-muted m-[0px] mt-[16px] text-center text-[12px] leading-[20px] text-[#64748b]">
40+
<Text className="email-muted m-[0px] mt-[16px] text-center text-[13px] leading-[20px] text-[#64748b]">
3741
<Trans>If you don't recognize the sender, you can safely ignore this email.</Trans>
3842
</Text>
3943
</TransactionalEmail>

application/account/WebApp/emails/templates/ResendEmailLogin.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,11 @@ export default function ResendEmailLogin({ locale }: Readonly<ResendEmailLoginPr
3636
</Text>
3737
</Section>
3838

39-
<Text className="email-muted m-[0px] text-center text-[12px] leading-[20px] text-[#64748b]">
39+
<Text className="email-muted m-[0px] text-center text-[13px] leading-[20px] text-[#64748b]">
4040
<Trans>This code will expire in a few minutes.</Trans>
4141
</Text>
4242

43-
<Text className="email-muted m-[0px] mt-[16px] text-center text-[12px] leading-[20px] text-[#64748b]">
43+
<Text className="email-muted m-[0px] mt-[16px] text-center text-[13px] leading-[20px] text-[#64748b]">
4444
<Trans>If you didn't request a new code, you can safely ignore this email.</Trans>
4545
</Text>
4646
</TransactionalEmail>

application/account/WebApp/emails/templates/StartLogin.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export default function StartLogin({ locale }: Readonly<StartLoginProps>) {
3636
</Text>
3737
</Section>
3838

39-
<Text className="email-muted m-[0px] mt-[16px] text-center text-[12px] leading-[20px] text-[#64748b]">
39+
<Text className="email-muted m-[0px] mt-[16px] text-center text-[13px] leading-[20px] text-[#64748b]">
4040
<Trans>If you didn't try to log in, you can safely ignore this email — your account is secure.</Trans>
4141
</Text>
4242
</TransactionalEmail>

application/account/WebApp/emails/templates/StartSignup.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export default function StartSignup({ locale }: Readonly<StartSignupProps>) {
3636
</Text>
3737
</Section>
3838

39-
<Text className="email-muted m-[0px] mt-[16px] text-center text-[12px] leading-[20px] text-[#64748b]">
39+
<Text className="email-muted m-[0px] mt-[16px] text-center text-[13px] leading-[20px] text-[#64748b]">
4040
<Trans>If you didn't request to sign up, you can safely ignore this email.</Trans>
4141
</Text>
4242
</TransactionalEmail>

application/account/WebApp/emails/templates/UnknownUser.tsx

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// @jsxRuntime automatic
22
import { Trans } from "@lingui/react/macro";
3-
import { Link, Text } from "@react-email/components";
3+
import { Button, Section, Text } from "@react-email/components";
44
import { Heading } from "@repo/emails/components/Heading";
55
import { Subject } from "@repo/emails/components/Subject";
66
import { TransactionalEmail } from "@repo/emails/components/TransactionalEmail";
@@ -12,33 +12,39 @@ type UnknownUserProps = {
1212

1313
export default function UnknownUser({ locale }: Readonly<UnknownUserProps>) {
1414
return (
15-
<TransactionalEmail locale={locale} preview="Unknown user tried to login to PlatformPlatform">
15+
<TransactionalEmail locale={locale} preview="No PlatformPlatform account found">
1616
<Subject>
17-
<Trans>Unknown user tried to login to PlatformPlatform</Trans>
17+
<Trans>No account found</Trans>
1818
</Subject>
1919

2020
<Heading level={1} className="text-center">
21-
<Trans>You or someone else tried to login to PlatformPlatform</Trans>
21+
<Trans>Is this the right email address?</Trans>
2222
</Heading>
2323

2424
<Text className="m-[0px] mb-[16px] text-center text-[14px] leading-[24px]">
2525
<Trans>
26-
This request was made by entering your mail <Value path="Email" sample="alex@example.com" />, but we have no
27-
record of such user.
26+
It looks like there isn't a PlatformPlatform account tied to{" "}
27+
<strong>
28+
<Value path="Email" sample="alex@example.com" />
29+
</strong>
30+
.
2831
</Trans>
2932
</Text>
3033

3134
<Text className="m-[0px] text-center text-[14px] leading-[24px]">
32-
<Trans>
33-
You can sign up for an account on{" "}
34-
<Link href="{{SignupUrl}}" className="email-link text-[#0f172a] underline">
35-
{`'{{'SignupUrl'}}'`}
36-
</Link>
37-
.
38-
</Trans>
35+
<Trans>You can try again with a different email, or sign up for a new account.</Trans>
3936
</Text>
4037

41-
<Text className="email-muted m-[0px] mt-[16px] text-center text-[12px] leading-[20px] text-[#64748b]">
38+
<Section className="mt-[24px] text-center">
39+
<Button
40+
href="{{SignupUrl}}"
41+
className="email-button-default rounded-[8px] bg-[#0f172a] px-[24px] py-[12px] text-[14px] font-medium text-white"
42+
>
43+
<Trans>Sign up for an account</Trans>
44+
</Button>
45+
</Section>
46+
47+
<Text className="email-muted m-[0px] mt-[24px] text-center text-[13px] leading-[20px] text-[#64748b]">
4248
<Trans>If this wasn't you, no action is needed — no account was created.</Trans>
4349
</Text>
4450
</TransactionalEmail>

application/account/WebApp/emails/translations/locale/da-DK.po

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ msgstr ""
1616
msgid "<0/> invited you to join PlatformPlatform."
1717
msgstr "<0/> har inviteret dig til at deltage i PlatformPlatform."
1818

19+
msgid "An open-source platform for building enterprise-grade, multi-tenant B2B SaaS products."
20+
msgstr "Open source-platform til at bygge enterprise-grade, multi-tenant B2B-SaaS-produkter."
21+
1922
msgid "Compliance"
2023
msgstr "Compliance"
2124

@@ -46,39 +49,42 @@ msgstr "Hvis du ikke har forsøgt at logge ind, kan du roligt ignorere denne e-m
4649
msgid "If you don't recognize the sender, you can safely ignore this email."
4750
msgstr "Hvis du ikke kender afsenderen, kan du roligt ignorere denne e-mail."
4851

52+
msgid "Is this the right email address?"
53+
msgstr "Er det den rigtige e-mailadresse?"
54+
55+
msgid "It looks like there isn't a PlatformPlatform account tied to <0><1/></0>."
56+
msgstr "Det ser ud til, at der ikke er en PlatformPlatform-konto knyttet til <0><1/></0>."
57+
58+
msgid "No account found"
59+
msgstr "Ingen konto fundet"
60+
4961
msgid "PlatformPlatform login verification code"
5062
msgstr "PlatformPlatform-bekræftelseskode til login"
5163

5264
msgid "Privacy"
5365
msgstr "Privatliv"
5466

67+
msgid "Sign up for an account"
68+
msgstr "Opret en konto"
69+
5570
msgid "Terms"
5671
msgstr "Vilkår"
5772

5873
msgid "This code will expire in a few minutes."
5974
msgstr "Denne kode udløber om få minutter."
6075

61-
msgid "This request was made by entering your mail <0/>, but we have no record of such user."
62-
msgstr "Anmodningen blev foretaget ved indtastning af din e-mail <0/>, men vi har ingen registrering af en sådan bruger."
63-
64-
msgid "To gain access, <0>go to this page in your open browser</0> and login using <1/>."
65-
msgstr "For at få adgang skal du <0>gå til denne side i din åbne browser</0> og logge ind med <1/>."
66-
67-
msgid "Unknown user tried to login to PlatformPlatform"
68-
msgstr "Ukendt bruger forsøgte at logge ind på PlatformPlatform"
76+
msgid "To gain access, <0>go to this page in your open browser</0> and login using <1><2/></1>."
77+
msgstr "For at få adgang skal du <0>gå til denne side i din åbne browser</0> og logge ind med <1><2/></1>."
6978

7079
msgid "We're sending this code again as you requested."
7180
msgstr "Vi sender denne kode igen, som du anmodede om."
7281

73-
msgid "You can sign up for an account on <0>'{{'SignupUrl'}}'</0>."
74-
msgstr "Du kan oprette en konto på <0>'{{'SignupUrl'}}'</0>."
82+
msgid "You can try again with a different email, or sign up for a new account."
83+
msgstr "Du kan prøve igen med en anden e-mailadresse eller oprette en ny konto."
7584

7685
msgid "You have been invited to join '{{'TenantName'}}' on PlatformPlatform"
7786
msgstr "Du er inviteret til at deltage i '{{'TenantName'}}' på PlatformPlatform"
7887

79-
msgid "You or someone else tried to login to PlatformPlatform"
80-
msgstr "Du eller en anden forsøgte at logge ind på PlatformPlatform"
81-
8288
msgid "Your confirmation code is below"
8389
msgstr "Din bekræftelseskode står herunder"
8490

application/account/WebApp/emails/translations/locale/en-US.po

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ msgstr ""
1616
msgid "<0/> invited you to join PlatformPlatform."
1717
msgstr "<0/> invited you to join PlatformPlatform."
1818

19+
msgid "An open-source platform for building enterprise-grade, multi-tenant B2B SaaS products."
20+
msgstr "An open-source platform for building enterprise-grade, multi-tenant B2B SaaS products."
21+
1922
msgid "Compliance"
2023
msgstr "Compliance"
2124

@@ -46,39 +49,42 @@ msgstr "If you didn't try to log in, you can safely ignore this email — your a
4649
msgid "If you don't recognize the sender, you can safely ignore this email."
4750
msgstr "If you don't recognize the sender, you can safely ignore this email."
4851

52+
msgid "Is this the right email address?"
53+
msgstr "Is this the right email address?"
54+
55+
msgid "It looks like there isn't a PlatformPlatform account tied to <0><1/></0>."
56+
msgstr "It looks like there isn't a PlatformPlatform account tied to <0><1/></0>."
57+
58+
msgid "No account found"
59+
msgstr "No account found"
60+
4961
msgid "PlatformPlatform login verification code"
5062
msgstr "PlatformPlatform login verification code"
5163

5264
msgid "Privacy"
5365
msgstr "Privacy"
5466

67+
msgid "Sign up for an account"
68+
msgstr "Sign up for an account"
69+
5570
msgid "Terms"
5671
msgstr "Terms"
5772

5873
msgid "This code will expire in a few minutes."
5974
msgstr "This code will expire in a few minutes."
6075

61-
msgid "This request was made by entering your mail <0/>, but we have no record of such user."
62-
msgstr "This request was made by entering your mail <0/>, but we have no record of such user."
63-
64-
msgid "To gain access, <0>go to this page in your open browser</0> and login using <1/>."
65-
msgstr "To gain access, <0>go to this page in your open browser</0> and login using <1/>."
66-
67-
msgid "Unknown user tried to login to PlatformPlatform"
68-
msgstr "Unknown user tried to login to PlatformPlatform"
76+
msgid "To gain access, <0>go to this page in your open browser</0> and login using <1><2/></1>."
77+
msgstr "To gain access, <0>go to this page in your open browser</0> and login using <1><2/></1>."
6978

7079
msgid "We're sending this code again as you requested."
7180
msgstr "We're sending this code again as you requested."
7281

73-
msgid "You can sign up for an account on <0>'{{'SignupUrl'}}'</0>."
74-
msgstr "You can sign up for an account on <0>'{{'SignupUrl'}}'</0>."
82+
msgid "You can try again with a different email, or sign up for a new account."
83+
msgstr "You can try again with a different email, or sign up for a new account."
7584

7685
msgid "You have been invited to join '{{'TenantName'}}' on PlatformPlatform"
7786
msgstr "You have been invited to join '{{'TenantName'}}' on PlatformPlatform"
7887

79-
msgid "You or someone else tried to login to PlatformPlatform"
80-
msgstr "You or someone else tried to login to PlatformPlatform"
81-
8288
msgid "Your confirmation code is below"
8389
msgstr "Your confirmation code is below"
8490

Lines changed: 13 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,26 @@
1+
import { useLingui } from "@lingui/react";
12
import { Trans } from "@lingui/react/macro";
23
import { Button } from "@repo/ui/components/Button";
3-
import { ExternalLinkIcon } from "lucide-react";
44
import { useState } from "react";
55

66
const TEMPLATES = ["StartSignup", "StartLogin", "ResendEmailLogin", "UnknownUser", "InviteUser"] as const;
7-
const LOCALES = ["en-US", "da-DK"] as const;
7+
const SUPPORTED_PREVIEW_LOCALES = ["en-US", "da-DK"] as const;
8+
const FALLBACK_PREVIEW_LOCALE = "en-US";
89

910
type Template = (typeof TEMPLATES)[number];
10-
type Locale = (typeof LOCALES)[number];
11+
type PreviewLocale = (typeof SUPPORTED_PREVIEW_LOCALES)[number];
1112

1213
export function EmailsPreview() {
14+
const { i18n } = useLingui();
1315
const [template, setTemplate] = useState<Template>("StartSignup");
14-
const [locale, setLocale] = useState<Locale>("en-US");
16+
17+
// The iframe locale follows the SPA's active locale (set by the avatar-menu locale switcher) so
18+
// designers don't have to toggle locales twice. Falls back to en-US if the user's chosen locale
19+
// doesn't have a rendered email artifact yet — the email build only emits the locales listed in
20+
// application/shared-webapp/infrastructure/translations/i18n.config.json that have .po catalogs.
21+
const locale: PreviewLocale = (SUPPORTED_PREVIEW_LOCALES as readonly string[]).includes(i18n.locale)
22+
? (i18n.locale as PreviewLocale)
23+
: FALLBACK_PREVIEW_LOCALE;
1524

1625
const iframeSrc = `/emails/assets/${template}.${locale}.preview.html`;
1726

@@ -35,24 +44,6 @@ export function EmailsPreview() {
3544
</div>
3645
</div>
3746

38-
<div className="flex flex-col gap-2">
39-
<h4>
40-
<Trans>Locale</Trans>
41-
</h4>
42-
<div className="flex flex-wrap gap-2">
43-
{LOCALES.map((code) => (
44-
<Button
45-
key={code}
46-
variant={locale === code ? "default" : "outline"}
47-
size="sm"
48-
onClick={() => setLocale(code)}
49-
>
50-
{code}
51-
</Button>
52-
))}
53-
</div>
54-
</div>
55-
5647
<div className="overflow-hidden rounded-md border border-border">
5748
<iframe
5849
key={iframeSrc}
@@ -61,22 +52,6 @@ export function EmailsPreview() {
6152
className="block h-[40rem] w-full border-0 bg-white"
6253
/>
6354
</div>
64-
65-
<p className="text-sm text-muted-foreground">
66-
<Trans>
67-
Need a new building block? Browse the MIT-licensed recipes at{" "}
68-
<a
69-
href="https://react.email/components"
70-
target="_blank"
71-
rel="noopener noreferrer"
72-
className="inline-flex items-center gap-1 underline"
73-
>
74-
react.email/components
75-
<ExternalLinkIcon className="size-[0.875rem]" />
76-
</a>{" "}
77-
and copy the source into <code>application/shared-webapp/emails/components/</code>.
78-
</Trans>
79-
</p>
8055
</div>
8156
);
8257
}

application/account/WebApp/routes/components/emails.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Trans } from "@lingui/react/macro";
33
import { AppLayout } from "@repo/ui/components/AppLayout";
44
import { SidebarInset, SidebarProvider } from "@repo/ui/components/Sidebar";
55
import { createFileRoute } from "@tanstack/react-router";
6+
import { ExternalLinkIcon } from "lucide-react";
67

78
import { ComponentsSideMenu } from "./-components/ComponentsSideMenu";
89
import { EmailsPreview } from "./-components/EmailsPreview";
@@ -22,6 +23,21 @@ function EmailsPage() {
2223
variant="full"
2324
browserTitle={t`Emails`}
2425
title={<Trans>Emails</Trans>}
26+
subtitle={
27+
<Trans>
28+
Need a new component? Copy a recipe from{" "}
29+
<a
30+
href="https://react.email/components"
31+
target="_blank"
32+
rel="noopener noreferrer"
33+
className="inline-flex items-center gap-1 underline"
34+
>
35+
react.email/components
36+
<ExternalLinkIcon className="size-[0.875rem]" />
37+
</a>
38+
.
39+
</Trans>
40+
}
2541
beforeHeader={<PreviewHeader currentPage="emails" defaultTab="" tabLabels={{ "": <Trans>Emails</Trans> }} />}
2642
>
2743
<EmailsPreview />

0 commit comments

Comments
 (0)