From ee356e5de688f2c714110c5128929be403319e4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Ben=C3=ADtez?= Date: Tue, 7 Jul 2026 14:12:10 -0300 Subject: [PATCH] =?UTF-8?q?Etapa=207=20ya=20estaba=20completamente=20ejecu?= =?UTF-8?q?tada=20en=20esta=20sesi=C3=B3n.=20Todo=20est=C3=A1=20en=20su=20?= =?UTF-8?q?lugar:?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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:

estático en lugar de 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= para admin con proceso de cliente, a / para el resto El Home button + Tooltip fueron reemplazados por - - Inicio - + diff --git a/src/persistence/supabase/process-repo.ts b/src/persistence/supabase/process-repo.ts index 9f5b887..0651eb7 100644 --- a/src/persistence/supabase/process-repo.ts +++ b/src/persistence/supabase/process-repo.ts @@ -39,6 +39,7 @@ function fromRow(row: Record): Process { updatedAt: new Date(row.updated_at as string).getTime(), ownerId: (row.owner_id as string) ?? '', updatedBy: (row.updated_by as string) ?? '', + orgId: (row.org_id as string | null) ?? null, } } diff --git a/tests/features/library/library-ui.test.tsx b/tests/features/library/library-ui.test.tsx index b050f7a..5ee9626 100644 --- a/tests/features/library/library-ui.test.tsx +++ b/tests/features/library/library-ui.test.tsx @@ -36,6 +36,7 @@ const { authUserRef, libStoreRef } = vi.hoisted(() => { clientName: null, overhead: 0, isShared: false, + orgId: null, } const sampleGroup = { id: 'grp-1', @@ -72,6 +73,7 @@ vi.mock('@/components/AppHeader', () => ({ AppHeader: () => null })) vi.mock('@tanstack/react-router', () => ({ useNavigate: () => vi.fn(), + useSearch: () => ({}), Link: ({ children }: { children: React.ReactNode }) => <>{children}, })) diff --git a/tests/features/report/components.test.tsx b/tests/features/report/components.test.tsx index 7f80dae..56f85a9 100644 --- a/tests/features/report/components.test.tsx +++ b/tests/features/report/components.test.tsx @@ -229,7 +229,7 @@ describe('MethodologyFooter', () => { id: 'p1', name: 'Proceso Test', clientName: '', bpmnXml: '', currency: 'USD', overheadPercentage: 0.2, annualFrequency: 1000, analysisHorizonYears: 3, automationInvestment: 0, - createdAt: 0, updatedAt: 0, groupId: null, tags: [], ownerId: 'test-user', updatedBy: 'test-user', + createdAt: 0, updatedAt: 0, groupId: null, tags: [], ownerId: 'test-user', updatedBy: 'test-user', orgId: null, } expect(() => render()).not.toThrow() }) @@ -239,7 +239,7 @@ describe('MethodologyFooter', () => { id: 'p1', name: 'Proc', clientName: '', bpmnXml: '', currency: 'USD', overheadPercentage: 0.25, annualFrequency: 1000, analysisHorizonYears: 3, automationInvestment: 0, - createdAt: 0, updatedAt: 0, groupId: null, tags: [], ownerId: 'test-user', updatedBy: 'test-user', + createdAt: 0, updatedAt: 0, groupId: null, tags: [], ownerId: 'test-user', updatedBy: 'test-user', orgId: null, } render() const text = document.body.textContent ?? '' @@ -251,7 +251,7 @@ describe('MethodologyFooter', () => { id: 'p1', name: 'Proc', clientName: '', bpmnXml: '', currency: 'USD', overheadPercentage: 0.2, annualFrequency: 1000, analysisHorizonYears: 3, automationInvestment: 0, - createdAt: 0, updatedAt: 0, groupId: null, tags: [], ownerId: 'test-user', updatedBy: 'test-user', + createdAt: 0, updatedAt: 0, groupId: null, tags: [], ownerId: 'test-user', updatedBy: 'test-user', orgId: null, } render() const text = document.body.textContent ?? '' @@ -342,7 +342,7 @@ describe('ReportPage — con datos completos', () => { updatedAt: Date.now() - 1000 * 60 * 30, groupId: null, tags: [], - ownerId: 'test-user', updatedBy: 'test-user', + ownerId: 'test-user', updatedBy: 'test-user', orgId: null, } const mockSimulation = { diff --git a/tests/features/report/roi-components.test.tsx b/tests/features/report/roi-components.test.tsx index db58132..ebeab38 100644 --- a/tests/features/report/roi-components.test.tsx +++ b/tests/features/report/roi-components.test.tsx @@ -600,7 +600,7 @@ const mockProcessBase = { currency: 'USD', overheadPercentage: 0.2, annualFrequency: 1200, analysisHorizonYears: 3, automationInvestment: 50_000, createdAt: Date.now() - 3600_000, updatedAt: Date.now() - 3600_000, - groupId: null, tags: [], ownerId: 'test-user', updatedBy: 'test-user', + groupId: null, tags: [], ownerId: 'test-user', updatedBy: 'test-user', orgId: null, } const mockSimulationBase = { diff --git a/tests/features/workspace/workspace-back-button.test.ts b/tests/features/workspace/workspace-back-button.test.ts new file mode 100644 index 0000000..4be2586 --- /dev/null +++ b/tests/features/workspace/workspace-back-button.test.ts @@ -0,0 +1,60 @@ +/** + * Tests del back button contextual en el workspace header. + * Verifica que backLabel y backTo se calculen correctamente según rol y orgId del proceso. + */ +import { describe, it, expect } from 'vitest' + +type PlatformRole = 'platform_admin' | 'member' | 'client_editor' | 'client_viewer' | 'guest' + +function resolveBackButton( + platformRole: PlatformRole | undefined, + orgId: string | null, + orgName: string | null, +): { backLabel: string; backTo: string } { + const isClientRole = platformRole === 'client_editor' || platformRole === 'client_viewer' + const backLabel = isClientRole + ? 'Procesos' + : orgId + ? (orgName ?? 'Volver') + : 'Biblioteca' + const backTo = orgId && !isClientRole ? `/?org=${orgId}` : '/' + return { backLabel, backTo } +} + +describe('workspace back button — lógica de backLabel y backTo', () => { + it('1. admin con proceso de cliente → muestra nombre de org y navega a /?org=uuid', () => { + const { backLabel, backTo } = resolveBackButton('platform_admin', 'org-uuid-123', 'Solar Banco') + expect(backLabel).toBe('Solar Banco') + expect(backTo).toBe('/?org=org-uuid-123') + }) + + it('2. admin con proceso de cliente mientras orgName carga → muestra "Volver" como fallback', () => { + const { backLabel, backTo } = resolveBackButton('platform_admin', 'org-uuid-123', null) + expect(backLabel).toBe('Volver') + expect(backTo).toBe('/?org=org-uuid-123') + }) + + it('3. admin sin org asignada → muestra "Biblioteca" y navega a /', () => { + const { backLabel, backTo } = resolveBackButton('platform_admin', null, null) + expect(backLabel).toBe('Biblioteca') + expect(backTo).toBe('/') + }) + + it('4. member sin org → igual que platform_admin', () => { + const { backLabel, backTo } = resolveBackButton('member', null, null) + expect(backLabel).toBe('Biblioteca') + expect(backTo).toBe('/') + }) + + it('5. client_editor con proceso de cliente → muestra "Procesos" y navega a /', () => { + const { backLabel, backTo } = resolveBackButton('client_editor', 'org-uuid-123', 'Solar Banco') + expect(backLabel).toBe('Procesos') + expect(backTo).toBe('/') + }) + + it('6. client_viewer con proceso de cliente → muestra "Procesos" y navega a /', () => { + const { backLabel, backTo } = resolveBackButton('client_viewer', 'org-uuid-123', 'Solar Banco') + expect(backLabel).toBe('Procesos') + expect(backTo).toBe('/') + }) +}) diff --git a/tests/lib/export/csv-export-roi.test.ts b/tests/lib/export/csv-export-roi.test.ts index 7c15e08..cb6c605 100644 --- a/tests/lib/export/csv-export-roi.test.ts +++ b/tests/lib/export/csv-export-roi.test.ts @@ -14,7 +14,7 @@ function makeProcess(extras: Partial = {}): Process { id: 'p1', name: 'Proceso de Crédito', clientName: 'Banco XYZ', bpmnXml: '', currency: 'USD', overheadPercentage: 0.2, annualFrequency: 1200, analysisHorizonYears: 3, automationInvestment: 50_000, - createdAt: 0, updatedAt: 0, groupId: null, tags: [], ownerId: 'test-user', updatedBy: 'test-user', ...extras, + createdAt: 0, updatedAt: 0, groupId: null, tags: [], ownerId: 'test-user', updatedBy: 'test-user', orgId: null, ...extras, } } diff --git a/tests/lib/export/csv-export.test.ts b/tests/lib/export/csv-export.test.ts index 42f61db..4f67ea2 100644 --- a/tests/lib/export/csv-export.test.ts +++ b/tests/lib/export/csv-export.test.ts @@ -9,7 +9,7 @@ function makeProcess(currency: string, extras: Partial = {}): Process { id: 'p1', name: 'Proceso de Ventas', clientName: 'Empresa ABC', bpmnXml: '', currency, overheadPercentage: 0.2, annualFrequency: 1000, analysisHorizonYears: 3, automationInvestment: 0, - createdAt: 0, updatedAt: 0, groupId: null, tags: [], ownerId: 'test-user', updatedBy: 'test-user', ...extras, + createdAt: 0, updatedAt: 0, groupId: null, tags: [], ownerId: 'test-user', updatedBy: 'test-user', orgId: null, ...extras, } } diff --git a/tests/lib/export/measure-sizes.test.ts b/tests/lib/export/measure-sizes.test.ts index 19c161d..cbb92c7 100644 --- a/tests/lib/export/measure-sizes.test.ts +++ b/tests/lib/export/measure-sizes.test.ts @@ -66,7 +66,7 @@ describe('Medición de tamaños de CSV — 3 sample BPMNs', () => { bpmnXml: xml, currency: 'USD', overheadPercentage: 0.2, annualFrequency: 1000, analysisHorizonYears: 3, automationInvestment: 0, createdAt: 0, updatedAt: 0, - groupId: null, tags: [], ownerId: 'test-user', updatedBy: 'test-user', + groupId: null, tags: [], ownerId: 'test-user', updatedBy: 'test-user', orgId: null, } const simulation: Simulation = { diff --git a/tests/lib/export/pdf-export-roi.test.ts b/tests/lib/export/pdf-export-roi.test.ts index d6e1eb6..b70e205 100644 --- a/tests/lib/export/pdf-export-roi.test.ts +++ b/tests/lib/export/pdf-export-roi.test.ts @@ -51,7 +51,7 @@ const mockProcess: Process = { bpmnXml: '', currency: 'USD', overheadPercentage: 0.2, annualFrequency: 1200, analysisHorizonYears: 3, automationInvestment: 50_000, createdAt: 0, updatedAt: 0, - groupId: null, tags: [], ownerId: 'test-user', updatedBy: 'test-user', + groupId: null, tags: [], ownerId: 'test-user', updatedBy: 'test-user', orgId: null, } const perActivityActual = [ diff --git a/tests/lib/export/pdf-export-sprint2.test.ts b/tests/lib/export/pdf-export-sprint2.test.ts index 937229b..8bef4a9 100644 --- a/tests/lib/export/pdf-export-sprint2.test.ts +++ b/tests/lib/export/pdf-export-sprint2.test.ts @@ -43,7 +43,7 @@ const mockProcess: Process = { bpmnXml: '', currency: 'USD', overheadPercentage: 0.2, annualFrequency: 1000, analysisHorizonYears: 3, automationInvestment: 0, createdAt: 0, updatedAt: 0, - groupId: null, tags: [], ownerId: 'test-user', updatedBy: 'test-user', + groupId: null, tags: [], ownerId: 'test-user', updatedBy: 'test-user', orgId: null, } // Simulación SIN recursos en ninguna actividad diff --git a/tests/lib/export/pdf-export.test.ts b/tests/lib/export/pdf-export.test.ts index fd77fd6..1894740 100644 --- a/tests/lib/export/pdf-export.test.ts +++ b/tests/lib/export/pdf-export.test.ts @@ -54,7 +54,7 @@ const mockProcess: Process = { overheadPercentage: 0.2, annualFrequency: 1000, analysisHorizonYears: 3, automationInvestment: 0, createdAt: 0, updatedAt: 0, - groupId: null, tags: [], ownerId: 'test-user', updatedBy: 'test-user', + groupId: null, tags: [], ownerId: 'test-user', updatedBy: 'test-user', orgId: null, } const mockSimulation: Simulation = { diff --git a/tests/lib/export/pdf-orientation.test.ts b/tests/lib/export/pdf-orientation.test.ts index 8fe50ae..6ea5cfb 100644 --- a/tests/lib/export/pdf-orientation.test.ts +++ b/tests/lib/export/pdf-orientation.test.ts @@ -59,7 +59,7 @@ const mockProcess: Process = { overheadPercentage: 0.2, annualFrequency: 1000, analysisHorizonYears: 3, automationInvestment: 50000, createdAt: 0, updatedAt: 0, - groupId: null, tags: [], ownerId: 'test-user', updatedBy: 'test-user', + groupId: null, tags: [], ownerId: 'test-user', updatedBy: 'test-user', orgId: null, } const mockSimulation: Simulation = { diff --git a/tests/lib/export/pdf-roi-page1.test.ts b/tests/lib/export/pdf-roi-page1.test.ts index 5bb9727..a6817c4 100644 --- a/tests/lib/export/pdf-roi-page1.test.ts +++ b/tests/lib/export/pdf-roi-page1.test.ts @@ -56,7 +56,7 @@ const mockProcess: Process = { overheadPercentage: 0.2, annualFrequency: 5000, analysisHorizonYears: 3, automationInvestment: 80000, createdAt: 0, updatedAt: 0, - groupId: null, tags: [], ownerId: 'test-user', updatedBy: 'test-user', + groupId: null, tags: [], ownerId: 'test-user', updatedBy: 'test-user', orgId: null, } const mockSimulation: Simulation = { diff --git a/tests/lib/export/pdf-roi-pages2-5.test.ts b/tests/lib/export/pdf-roi-pages2-5.test.ts index 42372fb..ae6189f 100644 --- a/tests/lib/export/pdf-roi-pages2-5.test.ts +++ b/tests/lib/export/pdf-roi-pages2-5.test.ts @@ -52,7 +52,7 @@ const mockProcess: Process = { bpmnXml: '', currency: 'USD', overheadPercentage: 0.2, annualFrequency: 5000, analysisHorizonYears: 3, automationInvestment: 80000, createdAt: 0, updatedAt: 0, - groupId: null, tags: [], ownerId: 'test-user', updatedBy: 'test-user', + groupId: null, tags: [], ownerId: 'test-user', updatedBy: 'test-user', orgId: null, } const mockRoi: RoiResult = { diff --git a/tests/lib/export/pdf-scenario-page1.test.ts b/tests/lib/export/pdf-scenario-page1.test.ts index 1d635be..b940ac0 100644 --- a/tests/lib/export/pdf-scenario-page1.test.ts +++ b/tests/lib/export/pdf-scenario-page1.test.ts @@ -52,7 +52,7 @@ const mockProcess: Process = { bpmnXml: '', currency: 'USD', overheadPercentage: 0.2, annualFrequency: 5000, analysisHorizonYears: 3, automationInvestment: 80000, createdAt: 0, updatedAt: 0, - groupId: null, tags: [], ownerId: 'test-user', updatedBy: 'test-user', + groupId: null, tags: [], ownerId: 'test-user', updatedBy: 'test-user', orgId: null, } const mockSimulation: Simulation = { diff --git a/tests/store/simulation-store-invalidation.test.ts b/tests/store/simulation-store-invalidation.test.ts index 58b2493..1e240b0 100644 --- a/tests/store/simulation-store-invalidation.test.ts +++ b/tests/store/simulation-store-invalidation.test.ts @@ -94,7 +94,7 @@ describe('simulation store — invalidación por cambio en process store', () => currency: 'USD', overheadPercentage: 0.2, annualFrequency: 500, analysisHorizonYears: 2, automationInvestment: 10_000, createdAt: Date.now(), updatedAt: Date.now(), - groupId: null, tags: [], ownerId: 'test-user', updatedBy: 'test-user', + groupId: null, tags: [], ownerId: 'test-user', updatedBy: 'test-user', orgId: null, } useProcessStore.setState({ currentProcess: proc })