Skip to main content
Glama

Convex MCP server

Official
by get-convex
MemberProjectRolesModal.tsx6.73 kB
import { Button } from "@ui/Button"; import { Tooltip } from "@ui/Tooltip"; import { Checkbox } from "@ui/Checkbox"; import { Modal } from "@ui/Modal"; import difference from "lodash/difference"; import React, { useState } from "react"; import type { Team, ProjectMemberRoleResponse, ProjectDetails, UpdateProjectRolesArgs, TeamMemberResponse, } from "generatedApi"; import Link from "next/link"; import { useHasProjectAdminPermissions } from "api/roles"; import sortBy from "lodash/sortBy"; import { TeamMemberLink } from "elements/TeamMemberLink"; import { ProjectLink } from "./AuditLogItem"; export function MemberProjectRolesModal({ team, projects, member, projectRoles, onUpdateProjectRoles, onClose, }: { team: Team; projects: ProjectDetails[]; member: TeamMemberResponse; projectRoles: ProjectMemberRoleResponse[]; onUpdateProjectRoles: (body: UpdateProjectRolesArgs) => Promise<undefined>; onClose: () => void; }) { const originalProjectRoles = projectRoles.map( (projectRole) => projectRole.projectId, ); const [newProjectRoles, setNewProjectRoles] = useState(originalProjectRoles); const addedProjects = difference(newProjectRoles, originalProjectRoles); const removedProjects = difference(originalProjectRoles, newProjectRoles); const [isSaving, setIsSaving] = useState(false); const closeWithConfirmation = () => { if (addedProjects.length > 0 || removedProjects.length > 0) { // eslint-disable-next-line no-alert const shouldClose = window.confirm( "Closing the popup will clear your unsaved changes. Are you sure you want to continue?", ); if (!shouldClose) { return; } } onClose(); }; return ( <Modal title="Manage Project Roles" size="md" description={ <div className="flex flex-col gap-2 text-sm"> <p> Manage Project Admin access for{" "} <TeamMemberLink memberId={member.id} name={member.name || member.email} /> . </p> <p> Project Admins have administrative access to a project, including the ability to delete the project and write to production. </p> </div> } onClose={closeWithConfirmation} > <form className="flex w-full flex-col gap-2" onSubmit={async (e) => { e.preventDefault(); setIsSaving(true); try { await onUpdateProjectRoles({ updates: [ ...addedProjects.map((added) => ({ memberId: member.id, projectId: added, role: "admin" as const, })), ...removedProjects.map((removed) => ({ memberId: member.id, projectId: removed, })), ], }); onClose(); } finally { setIsSaving(false); } }} > <div className="scrollbar max-h-[60vh] overflow-auto"> {sortBy(projects, (project) => project.name.toLocaleLowerCase()).map( (project) => ( <ProjectRoleItem key={project.id} project={project} projects={projects} team={team} originalProjectRoles={originalProjectRoles} newProjectRoles={newProjectRoles} setNewProjectRoles={setNewProjectRoles} /> ), )} </div> <p className="text-xs text-content-secondary"> Pro-tip! You can manage the Project Admin role for multiple members at the same time on the{" "} <Link href="https://docs.convex.dev/dashboard/projects#project-settings" className="text-content-link hover:underline" > Project Settings </Link>{" "} page.{" "} </p> <div className="ml-auto flex items-center gap-2 text-right"> <div className="text-xs"> {addedProjects.length > 0 && ( <div className="text-content-success"> Add {addedProjects.length} role {addedProjects.length > 1 ? "s" : ""} </div> )} {removedProjects.length > 0 && ( <div className="text-content-error"> Remove {removedProjects.length} role {removedProjects.length > 1 ? "s" : ""} </div> )} </div> <Button type="submit" disabled={ addedProjects.length === 0 && removedProjects.length === 0 } loading={isSaving} > Save </Button> </div> </form> </Modal> ); } function ProjectRoleItem({ project, projects, team, originalProjectRoles, newProjectRoles, setNewProjectRoles, }: { project: ProjectDetails; projects: ProjectDetails[]; team: Team; originalProjectRoles: number[]; newProjectRoles: number[]; setNewProjectRoles: React.Dispatch<React.SetStateAction<number[]>>; }) { const hasAdminPermissions = useHasProjectAdminPermissions(project.id); return ( <div className="flex h-12 items-center gap-4 border-b px-1 py-2 last:border-b-0"> <Tooltip tip={ !hasAdminPermissions && `You do not have permission to manage roles for ${project.name}` } side="left" > <Checkbox checked={newProjectRoles.includes(project.id)} disabled={!hasAdminPermissions} onChange={() => { setNewProjectRoles((prev) => newProjectRoles.includes(project.id) ? prev.filter((id) => id !== project.id) : [...prev, project.id], ); }} /> </Tooltip> <ProjectLink metadata={{}} projects={projects} projectId={project.id} team={team} /> <div className="ml-auto rounded-sm p-1 text-xs"> {originalProjectRoles.includes(project.id) && !newProjectRoles.includes(project.id) && ( <div className="rounded-sm bg-background-error p-1 text-xs text-content-error"> Role will be removed </div> )} {!originalProjectRoles.includes(project.id) && newProjectRoles.includes(project.id) && ( <div className="rounded-sm bg-background-success p-1 text-xs text-content-success"> Role will be added </div> )} </div> </div> ); }

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