mirror of
https://github.com/lukevella/rallly.git
synced 2025-07-19 17:27:26 +02:00
💄 Updated transactional email templates (#1285)
This commit is contained in:
parent
4078618afd
commit
2f00dac998
15 changed files with 186 additions and 234 deletions
BIN
apps/web/public/images/rallly-logo-mark.png
Normal file
BIN
apps/web/public/images/rallly-logo-mark.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 841 B |
|
@ -49,6 +49,12 @@ export const env = createEnv({
|
||||||
* Example: "user@example.com, *@example.com, *@*.example.com"
|
* Example: "user@example.com, *@example.com, *@*.example.com"
|
||||||
*/
|
*/
|
||||||
ALLOWED_EMAILS: z.string().optional(),
|
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).
|
* Environment variables available on the client (and server).
|
||||||
|
@ -90,6 +96,9 @@ export const env = createEnv({
|
||||||
NEXT_PUBLIC_POSTHOG_API_KEY: process.env.NEXT_PUBLIC_POSTHOG_API_KEY,
|
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_POSTHOG_API_HOST: process.env.NEXT_PUBLIC_POSTHOG_API_HOST,
|
||||||
NEXT_PUBLIC_SELF_HOSTED: process.env.NEXT_PUBLIC_SELF_HOSTED,
|
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,
|
skipValidation: !!process.env.SKIP_ENV_VALIDATION,
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,25 +1,26 @@
|
||||||
import { EmailClient, SupportedEmailProviders } from "@rallly/emails";
|
import { EmailClient, SupportedEmailProviders } from "@rallly/emails";
|
||||||
|
|
||||||
|
import { env } from "@/env";
|
||||||
import { absoluteUrl } from "@/utils/absolute-url";
|
import { absoluteUrl } from "@/utils/absolute-url";
|
||||||
|
import { isSelfHosted } from "@/utils/constants";
|
||||||
const env = process.env["NODE" + "_ENV"];
|
|
||||||
|
|
||||||
export const emailClient = new EmailClient({
|
export const emailClient = new EmailClient({
|
||||||
openPreviews: env === "development",
|
openPreviews: env.NODE_ENV === "development",
|
||||||
provider: {
|
provider: {
|
||||||
name: (process.env.EMAIL_PROVIDER as SupportedEmailProviders) ?? "smtp",
|
name: (process.env.EMAIL_PROVIDER as SupportedEmailProviders) ?? "smtp",
|
||||||
},
|
},
|
||||||
mail: {
|
mail: {
|
||||||
from: {
|
from: {
|
||||||
name: (process.env.NOREPLY_EMAIL_NAME as string) || "Rallly",
|
name: env.NOREPLY_EMAIL_NAME,
|
||||||
address:
|
address: env.NOREPLY_EMAIL || env.SUPPORT_EMAIL,
|
||||||
(process.env.NOREPLY_EMAIL as string) ||
|
|
||||||
(process.env.SUPPORT_EMAIL as string),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
context: {
|
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(""),
|
baseUrl: absoluteUrl(""),
|
||||||
domain: absoluteUrl("").replace(/(^\w+:|^)\/\//, ""),
|
domain: absoluteUrl("").replace(/(^\w+:|^)\/\//, ""),
|
||||||
|
supportEmail: env.SUPPORT_EMAIL,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,10 +2,12 @@ export type EmailContext = {
|
||||||
logoUrl: string;
|
logoUrl: string;
|
||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
domain: string;
|
domain: string;
|
||||||
|
supportEmail: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const defaultEmailContext = {
|
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",
|
baseUrl: "https://rallly.co",
|
||||||
domain: "rallly.co",
|
domain: "rallly.co",
|
||||||
|
supportEmail: "support@rallly.co",
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,97 +4,56 @@ import {
|
||||||
Head,
|
Head,
|
||||||
Html,
|
Html,
|
||||||
Img,
|
Img,
|
||||||
Link,
|
|
||||||
Preview,
|
Preview,
|
||||||
|
Section,
|
||||||
} from "@react-email/components";
|
} from "@react-email/components";
|
||||||
|
|
||||||
import { EmailContext } from "./email-context";
|
import { EmailContext } from "./email-context";
|
||||||
import { fontFamily, Section, Text } from "./styled-components";
|
import { darkTextColor, fontFamily, Link, Text } from "./styled-components";
|
||||||
|
|
||||||
export interface EmailLayoutProps {
|
export interface EmailLayoutProps {
|
||||||
preview: string;
|
preview: string;
|
||||||
recipientName?: string;
|
|
||||||
footNote?: React.ReactNode;
|
|
||||||
ctx: EmailContext;
|
ctx: EmailContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
const containerStyles = {
|
const containerStyles = {
|
||||||
maxWidth: "600px",
|
maxWidth: "480px",
|
||||||
margin: "0 auto",
|
margin: "0 auto",
|
||||||
background: "white",
|
background: "white",
|
||||||
fontFamily,
|
fontFamily,
|
||||||
padding: 16,
|
padding: "32px 8px",
|
||||||
border: "1px solid #E2E8F0",
|
color: darkTextColor,
|
||||||
borderRadius: 5,
|
|
||||||
};
|
|
||||||
|
|
||||||
const sectionStyles = {
|
|
||||||
marginTop: "16px",
|
|
||||||
marginBottom: "16px",
|
|
||||||
};
|
|
||||||
|
|
||||||
const linkStyles = {
|
|
||||||
color: "#64748B",
|
|
||||||
marginRight: "8px",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const EmailLayout = ({
|
export const EmailLayout = ({
|
||||||
preview,
|
preview,
|
||||||
recipientName,
|
|
||||||
children,
|
children,
|
||||||
footNote,
|
|
||||||
ctx,
|
ctx,
|
||||||
}: React.PropsWithChildren<EmailLayoutProps>) => {
|
}: React.PropsWithChildren<EmailLayoutProps>) => {
|
||||||
const { logoUrl, baseUrl } = ctx;
|
const { logoUrl } = ctx;
|
||||||
return (
|
return (
|
||||||
<Html>
|
<Html>
|
||||||
<Head />
|
<Head />
|
||||||
<Preview>{preview}</Preview>
|
<Preview>{preview}</Preview>
|
||||||
<Body
|
<Body style={{ backgroundColor: "#FFFFFF" }}>
|
||||||
style={{
|
|
||||||
backgroundColor: "#F3F4F6",
|
|
||||||
paddingTop: 20,
|
|
||||||
paddingBottom: 20,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Container style={containerStyles}>
|
<Container style={containerStyles}>
|
||||||
<Img src={logoUrl} alt="Rallly" width={128} />
|
<Img
|
||||||
<Section style={sectionStyles}>
|
src={logoUrl}
|
||||||
{recipientName ? <Text>Hi {recipientName},</Text> : null}
|
width="32"
|
||||||
{children}
|
height="32"
|
||||||
{footNote ? (
|
|
||||||
<Text
|
|
||||||
style={{
|
style={{
|
||||||
color: "#64748B",
|
marginBottom: 32,
|
||||||
fontFamily,
|
|
||||||
paddingTop: 16,
|
|
||||||
marginTop: 32,
|
|
||||||
borderTop: "1px solid #e2e8f0",
|
|
||||||
}}
|
}}
|
||||||
>
|
alt="Rallly Logo"
|
||||||
{footNote}
|
/>
|
||||||
|
{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>
|
</Text>
|
||||||
) : null}
|
|
||||||
</Section>
|
|
||||||
<Section style={{ ...sectionStyles, fontSize: 14, marginBottom: 0 }}>
|
|
||||||
<Link style={linkStyles} href={baseUrl}>
|
|
||||||
Home
|
|
||||||
</Link>
|
|
||||||
<span> • </span>
|
|
||||||
<Link style={linkStyles} href="https://twitter.com/ralllyco">
|
|
||||||
Twitter
|
|
||||||
</Link>
|
|
||||||
<span> • </span>
|
|
||||||
<Link style={linkStyles} href="https://github.com/lukevella/rallly">
|
|
||||||
Github
|
|
||||||
</Link>
|
|
||||||
<span> • </span>
|
|
||||||
<Link
|
|
||||||
style={linkStyles}
|
|
||||||
href={`mailto:${process.env["SUPPORT_EMAIL"]}`}
|
|
||||||
>
|
|
||||||
Contact
|
|
||||||
</Link>
|
|
||||||
</Section>
|
</Section>
|
||||||
</Container>
|
</Container>
|
||||||
</Body>
|
</Body>
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { Section } from "@react-email/section";
|
||||||
|
|
||||||
import { EmailContext } from "./email-context";
|
import { EmailContext } from "./email-context";
|
||||||
import { EmailLayout } from "./email-layout";
|
import { EmailLayout } from "./email-layout";
|
||||||
import { Button, Link, Text } from "./styled-components";
|
import { Button, Link, Text } from "./styled-components";
|
||||||
|
@ -15,7 +17,6 @@ export interface NotificationEmailProps extends NotificationBaseProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const NotificationEmail = ({
|
export const NotificationEmail = ({
|
||||||
name,
|
|
||||||
pollUrl,
|
pollUrl,
|
||||||
disableNotificationsUrl,
|
disableNotificationsUrl,
|
||||||
preview,
|
preview,
|
||||||
|
@ -24,23 +25,17 @@ export const NotificationEmail = ({
|
||||||
}: React.PropsWithChildren<NotificationEmailProps>) => {
|
}: React.PropsWithChildren<NotificationEmailProps>) => {
|
||||||
const { domain } = ctx;
|
const { domain } = ctx;
|
||||||
return (
|
return (
|
||||||
<EmailLayout
|
<EmailLayout ctx={ctx} preview={preview}>
|
||||||
ctx={ctx}
|
{children}
|
||||||
recipientName={name}
|
<Section style={{ marginTop: 32, marginBottom: 32 }}>
|
||||||
footNote={
|
<Button href={pollUrl}>View on {domain}</Button>
|
||||||
<>
|
</Section>
|
||||||
|
<Text light={true}>
|
||||||
If you would like to stop receiving updates you can{" "}
|
If you would like to stop receiving updates you can{" "}
|
||||||
<Link className="whitespace-nowrap" href={disableNotificationsUrl}>
|
<Link className="whitespace-nowrap" href={disableNotificationsUrl}>
|
||||||
turn notifications off
|
turn notifications off
|
||||||
</Link>
|
</Link>
|
||||||
.
|
.
|
||||||
</>
|
|
||||||
}
|
|
||||||
preview={preview}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
<Text>
|
|
||||||
<Button href={pollUrl}>View on {domain}</Button>
|
|
||||||
</Text>
|
</Text>
|
||||||
</EmailLayout>
|
</EmailLayout>
|
||||||
);
|
);
|
||||||
|
|
|
@ -11,6 +11,8 @@ import {
|
||||||
|
|
||||||
import { EmailContext } from "./email-context";
|
import { EmailContext } from "./email-context";
|
||||||
|
|
||||||
|
export const lightTextColor = "#4B5563";
|
||||||
|
export const darkTextColor = "#1F2937";
|
||||||
export const borderColor = "#E2E8F0";
|
export const borderColor = "#E2E8F0";
|
||||||
export const Text = (
|
export const Text = (
|
||||||
props: TextProps & { light?: boolean; small?: boolean },
|
props: TextProps & { light?: boolean; small?: boolean },
|
||||||
|
@ -23,7 +25,7 @@ export const Text = (
|
||||||
margin: "16px 0",
|
margin: "16px 0",
|
||||||
fontFamily,
|
fontFamily,
|
||||||
fontSize: small ? "14px" : "16px",
|
fontSize: small ? "14px" : "16px",
|
||||||
color: light ? "#64748B" : "#334155",
|
color: light ? lightTextColor : darkTextColor,
|
||||||
lineHeight: "1.5",
|
lineHeight: "1.5",
|
||||||
...props.style,
|
...props.style,
|
||||||
}}
|
}}
|
||||||
|
@ -46,6 +48,11 @@ export const Button = (props: React.ComponentProps<typeof UnstyledButton>) => {
|
||||||
borderRadius: "4px",
|
borderRadius: "4px",
|
||||||
padding: "12px 14px",
|
padding: "12px 14px",
|
||||||
fontFamily,
|
fontFamily,
|
||||||
|
boxSizing: "border-box",
|
||||||
|
display: "block",
|
||||||
|
width: "100%",
|
||||||
|
maxWidth: "100%",
|
||||||
|
textAlign: "center",
|
||||||
fontSize: "16px",
|
fontSize: "16px",
|
||||||
color: "white",
|
color: "white",
|
||||||
}}
|
}}
|
||||||
|
@ -62,23 +69,24 @@ export const Link = (props: LinkProps) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Heading = (
|
const fontSize = {
|
||||||
props: React.ComponentProps<typeof UnstyledHeading>,
|
h1: "20px",
|
||||||
) => {
|
h2: "18px",
|
||||||
const { as = "h3" } = props;
|
h3: "16px",
|
||||||
const fontSize = {
|
|
||||||
h1: "32px",
|
|
||||||
h2: "24px",
|
|
||||||
h3: "20px",
|
|
||||||
h4: "16px",
|
h4: "16px",
|
||||||
h5: "14px",
|
h5: "14px",
|
||||||
h6: "12px",
|
h6: "12px",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const Heading = (
|
||||||
|
props: React.ComponentProps<typeof UnstyledHeading>,
|
||||||
|
) => {
|
||||||
|
const { as = "h1" } = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<UnstyledHeading
|
<UnstyledHeading
|
||||||
{...props}
|
{...props}
|
||||||
as={as}
|
as={as}
|
||||||
className="font-sans font-bold text-gray-900"
|
|
||||||
style={{
|
style={{
|
||||||
fontSize: fontSize[as],
|
fontSize: fontSize[as],
|
||||||
...props.style,
|
...props.style,
|
||||||
|
@ -146,3 +154,5 @@ export const trackingWide = {
|
||||||
|
|
||||||
export const fontFamily =
|
export const fontFamily =
|
||||||
"'Inter UI', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif";
|
"'Inter UI', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif";
|
||||||
|
|
||||||
|
export const primaryColor = "#4F46E5";
|
||||||
|
|
|
@ -2,7 +2,12 @@ import { Column, Row, Section } from "@react-email/components";
|
||||||
|
|
||||||
import { defaultEmailContext, EmailContext } from "./_components/email-context";
|
import { defaultEmailContext, EmailContext } from "./_components/email-context";
|
||||||
import { EmailLayout } from "./_components/email-layout";
|
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 {
|
export interface FinalizeHostEmailProps {
|
||||||
date: string;
|
date: string;
|
||||||
|
@ -18,7 +23,6 @@ export interface FinalizeHostEmailProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FinalizeHostEmail = ({
|
export const FinalizeHostEmail = ({
|
||||||
name = "Guest",
|
|
||||||
title = "Untitled Poll",
|
title = "Untitled Poll",
|
||||||
pollUrl = "https://rallly.co",
|
pollUrl = "https://rallly.co",
|
||||||
day = "12",
|
day = "12",
|
||||||
|
@ -28,7 +32,8 @@ export const FinalizeHostEmail = ({
|
||||||
ctx = defaultEmailContext,
|
ctx = defaultEmailContext,
|
||||||
}: FinalizeHostEmailProps) => {
|
}: FinalizeHostEmailProps) => {
|
||||||
return (
|
return (
|
||||||
<EmailLayout ctx={ctx} recipientName={name} preview="Final date booked!">
|
<EmailLayout ctx={ctx} preview="Final date booked!">
|
||||||
|
<Heading>Final date booked!</Heading>
|
||||||
<Text>
|
<Text>
|
||||||
<strong>{title}</strong> has been booked for:
|
<strong>{title}</strong> has been booked for:
|
||||||
</Text>
|
</Text>
|
||||||
|
@ -73,9 +78,9 @@ export const FinalizeHostEmail = ({
|
||||||
<Text>
|
<Text>
|
||||||
We've notified participants and sent them calendar invites.
|
We've notified participants and sent them calendar invites.
|
||||||
</Text>
|
</Text>
|
||||||
<Text>
|
<Section style={{ marginTop: 32 }}>
|
||||||
<Button href={pollUrl}>View Event</Button>
|
<Button href={pollUrl}>View Event</Button>
|
||||||
</Text>
|
</Section>
|
||||||
</EmailLayout>
|
</EmailLayout>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,7 +2,12 @@ import { Column, Row, Section } from "@react-email/components";
|
||||||
|
|
||||||
import { defaultEmailContext, EmailContext } from "./_components/email-context";
|
import { defaultEmailContext, EmailContext } from "./_components/email-context";
|
||||||
import { EmailLayout } from "./_components/email-layout";
|
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 FinalizeParticipantEmailProps {
|
export interface FinalizeParticipantEmailProps {
|
||||||
date: string;
|
date: string;
|
||||||
|
@ -19,7 +24,6 @@ export interface FinalizeParticipantEmailProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FinalizeParticipantEmail = ({
|
export const FinalizeParticipantEmail = ({
|
||||||
name = "Guest",
|
|
||||||
title = "Untitled Poll",
|
title = "Untitled Poll",
|
||||||
hostName = "Host",
|
hostName = "Host",
|
||||||
pollUrl = "https://rallly.co",
|
pollUrl = "https://rallly.co",
|
||||||
|
@ -30,12 +34,13 @@ export const FinalizeParticipantEmail = ({
|
||||||
ctx = defaultEmailContext,
|
ctx = defaultEmailContext,
|
||||||
}: FinalizeParticipantEmailProps) => {
|
}: FinalizeParticipantEmailProps) => {
|
||||||
return (
|
return (
|
||||||
<EmailLayout ctx={ctx} recipientName={name} preview="Final date booked!">
|
<EmailLayout ctx={ctx} preview="Final date booked!">
|
||||||
|
<Heading>Final date booked!</Heading>
|
||||||
<Text>
|
<Text>
|
||||||
<strong>{hostName}</strong> has booked <strong>{title}</strong> for the
|
<strong>{hostName}</strong> has booked <strong>{title}</strong> for the
|
||||||
following date:
|
following date:
|
||||||
</Text>
|
</Text>
|
||||||
<Section>
|
<Section data-testid="date-section">
|
||||||
<Row>
|
<Row>
|
||||||
<Column style={{ width: 48 }}>
|
<Column style={{ width: 48 }}>
|
||||||
<Section
|
<Section
|
||||||
|
@ -74,9 +79,9 @@ export const FinalizeParticipantEmail = ({
|
||||||
</Row>
|
</Row>
|
||||||
</Section>
|
</Section>
|
||||||
<Text>Please find attached a calendar invite for this event.</Text>
|
<Text>Please find attached a calendar invite for this event.</Text>
|
||||||
<Text>
|
<Section style={{ marginTop: 32 }}>
|
||||||
<Button href={pollUrl}>View Event</Button>
|
<Button href={pollUrl}>View Event</Button>
|
||||||
</Text>
|
</Section>
|
||||||
</EmailLayout>
|
</EmailLayout>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { Section } from "@react-email/components";
|
||||||
|
|
||||||
import { defaultEmailContext, EmailContext } from "./_components/email-context";
|
import { defaultEmailContext, EmailContext } from "./_components/email-context";
|
||||||
import { EmailLayout } from "./_components/email-layout";
|
import { EmailLayout } from "./_components/email-layout";
|
||||||
import {
|
import {
|
||||||
|
@ -17,43 +19,40 @@ interface LoginEmailProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const LoginEmail = ({
|
export const LoginEmail = ({
|
||||||
name = "Guest",
|
|
||||||
code = "123456",
|
code = "123456",
|
||||||
magicLink = "https://rallly.co",
|
magicLink = "https://rallly.co",
|
||||||
ctx = defaultEmailContext,
|
ctx = defaultEmailContext,
|
||||||
}: LoginEmailProps) => {
|
}: LoginEmailProps) => {
|
||||||
return (
|
return (
|
||||||
<EmailLayout
|
<EmailLayout ctx={ctx} preview="Use this link to log in on this device.">
|
||||||
ctx={ctx}
|
<Heading>Login</Heading>
|
||||||
footNote={
|
<Text>Enter this one-time 6-digit verification code:</Text>
|
||||||
<>
|
<Card style={{ textAlign: "center" }}>
|
||||||
You're receiving this email because a request was made to login
|
<Text
|
||||||
to <Domain ctx={ctx} />. If this wasn't you, let us know by
|
style={{
|
||||||
replying to this email.
|
...trackingWide,
|
||||||
</>
|
textAlign: "center",
|
||||||
}
|
fontSize: "32px",
|
||||||
recipientName={name}
|
fontWeight: "bold",
|
||||||
preview="Use this link to log in on this device."
|
}}
|
||||||
|
id="code"
|
||||||
>
|
>
|
||||||
<Text>
|
{code}
|
||||||
To log in to your account, please choose one of the following options:
|
|
||||||
</Text>
|
</Text>
|
||||||
<Card>
|
<Text style={{ textAlign: "center" }} light={true}>
|
||||||
<Heading>Option 1: Magic Link</Heading>
|
This code is valid for 15 minutes
|
||||||
<Text>Click this magic link to log in on this device.</Text>
|
</Text>
|
||||||
|
</Card>
|
||||||
|
<Section style={{ marginBottom: 32 }}>
|
||||||
<Button href={magicLink} id="magicLink">
|
<Button href={magicLink} id="magicLink">
|
||||||
Log in to {ctx.domain}
|
Log in to {ctx.domain}
|
||||||
</Button>
|
</Button>
|
||||||
<Text light={true}>This link will expire in 15 minutes.</Text>
|
</Section>
|
||||||
</Card>
|
<Text light>
|
||||||
<Card>
|
You're receiving this email because a request was made to login to{" "}
|
||||||
<Heading>Option 2: Verification Code</Heading>
|
<Domain ctx={ctx} />. If this wasn't you contact{" "}
|
||||||
<Text>Enter this one-time 6-digit verification code.</Text>
|
<a href={`mailto:${ctx.supportEmail}`}>{ctx.supportEmail}</a>.
|
||||||
<Heading as="h1" style={trackingWide} id="code">
|
</Text>
|
||||||
{code}
|
|
||||||
</Heading>
|
|
||||||
<Text light={true}>This code will expire in 15 minutes.</Text>
|
|
||||||
</Card>
|
|
||||||
</EmailLayout>
|
</EmailLayout>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { defaultEmailContext } from "./_components/email-context";
|
||||||
import NotificationEmail, {
|
import NotificationEmail, {
|
||||||
NotificationBaseProps,
|
NotificationBaseProps,
|
||||||
} from "./_components/notification-email";
|
} from "./_components/notification-email";
|
||||||
import { Text } from "./_components/styled-components";
|
import { Heading, Text } from "./_components/styled-components";
|
||||||
|
|
||||||
export interface NewCommentEmailProps extends NotificationBaseProps {
|
export interface NewCommentEmailProps extends NotificationBaseProps {
|
||||||
authorName: string;
|
authorName: string;
|
||||||
|
@ -25,6 +25,7 @@ export const NewCommentEmail = ({
|
||||||
disableNotificationsUrl={disableNotificationsUrl}
|
disableNotificationsUrl={disableNotificationsUrl}
|
||||||
preview="Go to your poll to see what they said."
|
preview="Go to your poll to see what they said."
|
||||||
>
|
>
|
||||||
|
<Heading>New Comment</Heading>
|
||||||
<Text>
|
<Text>
|
||||||
<strong>{authorName}</strong> has commented on <strong>{title}</strong>.
|
<strong>{authorName}</strong> has commented on <strong>{title}</strong>.
|
||||||
</Text>
|
</Text>
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
import { defaultEmailContext, EmailContext } from "./_components/email-context";
|
import { defaultEmailContext, EmailContext } from "./_components/email-context";
|
||||||
import { EmailLayout } from "./_components/email-layout";
|
import { EmailLayout } from "./_components/email-layout";
|
||||||
import { Button, Domain, Section, Text } from "./_components/styled-components";
|
import {
|
||||||
|
Button,
|
||||||
|
Domain,
|
||||||
|
Heading,
|
||||||
|
Section,
|
||||||
|
Text,
|
||||||
|
} from "./_components/styled-components";
|
||||||
|
|
||||||
interface NewParticipantConfirmationEmailProps {
|
interface NewParticipantConfirmationEmailProps {
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -10,24 +16,13 @@ interface NewParticipantConfirmationEmailProps {
|
||||||
}
|
}
|
||||||
export const NewParticipantConfirmationEmail = ({
|
export const NewParticipantConfirmationEmail = ({
|
||||||
title = "Untitled Poll",
|
title = "Untitled Poll",
|
||||||
name = "John",
|
|
||||||
editSubmissionUrl = "https://rallly.co",
|
editSubmissionUrl = "https://rallly.co",
|
||||||
ctx = defaultEmailContext,
|
ctx = defaultEmailContext,
|
||||||
}: NewParticipantConfirmationEmailProps) => {
|
}: NewParticipantConfirmationEmailProps) => {
|
||||||
const { domain } = ctx;
|
const { domain } = ctx;
|
||||||
return (
|
return (
|
||||||
<EmailLayout
|
<EmailLayout ctx={ctx} preview="To edit your response use the link below">
|
||||||
ctx={ctx}
|
<Heading>Poll Response Confirmation</Heading>
|
||||||
footNote={
|
|
||||||
<>
|
|
||||||
You are receiving this email because a response was submitted on{" "}
|
|
||||||
<Domain ctx={ctx} />. If this wasn't you, please ignore this
|
|
||||||
email.
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
recipientName={name}
|
|
||||||
preview="To edit your response use the link below"
|
|
||||||
>
|
|
||||||
<Text>
|
<Text>
|
||||||
Your response to <strong>{title}</strong> has been submitted.
|
Your response to <strong>{title}</strong> has been submitted.
|
||||||
</Text>
|
</Text>
|
||||||
|
@ -35,11 +30,15 @@ export const NewParticipantConfirmationEmail = ({
|
||||||
While the poll is still open you can change your response using the link
|
While the poll is still open you can change your response using the link
|
||||||
below.
|
below.
|
||||||
</Text>
|
</Text>
|
||||||
<Section>
|
<Section style={{ marginTop: 32 }}>
|
||||||
<Button id="editSubmissionUrl" href={editSubmissionUrl}>
|
<Button id="editSubmissionUrl" href={editSubmissionUrl}>
|
||||||
Review response on {domain}
|
Review response on {domain}
|
||||||
</Button>
|
</Button>
|
||||||
</Section>
|
</Section>
|
||||||
|
<Text light>
|
||||||
|
You are receiving this email because a response was submitted on{" "}
|
||||||
|
<Domain ctx={ctx} />. If this wasn't you, please ignore this email.
|
||||||
|
</Text>
|
||||||
</EmailLayout>
|
</EmailLayout>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { defaultEmailContext } from "./_components/email-context";
|
||||||
import NotificationEmail, {
|
import NotificationEmail, {
|
||||||
NotificationBaseProps,
|
NotificationBaseProps,
|
||||||
} from "./_components/notification-email";
|
} from "./_components/notification-email";
|
||||||
import { Text } from "./_components/styled-components";
|
import { Heading, Text } from "./_components/styled-components";
|
||||||
|
|
||||||
export interface NewParticipantEmailProps extends NotificationBaseProps {
|
export interface NewParticipantEmailProps extends NotificationBaseProps {
|
||||||
participantName: string;
|
participantName: string;
|
||||||
|
@ -25,10 +25,12 @@ export const NewParticipantEmail = ({
|
||||||
disableNotificationsUrl={disableNotificationsUrl}
|
disableNotificationsUrl={disableNotificationsUrl}
|
||||||
preview="Go to your poll to see the new response."
|
preview="Go to your poll to see the new response."
|
||||||
>
|
>
|
||||||
|
<Heading>New Response</Heading>
|
||||||
<Text>
|
<Text>
|
||||||
<strong>{participantName}</strong> has responded to{" "}
|
<strong>{participantName}</strong> has responded to{" "}
|
||||||
<strong>{title}</strong>.
|
<strong>{title}</strong>.
|
||||||
</Text>
|
</Text>
|
||||||
|
<Text>Go to your poll to see the new response.</Text>
|
||||||
</NotificationEmail>
|
</NotificationEmail>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
import { defaultEmailContext, EmailContext } from "./_components/email-context";
|
import { defaultEmailContext, EmailContext } from "./_components/email-context";
|
||||||
import { EmailLayout } from "./_components/email-layout";
|
import { EmailLayout } from "./_components/email-layout";
|
||||||
import { Button, Card, Link, Text } from "./_components/styled-components";
|
import {
|
||||||
|
Button,
|
||||||
|
Card,
|
||||||
|
Heading,
|
||||||
|
Link,
|
||||||
|
Text,
|
||||||
|
} from "./_components/styled-components";
|
||||||
|
|
||||||
export interface NewPollEmailProps {
|
export interface NewPollEmailProps {
|
||||||
title: string;
|
title: string;
|
||||||
|
@ -10,82 +16,28 @@ export interface NewPollEmailProps {
|
||||||
ctx: EmailContext;
|
ctx: EmailContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ShareLink = ({
|
|
||||||
title,
|
|
||||||
participantLink,
|
|
||||||
name,
|
|
||||||
children,
|
|
||||||
}: React.PropsWithChildren<{
|
|
||||||
name: string;
|
|
||||||
title: string;
|
|
||||||
participantLink: string;
|
|
||||||
}>) => {
|
|
||||||
return (
|
|
||||||
<Link
|
|
||||||
href={`mailto:?subject=${encodeURIComponent(
|
|
||||||
`Availability for ${title}`,
|
|
||||||
)}&body=${encodeURIComponent(
|
|
||||||
`Hi all,\nI'm trying to find the best date for ${title}.\nCan you please use the link below to choose your preferred dates:\n${participantLink}\nThank you.\n${name}`,
|
|
||||||
)}`}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const NewPollEmail = ({
|
export const NewPollEmail = ({
|
||||||
title = "Untitled Poll",
|
title = "Untitled Poll",
|
||||||
name = "John",
|
|
||||||
adminLink = "https://rallly.co/admin/abcdefg123",
|
adminLink = "https://rallly.co/admin/abcdefg123",
|
||||||
participantLink = "https://rallly.co/invite/wxyz9876",
|
participantLink = "https://rallly.co/invite/wxyz9876",
|
||||||
ctx = defaultEmailContext,
|
ctx = defaultEmailContext,
|
||||||
}: NewPollEmailProps) => {
|
}: NewPollEmailProps) => {
|
||||||
const { baseUrl, domain } = ctx;
|
|
||||||
return (
|
return (
|
||||||
<EmailLayout
|
<EmailLayout
|
||||||
ctx={ctx}
|
ctx={ctx}
|
||||||
footNote={
|
|
||||||
<>
|
|
||||||
You are receiving this email because a new poll was created with this
|
|
||||||
email address on <Link href={baseUrl}>{domain}</Link>. If this
|
|
||||||
wasn't you, please ignore this email.
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
recipientName={name}
|
|
||||||
preview="Share your participant link to start collecting responses."
|
preview="Share your participant link to start collecting responses."
|
||||||
>
|
>
|
||||||
|
<Heading>New Poll Created</Heading>
|
||||||
<Text>
|
<Text>
|
||||||
Your poll has been successfully created! Here are the details:
|
Your meeting poll titled <strong>{`"${title}"`}</strong> is ready! Share
|
||||||
|
it using the link below:
|
||||||
</Text>
|
</Text>
|
||||||
<Card>
|
<Card style={{ textAlign: "center" }}>
|
||||||
<Text>
|
<Text style={{ textAlign: "center" }}>
|
||||||
<strong>Title:</strong> {title}
|
|
||||||
<br />
|
|
||||||
<strong>Invite Link:</strong>{" "}
|
|
||||||
<Link href={participantLink}>{participantLink}</Link>
|
<Link href={participantLink}>{participantLink}</Link>
|
||||||
</Text>
|
</Text>
|
||||||
<Text>
|
|
||||||
<ShareLink
|
|
||||||
title={title}
|
|
||||||
name={name}
|
|
||||||
participantLink={participantLink}
|
|
||||||
>
|
|
||||||
Share via email
|
|
||||||
</ShareLink>
|
|
||||||
</Text>
|
|
||||||
</Card>
|
</Card>
|
||||||
<Text>
|
|
||||||
To invite participants to your poll, simply share the{" "}
|
|
||||||
<strong>Invite Link</strong> above with them. They'll be able to
|
|
||||||
vote on their preferred meeting times and dates.
|
|
||||||
</Text>
|
|
||||||
<Text>
|
|
||||||
If you need to make any changes to your poll, or if you want to see the
|
|
||||||
results so far, just click on the button below:
|
|
||||||
</Text>
|
|
||||||
<Text>
|
|
||||||
<Button href={adminLink}>Manage Poll →</Button>
|
<Button href={adminLink}>Manage Poll →</Button>
|
||||||
</Text>
|
|
||||||
</EmailLayout>
|
</EmailLayout>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
|
import { Section } from "@react-email/section";
|
||||||
|
|
||||||
import { defaultEmailContext, EmailContext } from "./_components/email-context";
|
import { defaultEmailContext, EmailContext } from "./_components/email-context";
|
||||||
import { EmailLayout } from "./_components/email-layout";
|
import { EmailLayout } from "./_components/email-layout";
|
||||||
import {
|
import {
|
||||||
|
Card,
|
||||||
Domain,
|
Domain,
|
||||||
Heading,
|
Heading,
|
||||||
Text,
|
Text,
|
||||||
|
@ -17,24 +20,34 @@ export const RegisterEmail = ({
|
||||||
ctx = defaultEmailContext,
|
ctx = defaultEmailContext,
|
||||||
}: RegisterEmailProps) => {
|
}: RegisterEmailProps) => {
|
||||||
return (
|
return (
|
||||||
<EmailLayout
|
<EmailLayout ctx={ctx} preview={`Your 6-digit code is: ${code}`}>
|
||||||
ctx={ctx}
|
<Heading>Verify your email address</Heading>
|
||||||
footNote={
|
|
||||||
<>
|
|
||||||
You're receiving this email because a request was made to
|
|
||||||
register an account on <Domain ctx={ctx} />. If this wasn't you,
|
|
||||||
please ignore this email.
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
preview={`Your 6-digit code is: ${code}`}
|
|
||||||
>
|
|
||||||
<Text>
|
<Text>
|
||||||
Please use the following 6-digit verification code to verify your email:
|
Please use the following 6-digit verification code to verify your email:
|
||||||
</Text>
|
</Text>
|
||||||
<Heading as="h1" style={{ ...trackingWide }} id="code">
|
<Card style={{ textAlign: "center" }}>
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
...trackingWide,
|
||||||
|
textAlign: "center",
|
||||||
|
fontSize: "32px",
|
||||||
|
fontWeight: "bold",
|
||||||
|
}}
|
||||||
|
id="code"
|
||||||
|
>
|
||||||
{code}
|
{code}
|
||||||
</Heading>
|
</Text>
|
||||||
<Text>This code is valid for 15 minutes</Text>
|
<Text style={{ textAlign: "center" }} light={true}>
|
||||||
|
This code is valid for 15 minutes
|
||||||
|
</Text>
|
||||||
|
</Card>
|
||||||
|
<Section>
|
||||||
|
<Text light={true}>
|
||||||
|
You're receiving this email because a request was made to
|
||||||
|
register an account on <Domain ctx={ctx} />. If this wasn't you,
|
||||||
|
please ignore this email.
|
||||||
|
</Text>
|
||||||
|
</Section>
|
||||||
</EmailLayout>
|
</EmailLayout>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue