From 1660df20af9ca85ad6fd5623b4deb83d29680dbb Mon Sep 17 00:00:00 2001 From: Ahmed Bouhuolia Date: Sun, 28 Jul 2024 17:53:51 +0200 Subject: [PATCH] feat: wip billing page --- .../GetSubscriptionsTransformer.ts | 161 +++++++++++++++++- .../Subscription/LemonResumeSubscription.ts | 2 +- .../Subscription/SubscriptionService.ts | 32 +++- ...add_trial_columns_to_subscription_table.js | 13 ++ .../models/Subscriptions/PlanSubscription.ts | 80 +++++++-- .../src/components/DrawersContainer.tsx | 2 + packages/webapp/src/constants/drawers.ts | 1 + .../SetupSubscription/SubscriptionPlan.tsx | 26 +-- .../Subscriptions/BillingPageBoot.tsx | 9 +- .../Subscriptions/BillingPageContent.tsx | 10 +- .../BillingSubscription.module.scss | 6 +- .../Subscriptions/BillingSubscription.tsx | 99 ++++++++--- .../ChangeSubscriptionPlanContent.tsx | 54 ++++++ .../webapp/src/hooks/query/subscription.tsx | 62 ++++++- 14 files changed, 488 insertions(+), 69 deletions(-) create mode 100644 packages/server/src/system/migrations/20240728123419_add_trial_columns_to_subscription_table.js create mode 100644 packages/webapp/src/containers/Subscriptions/drawers/ChangeSubscriptionPlanDrawer/ChangeSubscriptionPlanContent.tsx diff --git a/packages/server/src/services/Subscription/GetSubscriptionsTransformer.ts b/packages/server/src/services/Subscription/GetSubscriptionsTransformer.ts index edc7d5dc0..194b41f78 100644 --- a/packages/server/src/services/Subscription/GetSubscriptionsTransformer.ts +++ b/packages/server/src/services/Subscription/GetSubscriptionsTransformer.ts @@ -6,6 +6,165 @@ export class GetSubscriptionsTransformer extends Transformer { * @returns {Array} */ public includeAttributes = (): string[] => { - return []; + return [ + 'canceledAtFormatted', + 'cancelsAtFormatted', + 'trialStartsAtFormatted', + 'trialEndsAtFormatted', + 'statusFormatted', + 'planName', + 'planSlug', + 'planPrice', + 'planPriceCurrency', + 'planPriceFormatted', + 'planPeriod', + 'lemonUrls', + ]; + }; + + /** + * Exclude attributes. + * @returns {string[]} + */ + public excludeAttributes = (): string[] => { + return ['id', 'plan']; + }; + + /** + * Retrieves the canceled at formatted. + * @param subscription + * @returns {string} + */ + public canceledAtFormatted = (subscription) => { + return subscription.canceledAt + ? this.formatDate(subscription.canceledAt) + : null; + }; + + /** + * Retrieves the cancels at formatted. + * @param subscription + * @returns {string} + */ + public cancelsAtFormatted = (subscription) => { + return subscription.cancelsAt + ? this.formatDate(subscription.cancelsAt) + : null; + }; + + /** + * Retrieves the trial starts at formatted date. + * @returns {string} + */ + public trialStartsAtFormatted = (subscription) => { + return subscription.trialStartsAt + ? this.formatDate(subscription.trialStartsAt) + : null; + }; + + /** + * Retrieves the trial ends at formatted date. + * @returns {string} + */ + public trialEndsAtFormatted = (subscription) => { + return subscription.trialEndsAt + ? this.formatDate(subscription.trialEndsAt) + : null; + }; + + /** + * Retrieves the Lemon subscription metadata. + * @param subscription + * @returns + */ + public lemonSubscription = (subscription) => { + return ( + this.options.lemonSubscriptions[subscription.lemonSubscriptionId] || null + ); + }; + + /** + * Retrieves the formatted subscription status. + * @param subscription + * @returns {string} + */ + public statusFormatted = (subscription) => { + const pairs = { + canceled: 'Canceled', + active: 'Active', + inactive: 'Inactive', + expired: 'Expired', + on_trial: 'On Trial', + }; + return pairs[subscription.status] || ''; + }; + + /** + * Retrieves the subscription plan name. + * @param subscription + * @returns {string} + */ + public planName(subscription) { + return subscription.plan?.name; + } + + /** + * Retrieves the subscription plan slug. + * @param subscription + * @returns {string} + */ + public planSlug(subscription) { + return subscription.plan?.slug; + } + + /** + * Retrieves the subscription plan price. + * @param subscription + * @returns {number} + */ + public planPrice(subscription) { + return subscription.plan?.price; + } + + /** + * Retrieves the subscription plan price currency. + * @param subscription + * @returns {string} + */ + public planPriceCurrency(subscription) { + return subscription.plan?.currency; + } + + /** + * Retrieves the subscription plan formatted price. + * @param subscription + * @returns {string} + */ + public planPriceFormatted(subscription) { + return this.formatMoney(subscription.plan?.price, { + currencyCode: subscription.plan?.currency, + precision: 0 + }); + } + + /** + * Retrieves the subscription plan period. + * @param subscription + * @returns {string} + */ + public planPeriod(subscription) { + return subscription?.plan?.period; + } + + /** + * Retrieve the subscription Lemon Urls. + * @param subscription + * @returns + */ + public lemonUrls = (subscription) => { + const lemonSusbcription = this.lemonSubscription(subscription); + console.log(lemonSusbcription); + + return lemonSusbcription?.data?.attributes?.urls; }; } diff --git a/packages/server/src/services/Subscription/LemonResumeSubscription.ts b/packages/server/src/services/Subscription/LemonResumeSubscription.ts index e6628cc0c..cd0ee0d2e 100644 --- a/packages/server/src/services/Subscription/LemonResumeSubscription.ts +++ b/packages/server/src/services/Subscription/LemonResumeSubscription.ts @@ -1,6 +1,6 @@ +import { Inject, Service } from 'typedi'; import { EventPublisher } from '@/lib/EventPublisher/EventPublisher'; import events from '@/subscribers/events'; -import { Inject, Service } from 'typedi'; import { configureLemonSqueezy } from './utils'; import { PlanSubscription } from '@/system/models'; import { ServiceError } from '@/exceptions'; diff --git a/packages/server/src/services/Subscription/SubscriptionService.ts b/packages/server/src/services/Subscription/SubscriptionService.ts index de3b1db93..a61714af3 100644 --- a/packages/server/src/services/Subscription/SubscriptionService.ts +++ b/packages/server/src/services/Subscription/SubscriptionService.ts @@ -1,7 +1,11 @@ import { Inject, Service } from 'typedi'; +import { getSubscription } from '@lemonsqueezy/lemonsqueezy.js'; +import { PromisePool } from '@supercharge/promise-pool'; import { PlanSubscription } from '@/system/models'; import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable'; import { GetSubscriptionsTransformer } from './GetSubscriptionsTransformer'; +import { configureLemonSqueezy } from './utils'; +import { fromPairs } from 'lodash'; @Service() export default class SubscriptionService { @@ -13,14 +17,34 @@ export default class SubscriptionService { * @param {number} tenantId */ public async getSubscriptions(tenantId: number) { - const subscriptions = await PlanSubscription.query().where( - 'tenant_id', - tenantId + configureLemonSqueezy(); + + const subscriptions = await PlanSubscription.query() + .where('tenant_id', tenantId) + .withGraphFetched('plan'); + + const lemonSubscriptionsResult = await PromisePool.withConcurrency(1) + .for(subscriptions) + .process(async (subscription, index, pool) => { + if (subscription.lemonSubscriptionId) { + const res = await getSubscription(subscription.lemonSubscriptionId); + + if (res.error) { + return; + } + return [subscription.lemonSubscriptionId, res.data]; + } + }); + const lemonSubscriptions = fromPairs( + lemonSubscriptionsResult?.results.filter((result) => !!result[1]) ); return this.transformer.transform( tenantId, subscriptions, - new GetSubscriptionsTransformer() + new GetSubscriptionsTransformer(), + { + lemonSubscriptions, + } ); } } diff --git a/packages/server/src/system/migrations/20240728123419_add_trial_columns_to_subscription_table.js b/packages/server/src/system/migrations/20240728123419_add_trial_columns_to_subscription_table.js new file mode 100644 index 000000000..1843b120a --- /dev/null +++ b/packages/server/src/system/migrations/20240728123419_add_trial_columns_to_subscription_table.js @@ -0,0 +1,13 @@ +exports.up = function (knex) { + return knex.schema.table('subscription_plan_subscriptions', (table) => { + table.dateTime('trial_starts_at').nullable(); + table.dateTime('trial_ends_at').nullable(); + }); +}; + +exports.down = function (knex) { + return knex.schema.table('subscription_plan_subscriptions', (table) => { + table.dropColumn('trial_starts_at').nullable(); + table.dropColumn('trial_ends_at').nullable(); + }); +}; diff --git a/packages/server/src/system/models/Subscriptions/PlanSubscription.ts b/packages/server/src/system/models/Subscriptions/PlanSubscription.ts index c3e63530c..3ae1c1fac 100644 --- a/packages/server/src/system/models/Subscriptions/PlanSubscription.ts +++ b/packages/server/src/system/models/Subscriptions/PlanSubscription.ts @@ -5,7 +5,16 @@ import SubscriptionPeriod from '@/services/Subscription/SubscriptionPeriod'; export default class PlanSubscription extends mixin(SystemModel) { lemonSubscriptionId: number; - + + canceledAt: Date; + cancelsAt: Date; + + trialStartsAt: Date; + trialEndsAt: Date; + + endsAt: Date; + startsAt: Date; + /** * Table name. */ @@ -24,7 +33,7 @@ export default class PlanSubscription extends mixin(SystemModel) { * Defined virtual attributes. */ static get virtualAttributes() { - return ['active', 'inactive', 'ended', 'onTrial']; + return ['active', 'inactive', 'ended', 'canceled', 'onTrial', 'status']; } /** @@ -40,7 +49,7 @@ export default class PlanSubscription extends mixin(SystemModel) { builder.where('trial_ends_at', '>', now); }, - inactiveSubscriptions() { + inactiveSubscriptions(builder) { builder.modify('endedTrial'); builder.modify('endedPeriod'); }, @@ -100,35 +109,80 @@ export default class PlanSubscription extends mixin(SystemModel) { } /** - * Check if subscription is active. + * Check if the subscription is expired. + * Expired mens the user his lost the right to use the product. + * @returns {Boolean} + */ + public expired() { + return this.ended() && !this.onTrial(); + } + + /** + * Check if paid subscription is active. * @return {Boolean} */ - active() { - return !this.ended() || this.onTrial(); + public active() { + return ( + !this.canceled() && !this.onTrial() && !this.ended() && this.started() + ); } /** * Check if subscription is inactive. * @return {Boolean} */ - inactive() { + public inactive() { return !this.active(); } /** - * Check if subscription period has ended. + * Check if paid subscription period has ended. * @return {Boolean} */ - ended() { + public ended() { return this.endsAt ? moment().isAfter(this.endsAt) : false; } + /** + * Check if the paid subscription has started. + * @returns {Boolean} + */ + public started() { + return this.startsAt ? moment().isAfter(this.startsAt) : false; + } + /** * Check if subscription is currently on trial. * @return {Boolean} */ - onTrial() { - return this.trailEndsAt ? moment().isAfter(this.trailEndsAt) : false; + public onTrial() { + return this.trialEndsAt ? moment().isBefore(this.trialEndsAt) : false; + } + + /** + * Check if the subscription is canceled. + * @returns {boolean} + */ + public canceled() { + return ( + this.canceledAt || + (this.cancelsAt && moment().isAfter(this.cancelsAt)) || + false + ); + } + + /** + * Retrieves the subscription status. + * @returns {string} + */ + public status() { + return this.canceled() + ? 'canceled' + : this.onTrial() + ? 'on_trial' + : this.active() + ? 'active' + : 'inactive'; } /** @@ -143,7 +197,7 @@ export default class PlanSubscription extends mixin(SystemModel) { const period = new SubscriptionPeriod( invoiceInterval, invoicePeriod, - start, + start ); const startsAt = period.getStartDate(); @@ -159,7 +213,7 @@ export default class PlanSubscription extends mixin(SystemModel) { renew(invoiceInterval, invoicePeriod) { const { startsAt, endsAt } = PlanSubscription.setNewPeriod( invoiceInterval, - invoicePeriod, + invoicePeriod ); return this.$query().update({ startsAt, endsAt }); } diff --git a/packages/webapp/src/components/DrawersContainer.tsx b/packages/webapp/src/components/DrawersContainer.tsx index af3c97525..cf9451d1c 100644 --- a/packages/webapp/src/components/DrawersContainer.tsx +++ b/packages/webapp/src/components/DrawersContainer.tsx @@ -22,6 +22,7 @@ import RefundVendorCreditDetailDrawer from '@/containers/Drawers/RefundVendorCre import WarehouseTransferDetailDrawer from '@/containers/Drawers/WarehouseTransferDetailDrawer'; import TaxRateDetailsDrawer from '@/containers/TaxRates/drawers/TaxRateDetailsDrawer/TaxRateDetailsDrawer'; import CategorizeTransactionDrawer from '@/containers/CashFlow/CategorizeTransaction/drawers/CategorizeTransactionDrawer/CategorizeTransactionDrawer'; +import ChangeSubscriptionPlanDrawer from '@/containers/Subscriptions/drawers/ChangeSubscriptionPlanDrawer/ChangeSubscriptionPlanDrawer'; import { DRAWERS } from '@/constants/drawers'; @@ -63,6 +64,7 @@ export default function DrawersContainer() { /> + ); } diff --git a/packages/webapp/src/constants/drawers.ts b/packages/webapp/src/constants/drawers.ts index 2dc3e92e9..c4e477352 100644 --- a/packages/webapp/src/constants/drawers.ts +++ b/packages/webapp/src/constants/drawers.ts @@ -24,4 +24,5 @@ export enum DRAWERS { WAREHOUSE_TRANSFER_DETAILS = 'warehouse-transfer-detail-drawer', TAX_RATE_DETAILS = 'tax-rate-detail-drawer', CATEGORIZE_TRANSACTION = 'categorize-transaction', + CHANGE_SUBSCARIPTION_PLAN = 'change-subscription-plan' } diff --git a/packages/webapp/src/containers/Setup/SetupSubscription/SubscriptionPlan.tsx b/packages/webapp/src/containers/Setup/SetupSubscription/SubscriptionPlan.tsx index 4ebb88d5f..e4463ad70 100644 --- a/packages/webapp/src/containers/Setup/SetupSubscription/SubscriptionPlan.tsx +++ b/packages/webapp/src/containers/Setup/SetupSubscription/SubscriptionPlan.tsx @@ -29,6 +29,7 @@ interface SubscriptionPricingProps { annuallyPriceLabel: string; monthlyVariantId?: string; annuallyVariantId?: string; + onSubscribe?: (variantId: number) => void; } interface SubscriptionPricingCombinedProps @@ -46,6 +47,7 @@ function SubscriptionPlanRoot({ annuallyPriceLabel, monthlyVariantId, annuallyVariantId, + onSubscribe, // #withPlans plansPeriod, @@ -59,17 +61,19 @@ function SubscriptionPlanRoot({ ? monthlyVariantId : annuallyVariantId; - getLemonCheckout({ variantId }) - .then((res) => { - const checkoutUrl = res.data.data.attributes.url; - window.LemonSqueezy.Url.Open(checkoutUrl); - }) - .catch(() => { - AppToaster.show({ - message: 'Something went wrong!', - intent: Intent.DANGER, - }); - }); + onSubscribe && onSubscribe(variantId); + + // getLemonCheckout({ variantId }) + // .then((res) => { + // const checkoutUrl = res.data.data.attributes.url; + // window.LemonSqueezy.Url.Open(checkoutUrl); + // }) + // .catch(() => { + // AppToaster.show({ + // message: 'Something went wrong!', + // intent: Intent.DANGER, + // }); + // }); }; return ( diff --git a/packages/webapp/src/containers/Subscriptions/BillingPageBoot.tsx b/packages/webapp/src/containers/Subscriptions/BillingPageBoot.tsx index 7cebf9759..06bb3513e 100644 --- a/packages/webapp/src/containers/Subscriptions/BillingPageBoot.tsx +++ b/packages/webapp/src/containers/Subscriptions/BillingPageBoot.tsx @@ -15,12 +15,17 @@ interface BillingPageBootProps { } export function BillingPageBoot({ children }: BillingPageBootProps) { - const { isLoading: isSubscriptionsLoading, data: subscriptions } = + const { isLoading: isSubscriptionsLoading, data: subscriptionsRes } = useGetSubscriptions(); + const mainSubscription = subscriptionsRes?.subscriptions?.find( + (s) => s.slug === 'main', + ); + const value = { isSubscriptionsLoading, - subscriptions, + subscriptions: subscriptionsRes?.subscriptions, + mainSubscription, }; return {children}; } diff --git a/packages/webapp/src/containers/Subscriptions/BillingPageContent.tsx b/packages/webapp/src/containers/Subscriptions/BillingPageContent.tsx index bea0ec377..dee9d0159 100644 --- a/packages/webapp/src/containers/Subscriptions/BillingPageContent.tsx +++ b/packages/webapp/src/containers/Subscriptions/BillingPageContent.tsx @@ -1,9 +1,17 @@ +// @ts-nocheck import { Box, Group } from '@/components'; -import { Text } from '@blueprintjs/core'; +import { Spinner, Text } from '@blueprintjs/core'; import { Subscription } from './BillingSubscription'; +import { useBillingPageBoot } from './BillingPageBoot'; import styles from './BillingPageContent.module.scss'; export function BillingPageContent() { + const { isSubscriptionsLoading, subscriptions } = useBillingPageBoot(); + + if (isSubscriptionsLoading || !subscriptions) { + return ; + } + return ( diff --git a/packages/webapp/src/containers/Subscriptions/BillingSubscription.module.scss b/packages/webapp/src/containers/Subscriptions/BillingSubscription.module.scss index 7f218431d..f99f30d8e 100644 --- a/packages/webapp/src/containers/Subscriptions/BillingSubscription.module.scss +++ b/packages/webapp/src/containers/Subscriptions/BillingSubscription.module.scss @@ -12,7 +12,7 @@ .title{ margin: 0; - font-size: 20px; + font-size: 18px; font-weight: 600; color: #3D4C58; } @@ -56,8 +56,4 @@ } .actions{ margin-top: 16px; - - button{ - font-size: 15px; - } } \ No newline at end of file diff --git a/packages/webapp/src/containers/Subscriptions/BillingSubscription.tsx b/packages/webapp/src/containers/Subscriptions/BillingSubscription.tsx index 0bef787a1..aff31eecb 100644 --- a/packages/webapp/src/containers/Subscriptions/BillingSubscription.tsx +++ b/packages/webapp/src/containers/Subscriptions/BillingSubscription.tsx @@ -4,25 +4,42 @@ import { Box, Group, Stack } from '@/components'; import { Button, Card, Intent, Text } from '@blueprintjs/core'; import withAlertActions from '../Alert/withAlertActions'; import styles from './BillingSubscription.module.scss'; +import withDrawerActions from '../Drawer/withDrawerActions'; +import { DRAWERS } from '@/constants/drawers'; +import { useBillingPageBoot } from './BillingPageBoot'; -function SubscriptionRoot({ openAlert }) { +function SubscriptionRoot({ openAlert, openDrawer }) { + const { mainSubscription } = useBillingPageBoot(); + + // Can't continue if the main subscription is not loaded. + if (!mainSubscription) { + return null; + } const handleCancelSubBtnClick = () => { openAlert('cancel-main-subscription'); }; const handleResumeSubBtnClick = () => { openAlert('resume-main-subscription'); }; - const handleUpdatePaymentMethod = () => {}; - - const handleUpgradeBtnClick = () => {}; + const handleUpdatePaymentMethod = () => { + window.LemonSqueezy.Url.Open( + mainSubscription.lemonUrls?.updatePaymentMethod, + ); + }; + // Handle upgrade button click. + const handleUpgradeBtnClick = () => { + openDrawer(DRAWERS.CHANGE_SUBSCARIPTION_PLAN); + }; return ( - -

Capital Essential

+ +

{mainSubscription.planName}

- Trial + + {mainSubscription.statusFormatted} + Trial ends in 10 days.
@@ -43,15 +60,29 @@ function SubscriptionRoot({ openAlert }) { > Upgrade the Plan - + + {mainSubscription.canceled && ( + + )} + {!mainSubscription.canceled && ( + + )} + {mainSubscription.canceled && ( + + )}
); } -export const Subscription = R.compose(withAlertActions)(SubscriptionRoot); +export const Subscription = R.compose( + withAlertActions, + withDrawerActions, +)(SubscriptionRoot); diff --git a/packages/webapp/src/containers/Subscriptions/drawers/ChangeSubscriptionPlanDrawer/ChangeSubscriptionPlanContent.tsx b/packages/webapp/src/containers/Subscriptions/drawers/ChangeSubscriptionPlanDrawer/ChangeSubscriptionPlanContent.tsx new file mode 100644 index 000000000..7c99e17e8 --- /dev/null +++ b/packages/webapp/src/containers/Subscriptions/drawers/ChangeSubscriptionPlanDrawer/ChangeSubscriptionPlanContent.tsx @@ -0,0 +1,54 @@ +// @ts-nocheck +import * as R from 'ramda'; +import { Callout, Classes, Intent } from '@blueprintjs/core'; +import { AppToaster, Box } from '@/components'; +import { SubscriptionPlans } from '@/containers/Setup/SetupSubscription/SubscriptionPlans'; +import { SubscriptionPlansPeriodSwitcher } from '@/containers/Setup/SetupSubscription/SubscriptionPlansPeriodSwitcher'; +import { useChangeSubscriptionPlan } from '@/hooks/query/subscription'; +import withDrawerActions from '@/containers/Drawer/withDrawerActions'; +import { DRAWERS } from '@/constants/drawers'; + +function ChangeSubscriptionPlanContent({ closeDrawer }) { + const { mutateAsync: changeSubscriptionPlan } = useChangeSubscriptionPlan(); + + // Handle the subscribe button click. + const handleSubscribe = (variantId: number) => { + changeSubscriptionPlan({ variant_id: variantId }) + .then(() => { + closeDrawer(DRAWERS.CHANGE_SUBSCARIPTION_PLAN); + AppToaster.show({ + intent: Intent.SUCCESS, + message: 'The subscription plan has been changed successfully.', + }); + }) + .catch(() => { + AppToaster.show({ + intent: Intent.DANGER, + message: 'Something went wrong.', + }); + }); + }; + + return ( + + + + Simple plans. Simple prices. Only pay for what you really need. All + plans come with award-winning 24/7 customer support. Prices do not + include applicable taxes. + + + + + + + ); +} + +export default R.compose(withDrawerActions)(ChangeSubscriptionPlanContent); diff --git a/packages/webapp/src/hooks/query/subscription.tsx b/packages/webapp/src/hooks/query/subscription.tsx index c9bbeec4e..9050aca1d 100644 --- a/packages/webapp/src/hooks/query/subscription.tsx +++ b/packages/webapp/src/hooks/query/subscription.tsx @@ -9,6 +9,11 @@ import { UseQueryResult, } from 'react-query'; import useApiRequest from '../useRequest'; +import { transformToCamelCase } from '@/utils'; + +const QueryKeys = { + Subscriptions: 'Subscriptions', +}; interface CancelMainSubscriptionValues {} interface CancelMainSubscriptionResponse {} @@ -40,6 +45,9 @@ export function useCancelMainSubscription( (values) => apiRequest.post(`/subscription/cancel`, values).then((res) => res.data), { + onSuccess: () => { + queryClient.invalidateQueries(QueryKeys.Subscriptions); + }, ...options, }, ); @@ -75,6 +83,9 @@ export function useResumeMainSubscription( (values) => apiRequest.post(`/subscription/resume`, values).then((res) => res.data), { + onSuccess: () => { + queryClient.invalidateQueries(QueryKeys.Subscriptions); + }, ...options, }, ); @@ -105,20 +116,58 @@ export function useChangeSubscriptionPlan( const apiRequest = useApiRequest(); return useMutation< - ChangeMainSubscriptionPlanValues, + ChangeMainSubscriptionPlanResponse, Error, - ChangeMainSubscriptionPlanResponse + ChangeMainSubscriptionPlanValues >( (values) => apiRequest.post(`/subscription/change`, values).then((res) => res.data), { + onSuccess: () => { + queryClient.invalidateQueries(QueryKeys.Subscriptions); + }, ...options, }, ); } +interface LemonSubscription { + active: boolean; + canceled: string | null; + canceledAt: string | null; + canceledAtFormatted: string | null; + cancelsAt: string | null; + cancelsAtFormatted: string | null; + createdAt: string; + ended: boolean; + endsAt: string | null; + inactive: boolean; + lemonSubscriptionId: string; + lemon_urls: { + updatePaymentMethod: string; + customerPortal: string; + customerPortalUpdateSubscription: string; + }; + onTrial: boolean; + planId: number; + planName: string; + planSlug: string; + slug: string; + startsAt: string | null; + status: string; + statusFormatted: string; + tenantId: number; + trialEndsAt: string | null; + trialEndsAtFormatted: string | null; + trialStartsAt: string | null; + trialStartsAtFormatted: string | null; + updatedAt: string; +} + interface GetSubscriptionsQuery {} -interface GetSubscriptionsResponse {} +interface GetSubscriptionsResponse { + subscriptions: Array; +} /** * Changese the main subscription of the current organization. @@ -135,8 +184,11 @@ export function useGetSubscriptions( const apiRequest = useApiRequest(); return useQuery( - ['SUBSCRIPTIONS'], - (values) => apiRequest.get(`/subscription`).then((res) => res.data), + [QueryKeys.Subscriptions], + (values) => + apiRequest + .get(`/subscription`) + .then((res) => transformToCamelCase(res.data)), { ...options, },