diff --git a/.changeset/thin-moles-obey.md b/.changeset/thin-moles-obey.md
new file mode 100644
index 0000000000..5e154ea690
--- /dev/null
+++ b/.changeset/thin-moles-obey.md
@@ -0,0 +1,9 @@
+---
+'@clerk/localizations': patch
+'@clerk/clerk-js': patch
+'@clerk/shared': patch
+'@clerk/types': patch
+---
+
+Introduces list of suggestions within
++ Users can request to join a suggested organization
diff --git a/packages/clerk-js/src/ui/components/OrganizationProfile/VerifiedDomainPage.tsx b/packages/clerk-js/src/ui/components/OrganizationProfile/VerifiedDomainPage.tsx
index 6d3725f55e..b2d84e9ef5 100644
--- a/packages/clerk-js/src/ui/components/OrganizationProfile/VerifiedDomainPage.tsx
+++ b/packages/clerk-js/src/ui/components/OrganizationProfile/VerifiedDomainPage.tsx
@@ -26,6 +26,10 @@ export const VerifiedDomainPage = withCardStateProvider(() => {
const enrollmentMode = useFormControl('enrollmentMode', '', {
type: 'radio',
radioOptions: [
+ {
+ value: 'automatic_suggestion',
+ label: 'Automatic suggestion',
+ },
{
value: 'automatic_invitation',
// TODO: Add labels
diff --git a/packages/clerk-js/src/ui/components/OrganizationSwitcher/OrganizationSwitcher.tsx b/packages/clerk-js/src/ui/components/OrganizationSwitcher/OrganizationSwitcher.tsx
index b318712736..f959c37f0c 100644
--- a/packages/clerk-js/src/ui/components/OrganizationSwitcher/OrganizationSwitcher.tsx
+++ b/packages/clerk-js/src/ui/components/OrganizationSwitcher/OrganizationSwitcher.tsx
@@ -12,10 +12,16 @@ const _OrganizationSwitcher = withFloatingTree(() => {
offset: 8,
});
+ /**
+ * Prefetch user invitations and suggestions
+ */
useCoreOrganizationList({
userInvitations: {
infinite: true,
},
+ userSuggestions: {
+ infinite: true,
+ },
});
return (
diff --git a/packages/clerk-js/src/ui/components/OrganizationSwitcher/OtherOrganizationActions.tsx b/packages/clerk-js/src/ui/components/OrganizationSwitcher/OtherOrganizationActions.tsx
index 1acae81650..172f0b8b9f 100644
--- a/packages/clerk-js/src/ui/components/OrganizationSwitcher/OtherOrganizationActions.tsx
+++ b/packages/clerk-js/src/ui/components/OrganizationSwitcher/OtherOrganizationActions.tsx
@@ -7,6 +7,7 @@ import { Action, SecondaryActions } from '../../elements';
import { UserInvitationList } from './UserInvitationList';
import type { UserMembershipListProps } from './UserMembershipList';
import { UserMembershipList } from './UserMembershipList';
+import { UserSuggestionList } from './UserSuggestionList';
export interface OrganizationActionListProps extends UserMembershipListProps {
onCreateOrganizationClick: React.MouseEventHandler;
@@ -45,6 +46,7 @@ export const OrganizationActionList = (props: OrganizationActionListProps) => {
+
);
diff --git a/packages/clerk-js/src/ui/components/OrganizationSwitcher/UserInvitationList.tsx b/packages/clerk-js/src/ui/components/OrganizationSwitcher/UserInvitationList.tsx
index 966640ffe5..b175e26a60 100644
--- a/packages/clerk-js/src/ui/components/OrganizationSwitcher/UserInvitationList.tsx
+++ b/packages/clerk-js/src/ui/components/OrganizationSwitcher/UserInvitationList.tsx
@@ -117,16 +117,15 @@ const AcceptRejectInvitationButtons = (props: UserOrganizationInvitationResource
};
return (
- <>
-
- >
+
);
};
diff --git a/packages/clerk-js/src/ui/components/OrganizationSwitcher/UserSuggestionList.tsx b/packages/clerk-js/src/ui/components/OrganizationSwitcher/UserSuggestionList.tsx
new file mode 100644
index 0000000000..0bf6948d45
--- /dev/null
+++ b/packages/clerk-js/src/ui/components/OrganizationSwitcher/UserSuggestionList.tsx
@@ -0,0 +1,154 @@
+import type { OrganizationSuggestionResource } from '@clerk/types';
+
+import { useCoreOrganizationList } from '../../contexts';
+import { Box, Button, descriptors, Flex, localizationKeys, Spinner, Text } from '../../customizables';
+import { OrganizationPreview, useCardState, withCardStateProvider } from '../../elements';
+import { useInView } from '../../hooks';
+import { common } from '../../styledSystem';
+import { handleError } from '../../utils';
+
+export const UserSuggestionList = () => {
+ const { userSuggestions } = useCoreOrganizationList({
+ userSuggestions: {
+ infinite: true,
+ },
+ });
+
+ const { ref } = useInView({
+ threshold: 0,
+ onChange: inView => {
+ if (inView) {
+ userSuggestions.fetchNext?.();
+ }
+ },
+ });
+
+ if ((userSuggestions.count ?? 0) === 0) {
+ return null;
+ }
+
+ return (
+
+ ({
+ minHeight: 'unset',
+ height: t.space.$12,
+ padding: `${t.space.$3} ${t.space.$6}`,
+ display: 'flex',
+ alignItems: 'center',
+ })}
+ // Handle plurals
+ localizationKey={localizationKeys(
+ (userSuggestions.count ?? 0) > 1
+ ? 'organizationSwitcher.suggestionCountLabel_many'
+ : 'organizationSwitcher.suggestionCountLabel_single',
+ {
+ count: userSuggestions.count,
+ },
+ )}
+ />
+ ({
+ maxHeight: `calc(4 * ${t.sizes.$12})`,
+ overflowY: 'auto',
+ ...common.unstyledScrollbar(t),
+ })}
+ >
+ {userSuggestions?.data?.map(inv => {
+ return (
+
+ );
+ })}
+
+ {(userSuggestions.hasNextPage || userSuggestions.isFetching) && (
+ ({
+ width: '100%',
+ height: t.space.$12,
+ position: 'relative',
+ })}
+ >
+
+
+
+
+ )}
+
+
+ );
+};
+
+const AcceptRejectSuggestionButtons = (props: OrganizationSuggestionResource) => {
+ const card = useCardState();
+ const { userSuggestions } = useCoreOrganizationList({
+ userSuggestions: {
+ infinite: true,
+ },
+ });
+
+ const mutateSwrState = () => {
+ (userSuggestions as any)?.unstable__mutate?.();
+ };
+
+ const handleAccept = () => {
+ return card
+ .runAsync(props.accept())
+ .then(mutateSwrState)
+ .catch(err => handleError(err, [], card.setError));
+ };
+
+ return (
+
+ );
+};
+
+const SuggestionPreview = withCardStateProvider((props: OrganizationSuggestionResource) => {
+ return (
+ ({
+ minHeight: 'unset',
+ height: t.space.$12,
+ justifyContent: 'space-between',
+ padding: `0 ${t.space.$6}`,
+ })}
+ >
+ ({ margin: `0 calc(${t.space.$3}/2)` })}
+ organization={props.publicOrganizationData}
+ size='sm'
+ />
+
+
+
+ );
+});
diff --git a/packages/clerk-js/src/ui/elements/OrganizationPreview.tsx b/packages/clerk-js/src/ui/elements/OrganizationPreview.tsx
index 7036e34df1..6c155fa868 100644
--- a/packages/clerk-js/src/ui/elements/OrganizationPreview.tsx
+++ b/packages/clerk-js/src/ui/elements/OrganizationPreview.tsx
@@ -27,7 +27,7 @@ export const OrganizationPreview = (props: OrganizationPreviewProps) => {
elementId={descriptors.organizationPreview.setId(elementId)}
gap={4}
align='center'
- sx={[{ minWidth: '0px', width: '100%' }, sx]}
+ sx={[{ minWidth: '0' }, sx]}
{...rest}
>
& {
isLoading?: boolean;
loadingText?: string;
diff --git a/packages/localizations/src/en-US.ts b/packages/localizations/src/en-US.ts
index 97f4282eae..260cef2459 100644
--- a/packages/localizations/src/en-US.ts
+++ b/packages/localizations/src/en-US.ts
@@ -530,6 +530,9 @@ export const enUS: LocalizationResource = {
invitationCountLabel_single: '1 pending invitation to join:',
invitationCountLabel_many: '{{count}} pending invitations to join:',
invitationAccept: 'Join',
+ suggestionCountLabel_single: '1 suggested organization:',
+ suggestionCountLabel_many: '{{count}} suggested organizations:',
+ suggestionsAccept: 'Request to join',
},
impersonationFab: {
title: 'Signed in as {{identifier}}',
diff --git a/packages/types/src/json.ts b/packages/types/src/json.ts
index 13a47b46a9..e08541fe07 100644
--- a/packages/types/src/json.ts
+++ b/packages/types/src/json.ts
@@ -364,7 +364,7 @@ export interface OrganizationDomainJSON extends ClerkResourceJSON {
updated_at: number;
}
-export interface PublicOrganizationDataJSON extends ClerkResourceJSON {
+export interface PublicOrganizationDataJSON {
id: string;
name: string;
slug: string | null;
@@ -381,6 +381,15 @@ export interface OrganizationSuggestionJSON extends ClerkResourceJSON {
updated_at: number;
}
+export interface OrganizationSuggestionJSON extends ClerkResourceJSON {
+ object: 'organization_suggestion';
+ id: string;
+ public_organization_data: PublicOrganizationDataJSON;
+ status: OrganizationSuggestionStatus;
+ created_at: number;
+ updated_at: number;
+}
+
export interface OrganizationMembershipRequestJSON extends ClerkResourceJSON {
object: 'organization_membership_request';
id: string;
diff --git a/packages/types/src/localization.ts b/packages/types/src/localization.ts
index 6f2ae449c9..b8f5a01a79 100644
--- a/packages/types/src/localization.ts
+++ b/packages/types/src/localization.ts
@@ -551,6 +551,9 @@ type _LocalizationResource = {
invitationCountLabel_single: LocalizationValue;
invitationCountLabel_many: LocalizationValue;
invitationAccept: LocalizationValue;
+ suggestionCountLabel_single: LocalizationValue;
+ suggestionCountLabel_many: LocalizationValue;
+ suggestionsAccept: LocalizationValue;
};
impersonationFab: {
title: LocalizationValue;