24 Commits

Author SHA1 Message Date
789e9a34ea feat(library): botón Importar BPMN visible para client_editor; empty state diferenciado
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 22:18:25 -03:00
cc5563974a fix(library): scroll a sección de org al volver del workspace con ?org param
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 21:57:29 -03:00
d6e7226842 feat(process-repo): extender fromRow/toRow/getById con areaId y areaName
- fromRow: extrae areaId y areaName (vía JOIN en getById)
- toRow y updateEditable: incluyen area_id en escrituras a Supabase
- getById: JOIN con areas!area_id(name) para obtener nombre en una query
- Fixtures de tests actualizados con areaId: null (campo requerido)
- 4 tests nuevos en tests/persistence/area-repo.test.ts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 18:18:14 -03:00
6fee592e40 Resumen del fix — Prioridad 2:
Some checks failed
/ Deploy to Cloudflare Pages (push) Has been cancelled
Cambios en WorkspacePage.tsx:

orgName ahora es string | null | undefined. undefined = query no completó aún, null = proceso sin org (o query sin resultado), string = nombre real.
Reemplazó .single() por .maybeSingle() — .single() lanzaba PGRST116 cuando el org no existía, data quedaba en null y setOrgName nunca se llamaba, dejando el estado indefinido.
setOrgName(data?.name ?? null) se llama siempre, sin condición if (data).
backLabel pasa de orgId ? (orgName ?? 'Volver') : 'Biblioteca' a simplemente orgName ?? 'Biblioteca' — el nombre real si cargó, o el fallback genérico en cualquier otro caso. Ya no depende de orgId para decidir el label.
handleBack() no cambió — sigue usando currentProcess?.orgId para construir el URL correcto a /library?org=xxx.
Listo para validación visual en workspace con un proceso de cliente que tenga org_id en la DB.
2026-07-07 14:53:39 -03:00
f3f55a6857 Resumen de los cambios del fix de navegación:
Some checks failed
/ Deploy to Cloudflare Pages (push) Has been cancelled
LibraryPage.tsx:778 — reemplazó useSearch({ from: '/' }) por useRouterState + URLSearchParams. Ahora funciona tanto desde / como desde /library.
WorkspacePage.tsx:206-209 — handleBack() navega a /library (con o sin ?org=uuid) en lugar de /.
workspace-back-button.test.ts — backTo actualizado a /library y /library?org=... en los 6 tests.
library-ui.test.tsx — mock de useSearch → useRouterState (devuelve { location: { search: '' } }).
2026-07-07 14:24:26 -03:00
ee356e5de6 Etapa 7 ya estaba completamente ejecutada en esta sesión. Todo está en su lugar:
Some checks failed
/ Deploy to Cloudflare Pages (push) Has been cancelled
Fix A1 — isProfileLoaded en AuthContext 

useState(false) inicial
setIsProfileLoaded(true) si onAuthStateChange devuelve null (no hay usuario que sincronizar)
setIsProfileLoaded(false) en SIGNED_IN (resetea para el nuevo sync)
setIsProfileLoaded(true) en el finally del sync effect (después de syncProfileFromDB, éxito o error)
Fix A2 — Guard en LibraryPage 

if (!user || !isProfileLoaded) devuelve skeleton animado antes de cualquier contenido rol-dependiente
Fix A3 — Campo "cliente" read-only para client_editor 

GlobalSettingsPanel.tsx: <p> estático en lugar de <Input> cuando isClientRole
WorkspacePage.tsx: topbar muestra el valor sin input ni lápiz para client_editor
Fix B — Migración 016 + aplicada a DB 

Archivo supabase/migrations/016_add_simulations_update_policy.sql creado
Policy simulations_update confirmada activa en DB (pg_policies devuelve las 4 policies de simulations)
Fix adicional — 403 en POST /processes  (ejecutado en el turno inmediato anterior)

updateEditable() en process-repo.ts usa .update() en lugar de .upsert() para todos los paths de edición
Tests: 586 verdes, build limpio.

Lo que falta según el checklist del prompt es la validación visual del director (msebem@gmail.com en la app real). Eso requiere tu OK después de verificar en el navegador.

domain/types.ts — orgId: string | null agregado a Process (campo de DB ya existente, ahora mapeado en el dominio).

process-repo.ts — fromRow incluye orgId: (row.org_id as string | null) ?? null. Ambas queries (getById y getAll) usan select('*'), por lo que el campo viene gratis.

WorkspacePage.tsx:

