mirror of
https://github.com/lukevella/rallly.git
synced 2025-06-03 11:11:48 +02:00
♻️ Update react-email package (#1001)
This commit is contained in:
parent
317244ef28
commit
7e213b0b4c
24 changed files with 2493 additions and 1913 deletions
|
@ -39,19 +39,10 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@next/bundle-analyzer": "^12.3.4",
|
"@next/bundle-analyzer": "^12.3.4",
|
||||||
"@rallly/tsconfig": "*",
|
"@rallly/tsconfig": "*",
|
||||||
"@types/accept-language-parser": "^1.5.3",
|
|
||||||
"@types/color-hash": "^1.0.2",
|
"@types/color-hash": "^1.0.2",
|
||||||
"@types/lodash": "^4.14.178",
|
"@types/lodash": "^4.14.178",
|
||||||
"@types/react": "^18.0.28",
|
|
||||||
"@types/react-big-calendar": "^0.31.0",
|
|
||||||
"@types/react-dom": "^18.0.11",
|
|
||||||
"@types/react-linkify": "^1.0.1",
|
|
||||||
"@types/smoothscroll-polyfill": "^0.3.1",
|
|
||||||
"cheerio": "^1.0.0-rc.12",
|
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"i18next-scanner": "^4.2.0",
|
"i18next-scanner": "^4.2.0",
|
||||||
"i18next-scanner-typescript": "^1.1.1",
|
"i18next-scanner-typescript": "^1.1.1"
|
||||||
"smtp-tester": "^2.0.1",
|
|
||||||
"wait-on": "^6.0.1"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,7 +68,8 @@
|
||||||
"react-hook-form-persist": "^3.0.0",
|
"react-hook-form-persist": "^3.0.0",
|
||||||
"react-hot-toast": "^2.4.0",
|
"react-hot-toast": "^2.4.0",
|
||||||
"react-i18next": "^12.1.4",
|
"react-i18next": "^12.1.4",
|
||||||
"react-linkify": "^1.0.0-alpha",
|
"linkifyjs": "^4.1.3",
|
||||||
|
"linkify-react": "^4.1.3",
|
||||||
"react-remove-scroll": "^2.5.6",
|
"react-remove-scroll": "^2.5.6",
|
||||||
"react-use": "^17.4.0",
|
"react-use": "^17.4.0",
|
||||||
"smoothscroll-polyfill": "^0.4.4",
|
"smoothscroll-polyfill": "^0.4.4",
|
||||||
|
@ -83,10 +84,7 @@
|
||||||
"@types/accept-language-parser": "^1.5.3",
|
"@types/accept-language-parser": "^1.5.3",
|
||||||
"@types/color-hash": "^1.0.2",
|
"@types/color-hash": "^1.0.2",
|
||||||
"@types/lodash": "^4.14.178",
|
"@types/lodash": "^4.14.178",
|
||||||
"@types/react": "^18.0.28",
|
"@types/react-big-calendar": "^1.8.8",
|
||||||
"@types/react-big-calendar": "^0.31.0",
|
|
||||||
"@types/react-dom": "^18.0.11",
|
|
||||||
"@types/react-linkify": "^1.0.1",
|
|
||||||
"@types/smoothscroll-polyfill": "^0.3.1",
|
"@types/smoothscroll-polyfill": "^0.3.1",
|
||||||
"cheerio": "^1.0.0-rc.12",
|
"cheerio": "^1.0.0-rc.12",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
|
|
|
@ -2,62 +2,66 @@ import { AnimatePresence, m } from "framer-motion";
|
||||||
import Cookies from "js-cookie";
|
import Cookies from "js-cookie";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import ReactDOM from "react-dom";
|
import { createPortal } from "react-dom";
|
||||||
|
|
||||||
import { getPortal } from "@/utils/selectors";
|
import { getPortal } from "@/utils/selectors";
|
||||||
|
|
||||||
import CookiesIllustration from "./cookie-consent/cookies.svg";
|
import CookiesIllustration from "./cookie-consent/cookies.svg";
|
||||||
|
|
||||||
const CookieConsentPopover: React.FunctionComponent = () => {
|
const CookieConsentPopover = () => {
|
||||||
const [visible, setVisible] = React.useState(true);
|
const [visible, setVisible] = React.useState(true);
|
||||||
|
|
||||||
return ReactDOM.createPortal(
|
return (
|
||||||
<AnimatePresence>
|
<>
|
||||||
{visible ? (
|
{createPortal(
|
||||||
<m.div
|
<AnimatePresence>
|
||||||
variants={{
|
{visible ? (
|
||||||
enter: {
|
<m.div
|
||||||
opacity: 1,
|
variants={{
|
||||||
y: 0,
|
enter: {
|
||||||
transition: { type: "spring", delay: 2, duration: 1 },
|
opacity: 1,
|
||||||
},
|
y: 0,
|
||||||
exit: {
|
transition: { type: "spring", delay: 2, duration: 1 },
|
||||||
opacity: 0,
|
},
|
||||||
y: 10,
|
exit: {
|
||||||
transition: { duration: 0.1 },
|
opacity: 0,
|
||||||
},
|
y: 10,
|
||||||
}}
|
transition: { duration: 0.1 },
|
||||||
initial={{ opacity: 0, y: 100 }}
|
},
|
||||||
animate="enter"
|
|
||||||
exit="exit"
|
|
||||||
className="fixed bottom-8 right-8 z-50 w-60 rounded-lg bg-white p-4 pt-8 text-sm shadow-lg"
|
|
||||||
>
|
|
||||||
<CookiesIllustration className="absolute -top-6" />
|
|
||||||
<div className="mb-3">
|
|
||||||
Your privacy is important to us. We only use cookies to improve the
|
|
||||||
browsing experience on this website.
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center space-x-6">
|
|
||||||
<Link
|
|
||||||
href="/privacy-policy"
|
|
||||||
className="hover:text-primary-600 text-gray-500"
|
|
||||||
>
|
|
||||||
Privacy Policy
|
|
||||||
</Link>
|
|
||||||
<button
|
|
||||||
onClick={() => {
|
|
||||||
Cookies.set("rallly_cookie_consent", "1", { expires: 365 });
|
|
||||||
setVisible(false);
|
|
||||||
}}
|
}}
|
||||||
className="bg-primary-600 hover:bg-primary-600/90 focus:ring-primary-200 active:bg-primary-600/90 grow rounded-md px-5 py-1 font-semibold text-white shadow-sm transition-all focus:ring-2"
|
initial={{ opacity: 0, y: 100 }}
|
||||||
|
animate="enter"
|
||||||
|
exit="exit"
|
||||||
|
className="fixed bottom-8 right-8 z-50 w-60 rounded-lg bg-white p-4 pt-8 text-sm shadow-lg"
|
||||||
>
|
>
|
||||||
OK
|
<CookiesIllustration className="absolute -top-6" />
|
||||||
</button>
|
<div className="mb-3">
|
||||||
</div>
|
Your privacy is important to us. We only use cookies to improve
|
||||||
</m.div>
|
the browsing experience on this website.
|
||||||
) : null}
|
</div>
|
||||||
</AnimatePresence>,
|
<div className="flex items-center space-x-6">
|
||||||
getPortal(),
|
<Link
|
||||||
|
href="/privacy-policy"
|
||||||
|
className="hover:text-primary-600 text-gray-500"
|
||||||
|
>
|
||||||
|
Privacy Policy
|
||||||
|
</Link>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
Cookies.set("rallly_cookie_consent", "1", { expires: 365 });
|
||||||
|
setVisible(false);
|
||||||
|
}}
|
||||||
|
className="bg-primary-600 hover:bg-primary-600/90 focus:ring-primary-200 active:bg-primary-600/90 grow rounded-md px-5 py-1 font-semibold text-white shadow-sm transition-all focus:ring-2"
|
||||||
|
>
|
||||||
|
OK
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</m.div>
|
||||||
|
) : null}
|
||||||
|
</AnimatePresence>,
|
||||||
|
getPortal(),
|
||||||
|
)}
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import "./rbc-overrides.css";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { XIcon } from "lucide-react";
|
import { XIcon } from "lucide-react";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Calendar } from "react-big-calendar";
|
import { Calendar, CalendarProps } from "react-big-calendar";
|
||||||
import { createBreakpoint } from "react-use";
|
import { createBreakpoint } from "react-use";
|
||||||
|
|
||||||
import { getDuration } from "../../../utils/date-time-utils";
|
import { getDuration } from "../../../utils/date-time-utils";
|
||||||
|
@ -17,6 +17,12 @@ const localizer = dayjsLocalizer(dayjs);
|
||||||
|
|
||||||
const useDevice = createBreakpoint({ desktop: 720, mobile: 360 });
|
const useDevice = createBreakpoint({ desktop: 720, mobile: 360 });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Not sure what's wrong with the type definitions for react-big-calendar but it's not working properly.
|
||||||
|
* This is a temporary fix that overrides their types which ideally we wouldn't have to do.
|
||||||
|
*/
|
||||||
|
const CalendarTempFix = Calendar as React.ComponentType<CalendarProps>;
|
||||||
|
|
||||||
const WeekCalendar: React.FunctionComponent<DateTimePickerProps> = ({
|
const WeekCalendar: React.FunctionComponent<DateTimePickerProps> = ({
|
||||||
options,
|
options,
|
||||||
onNavigate,
|
onNavigate,
|
||||||
|
@ -36,7 +42,7 @@ const WeekCalendar: React.FunctionComponent<DateTimePickerProps> = ({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative flex h-[600px]">
|
<div className="relative flex h-[600px]">
|
||||||
<Calendar
|
<CalendarTempFix
|
||||||
className="absolute inset-0"
|
className="absolute inset-0"
|
||||||
events={options.map((option) => {
|
events={options.map((option) => {
|
||||||
if (option.type === "date") {
|
if (option.type === "date") {
|
||||||
|
@ -61,6 +67,7 @@ const WeekCalendar: React.FunctionComponent<DateTimePickerProps> = ({
|
||||||
(option) =>
|
(option) =>
|
||||||
!(
|
!(
|
||||||
option.type === "timeSlot" &&
|
option.type === "timeSlot" &&
|
||||||
|
event.start &&
|
||||||
option.start === formatDateWithoutTz(event.start) &&
|
option.start === formatDateWithoutTz(event.start) &&
|
||||||
event.end &&
|
event.end &&
|
||||||
option.end === formatDateWithoutTz(event.end)
|
option.end === formatDateWithoutTz(event.end)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@rallly/ui/tooltip";
|
import { Tooltip, TooltipContent, TooltipTrigger } from "@rallly/ui/tooltip";
|
||||||
|
import Linkify from "linkify-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import ReactLinkify from "react-linkify";
|
|
||||||
|
|
||||||
export const truncateLink = (href: string, text: string, key: number) => {
|
export const truncateLink = (href: string, text: string, key: number) => {
|
||||||
const textWithoutProtocol = text.replace(/^https?:\/\//i, "");
|
const textWithoutProtocol = text.replace(/^https?:\/\//i, "");
|
||||||
|
@ -45,11 +45,21 @@ export const truncateLink = (href: string, text: string, key: number) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const TruncatedLinkify: React.FunctionComponent<{
|
const TruncatedLinkify = ({ children }: { children: React.ReactNode }) => {
|
||||||
children?: React.ReactNode;
|
|
||||||
}> = ({ children }) => {
|
|
||||||
return (
|
return (
|
||||||
<ReactLinkify componentDecorator={truncateLink}>{children}</ReactLinkify>
|
<Linkify
|
||||||
|
options={{
|
||||||
|
render: ({ attributes, content }) => {
|
||||||
|
return truncateLink(
|
||||||
|
attributes.href,
|
||||||
|
content,
|
||||||
|
attributes.key as number,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Linkify>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,8 @@
|
||||||
"zod": "^3.22.3"
|
"zod": "^3.22.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/react": "^18.2.48",
|
||||||
|
"@types/react-dom": "^18.2.18",
|
||||||
"dotenv-cli": "^7.1.0",
|
"dotenv-cli": "^7.1.0",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"prettier": "^3.2.4",
|
"prettier": "^3.2.4",
|
||||||
|
|
|
@ -10,20 +10,19 @@
|
||||||
"main": "./src/index.ts",
|
"main": "./src/index.ts",
|
||||||
"types": "./src/index.ts",
|
"types": "./src/index.ts",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-ses": "^3.292.0",
|
"@aws-sdk/client-ses": "^3.501.0",
|
||||||
"@aws-sdk/credential-provider-node": "^3.292.0",
|
"@aws-sdk/credential-provider-node": "^3.501.0",
|
||||||
"@react-email/components": "0.0.2",
|
"@react-email/components": "^0.0.14",
|
||||||
"@react-email/render": "0.0.6",
|
"@react-email/render": "^0.0.12",
|
||||||
"clsx": "^1.2.1",
|
"nodemailer": "^6.9.8",
|
||||||
"nodemailer": "^6.9.1",
|
|
||||||
"preview-email": "^3.0.19",
|
"preview-email": "^3.0.19",
|
||||||
"react-email": "^1.9.1"
|
"react-email": "^2.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rallly/tailwind-config": "*",
|
"@rallly/tailwind-config": "*",
|
||||||
"@rallly/tsconfig": "*",
|
"@rallly/tsconfig": "*",
|
||||||
"@rallly/utils": "*",
|
"@rallly/utils": "*",
|
||||||
"@types/nodemailer": "^6.4.7",
|
"@types/nodemailer": "^6.4.14",
|
||||||
"@types/preview-email": "^3.0.1"
|
"@types/preview-email": "^3.0.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import previewEmail from "preview-email";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import * as templates from "./templates";
|
import * as templates from "./templates";
|
||||||
import { EmailContext } from "./templates/components/email-context";
|
import { EmailContext } from "./templates/_components/email-context";
|
||||||
|
|
||||||
type Templates = typeof templates;
|
type Templates = typeof templates;
|
||||||
|
|
||||||
|
|
11
packages/emails/src/templates/_components/email-context.tsx
Normal file
11
packages/emails/src/templates/_components/email-context.tsx
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
export type EmailContext = {
|
||||||
|
logoUrl: string;
|
||||||
|
baseUrl: string;
|
||||||
|
domain: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const defaultEmailContext = {
|
||||||
|
logoUrl: "https://rallly.co/logo.png",
|
||||||
|
baseUrl: "https://rallly.co",
|
||||||
|
domain: "rallly.co",
|
||||||
|
};
|
90
packages/emails/src/templates/_components/email-layout.tsx
Normal file
90
packages/emails/src/templates/_components/email-layout.tsx
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
import {
|
||||||
|
Body,
|
||||||
|
Container,
|
||||||
|
Head,
|
||||||
|
Html,
|
||||||
|
Img,
|
||||||
|
Link,
|
||||||
|
Preview,
|
||||||
|
Tailwind,
|
||||||
|
} from "@react-email/components";
|
||||||
|
|
||||||
|
import { EmailContext } from "./email-context";
|
||||||
|
import { Section, Text } from "./styled-components";
|
||||||
|
|
||||||
|
export interface EmailLayoutProps {
|
||||||
|
preview: string;
|
||||||
|
recipientName: string;
|
||||||
|
footNote?: React.ReactNode;
|
||||||
|
ctx: EmailContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sectionStyles = {
|
||||||
|
marginTop: "16px",
|
||||||
|
marginBottom: "16px",
|
||||||
|
};
|
||||||
|
|
||||||
|
export const EmailLayout = ({
|
||||||
|
preview,
|
||||||
|
recipientName = "Guest",
|
||||||
|
children,
|
||||||
|
footNote,
|
||||||
|
ctx,
|
||||||
|
}: React.PropsWithChildren<EmailLayoutProps>) => {
|
||||||
|
const { logoUrl, baseUrl } = ctx;
|
||||||
|
return (
|
||||||
|
<Tailwind>
|
||||||
|
<Html>
|
||||||
|
<Head />
|
||||||
|
<Preview>{preview}</Preview>
|
||||||
|
<Body className="bg-gray-50 py-5">
|
||||||
|
<Container className="w-full rounded-md border border-solid border-gray-200 bg-white p-4">
|
||||||
|
<Img src={logoUrl} alt="Rallly" width={128} />
|
||||||
|
<Section style={sectionStyles}>
|
||||||
|
<Text>Hi {recipientName},</Text>
|
||||||
|
{children}
|
||||||
|
</Section>
|
||||||
|
{footNote ? (
|
||||||
|
<Section className="border-t border-solid border-gray-200">
|
||||||
|
<Text className="text-sm text-gray-500">{footNote}</Text>
|
||||||
|
</Section>
|
||||||
|
) : null}
|
||||||
|
<Section className="my-0 font-sans text-sm text-gray-500">
|
||||||
|
<Link className="text-gray-500" href={baseUrl}>
|
||||||
|
Home
|
||||||
|
</Link>
|
||||||
|
<span> • </span>
|
||||||
|
<Link
|
||||||
|
className="text-gray-500"
|
||||||
|
href="https://twitter.com/ralllyco"
|
||||||
|
>
|
||||||
|
Twitter
|
||||||
|
</Link>
|
||||||
|
<span> • </span>
|
||||||
|
<Link
|
||||||
|
className="text-gray-500"
|
||||||
|
href="https://github.com/lukevella/rallly"
|
||||||
|
>
|
||||||
|
Github
|
||||||
|
</Link>
|
||||||
|
<span> • </span>
|
||||||
|
<Link
|
||||||
|
className="text-gray-500"
|
||||||
|
href="https://www.paypal.com/donate/?hosted_button_id=7QXP2CUBLY88E"
|
||||||
|
>
|
||||||
|
Donate
|
||||||
|
</Link>
|
||||||
|
<span> • </span>
|
||||||
|
<Link
|
||||||
|
className="text-gray-500"
|
||||||
|
href={`mailto:${process.env["SUPPORT_EMAIL"]}`}
|
||||||
|
>
|
||||||
|
Contact
|
||||||
|
</Link>
|
||||||
|
</Section>
|
||||||
|
</Container>
|
||||||
|
</Body>
|
||||||
|
</Html>
|
||||||
|
</Tailwind>
|
||||||
|
);
|
||||||
|
};
|
|
@ -1,6 +1,5 @@
|
||||||
import {
|
import {
|
||||||
Button as UnstyledButton,
|
Button as UnstyledButton,
|
||||||
ButtonProps,
|
|
||||||
Heading as UnstyledHeading,
|
Heading as UnstyledHeading,
|
||||||
Link as UnstyledLink,
|
Link as UnstyledLink,
|
||||||
LinkProps,
|
LinkProps,
|
||||||
|
@ -37,7 +36,7 @@ export const Domain = ({ ctx }: { ctx: EmailContext }) => {
|
||||||
return <Link href={baseUrl}>{domain}</Link>;
|
return <Link href={baseUrl}>{domain}</Link>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Button = (props: ButtonProps) => {
|
export const Button = (props: React.ComponentProps<typeof UnstyledButton>) => {
|
||||||
return (
|
return (
|
||||||
<UnstyledButton
|
<UnstyledButton
|
||||||
{...props}
|
{...props}
|
||||||
|
@ -79,14 +78,9 @@ export const Heading = (
|
||||||
<UnstyledHeading
|
<UnstyledHeading
|
||||||
{...props}
|
{...props}
|
||||||
as={as}
|
as={as}
|
||||||
|
className="font-sans font-bold text-gray-900"
|
||||||
style={{
|
style={{
|
||||||
marginTop: "16px",
|
|
||||||
marginBottom: "8px",
|
|
||||||
letterSpacing: "-0.75px",
|
|
||||||
fontFamily: "sans-serif",
|
|
||||||
fontWeight: "bold",
|
|
||||||
fontSize: fontSize[as],
|
fontSize: fontSize[as],
|
||||||
color: "#1E293B",
|
|
||||||
...props.style,
|
...props.style,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
|
@ -1,5 +0,0 @@
|
||||||
export type EmailContext = {
|
|
||||||
logoUrl: string;
|
|
||||||
baseUrl: string;
|
|
||||||
domain: string;
|
|
||||||
};
|
|
|
@ -1,104 +0,0 @@
|
||||||
import {
|
|
||||||
Body,
|
|
||||||
Container,
|
|
||||||
Head,
|
|
||||||
Html,
|
|
||||||
Img,
|
|
||||||
Link,
|
|
||||||
Preview,
|
|
||||||
} from "@react-email/components";
|
|
||||||
|
|
||||||
import { EmailContext } from "./email-context";
|
|
||||||
import { fontFamily, Section, Text } from "./styled-components";
|
|
||||||
|
|
||||||
export interface EmailLayoutProps {
|
|
||||||
preview: string;
|
|
||||||
recipientName: string;
|
|
||||||
footNote?: React.ReactNode;
|
|
||||||
ctx: EmailContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
const containerStyles = {
|
|
||||||
maxWidth: "600px",
|
|
||||||
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",
|
|
||||||
};
|
|
||||||
|
|
||||||
export const EmailLayout = ({
|
|
||||||
preview,
|
|
||||||
recipientName = "Guest",
|
|
||||||
children,
|
|
||||||
footNote,
|
|
||||||
ctx,
|
|
||||||
}: React.PropsWithChildren<EmailLayoutProps>) => {
|
|
||||||
const { logoUrl, baseUrl } = ctx;
|
|
||||||
return (
|
|
||||||
<Html>
|
|
||||||
<Head />
|
|
||||||
<Preview>{preview}</Preview>
|
|
||||||
<Body style={{ backgroundColor: "#F3F4F6", padding: "16px" }}>
|
|
||||||
<Container style={containerStyles}>
|
|
||||||
<Img src={logoUrl} alt="Rallly" width={128} />
|
|
||||||
<Section style={sectionStyles}>
|
|
||||||
<Text>Hi {recipientName},</Text>
|
|
||||||
{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> • </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="https://www.paypal.com/donate/?hosted_button_id=7QXP2CUBLY88E"
|
|
||||||
>
|
|
||||||
Donate
|
|
||||||
</Link>
|
|
||||||
<span> • </span>
|
|
||||||
<Link
|
|
||||||
style={linkStyles}
|
|
||||||
href={`mailto:${process.env["SUPPORT_EMAIL"]}`}
|
|
||||||
>
|
|
||||||
Contact
|
|
||||||
</Link>
|
|
||||||
</Section>
|
|
||||||
</Container>
|
|
||||||
</Body>
|
|
||||||
</Html>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { Column, Row, Section } from "@react-email/components";
|
import { Column, Row, Section } from "@react-email/components";
|
||||||
|
|
||||||
import { 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, Text } from "./_components/styled-components";
|
||||||
|
|
||||||
export interface FinalizeHostEmailProps {
|
export interface FinalizeHostEmailProps {
|
||||||
date: string;
|
date: string;
|
||||||
|
@ -25,7 +25,7 @@ export const FinalizeHostEmail = ({
|
||||||
dow = "Fri",
|
dow = "Fri",
|
||||||
date = "Friday, 12th June 2020",
|
date = "Friday, 12th June 2020",
|
||||||
time = "6:00 PM to 11:00 PM BST",
|
time = "6:00 PM to 11:00 PM BST",
|
||||||
ctx,
|
ctx = defaultEmailContext,
|
||||||
}: FinalizeHostEmailProps) => {
|
}: FinalizeHostEmailProps) => {
|
||||||
return (
|
return (
|
||||||
<EmailLayout ctx={ctx} recipientName={name} preview="Final date booked!">
|
<EmailLayout ctx={ctx} recipientName={name} preview="Final date booked!">
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { Column, Row, Section } from "@react-email/components";
|
import { Column, Row, Section } from "@react-email/components";
|
||||||
|
|
||||||
import { 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, Text } from "./_components/styled-components";
|
||||||
|
|
||||||
export interface FinalizeParticipantEmailProps {
|
export interface FinalizeParticipantEmailProps {
|
||||||
date: string;
|
date: string;
|
||||||
|
@ -27,7 +27,7 @@ export const FinalizeParticipantEmail = ({
|
||||||
dow = "Fri",
|
dow = "Fri",
|
||||||
date = "Friday, 12th June 2020",
|
date = "Friday, 12th June 2020",
|
||||||
time = "6:00 PM to 11:00 PM BST",
|
time = "6:00 PM to 11:00 PM BST",
|
||||||
ctx,
|
ctx = defaultEmailContext,
|
||||||
}: FinalizeParticipantEmailProps) => {
|
}: FinalizeParticipantEmailProps) => {
|
||||||
return (
|
return (
|
||||||
<EmailLayout ctx={ctx} recipientName={name} preview="Final date booked!">
|
<EmailLayout ctx={ctx} recipientName={name} preview="Final date booked!">
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { 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 {
|
||||||
Button,
|
Button,
|
||||||
Card,
|
Card,
|
||||||
|
@ -7,7 +7,7 @@ import {
|
||||||
Heading,
|
Heading,
|
||||||
Text,
|
Text,
|
||||||
trackingWide,
|
trackingWide,
|
||||||
} from "./components/styled-components";
|
} from "./_components/styled-components";
|
||||||
|
|
||||||
interface LoginEmailProps {
|
interface LoginEmailProps {
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -20,7 +20,7 @@ export const LoginEmail = ({
|
||||||
name = "Guest",
|
name = "Guest",
|
||||||
code = "123456",
|
code = "123456",
|
||||||
magicLink = "https://rallly.co",
|
magicLink = "https://rallly.co",
|
||||||
ctx,
|
ctx = defaultEmailContext,
|
||||||
}: LoginEmailProps) => {
|
}: LoginEmailProps) => {
|
||||||
return (
|
return (
|
||||||
<EmailLayout
|
<EmailLayout
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
|
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 { Text } from "./_components/styled-components";
|
||||||
|
|
||||||
export interface NewCommentEmailProps extends NotificationBaseProps {
|
export interface NewCommentEmailProps extends NotificationBaseProps {
|
||||||
authorName: string;
|
authorName: string;
|
||||||
|
@ -13,7 +14,7 @@ export const NewCommentEmail = ({
|
||||||
authorName = "Someone",
|
authorName = "Someone",
|
||||||
pollUrl = "https://rallly.co",
|
pollUrl = "https://rallly.co",
|
||||||
disableNotificationsUrl = "https://rallly.co",
|
disableNotificationsUrl = "https://rallly.co",
|
||||||
ctx,
|
ctx = defaultEmailContext,
|
||||||
}: NewCommentEmailProps) => {
|
}: NewCommentEmailProps) => {
|
||||||
return (
|
return (
|
||||||
<NotificationEmail
|
<NotificationEmail
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { 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, Section, Text } from "./_components/styled-components";
|
||||||
|
|
||||||
interface NewParticipantConfirmationEmailProps {
|
interface NewParticipantConfirmationEmailProps {
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -12,7 +12,7 @@ export const NewParticipantConfirmationEmail = ({
|
||||||
title = "Untitled Poll",
|
title = "Untitled Poll",
|
||||||
name = "John",
|
name = "John",
|
||||||
editSubmissionUrl = "https://rallly.co",
|
editSubmissionUrl = "https://rallly.co",
|
||||||
ctx,
|
ctx = defaultEmailContext,
|
||||||
}: NewParticipantConfirmationEmailProps) => {
|
}: NewParticipantConfirmationEmailProps) => {
|
||||||
const { domain } = ctx;
|
const { domain } = ctx;
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
|
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 { Text } from "./_components/styled-components";
|
||||||
|
|
||||||
export interface NewParticipantEmailProps extends NotificationBaseProps {
|
export interface NewParticipantEmailProps extends NotificationBaseProps {
|
||||||
participantName: string;
|
participantName: string;
|
||||||
|
@ -13,7 +14,7 @@ export const NewParticipantEmail = ({
|
||||||
participantName = "Someone",
|
participantName = "Someone",
|
||||||
pollUrl = "https://rallly.co",
|
pollUrl = "https://rallly.co",
|
||||||
disableNotificationsUrl = "https://rallly.co",
|
disableNotificationsUrl = "https://rallly.co",
|
||||||
ctx,
|
ctx = defaultEmailContext,
|
||||||
}: NewParticipantEmailProps) => {
|
}: NewParticipantEmailProps) => {
|
||||||
return (
|
return (
|
||||||
<NotificationEmail
|
<NotificationEmail
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { 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, Link, Text } from "./_components/styled-components";
|
||||||
|
|
||||||
export interface NewPollEmailProps {
|
export interface NewPollEmailProps {
|
||||||
title: string;
|
title: string;
|
||||||
|
@ -38,7 +38,7 @@ export const NewPollEmail = ({
|
||||||
name = "John",
|
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,
|
ctx = defaultEmailContext,
|
||||||
}: NewPollEmailProps) => {
|
}: NewPollEmailProps) => {
|
||||||
const { baseUrl, domain } = ctx;
|
const { baseUrl, domain } = ctx;
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import { 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 {
|
||||||
Domain,
|
Domain,
|
||||||
Heading,
|
Heading,
|
||||||
Text,
|
Text,
|
||||||
trackingWide,
|
trackingWide,
|
||||||
} from "./components/styled-components";
|
} from "./_components/styled-components";
|
||||||
|
|
||||||
interface RegisterEmailProps {
|
interface RegisterEmailProps {
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -16,7 +16,7 @@ interface RegisterEmailProps {
|
||||||
export const RegisterEmail = ({
|
export const RegisterEmail = ({
|
||||||
name = "John",
|
name = "John",
|
||||||
code = "123456",
|
code = "123456",
|
||||||
ctx,
|
ctx = defaultEmailContext,
|
||||||
}: RegisterEmailProps) => {
|
}: RegisterEmailProps) => {
|
||||||
return (
|
return (
|
||||||
<EmailLayout
|
<EmailLayout
|
||||||
|
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
Loading…
Add table
Add a link
Reference in a new issue