From 0609f30e257166b9b3835f8d2d389f9cafbaab03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C4=B1dvan=20Akca?= Date: Thu, 28 Mar 2024 15:25:40 +0300 Subject: [PATCH 1/5] feat: introduce usePlanAndUsage with RQ --- .../UsageDataInformation/index.ee.jsx | 16 +++++++++++- packages/web/src/hooks/usePlanAndUsage.js | 25 +++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 packages/web/src/hooks/usePlanAndUsage.js diff --git a/packages/web/src/components/UsageDataInformation/index.ee.jsx b/packages/web/src/components/UsageDataInformation/index.ee.jsx index bbc5995f..502bb1df 100644 --- a/packages/web/src/components/UsageDataInformation/index.ee.jsx +++ b/packages/web/src/components/UsageDataInformation/index.ee.jsx @@ -10,15 +10,20 @@ import CardContent from '@mui/material/CardContent'; import Divider from '@mui/material/Divider'; import Grid from '@mui/material/Grid'; import Typography from '@mui/material/Typography'; + import TrialOverAlert from 'components/TrialOverAlert/index.ee'; import SubscriptionCancelledAlert from 'components/SubscriptionCancelledAlert/index.ee'; import CheckoutCompletedAlert from 'components/CheckoutCompletedAlert/index.ee'; import * as URLS from 'config/urls'; import useBillingAndUsageData from 'hooks/useBillingAndUsageData.ee'; import useFormatMessage from 'hooks/useFormatMessage'; +import usePlanAndUsage from 'hooks/usePlanAndUsage'; + const capitalize = (str) => str[0].toUpperCase() + str.slice(1, str.length); + function BillingCard(props) { const { name, title = '', action } = props; + return ( ); } + function Action(props) { const { action } = props; + if (!action) return ; + const { text, type } = action; + if (type === 'link') { if (action.src.startsWith('http')) { return ( @@ -64,6 +73,7 @@ function Action(props) { ); } } + if (type === 'text') { return ( @@ -71,11 +81,15 @@ function Action(props) { ); } + return ; } export default function UsageDataInformation() { const formatMessage = useFormatMessage(); const billingAndUsageData = useBillingAndUsageData(); + const { data } = usePlanAndUsage(); + const planAndUsage = data?.data; + return ( @@ -171,7 +185,7 @@ export default function UsageDataInformation() { variant="subtitle2" sx={{ color: 'text.secondary', mt: 2, fontWeight: 500 }} > - {billingAndUsageData?.usage.task} + {planAndUsage?.usage.task} diff --git a/packages/web/src/hooks/usePlanAndUsage.js b/packages/web/src/hooks/usePlanAndUsage.js new file mode 100644 index 00000000..39af9996 --- /dev/null +++ b/packages/web/src/hooks/usePlanAndUsage.js @@ -0,0 +1,25 @@ +import { useQuery } from '@tanstack/react-query'; + +import api from 'helpers/api'; +import useCurrentUser from './useCurrentUser'; + +export default function usePlanAndUsage() { + const { data } = useCurrentUser(); + const currentUserId = data?.data?.id; + + const query = useQuery({ + queryKey: ['planAndUsage', currentUserId], + queryFn: async ({ signal }) => { + const { data } = await api.get( + `/v1/users/${currentUserId}/plan-and-usage`, + { + signal, + }, + ); + + return data; + }, + }); + + return query; +} From 1827f5413f39096b58daf18dd9333bfef6295342 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C4=B1dvan=20Akca?= Date: Thu, 28 Mar 2024 15:29:04 +0300 Subject: [PATCH 2/5] refactor(useUserTrial): return hasTrial field from hook --- .../components/TrialStatusBadge/index.ee.jsx | 2 +- packages/web/src/hooks/useUserTrial.ee.js | 34 +++---------------- 2 files changed, 6 insertions(+), 30 deletions(-) diff --git a/packages/web/src/components/TrialStatusBadge/index.ee.jsx b/packages/web/src/components/TrialStatusBadge/index.ee.jsx index f4ab263f..d31d9e66 100644 --- a/packages/web/src/components/TrialStatusBadge/index.ee.jsx +++ b/packages/web/src/components/TrialStatusBadge/index.ee.jsx @@ -8,7 +8,7 @@ import useUserTrial from 'hooks/useUserTrial.ee'; export default function TrialStatusBadge() { const data = useUserTrial(); - if (!data) return ; + if (!data.hasTrial) return ; const { message, status } = data; diff --git a/packages/web/src/hooks/useUserTrial.ee.js b/packages/web/src/hooks/useUserTrial.ee.js index 4e61ad58..0ff5c80e 100644 --- a/packages/web/src/hooks/useUserTrial.ee.js +++ b/packages/web/src/hooks/useUserTrial.ee.js @@ -1,10 +1,8 @@ -import * as React from 'react'; -import { useLocation } from 'react-router-dom'; import { DateTime } from 'luxon'; +import { useQuery } from '@tanstack/react-query'; import useFormatMessage from './useFormatMessage'; import api from 'helpers/api'; -import { useQuery } from '@tanstack/react-query'; function getDiffInDays(date) { const today = DateTime.now().startOf('day'); @@ -13,6 +11,7 @@ function getDiffInDays(date) { return roundedDiffInDays; } + function getFeedbackPayload(date) { const diffInDays = getDiffInDays(date); @@ -41,12 +40,8 @@ function getFeedbackPayload(date) { } export default function useUserTrial() { const formatMessage = useFormatMessage(); - const location = useLocation(); - const state = location.state; - const checkoutCompleted = state?.checkoutCompleted; - const [isPolling, setIsPolling] = React.useState(false); - const { data, isLoading: isUserTrialLoading } = useQuery({ + const { data } = useQuery({ queryKey: ['userTrial'], queryFn: async ({ signal }) => { const { data } = await api.get('/v1/users/me/trial', { @@ -55,32 +50,12 @@ export default function useUserTrial() { return data; }, - refetchInterval: isPolling ? 1000 : false, }); + const userTrial = data?.data; const hasTrial = userTrial?.inTrial; - React.useEffect( - function pollDataUntilTrialEnds() { - if (checkoutCompleted && hasTrial) { - setIsPolling(true); - } - }, - [checkoutCompleted, hasTrial, setIsPolling], - ); - - React.useEffect( - function stopPollingWhenTrialEnds() { - if (checkoutCompleted && !hasTrial) { - setIsPolling(false); - } - }, - [checkoutCompleted, hasTrial, setIsPolling], - ); - - if (isUserTrialLoading || !hasTrial) return null; - const expireAt = DateTime.fromISO(userTrial?.expireAt).startOf('day'); const { translationEntryId, translationEntryValues, status, over } = @@ -91,5 +66,6 @@ export default function useUserTrial() { expireAt, over, status, + hasTrial, }; } From f0ef12f904064ca9125998367642974a2ce3e084 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C4=B1dvan=20Akca?= Date: Thu, 28 Mar 2024 15:53:10 +0300 Subject: [PATCH 3/5] refactor: rewrite useSubscription with RQ and use it in UsageDataInformation --- .../UsageDataInformation/index.ee.jsx | 124 ++++++++++++------ packages/web/src/hooks/usePlanAndUsage.js | 17 +-- packages/web/src/hooks/useSubscription.ee.js | 52 ++++++-- packages/web/src/locales/en.json | 4 + 4 files changed, 136 insertions(+), 61 deletions(-) diff --git a/packages/web/src/components/UsageDataInformation/index.ee.jsx b/packages/web/src/components/UsageDataInformation/index.ee.jsx index 502bb1df..9b737f59 100644 --- a/packages/web/src/components/UsageDataInformation/index.ee.jsx +++ b/packages/web/src/components/UsageDataInformation/index.ee.jsx @@ -18,11 +18,15 @@ import * as URLS from 'config/urls'; import useBillingAndUsageData from 'hooks/useBillingAndUsageData.ee'; import useFormatMessage from 'hooks/useFormatMessage'; import usePlanAndUsage from 'hooks/usePlanAndUsage'; +import useSubscription from 'hooks/useSubscription.ee'; +import useUserTrial from 'hooks/useUserTrial.ee'; +import { useQueryClient } from '@tanstack/react-query'; +import useCurrentUser from 'hooks/useCurrentUser'; const capitalize = (str) => str[0].toUpperCase() + str.slice(1, str.length); function BillingCard(props) { - const { name, title = '', action } = props; + const { name, title = '', action, text } = props; return ( - + ); } function Action(props) { - const { action } = props; + const { action, text } = props; if (!action) return ; - const { text, type } = action; - - if (type === 'link') { - if (action.src.startsWith('http')) { - return ( - - ); - } else { - return ( - - ); - } - } - - if (type === 'text') { + if (action.startsWith('http')) { return ( - + + ); + } else if (action.startsWith('/')) { + return ( + ); } - return ; + return ( + + {text} + + ); } + export default function UsageDataInformation() { const formatMessage = useFormatMessage(); const billingAndUsageData = useBillingAndUsageData(); + const queryClient = useQueryClient(); const { data } = usePlanAndUsage(); const planAndUsage = data?.data; + const { data: currentUser } = useCurrentUser(); + const currentUserId = currentUser?.data?.id; + const trial = useUserTrial(); + const subscriptionData = useSubscription(); + const subscription = subscriptionData?.data; + let billingInfo; + + React.useEffect(() => { + queryClient.invalidateQueries({ + queryKey: ['planAndUsage', currentUserId], + }); + }, [subscription, queryClient, currentUserId]); + + if (trial.hasTrial) { + billingInfo = { + monthlyQuota: { + title: formatMessage('usageDataInformation.freeTrial'), + action: '/settings/billing/upgrade', + text: 'Upgrade plan', + }, + nextBillAmount: { + title: '---', + action: null, + text: null, + }, + nextBillDate: { + title: '---', + action: null, + text: null, + }, + }; + } else { + billingInfo = { + monthlyQuota: { + title: planAndUsage?.plan?.limit, + action: subscription?.cancelUrl, + text: formatMessage('usageDataInformation.cancelPlan'), + }, + nextBillAmount: { + title: `€${subscription?.nextBillAmount}`, + action: subscription?.updateUrl, + text: formatMessage('usageDataInformation.updatePaymentMethod'), + }, + nextBillDate: { + title: subscription?.nextBillDate, + action: formatMessage('usageDataInformation.monthlyPayment'), + text: formatMessage('usageDataInformation.monthlyPayment'), + }, + }; + } return ( @@ -106,11 +154,8 @@ export default function UsageDataInformation() { {formatMessage('usageDataInformation.subscriptionPlan')} - {billingAndUsageData?.subscription?.status && ( - + {subscription?.status && ( + )} @@ -127,26 +172,27 @@ export default function UsageDataInformation() { @@ -193,7 +239,7 @@ export default function UsageDataInformation() { {/* free plan has `null` status so that we can show the upgrade button */} - {billingAndUsageData?.subscription?.status === null && ( + {subscription?.status === null && ( ); } else if (action.startsWith('/')) { return ( - ); @@ -83,7 +82,6 @@ function Action(props) { export default function UsageDataInformation() { const formatMessage = useFormatMessage(); - const billingAndUsageData = useBillingAndUsageData(); const queryClient = useQueryClient(); const { data } = usePlanAndUsage(); const planAndUsage = data?.data; @@ -239,7 +237,7 @@ export default function UsageDataInformation() { {/* free plan has `null` status so that we can show the upgrade button */} - {subscription?.status === null && ( + {subscription?.status === undefined && (