diff --git a/src/components/asignaturas/detalle/AsignaturaDetailPage.tsx b/src/components/asignaturas/detalle/AsignaturaDetailPage.tsx index 3d22627..ae37ea4 100644 --- a/src/components/asignaturas/detalle/AsignaturaDetailPage.tsx +++ b/src/components/asignaturas/detalle/AsignaturaDetailPage.tsx @@ -7,6 +7,13 @@ import type { AsignaturaDetail } from '@/data' import { Button } from '@/components/ui/button' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { Input } from '@/components/ui/input' +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select' import { Textarea } from '@/components/ui/textarea' import { Tooltip, @@ -14,6 +21,7 @@ import { TooltipProvider, TooltipTrigger, } from '@/components/ui/tooltip' +import { usePlanAsignaturas } from '@/data' import { useSubject, useUpdateAsignatura } from '@/data/hooks/useSubjects' export interface BibliografiaEntry { @@ -59,8 +67,12 @@ export default function AsignaturaDetailPage() { const { asignaturaId } = useParams({ from: '/planes/$planId/asignaturas/$asignaturaId', }) + const { planId } = useParams({ + from: '/planes/$planId/asignaturas/$asignaturaId', + }) const { data: asignaturaApi } = useSubject(asignaturaId) - + const { data: asignaturasApi, isLoading: loadingAsig } = + usePlanAsignaturas(planId) const [asignatura, setAsignatura] = useState(null) const updateAsignatura = useUpdateAsignatura() @@ -81,16 +93,54 @@ export default function AsignaturaDetailPage() { }, }) } + + const asignaturaSeriada = useMemo(() => { + if (!asignaturaApi?.prerrequisito_asignatura_id || !asignaturasApi) + return null + return asignaturasApi.find( + (asig) => asig.id === asignaturaApi.prerrequisito_asignatura_id, + ) + }, [asignaturaApi, asignaturasApi]) + const requisitosFormateados = useMemo(() => { + if (!asignaturaSeriada) return [] + return [ + { + type: 'Pre-requisito', + code: asignaturaSeriada.codigo, + name: asignaturaSeriada.nombre, + id: asignaturaSeriada.id, // Guardamos el ID para el select + }, + ] + }, [asignaturaSeriada]) + + const handleUpdatePrerrequisito = (newId: string | null) => { + updateAsignatura.mutate({ + asignaturaId, + patch: { + prerrequisito_asignatura_id: newId, + }, + }) + } /* ---------- sincronizar API ---------- */ useEffect(() => { - if (asignaturaApi) setAsignatura(asignaturaApi) - }, [asignaturaApi]) + console.log(requisitosFormateados) - return + if (asignaturaApi) setAsignatura(asignaturaApi) + }, [asignaturaApi, requisitosFormateados]) + + return ( + + ) } function DatosGenerales({ onPersistDato, + pre, + availableSubjects, }: { onPersistDato: (clave: string, value: string) => void }) { @@ -265,18 +315,19 @@ function DatosGenerales({ a.id !== asignaturaId) || [] + } + onPersist={({ value }) => { + updateAsignatura.mutate({ + asignaturaId, + patch: { + prerrequisito_asignatura_id: value, // value ya viene como ID o null desde handleSave + }, + }) + }} /> {/* Tarjeta de Evaluación */} @@ -316,6 +367,7 @@ interface InfoCardProps { containerRef?: React.RefObject forceEditToken?: number highlightToken?: number + availableSubjects?: any } function InfoCard({ @@ -332,6 +384,7 @@ function InfoCard({ containerRef, forceEditToken, highlightToken, + availableSubjects, }: InfoCardProps) { const [isEditing, setIsEditing] = useState(false) const [isHighlighted, setIsHighlighted] = useState(false) @@ -349,7 +402,8 @@ function InfoCard({ useEffect(() => { setData(initialContent) setTempText(initialContent) - + console.log(data) + console.log(initialContent) if (type === 'evaluation') { const raw = Array.isArray(initialContent) ? initialContent : [] const rows: Array = raw @@ -392,6 +446,8 @@ function InfoCard({ const handleSave = () => { console.log('clave, valor:', clave, String(tempText ?? '')) + console.log(clave) + console.log(tempText) if (type === 'evaluation') { const cleaned: Array = [] @@ -422,6 +478,25 @@ function InfoCard({ void onPersist?.({ type, clave, value: cleaned }) return } + if (type === 'requirements') { + console.log('entre aqui ') + + // Si tempText es un array y tiene elementos, tomamos el ID del primero + // Si es "none" o está vacío, mandamos null (para limpiar la seriación) + const prerequisiteId = + Array.isArray(tempText) && tempText.length > 0 ? tempText[0].id : null + + setData(tempText) // Actualiza la vista local + setIsEditing(false) + + // Mandamos el ID específico a la base de datos + void onPersist?.({ + type, + clave: 'prerrequisito_asignatura_id', // Forzamos la columna correcta + value: prerequisiteId, + }) + return + } setData(tempText) setIsEditing(false) @@ -541,7 +616,52 @@ function InfoCard({ {isEditing ? (
- {type === 'evaluation' ? ( + {/* Condicionales de edición según el tipo */} + {type === 'requirements' ? ( +
+ + +
+ ) : type === 'evaluation' ? (
{evalRows.map((row) => ( @@ -563,85 +683,36 @@ function InfoCard({ ) }} /> - { const raw = e.target.value - // Solo permitir '' o dígitos if (raw !== '' && !/^\d+$/.test(raw)) return - if (raw === '') { - setEvalRows((prev) => - prev.map((r) => - r.id === row.id - ? { - id: r.id, - criterio: r.criterio, - porcentaje: '', - } - : r, - ), - ) - return - } - - const n = Number(raw) - if (!Number.isFinite(n)) return - const porcentaje = Math.trunc(n) - if (porcentaje < 1 || porcentaje > 100) return - - // No permitir suma > 100 setEvalRows((prev) => { const next = prev.map((r) => - r.id === row.id - ? { - id: r.id, - criterio: r.criterio, - porcentaje: raw, - } - : r, + r.id === row.id ? { ...r, porcentaje: raw } : r, + ) + const total = next.reduce( + (acc, r) => acc + (Number(r.porcentaje) || 0), + 0, ) - - const total = next.reduce((acc, r) => { - const v = String(r.porcentaje).trim() - if (!v) return acc - const nn = Number(v) - if (!Number.isFinite(nn)) return acc - const vv = Math.trunc(nn) - if (vv < 1 || vv > 100) return acc - return acc + vv - }, 0) - return total > 100 ? prev : next }) }} /> - -
- % -
- +
%
@@ -651,22 +722,15 @@ function InfoCard({
Total: {evaluationTotal}/100 - @@ -689,28 +753,15 @@ function InfoCard({ className="min-h-30 text-sm leading-relaxed" /> )} + + {/* Botones de acción comunes */}
) : ( + /* Modo Visualización */
{type === 'text' && (data ? ( @@ -734,9 +786,7 @@ function InfoCard({

Sin información.

))} {type === 'requirements' && } - {type === 'evaluation' && ( - } /> - )} + {type === 'evaluation' && }
)} diff --git a/src/data/api/subjects.api.ts b/src/data/api/subjects.api.ts index 4b8059e..9b4ab51 100644 --- a/src/data/api/subjects.api.ts +++ b/src/data/api/subjects.api.ts @@ -191,7 +191,7 @@ export async function subjects_get(subjectId: UUID): Promise { .from('asignaturas') .select( ` - id,plan_estudio_id,estructura_id,codigo,nombre,tipo,creditos,numero_ciclo,linea_plan_id,orden_celda,estado,datos,contenido_tematico,horas_academicas,horas_independientes,asignatura_hash,tipo_origen,meta_origen,creado_por,actualizado_por,creado_en,actualizado_en,criterios_de_evaluacion, + id,plan_estudio_id,estructura_id,codigo,nombre,tipo,creditos,numero_ciclo,linea_plan_id,orden_celda,estado,datos,contenido_tematico,horas_academicas,horas_independientes,asignatura_hash,tipo_origen,meta_origen,creado_por,actualizado_por,creado_en,actualizado_en,criterios_de_evaluacion,prerrequisito_asignatura_id, planes_estudio( id,carrera_id,estructura_id,nombre,nivel,tipo_ciclo,numero_ciclos,datos,estado_actual_id,activo,tipo_origen,meta_origen,creado_por,actualizado_por,creado_en,actualizado_en, carreras(id,facultad_id,nombre,nombre_corto,clave_sep,activa, facultades(id,nombre,nombre_corto,color,icono))