Se agrega persistencia a planes en datos, se arregla bug de nombre de claves en asignaturas, se cambia en historial clves por los titulos corresppndientes

This commit is contained in:
2026-01-30 15:51:43 -06:00
parent 2185901c7a
commit 64d9aa336f
5 changed files with 72 additions and 19 deletions

View File

@@ -442,20 +442,13 @@ function DatosGenerales({
? config.examples[0]
: ''
// 2. CONTENIDO REAL (Viene de data.datos -> valoresActuales)
// El problema: Si 'description' en 'datos' es igual a la de la 'estructura',
// el usuario aún no ha redactado nada real.
const valActual = valoresActuales[key]
// Lógica para determinar si mostrar el contenido o dejarlo vacío (para que salga el placeholder)
// Si el contenido en 'datos' es idéntico a la instrucción de la 'estructura',
// asumimos que no hay contenido real todavía.
const isContentEmpty =
!valActual?.description ||
valActual.description === config.description
const currentContent = valActual?.description ?? ''
const currentContent = valActual ?? ''
return (
<InfoCard

View File

@@ -27,6 +27,9 @@ import { usePlanHistorial } from '@/data/hooks/usePlans'
export const Route = createFileRoute('/planes/$planId/_detalle/historial')({
component: RouteComponent,
validateSearch: (search: { structure?: any }) => ({
structure: search.structure ?? null,
}),
})
const getEventConfig = (tipo: string, campo: string) => {
@@ -58,6 +61,9 @@ const getEventConfig = (tipo: string, campo: string) => {
function RouteComponent() {
const { planId } = Route.useParams()
const { data: rawData, isLoading } = usePlanHistorial(planId)
const { structure } = Route.useSearch()
console.log(structure?.vigencia?.title)
console.log(structure)
// ESTADOS PARA EL MODAL
const [selectedEvent, setSelectedEvent] = useState<any>(null)
@@ -77,7 +83,9 @@ function RouteComponent() {
description:
item.campo === 'datos'
? `Actualización general de: ${item.valor_nuevo?.nombre || 'información del plan'}`
: `Se modificó el campo ${item.campo}`,
: `Se modificó el campo ${
structure?.[item.campo]?.title ?? item.campo
}`,
date: parseISO(item.cambiado_en),
icon: config.icon,
campo: item.campo,

View File

@@ -18,7 +18,7 @@ import {
TooltipProvider,
TooltipTrigger,
} from '@/components/ui/tooltip'
import { usePlan } from '@/data'
import { usePlan, useUpdatePlanFields } from '@/data'
// import { toast } from 'sonner' // Asegúrate de tener sonner instalado o quita la línea
export const Route = createFileRoute('/planes/$planId/_detalle/')({
@@ -39,7 +39,7 @@ function DatosGeneralesPage() {
const [editingId, setEditingId] = useState<string | null>(null)
const [editValue, setEditValue] = useState('')
const location = useLocation()
const updatePlan = useUpdatePlanFields()
// Confetti al llegar desde creación
useEffect(() => {
if (location.state.showConfetti) {
@@ -122,14 +122,47 @@ function DatosGeneralesPage() {
setEditValue('')
}
const handleSave = (id: string) => {
// Actualizamos el estado local de la lista
const handleSave = (campo: DatosGeneralesField) => {
if (!data?.datos) return
const currentValue = (data.datos as any)[campo.clave]
let newValue: any
if (
typeof currentValue === 'object' &&
currentValue !== null &&
'description' in currentValue
) {
// Caso 1: objeto con description
newValue = {
...currentValue,
description: editValue,
}
} else {
// Caso 2: valor plano (string, number, etc)
newValue = editValue
}
const datosActualizados = {
...data.datos,
[campo.clave]: newValue,
}
updatePlan.mutate({
planId,
patch: {
datos: datosActualizados,
},
})
// UI optimista
setCampos((prev) =>
prev.map((c) => (c.id === id ? { ...c, value: editValue } : c)),
prev.map((c) => (c.id === campo.id ? { ...c, value: editValue } : c)),
)
setEditingId(null)
setEditValue('')
// toast.success('Cambios guardados localmente')
}
const handleIARequest = (clave: string) => {
@@ -245,7 +278,7 @@ function DatosGeneralesPage() {
<Button
size="sm"
className="bg-teal-600 hover:bg-teal-700"
onClick={() => handleSave(campo.id)}
onClick={() => handleSave(campo)}
>
<Check size={14} className="mr-1" /> Guardar
</Button>

View File

@@ -157,10 +157,15 @@ function AsignaturaCardItem({
export const Route = createFileRoute('/planes/$planId/_detalle/mapa')({
component: MapaCurricularPage,
validateSearch: (search: { ciclo?: number }) => ({
ciclo: search.ciclo ?? null,
}),
})
function MapaCurricularPage() {
const { planId } = Route.useParams() // Idealmente usa el ID de la ruta
const { ciclo } = Route.useSearch()
console.log(ciclo)
// 1. Fetch de Datos
const { data: asignaturasApi, isLoading: loadingAsig } =
@@ -249,7 +254,7 @@ function MapaCurricularPage() {
if (lineasApi) setLineas(mapLineasToLineaCurricular(lineasApi))
}, [lineasApi])
const ciclosTotales = 9
const ciclosTotales = Number(ciclo)
const ciclosArray = Array.from({ length: ciclosTotales }, (_, i) => i + 1)
// Nuevo estado para controlar los datos temporales del modal de edición

View File

@@ -219,7 +219,11 @@ function RouteComponent() {
<Tab to="/planes/$planId/" params={{ planId }}>
Datos Generales
</Tab>
<Tab to="/planes/$planId/mapa" params={{ planId }}>
<Tab
to="/planes/$planId/mapa"
params={{ planId }}
search={{ ciclo: data?.numero_ciclos }}
>
Mapa Curricular
</Tab>
<Tab to="/planes/$planId/asignaturas" params={{ planId }}>
@@ -234,7 +238,13 @@ function RouteComponent() {
<Tab to="/planes/$planId/documento" params={{ planId }}>
Documento
</Tab>
<Tab to="/planes/$planId/historial" params={{ planId }}>
<Tab
to="/planes/$planId/historial"
params={{ planId }}
search={{
structure: data?.estructuras_plan?.definicion?.properties,
}}
>
Historial
</Tab>
</nav>
@@ -288,16 +298,20 @@ const InfoCard = forwardRef<
function Tab({
to,
params,
search,
children,
}: {
to: string
params?: any
search?: any
children: React.ReactNode
}) {
console.log(search)
return (
<Link
to={to}
params={params}
search={search}
className="border-b-2 border-transparent pb-3 text-sm font-medium text-slate-500 transition-all hover:text-slate-800"
activeProps={{ className: 'border-teal-600 text-teal-700 font-bold' }}
activeOptions={{