This commit is contained in:
2025-12-22 21:05:35 -06:00
parent b303398cd4
commit 2f4f445ff0
7 changed files with 205 additions and 9 deletions

View File

@@ -13,6 +13,7 @@
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
},
"editor.tabSize": 2,
"eslint.validate": [
"javascript",
"javascriptreact",

View File

@@ -5,6 +5,7 @@ const config = {
semi: false,
singleQuote: true,
trailingComma: 'all',
tabWidth: 2,
plugins: ['prettier-plugin-tailwindcss'],
tailwindFunctions: ['clsx', 'cn', 'cva'],
endOfLine: 'lf',

View File

@@ -0,0 +1,29 @@
import { SearchIcon } from 'lucide-react'
import { useId } from 'react'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
const InputSearchIconDemo = () => {
const id = useId()
return (
<div className="w-full max-w-xs space-y-2">
<Label htmlFor={id}>Search input with icon and button</Label>
<div className="relative">
<div className="text-muted-foreground pointer-events-none absolute inset-y-0 left-0 flex items-center justify-center pl-3 peer-disabled:opacity-50">
<SearchIcon className="size-4" />
<span className="sr-only">Search</span>
</div>
<Input
id={id}
type="search"
placeholder="Search..."
className="peer px-9 [&::-webkit-search-cancel-button]:appearance-none [&::-webkit-search-decoration]:appearance-none [&::-webkit-search-results-button]:appearance-none [&::-webkit-search-results-decoration]:appearance-none"
/>
</div>
</div>
)
}
export default InputSearchIconDemo

View File

@@ -1,6 +1,6 @@
import { ArrowRight } from 'lucide-react'
import { ArrowRight } from 'lucide-react'
import type {LucideIcon} from 'lucide-react';
import type { LucideIcon } from 'lucide-react'
import { Badge } from '@/components/ui/badge'
import { Card, CardContent, CardFooter, CardHeader } from '@/components/ui/card'
@@ -36,7 +36,7 @@ export default function PlanEstudiosCard({
<Card
onClick={onClick}
className={cn(
'group relative flex h-full cursor-pointer flex-col justify-between overflow-hidden border-l-4 transition-all hover:shadow-lg',
'group relative flex h-full cursor-pointer flex-col justify-between gap-2 overflow-hidden border-l-4 transition-all hover:shadow-lg',
)}
// Aplicamos el color de la facultad dinámicamente al borde y un fondo muy sutil
style={{
@@ -61,14 +61,14 @@ export default function PlanEstudiosCard({
</h4>
</CardHeader>
<CardContent className="text-muted-foreground space-y-1 pb-4 text-sm">
<CardContent className="text-muted-foreground space-y-1 text-sm">
<p className="text-foreground font-medium">
{nivel} {ciclos}
</p>
<p>{facultad}</p>
</CardContent>
<CardFooter className="bg-background/50 flex items-center justify-between border-t px-6 py-3 backdrop-blur-sm">
<CardFooter className="bg-background/50 flex items-center justify-between border-t px-6 pb-3 backdrop-blur-sm [.border-t]:pt-3">
<Badge className={`text-sm font-semibold ${claseColorEstado}`}>
{estado}
</Badge>

View File

@@ -9,11 +9,17 @@
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
import { Route as rootRouteImport } from './routes/__root'
import { Route as PlanesRouteImport } from './routes/planes'
import { Route as LoginRouteImport } from './routes/login'
import { Route as DashboardRouteImport } from './routes/dashboard'
import { Route as IndexRouteImport } from './routes/index'
import { Route as DemoTanstackQueryRouteImport } from './routes/demo/tanstack-query'
const PlanesRoute = PlanesRouteImport.update({
id: '/planes',
path: '/planes',
getParentRoute: () => rootRouteImport,
} as any)
const LoginRoute = LoginRouteImport.update({
id: '/login',
path: '/login',
@@ -39,12 +45,14 @@ export interface FileRoutesByFullPath {
'/': typeof IndexRoute
'/dashboard': typeof DashboardRoute
'/login': typeof LoginRoute
'/planes': typeof PlanesRoute
'/demo/tanstack-query': typeof DemoTanstackQueryRoute
}
export interface FileRoutesByTo {
'/': typeof IndexRoute
'/dashboard': typeof DashboardRoute
'/login': typeof LoginRoute
'/planes': typeof PlanesRoute
'/demo/tanstack-query': typeof DemoTanstackQueryRoute
}
export interface FileRoutesById {
@@ -52,25 +60,40 @@ export interface FileRoutesById {
'/': typeof IndexRoute
'/dashboard': typeof DashboardRoute
'/login': typeof LoginRoute
'/planes': typeof PlanesRoute
'/demo/tanstack-query': typeof DemoTanstackQueryRoute
}
export interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath
fullPaths: '/' | '/dashboard' | '/login' | '/demo/tanstack-query'
fullPaths: '/' | '/dashboard' | '/login' | '/planes' | '/demo/tanstack-query'
fileRoutesByTo: FileRoutesByTo
to: '/' | '/dashboard' | '/login' | '/demo/tanstack-query'
id: '__root__' | '/' | '/dashboard' | '/login' | '/demo/tanstack-query'
to: '/' | '/dashboard' | '/login' | '/planes' | '/demo/tanstack-query'
id:
| '__root__'
| '/'
| '/dashboard'
| '/login'
| '/planes'
| '/demo/tanstack-query'
fileRoutesById: FileRoutesById
}
export interface RootRouteChildren {
IndexRoute: typeof IndexRoute
DashboardRoute: typeof DashboardRoute
LoginRoute: typeof LoginRoute
PlanesRoute: typeof PlanesRoute
DemoTanstackQueryRoute: typeof DemoTanstackQueryRoute
}
declare module '@tanstack/react-router' {
interface FileRoutesByPath {
'/planes': {
id: '/planes'
path: '/planes'
fullPath: '/planes'
preLoaderRoute: typeof PlanesRouteImport
parentRoute: typeof rootRouteImport
}
'/login': {
id: '/login'
path: '/login'
@@ -106,6 +129,7 @@ const rootRouteChildren: RootRouteChildren = {
IndexRoute: IndexRoute,
DashboardRoute: DashboardRoute,
LoginRoute: LoginRoute,
PlanesRoute: PlanesRoute,
DemoTanstackQueryRoute: DemoTanstackQueryRoute,
}
export const routeTree = rootRouteImport

View File

@@ -13,7 +13,7 @@ import {
} from 'lucide-react'
import DashboardHeader from '@/components/dashboard/DashboardHeader'
import PlanEstudiosCard from '@/components/plan_estudios/PlanEstudiosCard'
import PlanEstudiosCard from '@/components/planes/PlanEstudiosCard'
export const Route = createFileRoute('/dashboard')({
component: RouteComponent,

141
src/routes/planes.tsx Normal file
View File

@@ -0,0 +1,141 @@
import { createFileRoute } from '@tanstack/react-router'
import {
Activity,
BookOpenText,
Calculator,
FlaskConical,
Laptop,
PencilRuler,
Plus,
Scale,
Stethoscope,
} from 'lucide-react'
import PlanEstudiosCard from '@/components/planes/PlanEstudiosCard'
export const Route = createFileRoute('/planes')({
component: RouteComponent,
})
function RouteComponent() {
return (
<main className="bg-background min-h-screen w-full">
<div className="mx-auto flex w-full max-w-7xl flex-col gap-4 px-4 py-6 md:px-6 lg:px-8">
<div className="flex flex-col gap-4 lg:col-span-3">
<div className="flex flex-col items-stretch justify-between gap-4 sm:flex-row sm:items-center">
<div className="flex items-center gap-3">
<div className="bg-primary/10 text-primary flex h-10 w-10 items-center justify-center rounded-xl">
<BookOpenText className="h-5 w-5" strokeWidth={2} />
</div>
<div>
<h1 className="font-display text-foreground text-2xl font-bold">
Planes de Estudio
</h1>
<p className="text-muted-foreground text-sm">
Gestiona los planes curriculares de tu institución
</p>
</div>
</div>
<button
type="button"
className={
'ring-offset-background focus-visible:ring-ring 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 whitespace-nowrap shadow-md transition-colors focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:h-4 [&_svg]:w-4 [&_svg]:shrink-0'
}
aria-label="Nuevo plan de estudios"
title="Nuevo plan de estudios"
>
<Plus className="" />
Nuevo plan de estudios
</button>
</div>
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
<PlanEstudiosCard
Icono={Laptop}
nombrePrograma="Ingeniería en Sistemas Computacionales"
nivel="Licenciatura"
ciclos="8 semestres"
facultad="Facultad de Ingeniería"
estado="Revisión expertos"
claseColorEstado="bg-amber-600"
colorFacultad="#2563eb"
onClick={() => console.log('Navegar a Sistemas...')}
/>
<PlanEstudiosCard
Icono={Stethoscope}
nombrePrograma="Médico Cirujano"
nivel="Licenciatura"
ciclos="10 semestres"
facultad="Facultad de Medicina"
estado="Aprobado"
claseColorEstado="bg-emerald-600"
colorFacultad="#dc2626"
/>
<PlanEstudiosCard
Icono={Calculator}
nombrePrograma="Licenciatura en Actuaría"
nivel="Licenciatura"
ciclos="9 semestres"
facultad="Facultad de Negocios"
estado="Aprobado"
claseColorEstado="bg-emerald-600"
colorFacultad="#059669"
onClick={() => console.log('Ver Actuaría')}
/>
<PlanEstudiosCard
Icono={PencilRuler}
nombrePrograma="Licenciatura en Arquitectura"
nivel="Licenciatura"
ciclos="10 semestres"
facultad="Facultad Mexicana de Arquitectura, Diseño y Comunicación"
estado="En proceso"
claseColorEstado="bg-orange-500"
colorFacultad="#ea580c"
onClick={() => console.log('Ver Arquitectura')}
/>
<PlanEstudiosCard
Icono={Activity}
nombrePrograma="Licenciatura en Fisioterapia"
nivel="Licenciatura"
ciclos="8 semestres"
facultad="Escuela de Altos Estudios en Salud"
estado="Revisión expertos"
claseColorEstado="bg-amber-600"
colorFacultad="#0891b2"
onClick={() => console.log('Ver Fisioterapia')}
/>
<PlanEstudiosCard
Icono={Scale}
nombrePrograma="Licenciatura en Derecho"
nivel="Licenciatura"
ciclos="10 semestres"
facultad="Facultad de Derecho"
estado="Pendiente"
claseColorEstado="bg-yellow-500"
colorFacultad="#7c3aed"
onClick={() => console.log('Ver Derecho')}
/>
<PlanEstudiosCard
Icono={FlaskConical}
nombrePrograma="Químico Farmacéutico Biólogo"
nivel="Licenciatura"
ciclos="9 semestres"
facultad="Facultad de Ciencias Químicas"
estado="Actualización"
claseColorEstado="bg-lime-600"
colorFacultad="#65a30d"
onClick={() => console.log('Ver QFB')}
/>
</div>
</div>
</div>
</main>
)
}