From 31a47934e58c9afc79bc8cdd76646eec6ea23149 Mon Sep 17 00:00:00 2001 From: "Roberto.silva" Date: Fri, 6 Feb 2026 14:42:38 -0600 Subject: [PATCH] Se corrige limite de 200 y wrap en titulo validaciones en modal de adeicion de matria en mapa --- src/routes/planes/$planId/_detalle.tsx | 56 +++++++++++++------- src/routes/planes/$planId/_detalle/index.tsx | 1 - src/routes/planes/$planId/_detalle/mapa.tsx | 18 +++++-- 3 files changed, 51 insertions(+), 24 deletions(-) diff --git a/src/routes/planes/$planId/_detalle.tsx b/src/routes/planes/$planId/_detalle.tsx index a00b877..9717dda 100644 --- a/src/routes/planes/$planId/_detalle.tsx +++ b/src/routes/planes/$planId/_detalle.tsx @@ -76,31 +76,43 @@ function RouteComponent() { mutate({ planId, patch }) } + const MAX_CHARACTERS = 200 + const handleKeyDown = (e: React.KeyboardEvent) => { + // 1. Permitir teclas de control (Borrar, flechas, etc.) siempre + const isControlKey = + e.key === 'Backspace' || + e.key === 'Delete' || + e.key.includes('Arrow') || + e.metaKey || + e.ctrlKey + if (e.key === 'Enter') { e.preventDefault() - e.currentTarget.blur() // Esto disparará el onBlur automáticamente + e.currentTarget.blur() + return + } + + // 2. Bloquear si excede los 200 caracteres y no es una tecla de control + const currentText = e.currentTarget.textContent || '' + if (currentText.length >= MAX_CHARACTERS && !isControlKey) { + e.preventDefault() } } - const handleBlurNombre = (e: React.FocusEvent) => { - const nuevoNombre = e.currentTarget.textContent || '' - setNombrePlan(nuevoNombre) + const handlePaste = (e: React.ClipboardEvent) => { + e.preventDefault() + const text = e.clipboardData.getData('text/plain') + const currentText = e.currentTarget.textContent || '' - // Solo guardamos si el valor es realmente distinto al de la base de datos - if (nuevoNombre !== data?.nombre) { - persistChange({ nombre: nuevoNombre }) + // Calcular cuánto espacio queda + const remainingSpace = MAX_CHARACTERS - currentText.length + + if (remainingSpace > 0) { + const slicedText = text.slice(0, remainingSpace) + document.execCommand('insertText', false, slicedText) } } - - const handleSelectNivel = (n: string) => { - setNivelPlan(n) - // Guardamos inmediatamente al seleccionar - if (n !== data?.nivel) { - persistChange({ nivel: n }) - } - } - return (
{/* 1. Header Superior */} @@ -127,8 +139,9 @@ function RouteComponent() { ) : (
-

- {nivelPlan} en +

+ {/* El prefijo "Nivel en" lo mantenemos simple */} + {nivelPlan} en { - const nuevoNombre = e.currentTarget.textContent || '' + const nuevoNombre = + e.currentTarget.textContent?.trim() || '' setNombrePlan(nuevoNombre) if (nuevoNombre !== data?.nombre) { mutate({ planId, patch: { nombre: nuevoNombre } }) } }} - className="cursor-text border-b border-transparent transition-colors outline-none select-text hover:border-slate-300 focus:border-teal-500" + // Clases añadidas: break-words y whitespace-pre-wrap para el wrap + className="block w-full cursor-text border-b border-transparent break-words whitespace-pre-wrap transition-colors outline-none select-text hover:border-slate-300 focus:border-teal-500 sm:inline-block sm:w-auto" style={{ textDecoration: 'none' }} > {nombrePlan} diff --git a/src/routes/planes/$planId/_detalle/index.tsx b/src/routes/planes/$planId/_detalle/index.tsx index b749151..38e64d2 100644 --- a/src/routes/planes/$planId/_detalle/index.tsx +++ b/src/routes/planes/$planId/_detalle/index.tsx @@ -93,7 +93,6 @@ function DatosGeneralesPage() { requerido: true, - // 👇 TIPO DE CAMPO tipo: Array.isArray(schema?.enum) ? 'select' : schema?.type === 'number' diff --git a/src/routes/planes/$planId/_detalle/mapa.tsx b/src/routes/planes/$planId/_detalle/mapa.tsx index be55a1a..8ee42f5 100644 --- a/src/routes/planes/$planId/_detalle/mapa.tsx +++ b/src/routes/planes/$planId/_detalle/mapa.tsx @@ -284,6 +284,16 @@ function MapaCurricularPage() { const ciclosTotales = Number(ciclo) const ciclosArray = Array.from({ length: ciclosTotales }, (_, i) => i + 1) const [editingData, setEditingData] = useState(null) + const handleIntegerChange = (value: string) => { + if (value === '') return value + + // Solo números, máximo 3 cifras + const regex = /^\d{1,3}$/ + + if (!regex.test(value)) return null + + return value + } const handleDecimalChange = (value: string, max?: number): string | null => { if (value === '') return '' @@ -562,7 +572,6 @@ function MapaCurricularPage() { > {editingLineaId === linea.id ? ( setTempNombreLinea(e.target.value)} @@ -572,6 +581,7 @@ function MapaCurricularPage() { } /> ) : ( + // eslint-disable-next-line jsx-a11y/no-static-element-interactions { @@ -727,6 +737,7 @@ function MapaCurricularPage() { Clave setEditingData({ ...editingData, clave: e.target.value }) @@ -738,6 +749,7 @@ function MapaCurricularPage() { Nombre setEditingData({ ...editingData, nombre: e.target.value }) @@ -775,7 +787,7 @@ function MapaCurricularPage() { type="number" value={editingData.hd} onChange={(e) => { - const val = handleDecimalChange(e.target.value, 10) + const val = handleIntegerChange(e.target.value) if (val !== null) { setEditingData({ ...editingData, @@ -793,7 +805,7 @@ function MapaCurricularPage() { type="number" value={editingData.hi} onChange={(e) => { - const val = handleDecimalChange(e.target.value, 10) + const val = handleIntegerChange(e.target.value) if (val !== null) { setEditingData({ ...editingData,