create-m5kdev 0.21.6 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/templates/minimal-app/apps/email/package.json.tpl +8 -1
- package/templates/minimal-app/apps/email/src/emails/accountDeletionEmail.tsx.tpl +25 -21
- package/templates/minimal-app/apps/email/src/emails/organizationInviteEmail.tsx.tpl +24 -22
- package/templates/minimal-app/apps/email/src/emails/passwordResetEmail.tsx.tpl +25 -16
- package/templates/minimal-app/apps/email/src/emails/verificationEmail.tsx.tpl +27 -17
- package/templates/minimal-app/apps/email/src/index.ts.tpl +8 -8
- package/templates/minimal-app/apps/email/src/resources.ts.tpl +36 -0
- package/templates/minimal-app/apps/server/src/app.ts.tpl +10 -1
- package/templates/minimal-app/apps/shared/src/modules/app/locale.constants.ts.tpl +7 -0
- package/templates/minimal-app/apps/webapp/src/Router.tsx.tpl +3 -0
- package/templates/minimal-app/apps/email/src/components/BaseEmail.tsx.tpl +0 -117
package/package.json
CHANGED
|
@@ -10,11 +10,14 @@
|
|
|
10
10
|
"email:dev": "react-email dev --dir src/emails"
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
|
+
"@m5kdev/commons": "workspace:*",
|
|
14
|
+
"@m5kdev/email": "workspace:*",
|
|
13
15
|
"@react-email/components": "catalog:",
|
|
14
16
|
"react": "catalog:",
|
|
15
17
|
"react-dom": "catalog:"
|
|
16
18
|
},
|
|
17
19
|
"devDependencies": {
|
|
20
|
+
"@m5kdev/backend": "workspace:*",
|
|
18
21
|
"@m5kdev/config": "catalog:",
|
|
19
22
|
"@types/node": "catalog:",
|
|
20
23
|
"@types/react": "catalog:",
|
|
@@ -23,5 +26,9 @@
|
|
|
23
26
|
"tslib": "catalog:",
|
|
24
27
|
"typescript": "catalog:"
|
|
25
28
|
},
|
|
26
|
-
"main": "./src/index.ts"
|
|
29
|
+
"main": "./src/index.ts",
|
|
30
|
+
"exports": {
|
|
31
|
+
".": "./src/index.ts",
|
|
32
|
+
"./resources": "./src/resources.ts"
|
|
33
|
+
}
|
|
27
34
|
}
|
|
@@ -1,30 +1,34 @@
|
|
|
1
|
-
import { Text } from "@react-email/components";
|
|
2
|
-
import {
|
|
1
|
+
import { Heading, Text } from "@react-email/components";
|
|
2
|
+
import { CtaButton } from "@m5kdev/email/components/CtaButton";
|
|
3
|
+
import { EmailLayout } from "@m5kdev/email/components/EmailLayout";
|
|
4
|
+
import type { Brand, EmailTranslateFn } from "@m5kdev/email/types";
|
|
5
|
+
|
|
6
|
+
function resolveT(t?: EmailTranslateFn): EmailTranslateFn {
|
|
7
|
+
return t ?? ((key: string) => key);
|
|
8
|
+
}
|
|
3
9
|
|
|
4
10
|
export default function AccountDeletionEmail({
|
|
5
|
-
previewText
|
|
11
|
+
previewText,
|
|
6
12
|
url,
|
|
13
|
+
brand,
|
|
14
|
+
t,
|
|
15
|
+
htmlLang,
|
|
7
16
|
}: {
|
|
8
|
-
previewText
|
|
17
|
+
previewText: string;
|
|
9
18
|
url: string;
|
|
19
|
+
brand: Brand;
|
|
20
|
+
t?: EmailTranslateFn;
|
|
21
|
+
htmlLang?: string;
|
|
10
22
|
}) {
|
|
23
|
+
const translate = resolveT(t);
|
|
24
|
+
|
|
11
25
|
return (
|
|
12
|
-
<
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
<>
|
|
20
|
-
<Text style={{ margin: "0" }}>
|
|
21
|
-
We received a request to delete your {{APP_NAME}} account.
|
|
22
|
-
</Text>
|
|
23
|
-
<Text style={{ margin: "0" }}>
|
|
24
|
-
If that was you, use the link below to confirm the action.
|
|
25
|
-
</Text>
|
|
26
|
-
</>
|
|
27
|
-
}
|
|
28
|
-
/>
|
|
26
|
+
<EmailLayout previewText={previewText} brand={brand} htmlLang={htmlLang}>
|
|
27
|
+
<Heading className="mb-4 text-2xl font-bold text-black">
|
|
28
|
+
{translate("accountDeletion.title")}
|
|
29
|
+
</Heading>
|
|
30
|
+
<Text className="mb-6 text-base text-gray-700">{translate("accountDeletion.body")}</Text>
|
|
31
|
+
<CtaButton href={url}>{translate("accountDeletion.action")}</CtaButton>
|
|
32
|
+
</EmailLayout>
|
|
29
33
|
);
|
|
30
34
|
}
|
|
@@ -1,31 +1,33 @@
|
|
|
1
|
-
import { Text } from "@react-email/components";
|
|
2
|
-
import {
|
|
1
|
+
import { Heading, Text } from "@react-email/components";
|
|
2
|
+
import { CtaButton } from "@m5kdev/email/components/CtaButton";
|
|
3
|
+
import { EmailLayout } from "@m5kdev/email/components/EmailLayout";
|
|
4
|
+
import type { EmailTranslateFn, OrganizationInviteTemplateProps } from "@m5kdev/email/types";
|
|
5
|
+
|
|
6
|
+
function resolveT(t?: EmailTranslateFn): EmailTranslateFn {
|
|
7
|
+
return t ?? ((key: string) => key);
|
|
8
|
+
}
|
|
3
9
|
|
|
4
10
|
export default function OrganizationInviteEmail({
|
|
5
|
-
previewText
|
|
11
|
+
previewText,
|
|
6
12
|
inviterName,
|
|
7
13
|
organizationName,
|
|
8
14
|
role,
|
|
9
15
|
url,
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}) {
|
|
16
|
+
brand,
|
|
17
|
+
t,
|
|
18
|
+
htmlLang,
|
|
19
|
+
}: OrganizationInviteTemplateProps) {
|
|
20
|
+
const translate = resolveT(t);
|
|
21
|
+
|
|
17
22
|
return (
|
|
18
|
-
<
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
</Text>
|
|
28
|
-
}
|
|
29
|
-
/>
|
|
23
|
+
<EmailLayout previewText={previewText} brand={brand} htmlLang={htmlLang}>
|
|
24
|
+
<Heading className="mb-4 text-2xl font-bold text-black">
|
|
25
|
+
{translate("organizationInvite.title", { organizationName })}
|
|
26
|
+
</Heading>
|
|
27
|
+
<Text className="mb-6 text-base text-gray-700">
|
|
28
|
+
{translate("organizationInvite.body", { inviterName, organizationName, role })}
|
|
29
|
+
</Text>
|
|
30
|
+
<CtaButton href={url}>{translate("organizationInvite.action")}</CtaButton>
|
|
31
|
+
</EmailLayout>
|
|
30
32
|
);
|
|
31
33
|
}
|
|
@@ -1,25 +1,34 @@
|
|
|
1
|
-
import { Text } from "@react-email/components";
|
|
2
|
-
import {
|
|
1
|
+
import { Heading, Text } from "@react-email/components";
|
|
2
|
+
import { CtaButton } from "@m5kdev/email/components/CtaButton";
|
|
3
|
+
import { EmailLayout } from "@m5kdev/email/components/EmailLayout";
|
|
4
|
+
import type { Brand, EmailTranslateFn } from "@m5kdev/email/types";
|
|
5
|
+
|
|
6
|
+
function resolveT(t?: EmailTranslateFn): EmailTranslateFn {
|
|
7
|
+
return t ?? ((key: string) => key);
|
|
8
|
+
}
|
|
3
9
|
|
|
4
10
|
export default function PasswordResetEmail({
|
|
5
|
-
previewText
|
|
11
|
+
previewText,
|
|
6
12
|
url,
|
|
13
|
+
brand,
|
|
14
|
+
t,
|
|
15
|
+
htmlLang,
|
|
7
16
|
}: {
|
|
8
|
-
previewText
|
|
17
|
+
previewText: string;
|
|
9
18
|
url: string;
|
|
19
|
+
brand: Brand;
|
|
20
|
+
t?: EmailTranslateFn;
|
|
21
|
+
htmlLang?: string;
|
|
10
22
|
}) {
|
|
23
|
+
const translate = resolveT(t);
|
|
24
|
+
|
|
11
25
|
return (
|
|
12
|
-
<
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
<Text style={{ margin: "0" }}>
|
|
20
|
-
Use the secure link below to choose a new password for your {{APP_NAME}} account.
|
|
21
|
-
</Text>
|
|
22
|
-
}
|
|
23
|
-
/>
|
|
26
|
+
<EmailLayout previewText={previewText} brand={brand} htmlLang={htmlLang}>
|
|
27
|
+
<Heading className="mb-4 text-2xl font-bold text-black">
|
|
28
|
+
{translate("passwordReset.title")}
|
|
29
|
+
</Heading>
|
|
30
|
+
<Text className="mb-6 text-base text-gray-700">{translate("passwordReset.body")}</Text>
|
|
31
|
+
<CtaButton href={url}>{translate("passwordReset.action")}</CtaButton>
|
|
32
|
+
</EmailLayout>
|
|
24
33
|
);
|
|
25
34
|
}
|
|
@@ -1,26 +1,36 @@
|
|
|
1
|
-
import { Text } from "@react-email/components";
|
|
2
|
-
import {
|
|
1
|
+
import { Heading, Text } from "@react-email/components";
|
|
2
|
+
import { CtaButton } from "@m5kdev/email/components/CtaButton";
|
|
3
|
+
import { EmailLayout } from "@m5kdev/email/components/EmailLayout";
|
|
4
|
+
import type { Brand, EmailTranslateFn } from "@m5kdev/email/types";
|
|
5
|
+
|
|
6
|
+
function resolveT(t?: EmailTranslateFn): EmailTranslateFn {
|
|
7
|
+
return t ?? ((key: string) => key);
|
|
8
|
+
}
|
|
3
9
|
|
|
4
10
|
export default function VerificationEmail({
|
|
5
|
-
previewText
|
|
11
|
+
previewText,
|
|
6
12
|
url,
|
|
13
|
+
brand,
|
|
14
|
+
t,
|
|
15
|
+
htmlLang,
|
|
7
16
|
}: {
|
|
8
|
-
previewText
|
|
17
|
+
previewText: string;
|
|
9
18
|
url: string;
|
|
19
|
+
brand: Brand;
|
|
20
|
+
t?: EmailTranslateFn;
|
|
21
|
+
htmlLang?: string;
|
|
10
22
|
}) {
|
|
23
|
+
const translate = resolveT(t);
|
|
24
|
+
|
|
11
25
|
return (
|
|
12
|
-
<
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
workspace.
|
|
22
|
-
</Text>
|
|
23
|
-
}
|
|
24
|
-
/>
|
|
26
|
+
<EmailLayout previewText={previewText} brand={brand} htmlLang={htmlLang}>
|
|
27
|
+
<Heading className="mb-4 text-2xl font-bold text-black">
|
|
28
|
+
{translate("verification.title")}
|
|
29
|
+
</Heading>
|
|
30
|
+
<Text className="mb-6 text-base text-gray-700">
|
|
31
|
+
{translate("verification.body", { appName: "{{APP_NAME}}" })}
|
|
32
|
+
</Text>
|
|
33
|
+
<CtaButton href={url}>{translate("verification.action")}</CtaButton>
|
|
34
|
+
</EmailLayout>
|
|
25
35
|
);
|
|
26
36
|
}
|
|
@@ -7,26 +7,26 @@ import VerificationEmail from "./emails/verificationEmail";
|
|
|
7
7
|
export const templates = {
|
|
8
8
|
accountDeletion: {
|
|
9
9
|
id: "account-deletion",
|
|
10
|
-
subject: "
|
|
11
|
-
previewText: "
|
|
10
|
+
subject: "accountDeletion.subject",
|
|
11
|
+
previewText: "accountDeletion.previewText",
|
|
12
12
|
react: AccountDeletionEmail as FunctionComponent<Record<string, unknown>>,
|
|
13
13
|
},
|
|
14
14
|
passwordReset: {
|
|
15
15
|
id: "password-reset",
|
|
16
|
-
subject: "
|
|
17
|
-
previewText: "
|
|
16
|
+
subject: "passwordReset.subject",
|
|
17
|
+
previewText: "passwordReset.previewText",
|
|
18
18
|
react: PasswordResetEmail as FunctionComponent<Record<string, unknown>>,
|
|
19
19
|
},
|
|
20
20
|
verification: {
|
|
21
21
|
id: "verification",
|
|
22
|
-
subject: "
|
|
23
|
-
previewText: "
|
|
22
|
+
subject: "verification.subject",
|
|
23
|
+
previewText: "verification.previewText",
|
|
24
24
|
react: VerificationEmail as FunctionComponent<Record<string, unknown>>,
|
|
25
25
|
},
|
|
26
26
|
organizationInvite: {
|
|
27
27
|
id: "organization-invite",
|
|
28
|
-
subject: "
|
|
29
|
-
previewText: "
|
|
28
|
+
subject: "organizationInvite.subject",
|
|
29
|
+
previewText: "organizationInvite.previewText",
|
|
30
30
|
react: OrganizationInviteEmail as FunctionComponent<Record<string, unknown>>,
|
|
31
31
|
},
|
|
32
32
|
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { BackendAppI18nResources } from "@m5kdev/backend/i18n/app-i18n";
|
|
2
|
+
|
|
3
|
+
export const emailResources = {
|
|
4
|
+
en: {
|
|
5
|
+
translation: {
|
|
6
|
+
verification: {
|
|
7
|
+
subject: "Verify your email",
|
|
8
|
+
previewText: "Verify your email address",
|
|
9
|
+
title: "Verify your email",
|
|
10
|
+
body: "Confirm your email address to finish setting up {{APP_NAME}} and access your editorial workspace.",
|
|
11
|
+
action: "Verify account",
|
|
12
|
+
},
|
|
13
|
+
passwordReset: {
|
|
14
|
+
subject: "Reset your password",
|
|
15
|
+
previewText: "Reset your password request",
|
|
16
|
+
title: "Reset your password",
|
|
17
|
+
body: "Use this link to choose a new password for your account.",
|
|
18
|
+
action: "Reset password",
|
|
19
|
+
},
|
|
20
|
+
accountDeletion: {
|
|
21
|
+
subject: "Delete your account",
|
|
22
|
+
previewText: "Confirm your account deletion",
|
|
23
|
+
title: "Confirm account deletion",
|
|
24
|
+
body: "Use this link to confirm deleting your account.",
|
|
25
|
+
action: "Confirm deletion",
|
|
26
|
+
},
|
|
27
|
+
organizationInvite: {
|
|
28
|
+
subject: "Join the organization",
|
|
29
|
+
previewText: "You have been invited to join an organization",
|
|
30
|
+
title: "Join {{organizationName}}",
|
|
31
|
+
body: "{{inviterName}} invited you to join {{organizationName}} as {{role}}.",
|
|
32
|
+
action: "Accept invitation",
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
} satisfies BackendAppI18nResources;
|
|
@@ -5,7 +5,10 @@ import { EmailModule } from "@m5kdev/backend/modules/email/email.module";
|
|
|
5
5
|
import { NotificationModule } from "@m5kdev/backend/modules/notification/notification.module";
|
|
6
6
|
import { WorkflowModule } from "@m5kdev/backend/modules/workflow/workflow.module";
|
|
7
7
|
import { templates } from "{{PACKAGE_SCOPE}}/email";
|
|
8
|
+
import { emailResources } from "{{PACKAGE_SCOPE}}/email/resources";
|
|
8
9
|
import { APP_NAME } from "{{PACKAGE_SCOPE}}/shared/modules/app/app.constants";
|
|
10
|
+
import { AUTH_LOCALE_CONFIG } from "{{PACKAGE_SCOPE}}/shared/modules/app/locale.constants";
|
|
11
|
+
import { USER_LOCALE_HEADER } from "@m5kdev/commons/modules/auth/auth.constants";
|
|
9
12
|
import cors from "cors";
|
|
10
13
|
import express from "express";
|
|
11
14
|
import { schema } from "./generated/schema";
|
|
@@ -46,6 +49,7 @@ app.use(
|
|
|
46
49
|
"Waitlist-Invitation-Code",
|
|
47
50
|
"Organization-Invitation-Code",
|
|
48
51
|
"Admin-Create-Verified-User",
|
|
52
|
+
USER_LOCALE_HEADER,
|
|
49
53
|
],
|
|
50
54
|
})
|
|
51
55
|
);
|
|
@@ -61,6 +65,10 @@ export const builtBackendApp = createBackendApp(
|
|
|
61
65
|
web: appUrl,
|
|
62
66
|
api: serverUrl,
|
|
63
67
|
},
|
|
68
|
+
locales: AUTH_LOCALE_CONFIG,
|
|
69
|
+
},
|
|
70
|
+
i18n: {
|
|
71
|
+
resources: emailResources,
|
|
64
72
|
},
|
|
65
73
|
redis: {
|
|
66
74
|
url: redisUrl,
|
|
@@ -74,7 +82,7 @@ export const builtBackendApp = createBackendApp(
|
|
|
74
82
|
outputDirectory: ".emails",
|
|
75
83
|
},
|
|
76
84
|
auth: {
|
|
77
|
-
factory({ db, services, appConfig }) {
|
|
85
|
+
factory({ db, services, appConfig, i18n }) {
|
|
78
86
|
return createBetterAuth({
|
|
79
87
|
orm: db.orm as never,
|
|
80
88
|
schema: db.schema as never,
|
|
@@ -82,6 +90,7 @@ export const builtBackendApp = createBackendApp(
|
|
|
82
90
|
email: services.email.email,
|
|
83
91
|
},
|
|
84
92
|
app: appConfig,
|
|
93
|
+
i18n,
|
|
85
94
|
config: {
|
|
86
95
|
waitlist: enableWaitlist,
|
|
87
96
|
provisionedAccountEmailDomain: "provisioned.{{APP_SLUG}}.local",
|
|
@@ -5,6 +5,7 @@ import { AuthOrganizationChildOrganizationsRoute } from "@m5kdev/web-ui/modules/
|
|
|
5
5
|
import { AuthOrganizationMembersRoute } from "@m5kdev/web-ui/modules/auth/components/AuthOrganizationMembersRoute";
|
|
6
6
|
import { AuthOrganizationPreferences } from "@m5kdev/web-ui/modules/auth/components/AuthOrganizationPreferences";
|
|
7
7
|
import { AuthPublicRouter } from "@m5kdev/web-ui/modules/auth/components/AuthPublicRouter";
|
|
8
|
+
import { syncI18nLocale } from "@m5kdev/frontend/modules/app/utils/locale";
|
|
8
9
|
import { AuthUserRouter } from "@m5kdev/web-ui/modules/auth/components/AuthUserRouter";
|
|
9
10
|
import { APP_NAME } from "{{PACKAGE_SCOPE}}/shared/modules/app/app.constants";
|
|
10
11
|
import type { ReactNode } from "react";
|
|
@@ -65,6 +66,7 @@ export function Router() {
|
|
|
65
66
|
{AuthPublicRouter({
|
|
66
67
|
header: <AuthHeader />,
|
|
67
68
|
waitlist: isWaitlist,
|
|
69
|
+
onLocaleChange: syncI18nLocale,
|
|
68
70
|
})}
|
|
69
71
|
|
|
70
72
|
<Route
|
|
@@ -97,6 +99,7 @@ export function Router() {
|
|
|
97
99
|
{AuthUserRouter({
|
|
98
100
|
schema: preferenceSchema,
|
|
99
101
|
controls: preferenceControls,
|
|
102
|
+
onLocaleChange: syncI18nLocale,
|
|
100
103
|
})}
|
|
101
104
|
{AuthAdminRouter({
|
|
102
105
|
enableWaitlist: true,
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Body,
|
|
3
|
-
Button,
|
|
4
|
-
Container,
|
|
5
|
-
Head,
|
|
6
|
-
Heading,
|
|
7
|
-
Hr,
|
|
8
|
-
Html,
|
|
9
|
-
Preview,
|
|
10
|
-
Section,
|
|
11
|
-
Text,
|
|
12
|
-
} from "@react-email/components";
|
|
13
|
-
import type { ReactNode } from "react";
|
|
14
|
-
|
|
15
|
-
interface BaseEmailProps {
|
|
16
|
-
previewText: string;
|
|
17
|
-
title: string;
|
|
18
|
-
eyebrow: string;
|
|
19
|
-
body: ReactNode;
|
|
20
|
-
ctaLabel?: string;
|
|
21
|
-
ctaUrl?: string;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export function BaseEmail({ previewText, title, eyebrow, body, ctaLabel, ctaUrl }: BaseEmailProps) {
|
|
25
|
-
return (
|
|
26
|
-
<Html>
|
|
27
|
-
<Head />
|
|
28
|
-
<Preview>{previewText}</Preview>
|
|
29
|
-
<Body
|
|
30
|
-
style={{
|
|
31
|
-
backgroundColor: "#f3efe6",
|
|
32
|
-
fontFamily: "'Manrope', 'Helvetica Neue', Arial, sans-serif",
|
|
33
|
-
margin: "0",
|
|
34
|
-
padding: "32px 0",
|
|
35
|
-
}}
|
|
36
|
-
>
|
|
37
|
-
<Container
|
|
38
|
-
style={{
|
|
39
|
-
backgroundColor: "#fffaf1",
|
|
40
|
-
border: "1px solid #d6c7ab",
|
|
41
|
-
borderRadius: "24px",
|
|
42
|
-
margin: "0 auto",
|
|
43
|
-
maxWidth: "620px",
|
|
44
|
-
padding: "40px",
|
|
45
|
-
}}
|
|
46
|
-
>
|
|
47
|
-
<Text
|
|
48
|
-
style={{
|
|
49
|
-
color: "#8b5e34",
|
|
50
|
-
fontSize: "12px",
|
|
51
|
-
fontWeight: "700",
|
|
52
|
-
letterSpacing: "0.18em",
|
|
53
|
-
margin: "0 0 16px",
|
|
54
|
-
textTransform: "uppercase",
|
|
55
|
-
}}
|
|
56
|
-
>
|
|
57
|
-
{eyebrow}
|
|
58
|
-
</Text>
|
|
59
|
-
<Heading
|
|
60
|
-
style={{
|
|
61
|
-
color: "#171314",
|
|
62
|
-
fontFamily: "'Cormorant Garamond', Georgia, serif",
|
|
63
|
-
fontSize: "34px",
|
|
64
|
-
lineHeight: "1.1",
|
|
65
|
-
margin: "0 0 20px",
|
|
66
|
-
}}
|
|
67
|
-
>
|
|
68
|
-
{title}
|
|
69
|
-
</Heading>
|
|
70
|
-
<Section
|
|
71
|
-
style={{
|
|
72
|
-
color: "#463630",
|
|
73
|
-
fontSize: "16px",
|
|
74
|
-
lineHeight: "1.7",
|
|
75
|
-
}}
|
|
76
|
-
>
|
|
77
|
-
{body}
|
|
78
|
-
</Section>
|
|
79
|
-
{ctaLabel && ctaUrl ? (
|
|
80
|
-
<Section style={{ marginTop: "28px" }}>
|
|
81
|
-
<Button
|
|
82
|
-
href={ctaUrl}
|
|
83
|
-
style={{
|
|
84
|
-
backgroundColor: "#1f4f46",
|
|
85
|
-
borderRadius: "999px",
|
|
86
|
-
color: "#fdf9f1",
|
|
87
|
-
fontSize: "15px",
|
|
88
|
-
fontWeight: "700",
|
|
89
|
-
padding: "14px 24px",
|
|
90
|
-
textDecoration: "none",
|
|
91
|
-
}}
|
|
92
|
-
>
|
|
93
|
-
{ctaLabel}
|
|
94
|
-
</Button>
|
|
95
|
-
</Section>
|
|
96
|
-
) : null}
|
|
97
|
-
<Hr
|
|
98
|
-
style={{
|
|
99
|
-
borderColor: "#e5dccd",
|
|
100
|
-
margin: "28px 0",
|
|
101
|
-
}}
|
|
102
|
-
/>
|
|
103
|
-
<Text
|
|
104
|
-
style={{
|
|
105
|
-
color: "#7f6b5d",
|
|
106
|
-
fontSize: "13px",
|
|
107
|
-
lineHeight: "1.6",
|
|
108
|
-
margin: "0",
|
|
109
|
-
}}
|
|
110
|
-
>
|
|
111
|
-
Sent by {{APP_NAME}}.
|
|
112
|
-
</Text>
|
|
113
|
-
</Container>
|
|
114
|
-
</Body>
|
|
115
|
-
</Html>
|
|
116
|
-
);
|
|
117
|
-
}
|