From 76e6ee74bac4cb4ca803b78764ffae09c46507df Mon Sep 17 00:00:00 2001 From: Michal Kleszcz Date: Mon, 6 May 2024 15:15:26 +0200 Subject: [PATCH] feat: Adjust subscriptions routing Add TenantRoleAccess component, fix subscriptions routing, add Subscription tab to tenant settings page --- .../editPaymentMethod.component.tsx | 6 +-- .../currentSubscription.component.tsx | 4 +- .../tenantRoleAccess.component.spec.tsx | 37 +++++++++++++++++++ .../src/components/tenantRoleAccess/index.ts | 1 + .../tenantRoleAccess.component.tsx | 14 +++++++ .../tenantSettings.component.tsx | 6 +++ packages/webapp/src/app/app.component.tsx | 29 +++++++-------- .../layout/sidebar/sidebar.component.tsx | 21 +++++++++-- .../__tests__/roleAccess.component.spec.tsx | 6 +-- 9 files changed, 97 insertions(+), 27 deletions(-) create mode 100644 packages/webapp-libs/webapp-tenants/src/components/tenantRoleAccess/__tests__/tenantRoleAccess.component.spec.tsx create mode 100644 packages/webapp-libs/webapp-tenants/src/components/tenantRoleAccess/index.ts create mode 100644 packages/webapp-libs/webapp-tenants/src/components/tenantRoleAccess/tenantRoleAccess.component.tsx diff --git a/packages/webapp-libs/webapp-finances/src/routes/editPaymentMethod/editPaymentMethod.component.tsx b/packages/webapp-libs/webapp-finances/src/routes/editPaymentMethod/editPaymentMethod.component.tsx index 4c3404468..f7947d723 100644 --- a/packages/webapp-libs/webapp-finances/src/routes/editPaymentMethod/editPaymentMethod.component.tsx +++ b/packages/webapp-libs/webapp-finances/src/routes/editPaymentMethod/editPaymentMethod.component.tsx @@ -1,7 +1,7 @@ import { PageHeadline } from '@sb/webapp-core/components/pageHeadline'; import { PageLayout } from '@sb/webapp-core/components/pageLayout'; -import { useGenerateLocalePath } from '@sb/webapp-core/hooks'; import { useToast } from '@sb/webapp-core/toast/useToast'; +import { useGenerateTenantPath } from '@sb/webapp-tenants/hooks'; import { Elements } from '@stripe/react-stripe-js'; import { FormattedMessage, useIntl } from 'react-intl'; import { useNavigate } from 'react-router-dom'; @@ -14,7 +14,7 @@ export const EditPaymentMethod = () => { const intl = useIntl(); const { toast } = useToast(); const navigate = useNavigate(); - const generateLocalePath = useGenerateLocalePath(); + const generateTenantPath = useGenerateTenantPath(); const successMessage = intl.formatMessage({ defaultMessage: 'Payment method changed successfully', @@ -34,7 +34,7 @@ export const EditPaymentMethod = () => { { - navigate(generateLocalePath(RoutesConfig.subscriptions.index)); + navigate(generateTenantPath(RoutesConfig.subscriptions.index)); toast({ description: successMessage }); }} /> diff --git a/packages/webapp-libs/webapp-finances/src/routes/subscriptions/currentSubscription.component.tsx b/packages/webapp-libs/webapp-finances/src/routes/subscriptions/currentSubscription.component.tsx index ba89b0062..731b71206 100644 --- a/packages/webapp-libs/webapp-finances/src/routes/subscriptions/currentSubscription.component.tsx +++ b/packages/webapp-libs/webapp-finances/src/routes/subscriptions/currentSubscription.component.tsx @@ -17,11 +17,11 @@ export const Subscriptions = () => { return ( } + header={} subheader={ } /> diff --git a/packages/webapp-libs/webapp-tenants/src/components/tenantRoleAccess/__tests__/tenantRoleAccess.component.spec.tsx b/packages/webapp-libs/webapp-tenants/src/components/tenantRoleAccess/__tests__/tenantRoleAccess.component.spec.tsx new file mode 100644 index 000000000..d009ef85f --- /dev/null +++ b/packages/webapp-libs/webapp-tenants/src/components/tenantRoleAccess/__tests__/tenantRoleAccess.component.spec.tsx @@ -0,0 +1,37 @@ +import { TenantUserRole } from '@sb/webapp-api-client'; +import { currentUserFactory, fillCommonQueryWithUser } from '@sb/webapp-api-client/tests/factories'; +import { screen } from '@testing-library/react'; + +import { membershipFactory, tenantFactory } from '../../../tests/factories/tenant'; +import { PLACEHOLDER_CONTENT, PLACEHOLDER_TEST_ID, render } from '../../../tests/utils/rendering'; +import { RoleAccessProps, TenantRoleAccess } from '../tenantRoleAccess.component'; + +describe('TenantRoleAccess: Component', () => { + const defaultProps: RoleAccessProps = { + allowedRoles: [TenantUserRole.ADMIN], + children: PLACEHOLDER_CONTENT, + }; + + const Component = (props: Partial) => ; + + const createUser = (role: TenantUserRole) => { + const currentUserMembership = membershipFactory({ role }); + const tenant = tenantFactory({ membership: currentUserMembership }); + return currentUserFactory({ tenants: [tenant] }); + }; + + it('should render children if user has allowed role', async () => { + const user = createUser(TenantUserRole.ADMIN); + const apolloMocks = [fillCommonQueryWithUser(user)]; + render(, { apolloMocks }); + expect(await screen.findByTestId(PLACEHOLDER_TEST_ID)).toBeInTheDocument(); + }); + + it('should render nothing if user doesnt have allowed role', async () => { + const user = createUser(TenantUserRole.MEMBER); + const apolloMocks = [fillCommonQueryWithUser(user)]; + const { waitForApolloMocks } = render(, { apolloMocks }); + await waitForApolloMocks(); + expect(screen.queryByTestId(PLACEHOLDER_TEST_ID)).not.toBeInTheDocument(); + }); +}); diff --git a/packages/webapp-libs/webapp-tenants/src/components/tenantRoleAccess/index.ts b/packages/webapp-libs/webapp-tenants/src/components/tenantRoleAccess/index.ts new file mode 100644 index 000000000..262288559 --- /dev/null +++ b/packages/webapp-libs/webapp-tenants/src/components/tenantRoleAccess/index.ts @@ -0,0 +1 @@ +export { TenantRoleAccess } from './tenantRoleAccess.component'; diff --git a/packages/webapp-libs/webapp-tenants/src/components/tenantRoleAccess/tenantRoleAccess.component.tsx b/packages/webapp-libs/webapp-tenants/src/components/tenantRoleAccess/tenantRoleAccess.component.tsx new file mode 100644 index 000000000..b360f87af --- /dev/null +++ b/packages/webapp-libs/webapp-tenants/src/components/tenantRoleAccess/tenantRoleAccess.component.tsx @@ -0,0 +1,14 @@ +import { TenantUserRole } from '@sb/webapp-api-client'; +import { ReactNode } from 'react'; + +import { useTenantRoleAccessCheck } from '../../hooks'; + +export type RoleAccessProps = { + children: ReactNode; + allowedRoles?: TenantUserRole | TenantUserRole[]; +}; + +export const TenantRoleAccess = ({ children, allowedRoles }: RoleAccessProps) => { + const { isAllowed } = useTenantRoleAccessCheck(allowedRoles); + return <>{isAllowed ? children : null}; +}; diff --git a/packages/webapp-libs/webapp-tenants/src/routes/tenantSettings/tenantSettings.component.tsx b/packages/webapp-libs/webapp-tenants/src/routes/tenantSettings/tenantSettings.component.tsx index 1abf8e5fa..443e5600c 100644 --- a/packages/webapp-libs/webapp-tenants/src/routes/tenantSettings/tenantSettings.component.tsx +++ b/packages/webapp-libs/webapp-tenants/src/routes/tenantSettings/tenantSettings.component.tsx @@ -1,6 +1,7 @@ import { PageHeadline } from '@sb/webapp-core/components/pageHeadline'; import { PageLayout } from '@sb/webapp-core/components/pageLayout'; import { Tabs, TabsList, TabsTrigger } from '@sb/webapp-core/components/tabs'; +import { RoutesConfig as FinancesRoutesConfig } from '@sb/webapp-finances/config/routes'; import { FormattedMessage } from 'react-intl'; import { Link, Outlet, useLocation } from 'react-router-dom'; @@ -29,6 +30,11 @@ export const TenantSettings = () => { + + + + + diff --git a/packages/webapp/src/app/app.component.tsx b/packages/webapp/src/app/app.component.tsx index 0cc245e5e..60003f46f 100644 --- a/packages/webapp/src/app/app.component.tsx +++ b/packages/webapp/src/app/app.component.tsx @@ -58,23 +58,22 @@ export const App = () => { } /> } /> - - - }> - }> - } /> - } /> - } - /> + }> + }> + } /> + } /> + } + /> + + } /> + } /> + } /> - } /> - } /> - } /> + } /> + } /> - } /> - } /> } /> } /> } /> diff --git a/packages/webapp/src/shared/components/layout/sidebar/sidebar.component.tsx b/packages/webapp/src/shared/components/layout/sidebar/sidebar.component.tsx index dbc58d527..e9c3addb1 100644 --- a/packages/webapp/src/shared/components/layout/sidebar/sidebar.component.tsx +++ b/packages/webapp/src/shared/components/layout/sidebar/sidebar.component.tsx @@ -1,3 +1,4 @@ +import { TenantUserRole } from '@sb/webapp-api-client'; import { Alert, AlertDescription, AlertTitle } from '@sb/webapp-core/components/alert'; import { Link } from '@sb/webapp-core/components/buttons'; import { buttonVariants } from '@sb/webapp-core/components/buttons/button/button.styles'; @@ -5,6 +6,7 @@ import { Separator } from '@sb/webapp-core/components/separator'; import { useGenerateLocalePath, useMediaQuery } from '@sb/webapp-core/hooks'; import { cn } from '@sb/webapp-core/lib/utils'; import { media } from '@sb/webapp-core/theme'; +import { TenantRoleAccess } from '@sb/webapp-tenants/components/tenantRoleAccess'; import { useGenerateTenantPath } from '@sb/webapp-tenants/hooks'; import { X } from 'lucide-react'; import { HTMLAttributes, useCallback, useContext } from 'react'; @@ -94,7 +96,7 @@ export const Sidebar = (props: HTMLAttributes) => { - + ) => { > - + - + ) => { > - + ) => { + + + + + +

diff --git a/packages/webapp/src/shared/components/roleAccess/__tests__/roleAccess.component.spec.tsx b/packages/webapp/src/shared/components/roleAccess/__tests__/roleAccess.component.spec.tsx index 82f9a35e9..ee76a2062 100644 --- a/packages/webapp/src/shared/components/roleAccess/__tests__/roleAccess.component.spec.tsx +++ b/packages/webapp/src/shared/components/roleAccess/__tests__/roleAccess.component.spec.tsx @@ -3,15 +3,15 @@ import { screen } from '@testing-library/react'; import { Role } from '../../../../modules/auth/auth.types'; import { PLACEHOLDER_CONTENT, PLACEHOLDER_TEST_ID, render } from '../../../../tests/utils/rendering'; -import { RoleAccess, RoleAccessProps } from '../roleAccess.component'; +import { RoleAccess, TenantRoleAccessProps } from '../roleAccess.component'; describe('RoleAccess: Component', () => { - const defaultProps: RoleAccessProps = { + const defaultProps: TenantRoleAccessProps = { allowedRoles: [Role.ADMIN], children: PLACEHOLDER_CONTENT, }; - const Component = (props: Partial) => ; + const Component = (props: Partial) => ; it('should render children if user has allowed role', async () => { const apolloMocks = [fillCommonQueryWithUser(currentUserFactory({ roles: [Role.ADMIN] }))];