import { useState, useEffect } from 'react' import { Plus, X, HelpCircle, Zap, CheckCircle } from 'lucide-react' import { cn } from '@/lib/utils' import { stepCategoriesApi } from '@/api' import type { StepCreate, StepCategory, StepCommand } from '@/types/step' interface StepFormProps { onSubmit: (data: StepCreate) => void onCancel: () => void initialData?: Partial } const stepTypeOptions = [ { value: 'decision', label: 'Decision', icon: HelpCircle, description: 'Question with multiple options' }, { value: 'action', label: 'Action', icon: Zap, description: 'Task to perform' }, { value: 'solution', label: 'Solution', icon: CheckCircle, description: 'Resolution endpoint' } ] as const export function StepForm({ onSubmit, onCancel, initialData }: StepFormProps) { // Form state const [stepType, setStepType] = useState<'decision' | 'action' | 'solution'>( initialData?.step_type || 'action' ) const [title, setTitle] = useState(initialData?.title || '') const [instructions, setInstructions] = useState(initialData?.content?.instructions || '') const [helpText, setHelpText] = useState(initialData?.content?.help_text || '') const [commands, setCommands] = useState(initialData?.content?.commands || []) const [categoryId, setCategoryId] = useState(initialData?.category_id || '') const [tags, setTags] = useState(initialData?.tags || []) const [tagInput, setTagInput] = useState('') const [visibility, setVisibility] = useState<'private' | 'team' | 'public'>( initialData?.visibility || 'private' ) // Categories const [categories, setCategories] = useState([]) // Validation const [errors, setErrors] = useState>({}) useEffect(() => { const loadCategories = async () => { try { const data = await stepCategoriesApi.list() setCategories(data.filter(c => c.is_active)) } catch (err) { console.error('Failed to load categories:', err) } } loadCategories() }, []) const addCommand = () => { setCommands([...commands, { label: '', command: '', command_type: 'shell' }]) } const removeCommand = (index: number) => { setCommands(commands.filter((_, i) => i !== index)) } const updateCommand = (index: number, field: keyof StepCommand, value: string) => { const updated = [...commands] updated[index] = { ...updated[index], [field]: value } setCommands(updated) } const addTag = () => { const trimmed = tagInput.trim() if (trimmed && !tags.includes(trimmed)) { setTags([...tags, trimmed]) setTagInput('') } } const removeTag = (tag: string) => { setTags(tags.filter(t => t !== tag)) } const handleTagInputKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Enter') { e.preventDefault() addTag() } } const validate = (): boolean => { const newErrors: Record = {} if (!title.trim()) { newErrors.title = 'Title is required' } if (!instructions.trim()) { newErrors.instructions = 'Instructions are required' } // Validate commands commands.forEach((cmd, i) => { if (cmd.label && !cmd.command) { newErrors[`command_${i}_command`] = 'Command is required if label is provided' } if (cmd.command && !cmd.label) { newErrors[`command_${i}_label`] = 'Label is required if command is provided' } }) setErrors(newErrors) return Object.keys(newErrors).length === 0 } const handleSubmit = (e: React.FormEvent) => { e.preventDefault() if (!validate()) { return } const data: StepCreate = { title: title.trim(), step_type: stepType, content: { instructions: instructions.trim(), help_text: helpText.trim() || undefined, commands: commands.filter(c => c.label && c.command).length > 0 ? commands.filter(c => c.label && c.command) : undefined }, visibility, category_id: categoryId || undefined, tags: tags.length > 0 ? tags : undefined } onSubmit(data) } return (
{/* Step Type */}
{stepTypeOptions.map(option => { const Icon = option.icon return ( ) })}
{/* Title */}
setTitle(e.target.value)} placeholder="Enter step title" className={cn( 'w-full rounded-md border bg-black/50 px-3 py-2 text-sm text-white focus:outline-none focus:border-white/30 focus:ring-1 focus:ring-white/20', errors.title ? 'border-red-400/50' : 'border-white/10' )} /> {errors.title && (

{errors.title}

)}
{/* Instructions */}