Files
2026-07-07 17:39:36 -03:00

26 KiB

Sprint 8 BRIEF — InQ ROI

Estado: borrador pendiente aprobación del director Abierto: 2026-07-07 Sprint anterior: Sprint 7 — multi-tenancy (organizations, 4 roles, Edge Functions, Admin UI) Versión base: v0.8.0 (según SemVer: Sprint 8 arranca en v0.8.0)


Contexto y dirección estratégica

Sprint 7 entregó la fundación de multi-tenancy. El producto puede invitar clientes, asignarles roles, y que vean solo sus procesos. Ahora hay tres gaps que bloquean el uso real con clientes:

  1. El modelo de "cliente" en la UI es inconsistenteprocess_groups (agrupador visual), clientName (texto libre), y org_id (FK real) son tres conceptos que intentan modelar lo mismo sin coordinación. Un admin que invita a Solar Banco no puede ver sus procesos agrupados como "Solar Banco" en la biblioteca.

  2. client_editor no puede subir su propio BPMN — si Solar Banco tiene un proceso que quiere modelar, necesita pedirle a InQuality que lo importe. Esto no escala.

  3. No hay versión visible ni changelog — el producto está en producción sin que nadie sepa qué versión está corriendo ni qué cambió.

Objetivo del sprint: consolidar el modelo de cliente, habilitar autonomía de client_editor para BPMN, y establecer el ciclo profesional de versiones. Sprint relativamente arquitectónico — ~40% DB/repos, ~40% UI/features, ~20% infra/deuda.


Prioridades del sprint

Bloque A — Comprometidas (must have)

# Feature Tipo
Etapa 0 Versionado: Conventional Commits + CHANGELOG + footer versión Infra
Etapa 1 DB: tabla areas, area_id en processes, drop process_groups y clientName DB/migración
Etapa 2 Repositorios: area-repo.ts, actualizar process-repo.ts, tipos y stores Backend
Etapa 3 LibraryPage: agrupar por org → área + ocultar Settings para roles cliente UI
Etapa 4 Workspace sidebar: selector de área reemplaza clientName + Admin CRUD de áreas UI + Admin
Etapa 5 client_editor BPMN: política INSERT + UPDATE bpmn_xml, botón importar visible DB/UI

Bloque B — Si hay tiempo (prioridad 2)

# Feature Tipo
Etapa 6 Deuda técnica: guards → beforeLoad + hard delete + email en admin Refactor
Etapa 7 Página /help con documentación de uso para todos los roles UI

Nota del director al Bloque B: si las Etapas 0-5 consumen el sprint, Etapas 6-7 pasan a BACKLOG. No forzar a sprint siguiente sin capacidad real.


Reglas del sprint (no negociables)

  • simulador-web/CLAUDE.md tiene la guía completa de stack, reglas de marca, y la nueva sección de versionado (agregada como Etapa 0)
  • Validación visual mandatoria en cada etapa que modifique UI antes del OK formal
  • Brief como contrato: ninguna etapa arranca sin ETAPA_X_PROMPT.md aprobado
  • git diff antes de reportar: incluir en cada reporte la lista de archivos modificados vs. los autorizados
  • Iniciativa agéntica clasificada — Nivel 3 (marca, arquitectura, datos, TECH_DEBT.md) requiere consulta antes de ejecutar
  • isProfileLoaded como prerequisito en cualquier componente nuevo condicionado por rol (aprendizaje Sprint 7)
  • JOIN vs fetch separado — cuando un componente necesita datos relacionales para display, preferir JOIN en repo (aprendizaje Sprint 7)

Etapa 0: Versionado profesional

Objetivo: establecer el ciclo de versiones antes de abrir cualquier feature nueva.

Alcance

1. Actualizar simulador-web/CLAUDE.md — agregar sección "Gestión de versiones y commits" con:

Conventional Commits:
  feat(scope): descripción
  fix(scope): descripción
  refactor(scope): descripción
  test(scope): descripción
  docs(scope): descripción
  chore(scope): descripción

scopes comunes: auth, workspace, library, report, pdf, admin, db, e2e, help

SemVer:
  MAJOR: cambio incompatible (nuevo modelo de datos, cambio de rol fundamental)
  MINOR: feature nueva visible al usuario
  PATCH: fix de bug o mejora de UX sin feature nueva

