8.2 KiB
Sprint 8 — Etapa 4.5: Fixes de navegación e InQuality
Tipo: UI — fixes puntuales Rama: main Estimación: 1 hora Dependencias: Etapa 4 completada ✅
Objetivo
Dos fixes independientes detectados en validación de Etapa 4:
-
InQuality invisible en admin:
OrganizationsPagefiltra con.eq('is_provider', false), dejando fuera a InQuality. El equipo no puede gestionar sus propias áreas ni procesos desde el panel. Fix: mostrar todas las orgs, con un badge visual para InQuality. -
Back button sin contexto de org: En el workspace, el botón "← Solar Banco" (o cualquier org) navega a
/librarysin parámetros — el usuario llega al home genérico en lugar de la sección de esa org. Fix: pasar?org=<orgId>al navegar y hacer scroll al bloque correcto en LibraryPage.
Fix 1: OrganizationsPage.tsx
Problema actual
Línea en fetchOrgs():
.eq('is_provider', false)
Esta condición excluye a InQuality (que tiene is_provider = true) de la lista. El equipo de InQuality no puede entrar a /admin/organizations/:id de su propia org para gestionar áreas y procesos.
Cambios en OrganizationsPage.tsx
1A. Extender la interface OrgRow:
interface OrgRow {
id: string
name: string
is_provider: boolean // 🆕
created_at: string
processes: [{ count: number }]
users: [{ count: number }]
}
1B. Actualizar fetchOrgs() — quitar el filtro y agregar is_provider al select:
const fetchOrgs = async () => {
const { data } = await supabase
.from('organizations')
.select('id, name, is_provider, created_at, processes:processes(count), users:users(count)')
// ← eliminar el .eq('is_provider', false)
.order('name')
// Ordenar: proveedor primero, luego clientes alfabético
const sorted = ((data as OrgRow[] | null) ?? []).sort((a, b) => {
if (a.is_provider && !b.is_provider) return -1
if (!a.is_provider && b.is_provider) return 1
return a.name.localeCompare(b.name)
})
setOrgs(sorted)
setLoading(false)
}
1C. Actualizar el título:
// Antes:
<h2 className="text-lg font-semibold text-slate-900">Organizaciones clientes</h2>
// Después:
<h2 className="text-lg font-semibold text-slate-900">Organizaciones</h2>
1D. Agregar badge "Proveedor" en la columna de nombre:
En la fila de la tabla, en la celda de nombre:
<td className="py-3 px-4 font-medium text-slate-900">
<div className="flex items-center gap-2">
{org.name}
{org.is_provider && (
<span className="text-[10px] font-semibold px-1.5 py-0.5 rounded-full border"
style={{ color: '#F59845', background: '#fff8f0', borderColor: '#fcd9aa' }}>
InQuality
</span>
)}
</div>
</td>
No cambiar el botón "Ver detalle →" — funciona igual para InQuality que para cualquier otra org.
Fix 2: Back button con contexto de org
Son tres cambios coordinados: router.tsx, WorkspacePage.tsx, y LibraryPage.tsx.
Fix 2A: router.tsx — declarar org como search param de /library
TanStack Router requiere que los search params estén declarados en la ruta para que navigate({ search: ... }) sea type-safe.
// Antes:
const libraryRoute = createRoute({
getParentRoute: () => rootRoute,
path: '/library',
component: LibraryPage,
})
// Después:
const libraryRoute = createRoute({
getParentRoute: () => rootRoute,
path: '/library',
component: LibraryPage,
validateSearch: (search: Record<string, unknown>) => ({
org: typeof search.org === 'string' ? search.org : undefined,
}),
})
NO tocar indexRoute (/) — no necesita el param porque nunca se navega a / desde el workspace.
Fix 2B: WorkspacePage.tsx — pasar org en handleBack
// Antes (línea ~194-196):
function handleBack() {
void navigate({ to: '/library' })
}
// Después:
function handleBack() {
void navigate({
to: '/library',
search: currentProcess.orgId ? { org: currentProcess.orgId } : {},
})
}
Fix 2C: LibraryPage.tsx — leer param y hacer scroll
Imports: agregar useLocation al import existente de @tanstack/react-router:
// Antes:
import { useNavigate } from '@tanstack/react-router'
// Después:
import { useNavigate, useLocation } from '@tanstack/react-router'
Leer el param (agregar dentro del cuerpo de LibraryPage, junto a los otros hooks):
const location = useLocation()
const orgParam = useMemo(
() => new URLSearchParams(location.search).get('org') ?? undefined,
[location.search]
)
useMemo ya está importado en el archivo.
Efecto de scroll (agregar después de los efectos existentes):
// Scroll a la sección del org si venimos del workspace
useEffect(() => {
if (!orgParam || isLoading || processes.length === 0) return
requestAnimationFrame(() => {
const el = document.getElementById(`org-section-${orgParam}`)
el?.scrollIntoView({ behavior: 'smooth', block: 'start' })
})
}, [orgParam, isLoading, processes.length])
Agregar id al div de cada org en el bloque admin (en el orgGroups.map(...)):
// Antes:
<div key={orgId} className="border border-border rounded-xl overflow-hidden">
// Después:
<div key={orgId} id={`org-section-${orgId}`} className="border border-border rounded-xl overflow-hidden">
Solo en el div raíz de cada org (el que tiene el key={orgId}). No tocar divs internos.
Lo que NO cambia en esta etapa
src/features/admin/OrganizationDetailPage.tsx— sin cambios; el detalle de InQuality ya funciona una vez que aparece en la lista.src/store/library-store.ts— sin cambios.- Cualquier otro archivo — sin cambios.
Criterios de validación
Fix 1 — InQuality visible
- La página
/admin/organizationsmuestra a InQuality en la lista - InQuality aparece primero (antes que las orgs clientes)
- InQuality tiene el badge naranja "InQuality"
- Al hacer click en "Ver detalle →" de InQuality, lleva a su detalle con procesos, usuarios y áreas
- Las otras orgs clientes siguen apareciendo igual que antes
Fix 2 — Back button con scroll
- Abrir un proceso de "Solar Banco" → workspace → botón "← Solar Banco" → navega a
/library?org=<orgId> - Al llegar a
/library, la página hace scroll automático al bloque de "Solar Banco" - El bloque de "Solar Banco" está expandido (todas las orgs empiezan expandidas por default)
- Si el proceso no tiene org (edge case), el back button va a
/librarysin param y no hay scroll
Técnicos
npm run buildsin errores TypeScriptnpm run testsin regresiones (596 tests)
Restricciones explícitas
- NO agregar
validateSearchaindexRoute(/) — solo alibraryRoute - NO modificar
OrganizationDetailPage.tsxen esta etapa - NO cambiar el comportamiento del botón "Nueva organización" — InQuality se crea con
is_provider: false, InQuality nunca se crea desde la UI (es dato de seeding) - NO agregar tests en esta etapa
Archivos autorizados a modificar
src/
├── router.tsx ← validateSearch en libraryRoute
├── features/
│ ├── workspace/
│ │ └── WorkspacePage.tsx ← handleBack con search: { org }
│ ├── library/
│ │ └── LibraryPage.tsx ← useLocation + scroll effect + id en org divs
│ └── admin/
│ └── OrganizationsPage.tsx ← quitar filtro is_provider + badge + título
Commits esperados
fix(admin): InQuality visible en OrganizationsPage con badge — quitar filtro is_provider
fix(workspace): handleBack pasa ?org=orgId para scroll contextual en biblioteca
fix(library): scroll a sección de org al volver del workspace con ?org param
Reporte esperado
- Confirmar que InQuality aparece en la lista de organizaciones con el badge
- Confirmar que el back button del workspace pasa
?org=en la URL - Confirmar que la biblioteca hace scroll al bloque correcto al recibir el param
npm run buildynpm run test(con conteo)- Solicitar validación visual del director en los dos fixes antes del OK formal