diff --git a/frontend/src/components/membershipFunction/Chart.jsx b/frontend/src/components/membershipFunction/Chart.jsx index ac96c84..7c0134b 100644 --- a/frontend/src/components/membershipFunction/Chart.jsx +++ b/frontend/src/components/membershipFunction/Chart.jsx @@ -1,7 +1,7 @@ import React from 'react'; import { ComposedChart, Line, XAxis, YAxis, CartesianGrid, ReferenceArea, ReferenceLine, ResponsiveContainer, Tooltip } from 'recharts'; -export default function MembershipFunctionChart({ baseScale, mfDefinitions, selectedTerm, colors }) { +export default function Chart({ baseScale, mfDefinitions, selectedTerm, colors }) { const scaleKeys = Object.keys(baseScale); return ( diff --git a/frontend/src/components/membershipFunction/Controls.jsx b/frontend/src/components/membershipFunction/Controls.jsx index 6457562..9b653ae 100644 --- a/frontend/src/components/membershipFunction/Controls.jsx +++ b/frontend/src/components/membershipFunction/Controls.jsx @@ -1,19 +1,23 @@ -import React from 'react'; - -export default function MembershipFunctionControls({ selectedTerm, currentMf, selectedColor, baseScale, updateCurrentMf }) { +export default function Controls({ selectedTerm, currentMf, selectedColor, baseScale, mfDefinitions, updateCurrentMf }) { if (!selectedTerm || !currentMf) return null; const scaleKeys = Object.keys(baseScale); const selectedIndex = scaleKeys.indexOf(selectedTerm); - let minBound = 0; - let maxBound = 1; + let prevCoreEnd = 0; + let prevSupportEnd = 0; + let nextCoreStart = 1; + let nextSupportStart = 1; if (selectedIndex > 0) { - minBound = baseScale[scaleKeys[selectedIndex - 1]]; + const prevTerm = scaleKeys[selectedIndex - 1]; + prevCoreEnd = mfDefinitions[prevTerm].coreEnd; + prevSupportEnd = mfDefinitions[prevTerm].supportEnd; } - if (selectedIndex >= 0 && selectedIndex < scaleKeys.length - 1) { - maxBound = baseScale[scaleKeys[selectedIndex + 1]]; + if (selectedIndex < scaleKeys.length - 1) { + const nextTerm = scaleKeys[selectedIndex + 1]; + nextCoreStart = mfDefinitions[nextTerm].coreStart; + nextSupportStart = mfDefinitions[nextTerm].supportStart; } return ( @@ -25,35 +29,41 @@ export default function MembershipFunctionControls({ selectedTerm, currentMf, se
+ {/* Columna izquierda: Inicios */}
- updateCurrentMf('coreStart', e.target.value)} className="w-full cursor-pointer" style={{ accentColor: selectedColor }} /> + updateCurrentMf('coreStart', e.target.value)} className="w-full cursor-pointer" style={{ accentColor: selectedColor }} />
- updateCurrentMf('supportStart', e.target.value)} className="w-full cursor-pointer" style={{ accentColor: selectedColor, opacity: 0.7 }} /> + updateCurrentMf('supportStart', e.target.value)} className="w-full cursor-pointer" style={{ accentColor: selectedColor, opacity: 0.7 }} />
+ {/* Columna derecha: Fines */}
- updateCurrentMf('coreEnd', e.target.value)} className="w-full cursor-pointer" style={{ accentColor: selectedColor }} /> + updateCurrentMf('coreEnd', e.target.value)} className="w-full cursor-pointer" style={{ accentColor: selectedColor }} />
- updateCurrentMf('supportEnd', e.target.value)} className="w-full cursor-pointer" style={{ accentColor: selectedColor, opacity: 0.7 }} /> + updateCurrentMf('supportEnd', e.target.value)} className="w-full cursor-pointer" style={{ accentColor: selectedColor, opacity: 0.7 }} />
diff --git a/frontend/src/pages/AdvancedMode.jsx b/frontend/src/pages/AdvancedMode.jsx index 9119856..fb0b57b 100644 --- a/frontend/src/pages/AdvancedMode.jsx +++ b/frontend/src/pages/AdvancedMode.jsx @@ -3,8 +3,8 @@ import CriterionInput from '../components/CriterionInput'; import CardEditor from '../components/CardEditor'; import BlankCardsCounter from '../components/BlankCardsCounter'; import AddLevelButton from '../components/AddLevelButton'; -import MembershipFunctionChart from '../components/membershipFunction/Chart'; -import MembershipFunctionControls from '../components/membershipFunction/Controls'; +import Chart from '../components/membershipFunction/Chart'; +import Controls from '../components/membershipFunction/Controls'; import { calculateValueFunction } from '../services/docService'; const COLORS = ['#ef4444', '#f59e0b', '#10b981', '#3b82f6', '#d946ef', '#06b6d4', '#8b5cf6', '#f43f5e', '#6366f1']; @@ -24,7 +24,7 @@ export default function AdvancedMode() { const [selectedTerm, setSelectedTerm] = useState(null); const [mfDefinitions, setMfDefinitions] = useState({}); - // --- Manejadores de Escala --- + // Manejadores de Escala const handleCriterionChange = (val) => { setCriterionName(val); if (errors.criterion) setErrors({ ...errors, criterion: false }); }; const handleLevelChange = (index, newValue) => { const newLevels = [...levels]; newLevels[index] = newValue; setLevels(newLevels); if (errors.levels[index]) setErrors({ ...errors, levels: errors.levels.map((e, i) => i === index ? false : e) }); }; const handleAddLevel = () => { setLevels([...levels, '']); setBlankCards([...blankCards, 0]); setErrors({ ...errors, levels: [...errors.levels, false] }); }; @@ -32,9 +32,11 @@ export default function AdvancedMode() { const handleBlankCardChange = (index, delta) => { const newCards = [...blankCards]; if (newCards[index] + delta >= 0) { newCards[index] += delta; setBlankCards(newCards); } }; const handleGenerateBaseScale = async () => { - let hasError = false; const newErrors = { criterion: !criterionName.trim(), levels: levels.map(l => !l.trim()) }; - if (newErrors.criterion || newErrors.levels.includes(true)) { setErrors(newErrors); return alert("Por favor, rellena todos los campos."); } + if (newErrors.criterion || newErrors.levels.includes(true)) { + setErrors(newErrors); + return alert("Por favor, rellena todos los campos."); + } setIsLoading(true); try { @@ -51,17 +53,38 @@ export default function AdvancedMode() { } catch (error) { alert("Error: " + error); } finally { setIsLoading(false); } }; - // --- Manejadores de Franjas --- + // Manejadores de Franjas const updateCurrentMf = (field, value) => { if (!selectedTerm) return; - const numValue = parseFloat(value); + let numValue = parseFloat(value); setMfDefinitions(prev => { + const scaleKeys = Object.keys(baseScale); + const selectedIndex = scaleKeys.indexOf(selectedTerm); + + let prevCoreEnd = 0, prevSupportEnd = 0, nextCoreStart = 1, nextSupportStart = 1; + + if (selectedIndex > 0) { + prevCoreEnd = prev[scaleKeys[selectedIndex - 1]].coreEnd; + prevSupportEnd = prev[scaleKeys[selectedIndex - 1]].supportEnd; + } + if (selectedIndex < scaleKeys.length - 1) { + nextCoreStart = prev[scaleKeys[selectedIndex + 1]].coreStart; + nextSupportStart = prev[scaleKeys[selectedIndex + 1]].supportStart; + } + + if (field === 'supportStart' && numValue < prevCoreEnd) numValue = prevCoreEnd; + if (field === 'coreStart' && numValue < prevSupportEnd) numValue = prevSupportEnd; + if (field === 'coreEnd' && numValue > nextSupportStart) numValue = nextSupportStart; + if (field === 'supportEnd' && numValue > nextCoreStart) numValue = nextCoreStart; + const current = { ...prev[selectedTerm], [field]: numValue }; + if (field === 'supportStart' && current.supportStart > current.coreStart) current.coreStart = current.supportStart; if (field === 'coreStart') { if (current.coreStart < current.supportStart) current.supportStart = current.coreStart; if (current.coreStart > current.coreEnd) current.coreEnd = current.coreStart; } if (field === 'coreEnd') { if (current.coreEnd < current.coreStart) current.coreStart = current.coreEnd; if (current.coreEnd > current.supportEnd) current.supportEnd = current.coreEnd; } if (field === 'supportEnd' && current.supportEnd < current.coreEnd) current.coreEnd = current.supportEnd; + return { ...prev, [selectedTerm]: current }; }); }; @@ -75,6 +98,7 @@ export default function AdvancedMode() { const scaleKeys = Object.keys(baseScale); const selectedColor = COLORS[scaleKeys.indexOf(selectedTerm) % COLORS.length] || '#2563eb'; + return (
@@ -122,10 +146,16 @@ export default function AdvancedMode() { })}
- - - + +