Se corrige ediciones del modal y rutas de la pagina con id
This commit is contained in:
@@ -73,15 +73,13 @@ function EditableHeaderField({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span
|
<input
|
||||||
contentEditable
|
type="text"
|
||||||
suppressContentEditableWarning
|
value={String(value)}
|
||||||
onKeyDown={handleKeyDown}
|
onChange={(e) => onSave(e.target.value)}
|
||||||
onBlur={handleBlur}
|
onBlur={(e) => onSave(e.target.value)}
|
||||||
className={`cursor-text rounded px-1 transition-all outline-none focus:ring-2 focus:ring-blue-400 ${className}`}
|
className={`border-none bg-transparent outline-none focus:ring-2 focus:ring-blue-400 ${className}`}
|
||||||
>
|
/>
|
||||||
{value}
|
|
||||||
</span>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,6 +95,9 @@ export default function MateriaDetailPage() {
|
|||||||
const { asignaturaId } = useParams({
|
const { asignaturaId } = useParams({
|
||||||
from: '/planes/$planId/asignaturas/$asignaturaId',
|
from: '/planes/$planId/asignaturas/$asignaturaId',
|
||||||
})
|
})
|
||||||
|
const { planId } = useParams({
|
||||||
|
from: '/planes/$planId/asignaturas/$asignaturaId',
|
||||||
|
})
|
||||||
const { data: asignaturasApi, isLoading: loadingAsig } =
|
const { data: asignaturasApi, isLoading: loadingAsig } =
|
||||||
useSubject(asignaturaId)
|
useSubject(asignaturaId)
|
||||||
// 1. Asegúrate de tener estos estados en tu componente principal
|
// 1. Asegúrate de tener estos estados en tu componente principal
|
||||||
@@ -116,10 +117,10 @@ export default function MateriaDetailPage() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (asignaturasApi) {
|
if (asignaturasApi) {
|
||||||
setHeaderData({
|
setHeaderData({
|
||||||
codigo: asignaturasApi?.codigo ?? '',
|
codigo: asignaturasApi.codigo ?? '',
|
||||||
nombre: asignaturasApi?.nombre ?? '',
|
nombre: asignaturasApi.nombre,
|
||||||
creditos: asignaturasApi?.creditos ?? '',
|
creditos: asignaturasApi.creditos,
|
||||||
ciclo: asignaturasApi?.numero_ciclo ?? 0,
|
ciclo: asignaturasApi.numero_ciclo ?? 0,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}, [asignaturasApi])
|
}, [asignaturasApi])
|
||||||
@@ -194,6 +195,7 @@ export default function MateriaDetailPage() {
|
|||||||
<div className="mx-auto max-w-7xl px-6 py-10">
|
<div className="mx-auto max-w-7xl px-6 py-10">
|
||||||
<Link
|
<Link
|
||||||
to="/planes/$planId"
|
to="/planes/$planId"
|
||||||
|
params={{ planId }}
|
||||||
className="mb-4 flex items-center gap-2 text-sm text-blue-200 hover:text-white"
|
className="mb-4 flex items-center gap-2 text-sm text-blue-200 hover:text-white"
|
||||||
>
|
>
|
||||||
<ArrowLeft className="h-4 w-4" /> Volver al plan
|
<ArrowLeft className="h-4 w-4" /> Volver al plan
|
||||||
|
|||||||
@@ -177,7 +177,29 @@ function MapaCurricularPage() {
|
|||||||
const manejarAgregarLinea = (nombre: string) => {
|
const manejarAgregarLinea = (nombre: string) => {
|
||||||
const nombreNormalizado = nombre.trim()
|
const nombreNormalizado = nombre.trim()
|
||||||
|
|
||||||
// Validar si es Área Común (insensible a mayúsculas/minúsculas)
|
// 1. Validar que no esté vacío
|
||||||
|
if (!nombreNormalizado) return
|
||||||
|
|
||||||
|
// 2. Validar duplicados (Insensible a mayúsculas/minúsculas y acentos)
|
||||||
|
const nombreParaComparar = nombreNormalizado
|
||||||
|
.toLowerCase()
|
||||||
|
.normalize('NFD')
|
||||||
|
.replace(/[\u0300-\u036f]/g, '')
|
||||||
|
|
||||||
|
const yaExiste = lineas.some((l) => {
|
||||||
|
const lineaNombreBase = l.nombre
|
||||||
|
.toLowerCase()
|
||||||
|
.normalize('NFD')
|
||||||
|
.replace(/[\u0300-\u036f]/g, '')
|
||||||
|
return lineaNombreBase === nombreParaComparar
|
||||||
|
})
|
||||||
|
|
||||||
|
if (yaExiste) {
|
||||||
|
alert(`La línea "${nombreNormalizado}" ya existe.`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Validar Área Común (usando tu lógica previa)
|
||||||
const esAreaComun =
|
const esAreaComun =
|
||||||
nombreNormalizado.toLowerCase() === 'área común' ||
|
nombreNormalizado.toLowerCase() === 'área común' ||
|
||||||
nombreNormalizado.toLowerCase() === 'area comun'
|
nombreNormalizado.toLowerCase() === 'area comun'
|
||||||
@@ -187,10 +209,12 @@ function MapaCurricularPage() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 4. Agregar la línea si todo está bien
|
||||||
const nueva = {
|
const nueva = {
|
||||||
id: crypto.randomUUID(),
|
id: crypto.randomUUID(),
|
||||||
nombre: nombreNormalizado,
|
nombre: nombreNormalizado,
|
||||||
orden: lineas.length + 1,
|
orden: lineas.length + 1,
|
||||||
|
color: '#1976d2',
|
||||||
}
|
}
|
||||||
|
|
||||||
setLineas([...lineas, nueva])
|
setLineas([...lineas, nueva])
|
||||||
@@ -198,6 +222,7 @@ function MapaCurricularPage() {
|
|||||||
if (esAreaComun) {
|
if (esAreaComun) {
|
||||||
setHasAreaComun(true)
|
setHasAreaComun(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
setNombreNuevaLinea('') // Limpiar input
|
setNombreNuevaLinea('') // Limpiar input
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -599,19 +624,46 @@ function MapaCurricularPage() {
|
|||||||
<label className="text-xs font-bold text-slate-500 uppercase">
|
<label className="text-xs font-bold text-slate-500 uppercase">
|
||||||
Créditos
|
Créditos
|
||||||
</label>
|
</label>
|
||||||
<Input type="number" value={editingData.creditos} />
|
<Input
|
||||||
|
type="number"
|
||||||
|
value={editingData.creditos}
|
||||||
|
onChange={(e) =>
|
||||||
|
setEditingData({
|
||||||
|
...editingData,
|
||||||
|
creditos: Number(e.target.value),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label className="text-xs font-bold text-slate-500 uppercase">
|
<label className="text-xs font-bold text-slate-500 uppercase">
|
||||||
HD (Horas Docente)
|
HD (Horas Docente)
|
||||||
</label>
|
</label>
|
||||||
<Input type="number" value={editingData.hd} />
|
<Input
|
||||||
|
type="number"
|
||||||
|
value={editingData.hd}
|
||||||
|
onChange={(e) =>
|
||||||
|
setEditingData({
|
||||||
|
...editingData,
|
||||||
|
hd: Number(e.target.value),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label className="text-xs font-bold text-slate-500 uppercase">
|
<label className="text-xs font-bold text-slate-500 uppercase">
|
||||||
HI (Horas Indep.)
|
HI (Horas Indep.)
|
||||||
</label>
|
</label>
|
||||||
<Input type="number" value={editingData.hi} />
|
<Input
|
||||||
|
type="number"
|
||||||
|
value={editingData.hi}
|
||||||
|
onChange={(e) =>
|
||||||
|
setEditingData({
|
||||||
|
...editingData,
|
||||||
|
hi: Number(e.target.value),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -621,11 +673,22 @@ function MapaCurricularPage() {
|
|||||||
<label className="text-xs font-bold text-slate-500 uppercase">
|
<label className="text-xs font-bold text-slate-500 uppercase">
|
||||||
Ciclo
|
Ciclo
|
||||||
</label>
|
</label>
|
||||||
<Select value={editingData.ciclo?.toString() || 'null'}>
|
<Select
|
||||||
|
value={editingData.ciclo?.toString() || 'unassigned'}
|
||||||
|
onValueChange={(val) =>
|
||||||
|
setEditingData({
|
||||||
|
...editingData,
|
||||||
|
ciclo: val === 'unassigned' ? null : Number(val),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
<SelectTrigger>
|
<SelectTrigger>
|
||||||
<SelectValue />
|
<SelectValue />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
|
<SelectItem value="unassigned">
|
||||||
|
-- Sin Asignar --
|
||||||
|
</SelectItem>
|
||||||
{ciclosArray.map((n) => (
|
{ciclosArray.map((n) => (
|
||||||
<SelectItem key={n} value={n.toString()}>
|
<SelectItem key={n} value={n.toString()}>
|
||||||
Ciclo {n}
|
Ciclo {n}
|
||||||
@@ -634,15 +697,27 @@ function MapaCurricularPage() {
|
|||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label className="text-xs font-bold text-slate-500 uppercase">
|
<label className="text-xs font-bold text-slate-500 uppercase">
|
||||||
Línea Curricular
|
Línea Curricular
|
||||||
</label>
|
</label>
|
||||||
<Select value={editingData.lineaCurricularId || 'null'}>
|
<Select
|
||||||
|
value={editingData.lineaCurricularId || 'unassigned'}
|
||||||
|
onValueChange={(val) =>
|
||||||
|
setEditingData({
|
||||||
|
...editingData,
|
||||||
|
lineaCurricularId: val === 'unassigned' ? null : val,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
<SelectTrigger>
|
<SelectTrigger>
|
||||||
<SelectValue />
|
<SelectValue />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
|
<SelectItem value="unassigned">
|
||||||
|
-- Sin Asignar --
|
||||||
|
</SelectItem>
|
||||||
{lineas.map((l) => (
|
{lineas.map((l) => (
|
||||||
<SelectItem key={l.id} value={l.id}>
|
<SelectItem key={l.id} value={l.id}>
|
||||||
{l.nombre}
|
{l.nombre}
|
||||||
@@ -689,7 +764,12 @@ function MapaCurricularPage() {
|
|||||||
<label className="text-xs font-bold text-slate-500 uppercase">
|
<label className="text-xs font-bold text-slate-500 uppercase">
|
||||||
Tipo
|
Tipo
|
||||||
</label>
|
</label>
|
||||||
<Select value={editingData.tipo}>
|
<Select
|
||||||
|
value={editingData.tipo}
|
||||||
|
onValueChange={(val: 'obligatoria' | 'optativa') =>
|
||||||
|
setEditingData({ ...editingData, tipo: val })
|
||||||
|
}
|
||||||
|
>
|
||||||
<SelectTrigger>
|
<SelectTrigger>
|
||||||
<SelectValue />
|
<SelectValue />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
|
|||||||
Reference in New Issue
Block a user