import { useState, useCallback } from 'react'; import ScenariosPanel from '../components/ScenariosPanel'; import StepLibrary from '../components/StepsPanel'; import { Feature, Scenario } from '../App'; import { ScenarioColumn } from '../components/ScenarioColumn'; import BddCodePanel from '../components/BddCodePanel'; import { AnimatePresence, Reorder } from 'framer-motion'; import Sidebar from '../components/Sidebar'; import { ScenarioSettingsPanel } from '../components/ScenarioSettingsPanel'; // --- Main Workflow Editor Component --- const getUniqueNodeId = () => `step_${Date.now()}_${Math.random()}`; function WorkflowEditor({ feature, onNavigateBack, updateFeatureInState, API_URL, stats }: { feature: Feature; onNavigateBack: () => void; updateFeatureInState: (featureId: string, featureUpdater: (prevFeature: Feature) => Feature) => void; API_URL: string; stats: { featuresCount: number; scenariosCount: number }; }) { const [selectedScenarioId, setSelectedScenarioId] = useState(null); const [editingScenarioId, setEditingScenarioId] = useState(null); const selectedScenario = feature.scenarios.find(s => s.id === selectedScenarioId) || null; const scenarioToEdit = feature.scenarios.find(s => s.id === editingScenarioId) || null; const handleSelectScenario = (id: string) => { setSelectedScenarioId(id); }; // --- API-Driven Handlers --- const handleAddScenario = async () => { const newId = `scenario_${Date.now()}`; const newScenarioData: Omit = { id: newId, text: "New Scenario", type: "Happy", isOutline: false, examples: '', }; // BUG FIX: Correctly update the feature's scenarios array within the feature object updateFeatureInState(feature.id, (prevFeature) => ({ ...prevFeature, scenarios: [...prevFeature.scenarios, { ...newScenarioData, nodes: [], edges: [] }] })); handleSelectScenario(newId); await fetch(`${API_URL}/features/${feature.id}/scenarios`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ ...newScenarioData, idx: feature.scenarios.length }), }); }; const handleDeleteScenario = async (scenarioId: string) => { // BUG FIX: Correctly update the feature's scenarios array within the feature object updateFeatureInState(feature.id, (prevFeature) => ({ ...prevFeature, scenarios: prevFeature.scenarios.filter(sc => sc.id !== scenarioId) })); if (selectedScenarioId === scenarioId) setSelectedScenarioId(null); if (editingScenarioId === scenarioId) setEditingScenarioId(null); await fetch(`${API_URL}/scenarios/${scenarioId}`, { method: 'DELETE' }); }; const handleUpdateScenario = async (scenarioId: string, newName: string, newType?: string, isOutline?: boolean, examples?: string) => { const scenario = feature.scenarios.find(sc => sc.id === scenarioId); if (!scenario) return; const updatedData = { text: newName, type: newType ?? scenario.type, isOutline: isOutline ?? scenario.isOutline, examples: examples ?? scenario.examples }; // BUG FIX: Correctly update the feature's scenarios array within the feature object updateFeatureInState(feature.id, (prevFeature) => ({ ...prevFeature, scenarios: prevFeature.scenarios.map(sc => sc.id === scenarioId ? { ...sc, ...updatedData } : sc) })); await fetch(`${API_URL}/scenarios/${scenarioId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(updatedData), }); }; const handleReorderScenarios = (reorderedScenarios: Scenario[]) => { // BUG FIX: Correctly update the feature's scenarios array within the feature object updateFeatureInState(feature.id, (prevFeature) => ({ ...prevFeature, scenarios: reorderedScenarios })); fetch(`${API_URL}/features/${feature.id}/reorder`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ scenarios: reorderedScenarios.map(s => s.id) }) }); }; const updateScenarioNodes = useCallback(async (scenarioId: string, nodes: any[]) => { // BUG FIX: Correctly update the feature's scenarios array within the feature object updateFeatureInState(feature.id, (prevFeature) => ({ ...prevFeature, scenarios: prevFeature.scenarios.map(sc => sc.id === scenarioId ? { ...sc, nodes } : sc) })); await fetch(`${API_URL}/scenarios/${scenarioId}/nodes`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ nodes }), }); }, [feature.id, updateFeatureInState, API_URL]); const handleReorderSteps = (scenarioId: string, reorderedSteps: any[]) => { const updatedNodes = reorderedSteps.map((node, index) => ({ ...node, position: { ...node.position, y: index * 100 } })); updateScenarioNodes(scenarioId, updatedNodes); }; const handleAddStep = (stepType: string, stepTemplate: string) => { if (!selectedScenarioId || !selectedScenario) return; const placeholderRegex = new RegExp('\\{([^}]+)\\}', 'g'); const placeholders = stepTemplate.match(placeholderRegex) || []; const newStep = { id: getUniqueNodeId(), type: 'stepNode', position: { x: 0, y: (selectedScenario.nodes.length + 1) * 100 }, data: { type: stepType, template: stepTemplate, values: new Array(placeholders.length).fill('') }, }; updateScenarioNodes(selectedScenarioId, [...selectedScenario.nodes, newStep]); }; const handleDeleteStep = (stepId: string, scenarioId: string) => { const targetScenario = feature.scenarios.find(sc => sc.id === scenarioId); if (!targetScenario) return; const updatedNodes = targetScenario.nodes.filter(node => node.id !== stepId); updateScenarioNodes(scenarioId, updatedNodes); }; const handleUpdateStep = (stepId: string, newValues: string[], scenarioId: string) => { const targetScenario = feature.scenarios.find(sc => sc.id === scenarioId); if (!targetScenario) return; const updatedNodes = targetScenario.nodes.map(node => node.id === stepId ? { ...node, data: { ...node.data, values: newValues } } : node); updateScenarioNodes(scenarioId, updatedNodes); }; return (

{feature.name}.feature

{feature.scenarios.map(scenario => ( handleDeleteStep(stepId, scenario.id)} onUpdateStep={(stepId, newValues) => handleUpdateStep(stepId, newValues, scenario.id)} onReorderSteps={(reorderedSteps) => handleReorderSteps(scenario.id, reorderedSteps)} /> ))}
setEditingScenarioId(null)} onUpdate={handleUpdateScenario} />
); } export default WorkflowEditor;