Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Kenny/futr 538 improve tools section #493

Merged
merged 5 commits into from
Oct 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/admin/src/app/(dashboard)/agents/create/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ export default function Page() {
</span>
<h1 className="text-base">Create New Agent</h1>
</div>
<AgentFormButton />
</div>
<section className="grid flex-1 overflow-hidden gap-x-[0.62rem] grid-cols-[30rem_30rem]">
<AgentsCreationHeader />
<div className=" px-[1.31rem] py-4 ">
<AgentTools />
<AgentFormButton />
</div>
</section>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,7 @@ export const AgentsCreationHeader = () => {
}}
disabled={isDisabled}
type="submit"
className="h-8 w-full px-4 mt-5 flex justify-center rounded"
className="h-7 w-full py-1 px-4 flex justify-center rounded"
>
Create Agent
</Button>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ export const AgentTools = async () => {

return (
<section className=" space-y-2">
<h1 className="font-medium text-sm">Tools:</h1>
<ToolsMultiSelect data={serializedFrameworkApis} />
</section>
);
Expand Down
43 changes: 43 additions & 0 deletions packages/admin/src/domains/agents/components/child-button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { forwardRef } from 'react';

import { cn } from '@/lib/utils';

export const ChildButton = forwardRef<
HTMLButtonElement,
React.HTMLAttributes<HTMLButtonElement> & { childApi: Array<{ name: string; value: string }> }
>((props, ref) => {
const { childApi, ...rest } = props;
return (
<button
type="button"
ref={ref}
{...rest}
className="group/cell w-full max shadow-lg -ml-1 flex h-full max-h-[40px] min-h-[40px] grow transition-all"
>
<span className="-mr-4 h-full max-h-[40px] min-h-[40px] w-6 origin-top-right -skew-x-[21deg] rounded-bl-[10px] rounded-tl-[4px] border-transparent bg-mastra-bg-4 bg-clip-padding transition-all"></span>
<div className="flex h-full max-h-[40px] min-h-[40px] grow items-center justify-between truncate bg-mastra-bg-4 bg-clip-padding text-white transition-colors">
<div className="z-10 w-full">
<span className="flex items-center px-0 text-sm transition-all">
{childApi.length ? (
<span>
<span className="text-xs rounded-full px-2 py-1 bg-mastra-bg-9">{childApi[0]?.name}</span>{' '}
<span
className={cn(
childApi.length > 1 ? 'text-xs italic text-mastra-el-3 rounded-full px-2 py-1 bg-mastra-bg-9' : '',
)}
>
{childApi.length > 1 ? `+ ${childApi.length - 1}` : ''}
</span>
</span>
) : (
<span className="text-mastra-el-3">Add api</span>
)}
</span>
</div>
</div>
<span className="-ml-0.5 h-full max-h-[40px] min-h-[40px] w-3 origin-top-right rounded-r-[6px] border-transparent bg-mastra-bg-4 bg-clip-padding transition-all"></span>
</button>
);
});

ChildButton.displayName = 'ChildButton';
64 changes: 64 additions & 0 deletions packages/admin/src/domains/agents/components/parent-button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { forwardRef } from 'react';

import { iconArr } from '@/components/ui/svg/iconArr';

import { cn } from '@/lib/utils';

import { Icon } from '@/app/components/icon';
import { IconName } from '@/types/icons';

export const ParentButton = forwardRef<
HTMLButtonElement,
React.HTMLAttributes<HTMLButtonElement> & { parentIntegration: Array<{ name: string; value: string; icon: string }> }
>((props, ref) => {
const { parentIntegration, ...rest } = props;
return (
<button type="button" ref={ref} {...rest} className="relative overflow-x-scroll flex grow">
<div className="z-10 flex grow gap-x-2.5">
<div className="group/cell flex h-full shadow-lg max-h-[40px] min-h-[40px] grow transition-all">
<span className="-mr-0.5 h-full max-h-[40px] min-h-[40px] w-3 origin-top-right rounded-l-[6px] border-transparent bg-mastra-bg-4 bg-clip-padding transition-all"></span>
<div className="flex h-full max-h-[40px] min-h-[40px] grow items-center justify-between truncate bg-mastra-bg-4 bg-clip-padding text-white transition-colors">
<div className="z-10 w-full">
<span className="flex w-full items-center justify-between px-0 transition-all">
<span className="text-sm flex items-center gap-2 capitalize">
{parentIntegration.length ? (
<>
{iconArr.includes(parentIntegration[0]?.icon) ? (
<Icon name={parentIntegration[0]?.icon as IconName} className=" text-mastra-el-3" />
) : (
<Icon name={'system'} className=" text-mastra-el-3" />
)}
<span>
<span className="text-xs rounded-full px-2 py-1 bg-mastra-bg-9">
{parentIntegration[0]?.value}
</span>{' '}
<span
className={cn(
parentIntegration.length > 1
? 'text-xs italic text-mastra-el-3 rounded-full px-2 py-1 bg-mastra-bg-9'
: '',
)}
>
{parentIntegration.length > 1 ? `+ ${parentIntegration.length - 1}` : ''}
</span>
</span>
</>
) : (
<>
<Icon name="blocks" className=" text-mastra-el-3" />
<span>Add integration</span>
</>
)}
<br />
</span>
</span>
</div>
</div>
<span className="h-full max-h-[40px] min-h-[40px] w-6 origin-top-right -skew-x-[21deg] rounded-br-[4px] rounded-tr-[10px] border-transparent bg-mastra-bg-4 bg-clip-padding transition-all"></span>
</div>
</div>
</button>
);
});

ParentButton.displayName = 'ParentButton';
116 changes: 116 additions & 0 deletions packages/admin/src/domains/agents/components/tools-dropdown-pair.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
'use client';

import { RefinedIntegrationApi } from '@mastra/core';
import { AnimatePresence, motion } from 'framer-motion';
import { snakeCase } from 'lodash';
import { useState } from 'react';

import SelectDropDown from '@/components/ui/select-dropdown';
import { iconArr } from '@/components/ui/svg/iconArr';

import { Icon } from '@/app/components/icon';
import { IconName } from '@/types/icons';

import { ChildButton } from './child-button';
import { ParentButton } from './parent-button';

export interface DropdownPairProps {
integrationKeys: Array<{ name: string; value: string; icon: string }>;
setIntegrationKeys: React.Dispatch<React.SetStateAction<Array<{ name: string; value: string; icon: string }>>>;
deserializedData: RefinedIntegrationApi[];
setTools: React.Dispatch<React.SetStateAction<Record<string, unknown>>>;
}

export const DropdownPair = ({
integrationKeys,
deserializedData,
setIntegrationKeys,
setTools,
}: DropdownPairProps) => {
const [parentIntegration, setParentIntegration] = useState<Array<{ name: string; value: string; icon: string }>>([]);
const [childApi, setChildApi] = useState<Array<{ name: string; value: string; parent: string }>>([]);
const [openParent, setOpenParent] = useState(false);
const [openChild, setOpenChild] = useState(false);

const apiArr = [] as Array<{ name: string; value: string; parent: string }>;

parentIntegration.forEach(item => {
//get the api for the integration
const apisForIntegration = deserializedData
.map(data => {
if (data.integrationName === item.value) {
{
return data;
}
}
})
.filter(val => val !== undefined)
.map(item => ({ name: snakeCase(item.label), value: item.label, parent: item.integrationName }));

apiArr.push(...apisForIntegration);
});

return (
<div className="flex gap-2 mb-1 rounded">
<SelectDropDown<{ name: string; value: string; icon: string }>
idKey="value"
data={integrationKeys}
selectedValues={parentIntegration}
setSelectedValues={setParentIntegration}
placeholder="Select System or integration"
open={openParent}
isSingleSelect
onOpenChange={setOpenParent}
onSelectItem={item => {
setIntegrationKeys(prev => prev.filter(key => key.name !== item.name));
}}
iconRenderProp={item => {
if (!iconArr.includes(item.icon)) {
return <Icon name="system" />;
}
return <Icon name={item.icon as IconName} />;
}}
>
<ParentButton parentIntegration={parentIntegration} onClick={() => setOpenParent(prev => !prev)} />
</SelectDropDown>
<AnimatePresence>
{parentIntegration.length ? (
<motion.div
animate={{
width: parentIntegration.length ? '206px' : 0,
}}
exit={{ width: 0 }}
className="max-w-[206px]"
>
<SelectDropDown<{ name: string; value: string; parent: string }>
idKey="value"
data={apiArr}
selectedValues={childApi}
setSelectedValues={setChildApi}
placeholder="Select Api"
open={openChild}
onOpenChange={setOpenChild}
onSelectItem={item => {
setTools(tools => ({
...tools,
[item.name]: true,
}));
}}
onDeselectItem={item => {
setTools(tools => {
if (Object.keys(tools).includes(item.name)) {
delete tools[item.name];
return { ...tools };
}
return tools;
});
}}
>
<ChildButton childApi={childApi} onClick={() => setOpenChild(prev => !prev)} />
</SelectDropDown>
</motion.div>
) : null}
</AnimatePresence>
</div>
);
};
Loading
Loading