Skip to content

Commit b4058ce

Browse files
committed
fix: pass bcc/cc to all email providers
bcc and cc were normalized in send() but never included in the transport-specific mailOptions or API payloads, so transporter.sendMail() and all API calls silently dropped them. Fixes all 6 providers: SMTP, Gmail OAuth, Microsoft OAuth, Yahoo OAuth, SendGrid (refactored to proper personalizations format), and Mailgun. Closes Trustpilot BCC invite delivery issue.
1 parent e51d65e commit b4058ce

25 files changed

Lines changed: 37516 additions & 7 deletions

dist/_chunks/App-CaT5U0uM.mjs

Lines changed: 8613 additions & 0 deletions
Large diffs are not rendered by default.

dist/_chunks/App-DGGqHEPC.js

Lines changed: 8634 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 352 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,352 @@
1+
import { jsx, jsxs } from "react/jsx-runtime";
2+
import { useState, useEffect } from "react";
3+
import { useFetchClient, useNotification } from "@strapi/strapi/admin";
4+
import styled from "styled-components";
5+
import { Flex, Typography, Box, Badge, Button } from "@strapi/design-system";
6+
import { Check, Cross, Sparkle, Lightning, Rocket } from "@strapi/icons";
7+
const Container = styled(Box)`
8+
padding: 32px;
9+
max-width: 1400px;
10+
margin: 0 auto;
11+
`;
12+
const Header = styled(Box)`
13+
text-align: center;
14+
margin-bottom: 48px;
15+
display: flex;
16+
flex-direction: column;
17+
align-items: center;
18+
gap: 8px;
19+
`;
20+
const Title = styled(Typography)`
21+
font-size: 2.5rem;
22+
font-weight: 700;
23+
margin-bottom: 8px;
24+
background: linear-gradient(135deg, #4945ff, #7c3aed);
25+
-webkit-background-clip: text;
26+
-webkit-text-fill-color: transparent;
27+
display: block;
28+
`;
29+
const Subtitle = styled(Typography)`
30+
font-size: 1.125rem;
31+
color: ${(p) => p.theme.colors.neutral600};
32+
line-height: 1.6;
33+
display: block;
34+
`;
35+
const TierGrid = styled(Flex)`
36+
gap: 32px;
37+
margin: 0 auto 48px;
38+
max-width: 1080px;
39+
justify-content: center;
40+
flex-wrap: wrap;
41+
align-items: stretch;
42+
`;
43+
const TierWrapper = styled(Box)`
44+
flex: 1;
45+
min-width: 280px;
46+
max-width: 340px;
47+
display: flex;
48+
`;
49+
const TierCard = styled(Box)`
50+
background: ${(p) => p.theme.colors.neutral0};
51+
border-radius: 16px;
52+
padding: 32px;
53+
border: 2px solid ${(props) => props.$featured ? "#4945ff" : props.theme.colors.neutral200};
54+
position: relative;
55+
transition: all 0.3s ease;
56+
box-shadow: ${(props) => props.$featured ? "0 20px 25px -5px rgba(73, 69, 255, 0.25), 0 8px 10px -6px rgba(73, 69, 255, 0.2)" : "0 10px 15px -3px rgba(0, 0, 0, 0.08), 0 4px 6px -4px rgba(0, 0, 0, 0.05)"};
57+
display: flex;
58+
flex-direction: column;
59+
width: 100%;
60+
61+
&:hover {
62+
transform: translateY(-4px);
63+
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.15), 0 8px 10px -6px rgba(0, 0, 0, 0.1);
64+
}
65+
`;
66+
const PopularBadge = styled(Badge)`
67+
position: absolute;
68+
top: -12px;
69+
right: 24px;
70+
background: linear-gradient(135deg, #4945ff, #4338ca);
71+
color: white;
72+
padding: 4px 16px;
73+
font-size: 12px;
74+
font-weight: 600;
75+
`;
76+
const TierIcon = styled(Box)`
77+
width: 48px;
78+
height: 48px;
79+
border-radius: 12px;
80+
display: flex;
81+
align-items: center;
82+
justify-content: center;
83+
margin-bottom: 16px;
84+
background: ${(props) => props.$color};
85+
86+
svg {
87+
width: 28px;
88+
height: 28px;
89+
color: white;
90+
}
91+
`;
92+
const TierName = styled(Typography)`
93+
font-size: 1.5rem;
94+
font-weight: 700;
95+
margin-bottom: 8px;
96+
color: ${(p) => p.theme.colors.neutral800};
97+
`;
98+
const TierPrice = styled(Typography)`
99+
font-size: 2rem;
100+
font-weight: 800;
101+
margin-bottom: 4px;
102+
color: ${(p) => p.theme.colors.neutral800};
103+
`;
104+
const TierDescription = styled(Typography)`
105+
color: ${(p) => p.theme.colors.neutral600};
106+
margin-bottom: 24px;
107+
`;
108+
const LimitsBox = styled(Box)`
109+
background: ${(p) => p.theme.colors.neutral100};
110+
border-radius: 8px;
111+
padding: 12px;
112+
margin-bottom: 20px;
113+
`;
114+
const LimitText = styled(Typography)`
115+
font-size: 13px;
116+
color: ${(p) => p.theme.colors.neutral800};
117+
`;
118+
const PeriodText = styled(Typography)`
119+
color: ${(p) => p.theme.colors.neutral600};
120+
`;
121+
const FeatureText = styled(Typography)`
122+
font-size: 14px;
123+
color: ${(p) => p.$included ? p.theme.colors.neutral800 : p.theme.colors.neutral500};
124+
text-decoration: ${(p) => p.$included ? "none" : "line-through"};
125+
`;
126+
const FeatureList = styled(Box)`
127+
margin-bottom: 24px;
128+
flex: 1;
129+
`;
130+
const Feature = styled(Flex)`
131+
gap: 12px;
132+
margin-bottom: 12px;
133+
align-items: flex-start;
134+
`;
135+
const FeatureIcon = styled(Box)`
136+
width: 20px;
137+
height: 20px;
138+
border-radius: 50%;
139+
display: flex;
140+
align-items: center;
141+
justify-content: center;
142+
flex-shrink: 0;
143+
margin-top: 2px;
144+
145+
${(props) => props.$included ? `
146+
background: rgba(34, 197, 94, 0.15);
147+
svg { color: #16A34A; }
148+
` : `
149+
background: rgba(220, 38, 38, 0.12);
150+
svg { color: #DC2626; }
151+
`}
152+
`;
153+
const UpgradeButton = styled(Button)`
154+
width: 100%;
155+
height: 48px;
156+
font-weight: 600;
157+
font-size: 15px;
158+
background: ${(props) => props.$gradient};
159+
border: none;
160+
color: white;
161+
162+
&:hover {
163+
transform: translateY(-2px);
164+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
165+
}
166+
`;
167+
const CurrentPlanBadge = styled(Badge)`
168+
width: 100%;
169+
height: 48px;
170+
display: flex;
171+
align-items: center;
172+
justify-content: center;
173+
background: ${(p) => p.theme.colors.neutral100};
174+
color: ${(p) => p.theme.colors.neutral600};
175+
font-weight: 600;
176+
font-size: 15px;
177+
`;
178+
const LicensePage = () => {
179+
const { get, post } = useFetchClient();
180+
const { toggleNotification } = useNotification();
181+
const [currentTier, setCurrentTier] = useState("free");
182+
const [limits, setLimits] = useState(null);
183+
const [loading, setLoading] = useState(true);
184+
useEffect(() => {
185+
fetchLicenseInfo();
186+
}, []);
187+
const fetchLicenseInfo = async () => {
188+
try {
189+
const response = await get("/magic-mail/license/limits");
190+
const licenseData = response.data || {};
191+
let tier = "free";
192+
if (licenseData.tier) {
193+
tier = licenseData.tier;
194+
}
195+
setCurrentTier(tier);
196+
setLimits(licenseData.limits);
197+
setLoading(false);
198+
} catch (error) {
199+
console.error("Failed to fetch license info:", error);
200+
setLoading(false);
201+
}
202+
};
203+
const getTierRank = (tierId) => {
204+
const ranks = {
205+
"free": 0,
206+
"premium": 1,
207+
"advanced": 2,
208+
"enterprise": 3
209+
};
210+
return ranks[tierId] || 0;
211+
};
212+
const getButtonText = (tierId) => {
213+
const currentRank = getTierRank(currentTier);
214+
const targetRank = getTierRank(tierId);
215+
if (currentRank === targetRank) {
216+
return "Current Plan";
217+
} else if (targetRank > currentRank) {
218+
return "Upgrade Now";
219+
} else {
220+
return "Downgrade";
221+
}
222+
};
223+
const tiers = [
224+
{
225+
id: "free",
226+
name: "FREE",
227+
price: "$0",
228+
period: "forever",
229+
description: "Perfect for small projects and testing",
230+
icon: /* @__PURE__ */ jsx(Sparkle, {}),
231+
color: "linear-gradient(135deg, #6B7280, #4B5563)",
232+
features: [
233+
{ name: "25 Email Templates", included: true },
234+
{ name: "3 Email Accounts", included: true },
235+
{ name: "5 Routing Rules", included: true },
236+
{ name: "All OAuth Providers", included: true },
237+
{ name: "Import/Export Templates", included: true },
238+
{ name: "Template Versioning", included: false },
239+
{ name: "Analytics Dashboard", included: false },
240+
{ name: "Priority Support", included: false }
241+
],
242+
limits: {
243+
templates: "25",
244+
accounts: "3",
245+
rules: "5"
246+
}
247+
},
248+
{
249+
id: "premium",
250+
name: "PREMIUM",
251+
price: "$14.50",
252+
period: "/month",
253+
description: "Enhanced features for growing teams",
254+
icon: /* @__PURE__ */ jsx(Lightning, {}),
255+
color: "linear-gradient(135deg, #8B5CF6, #7C3AED)",
256+
featured: true,
257+
features: [
258+
{ name: "100 Email Templates", included: true },
259+
{ name: "10 Email Accounts", included: true },
260+
{ name: "20 Routing Rules", included: true },
261+
{ name: "All OAuth Providers", included: true },
262+
{ name: "Import/Export Templates", included: true },
263+
{ name: "Template Versioning", included: true },
264+
{ name: "Basic Analytics", included: true },
265+
{ name: "Priority Support", included: true }
266+
],
267+
limits: {
268+
templates: "100",
269+
accounts: "10",
270+
rules: "20"
271+
}
272+
},
273+
{
274+
id: "advanced",
275+
name: "ADVANCED",
276+
price: "$39.50",
277+
period: "/month",
278+
description: "Maximum features for power users",
279+
icon: /* @__PURE__ */ jsx(Rocket, {}),
280+
color: "linear-gradient(135deg, #0EA5E9, #0284C7)",
281+
features: [
282+
{ name: "500 Email Templates", included: true },
283+
{ name: "Unlimited Accounts", included: true },
284+
{ name: "Unlimited Routing Rules", included: true },
285+
{ name: "All OAuth Providers", included: true },
286+
{ name: "Import/Export Templates", included: true },
287+
{ name: "Template Versioning", included: true },
288+
{ name: "Advanced Analytics & Tracking", included: true },
289+
{ name: "SendGrid & Mailgun APIs", included: true }
290+
],
291+
limits: {
292+
templates: "500",
293+
accounts: "Unlimited",
294+
rules: "Unlimited"
295+
}
296+
}
297+
];
298+
const handleUpgrade = (tierId) => {
299+
window.open("https://store.magicdx.dev/", "_blank");
300+
};
301+
if (loading) {
302+
return /* @__PURE__ */ jsx(Container, { children: /* @__PURE__ */ jsx(Flex, { justifyContent: "center", alignItems: "center", style: { minHeight: "400px" }, children: /* @__PURE__ */ jsx(Typography, { children: "Loading license information..." }) }) });
303+
}
304+
return /* @__PURE__ */ jsxs(Container, { children: [
305+
/* @__PURE__ */ jsxs(Header, { children: [
306+
/* @__PURE__ */ jsx(Title, { variant: "alpha", children: "Choose Your Plan" }),
307+
/* @__PURE__ */ jsx(Subtitle, { variant: "omega", children: "Unlock powerful email management features for your Strapi application" })
308+
] }),
309+
/* @__PURE__ */ jsx(TierGrid, { children: tiers.map((tier) => /* @__PURE__ */ jsx(TierWrapper, { children: /* @__PURE__ */ jsxs(TierCard, { $featured: tier.featured, children: [
310+
tier.featured && /* @__PURE__ */ jsx(PopularBadge, { children: "MOST POPULAR" }),
311+
/* @__PURE__ */ jsx(TierIcon, { $color: tier.color, children: tier.icon }),
312+
/* @__PURE__ */ jsx(TierName, { variant: "beta", children: tier.name }),
313+
/* @__PURE__ */ jsxs(Flex, { alignItems: "baseline", gap: 1, children: [
314+
/* @__PURE__ */ jsx(TierPrice, { variant: "alpha", children: tier.price }),
315+
/* @__PURE__ */ jsx(PeriodText, { variant: "omega", children: tier.period })
316+
] }),
317+
/* @__PURE__ */ jsx(TierDescription, { variant: "omega", children: tier.description }),
318+
/* @__PURE__ */ jsx(LimitsBox, { children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, children: [
319+
/* @__PURE__ */ jsxs(LimitText, { variant: "pi", children: [
320+
/* @__PURE__ */ jsx("strong", { children: "Templates:" }),
321+
" ",
322+
tier.limits.templates
323+
] }),
324+
/* @__PURE__ */ jsxs(LimitText, { variant: "pi", children: [
325+
/* @__PURE__ */ jsx("strong", { children: "Accounts:" }),
326+
" ",
327+
tier.limits.accounts
328+
] }),
329+
/* @__PURE__ */ jsxs(LimitText, { variant: "pi", children: [
330+
/* @__PURE__ */ jsx("strong", { children: "Routing Rules:" }),
331+
" ",
332+
tier.limits.rules
333+
] })
334+
] }) }),
335+
/* @__PURE__ */ jsx(FeatureList, { children: tier.features.map((feature, index) => /* @__PURE__ */ jsxs(Feature, { children: [
336+
/* @__PURE__ */ jsx(FeatureIcon, { $included: feature.included, children: feature.included ? /* @__PURE__ */ jsx(Check, { style: { width: 14, height: 14 } }) : /* @__PURE__ */ jsx(Cross, { style: { width: 14, height: 14 } }) }),
337+
/* @__PURE__ */ jsx(FeatureText, { variant: "omega", $included: feature.included, children: feature.name })
338+
] }, index)) }),
339+
currentTier === tier.id ? /* @__PURE__ */ jsx(CurrentPlanBadge, { children: "Current Plan" }) : /* @__PURE__ */ jsx(
340+
UpgradeButton,
341+
{
342+
$gradient: tier.color,
343+
onClick: () => handleUpgrade(tier.id),
344+
children: getButtonText(tier.id)
345+
}
346+
)
347+
] }) }, tier.id)) })
348+
] });
349+
};
350+
export {
351+
LicensePage as default
352+
};

0 commit comments

Comments
 (0)