Skip to main content
Glama
index.tsx7.56 kB
import { DialogTrigger } from '@radix-ui/react-dialog'; import { t } from 'i18next'; import { ChevronLeft, Search } from 'lucide-react'; import React, { useState, useMemo } from 'react'; import { useDebounce } from 'use-debounce'; import { Button } from '@/components/ui/button'; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, DialogClose, } from '@/components/ui/dialog'; import { Input } from '@/components/ui/input'; import { ScrollArea } from '@/components/ui/scroll-area'; import { Tooltip, TooltipContent, TooltipTrigger, } from '@/components/ui/tooltip'; import { stepsHooks } from '@/features/pieces/lib/steps-hooks'; import { PieceStepMetadataWithSuggestions } from '@/lib/types'; import { isNil, AgentTool, AgentPieceTool, AgentToolType, } from '@activepieces/shared'; import { PieceActionsDialog } from './piece-actions'; import { PiecesContent } from './pieces-content'; type AgentPieceDialogProps = { children: React.ReactNode; tools: AgentTool[]; open: boolean; onToolsUpdate: (tools: AgentTool[]) => void; onClose: () => void; }; export type ActionInfo = { actionName: string; actionDisplayName: string; }; export function AgentPieceDialog({ tools, open, onToolsUpdate, children, onClose, }: AgentPieceDialogProps) { const [searchQuery, setSearchQuery] = useState(''); const [selectedConnectionExternalId, setSelectedConnectionExternalId] = useState<string | null>(null); console.log('@@@@@@@@@@@@@@@@@@@@@222222222'); console.log(selectedConnectionExternalId); console.log('@@@@@@@@@@@@@@@@@@@@@222222222'); const [debouncedQuery] = useDebounce(searchQuery, 300); const [showValidationErrors, setShowValidationErrors] = useState(false); const { metadata, isLoading: isPiecesLoading } = stepsHooks.useAllStepsMetadata({ searchQuery: debouncedQuery, type: 'action', }); const [selectedPiece, setSelectedPiece] = useState<PieceStepMetadataWithSuggestions | null>(null); const [selectedActions, setSelectedActions] = useState<ActionInfo[]>([]); const pieceMetadata = useMemo(() => { return ( metadata?.filter( (m): m is PieceStepMetadataWithSuggestions => 'suggestedActions' in m && 'suggestedTriggers' in m, ) ?? [] ); }, [metadata]); const handlePieceSelect = (piece: PieceStepMetadataWithSuggestions) => { const existingTools = tools?.filter( (tool): tool is AgentPieceTool => tool.type === AgentToolType.PIECE && tool.pieceMetadata?.pieceName === piece.pieceName, ); if (existingTools && existingTools.length > 0) { setSelectedActions( existingTools.map((tool) => ({ actionName: tool.pieceMetadata?.actionName, actionDisplayName: tool.pieceMetadata?.actionName, })), ); setSelectedConnectionExternalId( (existingTools[0].pieceMetadata?.predefinedInput?.auth as string) || null, ); } setSelectedPiece(piece); }; const handleActionSelect = (action: ActionInfo) => { setSelectedActions((prev) => { const isAlreadySelected = prev.some( (a) => a.actionName === action.actionName, ); const newSelected = isAlreadySelected ? prev.filter((a) => a.actionName !== action.actionName) : [...prev, action]; return newSelected; }); }; const handleSelectAll = (checked: boolean) => { if (checked && selectedPiece) { setSelectedActions( selectedPiece.suggestedActions?.map((a) => ({ actionName: a.name, actionDisplayName: a.displayName, })) ?? [], ); } else { setSelectedActions([]); } }; const handleSave = () => { if (!isNil(selectedPiece?.auth) && isNil(selectedConnectionExternalId)) { setShowValidationErrors(true); return; } setShowValidationErrors(false); if (!selectedPiece) return; const newTools: AgentTool[] = selectedActions.map((action) => ({ type: AgentToolType.PIECE, toolName: action.actionName, pieceMetadata: { pieceVersion: selectedPiece.pieceVersion, pieceName: selectedPiece.pieceName, actionName: action.actionName, predefinedInput: { auth: !isNil(selectedConnectionExternalId) ? `{{connections['${selectedConnectionExternalId}']}}` : undefined, }, }, })); const oldTools = tools; onToolsUpdate([...oldTools, ...newTools]); handleClose(); }; const handleClose = () => { setSelectedPiece(null); setSearchQuery(''); setSelectedActions([]); setShowValidationErrors(false); onClose(); }; return ( <Dialog open={open} onOpenChange={(open) => { if (!open) { handleClose(); } }} > <DialogTrigger asChild>{children}</DialogTrigger> <DialogContent className="w-[90vw] max-w-[750px] h-[80vh] max-h-[800px] flex flex-col overflow-hidden"> <DialogHeader className={`${selectedPiece ? 'gap-2' : 'gap-0'}`}> <DialogTitle> {selectedPiece ? ( <div className="flex items-center gap-2"> <Tooltip> <TooltipTrigger asChild> <Button variant="ghost" size="icon" onClick={() => { setSelectedPiece(null); setSearchQuery(''); }} > <ChevronLeft className="w-4 h-4" /> </Button> </TooltipTrigger> <TooltipContent>{t('Back')}</TooltipContent> </Tooltip> {selectedPiece.displayName} </div> ) : ( t('Add Tool') )} </DialogTitle> </DialogHeader> {selectedPiece ? ( <PieceActionsDialog piece={selectedPiece} selectedActions={selectedActions} onSelectAction={handleActionSelect} onSelectAll={handleSelectAll} selectedConnectionExternalId={selectedConnectionExternalId} setSelectedConnectionExternalId={setSelectedConnectionExternalId} showValidationErrors={showValidationErrors} /> ) : ( <> <div className="flex flex-col gap-4 px-1"> <div className="relative mt-1"> <Search className="absolute left-2 top-3 h-4 w-4 text-muted-foreground" /> <Input placeholder={t('Search')} value={searchQuery} onChange={(e) => setSearchQuery(e.target.value)} className="pl-8" /> </div> </div> <ScrollArea className="flex-grow overflow-y-auto px-1 pt-4"> <PiecesContent isPiecesLoading={isPiecesLoading} pieceMetadata={pieceMetadata} onPieceSelect={handlePieceSelect} /> </ScrollArea> </> )} <DialogFooter> <DialogClose asChild> <Button type="button" variant="ghost"> {t('Close')} </Button> </DialogClose> <Button loading={false} type="button" onClick={handleSave}> {t('Save')} </Button> </DialogFooter> </DialogContent> </Dialog> ); }

Latest Blog Posts

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