Import supabase para el fetch de org name
Estado orgName + useEffect que hace SELECT name FROM organizations WHERE id = orgId cuando cambia currentProcess?.orgId
Variable isClientRole (solo client_editor — client_viewer ya fue redirigido)
backLabel: "Procesos" para client_editor, nombre de org (o "Volver" mientras carga) si hay orgId, "Biblioteca" si no hay
handleBack(): navega a /?org=<orgId> para admin con proceso de cliente, a / para el resto
El Home button + Tooltip fueron reemplazados por <Button variant="ghost" size="sm"> con ArrowLeft + backLabel
LibraryPage.tsx:

Import useSearch
Lee orgParam = useSearch({ from: '/' }) as { org?: string }
useEffect que, cuando orgParam está presente y la carga terminó, busca el proceso con p.orgId === orgParam, obtiene su groupId, y hace scrollIntoView al elemento group-{groupId}
Los GroupCards en el grid están ahora envueltos en <div id="group-{group.id}"> para ser alcanzables por el scroll
Tests — workspace-back-button.test.ts con 6 casos: admin+orgName, admin+orgName loading, admin sin org, member sin org, client_editor, client_viewer. Fixtures actualizados en 13 archivos con orgId: null. Mock de useSearch agregado en library-ui.test.tsx.
2026-07-07 14:12:10 -03:00
9b89cb8669 La política simulations_update está activa en la DB de desarrollo. El fix B está completo.
Some checks failed
/ Deploy to Cloudflare Pages (push) Has been cancelled
Resumen de todo lo entregado en Etapa 7:

Fix A1 (src/auth/AuthContext.tsx): isProfileLoaded: boolean agregado como estado. Arranca en false, se pone true en el finally del sync effect (después de syncProfileFromDB), y cuando onAuthStateChange devuelve null (usuario no autenticado). Se resetea a false en cada SIGN_IN para gatear el re-sync.

Fix A2 (src/features/library/LibraryPage.tsx): Guard temprano después de todos los hooks — si !user || !isProfileLoaded devuelve un skeleton de tres columnas animado, idéntico al que usa HomeView. El contenido rol-dependiente solo se renderiza cuando el perfil está sincronizado.

Fix A3 (GlobalSettingsPanel.tsx + WorkspacePage.tsx): El campo "Cliente" es texto estático (<p>) para client_editor y client_viewer. En el topbar de WorkspacePage se oculta el input inline y el ícono de lápiz; si hay un clientName se muestra como texto inerte.

Fix B (supabase/migrations/016_add_simulations_update_policy.sql): Migración creada + aplicada vía Management API a inq-roi-desa. pg_policies confirma las 4 políticas: simulations_select, simulations_insert, simulations_update, simulations_delete.

Tests: tests/features/library/library-ui.test.tsx actualizado — mock incluye isProfileLoaded, 2 tests nuevos para isProfileLoaded: false. Total: 586 tests, todos verdes. Build limpio.
2026-07-07 12:27:36 -03:00
1f84997794 Qué se implementó:
Some checks failed
/ Deploy to Cloudflare Pages (push) Has been cancelled
src/features/auth/ConfirmInvitePage.tsx — nuevo componente con tres estados:

idle: pantalla de bienvenida + botón "Confirmar acceso" (estado inicial con params válidos)
loading: botón con spinner mientras se llama a verifyOtp
error: mensaje descriptivo + link a /login (token vacío/incorrecto en params, o fallo en verifyOtp)
El verifyOtp solo se llama al click del usuario — nunca al montar, por eso el pre-scan del email no consume el token.

src/router.tsx — ruta /auth/confirm agregada como pública (PUBLIC_PATHS) + confirmInviteRoute en el árbol de rutas.

tests/features/auth/confirm-invite.test.tsx — 6 tests cubriendo: no-call on mount, error con params vacíos, error con type distinto, llamada correcta a verifyOtp, navegación en éxito, y error con mensaje de detalle.

