diff --git a/frontend/index.html b/frontend/index.html index f94d687..609e6e7 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -4,7 +4,7 @@ - frontend + Deck of Cards
diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 292034c..0545d76 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,243 +1,9 @@ -import { useState } from 'react'; -import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts'; -import { calculateValueFunction } from './services/docService'; +import BasicMode from './pages/BasicMode'; function App() { - - const [criterionName, setCriterionName] = useState(''); - const [levels, setLevels] = useState(['', '', '']); - const [blankCards, setBlankCards] = useState([0, 0]); - - const [isLoading, setIsLoading] = useState(false); - const [result, setResult] = useState(null); - - const handleCalculate = async () => { - setIsLoading(true); - setResult(null); - - const firstIndex = "0"; - const lastIndex = (levels.length - 1).toString(); - - const currentReferences = { - [firstIndex]: 0, - [lastIndex]: 1 - }; - - const payload = { - criterion_name: criterionName || "Criterio sin nombre", - levels: levels, - blank_cards: blankCards, - references: currentReferences - }; - - try { - const data = await calculateValueFunction(payload); - setResult(data); - } catch (error) { - alert("No se ha podido conectar con el backend: " + error); - } finally { - setIsLoading(false); - } - }; - - - const handleLevelChange = (index, newValue) => { - const newLevels = [...levels]; - newLevels[index] = newValue; - setLevels(newLevels); - }; - - const handleAddLevel = () => { - setLevels([...levels, '']); - setBlankCards([...blankCards, 0]); - }; - - const handleRemoveLevel = (indexToRemove) => { - if (levels.length <= 2) return; - const newLevels = levels.filter((_, index) => index !== indexToRemove); - const blankIndexToRemove = indexToRemove === 0 ? 0 : indexToRemove - 1; - const newBlankCards = blankCards.filter((_, index) => index !== blankIndexToRemove); - setLevels(newLevels); - setBlankCards(newBlankCards); - }; - - const handleBlankCardChange = (index, delta) => { - const newBlankCards = [...blankCards]; - const newValue = newBlankCards[index] + delta; - if (newValue >= 0) { - newBlankCards[index] = newValue; - setBlankCards(newBlankCards); - } - }; - return (
- - {/* TÍTULO */} -
- - setCriterionName(e.target.value)} - className="w-full text-3xl font-bold p-2 text-center text-slate-700 border-b-2 border-transparent hover:border-slate-200 focus:border-blue-500 outline-none transition-colors" - /> -
- - {/* TIMELINE VERTICAL */} -
- {levels.map((level, index) => ( -
- - {/* CARTA DE NIVEL (etiqueta) */} -
- - {/* Botón Eliminar */} - {levels.length > 2 && ( - - )} - - {/* Detalles tipo naipe */} - - {index + 1} - - - {index + 1} - - - handleLevelChange(index, e.target.value)} - className="w-4/5 text-center text-2xl font-bold text-slate-700 bg-transparent border-b-2 border-dashed border-slate-300 focus:border-blue-500 outline-none pb-1" - /> -
- - {/* CONECTOR Y CARTAS BLANCAS */} - {index < levels.length - 1 && ( -
-
- -
- Blancas: - - - {blankCards[index]} - - -
- - {blankCards[index] > 0 && ( -
- {Array.from({ length: blankCards[index] }).map((_, i) => ( -
- ))} -
- )} - -
-
- )} -
- ))} - - {/* BOTÓN AÑADIR */} -
- -
- -
- - {/* BOTÓN DE CALCULAR */} -
- -
- - {/* GRÁFICA */} - {result && ( -
-

- Función de Valor: {result.criterion_name} -

- -
- - ({ - nombre: label, - valor: value - }))} - margin={{ top: 20, right: 30, left: 20, bottom: 20 }} - > - - - - - - - [value.toFixed(4), 'Valor DoC']} - labelStyle={{ fontWeight: 'bold', color: '#1e293b', marginBottom: '4px' }} - /> - - - - -
-
- )} - +
); } diff --git a/frontend/src/components/AddLevelButton.jsx b/frontend/src/components/AddLevelButton.jsx new file mode 100644 index 0000000..1d39d4e --- /dev/null +++ b/frontend/src/components/AddLevelButton.jsx @@ -0,0 +1,13 @@ +export default function AddLevelButton({ handleAddLevel }) { + return ( +
+ +
+ ); +} \ No newline at end of file diff --git a/frontend/src/components/BlankCardsCounter.jsx b/frontend/src/components/BlankCardsCounter.jsx index e69de29..2cabe8d 100644 --- a/frontend/src/components/BlankCardsCounter.jsx +++ b/frontend/src/components/BlankCardsCounter.jsx @@ -0,0 +1,36 @@ +export default function BlankCardsCounter({ index, blankCardsCount, handleBlankCardChange }) { + return ( +
+
+ +
+ Blancas: + + + {blankCardsCount} + + +
+ + {blankCardsCount > 0 && ( +
+ {Array.from({ length: blankCardsCount }).map((_, i) => ( +
+ ))} +
+ )} + +
+
+ ); +} \ No newline at end of file diff --git a/frontend/src/components/CardEditor.jsx b/frontend/src/components/CardEditor.jsx index e69de29..77c0c73 100644 --- a/frontend/src/components/CardEditor.jsx +++ b/frontend/src/components/CardEditor.jsx @@ -0,0 +1,33 @@ +export default function CardEditor({ index, level, handleLevelChange, handleRemoveLevel, totalLevels }) { + return ( +
+ + {/* Botón Eliminar */} + {totalLevels > 2 && ( + + )} + + {/* Detalles tipo naipe */} + + {index + 1} + + + {index + 1} + + + handleLevelChange(index, e.target.value)} + className="w-4/5 text-center text-2xl font-bold text-slate-700 bg-transparent border-b-2 border-dashed border-slate-300 focus:border-blue-500 outline-none pb-1" + /> +
+ ); +} \ No newline at end of file diff --git a/frontend/src/components/CriterionInput.jsx b/frontend/src/components/CriterionInput.jsx new file mode 100644 index 0000000..300c2de --- /dev/null +++ b/frontend/src/components/CriterionInput.jsx @@ -0,0 +1,16 @@ +export default function CriterionInput({ criterionName, setCriterionName }) { + return ( +
+ + setCriterionName(e.target.value)} + className="w-full text-3xl font-bold p-2 text-center text-slate-700 border-b-2 border-transparent hover:border-slate-200 focus:border-blue-500 outline-none transition-colors" + /> +
+ ); +} \ No newline at end of file diff --git a/frontend/src/components/ValueFunctionChart.jsx b/frontend/src/components/ValueFunctionChart.jsx index e69de29..e496d46 100644 --- a/frontend/src/components/ValueFunctionChart.jsx +++ b/frontend/src/components/ValueFunctionChart.jsx @@ -0,0 +1,43 @@ +import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts'; + +export default function ValueFunctionChart({ result }) { + if (!result) return null; + + return ( +
+

