Se corrigen incidencias
This commit is contained in:
@@ -1,37 +1,27 @@
|
|||||||
|
import { Link, useRouterState } from '@tanstack/react-router'
|
||||||
|
import { ArrowLeft, GraduationCap, Pencil, Sparkles } from 'lucide-react'
|
||||||
import { useCallback, useState, useEffect } from 'react'
|
import { useCallback, useState, useEffect } from 'react'
|
||||||
import { Link } from '@tanstack/react-router'
|
|
||||||
import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/tabs'
|
import { BibliographyItem } from './BibliographyItem'
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
import { ContenidoTematico } from './ContenidoTematico'
|
||||||
|
import { DocumentoSEPTab } from './DocumentoSEPTab'
|
||||||
|
import { HistorialTab } from './HistorialTab'
|
||||||
|
import { IAMateriaTab } from './IAMateriaTab'
|
||||||
|
|
||||||
|
import type { CampoEstructura, IAMessage, IASugerencia } from '@/types/materia'
|
||||||
|
|
||||||
import { Badge } from '@/components/ui/badge'
|
import { Badge } from '@/components/ui/badge'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
||||||
import { Separator } from '@/components/ui/separator'
|
import { Separator } from '@/components/ui/separator'
|
||||||
|
import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/tabs'
|
||||||
import { Textarea } from '@/components/ui/textarea'
|
import { Textarea } from '@/components/ui/textarea'
|
||||||
import {
|
import { useSubject } from '@/data/hooks/useSubjects'
|
||||||
ArrowLeft,
|
|
||||||
GraduationCap,
|
|
||||||
Edit2,
|
|
||||||
Save,
|
|
||||||
Pencil,
|
|
||||||
Sparkles,
|
|
||||||
} from 'lucide-react'
|
|
||||||
import { ContenidoTematico } from './ContenidoTematico'
|
|
||||||
import { BibliographyItem } from './BibliographyItem'
|
|
||||||
import { IAMateriaTab } from './IAMateriaTab'
|
|
||||||
import type {
|
|
||||||
CampoEstructura,
|
|
||||||
IAMessage,
|
|
||||||
IASugerencia,
|
|
||||||
UnidadTematica,
|
|
||||||
} from '@/types/materia'
|
|
||||||
import {
|
import {
|
||||||
mockMateria,
|
mockMateria,
|
||||||
mockEstructura,
|
mockEstructura,
|
||||||
mockDocumentoSep,
|
mockDocumentoSep,
|
||||||
mockHistorial,
|
|
||||||
} from '@/data/mockMateriaData'
|
} from '@/data/mockMateriaData'
|
||||||
import { DocumentoSEPTab } from './DocumentoSEPTab'
|
|
||||||
import { HistorialTab } from './HistorialTab'
|
|
||||||
import { useSubject } from '@/data/hooks/useSubjects'
|
|
||||||
|
|
||||||
export interface BibliografiaEntry {
|
export interface BibliografiaEntry {
|
||||||
id: string
|
id: string
|
||||||
@@ -41,8 +31,8 @@ export interface BibliografiaEntry {
|
|||||||
fuenteBiblioteca?: any
|
fuenteBiblioteca?: any
|
||||||
}
|
}
|
||||||
export interface BibliografiaTabProps {
|
export interface BibliografiaTabProps {
|
||||||
bibliografia: BibliografiaEntry[]
|
bibliografia: Array<BibliografiaEntry>
|
||||||
onSave: (bibliografia: BibliografiaEntry[]) => void
|
onSave: (bibliografia: Array<BibliografiaEntry>) => void
|
||||||
isSaving: boolean
|
isSaving: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,13 +80,15 @@ function EditableHeaderField({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
export default function MateriaDetailPage() {
|
export default function MateriaDetailPage() {
|
||||||
|
const routerState = useRouterState()
|
||||||
|
const state = routerState.location.state as any
|
||||||
const { data: asignaturasApi, isLoading: loadingAsig } = useSubject(
|
const { data: asignaturasApi, isLoading: loadingAsig } = useSubject(
|
||||||
'9d4dda6a-488f-428a-8a07-38081592a641',
|
state?.realId,
|
||||||
)
|
)
|
||||||
// 1. Asegúrate de tener estos estados en tu componente principal
|
// 1. Asegúrate de tener estos estados en tu componente principal
|
||||||
const [messages, setMessages] = useState<IAMessage[]>([])
|
const [messages, setMessages] = useState<Array<IAMessage>>([])
|
||||||
const [datosGenerales, setDatosGenerales] = useState({})
|
const [datosGenerales, setDatosGenerales] = useState({})
|
||||||
const [campos, setCampos] = useState<CampoEstructura[]>([])
|
const [campos, setCampos] = useState<Array<CampoEstructura>>([])
|
||||||
|
|
||||||
// Dentro de MateriaDetailPage
|
// Dentro de MateriaDetailPage
|
||||||
const [headerData, setHeaderData] = useState({
|
const [headerData, setHeaderData] = useState({
|
||||||
@@ -142,16 +134,16 @@ export default function MateriaDetailPage() {
|
|||||||
setMessages([...messages, newMessage])
|
setMessages([...messages, newMessage])
|
||||||
|
|
||||||
// Aquí llamarías a tu API de OpenAI/Claude
|
// Aquí llamarías a tu API de OpenAI/Claude
|
||||||
//toast.info("Enviando consulta a la IA...");
|
// toast.info("Enviando consulta a la IA...");
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleAcceptSuggestion = (sugerencia: IASugerencia) => {
|
const handleAcceptSuggestion = (sugerencia: IASugerencia) => {
|
||||||
// Lógica para actualizar el valor del campo en tu estado de datosGenerales
|
// Lógica para actualizar el valor del campo en tu estado de datosGenerales
|
||||||
//toast.success(`Sugerencia aplicada a ${sugerencia.campoNombre}`);
|
// toast.success(`Sugerencia aplicada a ${sugerencia.campoNombre}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dentro de tu componente principal (donde están los Tabs)
|
// Dentro de tu componente principal (donde están los Tabs)
|
||||||
const [bibliografia, setBibliografia] = useState<BibliografiaEntry[]>([
|
const [bibliografia, setBibliografia] = useState<Array<BibliografiaEntry>>([
|
||||||
{
|
{
|
||||||
id: '1',
|
id: '1',
|
||||||
tipo: 'BASICA',
|
tipo: 'BASICA',
|
||||||
@@ -160,7 +152,7 @@ export default function MateriaDetailPage() {
|
|||||||
])
|
])
|
||||||
const [isSaving, setIsSaving] = useState(false)
|
const [isSaving, setIsSaving] = useState(false)
|
||||||
|
|
||||||
const handleSaveBibliografia = (data: BibliografiaEntry[]) => {
|
const handleSaveBibliografia = (data: Array<BibliografiaEntry>) => {
|
||||||
setIsSaving(true)
|
setIsSaving(true)
|
||||||
// Aquí iría tu llamada a la API
|
// Aquí iría tu llamada a la API
|
||||||
setBibliografia(data)
|
setBibliografia(data)
|
||||||
@@ -168,7 +160,7 @@ export default function MateriaDetailPage() {
|
|||||||
// Simulamos un guardado
|
// Simulamos un guardado
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setIsSaving(false)
|
setIsSaving(false)
|
||||||
//toast.success("Cambios guardados");
|
// toast.success("Cambios guardados");
|
||||||
}, 1000)
|
}, 1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -304,7 +296,7 @@ export default function MateriaDetailPage() {
|
|||||||
(id) =>
|
(id) =>
|
||||||
console.log(
|
console.log(
|
||||||
'Rechazada',
|
'Rechazada',
|
||||||
) /*toast.error("Sugerencia rechazada")*/
|
) /* toast.error("Sugerencia rechazada")*/
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
@@ -507,7 +499,7 @@ function InfoCard({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Vista de Requisitos
|
// Vista de Requisitos
|
||||||
function RequirementsView({ items }: { items: any[] }) {
|
function RequirementsView({ items }: { items: Array<any> }) {
|
||||||
return (
|
return (
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
{items.map((req, i) => (
|
{items.map((req, i) => (
|
||||||
@@ -528,7 +520,7 @@ function RequirementsView({ items }: { items: any[] }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Vista de Evaluación
|
// Vista de Evaluación
|
||||||
function EvaluationView({ items }: { items: any[] }) {
|
function EvaluationView({ items }: { items: Array<any> }) {
|
||||||
return (
|
return (
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{items.map((item, i) => (
|
{items.map((item, i) => (
|
||||||
|
|||||||
@@ -1,24 +1,4 @@
|
|||||||
import { createFileRoute } from '@tanstack/react-router'
|
import { createFileRoute, useNavigate } from '@tanstack/react-router'
|
||||||
import { useState, useMemo } from 'react'
|
|
||||||
import type { Materia, LineaCurricular } from '@/types/plan'
|
|
||||||
import { Button } from '@/components/ui/button'
|
|
||||||
import { Input } from '@/components/ui/input'
|
|
||||||
import { Badge } from '@/components/ui/badge'
|
|
||||||
import {
|
|
||||||
Table,
|
|
||||||
TableBody,
|
|
||||||
TableCell,
|
|
||||||
TableHead,
|
|
||||||
TableHeader,
|
|
||||||
TableRow,
|
|
||||||
} from '@/components/ui/table'
|
|
||||||
import {
|
|
||||||
Select,
|
|
||||||
SelectContent,
|
|
||||||
SelectItem,
|
|
||||||
SelectTrigger,
|
|
||||||
SelectValue,
|
|
||||||
} from '@/components/ui/select'
|
|
||||||
import {
|
import {
|
||||||
Plus,
|
Plus,
|
||||||
Copy,
|
Copy,
|
||||||
@@ -28,6 +8,28 @@ import {
|
|||||||
BookOpen,
|
BookOpen,
|
||||||
Loader2,
|
Loader2,
|
||||||
} from 'lucide-react'
|
} from 'lucide-react'
|
||||||
|
import { useState, useMemo } from 'react'
|
||||||
|
|
||||||
|
import type { Materia } from '@/types/plan'
|
||||||
|
|
||||||
|
import { Badge } from '@/components/ui/badge'
|
||||||
|
import { Button } from '@/components/ui/button'
|
||||||
|
import { Input } from '@/components/ui/input'
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from '@/components/ui/select'
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
} from '@/components/ui/table'
|
||||||
import { usePlanAsignaturas, usePlanLineas } from '@/data'
|
import { usePlanAsignaturas, usePlanLineas } from '@/data'
|
||||||
|
|
||||||
// --- Configuración de Estilos ---
|
// --- Configuración de Estilos ---
|
||||||
@@ -44,7 +46,7 @@ const tipoConfig: Record<string, { label: string; className: string }> = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// --- Mapeadores de API ---
|
// --- Mapeadores de API ---
|
||||||
const mapAsignaturas = (asigApi: any[] = []): Materia[] => {
|
const mapAsignaturas = (asigApi: Array<any> = []): Array<Materia> => {
|
||||||
return asigApi.map((asig) => ({
|
return asigApi.map((asig) => ({
|
||||||
id: asig.id,
|
id: asig.id,
|
||||||
clave: asig.codigo,
|
clave: asig.codigo,
|
||||||
@@ -66,6 +68,7 @@ export const Route = createFileRoute('/planes/$planId/_detalle/materias')({
|
|||||||
|
|
||||||
function MateriasPage() {
|
function MateriasPage() {
|
||||||
const { planId } = Route.useParams()
|
const { planId } = Route.useParams()
|
||||||
|
const navigate = useNavigate()
|
||||||
|
|
||||||
// 1. Fetch de datos reales
|
// 1. Fetch de datos reales
|
||||||
const { data: asignaturasApi, isLoading: loadingAsig } = usePlanAsignaturas(
|
const { data: asignaturasApi, isLoading: loadingAsig } = usePlanAsignaturas(
|
||||||
@@ -224,6 +227,18 @@ function MateriasPage() {
|
|||||||
<TableRow
|
<TableRow
|
||||||
key={materia.id}
|
key={materia.id}
|
||||||
className="group cursor-pointer transition-colors hover:bg-slate-50/80"
|
className="group cursor-pointer transition-colors hover:bg-slate-50/80"
|
||||||
|
onClick={() =>
|
||||||
|
navigate({
|
||||||
|
to: '/planes/$planId/asignaturas/$asignaturaId',
|
||||||
|
params: {
|
||||||
|
planId,
|
||||||
|
asignaturaId: 'asignatura', // 👈 puede ser índice, consecutivo o slug
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
realId: materia.id, // 👈 ID largo oculto
|
||||||
|
} as any,
|
||||||
|
})
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<TableCell className="font-mono text-xs font-bold text-slate-400">
|
<TableCell className="font-mono text-xs font-bold text-slate-400">
|
||||||
{materia.clave}
|
{materia.clave}
|
||||||
|
|||||||
@@ -1,6 +1,16 @@
|
|||||||
import { createFileRoute, Outlet, Link } from '@tanstack/react-router'
|
import { createFileRoute, Outlet, Link } from '@tanstack/react-router'
|
||||||
import { ChevronLeft, GraduationCap, Clock, Hash, CalendarDays, Rocket, BookOpen, CheckCircle2 } from "lucide-react"
|
import {
|
||||||
import { Badge } from "@/components/ui/badge"
|
ChevronLeft,
|
||||||
|
GraduationCap,
|
||||||
|
Clock,
|
||||||
|
Hash,
|
||||||
|
CalendarDays,
|
||||||
|
Rocket,
|
||||||
|
BookOpen,
|
||||||
|
CheckCircle2,
|
||||||
|
} from 'lucide-react'
|
||||||
|
|
||||||
|
import { Badge } from '@/components/ui/badge'
|
||||||
|
|
||||||
export const Route = createFileRoute('/planes/$planId/_detalle')({
|
export const Route = createFileRoute('/planes/$planId/_detalle')({
|
||||||
component: RouteComponent,
|
component: RouteComponent,
|
||||||
@@ -12,11 +22,11 @@ function RouteComponent() {
|
|||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-white">
|
<div className="min-h-screen bg-white">
|
||||||
{/* 1. Header Superior con Sombra (Volver a planes) */}
|
{/* 1. Header Superior con Sombra (Volver a planes) */}
|
||||||
<div className="border-b bg-white/50 backdrop-blur-sm sticky top-0 z-20 shadow-sm">
|
<div className="sticky top-0 z-20 border-b bg-white/50 shadow-sm backdrop-blur-sm">
|
||||||
<div className="px-6 py-2">
|
<div className="px-6 py-2">
|
||||||
<Link
|
<Link
|
||||||
to="/planes"
|
to="/planes"
|
||||||
className="flex items-center gap-1 text-xs text-gray-500 hover:text-gray-800 transition-colors w-fit"
|
className="flex w-fit items-center gap-1 text-xs text-gray-500 transition-colors hover:text-gray-800"
|
||||||
>
|
>
|
||||||
<ChevronLeft size={14} /> Volver a planes
|
<ChevronLeft size={14} /> Volver a planes
|
||||||
</Link>
|
</Link>
|
||||||
@@ -24,54 +34,91 @@ function RouteComponent() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 2. Contenido Principal con Padding */}
|
{/* 2. Contenido Principal con Padding */}
|
||||||
<div className="p-8 max-w-[1600px] mx-auto space-y-8">
|
<div className="mx-auto max-w-[1600px] space-y-8 p-8">
|
||||||
|
|
||||||
{/* Header del Plan y Badges */}
|
{/* Header del Plan y Badges */}
|
||||||
<div className="flex flex-col md:flex-row justify-between items-start gap-4">
|
<div className="flex flex-col items-start justify-between gap-4 md:flex-row">
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-3xl font-bold tracking-tight text-slate-900">Plan de Estudios 2024</h1>
|
<h1 className="text-3xl font-bold tracking-tight text-slate-900">
|
||||||
<p className="text-lg text-slate-500 font-medium mt-1">
|
Plan de Estudios 2024
|
||||||
|
</h1>
|
||||||
|
<p className="mt-1 text-lg font-medium text-slate-500">
|
||||||
Ingeniería en Sistemas Computacionales
|
Ingeniería en Sistemas Computacionales
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Badges de la derecha */}
|
{/* Badges de la derecha */}
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Badge variant="secondary" className="bg-blue-50 text-blue-700 border-blue-100 gap-1 px-3">
|
<Badge
|
||||||
|
variant="secondary"
|
||||||
|
className="gap-1 border-blue-100 bg-blue-50 px-3 text-blue-700"
|
||||||
|
>
|
||||||
<Rocket size={12} /> Ingeniería
|
<Rocket size={12} /> Ingeniería
|
||||||
</Badge>
|
</Badge>
|
||||||
<Badge variant="secondary" className="bg-orange-50 text-orange-700 border-orange-100 gap-1 px-3">
|
<Badge
|
||||||
|
variant="secondary"
|
||||||
|
className="gap-1 border-orange-100 bg-orange-50 px-3 text-orange-700"
|
||||||
|
>
|
||||||
<BookOpen size={12} /> Licenciatura
|
<BookOpen size={12} /> Licenciatura
|
||||||
</Badge>
|
</Badge>
|
||||||
<Badge className="bg-teal-50 text-teal-700 border-teal-200 gap-1 px-3 hover:bg-teal-100">
|
<Badge className="gap-1 border-teal-200 bg-teal-50 px-3 text-teal-700 hover:bg-teal-100">
|
||||||
<CheckCircle2 size={12} /> En Revisión
|
<CheckCircle2 size={12} /> En Revisión
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 3. Cards de Información (Nivel, Duración, etc.) */}
|
{/* 3. Cards de Información (Nivel, Duración, etc.) */}
|
||||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
<div className="grid grid-cols-2 gap-4 md:grid-cols-4">
|
||||||
<InfoCard icon={<GraduationCap className="text-slate-400" />} label="Nivel" value="Superior" />
|
<InfoCard
|
||||||
<InfoCard icon={<Clock className="text-slate-400" />} label="Duración" value="9 Semestres" />
|
icon={<GraduationCap className="text-slate-400" />}
|
||||||
<InfoCard icon={<Hash className="text-slate-400" />} label="Créditos" value="320" />
|
label="Nivel"
|
||||||
<InfoCard icon={<CalendarDays className="text-slate-400" />} label="Creación" value="14 ene 2024" />
|
value="Superior"
|
||||||
|
/>
|
||||||
|
<InfoCard
|
||||||
|
icon={<Clock className="text-slate-400" />}
|
||||||
|
label="Duración"
|
||||||
|
value="9 Semestres"
|
||||||
|
/>
|
||||||
|
<InfoCard
|
||||||
|
icon={<Hash className="text-slate-400" />}
|
||||||
|
label="Créditos"
|
||||||
|
value="320"
|
||||||
|
/>
|
||||||
|
<InfoCard
|
||||||
|
icon={<CalendarDays className="text-slate-400" />}
|
||||||
|
label="Creación"
|
||||||
|
value="14 ene 2024"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 4. Navegación de Tabs */}
|
{/* 4. Navegación de Tabs */}
|
||||||
<div className="border-b overflow-x-auto scrollbar-hide">
|
<div className="scrollbar-hide overflow-x-auto border-b">
|
||||||
<nav className="flex gap-8 min-w-max">
|
<nav className="flex min-w-max gap-8">
|
||||||
<Tab to="/planes/$planId/datos" params={{ planId }}>Datos Generales</Tab>
|
<Tab to="/planes/$planId/datos" params={{ planId }}>
|
||||||
<Tab to="/planes/$planId/mapa" params={{ planId }}>Mapa Curricular</Tab>
|
Datos Generales
|
||||||
<Tab to="/planes/$planId/materias" params={{ planId }}>Materias</Tab>
|
</Tab>
|
||||||
<Tab to="/planes/$planId/flujo" params={{ planId }}>Flujo y Estados</Tab>
|
<Tab to="/planes/$planId/mapa" params={{ planId }}>
|
||||||
<Tab to="/planes/$planId/iaplan" params={{ planId }}>IA del Plan</Tab>
|
Mapa Curricular
|
||||||
<Tab to="/planes/$planId/documento" params={{ planId }}>Documento</Tab>
|
</Tab>
|
||||||
<Tab to="/planes/$planId/historial" params={{ planId }}>Historial</Tab>
|
<Tab to="/planes/$planId/materias" params={{ planId }}>
|
||||||
|
Materias
|
||||||
|
</Tab>
|
||||||
|
<Tab to="/planes/$planId/flujo" params={{ planId }}>
|
||||||
|
Flujo y Estados
|
||||||
|
</Tab>
|
||||||
|
<Tab to="/planes/$planId/iaplan" params={{ planId }}>
|
||||||
|
IA del Plan
|
||||||
|
</Tab>
|
||||||
|
<Tab to="/planes/$planId/documento" params={{ planId }}>
|
||||||
|
Documento
|
||||||
|
</Tab>
|
||||||
|
<Tab to="/planes/$planId/historial" params={{ planId }}>
|
||||||
|
Historial
|
||||||
|
</Tab>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 5. Contenido del Tab */}
|
{/* 5. Contenido del Tab */}
|
||||||
<main className="pt-2 animate-in fade-in duration-500">
|
<main className="animate-in fade-in pt-2 duration-500">
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
@@ -79,16 +126,23 @@ function RouteComponent() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Sub-componente para las tarjetas de información
|
// Sub-componente para las tarjetas de información
|
||||||
function InfoCard({ icon, label, value }: { icon: React.ReactNode, label: string, value: string }) {
|
function InfoCard({
|
||||||
|
icon,
|
||||||
|
label,
|
||||||
|
value,
|
||||||
|
}: {
|
||||||
|
icon: React.ReactNode
|
||||||
|
label: string
|
||||||
|
value: string
|
||||||
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-4 bg-slate-50/50 border border-slate-200/60 p-4 rounded-xl shadow-sm">
|
<div className="flex items-center gap-4 rounded-xl border border-slate-200/60 bg-slate-50/50 p-4 shadow-sm">
|
||||||
<div className="p-2 bg-white rounded-lg border shadow-sm">
|
<div className="rounded-lg border bg-white p-2 shadow-sm">{icon}</div>
|
||||||
{icon}
|
|
||||||
</div>
|
|
||||||
<div>
|
<div>
|
||||||
<p className="text-[10px] uppercase font-bold text-slate-400 tracking-wider leading-none mb-1">{label}</p>
|
<p className="mb-1 text-[10px] leading-none font-bold tracking-wider text-slate-400 uppercase">
|
||||||
|
{label}
|
||||||
|
</p>
|
||||||
<p className="text-sm font-semibold text-slate-700">{value}</p>
|
<p className="text-sm font-semibold text-slate-700">{value}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -98,17 +152,17 @@ function InfoCard({ icon, label, value }: { icon: React.ReactNode, label: string
|
|||||||
function Tab({
|
function Tab({
|
||||||
to,
|
to,
|
||||||
params,
|
params,
|
||||||
children
|
children,
|
||||||
}: {
|
}: {
|
||||||
to: string;
|
to: string
|
||||||
params?: any;
|
params?: any
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
to={to}
|
to={to}
|
||||||
params={params}
|
params={params}
|
||||||
className="pb-3 text-sm font-medium text-slate-500 border-b-2 border-transparent hover:text-slate-800 transition-all"
|
className="border-b-2 border-transparent pb-3 text-sm font-medium text-slate-500 transition-all hover:text-slate-800"
|
||||||
activeProps={{
|
activeProps={{
|
||||||
className: 'border-teal-600 text-teal-700 font-bold',
|
className: 'border-teal-600 text-teal-700 font-bold',
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -1,146 +1,10 @@
|
|||||||
import { createFileRoute } from '@tanstack/react-router'
|
import { createFileRoute, redirect } from '@tanstack/react-router'
|
||||||
import { Button } from '@/components/ui/button'
|
|
||||||
import { Card, CardContent } from '@/components/ui/card'
|
|
||||||
import {
|
|
||||||
BookOpen,
|
|
||||||
Sparkles,
|
|
||||||
FileText,
|
|
||||||
Library,
|
|
||||||
LayoutTemplate,
|
|
||||||
History,
|
|
||||||
ArrowRight,
|
|
||||||
GraduationCap,
|
|
||||||
} from 'lucide-react'
|
|
||||||
|
|
||||||
export const Route = createFileRoute(
|
export const Route = createFileRoute('/planes/$planId/asignaturas/')({
|
||||||
'/planes/$planId/asignaturas/'
|
beforeLoad: ({ params }) => {
|
||||||
)({
|
throw redirect({
|
||||||
component: MateriasLandingPage,
|
to: '/planes/$planId/materias',
|
||||||
|
params,
|
||||||
|
})
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
export default function MateriasLandingPage() {
|
|
||||||
return (
|
|
||||||
<div className="w-full">
|
|
||||||
{/* ================= HERO ================= */}
|
|
||||||
<section className="bg-gradient-to-b from-[#0b1d3a] to-[#0e2a5c] text-white">
|
|
||||||
<div className="max-w-7xl mx-auto px-6 py-28">
|
|
||||||
<div className="flex items-center gap-2 mb-6 text-sm text-blue-200">
|
|
||||||
<GraduationCap className="w-5 h-5 text-yellow-400" />
|
|
||||||
<span>SISTEMA DE GESTIÓN CURRICULAR</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h1 className="text-5xl font-bold mb-6">
|
|
||||||
Universidad La Salle
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
<p className="max-w-xl text-lg text-blue-100 mb-10">
|
|
||||||
Diseña, documenta y mejora programas de estudio con herramientas
|
|
||||||
de inteligencia artificial integradas y cumplimiento normativo SEP.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
size="lg"
|
|
||||||
className="bg-yellow-400 text-black hover:bg-yellow-300 font-semibold"
|
|
||||||
>
|
|
||||||
Ver materia de ejemplo
|
|
||||||
<ArrowRight className="ml-2 h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
{/* ================= FEATURES ================= */}
|
|
||||||
<section className="bg-white py-24">
|
|
||||||
<div className="max-w-7xl mx-auto px-6">
|
|
||||||
<h2 className="text-center text-2xl font-semibold mb-14">
|
|
||||||
Características principales
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<div className="grid gap-8 md:grid-cols-3">
|
|
||||||
<FeatureCard
|
|
||||||
icon={<BookOpen />}
|
|
||||||
title="Gestión de Materias"
|
|
||||||
description="Edita datos generales, contenido temático y bibliografía con una interfaz intuitiva."
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FeatureCard
|
|
||||||
icon={<Sparkles />}
|
|
||||||
title="IA Integrada"
|
|
||||||
description="Usa inteligencia artificial para mejorar objetivos, competencias y alinear con perfiles de egreso."
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FeatureCard
|
|
||||||
icon={<FileText />}
|
|
||||||
title="Documentos SEP"
|
|
||||||
description="Genera automáticamente documentos oficiales para la Secretaría de Educación Pública."
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FeatureCard
|
|
||||||
icon={<Library />}
|
|
||||||
title="Biblioteca Digital"
|
|
||||||
description="Busca y vincula recursos del repositorio de Biblioteca La Salle directamente."
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FeatureCard
|
|
||||||
icon={<LayoutTemplate />}
|
|
||||||
title="Plantillas Flexibles"
|
|
||||||
description="Adapta la estructura de materias según plantillas SEP o institucionales."
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FeatureCard
|
|
||||||
icon={<History />}
|
|
||||||
title="Historial Completo"
|
|
||||||
description="Rastrea todos los cambios con historial detallado por usuario y fecha."
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
{/* ================= CTA ================= */}
|
|
||||||
<section className="bg-gray-50 py-20">
|
|
||||||
<div className="max-w-3xl mx-auto text-center px-6">
|
|
||||||
<h3 className="text-xl font-semibold mb-4">
|
|
||||||
Explora la vista de detalle de materia
|
|
||||||
</h3>
|
|
||||||
|
|
||||||
<p className="text-muted-foreground mb-8">
|
|
||||||
Navega por las diferentes pestañas para ver cómo funciona el sistema
|
|
||||||
de gestión curricular.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<Button size="lg" className="bg-[#0e2a5c] hover:bg-[#0b1d3a]">
|
|
||||||
Ir a Inteligencia Artificial Aplicada
|
|
||||||
<ArrowRight className="ml-2 h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ================= FEATURE CARD ================= */
|
|
||||||
|
|
||||||
function FeatureCard({
|
|
||||||
icon,
|
|
||||||
title,
|
|
||||||
description,
|
|
||||||
}: {
|
|
||||||
icon: React.ReactNode
|
|
||||||
title: string
|
|
||||||
description: string
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<Card className="border border-gray-200 shadow-sm">
|
|
||||||
<CardContent className="p-6 space-y-4">
|
|
||||||
<div className="w-10 h-10 rounded-md bg-yellow-100 text-yellow-600 flex items-center justify-center">
|
|
||||||
{icon}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h4 className="font-semibold">{title}</h4>
|
|
||||||
|
|
||||||
<p className="text-sm text-muted-foreground">
|
|
||||||
{description}
|
|
||||||
</p>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|||||||
10
src/routes/planes/$planId/index.tsx
Normal file
10
src/routes/planes/$planId/index.tsx
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { createFileRoute, redirect } from '@tanstack/react-router'
|
||||||
|
|
||||||
|
export const Route = createFileRoute('/planes/$planId/')({
|
||||||
|
beforeLoad: ({ params }) => {
|
||||||
|
throw redirect({
|
||||||
|
to: '/planes/$planId/datos',
|
||||||
|
params,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user