@codecademy/brand 3.6.0-alpha.6ca7d4c7b6.0 → 3.6.0-alpha.a5f54cdefd.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.
@@ -1,109 +1,44 @@
1
- import { Box, FillButton, FlexBox, StrokeButton, Text } from '@codecademy/gamut';
1
+ import { FillButton, StrokeButton } from '@codecademy/gamut';
2
2
  import { Product } from './types';
3
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
4
- export const getCTATitle = (product, isTrialPlan, isPlusUser) => {
3
+ import { jsx as _jsx } from "react/jsx-runtime";
4
+ const getDefaultCtaConfig = product => {
5
5
  switch (product) {
6
- case Product.Teams:
7
- if (isTrialPlan) {
8
- return 'Try Teams for free';
9
- }
10
- return 'Purchase now';
11
- case Product.Enterprise:
12
- return 'Request a demo';
13
6
  case Product.Basic:
14
- return 'Sign up';
15
- case Product.Pro:
16
- case Product.Gold:
17
- if (isTrialPlan) {
18
- return 'Try Pro for free';
19
- }
20
- if (isPlusUser) return 'Upgrade to Pro';
21
- return 'Upgrade now';
22
- case Product.Silver:
23
- if (isTrialPlan) {
24
- return 'Try Plus for free';
25
- }
26
- if (isPlusUser) return 'Your current plan';
27
- return 'Upgrade now';
28
- default:
29
- return '';
30
- }
31
- };
32
- const getCTAUrl = ({
33
- product,
34
- productDetails,
35
- userHasSilver
36
- }) => {
37
- const planId = productDetails?.planId;
38
- switch (product) {
39
- case Product.Basic:
40
- return '/register';
41
- case Product.Pro:
7
+ return {
8
+ label: 'Get free account',
9
+ href: '/register'
10
+ };
42
11
  case Product.Silver:
12
+ return {
13
+ label: 'Try Plus for free',
14
+ href: '/checkout?plan_id=proSilverAnnualV2_7trial&plan_type=plus'
15
+ };
43
16
  case Product.Gold:
44
- if (userHasSilver) {
45
- return '/account/billing?changePlan=true';
46
- }
47
- return `/checkout?plan_id=${planId || undefined}`;
48
- case Product.Teams:
49
- return '/business/checkout?plan_type=trial';
50
- case Product.Enterprise:
51
- return '/business/teams-quote';
17
+ case Product.Pro:
18
+ return {
19
+ label: 'Try Pro for free',
20
+ href: '/checkout?plan_id=proGoldAnnualV2_7trial&plan_type=pro'
21
+ };
52
22
  default:
53
- return '';
23
+ return {
24
+ label: 'Try Pro for free',
25
+ href: '/checkout'
26
+ };
54
27
  }
55
28
  };
56
29
  export const ProductCTA = ({
57
30
  product,
58
- productDetails,
59
- user,
60
- simple
31
+ ctaConfig,
32
+ isCurrentPlan
61
33
  }) => {
62
- const userHasBasic = !user.anonymous && (!user.proSilver || !user.proGold);
63
- const userHasSilver = user.proSilver;
64
- const isTeams = product === Product.Teams;
65
- const shouldHideCTA = () => {
66
- if (isTeams) return false;
67
- if ((userHasBasic || userHasSilver) && product === Product.Basic) {
68
- return true;
69
- }
70
- if (userHasSilver) return false;
71
- if (user.proGold) return true;
72
- return false;
73
- };
74
- const hideCTAButton = shouldHideCTA();
75
- const ButtonComponent = product === Product.Gold || product === Product.Pro || product === Product.Teams ? FillButton : StrokeButton;
76
- return hideCTAButton ? null : /*#__PURE__*/_jsxs(Box, {
77
- pt: 0,
78
- display: "flex",
79
- justifyContent: "center",
80
- alignItems: "center",
81
- flexDirection: isTeams && !simple ? 'row' : 'column',
82
- gap: 8,
83
- children: [/*#__PURE__*/_jsx(ButtonComponent, {
84
- mt: 8,
85
- disabled: product === Product.Silver && userHasSilver,
86
- href: getCTAUrl({
87
- productDetails,
88
- product,
89
- userHasSilver
90
- }),
91
- children: /*#__PURE__*/_jsx(Text, {
92
- as: "span",
93
- children: getCTATitle(product, !!productDetails?.isTrialPlan, userHasSilver)
94
- })
95
- }), isTeams ? /*#__PURE__*/_jsxs(_Fragment, {
96
- children: [simple ? null : /*#__PURE__*/_jsx(Text, {
97
- as: "span",
98
- mt: 8,
99
- children: ` or `
100
- }), /*#__PURE__*/_jsx(FlexBox, {
101
- mt: 8,
102
- children: /*#__PURE__*/_jsx(StrokeButton, {
103
- href: "/business/checkout?plan_type=directpurchase",
104
- children: "Purchase now"
105
- })
106
- })]
107
- }) : null]
34
+ const defaultCtaConfig = getDefaultCtaConfig(product);
35
+ const ButtonComponent = product === Product.Gold || product === Product.Pro ? FillButton : StrokeButton;
36
+ return /*#__PURE__*/_jsx(ButtonComponent, {
37
+ mt: 8,
38
+ href: ctaConfig?.href || defaultCtaConfig.href,
39
+ onClick: ctaConfig?.onClick,
40
+ mx: 24,
41
+ disabled: isCurrentPlan,
42
+ children: isCurrentPlan && 'Your current plan' || ctaConfig?.label || defaultCtaConfig.label
108
43
  });
109
44
  };
@@ -1,11 +1,11 @@
1
1
  import { PlansByType } from './config';
2
- import { AllCurrency } from './PricingCard/types';
3
- import { User } from './types';
2
+ import { CopyConfig, CtaConfig, User } from './types';
4
3
  interface ProductsProps {
5
4
  plansByType: PlansByType;
6
- user: User;
5
+ ctaConfig?: CtaConfig;
6
+ copyConfig?: CopyConfig;
7
+ user?: User;
7
8
  isUserInIndia: boolean;
8
- currency: AllCurrency;
9
9
  }
10
10
  export declare const Products: React.FC<ProductsProps>;
11
11
  export {};
@@ -5,9 +5,10 @@ import { Product } from './types';
5
5
  import { jsx as _jsx } from "react/jsx-runtime";
6
6
  export const Products = ({
7
7
  plansByType,
8
+ ctaConfig,
9
+ copyConfig,
8
10
  user,
9
- isUserInIndia,
10
- currency
11
+ isUserInIndia
11
12
  }) => {
12
13
  const productsLength = Object.keys(plansByType).length;
13
14
  const renderCard = product => {
@@ -17,45 +18,30 @@ export const Products = ({
17
18
  product: Product.Basic,
18
19
  productDetails: getProductDetails(plansByType, product),
19
20
  plansByType: plansByType,
21
+ isCurrentPlan: user && !user.anonymous && !user.proSilver && !user.proGold,
20
22
  isUserInIndia: isUserInIndia,
21
- currency: currency,
22
- user: user
23
+ ctaConfig: ctaConfig?.basic,
24
+ copyConfig: copyConfig?.basic
23
25
  }, product);
24
26
  case Product.Silver:
25
27
  return /*#__PURE__*/_jsx(PricingCard, {
26
28
  product: Product.Silver,
27
29
  productDetails: getProductDetails(plansByType, product),
28
30
  plansByType: plansByType,
31
+ isCurrentPlan: user?.proSilver,
29
32
  isUserInIndia: isUserInIndia,
30
- currency: currency,
31
- user: user
33
+ ctaConfig: ctaConfig?.['pro-silver'],
34
+ copyConfig: copyConfig?.['pro-silver']
32
35
  }, product);
33
36
  case Product.Gold:
34
37
  return /*#__PURE__*/_jsx(PricingCard, {
35
38
  product: Product.Gold,
36
39
  productDetails: getProductDetails(plansByType, product),
37
40
  plansByType: plansByType,
41
+ isCurrentPlan: user?.proGold,
38
42
  isUserInIndia: isUserInIndia,
39
- currency: currency,
40
- user: user
41
- }, product);
42
- case Product.Teams:
43
- return /*#__PURE__*/_jsx(PricingCard, {
44
- product: Product.Teams,
45
- productDetails: getProductDetails(plansByType, product),
46
- plansByType: plansByType,
47
- isUserInIndia: isUserInIndia,
48
- currency: currency,
49
- user: user
50
- }, product);
51
- case Product.Enterprise:
52
- return /*#__PURE__*/_jsx(PricingCard, {
53
- product: Product.Enterprise,
54
- productDetails: getProductDetails(plansByType, product),
55
- plansByType: plansByType,
56
- isUserInIndia: isUserInIndia,
57
- currency: currency,
58
- user: user
43
+ ctaConfig: ctaConfig?.['pro-gold'],
44
+ copyConfig: copyConfig?.['pro-gold']
59
45
  }, product);
60
46
  default:
61
47
  return null;
@@ -63,8 +49,7 @@ export const Products = ({
63
49
  };
64
50
  return /*#__PURE__*/_jsx(GridBox, {
65
51
  as: "ul",
66
- pt: 16,
67
- pb: 24,
52
+ pt: 24,
68
53
  px: 0,
69
54
  mb: 0,
70
55
  center: true,
@@ -2,7 +2,7 @@ import { Product, ProductDetails } from './types';
2
2
  type Period = 'annual' | 'monthly';
3
3
  type PlansByPeriod = Record<Period, ProductDetails>;
4
4
  export type PlansByType = Record<Product, PlansByPeriod>;
5
- export declare const getProductDescription: (product: Product) => "Start learning something new with basic access" | "Train your team with in-demand skills and flexible admin tools" | "Customizable training for your organization" | "Build in-demand technical skills for work or a personal project" | "Develop the skills and experience to land a job in tech";
5
+ export declare const getProductDescription: (product: Product) => "" | "Start learning something new with basic access" | "Build in-demand technical skills for work or a personal project" | "Develop the skills and experience to land a job in tech";
6
6
  export declare const getProductDetails: (products: PlansByType, product: Product) => ProductDetails;
7
7
  export declare const groupPlansByType: (products: ProductDetails[]) => PlansByType;
8
8
  export {};
@@ -4,9 +4,7 @@ export const getProductDescription = product => {
4
4
  case Product.Basic:
5
5
  return 'Start learning something new with basic access';
6
6
  case Product.Teams:
7
- return 'Train your team with in-demand skills and flexible admin tools';
8
- case Product.Enterprise:
9
- return 'Customizable training for your organization';
7
+ return '';
10
8
  case Product.Pro:
11
9
  case Product.Silver:
12
10
  return 'Build in-demand technical skills for work or a personal project';
@@ -15,7 +13,7 @@ export const getProductDescription = product => {
15
13
  }
16
14
  };
17
15
  export const getProductDetails = (products, product) => {
18
- if (product === Product.Basic) {
16
+ if (product === Product.Basic || product === Product.Teams) {
19
17
  return products[product].monthly;
20
18
  }
21
19
  return products[product].annual;
@@ -1,12 +1,13 @@
1
1
  import { Background } from '@codecademy/gamut-styles';
2
- import { AllCurrency } from './PricingCard/types';
3
- import type { ProductDetails, User } from './types';
2
+ import type { CopyConfig, CtaConfig, PlanType, ProductDetails, User } from './types';
4
3
  type PricingSectionProps = Omit<Partial<React.ComponentProps<typeof Background>>, 'title'> & {
5
4
  products: ProductDetails[];
6
5
  title?: React.ReactNode;
7
- user: User;
6
+ planTypes?: PlanType[];
7
+ ctaConfig?: CtaConfig;
8
+ copyConfig?: CopyConfig;
9
+ user?: User;
8
10
  isUserInIndia: boolean;
9
- currency: AllCurrency;
10
11
  };
11
12
  export declare const PricingSection: React.FC<PricingSectionProps>;
12
13
  export {};
@@ -1,39 +1,58 @@
1
- import { Box, ContentContainer, Text } from '@codecademy/gamut';
2
- import { CheckerLoose } from '@codecademy/gamut-patterns';
1
+ import { Box } from '@codecademy/gamut';
2
+ import { Background } from '@codecademy/gamut-styles';
3
3
  import { groupPlansByType } from './config';
4
4
  import { Products } from './Products';
5
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
5
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
6
6
  export const PricingSection = ({
7
7
  products,
8
8
  title,
9
+ planTypes,
10
+ ctaConfig,
11
+ copyConfig,
9
12
  user,
10
13
  isUserInIndia,
11
- currency
14
+ ...bgProps
12
15
  }) => {
13
- return /*#__PURE__*/_jsxs(_Fragment, {
14
- children: [/*#__PURE__*/_jsx(Text, {
15
- as: "h1",
16
- textAlign: "center",
17
- fontSize: {
18
- _: 34,
19
- md: 44
16
+ const filteredProducts = products.filter(product => {
17
+ // if planTypes prop is provided, only show products that match the specified planTypes
18
+ const isValidPlanType = planTypes ? planTypes.includes(product.planType) : product.planType !== 'teams';
19
+
20
+ // if user prop is provided, only show upsell cards (and the user's current plan for comparison)
21
+ if (user) {
22
+ if (product.planType === 'basic') {
23
+ return isValidPlanType && !user.proSilver && !user.proGold;
24
+ }
25
+ if (product.planType === 'pro-silver') {
26
+ return isValidPlanType && !user.proGold;
27
+ }
28
+ }
29
+ return isValidPlanType;
30
+ });
31
+ return /*#__PURE__*/_jsx(Background, {
32
+ bg: "beige",
33
+ px: {
34
+ _: 16,
35
+ md: 32,
36
+ lg: 64,
37
+ xl: 96
38
+ },
39
+ mb: 48,
40
+ ...bgProps,
41
+ children: /*#__PURE__*/_jsxs(Box, {
42
+ maxWidth: 1440,
43
+ px: {
44
+ _: 32,
45
+ lg: 64,
46
+ xl: 96
20
47
  },
21
- width: "100%",
22
- mb: 16,
23
- children: title
24
- }), /*#__PURE__*/_jsxs(Box, {
25
- position: "relative",
26
- children: [/*#__PURE__*/_jsx(CheckerLoose, {
27
- height: "100%",
28
- position: "absolute"
29
- }), /*#__PURE__*/_jsx(ContentContainer, {
30
- children: /*#__PURE__*/_jsx(Products, {
31
- plansByType: groupPlansByType(products),
32
- user: user,
33
- isUserInIndia: isUserInIndia,
34
- currency: currency
35
- })
48
+ mx: "auto",
49
+ children: [title, /*#__PURE__*/_jsx(Products, {
50
+ plansByType: groupPlansByType(filteredProducts),
51
+ ctaConfig: ctaConfig,
52
+ copyConfig: copyConfig,
53
+ user: user,
54
+ isUserInIndia: isUserInIndia
36
55
  })]
37
- })]
56
+ })
38
57
  });
39
58
  };
@@ -3,8 +3,7 @@ export declare enum Product {
3
3
  Pro = "pro",
4
4
  Teams = "teams",
5
5
  Silver = "pro-silver",
6
- Gold = "pro-gold",
7
- Enterprise = "enterprise"
6
+ Gold = "pro-gold"
8
7
  }
9
8
  export interface ProductDetails {
10
9
  isTrialPlan?: boolean | null | undefined;
@@ -15,7 +14,7 @@ export interface ProductDetails {
15
14
  savings?: string | null | undefined;
16
15
  includedTaxRate?: string | null | undefined;
17
16
  }
18
- export type PlanType = 'basic' | 'pro-silver' | 'pro-gold' | 'teams' | 'enterprise';
17
+ export type PlanType = 'basic' | 'pro-silver' | 'pro-gold' | 'teams';
19
18
  export type CtaConfig = Partial<Record<PlanType, {
20
19
  label?: string;
21
20
  href?: string;
@@ -4,6 +4,5 @@ export let Product = /*#__PURE__*/function (Product) {
4
4
  Product["Teams"] = "teams";
5
5
  Product["Silver"] = "pro-silver";
6
6
  Product["Gold"] = "pro-gold";
7
- Product["Enterprise"] = "enterprise";
8
7
  return Product;
9
8
  }({});
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@codecademy/brand",
3
3
  "description": "Brand component library for Codecademy",
4
- "version": "3.6.0-alpha.6ca7d4c7b6.0",
4
+ "version": "3.6.0-alpha.a5f54cdefd.0",
5
5
  "author": "Codecademy Engineering <dev@codecademy.com>",
6
6
  "dependencies": {
7
7
  "@emotion/is-prop-valid": "^1.2.1",
@@ -1,4 +0,0 @@
1
- export declare const ConditionalColorMode: React.FC<{
2
- condition: boolean;
3
- children: React.ReactNode;
4
- }>;
@@ -1,13 +0,0 @@
1
- import { ColorMode } from '@codecademy/gamut-styles';
2
- import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
3
- export const ConditionalColorMode = ({
4
- condition,
5
- children
6
- }) => {
7
- return condition ? /*#__PURE__*/_jsx(ColorMode, {
8
- mode: "dark",
9
- children: children
10
- }) : /*#__PURE__*/_jsx(_Fragment, {
11
- children: children
12
- });
13
- };
@@ -1,40 +0,0 @@
1
- export declare enum Currency {
2
- Aud = "AUD",
3
- Cad = "CAD",
4
- Eur = "EUR",
5
- Gbp = "GBP",
6
- Inr = "INR",
7
- Nok = "NOK",
8
- Pln = "PLN",
9
- Sek = "SEK",
10
- Usd = "USD"
11
- }
12
- export declare enum CFBCurrency {
13
- Dkk = "DKK",
14
- Brl = "BRL",
15
- Bgn = "BGN",
16
- Chf = "CHF",
17
- Czk = "CZK",
18
- Hrk = "HRK",
19
- Huf = "HUF",
20
- Mxn = "MXN",
21
- Ron = "RON"
22
- }
23
- export declare enum CurrencySymbol {
24
- Dollar = "$",
25
- Pound = "\u00A3",
26
- Euro = "\u20AC",
27
- Rupee = "\u20B9",
28
- Krona = "kr",
29
- Krone = "kr",
30
- ZLOTY = "z\u0142",
31
- Lev = "\u041B\u0432.",
32
- Real = "R$",
33
- Franc = "Fr.",
34
- Koruna = "K\u010D",
35
- Kuna = "kn",
36
- Forint = "Ft",
37
- Leu = "lei"
38
- }
39
- export type AllCurrency = Currency | CFBCurrency;
40
- export declare const getCurrencySymbol: (currency: AllCurrency) => CurrencySymbol;
@@ -1,63 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-duplicate-enum-values */
2
- export let Currency = /*#__PURE__*/function (Currency) {
3
- Currency["Aud"] = "AUD";
4
- Currency["Cad"] = "CAD";
5
- Currency["Eur"] = "EUR";
6
- Currency["Gbp"] = "GBP";
7
- Currency["Inr"] = "INR";
8
- Currency["Nok"] = "NOK";
9
- Currency["Pln"] = "PLN";
10
- Currency["Sek"] = "SEK";
11
- Currency["Usd"] = "USD";
12
- return Currency;
13
- }({});
14
- export let CFBCurrency = /*#__PURE__*/function (CFBCurrency) {
15
- CFBCurrency["Dkk"] = "DKK";
16
- CFBCurrency["Brl"] = "BRL";
17
- CFBCurrency["Bgn"] = "BGN";
18
- CFBCurrency["Chf"] = "CHF";
19
- CFBCurrency["Czk"] = "CZK";
20
- CFBCurrency["Hrk"] = "HRK";
21
- CFBCurrency["Huf"] = "HUF";
22
- CFBCurrency["Mxn"] = "MXN";
23
- CFBCurrency["Ron"] = "RON";
24
- return CFBCurrency;
25
- }({});
26
- export let CurrencySymbol = /*#__PURE__*/function (CurrencySymbol) {
27
- CurrencySymbol["Dollar"] = "$";
28
- CurrencySymbol["Pound"] = "\xA3";
29
- CurrencySymbol["Euro"] = "\u20AC";
30
- CurrencySymbol["Rupee"] = "\u20B9";
31
- CurrencySymbol["Krona"] = "kr";
32
- CurrencySymbol["Krone"] = "kr";
33
- CurrencySymbol["ZLOTY"] = "z\u0142";
34
- CurrencySymbol["Lev"] = "\u041B\u0432.";
35
- CurrencySymbol["Real"] = "R$";
36
- CurrencySymbol["Franc"] = "Fr.";
37
- CurrencySymbol["Koruna"] = "K\u010D";
38
- CurrencySymbol["Kuna"] = "kn";
39
- CurrencySymbol["Forint"] = "Ft";
40
- CurrencySymbol["Leu"] = "lei";
41
- return CurrencySymbol;
42
- }({});
43
- const currencySymbols = {
44
- [Currency.Usd]: CurrencySymbol.Dollar,
45
- [Currency.Cad]: CurrencySymbol.Dollar,
46
- [Currency.Aud]: CurrencySymbol.Dollar,
47
- [Currency.Eur]: CurrencySymbol.Euro,
48
- [Currency.Gbp]: CurrencySymbol.Pound,
49
- [Currency.Inr]: CurrencySymbol.Rupee,
50
- [Currency.Sek]: CurrencySymbol.Krona,
51
- [Currency.Nok]: CurrencySymbol.Krone,
52
- [Currency.Pln]: CurrencySymbol.ZLOTY,
53
- [CFBCurrency.Bgn]: CurrencySymbol.Lev,
54
- [CFBCurrency.Brl]: CurrencySymbol.Real,
55
- [CFBCurrency.Chf]: CurrencySymbol.Franc,
56
- [CFBCurrency.Czk]: CurrencySymbol.Koruna,
57
- [CFBCurrency.Dkk]: CurrencySymbol.Krone,
58
- [CFBCurrency.Hrk]: CurrencySymbol.Kuna,
59
- [CFBCurrency.Huf]: CurrencySymbol.Forint,
60
- [CFBCurrency.Mxn]: CurrencySymbol.Dollar,
61
- [CFBCurrency.Ron]: CurrencySymbol.Leu
62
- };
63
- export const getCurrencySymbol = currency => currencySymbols[currency];