+ Función de Valor: {result.criterion_name} +

+ +
+ + ({ + nombre: label, + valor: value + }))} + margin={{ top: 20, right: 30, left: 20, bottom: 20 }} + > + + + + [value.toFixed(4), 'Valor DoC']} + labelStyle={{ fontWeight: 'bold', color: '#1e293b', marginBottom: '4px' }} + /> + + + +
+
+ ); +} \ No newline at end of file diff --git a/frontend/src/pages/BasicMode.jsx b/frontend/src/pages/BasicMode.jsx index e69de29..d5c7976 100644 --- a/frontend/src/pages/BasicMode.jsx +++ b/frontend/src/pages/BasicMode.jsx @@ -0,0 +1,124 @@ +import { useState } from 'react'; +import CriterionInput from '../components/CriterionInput'; +import CardEditor from '../components/CardEditor'; +import BlankCardsCounter from '../components/BlankCardsCounter'; +import AddLevelButton from '../components/AddLevelButton'; +import ValueFunctionChart from '../components/ValueFunctionChart'; +import { calculateValueFunction } from '../services/docService'; + +export default function BasicMode() { + const [criterionName, setCriterionName] = useState(''); + const [levels, setLevels] = useState(['', '', '']); + const [blankCards, setBlankCards] = useState([0, 0]); + + const [isLoading, setIsLoading] = useState(false); + const [result, setResult] = useState(null); + + const handleCalculate = async () => { + setIsLoading(true); + setResult(null); + + const firstIndex = "0"; + const lastIndex = (levels.length - 1).toString(); + + const currentReferences = { + [firstIndex]: 0, + [lastIndex]: 1 + }; + + const payload = { + criterion_name: criterionName || "Criterio sin nombre", + levels: levels, + blank_cards: blankCards, + references: currentReferences + }; + + try { + const data = await calculateValueFunction(payload); + setResult(data); + } catch (error) { + alert("No se ha podido conectar con el backend: " + error); + } finally { + setIsLoading(false); + } + }; + + const handleLevelChange = (index, newValue) => { + const newLevels = [...levels]; + newLevels[index] = newValue; + setLevels(newLevels); + }; + + const handleAddLevel = () => { + setLevels([...levels, '']); + setBlankCards([...blankCards, 0]); + }; + + const handleRemoveLevel = (indexToRemove) => { + if (levels.length <= 2) return; + const newLevels = levels.filter((_, index) => index !== indexToRemove); + const blankIndexToRemove = indexToRemove === 0 ? 0 : indexToRemove - 1; + const newBlankCards = blankCards.filter((_, index) => index !== blankIndexToRemove); + setLevels(newLevels); + setBlankCards(newBlankCards); + }; + + const handleBlankCardChange = (index, delta) => { + const newBlankCards = [...blankCards]; + const newValue = newBlankCards[index] + delta; + if (newValue >= 0) { + newBlankCards[index] = newValue; + setBlankCards(newBlankCards); + } + }; + + return ( +
+ + + +
+ {levels.map((level, index) => ( +
+ + + + {index < levels.length - 1 && ( + + )} +
+ ))} + + +
+ +
+ +
+ + + +
+); +} \ No newline at end of file