Sprint 8 arranca en v0.8.0

2. Crear CHANGELOG.md en raíz de simulador-web/, formato Keep a Changelog:

# Changelog

All notable changes to InQ ROI will be documented in this file.

## [Unreleased]

## [0.7.0] - 2026-07-07
### Added
- Multi-tenancy: tabla organizations, 4 roles de plataforma
- Edge Functions invite-user y revoke-user
- Panel de administración /admin (Organizations, Users)
- Login email/password + página /reset-password
- back button contextual en workspace (orgName via JOIN en getById)
- Política simulations_update (fix para client_editor)

### Fixed
- Race condition AuthContext: isProfileLoaded guard
- syncProfileFromDB leyendo platform_role desde DB (no inferido por email)
- normalizeGroupLabel: limpia artículos iniciales de labels de grupos

## [0.6.0] - 2026-06-24
...

Completar el historial con los sprints anteriores hasta v0.1.0 (MVP).

3. Actualizar package.json"version": "0.8.0" (actualmente tiene algún valor anterior, verificar).

4. Footer con versión visible — crear src/components/AppFooter.tsx:

  • Muestra InQ ROI v{version} leyendo de src/lib/version.ts (que importa de package.json)
  • Visible en todas las páginas (LibraryPage, ReportPage, al menos)
  • Texto pequeño, discreto, esquina inferior

5. Tag de cierre de sprint: al finalizar Sprint 8, git tag -a v0.8.0 -m "Release v0.8.0: Sprint 8".

Criterios de validación

  • CHANGELOG.md existe con historial desde v0.1.0 hasta v0.7.0
  • package.json tiene "version": "0.8.0"
  • Footer visible en LibraryPage mostrando "InQ ROI v0.8.0"
  • Build sin errores

NO entra

  • Página de "qué hay de nuevo" o modal de release notes
  • Versionado de procesos (FASE-2)

Etapa 1: Migración DB — tabla areas

Objetivo: crear la infraestructura de datos para el modelo organizations → areas → processes.

Contexto

Los tres conceptos de "cliente" actuales:

Concepto Tabla/Campo Problema
Agrupador visual process_groups Nombre confuso, sin FK a organizations
Campo libre clientName (text en processes) Sin normalización, sin validación
Tenant real org_id en processes Correcto pero desconectado de la UI

Alcance — Migración 017

-- 1. Crear tabla areas
CREATE TABLE public.areas (
  id uuid DEFAULT gen_random_uuid() PRIMARY KEY,
  org_id uuid REFERENCES public.organizations(id) ON DELETE CASCADE NOT NULL,
  name text NOT NULL,
  description text,
  created_at timestamptz DEFAULT now() NOT NULL,
  updated_at timestamptz DEFAULT now() NOT NULL
);

-- 2. Agregar area_id en processes (reemplaza group_id)
ALTER TABLE public.processes ADD COLUMN area_id uuid REFERENCES public.areas(id) ON DELETE SET NULL;

-- 3. Eliminar client_name (texto libre) de processes
--    Verificar que clientName existe con su nombre exacto en la tabla
ALTER TABLE public.processes DROP COLUMN IF EXISTS client_name;
-- Nota: verificar nombre exacto antes de ejecutar (puede ser "client_name" o "clientName" en postgres)

-- 4. RLS para areas
ALTER TABLE public.areas ENABLE ROW LEVEL SECURITY;

CREATE POLICY "areas_select" ON public.areas
  FOR SELECT USING (
    -- InQuality ve todas; otros ven solo las de su org
    public.is_inquality()
    OR org_id = public.current_org()
  );

CREATE POLICY "areas_insert" ON public.areas
  FOR INSERT WITH CHECK (
    -- Solo platform_admin puede crear áreas
    public.current_platform_role() = 'platform_admin'
  );

CREATE POLICY "areas_update" ON public.areas
  FOR UPDATE USING (
    public.current_platform_role() = 'platform_admin'
  );

CREATE POLICY "areas_delete" ON public.areas
  FOR DELETE USING (
    public.current_platform_role() = 'platform_admin'
  );

