Skip to main content
Glama

Convex MCP server

Official
by get-convex
TeamMemberList.tsx6.05 kB
import { Team, InvitationResponse, TeamMemberResponse } from "generatedApi"; import { Sheet } from "@ui/Sheet"; import { LoadingTransition } from "@ui/Loading"; import { TextInput } from "@ui/TextInput"; import { useState } from "react"; import { useProjects } from "api/projects"; import { useUpdateTeamMemberRole, useIsCurrentMemberTeamAdmin, useProjectRoles, useUpdateProjectRoles, } from "api/roles"; import { useRemoveTeamMember } from "api/teams"; import { useProfile } from "api/profile"; import { useCancelInvite, useCreateInvite } from "api/invitations"; import sortBy from "lodash/sortBy"; import { TeamMemberInviteListItem } from "./TeamMemberInviteListItem"; import { TeamMemberListItem } from "./TeamMemberListItem"; import { TeamMemberListSkeleton } from "./TeamMemberListSkeleton"; type TeamMemberListProps = { team: Team; members?: TeamMemberResponse[]; invites?: InvitationResponse[]; }; export function TeamMemberList({ team, members, invites, }: TeamMemberListProps) { const [memberSearch, setMemberSearch] = useState(""); const [inviteSearch, setInviteSearch] = useState(""); const filteredMembers = members?.filter( (member) => member.name?.toLowerCase().includes(memberSearch.toLowerCase()) || member.email.toLowerCase().includes(memberSearch.toLowerCase()), ); const profile = useProfile(); const me = filteredMembers?.find((member) => member.id === profile?.id); const filteredMembersWithoutMe = filteredMembers?.filter( (member) => member.id !== profile?.id, ); const filteredInvites = invites?.filter((invite) => invite.email.toLowerCase().includes(inviteSearch.toLowerCase()), ); const onChangeRole = useUpdateTeamMemberRole(team.id); const onRemoveMember = useRemoveTeamMember(team.id); const onCreateInvite = useCreateInvite(team.id); const onCancelInvite = useCancelInvite(team.id); const hasAdminPermissions = useIsCurrentMemberTeamAdmin(); const { projectRoles } = useProjectRoles(); const projects = useProjects(team.id); const updateProjectRoles = useUpdateProjectRoles(team.id); return ( <> <Sheet className="min-w-[20rem]"> <div className="mb-4 flex w-full items-center justify-between gap-2"> <h3 className="grow">Team Members</h3> <div className="w-[12rem]"> <TextInput type="search" id="memberSearch" value={memberSearch} onChange={(e) => setMemberSearch(e.target.value)} placeholder="Search members" /> </div> </div> <LoadingTransition> {profile && members && projectRoles && projects && ( <div className="flex w-full flex-col"> {/* Always show self at the top */} {me && ( <TeamMemberListItem team={team} projects={projects} member={me} members={members} canChangeRole={false} myProfile={profile} onChangeRole={onChangeRole} onRemoveMember={onRemoveMember} hasAdminPermissions={hasAdminPermissions} projectRoles={projectRoles?.filter( (role) => role.memberId === me.id, )} onUpdateProjectRoles={updateProjectRoles} /> )} {!filteredMembersWithoutMe ? ( <TeamMemberListSkeleton /> ) : filteredMembers && filteredMembers.length === 0 ? ( <div className="my-4 flex w-full justify-center text-content-secondary"> No members match your search. </div> ) : ( sortBy(filteredMembersWithoutMe, (member) => (member.name || member.email).toLocaleLowerCase(), ).map((member) => ( <TeamMemberListItem key={`member${member.id}`} team={team} projects={projects} member={member} members={members} canChangeRole myProfile={profile} onChangeRole={onChangeRole} onRemoveMember={onRemoveMember} hasAdminPermissions={hasAdminPermissions} projectRoles={projectRoles?.filter( (role) => role.memberId === member.id, )} onUpdateProjectRoles={updateProjectRoles} /> )) )} </div> )} </LoadingTransition> </Sheet> {invites && invites.length > 0 && ( <Sheet> <div className="mb-4 flex w-full items-center justify-between gap-2"> <h3 className="grow">Pending Invitations</h3> <div className="w-[12rem]"> <TextInput type="search" id="inviteSearch" value={inviteSearch} onChange={(e) => setInviteSearch(e.target.value)} placeholder="Search invitations" /> </div> </div> <div className="flex flex-col"> {!filteredInvites ? ( <TeamMemberListSkeleton /> ) : filteredInvites.length === 0 ? ( <div className="my-4 flex w-full justify-center text-content-secondary"> No invites match your search. </div> ) : ( filteredInvites.map((invite, idx) => ( <TeamMemberInviteListItem key={`invite${idx}`} invite={invite} hasAdminPermissions={hasAdminPermissions} onCreateInvite={onCreateInvite} onCancelInvite={onCancelInvite} /> )) )} </div> </Sheet> )} </> ); }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/get-convex/convex-backend'

If you have feedback or need assistance with the MCP directory API, please join our Discord server