Skip to main content
Glama

Convex MCP server

Official
by get-convex
ProjectCard.tsx5.86 kB
import { EyeOpenIcon, TrashIcon, GearIcon } from "@radix-ui/react-icons"; import { Card, CardProps } from "elements/Card"; import { Tooltip } from "@ui/Tooltip"; import { Loading } from "@ui/Loading"; import { TimestampDistance } from "@common/elements/TimestampDistance"; import { LostAccessModal } from "components/projects/modals/LostAccessModal"; import { useDeploymentUris } from "hooks/useDeploymentUris"; import classNames from "classnames"; import Link from "next/link"; import { useRouter } from "next/router"; import { ReactNode, useState } from "react"; import { ProjectDetails } from "generatedApi"; import { useHasProjectAdminPermissions } from "api/roles"; import { useCurrentTeam } from "api/teams"; import { DeleteProjectModal } from "./modals/DeleteProjectModal"; export function ProjectCard({ project }: { project: ProjectDetails }) { const router = useRouter(); const { id, slug, name } = project; const team = useCurrentTeam(); const [deleteModal, setDeleteModal] = useState(false); const [lostAccessModal, setLostAccessModal] = useState(false); const { prodHref, devHref, isProdDefault, isLoading: isLoadingDeployments, } = useDeploymentUris(id, slug); const hasAdminPermissions = useHasProjectAdminPermissions(project.id); function openSettings() { void router.push(`/t/${team?.slug}/${project.slug}/settings`); } const dropdownItems: CardProps["dropdownItems"] = [ { Icon: GearIcon, text: "Settings", action: openSettings, }, { Icon: EyeOpenIcon, text: "Lost Access", action: () => setLostAccessModal(true), }, { Icon: TrashIcon, destructive: true, text: "Delete project", action: () => setDeleteModal(true), disabled: !hasAdminPermissions, tip: !hasAdminPermissions ? "You do not have permission to delete this project." : undefined, }, ]; return ( <Card cardClassName="group animate-fadeInFromLoading" href={ deleteModal || lostAccessModal ? undefined : isProdDefault ? prodHref : devHref } dropdownItems={dropdownItems} overlayed={ <div className="flex gap-1"> {!isLoadingDeployments ? ( <div className="flex flex-col items-end"> <DeploymentLinks isProdDefault={isProdDefault} devHref={devHref ?? null} prodHref={prodHref} /> <TimestampDistance date={new Date(project.createTime)} className="truncate" prefix="Created" /> </div> ) : ( <Loading className="min-h-6 w-36" /> )} </div> } > <div> <div className={classNames( "truncate", !name ? "text-content-secondary" : "", )} > <span className="flex items-center gap-2 text-content-primary"> <span className="shrink truncate"> {name?.length ? name : "Untitled Project"} </span> </span> </div> <div className="mb-1 h-4 truncate text-xs text-content-secondary"> {slug} </div> {team && deleteModal && ( <DeleteProjectModal team={team} project={project} onClose={() => setDeleteModal(false)} /> )} {lostAccessModal && ( <LostAccessModal onClose={() => setLostAccessModal(false)} teamSlug={team?.slug || ""} projectSlug={slug} /> )} </div> </Card> ); } // Displays links to the production and development environment in the project card // // Uses Tailwind's group and peer hovering feature to underline the default (last viewed) environment when hovering the project card, // but underline only the selected environment when hovering over a link. // // Because sibling elements with the `peer` classname need to be rendered first, we conditionally use `flex-row-reverse` // to render the list backwards depending on which environment is the default (isProdDefault) function DeploymentLinks({ devHref, prodHref, isProdDefault, }: { isProdDefault: boolean; devHref: string | null; prodHref: string; }) { const prod = ( <DeploymentLabel href={prodHref} isDefault={isProdDefault} title="Production" /> ); const dev = ( <DeploymentLabel href={devHref} isDefault={!isProdDefault} tip={ <> You do not have a personal development deployment for this project yet. Run <code className="px-1">npx convex dev</code> to provision one. </> } title="Development" /> ); return ( <div className="flex gap-1"> <div className={`flex ${ isProdDefault && "flex-row-reverse" } h-6 items-center justify-end gap-1 truncate text-xs`} > {isProdDefault ? dev : prod} <div className="text-neutral-4">•</div> {isProdDefault ? prod : dev} </div> </div> ); } function DeploymentLabel({ href, isDefault, title, tip, }: | { href: string | null; isDefault: boolean; tip: ReactNode; title: string; } | { href: string; isDefault: boolean; title: string; tip?: never; }) { return href === null ? ( <Tooltip tip={tip}> <div className="text-xs text-content-secondary select-none">{title}</div> </Tooltip> ) : ( <Link passHref href={href} className={`${ isDefault ? "group-hover:underline peer-hover:no-underline" : "peer" } hover:underline`} > {title} </Link> ); }

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