From 75f24a5545dc50483d94a989ba379fdcf0e6ece0 Mon Sep 17 00:00:00 2001 From: Laura Beatris <48022589+LauraBeatris@users.noreply.github.com> Date: Sat, 18 Jan 2025 11:38:04 -0300 Subject: [PATCH 01/21] chore: Add `query` param to memberships query --- packages/shared/src/react/hooks/useOrganization.tsx | 1 + packages/types/src/organization.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/shared/src/react/hooks/useOrganization.tsx b/packages/shared/src/react/hooks/useOrganization.tsx index aa07e970651..c0e44dbf937 100644 --- a/packages/shared/src/react/hooks/useOrganization.tsx +++ b/packages/shared/src/react/hooks/useOrganization.tsx @@ -163,6 +163,7 @@ export const useOrganization: UseOrganization = params => { initialPage: membersSafeValues.initialPage, pageSize: membersSafeValues.pageSize, role: membersSafeValues.role, + query: membersSafeValues.query, }; const invitationsParams = diff --git a/packages/types/src/organization.ts b/packages/types/src/organization.ts index a749bb6d9b5..fc487ad4c60 100644 --- a/packages/types/src/organization.ts +++ b/packages/types/src/organization.ts @@ -64,6 +64,7 @@ export type GetRolesParams = ClerkPaginationParams; export type GetMembersParams = ClerkPaginationParams<{ role?: OrganizationCustomRoleKey[]; + query?: string; }>; export type GetDomainsParams = ClerkPaginationParams<{ From 45afbc88f4ebe645cbfdeee84c74274393c97dd9 Mon Sep 17 00:00:00 2001 From: Laura Beatris <48022589+LauraBeatris@users.noreply.github.com> Date: Sat, 18 Jan 2025 11:38:49 -0300 Subject: [PATCH 02/21] chore: Add initial UI implementation --- .../OrganizationProfile/MembersActions.tsx | 2 +- .../OrganizationProfile/MembersSearch.tsx | 35 +++++++++++++++++++ .../OrganizationMembers.tsx | 19 +++++++++- .../ui/components/UserProfile/EmailForm.tsx | 2 ++ 4 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 packages/clerk-js/src/ui/components/OrganizationProfile/MembersSearch.tsx diff --git a/packages/clerk-js/src/ui/components/OrganizationProfile/MembersActions.tsx b/packages/clerk-js/src/ui/components/OrganizationProfile/MembersActions.tsx index 9f44d2adb64..6a0c3ab27b6 100644 --- a/packages/clerk-js/src/ui/components/OrganizationProfile/MembersActions.tsx +++ b/packages/clerk-js/src/ui/components/OrganizationProfile/MembersActions.tsx @@ -13,7 +13,7 @@ export const MembersActionsRow = () => { ({ - width: '100%', + // TODO - See if this would break the component in other places marginLeft: 'auto', padding: `${t.space.$none} ${t.space.$1}`, })} diff --git a/packages/clerk-js/src/ui/components/OrganizationProfile/MembersSearch.tsx b/packages/clerk-js/src/ui/components/OrganizationProfile/MembersSearch.tsx new file mode 100644 index 00000000000..38bf1cdbb6c --- /dev/null +++ b/packages/clerk-js/src/ui/components/OrganizationProfile/MembersSearch.tsx @@ -0,0 +1,35 @@ +import { useOrganization } from '@clerk/shared/react'; +import { useState } from 'react'; + +import { InputWithIcon } from '../../../ui/elements'; +import { MagnifyingGlass } from '../../../ui/icons'; +import { Spinner } from '../../../ui/primitives'; + +export const MembersSearchRow = () => { + const [query, setQuery] = useState(); + + const { memberships } = useOrganization({ + memberships: { + query, + }, + }); + + /* TODO - Only fire update once the user stops typing */ + /* TODO - Consider how it'll overlap the invite input */ + const handleSearch = async (event: React.ChangeEvent) => { + setQuery(event?.target.value); + }; + + return ( + : } + autoCapitalize='none' + spellCheck={false} + type='search' + onChange={handleSearch} + /> + ); +}; diff --git a/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationMembers.tsx b/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationMembers.tsx index 36072461ddc..377047683b5 100644 --- a/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationMembers.tsx +++ b/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationMembers.tsx @@ -20,6 +20,7 @@ import { mqu } from '../../styledSystem'; import { ActiveMembersList } from './ActiveMembersList'; import { MembersActionsRow } from './MembersActions'; import { MembershipWidget } from './MembershipWidget'; +import { MembersSearchRow } from './MembersSearch'; import { OrganizationMembersTabInvitations } from './OrganizationMembersTabInvitations'; import { OrganizationMembersTabRequests } from './OrganizationMembersTabRequests'; @@ -123,7 +124,23 @@ export const OrganizationMembers = withCardStateProvider(() => { width: '100%', }} > - + + ({ + flex: 1, + paddingLeft: t.space.$1, + })} + > + + + + diff --git a/packages/clerk-js/src/ui/components/UserProfile/EmailForm.tsx b/packages/clerk-js/src/ui/components/UserProfile/EmailForm.tsx index 43ed2258827..0db2236f47d 100644 --- a/packages/clerk-js/src/ui/components/UserProfile/EmailForm.tsx +++ b/packages/clerk-js/src/ui/components/UserProfile/EmailForm.tsx @@ -56,7 +56,9 @@ export const EmailForm = withCardStateProvider((props: EmailFormProps) => { return ( + {/* Example of form */} organizationProfile.membersPage */ headerTitle={localizationKeys('userProfile.emailAddressPage.title')} headerSubtitle={localizationKeys('userProfile.emailAddressPage.formHint')} > From dc6c79bb7e19ad1ae96d143f3583f0c6930deac4 Mon Sep 17 00:00:00 2001 From: Laura Beatris <48022589+LauraBeatris@users.noreply.github.com> Date: Mon, 20 Jan 2025 11:06:06 -0300 Subject: [PATCH 03/21] Provide search as slot --- .../OrganizationProfile/MembersActions.tsx | 12 +++++--- .../OrganizationProfile/MembersSearch.tsx | 30 +++++++++++-------- .../OrganizationMembers.tsx | 20 ++----------- .../src/react/hooks/useOrganization.tsx | 1 + 4 files changed, 29 insertions(+), 34 deletions(-) diff --git a/packages/clerk-js/src/ui/components/OrganizationProfile/MembersActions.tsx b/packages/clerk-js/src/ui/components/OrganizationProfile/MembersActions.tsx index 6a0c3ab27b6..f30c2bf969f 100644 --- a/packages/clerk-js/src/ui/components/OrganizationProfile/MembersActions.tsx +++ b/packages/clerk-js/src/ui/components/OrganizationProfile/MembersActions.tsx @@ -4,20 +4,24 @@ import { Animated } from '../../elements'; import { Action } from '../../elements/Action'; import { InviteMembersScreen } from './InviteMembersScreen'; -export const MembersActionsRow = () => { +type MembersActionsRowProps = { + actionSlot: React.ReactNode; +}; + +export const MembersActionsRow = ({ actionSlot }: MembersActionsRowProps) => { const canManageMemberships = useProtect({ permission: 'org:sys_memberships:manage' }); return ( ({ - // TODO - See if this would break the component in other places - marginLeft: 'auto', padding: `${t.space.$none} ${t.space.$1}`, })} + gap={actionSlot ? 2 : undefined} > + {actionSlot} {canManageMemberships && (