@codecademy/brand 3.6.0-alpha.eb8166002f.0 → 3.6.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,6 +1,8 @@
1
- import { CtaConfig, Product } from './types';
2
- export declare const ProductCTA: ({ product, ctaConfig, isCurrentPlan, }: {
1
+ import { Product, ProductDetails, User } from './types';
2
+ export declare const getCTATitle: (product: Product, isTrialPlan: boolean, isPlusUser?: boolean) => "" | "Request a demo" | "Try Teams for free" | "Purchase now" | "Sign up" | "Try Pro for free" | "Upgrade to Pro" | "Upgrade now" | "Try Plus for free" | "Your current plan";
3
+ export declare const ProductCTA: ({ product, productDetails, user, simple, }: {
3
4
  product: Product;
4
- ctaConfig: CtaConfig[keyof CtaConfig];
5
- isCurrentPlan?: boolean;
6
- }) => import("react/jsx-runtime").JSX.Element;
5
+ productDetails: ProductDetails;
6
+ user: User;
7
+ simple?: boolean;
8
+ }) => import("react/jsx-runtime").JSX.Element | null;
@@ -1,44 +1,109 @@
1
- import { FillButton, StrokeButton } from '@codecademy/gamut';
1
+ import { Box, FillButton, FlexBox, StrokeButton, Text } from '@codecademy/gamut';
2
2
  import { Product } from './types';
3
- import { jsx as _jsx } from "react/jsx-runtime";
4
- const getDefaultCtaConfig = product => {
3
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
4
+ export const getCTATitle = (product, isTrialPlan, isPlusUser) => {
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';
6
13
  case Product.Basic:
7
- return {
8
- label: 'Get free account',
9
- href: '/register'
10
- };
11
- case Product.Silver:
12
- return {
13
- label: 'Try Plus for free',
14
- href: '/checkout?plan_id=proSilverAnnualV2_7trial&plan_type=plus'
15
- };
14
+ return 'Sign up';
15
+ case Product.Pro:
16
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';
17
41
  case Product.Pro:
18
- return {
19
- label: 'Try Pro for free',
20
- href: '/checkout?plan_id=proGoldAnnualV2_7trial&plan_type=pro'
21
- };
42
+ case Product.Silver:
43
+ 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';
22
52
  default:
23
- return {
24
- label: 'Try Pro for free',
25
- href: '/checkout'
26
- };
53
+ return '';
27
54
  }
28
55
  };
29
56
  export const ProductCTA = ({
30
57
  product,
31
- ctaConfig,
32
- isCurrentPlan
58
+ productDetails,
59
+ user,
60
+ simple
33
61
  }) => {
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
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]
43
108
  });
44
109
  };
@@ -1,11 +1,11 @@
1
1
  import { PlansByType } from './config';
2
- import { CopyConfig, CtaConfig, User } from './types';
2
+ import { AllCurrency } from './PricingCard/types';
3
+ import { User } from './types';
3
4
  interface ProductsProps {
4
5
  plansByType: PlansByType;
5
- ctaConfig?: CtaConfig;
6
- copyConfig?: CopyConfig;
7
- user?: User;
6
+ user: User;
8
7
  isUserInIndia: boolean;
8
+ currency: AllCurrency;
9
9
  }
10
10
  export declare const Products: React.FC<ProductsProps>;
11
11
  export {};
@@ -5,10 +5,9 @@ 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,
10
8
  user,
