feat: i18n for emails (#1442)

## Description

Support setting a document language that will control the language used
for sending emails to recipients. Additional work has been done to
convert all emails to using our i18n implementation so we can later add
controls for sending other kinds of emails in a users target language.

## Related Issue

N/A

## Changes Made

- Added `<Trans>` and `msg` macros to emails
- Introduced a new `renderEmailWithI18N` utility in the lib package
- Updated all emails to use the `<Tailwind>` component at the top level
due to rendering constraints
- Updated the `i18n.server.tsx` file to not use a top level await

## Testing Performed

- Configured document language and verified emails were sent in the
expected language
- Created a document from a template and verified that the templates
language was transferred to the document
This commit is contained in:
Lucas Smith
2024-11-05 11:52:54 +11:00
committed by GitHub
parent 04b1ce1aab
commit 4dd95016b1
163 changed files with 3549 additions and 1461 deletions

View File

@ -1,6 +1,7 @@
import config from '@documenso/tailwind-config';
import { msg } from '@lingui/macro';
import { useLingui } from '@lingui/react';
import { Body, Container, Head, Html, Img, Preview, Section, Tailwind } from '../components';
import { Body, Container, Head, Html, Img, Preview, Section } from '../components';
import type { TemplateConfirmationEmailProps } from '../template-components/template-confirmation-email';
import { TemplateConfirmationEmail } from '../template-components/template-confirmation-email';
import { TemplateFooter } from '../template-components/template-footer';
@ -9,7 +10,9 @@ export const ConfirmEmailTemplate = ({
confirmationLink,
assetBaseUrl = 'http://localhost:3002',
}: TemplateConfirmationEmailProps) => {
const previewText = `Please confirm your email address`;
const { _ } = useLingui();
const previewText = msg`Please confirm your email address`;
const getAssetUrl = (path: string) => {
return new URL(path, assetBaseUrl).toString();
@ -18,40 +21,30 @@ export const ConfirmEmailTemplate = ({
return (
<Html>
<Head />
<Preview>{previewText}</Preview>
<Tailwind
config={{
theme: {
extend: {
colors: config.theme.extend.colors,
},
},
}}
>
<Body className="mx-auto my-auto bg-white font-sans">
<Section>
<Container className="mx-auto mb-2 mt-8 max-w-xl rounded-lg border border-solid border-slate-200 p-4 backdrop-blur-sm">
<Section>
<Img
src={getAssetUrl('/static/logo.png')}
alt="Documenso Logo"
className="mb-4 h-6"
/>
<Preview>{_(previewText)}</Preview>
<Body className="mx-auto my-auto bg-white font-sans">
<Section>
<Container className="mx-auto mb-2 mt-8 max-w-xl rounded-lg border border-solid border-slate-200 p-4 backdrop-blur-sm">
<Section>
<Img
src={getAssetUrl('/static/logo.png')}
alt="Documenso Logo"
className="mb-4 h-6"
/>
<TemplateConfirmationEmail
confirmationLink={confirmationLink}
assetBaseUrl={assetBaseUrl}
/>
</Section>
</Container>
<div className="mx-auto mt-12 max-w-xl" />
<TemplateConfirmationEmail
confirmationLink={confirmationLink}
assetBaseUrl={assetBaseUrl}
/>
</Section>
</Container>
<div className="mx-auto mt-12 max-w-xl" />
<Container className="mx-auto max-w-xl">
<TemplateFooter isDocument={false} />
</Container>
</Section>
</Body>
</Tailwind>
<Container className="mx-auto max-w-xl">
<TemplateFooter isDocument={false} />
</Container>
</Section>
</Body>
</Html>
);
};

View File

@ -1,5 +1,7 @@
import { msg } from '@lingui/macro';
import { useLingui } from '@lingui/react';
import { formatTeamUrl } from '@documenso/lib/utils/teams';
import config from '@documenso/tailwind-config';
import {
Body,
@ -11,7 +13,6 @@ import {
Link,
Preview,
Section,
Tailwind,
Text,
} from '../components';
import { TemplateFooter } from '../template-components/template-footer';
@ -32,97 +33,90 @@ export const ConfirmTeamEmailTemplate = ({
teamUrl = 'demo',
token = '',
}: ConfirmTeamEmailProps) => {
const previewText = `Accept team email request for ${teamName} on Documenso`;
const { _ } = useLingui();
const previewText = msg`Accept team email request for ${teamName} on Documenso`;
return (
<Html>
<Head />
<Preview>{previewText}</Preview>
<Tailwind
config={{
theme: {
extend: {
colors: config.theme.extend.colors,
},
},
}}
>
<Body className="mx-auto my-auto font-sans">
<Section className="bg-white">
<Container className="mx-auto mb-2 mt-8 max-w-xl rounded-lg border border-solid border-slate-200 px-2 pt-2 backdrop-blur-sm">
<Preview>{_(previewText)}</Preview>
<Body className="mx-auto my-auto font-sans">
<Section className="bg-white">
<Container className="mx-auto mb-2 mt-8 max-w-xl rounded-lg border border-solid border-slate-200 px-2 pt-2 backdrop-blur-sm">
<TemplateImage
assetBaseUrl={assetBaseUrl}
className="mb-4 h-6 p-2"
staticAsset="logo.png"
/>
<Section>
<TemplateImage
className="mx-auto"
assetBaseUrl={assetBaseUrl}
className="mb-4 h-6 p-2"
staticAsset="logo.png"
staticAsset="mail-open.png"
/>
</Section>
<Section>
<TemplateImage
className="mx-auto"
assetBaseUrl={assetBaseUrl}
staticAsset="mail-open.png"
/>
</Section>
<Section className="p-2 text-slate-500">
<Text className="text-center text-lg font-medium text-black">
Verify your team email address
</Text>
<Section className="p-2 text-slate-500">
<Text className="text-center text-lg font-medium text-black">
Verify your team email address
<Text className="text-center text-base">
<span className="font-bold">{teamName}</span> has requested to use your email
address for their team on Documenso.
</Text>
<div className="mx-auto mt-6 w-fit rounded-lg bg-gray-50 px-4 py-2 text-base font-medium text-slate-600">
{formatTeamUrl(teamUrl, baseUrl)}
</div>
<Section className="mt-6">
<Text className="my-0 text-sm">
By accepting this request, you will be granting <strong>{teamName}</strong> access
to:
</Text>
<Text className="text-center text-base">
<span className="font-bold">{teamName}</span> has requested to use your email
address for their team on Documenso.
<ul className="mb-0 mt-2">
<li className="text-sm">
View all documents sent to and from this email address
</li>
<li className="mt-1 text-sm">
Allow document recipients to reply directly to this email address
</li>
<li className="mt-1 text-sm">
Send documents on behalf of the team using the email address
</li>
</ul>
<Text className="mt-2 text-sm">
You can revoke access at any time in your team settings on Documenso{' '}
<Link href={`${baseUrl}/settings/teams`}>here.</Link>
</Text>
<div className="mx-auto mt-6 w-fit rounded-lg bg-gray-50 px-4 py-2 text-base font-medium text-slate-600">
{formatTeamUrl(teamUrl, baseUrl)}
</div>
<Section className="mt-6">
<Text className="my-0 text-sm">
By accepting this request, you will be granting <strong>{teamName}</strong>{' '}
access to:
</Text>
<ul className="mb-0 mt-2">
<li className="text-sm">
View all documents sent to and from this email address
</li>
<li className="mt-1 text-sm">
Allow document recipients to reply directly to this email address
</li>
<li className="mt-1 text-sm">
Send documents on behalf of the team using the email address
</li>
</ul>
<Text className="mt-2 text-sm">
You can revoke access at any time in your team settings on Documenso{' '}
<Link href={`${baseUrl}/settings/teams`}>here.</Link>
</Text>
</Section>
<Section className="mb-6 mt-8 text-center">
<Button
className="bg-documenso-500 inline-flex items-center justify-center rounded-lg px-6 py-3 text-center text-sm font-medium text-black no-underline"
href={`${baseUrl}/team/verify/email/${token}`}
>
Accept
</Button>
</Section>
</Section>
<Text className="text-center text-xs text-slate-500">Link expires in 1 hour.</Text>
</Container>
<Section className="mb-6 mt-8 text-center">
<Button
className="bg-documenso-500 inline-flex items-center justify-center rounded-lg px-6 py-3 text-center text-sm font-medium text-black no-underline"
href={`${baseUrl}/team/verify/email/${token}`}
>
Accept
</Button>
</Section>
</Section>
<Hr className="mx-auto mt-12 max-w-xl" />
<Text className="text-center text-xs text-slate-500">Link expires in 1 hour.</Text>
</Container>
<Container className="mx-auto max-w-xl">
<TemplateFooter isDocument={false} />
</Container>
</Section>
</Body>
</Tailwind>
<Hr className="mx-auto mt-12 max-w-xl" />
<Container className="mx-auto max-w-xl">
<TemplateFooter isDocument={false} />
</Container>
</Section>
</Body>
</Html>
);
};

View File

@ -1,6 +1,7 @@
import config from '@documenso/tailwind-config';
import { msg } from '@lingui/macro';
import { useLingui } from '@lingui/react';
import { Body, Container, Head, Hr, Html, Img, Preview, Section, Tailwind } from '../components';
import { Body, Container, Head, Hr, Html, Img, Preview, Section } from '../components';
import type { TemplateDocumentCancelProps } from '../template-components/template-document-cancel';
import { TemplateDocumentCancel } from '../template-components/template-document-cancel';
import { TemplateFooter } from '../template-components/template-footer';
@ -13,7 +14,9 @@ export const DocumentCancelTemplate = ({
documentName = 'Open Source Pledge.pdf',
assetBaseUrl = 'http://localhost:3002',
}: DocumentCancelEmailTemplateProps) => {
const previewText = `${inviterName} has cancelled the document ${documentName}, you don't need to sign it anymore.`;
const { _ } = useLingui();
const previewText = msg`${inviterName} has cancelled the document ${documentName}, you don't need to sign it anymore.`;
const getAssetUrl = (path: string) => {
return new URL(path, assetBaseUrl).toString();
@ -22,43 +25,34 @@ export const DocumentCancelTemplate = ({
return (
<Html>
<Head />
<Preview>{previewText}</Preview>
<Tailwind
config={{
theme: {
extend: {
colors: config.theme.extend.colors,
},
},
}}
>
<Body className="mx-auto my-auto bg-white font-sans">
<Section>
<Container className="mx-auto mb-2 mt-8 max-w-xl rounded-lg border border-solid border-slate-200 p-4 backdrop-blur-sm">
<Section>
<Img
src={getAssetUrl('/static/logo.png')}
alt="Documenso Logo"
className="mb-4 h-6"
/>
<Preview>{_(previewText)}</Preview>
<TemplateDocumentCancel
inviterName={inviterName}
inviterEmail={inviterEmail}
documentName={documentName}
assetBaseUrl={assetBaseUrl}
/>
</Section>
</Container>
<Body className="mx-auto my-auto bg-white font-sans">
<Section>
<Container className="mx-auto mb-2 mt-8 max-w-xl rounded-lg border border-solid border-slate-200 p-4 backdrop-blur-sm">
<Section>
<Img
src={getAssetUrl('/static/logo.png')}
alt="Documenso Logo"
className="mb-4 h-6"
/>
<Hr className="mx-auto mt-12 max-w-xl" />
<TemplateDocumentCancel
inviterName={inviterName}
inviterEmail={inviterEmail}
documentName={documentName}
assetBaseUrl={assetBaseUrl}
/>
</Section>
</Container>
<Container className="mx-auto max-w-xl">
<TemplateFooter />
</Container>
</Section>
</Body>
</Tailwind>
<Hr className="mx-auto mt-12 max-w-xl" />
<Container className="mx-auto max-w-xl">
<TemplateFooter />
</Container>
</Section>
</Body>
</Html>
);
};

View File

@ -1,6 +1,7 @@
import config from '@documenso/tailwind-config';
import { msg } from '@lingui/macro';
import { useLingui } from '@lingui/react';
import { Body, Container, Head, Html, Img, Preview, Section, Tailwind } from '../components';
import { Body, Container, Head, Html, Img, Preview, Section } from '../components';
import type { TemplateDocumentCompletedProps } from '../template-components/template-document-completed';
import { TemplateDocumentCompleted } from '../template-components/template-document-completed';
import { TemplateFooter } from '../template-components/template-footer';
@ -15,7 +16,9 @@ export const DocumentCompletedEmailTemplate = ({
assetBaseUrl = 'http://localhost:3002',
customBody,
}: DocumentCompletedEmailTemplateProps) => {
const previewText = `Completed Document`;
const { _ } = useLingui();
const previewText = msg`Completed Document`;
const getAssetUrl = (path: string) => {
return new URL(path, assetBaseUrl).toString();
@ -24,41 +27,32 @@ export const DocumentCompletedEmailTemplate = ({
return (
<Html>
<Head />
<Preview>{previewText}</Preview>
<Tailwind
config={{
theme: {
extend: {
colors: config.theme.extend.colors,
},
},
}}
>
<Body className="mx-auto my-auto font-sans">
<Section className="bg-white">
<Container className="mx-auto mb-2 mt-8 max-w-xl rounded-lg border border-solid border-slate-200 p-2 backdrop-blur-sm">
<Section className="p-2">
<Img
src={getAssetUrl('/static/logo.png')}
alt="Documenso Logo"
className="mb-4 h-6"
/>
<Preview>{_(previewText)}</Preview>
<TemplateDocumentCompleted
downloadLink={downloadLink}
documentName={documentName}
assetBaseUrl={assetBaseUrl}
customBody={customBody}
/>
</Section>
</Container>
<Body className="mx-auto my-auto font-sans">
<Section className="bg-white">
<Container className="mx-auto mb-2 mt-8 max-w-xl rounded-lg border border-solid border-slate-200 p-2 backdrop-blur-sm">
<Section className="p-2">
<Img
src={getAssetUrl('/static/logo.png')}
alt="Documenso Logo"
className="mb-4 h-6"
/>
<Container className="mx-auto max-w-xl">
<TemplateFooter />
</Container>
</Section>
</Body>
</Tailwind>
<TemplateDocumentCompleted
downloadLink={downloadLink}
documentName={documentName}
assetBaseUrl={assetBaseUrl}
customBody={customBody}
/>
</Section>
</Container>
<Container className="mx-auto max-w-xl">
<TemplateFooter />
</Container>
</Section>
</Body>
</Html>
);
};

View File

@ -1,18 +1,9 @@
import { RECIPIENT_ROLES_DESCRIPTION_ENG } from '@documenso/lib/constants/recipient-roles';
import config from '@documenso/tailwind-config';
import { Trans, msg } from '@lingui/macro';
import { useLingui } from '@lingui/react';
import {
Body,
Button,
Container,
Head,
Html,
Img,
Preview,
Section,
Tailwind,
Text,
} from '../components';
import { RECIPIENT_ROLES_DESCRIPTION_ENG } from '@documenso/lib/constants/recipient-roles';
import { Body, Button, Container, Head, Html, Img, Preview, Section, Text } from '../components';
import TemplateDocumentImage from '../template-components/template-document-image';
import { TemplateFooter } from '../template-components/template-footer';
import { RecipientRole } from '.prisma/client';
@ -32,9 +23,11 @@ export const DocumentCreatedFromDirectTemplateEmailTemplate = ({
documentName = 'Open Source Pledge.pdf',
assetBaseUrl = 'http://localhost:3002',
}: DocumentCompletedEmailTemplateProps) => {
const action = RECIPIENT_ROLES_DESCRIPTION_ENG[recipientRole].actioned.toLowerCase();
const { _ } = useLingui();
const previewText = `Document created from direct template`;
const action = _(RECIPIENT_ROLES_DESCRIPTION_ENG[recipientRole].actioned).toLowerCase();
const previewText = msg`Document created from direct template`;
const getAssetUrl = (path: string) => {
return new URL(path, assetBaseUrl).toString();
@ -43,55 +36,48 @@ export const DocumentCreatedFromDirectTemplateEmailTemplate = ({
return (
<Html>
<Head />
<Preview>{previewText}</Preview>
<Tailwind
config={{
theme: {
extend: {
colors: config.theme.extend.colors,
},
},
}}
>
<Body className="mx-auto my-auto font-sans">
<Section className="bg-white">
<Container className="mx-auto mb-2 mt-8 max-w-xl rounded-lg border border-solid border-slate-200 p-2 backdrop-blur-sm">
<Section className="p-2">
<Img
src={getAssetUrl('/static/logo.png')}
alt="Documenso Logo"
className="mb-4 h-6"
/>
<Preview>{_(previewText)}</Preview>
<TemplateDocumentImage className="mt-6" assetBaseUrl={assetBaseUrl} />
<Body className="mx-auto my-auto font-sans">
<Section className="bg-white">
<Container className="mx-auto mb-2 mt-8 max-w-xl rounded-lg border border-solid border-slate-200 p-2 backdrop-blur-sm">
<Section className="p-2">
<Img
src={getAssetUrl('/static/logo.png')}
alt="Documenso Logo"
className="mb-4 h-6"
/>
<Section>
<Text className="text-primary mb-0 text-center text-lg font-semibold">
<TemplateDocumentImage className="mt-6" assetBaseUrl={assetBaseUrl} />
<Section>
<Text className="text-primary mb-0 text-center text-lg font-semibold">
<Trans>
{recipientName} {action} a document by using one of your direct links
</Text>
</Trans>
</Text>
<div className="mx-auto my-2 w-fit rounded-lg bg-gray-50 px-4 py-2 text-sm text-slate-600">
{documentName}
</div>
<div className="mx-auto my-2 w-fit rounded-lg bg-gray-50 px-4 py-2 text-sm text-slate-600">
{documentName}
</div>
<Section className="my-6 text-center">
<Button
className="bg-documenso-500 inline-flex items-center justify-center rounded-lg px-6 py-3 text-center text-sm font-medium text-black no-underline"
href={documentLink}
>
View document
</Button>
</Section>
<Section className="my-6 text-center">
<Button
className="bg-documenso-500 inline-flex items-center justify-center rounded-lg px-6 py-3 text-center text-sm font-medium text-black no-underline"
href={documentLink}
>
<Trans>View document</Trans>
</Button>
</Section>
</Section>
</Container>
</Section>
</Container>
<Container className="mx-auto max-w-xl">
<TemplateFooter />
</Container>
</Section>
</Body>
</Tailwind>
<Container className="mx-auto max-w-xl">
<TemplateFooter />
</Container>
</Section>
</Body>
</Html>
);
};

View File

@ -1,20 +1,10 @@
import { Trans, msg } from '@lingui/macro';
import { useLingui } from '@lingui/react';
import { RECIPIENT_ROLES_DESCRIPTION_ENG } from '@documenso/lib/constants/recipient-roles';
import type { RecipientRole } from '@documenso/prisma/client';
import config from '@documenso/tailwind-config';
import {
Body,
Container,
Head,
Hr,
Html,
Img,
Link,
Preview,
Section,
Tailwind,
Text,
} from '../components';
import { Body, Container, Head, Hr, Html, Img, Link, Preview, Section, Text } from '../components';
import type { TemplateDocumentInviteProps } from '../template-components/template-document-invite';
import { TemplateDocumentInvite } from '../template-components/template-document-invite';
import { TemplateFooter } from '../template-components/template-footer';
@ -40,13 +30,15 @@ export const DocumentInviteEmailTemplate = ({
isTeamInvite = false,
teamName,
}: DocumentInviteEmailTemplateProps) => {
const action = RECIPIENT_ROLES_DESCRIPTION_ENG[role].actionVerb.toLowerCase();
const { _ } = useLingui();
const action = _(RECIPIENT_ROLES_DESCRIPTION_ENG[role].actionVerb).toLowerCase();
const previewText = selfSigner
? `Please ${action} your document ${documentName}`
? msg`Please ${action} your document ${documentName}`
: isTeamInvite
? `${inviterName} on behalf of ${teamName} has invited you to ${action} ${documentName}`
: `${inviterName} has invited you to ${action} ${documentName}`;
? msg`${inviterName} on behalf of ${teamName} has invited you to ${action} ${documentName}`
: msg`${inviterName} has invited you to ${action} ${documentName}`;
const getAssetUrl = (path: string) => {
return new URL(path, assetBaseUrl).toString();
@ -55,67 +47,62 @@ export const DocumentInviteEmailTemplate = ({
return (
<Html>
<Head />
<Preview>{previewText}</Preview>
<Tailwind
config={{
theme: {
extend: {
colors: config.theme.extend.colors,
},
},
}}
>
<Body className="mx-auto my-auto bg-white font-sans">
<Section>
<Container className="mx-auto mb-2 mt-8 max-w-xl rounded-lg border border-solid border-slate-200 p-4 backdrop-blur-sm">
<Section>
<Img
src={getAssetUrl('/static/logo.png')}
alt="Documenso Logo"
className="mb-4 h-6"
/>
<Preview>{_(previewText)}</Preview>
<TemplateDocumentInvite
inviterName={inviterName}
inviterEmail={inviterEmail}
documentName={documentName}
signDocumentLink={signDocumentLink}
assetBaseUrl={assetBaseUrl}
role={role}
selfSigner={selfSigner}
isTeamInvite={isTeamInvite}
teamName={teamName}
/>
</Section>
</Container>
<Body className="mx-auto my-auto bg-white font-sans">
<Section>
<Container className="mx-auto mb-2 mt-8 max-w-xl rounded-lg border border-solid border-slate-200 p-4 backdrop-blur-sm">
<Section>
<Img
src={getAssetUrl('/static/logo.png')}
alt="Documenso Logo"
className="mb-4 h-6"
/>
<Container className="mx-auto mt-12 max-w-xl">
<Section>
<Text className="my-4 text-base font-semibold">
<TemplateDocumentInvite
inviterName={inviterName}
inviterEmail={inviterEmail}
documentName={documentName}
signDocumentLink={signDocumentLink}
assetBaseUrl={assetBaseUrl}
role={role}
selfSigner={selfSigner}
isTeamInvite={isTeamInvite}
teamName={teamName}
/>
</Section>
</Container>
<Container className="mx-auto mt-12 max-w-xl">
<Section>
<Text className="my-4 text-base font-semibold">
<Trans>
{inviterName}{' '}
<Link className="font-normal text-slate-400" href="mailto:{inviterEmail}">
({inviterEmail})
</Link>
</Text>
</Trans>
</Text>
<Text className="mt-2 text-base text-slate-400">
{customBody ? (
<pre className="font-sans text-base text-slate-400">{customBody}</pre>
) : (
<Text className="mt-2 text-base text-slate-400">
{customBody ? (
<pre className="font-sans text-base text-slate-400">{customBody}</pre>
) : (
<Trans>
`${inviterName} has invited you to ${action} the document "${documentName}".`
)}
</Text>
</Section>
</Container>
</Trans>
)}
</Text>
</Section>
</Container>
<Hr className="mx-auto mt-12 max-w-xl" />
<Hr className="mx-auto mt-12 max-w-xl" />
<Container className="mx-auto max-w-xl">
<TemplateFooter />
</Container>
</Section>
</Body>
</Tailwind>
<Container className="mx-auto max-w-xl">
<TemplateFooter />
</Container>
</Section>
</Body>
</Html>
);
};

View File

@ -1,6 +1,7 @@
import config from '@documenso/tailwind-config';
import { msg } from '@lingui/macro';
import { useLingui } from '@lingui/react';
import { Body, Container, Head, Html, Img, Preview, Section, Tailwind } from '../components';
import { Body, Container, Head, Html, Img, Preview, Section } from '../components';
import type { TemplateDocumentPendingProps } from '../template-components/template-document-pending';
import { TemplateDocumentPending } from '../template-components/template-document-pending';
import { TemplateFooter } from '../template-components/template-footer';
@ -11,7 +12,9 @@ export const DocumentPendingEmailTemplate = ({
documentName = 'Open Source Pledge.pdf',
assetBaseUrl = 'http://localhost:3002',
}: DocumentPendingEmailTemplateProps) => {
const previewText = `Pending Document`;
const { _ } = useLingui();
const previewText = msg`Pending Document`;
const getAssetUrl = (path: string) => {
return new URL(path, assetBaseUrl).toString();
@ -20,36 +23,27 @@ export const DocumentPendingEmailTemplate = ({
return (
<Html>
<Head />
<Preview>{previewText}</Preview>
<Tailwind
config={{
theme: {
extend: {
colors: config.theme.extend.colors,
},
},
}}
>
<Body className="mx-auto my-auto font-sans">
<Section className="bg-white">
<Container className="mx-auto mb-2 mt-8 max-w-xl rounded-lg border border-solid border-slate-200 p-4 backdrop-blur-sm">
<Section>
<Img
src={getAssetUrl('/static/logo.png')}
alt="Documenso Logo"
className="mb-4 h-6"
/>
<Preview>{_(previewText)}</Preview>
<TemplateDocumentPending documentName={documentName} assetBaseUrl={assetBaseUrl} />
</Section>
</Container>
<Body className="mx-auto my-auto font-sans">
<Section className="bg-white">
<Container className="mx-auto mb-2 mt-8 max-w-xl rounded-lg border border-solid border-slate-200 p-4 backdrop-blur-sm">
<Section>
<Img
src={getAssetUrl('/static/logo.png')}
alt="Documenso Logo"
className="mb-4 h-6"
/>
<Container className="mx-auto max-w-xl">
<TemplateFooter />
</Container>
</Section>
</Body>
</Tailwind>
<TemplateDocumentPending documentName={documentName} assetBaseUrl={assetBaseUrl} />
</Section>
</Container>
<Container className="mx-auto max-w-xl">
<TemplateFooter />
</Container>
</Section>
</Body>
</Html>
);
};

View File

@ -1,6 +1,7 @@
import config from '@documenso/tailwind-config';
import { msg } from '@lingui/macro';
import { useLingui } from '@lingui/react';
import { Body, Container, Head, Html, Img, Preview, Section, Tailwind } from '../components';
import { Body, Container, Head, Html, Img, Preview, Section } from '../components';
import type { TemplateDocumentSelfSignedProps } from '../template-components/template-document-self-signed';
import { TemplateDocumentSelfSigned } from '../template-components/template-document-self-signed';
import { TemplateFooter } from '../template-components/template-footer';
@ -11,7 +12,9 @@ export const DocumentSelfSignedEmailTemplate = ({
documentName = 'Open Source Pledge.pdf',
assetBaseUrl = 'http://localhost:3002',
}: DocumentSelfSignedTemplateProps) => {
const previewText = `Completed Document`;
const { _ } = useLingui();
const previewText = msg`Completed Document`;
const getAssetUrl = (path: string) => {
return new URL(path, assetBaseUrl).toString();
@ -20,39 +23,27 @@ export const DocumentSelfSignedEmailTemplate = ({
return (
<Html>
<Head />
<Preview>{previewText}</Preview>
<Tailwind
config={{
theme: {
extend: {
colors: config.theme.extend.colors,
},
},
}}
>
<Body className="mx-auto my-auto font-sans">
<Section className="bg-white">
<Container className="mx-auto mb-2 mt-8 max-w-xl rounded-lg border border-solid border-slate-200 p-2 backdrop-blur-sm">
<Section className="p-2">
<Img
src={getAssetUrl('/static/logo.png')}
alt="Documenso Logo"
className="mb-4 h-6"
/>
<Preview>{_(previewText)}</Preview>
<TemplateDocumentSelfSigned
documentName={documentName}
assetBaseUrl={assetBaseUrl}
/>
</Section>
</Container>
<Body className="mx-auto my-auto font-sans">
<Section className="bg-white">
<Container className="mx-auto mb-2 mt-8 max-w-xl rounded-lg border border-solid border-slate-200 p-2 backdrop-blur-sm">
<Section className="p-2">
<Img
src={getAssetUrl('/static/logo.png')}
alt="Documenso Logo"
className="mb-4 h-6"
/>
<Container className="mx-auto max-w-xl">
<TemplateFooter />
</Container>
</Section>
</Body>
</Tailwind>
<TemplateDocumentSelfSigned documentName={documentName} assetBaseUrl={assetBaseUrl} />
</Section>
</Container>
<Container className="mx-auto max-w-xl">
<TemplateFooter />
</Container>
</Section>
</Body>
</Html>
);
};

View File

@ -1,6 +1,7 @@
import config from '@documenso/tailwind-config';
import { msg } from '@lingui/macro';
import { useLingui } from '@lingui/react';
import { Body, Container, Head, Hr, Html, Img, Preview, Section, Tailwind } from '../components';
import { Body, Container, Head, Hr, Html, Img, Preview, Section } from '../components';
import {
TemplateDocumentDelete,
type TemplateDocumentDeleteProps,
@ -14,7 +15,9 @@ export const DocumentSuperDeleteEmailTemplate = ({
assetBaseUrl = 'http://localhost:3002',
reason = 'Unknown',
}: DocumentDeleteEmailTemplateProps) => {
const previewText = `An admin has deleted your document "${documentName}".`;
const { _ } = useLingui();
const previewText = msg`An admin has deleted your document "${documentName}".`;
const getAssetUrl = (path: string) => {
return new URL(path, assetBaseUrl).toString();
@ -23,42 +26,33 @@ export const DocumentSuperDeleteEmailTemplate = ({
return (
<Html>
<Head />
<Preview>{previewText}</Preview>
<Tailwind
config={{
theme: {
extend: {
colors: config.theme.extend.colors,
},
},
}}
>
<Body className="mx-auto my-auto bg-white font-sans">
<Section>
<Container className="mx-auto mb-2 mt-8 max-w-xl rounded-lg border border-solid border-slate-200 p-4 backdrop-blur-sm">
<Section>
<Img
src={getAssetUrl('/static/logo.png')}
alt="Documenso Logo"
className="mb-4 h-6"
/>
<Preview>{_(previewText)}</Preview>
<TemplateDocumentDelete
reason={reason}
documentName={documentName}
assetBaseUrl={assetBaseUrl}
/>
</Section>
</Container>
<Body className="mx-auto my-auto bg-white font-sans">
<Section>
<Container className="mx-auto mb-2 mt-8 max-w-xl rounded-lg border border-solid border-slate-200 p-4 backdrop-blur-sm">
<Section>
<Img
src={getAssetUrl('/static/logo.png')}
alt="Documenso Logo"
className="mb-4 h-6"
/>
<Hr className="mx-auto mt-12 max-w-xl" />
<TemplateDocumentDelete
reason={reason}
documentName={documentName}
assetBaseUrl={assetBaseUrl}
/>
</Section>
</Container>
<Container className="mx-auto max-w-xl">
<TemplateFooter />
</Container>
</Section>
</Body>
</Tailwind>
<Hr className="mx-auto mt-12 max-w-xl" />
<Container className="mx-auto max-w-xl">
<TemplateFooter />
</Container>
</Section>
</Body>
</Html>
);
};

View File

@ -1,6 +1,7 @@
import config from '@documenso/tailwind-config';
import { msg } from '@lingui/macro';
import { useLingui } from '@lingui/react';
import { Body, Container, Head, Html, Img, Preview, Section, Tailwind } from '../components';
import { Body, Container, Head, Html, Img, Preview, Section } from '../components';
import { TemplateFooter } from '../template-components/template-footer';
import type { TemplateForgotPasswordProps } from '../template-components/template-forgot-password';
import { TemplateForgotPassword } from '../template-components/template-forgot-password';
@ -11,7 +12,9 @@ export const ForgotPasswordTemplate = ({
resetPasswordLink = 'https://documenso.com',
assetBaseUrl = 'http://localhost:3002',
}: ForgotPasswordTemplateProps) => {
const previewText = `Password Reset Requested`;
const { _ } = useLingui();
const previewText = msg`Password Reset Requested`;
const getAssetUrl = (path: string) => {
return new URL(path, assetBaseUrl).toString();
@ -20,41 +23,32 @@ export const ForgotPasswordTemplate = ({
return (
<Html>
<Head />
<Preview>{previewText}</Preview>
<Tailwind
config={{
theme: {
extend: {
colors: config.theme.extend.colors,
},
},
}}
>
<Body className="mx-auto my-auto bg-white font-sans">
<Section>
<Container className="mx-auto mb-2 mt-8 max-w-xl rounded-lg border border-solid border-slate-200 p-4 backdrop-blur-sm">
<Section>
<Img
src={getAssetUrl('/static/logo.png')}
alt="Documenso Logo"
className="mb-4 h-6"
/>
<Preview>{_(previewText)}</Preview>
<TemplateForgotPassword
resetPasswordLink={resetPasswordLink}
assetBaseUrl={assetBaseUrl}
/>
</Section>
</Container>
<Body className="mx-auto my-auto bg-white font-sans">
<Section>
<Container className="mx-auto mb-2 mt-8 max-w-xl rounded-lg border border-solid border-slate-200 p-4 backdrop-blur-sm">
<Section>
<Img
src={getAssetUrl('/static/logo.png')}
alt="Documenso Logo"
className="mb-4 h-6"
/>
<div className="mx-auto mt-12 max-w-xl" />
<TemplateForgotPassword
resetPasswordLink={resetPasswordLink}
assetBaseUrl={assetBaseUrl}
/>
</Section>
</Container>
<Container className="mx-auto max-w-xl">
<TemplateFooter isDocument={false} />
</Container>
</Section>
</Body>
</Tailwind>
<div className="mx-auto mt-12 max-w-xl" />
<Container className="mx-auto max-w-xl">
<TemplateFooter isDocument={false} />
</Container>
</Section>
</Body>
</Html>
);
};

View File

@ -1,17 +1,7 @@
import config from '@documenso/tailwind-config';
import { msg } from '@lingui/macro';
import { useLingui } from '@lingui/react';
import {
Body,
Container,
Head,
Hr,
Html,
Img,
Preview,
Section,
Tailwind,
Text,
} from '../components';
import { Body, Container, Head, Hr, Html, Img, Preview, Section, Text } from '../components';
import type { TemplateDocumentCancelProps } from '../template-components/template-document-cancel';
import TemplateDocumentImage from '../template-components/template-document-image';
import { TemplateFooter } from '../template-components/template-footer';
@ -23,7 +13,9 @@ export const RecipientRemovedFromDocumentTemplate = ({
documentName = 'Open Source Pledge.pdf',
assetBaseUrl = 'http://localhost:3002',
}: DocumentCancelEmailTemplateProps) => {
const previewText = `${inviterName} has removed you from the document ${documentName}.`;
const { _ } = useLingui();
const previewText = msg`${inviterName} has removed you from the document ${documentName}.`;
const getAssetUrl = (path: string) => {
return new URL(path, assetBaseUrl).toString();
@ -32,45 +24,36 @@ export const RecipientRemovedFromDocumentTemplate = ({
return (
<Html>
<Head />
<Preview>{previewText}</Preview>
<Tailwind
config={{
theme: {
extend: {
colors: config.theme.extend.colors,
},
},
}}
>
<Body className="mx-auto my-auto bg-white font-sans">
<Section>
<Container className="mx-auto mb-2 mt-8 max-w-xl rounded-lg border border-solid border-slate-200 p-4 backdrop-blur-sm">
<Preview>{_(previewText)}</Preview>
<Body className="mx-auto my-auto bg-white font-sans">
<Section>
<Container className="mx-auto mb-2 mt-8 max-w-xl rounded-lg border border-solid border-slate-200 p-4 backdrop-blur-sm">
<Section>
<Img
src={getAssetUrl('/static/logo.png')}
alt="Documenso Logo"
className="mb-4 h-6"
/>
<TemplateDocumentImage className="mt-6" assetBaseUrl={assetBaseUrl} />
<Section>
<Img
src={getAssetUrl('/static/logo.png')}
alt="Documenso Logo"
className="mb-4 h-6"
/>
<TemplateDocumentImage className="mt-6" assetBaseUrl={assetBaseUrl} />
<Section>
<Text className="text-primary mx-auto mb-0 max-w-[80%] text-center text-lg font-semibold">
{inviterName} has removed you from the document
<br />"{documentName}"
</Text>
</Section>
<Text className="text-primary mx-auto mb-0 max-w-[80%] text-center text-lg font-semibold">
{inviterName} has removed you from the document
<br />"{documentName}"
</Text>
</Section>
</Container>
</Section>
</Container>
<Hr className="mx-auto mt-12 max-w-xl" />
<Hr className="mx-auto mt-12 max-w-xl" />
<Container className="mx-auto max-w-xl">
<TemplateFooter />
</Container>
</Section>
</Body>
</Tailwind>
<Container className="mx-auto max-w-xl">
<TemplateFooter />
</Container>
</Section>
</Body>
</Html>
);
};

View File

@ -1,18 +1,7 @@
import config from '@documenso/tailwind-config';
import { Trans, msg } from '@lingui/macro';
import { useLingui } from '@lingui/react';
import {
Body,
Container,
Head,
Hr,
Html,
Img,
Link,
Preview,
Section,
Tailwind,
Text,
} from '../components';
import { Body, Container, Head, Hr, Html, Img, Link, Preview, Section, Text } from '../components';
import { TemplateFooter } from '../template-components/template-footer';
import type { TemplateResetPasswordProps } from '../template-components/template-reset-password';
import { TemplateResetPassword } from '../template-components/template-reset-password';
@ -24,7 +13,9 @@ export const ResetPasswordTemplate = ({
userEmail = 'lucas@documenso.com',
assetBaseUrl = 'http://localhost:3002',
}: ResetPasswordTemplateProps) => {
const previewText = `Password Reset Successful`;
const { _ } = useLingui();
const previewText = msg`Password Reset Successful`;
const getAssetUrl = (path: string) => {
return new URL(path, assetBaseUrl).toString();
@ -33,65 +24,62 @@ export const ResetPasswordTemplate = ({
return (
<Html>
<Head />
<Preview>{previewText}</Preview>
<Tailwind
config={{
theme: {
extend: {
colors: config.theme.extend.colors,
},
},
}}
>
<Body className="mx-auto my-auto bg-white font-sans">
<Section>
<Container className="mx-auto mb-2 mt-8 max-w-xl rounded-lg border border-solid border-slate-200 p-4 backdrop-blur-sm">
<Section>
<Img
src={getAssetUrl('/static/logo.png')}
alt="Documenso Logo"
className="mb-4 h-6"
/>
<Preview>{_(previewText)}</Preview>
<TemplateResetPassword
userName={userName}
userEmail={userEmail}
assetBaseUrl={assetBaseUrl}
/>
</Section>
</Container>
<Body className="mx-auto my-auto bg-white font-sans">
<Section>
<Container className="mx-auto mb-2 mt-8 max-w-xl rounded-lg border border-solid border-slate-200 p-4 backdrop-blur-sm">
<Section>
<Img
src={getAssetUrl('/static/logo.png')}
alt="Documenso Logo"
className="mb-4 h-6"
/>
<Container className="mx-auto mt-12 max-w-xl">
<Section>
<Text className="my-4 text-base font-semibold">
<TemplateResetPassword
userName={userName}
userEmail={userEmail}
assetBaseUrl={assetBaseUrl}
/>
</Section>
</Container>
<Container className="mx-auto mt-12 max-w-xl">
<Section>
<Text className="my-4 text-base font-semibold">
<Trans>
Hi, {userName}{' '}
<Link className="font-normal text-slate-400" href={`mailto:${userEmail}`}>
({userEmail})
</Link>
</Text>
</Trans>
</Text>
<Text className="mt-2 text-base text-slate-400">
<Text className="mt-2 text-base text-slate-400">
<Trans>
We've changed your password as you asked. You can now sign in with your new
password.
</Text>
<Text className="mt-2 text-base text-slate-400">
</Trans>
</Text>
<Text className="mt-2 text-base text-slate-400">
<Trans>
Didn't request a password change? We are here to help you secure your account,
just{' '}
<Link className="text-documenso-700 font-normal" href="mailto:hi@documenso.com">
contact us.
</Link>
</Text>
</Section>
</Container>
</Trans>
</Text>
</Section>
</Container>
<Hr className="mx-auto mt-12 max-w-xl" />
<Hr className="mx-auto mt-12 max-w-xl" />
<Container className="mx-auto max-w-xl">
<TemplateFooter isDocument={false} />
</Container>
</Section>
</Body>
</Tailwind>
<Container className="mx-auto max-w-xl">
<TemplateFooter isDocument={false} />
</Container>
</Section>
</Body>
</Html>
);
};

View File

@ -1,7 +1,9 @@
import { formatTeamUrl } from '@documenso/lib/utils/teams';
import config from '@documenso/tailwind-config';
import { msg } from '@lingui/macro';
import { useLingui } from '@lingui/react';
import { Body, Container, Head, Hr, Html, Preview, Section, Tailwind, Text } from '../components';
import { formatTeamUrl } from '@documenso/lib/utils/teams';
import { Body, Container, Head, Hr, Html, Preview, Section, Text } from '../components';
import { TemplateFooter } from '../template-components/template-footer';
import TemplateImage from '../template-components/template-image';
@ -18,67 +20,60 @@ export const TeamDeleteEmailTemplate = ({
teamUrl = 'demo',
isOwner = false,
}: TeamDeleteEmailProps) => {
const { _ } = useLingui();
const previewText = isOwner
? 'Your team has been deleted'
: 'A team you were a part of has been deleted';
? msg`Your team has been deleted`
: msg`A team you were a part of has been deleted`;
const title = isOwner
? 'Your team has been deleted'
: 'A team you were a part of has been deleted';
? msg`Your team has been deleted`
: msg`A team you were a part of has been deleted`;
const description = isOwner
? 'The following team has been deleted by you'
: 'The following team has been deleted by its owner. You will no longer be able to access this team and its documents';
? msg`The following team has been deleted by you`
: msg`The following team has been deleted by its owner. You will no longer be able to access this team and its documents`;
return (
<Html>
<Head />
<Preview>{previewText}</Preview>
<Tailwind
config={{
theme: {
extend: {
colors: config.theme.extend.colors,
},
},
}}
>
<Body className="mx-auto my-auto font-sans">
<Section className="bg-white text-slate-500">
<Container className="mx-auto mb-2 mt-8 max-w-xl rounded-lg border border-solid border-slate-200 p-2 backdrop-blur-sm">
<Preview>{_(previewText)}</Preview>
<Body className="mx-auto my-auto font-sans">
<Section className="bg-white text-slate-500">
<Container className="mx-auto mb-2 mt-8 max-w-xl rounded-lg border border-solid border-slate-200 p-2 backdrop-blur-sm">
<TemplateImage
assetBaseUrl={assetBaseUrl}
className="mb-4 h-6 p-2"
staticAsset="logo.png"
/>
<Section>
<TemplateImage
className="mx-auto"
assetBaseUrl={assetBaseUrl}
className="mb-4 h-6 p-2"
staticAsset="logo.png"
staticAsset="delete-team.png"
/>
</Section>
<Section>
<TemplateImage
className="mx-auto"
assetBaseUrl={assetBaseUrl}
staticAsset="delete-team.png"
/>
</Section>
<Section className="p-2 text-slate-500">
<Text className="text-center text-lg font-medium text-black">{_(title)}</Text>
<Section className="p-2 text-slate-500">
<Text className="text-center text-lg font-medium text-black">{title}</Text>
<Text className="my-1 text-center text-base">{_(description)}</Text>
<Text className="my-1 text-center text-base">{description}</Text>
<div className="mx-auto my-2 w-fit rounded-lg bg-gray-50 px-4 py-2 text-base font-medium text-slate-600">
{formatTeamUrl(teamUrl, baseUrl)}
</div>
</Section>
</Container>
<div className="mx-auto my-2 w-fit rounded-lg bg-gray-50 px-4 py-2 text-base font-medium text-slate-600">
{formatTeamUrl(teamUrl, baseUrl)}
</div>
</Section>
</Container>
<Hr className="mx-auto mt-12 max-w-xl" />
<Hr className="mx-auto mt-12 max-w-xl" />
<Container className="mx-auto max-w-xl">
<TemplateFooter isDocument={false} />
</Container>
</Section>
</Body>
</Tailwind>
<Container className="mx-auto max-w-xl">
<TemplateFooter isDocument={false} />
</Container>
</Section>
</Body>
</Html>
);
};

View File

@ -1,7 +1,9 @@
import { formatTeamUrl } from '@documenso/lib/utils/teams';
import config from '@documenso/tailwind-config';
import { Trans, msg } from '@lingui/macro';
import { useLingui } from '@lingui/react';
import { Body, Container, Head, Hr, Html, Preview, Section, Tailwind, Text } from '../components';
import { formatTeamUrl } from '@documenso/lib/utils/teams';
import { Body, Container, Head, Hr, Html, Preview, Section, Text } from '../components';
import { TemplateFooter } from '../template-components/template-footer';
import TemplateImage from '../template-components/template-image';
@ -20,62 +22,57 @@ export const TeamEmailRemovedTemplate = ({
teamName = 'Team Name',
teamUrl = 'demo',
}: TeamEmailRemovedTemplateProps) => {
const previewText = `Team email removed for ${teamName} on Documenso`;
const { _ } = useLingui();
const previewText = msg`Team email removed for ${teamName} on Documenso`;
return (
<Html>
<Head />
<Preview>{previewText}</Preview>
<Tailwind
config={{
theme: {
extend: {
colors: config.theme.extend.colors,
},
},
}}
>
<Body className="mx-auto my-auto font-sans">
<Section className="bg-white text-slate-500">
<Container className="mx-auto mb-2 mt-8 max-w-xl rounded-lg border border-solid border-slate-200 px-2 pt-2 backdrop-blur-sm">
<Preview>{_(previewText)}</Preview>
<Body className="mx-auto my-auto font-sans">
<Section className="bg-white text-slate-500">
<Container className="mx-auto mb-2 mt-8 max-w-xl rounded-lg border border-solid border-slate-200 px-2 pt-2 backdrop-blur-sm">
<TemplateImage
assetBaseUrl={assetBaseUrl}
className="mb-4 h-6 p-2"
staticAsset="logo.png"
/>
<Section>
<TemplateImage
className="mx-auto"
assetBaseUrl={assetBaseUrl}
className="mb-4 h-6 p-2"
staticAsset="logo.png"
staticAsset="mail-open-alert.png"
/>
</Section>
<Section>
<TemplateImage
className="mx-auto"
assetBaseUrl={assetBaseUrl}
staticAsset="mail-open-alert.png"
/>
</Section>
<Section className="p-2 text-slate-500">
<Text className="text-center text-lg font-medium text-black">
<Trans>Team email removed</Trans>
</Text>
<Section className="p-2 text-slate-500">
<Text className="text-center text-lg font-medium text-black">
Team email removed
</Text>
<Text className="my-1 text-center text-base">
<Text className="my-1 text-center text-base">
<Trans>
The team email <span className="font-bold">{teamEmail}</span> has been removed
from the following team
</Text>
</Trans>
</Text>
<div className="mx-auto mb-6 mt-2 w-fit rounded-lg bg-gray-50 px-4 py-2 text-base font-medium text-slate-600">
{formatTeamUrl(teamUrl, baseUrl)}
</div>
</Section>
</Container>
<div className="mx-auto mb-6 mt-2 w-fit rounded-lg bg-gray-50 px-4 py-2 text-base font-medium text-slate-600">
{formatTeamUrl(teamUrl, baseUrl)}
</div>
</Section>
</Container>
<Hr className="mx-auto mt-12 max-w-xl" />
<Hr className="mx-auto mt-12 max-w-xl" />
<Container className="mx-auto max-w-xl">
<TemplateFooter isDocument={false} />
</Container>
</Section>
</Body>
</Tailwind>
<Container className="mx-auto max-w-xl">
<TemplateFooter isDocument={false} />
</Container>
</Section>
</Body>
</Html>
);
};

View File

@ -1,18 +1,9 @@
import { formatTeamUrl } from '@documenso/lib/utils/teams';
import config from '@documenso/tailwind-config';
import { Trans, msg } from '@lingui/macro';
import { useLingui } from '@lingui/react';
import {
Body,
Button,
Container,
Head,
Hr,
Html,
Preview,
Section,
Tailwind,
Text,
} from '../components';
import { formatTeamUrl } from '@documenso/lib/utils/teams';
import { Body, Button, Container, Head, Hr, Html, Preview, Section, Text } from '../components';
import { TemplateFooter } from '../template-components/template-footer';
import TemplateImage from '../template-components/template-image';
@ -33,80 +24,75 @@ export const TeamInviteEmailTemplate = ({
teamUrl = 'demo',
token = '',
}: TeamInviteEmailProps) => {
const previewText = `Accept invitation to join a team on Documenso`;
const { _ } = useLingui();
const previewText = msg`Accept invitation to join a team on Documenso`;
return (
<Html>
<Head />
<Preview>{previewText}</Preview>
<Tailwind
config={{
theme: {
extend: {
colors: config.theme.extend.colors,
},
},
}}
>
<Body className="mx-auto my-auto font-sans">
<Section className="bg-white text-slate-500">
<Container className="mx-auto mb-2 mt-8 max-w-xl rounded-lg border border-solid border-slate-200 p-2 backdrop-blur-sm">
<Preview>{_(previewText)}</Preview>
<Body className="mx-auto my-auto font-sans">
<Section className="bg-white text-slate-500">
<Container className="mx-auto mb-2 mt-8 max-w-xl rounded-lg border border-solid border-slate-200 p-2 backdrop-blur-sm">
<TemplateImage
assetBaseUrl={assetBaseUrl}
className="mb-4 h-6 p-2"
staticAsset="logo.png"
/>
<Section>
<TemplateImage
className="mx-auto"
assetBaseUrl={assetBaseUrl}
className="mb-4 h-6 p-2"
staticAsset="logo.png"
staticAsset="add-user.png"
/>
</Section>
<Section>
<TemplateImage
className="mx-auto"
assetBaseUrl={assetBaseUrl}
staticAsset="add-user.png"
/>
</Section>
<Section className="p-2 text-slate-500">
<Text className="text-center text-lg font-medium text-black">
<Trans>Join {teamName} on Documenso</Trans>
</Text>
<Section className="p-2 text-slate-500">
<Text className="text-center text-lg font-medium text-black">
Join {teamName} on Documenso
</Text>
<Text className="my-1 text-center text-base">
<Trans>You have been invited to join the following team</Trans>
</Text>
<Text className="my-1 text-center text-base">
You have been invited to join the following team
</Text>
<div className="mx-auto my-2 w-fit rounded-lg bg-gray-50 px-4 py-2 text-base font-medium text-slate-600">
{formatTeamUrl(teamUrl, baseUrl)}
</div>
<div className="mx-auto my-2 w-fit rounded-lg bg-gray-50 px-4 py-2 text-base font-medium text-slate-600">
{formatTeamUrl(teamUrl, baseUrl)}
</div>
<Text className="my-1 text-center text-base">
<Text className="my-1 text-center text-base">
<Trans>
by <span className="text-slate-900">{senderName}</span>
</Text>
</Trans>
</Text>
<Section className="mb-6 mt-6 text-center">
<Button
className="bg-documenso-500 inline-flex items-center justify-center rounded-lg px-6 py-3 text-center text-sm font-medium text-black no-underline"
href={`${baseUrl}/team/invite/${token}`}
>
Accept
</Button>
<Button
className="ml-4 inline-flex items-center justify-center rounded-lg bg-gray-50 px-6 py-3 text-center text-sm font-medium text-slate-600 no-underline"
href={`${baseUrl}/team/decline/${token}`}
>
Decline
</Button>
</Section>
<Section className="mb-6 mt-6 text-center">
<Button
className="bg-documenso-500 inline-flex items-center justify-center rounded-lg px-6 py-3 text-center text-sm font-medium text-black no-underline"
href={`${baseUrl}/team/invite/${token}`}
>
<Trans>Accept</Trans>
</Button>
<Button
className="ml-4 inline-flex items-center justify-center rounded-lg bg-gray-50 px-6 py-3 text-center text-sm font-medium text-slate-600 no-underline"
href={`${baseUrl}/team/decline/${token}`}
>
<Trans>Decline</Trans>
</Button>
</Section>
</Container>
</Section>
</Container>
<Hr className="mx-auto mt-12 max-w-xl" />
<Hr className="mx-auto mt-12 max-w-xl" />
<Container className="mx-auto max-w-xl">
<TemplateFooter isDocument={false} />
</Container>
</Section>
</Body>
</Tailwind>
<Container className="mx-auto max-w-xl">
<TemplateFooter isDocument={false} />
</Container>
</Section>
</Body>
</Html>
);
};

View File

@ -1,7 +1,9 @@
import { formatTeamUrl } from '@documenso/lib/utils/teams';
import config from '@documenso/tailwind-config';
import { Trans, msg } from '@lingui/macro';
import { useLingui } from '@lingui/react';
import { Body, Container, Head, Hr, Html, Preview, Section, Tailwind, Text } from '../components';
import { formatTeamUrl } from '@documenso/lib/utils/teams';
import { Body, Container, Head, Hr, Html, Preview, Section, Text } from '../components';
import { TemplateFooter } from '../template-components/template-footer';
import TemplateImage from '../template-components/template-image';
@ -22,61 +24,56 @@ export const TeamJoinEmailTemplate = ({
teamName = 'Team Name',
teamUrl = 'demo',
}: TeamJoinEmailProps) => {
const previewText = 'A team member has joined a team on Documenso';
const { _ } = useLingui();
const previewText = msg`A team member has joined a team on Documenso`;
return (
<Html>
<Head />
<Preview>{previewText}</Preview>
<Tailwind
config={{
theme: {
extend: {
colors: config.theme.extend.colors,
},
},
}}
>
<Body className="mx-auto my-auto font-sans">
<Section className="bg-white text-slate-500">
<Container className="mx-auto mb-2 mt-8 max-w-xl rounded-lg border border-solid border-slate-200 p-2 backdrop-blur-sm">
<Preview>{_(previewText)}</Preview>
<Body className="mx-auto my-auto font-sans">
<Section className="bg-white text-slate-500">
<Container className="mx-auto mb-2 mt-8 max-w-xl rounded-lg border border-solid border-slate-200 p-2 backdrop-blur-sm">
<TemplateImage
assetBaseUrl={assetBaseUrl}
className="mb-4 h-6 p-2"
staticAsset="logo.png"
/>
<Section>
<TemplateImage
className="mx-auto"
assetBaseUrl={assetBaseUrl}
className="mb-4 h-6 p-2"
staticAsset="logo.png"
staticAsset="add-user.png"
/>
</Section>
<Section>
<TemplateImage
className="mx-auto"
assetBaseUrl={assetBaseUrl}
staticAsset="add-user.png"
/>
</Section>
<Section className="p-2 text-slate-500">
<Text className="text-center text-lg font-medium text-black">
<Section className="p-2 text-slate-500">
<Text className="text-center text-lg font-medium text-black">
<Trans>
{memberName || memberEmail} joined the team {teamName} on Documenso
</Text>
</Trans>
</Text>
<Text className="my-1 text-center text-base">
{memberEmail} joined the following team
</Text>
<Text className="my-1 text-center text-base">
<Trans>{memberEmail} joined the following team</Trans>
</Text>
<div className="mx-auto my-2 w-fit rounded-lg bg-gray-50 px-4 py-2 text-base font-medium text-slate-600">
{formatTeamUrl(teamUrl, baseUrl)}
</div>
</Section>
</Container>
<div className="mx-auto my-2 w-fit rounded-lg bg-gray-50 px-4 py-2 text-base font-medium text-slate-600">
{formatTeamUrl(teamUrl, baseUrl)}
</div>
</Section>
</Container>
<Hr className="mx-auto mt-12 max-w-xl" />
<Hr className="mx-auto mt-12 max-w-xl" />
<Container className="mx-auto max-w-xl">
<TemplateFooter isDocument={false} />
</Container>
</Section>
</Body>
</Tailwind>
<Container className="mx-auto max-w-xl">
<TemplateFooter isDocument={false} />
</Container>
</Section>
</Body>
</Html>
);
};

View File

@ -1,7 +1,9 @@
import { formatTeamUrl } from '@documenso/lib/utils/teams';
import config from '@documenso/tailwind-config';
import { Trans, msg } from '@lingui/macro';
import { useLingui } from '@lingui/react';
import { Body, Container, Head, Hr, Html, Preview, Section, Tailwind, Text } from '../components';
import { formatTeamUrl } from '@documenso/lib/utils/teams';
import { Body, Container, Head, Hr, Html, Preview, Section, Text } from '../components';
import { TemplateFooter } from '../template-components/template-footer';
import TemplateImage from '../template-components/template-image';
@ -22,61 +24,56 @@ export const TeamLeaveEmailTemplate = ({
teamName = 'Team Name',
teamUrl = 'demo',
}: TeamLeaveEmailProps) => {
const previewText = 'A team member has left a team on Documenso';
const { _ } = useLingui();
const previewText = msg`A team member has left a team on Documenso`;
return (
<Html>
<Head />
<Preview>{previewText}</Preview>
<Tailwind
config={{
theme: {
extend: {
colors: config.theme.extend.colors,
},
},
}}
>
<Body className="mx-auto my-auto font-sans">
<Section className="bg-white text-slate-500">
<Container className="mx-auto mb-2 mt-8 max-w-xl rounded-lg border border-solid border-slate-200 p-2 backdrop-blur-sm">
<Preview>{_(previewText)}</Preview>
<Body className="mx-auto my-auto font-sans">
<Section className="bg-white text-slate-500">
<Container className="mx-auto mb-2 mt-8 max-w-xl rounded-lg border border-solid border-slate-200 p-2 backdrop-blur-sm">
<TemplateImage
assetBaseUrl={assetBaseUrl}
className="mb-4 h-6 p-2"
staticAsset="logo.png"
/>
<Section>
<TemplateImage
className="mx-auto"
assetBaseUrl={assetBaseUrl}
className="mb-4 h-6 p-2"
staticAsset="logo.png"
staticAsset="delete-user.png"
/>
</Section>
<Section>
<TemplateImage
className="mx-auto"
assetBaseUrl={assetBaseUrl}
staticAsset="delete-user.png"
/>
</Section>
<Section className="p-2 text-slate-500">
<Text className="text-center text-lg font-medium text-black">
<Section className="p-2 text-slate-500">
<Text className="text-center text-lg font-medium text-black">
<Trans>
{memberName || memberEmail} left the team {teamName} on Documenso
</Text>
</Trans>
</Text>
<Text className="my-1 text-center text-base">
{memberEmail} left the following team
</Text>
<Text className="my-1 text-center text-base">
<Trans>{memberEmail} left the following team</Trans>
</Text>
<div className="mx-auto my-2 w-fit rounded-lg bg-gray-50 px-4 py-2 text-base font-medium text-slate-600">
{formatTeamUrl(teamUrl, baseUrl)}
</div>
</Section>
</Container>
<div className="mx-auto my-2 w-fit rounded-lg bg-gray-50 px-4 py-2 text-base font-medium text-slate-600">
{formatTeamUrl(teamUrl, baseUrl)}
</div>
</Section>
</Container>
<Hr className="mx-auto mt-12 max-w-xl" />
<Hr className="mx-auto mt-12 max-w-xl" />
<Container className="mx-auto max-w-xl">
<TemplateFooter isDocument={false} />
</Container>
</Section>
</Body>
</Tailwind>
<Container className="mx-auto max-w-xl">
<TemplateFooter isDocument={false} />
</Container>
</Section>
</Body>
</Html>
);
};

View File

@ -1,18 +1,9 @@
import { formatTeamUrl } from '@documenso/lib/utils/teams';
import config from '@documenso/tailwind-config';
import { Trans, msg } from '@lingui/macro';
import { useLingui } from '@lingui/react';
import {
Body,
Button,
Container,
Head,
Hr,
Html,
Preview,
Section,
Tailwind,
Text,
} from '../components';
import { formatTeamUrl } from '@documenso/lib/utils/teams';
import { Body, Button, Container, Head, Hr, Html, Preview, Section, Text } from '../components';
import { TemplateFooter } from '../template-components/template-footer';
import TemplateImage from '../template-components/template-image';
@ -33,78 +24,77 @@ export const TeamTransferRequestTemplate = ({
teamUrl = 'demo',
token = '',
}: TeamTransferRequestTemplateProps) => {
const previewText = 'Accept team transfer request on Documenso';
const { _ } = useLingui();
const previewText = msg`Accept team transfer request on Documenso`;
return (
<Html>
<Head />
<Preview>{previewText}</Preview>
<Tailwind
config={{
theme: {
extend: {
colors: config.theme.extend.colors,
},
},
}}
>
<Body className="mx-auto my-auto font-sans">
<Section className="bg-white text-slate-500">
<Container className="mx-auto mb-2 mt-8 max-w-xl rounded-lg border border-solid border-slate-200 px-2 pt-2 backdrop-blur-sm">
<Preview>{_(previewText)}</Preview>
<Body className="mx-auto my-auto font-sans">
<Section className="bg-white text-slate-500">
<Container className="mx-auto mb-2 mt-8 max-w-xl rounded-lg border border-solid border-slate-200 px-2 pt-2 backdrop-blur-sm">
<TemplateImage
assetBaseUrl={assetBaseUrl}
className="mb-4 h-6 p-2"
staticAsset="logo.png"
/>
<Section>
<TemplateImage
className="mx-auto"
assetBaseUrl={assetBaseUrl}
className="mb-4 h-6 p-2"
staticAsset="logo.png"
staticAsset="add-user.png"
/>
</Section>
<Section>
<TemplateImage
className="mx-auto"
assetBaseUrl={assetBaseUrl}
staticAsset="add-user.png"
/>
</Section>
<Section className="p-2 text-slate-500">
<Text className="text-center text-lg font-medium text-black">
<Trans>{teamName} ownership transfer request</Trans>
</Text>
<Section className="p-2 text-slate-500">
<Text className="text-center text-lg font-medium text-black">
{teamName} ownership transfer request
</Text>
<Text className="my-1 text-center text-base">
<Text className="my-1 text-center text-base">
<Trans>
<span className="font-bold">{senderName}</span> has requested that you take
ownership of the following team
</Text>
</Trans>
</Text>
<div className="mx-auto my-2 w-fit rounded-lg bg-gray-50 px-4 py-2 text-base font-medium text-slate-600">
{formatTeamUrl(teamUrl, baseUrl)}
</div>
<div className="mx-auto my-2 w-fit rounded-lg bg-gray-50 px-4 py-2 text-base font-medium text-slate-600">
{formatTeamUrl(teamUrl, baseUrl)}
</div>
<Text className="text-center text-sm">
<Text className="text-center text-sm">
<Trans>
By accepting this request, you will take responsibility for any billing items
associated with this team.
</Text>
</Trans>
</Text>
<Section className="mb-6 mt-6 text-center">
<Button
className="bg-documenso-500 ml-2 inline-flex items-center justify-center rounded-lg px-6 py-3 text-center text-sm font-medium text-black no-underline"
href={`${baseUrl}/team/verify/transfer/${token}`}
>
Accept
</Button>
</Section>
<Section className="mb-6 mt-6 text-center">
<Button
className="bg-documenso-500 ml-2 inline-flex items-center justify-center rounded-lg px-6 py-3 text-center text-sm font-medium text-black no-underline"
href={`${baseUrl}/team/verify/transfer/${token}`}
>
<Trans>Accept</Trans>
</Button>
</Section>
</Section>
<Text className="text-center text-xs">Link expires in 1 hour.</Text>
</Container>
<Text className="text-center text-xs">
<Trans>Link expires in 1 hour.</Trans>
</Text>
</Container>
<Hr className="mx-auto mt-12 max-w-xl" />
<Hr className="mx-auto mt-12 max-w-xl" />
<Container className="mx-auto max-w-xl">
<TemplateFooter isDocument={false} />
</Container>
</Section>
</Body>
</Tailwind>
<Container className="mx-auto max-w-xl">
<TemplateFooter isDocument={false} />
</Container>
</Section>
</Body>
</Html>
);
};