diff --git a/sdks/js/packages/core/jest.config.js b/sdks/js/packages/core/jest.config.js index b413e106d..2359e6c1c 100644 --- a/sdks/js/packages/core/jest.config.js +++ b/sdks/js/packages/core/jest.config.js @@ -1,5 +1,22 @@ /** @type {import('ts-jest').JestConfigWithTsJest} */ module.exports = { - preset: 'ts-jest', + preset: 'ts-jest/presets/default-esm', testEnvironment: 'node', + extensionsToTreatAsEsm: ['.ts', '.tsx'], + moduleNameMapper: { + '^~/(.*)$': '/$1', + '^(\\.{1,2}/.*)\\.js$': '$1' + }, + transform: { + '^.+\\.tsx?$': [ + 'ts-jest', + { + useESM: true, + tsconfig: { + module: 'ES2020', + moduleResolution: 'node' + } + } + ] + } }; \ No newline at end of file diff --git a/sdks/js/packages/core/package.json b/sdks/js/packages/core/package.json index 0922d30ea..dd0779a18 100644 --- a/sdks/js/packages/core/package.json +++ b/sdks/js/packages/core/package.json @@ -22,7 +22,7 @@ "release:ci": "release-it --ci --no-increment --npm.ignoreVersion", "release:dry": "release-it --dry-run --npm.ignoreVersion", "release": "release-it", - "test": "jest", + "test": "NODE_OPTIONS=--experimental-vm-modules jest", "gen:client": "npx swagger-typescript-api -p ../../../../proto/apidocs.swagger.yaml -o api-client --modular", "build:client": "node scripts/gen-swagger-client.mjs" }, @@ -99,7 +99,7 @@ "@connectrpc/connect-query": "^2.1.1", "@connectrpc/connect-web": "^2.0.2", "@hookform/resolvers": "^3.10.0", - "@raystack/proton": "0.1.0-2dbafa5913a214851fdc4f1beefbe27c4a6de55a", + "@raystack/proton": "0.1.0-fba39927b8b974dc1cc1ae0f05f1390580ec6d58", "@tanstack/react-query": "^5.83.0", "@tanstack/react-router": "1.58.17", "axios": "^1.9.0", diff --git a/sdks/js/packages/core/react/components/common/upcoming-plan-change-banner/index.tsx b/sdks/js/packages/core/react/components/common/upcoming-plan-change-banner/index.tsx index 01bd9b409..4202644a3 100644 --- a/sdks/js/packages/core/react/components/common/upcoming-plan-change-banner/index.tsx +++ b/sdks/js/packages/core/react/components/common/upcoming-plan-change-banner/index.tsx @@ -3,11 +3,11 @@ import { DEFAULT_DATE_FORMAT, SUBSCRIPTION_STATES } from '~/react/utils/constants'; -import { V1Beta1Plan, V1Beta1Subscription } from '~/src'; +import { Subscription, Plan } from '@raystack/proton/frontier'; import styles from './styles.module.css'; import { InfoCircledIcon } from '@radix-ui/react-icons'; import dayjs from 'dayjs'; -import { useCallback, useEffect, useState } from 'react'; +import { useCallback, useMemo, useState } from 'react'; import { useFrontier } from '~/react/contexts/FrontierContext'; import { checkSimilarPlans, @@ -15,10 +15,11 @@ import { getPlanIntervalName, getPlanNameWithInterval } from '~/react/utils'; +import { timestampToDayjs } from '~/utils/timestamp'; interface ChangeBannerProps { isLoading?: boolean; - subscription?: V1Beta1Subscription; + subscription?: Subscription; isAllowed: boolean; } @@ -34,47 +35,28 @@ export function UpcomingPlanChangeBanner({ activeOrganization, billingAccount, fetchActiveSubsciption, - basePlan + basePlan, + allPlans, + isAllPlansLoading } = useFrontier(); - const [upcomingPlan, setUpcomingPlan] = useState(); - const [isPlanLoading, setIsPlanLoading] = useState(false); const [isPlanChangeLoading, setIsPlanChangeLoading] = useState(false); const phases = subscription?.phases?.filter(phase => - dayjs(phase.effective_at).isAfter(dayjs()) + timestampToDayjs(phase.effectiveAt)?.isAfter(dayjs()) ) || []; const nextPhase = phases?.[0]; - const fetchPlan = useCallback( - async (planId: string) => { - setIsPlanLoading(true); - try { - const resp = await client?.frontierServiceGetPlan(planId); - const plan = resp?.data?.plan ?? {}; - if (plan) { - setUpcomingPlan(plan); - } else { - setUpcomingPlan(undefined); - } - } catch (err) { - console.error(err); - } finally { - setIsPlanLoading(false); - } - }, - [client] - ); - - useEffect(() => { - if (nextPhase?.plan_id) { - fetchPlan(nextPhase?.plan_id); + const upcomingPlan = useMemo(() => { + if (nextPhase?.planId && allPlans.length > 0) { + const plan = allPlans.find(p => p.id === nextPhase?.planId); + return plan; } - }, [fetchPlan, nextPhase?.plan_id]); + }, [nextPhase?.planId, allPlans]); - const expiryDate = nextPhase?.effective_at - ? dayjs(nextPhase?.effective_at).format( + const expiryDate = nextPhase?.effectiveAt + ? timestampToDayjs(nextPhase?.effectiveAt)?.format( config?.dateFormat || DEFAULT_DATE_FORMAT ) : ''; @@ -87,7 +69,7 @@ export function UpcomingPlanChangeBanner({ Number(activePlanMetadata?.weightage) ); - const showLoader = isLoading || isPlanLoading; + const showLoader = isLoading || isAllPlansLoading; const onPlanChangeCancel = useCallback(async () => { setIsPlanChangeLoading(true); @@ -125,17 +107,14 @@ export function UpcomingPlanChangeBanner({ const currentPlanName = getPlanNameWithInterval(activePlan); const upcomingPlanName = getPlanNameWithInterval(upcomingPlan || basePlan); - const areSimilarPlans = checkSimilarPlans( - activePlan || {}, - upcomingPlan || {} - ); + const areSimilarPlans = checkSimilarPlans(activePlan, upcomingPlan); const resumePlanTitle = areSimilarPlans ? getPlanIntervalName(activePlan) : activePlan?.title; const showBanner = - nextPhase?.plan_id || + nextPhase?.planId || (subscription?.state === SUBSCRIPTION_STATES.ACTIVE && nextPhase?.reason === 'cancel'); diff --git a/sdks/js/packages/core/react/components/organization/billing/cycle-switch/index.tsx b/sdks/js/packages/core/react/components/organization/billing/cycle-switch/index.tsx index 64cc5c956..92a07a128 100644 --- a/sdks/js/packages/core/react/components/organization/billing/cycle-switch/index.tsx +++ b/sdks/js/packages/core/react/components/organization/billing/cycle-switch/index.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useState } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import { Button, Skeleton, @@ -10,7 +10,6 @@ import { } from '@raystack/apsara'; import { useNavigate, useParams } from '@tanstack/react-router'; import { useFrontier } from '~/react/contexts/FrontierContext'; -import { V1Beta1Plan } from '~/src'; import { getPlanIntervalName, getPlanPrice } from '~/react/utils'; import * as _ from 'lodash'; import { usePlans } from '../../plans/hooks/usePlans'; @@ -18,16 +17,22 @@ import dayjs from 'dayjs'; import { DEFAULT_DATE_FORMAT } from '~/react/utils/constants'; import cross from '~/react/assets/cross.svg'; import styles from '../../organization.module.css'; +import { timestampToDayjs } from '~/utils/timestamp'; +import { Plan } from '@raystack/proton/frontier'; export function ConfirmCycleSwitch() { - const { activePlan, client, paymentMethod, config, activeSubscription } = - useFrontier(); + const { + activePlan, + paymentMethod, + config, + activeSubscription, + allPlans, + isAllPlansLoading + } = useFrontier(); const navigate = useNavigate({ from: '/billing/cycle-switch/$planId' }); const { planId } = useParams({ from: '/billing/cycle-switch/$planId' }); const dateFormat = config?.dateFormat || DEFAULT_DATE_FORMAT; - const [isPlanLoading, setIsPlanLoading] = useState(false); - const [nextPlan, setNextPlan] = useState(); const [isCycleSwitching, setCycleSwitching] = useState(false); const closeModal = useCallback( @@ -42,6 +47,13 @@ export function ConfirmCycleSwitch() { verifyPlanChange } = usePlans(); + const nextPlan = useMemo(() => { + if (planId && allPlans.length > 0) { + const plan = allPlans.find(p => p.id === planId); + return plan; + } + }, [planId, allPlans]); + const nextPlanPrice = nextPlan ? getPlanPrice(nextPlan) : { amount: 0 }; const isPaymentMethodRequired = _.isEmpty(paymentMethod) && nextPlanPrice.amount > 0; @@ -56,28 +68,7 @@ export function ConfirmCycleSwitch() { (Number(activePlanMetadata?.weightage) || 0) > 0; - useEffect(() => { - async function getNextPlan(nextPlanId: string) { - setIsPlanLoading(true); - try { - const resp = await client?.frontierServiceGetPlan(nextPlanId); - const plan = resp?.data?.plan; - setNextPlan(plan); - } catch (err: any) { - toast.error('Something went wrong', { - description: err.message - }); - console.error(err); - } finally { - setIsPlanLoading(false); - } - } - if (planId) { - getNextPlan(planId); - } - }, [client, planId]); - - const isLoading = isPlanLoading; + const isLoading = isAllPlansLoading; async function onConfirm() { setCycleSwitching(true); @@ -101,9 +92,9 @@ export function ConfirmCycleSwitch() { }); if (planPhase) { closeModal(); - const changeDate = dayjs(planPhase?.effective_at).format( - dateFormat - ); + const changeDate = timestampToDayjs( + planPhase?.effectiveAt + )?.format(dateFormat); toast.success(`Plan cycle switch successful`, { description: `Your plan cycle will switched to ${nextPlanIntervalName} on ${changeDate}` }); @@ -122,8 +113,8 @@ export function ConfirmCycleSwitch() { } } - const cycleSwitchDate = activeSubscription?.current_period_end_at - ? dayjs(activeSubscription?.current_period_end_at).format( + const cycleSwitchDate = activeSubscription?.currentPeriodEndAt + ? timestampToDayjs(activeSubscription?.currentPeriodEndAt)?.format( config?.dateFormat || DEFAULT_DATE_FORMAT ) : 'the next billing cycle'; diff --git a/sdks/js/packages/core/react/components/organization/billing/index.tsx b/sdks/js/packages/core/react/components/organization/billing/index.tsx index 6b3c9a2e9..82224f91a 100644 --- a/sdks/js/packages/core/react/components/organization/billing/index.tsx +++ b/sdks/js/packages/core/react/components/organization/billing/index.tsx @@ -13,10 +13,10 @@ import { useFrontier } from '~/react/contexts/FrontierContext'; import { useCallback, useEffect, useState } from 'react'; import billingStyles from './billing.module.css'; import { - V1Beta1BillingAccount, V1Beta1CheckoutSetupBody, V1Beta1Invoice } from '~/src'; +import { BillingAccount } from '@raystack/proton/frontier'; // import { converBillingAddressToString } from '~/react/utils'; import Invoices from './invoices'; import qs from 'query-string'; @@ -70,7 +70,7 @@ const BillingHeader = ({ }; interface BillingDetailsProps { - billingAccount?: V1Beta1BillingAccount; + billingAccount?: BillingAccount; onAddDetailsClick?: () => void; isLoading: boolean; isAllowed: boolean; @@ -165,13 +165,13 @@ export default function Billing() { ); useEffect(() => { - if (billingAccount?.id && billingAccount?.org_id) { - fetchInvoices(billingAccount?.org_id, billingAccount?.id); + if (billingAccount?.id && billingAccount?.orgId) { + fetchInvoices(billingAccount?.orgId, billingAccount?.id); } - }, [billingAccount?.id, billingAccount?.org_id, client, fetchInvoices]); + }, [billingAccount?.id, billingAccount?.orgId, client, fetchInvoices]); const onAddDetailsClick = useCallback(async () => { - const orgId = billingAccount?.org_id || ''; + const orgId = billingAccount?.orgId || ''; const billingAccountId = billingAccount?.id || ''; if (billingAccountId && orgId) { try { @@ -180,7 +180,7 @@ export default function Billing() { details: btoa( qs.stringify({ billing_id: billingAccount?.id, - organization_id: billingAccount?.org_id, + organization_id: billingAccount?.orgId, type: 'billing' }) ), @@ -196,7 +196,7 @@ export default function Billing() { }; const resp = await client?.frontierServiceCreateCheckout( - billingAccount?.org_id || '', + billingAccount?.orgId || '', billingAccount?.id || '', { cancel_url, @@ -215,7 +215,7 @@ export default function Billing() { } }, [ billingAccount?.id, - billingAccount?.org_id, + billingAccount?.orgId, client, config?.billing?.cancelUrl, config?.billing?.successUrl diff --git a/sdks/js/packages/core/react/components/organization/billing/payment-issue.tsx b/sdks/js/packages/core/react/components/organization/billing/payment-issue.tsx index 0fda6be0e..b930b094d 100644 --- a/sdks/js/packages/core/react/components/organization/billing/payment-issue.tsx +++ b/sdks/js/packages/core/react/components/organization/billing/payment-issue.tsx @@ -1,6 +1,7 @@ import { Button, Skeleton, Image, Text, Flex } from '@raystack/apsara'; import { INVOICE_STATES, SUBSCRIPTION_STATES } from '~/react/utils/constants'; -import { V1Beta1Invoice, V1Beta1Subscription } from '~/src'; +import { V1Beta1Invoice } from '~/src'; +import { Subscription } from '@raystack/proton/frontier'; import billingStyles from './billing.module.css'; import exclamationTriangle from '~/react/assets/exclamation-triangle.svg'; import dayjs from 'dayjs'; @@ -8,7 +9,7 @@ import { useCallback } from 'react'; interface PaymentIssueProps { isLoading?: boolean; - subscription?: V1Beta1Subscription; + subscription?: Subscription; invoices: V1Beta1Invoice[]; } diff --git a/sdks/js/packages/core/react/components/organization/billing/payment-method.tsx b/sdks/js/packages/core/react/components/organization/billing/payment-method.tsx index ad0ea0e1d..5ec689e1c 100644 --- a/sdks/js/packages/core/react/components/organization/billing/payment-method.tsx +++ b/sdks/js/packages/core/react/components/organization/billing/payment-method.tsx @@ -3,40 +3,41 @@ import { useFrontier } from '~/react/contexts/FrontierContext'; import * as _ from 'lodash'; import { Button, Skeleton, Text, Flex } from '@raystack/apsara'; import billingStyles from './billing.module.css'; -import { V1Beta1CheckoutSetupBody, V1Beta1PaymentMethod } from '~/src'; +import { V1Beta1CheckoutSetupBody } from '~/src'; +import { PaymentMethod as PaymentMethodType } from '@raystack/proton/frontier'; import { toast } from '@raystack/apsara'; import { useState } from 'react'; interface PaymentMethodProps { - paymentMethod?: V1Beta1PaymentMethod; + paymentMethod?: PaymentMethodType; isLoading: boolean; isAllowed: boolean; } export const PaymentMethod = ({ - paymentMethod = {}, + paymentMethod, isLoading, isAllowed }: PaymentMethodProps) => { const { client, config, billingAccount } = useFrontier(); const [isActionLoading, setIsActionLoading] = useState(false); const { - card_last4 = '', - card_expiry_month, - card_expiry_year - } = paymentMethod; + cardLast4 = '', + cardExpiryMonth, + cardExpiryYear + } = paymentMethod || {}; // TODO: change card digit as per card type const cardDigit = 12; - const cardNumber = card_last4 ? _.repeat('*', cardDigit) + card_last4 : 'N/A'; + const cardNumber = cardLast4 ? _.repeat('*', cardDigit) + cardLast4 : 'N/A'; const cardExp = - card_expiry_month && card_expiry_year - ? `${card_expiry_month}/${card_expiry_year}` + cardExpiryMonth && cardExpiryYear + ? `${cardExpiryMonth}/${cardExpiryYear}` : 'N/A'; - const isPaymentMethodAvailable = card_last4 !== ''; + const isPaymentMethodAvailable = cardLast4 !== ''; const updatePaymentMethod = async () => { - const orgId = billingAccount?.org_id || ''; + const orgId = billingAccount?.orgId || ''; const billingAccountId = billingAccount?.id || ''; if (billingAccountId && orgId) { setIsActionLoading(true); @@ -46,7 +47,7 @@ export const PaymentMethod = ({ details: btoa( qs.stringify({ billing_id: billingAccount?.id, - organization_id: billingAccount?.org_id, + organization_id: billingAccount?.orgId, type: 'billing' }) ), @@ -62,7 +63,7 @@ export const PaymentMethod = ({ }; const resp = await client?.frontierServiceCreateCheckout( - billingAccount?.org_id || '', + billingAccount?.orgId || '', billingAccount?.id || '', { cancel_url, diff --git a/sdks/js/packages/core/react/components/organization/billing/upcoming-billing-cycle.tsx b/sdks/js/packages/core/react/components/organization/billing/upcoming-billing-cycle.tsx index 107d96b16..0e7c91ca2 100644 --- a/sdks/js/packages/core/react/components/organization/billing/upcoming-billing-cycle.tsx +++ b/sdks/js/packages/core/react/components/organization/billing/upcoming-billing-cycle.tsx @@ -1,7 +1,7 @@ import { useNavigate } from '@tanstack/react-router'; -import { ReactNode, useEffect, useState } from 'react'; +import { ReactNode, useEffect, useMemo, useState } from 'react'; import { useFrontier } from '~/react/contexts/FrontierContext'; -import { V1Beta1Invoice, V1Beta1Plan } from '~/src'; +import { V1Beta1Invoice } from '~/src'; import { Button, Tooltip, @@ -22,6 +22,7 @@ import { import { NEGATIVE_BALANCE_TOOLTIP_MESSAGE } from '~/react/utils/constants'; import line from '~/react/assets/line.svg'; import billingStyles from './billing.module.css'; +import { Plan } from '@raystack/proton/frontier'; function LabeledBillingData({ label, @@ -40,7 +41,7 @@ function LabeledBillingData({ ); } -function PlanSwitchButton({ nextPlan }: { nextPlan: V1Beta1Plan }) { +function PlanSwitchButton({ nextPlan }: { nextPlan: Plan }) { const intervalName = getPlanIntervalName(nextPlan).toLowerCase(); const navigate = useNavigate({ from: '/billing' }); @@ -68,7 +69,7 @@ function PlanSwitchButton({ nextPlan }: { nextPlan: V1Beta1Plan }) { ); } -function getSwitchablePlan(plans: V1Beta1Plan[], currentPlan: V1Beta1Plan) { +function getSwitchablePlan(plans: Plan[], currentPlan: Plan) { const currentPlanMetaData = (currentPlan?.metadata as Record) || {}; const currentPlanSlug = @@ -98,44 +99,27 @@ export const UpcomingBillingCycle = ({ activeSubscription, trialSubscription, isActiveOrganizationLoading, - basePlan + basePlan, + allPlans, + isAllPlansLoading } = useFrontier(); const [isInvoiceLoading, setIsInvoiceLoading] = useState(false); const [memberCount, setMemberCount] = useState(0); const [isMemberCountLoading, setIsMemberCountLoading] = useState(false); const navigate = useNavigate({ from: '/billing' }); - const [isPlansLoading, setIsPlansLoading] = useState(false); - const [plan, setPlan] = useState(); - const [switchablePlan, setSwitchablePlan] = useState( - null - ); - - useEffect(() => { - async function getPlans(planId: string) { - setIsPlansLoading(true); - try { - const resp = await client?.frontierServiceListPlans(); - const plansList = resp?.data?.plans || []; - const currentPlan = plansList.find(p => p.id === planId); - setPlan(currentPlan); - const otherPlan = currentPlan - ? getSwitchablePlan(plansList, currentPlan) - : null; - setSwitchablePlan(otherPlan); - } catch (err: any) { - toast.error('Something went wrong', { - description: err.message - }); - console.error(err); - } finally { - setIsPlansLoading(false); - } - } - if (activeSubscription?.plan_id) { - getPlans(activeSubscription?.plan_id); + const { plan, switchablePlan } = useMemo(() => { + if (activeSubscription?.planId && allPlans.length > 0) { + const currentPlan = allPlans.find( + p => p.id === activeSubscription.planId + ); + const otherPlan = currentPlan + ? getSwitchablePlan(allPlans, currentPlan) + : null; + return { plan: currentPlan, switchablePlan: otherPlan }; } - }, [client, activeSubscription?.plan_id]); + return { plan: null, switchablePlan: null }; + }, [activeSubscription?.planId, allPlans]); useEffect(() => { async function getMemberCount(orgId: string) { @@ -179,17 +163,17 @@ export const UpcomingBillingCycle = ({ if ( billingAccount?.id && - billingAccount?.org_id && - billingAccount?.provider_id + billingAccount?.orgId && + billingAccount?.providerId ) { - getUpcomingInvoice(billingAccount?.org_id, billingAccount?.id); - getMemberCount(billingAccount?.org_id); + getUpcomingInvoice(billingAccount?.orgId, billingAccount?.id); + getMemberCount(billingAccount?.orgId); } }, [ client, - billingAccount?.org_id, + billingAccount?.orgId, billingAccount?.id, - billingAccount?.provider_id + billingAccount?.providerId ]); const planName = activeSubscription @@ -219,14 +203,14 @@ export const UpcomingBillingCycle = ({ }; const alreadyPhased = activeSubscription?.phases?.find( - phase => phase.plan_id === switchablePlan?.id + phase => phase.planId === switchablePlan?.id ); const isLoading = isActiveOrganizationLoading || isInvoiceLoading || isMemberCountLoading || - isPlansLoading || + isAllPlansLoading || isPermissionLoading; const isUserOnlyTrialing = !activeSubscription?.id && trialSubscription?.id; diff --git a/sdks/js/packages/core/react/components/organization/plans/confirm-change/index.tsx b/sdks/js/packages/core/react/components/organization/plans/confirm-change/index.tsx index c6c78e562..7103a6a59 100644 --- a/sdks/js/packages/core/react/components/organization/plans/confirm-change/index.tsx +++ b/sdks/js/packages/core/react/components/organization/plans/confirm-change/index.tsx @@ -16,27 +16,28 @@ import { DEFAULT_DATE_FORMAT, DEFAULT_PLAN_UPGRADE_MESSAGE } from '~/react/utils/constants'; -import { V1Beta1Plan } from '~/src'; import { getPlanChangeAction, getPlanNameWithInterval } from '~/react/utils'; import planStyles from '../plans.module.css'; import { usePlans } from '../hooks/usePlans'; import cross from '~/react/assets/cross.svg'; import styles from '../../organization.module.css'; import { useMessages } from '~/react/hooks/useMessages'; +import { timestampToDayjs } from '~/utils/timestamp'; +import { Plan } from '@raystack/proton/frontier'; export default function ConfirmPlanChange() { const navigate = useNavigate({ from: '/plans/confirm-change/$planId' }); const { planId } = useParams({ from: '/plans/confirm-change/$planId' }); const { activePlan, - isActivePlanLoading, + isAllPlansLoading, config, client, activeSubscription, basePlan, allPlans } = useFrontier(); - const [newPlan, setNewPlan] = useState(); + const [newPlan, setNewPlan] = useState(); const [isNewPlanLoading, setIsNewPlanLoading] = useState(false); const m = useMessages(); @@ -83,7 +84,7 @@ export default function ConfirmPlanChange() { : await verifyPlanChange({ planId }); const actionName = planAction?.btnLabel.toLowerCase(); if (planPhase) { - const changeDate = dayjs(planPhase?.effective_at).format( + const changeDate = timestampToDayjs(planPhase?.effectiveAt)?.format( config?.dateFormat || DEFAULT_DATE_FORMAT ); toast.success(`Plan ${actionName} successful`, { @@ -145,7 +146,7 @@ export default function ConfirmPlanChange() { } }, [getPlan, planId]); - const isLoading = isActivePlanLoading || isNewPlanLoading; + const isLoading = isAllPlansLoading || isNewPlanLoading; const currentPlanName = getPlanNameWithInterval(activePlan, { hyphenSeperated: true @@ -155,8 +156,8 @@ export default function ConfirmPlanChange() { hyphenSeperated: true }); - const cycleSwitchDate = activeSubscription?.current_period_end_at - ? dayjs(activeSubscription?.current_period_end_at).format( + const cycleSwitchDate = activeSubscription?.currentPeriodEndAt + ? timestampToDayjs(activeSubscription?.currentPeriodEndAt)?.format( config?.dateFormat || DEFAULT_DATE_FORMAT ) : 'the next billing cycle'; diff --git a/sdks/js/packages/core/react/components/organization/plans/helpers/helpers.test.ts b/sdks/js/packages/core/react/components/organization/plans/helpers/helpers.test.ts index 984ca3f88..25bfc972a 100644 --- a/sdks/js/packages/core/react/components/organization/plans/helpers/helpers.test.ts +++ b/sdks/js/packages/core/react/components/organization/plans/helpers/helpers.test.ts @@ -1,5 +1,7 @@ -import { V1Beta1Plan } from '~/src'; +import { Plan } from '@raystack/proton/frontier'; import { groupPlansPricingByInterval } from './index'; +import { create } from '@bufbuild/protobuf'; +import { PlanSchema } from '@raystack/proton/frontier'; describe('Plans:helpers:groupPlansPricingByInterval', () => { test('should return empty array for no plans', () => { @@ -7,8 +9,8 @@ describe('Plans:helpers:groupPlansPricingByInterval', () => { expect(result).toEqual([]); }); test('should merge plan based on name and productIds', () => { - const plans: V1Beta1Plan[] = [ - { + const plans: Plan[] = [ + create(PlanSchema, { id: 'plan-1', name: 'starter_plan_plan-1', title: 'Starter Plan', @@ -19,12 +21,12 @@ describe('Plans:helpers:groupPlansPricingByInterval', () => { id: 'product-1', prices: [ { - amount: '0', + amount: BigInt(0), interval: 'year', currency: 'INR' }, { - amount: '0', + amount: BigInt(0), interval: 'month', currency: 'INR' } @@ -34,20 +36,20 @@ describe('Plans:helpers:groupPlansPricingByInterval', () => { id: 'product-2', prices: [ { - amount: '0', + amount: BigInt(0), interval: 'year', currency: 'INR' }, { - amount: '0', + amount: BigInt(0), interval: 'month', currency: 'INR' } ] } ] - }, - { + }), + create(PlanSchema, { id: 'plan-2', name: 'starter_plan_plan-2', title: 'Starter Plan', @@ -58,12 +60,12 @@ describe('Plans:helpers:groupPlansPricingByInterval', () => { id: 'product-1', prices: [ { - amount: '0', + amount: BigInt(0), interval: 'year', currency: 'INR' }, { - amount: '0', + amount: BigInt(0), interval: 'month', currency: 'INR' } @@ -73,20 +75,20 @@ describe('Plans:helpers:groupPlansPricingByInterval', () => { id: 'product-2', prices: [ { - amount: '0', + amount: BigInt(0), interval: 'year', currency: 'INR' }, { - amount: '0', + amount: BigInt(0), interval: 'month', currency: 'INR' } ] } ] - }, - { + }), + create(PlanSchema, { id: 'plan-3', name: 'starter_plan_plan-3', title: 'Starter Plan 3', @@ -97,12 +99,12 @@ describe('Plans:helpers:groupPlansPricingByInterval', () => { id: 'product-1', prices: [ { - amount: '0', + amount: BigInt(0), interval: 'year', currency: 'INR' }, { - amount: '0', + amount: BigInt(0), interval: 'month', currency: 'INR' } @@ -112,19 +114,19 @@ describe('Plans:helpers:groupPlansPricingByInterval', () => { id: 'product-3', prices: [ { - amount: '100', + amount: BigInt(100), interval: 'year', currency: 'INR' }, { - amount: '500', + amount: BigInt(500), interval: 'month', currency: 'INR' } ] } ] - } + }) ]; const result = groupPlansPricingByInterval(plans); @@ -139,19 +141,23 @@ describe('Plans:helpers:groupPlansPricingByInterval', () => { planId: 'plan-1', planName: 'starter_plan_plan-1', amount: 0, - behavior: '', currency: 'INR', interval: 'year', - weightage: 0 + weightage: 0, + productNames: ['', ''], + trial_days: '', + features: {} }, month: { planId: 'plan-2', planName: 'starter_plan_plan-2', amount: 0, - behavior: '', currency: 'INR', interval: 'month', - weightage: 0 + weightage: 0, + productNames: ['', ''], + trial_days: '', + features: {} } }, features: {} @@ -164,12 +170,14 @@ describe('Plans:helpers:groupPlansPricingByInterval', () => { intervals: { month: { amount: 500, - behavior: '', currency: 'INR', planId: 'plan-3', planName: 'starter_plan_plan-3', interval: 'month', - weightage: 0 + weightage: 0, + productNames: ['', ''], + trial_days: '', + features: {} } }, features: {} @@ -178,8 +186,8 @@ describe('Plans:helpers:groupPlansPricingByInterval', () => { }); test('should add plans weightage', () => { - const plans: V1Beta1Plan[] = [ - { + const plans: Plan[] = [ + create(PlanSchema, { id: 'plan-1', name: 'starter_plan_plan-1', title: 'Starter Plan', @@ -190,12 +198,12 @@ describe('Plans:helpers:groupPlansPricingByInterval', () => { id: 'product-1', prices: [ { - amount: '0', + amount: BigInt(0), interval: 'year', currency: 'INR' }, { - amount: '0', + amount: BigInt(0), interval: 'month', currency: 'INR' } @@ -205,12 +213,12 @@ describe('Plans:helpers:groupPlansPricingByInterval', () => { id: 'product-2', prices: [ { - amount: '0', + amount: BigInt(0), interval: 'year', currency: 'INR' }, { - amount: '0', + amount: BigInt(0), interval: 'month', currency: 'INR' } @@ -220,8 +228,8 @@ describe('Plans:helpers:groupPlansPricingByInterval', () => { metadata: { weightage: '1' } - }, - { + }), + create(PlanSchema, { id: 'plan-2', name: 'starter_plan_plan-2', title: 'Starter Plan', @@ -232,12 +240,12 @@ describe('Plans:helpers:groupPlansPricingByInterval', () => { id: 'product-1', prices: [ { - amount: '0', + amount: BigInt(0), interval: 'year', currency: 'INR' }, { - amount: '0', + amount: BigInt(0), interval: 'month', currency: 'INR' } @@ -247,12 +255,12 @@ describe('Plans:helpers:groupPlansPricingByInterval', () => { id: 'product-2', prices: [ { - amount: '0', + amount: BigInt(0), interval: 'year', currency: 'INR' }, { - amount: '0', + amount: BigInt(0), interval: 'month', currency: 'INR' } @@ -262,8 +270,8 @@ describe('Plans:helpers:groupPlansPricingByInterval', () => { metadata: { weightage: '2' } - }, - { + }), + create(PlanSchema, { id: 'plan-3', name: 'starter_plan_plan-3', title: 'Starter Plan 3', @@ -274,12 +282,12 @@ describe('Plans:helpers:groupPlansPricingByInterval', () => { id: 'product-1', prices: [ { - amount: '0', + amount: BigInt(0), interval: 'year', currency: 'INR' }, { - amount: '0', + amount: BigInt(0), interval: 'month', currency: 'INR' } @@ -289,12 +297,12 @@ describe('Plans:helpers:groupPlansPricingByInterval', () => { id: 'product-3', prices: [ { - amount: '100', + amount: BigInt(100), interval: 'year', currency: 'INR' }, { - amount: '500', + amount: BigInt(500), interval: 'month', currency: 'INR' } @@ -304,7 +312,7 @@ describe('Plans:helpers:groupPlansPricingByInterval', () => { metadata: { weightage: '5' } - } + }) ]; const result = groupPlansPricingByInterval(plans); @@ -319,19 +327,23 @@ describe('Plans:helpers:groupPlansPricingByInterval', () => { planId: 'plan-1', planName: 'starter_plan_plan-1', amount: 0, - behavior: '', currency: 'INR', interval: 'year', - weightage: 1 + weightage: 1, + productNames: ['', ''], + trial_days: '', + features: {} }, month: { planId: 'plan-2', planName: 'starter_plan_plan-2', amount: 0, - behavior: '', currency: 'INR', interval: 'month', - weightage: 2 + weightage: 2, + productNames: ['', ''], + trial_days: '', + features: {} } }, features: {} @@ -344,12 +356,14 @@ describe('Plans:helpers:groupPlansPricingByInterval', () => { intervals: { month: { amount: 500, - behavior: '', currency: 'INR', planId: 'plan-3', planName: 'starter_plan_plan-3', interval: 'month', - weightage: 5 + weightage: 5, + productNames: ['', ''], + trial_days: '', + features: {} } }, features: {} @@ -358,8 +372,8 @@ describe('Plans:helpers:groupPlansPricingByInterval', () => { }); test('should group plans based on `plan_group_id`', () => { - const plans: V1Beta1Plan[] = [ - { + const plans: Plan[] = [ + create(PlanSchema, { id: 'plan-1', name: 'starter_plan_plan-1', title: 'Starter Plan', @@ -370,12 +384,12 @@ describe('Plans:helpers:groupPlansPricingByInterval', () => { id: 'product-1', prices: [ { - amount: '0', + amount: BigInt(0), interval: 'year', currency: 'INR' }, { - amount: '0', + amount: BigInt(0), interval: 'month', currency: 'INR' } @@ -385,12 +399,12 @@ describe('Plans:helpers:groupPlansPricingByInterval', () => { id: 'product-2', prices: [ { - amount: '0', + amount: BigInt(0), interval: 'year', currency: 'INR' }, { - amount: '0', + amount: BigInt(0), interval: 'month', currency: 'INR' } @@ -401,8 +415,8 @@ describe('Plans:helpers:groupPlansPricingByInterval', () => { weightage: '1', plan_group_id: 'group-1' } - }, - { + }), + create(PlanSchema, { id: 'plan-2', name: 'starter_plan_plan-2', title: 'Starter Plan', @@ -413,12 +427,12 @@ describe('Plans:helpers:groupPlansPricingByInterval', () => { id: 'product-1', prices: [ { - amount: '0', + amount: BigInt(0), interval: 'year', currency: 'INR' }, { - amount: '0', + amount: BigInt(0), interval: 'month', currency: 'INR' } @@ -428,12 +442,12 @@ describe('Plans:helpers:groupPlansPricingByInterval', () => { id: 'product-2', prices: [ { - amount: '0', + amount: BigInt(0), interval: 'year', currency: 'INR' }, { - amount: '0', + amount: BigInt(0), interval: 'month', currency: 'INR' } @@ -444,8 +458,8 @@ describe('Plans:helpers:groupPlansPricingByInterval', () => { weightage: '2', plan_group_id: 'group-1' } - }, - { + }), + create(PlanSchema, { id: 'plan-3', name: 'starter_plan_plan-3', title: 'Starter Plan 3', @@ -456,12 +470,12 @@ describe('Plans:helpers:groupPlansPricingByInterval', () => { id: 'product-1', prices: [ { - amount: '0', + amount: BigInt(0), interval: 'year', currency: 'INR' }, { - amount: '0', + amount: BigInt(0), interval: 'month', currency: 'INR' } @@ -471,17 +485,17 @@ describe('Plans:helpers:groupPlansPricingByInterval', () => { id: 'product-3', prices: [ { - amount: '100', + amount: BigInt(100), interval: 'year', currency: 'INR' }, { - amount: '500', + amount: BigInt(500), interval: 'month', currency: 'INR' }, { - amount: '500', + amount: BigInt(500), interval: 'week', currency: 'INR' } @@ -492,7 +506,7 @@ describe('Plans:helpers:groupPlansPricingByInterval', () => { weightage: '5', plan_group_id: 'group-1' } - } + }) ]; const result = groupPlansPricingByInterval(plans); @@ -507,28 +521,34 @@ describe('Plans:helpers:groupPlansPricingByInterval', () => { planId: 'plan-1', planName: 'starter_plan_plan-1', amount: 0, - behavior: '', currency: 'INR', interval: 'year', - weightage: 1 + weightage: 1, + productNames: ['', ''], + trial_days: '', + features: {} }, month: { planId: 'plan-2', planName: 'starter_plan_plan-2', amount: 0, - behavior: '', currency: 'INR', interval: 'month', - weightage: 2 + weightage: 2, + productNames: ['', ''], + trial_days: '', + features: {} }, week: { amount: 500, - behavior: '', currency: 'INR', planId: 'plan-3', planName: 'starter_plan_plan-3', interval: 'week', - weightage: 5 + weightage: 5, + productNames: ['', ''], + trial_days: '', + features: {} } }, features: {} diff --git a/sdks/js/packages/core/react/components/organization/plans/helpers/index.ts b/sdks/js/packages/core/react/components/organization/plans/helpers/index.ts index f24442a0d..0f64b5607 100644 --- a/sdks/js/packages/core/react/components/organization/plans/helpers/index.ts +++ b/sdks/js/packages/core/react/components/organization/plans/helpers/index.ts @@ -1,12 +1,12 @@ -import { V1Beta1Plan } from '~/src'; import { IntervalKeys, IntervalPricing, PlanIntervalPricing } from '~/src/types'; import { getPlanPrice, makePlanSlug } from '~/react/utils'; +import { Plan } from '@raystack/proton/frontier'; -export function groupPlansPricingByInterval(plans: V1Beta1Plan[]) { +export function groupPlansPricingByInterval(plans: Plan[]) { const plansMap: Record = {}; plans.forEach(plan => { const metaData = (plan?.metadata as Record) || {}; @@ -29,7 +29,7 @@ export function groupPlansPricingByInterval(plans: V1Beta1Plan[]) { interval: planInterval, weightage: planMetadata?.weightage ? Number(planMetadata?.weightage) : 0, productNames: [], - trial_days: plan?.trial_days || '', + trial_days: plan?.trialDays || '', features: {}, ...productPrices }; diff --git a/sdks/js/packages/core/react/components/organization/plans/hooks/usePlans.tsx b/sdks/js/packages/core/react/components/organization/plans/hooks/usePlans.tsx index 69a965a1d..f28a923d4 100644 --- a/sdks/js/packages/core/react/components/organization/plans/hooks/usePlans.tsx +++ b/sdks/js/packages/core/react/components/organization/plans/hooks/usePlans.tsx @@ -2,10 +2,11 @@ import { useCallback, useState } from 'react'; import { useFrontier } from '~/react/contexts/FrontierContext'; import qs from 'query-string'; import { toast } from '@raystack/apsara'; -import { SubscriptionPhase, V1Beta1CheckoutSession, V1Beta1Plan } from '~/src'; +import { SubscriptionPhase, V1Beta1CheckoutSession } from '~/src'; import { SUBSCRIPTION_STATES } from '~/react/utils/constants'; import { PlanMetadata } from '~/src/types'; import { NIL as NIL_UUID } from 'uuid'; +import { Plan } from '@raystack/proton/frontier'; interface checkoutPlanOptions { isTrial: boolean; @@ -50,7 +51,7 @@ export const usePlans = () => { const planMap = allPlans.reduce((acc, p) => { if (p.id) acc[p.id] = p; return acc; - }, {} as Record); + }, {} as Record); const isCurrentlyTrialing = subscriptions?.some( sub => sub.state === SUBSCRIPTION_STATES.TRIALING @@ -164,7 +165,7 @@ export const usePlans = () => { const activeSub = await fetchActiveSubsciption(); if (activeSub) { const planPhase = activeSub.phases?.find( - phase => phase?.plan_id === planId && phase.reason === 'change' + phase => phase?.planId === planId && phase.reason === 'change' ); if (planPhase) { onSuccess(planPhase); @@ -180,7 +181,7 @@ export const usePlans = () => { const activeSub = await fetchActiveSubsciption(); if (activeSub) { const planPhase = activeSub.phases?.find( - phase => phase?.plan_id === '' && phase.reason === 'cancel' + phase => phase?.planId === '' && phase.reason === 'cancel' ); if (planPhase) { onSuccess(planPhase); @@ -193,11 +194,11 @@ export const usePlans = () => { const getSubscribedPlans = useCallback(() => { return subscriptions - .map(t => (t.plan_id ? planMap[t.plan_id] : null)) - .filter((plan): plan is V1Beta1Plan => !!plan); + .map(t => (t.planId ? planMap[t.planId] : null)) + .filter((plan): plan is Plan => !!plan); }, [planMap, subscriptions]); - const getTrialedPlanMaxWeightage = (plans: V1Beta1Plan[]) => { + const getTrialedPlanMaxWeightage = (plans: Plan[]) => { return Math.max( ...plans .map(plan => { diff --git a/sdks/js/packages/core/react/components/organization/plans/index.tsx b/sdks/js/packages/core/react/components/organization/plans/index.tsx index 050294713..6564e5c11 100644 --- a/sdks/js/packages/core/react/components/organization/plans/index.tsx +++ b/sdks/js/packages/core/react/components/organization/plans/index.tsx @@ -1,9 +1,7 @@ -import { useEffect, useState } from 'react'; -import { EmptyState, toast, Skeleton, Text, Flex } from '@raystack/apsara'; +import { EmptyState, Skeleton, Text, Flex } from '@raystack/apsara'; import { Outlet } from '@tanstack/react-router'; import { ExclamationTriangleIcon } from '@radix-ui/react-icons'; import { useFrontier } from '~/react/contexts/FrontierContext'; -import { V1Beta1Feature, V1Beta1Plan } from '~/src'; import { groupPlansPricingByInterval } from './helpers'; import { IntervalPricingWithPlan } from '~/src/types'; import { UpcomingPlanChangeBanner } from '~/react/components/common/upcoming-plan-change-banner'; @@ -12,6 +10,11 @@ import { PlanPricingColumn } from './pricing-column'; import { useBillingPermission } from '~/react/hooks/useBillingPermission'; import plansStyles from './plans.module.css'; import { styles } from '../styles'; +import { useQuery as useConnectQuery } from '@connectrpc/connect-query'; +import { FrontierServiceQueries } from '~hooks'; +import { create } from '@bufbuild/protobuf'; +import { Feature, ListFeaturesRequestSchema } from '@raystack/proton/frontier'; +import { Plan } from '@raystack/proton/frontier'; const PlansLoader = () => { return ( @@ -36,10 +39,10 @@ const NoPlans = () => { }; interface PlansListProps { - plans: V1Beta1Plan[]; + plans: Plan[]; currentPlanId: string; allowAction: boolean; - features: V1Beta1Feature[]; + features: Feature[]; } const PlansList = ({ @@ -127,48 +130,28 @@ const PlansList = ({ export default function Plans() { const { config, - client, activeSubscription, isActiveSubscriptionLoading, isActiveOrganizationLoading, - basePlan + basePlan, + allPlans, + isAllPlansLoading } = useFrontier(); - const [isPlansLoading, setIsPlansLoading] = useState(false); - const [plans, setPlans] = useState([]); - const [features, setFeatures] = useState([]); const { isFetching: isPermissionsFetching, isAllowed: canChangePlan } = useBillingPermission(); - useEffect(() => { - async function getPlansAndFeatures() { - setIsPlansLoading(true); - try { - const [planResp, featuresResp] = await Promise.all([ - client?.frontierServiceListPlans(), - client?.frontierServiceListFeatures() - ]); - if (planResp?.data?.plans) { - setPlans([...(basePlan ? [basePlan] : []), ...planResp?.data?.plans]); - } - if (featuresResp?.data?.features) { - setFeatures(featuresResp?.data?.features); - } - } catch (err: any) { - toast.error('Something went wrong', { - description: err.message - }); - console.error(err); - } finally { - setIsPlansLoading(false); - } - } + const { data: featuresData } = useConnectQuery( + FrontierServiceQueries.listFeatures, + create(ListFeaturesRequestSchema, {}) + ); + + const features = (featuresData?.features || []) as Feature[]; - getPlansAndFeatures(); - }, [client, basePlan]); + const plans = [...(basePlan ? [basePlan] : []), ...allPlans]; const isLoading = - isPlansLoading || + isAllPlansLoading || isPermissionsFetching || isActiveSubscriptionLoading || isActiveOrganizationLoading; @@ -193,7 +176,7 @@ export default function Plans() { )} diff --git a/sdks/js/packages/core/react/components/organization/plans/pricing-column.tsx b/sdks/js/packages/core/react/components/organization/plans/pricing-column.tsx index ebdd2506a..e85276a86 100644 --- a/sdks/js/packages/core/react/components/organization/plans/pricing-column.tsx +++ b/sdks/js/packages/core/react/components/organization/plans/pricing-column.tsx @@ -29,6 +29,7 @@ import { import checkCircle from '~/react/assets/check-circle.svg'; import plansStyles from './plans.module.css'; +import { timestampToDayjs } from '~/utils/timestamp'; interface PricingColumnHeaderProps { plan: PlanIntervalPricing; @@ -202,12 +203,12 @@ const TrialLink = function TrialLink({ const trialSubscription = subscriptions.find( sub => - planIds.includes(sub.plan_id || '') && + planIds.includes(sub.planId || '') && sub.state === SUBSCRIPTION_STATES.TRIALING ); - const trialEndDate = trialSubscription?.trial_ends_at - ? dayjs(trialSubscription?.trial_ends_at).format(dateFormat) + const trialEndDate = trialSubscription?.trialEndsAt + ? timestampToDayjs(trialSubscription?.trialEndsAt)?.format(dateFormat) : ''; const showButton = @@ -346,7 +347,7 @@ export const PlanPricingColumn = ({ onSuccess: async () => { const planPhase = await verifyPlanChange({ planId }); if (planPhase) { - const changeDate = dayjs(planPhase?.effective_at).format( + const changeDate = timestampToDayjs(planPhase?.effectiveAt)?.format( dateFormat ); const actionName = action?.btnLabel.toLowerCase(); diff --git a/sdks/js/packages/core/react/components/organization/user/update.tsx b/sdks/js/packages/core/react/components/organization/user/update.tsx index 081cfc4ce..0f2200be1 100644 --- a/sdks/js/packages/core/react/components/organization/user/update.tsx +++ b/sdks/js/packages/core/react/components/organization/user/update.tsx @@ -14,6 +14,9 @@ import * as yup from 'yup'; import { useFrontier } from '~/react/contexts/FrontierContext'; import { AvatarUpload } from '../../avatar-upload'; import { styles } from '../styles'; +import { useMutation, FrontierServiceQueries } from '~hooks'; +import { useQueryClient } from '@tanstack/react-query'; +import { createConnectQueryKey } from '@connectrpc/connect-query'; const generalSchema = yup .object({ @@ -26,7 +29,21 @@ const generalSchema = yup type FormData = yup.InferType; export const UpdateProfile = () => { - const { client, user, isUserLoading: isLoading, setUser } = useFrontier(); + const { user, isUserLoading: isLoading } = useFrontier(); + const queryClient = useQueryClient(); + const { mutateAsync: updateCurrentUser } = useMutation( + FrontierServiceQueries.updateCurrentUser, + { + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: createConnectQueryKey({ + schema: FrontierServiceQueries.getCurrentUser, + cardinality: 'finite' + }) + }); + } + } + ); const { reset, control, @@ -43,13 +60,11 @@ export const UpdateProfile = () => { async function onSubmit(data: FormData) { try { - if (!client) return; if (!user?.id) return; - const updatedUser = await client.frontierServiceUpdateCurrentUser(data); - if (updatedUser?.data?.user) { - setUser(updatedUser?.data?.user); - } + await updateCurrentUser({ + body: data + }); toast.success('Updated user'); } catch ({ error }: any) { toast.error('Something went wrong', { diff --git a/sdks/js/packages/core/react/contexts/FrontierContext.tsx b/sdks/js/packages/core/react/contexts/FrontierContext.tsx index 98739d0a8..370845126 100644 --- a/sdks/js/packages/core/react/contexts/FrontierContext.tsx +++ b/sdks/js/packages/core/react/contexts/FrontierContext.tsx @@ -8,6 +8,8 @@ import { useMemo, useState } from 'react'; +import { useQuery as useConnectQuery } from '@connectrpc/connect-query'; +import { FrontierServiceQueries } from '~hooks'; import { FrontierClientOptions, @@ -15,18 +17,24 @@ import { } from '../../shared/types'; import { V1Beta1 } from '../../api-client/V1Beta1'; +import { V1Beta1Organization } from '../../api-client/data-contracts'; import { - V1Beta1AuthStrategy, - V1Beta1BillingAccount, - V1Beta1Group, - V1Beta1Organization, - V1Beta1OrganizationKyc, - V1Beta1PaymentMethod, - V1Beta1Plan, - V1Beta1Subscription, - V1Beta1User, - V1Beta1BillingAccountDetails -} from '../../api-client/data-contracts'; + User, + Group, + Organization, + OrganizationKyc, + GetOrganizationKycRequestSchema, + GetBillingAccountRequestSchema, + ListBillingAccountsRequestSchema, + ListSubscriptionsRequestSchema, + ListPlansRequestSchema, + BillingAccount, + BillingAccountDetails, + PaymentMethod, + Subscription, + Plan +} from '@raystack/proton/frontier'; +import { create } from '@bufbuild/protobuf'; import { getActiveSubscription, getDefaultPaymentMethod, @@ -38,20 +46,16 @@ import { DEFAULT_DATE_FORMAT, DEFAULT_DATE_SHORT_FORMAT } from '../utils/constants'; -import { AxiosError } from 'axios'; interface FrontierContextProviderProps { config: FrontierClientOptions; client: V1Beta1 | undefined; - organizations: V1Beta1Organization[]; - setOrganizations: Dispatch>; + organizations: Organization[]; - groups: V1Beta1Group[]; - setGroups: Dispatch>; + groups: Group[]; - user: V1Beta1User | undefined; - setUser: Dispatch>; + user: User | undefined; activeOrganization: V1Beta1Organization | undefined; setActiveOrganization: Dispatch< @@ -62,55 +66,35 @@ interface FrontierContextProviderProps { setIsActiveOrganizationLoading: Dispatch>; isUserLoading: boolean; - setIsUserLoading: Dispatch>; - billingAccount: V1Beta1BillingAccount | undefined; - setBillingAccount: Dispatch< - SetStateAction - >; + billingAccount: BillingAccount | undefined; isBillingAccountLoading: boolean; - setIsBillingAccountLoading: Dispatch>; - trialSubscription: V1Beta1Subscription | undefined; - activeSubscription: V1Beta1Subscription | undefined; - setActiveSubscription: Dispatch< - SetStateAction - >; + trialSubscription: Subscription | undefined; + activeSubscription: Subscription | undefined; - subscriptions: V1Beta1Subscription[]; + subscriptions: Subscription[]; isActiveSubscriptionLoading: boolean; - setIsActiveSubscriptionLoading: Dispatch>; - trialPlan: V1Beta1Plan | undefined; - activePlan: V1Beta1Plan | undefined; - setActivePlan: Dispatch>; + trialPlan: Plan | undefined; + activePlan: Plan | undefined; - allPlans: V1Beta1Plan[]; + allPlans: Plan[]; isAllPlansLoading: boolean; - isActivePlanLoading: boolean; - setIsActivePlanLoading: Dispatch>; + fetchActiveSubsciption: () => Promise; - fetchActiveSubsciption: () => Promise; + paymentMethod: PaymentMethod | undefined; - paymentMethod: V1Beta1PaymentMethod | undefined; + basePlan?: Plan; - basePlan?: V1Beta1Plan; - - organizationKyc: V1Beta1OrganizationKyc | undefined; - setOrganizationKyc: Dispatch< - SetStateAction - >; + organizationKyc: OrganizationKyc | undefined; isOrganizationKycLoading: boolean; - setIsOrganizationKycLoading: Dispatch>; - billingDetails: V1Beta1BillingAccountDetails | undefined; - setBillingDetails: Dispatch< - SetStateAction - >; + billingDetails: BillingAccountDetails | undefined; } const defaultConfig: FrontierClientOptions = { @@ -134,48 +118,36 @@ const initialValues: FrontierContextProviderProps = { client: undefined, organizations: [], - setOrganizations: () => undefined, groups: [], - setGroups: () => undefined, user: undefined, - setUser: () => undefined, activeOrganization: undefined, setActiveOrganization: () => undefined, isUserLoading: false, - setIsUserLoading: () => undefined, isActiveOrganizationLoading: false, setIsActiveOrganizationLoading: () => undefined, billingAccount: undefined, - setBillingAccount: () => undefined, isBillingAccountLoading: false, - setIsBillingAccountLoading: () => false, trialSubscription: undefined, activeSubscription: undefined, - setActiveSubscription: () => undefined, subscriptions: [], isActiveSubscriptionLoading: false, - setIsActiveSubscriptionLoading: () => false, trialPlan: undefined, activePlan: undefined, - setActivePlan: () => undefined, allPlans: [], isAllPlansLoading: false, - isActivePlanLoading: false, - setIsActivePlanLoading: () => false, - fetchActiveSubsciption: async () => undefined, paymentMethod: undefined, @@ -183,13 +155,10 @@ const initialValues: FrontierContextProviderProps = { basePlan: undefined, organizationKyc: undefined, - setOrganizationKyc: () => undefined, isOrganizationKycLoading: false, - setIsOrganizationKycLoading: () => false, - billingDetails: undefined, - setBillingDetails: () => undefined + billingDetails: undefined }; export const FrontierContext = @@ -221,263 +190,118 @@ export const FrontierContextProvider = ({ [activeOrganization?.id, config.endpoint] ); - const [organizations, setOrganizations] = useState([]); - const [groups, setGroups] = useState([]); - const [user, setUser] = useState(); - - const [isUserLoading, setIsUserLoading] = useState(false); - - const [billingAccount, setBillingAccount] = useState(); - const [paymentMethod, setPaymentMethod] = useState(); - const [billingDetails, setBillingDetails] = - useState(); - const [isBillingAccountLoading, setIsBillingAccountLoading] = useState(false); + const { data: currentUserData, isLoading: isUserLoading } = useConnectQuery( + FrontierServiceQueries.getCurrentUser + ); - const [isActiveSubscriptionLoading, setIsActiveSubscriptionLoading] = - useState(false); - const [activeSubscription, setActiveSubscription] = - useState(); + const user = currentUserData?.user; - const [trialSubscription, setTrialSubscription] = - useState(); + const { data: groupsData } = useConnectQuery( + FrontierServiceQueries.listCurrentUserGroups, + {}, + { enabled: !!user?.id } + ); - const [subscriptions, setSubscriptions] = useState([]); + const { data: organizationsData } = useConnectQuery( + FrontierServiceQueries.listOrganizationsByCurrentUser, + {}, + { enabled: !!user?.id } + ); - const [allPlans, setAllPlans] = useState([]); - const [isAllPlansLoading, setIsAllPlansLoading] = useState(false); + const groups = groupsData?.groups || []; + const organizations = organizationsData?.organizations || []; - const [activePlan, setActivePlan] = useState(); - const [trialPlan, setTrialPlan] = useState(); - const [isActivePlanLoading, setIsActivePlanLoading] = useState(false); + const { data: organizationKycData, isLoading: isOrganizationKycLoading } = + useConnectQuery( + FrontierServiceQueries.getOrganizationKyc, + create(GetOrganizationKycRequestSchema, { + orgId: activeOrganization?.id ?? '' + }), + { enabled: !!activeOrganization?.id } + ); - const [basePlan, setBasePlan] = useState(); + const organizationKyc = organizationKycData?.organizationKyc; - const [organizationKyc, setOrganizationKyc] = - useState(); - const [isOrganizationKycLoading, setIsOrganizationKycLoading] = - useState(false); + const { data: billingAccountsData } = useConnectQuery( + FrontierServiceQueries.listBillingAccounts, + create(ListBillingAccountsRequestSchema, { + orgId: activeOrganization?.id ?? '' + }), + { enabled: !!activeOrganization?.id } + ); - useEffect(() => { - async function getFrontierCurrentUser() { - try { - setIsUserLoading(true); - const { - data: { user } - } = await frontierClient.frontierServiceGetCurrentUser(); - setUser(user); - } catch (error) { - console.error( - 'frontier:sdk:: There is problem with fetching current user information' - ); - } finally { - setIsUserLoading(false); - } - } - getFrontierCurrentUser(); - }, [frontierClient]); - - const getFrontierCurrentUserGroups = useCallback(async () => { - try { - const { - data: { groups = [] } - } = await frontierClient.frontierServiceListCurrentUserGroups(); - setGroups(groups); - } catch (error) { - console.error( - 'frontier:sdk:: There is problem with fetching user groups information' - ); - } - }, [frontierClient]); - - const getFrontierCurrentUserOrganizations = useCallback(async () => { - try { - const { - data: { organizations = [] } - } = await frontierClient.frontierServiceListOrganizationsByCurrentUser(); - setOrganizations(organizations); - } catch (error) { - console.error( - 'frontier:sdk:: There is problem with fetching user current organizations' - ); - } - }, [frontierClient]); + const billingAccountId = billingAccountsData?.billingAccounts?.[0]?.id || ''; + + const { data: billingAccountData } = useConnectQuery( + FrontierServiceQueries.getBillingAccount, + create(GetBillingAccountRequestSchema, { + id: billingAccountId, + orgId: activeOrganization?.id ?? '', + withPaymentMethods: true, + withBillingDetails: true + }), + { enabled: !!activeOrganization?.id && !!billingAccountId } + ); - useEffect(() => { - if (user?.id) { - getFrontierCurrentUserGroups(); - getFrontierCurrentUserOrganizations(); + const billingAccount = billingAccountData?.billingAccount; + const billingDetails = billingAccountData?.billingDetails; + const paymentMethod = useMemo(() => { + if (billingAccountData?.paymentMethods) { + return getDefaultPaymentMethod(billingAccountData.paymentMethods); } - }, [getFrontierCurrentUserGroups, getFrontierCurrentUserOrganizations, user]); - - const getPlan = useCallback( - async (planId?: string) => { - if (!planId) return; - setIsActivePlanLoading(true); - try { - const resp = await frontierClient?.frontierServiceGetPlan(planId); - return resp?.data?.plan; - } catch (err) { - console.error( - 'frontier:sdk:: There is problem with fetching active plan' - ); - console.error(err); - return; - } finally { - setIsActivePlanLoading(false); - } - }, - [frontierClient] - ); + return undefined; + }, [billingAccountData?.paymentMethods]); + + const { data: subscriptionsData, isLoading: isActiveSubscriptionLoading } = + useConnectQuery( + FrontierServiceQueries.listSubscriptions, + create(ListSubscriptionsRequestSchema, { + orgId: activeOrganization?.id ?? '', + billingId: billingAccount?.id ?? '' + }), + { enabled: !!activeOrganization?.id && !!billingAccount?.id } + ); - const setActiveAndTrialSubscriptions = useCallback( - async (subscriptionsList: V1Beta1Subscription[] = []) => { - const activeSub = getActiveSubscription(subscriptionsList); - setActiveSubscription(activeSub); - const activeSubPlan = await getPlan(activeSub?.plan_id); - setActivePlan(activeSubPlan); - - const trialSub = getTrialingSubscription(subscriptionsList); - setTrialSubscription(trialSub); - const trialSubPlan = await getPlan(trialSub?.plan_id); - setTrialPlan(trialSubPlan); - - return [activeSub, trialSub]; - }, - [getPlan] - ); + const subscriptions = (subscriptionsData?.subscriptions || + []) as Subscription[]; - const getSubscription = useCallback( - async (orgId: string, billingId: string) => { - setIsActiveSubscriptionLoading(true); - try { - const resp = await frontierClient?.frontierServiceListSubscriptions( - orgId, - billingId - ); - const subscriptionsList = resp?.data?.subscriptions || []; - setSubscriptions(subscriptionsList); - const [activeSub] = await setActiveAndTrialSubscriptions( - subscriptionsList - ); - return activeSub; - } catch (err: any) { - console.error( - 'frontier:sdk:: There is problem with fetching active subscriptions' - ); - console.error(err); - } finally { - setIsActiveSubscriptionLoading(false); - } - }, - [frontierClient, setActiveAndTrialSubscriptions] + const { data: plansData, isLoading: isAllPlansLoading } = useConnectQuery( + FrontierServiceQueries.listPlans, + create(ListPlansRequestSchema, {}), + { enabled: !!activeOrganization?.id } ); - const getBillingAccount = useCallback( - async (orgId: string) => { - setIsBillingAccountLoading(true); - try { - const { - data: { billing_accounts = [] } - } = await frontierClient.frontierServiceListBillingAccounts(orgId); - const billingAccountId = billing_accounts[0]?.id || ''; - if (billingAccountId) { - const [resp] = await Promise.all([ - frontierClient?.frontierServiceGetBillingAccount( - orgId, - billingAccountId, - { with_payment_methods: true, with_billing_details: true } - ), - getSubscription(orgId, billingAccountId) - ]); - - if (resp?.data) { - const paymentMethods = resp?.data?.payment_methods || []; - setBillingAccount(resp.data.billing_account); - setBillingDetails(resp.data.billing_details); - const defaultPaymentMethod = - getDefaultPaymentMethod(paymentMethods); - setPaymentMethod(defaultPaymentMethod); - } - } else { - setBillingAccount(undefined); - setBillingDetails(undefined); - setActiveSubscription(undefined); - } - } catch (error) { - console.error( - 'frontier:sdk:: There is problem with fetching org billing accounts' - ); - console.error(error); - } finally { - setIsBillingAccountLoading(false); - } - }, - [frontierClient, getSubscription] - ); + const allPlans = (plansData?.plans || []) as Plan[]; - const fetchActiveSubsciption = useCallback(async () => { - if (activeOrganization?.id && billingAccount?.id) { - return getSubscription(activeOrganization?.id, billingAccount?.id); - } - }, [activeOrganization?.id, billingAccount?.id, getSubscription]); - - const fetchAllPlans = useCallback(async () => { - try { - setIsAllPlansLoading(true); - const resp = await frontierClient.frontierServiceListPlans(); - const plans = resp?.data?.plans || []; - setAllPlans(plans); - } catch (err) { - console.error('frontier:sdk:: There is problem with fetching plans'); - console.error(err); - } finally { - setIsAllPlansLoading(false); - } - }, [frontierClient]); + const { activeSubscription, trialSubscription } = useMemo(() => { + const activeSubscription = + billingAccountId && subscriptions.length + ? getActiveSubscription(subscriptions) + : undefined; + const trialSubscription = + billingAccountId && subscriptions.length + ? getTrialingSubscription(subscriptions) + : undefined; + return { activeSubscription, trialSubscription }; + }, [subscriptions, billingAccountId]); - useEffect(() => { - if (activeOrganization?.id) { - getBillingAccount(activeOrganization.id); - fetchAllPlans(); - } - }, [activeOrganization?.id, getBillingAccount, fetchAllPlans]); + const activePlan = useMemo(() => { + return allPlans.find(p => p.id === activeSubscription?.planId); + }, [allPlans, activeSubscription?.planId]); - useEffect(() => { - if (config?.billing?.basePlan) { - setBasePlan(enrichBasePlan(config.billing.basePlan)); - } - }, [config?.billing?.basePlan]); + const trialPlan = useMemo(() => { + return allPlans.find(p => p.id === trialSubscription?.planId); + }, [allPlans, trialSubscription?.planId]); - const fetchOrganizationKyc = useCallback( - async (orgId: string) => { - try { - setIsOrganizationKycLoading(true); - const resp = await frontierClient.frontierServiceGetOrganizationKyc( - orgId - ); - setOrganizationKyc(resp?.data?.organization_kyc); - } catch (err: unknown) { - if (err instanceof AxiosError && err.response?.status === 404) { - console.warn('frontier:sdk:: org kyc details not found'); - setOrganizationKyc({ org_id: orgId, status: false, link: '' }); - } else { - console.error( - 'frontier:sdk:: There is problem with fetching org kyc' - ); - console.error(err); - } - } finally { - setIsOrganizationKycLoading(false); - } - }, - [frontierClient, activeOrganization?.id] - ); + const fetchActiveSubsciption = useCallback(async () => { + return getActiveSubscription(subscriptions); + }, [subscriptions]); - useEffect(() => { - if (activeOrganization?.id) { - fetchOrganizationKyc(activeOrganization?.id); - } - }, [activeOrganization?.id, fetchOrganizationKyc]); + const basePlan = useMemo(() => { + return config?.billing?.basePlan + ? enrichBasePlan(config.billing.basePlan) + : undefined; + }, [config?.billing?.basePlan]); return ( {children} diff --git a/sdks/js/packages/core/react/hooks/useTokens.ts b/sdks/js/packages/core/react/hooks/useTokens.ts index d97564232..76afd56ec 100644 --- a/sdks/js/packages/core/react/hooks/useTokens.ts +++ b/sdks/js/packages/core/react/hooks/useTokens.ts @@ -29,10 +29,10 @@ export const useTokens = () => { ); const fetchTokenBalance = useCallback(() => { - if (client && billingAccount?.org_id && billingAccount?.id) { - getBalance(billingAccount?.org_id, billingAccount.id); + if (client && billingAccount?.orgId && billingAccount?.id) { + getBalance(billingAccount?.orgId, billingAccount.id); } - }, [billingAccount?.org_id, billingAccount?.id, client, getBalance]); + }, [billingAccount?.orgId, billingAccount?.id, client, getBalance]); useEffect(() => { fetchTokenBalance(); diff --git a/sdks/js/packages/core/react/index.ts b/sdks/js/packages/core/react/index.ts index e5d14ae1e..3a904ad38 100644 --- a/sdks/js/packages/core/react/index.ts +++ b/sdks/js/packages/core/react/index.ts @@ -32,3 +32,6 @@ export type { } from '../shared/types'; export { PREFERENCE_OPTIONS } from './utils/constants'; + +export { timestampToDate, timestampToDayjs, isNullTimestamp } from '../utils/timestamp'; +export type { TimeStamp } from '../utils/timestamp'; diff --git a/sdks/js/packages/core/react/utils/index.ts b/sdks/js/packages/core/react/utils/index.ts index c900ed42b..49f66e1e9 100644 --- a/sdks/js/packages/core/react/utils/index.ts +++ b/sdks/js/packages/core/react/utils/index.ts @@ -1,10 +1,4 @@ -import dayjs from 'dayjs'; -import { - V1Beta1Subscription, - BillingAccountAddress, - V1Beta1Plan, - V1Beta1PaymentMethod -} from '~/src'; +import { BillingAccountAddress } from '~/src'; import { BasePlan, IntervalKeys, @@ -15,7 +9,16 @@ import { import { SUBSCRIPTION_STATES } from './constants'; import slugify from 'slugify'; import { NIL as NIL_UUID } from 'uuid'; -import type { RpcStatus } from '~/src'; +import type { GooglerpcStatus } from '~/src'; +import { + FeatureSchema, + PaymentMethod, + Plan, + PlanSchema, + Subscription +} from '@raystack/proton/frontier'; +import { timestampToDayjs } from '../../utils/timestamp'; +import { create } from '@bufbuild/protobuf'; export const AuthTooltipMessage = 'You don’t have access to perform this action'; @@ -30,24 +33,30 @@ export const converBillingAddressToString = ( .join(', '); }; -export const getActiveSubscription = (subscriptions: V1Beta1Subscription[]) => { +export const getActiveSubscription = (subscriptions: Subscription[]) => { const activeSubscriptions = subscriptions .filter( sub => sub.state === SUBSCRIPTION_STATES.ACTIVE || sub.state === SUBSCRIPTION_STATES.PAST_DUE ) - .sort((a, b) => (dayjs(a.updated_at).isAfter(b.updated_at) ? -1 : 1)); + .sort((a, b) => + timestampToDayjs(a.updatedAt)?.isAfter(timestampToDayjs(b.updatedAt)) + ? -1 + : 1 + ); return activeSubscriptions[0]; }; -export const getTrialingSubscription = ( - subscriptions: V1Beta1Subscription[] -) => { +export const getTrialingSubscription = (subscriptions: Subscription[]) => { const activeSubscriptions = subscriptions .filter(sub => sub.state === SUBSCRIPTION_STATES.TRIALING) - .sort((a, b) => (dayjs(a.updated_at).isAfter(b.updated_at) ? -1 : 1)); + .sort((a, b) => + timestampToDayjs(a.updatedAt)?.isAfter(timestampToDayjs(b.updatedAt)) + ? -1 + : 1 + ); return activeSubscriptions[0]; }; @@ -104,7 +113,10 @@ export const getPlanChangeAction = ( } }; -export const checkSimilarPlans = (plan1: V1Beta1Plan, plan2: V1Beta1Plan) => { +export const checkSimilarPlans = ( + plan1: Plan = create(PlanSchema, {}), + plan2: Plan = create(PlanSchema, {}) +) => { const plan1Metadata = (plan1.metadata as Record) || {}; const plan2Metadata = (plan2.metadata as Record) || {}; const plan1Slug = plan1Metadata?.plan_group_id || makePlanSlug(plan1); @@ -129,12 +141,12 @@ interface getPlanNameWithIntervalOptions { hyphenSeperated?: boolean; } -export function getPlanIntervalName(plan: V1Beta1Plan = {}) { +export function getPlanIntervalName(plan: Plan = create(PlanSchema, {})) { return IntervalLabelMap[plan?.interval as IntervalKeys]; } export function getPlanNameWithInterval( - plan: V1Beta1Plan = {}, + plan: Plan = create(PlanSchema, {}), { hyphenSeperated }: getPlanNameWithIntervalOptions = {} ) { const interval = getPlanIntervalName(plan); @@ -143,7 +155,7 @@ export function getPlanNameWithInterval( : `${plan?.title} (${interval})`; } -export function makePlanSlug(plan: V1Beta1Plan): string { +export function makePlanSlug(plan: Plan): string { const productIds = plan?.products ?.map(p => p.id) .sort() @@ -152,7 +164,7 @@ export function makePlanSlug(plan: V1Beta1Plan): string { return `${titleSlug}-${productIds}`; } -export function getPlanPrice(plan: V1Beta1Plan) { +export function getPlanPrice(plan: Plan) { const planInterval = (plan?.interval || '') as IntervalKeys; return ( plan?.products?.reduce((acc, product) => { @@ -167,9 +179,7 @@ export function getPlanPrice(plan: V1Beta1Plan) { ); } -export function getDefaultPaymentMethod( - paymentMethods: V1Beta1PaymentMethod[] = [] -) { +export function getDefaultPaymentMethod(paymentMethods: PaymentMethod[] = []) { const defaultMethod = paymentMethods.find(pm => { const metadata = pm.metadata as PaymentMethodMetadata; return metadata.default; @@ -178,14 +188,14 @@ export function getDefaultPaymentMethod( return defaultMethod ? defaultMethod : paymentMethods[0]; } -export const enrichBasePlan = (plan?: BasePlan): V1Beta1Plan | undefined => { +export const enrichBasePlan = (plan?: BasePlan): Plan | undefined => { const features = Object.entries(plan?.features || {}).map(([key, value]) => { - return { + return create(FeatureSchema, { title: key, metadata: { [plan?.title || '']: value } - }; + }); }); return plan ? { @@ -194,9 +204,9 @@ export const enrichBasePlan = (plan?: BasePlan): V1Beta1Plan | undefined => { interval: 'year', products: [ { + ...plan.products?.[0], name: plan.title, - features: features, - ...plan.products?.[0] + features: features } ] } @@ -208,7 +218,7 @@ export const defaultFetch = (...fetchParams: Parameters) => export interface HttpErrorResponse extends Response { data: unknown; - error: RpcStatus; + error: GooglerpcStatus; } export const handleSelectValueChange = (onChange: (value: string) => void) => { diff --git a/sdks/js/packages/core/src/types.ts b/sdks/js/packages/core/src/types.ts index f7a0b09a3..e74fc843c 100644 --- a/sdks/js/packages/core/src/types.ts +++ b/sdks/js/packages/core/src/types.ts @@ -1,5 +1,5 @@ import React from 'react'; -import { V1Beta1Feature, V1Beta1Plan } from '.'; +import { Feature, Plan } from '@raystack/proton/frontier'; export interface Strategy { name: string; @@ -87,7 +87,7 @@ export interface IntervalPricingWithPlan extends IntervalPricing { planName: string; interval: IntervalKeys; weightage: number; - features: Record; + features: Record; trial_days: string; productNames: string[]; } @@ -104,7 +104,7 @@ export interface PaymentMethodMetadata extends Record { default?: boolean; } -export interface BasePlan extends Omit { +export interface BasePlan extends Omit { features?: Record; title: string; } diff --git a/sdks/js/packages/core/utils/timestamp.ts b/sdks/js/packages/core/utils/timestamp.ts new file mode 100644 index 000000000..ea2a69f99 --- /dev/null +++ b/sdks/js/packages/core/utils/timestamp.ts @@ -0,0 +1,32 @@ +import { timestampDate, Timestamp } from '@bufbuild/protobuf/wkt'; +import dayjs, { Dayjs } from 'dayjs'; + +/** + * Converts a ConnectRPC Timestamp to a JavaScript Date + */ +export function timestampToDate(timestamp?: Timestamp): Date | null { + if (!timestamp) return null; + + // Use protobuf WKT utility + return timestampDate(timestamp); +} + +/** + * Converts a ConnectRPC Timestamp to a Dayjs object + */ +export function timestampToDayjs(timestamp?: Timestamp): Dayjs | null { + const date = timestampToDate(timestamp); + return date ? dayjs(date) : null; +} + +/** + * Checks if a ConnectRPC Timestamp is the null time (0001-01-01T00:00:00Z) + */ +export function isNullTimestamp(timestamp?: Timestamp): boolean { + if (!timestamp) return true; + + // Null timestamp has very negative seconds value + return timestamp.seconds <= BigInt(0); +} + +export type TimeStamp = Timestamp; \ No newline at end of file diff --git a/sdks/js/pnpm-lock.yaml b/sdks/js/pnpm-lock.yaml index e0168a594..fee151a39 100644 --- a/sdks/js/pnpm-lock.yaml +++ b/sdks/js/pnpm-lock.yaml @@ -46,8 +46,8 @@ importers: specifier: ^3.10.0 version: 3.10.0(react-hook-form@7.57.0(react@18.3.1)) '@raystack/proton': - specifier: 0.1.0-2dbafa5913a214851fdc4f1beefbe27c4a6de55a - version: 0.1.0-2dbafa5913a214851fdc4f1beefbe27c4a6de55a(@tanstack/query-core@5.83.0)(@tanstack/react-query@5.83.0(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: 0.1.0-fba39927b8b974dc1cc1ae0f05f1390580ec6d58 + version: 0.1.0-fba39927b8b974dc1cc1ae0f05f1390580ec6d58(@tanstack/query-core@5.83.0)(@tanstack/react-query@5.83.0(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@tanstack/react-query': specifier: ^5.83.0 version: 5.83.0(react@18.3.1) @@ -2098,8 +2098,8 @@ packages: resolution: {integrity: sha512-UJM1mgqtY7WphnI0uZNDofjCmtpV6OGfjO5JEBfBTUI+aRfJ1YLWFDP7d+S8c2qZxc9wvupyrvqGWRJB+jMxeQ==} engines: {node: '>=18'} - '@raystack/proton@0.1.0-2dbafa5913a214851fdc4f1beefbe27c4a6de55a': - resolution: {integrity: sha512-YSDOtLJn28NiqSptZmLIFTLpQ+utzP/8i+2/aARBtXXuK2dpjKLKkRRwZMX2P67YKcgSswgaqpKfgEaPuQhyfg==} + '@raystack/proton@0.1.0-fba39927b8b974dc1cc1ae0f05f1390580ec6d58': + resolution: {integrity: sha512-cPJesmAHojNJf9ADrNMMwwHlhE+FaWL4PD8sBG76Nqks5ojJ1hrp+9KxkoP1hCKzZnPrfOE7/DNnM5UMcBz1ig==} peerDependencies: '@tanstack/react-query': ^5.0.0 peerDependenciesMeta: @@ -9351,7 +9351,7 @@ snapshots: - '@types/react-dom' - react-dom - '@raystack/proton@0.1.0-2dbafa5913a214851fdc4f1beefbe27c4a6de55a(@tanstack/query-core@5.83.0)(@tanstack/react-query@5.83.0(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@raystack/proton@0.1.0-fba39927b8b974dc1cc1ae0f05f1390580ec6d58(@tanstack/query-core@5.83.0)(@tanstack/react-query@5.83.0(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@bufbuild/protobuf': 2.6.3 '@connectrpc/connect': 2.0.3(@bufbuild/protobuf@2.6.3) @@ -11159,7 +11159,7 @@ snapshots: debug: 4.4.1 enhanced-resolve: 5.17.1 eslint: 7.32.0 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@7.32.0) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.30.0)(eslint@7.32.0))(eslint@7.32.0) fast-glob: 3.3.2 get-tsconfig: 4.8.1 is-bun-module: 1.2.1 @@ -11172,7 +11172,7 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@7.32.0): + eslint-module-utils@2.12.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.30.0)(eslint@7.32.0))(eslint@7.32.0): dependencies: debug: 3.2.7 optionalDependencies: @@ -11194,7 +11194,7 @@ snapshots: doctrine: 2.1.0 eslint: 7.32.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@7.32.0) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.30.0)(eslint@7.32.0))(eslint@7.32.0) hasown: 2.0.2 is-core-module: 2.15.1 is-glob: 4.0.3