11 KiB
GFIBER-673 — Propuesta de tutorial in-app con reactour
Contexto
GFIBER-673 es un spike: "¿cómo se vería un tutorial paso a paso al primer login o tras un release mayor, para Admins y para Users?" No existe hoy ningún componente de tour/onboarding en el repo (confirmado por grep exhaustivo). El usuario propuso evaluar reactour como librería base. Esta propuesta cubre: (1) el veredicto de viabilidad técnica, (2) el contenido concreto del tour para cada rol, anclado en pantallas reales del repo, y (3) el enfoque de implementación e integración con las convenciones ya establecidas del proyecto.
Veredicto de viabilidad: apto, sin bloqueantes
| Chequeo | Requisito de reactour | Este repo | Resultado |
|---|---|---|---|
| React | 16.x | 17.x | 18.x | 19.x (peer dep) |
React 19.2.4 (pinneado) | Compatible |
| Next.js / router | Necesita boundary 'use client' (hooks + DOM) |
Next.js 16.2.7, App Router, 57 archivos ya usan 'use client' |
Patrón ya establecido |
| TypeScript | Reescrito en TS nativo | TS 5 strict | Compatible |
| Estilos | Popover/mask vía portal, estilable por props inline | Tailwind v4 (app/) + SCSS Modules (packages/design-system), sin Radix/MUI/Headless UI |
Sin conflicto de dependencias |
| Persistencia "ya lo vio" | Fuera del alcance de la librería | Convención ya usada: columna boolean en profiles vía migración (is_enabled, notify_pending_attendance) |
Patrón reusable directo |
| Paquete | @reactour/tour v3.8.0, MIT, activo (4.1k★) |
— | Apto |
Dos matices para la implementación (no bloquean el spike, pero condicionan el enfoque):
- reactour apunta pasos por selector CSS — usar atributos dedicados
data-tour="..."en los elementos objetivo, nunca clases de Tailwind (frágil ante cambios de diseño). - El theming de reactour es vía objetos de estilo inline, no clases Tailwind — se puede lograr consistencia visual referenciando directamente las custom properties
--fiber-*ya definidas enapp/globals.css(ej.backgroundColor: 'var(--fiber-primary)'), sin depender de Tailwind dentro del portal de reactour.
Fuera de alcance de este spike: Triple T no existe en este repositorio (confirmado — solo aparece mencionado como "fuera de alcance" en docs/plans/*). El AC del ticket pide tours por rol (Admin / User), no por producto, así que esto no bloquea la propuesta — se deja anotado como gap a resolver el día que Triple T viva en este monorepo o se integre.
Hallazgo clave que moldea el diseño
Todos los roles — Admin, Owner y User — aterrizan en la misma página tras login: /upsell-evaluator (confirmado: app/page.tsx redirige a /login, y el wordmark del NavBar en components/NavBar.tsx:38 linkea /upsell-evaluator como "GFiber Pilot home" para todos). No existe un "home de admin" separado. Esto significa que un tour "por rol" en el sentido de páginas de entrada distintas no aplica — en cambio, el diseño correcto es:
- Un tour base común (Upsell Evaluator) que ven Admin y User por igual, la primera vez que llegan a
/upsell-evaluator. - Un tour adicional, solo-Admin/Owner, que se dispara la primera vez que visitan
/admino/dashboard— páginas que ni siquiera son visibles en el NavBar para un User raso (canAccessAdminencomponents/NavBar.tsx).
Esto respeta el AC del ticket ("proponer tutorial para Admins" + "proponer tutorial para Users") sin inventar una página de entrada que no existe.
Propuesta — Tour de Users (y base para Admins)
Disparado la primera vez que cualquier usuario autenticado visita /upsell-evaluator (app/(protected)/upsell-evaluator/page.tsx).
| # | Elemento objetivo (data-tour) |
Componente real | Contenido del paso |
|---|---|---|---|
| 1 | nav-wordmark |
components/NavBar.tsx |
"Este es tu punto de partida — GFiber Pilot home." |
| 2 | stage-transition-gaid |
_flow/StageTransition.tsx |
"Ingresa el GAID y Case ID del cliente, luego presiona Check para validar elegibilidad." |
| 3 | stage-transition-hardstops |
Chips de riesgo (Financial Sensitivity, Technical Instability, Negative Sentiment) | "Marca estos chips si detectas alguna señal de riesgo antes de continuar." |
| 4 | objection-drawer-trigger |
ObjectionDrawer.tsx |
"En cualquier momento de la llamada, abre este panel para scripts de manejo de objeciones." |
| 5 | copilot-widget-launcher |
components/upsell/CopilotWidget.tsx |
"¿Duda sobre qué responder? Pega el chat del cliente aquí y el asistente sugiere una respuesta." |
| 6 | stage-discovery-quickcapture |
_flow/StageDiscovery.tsx |
"Usa estos chips rápidos (Gaming, WFH, Cámaras...) o profundiza en el acordeón de abajo." |
| 7 | stage-value-comparison |
_flow/StageValue.tsx |
"Aquí comparas el plan actual del cliente contra el upgrade recomendado, con costo diario." |
| 8 | stage-close-outcome |
_flow/StageClose.tsx |
"Registra el resultado (Aceptado/Declinado); si aceptó, la nota para Salesforce se genera sola." |
| 9 | navbar-my-metrics |
Link "My Metrics" en components/NavBar.tsx |
"Aquí revisas tu historial de sesiones y métricas personales." |
Persistencia: nueva columna has_seen_upsell_tour boolean not null default false en profiles, siguiendo el patrón exacto de supabase/migrations/20260625000000_add_notify_pending_attendance_to_profiles.sql. Se marca true vía Server Action al completar o saltar el tour.
Propuesta — Tour adicional de Admins/Owners
Disparado la primera vez que un usuario con role IN ('admin', 'owner') visita /admin (y, por separado, la primera vez que visita /dashboard) — ambos gateados hoy por app/(protected)/admin/layout.tsx y app/(protected)/dashboard/layout.tsx.
Parte A — /admin (User Management)
| # | Elemento objetivo | Componente real | Contenido del paso |
|---|---|---|---|
| 1 | admin-invite-form |
components/admin/InviteUserForm.tsx |
"Invita usuarios nuevos por email, asignando su rol desde aquí." |
| 2 | admin-user-table |
components/admin/UserTable.tsx |
"Esta tabla lista a todos los usuarios; usa '⋯ More' para gestionar cada uno." |
| 3 | admin-manage-drawer |
components/admin/UserManageDrawer.tsx |
"Desde aquí cambias rol, equipo, activas/desactivas o reenvías la invitación." |
Parte B — /dashboard (Sales Dashboard)
| # | Elemento objetivo | Componente real | Contenido del paso |
|---|---|---|---|
| 1 | dashboard-date-range |
DateRangeControl |
"Filtra todas las métricas por rango de fechas." |
| 2 | dashboard-team-toggle |
TeamToggle |
"Alterna entre ver solo tu equipo o todos los equipos." |
| 3 | dashboard-leaderboard |
Leaderboard / AllTeamsCard |
"Aquí comparas el desempeño de agentes y equipos." |
| 4 | dashboard-export |
Botón "Export report" (app/(protected)/dashboard/api/export/route.ts) |
"Descarga el reporte completo en un clic." |
Persistencia: columna has_seen_admin_tour boolean not null default false en profiles — mismo patrón de migración, solo relevante/seteable para admin/owner.
Controles de usuario: Skip y reinicio manual
Ambos tours (Users y Admin/Owner) deben poder saltarse y reiniciarse a demanda — no solo dispararse una vez de forma automática:
- Skip: cada paso del popover incluye un botón "Saltar tutorial" (override del componente
Close/footer de reactour, vía la propcomponentsdeTourProvider), visible desde el primer paso — no solo al final. Al presionarlo,setIsOpen(false)cierra el tour y dispara la misma Server Action que marcahas_seen_upsell_tour/has_seen_admin_tourentrue, para que no se vuelva a mostrar automáticamente en el siguiente login. - Reinicio manual: un botón persistente para volver a lanzar el tour cuando el usuario quiera, sin depender del flag de "ya lo vio":
- Tour de Users → ícono "? Ver tutorial" junto al wordmark en
components/NavBar.tsx, visible para todos los roles. LlamasetIsOpen(true)reseteandocurrentStepa 0, sin tocar el flag enprofiles(reiniciar no debe tener efectos secundarios de persistencia). - Tour de Admin → mismo patrón de botón "? Ver tutorial", ubicado en el header de
app/(protected)/admin/page.tsxy deapp/(protected)/dashboard/page.tsxrespectivamente (cada página relanza solo su propia mitad del tour — Parte A o Parte B).
- Tour de Users → ícono "? Ver tutorial" junto al wordmark en
Esto significa que el TourProvider necesita exponer el hook useTour no solo para auto-arranque en el layout, sino también consumible desde estos botones — encaja de forma natural con la API de reactour (useTour() es válido en cualquier client component descendiente del TourProvider).
Enfoque técnico de implementación (para cuando se apruebe pasar de spike a build)
- Instalar
@reactour/tour. - Nuevo client component
components/ProductTour.tsx('use client'), envolviendo el contenido deapp/(protected)/layout.tsxcon<TourProvider>. Recibeprofile.role,profile.has_seen_upsell_tour,profile.has_seen_admin_tourcomo props desde el server component padre (ya se resuelve ahí víagetCurrentUserWithProfile()). - Dos arrays de
steps(upsellTourSteps,adminTourSteps) definidos por selectordata-tour, no por clase Tailwind. - Botón "? Ver tutorial" reutilizable (
components/TourRestartButton.tsx) que consumeuseTour()— instanciado enNavBar.tsx(tour de Users) y en los headers de/adminy/dashboard(tour de Admin). - Override del footer/
Closede reactour vía la propcomponentsdeTourProvider, agregando la acción "Saltar tutorial" en todos los pasos. - Dos migraciones nuevas en
supabase/migrations/, siguiendo el formatoYYYYMMDDHHMMSS_add_<column>_to_profiles.sql. - Server Action para marcar cada flag
trueal completar o saltar el tour respectivo (patrón ya usado paranotify_pending_attendance); el botón de reinicio manual NO llama a esta acción. - Theming del popover/mask vía las custom properties
--fiber-*existentes enapp/globals.css, para que visualmente no desentone con el resto de la UI. - Spike técnico corto (≈30 min) antes de comprometerse: confirmar si los componentes internos de reactour (
Wrapper,Badge,Close) aceptan override completo o solo estilos inline — determina cuánto se puede "tailwindizar" el look del tour, incluyendo el botón de Skip.
Verificación
- Levantar el repo local, loguear como
user→ confirmar que el tour de Upsell Evaluator se dispara una sola vez (flag persiste tras reload). - Loguear como
admin/owner→ confirmar que ven el tour base de Upsell Evaluator y, al visitar/adminy/dashboardpor primera vez, el tour adicional. - Confirmar que un
usersin acceso a/adminnunca ve ese tour (ni el flag se crea/lee para él de forma que rompa nada). - Revisar visualmente que el popover de reactour respeta paleta y modo oscuro (
--fiber-*, variante.dark). - Presionar "Saltar tutorial" en el paso 1 → confirmar que el tour se cierra y el flag correspondiente queda en
true(no vuelve a auto-abrirse en el siguiente login). - Con el flag ya en
true, presionar el botón "? Ver tutorial" en NavBar//admin//dashboard→ confirmar que el tour se relanza desde el paso 1 sin alterar el flag.
Entrega
Publicar esta propuesta como página en el space de Docmost del proyecto (gfiber-pilot-extension), como respuesta al spike GFIBER-673. Se enlaza el ticket de Jira en la página.