feat(ti/proyectos): filtros multi-select + barra reorganizada
- Backend (
backend/routers/ti.py): el endpoint/dashboard/statspasa los 8 filtros deOptional[str|int]alist[str|int] = Query(default=[]). Strings se filtran conor_(*[col.ilike(v) ...])para preservar case-insensitivity; numéricos concol.in_(lista)yextract("year", ...). Nuevo agregadopor_empresapara sembrar el filtro Empresa. - API typing (
portal_ti/lib/ti.api.ts): todos los filtros pasan astring[]/number[];DashboardStats.por_empresaagregado;getDashboardStatsahora usaparamsSerializer: { indexes: null }para que axios serialice arrays como?k=a&k=b(formato que FastAPI espera). - Nuevo componente
portal_ti/components/filters/MultiSelectFilter.tsx(portado defrontend/): trigger con badge de count, dropdown con buscador interno, botones Todos/Limpiar, checkboxes y "Quitar filtro" cuando hay selección. Click-outside cierra. - Page (
portal_ti/app/sistemas/proyectos/page.tsx):- 8
FilterSelectlegacy reemplazados por 9MultiSelectFilter(incluye nuevo filtro Empresa); numéricos (año, etapa) parsean en elonChange. - Consumidores del filtro semáforo normalizan como array
(
.map(s => s.trim().toLowerCase())+includes) en lugar de.trim()sobrestring[](fix de error de tipos en build). - Eliminada la barra de búsqueda general + debounce; el fetch
reactiva solo a
dashFiltros(clics, no input continuo). - Eliminados
KpiTotal, chips de buckets y card "Datos sincronizados"; grid de KPIslg:5 → lg:4. - Nueva mini-card "PROYECTOS" a la derecha de la barra de filtros
con el total dinámico (icon
FolderKanban, tabular-nums).
- 8