From 8df7a5db017f14f917dea89bc8180c3cae2f361d Mon Sep 17 00:00:00 2001 From: Joel DSouza Date: Mon, 4 Sep 2023 10:52:22 +0200 Subject: [PATCH 1/6] feat(research): added monthly/ yearly paywall for new users --- .../article-actions/StartTrialCall.tsx | 9 +- .../article-actions/styles.module.scss | 7 ++ .../article/article-body/PrivateArticle.tsx | 8 +- .../article-payments/ProCheckoutCard.tsx | 82 ++++++++++++++++--- .../src/components/platform/PricingTable.tsx | 6 +- apps/research/src/hooks/index.ts | 7 +- apps/research/src/types/ui.ts | 2 + 7 files changed, 98 insertions(+), 23 deletions(-) diff --git a/apps/research/src/components/article/article-actions/StartTrialCall.tsx b/apps/research/src/components/article/article-actions/StartTrialCall.tsx index 520ffdf3..776b49e8 100644 --- a/apps/research/src/components/article/article-actions/StartTrialCall.tsx +++ b/apps/research/src/components/article/article-actions/StartTrialCall.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import CallToActionCard from './CallToActionCard'; import { Space, Typography, theme } from 'antd'; import { ProCheckoutCard } from '../article-payments'; +import styles from './styles.module.scss'; interface EndedCallProps { checkout: () => void; @@ -22,21 +23,21 @@ const StartTrialCall: React.FC = ({ } = useToken(); return ( - +
{isReport ? 'Register to K33 Research Pro to download the report' : 'Register to K33 Research Pro to keep reading the article'} - Subscribe and get full access to all research. No credit card needed for free trial. - + */} = ({ No charge until the trial is complete. Cancel anytime. - +
); }; diff --git a/apps/research/src/components/article/article-actions/styles.module.scss b/apps/research/src/components/article/article-actions/styles.module.scss index 5d2d88df..bf9237ac 100644 --- a/apps/research/src/components/article/article-actions/styles.module.scss +++ b/apps/research/src/components/article/article-actions/styles.module.scss @@ -74,6 +74,13 @@ } } +.paymentCard { + width: 100% !important; + > div { + width: 100% !important; + } +} + .signupHeader { padding: 0px 8px; gap: 8px; diff --git a/apps/research/src/components/article/article-body/PrivateArticle.tsx b/apps/research/src/components/article/article-body/PrivateArticle.tsx index e74aaac6..df3ad789 100644 --- a/apps/research/src/components/article/article-body/PrivateArticle.tsx +++ b/apps/research/src/components/article/article-body/PrivateArticle.tsx @@ -96,12 +96,18 @@ const PrivateArticle: React.FC = ({ if (appState === 'SIGNED_OUT') return ( - */} + + ); diff --git a/apps/research/src/components/article/article-payments/ProCheckoutCard.tsx b/apps/research/src/components/article/article-payments/ProCheckoutCard.tsx index f842a93c..f381a696 100644 --- a/apps/research/src/components/article/article-payments/ProCheckoutCard.tsx +++ b/apps/research/src/components/article/article-payments/ProCheckoutCard.tsx @@ -1,6 +1,16 @@ import * as React from 'react'; -import { Space, Divider, List, theme, Typography, Button } from 'antd'; +import { + Space, + Divider, + List, + theme, + Typography, + Button, + Switch, + Tag, +} from 'antd'; import { CheckCircleFilled } from '@ant-design/icons'; +import { usePlan } from '@/hooks'; const { Title, Text } = Typography; const { useToken } = theme; @@ -28,6 +38,7 @@ const ProCheckoutCard: React.FC = ({ const { token: { colorBgContainer, borderRadius, colorPrimary }, } = useToken(); + const { plan, setPlan } = usePlan(); return ( = ({ borderRadius: borderRadius, textAlign: 'start', padding: '20px 28px', + width: '100%', }} direction="vertical" size={20} > - - - K33 Research Pro - + + + Monthly + { + setPlan(isYear ? 'year' : 'monthly'); + }} + /> +
+ Save $100 + Yearly +
+
: null} > - - $50 - - month + {plan === 'monthly' ? ( + <> + + $50 + + month + + ) : ( + <> + + $500 + + year + + )} - {isFreeTrial && ( + {/* {isFreeTrial && ( After Free Trial - )} + )} */} + + K33 Research Pro +
= ({ - {feat} + + {feat} + )} diff --git a/apps/research/src/components/platform/PricingTable.tsx b/apps/research/src/components/platform/PricingTable.tsx index ca35229b..d4a3e5b8 100644 --- a/apps/research/src/components/platform/PricingTable.tsx +++ b/apps/research/src/components/platform/PricingTable.tsx @@ -4,6 +4,7 @@ import { UserOutlined, UnlockTwoTone, EditOutlined } from '@ant-design/icons'; import { useCustomerCheckout, useCustomerDashboard, + usePlan, useProductInfo, } from '@/hooks'; import { appStructure } from '@/config'; @@ -16,8 +17,6 @@ setTwoToneColor('#777777'); const { useToken } = theme; const { Title, Text } = Typography; -type Plan = 'monthly' | 'year'; - const proPlanFeatures = [ 'The latest detailed insights into the markets', 'The most important weekly crypto news distilled and explained', @@ -28,8 +27,7 @@ const proPlanFeatures = [ ]; const PricingTable = () => { - const [plan, setPlan] = React.useState('monthly'); - + const { plan, setPlan } = usePlan(); const productId = appStructure.payments.productId; const monthlyPriceId = appStructure.payments.monthlyPriceId; const annualPriceId = appStructure.payments.annualPriceId; diff --git a/apps/research/src/hooks/index.ts b/apps/research/src/hooks/index.ts index 7563cfbb..4924dfc2 100644 --- a/apps/research/src/hooks/index.ts +++ b/apps/research/src/hooks/index.ts @@ -4,7 +4,7 @@ import { useCustomerMutation, useLazyGetProductInfoQuery, } from '@/services'; -import { ProductStatus } from '@/types'; +import { Plan, ProductStatus } from '@/types'; import { useAppState } from 'platform-js'; import * as React from 'react'; import { useHistoryTravel } from 'ahooks'; @@ -110,3 +110,8 @@ export const useBrowser = () => { return state; }; + +export const usePlan = (initialPlan: Plan = 'monthly') => { + const [plan, setPlan] = React.useState(initialPlan); + return { plan, setPlan }; +}; diff --git a/apps/research/src/types/ui.ts b/apps/research/src/types/ui.ts index 27b4186b..9502c469 100644 --- a/apps/research/src/types/ui.ts +++ b/apps/research/src/types/ui.ts @@ -26,3 +26,5 @@ export interface AppStructure { export type Navigations = Array; export type DividerConfig = boolean | { hideLast: boolean; hideFirst: boolean }; + +export type Plan = 'monthly' | 'year'; From f72a46c8864e524da596be0f78ca51a84b605963 Mon Sep 17 00:00:00 2001 From: Joel DSouza Date: Mon, 4 Sep 2023 13:32:26 +0200 Subject: [PATCH 2/6] feat(research): added new user yearly and monthly checkout --- .../article-actions/StartTrialCall.tsx | 3 ++ .../article/article-body/PrivateArticle.tsx | 14 ++++---- .../article-payments/ProCheckoutCard.tsx | 32 +++++++++++++------ 3 files changed, 32 insertions(+), 17 deletions(-) diff --git a/apps/research/src/components/article/article-actions/StartTrialCall.tsx b/apps/research/src/components/article/article-actions/StartTrialCall.tsx index 776b49e8..08beb460 100644 --- a/apps/research/src/components/article/article-actions/StartTrialCall.tsx +++ b/apps/research/src/components/article/article-actions/StartTrialCall.tsx @@ -6,6 +6,7 @@ import styles from './styles.module.scss'; interface EndedCallProps { checkout: () => void; + yearlyCheckout: () => void; isLoading?: boolean; isReport?: boolean; } @@ -15,6 +16,7 @@ const { useToken } = theme; const StartTrialCall: React.FC = ({ checkout, + yearlyCheckout, isLoading = false, isReport = false, }) => { @@ -40,6 +42,7 @@ const StartTrialCall: React.FC = ({ */}
= ({ appStructure.payments.monthlyPriceId ); + const { doCheckOut: doYearlyCheckOut, isLoading: isYearlyLoading } = + useCustomerCheckout(appStructure.payments.annualPriceId); + const { productStatus, appState } = useProductInfo( appStructure.payments.productId ); @@ -85,7 +88,8 @@ const PrivateArticle: React.FC = ({ default: return ( @@ -96,18 +100,12 @@ const PrivateArticle: React.FC = ({ if (appState === 'SIGNED_OUT') return ( - {/* */} - - ); diff --git a/apps/research/src/components/article/article-payments/ProCheckoutCard.tsx b/apps/research/src/components/article/article-payments/ProCheckoutCard.tsx index f381a696..7aa83d4c 100644 --- a/apps/research/src/components/article/article-payments/ProCheckoutCard.tsx +++ b/apps/research/src/components/article/article-payments/ProCheckoutCard.tsx @@ -24,6 +24,7 @@ const features = [ interface ProCheckoutCardProps { handleCheckout: () => void; + handleYearlyCheckout: () => void; label: string; isFreeTrial?: boolean; isLoading?: boolean; @@ -31,6 +32,7 @@ interface ProCheckoutCardProps { const ProCheckoutCard: React.FC = ({ handleCheckout, + handleYearlyCheckout, label, isFreeTrial = false, isLoading = false, @@ -144,15 +146,27 @@ const ProCheckoutCard: React.FC = ({ )} /> - + {plan === 'monthly' ? ( + + ) : ( + + )}
); }; From 97c4bc7dfb61e010593cb801979c676c63a92f7c Mon Sep 17 00:00:00 2001 From: Joel DSouza Date: Mon, 4 Sep 2023 14:25:24 +0200 Subject: [PATCH 3/6] refactor(research): added yearlyCheckout action to ex user paywall --- .../src/components/article/article-actions/EndedCall.tsx | 3 +++ .../src/components/article/article-body/PrivateArticle.tsx | 1 + 2 files changed, 4 insertions(+) diff --git a/apps/research/src/components/article/article-actions/EndedCall.tsx b/apps/research/src/components/article/article-actions/EndedCall.tsx index 0a0716ec..dc445fec 100644 --- a/apps/research/src/components/article/article-actions/EndedCall.tsx +++ b/apps/research/src/components/article/article-actions/EndedCall.tsx @@ -5,6 +5,7 @@ import { ProCheckoutCard } from '../article-payments'; interface EndedCallProps { checkout: () => void; + yearlyCheckout: () => void; isLoading?: boolean; isReport?: boolean; } @@ -14,6 +15,7 @@ const { useToken } = theme; const EndedCall: React.FC = ({ checkout, + yearlyCheckout, isLoading = false, isReport = false, }) => { @@ -38,6 +40,7 @@ const EndedCall: React.FC = ({ = ({ case 'ended': return ( Date: Tue, 5 Sep 2023 12:12:58 +0200 Subject: [PATCH 4/6] feat(research): cleaned up the ex user paywall --- .../article/article-actions/EndedCall.tsx | 8 +- .../article/article-body/PrivateArticle.tsx | 8 +- .../article-payments/ProCheckoutCard.tsx | 160 ++++++++++-------- 3 files changed, 99 insertions(+), 77 deletions(-) diff --git a/apps/research/src/components/article/article-actions/EndedCall.tsx b/apps/research/src/components/article/article-actions/EndedCall.tsx index dc445fec..50058d2b 100644 --- a/apps/research/src/components/article/article-actions/EndedCall.tsx +++ b/apps/research/src/components/article/article-actions/EndedCall.tsx @@ -31,15 +31,9 @@ const EndedCall: React.FC = ({ ? 'Renew your K33 Research Pro subscription to download the report' : 'Renew your K33 Research Pro subscription to keep reading the article'} - - Subscribe again and regain full access to all research. - = ({ if (appState === 'SIGNED_OUT') return ( - */} + ); diff --git a/apps/research/src/components/article/article-payments/ProCheckoutCard.tsx b/apps/research/src/components/article/article-payments/ProCheckoutCard.tsx index 7aa83d4c..020c5948 100644 --- a/apps/research/src/components/article/article-payments/ProCheckoutCard.tsx +++ b/apps/research/src/components/article/article-payments/ProCheckoutCard.tsx @@ -28,6 +28,7 @@ interface ProCheckoutCardProps { label: string; isFreeTrial?: boolean; isLoading?: boolean; + isEx?: boolean; } const ProCheckoutCard: React.FC = ({ @@ -36,13 +37,14 @@ const ProCheckoutCard: React.FC = ({ label, isFreeTrial = false, isLoading = false, + isEx = false, }) => { const { token: { colorBgContainer, borderRadius, colorPrimary }, } = useToken(); const { plan, setPlan } = usePlan(); return ( - = ({ textAlign: 'start', padding: '20px 28px', width: '100%', + flexDirection: 'column', + gap: 20, }} - direction="vertical" - size={20} > = ({ }} size={16} > - - Monthly - { - setPlan(isYear ? 'year' : 'monthly'); - }} - /> +
+ +
+ { + setPlan(isYear ? 'year' : 'monthly'); + }} + /> +
Save $100 - Yearly +
- - : null} +
+
- - {plan === 'monthly' ? ( - <> - - $50 - - month - - ) : ( - <> - - $500 - - year - - )} - - {/* {isFreeTrial && ( + : null} + > + + {plan === 'monthly' ? ( + <> + + $50 + + month + + ) : ( + <> + + $500 + + year + + )} + + {/* {isFreeTrial && ( After Free Trial )} */} - - - K33 Research Pro - + + + K33 Research Pro + +
- - ( - + + - - - ( + - {feat} - - - - )} - /> + + + + {feat} + + + + )} + /> + + )} + {plan === 'monthly' ? ( )} -
+ ); }; From 457f35d4a98873f43e3b5caccd7874045c526ec7 Mon Sep 17 00:00:00 2001 From: Joel DSouza Date: Tue, 5 Sep 2023 12:16:45 +0200 Subject: [PATCH 5/6] feat(research): updated the payments card for new user --- .../components/article/article-body/PrivateArticle.tsx | 8 +------- .../article/article-payments/ProCheckoutCard.tsx | 3 ++- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/apps/research/src/components/article/article-body/PrivateArticle.tsx b/apps/research/src/components/article/article-body/PrivateArticle.tsx index 443105b9..060d71d0 100644 --- a/apps/research/src/components/article/article-body/PrivateArticle.tsx +++ b/apps/research/src/components/article/article-body/PrivateArticle.tsx @@ -101,18 +101,12 @@ const PrivateArticle: React.FC = ({ if (appState === 'SIGNED_OUT') return ( - {/* */} - ); diff --git a/apps/research/src/components/article/article-payments/ProCheckoutCard.tsx b/apps/research/src/components/article/article-payments/ProCheckoutCard.tsx index 020c5948..db9c4d17 100644 --- a/apps/research/src/components/article/article-payments/ProCheckoutCard.tsx +++ b/apps/research/src/components/article/article-payments/ProCheckoutCard.tsx @@ -132,12 +132,13 @@ const ProCheckoutCard: React.FC = ({ {!isEx && ( <> - + ( From 812f5294ba3f7352d66b1ef9720c4993da83d256 Mon Sep 17 00:00:00 2001 From: Joel DSouza Date: Tue, 5 Sep 2023 14:53:59 +0200 Subject: [PATCH 6/6] feat(research): updated font color for toggle switch in payment card --- .../article-payments/ProCheckoutCard.tsx | 28 +++++++++++++++++-- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/apps/research/src/components/article/article-payments/ProCheckoutCard.tsx b/apps/research/src/components/article/article-payments/ProCheckoutCard.tsx index db9c4d17..32941a96 100644 --- a/apps/research/src/components/article/article-payments/ProCheckoutCard.tsx +++ b/apps/research/src/components/article/article-payments/ProCheckoutCard.tsx @@ -40,7 +40,12 @@ const ProCheckoutCard: React.FC = ({ isEx = false, }) => { const { - token: { colorBgContainer, borderRadius, colorPrimary }, + token: { + colorBgContainer, + borderRadius, + colorPrimary, + colorTextQuaternary, + }, } = useToken(); const { plan, setPlan } = usePlan(); return ( @@ -66,11 +71,20 @@ const ProCheckoutCard: React.FC = ({ >
{ setPlan(isYear ? 'year' : 'monthly'); }} @@ -84,7 +98,15 @@ const ProCheckoutCard: React.FC = ({ > Save $100