brookmind-emails 0.1.12 → 0.1.13

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.
@@ -2,8 +2,9 @@ import { OtpEmail, type OtpEmailProps } from "./templates/OtpEmail.js";
2
2
  import { PromoSubscriptionEmail, type PromoSubscriptionEmailProps } from "./templates/PromoSubscriptionEmail.js";
3
3
  import { GrantCreditsEmail, type GrantCreditsEmailProps } from "./templates/GrantCreditsEmail.js";
4
4
  import { WelcomeEmail, type WelcomeEmailProps } from "./templates/WelcomeEmail.js";
5
- export type { OtpEmailProps, PromoSubscriptionEmailProps, GrantCreditsEmailProps, WelcomeEmailProps };
6
- export { OtpEmail, PromoSubscriptionEmail, GrantCreditsEmail, WelcomeEmail };
5
+ import { FeedbackRequestEmail, type FeedbackRequestEmailProps } from "./templates/FeedbackRequestEmail.js";
6
+ export type { OtpEmailProps, PromoSubscriptionEmailProps, GrantCreditsEmailProps, WelcomeEmailProps, FeedbackRequestEmailProps, };
7
+ export { OtpEmail, PromoSubscriptionEmail, GrantCreditsEmail, WelcomeEmail, FeedbackRequestEmail };
7
8
  export interface RenderResult {
8
9
  subject: string;
9
10
  html: string;
@@ -13,4 +14,5 @@ export declare function renderOtpEmail(props: OtpEmailProps): Promise<RenderResu
13
14
  export declare function renderPromoSubscriptionEmail(props: PromoSubscriptionEmailProps): Promise<RenderResult>;
14
15
  export declare function renderGrantCreditsEmail(props: GrantCreditsEmailProps): Promise<RenderResult>;
15
16
  export declare function renderWelcomeEmail(props: WelcomeEmailProps): Promise<RenderResult>;
17
+ export declare function renderFeedbackRequestEmail(props: FeedbackRequestEmailProps): Promise<RenderResult>;
16
18
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/modelfy/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACvE,OAAO,EACL,sBAAsB,EACtB,KAAK,2BAA2B,EACjC,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EAAE,iBAAiB,EAAE,KAAK,sBAAsB,EAAE,MAAM,kCAAkC,CAAC;AAClG,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAGnF,YAAY,EAAE,aAAa,EAAE,2BAA2B,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,CAAC;AAGtG,OAAO,EAAE,QAAQ,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,YAAY,EAAE,CAAC;AAG7E,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAsB,cAAc,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CAShF;AAED,wBAAsB,4BAA4B,CAChD,KAAK,EAAE,2BAA2B,GACjC,OAAO,CAAC,YAAY,CAAC,CASvB;AAED,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,sBAAsB,GAC5B,OAAO,CAAC,YAAY,CAAC,CASvB;AAED,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,iBAAiB,GACvB,OAAO,CAAC,YAAY,CAAC,CASvB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/modelfy/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACvE,OAAO,EACL,sBAAsB,EACtB,KAAK,2BAA2B,EACjC,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EAAE,iBAAiB,EAAE,KAAK,sBAAsB,EAAE,MAAM,kCAAkC,CAAC;AAClG,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AACnF,OAAO,EACL,oBAAoB,EACpB,KAAK,yBAAyB,EAC/B,MAAM,qCAAqC,CAAC;AAG7C,YAAY,EACV,aAAa,EACb,2BAA2B,EAC3B,sBAAsB,EACtB,iBAAiB,EACjB,yBAAyB,GAC1B,CAAC;AAGF,OAAO,EAAE,QAAQ,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,YAAY,EAAE,oBAAoB,EAAE,CAAC;AAGnG,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAsB,cAAc,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CAShF;AAED,wBAAsB,4BAA4B,CAChD,KAAK,EAAE,2BAA2B,GACjC,OAAO,CAAC,YAAY,CAAC,CASvB;AAED,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,sBAAsB,GAC5B,OAAO,CAAC,YAAY,CAAC,CASvB;AAED,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,iBAAiB,GACvB,OAAO,CAAC,YAAY,CAAC,CASvB;AAED,wBAAsB,0BAA0B,CAC9C,KAAK,EAAE,yBAAyB,GAC/B,OAAO,CAAC,YAAY,CAAC,CASvB"}
@@ -3,8 +3,9 @@ import { OtpEmail } from "./templates/OtpEmail.js";
3
3
  import { PromoSubscriptionEmail, } from "./templates/PromoSubscriptionEmail.js";
4
4
  import { GrantCreditsEmail } from "./templates/GrantCreditsEmail.js";
5
5
  import { WelcomeEmail } from "./templates/WelcomeEmail.js";
6
+ import { FeedbackRequestEmail, } from "./templates/FeedbackRequestEmail.js";
6
7
  // Re-export components for preview
7
- export { OtpEmail, PromoSubscriptionEmail, GrantCreditsEmail, WelcomeEmail };
8
+ export { OtpEmail, PromoSubscriptionEmail, GrantCreditsEmail, WelcomeEmail, FeedbackRequestEmail };
8
9
  export async function renderOtpEmail(props) {
9
10
  const html = await render(OtpEmail(props));
10
11
  const text = await render(OtpEmail(props), { plainText: true });
@@ -41,3 +42,12 @@ export async function renderWelcomeEmail(props) {
41
42
  text,
42
43
  };
43
44
  }
45
+ export async function renderFeedbackRequestEmail(props) {
46
+ const html = await render(FeedbackRequestEmail(props));
47
+ const text = await render(FeedbackRequestEmail(props), { plainText: true });
48
+ return {
49
+ subject: "We'd love your feedback on Modelfy",
50
+ html,
51
+ text,
52
+ };
53
+ }
@@ -0,0 +1,8 @@
1
+ export interface FeedbackRequestEmailProps {
2
+ email: string;
3
+ planName: string;
4
+ subscriptionEndDate: string;
5
+ }
6
+ export declare function FeedbackRequestEmail({ email, planName, subscriptionEndDate, }: FeedbackRequestEmailProps): React.ReactElement;
7
+ export default FeedbackRequestEmail;
8
+ //# sourceMappingURL=FeedbackRequestEmail.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FeedbackRequestEmail.d.ts","sourceRoot":"","sources":["../../../src/modelfy/templates/FeedbackRequestEmail.tsx"],"names":[],"mappings":"AAeA,MAAM,WAAW,yBAAyB;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAoMD,wBAAgB,oBAAoB,CAAC,EACnC,KAA0B,EAC1B,QAAgB,EAChB,mBAAuC,GACxC,EAAE,yBAAyB,GAAG,KAAK,CAAC,YAAY,CAiKhD;AAED,eAAe,oBAAoB,CAAC"}
@@ -0,0 +1,208 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Html, Head, Body, Container, Section, Text, Link, Preview, Font, Img, Button as ReactEmailButton, } from "@react-email/components";
3
+ import { modelfyConfig } from "../config.js";
4
+ // Design System Colors - Matching Modelfy Landing
5
+ const colors = {
6
+ background: "#0d0d0d",
7
+ cardBg: "#121212",
8
+ primary: "#0a8f5a",
9
+ white: "#f2f2f2",
10
+ textSecondary: "rgba(242, 242, 242, 0.7)",
11
+ textMuted: "#8c8c8c",
12
+ border: "#262626",
13
+ };
14
+ const styles = {
15
+ body: {
16
+ margin: 0,
17
+ padding: 0,
18
+ backgroundColor: colors.background,
19
+ fontFamily: "'Inter', 'Segoe UI', Arial, sans-serif",
20
+ },
21
+ container: {
22
+ maxWidth: "600px",
23
+ margin: "0 auto",
24
+ backgroundColor: colors.background,
25
+ },
26
+ header: {
27
+ backgroundColor: colors.background,
28
+ padding: "40px 32px 24px",
29
+ textAlign: "center",
30
+ },
31
+ logo: {
32
+ display: "block",
33
+ margin: "0 auto",
34
+ },
35
+ heroSection: {
36
+ padding: "24px 32px 16px",
37
+ textAlign: "center",
38
+ },
39
+ heroTagline: {
40
+ margin: "0 0 12px",
41
+ fontSize: "13px",
42
+ fontWeight: 600,
43
+ color: colors.primary,
44
+ letterSpacing: "2px",
45
+ textTransform: "uppercase",
46
+ },
47
+ heroHeading: {
48
+ margin: "0 0 16px",
49
+ fontSize: "28px",
50
+ fontWeight: 700,
51
+ color: colors.white,
52
+ lineHeight: "1.3",
53
+ },
54
+ heroSubheading: {
55
+ margin: "0",
56
+ fontSize: "16px",
57
+ lineHeight: "1.6",
58
+ color: colors.textSecondary,
59
+ },
60
+ feedbackSection: {
61
+ backgroundColor: colors.cardBg,
62
+ padding: "32px",
63
+ borderTop: `1px solid ${colors.border}`,
64
+ borderBottom: `1px solid ${colors.border}`,
65
+ },
66
+ feedbackText: {
67
+ margin: "0 0 20px",
68
+ fontSize: "15px",
69
+ lineHeight: "1.6",
70
+ color: colors.textSecondary,
71
+ textAlign: "center",
72
+ },
73
+ feedbackBox: {
74
+ backgroundColor: "rgba(10, 143, 90, 0.1)",
75
+ border: `1px solid rgba(10, 143, 90, 0.3)`,
76
+ borderRadius: "12px",
77
+ padding: "20px 24px",
78
+ marginBottom: "24px",
79
+ },
80
+ feedbackBoxText: {
81
+ margin: 0,
82
+ fontSize: "14px",
83
+ lineHeight: "1.6",
84
+ color: colors.textSecondary,
85
+ textAlign: "center",
86
+ },
87
+ questionsSection: {
88
+ backgroundColor: colors.background,
89
+ padding: "32px",
90
+ },
91
+ questionsSectionTitle: {
92
+ margin: "0 0 20px",
93
+ fontSize: "14px",
94
+ fontWeight: 600,
95
+ color: colors.white,
96
+ textAlign: "center",
97
+ },
98
+ questionsList: {
99
+ margin: "0 auto",
100
+ padding: "0 20px",
101
+ maxWidth: "400px",
102
+ },
103
+ questionItem: {
104
+ margin: "0 0 12px",
105
+ fontSize: "14px",
106
+ lineHeight: "1.5",
107
+ color: colors.textMuted,
108
+ },
109
+ questionBullet: {
110
+ color: colors.primary,
111
+ marginRight: "8px",
112
+ },
113
+ ctaSection: {
114
+ backgroundColor: colors.cardBg,
115
+ padding: "32px",
116
+ textAlign: "center",
117
+ borderTop: `1px solid ${colors.border}`,
118
+ },
119
+ ctaButton: {
120
+ display: "inline-block",
121
+ padding: "14px 36px",
122
+ fontSize: "15px",
123
+ fontWeight: 600,
124
+ textDecoration: "none",
125
+ borderRadius: "10px",
126
+ backgroundColor: colors.primary,
127
+ color: "#ffffff",
128
+ textAlign: "center",
129
+ },
130
+ winbackSection: {
131
+ backgroundColor: colors.background,
132
+ padding: "32px",
133
+ textAlign: "center",
134
+ },
135
+ winbackText: {
136
+ margin: "0 0 8px",
137
+ fontSize: "14px",
138
+ lineHeight: "1.6",
139
+ color: colors.textMuted,
140
+ textAlign: "center",
141
+ },
142
+ winbackLink: {
143
+ color: colors.primary,
144
+ textDecoration: "none",
145
+ fontWeight: 600,
146
+ },
147
+ downloadSection: {
148
+ backgroundColor: colors.cardBg,
149
+ padding: "24px 32px",
150
+ textAlign: "center",
151
+ borderTop: `1px solid ${colors.border}`,
152
+ },
153
+ downloadText: {
154
+ margin: "0 0 16px",
155
+ fontSize: "14px",
156
+ color: colors.white,
157
+ },
158
+ badgeContainer: {
159
+ textAlign: "center",
160
+ },
161
+ badge: {
162
+ display: "inline-block",
163
+ margin: "0 8px",
164
+ },
165
+ footer: {
166
+ backgroundColor: colors.cardBg,
167
+ padding: "24px 32px",
168
+ textAlign: "center",
169
+ borderTop: `1px solid ${colors.border}`,
170
+ },
171
+ footerLogo: {
172
+ display: "block",
173
+ margin: "0 auto 12px",
174
+ opacity: 0.6,
175
+ },
176
+ footerText: {
177
+ margin: "0 0 4px",
178
+ fontSize: "11px",
179
+ color: colors.textMuted,
180
+ lineHeight: "1.6",
181
+ },
182
+ footerLink: {
183
+ color: colors.textMuted,
184
+ textDecoration: "underline",
185
+ },
186
+ };
187
+ const feedbackReasons = [
188
+ "The pricing didn't fit my budget",
189
+ "I found an alternative solution",
190
+ "The features didn't meet my needs",
191
+ "I experienced technical issues",
192
+ "I didn't use the product enough",
193
+ "Other reasons",
194
+ ];
195
+ export function FeedbackRequestEmail({ email = "user@example.com", planName = "Pro", subscriptionEndDate = "January 1, 2025", }) {
196
+ const feedbackMailto = `mailto:${modelfyConfig.supportEmail}?subject=Feedback%20on%20my%20Modelfy%20subscription&body=Hi%20Modelfy%20team%2C%0A%0AI%27m%20sharing%20feedback%20about%20my%20${encodeURIComponent(planName)}%20subscription%20that%20ended%20on%20${encodeURIComponent(subscriptionEndDate)}.%0A%0A`;
197
+ return (_jsxs(Html, { lang: "en", children: [_jsxs(Head, { children: [_jsx(Font, { fontFamily: "Inter", fallbackFontFamily: "Arial", webFont: {
198
+ url: "https://fonts.gstatic.com/s/inter/v13/UcCO3FwrK3iLTeHuS_fvQtMwCp50KnMw2boKoduKmMEVuLyfAZ9hjp-Ek-_0.woff2",
199
+ format: "woff2",
200
+ }, fontWeight: 400, fontStyle: "normal" }), _jsx(Font, { fontFamily: "Inter", fallbackFontFamily: "Arial", webFont: {
201
+ url: "https://fonts.gstatic.com/s/inter/v13/UcCO3FwrK3iLTeHuS_fvQtMwCp50KnMw2boKoduKmMEVuI6fAZ9hjp-Ek-_0.woff2",
202
+ format: "woff2",
203
+ }, fontWeight: 600, fontStyle: "normal" }), _jsx(Font, { fontFamily: "Inter", fallbackFontFamily: "Arial", webFont: {
204
+ url: "https://fonts.gstatic.com/s/inter/v13/UcCO3FwrK3iLTeHuS_fvQtMwCp50KnMw2boKoduKmMEVuFuYAZ9hjp-Ek-_0.woff2",
205
+ format: "woff2",
206
+ }, fontWeight: 700, fontStyle: "normal" })] }), _jsx(Preview, { children: "We'd love your feedback on Modelfy - Help us improve" }), _jsx(Body, { style: styles.body, children: _jsxs(Container, { style: styles.container, children: [_jsx(Section, { style: styles.header, children: _jsx(Img, { src: modelfyConfig.logoUrl, width: "130", height: "auto", alt: "Modelfy", style: styles.logo }) }), _jsxs(Section, { style: styles.heroSection, children: [_jsx(Text, { style: styles.heroTagline, children: "We'd Love Your Feedback" }), _jsx(Text, { style: styles.heroHeading, children: "We're Sorry to See You Go" }), _jsxs(Text, { style: styles.heroSubheading, children: ["Hi ", _jsx("span", { style: { color: colors.white, fontWeight: 600 }, children: email }), ", your", " ", _jsx("span", { style: { color: colors.white, fontWeight: 600 }, children: planName }), " subscription ended on", " ", _jsx("span", { style: { color: colors.white, fontWeight: 600 }, children: subscriptionEndDate }), ". Your feedback would help us improve Modelfy for everyone."] })] }), _jsxs(Section, { style: styles.feedbackSection, children: [_jsx(Text, { style: styles.feedbackText, children: "We'd genuinely appreciate hearing about your experience. Just reply to this email or click the button below." }), _jsx("div", { style: styles.feedbackBox, children: _jsxs(Text, { style: styles.feedbackBoxText, children: ["Simply hit reply and let us know what we could do better.", _jsx("br", {}), "All feedback goes directly to our team."] }) })] }), _jsxs(Section, { style: styles.questionsSection, children: [_jsx(Text, { style: styles.questionsSectionTitle, children: "Some things we'd love to know:" }), _jsx("div", { style: styles.questionsList, children: feedbackReasons.map((reason, index) => (_jsxs(Text, { style: styles.questionItem, children: [_jsx("span", { style: styles.questionBullet, children: "\u2022" }), reason] }, index))) })] }), _jsx(Section, { style: styles.ctaSection, children: _jsx(ReactEmailButton, { href: feedbackMailto, style: styles.ctaButton, children: "Share Your Feedback" }) }), _jsxs(Section, { style: styles.winbackSection, children: [_jsx(Text, { style: styles.winbackText, children: "Changed your mind? Your account is still here waiting for you." }), _jsx(Text, { style: styles.winbackText, children: _jsx(Link, { href: modelfyConfig.appUrl, style: styles.winbackLink, children: "Reactivate your subscription" }) })] }), _jsxs(Section, { style: styles.downloadSection, children: [_jsx(Text, { style: styles.downloadText, children: "Get the Modelfy app" }), _jsxs("div", { style: styles.badgeContainer, children: [_jsx(Link, { href: modelfyConfig.appStoreUrl, style: styles.badge, children: _jsx(Img, { src: modelfyConfig.appStoreBadgeUrl, width: "120", height: "40", alt: "Download on the App Store" }) }), _jsx(Link, { href: modelfyConfig.playStoreUrl, style: styles.badge, children: _jsx(Img, { src: modelfyConfig.playStoreBadgeUrl, width: "135", height: "40", alt: "Get it on Google Play" }) })] })] }), _jsxs(Section, { style: styles.footer, children: [_jsx(Img, { src: modelfyConfig.logoUrl, width: "80", height: "auto", alt: "Modelfy", style: styles.footerLogo }), _jsxs(Text, { style: styles.footerText, children: ["\u00A9 ", new Date().getFullYear(), " ", modelfyConfig.companyName, ". All rights reserved."] }), _jsx(Text, { style: styles.footerText, children: "You received this email because your Modelfy subscription has ended." }), _jsx(Text, { style: styles.footerText, children: _jsx(Link, { href: `${modelfyConfig.appUrl}/privacy`, style: styles.footerLink, children: "Privacy Policy" }) })] })] }) })] }));
207
+ }
208
+ export default FeedbackRequestEmail;
@@ -253,7 +253,7 @@ const styles = {
253
253
  };
