mirror of
https://github.com/lukevella/rallly.git
synced 2025-07-10 21:17:31 +02:00
✨ Translations for Email Notifications (#1278)
Co-authored-by: Niko Heller <hellerniko@gmail.com>
This commit is contained in:
parent
aa52a0f26f
commit
f4218c3115
51 changed files with 1071 additions and 970 deletions
16
packages/emails/src/components/email-context.tsx
Normal file
16
packages/emails/src/components/email-context.tsx
Normal file
|
@ -0,0 +1,16 @@
|
|||
import { i18nDefaultConfig, i18nInstance } from "../i18n";
|
||||
import { EmailContext } from "../types";
|
||||
|
||||
i18nInstance.init({
|
||||
...i18nDefaultConfig,
|
||||
initImmediate: true,
|
||||
});
|
||||
|
||||
export const previewEmailContext: EmailContext = {
|
||||
logoUrl: "https://rallly-public.s3.amazonaws.com/images/rallly-logo-mark.png",
|
||||
baseUrl: "https://rallly.co",
|
||||
domain: "rallly.co",
|
||||
supportEmail: "support@rallly.co",
|
||||
i18n: i18nInstance,
|
||||
t: i18nInstance.getFixedT("en"),
|
||||
};
|
72
packages/emails/src/components/email-layout.tsx
Normal file
72
packages/emails/src/components/email-layout.tsx
Normal file
|
@ -0,0 +1,72 @@
|
|||
import {
|
||||
Body,
|
||||
Container,
|
||||
Head,
|
||||
Html,
|
||||
Img,
|
||||
Preview,
|
||||
Section,
|
||||
} from "@react-email/components";
|
||||
import { Trans } from "react-i18next/TransWithoutContext";
|
||||
|
||||
import { EmailContext } from "../types";
|
||||
import { darkTextColor, fontFamily, Link, Text } from "./styled-components";
|
||||
|
||||
export interface EmailLayoutProps {
|
||||
preview: string;
|
||||
ctx: EmailContext;
|
||||
}
|
||||
|
||||
const containerStyles = {
|
||||
maxWidth: "480px",
|
||||
margin: "0 auto",
|
||||
background: "white",
|
||||
fontFamily,
|
||||
padding: "32px 8px",
|
||||
color: darkTextColor,
|
||||
};
|
||||
|
||||
export const EmailLayout = ({
|
||||
preview,
|
||||
children,
|
||||
ctx,
|
||||
}: React.PropsWithChildren<EmailLayoutProps>) => {
|
||||
const { logoUrl } = ctx;
|
||||
return (
|
||||
<Html>
|
||||
<Head />
|
||||
<Preview>{preview}</Preview>
|
||||
<Body style={{ backgroundColor: "#FFFFFF" }}>
|
||||
<Container style={containerStyles}>
|
||||
<Img
|
||||
src={logoUrl}
|
||||
width="32"
|
||||
height="32"
|
||||
style={{
|
||||
marginBottom: 32,
|
||||
}}
|
||||
alt="Rallly Logo"
|
||||
/>
|
||||
{children}
|
||||
<Section style={{ marginTop: 32 }}>
|
||||
<Text light={true}>
|
||||
<Trans
|
||||
i18n={ctx.i18n}
|
||||
t={ctx.t}
|
||||
i18nKey="common_poweredBy"
|
||||
ns="emails"
|
||||
defaults="Powered by <a>{{domain}}</a>"
|
||||
values={{ domain: "rallly.co" }}
|
||||
components={{
|
||||
a: (
|
||||
<Link href="https://rallly.co?utm_source=email&utm_medium=transactional" />
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Text>
|
||||
</Section>
|
||||
</Container>
|
||||
</Body>
|
||||
</Html>
|
||||
);
|
||||
};
|
60
packages/emails/src/components/notification-email.tsx
Normal file
60
packages/emails/src/components/notification-email.tsx
Normal file
|
@ -0,0 +1,60 @@
|
|||
import { Section } from "@react-email/section";
|
||||
import { Trans } from "react-i18next/TransWithoutContext";
|
||||
|
||||
import type { EmailContext } from "../types";
|
||||
import { EmailLayout } from "./email-layout";
|
||||
import { Button, Link, Text } from "./styled-components";
|
||||
|
||||
export interface NotificationBaseProps {
|
||||
title: string;
|
||||
pollUrl: string;
|
||||
disableNotificationsUrl: string;
|
||||
ctx: EmailContext;
|
||||
}
|
||||
|
||||
export interface NotificationEmailProps extends NotificationBaseProps {
|
||||
preview: string;
|
||||
}
|
||||
|
||||
export const NotificationEmail = ({
|
||||
pollUrl,
|
||||
disableNotificationsUrl,
|
||||
preview,
|
||||
children,
|
||||
ctx,
|
||||
}: React.PropsWithChildren<NotificationEmailProps>) => {
|
||||
const { domain } = ctx;
|
||||
return (
|
||||
<EmailLayout ctx={ctx} preview={preview}>
|
||||
{children}
|
||||
<Section style={{ marginTop: 32, marginBottom: 32 }}>
|
||||
<Button href={pollUrl}>
|
||||
{ctx.t("common_viewOn", {
|
||||
ns: "emails",
|
||||
defaultValue: "View on {{domain}}",
|
||||
domain,
|
||||
})}
|
||||
</Button>
|
||||
</Section>
|
||||
<Text light={true}>
|
||||
<Trans
|
||||
i18n={ctx.i18n}
|
||||
t={ctx.t}
|
||||
i18nKey="common_disableNotifications"
|
||||
ns="emails"
|
||||
defaults="If you would like to stop receiving updates you can <a>turn notifications off</a>."
|
||||
components={{
|
||||
a: (
|
||||
<Link
|
||||
className="whitespace-nowrap"
|
||||
href={disableNotificationsUrl}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Text>
|
||||
</EmailLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default NotificationEmail;
|
158
packages/emails/src/components/styled-components.tsx
Normal file
158
packages/emails/src/components/styled-components.tsx
Normal file
|
@ -0,0 +1,158 @@
|
|||
import {
|
||||
Button as UnstyledButton,
|
||||
Heading as UnstyledHeading,
|
||||
Link as UnstyledLink,
|
||||
LinkProps,
|
||||
Section as UnstyledSection,
|
||||
SectionProps,
|
||||
Text as UnstyledText,
|
||||
TextProps,
|
||||
} from "@react-email/components";
|
||||
|
||||
import type { EmailContext } from "../types";
|
||||
|
||||
export const lightTextColor = "#4B5563";
|
||||
export const darkTextColor = "#1F2937";
|
||||
export const borderColor = "#E2E8F0";
|
||||
export const Text = (
|
||||
props: TextProps & { light?: boolean; small?: boolean },
|
||||
) => {
|
||||
const { light, small, ...forwardProps } = props;
|
||||
return (
|
||||
<UnstyledText
|
||||
{...forwardProps}
|
||||
style={{
|
||||
margin: "16px 0",
|
||||
fontFamily,
|
||||
fontSize: small ? "14px" : "16px",
|
||||
color: light ? lightTextColor : darkTextColor,
|
||||
lineHeight: "1.5",
|
||||
...props.style,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const Domain = ({ ctx }: { ctx: EmailContext }) => {
|
||||
const { baseUrl, domain } = ctx;
|
||||
return <Link href={baseUrl}>{domain}</Link>;
|
||||
};
|
||||
|
||||
export const Button = (props: React.ComponentProps<typeof UnstyledButton>) => {
|
||||
return (
|
||||
<UnstyledButton
|
||||
{...props}
|
||||
className={props.className}
|
||||
style={{
|
||||
backgroundColor: "#4F46E5",
|
||||
borderRadius: "4px",
|
||||
padding: "12px 14px",
|
||||
fontFamily,
|
||||
boxSizing: "border-box",
|
||||
display: "block",
|
||||
width: "100%",
|
||||
maxWidth: "100%",
|
||||
textAlign: "center",
|
||||
fontSize: "16px",
|
||||
color: "white",
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const Link = (props: LinkProps) => {
|
||||
return (
|
||||
<UnstyledLink
|
||||
{...props}
|
||||
style={{ color: "#4F46E5", fontFamily, ...props.style }}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const fontSize = {
|
||||
h1: "20px",
|
||||
h2: "18px",
|
||||
h3: "16px",
|
||||
h4: "16px",
|
||||
h5: "14px",
|
||||
h6: "12px",
|
||||
};
|
||||
|
||||
export const Heading = (
|
||||
props: React.ComponentProps<typeof UnstyledHeading>,
|
||||
) => {
|
||||
const { as = "h1" } = props;
|
||||
|
||||
return (
|
||||
<UnstyledHeading
|
||||
{...props}
|
||||
as={as}
|
||||
style={{
|
||||
fontSize: fontSize[as],
|
||||
...props.style,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const SubHeadingText = (props: TextProps) => {
|
||||
return (
|
||||
<UnstyledText
|
||||
{...props}
|
||||
style={{
|
||||
marginBottom: "16px",
|
||||
marginTop: "8px",
|
||||
fontSize: 16,
|
||||
color: "#374151",
|
||||
fontFamily,
|
||||
...props.style,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const Section = (props: SectionProps) => {
|
||||
return (
|
||||
<UnstyledSection
|
||||
{...props}
|
||||
style={{ marginTop: "16px", marginBottom: "16px", ...props.style }}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const SmallText = (props: TextProps) => {
|
||||
return (
|
||||
<UnstyledText
|
||||
{...props}
|
||||
style={{
|
||||
fontSize: "14px",
|
||||
color: "#6B7280",
|
||||
...props.style,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const Card = (props: SectionProps) => {
|
||||
return (
|
||||
<Section
|
||||
{...props}
|
||||
style={{
|
||||
borderRadius: "4px",
|
||||
backgroundColor: "#F9FAFB",
|
||||
paddingRight: "16px",
|
||||
paddingLeft: "16px",
|
||||
border: "1px solid #E2E8F0",
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const trackingWide = {
|
||||
letterSpacing: 2,
|
||||
};
|
||||
|
||||
export const fontFamily =
|
||||
"'Inter UI', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif";
|
||||
|
||||
export const primaryColor = "#4F46E5";
|
Loading…
Add table
Add a link
Reference in a new issue