diff --git a/packages/backend/src/graphql/queries/get-billing-and-usage.ee.js b/packages/backend/src/graphql/queries/get-billing-and-usage.ee.js
deleted file mode 100644
index d23a44e5..00000000
--- a/packages/backend/src/graphql/queries/get-billing-and-usage.ee.js
+++ /dev/null
@@ -1,101 +0,0 @@
-import { DateTime } from 'luxon';
-import Billing from '../../helpers/billing/index.ee.js';
-import ExecutionStep from '../../models/execution-step.js';
-
-const getBillingAndUsage = async (_parent, _params, context) => {
- const persistedSubscription = await context.currentUser.$relatedQuery(
- 'currentSubscription'
- );
-
- const subscription = persistedSubscription
- ? paidSubscription(persistedSubscription)
- : freeTrialSubscription();
-
- return {
- subscription,
- usage: {
- task: executionStepCount(context),
- },
- };
-};
-
-const paidSubscription = (subscription) => {
- const currentPlan = Billing.paddlePlans.find(
- (plan) => plan.productId === subscription.paddlePlanId
- );
-
- return {
- status: subscription.status,
- monthlyQuota: {
- title: currentPlan.limit,
- action: {
- type: 'link',
- text: 'Cancel plan',
- src: subscription.cancelUrl,
- },
- },
- nextBillAmount: {
- title: subscription.nextBillAmount
- ? '€' + subscription.nextBillAmount
- : '---',
- action: {
- type: 'link',
- text: 'Update payment method',
- src: subscription.updateUrl,
- },
- },
- nextBillDate: {
- title: subscription.nextBillDate ? subscription.nextBillDate : '---',
- action: {
- type: 'text',
- text: '(monthly payment)',
- },
- },
- };
-};
-
-const freeTrialSubscription = () => {
- return {
- status: null,
- monthlyQuota: {
- title: 'Free Trial',
- action: {
- type: 'link',
- text: 'Upgrade plan',
- src: '/settings/billing/upgrade',
- },
- },
- nextBillAmount: {
- title: '---',
- action: null,
- },
- nextBillDate: {
- title: '---',
- action: null,
- },
- };
-};
-
-const executionIds = async (context) => {
- return (
- await context.currentUser
- .$relatedQuery('executions')
- .select('executions.id')
- ).map((execution) => execution.id);
-};
-
-const executionStepCount = async (context) => {
- const executionStepCount = await ExecutionStep.query()
- .whereIn('execution_id', await executionIds(context))
- .andWhere(
- 'created_at',
- '>=',
- DateTime.now().minus({ days: 30 }).toISODate()
- )
- .count()
- .first();
-
- return executionStepCount.count;
-};
-
-export default getBillingAndUsage;
diff --git a/packages/backend/src/graphql/query-resolvers.js b/packages/backend/src/graphql/query-resolvers.js
index abde6d6b..68308897 100644
--- a/packages/backend/src/graphql/query-resolvers.js
+++ b/packages/backend/src/graphql/query-resolvers.js
@@ -1,5 +1,4 @@
import getAppAuthClient from './queries/get-app-auth-client.ee.js';
-import getBillingAndUsage from './queries/get-billing-and-usage.ee.js';
import getConnectedApps from './queries/get-connected-apps.js';
import getDynamicData from './queries/get-dynamic-data.js';
import getStepWithTestExecutions from './queries/get-step-with-test-executions.js';
@@ -7,7 +6,6 @@ import testConnection from './queries/test-connection.js';
const queryResolvers = {
getAppAuthClient,
- getBillingAndUsage,
getConnectedApps,
getDynamicData,
getStepWithTestExecutions,
diff --git a/packages/backend/src/graphql/schema.graphql b/packages/backend/src/graphql/schema.graphql
index 9714bde8..17e35206 100644
--- a/packages/backend/src/graphql/schema.graphql
+++ b/packages/backend/src/graphql/schema.graphql
@@ -8,7 +8,6 @@ type Query {
key: String!
parameters: JSONObject
): JSONObject
- getBillingAndUsage: GetBillingAndUsage
}
type Mutation {
@@ -560,43 +559,6 @@ type License {
verified: Boolean
}
-type GetBillingAndUsage {
- subscription: Subscription
- usage: Usage
-}
-
-type MonthlyQuota {
- title: String
- action: BillingCardAction
-}
-
-type NextBillAmount {
- title: String
- action: BillingCardAction
-}
-
-type NextBillDate {
- title: String
- action: BillingCardAction
-}
-
-type BillingCardAction {
- type: String
- text: String
- src: String
-}
-
-type Subscription {
- status: String
- monthlyQuota: MonthlyQuota
- nextBillAmount: NextBillAmount
- nextBillDate: NextBillDate
-}
-
-type Usage {
- task: Int
-}
-
type Permission {
id: String
action: String
diff --git a/packages/web/src/components/CheckoutCompletedAlert/index.ee.jsx b/packages/web/src/components/CheckoutCompletedAlert/index.ee.jsx
index 2bc213e8..d236987a 100644
--- a/packages/web/src/components/CheckoutCompletedAlert/index.ee.jsx
+++ b/packages/web/src/components/CheckoutCompletedAlert/index.ee.jsx
@@ -2,6 +2,7 @@ import * as React from 'react';
import { useLocation } from 'react-router-dom';
import Alert from '@mui/material/Alert';
import Typography from '@mui/material/Typography';
+
import useFormatMessage from 'hooks/useFormatMessage';
export default function CheckoutCompletedAlert() {
@@ -9,7 +10,9 @@ export default function CheckoutCompletedAlert() {
const location = useLocation();
const state = location.state;
const checkoutCompleted = state?.checkoutCompleted;
+
if (!checkoutCompleted) return ;
+
return (
;
+ if (subscription?.data?.status === 'active' || trial.hasTrial)
+ return ;
+
+ const cancellationEffectiveDateObject = DateTime.fromISO(
+ subscription?.data?.cancellationEffectiveDate,
+ );
return (
- {subscription.message}
+ {formatMessage('subscriptionCancelledAlert.text', {
+ date: cancellationEffectiveDateObject.toFormat('DDD'),
+ })}
);
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/components/UsageDataInformation/index.ee.jsx b/packages/web/src/components/UsageDataInformation/index.ee.jsx
index bbc5995f..cfacc841 100644
--- a/packages/web/src/components/UsageDataInformation/index.ee.jsx
+++ b/packages/web/src/components/UsageDataInformation/index.ee.jsx
@@ -10,15 +10,23 @@ 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';
+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: URLS.SETTINGS_PLAN_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 (
@@ -92,11 +152,8 @@ export default function UsageDataInformation() {
{formatMessage('usageDataInformation.subscriptionPlan')}
- {billingAndUsageData?.subscription?.status && (
-
+ {subscription?.status && (
+
)}
@@ -113,26 +170,27 @@ export default function UsageDataInformation() {
@@ -171,7 +229,7 @@ export default function UsageDataInformation() {
variant="subtitle2"
sx={{ color: 'text.secondary', mt: 2, fontWeight: 500 }}
>
- {billingAndUsageData?.usage.task}
+ {planAndUsage?.usage.task}
@@ -179,7 +237,7 @@ export default function UsageDataInformation() {
{/* free plan has `null` status so that we can show the upgrade button */}
- {billingAndUsageData?.subscription?.status === null && (
+ {subscription?.status === undefined && (