|
| 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