mirror of https://github.com/kortix-ai/suna.git
update event triggers
This commit is contained in:
parent
6a210390c2
commit
d5caa38e87
|
@ -4,7 +4,7 @@ import React, { useState } from 'react';
|
|||
import { Zap } from 'lucide-react';
|
||||
import { Dialog } from '@/components/ui/dialog';
|
||||
import { ConfiguredTriggersList } from './configured-triggers-list';
|
||||
import { TriggerConfigDialog } from './trigger-config-dialog';
|
||||
import { TriggerCreationDialog } from '../../triggers/trigger-creation-dialog';
|
||||
import { TriggerConfiguration, TriggerProvider } from './types';
|
||||
import {
|
||||
useAgentTriggers,
|
||||
|
@ -155,16 +155,15 @@ export const AgentTriggersConfiguration: React.FC<AgentTriggersConfigurationProp
|
|||
)}
|
||||
|
||||
{configuringProvider && (
|
||||
<Dialog open={!!configuringProvider} onOpenChange={() => setConfiguringProvider(null)}>
|
||||
<TriggerConfigDialog
|
||||
provider={configuringProvider}
|
||||
existingConfig={editingTrigger}
|
||||
onSave={handleSaveTrigger}
|
||||
onCancel={() => setConfiguringProvider(null)}
|
||||
isLoading={createTriggerMutation.isPending || updateTriggerMutation.isPending}
|
||||
agentId={agentId}
|
||||
/>
|
||||
</Dialog>
|
||||
<TriggerCreationDialog
|
||||
open={!!configuringProvider}
|
||||
onOpenChange={() => setConfiguringProvider(null)}
|
||||
type={configuringProvider.provider_id === 'schedule' ? 'schedule' : 'event'}
|
||||
isEditMode={!!editingTrigger}
|
||||
existingTrigger={editingTrigger}
|
||||
onTriggerCreated={handleSaveTrigger}
|
||||
onTriggerUpdated={handleSaveTrigger}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -13,6 +13,7 @@ import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
|
|||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import { Loader2, Search, ArrowLeft, Info, Zap, ChevronRight, Plus, Sparkles, CheckCircle2, Link2 } from 'lucide-react';
|
||||
import { useComposioAppsWithTriggers, useComposioAppTriggers, useCreateComposioEventTrigger, ComposioTriggerType } from '@/hooks/react-query/composio/use-composio-triggers';
|
||||
import { useUpdateTrigger } from '@/hooks/react-query/triggers';
|
||||
import { useComposioProfiles } from '@/hooks/react-query/composio/use-composio-profiles';
|
||||
import { useComposioToolkitDetails } from '@/hooks/react-query/composio/use-composio';
|
||||
import { toast } from 'sonner';
|
||||
|
@ -26,6 +27,9 @@ interface EventBasedTriggerDialogProps {
|
|||
onOpenChange: (open: boolean) => void;
|
||||
agentId: string;
|
||||
onTriggerCreated?: (triggerId: string) => void;
|
||||
isEditMode?: boolean;
|
||||
existingTrigger?: any; // TriggerConfiguration for edit mode
|
||||
onTriggerUpdated?: (triggerId: string) => void;
|
||||
}
|
||||
|
||||
type JSONSchema = {
|
||||
|
@ -296,8 +300,16 @@ const DynamicConfigForm: React.FC<{
|
|||
);
|
||||
};
|
||||
|
||||
export const EventBasedTriggerDialog: React.FC<EventBasedTriggerDialogProps> = ({ open, onOpenChange, agentId, onTriggerCreated }) => {
|
||||
const [step, setStep] = useState<'apps' | 'triggers' | 'config'>('apps');
|
||||
export const EventBasedTriggerDialog: React.FC<EventBasedTriggerDialogProps> = ({
|
||||
open,
|
||||
onOpenChange,
|
||||
agentId,
|
||||
onTriggerCreated,
|
||||
isEditMode = false,
|
||||
existingTrigger,
|
||||
onTriggerUpdated
|
||||
}) => {
|
||||
const [step, setStep] = useState<'apps' | 'triggers' | 'config'>(isEditMode ? 'config' : 'apps');
|
||||
const [search, setSearch] = useState('');
|
||||
const [selectedApp, setSelectedApp] = useState<{ slug: string; name: string; logo?: string } | null>(null);
|
||||
const [selectedTrigger, setSelectedTrigger] = useState<ComposioTriggerType | null>(null);
|
||||
|
@ -333,10 +345,11 @@ export const EventBasedTriggerDialog: React.FC<EventBasedTriggerDialogProps> = (
|
|||
}, [allProfiles]);
|
||||
|
||||
const createTrigger = useCreateComposioEventTrigger();
|
||||
const updateTrigger = useUpdateTrigger();
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) {
|
||||
setStep('apps');
|
||||
setStep(isEditMode ? 'config' : 'apps');
|
||||
setSelectedApp(null);
|
||||
setSelectedTrigger(null);
|
||||
setConfig({});
|
||||
|
@ -348,7 +361,7 @@ export const EventBasedTriggerDialog: React.FC<EventBasedTriggerDialogProps> = (
|
|||
setWorkflowInput({});
|
||||
setShowComposioConnector(false);
|
||||
}
|
||||
}, [open]);
|
||||
}, [open, isEditMode]);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedTrigger) {
|
||||
|
@ -369,6 +382,65 @@ export const EventBasedTriggerDialog: React.FC<EventBasedTriggerDialogProps> = (
|
|||
}
|
||||
}, [profiles, profileId]);
|
||||
|
||||
// Initialize form for edit mode
|
||||
useEffect(() => {
|
||||
if (isEditMode && existingTrigger && open) {
|
||||
console.log('Edit mode - existingTrigger:', existingTrigger);
|
||||
const triggerConfig = existingTrigger.config || {};
|
||||
console.log('Edit mode - triggerConfig:', triggerConfig);
|
||||
|
||||
// Set basic info
|
||||
setName(existingTrigger.name || '');
|
||||
setPrompt(triggerConfig.agent_prompt || '');
|
||||
setProfileId(triggerConfig.profile_id || '');
|
||||
setExecutionType(triggerConfig.execution_type || 'agent');
|
||||
setSelectedWorkflowId(triggerConfig.workflow_id || '');
|
||||
setWorkflowInput(triggerConfig.workflow_input || {});
|
||||
|
||||
// Set trigger config (excluding execution-specific fields)
|
||||
const { agent_prompt, workflow_id, workflow_input, execution_type, profile_id, ...triggerSpecificConfig } = triggerConfig;
|
||||
setConfig(triggerSpecificConfig);
|
||||
|
||||
// For composio triggers, we need to reconstruct the app and trigger selection
|
||||
if (triggerConfig.provider_id === 'composio' && triggerConfig.trigger_slug) {
|
||||
console.log('Edit mode - setting up composio trigger for:', triggerConfig.qualified_name, triggerConfig.trigger_slug);
|
||||
|
||||
// Extract toolkit slug from qualified_name (e.g., "composio.googledocs" -> "googledocs")
|
||||
const toolkitSlug = triggerConfig.qualified_name?.replace('composio.', '') || '';
|
||||
|
||||
if (toolkitSlug) {
|
||||
// Create app object to trigger the API call
|
||||
const app = {
|
||||
slug: toolkitSlug,
|
||||
name: toolkitSlug,
|
||||
logo: undefined
|
||||
};
|
||||
setSelectedApp(app);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [isEditMode, existingTrigger, open]);
|
||||
|
||||
// Find the matching trigger for edit mode once triggers are fetched
|
||||
useEffect(() => {
|
||||
if (isEditMode && existingTrigger && selectedApp && triggersData?.items) {
|
||||
const triggerConfig = existingTrigger.config || {};
|
||||
console.log('Edit mode - looking for trigger with slug:', triggerConfig.trigger_slug);
|
||||
console.log('Edit mode - available triggers:', triggersData.items.map(t => t.slug));
|
||||
|
||||
if (triggerConfig.trigger_slug) {
|
||||
// Find the matching trigger from the fetched data
|
||||
const matchingTrigger = triggersData.items.find(t => t.slug === triggerConfig.trigger_slug);
|
||||
if (matchingTrigger) {
|
||||
console.log('Edit mode - found matching trigger:', matchingTrigger);
|
||||
setSelectedTrigger(matchingTrigger);
|
||||
} else {
|
||||
console.log('Edit mode - no matching trigger found for slug:', triggerConfig.trigger_slug);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [isEditMode, existingTrigger, selectedApp, triggersData]);
|
||||
|
||||
const selectedWorkflow = useMemo(() => {
|
||||
return (workflows || []).find((w: any) => w.id === selectedWorkflowId);
|
||||
}, [workflows, selectedWorkflowId]);
|
||||
|
@ -409,30 +481,61 @@ export const EventBasedTriggerDialog: React.FC<EventBasedTriggerDialogProps> = (
|
|||
const handleCreate = async () => {
|
||||
if (!agentId || !profileId || !selectedTrigger) return;
|
||||
try {
|
||||
const selectedProfile = profiles?.find(p => p.profile_id === profileId);
|
||||
const base: any = {
|
||||
agent_id: agentId,
|
||||
profile_id: profileId,
|
||||
slug: selectedTrigger.slug,
|
||||
trigger_config: config,
|
||||
name: name || `${selectedTrigger.toolkit.name} → ${executionType === 'agent' ? 'Agent' : 'Workflow'}`,
|
||||
connected_account_id: selectedProfile?.connected_account_id,
|
||||
toolkit_slug: selectedApp?.slug,
|
||||
};
|
||||
const payload = executionType === 'agent'
|
||||
? { ...base, route: 'agent' as const, agent_prompt: (prompt || 'Read this') }
|
||||
: { ...base, route: 'workflow' as const, workflow_id: selectedWorkflowId, workflow_input: workflowInput };
|
||||
const result = await createTrigger.mutateAsync(payload);
|
||||
toast.success('Task created');
|
||||
if (isEditMode && existingTrigger) {
|
||||
// Update existing trigger using the general update API
|
||||
const updatedConfig = {
|
||||
...config,
|
||||
profile_id: profileId,
|
||||
trigger_slug: selectedTrigger.slug,
|
||||
qualified_name: `composio.${selectedApp?.slug}`,
|
||||
provider_id: 'composio',
|
||||
execution_type: executionType,
|
||||
...(executionType === 'agent' ? { agent_prompt: prompt || 'Read this' } : {
|
||||
workflow_id: selectedWorkflowId,
|
||||
workflow_input: workflowInput
|
||||
})
|
||||
};
|
||||
|
||||
if (onTriggerCreated && result?.trigger_id) {
|
||||
onTriggerCreated(result.trigger_id);
|
||||
await updateTrigger.mutateAsync({
|
||||
triggerId: existingTrigger.trigger_id,
|
||||
name: name || `${selectedTrigger.toolkit.name} → ${executionType === 'agent' ? 'Agent' : 'Workflow'}`,
|
||||
description: `Event trigger for ${selectedTrigger.toolkit.name}`,
|
||||
config: updatedConfig,
|
||||
is_active: true,
|
||||
});
|
||||
toast.success('Task updated');
|
||||
|
||||
if (onTriggerUpdated && existingTrigger.trigger_id) {
|
||||
onTriggerUpdated(existingTrigger.trigger_id);
|
||||
}
|
||||
} else {
|
||||
// Create new trigger using Composio-specific API
|
||||
const selectedProfile = profiles?.find(p => p.profile_id === profileId);
|
||||
const base: any = {
|
||||
agent_id: agentId,
|
||||
profile_id: profileId,
|
||||
slug: selectedTrigger.slug,
|
||||
trigger_config: config,
|
||||
name: name || `${selectedTrigger.toolkit.name} → ${executionType === 'agent' ? 'Agent' : 'Workflow'}`,
|
||||
connected_account_id: selectedProfile?.connected_account_id,
|
||||
toolkit_slug: selectedApp?.slug,
|
||||
};
|
||||
const payload = executionType === 'agent'
|
||||
? { ...base, route: 'agent' as const, agent_prompt: (prompt || 'Read this') }
|
||||
: { ...base, route: 'workflow' as const, workflow_id: selectedWorkflowId, workflow_input: workflowInput };
|
||||
|
||||
const result = await createTrigger.mutateAsync(payload);
|
||||
toast.success('Task created');
|
||||
|
||||
if (onTriggerCreated && result?.trigger_id) {
|
||||
onTriggerCreated(result.trigger_id);
|
||||
}
|
||||
}
|
||||
|
||||
onOpenChange(false);
|
||||
} catch (e: any) {
|
||||
// Handle nested error structure from API
|
||||
let errorMessage = 'Failed to create trigger';
|
||||
let errorMessage = isEditMode ? 'Failed to update trigger' : 'Failed to create trigger';
|
||||
console.error('Error creating trigger:', e);
|
||||
console.error('Error details:', e?.details);
|
||||
console.error('Error keys:', Object.keys(e || {}));
|
||||
|
@ -486,7 +589,9 @@ export const EventBasedTriggerDialog: React.FC<EventBasedTriggerDialogProps> = (
|
|||
<ArrowLeft className="h-4 w-4" />
|
||||
</Button>
|
||||
)}
|
||||
<DialogTitle className="text-lg font-semibold">Create Event Trigger</DialogTitle>
|
||||
<DialogTitle className="text-lg font-semibold">
|
||||
{isEditMode ? 'Edit Event Trigger' : 'Create Event Trigger'}
|
||||
</DialogTitle>
|
||||
</div>
|
||||
</DialogHeader>
|
||||
<ProgressStepper currentStep={step} />
|
||||
|
@ -612,12 +717,22 @@ export const EventBasedTriggerDialog: React.FC<EventBasedTriggerDialogProps> = (
|
|||
</div>
|
||||
)}
|
||||
|
||||
{step === 'config' && selectedTrigger && (
|
||||
{step === 'config' && (
|
||||
<div className="h-full flex flex-col">
|
||||
<div
|
||||
className="flex-1 overflow-y-auto p-6"
|
||||
style={{ maxHeight: 'calc(90vh - 250px)' }}
|
||||
>
|
||||
{/* Loading state for edit mode while waiting for trigger data */}
|
||||
{isEditMode && !selectedTrigger ? (
|
||||
<div className="flex-1 flex items-center justify-center p-6">
|
||||
<div className="text-center space-y-3">
|
||||
<Loader2 className="h-8 w-8 animate-spin mx-auto text-muted-foreground" />
|
||||
<p className="text-sm text-muted-foreground">Loading trigger configuration...</p>
|
||||
</div>
|
||||
</div>
|
||||
) : selectedTrigger ? (
|
||||
<>
|
||||
<div
|
||||
className="flex-1 overflow-y-auto p-6"
|
||||
style={{ maxHeight: 'calc(90vh - 250px)' }}
|
||||
>
|
||||
<div className="max-w-2xl mx-auto space-y-6">
|
||||
{selectedTrigger.instructions && (
|
||||
<Markdown className="text-sm w-full text-muted-foreground">
|
||||
|
@ -800,27 +915,29 @@ export const EventBasedTriggerDialog: React.FC<EventBasedTriggerDialogProps> = (
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{/* Fixed Footer */}
|
||||
{(!loadingProfiles && (profiles || []).filter(p => p.is_connected).length > 0) && (
|
||||
<div className="shrink-0 border-t p-4 bg-background">
|
||||
<div className="flex justify-end">
|
||||
<Button
|
||||
onClick={handleCreate}
|
||||
disabled={createTrigger.isPending || !name.trim() || !profileId || !isConfigValid || (executionType === 'agent' ? !prompt.trim() : !selectedWorkflowId)}
|
||||
size="sm"
|
||||
>
|
||||
{createTrigger.isPending ? (
|
||||
<>
|
||||
<Loader2 className="h-3 w-3 animate-spin mr-2" />
|
||||
Creating...
|
||||
</>
|
||||
) : (
|
||||
'Create Trigger'
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{/* Fixed Footer */}
|
||||
{(!loadingProfiles && (profiles || []).filter(p => p.is_connected).length > 0) && (
|
||||
<div className="shrink-0 border-t p-4 bg-background">
|
||||
<div className="flex justify-end">
|
||||
<Button
|
||||
onClick={handleCreate}
|
||||
disabled={(isEditMode ? updateTrigger.isPending : createTrigger.isPending) || !name.trim() || !profileId || !isConfigValid || (executionType === 'agent' ? !prompt.trim() : !selectedWorkflowId)}
|
||||
size="sm"
|
||||
>
|
||||
{(isEditMode ? updateTrigger.isPending : createTrigger.isPending) ? (
|
||||
<>
|
||||
<Loader2 className="h-3 w-3 animate-spin mr-2" />
|
||||
{isEditMode ? 'Updating...' : 'Creating...'}
|
||||
</>
|
||||
) : (
|
||||
isEditMode ? 'Update Trigger' : 'Create Trigger'
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
@ -1,276 +0,0 @@
|
|||
"use client";
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import {
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Textarea } from '@/components/ui/textarea';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import {
|
||||
Activity,
|
||||
Copy,
|
||||
ExternalLink,
|
||||
Loader2
|
||||
} from 'lucide-react';
|
||||
import { TriggerProvider, TriggerConfiguration, ScheduleTriggerConfig, EventTriggerConfig } from './types';
|
||||
import { SimplifiedScheduleConfig } from './providers/simplified-schedule-config';
|
||||
import { EventTriggerConfigForm } from './providers/event-config';
|
||||
import { getDialogIcon } from './utils';
|
||||
|
||||
|
||||
interface TriggerConfigDialogProps {
|
||||
provider: TriggerProvider;
|
||||
existingConfig?: TriggerConfiguration;
|
||||
onSave: (config: any) => void;
|
||||
onCancel: () => void;
|
||||
isLoading?: boolean;
|
||||
agentId: string;
|
||||
selectedAgent?: string;
|
||||
onAgentSelect?: (agentId: string) => void;
|
||||
open?: boolean;
|
||||
onOpenChange?: (open: boolean) => void;
|
||||
}
|
||||
|
||||
export const TriggerConfigDialog: React.FC<TriggerConfigDialogProps> = ({
|
||||
provider,
|
||||
existingConfig,
|
||||
onSave,
|
||||
onCancel,
|
||||
isLoading = false,
|
||||
agentId,
|
||||
selectedAgent,
|
||||
onAgentSelect,
|
||||
open = true,
|
||||
onOpenChange
|
||||
}) => {
|
||||
const [name, setName] = useState(existingConfig?.name || '');
|
||||
const [description, setDescription] = useState(existingConfig?.description || '');
|
||||
const [isActive, setIsActive] = useState(existingConfig?.is_active ?? true);
|
||||
const [config, setConfig] = useState(existingConfig?.config || {});
|
||||
const [errors, setErrors] = useState<Record<string, string>>({});
|
||||
|
||||
useEffect(() => {
|
||||
if (!name && !existingConfig) {
|
||||
setName(`${provider.name} Trigger`);
|
||||
}
|
||||
}, [provider, existingConfig, name]);
|
||||
|
||||
const validateForm = () => {
|
||||
const newErrors: Record<string, string> = {};
|
||||
if (!name.trim()) {
|
||||
newErrors.name = 'Name is required';
|
||||
}
|
||||
if (provider.provider_id === 'telegram') {
|
||||
if (!config.bot_token) {
|
||||
newErrors.bot_token = 'Bot token is required';
|
||||
}
|
||||
} else if (provider.provider_id === 'slack') {
|
||||
if (!config.signing_secret) {
|
||||
newErrors.signing_secret = 'Signing secret is required';
|
||||
}
|
||||
} else if (provider.provider_id === 'schedule') {
|
||||
if (!config.cron_expression) {
|
||||
newErrors.cron_expression = 'Cron expression is required';
|
||||
}
|
||||
if (config.execution_type === 'workflow') {
|
||||
if (!config.workflow_id) {
|
||||
newErrors.workflow_id = 'Workflow selection is required';
|
||||
}
|
||||
} else {
|
||||
if (!config.agent_prompt) {
|
||||
newErrors.agent_prompt = 'Agent prompt is required';
|
||||
}
|
||||
}
|
||||
} else if (provider.trigger_type === 'webhook' || provider.provider_id === 'composio') {
|
||||
// Validate event-based triggers
|
||||
if (config.execution_type === 'workflow') {
|
||||
if (!config.workflow_id) {
|
||||
newErrors.workflow_id = 'Workflow selection is required';
|
||||
}
|
||||
} else {
|
||||
if (!config.agent_prompt) {
|
||||
newErrors.agent_prompt = 'Agent prompt is required';
|
||||
}
|
||||
}
|
||||
}
|
||||
setErrors(newErrors);
|
||||
return Object.keys(newErrors).length === 0;
|
||||
};
|
||||
|
||||
const handleSave = () => {
|
||||
if (validateForm()) {
|
||||
onSave({
|
||||
name: name.trim(),
|
||||
description: description.trim(),
|
||||
is_active: isActive,
|
||||
config,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
if (provider.provider_id === 'schedule') {
|
||||
return (
|
||||
<SimplifiedScheduleConfig
|
||||
provider={provider}
|
||||
config={config as ScheduleTriggerConfig}
|
||||
onChange={setConfig}
|
||||
errors={errors}
|
||||
agentId={agentId}
|
||||
name={name}
|
||||
description={description}
|
||||
onNameChange={setName}
|
||||
onDescriptionChange={setDescription}
|
||||
isActive={isActive}
|
||||
onActiveChange={setIsActive}
|
||||
selectedAgent={selectedAgent}
|
||||
onAgentSelect={onAgentSelect}
|
||||
open={open}
|
||||
onOpenChange={onOpenChange || onCancel}
|
||||
onSave={onSave}
|
||||
isEditMode={!!existingConfig}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const renderProviderSpecificConfig = () => {
|
||||
switch (provider.provider_id) {
|
||||
case 'schedule':
|
||||
return (
|
||||
<SimplifiedScheduleConfig
|
||||
provider={provider}
|
||||
config={config as ScheduleTriggerConfig}
|
||||
onChange={setConfig}
|
||||
errors={errors}
|
||||
agentId={agentId}
|
||||
name={name}
|
||||
description={description}
|
||||
onNameChange={setName}
|
||||
onDescriptionChange={setDescription}
|
||||
isActive={isActive}
|
||||
onActiveChange={setIsActive}
|
||||
selectedAgent={selectedAgent}
|
||||
onAgentSelect={onAgentSelect}
|
||||
open={open}
|
||||
onOpenChange={onOpenChange || onCancel}
|
||||
onSave={onSave}
|
||||
isEditMode={!!existingConfig}
|
||||
/>
|
||||
);
|
||||
case 'composio':
|
||||
return (
|
||||
<EventTriggerConfigForm
|
||||
provider={provider}
|
||||
config={config as EventTriggerConfig}
|
||||
onChange={setConfig}
|
||||
errors={errors}
|
||||
agentId={agentId}
|
||||
name={name}
|
||||
description={description}
|
||||
onNameChange={setName}
|
||||
onDescriptionChange={setDescription}
|
||||
isActive={isActive}
|
||||
onActiveChange={setIsActive}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
if (provider.trigger_type === 'webhook') {
|
||||
return (
|
||||
<EventTriggerConfigForm
|
||||
provider={provider}
|
||||
config={config as EventTriggerConfig}
|
||||
onChange={setConfig}
|
||||
errors={errors}
|
||||
agentId={agentId}
|
||||
name={name}
|
||||
description={description}
|
||||
onNameChange={setName}
|
||||
onDescriptionChange={setDescription}
|
||||
isActive={isActive}
|
||||
onActiveChange={setIsActive}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className="text-center py-8 text-muted-foreground">
|
||||
<Activity className="h-12 w-12 mx-auto mb-4" />
|
||||
<p>Configuration form for {provider.name} is not yet implemented.</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<DialogContent className="sm:max-w-2xl max-h-[90vh] overflow-hidden p-0 flex flex-col">
|
||||
<div className="flex-1 overflow-y-auto">
|
||||
{renderProviderSpecificConfig()}
|
||||
{/* {provider.webhook_enabled && existingConfig?.webhook_url && (
|
||||
<div className="px-6 pb-6">
|
||||
<div className="border-t pt-6">
|
||||
<h3 className="text-sm font-medium mb-4">Webhook Information</h3>
|
||||
<div className="space-y-2">
|
||||
<Label>Webhook URL</Label>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Input
|
||||
value={existingConfig.webhook_url}
|
||||
readOnly
|
||||
className="font-mono text-sm"
|
||||
/>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={() => navigator.clipboard.writeText(existingConfig.webhook_url!)}
|
||||
>
|
||||
<Copy className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={() => window.open(existingConfig.webhook_url, '_blank')}
|
||||
>
|
||||
<ExternalLink className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Use this URL to configure the webhook in {provider.name}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)} */}
|
||||
</div>
|
||||
<div className="flex-shrink-0 px-6 py-6 border-t bg-muted/20">
|
||||
<div className="flex gap-3">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={onCancel}
|
||||
disabled={isLoading}
|
||||
className="px-8 h-11"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleSave}
|
||||
disabled={isLoading}
|
||||
className="flex-1 h-11 text-base font-medium"
|
||||
>
|
||||
{isLoading ? (
|
||||
<>
|
||||
<Loader2 className="animate-spin rounded-full h-4 w-4 mr-2" />
|
||||
{existingConfig ? 'Updating Task...' : 'Creating Task...'}
|
||||
</>
|
||||
) : (
|
||||
`${existingConfig ? 'Update' : 'Create'} Task`
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</DialogContent>
|
||||
);
|
||||
};
|
|
@ -26,7 +26,7 @@ import {
|
|||
import Link from 'next/link';
|
||||
import { TriggerWithAgent } from '@/hooks/react-query/triggers/use-all-triggers';
|
||||
import { useDeleteTrigger, useToggleTrigger, useUpdateTrigger } from '@/hooks/react-query/triggers';
|
||||
import { TriggerConfigDialog } from '@/components/agents/triggers/trigger-config-dialog';
|
||||
import { TriggerCreationDialog } from './trigger-creation-dialog';
|
||||
import { useAgentWorkflows } from '@/hooks/react-query/agents/use-agent-workflows';
|
||||
import { toast } from 'sonner';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
@ -334,16 +334,14 @@ export function SimplifiedTriggerDetailPanel({ trigger, onClose }: SimplifiedTri
|
|||
|
||||
{/* Edit Dialog */}
|
||||
{showEditDialog && (
|
||||
<Dialog open={showEditDialog} onOpenChange={setShowEditDialog}>
|
||||
<TriggerConfigDialog
|
||||
provider={provider}
|
||||
existingConfig={triggerConfig}
|
||||
onSave={handleEditSave}
|
||||
onCancel={() => setShowEditDialog(false)}
|
||||
isLoading={updateMutation.isPending}
|
||||
agentId={trigger.agent_id}
|
||||
/>
|
||||
</Dialog>
|
||||
<TriggerCreationDialog
|
||||
open={showEditDialog}
|
||||
onOpenChange={setShowEditDialog}
|
||||
type={isScheduled ? 'schedule' : 'event'}
|
||||
isEditMode={true}
|
||||
existingTrigger={triggerConfig}
|
||||
onTriggerUpdated={handleEditSave}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Delete Dialog */}
|
||||
|
|
|
@ -13,7 +13,7 @@ import { ArrowRight, Clock, PlugZap } from 'lucide-react';
|
|||
import { EventBasedTriggerDialog } from '@/components/agents/triggers/event-based-trigger-dialog';
|
||||
import { SimplifiedScheduleConfig } from '@/components/agents/triggers/providers/simplified-schedule-config';
|
||||
import { ScheduleTriggerConfig } from '@/components/agents/triggers/types';
|
||||
import { useCreateTrigger } from '@/hooks/react-query/triggers';
|
||||
import { useCreateTrigger, useUpdateTrigger } from '@/hooks/react-query/triggers';
|
||||
import { toast } from 'sonner';
|
||||
import { AgentSelectionDropdown } from '@/components/agents/agent-selection-dropdown';
|
||||
|
||||
|
@ -22,13 +22,19 @@ interface TriggerCreationDialogProps {
|
|||
onOpenChange: (open: boolean) => void;
|
||||
type: 'schedule' | 'event';
|
||||
onTriggerCreated?: (triggerId: string) => void;
|
||||
isEditMode?: boolean;
|
||||
existingTrigger?: any; // TriggerConfiguration for edit mode
|
||||
onTriggerUpdated?: (triggerId: string) => void;
|
||||
}
|
||||
|
||||
export function TriggerCreationDialog({
|
||||
open,
|
||||
onOpenChange,
|
||||
type,
|
||||
onTriggerCreated
|
||||
onTriggerCreated,
|
||||
isEditMode = false,
|
||||
existingTrigger,
|
||||
onTriggerUpdated
|
||||
}: TriggerCreationDialogProps) {
|
||||
const [selectedAgent, setSelectedAgent] = useState<string>('');
|
||||
const [step, setStep] = useState<'agent' | 'config'>('agent');
|
||||
|
@ -39,6 +45,19 @@ export function TriggerCreationDialog({
|
|||
execution_type: 'agent'
|
||||
});
|
||||
const createTriggerMutation = useCreateTrigger();
|
||||
const updateTriggerMutation = useUpdateTrigger();
|
||||
|
||||
// Initialize form for edit mode
|
||||
React.useEffect(() => {
|
||||
if (isEditMode && existingTrigger && open) {
|
||||
setSelectedAgent(existingTrigger.agent_id || '');
|
||||
setName(existingTrigger.name || '');
|
||||
setDescription(existingTrigger.description || '');
|
||||
setConfig(existingTrigger.config || { cron_expression: '', execution_type: 'agent' });
|
||||
// Skip agent selection step in edit mode
|
||||
setStep('config');
|
||||
}
|
||||
}, [isEditMode, existingTrigger, open]);
|
||||
|
||||
const scheduleProvider = {
|
||||
provider_id: 'schedule',
|
||||
|
@ -55,23 +74,40 @@ export function TriggerCreationDialog({
|
|||
}
|
||||
|
||||
try {
|
||||
const newTrigger = await createTriggerMutation.mutateAsync({
|
||||
agentId: selectedAgent,
|
||||
provider_id: 'schedule',
|
||||
name: data.name || 'Scheduled Trigger',
|
||||
description: data.description || 'Automatically scheduled trigger',
|
||||
config: data.config,
|
||||
});
|
||||
toast.success('Schedule trigger created successfully');
|
||||
if (isEditMode && existingTrigger) {
|
||||
// Update existing trigger
|
||||
await updateTriggerMutation.mutateAsync({
|
||||
triggerId: existingTrigger.trigger_id,
|
||||
name: data.name || 'Scheduled Trigger',
|
||||
description: data.description || 'Automatically scheduled trigger',
|
||||
config: data.config,
|
||||
is_active: data.is_active,
|
||||
});
|
||||
toast.success('Schedule trigger updated successfully');
|
||||
|
||||
if (onTriggerCreated && newTrigger?.trigger_id) {
|
||||
onTriggerCreated(newTrigger.trigger_id);
|
||||
if (onTriggerUpdated && existingTrigger.trigger_id) {
|
||||
onTriggerUpdated(existingTrigger.trigger_id);
|
||||
}
|
||||
} else {
|
||||
// Create new trigger
|
||||
const newTrigger = await createTriggerMutation.mutateAsync({
|
||||
agentId: selectedAgent,
|
||||
provider_id: 'schedule',
|
||||
name: data.name || 'Scheduled Trigger',
|
||||
description: data.description || 'Automatically scheduled trigger',
|
||||
config: data.config,
|
||||
});
|
||||
toast.success('Schedule trigger created successfully');
|
||||
|
||||
if (onTriggerCreated && newTrigger?.trigger_id) {
|
||||
onTriggerCreated(newTrigger.trigger_id);
|
||||
}
|
||||
}
|
||||
|
||||
handleClose();
|
||||
} catch (error: any) {
|
||||
toast.error(error.message || 'Failed to create schedule trigger');
|
||||
console.error('Error creating schedule trigger:', error);
|
||||
toast.error(error.message || `Failed to ${isEditMode ? 'update' : 'create'} schedule trigger`);
|
||||
console.error(`Error ${isEditMode ? 'updating' : 'creating'} schedule trigger:`, error);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -97,17 +133,17 @@ export function TriggerCreationDialog({
|
|||
{type === 'schedule' ? (
|
||||
<>
|
||||
<Clock className="h-5 w-5" />
|
||||
Create Scheduled Task
|
||||
{isEditMode ? 'Edit Scheduled Task' : 'Create Scheduled Task'}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<PlugZap className="h-5 w-5" />
|
||||
Create App-based Task
|
||||
{isEditMode ? 'Edit App-based Task' : 'Create App-based Task'}
|
||||
</>
|
||||
)}
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
First, select which agent should handle this task
|
||||
{isEditMode ? 'Update the agent for this task' : 'First, select which agent should handle this task'}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
|
@ -153,6 +189,7 @@ export function TriggerCreationDialog({
|
|||
open={open}
|
||||
onOpenChange={onOpenChange}
|
||||
onSave={handleScheduleSave}
|
||||
isEditMode={isEditMode}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -163,6 +200,9 @@ export function TriggerCreationDialog({
|
|||
onOpenChange={handleClose}
|
||||
agentId={selectedAgent}
|
||||
onTriggerCreated={onTriggerCreated}
|
||||
isEditMode={isEditMode}
|
||||
existingTrigger={existingTrigger}
|
||||
onTriggerUpdated={onTriggerUpdated}
|
||||
/>
|
||||
);
|
||||
}
|
Loading…
Reference in New Issue