254
254
  // Image pools for random selection
255
255
  const campaignImages = Array.from({ length: 33 }, (_, i) => `https://bucket.modelfy.ai/app-media/campaign/campaign${i + 1}.jpeg`);
256
- const campaignProImages = Array.from({ length: 15 }, (_, i) => `https://bucket.modelfy.ai/app-media/campaign-pro/campaign-pro-${i + 1}.jpg`);
256
+ const _campaignProImages = Array.from({ length: 15 }, (_, i) => `https://bucket.modelfy.ai/app-media/campaign-pro/campaign-pro-${i + 1}.jpg`);
257
257
  const studioImages = Array.from({ length: 31 }, (_, i) => `https://bucket.modelfy.ai/app-media/studio/studio${i + 1}.jpeg`);
258
258
  const colorSwapImages = Array.from({ length: 10 }, (_, i) => `https://modelfy.ai/images/color-swap/studio-color-change${i + 1}.jpg`);
259
259
  const modelImages = [
@@ -0,0 +1,25 @@
1
+ import globals from "globals";
2
+ import tseslint from "typescript-eslint";
3
+
4
+ export default tseslint.config(
5
+ { ignores: ["dist/**", "node_modules/**"] },
6
+ ...tseslint.configs.recommended,
7
+ {
8
+ files: ["src/**/*.{ts,tsx}"],
9
+ languageOptions: {
10
+ globals: globals.node,
11
+ parserOptions: {
12
+ ecmaFeatures: {
13
+ jsx: true,
14
+ },
15
+ },
16
+ },
17
+ rules: {
18
+ "@typescript-eslint/no-explicit-any": "error",
19
+ "@typescript-eslint/no-unused-vars": [
20
+ "error",
21
+ { argsIgnorePattern: "^_", varsIgnorePattern: "^_" },
22
+ ],
23
+ },
24
+ }
25
+ );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brookmind-emails",
3
- "version": "0.1.12",
3
+ "version": "0.1.13",
4
4
  "description": "Email templates for Brookmind projects",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -26,7 +26,7 @@
26
26
  "dev": "email dev --dir src/modelfy/templates",
27
27
  "build": "tsc",
28
28
  "prepare": "npm run build",
29
- "lint": "eslint src --ext .ts,.tsx",
29
+ "lint": "eslint src",
30
30
  "format": "prettier --write \"src/**/*.{ts,tsx}\""
31
31
  },
