brookmind-emails 0.1.7

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.
Files changed (51) hide show
  1. package/.prettierrc +7 -0
  2. package/dist/index.d.ts +3 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +4 -0
  5. package/dist/modelfy/config.d.ts +7 -0
  6. package/dist/modelfy/config.d.ts.map +1 -0
  7. package/dist/modelfy/config.js +6 -0
  8. package/dist/modelfy/index.d.ts +14 -0
  9. package/dist/modelfy/index.d.ts.map +1 -0
  10. package/dist/modelfy/index.js +33 -0
  11. package/dist/modelfy/templates/GrantCreditsEmail.d.ts +10 -0
  12. package/dist/modelfy/templates/GrantCreditsEmail.d.ts.map +1 -0
  13. package/dist/modelfy/templates/GrantCreditsEmail.js +101 -0
  14. package/dist/modelfy/templates/OtpEmail.d.ts +8 -0
  15. package/dist/modelfy/templates/OtpEmail.d.ts.map +1 -0
  16. package/dist/modelfy/templates/OtpEmail.js +66 -0
  17. package/dist/modelfy/templates/PromoSubscriptionEmail.d.ts +11 -0
  18. package/dist/modelfy/templates/PromoSubscriptionEmail.d.ts.map +1 -0
  19. package/dist/modelfy/templates/PromoSubscriptionEmail.js +83 -0
  20. package/dist/modelfy/templates/index.d.ts +4 -0
  21. package/dist/modelfy/templates/index.d.ts.map +1 -0
  22. package/dist/modelfy/templates/index.js +3 -0
  23. package/dist/shared/components/Button.d.ts +7 -0
  24. package/dist/shared/components/Button.d.ts.map +1 -0
  25. package/dist/shared/components/Button.js +27 -0
  26. package/dist/shared/components/Footer.d.ts +8 -0
  27. package/dist/shared/components/Footer.d.ts.map +1 -0
  28. package/dist/shared/components/Footer.js +28 -0
  29. package/dist/shared/components/Header.d.ts +7 -0
  30. package/dist/shared/components/Header.d.ts.map +1 -0
  31. package/dist/shared/components/Header.js +40 -0
  32. package/dist/shared/components/index.d.ts +4 -0
  33. package/dist/shared/components/index.d.ts.map +1 -0
  34. package/dist/shared/components/index.js +3 -0
  35. package/dist/shared/index.d.ts +2 -0
  36. package/dist/shared/index.d.ts.map +1 -0
  37. package/dist/shared/index.js +1 -0
  38. package/package.json +40 -0
  39. package/src/index.ts +5 -0
  40. package/src/modelfy/config.ts +6 -0
  41. package/src/modelfy/index.ts +57 -0
  42. package/src/modelfy/templates/GrantCreditsEmail.tsx +205 -0
  43. package/src/modelfy/templates/OtpEmail.tsx +128 -0
  44. package/src/modelfy/templates/PromoSubscriptionEmail.tsx +189 -0
  45. package/src/modelfy/templates/index.ts +3 -0
  46. package/src/shared/components/Button.tsx +39 -0
  47. package/src/shared/components/Footer.tsx +63 -0
  48. package/src/shared/components/Header.tsx +56 -0
  49. package/src/shared/components/index.ts +3 -0
  50. package/src/shared/index.ts +1 -0
  51. package/tsconfig.json +22 -0
