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 { Link } from '@tanstack/react-router'
|
||||
import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/tabs'
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
|
||||
import { BibliographyItem } from './BibliographyItem'
|
||||
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 { Button } from '@/components/ui/button'
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/tabs'
|
||||
import { Textarea } from '@/components/ui/textarea'
|
||||
import {
|
||||
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 { useSubject } from '@/data/hooks/useSubjects'
|
||||
import {
|
||||
mockMateria,
|
||||
mockEstructura,
|
||||
mockDocumentoSep,
|
||||
mockHistorial,
|
||||
} from '@/data/mockMateriaData'
|
||||
import { DocumentoSEPTab } from './DocumentoSEPTab'
|
||||
import { HistorialTab } from './HistorialTab'
|
||||
import { useSubject } from '@/data/hooks/useSubjects'
|
||||
|
||||
export interface BibliografiaEntry {
|
||||
id: string
|
||||
@@ -41,8 +31,8 @@ export interface BibliografiaEntry {
|
||||
fuenteBiblioteca?: any
|
||||
}
|
||||
export interface BibliografiaTabProps {
|
||||
bibliografia: BibliografiaEntry[]
|
||||
onSave: (bibliografia: BibliografiaEntry[]) => void
|
||||
bibliografia: Array<BibliografiaEntry>
|
||||
onSave: (bibliografia: Array<BibliografiaEntry>) => void
|
||||
isSaving: boolean
|
||||
}
|
||||
|
||||
@@ -90,13 +80,15 @@ function EditableHeaderField({
|
||||
)
|
||||
}
|
||||
export default function MateriaDetailPage() {
|
||||
const routerState = useRouterState()
|
||||
const state = routerState.location.state as any
|
||||
const { data: asignaturasApi, isLoading: loadingAsig } = useSubject(
|
||||
'9d4dda6a-488f-428a-8a07-38081592a641',
|
||||
state?.realId,
|
||||
)
|
||||
// 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 [campos, setCampos] = useState<CampoEstructura[]>([])
|
||||
const [campos, setCampos] = useState<Array<CampoEstructura>>([])
|
||||
|
||||
// Dentro de MateriaDetailPage
|
||||
const [headerData, setHeaderData] = useState({
|
||||
@@ -151,7 +143,7 @@ export default function MateriaDetailPage() {
|
||||
}
|
||||
|
||||
// Dentro de tu componente principal (donde están los Tabs)
|
||||
const [bibliografia, setBibliografia] = useState<BibliografiaEntry[]>([
|
||||
const [bibliografia, setBibliografia] = useState<Array<BibliografiaEntry>>([
|
||||
{
|
||||
id: '1',
|
||||
tipo: 'BASICA',
|
||||
@@ -160,7 +152,7 @@ export default function MateriaDetailPage() {
|
||||
])
|
||||
const [isSaving, setIsSaving] = useState(false)
|
||||
|
||||
const handleSaveBibliografia = (data: BibliografiaEntry[]) => {
|
||||
const handleSaveBibliografia = (data: Array<BibliografiaEntry>) => {
|
||||
setIsSaving(true)
|
||||
// Aquí iría tu llamada a la API
|
||||
setBibliografia(data)
|
||||
@@ -507,7 +499,7 @@ function InfoCard({
|
||||
}
|
||||
|
||||
// Vista de Requisitos
|
||||
function RequirementsView({ items }: { items: any[] }) {
|
||||
function RequirementsView({ items }: { items: Array<any> }) {
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
{items.map((req, i) => (
|
||||
@@ -528,7 +520,7 @@ function RequirementsView({ items }: { items: any[] }) {
|
||||
}
|
||||
|
||||
// Vista de Evaluación
|
||||
function EvaluationView({ items }: { items: any[] }) {
|
||||
function EvaluationView({ items }: { items: Array<any> }) {
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
{items.map((item, i) => (
|
||||
|
||||
@@ -1,24 +1,4 @@
|
||||
import { createFileRoute } 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 { createFileRoute, useNavigate } from '@tanstack/react-router'
|
||||
import {
|
||||
Plus,
|
||||
Copy,
|
||||
@@ -28,6 +8,28 @@ import {
|
||||
BookOpen,
|
||||
Loader2,
|
||||
} 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'
|
||||
|
||||
// --- Configuración de Estilos ---
|
||||
@@ -44,7 +46,7 @@ const tipoConfig: Record<string, { label: string; className: string }> = {
|
||||
}
|
||||
|
||||
// --- Mapeadores de API ---
|
||||
const mapAsignaturas = (asigApi: any[] = []): Materia[] => {
|
||||
const mapAsignaturas = (asigApi: Array<any> = []): Array<Materia> => {
|
||||
return asigApi.map((asig) => ({
|
||||
id: asig.id,
|
||||
clave: asig.codigo,
|
||||
@@ -66,6 +68,7 @@ export const Route = createFileRoute('/planes/$planId/_detalle/materias')({
|
||||
|
||||
function MateriasPage() {
|
||||
const { planId } = Route.useParams()
|
||||
const navigate = useNavigate()
|
||||
|
||||
// 1. Fetch de datos reales
|
||||
const { data: asignaturasApi, isLoading: loadingAsig } = usePlanAsignaturas(
|
||||
@@ -224,6 +227,18 @@ function MateriasPage() {
|
||||
<TableRow
|
||||
key={materia.id}
|
||||
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">
|
||||
{materia.clave}
|
||||
|
||||
@@ -1,6 +1,16 @@
|
||||
import { createFileRoute, Outlet, Link } from '@tanstack/react-router'
|
||||
import { ChevronLeft, GraduationCap, Clock, Hash, CalendarDays, Rocket, BookOpen, CheckCircle2 } from "lucide-react"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import {
|
||||
ChevronLeft,
|
||||
GraduationCap,
|
||||
Clock,
|
||||
Hash,
|
||||
CalendarDays,
|
||||
Rocket,
|
||||
BookOpen,
|
||||
CheckCircle2,
|
||||
} from 'lucide-react'
|
||||
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
|
||||
export const Route = createFileRoute('/planes/$planId/_detalle')({
|
||||
component: RouteComponent,
|
||||
@@ -12,11 +22,11 @@ function RouteComponent() {
|
||||
return (
|
||||
<div className="min-h-screen bg-white">
|
||||
{/* 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">
|
||||
<Link
|
||||
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
|
||||
</Link>
|
||||
@@ -24,54 +34,91 @@ function RouteComponent() {
|
||||
</div>
|
||||
|
||||
{/* 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 */}
|
||||
<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>
|
||||
<h1 className="text-3xl font-bold tracking-tight text-slate-900">Plan de Estudios 2024</h1>
|
||||
<p className="text-lg text-slate-500 font-medium mt-1">
|
||||
<h1 className="text-3xl font-bold tracking-tight text-slate-900">
|
||||
Plan de Estudios 2024
|
||||
</h1>
|
||||
<p className="mt-1 text-lg font-medium text-slate-500">
|
||||
Ingeniería en Sistemas Computacionales
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Badges de la derecha */}
|
||||
<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
|
||||
</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
|
||||
</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
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 3. Cards de Información (Nivel, Duración, etc.) */}
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||
<InfoCard icon={<GraduationCap className="text-slate-400" />} label="Nivel" 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 className="grid grid-cols-2 gap-4 md:grid-cols-4">
|
||||
<InfoCard
|
||||
icon={<GraduationCap className="text-slate-400" />}
|
||||
label="Nivel"
|
||||
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>
|
||||
|
||||
{/* 4. Navegación de Tabs */}
|
||||
<div className="border-b overflow-x-auto scrollbar-hide">
|
||||
<nav className="flex gap-8 min-w-max">
|
||||
<Tab to="/planes/$planId/datos" params={{ planId }}>Datos Generales</Tab>
|
||||
<Tab to="/planes/$planId/mapa" params={{ planId }}>Mapa Curricular</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>
|
||||
<div className="scrollbar-hide overflow-x-auto border-b">
|
||||
<nav className="flex min-w-max gap-8">
|
||||
<Tab to="/planes/$planId/datos" params={{ planId }}>
|
||||
Datos Generales
|
||||
</Tab>
|
||||
<Tab to="/planes/$planId/mapa" params={{ planId }}>
|
||||
Mapa Curricular
|
||||
</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>
|
||||
</div>
|
||||
|
||||
{/* 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 />
|
||||
</main>
|
||||
</div>
|
||||
@@ -79,16 +126,23 @@ function RouteComponent() {
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// 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 (
|
||||
<div className="flex items-center gap-4 bg-slate-50/50 border border-slate-200/60 p-4 rounded-xl shadow-sm">
|
||||
<div className="p-2 bg-white rounded-lg border shadow-sm">
|
||||
{icon}
|
||||
</div>
|
||||
<div className="flex items-center gap-4 rounded-xl border border-slate-200/60 bg-slate-50/50 p-4 shadow-sm">
|
||||
<div className="rounded-lg border bg-white p-2 shadow-sm">{icon}</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>
|
||||
</div>
|
||||
</div>
|
||||
@@ -98,17 +152,17 @@ function InfoCard({ icon, label, value }: { icon: React.ReactNode, label: string
|
||||
function Tab({
|
||||
to,
|
||||
params,
|
||||
children
|
||||
children,
|
||||
}: {
|
||||
to: string;
|
||||
params?: any;
|
||||
to: string
|
||||
params?: any
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
return (
|
||||
<Link
|
||||
to={to}
|
||||
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={{
|
||||
className: 'border-teal-600 text-teal-700 font-bold',
|
||||
}}
|
||||
|
||||
@@ -1,146 +1,10 @@
|
||||
import { createFileRoute } 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'
|
||||
import { createFileRoute, redirect } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/planes/$planId/asignaturas/'
|
||||
)({
|
||||
component: MateriasLandingPage,
|
||||
export const Route = createFileRoute('/planes/$planId/asignaturas/')({
|
||||
beforeLoad: ({ params }) => {
|
||||
throw redirect({
|
||||
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