Se corrige limite de 200 y wrap en titulo validaciones en modal de adeicion de matria en mapa
This commit is contained in:
@@ -76,31 +76,43 @@ function RouteComponent() {
|
|||||||
mutate({ planId, patch })
|
mutate({ planId, patch })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const MAX_CHARACTERS = 200
|
||||||
|
|
||||||
const handleKeyDown = (e: React.KeyboardEvent<HTMLSpanElement>) => {
|
const handleKeyDown = (e: React.KeyboardEvent<HTMLSpanElement>) => {
|
||||||
|
// 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') {
|
if (e.key === 'Enter') {
|
||||||
e.preventDefault()
|
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<HTMLSpanElement>) => {
|
const handlePaste = (e: React.ClipboardEvent<HTMLSpanElement>) => {
|
||||||
const nuevoNombre = e.currentTarget.textContent || ''
|
e.preventDefault()
|
||||||
setNombrePlan(nuevoNombre)
|
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
|
// Calcular cuánto espacio queda
|
||||||
if (nuevoNombre !== data?.nombre) {
|
const remainingSpace = MAX_CHARACTERS - currentText.length
|
||||||
persistChange({ nombre: nuevoNombre })
|
|
||||||
|
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 (
|
return (
|
||||||
<div className="min-h-screen bg-white">
|
<div className="min-h-screen bg-white">
|
||||||
{/* 1. Header Superior */}
|
{/* 1. Header Superior */}
|
||||||
@@ -127,8 +139,9 @@ function RouteComponent() {
|
|||||||
) : (
|
) : (
|
||||||
<div className="flex flex-col items-start justify-between gap-4 md:flex-row">
|
<div className="flex flex-col items-start justify-between gap-4 md:flex-row">
|
||||||
<div>
|
<div>
|
||||||
<h1 className="flex items-baseline gap-2 text-3xl font-bold tracking-tight text-slate-900">
|
<h1 className="flex flex-wrap items-baseline gap-2 text-3xl leading-tight font-bold tracking-tight text-slate-900">
|
||||||
<span>{nivelPlan} en</span>
|
{/* El prefijo "Nivel en" lo mantenemos simple */}
|
||||||
|
<span className="shrink-0">{nivelPlan} en</span>
|
||||||
<span
|
<span
|
||||||
role="textbox"
|
role="textbox"
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
@@ -136,14 +149,17 @@ function RouteComponent() {
|
|||||||
suppressContentEditableWarning
|
suppressContentEditableWarning
|
||||||
spellCheck={false}
|
spellCheck={false}
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
|
onPaste={handlePaste} // Añadido para controlar lo que pegan
|
||||||
onBlur={(e) => {
|
onBlur={(e) => {
|
||||||
const nuevoNombre = e.currentTarget.textContent || ''
|
const nuevoNombre =
|
||||||
|
e.currentTarget.textContent?.trim() || ''
|
||||||
setNombrePlan(nuevoNombre)
|
setNombrePlan(nuevoNombre)
|
||||||
if (nuevoNombre !== data?.nombre) {
|
if (nuevoNombre !== data?.nombre) {
|
||||||
mutate({ planId, patch: { nombre: nuevoNombre } })
|
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' }}
|
style={{ textDecoration: 'none' }}
|
||||||
>
|
>
|
||||||
{nombrePlan}
|
{nombrePlan}
|
||||||
|
|||||||
@@ -93,7 +93,6 @@ function DatosGeneralesPage() {
|
|||||||
|
|
||||||
requerido: true,
|
requerido: true,
|
||||||
|
|
||||||
// 👇 TIPO DE CAMPO
|
|
||||||
tipo: Array.isArray(schema?.enum)
|
tipo: Array.isArray(schema?.enum)
|
||||||
? 'select'
|
? 'select'
|
||||||
: schema?.type === 'number'
|
: schema?.type === 'number'
|
||||||
|
|||||||
@@ -284,6 +284,16 @@ function MapaCurricularPage() {
|
|||||||
const ciclosTotales = Number(ciclo)
|
const ciclosTotales = Number(ciclo)
|
||||||
const ciclosArray = Array.from({ length: ciclosTotales }, (_, i) => i + 1)
|
const ciclosArray = Array.from({ length: ciclosTotales }, (_, i) => i + 1)
|
||||||
const [editingData, setEditingData] = useState<Asignatura | null>(null)
|
const [editingData, setEditingData] = useState<Asignatura | null>(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 => {
|
const handleDecimalChange = (value: string, max?: number): string | null => {
|
||||||
if (value === '') return ''
|
if (value === '') return ''
|
||||||
|
|
||||||
@@ -562,7 +572,6 @@ function MapaCurricularPage() {
|
|||||||
>
|
>
|
||||||
{editingLineaId === linea.id ? (
|
{editingLineaId === linea.id ? (
|
||||||
<Input
|
<Input
|
||||||
autoFocus
|
|
||||||
className="h-7 bg-white text-xs"
|
className="h-7 bg-white text-xs"
|
||||||
value={tempNombreLinea}
|
value={tempNombreLinea}
|
||||||
onChange={(e) => setTempNombreLinea(e.target.value)}
|
onChange={(e) => setTempNombreLinea(e.target.value)}
|
||||||
@@ -572,6 +581,7 @@ function MapaCurricularPage() {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
|
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
|
||||||
<span
|
<span
|
||||||
className="cursor-pointer text-xs font-bold hover:underline"
|
className="cursor-pointer text-xs font-bold hover:underline"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -727,6 +737,7 @@ function MapaCurricularPage() {
|
|||||||
Clave
|
Clave
|
||||||
</label>
|
</label>
|
||||||
<Input
|
<Input
|
||||||
|
maxLength={100}
|
||||||
value={editingData.clave}
|
value={editingData.clave}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setEditingData({ ...editingData, clave: e.target.value })
|
setEditingData({ ...editingData, clave: e.target.value })
|
||||||
@@ -738,6 +749,7 @@ function MapaCurricularPage() {
|
|||||||
Nombre
|
Nombre
|
||||||
</label>
|
</label>
|
||||||
<Input
|
<Input
|
||||||
|
maxLength={200}
|
||||||
value={editingData.nombre}
|
value={editingData.nombre}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setEditingData({ ...editingData, nombre: e.target.value })
|
setEditingData({ ...editingData, nombre: e.target.value })
|
||||||
@@ -775,7 +787,7 @@ function MapaCurricularPage() {
|
|||||||
type="number"
|
type="number"
|
||||||
value={editingData.hd}
|
value={editingData.hd}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const val = handleDecimalChange(e.target.value, 10)
|
const val = handleIntegerChange(e.target.value)
|
||||||
if (val !== null) {
|
if (val !== null) {
|
||||||
setEditingData({
|
setEditingData({
|
||||||
...editingData,
|
...editingData,
|
||||||
@@ -793,7 +805,7 @@ function MapaCurricularPage() {
|
|||||||
type="number"
|
type="number"
|
||||||
value={editingData.hi}
|
value={editingData.hi}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const val = handleDecimalChange(e.target.value, 10)
|
const val = handleIntegerChange(e.target.value)
|
||||||
if (val !== null) {
|
if (val !== null) {
|
||||||
setEditingData({
|
setEditingData({
|
||||||
...editingData,
|
...editingData,
|
||||||
|
|||||||
Reference in New Issue
Block a user