have footer on emails (#11300)
# ISSUE - Closes #9622 --------- Co-authored-by: Félix Malfait <felix@twenty.com>
This commit is contained in:
@ -1,4 +1,5 @@
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import { i18n } from '@lingui/core';
|
||||
import { Trans } from '@lingui/react';
|
||||
import { BaseEmail } from 'src/components/BaseEmail';
|
||||
import { CallToAction } from 'src/components/CallToAction';
|
||||
import { MainText } from 'src/components/MainText';
|
||||
@ -20,32 +21,40 @@ export const CleanSuspendedWorkspaceEmail = ({
|
||||
}: CleanSuspendedWorkspaceEmailProps) => {
|
||||
return (
|
||||
<BaseEmail width={333} locale={locale}>
|
||||
<Title value={<Trans>Deleted Workspace</Trans>} />
|
||||
<Title value={i18n._('Deleted Workspace')} />
|
||||
<MainText>
|
||||
{userName?.length > 1 ? (
|
||||
<Trans>Dear {userName},</Trans>
|
||||
<Trans id="Dear {userName}," values={{ userName }} />
|
||||
) : (
|
||||
<Trans>Hello,</Trans>
|
||||
<Trans id="Hello," />
|
||||
)}
|
||||
<br />
|
||||
<br />
|
||||
<Trans>
|
||||
Your workspace <b>{workspaceDisplayName}</b> has been deleted as your
|
||||
subscription expired {daysSinceInactive} days ago.
|
||||
</Trans>
|
||||
<Trans
|
||||
id="Your workspace <0>{workspaceDisplayName}</0> has been deleted as your subscription expired {daysSinceInactive} days ago."
|
||||
values={{ workspaceDisplayName, daysSinceInactive }}
|
||||
components={{ 0: <b /> }}
|
||||
/>
|
||||
<br />
|
||||
<br />
|
||||
<Trans>All data in this workspace has been permanently deleted.</Trans>
|
||||
<Trans id="All data in this workspace has been permanently deleted." />
|
||||
<br />
|
||||
<br />
|
||||
<Trans>
|
||||
If you wish to use Twenty again, you can create a new workspace.
|
||||
</Trans>
|
||||
<Trans id="If you wish to use Twenty again, you can create a new workspace." />
|
||||
</MainText>
|
||||
<CallToAction
|
||||
href="https://app.twenty.com/"
|
||||
value={<Trans>Create a new workspace</Trans>}
|
||||
value={i18n._('Create a new workspace')}
|
||||
/>
|
||||
</BaseEmail>
|
||||
);
|
||||
};
|
||||
|
||||
CleanSuspendedWorkspaceEmail.PreviewProps = {
|
||||
daysSinceInactive: 1,
|
||||
userName: 'John Doe',
|
||||
workspaceDisplayName: 'My Workspace',
|
||||
locale: 'en',
|
||||
} as CleanSuspendedWorkspaceEmailProps;
|
||||
|
||||
export default CleanSuspendedWorkspaceEmail;
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import { i18n } from '@lingui/core';
|
||||
import { Trans } from '@lingui/react';
|
||||
import { BaseEmail } from 'src/components/BaseEmail';
|
||||
import { CallToAction } from 'src/components/CallToAction';
|
||||
import { Link } from 'src/components/Link';
|
||||
@ -19,16 +20,24 @@ export const PasswordResetLinkEmail = ({
|
||||
}: PasswordResetLinkEmailProps) => {
|
||||
return (
|
||||
<BaseEmail locale={locale}>
|
||||
<Title value={<Trans>Reset your password 🗝</Trans>} />
|
||||
<CallToAction href={link} value={<Trans>Reset</Trans>} />
|
||||
<Title value={i18n._('Reset your password 🗝')} />
|
||||
<CallToAction href={link} value={i18n._('Reset')} />
|
||||
<MainText>
|
||||
<Trans>
|
||||
This link is only valid for the next {duration}. If the link does not
|
||||
work, you can use the login verification link directly:
|
||||
</Trans>
|
||||
<Trans
|
||||
id="This link is only valid for the next {duration}. If the link does not work, you can use the login verification link directly:"
|
||||
values={{ duration }}
|
||||
/>
|
||||
<br />
|
||||
<Link href={link} value={link} />
|
||||
</MainText>
|
||||
</BaseEmail>
|
||||
);
|
||||
};
|
||||
|
||||
PasswordResetLinkEmail.PreviewProps = {
|
||||
duration: '24 hours',
|
||||
link: 'https://app.twenty.com/reset-password/123',
|
||||
locale: 'en',
|
||||
} as PasswordResetLinkEmailProps;
|
||||
|
||||
export default PasswordResetLinkEmail;
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
import { i18n } from '@lingui/core';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
|
||||
import { Trans } from '@lingui/react';
|
||||
import { BaseEmail } from 'src/components/BaseEmail';
|
||||
import { CallToAction } from 'src/components/CallToAction';
|
||||
import { MainText } from 'src/components/MainText';
|
||||
@ -21,29 +19,38 @@ export const PasswordUpdateNotifyEmail = ({
|
||||
link,
|
||||
locale,
|
||||
}: PasswordUpdateNotifyEmailProps) => {
|
||||
const helloString = userName?.length > 1 ? t`Dear ${userName}` : t`Hello`;
|
||||
const formattedDate = i18n.date(new Date());
|
||||
|
||||
return (
|
||||
<BaseEmail locale={locale}>
|
||||
<Title value={<Trans>Password updated</Trans>} />
|
||||
<Title value={i18n._('Password updated')} />
|
||||
<MainText>
|
||||
{helloString},
|
||||
{userName?.length > 1 ? (
|
||||
<Trans id="Dear {userName}," values={{ userName }} />
|
||||
) : (
|
||||
<Trans id="Hello," />
|
||||
)}
|
||||
<br />
|
||||
<br />
|
||||
<Trans>
|
||||
This is a confirmation that password for your account ({email}) was
|
||||
successfully changed on {formattedDate}.
|
||||
</Trans>
|
||||
<Trans
|
||||
id="This is a confirmation that password for your account ({email}) was successfully changed on {formattedDate}."
|
||||
values={{ email, formattedDate }}
|
||||
/>
|
||||
<br />
|
||||
<br />
|
||||
<Trans>
|
||||
If you did not initiate this change, please contact your workspace
|
||||
owner immediately.
|
||||
</Trans>
|
||||
<Trans id="If you did not initiate this change, please contact your workspace owner immediately." />
|
||||
<br />
|
||||
</MainText>
|
||||
<CallToAction value={<Trans>Connect to Twenty</Trans>} href={link} />
|
||||
<CallToAction value={i18n._('Connect to Twenty')} href={link} />
|
||||
</BaseEmail>
|
||||
);
|
||||
};
|
||||
|
||||
PasswordUpdateNotifyEmail.PreviewProps = {
|
||||
userName: 'John Doe',
|
||||
email: 'john.doe@example.com',
|
||||
link: 'https://app.twenty.com',
|
||||
locale: 'en',
|
||||
} as PasswordUpdateNotifyEmailProps;
|
||||
|
||||
export default PasswordUpdateNotifyEmail;
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
|
||||
import { i18n } from '@lingui/core';
|
||||
import { Trans } from '@lingui/react';
|
||||
import { BaseEmail } from 'src/components/BaseEmail';
|
||||
import { CallToAction } from 'src/components/CallToAction';
|
||||
import { Footer } from 'src/components/Footer';
|
||||
import { MainText } from 'src/components/MainText';
|
||||
import { Title } from 'src/components/Title';
|
||||
import { APP_LOCALES } from 'twenty-shared/translations';
|
||||
@ -18,18 +17,20 @@ export const SendEmailVerificationLinkEmail = ({
|
||||
}: SendEmailVerificationLinkEmailProps) => {
|
||||
return (
|
||||
<BaseEmail width={333} locale={locale}>
|
||||
<Title value={<Trans>Confirm your email address</Trans>} />
|
||||
<CallToAction href={link} value={<Trans>Verify Email</Trans>} />
|
||||
<Title value={i18n._('Confirm your email address')} />
|
||||
<CallToAction href={link} value={i18n._('Verify Email')} />
|
||||
<br />
|
||||
<br />
|
||||
<MainText>
|
||||
<Trans>
|
||||
Thanks for registering for an account on Twenty! Before we get
|
||||
started, we just need to confirm that this is you. Click above to
|
||||
verify your email address.
|
||||
</Trans>
|
||||
<Trans id="Thanks for registering for an account on Twenty! Before we get started, we just need to confirm that this is you. Click above to verify your email address." />
|
||||
</MainText>
|
||||
<Footer />
|
||||
</BaseEmail>
|
||||
);
|
||||
};
|
||||
|
||||
SendEmailVerificationLinkEmail.PreviewProps = {
|
||||
link: 'https://app.twenty.com/verify-email/123',
|
||||
locale: 'en',
|
||||
};
|
||||
|
||||
export default SendEmailVerificationLinkEmail;
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import { i18n } from '@lingui/core';
|
||||
import { Trans } from '@lingui/react';
|
||||
import { Img } from '@react-email/components';
|
||||
import { emailTheme } from 'src/common-style';
|
||||
|
||||
@ -37,26 +38,58 @@ export const SendInviteLinkEmail = ({
|
||||
? getImageAbsoluteURI({ imageUrl: workspace.logo, baseUrl: serverUrl })
|
||||
: null;
|
||||
|
||||
const senderName = capitalize(sender.firstName);
|
||||
const senderEmail = sender.email;
|
||||
const workspaceName = workspace.name;
|
||||
|
||||
return (
|
||||
<BaseEmail width={333} locale={locale}>
|
||||
<Title value={<Trans>Join your team on Twenty</Trans>} />
|
||||
<Title value={i18n._('Join your team on Twenty')} />
|
||||
<MainText>
|
||||
{capitalize(sender.firstName)} (
|
||||
<Link
|
||||
href={`mailto:${sender.email}`}
|
||||
value={sender.email}
|
||||
color={emailTheme.font.colors.blue}
|
||||
<Trans
|
||||
id="{senderName} (<0>{senderEmail}</0>) has invited you to join a workspace called <1>{workspaceName}</1>."
|
||||
values={{ senderName, senderEmail, workspaceName }}
|
||||
components={{
|
||||
0: (
|
||||
<Link
|
||||
href={`mailto:${senderEmail}`}
|
||||
value={senderEmail}
|
||||
color={emailTheme.font.colors.blue}
|
||||
/>
|
||||
),
|
||||
1: <b />,
|
||||
}}
|
||||
/>
|
||||
) <Trans>has invited you to join a workspace called </Trans>
|
||||
<b>{workspace.name}</b>
|
||||
<br />
|
||||
</MainText>
|
||||
<HighlightedContainer>
|
||||
{workspaceLogo && <Img src={workspaceLogo} width={40} height={40} />}
|
||||
{workspace.name && <HighlightedText value={workspace.name} />}
|
||||
<CallToAction href={link} value={<Trans>Accept invite</Trans>} />
|
||||
{workspaceLogo ? (
|
||||
<Img
|
||||
src={workspaceLogo}
|
||||
width={40}
|
||||
height={40}
|
||||
alt="Workspace logo"
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{workspace.name ? <HighlightedText value={workspace.name} /> : <></>}
|
||||
<CallToAction href={link} value={i18n._('Accept invite')} />
|
||||
</HighlightedContainer>
|
||||
<WhatIsTwenty />
|
||||
</BaseEmail>
|
||||
);
|
||||
};
|
||||
|
||||
SendInviteLinkEmail.PreviewProps = {
|
||||
link: 'https://app.twenty.com/invite/123',
|
||||
workspace: {
|
||||
name: 'Acme Inc.',
|
||||
logo: 'https://fakeimg.pl/200x200/?text=ACME&font=lobster',
|
||||
},
|
||||
sender: { email: 'john.doe@example.com', firstName: 'John', lastName: 'Doe' },
|
||||
serverUrl: 'https://app.twenty.com',
|
||||
locale: 'en',
|
||||
} as SendInviteLinkEmailProps;
|
||||
|
||||
export default SendInviteLinkEmail;
|
||||
|
||||
24
packages/twenty-emails/src/emails/test.email.tsx
Normal file
24
packages/twenty-emails/src/emails/test.email.tsx
Normal file
@ -0,0 +1,24 @@
|
||||
import { i18n } from '@lingui/core';
|
||||
import { BaseEmail } from 'src/components/BaseEmail';
|
||||
import { Title } from 'src/components/Title';
|
||||
import { APP_LOCALES } from 'twenty-shared/translations';
|
||||
|
||||
type TestEmailProps = {
|
||||
locale: keyof typeof APP_LOCALES;
|
||||
};
|
||||
|
||||
// This is a test email which isn't used in production
|
||||
// It's useful to do tests and play in a local environment
|
||||
export const TestEmail = ({ locale }: TestEmailProps) => {
|
||||
return (
|
||||
<BaseEmail locale={locale}>
|
||||
<Title value={i18n._('Test email')} />
|
||||
</BaseEmail>
|
||||
);
|
||||
};
|
||||
|
||||
TestEmail.PreviewProps = {
|
||||
locale: 'en',
|
||||
} as TestEmailProps;
|
||||
|
||||
export default TestEmail;
|
||||
@ -1,5 +1,5 @@
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import { i18n } from '@lingui/core';
|
||||
import { Trans } from '@lingui/react';
|
||||
import { Img } from '@react-email/components';
|
||||
import { emailTheme } from 'src/common-style';
|
||||
|
||||
@ -10,7 +10,6 @@ import { HighlightedText } from 'src/components/HighlightedText';
|
||||
import { Link } from 'src/components/Link';
|
||||
import { MainText } from 'src/components/MainText';
|
||||
import { Title } from 'src/components/Title';
|
||||
import { WhatIsTwenty } from 'src/components/WhatIsTwenty';
|
||||
import { capitalize } from 'src/utils/capitalize';
|
||||
import { APP_LOCALES } from 'twenty-shared/translations';
|
||||
import { getImageAbsoluteURI } from 'twenty-shared/utils';
|
||||
@ -40,30 +39,61 @@ export const SendApprovedAccessDomainValidation = ({
|
||||
? getImageAbsoluteURI({ imageUrl: workspace.logo, baseUrl: serverUrl })
|
||||
: null;
|
||||
|
||||
const senderName = capitalize(sender.firstName);
|
||||
const senderEmail = sender.email;
|
||||
|
||||
return (
|
||||
<BaseEmail width={333} locale={locale}>
|
||||
<Title value={t`Validate domain`} />
|
||||
<Title value={i18n._('Validate domain')} />
|
||||
<MainText>
|
||||
{capitalize(sender.firstName)} (
|
||||
<Link
|
||||
href={`mailto:${sender.email}`}
|
||||
value={sender.email}
|
||||
color={emailTheme.font.colors.blue}
|
||||
<Trans
|
||||
id="{senderName} (<0>{senderEmail}</0>): Please validate this domain to allow users with <1>@{domain}</1> email addresses to join your workspace without requiring an invitation."
|
||||
values={{ senderName, senderEmail, domain }}
|
||||
components={{
|
||||
0: (
|
||||
<Link
|
||||
href={`mailto:${senderEmail}`}
|
||||
value={senderEmail}
|
||||
color={emailTheme.font.colors.blue}
|
||||
/>
|
||||
),
|
||||
1: <b />,
|
||||
}}
|
||||
/>
|
||||
) <Trans>Please validate this domain to allow users with</Trans>{' '}
|
||||
<b>@{domain}</b>{' '}
|
||||
<Trans>
|
||||
email addresses to join your workspace without requiring an
|
||||
invitation.
|
||||
</Trans>
|
||||
<br />
|
||||
</MainText>
|
||||
<HighlightedContainer>
|
||||
{workspaceLogo && <Img src={workspaceLogo} width={40} height={40} />}
|
||||
{workspace.name && <HighlightedText value={workspace.name} />}
|
||||
<CallToAction href={link} value={t`Validate domain`} />
|
||||
{workspaceLogo ? (
|
||||
<Img
|
||||
src={workspaceLogo}
|
||||
width={40}
|
||||
height={40}
|
||||
alt="Workspace logo"
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{workspace.name ? <HighlightedText value={workspace.name} /> : <></>}
|
||||
<CallToAction href={link} value={i18n._('Validate domain')} />
|
||||
</HighlightedContainer>
|
||||
<WhatIsTwenty />
|
||||
</BaseEmail>
|
||||
);
|
||||
};
|
||||
|
||||
SendApprovedAccessDomainValidation.PreviewProps = {
|
||||
link: 'https://app.twenty.com/validate-domain',
|
||||
domain: 'example.com',
|
||||
workspace: {
|
||||
name: 'Acme Inc.',
|
||||
logo: 'https://fakeimg.pl/200x200/?text=ACME&font=lobster',
|
||||
},
|
||||
sender: {
|
||||
email: 'john.doe@example.com',
|
||||
firstName: 'John',
|
||||
lastName: 'Doe',
|
||||
},
|
||||
serverUrl: 'https://app.twenty.com',
|
||||
locale: 'en',
|
||||
} as SendApprovedAccessDomainValidationProps;
|
||||
|
||||
export default SendApprovedAccessDomainValidation;
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import { i18n } from '@lingui/core';
|
||||
import { Trans } from '@lingui/react';
|
||||
import { BaseEmail } from 'src/components/BaseEmail';
|
||||
import { CallToAction } from 'src/components/CallToAction';
|
||||
import { MainText } from 'src/components/MainText';
|
||||
@ -22,38 +23,51 @@ export const WarnSuspendedWorkspaceEmail = ({
|
||||
}: WarnSuspendedWorkspaceEmailProps) => {
|
||||
const daysLeft = inactiveDaysBeforeDelete - daysSinceInactive;
|
||||
const dayOrDays = daysLeft > 1 ? 'days' : 'day';
|
||||
const remainingDays = daysLeft > 0 ? `${daysLeft} ` : '';
|
||||
|
||||
const helloString = userName?.length > 1 ? `Hello ${userName}` : 'Hello';
|
||||
const remainingDays = daysLeft > 0 ? daysLeft : 0;
|
||||
|
||||
return (
|
||||
<BaseEmail width={333} locale={locale}>
|
||||
<Title value={<Trans>Suspended Workspace </Trans>} />
|
||||
<Title value={i18n._('Suspended Workspace')} />
|
||||
<MainText>
|
||||
{helloString},
|
||||
{userName?.length > 1 ? (
|
||||
<Trans id="Dear {userName}," values={{ userName }} />
|
||||
) : (
|
||||
<Trans id="Hello," />
|
||||
)}
|
||||
<br />
|
||||
<br />
|
||||
<Trans>
|
||||
It appears that your workspace <b>{workspaceDisplayName}</b> has been
|
||||
suspended for {daysSinceInactive} days.
|
||||
</Trans>
|
||||
<Trans
|
||||
id="It appears that your workspace <0>{workspaceDisplayName}</0> has been suspended for {daysSinceInactive} days."
|
||||
values={{ workspaceDisplayName, daysSinceInactive }}
|
||||
components={{ 0: <b /> }}
|
||||
/>
|
||||
<br />
|
||||
<br />
|
||||
<Trans>
|
||||
The workspace will be deactivated in {remainingDays} {dayOrDays}, and
|
||||
all its data will be deleted.
|
||||
</Trans>
|
||||
<Trans
|
||||
id="The workspace will be deactivated in {remainingDays} {dayOrDays}, and all its data will be deleted."
|
||||
values={{ remainingDays, dayOrDays }}
|
||||
/>
|
||||
<br />
|
||||
<br />
|
||||
<Trans>
|
||||
If you wish to continue using Twenty, please update your subscription
|
||||
within the next {remainingDays} {dayOrDays}.
|
||||
</Trans>
|
||||
<Trans
|
||||
id="If you wish to continue using Twenty, please update your subscription within the next {remainingDays} {dayOrDays}."
|
||||
values={{ remainingDays, dayOrDays }}
|
||||
/>
|
||||
</MainText>
|
||||
<CallToAction
|
||||
href="https://app.twenty.com/settings/billing"
|
||||
value={<Trans>Update your subscription</Trans>}
|
||||
value={i18n._('Update your subscription')}
|
||||
/>
|
||||
</BaseEmail>
|
||||
);
|
||||
};
|
||||
|
||||
WarnSuspendedWorkspaceEmail.PreviewProps = {
|
||||
daysSinceInactive: 10,
|
||||
inactiveDaysBeforeDelete: 14,
|
||||
userName: 'John Doe',
|
||||
workspaceDisplayName: 'Acme Inc.',
|
||||
locale: 'en',
|
||||
};
|
||||
|
||||
export default WarnSuspendedWorkspaceEmail;
|
||||
|
||||
Reference in New Issue
Block a user