-- 5. Eliminar process_groups si existe
--    Primero verificar que processes.group_id existe y hacer SET NULL antes de drop
UPDATE public.processes SET area_id = NULL WHERE area_id IS NULL; -- no-op, sanity check
-- Si process_groups existe:
-- ALTER TABLE public.processes DROP COLUMN IF EXISTS group_id;
-- DROP TABLE IF EXISTS public.areas_old; -- nombre provisional
-- DROP TABLE IF EXISTS public.process_groups;

-- 6. Migración de datos: bulk-assign procesos sin org a InQuality
-- UPDATE public.processes SET org_id = '<inq_uuid>' WHERE org_id IS NULL;
-- (ejecutar con el UUID real de InQuality — buscar en SELECT id FROM organizations WHERE name = 'InQuality')

Precaución importante: antes de escribir los DROP/ALTER, auditar el schema real con:

SELECT column_name, data_type FROM information_schema.columns
WHERE table_name = 'processes' AND table_schema = 'public';

SELECT table_name FROM information_schema.tables
WHERE table_schema = 'public' AND table_name LIKE '%group%';

Reportar nombres exactos antes de ejecutar cualquier DROP.

Criterios de validación

  • Tabla areas creada con RLS activa
  • area_id en processes nullable con FK
  • client_name (o equivalente) eliminado de processes
  • process_groups eliminada (verificar si existe)
  • group_id en processes eliminado (si existía)
  • Bulk-assign: todos los procesos tienen org_id NOT NULL (o decision explícita sobre los NULL)
  • Tests unitarios de RLS: platform_admin puede CRUD en areas, client_editor no puede

NO entra

  • CRUD en la UI (eso es Etapa 4)
  • Cambios en los repositorios de TypeScript (eso es Etapa 2)

Etapa 2: Repositorios y tipos

Objetivo: crear area-repo.ts y actualizar process-repo.ts y los tipos de dominio para reflejar el nuevo modelo.

Alcance

1. Actualizar src/domain/types.ts:

// Nuevo tipo
export interface Area {
  id: string
  orgId: string
  name: string
  description: string | null
  createdAt: string
}

// Actualizar Process — eliminar clientName, agregar areaId y areaName
export interface Process {
  // ... campos existentes ...
  orgId: string | null
  orgName?: string | null      // poblado via JOIN, no se persiste
  areaId: string | null        // ← nuevo, reemplaza groupId
  areaName?: string | null     // ← nuevo, poblado via JOIN
  // eliminar: clientName, groupId (si existían)
}

2. Crear src/persistence/supabase/area-repo.ts:

export interface AreaRepo {
  getByOrg(orgId: string): Promise<Area[]>
  create(data: { orgId: string; name: string; description?: string }): Promise<Area>
  update(id: string, data: { name?: string; description?: string }): Promise<Area>
  delete(id: string): Promise<void>
}

3. Actualizar src/persistence/supabase/process-repo.ts:

  • fromRow(): extraer areaId y areaName (via JOIN cuando el query lo incluya)
  • getById(): JOIN con areas para traer areaName embebido (patrón ya establecido con orgName)
  • getAll() / getByOrg(): incluir area_id en select, agregar optional JOIN para areaName
  • create(): incluir area_id en el objeto de insert (si está presente)
  • updateEditable(): incluir area_id en los campos actualizables
  • Eliminar cualquier referencia a clientName o groupId

4. Actualizar Zustand store (src/store/process-store.ts o equivalente):

  • Si el store tenía lógica de clientName, actualizarla
  • Verificar que el tipo Process del store usa los nuevos campos

5. Actualizar src/persistence/supabase/settings-repo.ts (si mantenía process_groups):

  • Identificar si hay lógica ligada a process_groups y eliminarla o migrarla

Criterios de validación

  • Area type definido en domain/types.ts
  • Process.areaId y Process.areaName presentes; clientName/groupId eliminados
  • area-repo.ts con CRUD completo
  • process-repo.ts actualizado: fromRow, getById (con JOIN), create, updateEditable
  • Build TypeScript sin errores
  • Tests unitarios de area-repo.ts

NO entra

  • Cambios en componentes React (Etapas 3 y 4)
  • Settings page todavía puede romper — se verá en Etapa 3

Etapa 3: LibraryPage — agrupación org → área

Objetivo: reemplazar la agrupación actual por clientName con agrupación real por org → área.

Contexto de la UI actual

