feat: Ability to change workflow trigger
This commit is contained in:
@@ -226,6 +226,22 @@ function CustomNode({
|
||||
onMouseLeave={() => setShowActions(false)}
|
||||
>
|
||||
{/* Action buttons - shown on hover */}
|
||||
{showActions && data.type === 'TRIGGER' && (
|
||||
<div className="absolute -top-3 -right-3 flex gap-1.5 z-10">
|
||||
<Button
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
data.onEdit?.();
|
||||
}}
|
||||
variant="outline"
|
||||
size="icon"
|
||||
className="h-7 w-7 shadow-md"
|
||||
title="Edit trigger settings"
|
||||
>
|
||||
<Settings className="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
{showActions && data.type !== 'TRIGGER' && (
|
||||
<div className="absolute -top-3 -right-3 flex gap-1.5 z-10">
|
||||
<Button
|
||||
@@ -322,6 +338,14 @@ function CustomNode({
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{data.type === 'TRIGGER' && data.config?.eventName && (
|
||||
<div className="mt-3 pt-3 border-t border-neutral-100">
|
||||
<div className="flex items-center gap-2 text-xs text-neutral-600">
|
||||
<Lightbulb className="h-3 w-3" />
|
||||
<span className="truncate">{data.config.eventName}</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{data.type === 'WEBHOOK' && data.config?.url && (
|
||||
<div className="mt-3 pt-3 border-t border-neutral-100">
|
||||
<div className="flex items-center gap-2 text-xs text-neutral-600">
|
||||
@@ -375,11 +399,23 @@ export function WorkflowBuilder({workflowId, steps, onUpdate}: WorkflowBuilderPr
|
||||
const [stepToDelete, setStepToDelete] = useState<string | null>(null);
|
||||
|
||||
// Define handlers before they are used in useMemo
|
||||
const handleEditStep = useCallback((stepId: string) => {
|
||||
// This will be handled by the parent component
|
||||
const event = new CustomEvent('workflow-edit-step', {detail: {stepId}});
|
||||
window.dispatchEvent(event);
|
||||
}, []);
|
||||
const handleEditStep = useCallback(
|
||||
(stepId: string) => {
|
||||
// Check if this is a TRIGGER step
|
||||
const step = steps.find(s => s.id === stepId);
|
||||
|
||||
if (step?.type === 'TRIGGER') {
|
||||
// For TRIGGER steps, open workflow settings instead
|
||||
const event = new CustomEvent('workflow-open-settings');
|
||||
window.dispatchEvent(event);
|
||||
} else {
|
||||
// For other steps, open step editor
|
||||
const event = new CustomEvent('workflow-edit-step', {detail: {stepId}});
|
||||
window.dispatchEvent(event);
|
||||
}
|
||||
},
|
||||
[steps],
|
||||
);
|
||||
|
||||
const handleDeleteStepClick = useCallback((stepId: string) => {
|
||||
setStepToDelete(stepId);
|
||||
|
||||
@@ -318,7 +318,12 @@ export default function WorkflowEditorPage() {
|
||||
}
|
||||
};
|
||||
|
||||
const handleUpdateSettings = async (data: {name: string; description?: string}) => {
|
||||
const handleUpdateSettings = async (data: {
|
||||
name: string;
|
||||
description?: string;
|
||||
allowReentry?: boolean;
|
||||
triggerConfig?: {eventName: string};
|
||||
}) => {
|
||||
try {
|
||||
await network.fetch<Workflow, typeof WorkflowSchemas.update>('PATCH', `/workflows/${id}`, data);
|
||||
toast.success('Workflow updated successfully');
|
||||
@@ -352,9 +357,15 @@ export default function WorkflowEditorPage() {
|
||||
}
|
||||
};
|
||||
|
||||
const handleOpenSettingsEvent = () => {
|
||||
setShowSettingsDialog(true);
|
||||
};
|
||||
|
||||
window.addEventListener('workflow-edit-step', handleEditStepEvent);
|
||||
window.addEventListener('workflow-open-settings', handleOpenSettingsEvent);
|
||||
return () => {
|
||||
window.removeEventListener('workflow-edit-step', handleEditStepEvent);
|
||||
window.removeEventListener('workflow-open-settings', handleOpenSettingsEvent);
|
||||
};
|
||||
}, [workflow]);
|
||||
|
||||
@@ -741,21 +752,49 @@ interface SettingsDialogProps {
|
||||
workflow: Workflow;
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
onSave: (data: {name: string; description?: string; allowReentry?: boolean}) => Promise<void>;
|
||||
onSave: (data: {
|
||||
name: string;
|
||||
description?: string;
|
||||
allowReentry?: boolean;
|
||||
triggerConfig?: {eventName: string};
|
||||
}) => Promise<void>;
|
||||
}
|
||||
|
||||
function SettingsDialog({workflow, open, onOpenChange, onSave}: SettingsDialogProps) {
|
||||
const triggerConfig = workflow.triggerConfig as {eventName?: string} | null;
|
||||
const [name, setName] = useState(workflow.name);
|
||||
const [description, setDescription] = useState(workflow.description ?? '');
|
||||
const [allowReentry, setAllowReentry] = useState(workflow.allowReentry ?? false);
|
||||
const [eventName, setEventName] = useState(triggerConfig?.eventName ?? '');
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
// Sync state when workflow changes or dialog opens
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
setName(workflow.name);
|
||||
setDescription(workflow.description ?? '');
|
||||
setAllowReentry(workflow.allowReentry ?? false);
|
||||
const config = workflow.triggerConfig as {eventName?: string} | null;
|
||||
setEventName(config?.eventName ?? '');
|
||||
}
|
||||
}, [open, workflow]);
|
||||
|
||||
// Fetch available event names
|
||||
const {data: eventNamesData} = useSWR<{eventNames: string[]}>(open ? '/events/names' : null, {
|
||||
revalidateOnFocus: false,
|
||||
});
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setIsSubmitting(true);
|
||||
|
||||
try {
|
||||
await onSave({name, description: description || undefined, allowReentry});
|
||||
await onSave({
|
||||
name,
|
||||
description: description || undefined,
|
||||
allowReentry,
|
||||
triggerConfig: eventName.trim() ? {eventName: eventName.trim()} : undefined,
|
||||
});
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
@@ -784,6 +823,36 @@ function SettingsDialog({workflow, open, onOpenChange, onSave}: SettingsDialogPr
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="eventName">Trigger Event *</Label>
|
||||
{eventNamesData?.eventNames && eventNamesData.eventNames.length > 0 ? (
|
||||
<Select value={eventName} onValueChange={setEventName} required>
|
||||
<SelectTrigger id="eventName">
|
||||
<SelectValue placeholder="Select an event" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{eventNamesData.eventNames.map(name => (
|
||||
<SelectItem key={name} value={name}>
|
||||
{name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
) : (
|
||||
<Input
|
||||
id="eventName"
|
||||
type="text"
|
||||
value={eventName}
|
||||
onChange={e => setEventName(e.target.value)}
|
||||
placeholder="e.g., contact.created, email.opened"
|
||||
required
|
||||
/>
|
||||
)}
|
||||
<p className="text-xs text-neutral-500 mt-1">
|
||||
The event that triggers this workflow to start for a contact
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start gap-3 p-4 bg-neutral-50 rounded-lg border border-neutral-200">
|
||||
<Switch id="allowReentry" checked={allowReentry} onCheckedChange={setAllowReentry} />
|
||||
<div className="flex-1">
|
||||
|
||||
Reference in New Issue
Block a user