diff --git a/frontend/src/components/agents/composio/composio-connector.tsx b/frontend/src/components/agents/composio/composio-connector.tsx index 95d22178..7e99ece9 100644 --- a/frontend/src/components/agents/composio/composio-connector.tsx +++ b/frontend/src/components/agents/composio/composio-connector.tsx @@ -89,31 +89,31 @@ const getStepIndex = (step: Step): number => { const StepIndicator = ({ currentStep, mode }: { currentStep: Step; mode: 'full' | 'profile-only' }) => { const currentIndex = getStepIndex(currentStep); - const visibleSteps = mode === 'profile-only' + const visibleSteps = mode === 'profile-only' ? stepConfigs.filter(step => step.id !== Step.ToolsSelection && step.id !== Step.ProfileSelect) : stepConfigs; - + const visibleCurrentIndex = visibleSteps.findIndex(step => step.id === currentStep); return (
- - + {visibleSteps.map((step, index) => { const stepIndex = getStepIndex(step.id); const isCompleted = stepIndex < currentIndex; const isCurrent = step.id === currentStep; const isUpcoming = stepIndex > currentIndex; - + return ( *}
- + {isBooleanField ? (
)} - + {field.description && !isBooleanField && (
@@ -247,13 +247,13 @@ const ToolPreviewCard = ({ tool, searchTerm }: { if (!term) return text; const regex = new RegExp(`(${term})`, 'gi'); const parts = text.split(regex); - return parts.map((part, index) => - regex.test(part) ? - {part} : + return parts.map((part, index) => + regex.test(part) ? + {part} : part ); }; - + // Generate icon based on tool name/category const getToolIcon = (toolName: string) => { const name = toolName.toLowerCase(); @@ -282,9 +282,9 @@ const ToolPreviewCard = ({ tool, searchTerm }: {
); }; - + return ( -
@@ -322,7 +322,7 @@ export const ComposioConnector: React.FC = ({ const [showToolsManager, setShowToolsManager] = useState(false); const [direction, setDirection] = useState<'forward' | 'backward'>('forward'); const [selectedConnectionType, setSelectedConnectionType] = useState<'existing' | 'new' | null>(null); - + const [initiationFields, setInitiationFields] = useState>({}); const [initiationFieldsErrors, setInitiationFieldsErrors] = useState>({}); @@ -330,13 +330,13 @@ export const ComposioConnector: React.FC = ({ const { mutate: createProfile, isPending: isCreating } = useCreateComposioProfile(); const { data: profiles, isLoading: isLoadingProfiles } = useComposioProfiles(); - + const { data: toolkitDetails, isLoading: isLoadingToolkitDetails } = useComposioToolkitDetails( app.slug, { enabled: open && currentStep === Step.ProfileCreate } ); - const existingProfiles = profiles?.filter(p => + const existingProfiles = profiles?.filter(p => p.toolkit_slug === app.slug && p.is_connected ) || []; @@ -344,13 +344,13 @@ export const ComposioConnector: React.FC = ({ const [toolsPreviewSearchTerm, setToolsPreviewSearchTerm] = useState(''); const { data: toolsResponse, isLoading: isLoadingToolsPreview } = useComposioTools( - app.slug, - { + app.slug, + { enabled: open && currentStep === Step.ProfileSelect, - limit: 50 + limit: 50 } ); - + const availableToolsPreview = toolsResponse?.tools || []; useEffect(() => { @@ -382,38 +382,38 @@ export const ComposioConnector: React.FC = ({ const validateInitiationFields = (): boolean => { const newErrors: Record = {}; const initiationRequirements = toolkitDetails?.toolkit.connected_account_initiation_fields; - + if (initiationRequirements?.required) { for (const field of initiationRequirements.required) { if (field.required) { const value = initiationFields[field.name]; const isEmpty = !value || value.trim() === ''; - + if (field.type.toLowerCase() === 'boolean') { continue; } - + if ((field.type.toLowerCase() === 'number' || field.type.toLowerCase() === 'double') && value) { if (isNaN(Number(value))) { newErrors[field.name] = `${field.displayName} must be a valid number`; continue; } } - + if (isEmpty) { newErrors[field.name] = `${field.displayName} is required`; } } } } - + setInitiationFieldsErrors(newErrors); return Object.keys(newErrors).length === 0; }; const handleSaveTools = async () => { if (!selectedProfile || !agentId) return; - + const mcpConfigResponse = await composioApi.getMcpConfigForProfile(selectedProfile.profile_id); const response = await backendApi.put(`/agents/${agentId}/custom-mcp-tools`, { custom_mcps: [{ @@ -458,12 +458,12 @@ export const ComposioConnector: React.FC = ({ toast.error('Profile name is required'); return; } - + if (!validateInitiationFields()) { toast.error('Please fill in all required fields'); return; } - + createProfile({ toolkit_slug: app.slug, profile_name: profileName, @@ -556,8 +556,8 @@ export const ComposioConnector: React.FC = ({ } }; - const filteredToolsPreview = availableToolsPreview.filter(tool => - !toolsPreviewSearchTerm || + const filteredToolsPreview = availableToolsPreview.filter(tool => + !toolsPreviewSearchTerm || tool.name.toLowerCase().includes(toolsPreviewSearchTerm.toLowerCase()) || tool.description.toLowerCase().includes(toolsPreviewSearchTerm.toLowerCase()) || tool.tags?.some(tag => tag.toLowerCase().includes(toolsPreviewSearchTerm.toLowerCase())) @@ -586,11 +586,11 @@ export const ComposioConnector: React.FC = ({ - + {currentStep !== Step.ToolsSelection ? ( <> @@ -647,10 +647,10 @@ export const ComposioConnector: React.FC = ({ {showToolsManager ? 'Hide' : 'View'} Tools
- +
{existingProfiles.length > 0 && ( - = ({ - {existingProfiles.map((profile) => ( - -
- {app.logo ? ( - {app.name} - ) : ( -
- {app.name.charAt(0)} + {existingProfiles.map((profile) => ( + +
+ {app.logo ? ( + {app.name} + ) : ( +
+ {app.name.charAt(0)} +
+ )} +
+
{profile.profile_name}
- )} -
-
{profile.profile_name}
-
-
- ))} + + ))}
@@ -718,7 +718,7 @@ export const ComposioConnector: React.FC = ({ )} - + = ({
- - {showToolsManager && ( - -
- -
- {isLoadingToolsPreview ? ( -
- {[...Array(8)].map((_, i) => ( -
-
-
-
- ))} -
- ) : filteredToolsPreview.length > 0 ? ( -
- {filteredToolsPreview.map((tool) => ( - - ))} -
- ) : ( -
- -

- {toolsPreviewSearchTerm ? 'No matches' : 'No tools available'} -

-
- )} -
-
-
-
- )} -
+ + {showToolsManager && ( + +
+ +
+ {isLoadingToolsPreview ? ( +
+ {[...Array(8)].map((_, i) => ( +
+
+
+
+ ))} +
+ ) : filteredToolsPreview.length > 0 ? ( +
+ {filteredToolsPreview.map((tool) => ( + + ))} +
+ ) : ( +
+ +

+ {toolsPreviewSearchTerm ? 'No matches' : 'No tools available'} +

+
+ )} +
+
+
+
+ )} +
@@ -800,10 +800,10 @@ export const ComposioConnector: React.FC = ({
- {selectedConnectionType === 'new' ? 'Ready to create new connection' : - selectedConnectionType === 'existing' && selectedProfileId ? 'Profile selected' : - selectedConnectionType === 'existing' ? 'Select a profile to continue' : - 'Choose how you want to connect'} + {selectedConnectionType === 'new' ? 'Ready to create new connection' : + selectedConnectionType === 'existing' && selectedProfileId ? 'Profile selected' : + selectedConnectionType === 'existing' ? 'Select a profile to continue' : + 'Choose how you want to connect'}
- )} + + + + Filter by tags + {selectedTagFilters.size > 0 && ( + + )} + + +
+ {allTags.map(tag => { + const isSelected = selectedTagFilters.has(tag); + const toolsWithTag = availableTools.filter(tool => tool.tags?.includes(tag)); + + return ( + { + e.preventDefault(); + handleTagFilterToggle(tag); + }} + onSelect={(e) => { + e.preventDefault(); + }} + > + handleTagFilterToggle(tag)} + className="pointer-events-none" + /> +
+
+ + {tag} + + + {toolsWithTag.length} + +
+
+
+ ); + })} +
+
+ + +
+ + {/* Compact Filter + Quick Actions */} +
+ + Showing {filteredTools.length} of {availableTools.length} tools + + +
+
+ + {/* Quick Actions */} + +
+
{/* Tools List */} @@ -273,23 +498,23 @@ export const ComposioToolsSelector: React.FC = ({ {error && ( - {error} + {error.message || 'Failed to load tools'} )} {isLoading ? ( -
+
{Array.from({ length: 6 }).map((_, i) => ( ))}
) : filteredTools.length > 0 ? ( -
+
{filteredTools.map((tool) => ( handleToolToggle(tool.name)} searchTerm={searchTerm} /> @@ -300,9 +525,24 @@ export const ComposioToolsSelector: React.FC = ({
-

- {searchTerm ? `No tools found matching "${searchTerm}"` : 'No tools available'} +

+ {searchTerm + ? `No tools found matching "${searchTerm}"` + : selectedTagFilters.size > 0 + ? 'No tools match the selected filters' + : 'No tools available' + }

+ {selectedTagFilters.size > 0 && ( + + )}
)}
@@ -313,8 +553,8 @@ export const ComposioToolsSelector: React.FC = ({
- {selectedCount > 0 ? ( - `${selectedCount} tool${selectedCount === 1 ? '' : 's'} will be added to your agent` + {getEffectiveSelectedTools.length > 0 ? ( + `${getEffectiveSelectedTools.length} tool${getEffectiveSelectedTools.length === 1 ? '' : 's'} will be added to your agent` ) : ( 'No tools selected' )}