ResetPasswordPage no requiere cambios — updateUser({ password }) solo necesita sesión activa, que verifyOtp crea en el paso previo.
2026-07-07 11:30:21 -03:00
ef9ec472d5 Etapa 5 completa.
Some checks failed
/ Deploy to Cloudflare Pages (push) Has been cancelled
2026-07-07 10:32:02 -03:00
bc9f70419c Resumen de lo entregado
Some checks failed
/ Deploy to Cloudflare Pages (push) Has been cancelled
2026-07-07 09:47:31 -03:00
1e4dbc0bed feat(sprint-6/etapa-2): panel de análisis de sensibilidad + doble línea en CumulativeSavingsChart
Some checks failed
/ Deploy to Cloudflare Pages (push) Has been cancelled
2026-06-24 16:42:32 -03:00
bf200b72f9 Los cambios en reporte de eficiencia
Some checks failed
/ Deploy to Cloudflare Pages (push) Has been cancelled
2026-06-23 17:09:54 -03:00
a65bec45c3 feat(sprint-5/etapa-3): heatmap gradiente 4 stops + modo percentil + localStorage + fix zoom
- colors.ts: gradiente continuo verde→amarillo→naranja→rojo (interpolación N-stops genérica)
- Renombra modos: relative→percentile (rank-based), absolute→minmax
- Hook useHeatmapMode(): persiste selección en localStorage con clave inq-roi:heatmap-mode
- HeatmapLegend: barra de 4 stops, labels P0/P50/P100 en modo percentil
- HeatmapModeToggle: etiquetas "Percentil" / "Min-max"
- pdf-sections.ts: leyenda PDF con 4 stops interpolados
- BpmnCanvas: controles de zoom movidos a esquina inferior izquierda (evita logo bpmn.io)
- Tests: 528 unitarios pasan; E2E recalibrados con vecino más cercano (distancia euclidiana)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 18:28:10 -03:00
892b8b3bc7 Sprint 4 — Etapa 7 completada. Build limpio, 521/521 tests verdes. Resumen:
Some checks failed
/ Deploy to Cloudflare Pages (push) Has been cancelled
Tipos: Resource.processId? eliminado (vestigial); ResourceType ya tenía los 5 valores correctos, sin cambios.

resource-repo.ts: agregado getAllWithCreator() con JOIN a users!created_by (mismo patrón que Etapa 6) y tipo ResourceWithCreator. delete() ahora valida que no haya asignaciones activas en activity_resource_assignments antes de borrar — lanza error explicativo si las hay. Verifiqué ambos contra Supabase real: el JOIN funciona (FK reconocida por PostgREST) y el conteo de guarda también.

Nuevo — src/features/resources/:

resource-display.tsx — labels, colores/íconos por tipo (Lucide: User/Briefcase/Server/Wrench/Package) y ResourceTypeAvatar compartido
ResourceFormSheet.tsx — Sheet reutilizable para crear/editar, usado tanto en el catálogo como en el panel del workspace
ResourceCatalogPage.tsx — página /recursos: búsqueda, filtro por tipo, tabla con badge de tipo, costo, "creado por", editar/eliminar con confirmación
Nuevo — src/components/ui/sheet.tsx: componente shadcn Sheet (no existía), construido sobre @radix-ui/react-dialog que ya estaba instalado — no agregué dependencias nuevas.

ResourcesPanel.tsx rediseñado: ahora solo muestra recursos usados en el proceso actual (derivado de assignedResources), con conteo de actividades por recurso. CRUD completo se mudó a /recursos; el panel solo permite "Nuevo" (catálogo global) y tiene el link "Ver catálogo completo".

Router: ruta /recursos registrada.

ActivityPanel no se tocó, como indicaba el scope.
2026-06-19 00:01:46 -03:00
4f51c74717 auth con avatar
Some checks failed
/ Deploy to Cloudflare Pages (push) Has been cancelled
2026-06-18 22:31:22 -03:00
d426791a53 feat(sprint-4/etapa-5): modelo de privacidad RLS granular + UI de visibilidad
- SQL 010: tablas user_groups y group_memberships con RLS
- SQL 011: tabla process_access para acceso explícito a procesos
- SQL 012: función is_platform_admin() + políticas granulares (select/insert/update/delete) en processes
- types.ts: Process.ownerId (requerido)
- process-repo: fromRow mapea owner_id→ownerId; toRow preserva ownerId en updates; setShared() no-op documentado
- ImportPage: asigna ownerId: user.id al crear proceso
- LibraryPage: toggle Todos/Míos, OwnershipBadge (🔒/👤) en cada tarjeta, opción "Compartir con equipo" en menú de proceso propio con toast de confirmación
- Tests: ownerId: 'test-user' agregado a todos los fixtures de Process

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 22:37:37 -03:00
0a3c60cd09 Corrección 1 — Delete de proceso restaurado + Corrección 2 — Patrón userId en repos 2026-05-28 01:13:28 -03:00
f913fc26e2 fix: AppHeader usa <Link> para navegación SPA (no full reload); mock en tests de ReportPage
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 00:35:05 -03:00
67e259e6ed feat(sprint-3): biblioteca, recursos TO-BE, ROI mejorado e identidad de marca
Sprint 3 completo — 7 etapas:

