'use client'; import { useState, useRef } from 'react'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Variable, Plus, Trash2, GripVertical, ChevronDown, Copy, } from 'lucide-react'; // ============================================================================ // Available fields per trigger source // ============================================================================ const TRIGGER_FIELDS: Record = { datto_rmm: [ { key: 'triggered', label: 'Triggered (True/False)', group: 'Alert' }, { key: 'alert_type', label: 'Alert Type', group: 'Alert' }, { key: 'alert_category', label: 'Alert Category', group: 'Alert' }, { key: 'alert_priority', label: 'Alert Priority', group: 'Alert' }, { key: 'alert_message_en', label: 'Alert Message', group: 'Alert' }, { key: 'alert_uid', label: 'Alert UID', group: 'Alert' }, { key: 'device_hostname', label: 'Device Hostname', group: 'Device' }, { key: 'device_id', label: 'Device ID', group: 'Device' }, { key: 'device_uid', label: 'Device UID', group: 'Device' }, { key: 'device_ip', label: 'Device IP', group: 'Device' }, { key: 'device_os', label: 'Device OS', group: 'Device' }, { key: 'device_description', label: 'Device Description', group: 'Device' }, { key: 'last_user', label: 'Last User', group: 'Device' }, { key: 'site_name', label: 'Site Name', group: 'Site' }, { key: 'site_id', label: 'Site ID', group: 'Site' }, { key: 'site_uid', label: 'Site UID', group: 'Site' }, { key: 'platform', label: 'Platform', group: 'Site' }, ...Array.from({ length: 29 }, (_, i) => ({ key: `device_udf${i + 1}`, label: `UDF ${i + 1}`, group: 'UDF', })), ], autotask: [ { key: 'Action', label: 'Action (Create/Update/Delete)', group: 'Event' }, { key: 'EntityType', label: 'Entity Type', group: 'Event' }, { key: 'Id', label: 'Entity ID', group: 'Event' }, { key: 'EventTime', label: 'Event Time', group: 'Event' }, { key: 'SequenceNumber', label: 'Sequence Number', group: 'Event' }, { key: 'PersonId', label: 'Person ID', group: 'Event' }, ], veeam: [ { key: 'eventType', label: 'Event Type', group: 'Event' }, { key: 'severity', label: 'Severity', group: 'Event' }, { key: 'message', label: 'Message', group: 'Event' }, ], manual: [], }; // Fields produced by enrichment steps const STEP_OUTPUT_FIELDS: Record = { enrich_device: [ { key: 'device_found', label: 'Device Found (bool)' }, { key: 'device_hostname', label: 'Device Hostname' }, { key: 'device_os', label: 'Device OS' }, { key: 'device_ip', label: 'Device IP' }, { key: 'company_id', label: 'Company ID' }, { key: 'company_name', label: 'Company Name' }, ], enrich_company: [ { key: 'company_found', label: 'Company Found (bool)' }, { key: 'company_id', label: 'Company ID' }, { key: 'company_name', label: 'Company Name' }, ], enrich_ticket: [ { key: 'ticket_found', label: 'Ticket Found (bool)' }, { key: 'ticket_id', label: 'Ticket ID' }, { key: 'ticket_number', label: 'Ticket Number' }, { key: 'ticket_title', label: 'Ticket Title' }, ], transform: [], // dynamic — comes from mappings set_variable: [], // dynamic create_ticket: [ { key: 'ticket_id', label: 'Created Ticket ID' }, { key: 'ticket_number', label: 'Created Ticket Number' }, ], ai_analyze: [ { key: 'ai_response', label: 'AI Response' }, { key: 'ai_provider', label: 'AI Provider' }, { key: 'ai_model', label: 'AI Model' }, ], approval: [ { key: 'approval_id', label: 'Approval ID' }, { key: 'approval_result', label: 'Approval Result' }, ], rmm_quick_job: [ { key: 'job_uid', label: 'Job UID' }, { key: 'quick_job_result', label: 'Job Result' }, ], rmm_get_job_results: [ { key: 'job_results', label: 'Job Results' }, { key: 'job_status', label: 'Job Status' }, ], notify: [{ key: 'notified', label: 'Notified (bool)' }], enrich_vspc: [ { key: 'vspc_found', label: 'VSPC Found (bool)' }, { key: 'vspc_last_job_status', label: 'Last Job Status' }, { key: 'vspc_last_success', label: 'Last Success Date' }, { key: 'vspc_hours_since_success', label: 'Hours Since Success' }, { key: 'vspc_failure_message', label: 'Failure Message' }, { key: 'vspc_restore_points', label: 'Restore Points' }, { key: 'vspc_backed_up_size', label: 'Backed Up Size' }, { key: 'vspc_alarm_count', label: 'Alarm Count' }, { key: 'vspc_failed_job_count', label: 'Failed Job Count' }, { key: 'vspc_summary', label: 'VSPC Summary Text' }, { key: 'vspc_agent_jobs', label: 'Agent Jobs (array)' }, { key: 'vspc_server_jobs', label: 'Server Jobs (array)' }, { key: 'vspc_workloads', label: 'Workloads (array)' }, { key: 'vspc_alarms', label: 'Alarms (array)' }, ], db_query: [], // dynamic — output_key from config fetch_b2_result: [], // dynamic — output_key from config }; // Autotask ticket fields for create_ticket const TICKET_FIELDS = [ { key: 'title', label: 'Title', required: true, type: 'text' as const }, { key: 'description', label: 'Description', required: false, type: 'textarea' as const }, { key: 'companyID', label: 'Company ID', required: true, type: 'variable' as const }, { key: 'ticketType', label: 'Ticket Type', required: false, type: 'select' as const, options: [ { value: '1', label: '1 — Service Request' }, { value: '2', label: '2 — Incident' }, { value: '3', label: '3 — Problem' }, { value: '4', label: '4 — Change Request' }, ]}, { key: 'priority', label: 'Priority', required: false, type: 'select' as const, options: [ { value: '1', label: '1 — Critical' }, { value: '2', label: '2 — High' }, { value: '3', label: '3 — Medium' }, { value: '4', label: '4 — Low' }, ]}, { key: 'status', label: 'Status', required: false, type: 'select' as const, options: [ { value: '1', label: '1 — New' }, { value: '5', label: '5 — In Progress' }, ]}, { key: 'queueID', label: 'Queue ID', required: false, type: 'text' as const }, { key: 'issueType', label: 'Issue Type ID', required: false, type: 'text' as const }, { key: 'subIssueType', label: 'Sub-Issue Type ID', required: false, type: 'text' as const }, { key: 'ticketCategory', label: 'Ticket Category', required: false, type: 'select' as const, options: [ { value: '3', label: '3 — Standard' }, ]}, ]; // ============================================================================ // Props // ============================================================================ interface StepConfigEditorProps { stepType: string; config: Record; triggerSource: string; priorSteps: { step_type: string; name: string; config: Record }[]; channels: { id: number; name: string; channel_type: string }[]; onChange: (config: Record) => void; } // ============================================================================ // Variable Picker — shows insertable {{variables}} from trigger + prior steps // ============================================================================ function VariablePicker({ triggerSource, priorSteps, onInsert, compact = false, }: { triggerSource: string; priorSteps: { step_type: string; name: string; config: Record }[]; onInsert: (variable: string) => void; compact?: boolean; }) { const [isOpen, setIsOpen] = useState(false); const triggerFields = TRIGGER_FIELDS[triggerSource] || []; // Gather context fields from prior steps const contextFields: { key: string; label: string; source: string }[] = []; for (const step of priorSteps) { const outputs = STEP_OUTPUT_FIELDS[step.step_type] || []; for (const f of outputs) { contextFields.push({ ...f, source: step.name }); } // For transform steps, add the mapping keys if (step.step_type === 'transform' && step.config.mappings) { for (const key of Object.keys(step.config.mappings)) { contextFields.push({ key, label: key, source: step.name }); } } // For set_variable, add the key if (step.step_type === 'set_variable' && step.config.key) { contextFields.push({ key: step.config.key, label: step.config.key, source: step.name }); } } // Group trigger fields const triggerGroups = new Map(); for (const f of triggerFields) { const group = triggerGroups.get(f.group) || []; group.push(f); triggerGroups.set(f.group, group); } if (!isOpen) { return ( ); } return (
Insert Variable
{/* Trigger fields */} {triggerFields.length > 0 && (
Trigger Payload
{Array.from(triggerGroups.entries()).map(([group, fields]) => (
{group}
{fields.map(f => ( ))}
))}
)} {/* Context fields from prior steps */} {contextFields.length > 0 && (
From Prior Steps
{contextFields.map((f, i) => ( ))}
)}
); } // ============================================================================ // Insertable Input — text input with variable insert button // ============================================================================ function InsertableInput({ value, onChange, placeholder, triggerSource, priorSteps, multiline = false, rows = 3, label, required = false, }: { value: string; onChange: (val: string) => void; placeholder?: string; triggerSource: string; priorSteps: { step_type: string; name: string; config: Record }[]; multiline?: boolean; rows?: number; label?: string; required?: boolean; }) { const inputRef = useRef(null); const handleInsert = (variable: string) => { const el = inputRef.current; if (el) { const start = el.selectionStart || value.length; const end = el.selectionEnd || value.length; const newVal = value.substring(0, start) + variable + value.substring(end); onChange(newVal); // Restore cursor after the inserted variable setTimeout(() => { el.focus(); el.setSelectionRange(start + variable.length, start + variable.length); }, 0); } else { onChange(value + variable); } }; return (
{label && (
)} {multiline ? (