mirror of
https://github.com/lukevella/rallly.git
synced 2025-07-29 22:27:25 +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
|
@ -1,13 +0,0 @@
|
|||
export type EmailContext = {
|
||||
logoUrl: string;
|
||||
baseUrl: string;
|
||||
domain: string;
|
||||
supportEmail: string;
|
||||
};
|
||||
|
||||
export const defaultEmailContext = {
|
||||
logoUrl: "https://rallly-public.s3.amazonaws.com/images/rallly-logo-mark.png",
|
||||
baseUrl: "https://rallly.co",
|
||||
domain: "rallly.co",
|
||||
supportEmail: "support@rallly.co",
|
||||
};
|
|
@ -1,62 +0,0 @@
|
|||
import {
|
||||
Body,
|
||||
Container,
|
||||
Head,
|
||||
Html,
|
||||
Img,
|
||||
Preview,
|
||||
Section,
|
||||
} from "@react-email/components";
|
||||
|
||||
import { EmailContext } from "./email-context";
|
||||
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, 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>
|
||||
</Html>
|
||||
);
|
||||
};
|
|
@ -1,44 +0,0 @@
|
|||
import { Section } from "@react-email/section";
|
||||
|
||||
import { EmailContext } from "./email-context";
|
||||
import { EmailLayout } from "./email-layout";
|
||||
import { Button, Link, Text } from "./styled-components";
|
||||
|
||||
export interface NotificationBaseProps {
|
||||
name: string;
|
||||
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}>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>
|
||||
);
|
||||
};
|
||||
|
||||
export default NotificationEmail;
|
|
@ -1,158 +0,0 @@
|
|||
import {
|
||||
Button as UnstyledButton,
|
||||
Heading as UnstyledHeading,
|
||||
Link as UnstyledLink,
|
||||
LinkProps,
|
||||
Section as UnstyledSection,
|
||||
SectionProps,
|
||||
Text as UnstyledText,
|
||||
TextProps,
|
||||
} from "@react-email/components";
|
||||
|
||||
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 },
|
||||
) => {
|
||||
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";
|
|
@ -1,13 +1,14 @@
|
|||
import { Column, Row, Section } from "@react-email/components";
|
||||
import { Trans } from "react-i18next/TransWithoutContext";
|
||||
|
||||
import { defaultEmailContext, EmailContext } from "./_components/email-context";
|
||||
import { EmailLayout } from "./_components/email-layout";
|
||||
import { EmailLayout } from "../components/email-layout";
|
||||
import {
|
||||
borderColor,
|
||||
Button,
|
||||
Heading,
|
||||
Text,
|
||||
} from "./_components/styled-components";
|
||||
} from "../components/styled-components";
|
||||
import type { EmailContext } from "../types";
|
||||
|
||||
export interface FinalizeHostEmailProps {
|
||||
date: string;
|
||||
|
@ -22,20 +23,43 @@ export interface FinalizeHostEmailProps {
|
|||
ctx: EmailContext;
|
||||
}
|
||||
|
||||
export const FinalizeHostEmail = ({
|
||||
title = "Untitled Poll",
|
||||
pollUrl = "https://rallly.co",
|
||||
day = "12",
|
||||
dow = "Fri",
|
||||
date = "Friday, 12th June 2020",
|
||||
time = "6:00 PM to 11:00 PM BST",
|
||||
ctx = defaultEmailContext,
|
||||
const FinalizeHostEmail = ({
|
||||
title,
|
||||
pollUrl,
|
||||
day,
|
||||
dow,
|
||||
date,
|
||||
time,
|
||||
ctx,
|
||||
}: FinalizeHostEmailProps) => {
|
||||
return (
|
||||
<EmailLayout ctx={ctx} preview="Final date booked!">
|
||||
<Heading>Final date booked!</Heading>
|
||||
<EmailLayout
|
||||
ctx={ctx}
|
||||
preview={ctx.t("finalizeHost_preview", {
|
||||
ns: "emails",
|
||||
defaultValue:
|
||||
"Final date booked! We've notified participants and sent them calendar invites.",
|
||||
title,
|
||||
})}
|
||||
>
|
||||
<Heading>
|
||||
{ctx.t("finalizeHost_heading", {
|
||||
defaultValue: "Final date booked!",
|
||||
ns: "emails",
|
||||
})}
|
||||
</Heading>
|
||||
<Text>
|
||||
<strong>{title}</strong> has been booked for:
|
||||
<Trans
|
||||
i18n={ctx.i18n}
|
||||
t={ctx.t}
|
||||
i18nKey="finalizeHost_content"
|
||||
ns="emails"
|
||||
values={{ title }}
|
||||
components={{
|
||||
b: <strong />,
|
||||
}}
|
||||
defaults="<b>{{title}}</b> has been booked for:"
|
||||
/>
|
||||
</Text>
|
||||
<Section>
|
||||
<Row>
|
||||
|
@ -76,13 +100,33 @@ export const FinalizeHostEmail = ({
|
|||
</Row>
|
||||
</Section>
|
||||
<Text>
|
||||
We've notified participants and sent them calendar invites.
|
||||
{ctx.t("finalizeHost_content2", {
|
||||
defaultValue:
|
||||
"We've notified participants and sent them calendar invites.",
|
||||
ns: "emails",
|
||||
})}
|
||||
</Text>
|
||||
<Section style={{ marginTop: 32 }}>
|
||||
<Button href={pollUrl}>View Event</Button>
|
||||
<Button href={pollUrl}>
|
||||
{ctx.t("finalizeHost_button", {
|
||||
defaultValue: "View Event",
|
||||
ns: "emails",
|
||||
})}
|
||||
</Button>
|
||||
</Section>
|
||||
</EmailLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default FinalizeHostEmail;
|
||||
FinalizeHostEmail.getSubject = (
|
||||
props: FinalizeHostEmailProps,
|
||||
ctx: EmailContext,
|
||||
) => {
|
||||
return ctx.t("finalizeHost_subject", {
|
||||
defaultValue: "Date booked for {{title}}",
|
||||
title: props.title,
|
||||
ns: "emails",
|
||||
});
|
||||
};
|
||||
|
||||
export { FinalizeHostEmail };
|
||||
|
|
|
@ -1,44 +1,62 @@
|
|||
import { Column, Row, Section } from "@react-email/components";
|
||||
import { Trans } from "react-i18next/TransWithoutContext";
|
||||
|
||||
import { defaultEmailContext, EmailContext } from "./_components/email-context";
|
||||
import { EmailLayout } from "./_components/email-layout";
|
||||
import { EmailLayout } from "../components/email-layout";
|
||||
import {
|
||||
borderColor,
|
||||
Button,
|
||||
Heading,
|
||||
Text,
|
||||
} from "./_components/styled-components";
|
||||
} from "../components/styled-components";
|
||||
import type { EmailContext } from "../types";
|
||||
|
||||
export interface FinalizeParticipantEmailProps {
|
||||
date: string;
|
||||
day: string;
|
||||
dow: string;
|
||||
time: string;
|
||||
name: string;
|
||||
title: string;
|
||||
hostName: string;
|
||||
location: string | null;
|
||||
pollUrl: string;
|
||||
attendees: string[];
|
||||
ctx: EmailContext;
|
||||
}
|
||||
|
||||
export const FinalizeParticipantEmail = ({
|
||||
title = "Untitled Poll",
|
||||
hostName = "Host",
|
||||
pollUrl = "https://rallly.co",
|
||||
day = "12",
|
||||
dow = "Fri",
|
||||
date = "Friday, 12th June 2020",
|
||||
time = "6:00 PM to 11:00 PM BST",
|
||||
ctx = defaultEmailContext,
|
||||
const FinalizeParticipantEmail = ({
|
||||
title,
|
||||
hostName,
|
||||
pollUrl,
|
||||
day,
|
||||
dow,
|
||||
date,
|
||||
time,
|
||||
ctx,
|
||||
}: FinalizeParticipantEmailProps) => {
|
||||
return (
|
||||
<EmailLayout ctx={ctx} preview="Final date booked!">
|
||||
<Heading>Final date booked!</Heading>
|
||||
<EmailLayout
|
||||
ctx={ctx}
|
||||
preview={ctx.t("finalizeParticipant_preview", {
|
||||
defaultValue: "Final date booked!",
|
||||
ns: "emails",
|
||||
})}
|
||||
>
|
||||
<Heading>
|
||||
{ctx.t("finalizeParticipant_heading", {
|
||||
defaultValue: "Final date booked!",
|
||||
ns: "emails",
|
||||
})}
|
||||
</Heading>
|
||||
<Text>
|
||||
<strong>{hostName}</strong> has booked <strong>{title}</strong> for the
|
||||
following date:
|
||||
<Trans
|
||||
i18n={ctx.i18n}
|
||||
t={ctx.t}
|
||||
i18nKey="finalizeParticipant_content"
|
||||
ns="emails"
|
||||
defaults="<b>{{hostName}}</b> has booked <b>{{title}}</b> for the following date:"
|
||||
values={{ hostName, title }}
|
||||
components={{
|
||||
b: <strong />,
|
||||
}}
|
||||
/>
|
||||
</Text>
|
||||
<Section data-testid="date-section">
|
||||
<Row>
|
||||
|
@ -78,7 +96,12 @@ export const FinalizeParticipantEmail = ({
|
|||
</Column>
|
||||
</Row>
|
||||
</Section>
|
||||
<Text>Please find attached a calendar invite for this event.</Text>
|
||||
<Text>
|
||||
{ctx.t("finalizeParticipant_content2", {
|
||||
defaultValue:
|
||||
"Please find attached a calendar invite for this event.",
|
||||
})}
|
||||
</Text>
|
||||
<Section style={{ marginTop: 32 }}>
|
||||
<Button href={pollUrl}>View Event</Button>
|
||||
</Section>
|
||||
|
@ -86,4 +109,15 @@ export const FinalizeParticipantEmail = ({
|
|||
);
|
||||
};
|
||||
|
||||
export default FinalizeParticipantEmail;
|
||||
FinalizeParticipantEmail.getSubject = (
|
||||
props: FinalizeParticipantEmailProps,
|
||||
ctx: EmailContext,
|
||||
) => {
|
||||
return ctx.t("finalizeParticipant_subject", {
|
||||
defaultValue: "Date booked for {{title}}",
|
||||
title: props.title,
|
||||
ns: "emails",
|
||||
});
|
||||
};
|
||||
|
||||
export { FinalizeParticipantEmail };
|
||||
|
|
|
@ -1,32 +1,42 @@
|
|||
import { Section } from "@react-email/components";
|
||||
import { Trans } from "react-i18next/TransWithoutContext";
|
||||
|
||||
import { defaultEmailContext, EmailContext } from "./_components/email-context";
|
||||
import { EmailLayout } from "./_components/email-layout";
|
||||
import { EmailLayout } from "../components/email-layout";
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Domain,
|
||||
Heading,
|
||||
Link,
|
||||
Text,
|
||||
trackingWide,
|
||||
} from "./_components/styled-components";
|
||||
} from "../components/styled-components";
|
||||
import type { EmailContext } from "../types";
|
||||
|
||||
interface LoginEmailProps {
|
||||
name: string;
|
||||
code: string;
|
||||
magicLink: string;
|
||||
ctx: EmailContext;
|
||||
}
|
||||
|
||||
export const LoginEmail = ({
|
||||
code = "123456",
|
||||
magicLink = "https://rallly.co",
|
||||
ctx = defaultEmailContext,
|
||||
}: LoginEmailProps) => {
|
||||
export const LoginEmail = ({ code, magicLink, ctx }: LoginEmailProps) => {
|
||||
return (
|
||||
<EmailLayout ctx={ctx} preview="Use this link to log in on this device.">
|
||||
<Heading>Login</Heading>
|
||||
<Text>Enter this one-time 6-digit verification code:</Text>
|
||||
<EmailLayout
|
||||
ctx={ctx}
|
||||
preview={ctx.t("login_preview", {
|
||||
defaultValue: "Use this link to log in on this device.",
|
||||
ns: "emails",
|
||||
})}
|
||||
>
|
||||
<Heading>
|
||||
{ctx.t("login_heading", { defaultValue: "Login", ns: "emails" })}
|
||||
</Heading>
|
||||
<Text>
|
||||
{ctx.t("login_content", {
|
||||
defaultValue: "Enter this one-time 6-digit verification code:",
|
||||
ns: "emails",
|
||||
})}
|
||||
</Text>
|
||||
<Card style={{ textAlign: "center" }}>
|
||||
<Text
|
||||
style={{
|
||||
|
@ -40,21 +50,48 @@ export const LoginEmail = ({
|
|||
{code}
|
||||
</Text>
|
||||
<Text style={{ textAlign: "center" }} light={true}>
|
||||
This code is valid for 15 minutes
|
||||
{ctx.t("login_codeValid", {
|
||||
defaultValue: "This code is valid for 15 minutes",
|
||||
ns: "emails",
|
||||
})}
|
||||
</Text>
|
||||
</Card>
|
||||
<Section style={{ marginBottom: 32 }}>
|
||||
<Button href={magicLink} id="magicLink">
|
||||
Log in to {ctx.domain}
|
||||
<Trans
|
||||
i18n={ctx.i18n}
|
||||
t={ctx.t}
|
||||
i18nKey="login_button"
|
||||
defaults="Log in to {domain}"
|
||||
values={{ domain: ctx.domain }}
|
||||
ns="emails"
|
||||
/>
|
||||
</Button>
|
||||
</Section>
|
||||
<Text light>
|
||||
You're receiving this email because a request was made to login to{" "}
|
||||
<Domain ctx={ctx} />. If this wasn't you contact{" "}
|
||||
<a href={`mailto:${ctx.supportEmail}`}>{ctx.supportEmail}</a>.
|
||||
<Trans
|
||||
i18n={ctx.i18n}
|
||||
t={ctx.t}
|
||||
i18nKey="login_content2"
|
||||
defaults="You're receiving this email because a request was made to login to <domain />. If this wasn't you contact <a>{supportEmail}</a>."
|
||||
values={{ supportEmail: ctx.supportEmail }}
|
||||
components={{
|
||||
domain: <Domain ctx={ctx} />,
|
||||
a: <Link href={`mailto:${ctx.supportEmail}`} />,
|
||||
}}
|
||||
ns="emails"
|
||||
/>
|
||||
</Text>
|
||||
</EmailLayout>
|
||||
);
|
||||
};
|
||||
|
||||
LoginEmail.getSubject = (props: LoginEmailProps, ctx: EmailContext) => {
|
||||
return ctx.t("login_subject", {
|
||||
defaultValue: "{{code}} is your 6-digit code",
|
||||
code: props.code,
|
||||
ns: "emails",
|
||||
});
|
||||
};
|
||||
|
||||
export default LoginEmail;
|
||||
|
|
|
@ -1,36 +1,70 @@
|
|||
import { defaultEmailContext } from "./_components/email-context";
|
||||
import { Trans } from "react-i18next/TransWithoutContext";
|
||||
|
||||
import NotificationEmail, {
|
||||
NotificationBaseProps,
|
||||
} from "./_components/notification-email";
|
||||
import { Heading, Text } from "./_components/styled-components";
|
||||
} from "../components/notification-email";
|
||||
import { Heading, Text } from "../components/styled-components";
|
||||
import type { EmailContext } from "../types";
|
||||
|
||||
export interface NewCommentEmailProps extends NotificationBaseProps {
|
||||
authorName: string;
|
||||
}
|
||||
|
||||
export const NewCommentEmail = ({
|
||||
name = "Guest",
|
||||
title = "Untitled Poll",
|
||||
authorName = "Someone",
|
||||
pollUrl = "https://rallly.co",
|
||||
disableNotificationsUrl = "https://rallly.co",
|
||||
ctx = defaultEmailContext,
|
||||
const NewCommentEmail = ({
|
||||
title,
|
||||
authorName,
|
||||
pollUrl,
|
||||
disableNotificationsUrl,
|
||||
ctx,
|
||||
}: NewCommentEmailProps) => {
|
||||
return (
|
||||
<NotificationEmail
|
||||
ctx={ctx}
|
||||
name={name}
|
||||
title={title}
|
||||
pollUrl={pollUrl}
|
||||
disableNotificationsUrl={disableNotificationsUrl}
|
||||
preview="Go to your poll to see what they said."
|
||||
preview={ctx.t("newComment_preview", {
|
||||
ns: "emails",
|
||||
defaultValue: "Go to your poll to see what they said.",
|
||||
})}
|
||||
>
|
||||
<Heading>New Comment</Heading>
|
||||
<Heading>
|
||||
<Trans
|
||||
i18n={ctx.i18n}
|
||||
ns="emails"
|
||||
i18nKey="newComment_heading"
|
||||
defaults="New Comment"
|
||||
/>
|
||||
</Heading>
|
||||
<Text>
|
||||
<strong>{authorName}</strong> has commented on <strong>{title}</strong>.
|
||||
<Trans
|
||||
i18n={ctx.i18n}
|
||||
ns="emails"
|
||||
i18nKey="newComment_content"
|
||||
defaults="<b>{{authorName}}</b> has commented on <b>{{title}}</b>."
|
||||
components={{
|
||||
b: <strong />,
|
||||
}}
|
||||
values={{
|
||||
authorName,
|
||||
title,
|
||||
}}
|
||||
/>
|
||||
</Text>
|
||||
</NotificationEmail>
|
||||
);
|
||||
};
|
||||
|
||||
export default NewCommentEmail;
|
||||
NewCommentEmail.getSubject = (
|
||||
props: NewCommentEmailProps,
|
||||
ctx: EmailContext,
|
||||
) => {
|
||||
return ctx.t("newComment_subject", {
|
||||
ns: "emails",
|
||||
defaultValue: "{{authorName}} has commented on {{title}}",
|
||||
authorName: props.authorName,
|
||||
title: props.title,
|
||||
});
|
||||
};
|
||||
|
||||
export { NewCommentEmail };
|
||||
|
|
|
@ -1,46 +1,99 @@
|
|||
import { defaultEmailContext, EmailContext } from "./_components/email-context";
|
||||
import { EmailLayout } from "./_components/email-layout";
|
||||
import { Trans } from "react-i18next/TransWithoutContext";
|
||||
|
||||
import { EmailLayout } from "../components/email-layout";
|
||||
import {
|
||||
Button,
|
||||
Domain,
|
||||
Heading,
|
||||
Section,
|
||||
Text,
|
||||
} from "./_components/styled-components";
|
||||
} from "../components/styled-components";
|
||||
import type { EmailContext } from "../types";
|
||||
|
||||
interface NewParticipantConfirmationEmailProps {
|
||||
name: string;
|
||||
title: string;
|
||||
editSubmissionUrl: string;
|
||||
ctx: EmailContext;
|
||||
}
|
||||
export const NewParticipantConfirmationEmail = ({
|
||||
title = "Untitled Poll",
|
||||
editSubmissionUrl = "https://rallly.co",
|
||||
ctx = defaultEmailContext,
|
||||
title,
|
||||
editSubmissionUrl,
|
||||
ctx,
|
||||
}: NewParticipantConfirmationEmailProps) => {
|
||||
const { domain } = ctx;
|
||||
return (
|
||||
<EmailLayout ctx={ctx} preview="To edit your response use the link below">
|
||||
<Heading>Poll Response Confirmation</Heading>
|
||||
<EmailLayout
|
||||
ctx={ctx}
|
||||
preview={ctx.t("newParticipantConfirmation_preview", {
|
||||
defaultValue: "To edit your response use the link below",
|
||||
ns: "emails",
|
||||
})}
|
||||
>
|
||||
<Heading>
|
||||
{ctx.t("newParticipantConfirmation_heading", {
|
||||
defaultValue: "Poll Response Confirmation",
|
||||
ns: "emails",
|
||||
})}
|
||||
</Heading>
|
||||
<Text>
|
||||
Your response to <strong>{title}</strong> has been submitted.
|
||||
<Trans
|
||||
i18n={ctx.i18n}
|
||||
t={ctx.t}
|
||||
i18nKey="newParticipantConfirmation_content"
|
||||
defaults="Your response to <b>{{title}}</b> has been submitted."
|
||||
components={{
|
||||
b: <strong />,
|
||||
}}
|
||||
values={{ title }}
|
||||
ns="emails"
|
||||
/>
|
||||
</Text>
|
||||
<Text>
|
||||
While the poll is still open you can change your response using the link
|
||||
below.
|
||||
<Trans
|
||||
i18n={ctx.i18n}
|
||||
t={ctx.t}
|
||||
i18nKey="newParticipantConfirmation_content2"
|
||||
defaults="While the poll is still open you can change your response using the link below."
|
||||
ns="emails"
|
||||
/>
|
||||
</Text>
|
||||
<Section style={{ marginTop: 32 }}>
|
||||
<Button id="editSubmissionUrl" href={editSubmissionUrl}>
|
||||
Review response on {domain}
|
||||
<Trans
|
||||
i18n={ctx.i18n}
|
||||
t={ctx.t}
|
||||
i18nKey="newParticipantConfirmation_button"
|
||||
defaults="Review response on {domain}"
|
||||
values={{ domain }}
|
||||
ns="emails"
|
||||
/>
|
||||
</Button>
|
||||
</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.
|
||||
<Trans
|
||||
i18n={ctx.i18n}
|
||||
t={ctx.t}
|
||||
i18nKey="newParticipantConfirmation_footnote"
|
||||
defaults="You are receiving this email because a response was submitted on <domain />. If this wasn't you, please ignore this email."
|
||||
components={{
|
||||
domain: <Domain ctx={ctx} />,
|
||||
}}
|
||||
ns="emails"
|
||||
/>
|
||||
</Text>
|
||||
</EmailLayout>
|
||||
);
|
||||
};
|
||||
|
||||
NewParticipantConfirmationEmail.getSubject = (
|
||||
props: NewParticipantConfirmationEmailProps,
|
||||
ctx: EmailContext,
|
||||
) => {
|
||||
return ctx.t("newParticipantConfirmation_subject", {
|
||||
defaultValue: "Thanks for responding to {{title}}",
|
||||
title: props.title,
|
||||
ns: "emails",
|
||||
});
|
||||
};
|
||||
|
||||
export default NewParticipantConfirmationEmail;
|
||||
|
|
|
@ -1,38 +1,75 @@
|
|||
import { defaultEmailContext } from "./_components/email-context";
|
||||
import { Trans } from "react-i18next/TransWithoutContext";
|
||||
|
||||
import NotificationEmail, {
|
||||
NotificationBaseProps,
|
||||
} from "./_components/notification-email";
|
||||
import { Heading, Text } from "./_components/styled-components";
|
||||
} from "../components/notification-email";
|
||||
import { Heading, Text } from "../components/styled-components";
|
||||
import type { EmailContext } from "../types";
|
||||
|
||||
export interface NewParticipantEmailProps extends NotificationBaseProps {
|
||||
participantName: string;
|
||||
}
|
||||
|
||||
export const NewParticipantEmail = ({
|
||||
name = "John",
|
||||
title = "Untitled Poll",
|
||||
participantName = "Someone",
|
||||
pollUrl = "https://rallly.co",
|
||||
disableNotificationsUrl = "https://rallly.co",
|
||||
ctx = defaultEmailContext,
|
||||
const NewParticipantEmail = ({
|
||||
title,
|
||||
participantName,
|
||||
pollUrl,
|
||||
disableNotificationsUrl,
|
||||
ctx,
|
||||
}: NewParticipantEmailProps) => {
|
||||
return (
|
||||
<NotificationEmail
|
||||
ctx={ctx}
|
||||
name={name}
|
||||
title={title}
|
||||
pollUrl={pollUrl}
|
||||
disableNotificationsUrl={disableNotificationsUrl}
|
||||
preview="Go to your poll to see the new response."
|
||||
preview={ctx.t("newParticipant_preview", {
|
||||
defaultValue: "Go to your poll to see the new response.",
|
||||
ns: "emails",
|
||||
})}
|
||||
>
|
||||
<Heading>New Response</Heading>
|
||||
<Heading>
|
||||
{ctx.t("newParticipant_heading", {
|
||||
defaultValue: "New Response",
|
||||
ns: "emails",
|
||||
})}
|
||||
</Heading>
|
||||
<Text>
|
||||
<strong>{participantName}</strong> has responded to{" "}
|
||||
<strong>{title}</strong>.
|
||||
<Trans
|
||||
i18n={ctx.i18n}
|
||||
t={ctx.t}
|
||||
i18nKey="newParticipant_content"
|
||||
ns="emails"
|
||||
defaults="<b>{{name}}</b> has responded to <b>{{title}}</b>."
|
||||
components={{
|
||||
b: <strong />,
|
||||
}}
|
||||
values={{ name: participantName, title }}
|
||||
/>
|
||||
</Text>
|
||||
<Text>
|
||||
<Trans
|
||||
i18n={ctx.i18n}
|
||||
t={ctx.t}
|
||||
i18nKey="newParticipant_content2"
|
||||
defaults="Go to your poll to see the new response."
|
||||
ns="emails"
|
||||
/>
|
||||
</Text>
|
||||
<Text>Go to your poll to see the new response.</Text>
|
||||
</NotificationEmail>
|
||||
);
|
||||
};
|
||||
|
||||
export default NewParticipantEmail;
|
||||
NewParticipantEmail.getSubject = (
|
||||
props: NewParticipantEmailProps,
|
||||
ctx: EmailContext,
|
||||
) => {
|
||||
return ctx.t("newParticipant_subject", {
|
||||
defaultValue: "{{name}} has responded to {{title}}",
|
||||
name: props.participantName,
|
||||
title: props.title,
|
||||
ns: "emails",
|
||||
});
|
||||
};
|
||||
|
||||
export { NewParticipantEmail };
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
import { defaultEmailContext, EmailContext } from "./_components/email-context";
|
||||
import { EmailLayout } from "./_components/email-layout";
|
||||
import { Trans } from "react-i18next/TransWithoutContext";
|
||||
|
||||
import { EmailLayout } from "../components/email-layout";
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Heading,
|
||||
Link,
|
||||
Text,
|
||||
} from "./_components/styled-components";
|
||||
} from "../components/styled-components";
|
||||
import type { EmailContext } from "../types";
|
||||
|
||||
export interface NewPollEmailProps {
|
||||
title: string;
|
||||
|
@ -17,29 +19,61 @@ export interface NewPollEmailProps {
|
|||
}
|
||||
|
||||
export const NewPollEmail = ({
|
||||
title = "Untitled Poll",
|
||||
adminLink = "https://rallly.co/admin/abcdefg123",
|
||||
participantLink = "https://rallly.co/invite/wxyz9876",
|
||||
ctx = defaultEmailContext,
|
||||
title,
|
||||
adminLink,
|
||||
participantLink,
|
||||
ctx,
|
||||
}: NewPollEmailProps) => {
|
||||
return (
|
||||
<EmailLayout
|
||||
ctx={ctx}
|
||||
preview="Share your participant link to start collecting responses."
|
||||
preview={ctx.t("newPoll_preview", {
|
||||
defaultValue:
|
||||
"Share your participant link to start collecting responses.",
|
||||
ns: "emails",
|
||||
})}
|
||||
>
|
||||
<Heading>New Poll Created</Heading>
|
||||
<Heading>
|
||||
{ctx.t("newPoll_heading", {
|
||||
defaultValue: "New Poll Created",
|
||||
ns: "emails",
|
||||
})}
|
||||
</Heading>
|
||||
<Text>
|
||||
Your meeting poll titled <strong>{`"${title}"`}</strong> is ready! Share
|
||||
it using the link below:
|
||||
<Trans
|
||||
i18n={ctx.i18n}
|
||||
t={ctx.t}
|
||||
i18nKey="newPoll_content"
|
||||
ns="emails"
|
||||
values={{ title }}
|
||||
components={{
|
||||
b: <strong />,
|
||||
}}
|
||||
defaults="Your meeting poll titled <b>{{title}}</b> is ready! Share it using the link below:"
|
||||
/>
|
||||
</Text>
|
||||
<Card style={{ textAlign: "center" }}>
|
||||
<Text style={{ textAlign: "center" }}>
|
||||
<Link href={participantLink}>{participantLink}</Link>
|
||||
</Text>
|
||||
</Card>
|
||||
<Button href={adminLink}>Manage Poll →</Button>
|
||||
<Button href={adminLink}>
|
||||
{ctx.t("newPoll_button", {
|
||||
defaultValue: "Manage Poll",
|
||||
ns: "emails",
|
||||
})}
|
||||
→
|
||||
</Button>
|
||||
</EmailLayout>
|
||||
);
|
||||
};
|
||||
|
||||
NewPollEmail.getSubject = (props: NewPollEmailProps, ctx: EmailContext) => {
|
||||
return ctx.t("newPoll_subject", {
|
||||
defaultValue: "Let's find a date for {{title}}!",
|
||||
title: props.title,
|
||||
ns: "emails",
|
||||
});
|
||||
};
|
||||
|
||||
export default NewPollEmail;
|
||||
|
|
|
@ -1,29 +1,43 @@
|
|||
import { Section } from "@react-email/section";
|
||||
import { Trans } from "react-i18next/TransWithoutContext";
|
||||
|
||||
import { defaultEmailContext, EmailContext } from "./_components/email-context";
|
||||
import { EmailLayout } from "./_components/email-layout";
|
||||
import { EmailLayout } from "../components/email-layout";
|
||||
import {
|
||||
Card,
|
||||
Domain,
|
||||
Heading,
|
||||
Text,
|
||||
trackingWide,
|
||||
} from "./_components/styled-components";
|
||||
} from "../components/styled-components";
|
||||
import type { EmailContext } from "../types";
|
||||
|
||||
interface RegisterEmailProps {
|
||||
code: string;
|
||||
ctx: EmailContext;
|
||||
}
|
||||
|
||||
export const RegisterEmail = ({
|
||||
code = "123456",
|
||||
ctx = defaultEmailContext,
|
||||
}: RegisterEmailProps) => {
|
||||
export const RegisterEmail = ({ code, ctx }: RegisterEmailProps) => {
|
||||
return (
|
||||
<EmailLayout ctx={ctx} preview={`Your 6-digit code is: ${code}`}>
|
||||
<Heading>Verify your email address</Heading>
|
||||
<EmailLayout
|
||||
ctx={ctx}
|
||||
preview={ctx.t("register_preview", {
|
||||
ns: "emails",
|
||||
defaultValue: "Your 6-digit code is: {{code}}",
|
||||
code,
|
||||
})}
|
||||
>
|
||||
<Heading>
|
||||
{ctx.t("register_heading", {
|
||||
defaultValue: "Verify your email address",
|
||||
ns: "emails",
|
||||
})}
|
||||
</Heading>
|
||||
<Text>
|
||||
Please use the following 6-digit verification code to verify your email:
|
||||
{ctx.t("register_text", {
|
||||
defaultValue:
|
||||
"Please use the following 6-digit verification code to verify your email",
|
||||
ns: "emails",
|
||||
})}
|
||||
</Text>
|
||||
<Card style={{ textAlign: "center" }}>
|
||||
<Text
|
||||
|
@ -38,18 +52,36 @@ export const RegisterEmail = ({
|
|||
{code}
|
||||
</Text>
|
||||
<Text style={{ textAlign: "center" }} light={true}>
|
||||
This code is valid for 15 minutes
|
||||
{ctx.t("register_codeValid", {
|
||||
defaultValue: "This code is valid for 15 minutes",
|
||||
ns: "emails",
|
||||
})}
|
||||
</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.
|
||||
<Trans
|
||||
i18n={ctx.i18n}
|
||||
t={ctx.t}
|
||||
i18nKey="register_footer"
|
||||
ns="emails"
|
||||
values={{ domain: ctx.domain }}
|
||||
components={{
|
||||
domain: <Domain ctx={ctx} />,
|
||||
}}
|
||||
defaults="You're receiving this email because a request was made to register an account on <domain />. If this wasn't you, please ignore this email."
|
||||
/>
|
||||
</Text>
|
||||
</Section>
|
||||
</EmailLayout>
|
||||
);
|
||||
};
|
||||
|
||||
RegisterEmail.getSubject = (_props: RegisterEmailProps, ctx: EmailContext) => {
|
||||
return ctx.t("register_subject", {
|
||||
defaultValue: "Please verify your email address",
|
||||
ns: "emails",
|
||||
});
|
||||
};
|
||||
|
||||
export default RegisterEmail;
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 2.2 KiB |
Loading…
Add table
Add a link
Reference in a new issue