import { useState, useEffect } from "react"; import { useNavigate } from "react-router-dom"; import { useQueryClient } from "@tanstack/react-query"; import { usePacks } from "@/hooks/usePacks"; import { useCreateTrigger, useUpdateTrigger } from "@/hooks/useTriggers"; import { labelToRef, extractLocalRef, combinePackLocalRef, } from "@/lib/format-utils"; import SchemaBuilder from "@/components/common/SchemaBuilder"; import SearchableSelect from "@/components/common/SearchableSelect"; import { WebhooksService } from "@/api"; interface TriggerFormProps { initialData?: any; isEditing?: boolean; } export default function TriggerForm({ initialData, isEditing = false, }: TriggerFormProps) { const navigate = useNavigate(); const queryClient = useQueryClient(); // Form fields const [packId, setPackId] = useState(0); const [localRef, setLocalRef] = useState(""); const [label, setLabel] = useState(""); const [description, setDescription] = useState(""); const [webhookEnabled, setWebhookEnabled] = useState(false); const [enabled, setEnabled] = useState(true); const [paramSchema, setParamSchema] = useState>({}); const [outSchema, setOutSchema] = useState>({}); const [errors, setErrors] = useState>({}); // Fetch packs const { data: packsData } = usePacks({ page: 1, pageSize: 100 }); const packs = packsData?.data || []; const selectedPack = packs.find((p: any) => p.id === packId); // Mutations const createTrigger = useCreateTrigger(); const updateTrigger = useUpdateTrigger(); // Initialize form with existing data useEffect(() => { if (initialData) { setLabel(initialData.label || ""); setDescription(initialData.description || ""); setWebhookEnabled(initialData.webhook_enabled || false); setEnabled(initialData.enabled ?? true); setParamSchema(initialData.param_schema || {}); setOutSchema(initialData.out_schema || {}); if (isEditing) { // Find pack by pack_ref const pack = packs.find((p: any) => p.ref === initialData.pack_ref); if (pack) { setPackId(pack.id); } // Extract local ref from full ref setLocalRef(extractLocalRef(initialData.ref, initialData.pack_ref)); } } }, [initialData, packs, isEditing]); const validateForm = (): boolean => { const newErrors: Record = {}; if (!packId) { newErrors.pack = "Pack is required"; } if (!label.trim()) { newErrors.label = "Label is required"; } if (!localRef.trim()) { newErrors.ref = "Reference is required"; } else if (!/^[a-z0-9_]+$/.test(localRef)) { newErrors.ref = "Reference must contain only lowercase letters, numbers, and underscores"; } setErrors(newErrors); return Object.keys(newErrors).length === 0; }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!validateForm()) { return; } try { const selectedPackData = packs.find((p: any) => p.id === packId); if (!selectedPackData) { throw new Error("Selected pack not found"); } const fullRef = combinePackLocalRef(selectedPackData.ref, localRef); const formData = { pack_ref: selectedPackData.ref, ref: fullRef, label: label.trim(), description: description.trim() || undefined, enabled, param_schema: Object.keys(paramSchema).length > 0 ? paramSchema : undefined, out_schema: Object.keys(outSchema).length > 0 ? outSchema : undefined, }; if (isEditing && initialData?.ref) { await updateTrigger.mutateAsync({ ref: initialData.ref, data: formData, }); // Handle webhook enable/disable separately for updates if (webhookEnabled !== initialData?.webhook_enabled) { try { if (webhookEnabled) { await WebhooksService.enableWebhook({ ref: initialData.ref }); } else { await WebhooksService.disableWebhook({ ref: initialData.ref }); } // Invalidate trigger cache to refresh UI with updated webhook status queryClient.invalidateQueries({ queryKey: ["triggers", initialData.ref], }); queryClient.invalidateQueries({ queryKey: ["triggers"] }); } catch (webhookError) { console.error("Failed to update webhook status:", webhookError); // Continue anyway - user can update it manually } } // Navigate back to trigger detail page navigate(`/triggers/${encodeURIComponent(initialData.ref)}`); return; } else { const response = await createTrigger.mutateAsync(formData); const newTrigger = response?.data; if (newTrigger?.ref) { // If webhook is enabled, enable it after trigger creation if (webhookEnabled) { try { await WebhooksService.enableWebhook({ ref: newTrigger.ref }); } catch (webhookError) { console.error("Failed to enable webhook:", webhookError); // Continue anyway - user can enable it manually } // Invalidate trigger cache to refresh UI with webhook data queryClient.invalidateQueries({ queryKey: ["triggers", newTrigger.ref], }); queryClient.invalidateQueries({ queryKey: ["triggers"] }); } navigate(`/triggers/${encodeURIComponent(newTrigger.ref)}`); return; } } navigate("/triggers"); } catch (error: any) { console.error("Error submitting trigger:", error); setErrors({ submit: error.response?.data?.message || error.message || "Failed to save trigger", }); } }; const handleCancel = () => { if (isEditing && initialData?.ref) { navigate(`/triggers/${encodeURIComponent(initialData.ref)}`); } else { navigate("/triggers"); } }; return (
{errors.submit && (

{errors.submit}

)} {/* Basic Information */}

Basic Information

{/* Pack Selection */}
setPackId(Number(v))} options={packs.map((pack: any) => ({ value: pack.id, label: `${pack.label} (${pack.version})`, }))} placeholder="Select a pack..." disabled={isEditing} error={!!errors.pack} /> {errors.pack && (

{errors.pack}

)}
{/* Label */}
setLabel(e.target.value)} onBlur={() => { // Auto-populate localRef from label if localRef is empty and not editing if (!isEditing && !localRef.trim() && label.trim()) { setLocalRef(labelToRef(label)); } }} placeholder="e.g., Webhook Received" className={`w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 ${ errors.label ? "border-red-500" : "border-gray-300" }`} /> {errors.label && (

{errors.label}

)}

Human-readable name for display

{/* Reference with Pack Prefix */}
{selectedPack?.ref || "pack"}. setLocalRef(e.target.value)} placeholder="e.g., webhook_received" disabled={isEditing} className={errors.ref ? "error" : ""} />
{errors.ref && (

{errors.ref}

)}

Local identifier within the pack. Auto-populated from label.

{/* Description */}