@jl0810/email-templates 1.0.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.
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Branding configuration for email templates
3
+ * Each app can provide its own branding
4
+ */
5
+ export interface AppBranding {
6
+ /** App name (e.g., "CardsGoneCrazy", "RetirementPlanner", "FakeSharp") */
7
+ appName: string;
8
+ /** Logo URL (absolute URL) */
9
+ logoUrl: string;
10
+ /** Primary brand color (hex, e.g., "#7C3AED") */
11
+ primaryColor: string;
12
+ /** Secondary/accent color (hex) */
13
+ accentColor?: string;
14
+ /** Support email address */
15
+ supportEmail: string;
16
+ /** App website URL */
17
+ websiteUrl: string;
18
+ /** Footer text */
19
+ footerText?: string;
20
+ /** CSS Gradient for the logo icon (e.g., "linear-gradient(to bottom right, #06B5D4, #3B82F6)") */
21
+ logoGradient?: string;
22
+ }
23
+ /**
24
+ * Pre-configured branding for RayDoug apps
25
+ */
26
+ export declare const APP_BRANDING: Record<string, AppBranding>;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Pre-configured branding for RayDoug apps
3
+ */
4
+ export const APP_BRANDING = {
5
+ cards: {
6
+ appName: "CardsGoneCrazy",
7
+ logoUrl: "",
8
+ primaryColor: "#06B5D4", // Cyan-500
9
+ accentColor: "#3B82F6", // Blue-500
10
+ logoGradient: "linear-gradient(135deg, #06B5D4 0%, #2563EB 100%)", // Cyan-500 to Blue-600
11
+ supportEmail: "support@cardsgonecrazy.com",
12
+ websiteUrl: "https://cardsgonecrazy.com",
13
+ footerText: "© CardsGoneCrazy. All rights reserved.",
14
+ },
15
+ retirement: {
16
+ appName: "RouteMyRetirement",
17
+ logoUrl: "",
18
+ primaryColor: "#2563EB", // Blue-600
19
+ accentColor: "#6366F1", // Indigo-500
20
+ logoGradient: "linear-gradient(135deg, #2563EB 0%, #4F46E5 100%)", // Blue-600 to Indigo-600
21
+ supportEmail: "support@routemyretirement.com",
22
+ websiteUrl: "https://routemyretirement.com",
23
+ footerText: "© RouteMyRetirement. All rights reserved.",
24
+ },
25
+ fakesharp: {
26
+ appName: "FakeSharp",
27
+ logoUrl: "",
28
+ primaryColor: "#10B981", // Emerald-500
29
+ accentColor: "#3B82F6", // Blue-500 for the logo gradient
30
+ logoGradient: "linear-gradient(135deg, #10B981 0%, #3B82F6 100%)", // Emerald-500 to Blue-500
31
+ supportEmail: "support@fakesharp.com",
32
+ websiteUrl: "https://fakesharp.com",
33
+ footerText: "© FakeSharp. All rights reserved.",
34
+ },
35
+ };
@@ -0,0 +1,72 @@
1
+ import { render } from "@react-email/components";
2
+ export * from "./branding";
3
+ export { MagicLinkEmail } from "./templates/magic-link";
4
+ export { WelcomeEmail } from "./templates/welcome";
5
+ export { PasswordResetEmail } from "./templates/password-reset";
6
+ export { render };
7
+ import type { AppBranding } from "./branding";
8
+ /**
9
+ * Helper to get branding by app key
10
+ */
11
+ export declare function getBranding(appKey: "cards" | "retirement" | "fakesharp"): AppBranding;
12
+ /**
13
+ * Render magic link email to HTML
14
+ */
15
+ export declare function renderMagicLinkEmail(options: {
16
+ magicLink: string;
17
+ branding: AppBranding;
18
+ userEmail?: string;
19
+ }): Promise<string>;
20
+ /**
21
+ * Render welcome email to HTML
22
+ */
23
+ export declare function renderWelcomeEmail(options: {
24
+ branding: AppBranding;
25
+ userName?: string;
26
+ dashboardUrl?: string;
27
+ }): Promise<string>;
28
+ /**
29
+ * Render password reset email to HTML
30
+ */
31
+ export declare function renderPasswordResetEmail(options: {
32
+ resetLink: string;
33
+ branding: AppBranding;
34
+ userEmail?: string;
35
+ expiresIn?: string;
36
+ }): Promise<string>;
37
+ /**
38
+ * useSend API helper - sends email via useSend
39
+ */
40
+ export declare function sendEmail(options: {
41
+ to: string;
42
+ from: string;
43
+ subject: string;
44
+ html: string;
45
+ apiKey: string;
46
+ apiUrl?: string;
47
+ }): Promise<{
48
+ emailId: string;
49
+ }>;
50
+ /**
51
+ * High-level helper: Send magic link email
52
+ */
53
+ export declare function sendMagicLinkEmail(options: {
54
+ to: string;
55
+ magicLink: string;
56
+ branding: AppBranding;
57
+ apiKey: string;
58
+ }): Promise<{
59
+ emailId: string;
60
+ }>;
61
+ /**
62
+ * High-level helper: Send welcome email
63
+ */
64
+ export declare function sendWelcomeEmail(options: {
65
+ to: string;
66
+ userName?: string;
67
+ branding: AppBranding;
68
+ apiKey: string;
69
+ dashboardUrl?: string;
70
+ }): Promise<{
71
+ emailId: string;
72
+ }>;
package/dist/index.js ADDED
@@ -0,0 +1,110 @@
1
+ import { render } from "@react-email/components";
2
+ import * as React from "react";
3
+ // Export branding
4
+ export * from "./branding";
5
+ // Export templates
6
+ export { MagicLinkEmail } from "./templates/magic-link";
7
+ export { WelcomeEmail } from "./templates/welcome";
8
+ export { PasswordResetEmail } from "./templates/password-reset";
9
+ // Re-export render for convenience
10
+ export { render };
11
+ // Import templates for the helper functions
12
+ import { MagicLinkEmail } from "./templates/magic-link";
13
+ import { WelcomeEmail } from "./templates/welcome";
14
+ import { PasswordResetEmail } from "./templates/password-reset";
15
+ import { APP_BRANDING } from "./branding";
16
+ /**
17
+ * Helper to get branding by app key
18
+ */
19
+ export function getBranding(appKey) {
20
+ return APP_BRANDING[appKey];
21
+ }
22
+ /**
23
+ * Render magic link email to HTML
24
+ */
25
+ export async function renderMagicLinkEmail(options) {
26
+ return render(React.createElement(MagicLinkEmail, {
27
+ magicLink: options.magicLink,
28
+ branding: options.branding,
29
+ userEmail: options.userEmail,
30
+ }));
31
+ }
32
+ /**
33
+ * Render welcome email to HTML
34
+ */
35
+ export async function renderWelcomeEmail(options) {
36
+ return render(React.createElement(WelcomeEmail, {
37
+ branding: options.branding,
38
+ userName: options.userName,
39
+ dashboardUrl: options.dashboardUrl,
40
+ }));
41
+ }
42
+ /**
43
+ * Render password reset email to HTML
44
+ */
45
+ export async function renderPasswordResetEmail(options) {
46
+ return render(React.createElement(PasswordResetEmail, {
47
+ resetLink: options.resetLink,
48
+ branding: options.branding,
49
+ userEmail: options.userEmail,
50
+ expiresIn: options.expiresIn,
51
+ }));
52
+ }
53
+ /**
54
+ * useSend API helper - sends email via useSend
55
+ */
56
+ export async function sendEmail(options) {
57
+ const baseUrl = options.apiUrl || "https://mail.raydoug.com/api/v1";
58
+ const response = await fetch(`${baseUrl}/emails`, {
59
+ method: "POST",
60
+ headers: {
61
+ Authorization: `Bearer ${options.apiKey}`,
62
+ "Content-Type": "application/json",
63
+ },
64
+ body: JSON.stringify({
65
+ to: options.to,
66
+ from: options.from,
67
+ subject: options.subject,
68
+ html: options.html,
69
+ }),
70
+ });
71
+ if (!response.ok) {
72
+ const error = await response.json().catch(() => ({}));
73
+ throw new Error(`Failed to send email: ${response.statusText} - ${JSON.stringify(error)}`);
74
+ }
75
+ return response.json();
76
+ }
77
+ /**
78
+ * High-level helper: Send magic link email
79
+ */
80
+ export async function sendMagicLinkEmail(options) {
81
+ const html = await renderMagicLinkEmail({
82
+ magicLink: options.magicLink,
83
+ branding: options.branding,
84
+ userEmail: options.to,
85
+ });
86
+ return sendEmail({
87
+ to: options.to,
88
+ from: options.branding.supportEmail.replace("support@", "noreply@"),
89
+ subject: `Sign in to ${options.branding.appName}`,
90
+ html,
91
+ apiKey: options.apiKey,
92
+ });
93
+ }
94
+ /**
95
+ * High-level helper: Send welcome email
96
+ */
97
+ export async function sendWelcomeEmail(options) {
98
+ const html = await renderWelcomeEmail({
99
+ branding: options.branding,
100
+ userName: options.userName,
101
+ dashboardUrl: options.dashboardUrl,
102
+ });
103
+ return sendEmail({
104
+ to: options.to,
105
+ from: options.branding.supportEmail.replace("support@", "noreply@"),
106
+ subject: `Welcome to ${options.branding.appName}! 🎉`,
107
+ html,
108
+ apiKey: options.apiKey,
109
+ });
110
+ }
@@ -0,0 +1,8 @@
1
+ import type { AppBranding } from "../branding";
2
+ interface MagicLinkEmailProps {
3
+ magicLink: string;
4
+ branding: AppBranding;
5
+ userEmail?: string;
6
+ }
7
+ export declare const MagicLinkEmail: ({ magicLink, branding, userEmail, }: MagicLinkEmailProps) => import("react/jsx-runtime").JSX.Element;
8
+ export default MagicLinkEmail;
@@ -0,0 +1,109 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Body, Button, Container, Head, Heading, Html, Link, Preview, Section, Text, Hr, } from "@react-email/components";
3
+ export const MagicLinkEmail = ({ magicLink, branding, userEmail, }) => {
4
+ const previewText = `Sign in to ${branding.appName}`;
5
+ return (_jsxs(Html, { children: [_jsx(Head, {}), _jsx(Preview, { children: previewText }), _jsx(Body, { style: main, children: _jsxs(Container, { style: container, children: [_jsx(Section, { style: {
6
+ ...heroHeader,
7
+ backgroundImage: branding.logoGradient,
8
+ backgroundColor: branding.primaryColor,
9
+ }, children: _jsx(Heading, { style: brandHeading, children: branding.appName.includes("Cards") ? (_jsxs(_Fragment, { children: ["Cards", _jsx("span", { style: { fontWeight: 400 }, children: "Gone" }), _jsx("span", { style: { fontStyle: "italic" }, children: "Crazy" })] })) : branding.appName.includes("Retirement") ? (_jsxs(_Fragment, { children: ["Route", _jsx("span", { style: { opacity: 0.75 }, children: "My" }), "Retirement"] })) : branding.appName.includes("Sharp") ? (_jsxs(_Fragment, { children: ["Fake", _jsx("span", { style: { fontWeight: 300 }, children: "Sharp" })] })) : (branding.appName) }) }), _jsxs(Section, { style: contentContainer, children: [_jsx(Heading, { style: mainHeading, children: "Sign in to your account" }), _jsxs(Text, { style: paragraph, children: ["We received a request to sign in to your account", userEmail ? _jsxs("span", { style: { color: "#334155", fontWeight: 600 }, children: [" (", userEmail, ")"] }) : "", "."] }), _jsx(Text, { style: paragraph, children: "Click the button below to authenticate clearly and securely." }), _jsx(Section, { style: buttonContainer, children: _jsx(Button, { style: {
10
+ ...button,
11
+ backgroundColor: branding.primaryColor,
12
+ boxShadow: `0 0 20px ${branding.primaryColor}40`, // 25% opacity glow
13
+ }, href: magicLink, children: "Sign In" }) }), _jsx(Text, { style: paragraphSmall, children: "Or paste this link into your browser:" }), _jsx(Text, { style: linkText, children: magicLink }), _jsx(Hr, { style: divider }), _jsx(Text, { style: paragraphMuted, children: "If you didn't request this, you can safely ignore this email." }), _jsxs(Section, { style: footer, children: [_jsx(Text, { style: footerText, children: branding.footerText || `© ${branding.appName}` }), _jsx(Link, { href: branding.websiteUrl, style: footerLink, children: branding.websiteUrl.replace("https://", "") })] })] })] }) })] }));
14
+ };
15
+ // Styles
16
+ const main = {
17
+ backgroundColor: "#f8fafc", // Slate-50
18
+ fontFamily: 'Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
19
+ padding: "40px 0",
20
+ };
21
+ const container = {
22
+ backgroundColor: "#ffffff", // White
23
+ margin: "0 auto",
24
+ padding: "0",
25
+ maxWidth: "520px",
26
+ borderRadius: "16px",
27
+ border: "1px solid #e2e8f0", // Slate-200
28
+ overflow: "hidden",
29
+ boxShadow: "0 10px 15px -3px rgba(0, 0, 0, 0.05), 0 4px 6px -2px rgba(0, 0, 0, 0.03)", // Soft shadow
30
+ };
31
+ const heroHeader = {
32
+ padding: "48px 0",
33
+ textAlign: "center",
34
+ };
35
+ const contentContainer = {
36
+ padding: "48px",
37
+ };
38
+ const brandHeading = {
39
+ margin: "0",
40
+ fontSize: "28px",
41
+ fontWeight: "800",
42
+ color: "#ffffff",
43
+ letterSpacing: "-0.5px",
44
+ textShadow: "0 2px 4px rgba(0,0,0,0.1)",
45
+ };
46
+ const mainHeading = {
47
+ color: "#0f172a", // Slate-900
48
+ fontSize: "30px",
49
+ fontWeight: "700",
50
+ margin: "0 0 24px",
51
+ letterSpacing: "-0.5px",
52
+ lineHeight: "38px",
53
+ };
54
+ const paragraph = {
55
+ color: "#334155", // Slate-700
56
+ fontSize: "16px",
57
+ lineHeight: "26px",
58
+ margin: "0 0 16px",
59
+ };
60
+ const buttonContainer = {
61
+ margin: "32px 0",
62
+ };
63
+ const button = {
64
+ color: "#ffffff",
65
+ fontSize: "15px",
66
+ fontWeight: "600",
67
+ textDecoration: "none",
68
+ textAlign: "center",
69
+ display: "inline-block",
70
+ padding: "14px 32px",
71
+ borderRadius: "8px",
72
+ };
73
+ const paragraphSmall = {
74
+ color: "#64748b", // Slate-500
75
+ fontSize: "13px",
76
+ lineHeight: "20px",
77
+ margin: "0 0 8px",
78
+ };
79
+ const linkText = {
80
+ color: "#94a3b8", // Slate-400
81
+ fontSize: "12px",
82
+ lineHeight: "18px",
83
+ wordBreak: "break-all",
84
+ margin: "0 0 24px",
85
+ };
86
+ const divider = {
87
+ borderColor: "#e2e8f0", // Slate-200
88
+ margin: "32px 0",
89
+ };
90
+ const paragraphMuted = {
91
+ color: "#94a3b8",
92
+ fontSize: "13px",
93
+ lineHeight: "20px",
94
+ margin: "0 0 24px",
95
+ };
96
+ const footer = {
97
+ textAlign: "center",
98
+ };
99
+ const footerText = {
100
+ color: "#94a3b8", // Slate-400
101
+ fontSize: "12px",
102
+ margin: "0 0 8px",
103
+ };
104
+ const footerLink = {
105
+ color: "#64748b", // Slate-500
106
+ fontSize: "12px",
107
+ textDecoration: "underline",
108
+ };
109
+ export default MagicLinkEmail;
@@ -0,0 +1,9 @@
1
+ import type { AppBranding } from "../branding";
2
+ interface PasswordResetEmailProps {
3
+ resetLink: string;
4
+ branding: AppBranding;
5
+ userEmail?: string;
6
+ expiresIn?: string;
7
+ }
8
+ export declare const PasswordResetEmail: ({ resetLink, branding, userEmail, expiresIn, }: PasswordResetEmailProps) => import("react/jsx-runtime").JSX.Element;
9
+ export default PasswordResetEmail;
@@ -0,0 +1,128 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Body, Button, Container, Head, Heading, Html, Link, Preview, Section, Text, Hr, } from "@react-email/components";
3
+ export const PasswordResetEmail = ({ resetLink, branding, userEmail, expiresIn = "1 hour", }) => {
4
+ const previewText = `Reset your ${branding.appName} password`;
5
+ return (_jsxs(Html, { children: [_jsx(Head, {}), _jsx(Preview, { children: previewText }), _jsx(Body, { style: main, children: _jsxs(Container, { style: container, children: [_jsx(Section, { style: {
6
+ ...heroHeader,
7
+ backgroundImage: branding.logoGradient,
8
+ backgroundColor: branding.primaryColor,
9
+ }, children: _jsx(Heading, { style: brandHeading, children: branding.appName.includes("Cards") ? (_jsxs(_Fragment, { children: ["Cards", _jsx("span", { style: { fontWeight: 400 }, children: "Gone" }), _jsx("span", { style: { fontStyle: "italic" }, children: "Crazy" })] })) : branding.appName.includes("Retirement") ? (_jsxs(_Fragment, { children: ["Route", _jsx("span", { style: { opacity: 0.75 }, children: "My" }), "Retirement"] })) : branding.appName.includes("Sharp") ? (_jsxs(_Fragment, { children: ["Fake", _jsx("span", { style: { fontWeight: 300 }, children: "Sharp" })] })) : (branding.appName) }) }), _jsxs(Section, { style: contentContainer, children: [_jsx(Heading, { style: mainHeading, children: "Reset your password" }), _jsxs(Text, { style: paragraph, children: ["We received a request to update the password for your account", userEmail ? _jsxs("span", { style: { color: "#334155", fontWeight: 600 }, children: [" (", userEmail, ")"] }) : "", "."] }), _jsx(Text, { style: paragraph, children: "To create a new password, click the button below." }), _jsx(Section, { style: buttonContainer, children: _jsx(Button, { style: {
10
+ ...button,
11
+ backgroundColor: branding.primaryColor,
12
+ boxShadow: `0 0 20px ${branding.primaryColor}40`,
13
+ }, href: resetLink, children: "Reset Password" }) }), _jsx(Text, { style: paragraphSmall, children: "Or paste this link into your browser:" }), _jsx(Text, { style: linkText, children: resetLink }), _jsxs(Text, { style: { ...paragraphMuted, margin: "16px 0 0" }, children: ["This link will expire in ", _jsx("span", { style: { color: "#d4d4d8" }, children: expiresIn }), "."] }), _jsx(Hr, { style: divider }), _jsxs(Section, { style: warningBox, children: [_jsx(Text, { style: warningTitle, children: "SECURITY NOTICE" }), _jsx(Text, { style: warningText, children: "If you didn't request a password reset, you can safely ignore this email. Your password will remain unchanged." })] }), _jsxs(Section, { style: footer, children: [_jsx(Text, { style: footerText, children: branding.footerText || `© ${branding.appName}` }), _jsx(Link, { href: branding.websiteUrl, style: footerLink, children: branding.websiteUrl.replace("https://", "") })] })] })] }) })] }));
14
+ };
15
+ // Styles
16
+ const main = {
17
+ backgroundColor: "#f8fafc", // Slate-50
18
+ fontFamily: 'Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
19
+ padding: "40px 0",
20
+ };
21
+ const container = {
22
+ backgroundColor: "#ffffff", // White
23
+ margin: "0 auto",
24
+ padding: "0",
25
+ maxWidth: "520px",
26
+ borderRadius: "16px",
27
+ border: "1px solid #e2e8f0", // Slate-200
28
+ overflow: "hidden",
29
+ boxShadow: "0 10px 15px -3px rgba(0, 0, 0, 0.05), 0 4px 6px -2px rgba(0, 0, 0, 0.03)",
30
+ };
31
+ const heroHeader = {
32
+ padding: "48px 0",
33
+ textAlign: "center",
34
+ };
35
+ const contentContainer = {
36
+ padding: "48px",
37
+ };
38
+ const brandHeading = {
39
+ margin: "0",
40
+ fontSize: "28px",
41
+ fontWeight: "800",
42
+ color: "#ffffff",
43
+ letterSpacing: "-0.5px",
44
+ textShadow: "0 2px 4px rgba(0,0,0,0.1)",
45
+ };
46
+ const mainHeading = {
47
+ color: "#0f172a", // Slate-900
48
+ fontSize: "30px",
49
+ fontWeight: "700",
50
+ margin: "0 0 24px",
51
+ letterSpacing: "-0.5px",
52
+ lineHeight: "38px",
53
+ };
54
+ const paragraph = {
55
+ color: "#334155", // Slate-700
56
+ fontSize: "16px",
57
+ lineHeight: "26px",
58
+ margin: "0 0 16px",
59
+ };
60
+ const buttonContainer = {
61
+ margin: "32px 0",
62
+ };
63
+ const button = {
64
+ color: "#ffffff",
65
+ fontSize: "15px",
66
+ fontWeight: "600",
67
+ textDecoration: "none",
68
+ textAlign: "center",
69
+ display: "inline-block",
70
+ padding: "14px 32px",
71
+ borderRadius: "8px",
72
+ };
73
+ const paragraphSmall = {
74
+ color: "#64748b", // Slate-500
75
+ fontSize: "13px",
76
+ lineHeight: "20px",
77
+ margin: "0 0 8px",
78
+ };
79
+ const linkText = {
80
+ color: "#94a3b8", // Slate-400
81
+ fontSize: "12px",
82
+ lineHeight: "18px",
83
+ wordBreak: "break-all",
84
+ margin: "0 0 24px",
85
+ };
86
+ const divider = {
87
+ borderColor: "#e2e8f0", // Slate-200
88
+ margin: "32px 0",
89
+ };
90
+ const paragraphMuted = {
91
+ color: "#64748b", // Slate-500
92
+ fontSize: "13px",
93
+ lineHeight: "20px",
94
+ };
95
+ const warningBox = {
96
+ padding: "16px",
97
+ backgroundColor: "#fffbeb", // Amber-50
98
+ border: "1px solid #fcd34d", // Amber-300
99
+ borderRadius: "8px",
100
+ margin: "0 0 24px",
101
+ };
102
+ const warningTitle = {
103
+ color: "#b45309", // Amber-700
104
+ fontSize: "11px",
105
+ fontWeight: "700",
106
+ letterSpacing: "1px",
107
+ margin: "0 0 8px",
108
+ };
109
+ const warningText = {
110
+ color: "#92400e", // Amber-800
111
+ fontSize: "13px",
112
+ lineHeight: "20px",
113
+ margin: "0",
114
+ };
115
+ const footer = {
116
+ textAlign: "center",
117
+ };
118
+ const footerText = {
119
+ color: "#94a3b8", // Slate-400
120
+ fontSize: "12px",
121
+ margin: "0 0 8px",
122
+ };
123
+ const footerLink = {
124
+ color: "#64748b", // Slate-500
125
+ fontSize: "12px",
126
+ textDecoration: "underline",
127
+ };
128
+ export default PasswordResetEmail;
@@ -0,0 +1,8 @@
1
+ import type { AppBranding } from "../branding";
2
+ interface WelcomeEmailProps {
3
+ branding: AppBranding;
4
+ userName?: string;
5
+ dashboardUrl?: string;
6
+ }
7
+ export declare const WelcomeEmail: ({ branding, userName, dashboardUrl, }: WelcomeEmailProps) => import("react/jsx-runtime").JSX.Element;
8
+ export default WelcomeEmail;
@@ -0,0 +1,134 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Body, Button, Container, Head, Heading, Html, Link, Preview, Section, Text, Hr, } from "@react-email/components";
3
+ export const WelcomeEmail = ({ branding, userName, dashboardUrl, }) => {
4
+ const previewText = `Welcome to ${branding.appName}!`;
5
+ const ctaUrl = dashboardUrl || branding.websiteUrl;
6
+ return (_jsxs(Html, { children: [_jsx(Head, {}), _jsx(Preview, { children: previewText }), _jsx(Body, { style: main, children: _jsxs(Container, { style: container, children: [_jsx(Section, { style: {
7
+ ...heroHeader,
8
+ backgroundImage: branding.logoGradient,
9
+ backgroundColor: branding.primaryColor,
10
+ }, children: _jsx(Heading, { style: brandHeading, children: branding.appName.includes("Cards") ? (_jsxs(_Fragment, { children: ["Cards", _jsx("span", { style: { fontWeight: 400 }, children: "Gone" }), _jsx("span", { style: { fontStyle: "italic" }, children: "Crazy" })] })) : branding.appName.includes("Retirement") ? (_jsxs(_Fragment, { children: ["Route", _jsx("span", { style: { opacity: 0.75 }, children: "My" }), "Retirement"] })) : branding.appName.includes("Sharp") ? (_jsxs(_Fragment, { children: ["Fake", _jsx("span", { style: { fontWeight: 300 }, children: "Sharp" })] })) : (branding.appName) }) }), _jsxs(Section, { style: contentContainer, children: [_jsx(Heading, { style: mainHeading, children: "Welcome aboard!" }), _jsx(Text, { style: paragraph, children: userName ? `Hi ${userName},` : "Hello," }), _jsxs(Text, { style: paragraph, children: ["Thanks for joining ", _jsx("span", { style: { color: "#334155", fontWeight: 700 }, children: branding.appName }), ". We're thrilled to have you with us."] }), _jsxs(Section, { style: featuresContainer, children: [_jsx(Text, { style: featureHeading, children: "GETTING STARTED" }), _jsxs(Section, { style: featureRow, children: [_jsx("div", { style: { ...bullet, backgroundColor: branding.primaryColor } }), _jsx(Text, { style: featureText, children: "Explore your new command center" })] }), _jsxs(Section, { style: featureRow, children: [_jsx("div", { style: { ...bullet, backgroundColor: branding.primaryColor } }), _jsx(Text, { style: featureText, children: "Customize your preferences and settings" })] }), _jsxs(Section, { style: featureRow, children: [_jsx("div", { style: { ...bullet, backgroundColor: branding.primaryColor } }), _jsx(Text, { style: featureText, children: "Start your first project or plan" })] })] }), _jsx(Section, { style: buttonContainer, children: _jsx(Button, { style: {
11
+ ...button,
12
+ backgroundColor: branding.primaryColor,
13
+ boxShadow: `0 0 20px ${branding.primaryColor}40`,
14
+ }, href: ctaUrl, children: "Get Started" }) }), _jsx(Hr, { style: divider }), _jsxs(Text, { style: paragraphMuted, children: ["Need help? Reply to this email or contact ", _jsx(Link, { href: `mailto:${branding.supportEmail}`, style: link, children: branding.supportEmail }), "."] }), _jsxs(Section, { style: footer, children: [_jsx(Text, { style: footerText, children: branding.footerText || `© ${branding.appName}` }), _jsx(Link, { href: branding.websiteUrl, style: footerLink, children: branding.websiteUrl.replace("https://", "") })] })] })] }) })] }));
15
+ };
16
+ // Styles
17
+ const main = {
18
+ backgroundColor: "#f8fafc", // Slate-50
19
+ fontFamily: 'Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
20
+ padding: "40px 0",
21
+ };
22
+ const container = {
23
+ backgroundColor: "#ffffff", // White
24
+ margin: "0 auto",
25
+ padding: "0",
26
+ maxWidth: "520px",
27
+ borderRadius: "16px",
28
+ border: "1px solid #e2e8f0", // Slate-200
29
+ overflow: "hidden",
30
+ boxShadow: "0 10px 15px -3px rgba(0, 0, 0, 0.05), 0 4px 6px -2px rgba(0, 0, 0, 0.03)",
31
+ };
32
+ const heroHeader = {
33
+ padding: "48px 0",
34
+ textAlign: "center",
35
+ };
36
+ const contentContainer = {
37
+ padding: "48px",
38
+ };
39
+ const brandHeading = {
40
+ margin: "0",
41
+ fontSize: "28px",
42
+ fontWeight: "800",
43
+ color: "#ffffff",
44
+ letterSpacing: "-0.5px",
45
+ textShadow: "0 2px 4px rgba(0,0,0,0.1)",
46
+ };
47
+ const mainHeading = {
48
+ color: "#0f172a", // Slate-900
49
+ fontSize: "30px",
50
+ fontWeight: "700",
51
+ margin: "0 0 24px",
52
+ letterSpacing: "-0.5px",
53
+ lineHeight: "38px",
54
+ };
55
+ const paragraph = {
56
+ color: "#334155", // Slate-700
57
+ fontSize: "16px",
58
+ lineHeight: "26px",
59
+ margin: "0 0 16px",
60
+ };
61
+ const featuresContainer = {
62
+ margin: "32px 0 32px",
63
+ padding: "24px",
64
+ backgroundColor: "#f1f5f9", // Slate-100
65
+ borderRadius: "12px",
66
+ border: "1px solid #e2e8f0", // Slate-200
67
+ };
68
+ const featureHeading = {
69
+ color: "#64748b", // Slate-500
70
+ fontSize: "11px",
71
+ fontWeight: "700",
72
+ letterSpacing: "1px",
73
+ margin: "0 0 16px",
74
+ textTransform: "uppercase",
75
+ };
76
+ const featureRow = {
77
+ marginBottom: "12px",
78
+ };
79
+ const bullet = {
80
+ width: "8px",
81
+ height: "8px",
82
+ borderRadius: "50%",
83
+ display: "inline-block",
84
+ marginRight: "12px",
85
+ };
86
+ const featureText = {
87
+ color: "#475569", // Slate-600
88
+ fontSize: "14px",
89
+ fontWeight: "600",
90
+ display: "inline-block",
91
+ margin: "0",
92
+ };
93
+ const buttonContainer = {
94
+ margin: "32px 0",
95
+ };
96
+ const button = {
97
+ color: "#ffffff",
98
+ fontSize: "15px",
99
+ fontWeight: "600",
100
+ textDecoration: "none",
101
+ textAlign: "center",
102
+ display: "inline-block",
103
+ padding: "14px 32px",
104
+ borderRadius: "8px",
105
+ };
106
+ const divider = {
107
+ borderColor: "#e2e8f0", // Slate-200
108
+ margin: "32px 0",
109
+ };
110
+ const paragraphMuted = {
111
+ color: "#94a3b8", // Slate-400
112
+ fontSize: "13px",
113
+ lineHeight: "20px",
114
+ margin: "0 0 8px",
115
+ };
116
+ const link = {
117
+ color: "#64748b", // Slate-500
118
+ textDecoration: "underline",
119
+ };
120
+ const footer = {
121
+ marginTop: "24px",
122
+ textAlign: "center",
123
+ };
124
+ const footerText = {
125
+ color: "#94a3b8", // Slate-400
126
+ fontSize: "12px",
127
+ margin: "0 0 8px",
128
+ };
129
+ const footerLink = {
130
+ color: "#64748b", // Slate-500
131
+ fontSize: "12px",
132
+ textDecoration: "underline",
133
+ };
134
+ export default WelcomeEmail;