Se hidrata de informacion las tabs de asignatura

This commit is contained in:
2026-01-14 15:52:25 -06:00
parent c4329785cc
commit b4b5134cb2
9 changed files with 1490 additions and 819 deletions

View File

@@ -1,142 +1,187 @@
import { useMemo } from 'react'
import { createFileRoute } from '@tanstack/react-router'
import {
GitBranch,
Edit3,
PlusCircle,
FileText,
RefreshCw,
User
} from "lucide-react"
import { Badge } from "@/components/ui/badge"
import { Card, CardContent } from "@/components/ui/card"
import { Avatar, AvatarFallback } from "@/components/ui/avatar"
import {
GitBranch,
Edit3,
PlusCircle,
FileText,
RefreshCw,
User,
Loader2,
Clock,
} from 'lucide-react'
import { Badge } from '@/components/ui/badge'
import { Card, CardContent } from '@/components/ui/card'
import { Avatar, AvatarFallback } from '@/components/ui/avatar'
import { usePlanHistorial } from '@/data/hooks/usePlans'
import { format, formatDistanceToNow, parseISO } from 'date-fns'
import { es } from 'date-fns/locale'
export const Route = createFileRoute('/planes/$planId/_detalle/historial')({
component: RouteComponent,
})
function RouteComponent() {
const historyEvents = [
{
id: 1,
type: 'Cambio de estado',
user: 'Dr. Juan Pérez',
description: 'Plan pasado de Borrador a En Revisión',
date: 'Hace 2 días',
icon: <GitBranch className="h-4 w-4" />,
details: { from: 'Borrador', to: 'En Revisión' }
},
{
id: 2,
type: 'Edición',
user: 'Lic. María García',
description: 'Actualizado perfil de egreso',
date: 'Hace 3 días',
icon: <Edit3 className="h-4 w-4" />,
},
{
id: 3,
type: 'Reorganización',
user: 'Ing. Carlos López',
description: 'Movida materia BD102 de ciclo 3 a ciclo 4',
date: 'Hace 5 días',
icon: <RefreshCw className="h-4 w-4" />,
details: { from: 'Ciclo 3', to: 'Ciclo 4' }
},
{
id: 4,
type: 'Creación',
user: 'Dr. Juan Pérez',
description: 'Añadida nueva materia: Inteligencia Artificial',
date: 'Hace 1 semana',
// Función para determinar el icono y tipo según la respuesta de la API
const getEventConfig = (tipo: string, campo: string) => {
if (tipo === 'CREACION')
return {
label: 'Creación',
icon: <PlusCircle className="h-4 w-4" />,
},
{
id: 5,
type: 'Documento',
user: 'Lic. María García',
description: 'Generado documento oficial v1.0',
date: 'Hace 1 semana',
icon: <FileText className="h-4 w-4" />,
color: 'teal',
}
]
if (campo === 'estado')
return {
label: 'Cambio de estado',
icon: <GitBranch className="h-4 w-4" />,
color: 'blue',
}
if (campo === 'datos')
return {
label: 'Edición de Datos',
icon: <Edit3 className="h-4 w-4" />,
color: 'amber',
}
return {
label: 'Actualización',
icon: <RefreshCw className="h-4 w-4" />,
color: 'slate',
}
}
function RouteComponent() {
const { planId } = Route.useParams()
const { data: rawData, isLoading } = usePlanHistorial(
'0e0aea4d-b8b4-4e75-8279-6224c3ac769f' /*planId*/,
)
// Transformación de datos de la API al formato de la UI
const historyEvents = useMemo(() => {
if (!rawData) return []
return rawData.map((item: any) => {
const config = getEventConfig(item.tipo, item.campo)
return {
id: item.id,
type: config.label,
user:
item.cambiado_por === '11111111-1111-1111-1111-111111111111'
? 'Administrador'
: 'Usuario Staff',
description:
item.campo === 'datos'
? `Actualización general de: ${item.valor_nuevo?.nombre || 'información del plan'}`
: `Se modificó el campo ${item.campo}`,
date: parseISO(item.cambiado_en),
icon: config.icon,
details:
item.valor_anterior && item.valor_nuevo
? {
from: String(item.valor_anterior),
to: String(item.valor_nuevo),
}
: null,
}
})
}, [rawData])
if (isLoading) {
return (
<div className="flex h-64 items-center justify-center">
<Loader2 className="h-8 w-8 animate-spin text-teal-600" />
</div>
)
}
return (
<div className="p-6 max-w-5xl mx-auto">
<div className="mx-auto max-w-5xl p-6">
<div className="mb-8">
<h1 className="text-xl font-bold text-slate-800">Historial de Cambios</h1>
<p className="text-sm text-muted-foreground">Registro de todas las modificaciones realizadas al plan</p>
<h1 className="flex items-center gap-2 text-xl font-bold text-slate-800">
<Clock className="h-5 w-5 text-teal-600" />
Historial de Cambios del Plan
</h1>
<p className="text-muted-foreground text-sm">
Registro cronológico de modificaciones realizadas
</p>
</div>
<div className="relative space-y-0">
{/* Línea vertical de fondo */}
<div className="absolute left-9 top-0 bottom-0 w-px bg-slate-200" />
<div className="absolute top-0 bottom-0 left-9 w-px bg-slate-200" />
{historyEvents.map((event) => (
<div key={event.id} className="relative flex gap-6 pb-8 group">
{/* Indicador con Icono */}
<div className="relative z-10 flex h-18 flex-col items-center">
<div className="flex h-[42px] w-[42px] items-center justify-center rounded-full border-4 border-white bg-slate-100 text-slate-600 shadow-sm group-hover:bg-teal-50 group-hover:text-teal-600 transition-colors">
{event.icon}
</div>
</div>
{/* Tarjeta de Contenido */}
<Card className="flex-1 shadow-none border-slate-200 hover:border-teal-200 transition-colors">
<CardContent className="p-4">
<div className="flex flex-col md:flex-row md:items-center justify-between gap-2 mb-2">
<div className="flex items-center gap-2">
<span className="font-bold text-slate-800 text-sm">{event.type}</span>
<Badge variant="outline" className="text-[10px] font-normal py-0">
{event.date}
</Badge>
</div>
<div className="flex items-center gap-2 text-xs text-muted-foreground">
<Avatar className="h-5 w-5 border">
<AvatarFallback className="text-[8px] bg-slate-50"><User size={10}/></AvatarFallback>
</Avatar>
{event.user}
</div>
{historyEvents.length === 0 ? (
<div className="ml-20 py-10 text-slate-500">
No hay registros en el historial.
</div>
) : (
historyEvents.map((event) => (
<div key={event.id} className="group relative flex gap-6 pb-8">
{/* Indicador con Icono */}
<div className="relative z-10 flex h-18 flex-col items-center">
<div className="flex h-[42px] w-[42px] items-center justify-center rounded-full border-4 border-white bg-slate-100 text-slate-600 shadow-sm transition-colors group-hover:bg-teal-50 group-hover:text-teal-600">
{event.icon}
</div>
</div>
<p className="text-sm text-slate-600 mb-3">{event.description}</p>
{/* Badges de transición (si existen) */}
{event.details && (
<div className="flex items-center gap-2 mt-2">
<Badge variant="secondary" className="bg-orange-50 text-orange-700 hover:bg-orange-50 border-orange-100 text-[10px]">
{event.details.from}
</Badge>
<span className="text-slate-400 text-xs"></span>
<Badge variant="secondary" className="bg-green-50 text-green-700 hover:bg-green-50 border-green-100 text-[10px]">
{event.details.to}
</Badge>
{/* Tarjeta de Contenido */}
<Card className="flex-1 border-slate-200 shadow-none transition-colors hover:border-teal-200">
<CardContent className="p-4">
<div className="mb-2 flex flex-col justify-between gap-2 md:flex-row md:items-center">
<div className="flex items-center gap-2">
<span className="text-sm font-bold text-slate-800">
{event.type}
</span>
<Badge
variant="outline"
className="py-0 text-[10px] font-normal capitalize"
>
{formatDistanceToNow(event.date, {
addSuffix: true,
locale: es,
})}
</Badge>
</div>
<div className="text-muted-foreground flex items-center gap-2 text-xs">
<Avatar className="h-5 w-5 border">
<AvatarFallback className="bg-slate-50 text-[8px]">
<User size={10} />
</AvatarFallback>
</Avatar>
{event.user}
</div>
</div>
)}
</CardContent>
</Card>
</div>
))}
{/* Evento inicial de creación */}
<div className="relative flex gap-6 group">
<div className="relative z-10 flex items-center">
<div className="flex h-[42px] w-[42px] items-center justify-center rounded-full border-4 border-white bg-teal-600 text-white shadow-sm">
<PlusCircle className="h-4 w-4" />
<p className="mb-1 text-sm text-slate-600">
{event.description}
</p>
<p className="mb-3 text-[10px] text-slate-400">
{format(event.date, "PPP 'a las' HH:mm", { locale: es })}
</p>
{/* Badges de transición (Si aplica para estados) */}
{event.details && (
<div className="mt-2 flex items-center gap-2">
<Badge
variant="secondary"
className="bg-slate-100 text-[10px]"
>
{event.details.from}
</Badge>
<span className="text-xs text-slate-400"></span>
<Badge
variant="secondary"
className="bg-teal-50 text-[10px] text-teal-700"
>
{event.details.to}
</Badge>
</div>
)}
</CardContent>
</Card>
</div>
</div>
<Card className="flex-1 bg-teal-50/30 border-teal-100 shadow-none">
<CardContent className="p-4">
<div className="flex items-center justify-between mb-1">
<span className="font-bold text-teal-900 text-sm">Creación</span>
<span className="text-[10px] text-teal-600 font-medium">14 Ene 2024</span>
</div>
<p className="text-sm text-teal-800/80">Plan de estudios creado</p>
</CardContent>
</Card>
</div>
))
)}
</div>
</div>
)
}
}