LibraryPage muestra procesos agrupados por clientName (texto libre). Hay un panel de "Clientes" (renderizado desde process_groups). Para roles cliente, muestra lista plana. El ícono de Settings (⚙) lleva a la página de configuración de grupos — visible para todos los roles.

Alcance

1. Lógica de agrupación:

  • Para platform_admin (InQuality): agrupar por orgName → dentro de cada org, sub-agrupar por areaName
  • Para client_editor / client_viewer: mostrar lista plana de sus procesos (sin agrupación — la org es implícita)
  • Grupos sin área asignada: mostrar en sección "Sin área" dentro de la org correspondiente

2. Estructura visual (admin view):

┌─ Solar Banco ───────────────────────────────────────────┐
│  ┌─ Área: Operaciones ──────────────────────────────┐   │
│  │  [proceso A]  [proceso B]                        │   │
│  └──────────────────────────────────────────────────┘   │
│  ┌─ Área: Recursos Humanos ─────────────────────────┐   │
│  │  [proceso C]                                      │   │
│  └──────────────────────────────────────────────────┘   │
│  ┌─ Sin área ───────────────────────────────────────┐   │
│  │  [proceso D]                                      │   │
│  └──────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────┘
┌─ InQuality ─────────────────────────────────────────────┐
│  ...                                                     │
└─────────────────────────────────────────────────────────┘

3. Eliminar el panel de "Clientes" (process_groups):

  • El componente o sección que renderizaba grupos desde process_groups se elimina
  • Verificar qué componentes lo referencian

4. Ocultar Settings para roles cliente:

  • El ícono ⚙ en LibraryPage se oculta para client_editor y client_viewer
  • Agregar guard en la ruta /settings (o el path real): client_editor y client_viewer redirigen a /library
  • Verificar si /recursos también debe ocultarse para clientes (reportar, no decidir unilateralmente)

5. Criterio de wow: al colapsar/expandir grupos, usar animación suave (Tailwind transition-all). Los grupos de org colapsan con click en el header.

Criterios de validación

  • Admin ve procesos agrupados por org → área (validación visual del director)
  • client_editor ve lista plana de sus procesos (sin panel de grupos)
  • ⚙ Settings no visible para client_editor y client_viewer
  • Ruta /settings tiene guard de rol
  • Animación de colapso funciona
  • Build sin errores, tests existentes pasan

NO entra

  • CRUD de áreas (Etapa 4)
  • Workspace sidebar (Etapa 4)

Etapa 4: Workspace sidebar + Admin CRUD de áreas

Objetivo: (a) reemplazar el campo clientName libre en el workspace por un selector de área normalizado; (b) habilitar a admin para crear/editar/eliminar áreas desde /admin.

4A — Workspace sidebar

Campo "Área" en el panel de configuración del proceso:

  • Reemplaza el campo de texto libre "Cliente" (clientName)
  • Para platform_admin: dropdown de todas las áreas de la org del proceso (si tiene org asignada) + opción "Sin área". Si el proceso no tiene org, mostrar selector de org primero.
  • Para client_editor: dropdown de áreas de su propia org (su org es implícita — no puede cambiarla)
  • Para client_viewer: read-only, muestra nombre del área si existe
  • Al cambiar área, actualizar processStore y hacer persist en Supabase vía updateEditable()

Campo "Organización" en el workspace:

  • Para platform_admin: selector de org (dropdown de todas las orgs) — permite reasignar un proceso a otra org
  • Para client_editor: read-only, muestra el nombre de su org
  • Actualizar org_id en Supabase cuando el admin cambia la org

Eliminar campo "Cliente" (clientName):

  • El campo de texto libre que existía en el sidebar se elimina completamente

4B — Admin CRUD de áreas

En /admin/organizations/[orgId] (o en la página de detalle de org que exista):

  • Agregar sección "Áreas" con lista de áreas de la org
  • CRUD: crear área (nombre + descripción opcional), renombrar, eliminar
  • Al eliminar: si hay procesos con esa área, mostrar confirmación "X procesos quedarán sin área asignada"
  • RLS garantiza que solo platform_admin puede hacer CRUD

Estructura sugerida:

/admin
  /organizations
    → lista de orgs (ya existe)
    /[orgId]
      → detalle de org: nombre, miembros, ÁREAS (nuevo)

Si /admin/organizations/[orgId] no existe, crear la página. Si ya existe (verificar), agregar la sección de Áreas.