32
32
  "dependencies": {
@@ -40,8 +40,10 @@
40
40
  "@typescript-eslint/eslint-plugin": "^8.18.0",
41
41
  "@typescript-eslint/parser": "^8.18.0",
42
42
  "eslint": "^9.16.0",
43
+ "globals": "^17.1.0",
43
44
  "prettier": "^3.4.2",
44
45
  "react-email": "^3.0.4",
45
- "typescript": "^5.7.2"
46
+ "typescript": "^5.7.2",
47
+ "typescript-eslint": "^8.53.1"
46
48
  }
47
49
  }
@@ -6,12 +6,22 @@ import {
6
6
  } from "./templates/PromoSubscriptionEmail.js";
7
7
  import { GrantCreditsEmail, type GrantCreditsEmailProps } from "./templates/GrantCreditsEmail.js";
8
8
  import { WelcomeEmail, type WelcomeEmailProps } from "./templates/WelcomeEmail.js";
9
+ import {
10
+ FeedbackRequestEmail,
11
+ type FeedbackRequestEmailProps,
12
+ } from "./templates/FeedbackRequestEmail.js";
9
13
 
10
14
  // Re-export types
11
- export type { OtpEmailProps, PromoSubscriptionEmailProps, GrantCreditsEmailProps, WelcomeEmailProps };
15
+ export type {
16
+ OtpEmailProps,
17
+ PromoSubscriptionEmailProps,
18
+ GrantCreditsEmailProps,
19
+ WelcomeEmailProps,
20
+ FeedbackRequestEmailProps,
21
+ };
12
22
 
