/** * GuidedWizard Component * Multi-step wizard for onboarding flows * Types: create_operation, onboard_agent, run_scan, first_time_setup */ import React, { useState, useEffect } from 'react'; const GuidedWizard = ({ wizardType = 'first_time_setup', onComplete, onCancel, initialData = {} }) => { const [currentStep, setCurrentStep] = useState(1); const [formData, setFormData] = useState(initialData); const [stepHelp, setStepHelp] = useState(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const wizardConfigs = { create_operation: { title: 'Create New Operation', steps: [ { number: 1, title: 'Operation Name and Type', fields: [ { name: 'operation_name', label: 'Operation Name', type: 'text', required: true, placeholder: 'Q4 Security Assessment' }, { name: 'operation_type', label: 'Operation Type', type: 'select', required: true, options: [ { value: 'external', label: 'External Penetration Test' }, { value: 'internal', label: 'Internal Network Assessment' }, { value: 'webapp', label: 'Web Application Test' }, { value: 'wireless', label: 'Wireless Security Assessment' } ]} ] }, { number: 2, title: 'Define Target Scope', fields: [ { name: 'target_range', label: 'Target Network Range', type: 'text', required: true, placeholder: '192.168.1.0/24' }, { name: 'excluded_hosts', label: 'Excluded Hosts (comma-separated)', type: 'text', placeholder: '192.168.1.1, 192.168.1.254' }, { name: 'domains', label: 'Target Domains', type: 'textarea', placeholder: 'example.com\napp.example.com' } ] }, { number: 3, title: 'Configure Assessment Tools', fields: [ { name: 'scan_intensity', label: 'Scan Intensity', type: 'select', required: true, options: [ { value: '1', label: 'Stealth (Slowest, least detectable)' }, { value: '3', label: 'Balanced (Recommended)' }, { value: '5', label: 'Aggressive (Fastest, easily detected)' } ]}, { name: 'tools', label: 'Tools to Use', type: 'multiselect', options: [ { value: 'nmap', label: 'Nmap (Network Scanning)' }, { value: 'nikto', label: 'Nikto (Web Server Scanning)' }, { value: 'gobuster', label: 'Gobuster (Directory Enumeration)' }, { value: 'sqlmap', label: 'SQLMap (SQL Injection Testing)' } ]} ] } ] }, run_scan: { title: 'Run Security Scan', steps: [ { number: 1, title: 'Select Scan Tool', fields: [ { name: 'tool', label: 'Security Tool', type: 'select', required: true, options: [ { value: 'nmap', label: 'Nmap - Network Scanner' }, { value: 'nikto', label: 'Nikto - Web Server Scanner' }, { value: 'gobuster', label: 'Gobuster - Directory/File Discovery' }, { value: 'sqlmap', label: 'SQLMap - SQL Injection' }, { value: 'whatweb', label: 'WhatWeb - Technology Detection' } ]} ] }, { number: 2, title: 'Specify Target', fields: [ { name: 'target', label: 'Target', type: 'text', required: true, placeholder: '192.168.1.0/24 or example.com' }, { name: 'ports', label: 'Ports (optional)', type: 'text', placeholder: '80,443,8080 or 1-1000' } ] }, { number: 3, title: 'Scan Options', fields: [ { name: 'scan_type', label: 'Scan Type', type: 'select', required: true, options: [ { value: 'quick', label: 'Quick Scan (Fast, common ports)' }, { value: 'full', label: 'Full Scan (Comprehensive, slower)' }, { value: 'stealth', label: 'Stealth Scan (Slow, harder to detect)' }, { value: 'vuln', label: 'Vulnerability Scan (Checks for known vulns)' } ]}, { name: 'timeout', label: 'Timeout (seconds)', type: 'number', placeholder: '300' } ] } ] }, first_time_setup: { title: 'Welcome to StrikePackageGPT', steps: [ { number: 1, title: 'Welcome', fields: [ { name: 'user_name', label: 'Your Name', type: 'text', placeholder: 'John Doe' }, { name: 'skill_level', label: 'Security Testing Experience', type: 'select', required: true, options: [ { value: 'beginner', label: 'Beginner - Learning the basics' }, { value: 'intermediate', label: 'Intermediate - Some experience' }, { value: 'advanced', label: 'Advanced - Professional pentester' } ]} ] }, { number: 2, title: 'Configure LLM Provider', fields: [ { name: 'llm_provider', label: 'LLM Provider', type: 'select', required: true, options: [ { value: 'ollama', label: 'Ollama (Local, Free)' }, { value: 'openai', label: 'OpenAI (Cloud, Requires API Key)' }, { value: 'anthropic', label: 'Anthropic Claude (Cloud, Requires API Key)' } ]}, { name: 'api_key', label: 'API Key (if using cloud provider)', type: 'password', placeholder: 'sk-...' } ] }, { number: 3, title: 'Review and Finish', fields: [] } ] } }; const config = wizardConfigs[wizardType] || wizardConfigs.first_time_setup; const totalSteps = config.steps.length; const currentStepConfig = config.steps[currentStep - 1]; useEffect(() => { fetchStepHelp(); }, [currentStep]); const fetchStepHelp = async () => { try { const response = await fetch(`/api/wizard/help?type=${wizardType}&step=${currentStep}`); if (response.ok) { const data = await response.json(); setStepHelp(data); } } catch (err) { console.error('Failed to fetch step help:', err); } }; const handleFieldChange = (fieldName, value) => { setFormData(prev => ({ ...prev, [fieldName]: value })); }; const validateCurrentStep = () => { const requiredFields = currentStepConfig.fields.filter(f => f.required); for (const field of requiredFields) { if (!formData[field.name]) { setError(`${field.label} is required`); return false; } } setError(null); return true; }; const handleNext = () => { if (!validateCurrentStep()) return; if (currentStep < totalSteps) { setCurrentStep(prev => prev + 1); } else { handleComplete(); } }; const handleBack = () => { if (currentStep > 1) { setCurrentStep(prev => prev - 1); setError(null); } }; const handleComplete = async () => { if (!validateCurrentStep()) return; setLoading(true); try { if (onComplete) { await onComplete(formData); } } catch (err) { setError('Failed to complete wizard: ' + err.message); } finally { setLoading(false); } }; const renderField = (field) => { const commonStyle = { width: '100%', padding: '10px', border: '1px solid #ddd', borderRadius: '4px', fontSize: '14px' }; switch (field.type) { case 'text': case 'password': case 'number': return ( handleFieldChange(field.name, e.target.value)} placeholder={field.placeholder} style={commonStyle} /> ); case 'textarea': return (