Criterios de validación

  • Campo "Cliente" (texto libre) eliminado del workspace sidebar
  • Selector de área funciona para platform_admin y client_editor (validación visual)
  • Cambio de área persiste en Supabase (verificar Network tab)
  • CRUD de áreas en admin funciona (crear, editar, eliminar con confirmación)
  • Tests E2E: admin crea área, asigna proceso, client_editor ve sus procesos con área correcta

NO entra

  • Jerarquía de áreas (sub-áreas) — FASE-2
  • Migrar todos los procesos existentes a áreas manualmente — eso se hace en bulk en la migración DB

Etapa 5: client_editor — permisos de BPMN

Objetivo: habilitar a client_editor para importar nuevos procesos y actualizar el BPMN de procesos existentes de su org.

Contexto

En Sprint 7 Etapa 5, client_editor fue bloqueado explícitamente de:

  • Ver el botón "Importar BPMN" (redireccionado desde /import)
  • Actualizar bpmn_xml vía updateEditable() (el campo no estaba en la lista de campos editables para ese rol)

Esta decisión fue provisional. Sprint 8 la revierte con las restricciones correctas.

Alcance

1. Migración 018 — política INSERT en processes para client_editor:

-- Ampliar la política de INSERT para incluir client_editor
-- (auditar el nombre real de la política INSERT actual antes de DROP)
DROP POLICY IF EXISTS "processes_insert" ON public.processes;

CREATE POLICY "processes_insert" ON public.processes
  FOR INSERT WITH CHECK (
    -- platform_admin y member pueden insertar sin restricción de org
    public.current_platform_role() IN ('platform_admin', 'member')
    OR (
      -- client_editor puede crear procesos solo en su propia org
      public.current_platform_role() = 'client_editor'
      AND org_id = public.current_org()
    )
  );

2. Actualizar updateEditable() en process-repo.ts:

  • Incluir bpmn_xml en los campos actualizables cuando el caller tiene permisos
  • Verificar que la política UPDATE existente ya permite que client_editor actualice bpmn_xml (debería estar cubierta por la política general de UPDATE que verifica org_id)
  • Si no, agregar rama en la política UPDATE

3. Botón "Importar BPMN" visible para client_editor:

  • En ImportPage.tsx: eliminar el guard que redirigía a client_editor
  • client_editor accede a /import, selecciona BPMN, el proceso se crea con org_id = current_org()
  • El owner_id se asigna como auth.uid() (el client_editor que importó)

4. Restricciones que NO cambian:

  • client_editor no puede mover procesos entre orgs (no puede cambiar org_id a otra org)
  • client_editor no puede cambiar owner_id
  • client_editor no puede ver ni modificar procesos de otra org

Criterios de validación

  • Validar con el director usando cambio temporal de rol en Supabase Studio (técnica de Sprint 7)
  • client_editor puede importar un BPMN nuevo → proceso creado con org_id correcto (verificar en Supabase Studio)
  • client_editor puede abrir un proceso de su org y actualizar el BPMN (re-import)
  • client_editor NO puede crear procesos con org_id de otra org (test E2E adversarial)
  • Tests E2E: flujo completo import → workspace → update bpmn como client_editor

NO entra

  • client_editor con permisos en resources o en admin — no cambia
  • Eliminar procesos (sigue siendo solo platform_admin)

Etapa 6: Deuda técnica consolidada

Objetivo: resolver items de TECH_DEBT.md acumulados de Sprint 7. Bloque B — solo si hay tiempo.

6A — Guards de rol → beforeLoad (3 componentes)

Migrar de useEffect a beforeLoad de TanStack Router en:

  • AdminLayout.tsx
  • WorkspacePage.tsx
  • ImportPage.tsx

Ejemplo de patrón correcto:

// En el router (router.tsx o route file)
export const adminRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: '/admin',
  beforeLoad: ({ context }) => {
    if (!context.auth.isProfileLoaded) return // esperar
    if (context.auth.user?.platformRole !== 'platform_admin') {
      throw redirect({ to: '/library' })
    }
  },
})

Restricción: no cambiar el comportamiento de los guards, solo el mecanismo. Los tests E2E existentes deben seguir pasando.

6B — Hard delete de usuarios

