Skip to main content
Glama

Activepieces MCP Server

by eldoonreval
todo-details.tsxβ€’14.5 kB
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { t } from 'i18next'; import { CheckIcon, ChevronDown, ChevronUp, CircleCheck, CircleX, Clock2, Loader, Send, UserRoundPen, X, } from 'lucide-react'; import { useRef, useState } from 'react'; import { ApMarkdown } from '@/components/custom/markdown'; import { RightDrawer, RightDrawerContent, RightDrawerHeader, RightDrawerTitle, } from '@/components/right-drawer'; import { Button } from '@/components/ui/button'; import { DropdownMenu, DropdownMenuItem, DropdownMenuContent, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu'; import { ScrollArea } from '@/components/ui/scroll-area'; import { Separator } from '@/components/ui/separator'; import { StatusIconWithText } from '@/components/ui/status-icon-with-text'; import { Textarea } from '@/components/ui/textarea'; import { Tooltip, TooltipTrigger, TooltipContent, } from '@/components/ui/tooltip'; import { flowRunsApi } from '@/features/flow-runs/lib/flow-runs-api'; import { flowsApi } from '@/features/flows/lib/flows-api'; import { todosApi } from '@/features/todos/lib/todos-api'; import { todoCommentApi } from '@/features/todos/lib/todos-comment-api'; import { flagsHooks } from '@/hooks/flags-hooks'; import { userHooks } from '@/hooks/user-hooks'; import { formatUtils } from '@/lib/utils'; import { FlowRunStatus, isFailedState, PopulatedFlow, FlowRun, isNil, TodoWithAssignee, StatusOption, UNRESOLVED_STATUS, STATUS_COLORS, ApFlagId, ApEdition, MarkdownVariant, } from '@activepieces/shared'; import { Comments } from './comment/comments'; type TodoDetailsProps = { open: boolean; currentTodo: TodoWithAssignee; isTesting?: boolean; onOpenChange: (open: boolean) => void; onClose: () => void; onNext: () => void; onPrevious: () => void; setIsStatusChanged?: () => void; }; function TodoDetails({ open, currentTodo, isTesting = false, onOpenChange, onClose, onNext, onPrevious, setIsStatusChanged, }: TodoDetailsProps) { const [todo, setTodo] = useState<TodoWithAssignee>(currentTodo); const { data: currentUser } = userHooks.useCurrentUser(); const queryClient = useQueryClient(); const { data: edition } = flagsHooks.useFlag<ApEdition>(ApFlagId.EDITION); const textAreaRef = useRef<HTMLTextAreaElement | null>(null); const [inputMessage, setInputMessage] = useState(''); const { data } = useQuery< { run: FlowRun; flow: PopulatedFlow; }, Error >({ queryKey: ['run', todo.runId], queryFn: async () => { const flowRun = await flowRunsApi.getPopulated(todo.runId!); const flow = await flowsApi.get(flowRun.flowId, { versionId: flowRun.flowVersionId, }); return { run: flowRun, flow: flow, }; }, staleTime: 0, gcTime: 0, enabled: !isNil(todo.runId), }); const { mutate: getTodo } = useMutation({ mutationFn: async () => { const response = await todosApi.get(todo.id); return response; }, onSuccess: (data) => { setTodo({ ...currentTodo, ...data, }); }, }); const { mutate: updateStatus, isPending: isUpdatingStatus } = useMutation({ mutationFn: async (status: StatusOption) => { await todosApi.update(todo.id, { status: status, }); if (isTesting) { setTodo({ ...todo, status: status, }); setTimeout(() => { setIsStatusChanged?.(); }, 2000); } }, onSuccess: async () => { queryClient.invalidateQueries({ queryKey: ['todos'] }); if (!isTesting) { getTodo(); } }, }); const { mutate: createComment } = useMutation({ mutationFn: async (content: string) => { await todoCommentApi.create(todo.id, { content: content, }); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['comments', todo.id] }); }, }); return ( <RightDrawer dismissible={false} open={open} onOpenChange={onOpenChange} className="w-1/2 max-w-3xl px-6" onClose={onClose} > <RightDrawerContent> <div className="flex flex-col w-full h-[100vh]"> <div className="flex py-5 gap-2"> <Button variant="outline" className="text-primary h-7 w-7 p-0 flex items-center justify-center" onClick={onClose} > <X className="h-4 w-4" /> </Button> <Button variant="outline" className="text-primary h-7 w-7 p-0 flex items-center justify-center" onClick={() => { onPrevious(); getTodo(); }} > <ChevronUp className="h-4 w-4" /> </Button> <Button variant="outline" className="text-primary h-7 w-7 p-0 flex items-center justify-center" onClick={() => { onNext(); getTodo(); }} > <ChevronDown className="h-4 w-4" /> </Button> </div> <RightDrawerHeader className="flex flex-col gap-2"> <div className="flex flex-col gap-2"> <RightDrawerTitle>{todo.title}</RightDrawerTitle> </div> <div className="flex items-center justify-between"> <div className="flex items-center gap-4"> <div className="flex items-center gap-2"> <UserRoundPen className="h-4 w-4 text-muted-foreground" /> <span className="text-sm text-muted-foreground"> Assigned to </span> <span className="text-sm"> {todo.assignee && ( <Tooltip> <TooltipTrigger> <span className="text-sm font-medium"> {todo.assignee.firstName} {todo.assignee.lastName}{' '} {todo.assigneeId === currentUser?.id ? '(Me)' : ''} </span> </TooltipTrigger> <TooltipContent> <span className="text-xs">{todo.assignee.email}</span> </TooltipContent> </Tooltip> )} </span> </div> <span className="text-sm"> / </span> <div className="flex items-center"> {!isNil(data) && ( <Tooltip> <TooltipTrigger> {data?.run.status && (data?.run.status === FlowRunStatus.RUNNING || data?.run.status === FlowRunStatus.PAUSED || data?.run.status === FlowRunStatus.STOPPED) && ( <Loader className="h-4 w-4 mr-2" /> )} {data?.run.status && isFailedState(data?.run.status) && ( <CircleX className="h-4 w-4 text-destructive mr-2" /> )} {data?.run.status && data?.run.status === FlowRunStatus.SUCCEEDED && ( <CircleCheck className="h-4 w-4 text-success mr-2" /> )} </TooltipTrigger> <TooltipContent> {data?.run.status && data?.run.status === FlowRunStatus.RUNNING && ( <span className="text-xs">Flow is running</span> )} {data?.run.status && data?.run.status === FlowRunStatus.PAUSED && ( <span className="text-xs">Flow is paused</span> )} {data?.run.status && data?.run.status === FlowRunStatus.STOPPED && ( <span className="text-xs">Flow is stopped</span> )} {data?.run.status && isFailedState(data?.run.status) && ( <span className="text-xs">Flow failed</span> )} {data?.run.status && data?.run.status === FlowRunStatus.SUCCEEDED && ( <span className="text-xs">Flow succeeded</span> )} </TooltipContent> </Tooltip> )} <span className="text-sm text-muted-foreground mr-2"> Status </span> {(todo.status.name === UNRESOLVED_STATUS.name || todo.status.continueFlow === false) && ( <DropdownMenu> <DropdownMenuTrigger> <Button variant="outline" className="h-8 px-2 flex gap-2 items-center justify-between border-1 border" style={{ backgroundColor: STATUS_COLORS[todo.status.variant].color, color: STATUS_COLORS[todo.status.variant].textColor, }} > {isUpdatingStatus ? ( <Loader className="h-4 w-4" /> ) : ( <> <span className="text-sm"> {' '} {todo.status.name}{' '} </span> <ChevronDown className="h-4 w-4" /> </> )} </Button> </DropdownMenuTrigger> <DropdownMenuContent> <div className="flex flex-col gap-1"> {todo.statusOptions.map((status) => ( <DropdownMenuItem key={status.name} onClick={() => { updateStatus(status); }} className="px-1 border-1 border" style={{ backgroundColor: STATUS_COLORS[status.variant].color, color: STATUS_COLORS[status.variant].textColor, }} > <span className="text-sm flex items-center justify-center px-2 rounded-full" style={{ backgroundColor: STATUS_COLORS[status.variant].color, color: STATUS_COLORS[status.variant].textColor, }} > {status.name} </span> </DropdownMenuItem> ))} </div> </DropdownMenuContent> </DropdownMenu> )} {todo.status.name !== UNRESOLVED_STATUS.name && todo.status.continueFlow !== false && ( <StatusIconWithText icon={CheckIcon} text={todo.status.name} color={STATUS_COLORS[todo.status.variant].color} textColor={STATUS_COLORS[todo.status.variant].textColor} /> )} </div> </div> <div className="flex items-center gap-2"> <Clock2 className="h-4 w-4 text-muted-foreground" /> <span className="text-sm text-muted-foreground"> {formatUtils.formatDateToAgo(new Date(todo.created))} </span> </div> </div> </RightDrawerHeader> <Separator className="mt-4 mb-6" /> <ScrollArea className="flex-grow pr-4"> <ApMarkdown markdown={todo.description ?? ''} variant={MarkdownVariant.BORDERLESS} /> {edition !== ApEdition.COMMUNITY && ( <div className="flex flex-col gap-4"> <Separator className="mt-4" /> <Comments task={todo} /> </div> )} </ScrollArea> <div className="relative w-full py-5"> <Textarea className="w-full focus:outline-none pb-10 pt-3 border rounded-xl bg-gray-100 dark:bg-gray-700 dark:text-gray-100 pr-12 resize-none" minRows={1} autoFocus={true} maxRows={4} placeholder={t('Write a comment...')} onChange={(e) => setInputMessage(e.target.value)} onKeyDown={(e) => { if ( e.key === 'Enter' && !e.shiftKey && inputMessage.length > 0 ) { createComment(inputMessage); setInputMessage(''); e.preventDefault(); } }} value={inputMessage} ref={textAreaRef} /> <Button variant="transparent" className="absolute bottom-5 right-0" disabled={inputMessage.length === 0} onClick={() => { createComment(inputMessage); setInputMessage(''); }} > <Send className="h-4 w-4" /> </Button> </div> </div> </RightDrawerContent> </RightDrawer> ); } export { TodoDetails };

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

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