diff --git a/README_Lucozade_Store_Audit.md b/README_Lucozade_Store_Audit.md new file mode 100644 index 0000000..f642fcf --- /dev/null +++ b/README_Lucozade_Store_Audit.md @@ -0,0 +1,651 @@ +# Lucozade Store Audit Dashboard + +> Dashboard web seguro para consultar y analizar auditorías de tiendas Lucozade, con autenticación por usuario/contraseña, recuperación de acceso y despliegue en DigitalCompass. + +--- + +## INFORMACIÓN GENERAL + +| Campo | Detalle | +|---|---| +| Proyecto | Lucozade Store Audit Dashboard | +| Área | Cliente externo / Trade Marketing / Auditoría de Punto de Venta | +| Estado | En despliegue final | +| Developer Principal | Isaac Aracena | +| IT Manager | Luis Matos | +| Fecha de Inicio | 2026-08-05 | +| Fecha de Cierre Estimada | Pendiente de validación final en servidor | +| Ciclo Shape Up | No especificado | +| Board de Ejecución | No especificado | +| PRD del Proyecto | No especificado | +| Repositorio | `https://git.digitalcompass.agency/Isaac_Aracena/lucozade-audit-dashboard` | +| URL de Producción | `https://digitalcompass.agency/lucozade/` | + +--- + +## OBJETIVO + +### Problema que resuelve + +El dashboard de auditorías podía ser accedido únicamente conociendo la URL, sin un mecanismo de autenticación para usuarios externos. Esto representaba un riesgo de acceso no autorizado y dificultaba controlar quién podía consultar la información de auditorías de tiendas. + +Adicionalmente, el dashboard necesita consolidar de forma simple la información de tiendas y respuestas de auditoría provenientes de Google Sheets, permitiendo revisar cobertura, disponibilidad, OOS, exhibiciones, inventario, facings, precios y evidencia fotográfica. + +### Solución implementada + +Se implementó una capa completa de autenticación para la aplicación: + +- Inicio de sesión con correo y contraseña. +- Registro de usuarios externos. +- Control de acceso mediante Supabase. +- Recuperación de contraseña por correo. +- Correos transaccionales enviados por n8n mediante Gmail OAuth. +- Opción **Remember me**. +- Sesiones persistentes o temporales según elección del usuario. +- Botón **Sign out** dentro del dashboard. +- Despliegue preparado bajo la ruta `/lucozade/`. +- Compatibilidad tanto con desarrollo local como con producción. + +La aplicación mantiene además su funcionalidad principal de auditoría y consume información desde Google Sheets para construir el dashboard. + +### Usuarios / Beneficiarios + +- Cliente externo. +- Usuarios autorizados responsables de consultar auditorías de punto de venta. +- Equipos de ejecución y seguimiento de Lucozade. +- Administradores responsables de gestionar accesos. + +--- + +## ARQUITECTURA + +### Diagrama de flujo + +```text + ┌───────────────────────────────┐ + │ Lucozade Store Audit │ + │ React + Vite SPA │ + └──────────────┬────────────────┘ + │ + ┌──────────────────┼──────────────────┐ + │ │ │ + ▼ ▼ ▼ + Supabase Auth n8n Auth Webhook Google Sheets + Sign in / Session Register / Recover Stores + Audits + │ │ │ + │ ▼ │ + │ Supabase Admin API │ + │ │ │ + │ lucozade_access │ + │ │ │ + │ ▼ │ + │ Gmail OAuth │ + │ │ │ + └──────────────────┴──────────────────┘ + │ + ▼ + Dashboard autenticado +``` + +### Flujo de autenticación + +```text +Registro +App -> n8n Webhook -> Supabase Auth Admin -> lucozade_access -> Gmail -> Usuario + +Inicio de sesión +App -> Supabase Auth -> Verificación lucozade_access -> Dashboard + +Recuperación +App -> n8n Webhook -> Supabase generate_link -> Gmail -> Usuario + -> Supabase Recovery Session -> Pantalla New Password -> Supabase Auth +``` + +### Stack tecnológico + +| Componente | Tecnología | Propósito | +|---|---|---| +| Frontend | React 19 + TypeScript | Interfaz del dashboard | +| Bundler | Vite 6 | Desarrollo y compilación | +| UI | Tailwind CSS 4 + Lucide React | Diseño e iconografía | +| Autenticación | Supabase Auth | Usuarios, contraseñas y sesiones | +| Base de datos | Supabase / PostgreSQL | Control de acceso de la aplicación | +| Automatización | n8n | Registro, recuperación y envío de correos | +| Notificaciones | Gmail OAuth | Correos transaccionales | +| Datos de auditoría | Google Sheets | Tiendas y respuestas de auditoría | +| Parsing CSV | PapaParse | Lectura de exportaciones de Google Sheets | +| Infraestructura | DigitalCompass + EasyPanel | Hosting y servicios | +| Control de versiones | Gitea | Repositorio del proyecto | + +### Integraciones externas + +| Sistema | Tipo de integración | Datos que fluyen | +|---|---|---| +| Supabase Auth | REST API | Registro, login, sesión, recuperación y cambio de contraseña | +| Supabase PostgREST | REST API | Autorización mediante `lucozade_access` | +| n8n | Webhook POST | Solicitudes de registro y recuperación | +| Gmail | OAuth 2.0 | Correos de bienvenida y recuperación | +| Google Sheets | CSV publicado / API v4 | Tiendas, auditorías y evidencia de campo | +| DigitalCompass | Hosting web | Publicación del `dist` de producción | + +--- + +## REGLAS DE NEGOCIO + +1. El sistema acepta usuarios externos; no existe restricción por dominio de correo. +2. La cuenta real del usuario vive en `auth.users` de Supabase. +3. `public.lucozade_access` determina si una cuenta puede acceder a esta aplicación. +4. Eliminar una fila de `lucozade_access` revoca el acceso, pero no elimina la cuenta de Supabase Auth. +5. Para eliminar completamente una cuenta se debe eliminar el usuario desde **Supabase Authentication → Users**. +6. Solo usuarios con `is_active = true` pueden ingresar al dashboard. +7. El registro se realiza desde n8n utilizando la API administrativa de Supabase. +8. La `service_role` de Supabase nunca debe estar disponible en el navegador ni commiteada en Gitea. +9. El inicio de sesión se realiza directamente contra Supabase Auth utilizando la clave pública `anon`. +10. **Remember me** conserva correo y sesión de forma persistente. +11. Si **Remember me** no está seleccionado, la sesión se conserva únicamente durante la sesión actual del navegador/pestaña. +12. La recuperación utiliza enlaces nativos de recuperación de Supabase generados desde n8n. +13. Los correos de autenticación utilizan **George Mendieta** como Sender Name en los nodos Gmail. +14. El workflow debe permitir CORS para los orígenes locales aprobados y para `https://digitalcompass.agency`. +15. La aplicación de producción se publica obligatoriamente bajo `/lucozade/`. +16. No se deben incluir referencias visuales a GomezLee Marketing dentro de la interfaz de autenticación orientada al cliente. +17. Si Google Sheets no está disponible, la aplicación dispone de un dataset de fallback para evitar que el dashboard falle completamente. + +--- + +## CONFIGURACIÓN Y SETUP + +### Prerrequisitos + +- Node.js y npm instalados. +- Acceso al repositorio Gitea. +- Acceso al proyecto Supabase. +- Acceso a n8n. +- Credencial Gmail OAuth configurada en n8n. +- Permisos para publicar archivos en el servidor de DigitalCompass. +- `ADDITIONAL_REDIRECT_URLS` configurado en el entorno de Supabase/EasyPanel. + +### Variables de entorno de la aplicación + +Crear `.env` a partir de `.env.example`. + +| Variable | Descripción | Dónde se obtiene | +|---|---|---| +| `VITE_SUPABASE_URL` | URL pública del proyecto Supabase | Infraestructura / Supabase | +| `VITE_SUPABASE_ANON_KEY` | Clave pública `anon` utilizada por el frontend | Supabase | +| `VITE_N8N_AUTH_WEBHOOK_URL` | Webhook de producción del workflow de autenticación | n8n | + +Ejemplo: + +```env +VITE_SUPABASE_URL="https://YOUR-SUPABASE-DOMAIN" +VITE_SUPABASE_ANON_KEY="YOUR_ANON_KEY" +VITE_N8N_AUTH_WEBHOOK_URL="https://YOUR-N8N-DOMAIN/webhook/lucozade-auth-v6" +``` + +> **NUNCA commitear `.env`, `SERVICE_ROLE_KEY`, contraseñas ni credenciales OAuth al repositorio.** + +### Configuración de n8n + +Workflow actual: + +```text +Lucozade - Auth Direct via n8n V6 +``` + +Webhook: + +```text +POST /webhook/lucozade-auth-v6 +``` + +El workflow debe tener configurados: + +- URL de Supabase. +- `service_role` de Supabase únicamente dentro de n8n. +- Credencial Gmail OAuth. +- Sender Name `George Mendieta`. +- CORS para producción y ambientes locales autorizados. + +La versión corregida debe mantener expresiones booleanas válidas en todos los nodos IF, por ejemplo: + +```text +={{ $json.valid }} +``` + +y no: + +```text +={ $json.valid } +``` + +### ADDITIONAL_REDIRECT_URLS + +Mantener los valores existentes de la instalación Supabase y agregar las URLs de Lucozade necesarias para producción y pruebas locales. + +Producción: + +```text +https://digitalcompass.agency/lucozade/ +https://digitalcompass.agency/lucozade +``` + +Desarrollo Vite: + +```text +http://localhost:3000/ +http://127.0.0.1:3000/ +http://localhost:5173/ +http://127.0.0.1:5173/ +``` + +Preview: + +```text +http://localhost:4173/lucozade/ +http://127.0.0.1:4173/lucozade/ +``` + +XAMPP: + +```text +http://localhost/lucozade/ +http://localhost/lucozade +http://127.0.0.1/lucozade/ +http://127.0.0.1/lucozade +``` + +### CORS del Webhook n8n + +Orígenes permitidos: + +```text +https://digitalcompass.agency,http://localhost,http://127.0.0.1,http://localhost:3000,http://127.0.0.1:3000,http://localhost:5173,http://127.0.0.1:5173,http://localhost:4173,http://127.0.0.1:4173 +``` + +No colocar rutas como `/lucozade/` en **Allowed Origins (CORS)**; se configuran únicamente los orígenes. + +--- + +## ESQUEMA DE BASE DE DATOS + +El script versionado en el repositorio es: + +```text +Lucozade_Auth_V6_Supabase.sql +``` + +La tabla principal de autorización es: + +```text +public.lucozade_access +``` + +Campos: + +| Campo | Tipo | Descripción | +|---|---|---| +| `user_id` | `uuid` | ID del usuario en `auth.users` | +| `email` | `text` | Correo normalizado | +| `full_name` | `text` | Nombre del usuario | +| `is_active` | `boolean` | Habilita o bloquea el acceso | +| `created_at` | `timestamptz` | Fecha de creación | +| `updated_at` | `timestamptz` | Última actualización | + +La tabla usa RLS y permite al usuario autenticado consultar únicamente su propio acceso activo. + +El script también instala un trigger para crear automáticamente `lucozade_access` cuando un usuario nuevo de la aplicación es creado en Supabase Auth. + +--- + +## INSTALACIÓN / DEPLOY + +### Clonar el repositorio + +```bash +git clone https://git.digitalcompass.agency/Isaac_Aracena/lucozade-audit-dashboard.git +cd lucozade-audit-dashboard +``` + +### Preparar variables + +```bash +cp .env.example .env +``` + +Editar `.env` con los valores reales. + +### Instalar dependencias + +```bash +npm install +``` + +### Desarrollo local + +```bash +npm run dev +``` + +URL: + +```text +http://localhost:3000/ +``` + +### Validación TypeScript + +```bash +npm run lint +``` + +### Build de producción + +```bash +npm run build +``` + +El resultado se genera en: + +```text +/dist +``` + +### Deploy en DigitalCompass + +La configuración de Vite utiliza: + +```ts +base: '/lucozade/' +``` + +Por lo tanto, en el servidor debe existir: + +```text +/lucozade/ +``` + +y dentro se debe copiar **el contenido interno de `dist`**: + +```text +lucozade/ +├── index.html +└── assets/ +``` + +No subir la carpeta así: + +```text +lucozade/dist/index.html +``` + +URL final: + +```text +https://digitalcompass.agency/lucozade/ +``` + +--- + +## CÓMO FUNCIONA + +### Flujo paso a paso — Registro + +1. El usuario selecciona **Create account**. +2. Ingresa nombre, correo y contraseña. +3. La aplicación envía la solicitud al webhook de n8n. +4. n8n crea el usuario mediante Supabase Auth Admin. +5. Se crea o actualiza `lucozade_access`. +6. n8n envía el correo de bienvenida mediante Gmail. +7. El usuario puede iniciar sesión con sus credenciales. + +### Flujo paso a paso — Inicio de sesión + +1. El usuario ingresa correo y contraseña. +2. La aplicación autentica directamente contra Supabase Auth. +3. La aplicación verifica que exista un acceso activo en `lucozade_access`. +4. Si está autorizado, se muestra el dashboard. +5. Si **Remember me** está seleccionado, la sesión queda persistente. +6. El botón **Sign out** invalida/limpia la sesión local. + +### Flujo paso a paso — Recuperación + +1. El usuario selecciona **Forgot password?**. +2. Ingresa su correo. +3. La aplicación llama al webhook de n8n. +4. n8n valida que el usuario tenga acceso activo. +5. n8n solicita a Supabase un enlace nativo de recuperación. +6. Gmail envía el correo al usuario. +7. El usuario pulsa **Create new password**. +8. Supabase procesa el token y redirige a la aplicación. +9. La aplicación detecta la sesión de recuperación. +10. Se muestra la pantalla para establecer la contraseña nueva. +11. Supabase actualiza la contraseña. +12. El usuario vuelve a **Sign in**. + +### Flujo del dashboard + +1. La aplicación carga la lista maestra de tiendas. +2. Carga las respuestas de auditoría desde Google Sheets. +3. Normaliza y relaciona tiendas con respuestas. +4. Calcula KPIs y estado de auditoría. +5. Presenta filtros, métricas, detalle por tienda, inventario, OOS y evidencia. +6. Si falla Google Sheets, utiliza el dataset fallback incluido en la aplicación. + +--- + +## SCHEDULES / TRIGGERS + +| Trigger | Frecuencia | Descripción | +|---|---|---| +| `POST /webhook/lucozade-auth-v6` | On demand | Registro y recuperación solicitados desde la aplicación | +| Supabase Auth REST | On demand | Login, refresh de sesión y cambio de contraseña | +| Carga Google Sheets | Al abrir/refrescar dashboard | Obtiene tiendas y respuestas para construir el dashboard | + +--- + +## TESTING + +### Casos de prueba mínimos + +| Caso | Input | Output esperado | Estado | +|---|---|---|---| +| Registro nuevo | Correo no existente + contraseña válida | Usuario en Auth + acceso activo | Verificado en local | +| Registro duplicado | Correo ya existente | No sobrescribir contraseña silenciosamente | Verificado | +| Login válido | Credenciales correctas | Acceso al dashboard | Verificado | +| Login inválido | Contraseña incorrecta | Error controlado | Verificado | +| Remember me | Checkbox activo | Correo/sesión persistentes | Implementado | +| Sesión temporal | Checkbox inactivo | Sesión no persistente entre sesiones del navegador | Implementado | +| Recuperación | Usuario activo | Correo de recuperación enviado | Verificado en local | +| Link recovery | Token válido | Pantalla para crear contraseña | Pendiente de validación final en servidor | +| Usuario bloqueado | `is_active=false` | Acceso denegado | Implementado | +| CORS Vite | `localhost:3000` | Webhook accesible | Configurado | +| CORS XAMPP | `http://localhost` | Webhook accesible | Configurado | +| Build producción | `npm run build` | `dist/index.html` + `dist/assets/` | Verificado | +| Base de Vite | `/lucozade/` | Assets resueltos bajo la subcarpeta | Verificado en build | +| Google Sheets inaccesible | Error de red/API | Dataset fallback, sin crash | Implementado | + +--- + +## ERRORES CONOCIDOS Y TROUBLESHOOTING + +| Error | Causa probable | Solución | +|---|---|---| +| `Invalid authentication credentials` | Contraseña incorrecta o cuenta Auth diferente al acceso público | Revisar **Authentication → Users**; `lucozade_access` no almacena contraseñas | +| `The authentication service could not be reached` | CORS, webhook no publicado o URL incorrecta | Revisar `VITE_N8N_AUTH_WEBHOOK_URL`, CORS y ejecución de n8n | +| `Wrong type ... expected a boolean` en n8n | Expresión IF importada como texto | Usar `={{ $json.valid }}` y expresiones equivalentes | +| Correo no llega | Credencial Gmail o rama de n8n falla | Revisar **n8n → Executions** y nodos Gmail | +| Recovery abre 404 | App aún no publicada en `/lucozade/` o redirect incorrecto | Publicar `dist` y revisar `ADDITIONAL_REDIRECT_URLS` | +| Assets 404 en producción | `base` de Vite o estructura del deploy incorrecta | Confirmar `base: '/lucozade/'` y contenido de `dist` directamente en `/lucozade/` | +| Cuenta “existe” después de borrar tabla pública | El usuario continúa en `auth.users` | Eliminar desde Supabase **Authentication → Users** si se necesita borrado total | +| Dashboard muestra demo | Google Sheets no pudo ser leído | Revisar publicación/permisos del Sheet y consola del navegador | +| Webhook funciona en Vite pero no XAMPP | Falta `http://localhost` en CORS | Agregar el origen exacto al Webhook | + +--- + +## MONITOREO + +- **n8n Executions:** revisar errores del workflow de autenticación. +- **Supabase Authentication → Users:** revisar usuarios reales del sistema. +- **`public.lucozade_access`:** verificar usuarios habilitados y bloqueados. +- **Gmail OAuth:** confirmar que los correos se están enviando correctamente. +- **Browser Console:** revisar errores de CORS, Supabase y Google Sheets. +- **Output normal esperado:** usuario autenticado, acceso activo, dashboard cargado y datos de auditoría visibles. + +--- + +## ESTRUCTURA DEL REPOSITORIO + +```text +lucozade-audit-dashboard/ +├── dist/ # Build listo para publicar en servidor +│ ├── index.html +│ └── assets/ +├── src/ +│ ├── components/ # UI del dashboard y autenticación +│ ├── data/ # Dataset fallback +│ ├── services/ +│ │ ├── supabaseAuth.ts # Auth, sesiones y recuperación +│ │ └── googleSheets.ts # Lectura y normalización de Sheets +│ ├── utils/ +│ ├── App.tsx +│ ├── main.tsx +│ └── index.css +├── .env.example +├── .gitignore +├── index.html +├── Lucozade_Auth_V6_Supabase.sql +├── metadata.json +├── package.json +├── package-lock.json +├── SETUP_RAPIDO.txt +├── tsconfig.json +├── VALIDACION_V6.txt +├── vite.config.ts +└── README.md +``` + +### Workflow n8n + +El workflow se ejecuta en la instancia de n8n y contiene configuración sensible. + +Si se decide versionarlo en Gitea, debe exportarse **sin secretos** a una ruta como: + +```text +/automation/Lucozade_Auth_Direct_n8n_V6_READY.json +``` + +Antes de commitearlo, eliminar cualquier `SERVICE_ROLE_KEY`, token o credencial incrustada. + +--- + +## CHANGELOG + +### 2026-08-07 — v1.0 + +- Login por correo y contraseña operativo. +- Registro mediante n8n + Supabase. +- Control de acceso con `lucozade_access`. +- Recuperación de contraseña mediante n8n + Gmail. +- Corrección de expresiones booleanas del workflow. +- **Remember me** agregado. +- **Sign out** agregado. +- Base Vite `/lucozade/` configurada. +- Compatibilidad con Vite, XAMPP y producción. +- `dist` generado y versionado para despliegue en servidor. +- Repositorio inicial publicado en Gitea. + +### 2026-08-06 — v0.6 + +- Integración completa de Supabase Auth. +- Flujo de recuperación y correos HTML. +- Configuración de redirect URLs y CORS. +- Preparación de build para DigitalCompass. + +### 2026-08-05 — v0.1 + +- Inicio de implementación de autenticación. +- Setup inicial de Supabase, n8n y aplicación. + +--- + +## DECISIONS LOG + +### DEC-001 — Supabase Auth como fuente de identidad + +- **Fecha:** 2026-08-05 +- **Contexto:** Se necesitaba proteger el dashboard con usuarios externos. +- **Opciones consideradas:** Autenticación propia vs Supabase Auth. +- **Decisión:** Usar Supabase Auth. +- **Razón:** Centraliza usuarios, contraseñas, sesiones y recuperación de forma segura. + +### DEC-002 — n8n para correos de autenticación + +- **Fecha:** 2026-08-05 +- **Contexto:** Se requerían correos HTML modernos sin depender de SMTP directo en Supabase. +- **Opciones consideradas:** SMTP/GoTrue Hook vs webhook directo desde la aplicación. +- **Decisión:** Usar n8n como backend de registro/recuperación y Gmail OAuth para envío. +- **Razón:** Mantiene las claves administrativas fuera del navegador y permite controlar completamente el HTML de los correos. + +### DEC-003 — Separar identidad de autorización + +- **Fecha:** 2026-08-06 +- **Contexto:** Borrar datos de acceso no debía confundirse con eliminar la cuenta real. +- **Opciones consideradas:** Solo `auth.users` vs tabla de acceso por aplicación. +- **Decisión:** `auth.users` mantiene la identidad y `lucozade_access` controla el acceso a Lucozade. +- **Razón:** Permite bloquear acceso sin destruir la cuenta. + +### DEC-004 — Base Vite `/lucozade/` + +- **Fecha:** 2026-08-06 +- **Contexto:** La aplicación se desplegará en una subcarpeta de DigitalCompass. +- **Decisión:** Configurar el build con `base: '/lucozade/'`. +- **Razón:** Garantiza rutas correctas de JavaScript, CSS y assets en producción. + +### DEC-005 — Sin branding de GomezLee en la autenticación + +- **Fecha:** 2026-08-05 +- **Contexto:** La aplicación está orientada a un cliente externo. +- **Decisión:** Mantener la pantalla de autenticación neutral y alineada visualmente con Lucozade. +- **Razón:** Evitar referencias internas en una aplicación de cliente. + +### DEC-006 — `service_role` solo en backend + +- **Fecha:** 2026-08-05 +- **Contexto:** El registro y generación de enlaces requieren permisos administrativos. +- **Decisión:** La `service_role` nunca se expone en el frontend. +- **Razón:** Es una credencial crítica que omite restricciones de seguridad de Supabase. + +--- + +## CONTACTOS DEL PROYECTO + +| Rol | Nombre | Contacto | +|---|---|---| +| Product Owner / Cliente | No especificado | No especificado | +| IT Manager | Luis Matos | Contacto interno | +| Developer Principal | Isaac Aracena | `iaracena@gomezleemarketing.com` | + +--- + +## DEFINITION OF DONE + +- [x] Autenticación por correo y contraseña implementada. +- [x] Registro de usuarios implementado. +- [x] Control de acceso con Supabase implementado. +- [x] Recuperación de contraseña implementada. +- [x] Correos HTML enviados mediante n8n + Gmail. +- [x] **Remember me** implementado. +- [x] **Sign out** implementado. +- [x] Variables de frontend documentadas en `.env.example`. +- [x] Script SQL versionado. +- [x] Build `dist` generado y agregado al repositorio. +- [x] Código commiteado y pusheado a Gitea. +- [x] README actualizado. +- [x] Pruebas principales realizadas en ambiente local. +- [ ] Publicar `dist` en `/lucozade/` del servidor. +- [ ] Validar recuperación completa desde la URL de producción. +- [ ] Validación final del cliente / responsable del proyecto. + +--- + +Documento basado en el estándar interno de documentación de repositorios y mantenido por el equipo responsable del proyecto.