11
- isUserInIndia
9
+ isUserInIndia,
10
+ currency
12
11
  }) => {
13
12
  const productsLength = Object.keys(plansByType).length;
14
13
  const renderCard = product => {
@@ -18,30 +17,45 @@ export const Products = ({
18
17
  product: Product.Basic,
19
18
  productDetails: getProductDetails(plansByType, product),
20
19
  plansByType: plansByType,
21
- isCurrentPlan: user && !user.anonymous && !user.proSilver && !user.proGold,
22
20
  isUserInIndia: isUserInIndia,
23
- ctaConfig: ctaConfig?.basic,
24
- copyConfig: copyConfig?.basic
21
+ currency: currency,
22
+ user: user
25
23
  }, product);
26
24
  case Product.Silver:
27
25
  return /*#__PURE__*/_jsx(PricingCard, {
28
26
  product: Product.Silver,
29
27
  productDetails: getProductDetails(plansByType, product),
30
28
  plansByType: plansByType,
31
- isCurrentPlan: user?.proSilver,
32
29
  isUserInIndia: isUserInIndia,
33
- ctaConfig: ctaConfig?.['pro-silver'],
34
- copyConfig: copyConfig?.['pro-silver']
30
+ currency: currency,
31
+ user: user
35
32
  }, product);
36
33
  case Product.Gold:
37
34
  return /*#__PURE__*/_jsx(PricingCard, {
38
35
  product: Product.Gold,
39
36
  productDetails: getProductDetails(plansByType, product),
40
37
  plansByType: plansByType,
41
- isCurrentPlan: user?.proGold,
42
38
  isUserInIndia: isUserInIndia,
43
- ctaConfig: ctaConfig?.['pro-gold'],
44
- copyConfig: copyConfig?.['pro-gold']
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
45
59
  }, product);
46
60
  default:
47
61
  return null;
@@ -49,7 +63,8 @@ export const Products = ({
49
63
  };
50
64
  return /*#__PURE__*/_jsx(GridBox, {
51
65
  as: "ul",
52
- pt: 24,
66
+ pt: 16,
67
+ pb: 24,
53
68
  px: 0,
54
69
  mb: 0,
55
70
  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" | "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" | "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";
6
6
  export declare const getProductDetails: (products: PlansByType, product: Product) => ProductDetails;
7
7
  export declare const groupPlansByType: (products: ProductDetails[]) => PlansByType;
8
8
  export {};
@@ -4,7 +4,9 @@ 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 '';
7
+ return 'Train your team with in-demand skills and flexible admin tools';
8
+ case Product.Enterprise:
9
+ return 'Customizable training for your organization';
8
10
  case Product.Pro:
9
11
  case Product.Silver:
10
12
  return 'Build in-demand technical skills for work or a personal project';
@@ -13,7 +15,7 @@ export const getProductDescription = product => {
13
15
  }
14
16
  };
15
17
  export const getProductDetails = (products, product) => {
16
- if (product === Product.Basic || product === Product.Teams) {
18
+ if (product === Product.Basic) {
17
19
  return products[product].monthly;
18
20
  }
19
21
  return products[product].annual;
@@ -1,13 +1,12 @@
1
1
  import { Background } from '@codecademy/gamut-styles';
2
- import type { CopyConfig, CtaConfig, PlanType, ProductDetails, User } from './types';
2
+ import { AllCurrency } from './PricingCard/types';
3
+ import type { ProductDetails, User } from './types';
3
4
  type PricingSectionProps = Omit<Partial<React.ComponentProps<typeof Background>>, 'title'> & {
4
5
  products: ProductDetails[];
5
6
  title?: React.ReactNode;
6
- planTypes?: PlanType[];
7
- ctaConfig?: CtaConfig;
8
- copyConfig?: CopyConfig;
9
- user?: User;
7
+ user: User;
10
8
  isUserInIndia: boolean;
9
+ currency: AllCurrency;
11
10
  };
12
11
  export declare const PricingSection: React.FC<PricingSectionProps>;
13
12
  export {};
@@ -1,58 +1,39 @@
1
- import { Box } from '@codecademy/gamut';
2
- import { Background } from '@codecademy/gamut-styles';
1
+ import { Box, ContentContainer, Text } from '@codecademy/gamut';
2
+ import { CheckerLoose } from '@codecademy/gamut-patterns';
3
3
  import { groupPlansByType } from './config';
4
4
  import { Products } from './Products';
5
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
5
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
6
6
  export const PricingSection = ({
7
7
  products,
8
8
  title,
9
- planTypes,
10
- ctaConfig,
11
- copyConfig,
12
9
  user,
13
10
  isUserInIndia,
14
- ...bgProps
11
+ currency
15
12
  }) => {
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
13
+ return /*#__PURE__*/_jsxs(_Fragment, {
14
+ children: [/*#__PURE__*/_jsx(Text, {
15
+ as: "h1",
16
+ textAlign: "center",
17
+ fontSize: {
18
+ _: 34,
19
+ md: 44
47
20
  },
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
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
+ })
55
36
  })]
56
- })
37
+ })]
57
38
  });
58
39
  };
@@ -3,7 +3,8 @@ export declare enum Product {
3
3
  Pro = "pro",
4
4
  Teams = "teams",
5
5
  Silver = "pro-silver",
6
- Gold = "pro-gold"
6
+ Gold = "pro-gold",
7
+ Enterprise = "enterprise"
7
8
  }
8
9
  export interface ProductDetails {
9
10
  isTrialPlan?: boolean | null | undefined;
@@ -14,7 +15,7 @@ export interface ProductDetails {
14
15
  savings?: string | null | undefined;
15
16
  includedTaxRate?: string | null | undefined;
16
17
  }
17
- export type PlanType = 'basic' | 'pro-silver' | 'pro-gold' | 'teams';
18
+ export type PlanType = 'basic' | 'pro-silver' | 'pro-gold' | 'teams' | 'enterprise';
18
19
  export type CtaConfig = Partial<Record<PlanType, {
19
20
  label?: string;
20
21
  href?: string;
@@ -4,5 +4,6 @@ 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";
7
8
  return Product;
8
9
  }({});
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.eb8166002f.0",
4
+ "version": "3.6.0",
5
5
  "author": "Codecademy Engineering <dev@codecademy.com>",
6
6
  "dependencies": {
7
7
  "@emotion/is-prop-valid": "^1.2.1",