diff --git a/web/sdk/react/components/organization/billing/cycle-switch/index.tsx b/web/sdk/react/components/organization/billing/cycle-switch/index.tsx index ad4d3d8a8..1c1252966 100644 --- a/web/sdk/react/components/organization/billing/cycle-switch/index.tsx +++ b/web/sdk/react/components/organization/billing/cycle-switch/index.tsx @@ -12,11 +12,11 @@ import { useNavigate, useParams } from '@tanstack/react-router'; import { useFrontier } from '~/react/contexts/FrontierContext'; import { getPlanIntervalName, getPlanPrice } from '~/react/utils'; import * as _ from 'lodash'; -import { usePlans } from '../../plans/hooks/usePlans'; 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 { usePlans } from '~/react/views/plans/hooks/usePlans'; export function ConfirmCycleSwitch() { const { @@ -61,7 +61,7 @@ export function ConfirmCycleSwitch() { const isUpgrade = (Number(nextPlanMetadata?.weightage) || 0) - - (Number(activePlanMetadata?.weightage) || 0) > + (Number(activePlanMetadata?.weightage) || 0) > 0; const isLoading = isAllPlansLoading; @@ -100,8 +100,8 @@ export function ConfirmCycleSwitch() { const cycleSwitchDate = activeSubscription?.currentPeriodEndAt ? timestampToDayjs(activeSubscription?.currentPeriodEndAt)?.format( - config?.dateFormat || DEFAULT_DATE_FORMAT - ) + config?.dateFormat || DEFAULT_DATE_FORMAT + ) : 'the next billing cycle'; return ( diff --git a/web/sdk/react/components/organization/plans/index.tsx b/web/sdk/react/components/organization/plans/index.tsx index 520ade198..490b7995b 100644 --- a/web/sdk/react/components/organization/plans/index.tsx +++ b/web/sdk/react/components/organization/plans/index.tsx @@ -1,189 +1,7 @@ -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 { groupPlansPricingByInterval } from './helpers'; -import { IntervalPricingWithPlan } from '~/src/types'; -import { UpcomingPlanChangeBanner } from '~/react/components/common/upcoming-plan-change-banner'; -import { PlansHeader } from './header'; -import { PlanPricingColumn } from './pricing-column'; -import { useBillingPermission } from '~/react/hooks/useBillingPermission'; -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'; -import sharedStyles from '../styles.module.css'; -import plansStyles from './plans.module.css'; +'use client'; -const PlansLoader = () => { - return ( - - {[...new Array(2)].map((_, i) => ( - - ))} - - ); -}; - -const NoPlans = () => { - return ( - } - heading={No Plans Available} - subHeading={ - 'Sorry, No plans available at this moment. Please try again later' - } - /> - ); -}; - -interface PlansListProps { - plans: Plan[]; - currentPlanId: string; - allowAction: boolean; - features: Feature[]; -} - -const PlansList = ({ - plans = [], - features = [], - currentPlanId, - allowAction -}: PlansListProps) => { - if (plans.length === 0) return ; - - const groupedPlans = groupPlansPricingByInterval(plans).sort( - (a, b) => a.weightage - b.weightage - ); - - let currentPlanPricing: IntervalPricingWithPlan | undefined; - groupedPlans.forEach(group => { - Object.values(group.intervals).forEach(plan => { - if (plan.planId === currentPlanId) { - currentPlanPricing = plan; - } - }); - }); - - const totalFeatures = features.length; - - const featureTitleMap = features.reduce((acc, f) => { - const weightage = - (f.metadata as Record)?.weightage || totalFeatures; - acc[f.title || ''] = weightage; - return acc; - }, {} as Record); - - const sortedFeatures = Object.entries(featureTitleMap) - .sort((f1, f2) => f1[1] - f2[1]) - .map(f => f[0]) - .filter(f => Boolean(f)); - - return ( - - -
-
{''}
- - - - Features - - - {sortedFeatures.map(feature => { - return ( - - - {feature} - - - ); - })} - -
- - {groupedPlans.map(plan => ( - - ))} - -
-
- ); -}; +import { PlansPage } from '~/react/views/plans'; export default function Plans() { - const { - config, - activeSubscription, - isActiveSubscriptionLoading, - isActiveOrganizationLoading, - basePlan, - allPlans, - isAllPlansLoading - } = useFrontier(); - - const { isFetching: isPermissionsFetching, isAllowed: canChangePlan } = - useBillingPermission(); - - const { data: featuresData } = useConnectQuery( - FrontierServiceQueries.listFeatures, - create(ListFeaturesRequestSchema, {}) - ); - - const features = (featuresData?.features || []) as Feature[]; - - const plans = [...(basePlan ? [basePlan] : []), ...allPlans]; - - const isLoading = - isAllPlansLoading || - isPermissionsFetching || - isActiveSubscriptionLoading || - isActiveOrganizationLoading; - - return ( - - - - - - - - {isLoading ? ( - - ) : ( - - )} - - - - - ); + return ; } diff --git a/web/sdk/react/components/organization/routes.tsx b/web/sdk/react/components/organization/routes.tsx index b4d158d6c..ef6d0eb19 100644 --- a/web/sdk/react/components/organization/routes.tsx +++ b/web/sdk/react/components/organization/routes.tsx @@ -30,7 +30,6 @@ import Tokens from './tokens'; import { AddTokens } from './tokens/add-tokens'; import { ConfirmCycleSwitch } from './billing/cycle-switch'; import Plans from './plans'; -import ConfirmPlanChange from './plans/confirm-change'; import APIKeys from './api-keys'; import { AddServiceAccount } from './api-keys/add'; import ServiceUserPage from './api-keys/service-user'; @@ -229,11 +228,6 @@ const plansRoute = createRoute({ component: Plans }); -const planDowngradeRoute = createRoute({ - getParentRoute: () => plansRoute, - path: '/confirm-change/$planId', - component: ConfirmPlanChange -}); const tokensRoute = createRoute({ getParentRoute: () => rootRoute, @@ -320,7 +314,7 @@ export function getRootTree({ customScreens = [] }: getRootTreeOptions) { profileRoute, preferencesRoute, billingRoute.addChildren([switchBillingCycleModalRoute]), - plansRoute.addChildren([planDowngradeRoute]), + plansRoute, tokensRoute.addChildren([addTokensRoute]), apiKeysRoute.addChildren([ addServiceAccountRoute, diff --git a/web/sdk/react/components/organization/plans/confirm-change/index.tsx b/web/sdk/react/views/plans/confirm-plan-change-dialog.tsx similarity index 89% rename from web/sdk/react/components/organization/plans/confirm-change/index.tsx rename to web/sdk/react/views/plans/confirm-plan-change-dialog.tsx index 305699d28..057e1ba5a 100644 --- a/web/sdk/react/components/organization/plans/confirm-change/index.tsx +++ b/web/sdk/react/views/plans/confirm-plan-change-dialog.tsx @@ -8,7 +8,6 @@ import { Flex, Dialog } from '@raystack/apsara'; -import { useNavigate, useParams } from '@tanstack/react-router'; import * as _ from 'lodash'; import { useFrontier } from '~/react/contexts/FrontierContext'; import { @@ -16,17 +15,25 @@ import { DEFAULT_PLAN_UPGRADE_MESSAGE } from '~/react/utils/constants'; import { getPlanChangeAction, getPlanNameWithInterval } from '~/react/utils'; -import planStyles from '../plans.module.css'; -import { usePlans } from '../hooks/usePlans'; +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 orgStyles from '../../components/organization/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' }); +export interface ConfirmPlanChangeDialogProps { + open: boolean; + onOpenChange?: (value: boolean) => void; + planId: string; +} + +export function ConfirmPlanChangeDialog({ + open, + onOpenChange, + planId +}: ConfirmPlanChangeDialogProps) { const { activePlan, isAllPlansLoading, @@ -63,7 +70,7 @@ export default function ConfirmPlanChange() { Number(activePlanMetadata?.weightage) ); - const cancel = useCallback(() => navigate({ to: '/plans' }), [navigate]); + const handleClose = useCallback(() => onOpenChange?.(false), [onOpenChange]); const newPlanSlug = isNewPlanBasePlan ? 'base' : newPlan?.name; const planChangeSlug = activePlan?.name @@ -88,10 +95,10 @@ export default function ConfirmPlanChange() { toast.success(`Plan ${actionName} successful`, { description: `Your plan will ${actionName} on ${changeDate}` }); - cancel(); + handleClose(); } }, [ - cancel, + handleClose, config?.dateFormat, planAction?.btnLabel, planId, @@ -139,10 +146,10 @@ export default function ConfirmPlanChange() { }, [isNewPlanBasePlan, basePlan, currentPlan]); useEffect(() => { - if (planId) { + if (planId && open) { getPlan(); } - }, [getPlan, planId]); + }, [getPlan, planId, open]); const isLoading = isAllPlansLoading || isNewPlanLoading; @@ -161,10 +168,10 @@ export default function ConfirmPlanChange() { : 'the next billing cycle'; return ( - + @@ -180,7 +187,7 @@ export default function ConfirmPlanChange() { alt="cross" style={{ cursor: 'pointer' }} src={cross as unknown as string} - onClick={cancel} + onClick={handleClose} data-test-id="frontier-sdk-confirm-plan-change-close-button" /> @@ -232,7 +239,7 @@ export default function ConfirmPlanChange() {