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.
10 KiB
CLAUDE.md — InQ ROI · Contexto de implementación
Leído automáticamente por Claude Code al arrancar desde este directorio. Contiene reglas de código, arquitectura, marca y convenciones de implementación. Para contexto estratégico y de colaboración ver
cowork/InQ ROI - Simulador de procesos/CLAUDE.md.
⚠️ Reglas de Marca — NO NEGOCIABLES
Estas reglas aplican a TODO string que aparezca en código, PDFs, CSVs, tests y documentación.
Ortografía de "InQ" — SIEMPRE I-n-Q
✅ InQ ← correcto
✅ InQuality ← correcto (nombre completo)
✅ InQ ROI ← nombre del producto
❌ INQ ❌ Inq ❌ inq ❌ inQ ← todos son bugs
Paleta protagonista
Primario: #F59845 (naranja InQ)
Gradiente: #F59845 → #ED3E8F (naranja → magenta InQ)
El gradiente se usa SOLO en KPI Hero y banners principales.
NO usar colores CONCILIA (#572FA2, #CB1889) en este producto.
Assets de logo
Logos disponibles en assets/:
MARCA FINAL 2023 AREA DE SEGURIDAD 1 COLOR.png— logo horizontal color (uso principal)MARCA FINAL 2023 AREA DE SEGURIDAD 1 BCO.png— logo horizontal blanco (sobre fondos oscuros)MARCA FINAL 2023 AREA DE SEGURIDAD 2 COLOR.png— versión con área de seguridad 2MARCA FINAL 2023 AREA DE SEGURIDAD 2 ISO COLOR LOGO BCO.png— isotipo + logo blanco
Usar el logo PNG en lugar de texto "InQ" / "InQuality" en headers de PDF y en la UI cuando corresponda.
Footer de PDFs
Tagline obligatorio: "Powered by InQuality" — en todos los footers de PDF, siempre.
Identidad del Proyecto
Nombre de trabajo: InQ ROI
Una frase: Plataforma web para que consultores de procesos cuantifiquen costos operativos y calculen ROI de automatización a partir de archivos BPMN 2.0.
Deploy: Cloudflare Pages — https://process-cost-platform.pages.dev
Estado: Sprint 2 en curso (ver sprints/sprint-2/BRIEF.md)
Stack Tecnológico (sin cambios hasta nuevo aviso)
| Capa | Tecnología |
|---|---|
| Lenguaje | TypeScript estricto |
| Framework | React 18 + Vite |
| Routing | TanStack Router |
| Estado | Zustand |
| Canvas BPMN | bpmn-js (Viewer, no Modeler) |
| UI | shadcn/ui sobre Tailwind CSS |
| Iconos | Lucide React |
| Gráficos | Apache ECharts (vía echarts-for-react) |
| Persistencia | IndexedDB vía Dexie (actualmente v2) |
| Validación | Zod |
| Export PDF | jsPDF + jsPDF-autotable + html2canvas |
| Export CSV | papaparse |
| Testing | Vitest + Playwright |
| Deploy | Cloudflare Pages |
No introduzcas librerías fuera de esta lista sin justificación explícita y consulta al director.
Arquitectura de Carpetas
src/
├── main.tsx
├── App.tsx
├── router.tsx
├── domain/ ← lógica de negocio pura, sin React, testeable
│ ├── types.ts ← tipos TypeScript del dominio
│ ├── simulation.ts ← motor de simulación (acepta scenario: 'actual' | 'automated')
│ ├── roi.ts ← cálculos puros de ROI
│ ├── bpmn-parser.ts
│ └── schemas.ts ← Zod schemas
├── store/ ← Zustand stores
├── persistence/ ← Dexie / IndexedDB
│ └── db.ts ← schema + migraciones (versión actual: v2)
├── features/ ← organizado por feature, no por tipo
│ ├── import/
│ ├── workspace/ ← ActivityPanel, ResourcesPanel, BpmnCanvas, WorkspacePage
│ ├── simulation/
│ └── report/ ← ReportPage, ActivitiesTable, CostByResourceChart, etc.
├── components/ui/ ← shadcn copy-pastes
├── lib/ ← utilidades transversales (format, colors, pdf, csv)
│ └── export/ ← pdf-sections.ts, pdf-sections-roi.ts, csv.ts
└── styles/
Reglas de dependencia (no negociables)
domain/NO importa de React, ni destore/, ni defeatures/. Es puro.features/puede importar dedomain/,store/,persistence/,lib/,components/.store/puede importar dedomain/ypersistence/. Nada más.
Estado del modelo de recursos (pre-Sprint 2)
El modelo de recursos está parcialmente implementado desde el MVP. Lo que existe:
Resourceentypes.ts:{ id, processId, name, type, costPerHour }ActivityResourceAssignmentembebido enActivity:{ resourceId, utilizationPercent: number (0-1) }- Tabla
resourcesen Dexie desde v1 ResourcesPanel.tsx: CRUD de recursos por procesoActivityPanel.tsx: asignación con slider de porcentaje- Motor de simulación: ya calcula
cost = costPerHour × (executionTimeMinutes/60) × utilizationPercent CostByResourceChart.tsx: gráfico en reporte
Sprint 2 mejora este modelo. No construye desde cero. Ver sprints/sprint-2/BRIEF.md para scope exacto.
Principios de Código
- TypeScript strict activado. Nada de
anysalvo en bordes con librerías sin tipos. - Dominio puro: funciones en
domain/son puras, testeables sin DOM. - Componentes tontos, hooks inteligentes: lógica en custom hooks, JSX limpio.
- Sin lógica de negocio en componentes.
- Convenciones de naming:
- Componentes:
PascalCase.tsx - Hooks:
useCamelCase.ts - Utilidades:
kebab-case.ts - Tipos:
PascalCase, interfaces con prefijoIPROHIBIDO
- Componentes:
- Comentarios en español, código (variables, funciones) en inglés.
- Errores con mensajes accionables para el usuario, nunca traces crudos en UI.
- Migraciones de schema: SIEMPRE incrementar versión en Dexie, escribir función de upgrade y test que valide preservación de datos.
Diferencial Visual
El reporte es el corazón del producto. Reglas:
- Tipografía: Inter para UI, JetBrains Mono para números/datos
- Paleta heatmap: verde
#10b981→ amarillo#f59e0b→ rojo#ef4444(saturados, calibrados en E2E) - Paleta ROI:
- Ahorro positivo: verde
#10b981 - Ahorro marginal: gris
#64748b - Ahorro negativo: rojo
#ef4444 - Advertencias de transparencia: amarillo
#f59e0b
- Ahorro positivo: verde
- Cifras de dinero: siempre con separadores y símbolo de moneda
- KPIs de ROI más grandes que KPIs de costos
- Export PDF igual o mejor que pantalla
- Densidad de información media-alta (audiencia: consultor profesional)
Definition of Done — Cada Feature
- Funciona en happy path
- Maneja datos vacíos sin crashear
- Maneja edge cases (división por cero, valores negativos, cero)
- Persiste en IndexedDB cuando corresponde (con migración si cambió schema)
- Estilo consistente con el resto de la app
- Si toca dominio: al menos un test en Vitest
- Si toca flujo end-to-end: cobertura en Playwright
Comandos del Proyecto
npm run dev # desarrollo
npm run build # producción
npm run test # tests unitarios con Vitest
npm run lint # eslint
npm run preview # previsualizar build de producción
npx playwright test # tests E2E (Playwright headless)
Verificación final antes de entregar artefactos
Antes de presentar cualquier PDF o pantalla:
- El header/footer dice "InQ ROI" (no "Process Cost Platform" ni otro)
- El footer incluye "Powered by InQuality"
- No hay caracteres rotos
- La marca "InQ" se escribe siempre I-n-Q
Gestión de versiones y commits
Aplica a partir de Sprint 8. Claude Code es responsable del ciclo completo de versionado.
Conventional Commits — obligatorio en todos los commits
feat(scope): descripción ← feature nueva visible al usuario
fix(scope): descripción ← corrección de bug
refactor(scope): descripción ← refactor sin cambio de comportamiento externo
test(scope): descripción ← solo tests, sin cambio de código de producción
docs(scope): descripción ← solo documentación
chore(scope): descripción ← tareas de infra, build, deps
scope es el área afectada: auth, workspace, library, report, pdf, admin, db, e2e, etc.
Ejemplos correctos:
feat(workspace): back button contextual con navegación a biblioteca del cliente
fix(process-repo): cambiar upsert por update para evitar 403 en client_editor
refactor(auth): agregar isProfileLoaded para evitar race condition de roles
CHANGELOG.md
Actualizar al cerrar cada etapa. Formato Keep a Changelog:
## [Unreleased]
### Added
- Back button contextual en workspace para admin
### Fixed
- 403 en Guardar configuración para client_editor (upsert → update)
Al cerrar un sprint, mover [Unreleased] a [vX.Y.Z] - YYYY-MM-DD.
SemVer — criterios
- MAJOR: cambio incompatible (nuevo modelo de datos, cambio de rol fundamental, migración forzada de usuarios)
- MINOR: feature nueva visible al usuario (nueva página, nueva funcionalidad en workspace)
- PATCH: corrección de bug o mejora de UX sin feature nueva
Sprint 7 = v0.7.x (MVP multi-tenant). Sprint 8 arranca en v0.8.0.
Secuencia de cierre de etapa
Al completar una etapa con cambios en producción:
git add -A
git commit -m "feat(scope): descripción concisa"
# Actualizar CHANGELOG.md
git add CHANGELOG.md
git commit -m "docs(changelog): etapa N sprint X"
git push origin main
Al cerrar un sprint completo, agregar además:
git tag -a vX.Y.Z -m "Release vX.Y.Z: descripción del sprint"
git push --tags
Versión visible en la app
La versión se lee de package.json (version field) y se muestra en el footer de todas las páginas (o al menos en /library y /report). Formato: InQ ROI v1.2.3.
Si no existe un componente de footer global, crearlo al implementar esta feature por primera vez.
Política de iniciativa proactiva
Antes de aplicar un cambio fuera del scope del prompt actual, evaluar en orden:
- ¿Resuelve un ítem listado en
TECH_DEBT.md,BACKLOG.mdo archivos de planificación? → Nivel 3 (consultar antes) - ¿Choca con restricción explícita del prompt ("NO entra", "NO tocar")? → Nivel 3
- ¿Modifica paginación, layout, estructura de salida, o contenido visible? → Nivel 2 mínimo (ejecutar y reportar)
- ¿Afecta marca, identidad, dependencias o configuración? → Nivel 3
- Si nada aplica → Nivel 1 (ejecutar libremente)
Argumentos NO válidos para evitar consulta:
- "Es mejora de legibilidad"
- "Es consistencia con etapa anterior"
- "Es trivial de revertir"
- "Resuelve un bug menor" (si está en TECH_DEBT.md, es Nivel 3)
Ver methodology/PATTERNS.md C.6 y methodology/ANTI_PATTERNS.md AP.8.