@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.
- package/dist/branding.d.ts +26 -0
- package/dist/branding.js +35 -0
- package/dist/index.d.ts +72 -0
- package/dist/index.js +110 -0
- package/dist/templates/magic-link.d.ts +8 -0
- package/dist/templates/magic-link.js +109 -0
- package/dist/templates/password-reset.d.ts +9 -0
- package/dist/templates/password-reset.js +128 -0
- package/dist/templates/welcome.d.ts +8 -0
- package/dist/templates/welcome.js +134 -0
- package/package.json +40 -0
- package/raydoug-email-templates-1.0.0.tgz +0 -0
- package/src/branding.ts +65 -0
- package/src/index.ts +164 -0
- package/src/templates/magic-link.tsx +228 -0
- package/src/templates/password-reset.tsx +260 -0
- package/src/templates/welcome.tsx +268 -0
- package/test-send.ts +52 -0
- package/tsconfig.json +22 -0
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Body,
|
|
3
|
+
Button,
|
|
4
|
+
Container,
|
|
5
|
+
Head,
|
|
6
|
+
Heading,
|
|
7
|
+
Html,
|
|
8
|
+
Img,
|
|
9
|
+
Link,
|
|
10
|
+
Preview,
|
|
11
|
+
Section,
|
|
12
|
+
Text,
|
|
13
|
+
Hr,
|
|
14
|
+
} from "@react-email/components";
|
|
15
|
+
import * as React from "react";
|
|
16
|
+
import type { AppBranding } from "../branding";
|
|
17
|
+
|
|
18
|
+
interface PasswordResetEmailProps {
|
|
19
|
+
resetLink: string;
|
|
20
|
+
branding: AppBranding;
|
|
21
|
+
userEmail?: string;
|
|
22
|
+
expiresIn?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const PasswordResetEmail = ({
|
|
26
|
+
resetLink,
|
|
27
|
+
branding,
|
|
28
|
+
userEmail,
|
|
29
|
+
expiresIn = "1 hour",
|
|
30
|
+
}: PasswordResetEmailProps) => {
|
|
31
|
+
const previewText = `Reset your ${branding.appName} password`;
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<Html>
|
|
35
|
+
<Head />
|
|
36
|
+
<Preview>{previewText}</Preview>
|
|
37
|
+
<Body style={main}>
|
|
38
|
+
<Container style={container}>
|
|
39
|
+
{/* Gradient Header with Logo */}
|
|
40
|
+
<Section style={{
|
|
41
|
+
...heroHeader,
|
|
42
|
+
backgroundImage: branding.logoGradient,
|
|
43
|
+
backgroundColor: branding.primaryColor,
|
|
44
|
+
}}>
|
|
45
|
+
<Heading style={brandHeading}>
|
|
46
|
+
{branding.appName.includes("Cards") ? (
|
|
47
|
+
<>
|
|
48
|
+
Cards<span style={{ fontWeight: 400 }}>Gone</span><span style={{ fontStyle: "italic" }}>Crazy</span>
|
|
49
|
+
</>
|
|
50
|
+
) : branding.appName.includes("Retirement") ? (
|
|
51
|
+
<>
|
|
52
|
+
Route<span style={{ opacity: 0.75 }}>My</span>Retirement
|
|
53
|
+
</>
|
|
54
|
+
) : branding.appName.includes("Sharp") ? (
|
|
55
|
+
<>
|
|
56
|
+
Fake<span style={{ fontWeight: 300 }}>Sharp</span>
|
|
57
|
+
</>
|
|
58
|
+
) : (
|
|
59
|
+
branding.appName
|
|
60
|
+
)}
|
|
61
|
+
</Heading>
|
|
62
|
+
</Section>
|
|
63
|
+
|
|
64
|
+
<Section style={contentContainer}>
|
|
65
|
+
{/* Main Heading */}
|
|
66
|
+
<Heading style={mainHeading}>Reset your password</Heading>
|
|
67
|
+
|
|
68
|
+
{/* Description */}
|
|
69
|
+
<Text style={paragraph}>
|
|
70
|
+
We received a request to update the password for your account
|
|
71
|
+
{userEmail ? <span style={{ color: "#334155", fontWeight: 600 }}> ({userEmail})</span> : ""}.
|
|
72
|
+
</Text>
|
|
73
|
+
<Text style={paragraph}>
|
|
74
|
+
To create a new password, click the button below.
|
|
75
|
+
</Text>
|
|
76
|
+
|
|
77
|
+
{/* CTA Button */}
|
|
78
|
+
<Section style={buttonContainer}>
|
|
79
|
+
<Button
|
|
80
|
+
style={{
|
|
81
|
+
...button,
|
|
82
|
+
backgroundColor: branding.primaryColor,
|
|
83
|
+
boxShadow: `0 0 20px ${branding.primaryColor}40`,
|
|
84
|
+
}}
|
|
85
|
+
href={resetLink}
|
|
86
|
+
>
|
|
87
|
+
Reset Password
|
|
88
|
+
</Button>
|
|
89
|
+
</Section>
|
|
90
|
+
|
|
91
|
+
{/* Fallback Link */}
|
|
92
|
+
<Text style={paragraphSmall}>
|
|
93
|
+
Or paste this link into your browser:
|
|
94
|
+
</Text>
|
|
95
|
+
<Text style={linkText}>{resetLink}</Text>
|
|
96
|
+
|
|
97
|
+
<Text style={{ ...paragraphMuted, margin: "16px 0 0" }}>
|
|
98
|
+
This link will expire in <span style={{ color: "#d4d4d8" }}>{expiresIn}</span>.
|
|
99
|
+
</Text>
|
|
100
|
+
|
|
101
|
+
<Hr style={divider} />
|
|
102
|
+
|
|
103
|
+
{/* Security Notice */}
|
|
104
|
+
<Section style={warningBox}>
|
|
105
|
+
<Text style={warningTitle}>SECURITY NOTICE</Text>
|
|
106
|
+
<Text style={warningText}>
|
|
107
|
+
If you didn't request a password reset, you can safely ignore this email.
|
|
108
|
+
Your password will remain unchanged.
|
|
109
|
+
</Text>
|
|
110
|
+
</Section>
|
|
111
|
+
|
|
112
|
+
{/* Footer */}
|
|
113
|
+
<Section style={footer}>
|
|
114
|
+
<Text style={footerText}>
|
|
115
|
+
{branding.footerText || `© ${branding.appName}`}
|
|
116
|
+
</Text>
|
|
117
|
+
<Link href={branding.websiteUrl} style={footerLink}>
|
|
118
|
+
{branding.websiteUrl.replace("https://", "")}
|
|
119
|
+
</Link>
|
|
120
|
+
</Section>
|
|
121
|
+
</Section>
|
|
122
|
+
</Container>
|
|
123
|
+
</Body>
|
|
124
|
+
</Html>
|
|
125
|
+
);
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// Styles
|
|
129
|
+
const main = {
|
|
130
|
+
backgroundColor: "#f8fafc", // Slate-50
|
|
131
|
+
fontFamily: 'Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
132
|
+
padding: "40px 0",
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const container = {
|
|
136
|
+
backgroundColor: "#ffffff", // White
|
|
137
|
+
margin: "0 auto",
|
|
138
|
+
padding: "0",
|
|
139
|
+
maxWidth: "520px",
|
|
140
|
+
borderRadius: "16px",
|
|
141
|
+
border: "1px solid #e2e8f0", // Slate-200
|
|
142
|
+
overflow: "hidden" as const,
|
|
143
|
+
boxShadow: "0 10px 15px -3px rgba(0, 0, 0, 0.05), 0 4px 6px -2px rgba(0, 0, 0, 0.03)",
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
const heroHeader = {
|
|
147
|
+
padding: "48px 0",
|
|
148
|
+
textAlign: "center" as const,
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const contentContainer = {
|
|
152
|
+
padding: "48px",
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const brandHeading = {
|
|
156
|
+
margin: "0",
|
|
157
|
+
fontSize: "28px",
|
|
158
|
+
fontWeight: "800",
|
|
159
|
+
color: "#ffffff",
|
|
160
|
+
letterSpacing: "-0.5px",
|
|
161
|
+
textShadow: "0 2px 4px rgba(0,0,0,0.1)",
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
const mainHeading = {
|
|
165
|
+
color: "#0f172a", // Slate-900
|
|
166
|
+
fontSize: "30px",
|
|
167
|
+
fontWeight: "700",
|
|
168
|
+
margin: "0 0 24px",
|
|
169
|
+
letterSpacing: "-0.5px",
|
|
170
|
+
lineHeight: "38px",
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
const paragraph = {
|
|
174
|
+
color: "#334155", // Slate-700
|
|
175
|
+
fontSize: "16px",
|
|
176
|
+
lineHeight: "26px",
|
|
177
|
+
margin: "0 0 16px",
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
const buttonContainer = {
|
|
181
|
+
margin: "32px 0",
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
const button = {
|
|
185
|
+
color: "#ffffff",
|
|
186
|
+
fontSize: "15px",
|
|
187
|
+
fontWeight: "600",
|
|
188
|
+
textDecoration: "none",
|
|
189
|
+
textAlign: "center" as const,
|
|
190
|
+
display: "inline-block",
|
|
191
|
+
padding: "14px 32px",
|
|
192
|
+
borderRadius: "8px",
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
const paragraphSmall = {
|
|
196
|
+
color: "#64748b", // Slate-500
|
|
197
|
+
fontSize: "13px",
|
|
198
|
+
lineHeight: "20px",
|
|
199
|
+
margin: "0 0 8px",
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
const linkText = {
|
|
203
|
+
color: "#94a3b8", // Slate-400
|
|
204
|
+
fontSize: "12px",
|
|
205
|
+
lineHeight: "18px",
|
|
206
|
+
wordBreak: "break-all" as const,
|
|
207
|
+
margin: "0 0 24px",
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
const divider = {
|
|
211
|
+
borderColor: "#e2e8f0", // Slate-200
|
|
212
|
+
margin: "32px 0",
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
const paragraphMuted = {
|
|
216
|
+
color: "#64748b", // Slate-500
|
|
217
|
+
fontSize: "13px",
|
|
218
|
+
lineHeight: "20px",
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
const warningBox = {
|
|
222
|
+
padding: "16px",
|
|
223
|
+
backgroundColor: "#fffbeb", // Amber-50
|
|
224
|
+
border: "1px solid #fcd34d", // Amber-300
|
|
225
|
+
borderRadius: "8px",
|
|
226
|
+
margin: "0 0 24px",
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
const warningTitle = {
|
|
230
|
+
color: "#b45309", // Amber-700
|
|
231
|
+
fontSize: "11px",
|
|
232
|
+
fontWeight: "700",
|
|
233
|
+
letterSpacing: "1px",
|
|
234
|
+
margin: "0 0 8px",
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
const warningText = {
|
|
238
|
+
color: "#92400e", // Amber-800
|
|
239
|
+
fontSize: "13px",
|
|
240
|
+
lineHeight: "20px",
|
|
241
|
+
margin: "0",
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
const footer = {
|
|
245
|
+
textAlign: "center" as const,
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
const footerText = {
|
|
249
|
+
color: "#94a3b8", // Slate-400
|
|
250
|
+
fontSize: "12px",
|
|
251
|
+
margin: "0 0 8px",
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
const footerLink = {
|
|
255
|
+
color: "#64748b", // Slate-500
|
|
256
|
+
fontSize: "12px",
|
|
257
|
+
textDecoration: "underline",
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
export default PasswordResetEmail;
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Body,
|
|
3
|
+
Button,
|
|
4
|
+
Container,
|
|
5
|
+
Head,
|
|
6
|
+
Heading,
|
|
7
|
+
Html,
|
|
8
|
+
Img,
|
|
9
|
+
Link,
|
|
10
|
+
Preview,
|
|
11
|
+
Section,
|
|
12
|
+
Text,
|
|
13
|
+
Hr,
|
|
14
|
+
} from "@react-email/components";
|
|
15
|
+
import * as React from "react";
|
|
16
|
+
import type { AppBranding } from "../branding";
|
|
17
|
+
|
|
18
|
+
interface WelcomeEmailProps {
|
|
19
|
+
branding: AppBranding;
|
|
20
|
+
userName?: string;
|
|
21
|
+
dashboardUrl?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const WelcomeEmail = ({
|
|
25
|
+
branding,
|
|
26
|
+
userName,
|
|
27
|
+
dashboardUrl,
|
|
28
|
+
}: WelcomeEmailProps) => {
|
|
29
|
+
const previewText = `Welcome to ${branding.appName}!`;
|
|
30
|
+
const ctaUrl = dashboardUrl || branding.websiteUrl;
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<Html>
|
|
34
|
+
<Head />
|
|
35
|
+
<Preview>{previewText}</Preview>
|
|
36
|
+
<Body style={main}>
|
|
37
|
+
<Container style={container}>
|
|
38
|
+
{/* Gradient Header with Logo */}
|
|
39
|
+
<Section style={{
|
|
40
|
+
...heroHeader,
|
|
41
|
+
backgroundImage: branding.logoGradient,
|
|
42
|
+
backgroundColor: branding.primaryColor,
|
|
43
|
+
}}>
|
|
44
|
+
<Heading style={brandHeading}>
|
|
45
|
+
{branding.appName.includes("Cards") ? (
|
|
46
|
+
<>
|
|
47
|
+
Cards<span style={{ fontWeight: 400 }}>Gone</span><span style={{ fontStyle: "italic" }}>Crazy</span>
|
|
48
|
+
</>
|
|
49
|
+
) : branding.appName.includes("Retirement") ? (
|
|
50
|
+
<>
|
|
51
|
+
Route<span style={{ opacity: 0.75 }}>My</span>Retirement
|
|
52
|
+
</>
|
|
53
|
+
) : branding.appName.includes("Sharp") ? (
|
|
54
|
+
<>
|
|
55
|
+
Fake<span style={{ fontWeight: 300 }}>Sharp</span>
|
|
56
|
+
</>
|
|
57
|
+
) : (
|
|
58
|
+
branding.appName
|
|
59
|
+
)}
|
|
60
|
+
</Heading>
|
|
61
|
+
</Section>
|
|
62
|
+
|
|
63
|
+
<Section style={contentContainer}>
|
|
64
|
+
{/* Main Heading */}
|
|
65
|
+
<Heading style={mainHeading}>Welcome aboard!</Heading>
|
|
66
|
+
|
|
67
|
+
{/* Personalized Greeting */}
|
|
68
|
+
<Text style={paragraph}>
|
|
69
|
+
{userName ? `Hi ${userName},` : "Hello,"}
|
|
70
|
+
</Text>
|
|
71
|
+
<Text style={paragraph}>
|
|
72
|
+
Thanks for joining <span style={{ color: "#334155", fontWeight: 700 }}>{branding.appName}</span>.
|
|
73
|
+
We're thrilled to have you with us.
|
|
74
|
+
</Text>
|
|
75
|
+
|
|
76
|
+
{/* Feature List */}
|
|
77
|
+
<Section style={featuresContainer}>
|
|
78
|
+
<Text style={featureHeading}>GETTING STARTED</Text>
|
|
79
|
+
<Section style={featureRow}>
|
|
80
|
+
<div style={{ ...bullet, backgroundColor: branding.primaryColor }} />
|
|
81
|
+
<Text style={featureText}>Explore your new command center</Text>
|
|
82
|
+
</Section>
|
|
83
|
+
<Section style={featureRow}>
|
|
84
|
+
<div style={{ ...bullet, backgroundColor: branding.primaryColor }} />
|
|
85
|
+
<Text style={featureText}>Customize your preferences and settings</Text>
|
|
86
|
+
</Section>
|
|
87
|
+
<Section style={featureRow}>
|
|
88
|
+
<div style={{ ...bullet, backgroundColor: branding.primaryColor }} />
|
|
89
|
+
<Text style={featureText}>Start your first project or plan</Text>
|
|
90
|
+
</Section>
|
|
91
|
+
</Section>
|
|
92
|
+
|
|
93
|
+
{/* CTA Button */}
|
|
94
|
+
<Section style={buttonContainer}>
|
|
95
|
+
<Button
|
|
96
|
+
style={{
|
|
97
|
+
...button,
|
|
98
|
+
backgroundColor: branding.primaryColor,
|
|
99
|
+
boxShadow: `0 0 20px ${branding.primaryColor}40`,
|
|
100
|
+
}}
|
|
101
|
+
href={ctaUrl}
|
|
102
|
+
>
|
|
103
|
+
Get Started
|
|
104
|
+
</Button>
|
|
105
|
+
</Section>
|
|
106
|
+
|
|
107
|
+
<Hr style={divider} />
|
|
108
|
+
|
|
109
|
+
{/* Support */}
|
|
110
|
+
<Text style={paragraphMuted}>
|
|
111
|
+
Need help? Reply to this email or contact <Link href={`mailto:${branding.supportEmail}`} style={link}>{branding.supportEmail}</Link>.
|
|
112
|
+
</Text>
|
|
113
|
+
|
|
114
|
+
{/* Footer */}
|
|
115
|
+
<Section style={footer}>
|
|
116
|
+
<Text style={footerText}>
|
|
117
|
+
{branding.footerText || `© ${branding.appName}`}
|
|
118
|
+
</Text>
|
|
119
|
+
<Link href={branding.websiteUrl} style={footerLink}>
|
|
120
|
+
{branding.websiteUrl.replace("https://", "")}
|
|
121
|
+
</Link>
|
|
122
|
+
</Section>
|
|
123
|
+
</Section>
|
|
124
|
+
</Container>
|
|
125
|
+
</Body>
|
|
126
|
+
</Html>
|
|
127
|
+
);
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
// Styles
|
|
131
|
+
const main = {
|
|
132
|
+
backgroundColor: "#f8fafc", // Slate-50
|
|
133
|
+
fontFamily: 'Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
134
|
+
padding: "40px 0",
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const container = {
|
|
138
|
+
backgroundColor: "#ffffff", // White
|
|
139
|
+
margin: "0 auto",
|
|
140
|
+
padding: "0",
|
|
141
|
+
maxWidth: "520px",
|
|
142
|
+
borderRadius: "16px",
|
|
143
|
+
border: "1px solid #e2e8f0", // Slate-200
|
|
144
|
+
overflow: "hidden" as const,
|
|
145
|
+
boxShadow: "0 10px 15px -3px rgba(0, 0, 0, 0.05), 0 4px 6px -2px rgba(0, 0, 0, 0.03)",
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
const heroHeader = {
|
|
149
|
+
padding: "48px 0",
|
|
150
|
+
textAlign: "center" as const,
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
const contentContainer = {
|
|
154
|
+
padding: "48px",
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
const brandHeading = {
|
|
158
|
+
margin: "0",
|
|
159
|
+
fontSize: "28px",
|
|
160
|
+
fontWeight: "800",
|
|
161
|
+
color: "#ffffff",
|
|
162
|
+
letterSpacing: "-0.5px",
|
|
163
|
+
textShadow: "0 2px 4px rgba(0,0,0,0.1)",
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const mainHeading = {
|
|
167
|
+
color: "#0f172a", // Slate-900
|
|
168
|
+
fontSize: "30px",
|
|
169
|
+
fontWeight: "700",
|
|
170
|
+
margin: "0 0 24px",
|
|
171
|
+
letterSpacing: "-0.5px",
|
|
172
|
+
lineHeight: "38px",
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
const paragraph = {
|
|
176
|
+
color: "#334155", // Slate-700
|
|
177
|
+
fontSize: "16px",
|
|
178
|
+
lineHeight: "26px",
|
|
179
|
+
margin: "0 0 16px",
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const featuresContainer = {
|
|
183
|
+
margin: "32px 0 32px",
|
|
184
|
+
padding: "24px",
|
|
185
|
+
backgroundColor: "#f1f5f9", // Slate-100
|
|
186
|
+
borderRadius: "12px",
|
|
187
|
+
border: "1px solid #e2e8f0", // Slate-200
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
const featureHeading = {
|
|
191
|
+
color: "#64748b", // Slate-500
|
|
192
|
+
fontSize: "11px",
|
|
193
|
+
fontWeight: "700",
|
|
194
|
+
letterSpacing: "1px",
|
|
195
|
+
margin: "0 0 16px",
|
|
196
|
+
textTransform: "uppercase" as const,
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
const featureRow = {
|
|
200
|
+
marginBottom: "12px",
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
const bullet = {
|
|
204
|
+
width: "8px",
|
|
205
|
+
height: "8px",
|
|
206
|
+
borderRadius: "50%",
|
|
207
|
+
display: "inline-block",
|
|
208
|
+
marginRight: "12px",
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
const featureText = {
|
|
212
|
+
color: "#475569", // Slate-600
|
|
213
|
+
fontSize: "14px",
|
|
214
|
+
fontWeight: "600",
|
|
215
|
+
display: "inline-block",
|
|
216
|
+
margin: "0",
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
const buttonContainer = {
|
|
220
|
+
margin: "32px 0",
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
const button = {
|
|
224
|
+
color: "#ffffff",
|
|
225
|
+
fontSize: "15px",
|
|
226
|
+
fontWeight: "600",
|
|
227
|
+
textDecoration: "none",
|
|
228
|
+
textAlign: "center" as const,
|
|
229
|
+
display: "inline-block",
|
|
230
|
+
padding: "14px 32px",
|
|
231
|
+
borderRadius: "8px",
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
const divider = {
|
|
235
|
+
borderColor: "#e2e8f0", // Slate-200
|
|
236
|
+
margin: "32px 0",
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
const paragraphMuted = {
|
|
240
|
+
color: "#94a3b8", // Slate-400
|
|
241
|
+
fontSize: "13px",
|
|
242
|
+
lineHeight: "20px",
|
|
243
|
+
margin: "0 0 8px",
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
const link = {
|
|
247
|
+
color: "#64748b", // Slate-500
|
|
248
|
+
textDecoration: "underline",
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
const footer = {
|
|
252
|
+
marginTop: "24px",
|
|
253
|
+
textAlign: "center" as const,
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
const footerText = {
|
|
257
|
+
color: "#94a3b8", // Slate-400
|
|
258
|
+
fontSize: "12px",
|
|
259
|
+
margin: "0 0 8px",
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
const footerLink = {
|
|
263
|
+
color: "#64748b", // Slate-500
|
|
264
|
+
fontSize: "12px",
|
|
265
|
+
textDecoration: "underline",
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
export default WelcomeEmail;
|
package/test-send.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { sendMagicLinkEmail, getBranding } from "./src/index.js";
|
|
2
|
+
|
|
3
|
+
const email = "jefflawson@gmail.com";
|
|
4
|
+
const apiKey = "us_sirrb8mb4i_f5e775df301bc55d93697fef6b024bf1";
|
|
5
|
+
|
|
6
|
+
async function sendTestEmails() {
|
|
7
|
+
console.log("🚀 Sending beautiful test emails to", email);
|
|
8
|
+
|
|
9
|
+
// 1. CardsGoneCrazy
|
|
10
|
+
try {
|
|
11
|
+
console.log("Sending CardsGoneCrazy...");
|
|
12
|
+
await sendMagicLinkEmail({
|
|
13
|
+
to: email,
|
|
14
|
+
magicLink: "https://cardsgonecrazy.com/api/auth/callback/email?token=test-token&email=jefflawson@gmail.com",
|
|
15
|
+
branding: getBranding("cards"),
|
|
16
|
+
apiKey,
|
|
17
|
+
});
|
|
18
|
+
console.log("✅ CardsGoneCrazy sent!");
|
|
19
|
+
} catch (err) {
|
|
20
|
+
console.error("❌ CardsGoneCrazy failed:", err);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// 2. RouteMyRetirement
|
|
24
|
+
try {
|
|
25
|
+
console.log("Sending RouteMyRetirement...");
|
|
26
|
+
await sendMagicLinkEmail({
|
|
27
|
+
to: email,
|
|
28
|
+
magicLink: "https://routemyretirement.com/api/auth/callback/email?token=test-token&email=jefflawson@gmail.com",
|
|
29
|
+
branding: getBranding("retirement"),
|
|
30
|
+
apiKey,
|
|
31
|
+
});
|
|
32
|
+
console.log("✅ RouteMyRetirement sent!");
|
|
33
|
+
} catch (err) {
|
|
34
|
+
console.error("❌ RouteMyRetirement failed:", err);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// 3. FakeSharp
|
|
38
|
+
try {
|
|
39
|
+
console.log("Sending FakeSharp...");
|
|
40
|
+
await sendMagicLinkEmail({
|
|
41
|
+
to: email,
|
|
42
|
+
magicLink: "https://fakesharp.com/api/auth/callback/email?token=test-token&email=jefflawson@gmail.com",
|
|
43
|
+
branding: getBranding("fakesharp"),
|
|
44
|
+
apiKey,
|
|
45
|
+
});
|
|
46
|
+
console.log("✅ FakeSharp sent!");
|
|
47
|
+
} catch (err) {
|
|
48
|
+
console.error("❌ FakeSharp failed:", err);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
sendTestEmails();
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"jsx": "react-jsx",
|
|
7
|
+
"declaration": true,
|
|
8
|
+
"outDir": "./dist",
|
|
9
|
+
"rootDir": "./src",
|
|
10
|
+
"strict": true,
|
|
11
|
+
"esModuleInterop": true,
|
|
12
|
+
"skipLibCheck": true,
|
|
13
|
+
"forceConsistentCasingInFileNames": true
|
|
14
|
+
},
|
|
15
|
+
"include": [
|
|
16
|
+
"src/**/*"
|
|
17
|
+
],
|
|
18
|
+
"exclude": [
|
|
19
|
+
"node_modules",
|
|
20
|
+
"dist"
|
|
21
|
+
]
|
|
22
|
+
}
|