Skip to content

Commit d586173

Browse files
committed
Render route in OrganizationProfile
1 parent a043a15 commit d586173

11 files changed

Lines changed: 146 additions & 8 deletions

File tree

packages/localizations/src/en-US.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -768,6 +768,7 @@ export const enUS: LocalizationResource = {
768768
navbar: {
769769
apiKeys: 'API keys',
770770
billing: 'Billing',
771+
selfServeSso: 'Self-Serve SSO',
771772
description: 'Manage your organization.',
772773
general: 'General',
773774
members: 'Members',

packages/shared/src/internal/clerk-js/componentGuards.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export const noOrganizationExists: ComponentGuard = clerk => {
1818
return !clerk.organization;
1919
};
2020

21+
// TODO -> Update with per org check
2122
export const disabledOrganizationsFeature: ComponentGuard = (_, environment) => {
2223
return !environment?.organizationSettings.enabled;
2324
};

packages/shared/src/types/localization.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1021,6 +1021,7 @@ export type __internal_LocalizationResource = {
10211021
members: LocalizationValue;
10221022
billing: LocalizationValue;
10231023
apiKeys: LocalizationValue;
1024+
selfServeSso: LocalizationValue;
10241025
};
10251026
badge__unverified: LocalizationValue;
10261027
badge__automaticInvitation: LocalizationValue;

packages/ui/src/components/OrganizationProfile/OrganizationProfileRoutes.tsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,22 @@ const OrganizationPaymentAttemptPage = lazy(() =>
3737
})),
3838
);
3939

40+
const OrganizationSelfServeSsoPage = lazy(() =>
41+
import(/* webpackChunkName: "op-self-serve-sso-page"*/ './OrganizationSelfServeSsoPage').then(module => ({
42+
default: module.OrganizationSelfServeSsoPage,
43+
})),
44+
);
45+
4046
export const OrganizationProfileRoutes = () => {
4147
const {
4248
pages,
4349
isMembersPageRoot,
4450
isGeneralPageRoot,
4551
isBillingPageRoot,
4652
isAPIKeysPageRoot,
53+
isSelfServeSsoPageRoot,
4754
shouldShowBilling,
55+
shouldShowSelfServeSso,
4856
apiKeysProps,
4957
} = useOrganizationProfileContext();
5058

@@ -142,6 +150,15 @@ export const OrganizationProfileRoutes = () => {
142150
</Route>
143151
</Protect>
144152
)}
153+
{shouldShowSelfServeSso ? (
154+
<Route path={isSelfServeSsoPageRoot ? undefined : 'organization-self-serve-sso'}>
155+
<Switch>
156+
<Route index>
157+
<OrganizationSelfServeSsoPage />
158+
</Route>
159+
</Switch>
160+
</Route>
161+
) : null}
145162
</Route>
146163
</Switch>
147164
);
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { useOrganization } from '@clerk/shared/react';
2+
3+
import { Col } from '@/ui/customizables';
4+
import { Header } from '@/ui/elements/Header';
5+
6+
export const OrganizationSelfServeSsoPage = () => {
7+
const { organization } = useOrganization();
8+
9+
if (!organization) {
10+
// We should never reach this point, but we'll return null to make TS happy
11+
return null;
12+
}
13+
14+
return (
15+
<Col gap={4}>
16+
<Header.Root>
17+
<Header.Title
18+
title='Self-Serve SSO'
19+
textVariant='h2'
20+
/>
21+
</Header.Root>
22+
</Col>
23+
);
24+
};

packages/ui/src/components/OrganizationProfile/__tests__/OrganizationProfile.test.tsx

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,67 @@ describe('OrganizationProfile', () => {
476476
});
477477
});
478478

