Merge remote-tracking branch 'origin/issue/45-integrar-el-wizard-de-creacin-de-materia' into issue/42-que-tenga-persistencia-el-plan-de-estudios
This commit is contained in:
@@ -35,6 +35,8 @@ export const Route = createFileRoute('/planes/$planId/_detalle')({
|
||||
} catch (e: any) {
|
||||
// PGRST116: The result contains 0 rows
|
||||
if (e?.code === 'PGRST116') {
|
||||
console.log('not found on', Route.path)
|
||||
|
||||
throw notFound()
|
||||
}
|
||||
throw e
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
} from 'lucide-react'
|
||||
import { useState, useMemo } from 'react'
|
||||
|
||||
import type { Asignatura } from '@/types/plan'
|
||||
import type { Asignatura, AsignaturaStatus, TipoAsignatura } from '@/types/plan'
|
||||
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Button } from '@/components/ui/button'
|
||||
@@ -33,17 +33,24 @@ import {
|
||||
import { usePlanAsignaturas, usePlanLineas } from '@/data'
|
||||
|
||||
// --- Configuración de Estilos ---
|
||||
const statusConfig: Record<string, { label: string; className: string }> = {
|
||||
const statusConfig: Record<
|
||||
AsignaturaStatus,
|
||||
{ label: string; className: string }
|
||||
> = {
|
||||
borrador: { label: 'Borrador', className: 'bg-slate-100 text-slate-600' },
|
||||
revisada: { label: 'Revisada', className: 'bg-amber-100 text-amber-700' },
|
||||
aprobada: { label: 'Aprobada', className: 'bg-emerald-100 text-emerald-700' },
|
||||
}
|
||||
|
||||
const tipoConfig: Record<string, { label: string; className: string }> = {
|
||||
obligatoria: { label: 'Obligatoria', className: 'bg-blue-100 text-blue-700' },
|
||||
optativa: { label: 'Optativa', className: 'bg-purple-100 text-purple-700' },
|
||||
troncal: { label: 'Troncal', className: 'bg-slate-100 text-slate-700' },
|
||||
}
|
||||
const tipoConfig: Record<TipoAsignatura, { label: string; className: string }> =
|
||||
{
|
||||
obligatoria: {
|
||||
label: 'Obligatoria',
|
||||
className: 'bg-blue-100 text-blue-700',
|
||||
},
|
||||
optativa: { label: 'Optativa', className: 'bg-purple-100 text-purple-700' },
|
||||
troncal: { label: 'Troncal', className: 'bg-slate-100 text-slate-700' },
|
||||
}
|
||||
|
||||
// --- Mapeadores de API ---
|
||||
const mapAsignaturas = (asigApi: Array<any> = []): Array<Asignatura> => {
|
||||
@@ -59,10 +66,13 @@ const mapAsignaturas = (asigApi: Array<any> = []): Array<Asignatura> => {
|
||||
estado: 'borrador', // O el campo que venga de tu API
|
||||
hd: Math.floor((asig.horas_semana ?? 0) / 2),
|
||||
hi: Math.ceil((asig.horas_semana ?? 0) / 2),
|
||||
prerrequisitos: Array.isArray(asig.prerrequisitos)
|
||||
? asig.prerrequisitos
|
||||
: [],
|
||||
}))
|
||||
}
|
||||
|
||||
export const Route = createFileRoute('/planes/$planId/_detalle/asignaturas')({
|
||||
export const Route = createFileRoute('/planes/$planId/_detalle/asignaturas/')({
|
||||
component: AsignaturasPage,
|
||||
})
|
||||
|
||||
@@ -131,7 +141,17 @@ function AsignaturasPage() {
|
||||
<Button variant="outline" size="sm">
|
||||
<Copy className="mr-2 h-4 w-4" /> Clonar
|
||||
</Button>
|
||||
<Button className="bg-emerald-700 hover:bg-emerald-800">
|
||||
<Button
|
||||
onClick={() => {
|
||||
console.log('planId desde asignaturas', planId)
|
||||
|
||||
navigate({
|
||||
to: `/planes/${planId}/asignaturas/nueva`,
|
||||
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"
|
||||
>
|
||||
<Plus className="mr-2 h-4 w-4" /> Nueva Asignatura
|
||||
</Button>
|
||||
</div>
|
||||
@@ -262,17 +282,17 @@ function AsignaturasPage() {
|
||||
<TableCell>
|
||||
<Badge
|
||||
variant="outline"
|
||||
className={`capitalize shadow-sm ${tipoConfig[asignatura.tipo]?.className}`}
|
||||
className={`capitalize shadow-sm ${tipoConfig[asignatura.tipo].className}`}
|
||||
>
|
||||
{tipoConfig[asignatura.tipo]?.label}
|
||||
{tipoConfig[asignatura.tipo].label}
|
||||
</Badge>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Badge
|
||||
variant="outline"
|
||||
className={`capitalize shadow-sm ${statusConfig[asignatura.estado]?.className}`}
|
||||
className={`capitalize shadow-sm ${statusConfig[asignatura.estado].className}`}
|
||||
>
|
||||
{statusConfig[asignatura.estado]?.label}
|
||||
{statusConfig[asignatura.estado].label}
|
||||
</Badge>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
@@ -3,12 +3,13 @@ import { createFileRoute } from '@tanstack/react-router'
|
||||
import { NuevaAsignaturaModalContainer } from '@/features/asignaturas/nueva/NuevaAsignaturaModalContainer'
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/planes/$planId/asignaturas/_lista/nueva',
|
||||
'/planes/$planId/_detalle/asignaturas/nueva',
|
||||
)({
|
||||
component: NuevaAsignaturaModal,
|
||||
})
|
||||
|
||||
function NuevaAsignaturaModal() {
|
||||
const { planId } = Route.useParams()
|
||||
console.log('planId desde nueva', planId)
|
||||
return <NuevaAsignaturaModalContainer planId={planId} />
|
||||
}
|
||||
44
src/routes/planes/$planId/asignaturas/$asignaturaId.tsx
Normal file
44
src/routes/planes/$planId/asignaturas/$asignaturaId.tsx
Normal file
@@ -0,0 +1,44 @@
|
||||
import { createFileRoute, notFound } from '@tanstack/react-router'
|
||||
|
||||
import AsignaturaDetailPage from '@/components/asignaturas/detalle/AsignaturaDetailPage'
|
||||
import { NotFoundPage } from '@/components/ui/NotFoundPage'
|
||||
import { subjects_get } from '@/data/api/subjects.api'
|
||||
import { qk } from '@/data/query/keys'
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/planes/$planId/asignaturas/$asignaturaId',
|
||||
)({
|
||||
loader: async ({ context: { queryClient }, params: { asignaturaId } }) => {
|
||||
try {
|
||||
await queryClient.ensureQueryData({
|
||||
queryKey: qk.asignatura(asignaturaId),
|
||||
queryFn: () => subjects_get(asignaturaId),
|
||||
})
|
||||
} catch (e: any) {
|
||||
// PGRST116: The result contains 0 rows (Supabase Single response error)
|
||||
if (e?.code === 'PGRST116') {
|
||||
throw notFound()
|
||||
}
|
||||
throw e
|
||||
}
|
||||
},
|
||||
notFoundComponent: () => {
|
||||
return (
|
||||
<NotFoundPage
|
||||
title="Materia no encontrada"
|
||||
message="La asignatura que buscas no existe o fue eliminada."
|
||||
/>
|
||||
)
|
||||
},
|
||||
component: RouteComponent,
|
||||
})
|
||||
|
||||
function RouteComponent() {
|
||||
// const { planId, asignaturaId } = Route.useParams()
|
||||
|
||||
return (
|
||||
<div>
|
||||
<AsignaturaDetailPage></AsignaturaDetailPage>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
import AsignaturaDetailPage from '@/components/asignaturas/detalle/AsignaturaDetailPage'
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/planes/$planId/asignaturas/$asignaturaId',
|
||||
)({
|
||||
component: RouteComponent,
|
||||
})
|
||||
|
||||
function RouteComponent() {
|
||||
//const { planId, asignaturaId } = Route.useParams()
|
||||
|
||||
return (
|
||||
<div>
|
||||
<AsignaturaDetailPage></AsignaturaDetailPage>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
import { createFileRoute, Outlet } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createFileRoute('/planes/$planId/asignaturas/_lista')({
|
||||
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">
|
||||
<h1 className="text-foreground text-2xl font-semibold">Asignaturas</h1>
|
||||
<Outlet />
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
import { createFileRoute, Outlet, notFound } from '@tanstack/react-router'
|
||||
|
||||
import { NotFoundPage } from '@/components/ui/NotFoundPage'
|
||||
import { plans_get } from '@/data/api/plans.api'
|
||||
import { qk } from '@/data/query/keys'
|
||||
|
||||
export const Route = createFileRoute('/planes/$planId/asignaturas')({
|
||||
loader: async ({ context: { queryClient }, params: { planId } }) => {
|
||||
try {
|
||||
await queryClient.ensureQueryData({
|
||||
queryKey: qk.plan(planId),
|
||||
queryFn: () => plans_get(planId),
|
||||
})
|
||||
} catch (e: any) {
|
||||
if (e?.code === 'PGRST116') {
|
||||
throw notFound()
|
||||
}
|
||||
throw e
|
||||
}
|
||||
},
|
||||
notFoundComponent: () => {
|
||||
return (
|
||||
<NotFoundPage
|
||||
title="Plan de Estudios no encontrado"
|
||||
message="El plan de estudios que intentas consultar no existe o no tienes permisos para verlo."
|
||||
/>
|
||||
)
|
||||
},
|
||||
component: AsignaturasLayout,
|
||||
})
|
||||
|
||||
function AsignaturasLayout() {
|
||||
return <Outlet />
|
||||
}
|
||||
Reference in New Issue
Block a user