13
23
  // Re-export components for preview
14
- export { OtpEmail, PromoSubscriptionEmail, GrantCreditsEmail, WelcomeEmail };
24
+ export { OtpEmail, PromoSubscriptionEmail, GrantCreditsEmail, WelcomeEmail, FeedbackRequestEmail };
15
25
 
16
26
  // Render functions
17
27
  export interface RenderResult {
@@ -69,3 +79,16 @@ export async function renderWelcomeEmail(
69
79
  text,
70
80
  };
71
81
  }
82
+
83
+ export async function renderFeedbackRequestEmail(
84
+ props: FeedbackRequestEmailProps
85
+ ): Promise<RenderResult> {
86
+ const html = await render(FeedbackRequestEmail(props));
87
+ const text = await render(FeedbackRequestEmail(props), { plainText: true });
88
+
89
+ return {
90
+ subject: "We'd love your feedback on Modelfy",
91
+ html,
92
+ text,
93
+ };
94
+ }
@@ -0,0 +1,383 @@
1
+ import {
2
+ Html,
3
+ Head,
4
+ Body,
5
+ Container,
6
+ Section,
7
+ Text,
8
+ Link,
9
+ Preview,
10
+ Font,
11
+ Img,
12
+ Button as ReactEmailButton,
13
+ } from "@react-email/components";
14
+ import { modelfyConfig } from "../config.js";
15
+
16
+ export interface FeedbackRequestEmailProps {
17
+ email: string;
18
+ planName: string;
19
+ subscriptionEndDate: string;
20
+ }
21
+
22
+ // Design System Colors - Matching Modelfy Landing
23
+ const colors = {
24
+ background: "#0d0d0d",
25
+ cardBg: "#121212",
26
+ primary: "#0a8f5a",
27
+ white: "#f2f2f2",
28
+ textSecondary: "rgba(242, 242, 242, 0.7)",
29
+ textMuted: "#8c8c8c",
30
+ border: "#262626",
31
+ };
32
+
33
+ const styles = {
34
+ body: {
35
+ margin: 0,
36
+ padding: 0,
37
+ backgroundColor: colors.background,
38
+ fontFamily: "'Inter', 'Segoe UI', Arial, sans-serif",
39
+ },
40
+ container: {
41
+ maxWidth: "600px",
42
+ margin: "0 auto",
43
+ backgroundColor: colors.background,
44
+ },
45
+ header: {
46
+ backgroundColor: colors.background,
47
+ padding: "40px 32px 24px",
48
+ textAlign: "center" as const,
49
+ },
50
+ logo: {
51
+ display: "block" as const,
52
+ margin: "0 auto",
53
+ },
54
+ heroSection: {
55
+ padding: "24px 32px 16px",
56
+ textAlign: "center" as const,
57
+ },
58
+ heroTagline: {
59
+ margin: "0 0 12px",
60
+ fontSize: "13px",
61
+ fontWeight: 600,
62
+ color: colors.primary,
63
+ letterSpacing: "2px",
64
+ textTransform: "uppercase" as const,
65
+ },
66
+ heroHeading: {
67
+ margin: "0 0 16px",
68
+ fontSize: "28px",
69
+ fontWeight: 700,
70
+ color: colors.white,
71
+ lineHeight: "1.3",
72
+ },
73
+ heroSubheading: {
74
+ margin: "0",
75
+ fontSize: "16px",
76
+ lineHeight: "1.6",
77
+ color: colors.textSecondary,
78
+ },
79
+ feedbackSection: {
80
+ backgroundColor: colors.cardBg,
81
+ padding: "32px",
82
+ borderTop: `1px solid ${colors.border}`,
83
+ borderBottom: `1px solid ${colors.border}`,
84
+ },
85
+ feedbackText: {
86
+ margin: "0 0 20px",
87
+ fontSize: "15px",
88
+ lineHeight: "1.6",
89
+ color: colors.textSecondary,
90
+ textAlign: "center" as const,
91
+ },
92
+ feedbackBox: {
93
+ backgroundColor: "rgba(10, 143, 90, 0.1)",
94
+ border: `1px solid rgba(10, 143, 90, 0.3)`,
95
+ borderRadius: "12px",
96
+ padding: "20px 24px",
97
+ marginBottom: "24px",
98
+ },
99
+ feedbackBoxText: {
100
+ margin: 0,
101
+ fontSize: "14px",
102
+ lineHeight: "1.6",
103
+ color: colors.textSecondary,
104
+ textAlign: "center" as const,
105
+ },
106
+ questionsSection: {
107
+ backgroundColor: colors.background,
108
+ padding: "32px",
109
+ },
110
+ questionsSectionTitle: {
111
+ margin: "0 0 20px",
112
+ fontSize: "14px",
113
+ fontWeight: 600,
114
+ color: colors.white,
115
+ textAlign: "center" as const,
116
+ },
117
+ questionsList: {
118
+ margin: "0 auto",
119
+ padding: "0 20px",
120
+ maxWidth: "400px",
121
+ },
122
+ questionItem: {
123
+ margin: "0 0 12px",
124
+ fontSize: "14px",
125
+ lineHeight: "1.5",
126
+ color: colors.textMuted,
127
+ },
128
+ questionBullet: {
129
+ color: colors.primary,
130
+ marginRight: "8px",
131
+ },
132
+ ctaSection: {
133
+ backgroundColor: colors.cardBg,
134
+ padding: "32px",
135
+ textAlign: "center" as const,
136
+ borderTop: `1px solid ${colors.border}`,
137
+ },
138
+ ctaButton: {
139
+ display: "inline-block",
140
+ padding: "14px 36px",
141
+ fontSize: "15px",
142
+ fontWeight: 600,
143
+ textDecoration: "none",
144
+ borderRadius: "10px",
145
+ backgroundColor: colors.primary,
146
+ color: "#ffffff",
147
+ textAlign: "center" as const,
148
+ },
149
+ winbackSection: {
150
+ backgroundColor: colors.background,
151
+ padding: "32px",
152
+ textAlign: "center" as const,
153
+ },
154
+ winbackText: {
155
+ margin: "0 0 8px",
156
+ fontSize: "14px",
157
+ lineHeight: "1.6",
158
+ color: colors.textMuted,
159
+ textAlign: "center" as const,
160
+ },
161
+ winbackLink: {
162
+ color: colors.primary,
163
+ textDecoration: "none",
164
+ fontWeight: 600,
165
+ },
166
+ downloadSection: {
167
+ backgroundColor: colors.cardBg,
168
+ padding: "24px 32px",
169
+ textAlign: "center" as const,
170
+ borderTop: `1px solid ${colors.border}`,
171
+ },
172
+ downloadText: {
173
+ margin: "0 0 16px",
174
+ fontSize: "14px",
175
+ color: colors.white,
176
+ },
177
+ badgeContainer: {
178
+ textAlign: "center" as const,
179
+ },
180
+ badge: {
181
+ display: "inline-block",
182
+ margin: "0 8px",
183
+ },
184
+ footer: {
185
+ backgroundColor: colors.cardBg,
186
+ padding: "24px 32px",
187
+ textAlign: "center" as const,
188
+ borderTop: `1px solid ${colors.border}`,
189
+ },
190
+ footerLogo: {
191
+ display: "block" as const,
192
+ margin: "0 auto 12px",
193
+ opacity: 0.6,
194
+ },
195
+ footerText: {
196
+ margin: "0 0 4px",
197
+ fontSize: "11px",
198
+ color: colors.textMuted,
199
+ lineHeight: "1.6",
200
+ },
201
+ footerLink: {
202
+ color: colors.textMuted,
203
+ textDecoration: "underline",
204
+ },
205
+ };
206
+
207
+ const feedbackReasons = [
208
+ "The pricing didn't fit my budget",
209
+ "I found an alternative solution",
210
+ "The features didn't meet my needs",
211
+ "I experienced technical issues",
212
+ "I didn't use the product enough",
213
+ "Other reasons",
214
+ ];
215
+
216
+ export function FeedbackRequestEmail({
217
+ email = "user@example.com",
218
+ planName = "Pro",
219
+ subscriptionEndDate = "January 1, 2025",
220
+ }: FeedbackRequestEmailProps): React.ReactElement {
221
+ const feedbackMailto = `mailto:${modelfyConfig.supportEmail}?subject=Feedback%20on%20my%20Modelfy%20subscription&body=Hi%20Modelfy%20team%2C%0A%0AI%27m%20sharing%20feedback%20about%20my%20${encodeURIComponent(planName)}%20subscription%20that%20ended%20on%20${encodeURIComponent(subscriptionEndDate)}.%0A%0A`;
222
+
223
+ return (
224
+ <Html lang="en">
225
+ <Head>
226
+ <Font
227
+ fontFamily="Inter"
228
+ fallbackFontFamily="Arial"
229
+ webFont={{
230
+ url: "https://fonts.gstatic.com/s/inter/v13/UcCO3FwrK3iLTeHuS_fvQtMwCp50KnMw2boKoduKmMEVuLyfAZ9hjp-Ek-_0.woff2",
231
+ format: "woff2",
232
+ }}
233
+ fontWeight={400}
234
+ fontStyle="normal"
235
+ />
236
+ <Font
237
+ fontFamily="Inter"
238
+ fallbackFontFamily="Arial"
239
+ webFont={{
240
+ url: "https://fonts.gstatic.com/s/inter/v13/UcCO3FwrK3iLTeHuS_fvQtMwCp50KnMw2boKoduKmMEVuI6fAZ9hjp-Ek-_0.woff2",
241
+ format: "woff2",
242
+ }}
243
+ fontWeight={600}
244
+ fontStyle="normal"
245
+ />
246
+ <Font
247
+ fontFamily="Inter"
248
+ fallbackFontFamily="Arial"
249
+ webFont={{
250
+ url: "https://fonts.gstatic.com/s/inter/v13/UcCO3FwrK3iLTeHuS_fvQtMwCp50KnMw2boKoduKmMEVuFuYAZ9hjp-Ek-_0.woff2",
251
+ format: "woff2",
252
+ }}
253
+ fontWeight={700}
254
+ fontStyle="normal"
255
+ />
256
+ </Head>
257
+ <Preview>We'd love your feedback on Modelfy - Help us improve</Preview>
258
+ <Body style={styles.body}>
259
+ <Container style={styles.container}>
260
+ {/* Header */}
261
+ <Section style={styles.header}>
262
+ <Img
263
+ src={modelfyConfig.logoUrl}
264
+ width="130"
265
+ height="auto"
266
+ alt="Modelfy"
267
+ style={styles.logo}
268
+ />
269
+ </Section>
270
+
271
+ {/* Hero Section */}
272
+ <Section style={styles.heroSection}>
273
+ <Text style={styles.heroTagline}>We'd Love Your Feedback</Text>
274
+ <Text style={styles.heroHeading}>We're Sorry to See You Go</Text>
275
+ <Text style={styles.heroSubheading}>
276
+ Hi <span style={{ color: colors.white, fontWeight: 600 }}>{email}</span>, your{" "}
277
+ <span style={{ color: colors.white, fontWeight: 600 }}>{planName}</span> subscription
278
+ ended on{" "}
279
+ <span style={{ color: colors.white, fontWeight: 600 }}>{subscriptionEndDate}</span>.
280
+ Your feedback would help us improve Modelfy for everyone.
281
+ </Text>
282
+ </Section>
283
+
284
+ {/* Feedback Explanation */}
285
+ <Section style={styles.feedbackSection}>
286
+ <Text style={styles.feedbackText}>
287
+ We'd genuinely appreciate hearing about your experience.
288
+ Just reply to this email or click the button below.
289
+ </Text>
290
+ <div style={styles.feedbackBox}>
291
+ <Text style={styles.feedbackBoxText}>
292
+ Simply hit reply and let us know what we could do better.
293
+ <br />
294
+ All feedback goes directly to our team.
295
+ </Text>
296
+ </div>
297
+ </Section>
298
+
299
+ {/* Quick Questions */}
300
+ <Section style={styles.questionsSection}>
301
+ <Text style={styles.questionsSectionTitle}>
302
+ Some things we'd love to know:
303
+ </Text>
304
+ <div style={styles.questionsList}>
305
+ {feedbackReasons.map((reason, index) => (
306
+ <Text key={index} style={styles.questionItem}>
307
+ <span style={styles.questionBullet}>•</span>
308
+ {reason}
309
+ </Text>
310
+ ))}
311
+ </div>
312
+ </Section>
313
+
314
+ {/* CTA Section */}
315
+ <Section style={styles.ctaSection}>
316
+ <ReactEmailButton href={feedbackMailto} style={styles.ctaButton}>
317
+ Share Your Feedback
318
+ </ReactEmailButton>
319
+ </Section>
320
+
321
+ {/* Win-back Section */}
322
+ <Section style={styles.winbackSection}>
323
+ <Text style={styles.winbackText}>
324
+ Changed your mind? Your account is still here waiting for you.
325
+ </Text>
326
+ <Text style={styles.winbackText}>
327
+ <Link href={modelfyConfig.appUrl} style={styles.winbackLink}>
328
+ Reactivate your subscription
329
+ </Link>
330
+ </Text>
331
+ </Section>
332
+
333
+ {/* Download App Section */}
334
+ <Section style={styles.downloadSection}>
335
+ <Text style={styles.downloadText}>Get the Modelfy app</Text>
336
+ <div style={styles.badgeContainer}>
337
+ <Link href={modelfyConfig.appStoreUrl} style={styles.badge}>
338
+ <Img
339
+ src={modelfyConfig.appStoreBadgeUrl}
340
+ width="120"
341
+ height="40"
342
+ alt="Download on the App Store"
343
+ />
344
+ </Link>
345
+ <Link href={modelfyConfig.playStoreUrl} style={styles.badge}>
346
+ <Img
347
+ src={modelfyConfig.playStoreBadgeUrl}
348
+ width="135"
349
+ height="40"
350
+ alt="Get it on Google Play"
351
+ />
352
+ </Link>
353
+ </div>
354
+ </Section>
355
+
356
+ {/* Footer */}
357
+ <Section style={styles.footer}>
358
+ <Img
359
+ src={modelfyConfig.logoUrl}
360
+ width="80"
361
+ height="auto"
362
+ alt="Modelfy"
363
+ style={styles.footerLogo}
364
+ />
365
+ <Text style={styles.footerText}>
366
+ © {new Date().getFullYear()} {modelfyConfig.companyName}. All rights reserved.
367
+ </Text>
368
+ <Text style={styles.footerText}>
369
+ You received this email because your Modelfy subscription has ended.
370
+ </Text>
371
+ <Text style={styles.footerText}>
372
+ <Link href={`${modelfyConfig.appUrl}/privacy`} style={styles.footerLink}>
373
+ Privacy Policy
374
+ </Link>
375
+ </Text>
376
+ </Section>
377
+ </Container>
378
+ </Body>
379
+ </Html>
380
+ );
381
+ }
382
+
383
+ export default FeedbackRequestEmail;
@@ -274,7 +274,7 @@ const campaignImages = Array.from(
274
274
  { length: 33 },
275
275
  (_, i) => `https://bucket.modelfy.ai/app-media/campaign/campaign${i + 1}.jpeg`
276
276
  );