Crear Edge Function delete-user:

  • Llama a Supabase Auth Admin API: supabase.auth.admin.deleteUser(userId)
  • Elimina registro de public.users
  • Solo accesible para platform_admin
  • Agregar botón "Eliminar permanentemente" en Admin → Users (con AlertDialog de confirmación doble: "Esta acción no puede deshacerse. El usuario perderá acceso permanentemente.")
  • Diferencia respecto al soft delete (revoke-user): hard delete borra el auth record

6C — Email visible en Admin → Users

La tabla de usuarios en admin no muestra el email (que está en auth.users, no en public.users). Fix:

  • En la Edge Function de listado de usuarios (o crear una nueva list-users), hacer join con auth.users via service_role
  • Mostrar email en la tabla de Admin → Users

Criterios de validación

  • Guards migrados: no hay flash visual al navegar a ruta protegida (verificar con conexión lenta)
  • Hard delete funciona y el usuario desaparece de la auth
  • Email visible en Admin → Users

Etapa 7: Página /help

Objetivo: documentación de uso del producto para todos los roles. Bloque B — solo si hay tiempo.

Alcance

Nueva ruta /help accesible para todos los roles autenticados.

Contenido mínimo (4 secciones):

  1. Cómo parametrizar actividades

    • Tiempos de ejecución (tiempo nominal y AS-IS/TO-BE)
    • Probabilidad de ejecución
    • Recursos asignados y minutos de utilización
    • Costo fijo por actividad
  2. Cómo simular y leer el reporte

    • Qué hace el botón "Simular"
    • Indicador de simulación desactualizada
    • Qué significa cada KPI del reporte
    • Cómo leer el heatmap
    • Columnas de la tabla comparativa (ahorro, eficiencia)
  3. Cómo exportar

    • PDF: qué incluye cada documento (Actual, Automatizado, ROI)
    • CSV: estructura del archivo exportado
    • Cuándo usar PDF vs. CSV
  4. Configuración del proceso (panel Global)

    • Campos del panel: nombre, descripción, volumen mensual, horas laborables
    • Escenario automatizado: costo fijo TO-BE, inversión inicial, ROI

UX:

  • Layout con sidebar de navegación a las 4 secciones
  • Link a /help en el footer (junto a la versión)
  • Criterio de wow: buscador simple de texto en la página

Criterios de validación

  • Las 4 secciones tienen contenido completo y preciso (validación visual del director)
  • Link desde footer funciona
  • Accesible para todos los roles

Definiciones estables durante Sprint 8 (no reabrir)

  • Stack tecnológico (sin cambios)
  • Paleta cromática y sistema de componentes (sin cambios)
  • Modelo de escenarios Modelo A (sin cambios)
  • ROI: cálculos nominales simples (sin cambios)
  • 4 roles de plataforma: platform_admin, member, client_editor, client_viewer (sin cambios)

Convención de commits para Sprint 8

feat(areas): tabla areas + RLS en migración 017
feat(area-repo): CRUD de áreas por organización
feat(library): agrupación por org → área reemplaza clientName
feat(workspace): selector de área reemplaza campo clientName libre
feat(admin): CRUD de áreas en panel de organización
feat(client-editor): permisos BPMN insert + update vía migración 018
feat(help): página /help con 4 secciones de documentación de uso
fix(import): botón importar visible para client_editor
refactor(router): guards AdminLayout + WorkspacePage + ImportPage → beforeLoad
chore(version): v0.8.0, CHANGELOG.md, footer con versión
docs(changelog): etapa N sprint 8

Archivos de referencia

Archivo Por qué leerlo antes
simulador-web/CLAUDE.md Reglas de implementación, stack, marca, versionado
simulador-web/STRATEGIC_DIRECTION.md Decisiones estratégicas para Sprint 8 (sección final)
simulador-web/BACKLOG.md Item [SPRINT-8] con scope de org→areas
simulador-web/TECH_DEBT.md Items guards + auth para Etapa 6
supabase/migrations/ Auditar schema actual antes de escribir migraciones 017-018
src/persistence/supabase/process-repo.ts Entender fromRow y getById para extender con areaId
src/features/library/LibraryPage.tsx Entender agrupación actual para reemplazarla
src/features/workspace/WorkspacePage.tsx Entender sidebar actual para reemplazar clientName
src/features/admin/ Entender estructura de Admin UI para agregar CRUD de áreas