Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

💄 Updated transactional email templates #1285

Merged
merged 8 commits into from
Aug 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added apps/web/public/images/rallly-logo-mark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions apps/web/src/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ export const env = createEnv({
* Example: "[email protected], *@example.com, *@*.example.com"
*/
ALLOWED_EMAILS: z.string().optional(),
/**
* Email addresses for support and no-reply emails.
*/
SUPPORT_EMAIL: z.string().email(),
NOREPLY_EMAIL: z.string().email().optional(),
NOREPLY_EMAIL_NAME: z.string().default("Rallly"),
},
/*
* Environment variables available on the client (and server).
Expand Down Expand Up @@ -90,6 +96,9 @@ export const env = createEnv({
NEXT_PUBLIC_POSTHOG_API_KEY: process.env.NEXT_PUBLIC_POSTHOG_API_KEY,
NEXT_PUBLIC_POSTHOG_API_HOST: process.env.NEXT_PUBLIC_POSTHOG_API_HOST,
NEXT_PUBLIC_SELF_HOSTED: process.env.NEXT_PUBLIC_SELF_HOSTED,
SUPPORT_EMAIL: process.env.SUPPORT_EMAIL,
NOREPLY_EMAIL: process.env.NOREPLY_EMAIL,
NOREPLY_EMAIL_NAME: process.env.NOREPLY_EMAIL_NAME,
},
skipValidation: !!process.env.SKIP_ENV_VALIDATION,
});
17 changes: 9 additions & 8 deletions apps/web/src/utils/emails.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
import { EmailClient, SupportedEmailProviders } from "@rallly/emails";

import { env } from "@/env";
import { absoluteUrl } from "@/utils/absolute-url";

const env = process.env["NODE" + "_ENV"];
import { isSelfHosted } from "@/utils/constants";

export const emailClient = new EmailClient({
openPreviews: env === "development",
openPreviews: env.NODE_ENV === "development",
provider: {
name: (process.env.EMAIL_PROVIDER as SupportedEmailProviders) ?? "smtp",
},
mail: {
from: {
name: (process.env.NOREPLY_EMAIL_NAME as string) || "Rallly",
address:
(process.env.NOREPLY_EMAIL as string) ||
(process.env.SUPPORT_EMAIL as string),
name: env.NOREPLY_EMAIL_NAME,
address: env.NOREPLY_EMAIL || env.SUPPORT_EMAIL,
},
},
context: {
logoUrl: absoluteUrl("/logo.png"),
logoUrl: isSelfHosted
? absoluteUrl("/images/rallly-logo-mark.png")
: "https://rallly-public.s3.amazonaws.com/images/rallly-logo-mark.png",
baseUrl: absoluteUrl(""),
domain: absoluteUrl("").replace(/(^\w+:|^)\/\//, ""),
supportEmail: env.SUPPORT_EMAIL,
},
});
4 changes: 3 additions & 1 deletion packages/emails/src/templates/_components/email-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ export type EmailContext = {
logoUrl: string;
baseUrl: string;
domain: string;
supportEmail: string;
};

export const defaultEmailContext = {
logoUrl: "https://rallly.co/logo.png",
logoUrl: "https://rallly-public.s3.amazonaws.com/images/rallly-logo-mark.png",
baseUrl: "https://rallly.co",
domain: "rallly.co",
supportEmail: "[email protected]",
};
89 changes: 24 additions & 65 deletions packages/emails/src/templates/_components/email-layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,97 +4,56 @@ import {
Head,
Html,
Img,
Link,
Preview,
Section,
} from "@react-email/components";

import { EmailContext } from "./email-context";
import { fontFamily, Section, Text } from "./styled-components";
import { darkTextColor, fontFamily, Link, Text } from "./styled-components";

export interface EmailLayoutProps {
preview: string;
recipientName?: string;
footNote?: React.ReactNode;
ctx: EmailContext;
}

const containerStyles = {
maxWidth: "600px",
maxWidth: "480px",
margin: "0 auto",
background: "white",
fontFamily,
padding: 16,
border: "1px solid #E2E8F0",
borderRadius: 5,
};

const sectionStyles = {
marginTop: "16px",
marginBottom: "16px",
};

const linkStyles = {
color: "#64748B",
marginRight: "8px",
padding: "32px 8px",
color: darkTextColor,
};

export const EmailLayout = ({
preview,
recipientName,
children,
footNote,
ctx,
}: React.PropsWithChildren<EmailLayoutProps>) => {
const { logoUrl, baseUrl } = ctx;
const { logoUrl } = ctx;
return (
<Html>
<Head />
<Preview>{preview}</Preview>
<Body
style={{
backgroundColor: "#F3F4F6",
paddingTop: 20,
paddingBottom: 20,
}}
>
<Body style={{ backgroundColor: "#FFFFFF" }}>
<Container style={containerStyles}>
<Img src={logoUrl} alt="Rallly" width={128} />
<Section style={sectionStyles}>
{recipientName ? <Text>Hi {recipientName},</Text> : null}
{children}
{footNote ? (
<Text
style={{
color: "#64748B",
fontFamily,
paddingTop: 16,
marginTop: 32,
borderTop: "1px solid #e2e8f0",
}}
>
{footNote}
</Text>
) : null}
</Section>
<Section style={{ ...sectionStyles, fontSize: 14, marginBottom: 0 }}>
<Link style={linkStyles} href={baseUrl}>
Home
</Link>
<span>&nbsp;&bull;&nbsp;</span>
<Link style={linkStyles} href="https://twitter.com/ralllyco">
Twitter
</Link>
<span>&nbsp;&bull;&nbsp;</span>
<Link style={linkStyles} href="https://github.com/lukevella/rallly">
Github
</Link>
<span>&nbsp;&bull;&nbsp;</span>
<Link
style={linkStyles}
href={`mailto:${process.env["SUPPORT_EMAIL"]}`}
>
Contact
</Link>
<Img
src={logoUrl}
width="32"
height="32"
style={{
marginBottom: 32,
}}
alt="Rallly Logo"
/>
{children}
<Section style={{ marginTop: 32, textAlign: "center" }}>
<Text light={true}>
Powered by{" "}
<Link href="https://rallly.co?utm_source=email&utm_medium=transactional">
rallly.co
</Link>
</Text>
</Section>
</Container>
</Body>
Expand Down
27 changes: 11 additions & 16 deletions packages/emails/src/templates/_components/notification-email.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Section } from "@react-email/section";

import { EmailContext } from "./email-context";
import { EmailLayout } from "./email-layout";
import { Button, Link, Text } from "./styled-components";
Expand All @@ -15,7 +17,6 @@ export interface NotificationEmailProps extends NotificationBaseProps {
}

export const NotificationEmail = ({
name,
pollUrl,
disableNotificationsUrl,
preview,
Expand All @@ -24,23 +25,17 @@ export const NotificationEmail = ({
}: React.PropsWithChildren<NotificationEmailProps>) => {
const { domain } = ctx;
return (
<EmailLayout
ctx={ctx}
recipientName={name}
footNote={
<>
If you would like to stop receiving updates you can{" "}
<Link className="whitespace-nowrap" href={disableNotificationsUrl}>
turn notifications off
</Link>
.
</>
}
preview={preview}
>
<EmailLayout ctx={ctx} preview={preview}>
{children}
<Text>
<Section style={{ marginTop: 32, marginBottom: 32 }}>
<Button href={pollUrl}>View on {domain}</Button>
</Section>
<Text light={true}>
If you would like to stop receiving updates you can{" "}
<Link className="whitespace-nowrap" href={disableNotificationsUrl}>
turn notifications off
</Link>
.
</Text>
</EmailLayout>
);
Expand Down
32 changes: 21 additions & 11 deletions packages/emails/src/templates/_components/styled-components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import {

import { EmailContext } from "./email-context";

export const lightTextColor = "#4B5563";
export const darkTextColor = "#1F2937";
export const borderColor = "#E2E8F0";
export const Text = (
props: TextProps & { light?: boolean; small?: boolean },
Expand All @@ -23,7 +25,7 @@ export const Text = (
margin: "16px 0",
fontFamily,
fontSize: small ? "14px" : "16px",
color: light ? "#64748B" : "#334155",
color: light ? lightTextColor : darkTextColor,
lineHeight: "1.5",
...props.style,
}}
Expand All @@ -46,6 +48,11 @@ export const Button = (props: React.ComponentProps<typeof UnstyledButton>) => {
borderRadius: "4px",
padding: "12px 14px",
fontFamily,
boxSizing: "border-box",
display: "block",
width: "100%",
maxWidth: "100%",
textAlign: "center",
fontSize: "16px",
color: "white",
}}
Expand All @@ -62,23 +69,24 @@ export const Link = (props: LinkProps) => {
);
};

const fontSize = {
h1: "20px",
h2: "18px",
h3: "16px",
h4: "16px",
h5: "14px",
h6: "12px",
};

export const Heading = (
props: React.ComponentProps<typeof UnstyledHeading>,
) => {
const { as = "h3" } = props;
const fontSize = {
h1: "32px",
h2: "24px",
h3: "20px",
h4: "16px",
h5: "14px",
h6: "12px",
};
const { as = "h1" } = props;

return (
<UnstyledHeading
{...props}
as={as}
className="font-sans font-bold text-gray-900"
style={{
fontSize: fontSize[as],
...props.style,
Expand Down Expand Up @@ -146,3 +154,5 @@ export const trackingWide = {

export const fontFamily =
"'Inter UI', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif";

export const primaryColor = "#4F46E5";
15 changes: 10 additions & 5 deletions packages/emails/src/templates/finalized-host.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ import { Column, Row, Section } from "@react-email/components";

import { defaultEmailContext, EmailContext } from "./_components/email-context";
import { EmailLayout } from "./_components/email-layout";
import { borderColor, Button, Text } from "./_components/styled-components";
import {
borderColor,
Button,
Heading,
Text,
} from "./_components/styled-components";

export interface FinalizeHostEmailProps {
date: string;
Expand All @@ -18,7 +23,6 @@ export interface FinalizeHostEmailProps {
}

export const FinalizeHostEmail = ({
name = "Guest",
title = "Untitled Poll",
pollUrl = "https://rallly.co",
day = "12",
Expand All @@ -28,7 +32,8 @@ export const FinalizeHostEmail = ({
ctx = defaultEmailContext,
}: FinalizeHostEmailProps) => {
return (
<EmailLayout ctx={ctx} recipientName={name} preview="Final date booked!">
<EmailLayout ctx={ctx} preview="Final date booked!">
<Heading>Final date booked!</Heading>
<Text>
<strong>{title}</strong> has been booked for:
</Text>
Expand Down Expand Up @@ -73,9 +78,9 @@ export const FinalizeHostEmail = ({
<Text>
We&apos;ve notified participants and sent them calendar invites.
</Text>
<Text>
<Section style={{ marginTop: 32 }}>
<Button href={pollUrl}>View Event</Button>
</Text>
</Section>
</EmailLayout>
);
};
Expand Down
Loading
Loading