Skip to main content
Glama

mcp-google-sheets

select-flow-template-dialog.tsx9.12 kB
import { DialogDescription } from '@radix-ui/react-dialog'; import { useMutation, useQuery } from '@tanstack/react-query'; import { t } from 'i18next'; import { ArrowLeft, Info, Search, SearchX } from 'lucide-react'; import React, { useRef, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { InputWithIcon } from '@/components/custom/input-with-icon'; import { ApMarkdown } from '@/components/custom/markdown'; import { Button } from '@/components/ui/button'; import { Carousel, CarouselApi, CarouselContent, CarouselItem, } from '@/components/ui/carousel'; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger, } from '@/components/ui/dialog'; import { ScrollArea } from '@/components/ui/scroll-area'; import { LoadingSpinner } from '@/components/ui/spinner'; import { TooltipContent, TooltipTrigger, Tooltip, } from '@/components/ui/tooltip'; import { PieceIconList } from '@/features/pieces/components/piece-icon-list'; import { templatesApi } from '@/features/templates/lib/templates-api'; import { authenticationSession } from '@/lib/authentication-session'; import { MarkdownVariant, FlowOperationType, FlowTemplate, PopulatedFlow, } from '@activepieces/shared'; import { flowsApi } from '../lib/flows-api'; type TemplateCardProps = { template: FlowTemplate; onSelectTemplate: (template: FlowTemplate) => void; }; const TemplateCard = ({ template, onSelectTemplate }: TemplateCardProps) => { const selectTemplate = (template: FlowTemplate) => { onSelectTemplate(template); }; const navigate = useNavigate(); const { mutate: createFlow, isPending } = useMutation< PopulatedFlow, Error, FlowTemplate >({ mutationFn: async (template: FlowTemplate) => { const newFlow = await flowsApi.create({ displayName: template.name, projectId: authenticationSession.getProjectId()!, }); return await flowsApi.update(newFlow.id, { type: FlowOperationType.IMPORT_FLOW, request: { displayName: template.name, trigger: template.template.trigger, schemaVersion: template.template.schemaVersion, }, }); }, onSuccess: (flow) => { navigate(`/flows/${flow.id}`); }, }); return ( <div key={template.id} className="rounded-lg border border-solid border-dividers overflow-hidden" > <div className="flex items-center gap-2 p-4 "> <PieceIconList trigger={template.template.trigger} maxNumberOfIconsToShow={2} /> </div> <div className="text-sm font-medium px-4 min-h-16">{template.name}</div> <div className="py-2 px-4 gap-1 flex items-center"> <Button variant="default" loading={isPending} className="px-2 h-8" onClick={() => createFlow(template)} > {t('Use Template')} </Button> <Tooltip> <TooltipTrigger asChild> <div className="size-10 flex justify-center items-center"> <Button variant="ghost" className="rounded-full p-3 hover:bg-muted cursor-pointer flex justify-center items-center" onClick={() => selectTemplate(template)} > <Info className="w-4 h-4" /> </Button> </div> </TooltipTrigger> <TooltipContent side="bottom"> <span className="text-sm">{t('Learn more')}</span> </TooltipContent> </Tooltip> </div> </div> ); }; const SelectFlowTemplateDialog = ({ children, }: { children: React.ReactNode; }) => { const [search, setSearch] = useState<string>(''); const carousel = useRef<CarouselApi>(); const [selectedTemplate, setSelectedTemplate] = useState<FlowTemplate | null>( null, ); const { data: templates, isLoading } = useQuery<FlowTemplate[], Error>({ queryKey: ['templates'], queryFn: async () => { const templates = await templatesApi.list(); return templates.data; }, staleTime: 0, }); const filteredTemplates = templates?.filter((template) => { const templateName = template.name.toLowerCase(); const templateDescription = template.description.toLowerCase(); return ( templateName.includes(search.toLowerCase()) || templateDescription.includes(search.toLowerCase()) ); }); const handleSearchChange = (event: React.ChangeEvent<HTMLInputElement>) => { setSearch(event.target.value); }; const unselectTemplate = () => { setSelectedTemplate(null); carousel.current?.scrollPrev(); }; return ( <Dialog onOpenChange={unselectTemplate}> <DialogTrigger asChild>{children}</DialogTrigger> <DialogContent className=" lg:min-w-[850px] flex flex-col"> <DialogHeader> <DialogTitle className="flex min-h-9 flex-row items-center justify-start gap-2 items-center h-full"> {selectedTemplate && ( <Button variant="ghost" size="sm" onClick={unselectTemplate}> <ArrowLeft className="w-4 h-4" /> </Button> )} {t('Browse Templates')} </DialogTitle> </DialogHeader> <Carousel setApi={(api) => (carousel.current = api)}> <CarouselContent className="min-h-[300px] h-[70vh] max-h-[820px] "> <CarouselItem key="templates"> <div> <div className="p-1 "> <InputWithIcon icon={<Search className="w-4 h-4" />} type="text" value={search} onChange={handleSearchChange} placeholder={t('Search templates')} className="mb-4" /> </div> <DialogDescription> {isLoading ? ( <div className="min-h-[300px] h-[70vh] max-h-[680px] o flex justify-center items-center"> <LoadingSpinner /> </div> ) : ( <> {filteredTemplates?.length === 0 && ( <div className="flex flex-col items-center justify-center gap-2 text-center "> <SearchX className="w-10 h-10" /> {t('No templates found, try adjusting your search')} </div> )} <ScrollArea className="min-h-[260px] h-[calc(70vh-80px)] max-h-[740px] overflow-y-auto px-1"> <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4"> {filteredTemplates?.map((template) => ( <TemplateCard key={template.id} template={template} onSelectTemplate={(template) => { setSelectedTemplate(template); carousel.current?.scrollNext(); }} /> ))} </div> </ScrollArea> </> )} </DialogDescription> </div> </CarouselItem> <CarouselItem key="template-details"> {selectedTemplate && ( <div className="px-2 "> <div className="mb-4 p-8 flex items-center justify-center gap-2 width-full bg-green-300 rounded-lg"> <PieceIconList size="xxl" trigger={selectedTemplate.template.trigger} maxNumberOfIconsToShow={3} /> </div> <ScrollArea className="px-2 min-h-[156px] h-[calc(70vh-144px)] max-h-[536px]"> <div className="mb-4 text-lg font-medium font-black"> {selectedTemplate?.name} </div> <ApMarkdown markdown={selectedTemplate?.description} variant={MarkdownVariant.BORDERLESS} /> {selectedTemplate.blogUrl && ( <div className="mt-4"> {t('Read more about this template in')}{' '} <a href={selectedTemplate.blogUrl} target="_blank" className="text-primary underline underline-offset-4" rel="noreferrer" > {t('this blog!')} </a> </div> )} </ScrollArea> </div> )} </CarouselItem> </CarouselContent> </Carousel> </DialogContent> </Dialog> ); }; export { SelectFlowTemplateDialog };

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/activepieces/activepieces'

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