add: manejar errores para que no lleguen datos vacíos al endpoint

This commit is contained in:
Alexis
2026-03-25 09:21:09 +01:00
parent 9a3c40e30e
commit 5ba0fe6711
4 changed files with 104 additions and 50 deletions
+22 -11
View File
@@ -1,8 +1,10 @@
export default function CardEditor({ index, level, handleLevelChange, handleRemoveLevel, totalLevels }) { export default function CardEditor({ index, level, handleLevelChange, handleRemoveLevel, totalLevels, error }) {
return ( return (
<div className="relative w-72 h-44 bg-white border-2 border-slate-200 rounded-xl shadow-[0_8px_30px_rgb(0,0,0,0.08)] flex flex-col items-center justify-center transition-transform hover:-translate-y-1 hover:shadow-[0_12px_40px_rgb(0,0,0,0.12)] group"> <div className="flex flex-col items-center">
<div className={`relative w-72 h-44 bg-white border-2 rounded-xl shadow-[0_8px_30px_rgb(0,0,0,0.08)] flex flex-col items-center justify-center transition-transform hover:-translate-y-1 hover:shadow-[0_12px_40px_rgb(0,0,0,0.12)] group ${
{/* Botón Eliminar */} error ? 'border-red-400 shadow-red-100' : 'border-slate-200'
}`}>
{/* Botón para eliminar */}
{totalLevels > 2 && ( {totalLevels > 2 && (
<button <button
onClick={() => handleRemoveLevel(index)} onClick={() => handleRemoveLevel(index)}
@@ -14,20 +16,29 @@ export default function CardEditor({ index, level, handleLevelChange, handleRemo
)} )}
{/* Detalles tipo naipe */} {/* Detalles tipo naipe */}
<span className="absolute top-4 left-4 text-sm font-black text-slate-300"> <span className="absolute top-4 left-4 text-sm font-black text-slate-300">{index + 1}</span>
{index + 1} <span className="absolute bottom-4 right-4 text-sm font-black text-slate-300 rotate-180">{index + 1}</span>
</span>
<span className="absolute bottom-4 right-4 text-sm font-black text-slate-300 rotate-180">
{index + 1}
</span>
<input <input
type="text" type="text"
placeholder="Escribe aquí..." placeholder="Escribe aquí..."
value={level} value={level}
onChange={(e) => handleLevelChange(index, e.target.value)} onChange={(e) => 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" className={`w-4/5 text-center text-2xl font-bold text-slate-700 bg-transparent border-b-2 border-dashed outline-none pb-1 ${
error ? 'border-red-300 focus:border-red-500 placeholder:text-red-200' : 'border-slate-300 focus:border-blue-500'
}`}
/> />
</div> </div>
{/* Mensaje de error */}
<div className="h-6 mt-2">
{error && (
<p className="text-red-500 text-sm font-semibold animate-pulse">
Escribe una etiqueta
</p>
)}
</div>
</div>
); );
} }
+13 -2
View File
@@ -1,4 +1,4 @@
export default function CriterionInput({ criterionName, setCriterionName }) { export default function CriterionInput({ criterionName, setCriterionName, error }) {
return ( return (
<div className="w-full max-w-2xl bg-white p-6 rounded-2xl shadow-sm border border-slate-200 mb-12"> <div className="w-full max-w-2xl bg-white p-6 rounded-2xl shadow-sm border border-slate-200 mb-12">
<label className="block text-sm font-bold text-slate-400 uppercase tracking-widest mb-2 text-center"> <label className="block text-sm font-bold text-slate-400 uppercase tracking-widest mb-2 text-center">
@@ -9,8 +9,19 @@ export default function CriterionInput({ criterionName, setCriterionName }) {
placeholder="Ej. Calidad del aceite..." placeholder="Ej. Calidad del aceite..."
value={criterionName} value={criterionName}
onChange={(e) => setCriterionName(e.target.value)} onChange={(e) => 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" className={`w-full text-3xl font-bold p-2 text-center text-slate-700 border-b-2 outline-none transition-colors ${
error
? 'border-red-400 focus:border-red-500'
: 'border-transparent hover:border-slate-200 focus:border-blue-500'
}`}
/> />
{error && (
<p className="text-red-500 text-sm font-semibold text-center mt-2 animate-pulse">
El nombre del criterio es obligatorio
</p>
)}
</div> </div>
); );
} }
@@ -22,7 +22,7 @@ export const MainLayout = () => {
}` }`
} }
> >
DoC Clásico DoC Básico
</NavLink> </NavLink>
<NavLink <NavLink
+44 -12
View File
@@ -14,23 +14,37 @@ export default function BasicMode() {
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const [result, setResult] = useState(null); const [result, setResult] = useState(null);
const [errors, setErrors] = useState({ criterion: false, levels: [] });
const handleCalculate = async () => { const handleCalculate = async () => {
let hasError = false;
const newErrors = { criterion: false, levels: Array(levels.length).fill(false) };
if (!criterionName.trim()) {
newErrors.criterion = true;
hasError = true;
}
levels.forEach((level, idx) => {
if (!level.trim()) {
newErrors.levels[idx] = true;
hasError = true;
}
});
setErrors(newErrors);
if (hasError) return;
setIsLoading(true); setIsLoading(true);
setResult(null); setResult(null);
const firstIndex = "0";
const lastIndex = (levels.length - 1).toString();
const currentReferences = {
[firstIndex]: 0,
[lastIndex]: 1
};
const payload = { const payload = {
criterion_name: criterionName || "Criterio sin nombre", criterion_name: criterionName.trim(),
levels: levels, levels: levels.map(l => l.trim()),
blank_cards: blankCards, blank_cards: blankCards,
references: currentReferences references: { "0": 0, [(levels.length - 1).toString()]: 1 }
}; };
try { try {
@@ -43,15 +57,27 @@ export default function BasicMode() {
} }
}; };
const handleCriterionChange = (val) => {
setCriterionName(val);
if (errors.criterion) setErrors({ ...errors, criterion: false });
};
const handleLevelChange = (index, newValue) => { const handleLevelChange = (index, newValue) => {
const newLevels = [...levels]; const newLevels = [...levels];
newLevels[index] = newValue; newLevels[index] = newValue;
setLevels(newLevels); setLevels(newLevels);
if (errors.levels[index]) {
const newErrLevels = [...errors.levels];
newErrLevels[index] = false;
setErrors({ ...errors, levels: newErrLevels });
}
}; };
const handleAddLevel = () => { const handleAddLevel = () => {
setLevels([...levels, '']); setLevels([...levels, '']);
setBlankCards([...blankCards, 0]); setBlankCards([...blankCards, 0]);
setErrors({ ...errors, levels: [...errors.levels, false] });
}; };
const handleRemoveLevel = (indexToRemove) => { const handleRemoveLevel = (indexToRemove) => {
@@ -59,8 +85,12 @@ export default function BasicMode() {
const newLevels = levels.filter((_, index) => index !== indexToRemove); const newLevels = levels.filter((_, index) => index !== indexToRemove);
const blankIndexToRemove = indexToRemove === 0 ? 0 : indexToRemove - 1; const blankIndexToRemove = indexToRemove === 0 ? 0 : indexToRemove - 1;
const newBlankCards = blankCards.filter((_, index) => index !== blankIndexToRemove); const newBlankCards = blankCards.filter((_, index) => index !== blankIndexToRemove);
const newErrLevels = errors.levels.filter((_, index) => index !== indexToRemove);
setLevels(newLevels); setLevels(newLevels);
setBlankCards(newBlankCards); setBlankCards(newBlankCards);
setErrors({ ...errors, levels: newErrLevels });
}; };
const handleBlankCardChange = (index, delta) => { const handleBlankCardChange = (index, delta) => {
@@ -77,7 +107,8 @@ export default function BasicMode() {
<CriterionInput <CriterionInput
criterionName={criterionName} criterionName={criterionName}
setCriterionName={setCriterionName} setCriterionName={handleCriterionChange}
error={errors.criterion}
/> />
<div className="w-full max-w-lg flex flex-col items-center"> <div className="w-full max-w-lg flex flex-col items-center">
@@ -90,6 +121,7 @@ export default function BasicMode() {
handleLevelChange={handleLevelChange} handleLevelChange={handleLevelChange}
handleRemoveLevel={handleRemoveLevel} handleRemoveLevel={handleRemoveLevel}
totalLevels={levels.length} totalLevels={levels.length}
error={errors.levels[index]}
/> />
{index < levels.length - 1 && ( {index < levels.length - 1 && (