Refactor components by removing unused imports and optimizing state management; add configuration for Azure Static Web Apps

This commit is contained in:
2025-11-28 09:52:53 -06:00
parent 2db3a0570a
commit 76170421aa
10 changed files with 106 additions and 205 deletions

View File

@@ -2,7 +2,7 @@ import { createFileRoute, Link, useRouter } from '@tanstack/react-router'
import { useSuspenseQuery, queryOptions, useQueryClient } from '@tanstack/react-query'
import { supabase } from '@/auth/supabase'
import * as Icons from 'lucide-react'
import { useEffect, useMemo, useState } from 'react'
import { useMemo, useState } from 'react'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Select, SelectTrigger, SelectContent, SelectItem, SelectValue } from '@/components/ui/select'
@@ -81,7 +81,7 @@ async function fetchAsignaturas(search: SearchState): Promise<Asignatura[]> {
const planIds = await fetchPlanIdsByScope(search)
if (planIds && planIds.length === 0) return []
console.log(AsignaturaCard);
let query = supabase
.from('asignaturas')
.select(`
@@ -168,25 +168,25 @@ function RouteComponent() {
const [q, setQ] = useState(search.q ?? '')
const [sem, setSem] = useState<string>('todos')
const [tipo, setTipo] = useState<string>('todos')
const [groupBy, setGroupBy] = useState<'semestre' | 'ninguno'>('semestre')
const [groupBy] = useState<'semestre' | 'ninguno'>('semestre')
const [flag, setFlag] = useState<'' | 'sinBibliografia' | 'sinCriterios' | 'sinContenidos'>(search.f ?? '')
const [facultad, setFacultad] = useState("todas")
const [carrera, setCarrera] = useState("todas")
const [facultad, setFacultad] = useState("todas")
const [carrera, setCarrera] = useState("todas")
/* useEffect(() => {
const timeout = setTimeout(() => {
router.navigate({
to: '/asignaturas',
search: { ...search, q },
replace: true,
})
}, 400)
return () => clearTimeout(timeout)
}, [q]) */
/* useEffect(() => {
const timeout = setTimeout(() => {
router.navigate({
to: '/asignaturas',
search: { ...search, q },
replace: true,
})
}, 400)
return () => clearTimeout(timeout)
}, [q]) */
function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
const value = e.target.value
setQ(value)
router.navigate({
@@ -199,30 +199,30 @@ function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
})
}
// 🟣 Lista única de facultades
const facultadesList = useMemo(() => {
const unique = new Map<string, string>()
planes?.forEach((p) => {
const fac = p.carrera?.facultad
if (fac?.id && fac?.nombre) unique.set(fac.id, fac.nombre)
})
return Array.from(unique.entries())
}, [planes])
// 🟣 Lista única de facultades
const facultadesList = useMemo(() => {
const unique = new Map<string, string>()
planes?.forEach((p) => {
const fac = p.carrera?.facultad
if (fac?.id && fac?.nombre) unique.set(fac.id, fac.nombre)
})
return Array.from(unique.entries())
}, [planes])
// 🎓 Lista de carreras según la facultad seleccionada
const carrerasList = useMemo(() => {
const unique = new Map<string, string>()
planes?.forEach((p) => {
if (
p.carrera?.id &&
p.carrera?.nombre &&
(!facultad || facultad === "todas" || p.carrera?.facultad?.id === facultad)
) {
unique.set(p.carrera.id, p.carrera.nombre)
}
})
return Array.from(unique.entries())
}, [planes, facultad])
// 🎓 Lista de carreras según la facultad seleccionada
const carrerasList = useMemo(() => {
const unique = new Map<string, string>()
planes?.forEach((p) => {
if (
p.carrera?.id &&
p.carrera?.nombre &&
(!facultad || facultad === "todas" || p.carrera?.facultad?.id === facultad)
) {
unique.set(p.carrera.id, p.carrera.nombre)
}
})
return Array.from(unique.entries())
}, [planes, facultad])
// NEW: Clonado individual
@@ -256,12 +256,6 @@ const carrerasList = useMemo(() => {
return Array.from(s).sort((a, b) => (a === '—' ? 1 : 0) - (b === '—' ? 1 : 0) || Number(a) - Number(b))
}, [asignaturas])
const tipos = useMemo(() => {
const s = new Set<string>()
asignaturas.forEach(a => s.add(a.tipo ?? '—'))
return Array.from(s).sort()
}, [asignaturas])
// Salud
const salud = useMemo(() => {
let sinBibliografia = 0, sinCriterios = 0, sinContenidos = 0
@@ -274,29 +268,29 @@ const carrerasList = useMemo(() => {
}, [asignaturas])
const filtered = useMemo(() => {
const t = q.trim().toLowerCase()
return asignaturas.filter(a => {
const matchesQ =
!t ||
[a.nombre, a.clave, a.tipo, a.objetivos, a.plan?.nombre, a.plan?.carrera?.nombre, a.plan?.carrera?.facultad?.nombre]
.filter(Boolean)
.some(v => String(v).toLowerCase().includes(t))
const t = q.trim().toLowerCase()
return asignaturas.filter(a => {
const matchesQ =
!t ||
[a.nombre, a.clave, a.tipo, a.objetivos, a.plan?.nombre, a.plan?.carrera?.nombre, a.plan?.carrera?.facultad?.nombre]
.filter(Boolean)
.some(v => String(v).toLowerCase().includes(t))
const semOK = sem === 'todos' || String(a.semestre ?? '—') === sem
const tipoOK = tipo === 'todos' || (a.tipo ?? '—') === tipo
const carreraOK = carrera === 'todas' || a.plan?.carrera?.id === carrera
const facultadOK = facultad === 'todas' || a.plan?.carrera?.facultad?.id === facultad
const planOK = !search.planId || a.plan?.id === search.planId
const semOK = sem === 'todos' || String(a.semestre ?? '—') === sem
const tipoOK = tipo === 'todos' || (a.tipo ?? '—') === tipo
const carreraOK = carrera === 'todas' || a.plan?.carrera?.id === carrera
const facultadOK = facultad === 'todas' || a.plan?.carrera?.facultad?.id === facultad
const planOK = !search.planId || a.plan?.id === search.planId
const flagOK =
!flag ||
(flag === 'sinBibliografia' && (!a.bibliografia || a.bibliografia.length === 0)) ||
(flag === 'sinCriterios' && (!a.criterios_evaluacion || !a.criterios_evaluacion.trim())) ||
(flag === 'sinContenidos' && (!a.contenidos || Object.keys(a.contenidos ?? {}).length === 0))
const flagOK =
!flag ||
(flag === 'sinBibliografia' && (!a.bibliografia || a.bibliografia.length === 0)) ||
(flag === 'sinCriterios' && (!a.criterios_evaluacion || !a.criterios_evaluacion.trim())) ||
(flag === 'sinContenidos' && (!a.contenidos || Object.keys(a.contenidos ?? {}).length === 0))
return matchesQ && semOK && tipoOK && flagOK && carreraOK && facultadOK && planOK
})
}, [q, sem, tipo, flag, carrera, facultad, asignaturas])
return matchesQ && semOK && tipoOK && flagOK && carreraOK && facultadOK && planOK
})
}, [q, sem, tipo, flag, carrera, facultad, asignaturas])
// Agrupación
@@ -316,18 +310,19 @@ const carrerasList = useMemo(() => {
}, [filtered, groupBy])
// Helpers
const clearFilters = () => { setQ(''); setSem('todos'); setTipo('todos'); setCarrera('todas'); setFlag('') ; setFacultad('todas')
const clearFilters = () => {
setQ(''); setSem('todos'); setTipo('todos'); setCarrera('todas'); setFlag(''); setFacultad('todas')
// Actualiza la URL limpiando todos los query params
router.navigate({
to: '/asignaturas',
search: {
q: '',
planId: '',
carreraId: '',
facultadId: '',
f: ''
},
})
router.navigate({
to: '/asignaturas',
search: {
q: '',
planId: '',
carreraId: '',
facultadId: '',
f: ''
},
})
}
// NEW: util para clonar 1 asignatura
@@ -363,7 +358,7 @@ const carrerasList = useMemo(() => {
if (error) throw error
}
// NEW: abrir modal clon individual
function openClone(a: Asignatura) {
@@ -550,7 +545,12 @@ const carrerasList = useMemo(() => {
value={search.planId ?? "todos"}
onValueChange={(val) => {
router.navigate({
search: { ...search, planId: val === "todos" ? "" : val },
to: '/asignaturas',
search: {
...search,
planId: val === 'todos' ? '' : val,
},
replace: true,
})
}}
>
@@ -828,15 +828,14 @@ function AsignaturaCard({ a, onClone, onAddToCart }: { a: Asignatura; onClone: (
const horasT = a.horas_teoricas ?? 0
const horasP = a.horas_practicas ?? 0
const meta = tipoMeta(a.tipo)
const FacIcon = (Icons as any)[a.plan?.carrera?.facultad?.icon ?? 'Building2'] || Icons.Building2
console.log(a);
return (
<li className="group relative overflow-hidden rounded-2xl border bg-white/75 dark:bg-neutral-900/60 shadow-sm hover:shadow-md transition-all"
style={{
borderColor: a.plan?.carrera?.facultad?.color ?? '#ccc',
backgroundColor: `${a.plan?.carrera?.facultad?.color}15`, // 15 = transparencia HEX
}}
style={{
borderColor: a.plan?.carrera?.facultad?.color ?? '#ccc',
backgroundColor: `${a.plan?.carrera?.facultad?.color}15`, // 15 = transparencia HEX
}}
>
<div className="p-3">
<div className="flex items-start gap-3">
@@ -890,15 +889,15 @@ function AsignaturaCard({ a, onClone, onAddToCart }: { a: Asignatura; onClone: (
{a.plan.carrera && (
<InfoChip
icon={<Icons.GraduationCap className="h-3 w-3" />}
label={a.plan.carrera.nombre}
/>
label={a.plan.carrera.nombre}
/>
)}
{a.plan.carrera?.facultad && (
<InfoChip
icon={<Icons.Building2 className="h-3 w-3" />}
label={a.plan.carrera.facultad.nombre}
tint={a.plan.carrera.facultad.color}
/>
tint={a.plan.carrera.facultad.color}
/>
)}
</div>
)}