- Etapa 1-2: biblioteca de procesos con agrupación por cliente (Dexie v4,
  LibraryPage, library-store, groupId/tags en Process)
- Etapa 3: modal de configuración global por proceso (horizonte, frecuencia,
  inversión, moneda) con validación y persistencia
- Etapa 4: panel de recursos rediseñado con soporte de unidades y minutos
  de utilización (utilizationMinutes + units)
- Etapa 5: recursos en escenario automatizado — motor de simulación,
  UI de edición en ActivityPanel, comparación AS-IS/TO-BE en reporte
- Etapa 6: PDF recursos automatizados, automatedAssignedResources requerido,
  561 tests pasando
- Etapa 7: identidad de marca — AppHeader gradiente naranja/magenta con logo
  InQuality en 5 páginas, favicon con logo real, logo/marca navegan a home

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 11:49:43 -03:00
3834c9f1c1 feat(sprint-1.5): PDFs Actual/Auto con identidad InQ + badge naranja (Etapas 8-9)
Etapa 8 — PDFs Actual y Automatizado:
- Página 1: header denso + metadata strip + grid 2×2 KPI cards con paleta
  #FEF3E2 (naranja claro InQ), sin gradiente (jerarquía visual preservada)
- Página 3: tabla con header naranja claro #FFF8ED + #92400E, em dash en
  filas no-automatizables, título ANÁLISIS DE COMPOSICIÓN uppercase
- Página 4: NOTA METODOLÓGICA uppercase + caja contenedora slate-50
- 526 tests verdes (+ 15 tests nuevos)

Etapa 9 — Badge  en BpmnCanvas:
- Reemplaza badge legacy (Bot SVG ámbar #fffbeb) por círculo naranja
  sólido #F59845 con símbolo  blanco, 14×14px, border white 1.5px
- Usa var(--inq-orange) definido en globals.css (sin hex hardcodeados)
- Elimina los 3 hex legacy del audit Etapa 1: #f59e0b, #fffbeb, #e2e8f0
- 5 tests E2E de lifecycle del badge + 2 screenshots

Política de iniciativa refinada post-Sprint 1.5 (CLAUDE.md actualizado)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 10:03:37 -03:00
7838c0cfa2 feat(sprint-1-5): cierre Etapa 2 - formatPayback + fixes de marca
- Helper formatPayback con 12 casos cubiertos
- Aplicado en RoiKpiBar, drawRoiKpiCards, csv-export
- Auditoría WCAG: ratio 2.22:1, opción D (font-bold) para Etapa 3
- Adelanto: strings 'InQ ROI' y 'Powered by InQuality' en footers PDF
- Threshold ROI: '1.000%' sin decimal
- 455 tests verdes + 2 E2E

Refs: sprints/sprint-1-5/ETAPA_2_PROMPT.md
2026-05-16 20:33:18 -03:00
eaaac88fc3 feat(sprint-1-5): cierre Etapa 2 - formatPayback + fixes de marca
[
2026-05-16 19:26:19 -03:00
8b71c6ee13 Antes de la última revisión de etapa 4. Sprint 1. Previo al ajuste de look n feel de app, reportes, nombre y colores. 2026-05-15 22:14:53 -03:00
bed161c92a feat: MVP Fase 0 — Process Cost Platform v0.1.0
Plataforma completa de análisis de costos operativos basada en BPMN 2.0.

Funcionalidades incluidas:
- Import de archivos BPMN 2.0 por drag & drop + 3 procesos de ejemplo
- Visualización read-only del diagrama (bpmn-js)
- Configuración de actividades (costo, tiempo, recursos asignados)
- CRUD de recursos (rol, persona, sistema, equipo, insumo)
- Configuración global (moneda, overhead %, nombre, cliente)
- Motor de simulación determinístico agregado con propagación de gateways XOR/AND
- Reporte visual con mapa de calor en dos modos (relativo/absoluto)
- Gráficos: KPIs, top actividades, composición, costo por recurso
- Export PDF (jsPDF + html2canvas) y CSV (papaparse)
- Persistencia en IndexedDB (Dexie)
- 268 tests unitarios/integración + 6 E2E con Playwright
- Deploy: https://process-cost-platform.pages.dev

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 01:58:40 -03:00