479+
describe('Self-Serve SSO visibility', () => {
480+
it('includes Self-Serve SSO when self-serve SSO is enabled in environment settings', async () => {
481+
const { wrapper } = await createFixtures(f => {
482+
f.withEnterpriseSso({ selfServeSso: true });
483+
f.withOrganizations();
484+
f.withUser({
485+
email_addresses: ['test@clerk.com'],
486+
organization_memberships: [
487+
{
488+
name: 'Org1',
489+
permissions: ['org:sys_entconns:manage'],
490+
},
491+
],
492+
});
493+
});
494+
495+
render(<OrganizationProfile />, { wrapper });
496+
expect(await screen.findByText('Self-Serve SSO')).toBeDefined();
497+
});
498+
499+
it('does not include Self-Serve SSO when self-serve SSO is disabled in environment settings', async () => {
500+
const { wrapper } = await createFixtures(f => {
501+
f.withEnterpriseSso({ selfServeSso: false });
502+
f.withOrganizations();
503+
f.withUser({
504+
email_addresses: ['test@clerk.com'],
505+
organization_memberships: [
506+
{
507+
name: 'Org1',
508+
permissions: ['org:sys_entconns:manage'],
509+
},
510+
],
511+
});
512+
});
513+
514+
const { queryByText } = render(<OrganizationProfile />, { wrapper });
515+
await waitFor(() => expect(queryByText('Self-Serve SSO')).toBeNull());
516+
});
517+
518+
// We keep showing the section but the internal page displays a warning for missing permission
519+
it('includes Self-Serve SSO even when the user does not have the manage enterprise connections permission', async () => {
520+
const { wrapper } = await createFixtures(f => {
521+
f.withEnterpriseSso({ selfServeSso: true });
522+
f.withOrganizations();
523+
f.withUser({
524+
email_addresses: ['test@clerk.com'],
525+
organization_memberships: [
526+
{
527+
name: 'Org1',
528+
permissions: [],
529+
},
530+
],
531+
});
532+
});
533+
534+
// TODO -> Add assertions for page content warning
535+
render(<OrganizationProfile />, { wrapper });
536+
expect(await screen.findByText('Self-Serve SSO')).toBeDefined();
537+
});
538+
});
539+
479540
it('removes member nav item if user is lacking permissions', async () => {
480541
const { wrapper } = await createFixtures(f => {
481542
f.withOrganizations();

packages/ui/src/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export const ORGANIZATION_PROFILE_NAVBAR_ROUTE_ID = {
1212
MEMBERS: 'members',
1313
BILLING: 'billing',
1414
API_KEYS: 'apiKeys',
15+
SELF_SERVE_SSO: 'selfServeSso',
1516
};
1617

1718
export const USER_BUTTON_ITEM_ID = {

packages/ui/src/contexts/components/OrganizationProfile.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@ export type OrganizationProfileContextType = OrganizationProfileCtx & {
2525
isGeneralPageRoot: boolean;
2626
isBillingPageRoot: boolean;
2727
isAPIKeysPageRoot: boolean;
28+
isSelfServeSsoPageRoot: boolean;
2829
shouldShowBilling: boolean;
30+
shouldShowSelfServeSso: boolean;
2931
};
3032

3133
export const OrganizationProfileContext = createContext<OrganizationProfileCtx | null>(null);
@@ -56,6 +58,9 @@ export const useOrganizationProfileContext = (): OrganizationProfileContextType
5658
// The C2 had a subscription in the past
5759
Boolean(statements.data.length > 0);
5860

61+
// TODO -> Check for org level as well
62+
const shouldShowSelfServeSso = environment.userSettings.enterpriseSSO.self_serve_sso;
63+
5964
const pages = useMemo(
6065
() => createOrganizationProfileCustomPages(customPages || [], clerk, shouldShowBilling, environment),
6166
[customPages, shouldShowBilling],
@@ -68,6 +73,7 @@ export const useOrganizationProfileContext = (): OrganizationProfileContextType
6873
const isGeneralPageRoot = pages.routes[0].id === ORGANIZATION_PROFILE_NAVBAR_ROUTE_ID.GENERAL;
6974
const isBillingPageRoot = pages.routes[0].id === ORGANIZATION_PROFILE_NAVBAR_ROUTE_ID.BILLING;
7075
const isAPIKeysPageRoot = pages.routes[0].id === ORGANIZATION_PROFILE_NAVBAR_ROUTE_ID.API_KEYS;
76+
const isSelfServeSsoPageRoot = pages.routes[0].id === ORGANIZATION_PROFILE_NAVBAR_ROUTE_ID.SELF_SERVE_SSO;
7177
const navigateToGeneralPageRoot = () =>
7278
navigate(isGeneralPageRoot ? '../' : isMembersPageRoot ? './organization-general' : '../organization-general');
7379

@@ -81,6 +87,8 @@ export const useOrganizationProfileContext = (): OrganizationProfileContextType
8187
isGeneralPageRoot,
8288
isBillingPageRoot,
8389
isAPIKeysPageRoot,
90+
isSelfServeSsoPageRoot,
8491
shouldShowBilling,
92+
shouldShowSelfServeSso,
8593
};
8694
};
Lines changed: 3 additions & 0 deletions
Loading

packages/ui/src/icons/index.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,21 @@ export { default as AuthApp } from './auth-app.svg';
1414
export { default as Billing } from './billing.svg';
1515
export { default as Block } from './block.svg';
1616
export { default as BoxIcon } from './box.svg';
17-
export { default as Caret } from './caret.svg';
1817
export { default as CaretLeft } from './caret-left.svg';
1918
export { default as CaretRight } from './caret-right.svg';
19+
export { default as Caret } from './caret.svg';
2020
export { default as ChatAltIcon } from './chat-alt.svg';
21-
export { default as Check } from './check.svg';
2221
export { default as CheckCircle } from './check-circle.svg';
22+
export { default as Check } from './check.svg';
2323
export { default as CheckmarkFilled } from './checkmark-filled.svg';
2424
export { default as ChevronDown } from './chevron-down.svg';
2525
export { default as ChevronUpDown } from './chevron-up-down.svg';
26-
export { default as Clipboard } from './clipboard.svg';
2726
export { default as ClipboardOutline } from './clipboard-outline.svg';
27+
export { default as Clipboard } from './clipboard.svg';
2828
export { default as Close } from './close.svg';
2929
export { default as Code } from './code.svg';
3030
export { default as CogFilled } from './cog-filled.svg';
31+
export { default as Connections } from './connections.svg';
3132
export { default as Copy } from './copy.svg';
3233
export { default as CreditCard } from './credit-card.svg';
3334
export { default as DeviceLaptop } from './device-laptop.svg';
@@ -38,8 +39,8 @@ export { default as DuotoneAtSymbol } from './duotone-at-symbol.svg';
3839
export { default as Email } from './email.svg';
3940
export { default as ExclamationCircle } from './exclamation-circle.svg';
4041
export { default as ExclamationTriangle } from './exclamation-triangle.svg';
41-
export { default as Eye } from './eye.svg';
4242
export { default as EyeSlash } from './eye-slash.svg';
43+
export { default as Eye } from './eye.svg';
4344
export { default as Fingerprint } from './fingerprint.svg';
4445
export { default as Folder } from './folder.svg';
4546
export { default as GenericPayment } from './generic-pay.svg';
@@ -53,17 +54,17 @@ export { default as Menu } from './menu.svg';
5354
export { default as Minus } from './minus.svg';
5455
export { default as Mobile } from './mobile-small.svg';
5556
export { default as Organization } from './organization.svg';
56-
export { default as Pencil } from './pencil.svg';
5757
export { default as PencilEdit } from './pencil-edit.svg';
58+
export { default as Pencil } from './pencil.svg';
5859
export { default as Plans } from './plans.svg';
5960
export { default as Plus } from './plus.svg';
6061
export { default as Print } from './print.svg';
6162
export { default as QuestionMark } from './question-mark.svg';
6263
export { default as RequestAuthIcon } from './request-auth.svg';
6364
export { default as RotateLeftRight } from './rotate-left-right.svg';
6465
export { default as Selector } from './selector.svg';
65-
export { default as SignOut } from './signout.svg';
6666
export { default as SignOutDouble } from './signout-double.svg';
67+
export { default as SignOut } from './signout.svg';
6768
export { default as SpinnerJumbo } from './spinner-jumbo.svg';
6869
export { default as SwitchArrowRight } from './switch-arrow-right.svg';
6970
export { default as SwitchArrows } from './switch-arrows.svg';

0 commit comments

Comments
 (0)