{sug.newValue}
diff --git a/src/components/plans/PlanCard.tsx b/src/components/plans/PlanCard.tsx
deleted file mode 100644
index d233a80..0000000
--- a/src/components/plans/PlanCard.tsx
+++ /dev/null
@@ -1,26 +0,0 @@
-import { StatusBadge } from "./StatusBadge";
-
-export function PlanCard({ plan }: any) {
- return (
-
-
- ⚙ Ingeniería
-
-
-
-
- {plan.title}
-
-
-
- {plan.subtitle}
-
-
-
- {plan.cycles} ciclos
- {plan.credits} créditos
- ➜
-
-
- )
-}
diff --git a/src/components/plans/PlanGrid.tsx b/src/components/plans/PlanGrid.tsx
deleted file mode 100644
index c6bcd65..0000000
--- a/src/components/plans/PlanGrid.tsx
+++ /dev/null
@@ -1,38 +0,0 @@
-import { PlanCard } from './PlanCard'
-
-const mockPlans = [
- {
- id: 1,
- name: 'Ingeniería en Sistemas',
- level: 'Licenciatura',
- status: 'Activo',
- },
- {
- id: 2,
- name: 'Arquitectura',
- level: 'Licenciatura',
- status: 'Activo',
- },
- {
- id: 3,
- name: 'Maestría en Educación',
- level: 'Maestría',
- status: 'Inactivo',
- },
-]
-
-export function PlanGrid() {
- return (
-
-
- Planes disponibles
-
-
-
- {mockPlans.map(plan => (
-
- ))}
-
-
- )
-}
diff --git a/src/components/plans/StatCard.tsx b/src/components/plans/StatCard.tsx
deleted file mode 100644
index d108612..0000000
--- a/src/components/plans/StatCard.tsx
+++ /dev/null
@@ -1,11 +0,0 @@
-export function StatCard({ icon, value, label }: any) {
- return (
-
-
- {icon}
- {value}
-
-
{label}
-
- )
-}
diff --git a/src/components/plans/StatsGrid.tsx b/src/components/plans/StatsGrid.tsx
deleted file mode 100644
index ca1bf96..0000000
--- a/src/components/plans/StatsGrid.tsx
+++ /dev/null
@@ -1,12 +0,0 @@
-import { StatCard } from "./StatCard";
-
-export function StatsGrid() {
- return (
-
-
-
-
-
-
- )
-}
diff --git a/src/components/plans/StatusBadge.tsx b/src/components/plans/StatusBadge.tsx
deleted file mode 100644
index 9de2053..0000000
--- a/src/components/plans/StatusBadge.tsx
+++ /dev/null
@@ -1,19 +0,0 @@
-export function StatusBadge({ status }: { status: string }) {
- const styles: any = {
- revision: 'bg-blue-100 text-blue-700',
- aprobado: 'bg-green-100 text-green-700',
- borrador: 'bg-gray-200 text-gray-600',
- }
-
- return (
-
- {status === 'revision'
- ? 'En Revisión'
- : status === 'aprobado'
- ? 'Aprobado'
- : 'Borrador'}
-
- )
-}
diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx
index 6a64f8b..148a6ef 100644
--- a/src/components/ui/button.tsx
+++ b/src/components/ui/button.tsx
@@ -7,7 +7,7 @@ import type { VariantProps } from 'class-variance-authority'
import { cn } from '@/lib/utils'
const buttonVariants = cva(
- "focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
+ "focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:ring-[3px] disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
{
variants: {
variant: {
diff --git a/src/routes/login.tsx b/src/routes/login.tsx
index ec1d416..cbbcbd8 100644
--- a/src/routes/login.tsx
+++ b/src/routes/login.tsx
@@ -8,7 +8,7 @@ export const Route = createFileRoute('/login')({
function LoginPage() {
return (
-
+
)
diff --git a/src/routes/planes/$planId/_detalle.tsx b/src/routes/planes/$planId/_detalle.tsx
index 511a87f..6c4139b 100644
--- a/src/routes/planes/$planId/_detalle.tsx
+++ b/src/routes/planes/$planId/_detalle.tsx
@@ -8,18 +8,24 @@ import {
} from 'lucide-react'
import { useState, useEffect, forwardRef } from 'react'
+import type { Database } from '@/types/supabase'
+
import { Badge } from '@/components/ui/badge'
-import {
- DropdownMenu,
- DropdownMenuContent,
- DropdownMenuItem,
- DropdownMenuTrigger,
-} from '@/components/ui/dropdown-menu'
import { NotFoundPage } from '@/components/ui/NotFoundPage'
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from '@/components/ui/select'
import { Skeleton } from '@/components/ui/skeleton'
import { plans_get } from '@/data/api/plans.api'
import { usePlan, useUpdatePlanFields } from '@/data/hooks/usePlans'
import { qk } from '@/data/query/keys'
+import { cn } from '@/lib/utils'
+
+type NivelPlanEstudio = Database['public']['Enums']['nivel_plan_estudio']
export const Route = createFileRoute('/planes/$planId/_detalle')({
loader: async ({ context: { queryClient }, params: { planId } }) => {
@@ -54,28 +60,26 @@ function RouteComponent() {
// Estados locales para manejar la edición "en vivo" antes de persistir
const [nombrePlan, setNombrePlan] = useState('')
- const [nivelPlan, setNivelPlan] = useState('')
- const [isDirty, setIsDirty] = useState(false)
+ const [nivelPlan, setNivelPlan] = useState
(
+ undefined,
+ )
useEffect(() => {
if (data) {
setNombrePlan(data.nombre || '')
- setNivelPlan(data.nivel || '')
+ setNivelPlan(data.nivel)
}
}, [data])
- const niveles = [
+ const niveles: Array = [
'Licenciatura',
'Maestría',
'Doctorado',
- 'Diplomado',
'Especialidad',
+ 'Diplomado',
+ 'Otro',
]
- const persistChange = (patch: any) => {
- mutate({ planId, patch })
- }
-
const MAX_CHARACTERS = 200
const handleKeyDown = (e: React.KeyboardEvent) => {
@@ -114,13 +118,13 @@ function RouteComponent() {
}
}
return (
-
+
{/* 1. Header Superior */}
-
+
Volver a planes
@@ -139,7 +143,7 @@ function RouteComponent() {
) : (
-
+
{/* El prefijo "Nivel en" lo mantenemos simple */}
{nivelPlan} en
{
- const nuevoNombre =
- e.currentTarget.textContent?.trim() || ''
+ const nuevoNombre = e.currentTarget.textContent.trim()
setNombrePlan(nuevoNombre)
if (nuevoNombre !== data?.nombre) {
mutate({ planId, patch: { nombre: nuevoNombre } })
}
}}
- // 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' }}
+ className="hover:border-input focus:border-primary block w-full cursor-text border-b border-transparent wrap-break-word whitespace-pre-wrap no-underline transition-colors outline-none select-text sm:inline-block sm:w-auto"
>
{nombrePlan}
-
+
{data?.carreras?.facultades?.nombre}{' '}
{data?.carreras?.nombre_corto}
-
-
- {data?.estados_plan?.etiqueta}
-
-
+ {(() => {
+ const estadoColorHex = (data?.estados_plan as any)?.color as
+ | string
+ | undefined
+ const badgeStyle = estadoColorHex
+ ? ({
+ backgroundColor: estadoColorHex,
+ borderColor: estadoColorHex,
+ } as const)
+ : undefined
+
+ return (
+
+
+ {data?.estados_plan?.etiqueta}
+
+
+ )
+ })()}
)}
{/* 3. Cards de Información */}
-
-
- }
- label="Nivel"
+
+
+
+
+
+
+ Nivel
+
+
-
-
-
- {niveles.map((n) => (
- {
- setNivelPlan(n)
- if (n !== data?.nivel) {
- mutate({ planId, patch: { nivel: n } })
- }
- }}
- >
- {n}
-
- ))}
-
-
+ onValueChange={(value) => {
+ const nuevoNivel = value as NivelPlanEstudio
+ setNivelPlan(nuevoNivel)
+ if (nuevoNivel !== data?.nivel) {
+ mutate({ planId, patch: { nivel: nuevoNivel } })
+ }
+ }}
+ >
+
+
+
+
+ {niveles.map((n) => (
+
+ {n}
+
+ ))}
+
+
+
+
}
+ icon={}
label="Duración"
value={`${data?.numero_ciclos || 0} Ciclos`}
/>
}
+ icon={}
label="Créditos"
value="320"
/>
}
+ icon={}
label="Creación"
- value={data?.creado_en?.split('T')[0]}
+ value={data?.creado_en.split('T')[0]}
/>
@@ -276,20 +305,20 @@ const InfoCard = forwardRef<
-
+
{icon}
-
+
{label}
-
+
{value || '---'}
@@ -311,8 +340,8 @@ function Tab({
+
{/* Header */}
diff --git a/src/routes/planes/$planId/_detalle/asignaturas.tsx b/src/routes/planes/$planId/_detalle/asignaturas.tsx
index cd0ef8a..2c9735c 100644
--- a/src/routes/planes/$planId/_detalle/asignaturas.tsx
+++ b/src/routes/planes/$planId/_detalle/asignaturas.tsx
@@ -152,7 +152,7 @@ function AsignaturasPage() {
resetScroll: false,
})
}}
- className="ring-offset-background bg-primary text-primary-foreground hover:bg-primary/90 inline-flex h-11 items-center justify-center gap-2 rounded-md px-8 text-sm font-medium shadow-md transition-colors"
+ className="shadow-md"
>
Nueva Asignatura
diff --git a/src/routes/planes/$planId/_detalle/documento.tsx b/src/routes/planes/$planId/_detalle/documento.tsx
index 2e1cd47..fd740ba 100644
--- a/src/routes/planes/$planId/_detalle/documento.tsx
+++ b/src/routes/planes/$planId/_detalle/documento.tsx
@@ -122,11 +122,7 @@ function RouteComponent() {
>
Regenerar
-
-
+
Avanzar a Revisión Expertos
diff --git a/src/routes/planes/$planId/_detalle/historial.tsx b/src/routes/planes/$planId/_detalle/historial.tsx
index 345b8c6..af05e7c 100644
--- a/src/routes/planes/$planId/_detalle/historial.tsx
+++ b/src/routes/planes/$planId/_detalle/historial.tsx
@@ -9,7 +9,6 @@ import {
User,
Loader2,
Clock,
- Eye,
History,
Calendar,
ChevronLeft,
@@ -37,24 +36,24 @@ const getEventConfig = (tipo: string, campo: string) => {
return {
label: 'Creación',
icon: ,
- color: 'teal',
+ color: 'primary',
}
if (campo === 'estado')
return {
label: 'Cambio de estado',
icon: ,
- color: 'blue',
+ color: 'secondary',
}
if (campo === 'datos')
return {
label: 'Edición de Datos',
icon: ,
- color: 'amber',
+ color: 'accent',
}
return {
label: 'Actualización',
icon: ,
- color: 'slate',
+ color: 'muted',
}
}
@@ -104,7 +103,7 @@ function RouteComponent() {
},
}
})
- }, [rawData])
+ }, [rawData, structure, data])
const openCompareModal = (event: any) => {
setSelectedEvent(event)
@@ -120,7 +119,7 @@ function RouteComponent() {
if (isLoading)
return (
-
+
)
@@ -128,8 +127,8 @@ function RouteComponent() {
-
- Historial de Cambios del
+
+ Historial de Cambios del
Plan
@@ -139,9 +138,11 @@ function RouteComponent() {
-
+
{historyEvents.length === 0 ? (
-
No hay registros.
+
+ No hay registros.
+
) : (
historyEvents.map((event) => (
-
-
+ openCompareModal(event)}
+ role="button"
+ tabIndex={0}
+ onKeyDown={(e) => {
+ if (e.key === 'Enter' || e.key === ' ')
+ openCompareModal(event)
+ }}
+ >
{/* LÍNEA SUPERIOR: Título a la izquierda --- Usuario, Botón y Fecha a la derecha */}
-
+
{event.type}
{/* Grupo de elementos alineados a la derecha */}
-
+
{/* Usuario e Icono */}
@@ -184,17 +194,8 @@ function RouteComponent() {
- {/* Botón Ver Cambios */}
-
openCompareModal(event)}
- className="group/btn flex items-center gap-1.5 text-xs font-medium text-teal-600 md:text-slate-500 md:hover:text-teal-600"
- >
-
- Ver cambios
-
-
{/* Fecha exacta (Solo visible en desktop para no amontonar) */}
-
+
{format(event.date, 'yyyy-MM-dd HH:mm')}
@@ -202,7 +203,7 @@ function RouteComponent() {
{/* LÍNEA INFERIOR: Descripción */}
-
+
{event.description}
@@ -213,16 +214,16 @@ function RouteComponent() {
{event.details.from}
-
+
→
{event.details.to}
@@ -237,7 +238,7 @@ function RouteComponent() {
)}
{historyEvents.length > 0 && (
-
+
Mostrando {rawData.length} de {totalRecords} cambios
@@ -255,7 +256,7 @@ function RouteComponent() {
Anterior
-
+
Página {page + 1} de {totalPages || 1}
@@ -266,7 +267,6 @@ function RouteComponent() {
setPage((p) => p + 1)
window.scrollTo(0, 0)
}}
- // Ahora se deshabilita si llegamos a la última página real
disabled={page + 1 >= totalPages || isLoading}
>
Siguiente
@@ -280,9 +280,9 @@ function RouteComponent() {
{/* MODAL DE COMPARACIÓN CON SCROLL INTERNO */}