From 794e6e2b2a62c75864af3e901c9a7795bcd23fc1 Mon Sep 17 00:00:00 2001 From: Ryan Hopper-Lowe <46546486+ryanhopperlowe@users.noreply.github.com> Date: Thu, 21 Nov 2024 09:22:05 -0600 Subject: [PATCH] feat: add agent and workflow dropdowns to workflow steps (#645) Signed-off-by: Ryan Hopper-Lowe --- .../components/agent/shared/AgentSelect.tsx | 58 ++++++ .../app/components/composed/SelectModule.tsx | 185 ++++++++++++++++++ ui/admin/app/components/ui/button.tsx | 2 +- .../workflow/WorkflowSelectModule.tsx | 60 ++++++ .../app/components/workflow/steps/Step.tsx | 29 +-- 5 files changed, 314 insertions(+), 20 deletions(-) create mode 100644 ui/admin/app/components/agent/shared/AgentSelect.tsx create mode 100644 ui/admin/app/components/composed/SelectModule.tsx create mode 100644 ui/admin/app/components/workflow/WorkflowSelectModule.tsx diff --git a/ui/admin/app/components/agent/shared/AgentSelect.tsx b/ui/admin/app/components/agent/shared/AgentSelect.tsx new file mode 100644 index 00000000..a70a275e --- /dev/null +++ b/ui/admin/app/components/agent/shared/AgentSelect.tsx @@ -0,0 +1,58 @@ +import useSWR from "swr"; + +import { Agent } from "~/lib/model/agents"; +import { AgentService } from "~/lib/service/api/agentService"; + +import { SelectModule } from "~/components/composed/SelectModule"; +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from "~/components/ui/tooltip"; + +type AgentSelectModuleProps = { + onChange: (agents: string[]) => void; + selection: string[]; +}; + +export function AgentSelectModule(props: AgentSelectModuleProps) { + const { data: agents } = useSWR( + AgentService.getAgents.key(), + AgentService.getAgents + ); + + return ( + } + renderListItem={(agent) => } + getItemKey={(agent) => agent.id} + buttonText="Add Agent" + items={agents} + /> + ); +} + +function AgentText({ agent }: { agent: Agent }) { + const content = ( +
+ {agent.name} + {agent.description && ( + <> + - + + {agent.description} + + + )} +
+ ); + + return ( + + {content} + {content} + + ); +} diff --git a/ui/admin/app/components/composed/SelectModule.tsx b/ui/admin/app/components/composed/SelectModule.tsx new file mode 100644 index 00000000..0a78eab3 --- /dev/null +++ b/ui/admin/app/components/composed/SelectModule.tsx @@ -0,0 +1,185 @@ +import { PlusIcon, TrashIcon } from "lucide-react"; +import { useMemo } from "react"; + +import { Button } from "~/components/ui/button"; +import { + Command, + CommandEmpty, + CommandInput, + CommandItem, + CommandList, +} from "~/components/ui/command"; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "~/components/ui/popover"; + +interface SelectModuleProps { + items?: T[]; + selection: string[]; + onChange: (selection: string[]) => void; + renderDropdownItem: (item: T) => React.ReactNode; + renderListItem: (item: T) => React.ReactNode; + buttonText?: string; + searchPlaceholder?: string; + emptyMessage?: string; + getItemKey: (item: T) => string; +} + +export function SelectModule({ + items = [], + selection, + onChange, + renderDropdownItem, + renderListItem, + buttonText, + searchPlaceholder, + emptyMessage, + getItemKey, +}: SelectModuleProps) { + return ( +
+ onChange(selection.filter((s) => s !== id))} + renderItem={renderListItem} + getItemKey={getItemKey} + /> + + onChange([...selection, getItemKey(item)])} + filter={(item) => !selection.includes(getItemKey(item))} + renderItem={renderDropdownItem} + buttonText={buttonText} + searchPlaceholder={searchPlaceholder} + emptyMessage={emptyMessage} + getItemKey={getItemKey} + /> +
+ ); +} + +interface SelectProps { + items?: T[]; + onSelect: (item: T) => void; + filter?: (item: T, index: number, array: T[]) => boolean; + renderItem: (item: T) => React.ReactNode; + searchPlaceholder?: string; + emptyMessage?: string; + getItemKey: (item: T) => string; +} + +export function Select({ + items = [], + onSelect, + filter, + renderItem, + searchPlaceholder = "Search...", + emptyMessage = "No items to select", + getItemKey, +}: SelectProps) { + const filteredItems = filter ? items.filter(filter) : items; + + return ( + + + + {filteredItems?.length ? ( + filteredItems?.map((item) => ( + onSelect(item)} + > + {renderItem(item)} + + )) + ) : ( + {emptyMessage} + )} + + + ); +} + +interface SelectPopoverProps extends SelectProps { + className?: string; + buttonText?: string; +} + +export function SelectPopover({ + className, + buttonText = "Select Item", + ...props +}: SelectPopoverProps) { + return ( + + + + + + +