Skip to content

Commit

Permalink
feat(sdk): add billing permission check (#486)
Browse files Browse the repository at this point in the history
* feat: check permission to show billing on sidebar

* feat: hide plan action button based on user permission
  • Loading branch information
rsbh authored Feb 6, 2024
1 parent a547e70 commit 915afbc
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 19 deletions.
68 changes: 55 additions & 13 deletions sdks/js/packages/core/react/components/organization/plans/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import { PlanChangeAction, getPlanChangeAction } from '~/react/utils';
import Amount from '../../helpers/Amount';
import { Outlet, useNavigate } from '@tanstack/react-router';
import { usePlans } from './hooks/usePlans';
import { PERMISSIONS, shouldShowComponent } from '~/utils';
import { usePermissions } from '~/react/hooks/usePermissions';

const PlansLoader = () => {
return (
Expand Down Expand Up @@ -82,11 +84,13 @@ const PlansHeader = ({ billingSupportEmail }: PlansHeaderProps) => {
const PlanPricingColumn = ({
plan,
featureMap = {},
currentPlan
currentPlan,
allowAction
}: {
plan: PlanIntervalPricing;
featureMap: Record<string, V1Beta1Feature>;
currentPlan?: IntervalPricingWithPlan;
allowAction: boolean;
}) => {
const { config } = useFrontier();

Expand Down Expand Up @@ -119,7 +123,8 @@ const PlanPricingColumn = ({
return {
disabled: true,
btnLabel: 'Current Plan',
btnLoadingLabel: 'Current Plan'
btnLoadingLabel: 'Current Plan',
btnDoneLabel: ''
};
}

Expand Down Expand Up @@ -179,14 +184,16 @@ const PlanPricingColumn = ({
</Text>
</Flex>
<Flex direction="column" gap="medium">
<Button
variant={'secondary'}
className={plansStyles.planActionBtn}
onClick={onPlanActionClick}
disabled={action?.disabled || isLoading}
>
{isLoading ? `${action.btnLoadingLabel}....` : action.btnLabel}
</Button>
{allowAction ? (
<Button
variant={'secondary'}
className={plansStyles.planActionBtn}
onClick={onPlanActionClick}
disabled={action?.disabled || isLoading}
>
{isLoading ? `${action.btnLoadingLabel}....` : action.btnLabel}
</Button>
) : null}
{planIntervals.length > 1 ? (
<ToggleGroup
className={plansStyles.plansIntervalList}
Expand Down Expand Up @@ -246,9 +253,14 @@ const PlanPricingColumn = ({
interface PlansListProps {
plans: V1Beta1Plan[];
currentPlanId: string;
allowAction: boolean;
}

const PlansList = ({ plans = [], currentPlanId }: PlansListProps) => {
const PlansList = ({
plans = [],
currentPlanId,
allowAction
}: PlansListProps) => {
if (plans.length === 0) return <NoPlans />;

const groupedPlans = groupPlansPricingByInterval(plans).sort(
Expand Down Expand Up @@ -303,6 +315,7 @@ const PlansList = ({ plans = [], currentPlanId }: PlansListProps) => {
key={plan.slug}
featureMap={featuresMap}
currentPlan={currentPlanPricing}
allowAction={allowAction}
/>
))}
</Flex>
Expand All @@ -312,10 +325,36 @@ const PlansList = ({ plans = [], currentPlanId }: PlansListProps) => {
};

export default function Plans() {
const { config, client, activeSubscription } = useFrontier();
const { config, client, activeSubscription, activeOrganization } =
useFrontier();
const [isPlansLoading, setIsPlansLoading] = useState(false);
const [plans, setPlans] = useState<V1Beta1Plan[]>([]);

const resource = `app/organization:${activeOrganization?.id}`;
const listOfPermissionsToCheck = useMemo(
() => [
{
permission: PERMISSIONS.UpdatePermission,
resource
}
],
[resource]
);

const { permissions, isFetching: isPermissionsFetching } = usePermissions(
listOfPermissionsToCheck,
!!activeOrganization?.id
);

const { canChangePlan } = useMemo(() => {
return {
canChangePlan: shouldShowComponent(
permissions,
`${PERMISSIONS.UpdatePermission}::${resource}`
)
};
}, [permissions, resource]);

useEffect(() => {
async function getPlans() {
setIsPlansLoading(true);
Expand All @@ -337,6 +376,8 @@ export default function Plans() {
getPlans();
}, [client]);

const isLoading = isPlansLoading || isPermissionsFetching;

return (
<Flex direction="column" style={{ width: '100%', overflow: 'hidden' }}>
<Flex style={styles.header}>
Expand All @@ -346,12 +387,13 @@ export default function Plans() {
<Flex direction="column">
<PlansHeader billingSupportEmail={config.billing?.supportEmail} />
</Flex>
{isPlansLoading ? (
{isLoading ? (
<PlansLoader />
) : (
<PlansList
plans={plans}
currentPlanId={activeSubscription?.plan_id || ''}
allowAction={canChangePlan}
/>
)}
</Flex>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export type NavigationItemsTypes = {

interface getOrganizationNavItemsOptions {
tempShowBilling?: boolean;
canSeeBilling?: boolean;
}

export const getOrganizationNavItems = (
Expand Down Expand Up @@ -41,7 +42,7 @@ export const getOrganizationNavItems = (
{
name: 'Billing',
to: '/billing',
show: options?.tempShowBilling
show: options?.tempShowBilling && options?.canSeeBilling
},
{
name: 'Plans',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,23 @@ import {
useRouterContext,
useRouterState
} from '@tanstack/react-router';
import { useCallback, useState } from 'react';
import { useCallback, useMemo, useState } from 'react';
import organization from '~/react/assets/organization.png';
import user from '~/react/assets/user.png';
import { getOrganizationNavItems, userNavItems } from './helpers';

// @ts-ignore
import { MagnifyingGlassIcon } from '@radix-ui/react-icons';
import styles from './sidebar.module.css';
import { usePermissions } from '~/react/hooks/usePermissions';
import { PERMISSIONS, shouldShowComponent } from '~/utils';

export const Sidebar = () => {
const [search, setSearch] = useState('');
const routerState = useRouterState();
const routerContext = useRouterContext({ from: '__root__' });
const { organizationId, tempShowBilling } = useRouterContext({
from: '__root__'
});

const isActive = useCallback(
(path: string) =>
Expand All @@ -34,9 +38,39 @@ export const Sidebar = () => {
[routerState.location.pathname]
);

const organizationNavItems = getOrganizationNavItems({
tempShowBilling: routerContext.tempShowBilling
});
const resource = `app/organization:${organizationId}`;
const listOfPermissionsToCheck = useMemo(
() => [
{
permission: PERMISSIONS.UpdatePermission,
resource
}
],
[resource]
);

const { permissions, isFetching: isPermissionsFetching } = usePermissions(
listOfPermissionsToCheck,
!!organizationId
);

const { canSeeBilling } = useMemo(() => {
return {
canSeeBilling: shouldShowComponent(
permissions,
`${PERMISSIONS.UpdatePermission}::${resource}`
)
};
}, [permissions, resource]);

const organizationNavItems = useMemo(
() =>
getOrganizationNavItems({
tempShowBilling: tempShowBilling,
canSeeBilling: canSeeBilling
}),
[tempShowBilling, canSeeBilling]
);

return (
<SidebarComponent>
Expand Down

0 comments on commit 915afbc

Please sign in to comment.