@@ -0,0 +1,57 @@
1
+ import { render } from "@react-email/render";
2
+ import { OtpEmail, type OtpEmailProps } from "./templates/OtpEmail.js";
3
+ import {
4
+ PromoSubscriptionEmail,
5
+ type PromoSubscriptionEmailProps,
6
+ } from "./templates/PromoSubscriptionEmail.js";
7
+ import { GrantCreditsEmail, type GrantCreditsEmailProps } from "./templates/GrantCreditsEmail.js";
8
+
9
+ // Re-export types
10
+ export type { OtpEmailProps, PromoSubscriptionEmailProps, GrantCreditsEmailProps };
11
+
12
+ // Re-export components for preview
13
+ export { OtpEmail, PromoSubscriptionEmail, GrantCreditsEmail };
14
+
15
+ // Render functions
16
+ export interface RenderResult {
17
+ subject: string;
18
+ html: string;
19
+ text: string;
20
+ }
21
+
22
+ export async function renderOtpEmail(props: OtpEmailProps): Promise<RenderResult> {
23
+ const html = await render(OtpEmail(props));
24
+ const text = await render(OtpEmail(props), { plainText: true });
25
+
26
+ return {
27
+ subject: "Your Modelfy access code",
28
+ html,
29
+ text,
30
+ };
31
+ }
32
+
33
+ export async function renderPromoSubscriptionEmail(
34
+ props: PromoSubscriptionEmailProps
35
+ ): Promise<RenderResult> {
36
+ const html = await render(PromoSubscriptionEmail(props));
37
+ const text = await render(PromoSubscriptionEmail(props), { plainText: true });
38
+
39
+ return {
40
+ subject: `You've received a ${props.durationDays}-day ${props.planName} subscription!`,
41
+ html,
42
+ text,
43
+ };
44
+ }
45
+
46
+ export async function renderGrantCreditsEmail(
47
+ props: GrantCreditsEmailProps
48
+ ): Promise<RenderResult> {
49
+ const html = await render(GrantCreditsEmail(props));
50
+ const text = await render(GrantCreditsEmail(props), { plainText: true });
51
+
52
+ return {
53
+ subject: `You've received ${(props.credits ?? 0).toLocaleString()} credits on Modelfy!`,
54
+ html,
55
+ text,
56
+ };
57
+ }
@@ -0,0 +1,205 @@
1
+ import {
2
+ Html,
3
+ Head,
4
+ Body,
5
+ Container,
6
+ Section,
7
+ Text,
8
+ Link,
9
+ Preview,
10
+ Font,
11
+ } from "@react-email/components";
12
+ import { Header, Footer, Button } from "../../shared/components/index.js";
13
+ import { modelfyConfig } from "../config.js";
14
+
15
+ export interface GrantCreditsEmailProps {
16
+ email: string;
17
+ credits?: number;
18
+ durationDays: number;
19
+ expiresAt: string;
20
+ description?: string;
21
+ }
22
+
23
+ const styles = {
24
+ body: {
25
+ margin: 0,
26
+ padding: 0,
27
+ backgroundColor: "#F8FAFC",
28
+ fontFamily: "'Outfit', Arial, sans-serif",
29
+ },
30
+ container: {
31
+ maxWidth: "560px",
32
+ margin: "32px auto",
33
+ backgroundColor: "#ffffff",
34
+ border: "1px solid #E2E8F0",
35
+ borderRadius: "16px",
36
+ boxShadow: "0 24px 48px rgba(15, 23, 42, 0.08)",
37
+ overflow: "hidden",
38
+ },
39
+ content: {
40
+ padding: "32px 40px 24px",
41
+ },
42
+ heading: {
43
+ margin: "0 0 16px",
44
+ fontSize: "24px",
45
+ fontWeight: 700,
46
+ color: "#0F172A",
47
+ textAlign: "center" as const,
48
+ },
49
+ paragraph: {
50
+ margin: "0 0 24px",
51
+ fontSize: "16px",
52
+ lineHeight: "1.6",
53
+ color: "rgba(15, 23, 42, 0.72)",
54
+ },
55
+ creditBox: {
56
+ backgroundColor: "rgba(34, 197, 94, 0.08)",
57
+ border: "1px solid rgba(34, 197, 94, 0.24)",
58
+ borderRadius: "12px",
59
+ padding: "24px",
60
+ marginBottom: "24px",
61
+ textAlign: "center" as const,
62
+ },
63
+ creditAmount: {
64
+ fontSize: "48px",
65
+ fontWeight: 700,
66
+ color: "#16A34A",
67
+ margin: "0 0 8px",
68
+ },
69
+ creditLabel: {
70
+ fontSize: "16px",
71
+ color: "rgba(15, 23, 42, 0.6)",
72
+ margin: 0,
73
+ },
74
+ detailsBox: {
75
+ backgroundColor: "#F8FAFC",
76
+ borderRadius: "8px",
77
+ padding: "16px",
78
+ marginBottom: "24px",
79
+ },
80
+ detailRow: {
81
+ display: "flex" as const,
82
+ justifyContent: "space-between" as const,
83
+ marginBottom: "8px",
84
+ },
85
+ detailLabel: {
86
+ fontSize: "14px",
87
+ color: "rgba(15, 23, 42, 0.6)",
88
+ margin: 0,
89
+ },
90
+ detailValue: {
91
+ fontSize: "14px",
92
+ fontWeight: 600,
93
+ color: "#0F172A",
94
+ margin: 0,
95
+ },
96
+ buttonContainer: {
97
+ textAlign: "center" as const,
98
+ marginBottom: "24px",
99
+ },
100
+ smallText: {
101
+ margin: "0 0 12px",
102
+ fontSize: "14px",
103
+ lineHeight: "1.6",
104
+ color: "rgba(15, 23, 42, 0.6)",
105
+ },
106
+ link: {
107
+ color: "#6366F1",
108
+ textDecoration: "none",
109
+ fontWeight: 600,
110
+ },
111
+ };
112
+
113
+ export function GrantCreditsEmail({
114
+ email,
115
+ credits,
116
+ durationDays,
117
+ expiresAt,
118
+ description,
119
+ }: GrantCreditsEmailProps) {
120
+ const safeCredits = Number.isFinite(credits) ? Number(credits) : 0;
121
+
122
+ return (
123
+ <Html lang="en">
124
+ <Head>
125
+ <Font
126
+ fontFamily="Outfit"
127
+ fallbackFontFamily="Arial"
128
+ webFont={{
129
+ url: "https://fonts.gstatic.com/s/outfit/v11/QGYyz_MVcBeNP4NjuGObqx1XmO1I4TC1C4G-EiAou6Y.woff2",
130
+ format: "woff2",
131
+ }}
132
+ fontWeight={400}
133
+ fontStyle="normal"
134
+ />
135
+ </Head>
136
+ <Preview>
137
+ You've received {safeCredits.toLocaleString()} credits on Modelfy!
138
+ </Preview>
139
+ <Body style={styles.body}>
140
+ <Container style={styles.container}>
141
+ <Header logoUrl={modelfyConfig.logoUrl} logoAlt="Modelfy" subtitle="Credits added" />
142
+ <Section style={styles.content}>
143
+ <Text style={styles.heading}>Credits Added to Your Account!</Text>
144
+ <Text style={styles.paragraph}>
145
+ Hi <strong>{email}</strong>, you've received bonus credits to use on Modelfy.
146
+ </Text>
147
+
148
+ <div style={styles.creditBox}>
149
+ <Text style={styles.creditAmount}>+{safeCredits.toLocaleString()}</Text>
150
+ <Text style={styles.creditLabel}>credits</Text>
151
+ </div>
152
+
153
+ <div style={styles.detailsBox}>
154
+ <table width="100%" cellPadding={0} cellSpacing={0}>
155
+ <tr>
156
+ <td style={{ paddingBottom: "8px" }}>
157
+ <Text style={styles.detailLabel}>Valid for</Text>
158
+ </td>
159
+ <td style={{ paddingBottom: "8px", textAlign: "right" }}>
160
+ <Text style={styles.detailValue}>{durationDays} days</Text>
161
+ </td>
162
+ </tr>
163
+ <tr>
164
+ <td>
165
+ <Text style={styles.detailLabel}>Expires</Text>
166
+ </td>
167
+ <td style={{ textAlign: "right" }}>
168
+ <Text style={styles.detailValue}>{expiresAt}</Text>
169
+ </td>
170
+ </tr>
171
+ </table>
172
+ </div>
173
+
174
+ {description && (
175
+ <Text style={styles.paragraph}>
176
+ <em>"{description}"</em>
177
+ </Text>
178
+ )}
179
+
180
+ <div style={styles.buttonContainer}>
181
+ <Button href={modelfyConfig.appUrl}>Start Creating</Button>
182
+ </div>
183
+
184
+ <Text style={styles.smallText}>
185
+ These credits will be used before your regular credits and expire on {expiresAt}.
186
+ </Text>
187
+ <Text style={styles.smallText}>
188
+ Questions? Contact us at{" "}
189
+ <Link href={`mailto:${modelfyConfig.supportEmail}`} style={styles.link}>
190
+ {modelfyConfig.supportEmail}
191
+ </Link>
192
+ </Text>
193
+ </Section>
194
+ <Footer
195
+ companyName={modelfyConfig.companyName}
196
+ message="You received this email because credits were added to your Modelfy account."
197
+ supportEmail={modelfyConfig.supportEmail}
198
+ />
199
+ </Container>
200
+ </Body>
201
+ </Html>
202
+ );
203
+ }
204
+
205
+ export default GrantCreditsEmail;
@@ -0,0 +1,128 @@
1
+ import {
2
+ Html,
3
+ Head,
4
+ Body,
5
+ Container,
6
+ Section,
7
+ Text,
8
+ Link,
9
+ Preview,
10
+ Font,
11
+ } from "@react-email/components";
12
+ import { Header, Footer } from "../../shared/components/index.js";
13
+ import { modelfyConfig } from "../config.js";
14
+
15
+ export interface OtpEmailProps {
16
+ otp: string;
17
+ email: string;
18
+ expirationMinutes?: number;
19
+ }
20
+
21
+ const styles = {
22
+ body: {
23
+ margin: 0,
24
+ padding: 0,
25
+ backgroundColor: "#F8FAFC",
26
+ fontFamily: "'Outfit', Arial, sans-serif",
27
+ },
28
+ container: {
29
+ maxWidth: "560px",
30
+ margin: "32px auto",
31
+ backgroundColor: "#ffffff",
32
+ border: "1px solid #E2E8F0",
33
+ borderRadius: "16px",
34
+ boxShadow: "0 24px 48px rgba(15, 23, 42, 0.08)",
35
+ overflow: "hidden",
36
+ },
37
+ content: {
38
+ padding: "32px 40px 24px",
39
+ },
40
+ paragraph: {
41
+ margin: "16px 0 24px",
42
+ fontSize: "16px",
43
+ lineHeight: "1.6",
44
+ color: "rgba(15, 23, 42, 0.72)",
45
+ },
46
+ otpContainer: {
47
+ display: "inline-block",
48
+ backgroundColor: "rgba(99, 102, 241, 0.08)",
49
+ border: "1px solid rgba(99, 102, 241, 0.24)",
50
+ borderRadius: "12px",
51
+ padding: "20px 32px",
52
+ marginBottom: "24px",
53
+ },
54
+ otp: {
55
+ fontSize: "32px",
56
+ letterSpacing: "8px",
57
+ fontWeight: 700,
58
+ color: "#0F172A",
59
+ margin: 0,
60
+ },
61
+ smallText: {
62
+ margin: "0 0 12px",
63
+ fontSize: "15px",
64
+ lineHeight: "1.6",
65
+ color: "rgba(15, 23, 42, 0.72)",
66
+ },
67
+ link: {
68
+ color: "#0F172A",
69
+ textDecoration: "none",
70
+ fontWeight: 600,
71
+ },
72
+ };
73
+
74
+ export function OtpEmail({ otp, email, expirationMinutes = 10 }: OtpEmailProps) {
75
+ const sanitizedOtp = (typeof otp === "string" ? otp : String(otp ?? ""))
76
+ .replace(/\s+/g, "")
77
+ .trim();
78
+
79
+ return (
80
+ <Html lang="en">
81
+ <Head>
82
+ <Font
83
+ fontFamily="Outfit"
84
+ fallbackFontFamily="Arial"
85
+ webFont={{
86
+ url: "https://fonts.gstatic.com/s/outfit/v11/QGYyz_MVcBeNP4NjuGObqx1XmO1I4TC1C4G-EiAou6Y.woff2",
87
+ format: "woff2",
88
+ }}
89
+ fontWeight={400}
90
+ fontStyle="normal"
91
+ />
92
+ </Head>
93
+ <Preview>Your Modelfy access code: {sanitizedOtp}</Preview>
94
+ <Body style={styles.body}>
95
+ <Container style={styles.container}>
96
+ <Header logoUrl={modelfyConfig.logoUrl} logoAlt="Modelfy" subtitle="Access code" />
97
+ <Section style={styles.content}>
98
+ <Text style={styles.paragraph}>
99
+ Hi <strong>{email}</strong>, use the code below to sign in. It will stay valid for the
100
+ next <strong>{expirationMinutes} minutes</strong>.
101
+ </Text>
102
+ <div style={styles.otpContainer}>
103
+ <Text style={styles.otp}>{sanitizedOtp}</Text>
104
+ </div>
105
+ <Text style={styles.smallText}>
106
+ If you didn't request this code, you can safely ignore this email. No one else can
107
+ access your account without it.
108
+ </Text>
109
+ <Text style={styles.smallText}>
110
+ Need a hand? Reach us anytime at{" "}
111
+ <Link href={`mailto:${modelfyConfig.supportEmail}`} style={styles.link}>
112
+ {modelfyConfig.supportEmail}
113
+ </Link>
114
+ .
115
+ </Text>
116
+ </Section>
117
+ <Footer
118
+ companyName={modelfyConfig.companyName}
119
+ message="You received this email because someone attempted to sign in to Modelfy with this address."
120
+ supportEmail={modelfyConfig.supportEmail}
121
+ />
122
+ </Container>
123
+ </Body>
124
+ </Html>
125
+ );
126
+ }
127
+
128
+ export default OtpEmail;
@@ -0,0 +1,189 @@
1
+ import {
2
+ Html,
3
+ Head,
4
+ Body,
5
+ Container,
6
+ Section,
7
+ Text,
8
+ Link,
9
+ Preview,
10
+ Font,
11
+ } from "@react-email/components";
12
+ import { Header, Footer, Button } from "../../shared/components/index.js";
13
+ import { modelfyConfig } from "../config.js";
14
+
15
+ export interface PromoSubscriptionEmailProps {
16
+ email: string;
17
+ planName: string;
18
+ durationDays: number;
19
+ credits?: number;
20
+ expiresAt: string;
21
+ notes?: string;
22
+ }
23
+
24
+ const styles = {
25
+ body: {
26
+ margin: 0,
27
+ padding: 0,
28
+ backgroundColor: "#F8FAFC",
29
+ fontFamily: "'Outfit', Arial, sans-serif",
30
+ },
31
+ container: {
32
+ maxWidth: "560px",
33
+ margin: "32px auto",
34
+ backgroundColor: "#ffffff",
35
+ border: "1px solid #E2E8F0",
36
+ borderRadius: "16px",
37
+ boxShadow: "0 24px 48px rgba(15, 23, 42, 0.08)",
38
+ overflow: "hidden",
39
+ },
40
+ content: {
41
+ padding: "32px 40px 24px",
42
+ },
43
+ heading: {
44
+ margin: "0 0 16px",
45
+ fontSize: "24px",
46
+ fontWeight: 700,
47
+ color: "#0F172A",
48
+ textAlign: "center" as const,
49
+ },
50
+ paragraph: {
51
+ margin: "0 0 24px",
52
+ fontSize: "16px",
53
+ lineHeight: "1.6",
54
+ color: "rgba(15, 23, 42, 0.72)",
55
+ },
56
+ highlightBox: {
57
+ backgroundColor: "rgba(99, 102, 241, 0.08)",
58
+ border: "1px solid rgba(99, 102, 241, 0.24)",
59
+ borderRadius: "12px",
60
+ padding: "24px",
61
+ marginBottom: "24px",
62
+ },
63
+ highlightRow: {
64
+ display: "flex" as const,
65
+ justifyContent: "space-between" as const,
66
+ marginBottom: "12px",
67
+ },
68
+ highlightLabel: {
69
+ fontSize: "14px",
70
+ color: "rgba(15, 23, 42, 0.6)",
71
+ margin: 0,
72
+ },
73
+ highlightValue: {
74
+ fontSize: "16px",
75
+ fontWeight: 600,
76
+ color: "#0F172A",
77
+ margin: 0,
78
+ },
79
+ buttonContainer: {
80
+ textAlign: "center" as const,
81
+ marginBottom: "24px",
82
+ },
83
+ smallText: {
84
+ margin: "0 0 12px",
85
+ fontSize: "14px",
86
+ lineHeight: "1.6",
87
+ color: "rgba(15, 23, 42, 0.6)",
88
+ },
89
+ link: {
90
+ color: "#6366F1",
91
+ textDecoration: "none",
92
+ fontWeight: 600,
93
+ },
94
+ };
95
+
96
+ export function PromoSubscriptionEmail({
97
+ email,
98
+ planName,
99
+ durationDays,
100
+ credits,
101
+ expiresAt,
102
+ notes,
103
+ }: PromoSubscriptionEmailProps) {
104
+ const safeCredits = Number.isFinite(credits) ? Number(credits) : 0;
105
+
106
+ return (
107
+ <Html lang="en">
108
+ <Head>
109
+ <Font
110
+ fontFamily="Outfit"
111
+ fallbackFontFamily="Arial"
112
+ webFont={{
113
+ url: "https://fonts.gstatic.com/s/outfit/v11/QGYyz_MVcBeNP4NjuGObqx1XmO1I4TC1C4G-EiAou6Y.woff2",
114
+ format: "woff2",
115
+ }}
116
+ fontWeight={400}
117
+ fontStyle="normal"
118
+ />
119
+ </Head>
120
+ <Preview>
121
+ {`You've received a ${durationDays}-day ${planName} subscription!`}
122
+ </Preview>
123
+ <Body style={styles.body}>
124
+ <Container style={styles.container}>
125
+ <Header logoUrl={modelfyConfig.logoUrl} logoAlt="Modelfy" subtitle="Gift for you" />
126
+ <Section style={styles.content}>
127
+ <Text style={styles.heading}>You've Got a Gift!</Text>
128
+ <Text style={styles.paragraph}>
129
+ Hi <strong>{email}</strong>, great news! You've been granted a promotional{" "}
130
+ <strong>{planName}</strong> subscription.
131
+ </Text>
132
+
133
+ <div style={styles.highlightBox}>
134
+ <table width="100%" cellPadding={0} cellSpacing={0}>
135
+ <tr>
136
+ <td style={{ paddingBottom: "12px" }}>
137
+ <Text style={styles.highlightLabel}>Plan</Text>
138
+ <Text style={styles.highlightValue}>{planName}</Text>
139
+ </td>
140
+ <td style={{ paddingBottom: "12px", textAlign: "right" }}>
141
+ <Text style={styles.highlightLabel}>Duration</Text>
142
+ <Text style={styles.highlightValue}>{durationDays} days</Text>
143
+ </td>
144
+ </tr>
145
+ <tr>
146
+ <td>
147
+ <Text style={styles.highlightLabel}>Credits Included</Text>
148
+ <Text style={styles.highlightValue}>{safeCredits.toLocaleString()}</Text>
149
+ </td>
150
+ <td style={{ textAlign: "right" }}>
151
+ <Text style={styles.highlightLabel}>Expires</Text>
152
+ <Text style={styles.highlightValue}>{expiresAt}</Text>
153
+ </td>
154
+ </tr>
155
+ </table>
156
+ </div>
157
+
158
+ {notes && (
159
+ <Text style={styles.paragraph}>
160
+ <em>"{notes}"</em>
161
+ </Text>
162
+ )}
163
+
164
+ <div style={styles.buttonContainer}>
165
+ <Button href={modelfyConfig.appUrl}>Open Modelfy</Button>
166
+ </div>
167
+
168
+ <Text style={styles.smallText}>
169
+ This promotional subscription will not auto-renew. Enjoy your free access!
170
+ </Text>
171
+ <Text style={styles.smallText}>
172
+ Questions? Contact us at{" "}
173
+ <Link href={`mailto:${modelfyConfig.supportEmail}`} style={styles.link}>
174
+ {modelfyConfig.supportEmail}
175
+ </Link>
176
+ </Text>
177
+ </Section>
178
+ <Footer
179
+ companyName={modelfyConfig.companyName}
180
+ message="You received this email because you were granted a promotional subscription."
181
+ supportEmail={modelfyConfig.supportEmail}
182
+ />
183
+ </Container>
184
+ </Body>
185
+ </Html>
186
+ );
187
+ }
188
+
189
+ export default PromoSubscriptionEmail;
@@ -0,0 +1,3 @@
1
+ export { OtpEmail, type OtpEmailProps } from "./OtpEmail.js";
2
+ export { PromoSubscriptionEmail, type PromoSubscriptionEmailProps } from "./PromoSubscriptionEmail.js";
3
+ export { GrantCreditsEmail, type GrantCreditsEmailProps } from "./GrantCreditsEmail.js";
@@ -0,0 +1,39 @@
1
+ import { Button as ReactEmailButton } from "@react-email/components";
2
+
3
+ export interface ButtonProps {
4
+ href: string;
5
+ children: React.ReactNode;
6
+ variant?: "primary" | "secondary";
7
+ }
8
+
9
+ const baseStyles = {
10
+ display: "inline-block",
11
+ padding: "14px 28px",
12
+ fontSize: "16px",
13
+ fontWeight: 600,
14
+ textDecoration: "none",
15
+ borderRadius: "8px",
16
+ textAlign: "center" as const,
17
+ };
18
+
19
+ const variants = {
20
+ primary: {
21
+ ...baseStyles,
22
+ backgroundColor: "#18181B",
23
+ color: "#ffffff",
24
+ },
25
+ secondary: {
26
+ ...baseStyles,
27
+ backgroundColor: "transparent",
28
+ color: "#18181B",
29
+ border: "2px solid #18181B",
30
+ },
31
+ };
32
+
33
+ export function Button({ href, children, variant = "primary" }: ButtonProps) {
34
+ return (
35
+ <ReactEmailButton href={href} style={variants[variant]}>
36
+ {children}
37
+ </ReactEmailButton>
38
+ );
39
+ }
@@ -0,0 +1,63 @@
1
+ import { Section, Text, Link } from "@react-email/components";
2
+
3
+ export interface FooterProps {
4
+ companyName: string;
5
+ year?: number;
6
+ message?: string;
7
+ supportEmail?: string;
8
+ }
9
+
10
+ const styles = {
11
+ footer: {
12
+ padding: "20px 40px",
13
+ backgroundColor: "#F1F5F9",
14
+ borderTop: "1px solid rgba(148, 163, 184, 0.2)",
15
+ },
16
+ text: {
17
+ margin: 0,
18
+ fontSize: "13px",
19
+ lineHeight: "1.6",
20
+ color: "rgba(15, 23, 42, 0.72)",
21
+ },
22
+ noReplyText: {
23
+ margin: "12px 0 0",
24
+ fontSize: "12px",
25
+ lineHeight: "1.5",
26
+ color: "rgba(15, 23, 42, 0.5)",
27
+ },
28
+ link: {
29
+ color: "#6366F1",
30
+ textDecoration: "none",
31
+ },
32
+ };
33
+
34
+ export function Footer({
35
+ companyName,
36
+ year = new Date().getFullYear(),
37
+ message,
38
+ supportEmail,
39
+ }: FooterProps) {
40
+ return (
41
+ <Section style={styles.footer}>
42
+ <Text style={styles.text}>
43
+ © {year} {companyName}. All rights reserved.
44
+ {message && (
45
+ <>
46
+ <br />
47
+ {message}
48
+ </>
49
+ )}
50
+ </Text>
51
+ {supportEmail && (
52
+ <Text style={styles.noReplyText}>
53
+ This is an automated message. Please do not reply to this email. For any questions,
54
+ contact us at{" "}
55
+ <Link href={`mailto:${supportEmail}`} style={styles.link}>
56
+ {supportEmail}
57
+ </Link>
58
+ .
59
+ </Text>
60
+ )}
61
+ </Section>
62
+ );
63
+ }