277
- const campaignProImages = Array.from(
277
+ const _campaignProImages = Array.from(
278
278
  { length: 15 },
279
279
  (_, i) => `https://bucket.modelfy.ai/app-media/campaign-pro/campaign-pro-${i + 1}.jpg`
280
280
  );
@@ -1,61 +0,0 @@
1
- You are performing a code review on the changes in the current branch.
2
-
3
- The current branch is **davidcastillog/foodstudio-templates**, and the target branch is **origin/master**.
4
-
5
- ## Code Review Instructions
6
-
7
- The entire git diff for this branch has been provided below, as well as a list of all commits made to this branch.
8
-
9
- **CRITICAL: EVERYTHING YOU NEED IS ALREADY PROVIDED BELOW.** The complete git diff and full commit history are included in this message.
10
-
11
- **DO NOT run git diff, git log, git status, or ANY other git commands.** All the information you need to perform this review is already here.
12
-
13
- When reviewing the diff:
14
- 1. **Focus on logic and correctness** - Check for bugs, edge cases, and potential issues.
15
- 2. **Consider readability** - Is the code clear and maintainable? Does it follow best practices in this repository?
16
- 3. **Evaluate performance** - Are there obvious performance concerns or optimizations that could be made?
17
- 4. **Assess test coverage** - Does the repository have testing patterns? If so, are there adequate tests for these changes?
18
- 5. **Ask clarifying questions** - Ask the user for clarification if you are unsure about the changes or need more context.
19
- 6. **Don't be overly pedantic** - Nitpicks are fine, but only if they are relevant issues within reason.
20
-
21
- In your output:
22
- - Provide a summary overview of the general code quality.
23
- - Present the identified issues in a table with the columns: index (1, 2, etc.), line number(s), code, issue, and potential solution(s).
24
- - If no issues are found, briefly state that the code meets best practices.
25
-
26
- ## Full Diff
27
-
28
- **REMINDER: DO NOT use any tools to fetch git information.** Simply read the diff and commit history that follow.
29
-
30
- ```
31
- > cd "/Users/davidcastillo/conductor/workspaces/brookmind-emails/hartford" && git diff 0f26955a0c71bc110156d37bac8834a5125a155d
32
- diff --git a/package-lock.json b/package-lock.json
33
- index 7d8e5ff..e0ba651 100644
34
- --- a/package-lock.json
35
- +++ b/package-lock.json
36
- @@ -1,12 +1,12 @@
37
- {
38
- - "name": "@brookmind/emails",
39
- - "version": "0.1.6",
40
- + "name": "brookmind-emails",
41
- + "version": "0.1.11",
42
- "lockfileVersion": 3,
43
- "requires": true,
44
- "packages": {
45
- "": {
46
- - "name": "@brookmind/emails",
47
- - "version": "0.1.3",
48
- + "name": "brookmind-emails",
49
- + "version": "0.1.11",
50
- "dependencies": {
51
- "@react-email/components": "^0.0.31",
52
- "@react-email/render": "^1.0.3",
53
-
54
- ```
55
-
56
- ## Commit History
57
-
58
- ```
59
- > cd "/Users/davidcastillo/conductor/workspaces/brookmind-emails/hartford" && git log origin/master..HEAD
60
- No commits found
61
- ```
package/.context/notes.md DELETED
File without changes
package/.context/todos.md DELETED
File without changes